diff options
author | Antonio Vazquez <blendergit@gmail.com> | 2020-06-18 21:35:31 +0300 |
---|---|---|
committer | Antonio Vazquez <blendergit@gmail.com> | 2020-06-18 21:35:31 +0300 |
commit | 3536a2aac43d705d6efc53936f194563031343e4 (patch) | |
tree | 43f9343eed4eb894d3ef4edba9490914b27ca4dd | |
parent | 362618ec0b11764acc8748be8e0c622b6be4540d (diff) | |
parent | d56444392f41e5115bb001af9fa50747f6295834 (diff) |
Merge branch 'master' into greasepencil-edit-curve
173 files changed, 3951 insertions, 2106 deletions
diff --git a/build_files/buildbot/README.md b/build_files/buildbot/README.md index cf129f83b39..06733c9a42d 100644 --- a/build_files/buildbot/README.md +++ b/build_files/buildbot/README.md @@ -8,7 +8,7 @@ Code signing is done as part of INSTALL target, which makes it possible to sign files which are aimed into a bundle and coming from a non-signed source (such as libraries SVN). -This is achieved by specifying `slave_codesign.cmake` as a post-install script +This is achieved by specifying `worker_codesign.cmake` as a post-install script run by CMake. This CMake script simply involves an utility script written in Python which takes care of an actual signing. diff --git a/build_files/buildbot/slave_rsync.py b/build_files/buildbot/slave_rsync.py deleted file mode 100644 index 19f1e67408d..00000000000 --- a/build_files/buildbot/slave_rsync.py +++ /dev/null @@ -1,37 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ##### END GPL LICENSE BLOCK ##### - -# <pep8 compliant> - -# Runs on buildbot slave, rsync zip directly to buildbot server rather -# than using upload which is much slower - -import buildbot_utils -import os -import sys - -if __name__ == "__main__": - builder = buildbot_utils.create_builder_from_arguments() - - # rsync, this assumes ssh keys are setup so no password is needed - local_zip = "buildbot_upload.zip" - remote_folder = "builder.blender.org:/data/buildbot-master/uploaded/" - remote_zip = remote_folder + "buildbot_upload_" + builder.name + ".zip" - - command = ["rsync", "-avz", local_zip, remote_zip] - buildbot_utils.call(command) diff --git a/build_files/buildbot/slave_bundle_dmg.py b/build_files/buildbot/worker_bundle_dmg.py index 11d2c3cb602..cd3da85e12a 100755 --- a/build_files/buildbot/slave_bundle_dmg.py +++ b/build_files/buildbot/worker_bundle_dmg.py @@ -30,7 +30,7 @@ from tempfile import TemporaryDirectory, NamedTemporaryFile from typing import List BUILDBOT_DIRECTORY = Path(__file__).absolute().parent -CODESIGN_SCRIPT = BUILDBOT_DIRECTORY / 'slave_codesign.py' +CODESIGN_SCRIPT = BUILDBOT_DIRECTORY / 'worker_codesign.py' BLENDER_GIT_ROOT_DIRECTORY = BUILDBOT_DIRECTORY.parent.parent DARWIN_DIRECTORY = BLENDER_GIT_ROOT_DIRECTORY / 'release' / 'darwin' diff --git a/build_files/buildbot/slave_codesign.cmake b/build_files/buildbot/worker_codesign.cmake index fd2beae11a0..f37feaef407 100644 --- a/build_files/buildbot/slave_codesign.cmake +++ b/build_files/buildbot/worker_codesign.cmake @@ -33,7 +33,7 @@ else() endif() execute_process( - COMMAND ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_LIST_DIR}/slave_codesign.py" + COMMAND ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_LIST_DIR}/worker_codesign.py" "${CMAKE_INSTALL_PREFIX}" WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} RESULT_VARIABLE exit_code diff --git a/build_files/buildbot/slave_codesign.py b/build_files/buildbot/worker_codesign.py index a82ee98b1b5..a82ee98b1b5 100755 --- a/build_files/buildbot/slave_codesign.py +++ b/build_files/buildbot/worker_codesign.py diff --git a/build_files/buildbot/slave_compile.py b/build_files/buildbot/worker_compile.py index 1bb72501648..f1357e1864f 100644 --- a/build_files/buildbot/slave_compile.py +++ b/build_files/buildbot/worker_compile.py @@ -25,7 +25,7 @@ import buildbot_utils def get_cmake_options(builder): post_install_script = os.path.join( - builder.blender_dir, 'build_files', 'buildbot', 'slave_codesign.cmake') + builder.blender_dir, 'build_files', 'buildbot', 'worker_codesign.cmake') config_file = "build_files/cmake/config/blender_release.cmake" options = ['-DCMAKE_BUILD_TYPE:STRING=Release', diff --git a/build_files/buildbot/slave_pack.py b/build_files/buildbot/worker_pack.py index 8549a7881e6..87ee49c87d8 100644 --- a/build_files/buildbot/slave_pack.py +++ b/build_files/buildbot/worker_pack.py @@ -18,7 +18,7 @@ # <pep8 compliant> -# Runs on buildbot slave, creating a release package using the build +# Runs on buildbot worker, creating a release package using the build # system and zipping it into buildbot_upload.zip. This is then uploaded # to the master in the next buildbot step. @@ -110,7 +110,7 @@ def pack_mac(builder): release_dir = os.path.join(builder.blender_dir, 'release', 'darwin') buildbot_dir = os.path.join(builder.blender_dir, 'build_files', 'buildbot') - bundle_script = os.path.join(buildbot_dir, 'slave_bundle_dmg.py') + bundle_script = os.path.join(buildbot_dir, 'worker_bundle_dmg.py') command = [bundle_script] command += ['--dmg', package_filepath] diff --git a/build_files/buildbot/slave_test.py b/build_files/buildbot/worker_test.py index b959568a5c6..b959568a5c6 100644 --- a/build_files/buildbot/slave_test.py +++ b/build_files/buildbot/worker_test.py diff --git a/build_files/buildbot/slave_update.py b/build_files/buildbot/worker_update.py index 36a7ae31c84..36a7ae31c84 100644 --- a/build_files/buildbot/slave_update.py +++ b/build_files/buildbot/worker_update.py diff --git a/doc/python_api/sphinx_doc_gen.py b/doc/python_api/sphinx_doc_gen.py index d4cc1d37262..d76775d749e 100644 --- a/doc/python_api/sphinx_doc_gen.py +++ b/doc/python_api/sphinx_doc_gen.py @@ -83,6 +83,8 @@ import inspect import shutil import logging +from textwrap import indent + from platform import platform PLATFORM = platform().split('-')[0].lower() # 'linux', 'darwin', 'windows' @@ -1196,12 +1198,15 @@ def pyrna_enum2sphinx(prop, use_empty_descriptions=False): break if ok: - return "".join(["* ``%s`` %s.\n" % - (identifier, - ", ".join(escape_rst(val) for val in (name, description) if val), - ) - for identifier, name, description in prop.enum_items - ]) + return "".join([ + "* ``%s``\n" + "%s.\n" % ( + identifier, + # Account for multi-line enum descriptions, allowing this to be a block of text. + indent(", ".join(escape_rst(val) for val in (name, description) if val) or "Undocumented", " "), + ) + for identifier, name, description in prop.enum_items + ]) else: return "" @@ -1268,7 +1273,7 @@ def pyrna2sphinx(basepath): fw(ident + ":%s%s:\n\n" % (id_name, identifier)) if prop.name or prop.description: - fw(ident + " " + ", ".join(val for val in (prop.name, prop.description) if val) + "\n\n") + fw(indent(", ".join(val for val in (prop.name, prop.description) if val), ident + " ") + "\n\n") # special exception, can't use generic code here for enums if enum_text: diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp index f207d8ae07f..19d2730dc93 100644 --- a/intern/cycles/blender/blender_shader.cpp +++ b/intern/cycles/blender/blender_shader.cpp @@ -813,6 +813,14 @@ static ShaderNode *add_node(Scene *scene, sky->sun_direction = normalize(get_float3(b_sky_node.sun_direction())); sky->turbidity = b_sky_node.turbidity(); sky->ground_albedo = b_sky_node.ground_albedo(); + sky->sun_disc = b_sky_node.sun_disc(); + sky->sun_size = b_sky_node.sun_size(); + sky->sun_elevation = b_sky_node.sun_elevation(); + sky->sun_rotation = b_sky_node.sun_rotation(); + sky->altitude = b_sky_node.altitude(); + sky->air_density = b_sky_node.air_density(); + sky->dust_density = b_sky_node.dust_density(); + sky->ozone_density = b_sky_node.ozone_density(); BL::TexMapping b_texture_mapping(b_sky_node.texture_mapping()); get_tex_mapping(&sky->tex_mapping, b_texture_mapping); node = sky; diff --git a/intern/cycles/device/cuda/device_cuda.h b/intern/cycles/device/cuda/device_cuda.h index 1aa2fdd0967..e7cf71ea96c 100644 --- a/intern/cycles/device/cuda/device_cuda.h +++ b/intern/cycles/device/cuda/device_cuda.h @@ -96,9 +96,9 @@ class CUDADevice : public Device { static bool have_precompiled_kernels(); - virtual bool show_samples() const; + virtual bool show_samples() const override; - virtual BVHLayoutMask get_bvh_layout_mask() const; + virtual BVHLayoutMask get_bvh_layout_mask() const override; void set_error(const string &error) override; @@ -108,7 +108,7 @@ class CUDADevice : public Device { bool support_device(const DeviceRequestedFeatures & /*requested_features*/); - bool check_peer_access(Device *peer_device); + bool check_peer_access(Device *peer_device) override; bool use_adaptive_compilation(); @@ -122,7 +122,7 @@ class CUDADevice : public Device { const char *base = "cuda", bool force_ptx = false); - virtual bool load_kernels(const DeviceRequestedFeatures &requested_features); + virtual bool load_kernels(const DeviceRequestedFeatures &requested_features) override; void load_functions(); @@ -140,19 +140,19 @@ class CUDADevice : public Device { void generic_free(device_memory &mem); - void mem_alloc(device_memory &mem); + void mem_alloc(device_memory &mem) override; - void mem_copy_to(device_memory &mem); + void mem_copy_to(device_memory &mem) override; - void mem_copy_from(device_memory &mem, int y, int w, int h, int elem); + void mem_copy_from(device_memory &mem, int y, int w, int h, int elem) override; - void mem_zero(device_memory &mem); + void mem_zero(device_memory &mem) override; - void mem_free(device_memory &mem); + void mem_free(device_memory &mem) override; - device_ptr mem_alloc_sub_ptr(device_memory &mem, int offset, int /*size*/); + device_ptr mem_alloc_sub_ptr(device_memory &mem, int offset, int /*size*/) override; - virtual void const_copy_to(const char *name, void *host, size_t size); + virtual void const_copy_to(const char *name, void *host, size_t size) override; void global_alloc(device_memory &mem); @@ -252,15 +252,15 @@ class CUDADevice : public Device { int dw, int dh, bool transparent, - const DeviceDrawParams &draw_params); + const DeviceDrawParams &draw_params) override; void thread_run(DeviceTask *task); - virtual void task_add(DeviceTask &task); + virtual void task_add(DeviceTask &task) override; - virtual void task_wait(); + virtual void task_wait() override; - virtual void task_cancel(); + virtual void task_cancel() override; }; CCL_NAMESPACE_END diff --git a/intern/cycles/device/opencl/device_opencl_impl.cpp b/intern/cycles/device/opencl/device_opencl_impl.cpp index beb3174b111..7a84b271878 100644 --- a/intern/cycles/device/opencl/device_opencl_impl.cpp +++ b/intern/cycles/device/opencl/device_opencl_impl.cpp @@ -1948,7 +1948,15 @@ string OpenCLDevice::kernel_build_options(const string *debug_src) int version_major, version_minor; if (OpenCLInfo::get_device_version(cdDevice, &version_major, &version_minor)) { if (version_major >= 2) { - build_options += "-cl-std=CL2.0 "; + /* This appears to trigger a driver bug in Radeon RX cards, so we + * don't use OpenCL 2.0 for those. */ + string device_name = OpenCLInfo::get_readable_device_name(cdDevice); + if (!(string_startswith(device_name, "Radeon RX 4") || + string_startswith(device_name, "Radeon (TM) RX 4") || + string_startswith(device_name, "Radeon RX 5") || + string_startswith(device_name, "Radeon (TM) RX 5"))) { + build_options += "-cl-std=CL2.0 "; + } } } diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt index 2e839a616e9..35339abff45 100644 --- a/intern/cycles/kernel/CMakeLists.txt +++ b/intern/cycles/kernel/CMakeLists.txt @@ -113,6 +113,8 @@ set(SRC_HEADERS kernel_id_passes.h kernel_jitter.h kernel_light.h + kernel_light_background.h + kernel_light_common.h kernel_math.h kernel_montecarlo.h kernel_passes.h diff --git a/intern/cycles/kernel/kernel_emission.h b/intern/cycles/kernel/kernel_emission.h index 71b176a0a8f..4ac07d86dda 100644 --- a/intern/cycles/kernel/kernel_emission.h +++ b/intern/cycles/kernel/kernel_emission.h @@ -326,9 +326,7 @@ ccl_device_noinline_cpu float3 indirect_background(KernelGlobals *kg, /* Background MIS weights. */ # ifdef __BACKGROUND_MIS__ /* Check if background light exists or if we should skip pdf. */ - int res_x = kernel_data.integrator.pdf_background_res_x; - - if (!(state->flag & PATH_RAY_MIS_SKIP) && res_x) { + if (!(state->flag & PATH_RAY_MIS_SKIP) && kernel_data.background.use_mis) { /* multiple importance sampling, get background light pdf for ray * direction, and compute weight with respect to BSDF pdf */ float pdf = background_light_pdf(kg, ray->P, ray->D); diff --git a/intern/cycles/kernel/kernel_light.h b/intern/cycles/kernel/kernel_light.h index 04472212d0c..0448d0165b9 100644 --- a/intern/cycles/kernel/kernel_light.h +++ b/intern/cycles/kernel/kernel_light.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#include "kernel_light_background.h" + CCL_NAMESPACE_BEGIN /* Light Sample result */ @@ -33,500 +35,6 @@ typedef struct LightSample { LightType type; /* type of light */ } LightSample; -/* Area light sampling */ - -/* Uses the following paper: - * - * Carlos Urena et al. - * An Area-Preserving Parametrization for Spherical Rectangles. - * - * https://www.solidangle.com/research/egsr2013_spherical_rectangle.pdf - * - * Note: light_p is modified when sample_coord is true. - */ -ccl_device_inline float rect_light_sample(float3 P, - float3 *light_p, - float3 axisu, - float3 axisv, - float randu, - float randv, - bool sample_coord) -{ - /* In our name system we're using P for the center, - * which is o in the paper. - */ - - float3 corner = *light_p - axisu * 0.5f - axisv * 0.5f; - float axisu_len, axisv_len; - /* Compute local reference system R. */ - float3 x = normalize_len(axisu, &axisu_len); - float3 y = normalize_len(axisv, &axisv_len); - float3 z = cross(x, y); - /* Compute rectangle coords in local reference system. */ - float3 dir = corner - P; - float z0 = dot(dir, z); - /* Flip 'z' to make it point against Q. */ - if (z0 > 0.0f) { - z *= -1.0f; - z0 *= -1.0f; - } - float x0 = dot(dir, x); - float y0 = dot(dir, y); - float x1 = x0 + axisu_len; - float y1 = y0 + axisv_len; - /* Compute internal angles (gamma_i). */ - float4 diff = make_float4(x0, y1, x1, y0) - make_float4(x1, y0, x0, y1); - float4 nz = make_float4(y0, x1, y1, x0) * diff; - nz = nz / sqrt(z0 * z0 * diff * diff + nz * nz); - float g0 = safe_acosf(-nz.x * nz.y); - float g1 = safe_acosf(-nz.y * nz.z); - float g2 = safe_acosf(-nz.z * nz.w); - float g3 = safe_acosf(-nz.w * nz.x); - /* Compute predefined constants. */ - float b0 = nz.x; - float b1 = nz.z; - float b0sq = b0 * b0; - float k = M_2PI_F - g2 - g3; - /* Compute solid angle from internal angles. */ - float S = g0 + g1 - k; - - if (sample_coord) { - /* Compute cu. */ - float au = randu * S + k; - float fu = (cosf(au) * b0 - b1) / sinf(au); - float cu = 1.0f / sqrtf(fu * fu + b0sq) * (fu > 0.0f ? 1.0f : -1.0f); - cu = clamp(cu, -1.0f, 1.0f); - /* Compute xu. */ - float xu = -(cu * z0) / max(sqrtf(1.0f - cu * cu), 1e-7f); - xu = clamp(xu, x0, x1); - /* Compute yv. */ - float z0sq = z0 * z0; - float y0sq = y0 * y0; - float y1sq = y1 * y1; - float d = sqrtf(xu * xu + z0sq); - float h0 = y0 / sqrtf(d * d + y0sq); - float h1 = y1 / sqrtf(d * d + y1sq); - float hv = h0 + randv * (h1 - h0), hv2 = hv * hv; - float yv = (hv2 < 1.0f - 1e-6f) ? (hv * d) / sqrtf(1.0f - hv2) : y1; - - /* Transform (xu, yv, z0) to world coords. */ - *light_p = P + xu * x + yv * y + z0 * z; - } - - /* return pdf */ - if (S != 0.0f) - return 1.0f / S; - else - return 0.0f; -} - -ccl_device_inline float3 ellipse_sample(float3 ru, float3 rv, float randu, float randv) -{ - to_unit_disk(&randu, &randv); - return ru * randu + rv * randv; -} - -ccl_device float3 disk_light_sample(float3 v, float randu, float randv) -{ - float3 ru, rv; - - make_orthonormals(v, &ru, &rv); - - return ellipse_sample(ru, rv, randu, randv); -} - -ccl_device float3 distant_light_sample(float3 D, float radius, float randu, float randv) -{ - return normalize(D + disk_light_sample(D, randu, randv) * radius); -} - -ccl_device float3 -sphere_light_sample(float3 P, float3 center, float radius, float randu, float randv) -{ - return disk_light_sample(normalize(P - center), randu, randv) * radius; -} - -ccl_device float spot_light_attenuation(float3 dir, - float spot_angle, - float spot_smooth, - LightSample *ls) -{ - float3 I = ls->Ng; - - float attenuation = dot(dir, I); - - if (attenuation <= spot_angle) { - attenuation = 0.0f; - } - else { - float t = attenuation - spot_angle; - - if (t < spot_smooth && spot_smooth != 0.0f) - attenuation *= smoothstepf(t / spot_smooth); - } - - return attenuation; -} - -ccl_device float lamp_light_pdf(KernelGlobals *kg, const float3 Ng, const float3 I, float t) -{ - float cos_pi = dot(Ng, I); - - if (cos_pi <= 0.0f) - return 0.0f; - - return t * t / cos_pi; -} - -/* Background Light */ - -#ifdef __BACKGROUND_MIS__ - -ccl_device float3 background_map_sample(KernelGlobals *kg, float randu, float randv, float *pdf) -{ - /* for the following, the CDF values are actually a pair of floats, with the - * function value as X and the actual CDF as Y. The last entry's function - * value is the CDF total. */ - int res_x = kernel_data.integrator.pdf_background_res_x; - int res_y = kernel_data.integrator.pdf_background_res_y; - int cdf_width = res_x + 1; - - /* this is basically std::lower_bound as used by pbrt */ - int first = 0; - int count = res_y; - - while (count > 0) { - int step = count >> 1; - int middle = first + step; - - if (kernel_tex_fetch(__light_background_marginal_cdf, middle).y < randv) { - first = middle + 1; - count -= step + 1; - } - else - count = step; - } - - int index_v = max(0, first - 1); - kernel_assert(index_v >= 0 && index_v < res_y); - - float2 cdf_v = kernel_tex_fetch(__light_background_marginal_cdf, index_v); - float2 cdf_next_v = kernel_tex_fetch(__light_background_marginal_cdf, index_v + 1); - float2 cdf_last_v = kernel_tex_fetch(__light_background_marginal_cdf, res_y); - - /* importance-sampled V direction */ - float dv = inverse_lerp(cdf_v.y, cdf_next_v.y, randv); - float v = (index_v + dv) / res_y; - - /* this is basically std::lower_bound as used by pbrt */ - first = 0; - count = res_x; - while (count > 0) { - int step = count >> 1; - int middle = first + step; - - if (kernel_tex_fetch(__light_background_conditional_cdf, index_v * cdf_width + middle).y < - randu) { - first = middle + 1; - count -= step + 1; - } - else - count = step; - } - - int index_u = max(0, first - 1); - kernel_assert(index_u >= 0 && index_u < res_x); - - float2 cdf_u = kernel_tex_fetch(__light_background_conditional_cdf, - index_v * cdf_width + index_u); - float2 cdf_next_u = kernel_tex_fetch(__light_background_conditional_cdf, - index_v * cdf_width + index_u + 1); - float2 cdf_last_u = kernel_tex_fetch(__light_background_conditional_cdf, - index_v * cdf_width + res_x); - - /* importance-sampled U direction */ - float du = inverse_lerp(cdf_u.y, cdf_next_u.y, randu); - float u = (index_u + du) / res_x; - - /* compute pdf */ - float sin_theta = sinf(M_PI_F * v); - float denom = (M_2PI_F * M_PI_F * sin_theta) * cdf_last_u.x * cdf_last_v.x; - - if (sin_theta == 0.0f || denom == 0.0f) - *pdf = 0.0f; - else - *pdf = (cdf_u.x * cdf_v.x) / denom; - - /* compute direction */ - return equirectangular_to_direction(u, v); -} - -/* TODO(sergey): Same as above, after the release we should consider using - * 'noinline' for all devices. - */ -ccl_device float background_map_pdf(KernelGlobals *kg, float3 direction) -{ - float2 uv = direction_to_equirectangular(direction); - int res_x = kernel_data.integrator.pdf_background_res_x; - int res_y = kernel_data.integrator.pdf_background_res_y; - int cdf_width = res_x + 1; - - float sin_theta = sinf(uv.y * M_PI_F); - - if (sin_theta == 0.0f) - return 0.0f; - - int index_u = clamp(float_to_int(uv.x * res_x), 0, res_x - 1); - int index_v = clamp(float_to_int(uv.y * res_y), 0, res_y - 1); - - /* pdfs in V direction */ - float2 cdf_last_u = kernel_tex_fetch(__light_background_conditional_cdf, - index_v * cdf_width + res_x); - float2 cdf_last_v = kernel_tex_fetch(__light_background_marginal_cdf, res_y); - - float denom = (M_2PI_F * M_PI_F * sin_theta) * cdf_last_u.x * cdf_last_v.x; - - if (denom == 0.0f) - return 0.0f; - - /* pdfs in U direction */ - float2 cdf_u = kernel_tex_fetch(__light_background_conditional_cdf, - index_v * cdf_width + index_u); - float2 cdf_v = kernel_tex_fetch(__light_background_marginal_cdf, index_v); - - return (cdf_u.x * cdf_v.x) / denom; -} - -ccl_device_inline bool background_portal_data_fetch_and_check_side( - KernelGlobals *kg, float3 P, int index, float3 *lightpos, float3 *dir) -{ - int portal = kernel_data.integrator.portal_offset + index; - const ccl_global KernelLight *klight = &kernel_tex_fetch(__lights, portal); - - *lightpos = make_float3(klight->co[0], klight->co[1], klight->co[2]); - *dir = make_float3(klight->area.dir[0], klight->area.dir[1], klight->area.dir[2]); - - /* Check whether portal is on the right side. */ - if (dot(*dir, P - *lightpos) > 1e-4f) - return true; - - return false; -} - -ccl_device_inline float background_portal_pdf( - KernelGlobals *kg, float3 P, float3 direction, int ignore_portal, bool *is_possible) -{ - float portal_pdf = 0.0f; - - int num_possible = 0; - for (int p = 0; p < kernel_data.integrator.num_portals; p++) { - if (p == ignore_portal) - continue; - - float3 lightpos, dir; - if (!background_portal_data_fetch_and_check_side(kg, P, p, &lightpos, &dir)) - continue; - - /* There's a portal that could be sampled from this position. */ - if (is_possible) { - *is_possible = true; - } - num_possible++; - - int portal = kernel_data.integrator.portal_offset + p; - const ccl_global KernelLight *klight = &kernel_tex_fetch(__lights, portal); - float3 axisu = make_float3( - klight->area.axisu[0], klight->area.axisu[1], klight->area.axisu[2]); - float3 axisv = make_float3( - klight->area.axisv[0], klight->area.axisv[1], klight->area.axisv[2]); - bool is_round = (klight->area.invarea < 0.0f); - - if (!ray_quad_intersect(P, - direction, - 1e-4f, - FLT_MAX, - lightpos, - axisu, - axisv, - dir, - NULL, - NULL, - NULL, - NULL, - is_round)) - continue; - - if (is_round) { - float t; - float3 D = normalize_len(lightpos - P, &t); - portal_pdf += fabsf(klight->area.invarea) * lamp_light_pdf(kg, dir, -D, t); - } - else { - portal_pdf += rect_light_sample(P, &lightpos, axisu, axisv, 0.0f, 0.0f, false); - } - } - - if (ignore_portal >= 0) { - /* We have skipped a portal that could be sampled as well. */ - num_possible++; - } - - return (num_possible > 0) ? portal_pdf / num_possible : 0.0f; -} - -ccl_device int background_num_possible_portals(KernelGlobals *kg, float3 P) -{ - int num_possible_portals = 0; - for (int p = 0; p < kernel_data.integrator.num_portals; p++) { - float3 lightpos, dir; - if (background_portal_data_fetch_and_check_side(kg, P, p, &lightpos, &dir)) - num_possible_portals++; - } - return num_possible_portals; -} - -ccl_device float3 background_portal_sample(KernelGlobals *kg, - float3 P, - float randu, - float randv, - int num_possible, - int *sampled_portal, - float *pdf) -{ - /* Pick a portal, then re-normalize randv. */ - randv *= num_possible; - int portal = (int)randv; - randv -= portal; - - /* TODO(sergey): Some smarter way of finding portal to sample - * is welcome. - */ - for (int p = 0; p < kernel_data.integrator.num_portals; p++) { - /* Search for the sampled portal. */ - float3 lightpos, dir; - if (!background_portal_data_fetch_and_check_side(kg, P, p, &lightpos, &dir)) - continue; - - if (portal == 0) { - /* p is the portal to be sampled. */ - int portal = kernel_data.integrator.portal_offset + p; - const ccl_global KernelLight *klight = &kernel_tex_fetch(__lights, portal); - float3 axisu = make_float3( - klight->area.axisu[0], klight->area.axisu[1], klight->area.axisu[2]); - float3 axisv = make_float3( - klight->area.axisv[0], klight->area.axisv[1], klight->area.axisv[2]); - bool is_round = (klight->area.invarea < 0.0f); - - float3 D; - if (is_round) { - lightpos += ellipse_sample(axisu * 0.5f, axisv * 0.5f, randu, randv); - float t; - D = normalize_len(lightpos - P, &t); - *pdf = fabsf(klight->area.invarea) * lamp_light_pdf(kg, dir, -D, t); - } - else { - *pdf = rect_light_sample(P, &lightpos, axisu, axisv, randu, randv, true); - D = normalize(lightpos - P); - } - - *pdf /= num_possible; - *sampled_portal = p; - return D; - } - - portal--; - } - - return make_float3(0.0f, 0.0f, 0.0f); -} - -ccl_device_inline float3 -background_light_sample(KernelGlobals *kg, float3 P, float randu, float randv, float *pdf) -{ - /* Probability of sampling portals instead of the map. */ - float portal_sampling_pdf = kernel_data.integrator.portal_pdf; - - /* Check if there are portals in the scene which we can sample. */ - if (portal_sampling_pdf > 0.0f) { - int num_portals = background_num_possible_portals(kg, P); - if (num_portals > 0) { - if (portal_sampling_pdf == 1.0f || randu < portal_sampling_pdf) { - if (portal_sampling_pdf < 1.0f) { - randu /= portal_sampling_pdf; - } - int portal; - float3 D = background_portal_sample(kg, P, randu, randv, num_portals, &portal, pdf); - if (num_portals > 1) { - /* Ignore the chosen portal, its pdf is already included. */ - *pdf += background_portal_pdf(kg, P, D, portal, NULL); - } - /* We could also have sampled the map, so combine with MIS. */ - if (portal_sampling_pdf < 1.0f) { - float cdf_pdf = background_map_pdf(kg, D); - *pdf = (portal_sampling_pdf * (*pdf) + (1.0f - portal_sampling_pdf) * cdf_pdf); - } - return D; - } - else { - /* Sample map, but with nonzero portal_sampling_pdf for MIS. */ - randu = (randu - portal_sampling_pdf) / (1.0f - portal_sampling_pdf); - } - } - else { - /* We can't sample a portal. - * Check if we can sample the map instead. - */ - if (portal_sampling_pdf == 1.0f) { - /* Use uniform as a fallback if we can't sample the map. */ - *pdf = 1.0f / M_4PI_F; - return sample_uniform_sphere(randu, randv); - } - else { - portal_sampling_pdf = 0.0f; - } - } - } - - float3 D = background_map_sample(kg, randu, randv, pdf); - /* Use MIS if portals could be sampled as well. */ - if (portal_sampling_pdf > 0.0f) { - float portal_pdf = background_portal_pdf(kg, P, D, -1, NULL); - *pdf = (portal_sampling_pdf * portal_pdf + (1.0f - portal_sampling_pdf) * (*pdf)); - } - return D; -} - -ccl_device float background_light_pdf(KernelGlobals *kg, float3 P, float3 direction) -{ - /* Probability of sampling portals instead of the map. */ - float portal_sampling_pdf = kernel_data.integrator.portal_pdf; - - float portal_pdf = 0.0f, map_pdf = 0.0f; - if (portal_sampling_pdf > 0.0f) { - /* Evaluate PDF of sampling this direction by portal sampling. */ - bool is_possible = false; - portal_pdf = background_portal_pdf(kg, P, direction, -1, &is_possible) * portal_sampling_pdf; - if (!is_possible) { - /* Portal sampling is not possible here because all portals point to the wrong side. - * If map sampling is possible, it would be used instead, - * otherwise fallback sampling is used. */ - if (portal_sampling_pdf == 1.0f) { - return kernel_data.integrator.pdf_lights / M_4PI_F; - } - else { - /* Force map sampling. */ - portal_sampling_pdf = 0.0f; - } - } - } - if (portal_sampling_pdf < 1.0f) { - /* Evaluate PDF of sampling this direction by map sampling. */ - map_pdf = background_map_pdf(kg, direction) * (1.0f - portal_sampling_pdf); - } - return (portal_pdf + map_pdf) * kernel_data.integrator.pdf_lights; -} -#endif - /* Regular Light */ ccl_device_inline bool lamp_light_sample( @@ -594,7 +102,7 @@ ccl_device_inline bool lamp_light_sample( /* spot light attenuation */ float3 dir = make_float3(klight->spot.dir[0], klight->spot.dir[1], klight->spot.dir[2]); ls->eval_fac *= spot_light_attenuation( - dir, klight->spot.spot_angle, klight->spot.spot_smooth, ls); + dir, klight->spot.spot_angle, klight->spot.spot_smooth, ls->Ng); if (ls->eval_fac == 0.0f) { return false; } @@ -732,7 +240,7 @@ ccl_device bool lamp_light_eval( /* spot light attenuation */ float3 dir = make_float3(klight->spot.dir[0], klight->spot.dir[1], klight->spot.dir[2]); ls->eval_fac *= spot_light_attenuation( - dir, klight->spot.spot_angle, klight->spot.spot_smooth, ls); + dir, klight->spot.spot_angle, klight->spot.spot_smooth, ls->Ng); if (ls->eval_fac == 0.0f) return false; diff --git a/intern/cycles/kernel/kernel_light_background.h b/intern/cycles/kernel/kernel_light_background.h new file mode 100644 index 00000000000..30e336f0f80 --- /dev/null +++ b/intern/cycles/kernel/kernel_light_background.h @@ -0,0 +1,448 @@ +/* + * Copyright 2011-2020 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "kernel_light_common.h" + +CCL_NAMESPACE_BEGIN + +/* Background Light */ + +#ifdef __BACKGROUND_MIS__ + +ccl_device float3 background_map_sample(KernelGlobals *kg, float randu, float randv, float *pdf) +{ + /* for the following, the CDF values are actually a pair of floats, with the + * function value as X and the actual CDF as Y. The last entry's function + * value is the CDF total. */ + int res_x = kernel_data.background.map_res_x; + int res_y = kernel_data.background.map_res_y; + int cdf_width = res_x + 1; + + /* this is basically std::lower_bound as used by pbrt */ + int first = 0; + int count = res_y; + + while (count > 0) { + int step = count >> 1; + int middle = first + step; + + if (kernel_tex_fetch(__light_background_marginal_cdf, middle).y < randv) { + first = middle + 1; + count -= step + 1; + } + else + count = step; + } + + int index_v = max(0, first - 1); + kernel_assert(index_v >= 0 && index_v < res_y); + + float2 cdf_v = kernel_tex_fetch(__light_background_marginal_cdf, index_v); + float2 cdf_next_v = kernel_tex_fetch(__light_background_marginal_cdf, index_v + 1); + float2 cdf_last_v = kernel_tex_fetch(__light_background_marginal_cdf, res_y); + + /* importance-sampled V direction */ + float dv = inverse_lerp(cdf_v.y, cdf_next_v.y, randv); + float v = (index_v + dv) / res_y; + + /* this is basically std::lower_bound as used by pbrt */ + first = 0; + count = res_x; + while (count > 0) { + int step = count >> 1; + int middle = first + step; + + if (kernel_tex_fetch(__light_background_conditional_cdf, index_v * cdf_width + middle).y < + randu) { + first = middle + 1; + count -= step + 1; + } + else + count = step; + } + + int index_u = max(0, first - 1); + kernel_assert(index_u >= 0 && index_u < res_x); + + float2 cdf_u = kernel_tex_fetch(__light_background_conditional_cdf, + index_v * cdf_width + index_u); + float2 cdf_next_u = kernel_tex_fetch(__light_background_conditional_cdf, + index_v * cdf_width + index_u + 1); + float2 cdf_last_u = kernel_tex_fetch(__light_background_conditional_cdf, + index_v * cdf_width + res_x); + + /* importance-sampled U direction */ + float du = inverse_lerp(cdf_u.y, cdf_next_u.y, randu); + float u = (index_u + du) / res_x; + + /* compute pdf */ + float sin_theta = sinf(M_PI_F * v); + float denom = (M_2PI_F * M_PI_F * sin_theta) * cdf_last_u.x * cdf_last_v.x; + + if (sin_theta == 0.0f || denom == 0.0f) + *pdf = 0.0f; + else + *pdf = (cdf_u.x * cdf_v.x) / denom; + + /* compute direction */ + return equirectangular_to_direction(u, v); +} + +/* TODO(sergey): Same as above, after the release we should consider using + * 'noinline' for all devices. + */ +ccl_device float background_map_pdf(KernelGlobals *kg, float3 direction) +{ + float2 uv = direction_to_equirectangular(direction); + int res_x = kernel_data.background.map_res_x; + int res_y = kernel_data.background.map_res_y; + int cdf_width = res_x + 1; + + float sin_theta = sinf(uv.y * M_PI_F); + + if (sin_theta == 0.0f) + return 0.0f; + + int index_u = clamp(float_to_int(uv.x * res_x), 0, res_x - 1); + int index_v = clamp(float_to_int(uv.y * res_y), 0, res_y - 1); + + /* pdfs in V direction */ + float2 cdf_last_u = kernel_tex_fetch(__light_background_conditional_cdf, + index_v * cdf_width + res_x); + float2 cdf_last_v = kernel_tex_fetch(__light_background_marginal_cdf, res_y); + + float denom = (M_2PI_F * M_PI_F * sin_theta) * cdf_last_u.x * cdf_last_v.x; + + if (denom == 0.0f) + return 0.0f; + + /* pdfs in U direction */ + float2 cdf_u = kernel_tex_fetch(__light_background_conditional_cdf, + index_v * cdf_width + index_u); + float2 cdf_v = kernel_tex_fetch(__light_background_marginal_cdf, index_v); + + return (cdf_u.x * cdf_v.x) / denom; +} + +ccl_device_inline bool background_portal_data_fetch_and_check_side( + KernelGlobals *kg, float3 P, int index, float3 *lightpos, float3 *dir) +{ + int portal = kernel_data.background.portal_offset + index; + const ccl_global KernelLight *klight = &kernel_tex_fetch(__lights, portal); + + *lightpos = make_float3(klight->co[0], klight->co[1], klight->co[2]); + *dir = make_float3(klight->area.dir[0], klight->area.dir[1], klight->area.dir[2]); + + /* Check whether portal is on the right side. */ + if (dot(*dir, P - *lightpos) > 1e-4f) + return true; + + return false; +} + +ccl_device_inline float background_portal_pdf( + KernelGlobals *kg, float3 P, float3 direction, int ignore_portal, bool *is_possible) +{ + float portal_pdf = 0.0f; + + int num_possible = 0; + for (int p = 0; p < kernel_data.background.num_portals; p++) { + if (p == ignore_portal) + continue; + + float3 lightpos, dir; + if (!background_portal_data_fetch_and_check_side(kg, P, p, &lightpos, &dir)) + continue; + + /* There's a portal that could be sampled from this position. */ + if (is_possible) { + *is_possible = true; + } + num_possible++; + + int portal = kernel_data.background.portal_offset + p; + const ccl_global KernelLight *klight = &kernel_tex_fetch(__lights, portal); + float3 axisu = make_float3( + klight->area.axisu[0], klight->area.axisu[1], klight->area.axisu[2]); + float3 axisv = make_float3( + klight->area.axisv[0], klight->area.axisv[1], klight->area.axisv[2]); + bool is_round = (klight->area.invarea < 0.0f); + + if (!ray_quad_intersect(P, + direction, + 1e-4f, + FLT_MAX, + lightpos, + axisu, + axisv, + dir, + NULL, + NULL, + NULL, + NULL, + is_round)) + continue; + + if (is_round) { + float t; + float3 D = normalize_len(lightpos - P, &t); + portal_pdf += fabsf(klight->area.invarea) * lamp_light_pdf(kg, dir, -D, t); + } + else { + portal_pdf += rect_light_sample(P, &lightpos, axisu, axisv, 0.0f, 0.0f, false); + } + } + + if (ignore_portal >= 0) { + /* We have skipped a portal that could be sampled as well. */ + num_possible++; + } + + return (num_possible > 0) ? portal_pdf / num_possible : 0.0f; +} + +ccl_device int background_num_possible_portals(KernelGlobals *kg, float3 P) +{ + int num_possible_portals = 0; + for (int p = 0; p < kernel_data.background.num_portals; p++) { + float3 lightpos, dir; + if (background_portal_data_fetch_and_check_side(kg, P, p, &lightpos, &dir)) + num_possible_portals++; + } + return num_possible_portals; +} + +ccl_device float3 background_portal_sample(KernelGlobals *kg, + float3 P, + float randu, + float randv, + int num_possible, + int *sampled_portal, + float *pdf) +{ + /* Pick a portal, then re-normalize randv. */ + randv *= num_possible; + int portal = (int)randv; + randv -= portal; + + /* TODO(sergey): Some smarter way of finding portal to sample + * is welcome. + */ + for (int p = 0; p < kernel_data.background.num_portals; p++) { + /* Search for the sampled portal. */ + float3 lightpos, dir; + if (!background_portal_data_fetch_and_check_side(kg, P, p, &lightpos, &dir)) + continue; + + if (portal == 0) { + /* p is the portal to be sampled. */ + int portal = kernel_data.background.portal_offset + p; + const ccl_global KernelLight *klight = &kernel_tex_fetch(__lights, portal); + float3 axisu = make_float3( + klight->area.axisu[0], klight->area.axisu[1], klight->area.axisu[2]); + float3 axisv = make_float3( + klight->area.axisv[0], klight->area.axisv[1], klight->area.axisv[2]); + bool is_round = (klight->area.invarea < 0.0f); + + float3 D; + if (is_round) { + lightpos += ellipse_sample(axisu * 0.5f, axisv * 0.5f, randu, randv); + float t; + D = normalize_len(lightpos - P, &t); + *pdf = fabsf(klight->area.invarea) * lamp_light_pdf(kg, dir, -D, t); + } + else { + *pdf = rect_light_sample(P, &lightpos, axisu, axisv, randu, randv, true); + D = normalize(lightpos - P); + } + + *pdf /= num_possible; + *sampled_portal = p; + return D; + } + + portal--; + } + + return make_float3(0.0f, 0.0f, 0.0f); +} + +ccl_device_inline float3 background_sun_sample(KernelGlobals *kg, + float randu, + float randv, + float *pdf) +{ + float3 D; + const float3 N = float4_to_float3(kernel_data.background.sun); + const float angle = kernel_data.background.sun.w; + sample_uniform_cone(N, angle, randu, randv, &D, pdf); + return D; +} + +ccl_device_inline float background_sun_pdf(KernelGlobals *kg, float3 D) +{ + const float3 N = float4_to_float3(kernel_data.background.sun); + const float angle = kernel_data.background.sun.w; + return pdf_uniform_cone(N, D, angle); +} + +ccl_device_inline float3 +background_light_sample(KernelGlobals *kg, float3 P, float randu, float randv, float *pdf) +{ + float portal_method_pdf = kernel_data.background.portal_weight; + float sun_method_pdf = kernel_data.background.sun_weight; + float map_method_pdf = kernel_data.background.map_weight; + + int num_portals = 0; + if (portal_method_pdf > 0.0f) { + /* Check if there are portals in the scene which we can sample. */ + num_portals = background_num_possible_portals(kg, P); + if (num_portals == 0) { + portal_method_pdf = 0.0f; + } + } + + float pdf_fac = (portal_method_pdf + sun_method_pdf + map_method_pdf); + if (pdf_fac == 0.0f) { + /* Use uniform as a fallback if we can't use any strategy. */ + *pdf = 1.0f / M_4PI_F; + return sample_uniform_sphere(randu, randv); + } + + pdf_fac = 1.0f / pdf_fac; + portal_method_pdf *= pdf_fac; + sun_method_pdf *= pdf_fac; + map_method_pdf *= pdf_fac; + + /* We have 100% in total and split it between the three categories. + * Therefore, we pick portals if randu is between 0 and portal_method_pdf, + * sun if randu is between portal_method_pdf and (portal_method_pdf + sun_method_pdf) + * and map if randu is between (portal_method_pdf + sun_method_pdf) and 1. */ + float sun_method_cdf = portal_method_pdf + sun_method_pdf; + + int method = 0; + float3 D; + if (randu < portal_method_pdf) { + method = 0; + /* Rescale randu. */ + if (portal_method_pdf != 1.0f) { + randu /= portal_method_pdf; + } + + /* Sample a portal. */ + int portal; + D = background_portal_sample(kg, P, randu, randv, num_portals, &portal, pdf); + if (num_portals > 1) { + /* Ignore the chosen portal, its pdf is already included. */ + *pdf += background_portal_pdf(kg, P, D, portal, NULL); + } + + /* Skip MIS if this is the only method. */ + if (portal_method_pdf == 1.0f) { + return D; + } + *pdf *= portal_method_pdf; + } + else if (randu < sun_method_cdf) { + method = 1; + /* Rescale randu. */ + if (sun_method_pdf != 1.0f) { + randu = (randu - portal_method_pdf) / sun_method_pdf; + } + + D = background_sun_sample(kg, randu, randv, pdf); + + /* Skip MIS if this is the only method. */ + if (sun_method_pdf == 1.0f) { + return D; + } + *pdf *= sun_method_pdf; + } + else { + method = 2; + /* Rescale randu. */ + if (map_method_pdf != 1.0f) { + randu = (randu - sun_method_cdf) / map_method_pdf; + } + + D = background_map_sample(kg, randu, randv, pdf); + + /* Skip MIS if this is the only method. */ + if (map_method_pdf == 1.0f) { + return D; + } + *pdf *= map_method_pdf; + } + + /* MIS weighting. */ + if (method != 0 && portal_method_pdf != 0.0f) { + *pdf += portal_method_pdf * background_portal_pdf(kg, P, D, -1, NULL); + } + if (method != 1 && sun_method_pdf != 0.0f) { + *pdf += sun_method_pdf * background_sun_pdf(kg, D); + } + if (method != 2 && map_method_pdf != 0.0f) { + *pdf += map_method_pdf * background_map_pdf(kg, D); + } + return D; +} + +ccl_device float background_light_pdf(KernelGlobals *kg, float3 P, float3 direction) +{ + float portal_method_pdf = kernel_data.background.portal_weight; + float sun_method_pdf = kernel_data.background.sun_weight; + float map_method_pdf = kernel_data.background.map_weight; + + float portal_pdf = 0.0f; + /* Portals are a special case here since we need to compute their pdf in order + * to find out if we can sample them. */ + if (portal_method_pdf > 0.0f) { + /* Evaluate PDF of sampling this direction by portal sampling. */ + bool is_possible = false; + portal_pdf = background_portal_pdf(kg, P, direction, -1, &is_possible); + if (!is_possible) { + /* Portal sampling is not possible here because all portals point to the wrong side. + * If other methods can be used instead, do so, otherwise uniform sampling is used as a + * fallback. */ + portal_method_pdf = 0.0f; + } + } + + float pdf_fac = (portal_method_pdf + sun_method_pdf + map_method_pdf); + if (pdf_fac == 0.0f) { + /* Use uniform as a fallback if we can't use any strategy. */ + return kernel_data.integrator.pdf_lights / M_4PI_F; + } + + pdf_fac = 1.0f / pdf_fac; + portal_method_pdf *= pdf_fac; + sun_method_pdf *= pdf_fac; + map_method_pdf *= pdf_fac; + + float pdf = portal_pdf * portal_method_pdf; + if (sun_method_pdf != 0.0f) { + pdf += background_sun_pdf(kg, direction) * sun_method_pdf; + } + if (map_method_pdf != 0.0f) { + pdf += background_map_pdf(kg, direction) * map_method_pdf; + } + + return pdf * kernel_data.integrator.pdf_lights; +} + +#endif + +CCL_NAMESPACE_END
\ No newline at end of file diff --git a/intern/cycles/kernel/kernel_light_common.h b/intern/cycles/kernel/kernel_light_common.h new file mode 100644 index 00000000000..39503a4b479 --- /dev/null +++ b/intern/cycles/kernel/kernel_light_common.h @@ -0,0 +1,159 @@ +/* + * Copyright 2011-2020 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +CCL_NAMESPACE_BEGIN + +/* Area light sampling */ + +/* Uses the following paper: + * + * Carlos Urena et al. + * An Area-Preserving Parametrization for Spherical Rectangles. + * + * https://www.solidangle.com/research/egsr2013_spherical_rectangle.pdf + * + * Note: light_p is modified when sample_coord is true. + */ +ccl_device_inline float rect_light_sample(float3 P, + float3 *light_p, + float3 axisu, + float3 axisv, + float randu, + float randv, + bool sample_coord) +{ + /* In our name system we're using P for the center, + * which is o in the paper. + */ + + float3 corner = *light_p - axisu * 0.5f - axisv * 0.5f; + float axisu_len, axisv_len; + /* Compute local reference system R. */ + float3 x = normalize_len(axisu, &axisu_len); + float3 y = normalize_len(axisv, &axisv_len); + float3 z = cross(x, y); + /* Compute rectangle coords in local reference system. */ + float3 dir = corner - P; + float z0 = dot(dir, z); + /* Flip 'z' to make it point against Q. */ + if (z0 > 0.0f) { + z *= -1.0f; + z0 *= -1.0f; + } + float x0 = dot(dir, x); + float y0 = dot(dir, y); + float x1 = x0 + axisu_len; + float y1 = y0 + axisv_len; + /* Compute internal angles (gamma_i). */ + float4 diff = make_float4(x0, y1, x1, y0) - make_float4(x1, y0, x0, y1); + float4 nz = make_float4(y0, x1, y1, x0) * diff; + nz = nz / sqrt(z0 * z0 * diff * diff + nz * nz); + float g0 = safe_acosf(-nz.x * nz.y); + float g1 = safe_acosf(-nz.y * nz.z); + float g2 = safe_acosf(-nz.z * nz.w); + float g3 = safe_acosf(-nz.w * nz.x); + /* Compute predefined constants. */ + float b0 = nz.x; + float b1 = nz.z; + float b0sq = b0 * b0; + float k = M_2PI_F - g2 - g3; + /* Compute solid angle from internal angles. */ + float S = g0 + g1 - k; + + if (sample_coord) { + /* Compute cu. */ + float au = randu * S + k; + float fu = (cosf(au) * b0 - b1) / sinf(au); + float cu = 1.0f / sqrtf(fu * fu + b0sq) * (fu > 0.0f ? 1.0f : -1.0f); + cu = clamp(cu, -1.0f, 1.0f); + /* Compute xu. */ + float xu = -(cu * z0) / max(sqrtf(1.0f - cu * cu), 1e-7f); + xu = clamp(xu, x0, x1); + /* Compute yv. */ + float z0sq = z0 * z0; + float y0sq = y0 * y0; + float y1sq = y1 * y1; + float d = sqrtf(xu * xu + z0sq); + float h0 = y0 / sqrtf(d * d + y0sq); + float h1 = y1 / sqrtf(d * d + y1sq); + float hv = h0 + randv * (h1 - h0), hv2 = hv * hv; + float yv = (hv2 < 1.0f - 1e-6f) ? (hv * d) / sqrtf(1.0f - hv2) : y1; + + /* Transform (xu, yv, z0) to world coords. */ + *light_p = P + xu * x + yv * y + z0 * z; + } + + /* return pdf */ + if (S != 0.0f) + return 1.0f / S; + else + return 0.0f; +} + +ccl_device_inline float3 ellipse_sample(float3 ru, float3 rv, float randu, float randv) +{ + to_unit_disk(&randu, &randv); + return ru * randu + rv * randv; +} + +ccl_device float3 disk_light_sample(float3 v, float randu, float randv) +{ + float3 ru, rv; + + make_orthonormals(v, &ru, &rv); + + return ellipse_sample(ru, rv, randu, randv); +} + +ccl_device float3 distant_light_sample(float3 D, float radius, float randu, float randv) +{ + return normalize(D + disk_light_sample(D, randu, randv) * radius); +} + +ccl_device float3 +sphere_light_sample(float3 P, float3 center, float radius, float randu, float randv) +{ + return disk_light_sample(normalize(P - center), randu, randv) * radius; +} + +ccl_device float spot_light_attenuation(float3 dir, float spot_angle, float spot_smooth, float3 N) +{ + float attenuation = dot(dir, N); + + if (attenuation <= spot_angle) { + attenuation = 0.0f; + } + else { + float t = attenuation - spot_angle; + + if (t < spot_smooth && spot_smooth != 0.0f) + attenuation *= smoothstepf(t / spot_smooth); + } + + return attenuation; +} + +ccl_device float lamp_light_pdf(KernelGlobals *kg, const float3 Ng, const float3 I, float t) +{ + float cos_pi = dot(Ng, I); + + if (cos_pi <= 0.0f) + return 0.0f; + + return t * t / cos_pi; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_montecarlo.h b/intern/cycles/kernel/kernel_montecarlo.h index 5c776e06547..0edcc1a5a14 100644 --- a/intern/cycles/kernel/kernel_montecarlo.h +++ b/intern/cycles/kernel/kernel_montecarlo.h @@ -98,6 +98,16 @@ ccl_device_inline void sample_uniform_cone( *pdf = M_1_2PI_F / (1.0f - zMin); } +ccl_device_inline float pdf_uniform_cone(const float3 N, float3 D, float angle) +{ + float zMin = cosf(angle); + float z = dot(N, D); + if (z > zMin) { + return M_1_2PI_F / (1.0f - zMin); + } + return 0.0f; +} + /* sample uniform point on the surface of a sphere */ ccl_device float3 sample_uniform_sphere(float u1, float u2) { diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index 0a0cf1bd6c0..a692d7a844f 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -1291,6 +1291,24 @@ typedef struct KernelBackground { float ao_factor; float ao_distance; float ao_bounces_factor; + + /* portal sampling */ + float portal_weight; + int num_portals; + int portal_offset; + + /* sun sampling */ + float sun_weight; + /* xyz store direction, w the angle. float4 instead of float3 is used + * to ensure consistent padding/alignment across devices. */ + float4 sun; + + /* map sampling */ + float map_weight; + int map_res_x; + int map_res_y; + + int use_mis; } KernelBackground; static_assert_align(KernelBackground, 16); @@ -1302,15 +1320,8 @@ typedef struct KernelIntegrator { int num_all_lights; float pdf_triangles; float pdf_lights; - int pdf_background_res_x; - int pdf_background_res_y; float light_inv_rr_threshold; - /* light portals */ - float portal_pdf; - int num_portals; - int portal_offset; - /* bounces */ int min_bounce; int max_bounce; @@ -1372,7 +1383,7 @@ typedef struct KernelIntegrator { int max_closures; - int pad1; + int pad1, pad2; } KernelIntegrator; static_assert_align(KernelIntegrator, 16); diff --git a/intern/cycles/kernel/osl/osl_closures.cpp b/intern/cycles/kernel/osl/osl_closures.cpp index 872a55143cc..7ee467a46dd 100644 --- a/intern/cycles/kernel/osl/osl_closures.cpp +++ b/intern/cycles/kernel/osl/osl_closures.cpp @@ -362,6 +362,9 @@ void OSLShader::register_closures(OSLShadingSystem *ss_) id++, closure_bsdf_transparent_params(), closure_bsdf_transparent_prepare); + + register_closure( + ss, "microfacet", id++, closure_bsdf_microfacet_params(), closure_bsdf_microfacet_prepare); register_closure(ss, "microfacet_ggx", id++, @@ -508,6 +511,82 @@ bool CBSDFClosure::skip(const ShaderData *sd, int path_flag, int scattering) return false; } +/* Standard Microfacet Closure */ + +class MicrofacetClosure : public CBSDFClosure { + public: + MicrofacetBsdf params; + ustring distribution; + int refract; + + void setup(ShaderData *sd, int path_flag, float3 weight) + { + static ustring u_ggx("ggx"); + static ustring u_default("default"); + + const int label = (refract) ? LABEL_TRANSMIT : LABEL_REFLECT; + if (skip(sd, path_flag, LABEL_GLOSSY | label)) { + return; + } + + MicrofacetBsdf *bsdf = (MicrofacetBsdf *)bsdf_alloc_osl( + sd, sizeof(MicrofacetBsdf), weight, ¶ms); + + if (!bsdf) { + return; + } + + /* GGX */ + if (distribution == u_ggx || distribution == u_default) { + if (!refract) { + if (params.alpha_x == params.alpha_y) { + /* Isotropic */ + sd->flag |= bsdf_microfacet_ggx_isotropic_setup(bsdf); + } + else { + /* Anisotropic */ + sd->flag |= bsdf_microfacet_ggx_setup(bsdf); + } + } + else { + sd->flag |= bsdf_microfacet_ggx_refraction_setup(bsdf); + } + } + /* Beckmann */ + else { + if (!refract) { + if (params.alpha_x == params.alpha_y) { + /* Isotropic */ + sd->flag |= bsdf_microfacet_beckmann_isotropic_setup(bsdf); + } + else { + /* Anisotropic */ + sd->flag |= bsdf_microfacet_beckmann_setup(bsdf); + } + } + else { + sd->flag |= bsdf_microfacet_beckmann_refraction_setup(bsdf); + } + } + } +}; + +ClosureParam *closure_bsdf_microfacet_params() +{ + static ClosureParam params[] = {CLOSURE_STRING_PARAM(MicrofacetClosure, distribution), + CLOSURE_FLOAT3_PARAM(MicrofacetClosure, params.N), + CLOSURE_FLOAT3_PARAM(MicrofacetClosure, params.T), + CLOSURE_FLOAT_PARAM(MicrofacetClosure, params.alpha_x), + CLOSURE_FLOAT_PARAM(MicrofacetClosure, params.alpha_y), + CLOSURE_FLOAT_PARAM(MicrofacetClosure, params.ior), + CLOSURE_INT_PARAM(MicrofacetClosure, refract), + CLOSURE_STRING_KEYPARAM(MicrofacetClosure, label, "label"), + CLOSURE_FINISH_PARAM(MicrofacetClosure)}; + + return params; +} +CCLOSURE_PREPARE(closure_bsdf_microfacet_prepare, MicrofacetClosure) + /* GGX closures with Fresnel */ class MicrofacetFresnelClosure : public CBSDFClosure { diff --git a/intern/cycles/kernel/osl/osl_closures.h b/intern/cycles/kernel/osl/osl_closures.h index d12afdb80dd..e4058e3a746 100644 --- a/intern/cycles/kernel/osl/osl_closures.h +++ b/intern/cycles/kernel/osl/osl_closures.h @@ -51,6 +51,7 @@ OSL::ClosureParam *closure_bsdf_transparent_params(); OSL::ClosureParam *closure_bssrdf_params(); OSL::ClosureParam *closure_absorption_params(); OSL::ClosureParam *closure_henyey_greenstein_params(); +OSL::ClosureParam *closure_bsdf_microfacet_params(); OSL::ClosureParam *closure_bsdf_microfacet_multi_ggx_params(); OSL::ClosureParam *closure_bsdf_microfacet_multi_ggx_glass_params(); OSL::ClosureParam *closure_bsdf_microfacet_multi_ggx_aniso_params(); @@ -70,6 +71,7 @@ void closure_bsdf_transparent_prepare(OSL::RendererServices *, int id, void *dat void closure_bssrdf_prepare(OSL::RendererServices *, int id, void *data); void closure_absorption_prepare(OSL::RendererServices *, int id, void *data); void closure_henyey_greenstein_prepare(OSL::RendererServices *, int id, void *data); +void closure_bsdf_microfacet_prepare(OSL::RendererServices *, int id, void *data); void closure_bsdf_microfacet_multi_ggx_prepare(OSL::RendererServices *, int id, void *data); void closure_bsdf_microfacet_multi_ggx_glass_prepare(OSL::RendererServices *, int id, void *data); void closure_bsdf_microfacet_multi_ggx_aniso_prepare(OSL::RendererServices *, int id, void *data); diff --git a/intern/cycles/kernel/shaders/node_sky_texture.osl b/intern/cycles/kernel/shaders/node_sky_texture.osl index 4def237a2e0..08bc8f85120 100644 --- a/intern/cycles/kernel/shaders/node_sky_texture.osl +++ b/intern/cycles/kernel/shaders/node_sky_texture.osl @@ -44,13 +44,13 @@ float sky_perez_function(float lam[9], float theta, float gamma) (1.0 + lam[2] * exp(lam[3] * gamma) + lam[4] * cgamma * cgamma); } -color sky_radiance_old(normal dir, - float sunphi, - float suntheta, - color radiance, - float config_x[9], - float config_y[9], - float config_z[9]) +color sky_radiance_preetham(normal dir, + float sunphi, + float suntheta, + color radiance, + float config_x[9], + float config_y[9], + float config_z[9]) { /* convert vector to spherical coordinates */ vector spherical = sky_spherical_coordinates(dir); @@ -88,13 +88,13 @@ float sky_radiance_internal(float config[9], float theta, float gamma) (config[2] + config[3] * expM + config[5] * rayM + config[6] * mieM + config[7] * zenith); } -color sky_radiance_new(normal dir, - float sunphi, - float suntheta, - color radiance, - float config_x[9], - float config_y[9], - float config_z[9]) +color sky_radiance_hosek(normal dir, + float sunphi, + float suntheta, + color radiance, + float config_x[9], + float config_y[9], + float config_z[9]) { /* convert vector to spherical coordinates */ vector spherical = sky_spherical_coordinates(dir); @@ -116,16 +116,103 @@ color sky_radiance_new(normal dir, return xyz_to_rgb(x, y, z) * (M_2PI / 683); } +/* Nishita improved */ +vector geographical_to_direction(float lat, float lon) +{ + return vector(cos(lat) * cos(lon), cos(lat) * sin(lon), sin(lat)); +} + +color sky_radiance_nishita(vector dir, float nishita_data[9], string filename) +{ + /* definitions */ + float sun_elevation = nishita_data[6]; + float sun_rotation = nishita_data[7]; + float angular_diameter = nishita_data[8]; + int sun_disc = angular_diameter > 0; + float alpha = 1.0; + color xyz; + /* convert dir to spherical coordinates */ + vector direction = sky_spherical_coordinates(dir); + + /* render above the horizon */ + if (dir[2] >= 0.0) { + /* definitions */ + vector sun_dir = geographical_to_direction(sun_elevation, sun_rotation + M_PI_2); + float sun_dir_angle = acos(dot(dir, sun_dir)); + float half_angular = angular_diameter / 2.0; + float dir_elevation = M_PI_2 - direction[0]; + + /* if ray inside sun disc render it, otherwise render sky */ + if (sun_dir_angle < half_angular && sun_disc == 1) { + /* get 3 pixels data */ + color pixel_bottom = color(nishita_data[0], nishita_data[1], nishita_data[2]); + color pixel_top = color(nishita_data[3], nishita_data[4], nishita_data[5]); + float y; + + /* sun interpolation */ + if (sun_elevation - half_angular > 0.0) { + if ((sun_elevation + half_angular) > 0.0) { + y = ((dir_elevation - sun_elevation) / angular_diameter) + 0.5; + xyz = mix(pixel_bottom, pixel_top, y); + } + } + else { + if (sun_elevation + half_angular > 0.0) { + y = dir_elevation / (sun_elevation + half_angular); + xyz = mix(pixel_bottom, pixel_top, y); + } + } + /* limb darkening, coefficient is 0.6f */ + float angle_fraction = sun_dir_angle / half_angular; + float limb_darkening = (1.0 - 0.6 * (1.0 - sqrt(1.0 - angle_fraction * angle_fraction))); + xyz *= limb_darkening; + } + /* sky */ + else { + /* sky interpolation */ + float x = (direction[1] + M_PI + sun_rotation) / M_2PI; + float y = 1.0 - (dir_elevation / M_PI_2); + if (x > 1.0) { + x = x - 1.0; + } + xyz = (color)texture(filename, x, y, "wrap", "clamp", "interp", "linear", "alpha", alpha); + } + } + /* ground */ + else { + if (dir[2] < -0.4) { + xyz = color(0, 0, 0); + } + else { + /* black ground fade */ + float mul = pow(1.0 + dir[2] * 2.5, 3.0); + /* interpolation */ + float x = (direction[1] + M_PI + sun_rotation) / M_2PI; + float y = 1.5; + if (x > 1.0) { + x = x - 1.0; + } + xyz = (color)texture( + filename, x, y, "wrap", "periodic", "interp", "linear", "alpha", alpha) * + mul; + } + } + /* convert to RGB and adjust strength */ + return xyz_to_rgb(xyz[0], xyz[1], xyz[2]) * 120000.0; +} + shader node_sky_texture(int use_mapping = 0, matrix mapping = matrix(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), vector Vector = P, string type = "hosek_wilkie", float theta = 0.0, float phi = 0.0, + string filename = "", color radiance = color(0.0, 0.0, 0.0), float config_x[9] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, float config_y[9] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, float config_z[9] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + float nishita_data[9] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, output color Color = color(0.0, 0.0, 0.0)) { vector p = Vector; @@ -133,8 +220,10 @@ shader node_sky_texture(int use_mapping = 0, if (use_mapping) p = transform(mapping, p); + if (type == "nishita_improved") + Color = sky_radiance_nishita(p, nishita_data, filename); if (type == "hosek_wilkie") - Color = sky_radiance_new(p, phi, theta, radiance, config_x, config_y, config_z); - else - Color = sky_radiance_old(p, phi, theta, radiance, config_x, config_y, config_z); + Color = sky_radiance_hosek(p, phi, theta, radiance, config_x, config_y, config_z); + if (type == "preetham") + Color = sky_radiance_preetham(p, phi, theta, radiance, config_x, config_y, config_z); } diff --git a/intern/cycles/kernel/svm/svm_sky.h b/intern/cycles/kernel/svm/svm_sky.h index 50fe0c8232f..e877bd9a5c8 100644 --- a/intern/cycles/kernel/svm/svm_sky.h +++ b/intern/cycles/kernel/svm/svm_sky.h @@ -37,16 +37,16 @@ ccl_device float sky_perez_function(float *lam, float theta, float gamma) (1.0f + lam[2] * expf(lam[3] * gamma) + lam[4] * cgamma * cgamma); } -ccl_device float3 sky_radiance_old(KernelGlobals *kg, - float3 dir, - float sunphi, - float suntheta, - float radiance_x, - float radiance_y, - float radiance_z, - float *config_x, - float *config_y, - float *config_z) +ccl_device float3 sky_radiance_preetham(KernelGlobals *kg, + float3 dir, + float sunphi, + float suntheta, + float radiance_x, + float radiance_y, + float radiance_z, + float *config_x, + float *config_y, + float *config_z) { /* convert vector to spherical coordinates */ float2 spherical = direction_to_spherical(dir); @@ -90,16 +90,16 @@ ccl_device float sky_radiance_internal(float *configuration, float theta, float configuration[6] * mieM + configuration[7] * zenith); } -ccl_device float3 sky_radiance_new(KernelGlobals *kg, - float3 dir, - float sunphi, - float suntheta, - float radiance_x, - float radiance_y, - float radiance_z, - float *config_x, - float *config_y, - float *config_z) +ccl_device float3 sky_radiance_hosek(KernelGlobals *kg, + float3 dir, + float sunphi, + float suntheta, + float radiance_x, + float radiance_y, + float radiance_z, + float *config_x, + float *config_y, + float *config_z) { /* convert vector to spherical coordinates */ float2 spherical = direction_to_spherical(dir); @@ -121,93 +121,206 @@ ccl_device float3 sky_radiance_new(KernelGlobals *kg, return xyz_to_rgb(kg, make_float3(x, y, z)) * (M_2PI_F / 683); } +/* Nishita improved sky model */ +ccl_device float3 geographical_to_direction(float lat, float lon) +{ + return make_float3(cos(lat) * cos(lon), cos(lat) * sin(lon), sin(lat)); +} + +ccl_device float3 sky_radiance_nishita(KernelGlobals *kg, + float3 dir, + float *nishita_data, + uint texture_id) +{ + /* definitions */ + float sun_elevation = nishita_data[6]; + float sun_rotation = nishita_data[7]; + float angular_diameter = nishita_data[8]; + bool sun_disc = (angular_diameter > 0.0f); + float3 xyz; + /* convert dir to spherical coordinates */ + float2 direction = direction_to_spherical(dir); + + /* render above the horizon */ + if (dir.z >= 0.0f) { + /* definitions */ + float3 sun_dir = geographical_to_direction(sun_elevation, sun_rotation + M_PI_2_F); + float sun_dir_angle = acos(dot(dir, sun_dir)); + float half_angular = angular_diameter / 2.0f; + float dir_elevation = M_PI_2_F - direction.x; + + /* if ray inside sun disc render it, otherwise render sky */ + if (sun_disc && sun_dir_angle < half_angular) { + /* get 3 pixels data */ + float3 pixel_bottom = make_float3(nishita_data[0], nishita_data[1], nishita_data[2]); + float3 pixel_top = make_float3(nishita_data[3], nishita_data[4], nishita_data[5]); + float y; + + /* sun interpolation */ + if (sun_elevation - half_angular > 0.0f) { + if (sun_elevation + half_angular > 0.0f) { + y = ((dir_elevation - sun_elevation) / angular_diameter) + 0.5f; + xyz = interp(pixel_bottom, pixel_top, y); + } + } + else { + if (sun_elevation + half_angular > 0.0f) { + y = dir_elevation / (sun_elevation + half_angular); + xyz = interp(pixel_bottom, pixel_top, y); + } + } + /* limb darkening, coefficient is 0.6f */ + float limb_darkening = (1.0f - + 0.6f * (1.0f - sqrtf(1.0f - sqr(sun_dir_angle / half_angular)))); + xyz *= limb_darkening; + } + /* sky */ + else { + /* sky interpolation */ + float x = (direction.y + M_PI_F + sun_rotation) / M_2PI_F; + float y = dir_elevation / M_PI_2_F; + if (x > 1.0f) { + x -= 1.0f; + } + xyz = float4_to_float3(kernel_tex_image_interp(kg, texture_id, x, y)); + } + } + /* ground */ + else { + if (dir.z < -0.4f) { + xyz = make_float3(0.0f, 0.0f, 0.0f); + } + else { + /* black ground fade */ + float fade = 1.0f + dir.z * 2.5f; + fade = sqr(fade) * fade; + /* interpolation */ + float x = (direction.y + M_PI_F + sun_rotation) / M_2PI_F; + if (x > 1.0f) { + x -= 1.0f; + } + xyz = float4_to_float3(kernel_tex_image_interp(kg, texture_id, x, -0.5)) * fade; + } + } + + /* convert to rgb and adjust strength */ + return xyz_to_rgb(kg, xyz) * 120000.0f; +} + ccl_device void svm_node_tex_sky( KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int *offset) { - /* Define variables */ - float sunphi, suntheta, radiance_x, radiance_y, radiance_z; - float config_x[9], config_y[9], config_z[9]; - /* Load data */ uint dir_offset = node.y; uint out_offset = node.z; int sky_model = node.w; - float4 data = read_node_float(kg, offset); - sunphi = data.x; - suntheta = data.y; - radiance_x = data.z; - radiance_y = data.w; - - data = read_node_float(kg, offset); - radiance_z = data.x; - config_x[0] = data.y; - config_x[1] = data.z; - config_x[2] = data.w; - - data = read_node_float(kg, offset); - config_x[3] = data.x; - config_x[4] = data.y; - config_x[5] = data.z; - config_x[6] = data.w; - - data = read_node_float(kg, offset); - config_x[7] = data.x; - config_x[8] = data.y; - config_y[0] = data.z; - config_y[1] = data.w; - - data = read_node_float(kg, offset); - config_y[2] = data.x; - config_y[3] = data.y; - config_y[4] = data.z; - config_y[5] = data.w; - - data = read_node_float(kg, offset); - config_y[6] = data.x; - config_y[7] = data.y; - config_y[8] = data.z; - config_z[0] = data.w; - - data = read_node_float(kg, offset); - config_z[1] = data.x; - config_z[2] = data.y; - config_z[3] = data.z; - config_z[4] = data.w; - - data = read_node_float(kg, offset); - config_z[5] = data.x; - config_z[6] = data.y; - config_z[7] = data.z; - config_z[8] = data.w; - float3 dir = stack_load_float3(stack, dir_offset); float3 f; - /* Compute Sky */ - if (sky_model == 0) { - f = sky_radiance_old(kg, - dir, - sunphi, - suntheta, - radiance_x, - radiance_y, - radiance_z, - config_x, - config_y, - config_z); + /* Preetham and Hosek share the same data */ + if (sky_model == 0 || sky_model == 1) { + /* Define variables */ + float sunphi, suntheta, radiance_x, radiance_y, radiance_z; + float config_x[9], config_y[9], config_z[9]; + + float4 data = read_node_float(kg, offset); + sunphi = data.x; + suntheta = data.y; + radiance_x = data.z; + radiance_y = data.w; + + data = read_node_float(kg, offset); + radiance_z = data.x; + config_x[0] = data.y; + config_x[1] = data.z; + config_x[2] = data.w; + + data = read_node_float(kg, offset); + config_x[3] = data.x; + config_x[4] = data.y; + config_x[5] = data.z; + config_x[6] = data.w; + + data = read_node_float(kg, offset); + config_x[7] = data.x; + config_x[8] = data.y; + config_y[0] = data.z; + config_y[1] = data.w; + + data = read_node_float(kg, offset); + config_y[2] = data.x; + config_y[3] = data.y; + config_y[4] = data.z; + config_y[5] = data.w; + + data = read_node_float(kg, offset); + config_y[6] = data.x; + config_y[7] = data.y; + config_y[8] = data.z; + config_z[0] = data.w; + + data = read_node_float(kg, offset); + config_z[1] = data.x; + config_z[2] = data.y; + config_z[3] = data.z; + config_z[4] = data.w; + + data = read_node_float(kg, offset); + config_z[5] = data.x; + config_z[6] = data.y; + config_z[7] = data.z; + config_z[8] = data.w; + + /* Compute Sky */ + if (sky_model == 0) { + f = sky_radiance_preetham(kg, + dir, + sunphi, + suntheta, + radiance_x, + radiance_y, + radiance_z, + config_x, + config_y, + config_z); + } + else { + f = sky_radiance_hosek(kg, + dir, + sunphi, + suntheta, + radiance_x, + radiance_y, + radiance_z, + config_x, + config_y, + config_z); + } } + /* Nishita */ else { - f = sky_radiance_new(kg, - dir, - sunphi, - suntheta, - radiance_x, - radiance_y, - radiance_z, - config_x, - config_y, - config_z); + /* Define variables */ + float nishita_data[9]; + + float4 data = read_node_float(kg, offset); + nishita_data[0] = data.x; + nishita_data[1] = data.y; + nishita_data[2] = data.z; + nishita_data[3] = data.w; + + data = read_node_float(kg, offset); + nishita_data[4] = data.x; + nishita_data[5] = data.y; + nishita_data[6] = data.z; + nishita_data[7] = data.w; + + data = read_node_float(kg, offset); + nishita_data[8] = data.x; + uint texture_id = __float_as_uint(data.y); + + /* Compute Sky */ + f = sky_radiance_nishita(kg, dir, nishita_data, texture_id); } stack_store_float3(stack, out_offset, f); diff --git a/intern/cycles/kernel/svm/svm_types.h b/intern/cycles/kernel/svm/svm_types.h index e913d9e0489..f1ebb37e23e 100644 --- a/intern/cycles/kernel/svm/svm_types.h +++ b/intern/cycles/kernel/svm/svm_types.h @@ -414,7 +414,7 @@ typedef enum NodeWaveProfile { NODE_WAVE_PROFILE_TRI, } NodeWaveProfile; -typedef enum NodeSkyType { NODE_SKY_OLD, NODE_SKY_NEW } NodeSkyType; +typedef enum NodeSkyType { NODE_SKY_PREETHAM, NODE_SKY_HOSEK, NODE_SKY_NISHITA } NodeSkyType; typedef enum NodeGradientType { NODE_BLEND_LINEAR, diff --git a/intern/cycles/render/CMakeLists.txt b/intern/cycles/render/CMakeLists.txt index 472b5a0c101..e37a0407976 100644 --- a/intern/cycles/render/CMakeLists.txt +++ b/intern/cycles/render/CMakeLists.txt @@ -24,6 +24,7 @@ set(SRC hair.cpp image.cpp image_oiio.cpp + image_sky.cpp image_vdb.cpp integrator.cpp jitter.cpp @@ -64,6 +65,7 @@ set(SRC_HEADERS hair.h image.h image_oiio.h + image_sky.h image_vdb.h integrator.h light.h diff --git a/intern/cycles/render/image_sky.cpp b/intern/cycles/render/image_sky.cpp new file mode 100644 index 00000000000..3e7b491f609 --- /dev/null +++ b/intern/cycles/render/image_sky.cpp @@ -0,0 +1,95 @@ +/* + * Copyright 2011-2020 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "render/image_sky.h" + +#include "util/util_image.h" +#include "util/util_logging.h" +#include "util/util_path.h" +#include "util/util_sky_model.h" + +CCL_NAMESPACE_BEGIN + +SkyLoader::SkyLoader( + float sun_elevation, int altitude, float air_density, float dust_density, float ozone_density) + : sun_elevation(sun_elevation), + altitude(altitude), + air_density(air_density), + dust_density(dust_density), + ozone_density(ozone_density) +{ +} + +SkyLoader::~SkyLoader(){}; + +bool SkyLoader::load_metadata(ImageMetaData &metadata) +{ + metadata.width = 512; + metadata.height = 128; + metadata.channels = 3; + metadata.depth = 1; + metadata.type = IMAGE_DATA_TYPE_FLOAT4; + metadata.compress_as_srgb = false; + return true; +} + +bool SkyLoader::load_pixels(const ImageMetaData &metadata, + void *pixels, + const size_t /*pixels_size*/, + const bool /*associate_alpha*/) +{ + /* definitions */ + int width = metadata.width; + int height = metadata.height; + float *pixel_data = (float *)pixels; + float altitude_f = (float)altitude; + + /* precompute sky texture */ + const int num_chunks = TaskScheduler::num_threads(); + const int chunk_size = height / num_chunks; + TaskPool pool; + for (int chunk = 0; chunk < num_chunks; chunk++) { + const int chunk_start = chunk * chunk_size; + const int chunk_end = (chunk + 1 < num_chunks) ? (chunk + 1) * chunk_size : height; + pool.push(function_bind(&nishita_skymodel_precompute_texture, + pixel_data, + metadata.channels, + chunk_start, + chunk_end, + width, + height, + sun_elevation, + altitude_f, + air_density, + dust_density, + ozone_density)); + } + pool.wait_work(); + + return true; +} + +string SkyLoader::name() const +{ + return "sky_nishita"; +} + +bool SkyLoader::equals(const ImageLoader & /*other*/) const +{ + return false; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/render/image_sky.h b/intern/cycles/render/image_sky.h new file mode 100644 index 00000000000..cf4a3e8942c --- /dev/null +++ b/intern/cycles/render/image_sky.h @@ -0,0 +1,49 @@ +/* + * Copyright 2011-2020 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "render/image.h" + +CCL_NAMESPACE_BEGIN + +class SkyLoader : public ImageLoader { + private: + float sun_elevation; + int altitude; + float air_density; + float dust_density; + float ozone_density; + + public: + SkyLoader(float sun_elevation, + int altitude, + float air_density, + float dust_density, + float ozone_density); + ~SkyLoader(); + + bool load_metadata(ImageMetaData &metadata) override; + + bool load_pixels(const ImageMetaData &metadata, + void *pixels, + const size_t /*pixels_size*/, + const bool /*associate_alpha*/) override; + + string name() const override; + + bool equals(const ImageLoader & /*other*/) const override; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/render/light.cpp b/intern/cycles/render/light.cpp index cb7474017fa..225cedfef55 100644 --- a/intern/cycles/render/light.cpp +++ b/intern/cycles/render/light.cpp @@ -450,6 +450,7 @@ void LightManager::device_update_distribution(Device *, /* update device */ KernelIntegrator *kintegrator = &dscene->data.integrator; + KernelBackground *kbackground = &dscene->data.background; KernelFilm *kfilm = &dscene->data.film; kintegrator->use_direct_light = (totarea > 0.0f); @@ -493,15 +494,18 @@ void LightManager::device_update_distribution(Device *, /* Portals */ if (num_portals > 0) { - kintegrator->portal_offset = light_index; - kintegrator->num_portals = num_portals; - kintegrator->portal_pdf = background_mis ? 0.5f : 1.0f; + kbackground->portal_offset = light_index; + kbackground->num_portals = num_portals; + kbackground->portal_weight = 1.0f; } else { - kintegrator->num_portals = 0; - kintegrator->portal_offset = 0; - kintegrator->portal_pdf = 0.0f; + kbackground->num_portals = 0; + kbackground->portal_offset = 0; + kbackground->portal_weight = 0.0f; } + + /* Map */ + kbackground->map_weight = background_mis ? 1.0f : 0.0f; } else { dscene->light_distribution.free(); @@ -511,9 +515,12 @@ void LightManager::device_update_distribution(Device *, kintegrator->pdf_triangles = 0.0f; kintegrator->pdf_lights = 0.0f; kintegrator->use_lamp_mis = false; - kintegrator->num_portals = 0; - kintegrator->portal_offset = 0; - kintegrator->portal_pdf = 0.0f; + + kbackground->num_portals = 0; + kbackground->portal_offset = 0; + kbackground->portal_weight = 0.0f; + kbackground->sun_weight = 0.0f; + kbackground->map_weight = 0.0f; kfilm->pass_shadow_scale = 1.0f; } @@ -562,7 +569,7 @@ void LightManager::device_update_background(Device *device, Scene *scene, Progress &progress) { - KernelIntegrator *kintegrator = &dscene->data.integrator; + KernelBackground *kbackground = &dscene->data.background; Light *background_light = NULL; /* find background light */ @@ -575,31 +582,79 @@ void LightManager::device_update_background(Device *device, /* no background light found, signal renderer to skip sampling */ if (!background_light || !background_light->is_enabled) { - kintegrator->pdf_background_res_x = 0; - kintegrator->pdf_background_res_y = 0; + kbackground->map_res_x = 0; + kbackground->map_res_y = 0; + kbackground->map_weight = 0.0f; + kbackground->sun_weight = 0.0f; + kbackground->use_mis = (kbackground->portal_weight > 0.0f); return; } progress.set_status("Updating Lights", "Importance map"); - assert(kintegrator->use_direct_light); + assert(dscene->data.integrator.use_direct_light); + + int2 environment_res = make_int2(0, 0); + Shader *shader = scene->background->get_shader(scene); + int num_suns = 0; + foreach (ShaderNode *node, shader->graph->nodes) { + if (node->type == EnvironmentTextureNode::node_type) { + EnvironmentTextureNode *env = (EnvironmentTextureNode *)node; + ImageMetaData metadata; + if (!env->handle.empty()) { + ImageMetaData metadata = env->handle.metadata(); + environment_res.x = max(environment_res.x, metadata.width); + environment_res.y = max(environment_res.y, metadata.height); + } + } + if (node->type == SkyTextureNode::node_type) { + SkyTextureNode *sky = (SkyTextureNode *)node; + if (sky->type == NODE_SKY_NISHITA && sky->sun_disc) { + /* Ensure that the input coordinates aren't transformed before they reach the node. + * If that is the case, the logic used for sampling the sun's location does not work + * and we have to fall back to map-based sampling. */ + const ShaderInput *vec_in = sky->input("Vector"); + if (vec_in && vec_in->link && vec_in->link->parent) { + ShaderNode *vec_src = vec_in->link->parent; + if ((vec_src->type != TextureCoordinateNode::node_type) || + (vec_in->link != vec_src->output("Generated"))) { + environment_res.x = max(environment_res.x, 4096); + environment_res.y = max(environment_res.y, 2048); + continue; + } + } + + float latitude = sky->sun_elevation; + float longitude = M_2PI_F - sky->sun_rotation + M_PI_2_F; + float half_angle = sky->sun_size * 0.5f; + kbackground->sun = make_float4(cosf(latitude) * cosf(longitude), + cosf(latitude) * sinf(longitude), + sinf(latitude), + half_angle); + kbackground->sun_weight = 4.0f; + environment_res.x = max(environment_res.x, 512); + environment_res.y = max(environment_res.y, 256); + num_suns++; + } + } + } + + /* If there's more than one sun, fall back to map sampling instead. */ + if (num_suns != 1) { + kbackground->sun_weight = 0.0f; + environment_res.x = max(environment_res.x, 4096); + environment_res.y = max(environment_res.y, 2048); + } + + /* Enable MIS for background sampling if any strategy is active. */ + kbackground->use_mis = (kbackground->portal_weight + kbackground->map_weight + + kbackground->sun_weight) > 0.0f; /* get the resolution from the light's size (we stuff it in there) */ int2 res = make_int2(background_light->map_resolution, background_light->map_resolution / 2); /* If the resolution isn't set manually, try to find an environment texture. */ if (res.x == 0) { - Shader *shader = scene->background->get_shader(scene); - foreach (ShaderNode *node, shader->graph->nodes) { - if (node->type == EnvironmentTextureNode::node_type) { - EnvironmentTextureNode *env = (EnvironmentTextureNode *)node; - ImageMetaData metadata; - if (!env->handle.empty()) { - ImageMetaData metadata = env->handle.metadata(); - res.x = max(res.x, metadata.width); - res.y = max(res.y, metadata.height); - } - } - } + res = environment_res; if (res.x > 0 && res.y > 0) { VLOG(2) << "Automatically set World MIS resolution to " << res.x << " by " << res.y << "\n"; } @@ -609,8 +664,8 @@ void LightManager::device_update_background(Device *device, res = make_int2(1024, 512); VLOG(2) << "Setting World MIS resolution to default\n"; } - kintegrator->pdf_background_res_x = res.x; - kintegrator->pdf_background_res_y = res.y; + kbackground->map_res_x = res.x; + kbackground->map_res_y = res.y; vector<float3> pixels; shade_background_pixels(device, dscene, res.x, res.y, pixels, progress); diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp index cdcaeb246dd..ab392839e52 100644 --- a/intern/cycles/render/nodes.cpp +++ b/intern/cycles/render/nodes.cpp @@ -19,6 +19,7 @@ #include "render/constant_fold.h" #include "render/film.h" #include "render/image.h" +#include "render/image_sky.h" #include "render/integrator.h" #include "render/light.h" #include "render/mesh.h" @@ -630,7 +631,7 @@ typedef struct SunSky { /* Parameter */ float radiance_x, radiance_y, radiance_z; - float config_x[9], config_y[9], config_z[9]; + float config_x[9], config_y[9], config_z[9], nishita_data[9]; } SunSky; /* Preetham model */ @@ -640,7 +641,7 @@ static float sky_perez_function(float lam[6], float theta, float gamma) (1.0f + lam[2] * expf(lam[3] * gamma) + lam[4] * cosf(gamma) * cosf(gamma)); } -static void sky_texture_precompute_old(SunSky *sunsky, float3 dir, float turbidity) +static void sky_texture_precompute_preetham(SunSky *sunsky, float3 dir, float turbidity) { /* * We re-use the SunSky struct of the new model, to avoid extra variables @@ -703,10 +704,10 @@ static void sky_texture_precompute_old(SunSky *sunsky, float3 dir, float turbidi } /* Hosek / Wilkie */ -static void sky_texture_precompute_new(SunSky *sunsky, - float3 dir, - float turbidity, - float ground_albedo) +static void sky_texture_precompute_hosek(SunSky *sunsky, + float3 dir, + float turbidity, + float ground_albedo) { /* Calculate Sun Direction and save coordinates */ float2 spherical = sky_spherical_coordinates(dir); @@ -743,6 +744,34 @@ static void sky_texture_precompute_new(SunSky *sunsky, arhosekskymodelstate_free(sky_state); } +/* Nishita improved */ +static void sky_texture_precompute_nishita(SunSky *sunsky, + bool sun_disc, + float sun_size, + float sun_elevation, + float sun_rotation, + int altitude, + float air_density, + float dust_density) +{ + /* sample 2 sun pixels */ + float pixel_bottom[3]; + float pixel_top[3]; + float altitude_f = (float)altitude; + nishita_skymodel_precompute_sun( + sun_elevation, sun_size, altitude_f, air_density, dust_density, pixel_bottom, pixel_top); + /* send data to svm_sky */ + sunsky->nishita_data[0] = pixel_bottom[0]; + sunsky->nishita_data[1] = pixel_bottom[1]; + sunsky->nishita_data[2] = pixel_bottom[2]; + sunsky->nishita_data[3] = pixel_top[0]; + sunsky->nishita_data[4] = pixel_top[1]; + sunsky->nishita_data[5] = pixel_top[2]; + sunsky->nishita_data[6] = sun_elevation; + sunsky->nishita_data[7] = M_2PI_F - sun_rotation; + sunsky->nishita_data[8] = sun_disc ? sun_size : 0.0f; +} + NODE_DEFINE(SkyTextureNode) { NodeType *type = NodeType::add("sky_texture", create, NodeType::SHADER); @@ -750,13 +779,22 @@ NODE_DEFINE(SkyTextureNode) TEXTURE_MAPPING_DEFINE(SkyTextureNode); static NodeEnum type_enum; - type_enum.insert("preetham", NODE_SKY_OLD); - type_enum.insert("hosek_wilkie", NODE_SKY_NEW); - SOCKET_ENUM(type, "Type", type_enum, NODE_SKY_NEW); + type_enum.insert("preetham", NODE_SKY_PREETHAM); + type_enum.insert("hosek_wilkie", NODE_SKY_HOSEK); + type_enum.insert("nishita_improved", NODE_SKY_NISHITA); + SOCKET_ENUM(type, "Type", type_enum, NODE_SKY_NISHITA); SOCKET_VECTOR(sun_direction, "Sun Direction", make_float3(0.0f, 0.0f, 1.0f)); SOCKET_FLOAT(turbidity, "Turbidity", 2.2f); SOCKET_FLOAT(ground_albedo, "Ground Albedo", 0.3f); + SOCKET_BOOLEAN(sun_disc, "Sun Disc", true); + SOCKET_FLOAT(sun_size, "Sun Size", 0.009512f); + SOCKET_FLOAT(sun_elevation, "Sun Elevation", M_PI_2_F); + SOCKET_FLOAT(sun_rotation, "Sun Rotation", 0.0f); + SOCKET_INT(altitude, "Altitude", 0); + SOCKET_FLOAT(air_density, "Air", 1.0f); + SOCKET_FLOAT(dust_density, "Dust", 1.0f); + SOCKET_FLOAT(ozone_density, "Ozone", 1.0f); SOCKET_IN_POINT( vector, "Vector", make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_TEXTURE_GENERATED); @@ -776,10 +814,32 @@ void SkyTextureNode::compile(SVMCompiler &compiler) ShaderOutput *color_out = output("Color"); SunSky sunsky; - if (type == NODE_SKY_OLD) - sky_texture_precompute_old(&sunsky, sun_direction, turbidity); - else if (type == NODE_SKY_NEW) - sky_texture_precompute_new(&sunsky, sun_direction, turbidity, ground_albedo); + if (type == NODE_SKY_PREETHAM) + sky_texture_precompute_preetham(&sunsky, sun_direction, turbidity); + else if (type == NODE_SKY_HOSEK) + sky_texture_precompute_hosek(&sunsky, sun_direction, turbidity, ground_albedo); + else if (type == NODE_SKY_NISHITA) { + sky_texture_precompute_nishita(&sunsky, + sun_disc, + sun_size, + sun_elevation, + sun_rotation, + altitude, + air_density, + dust_density); + /* precomputed texture image parameters */ + ImageManager *image_manager = compiler.scene->image_manager; + ImageParams impar; + impar.interpolation = INTERPOLATION_LINEAR; + impar.extension = EXTENSION_EXTEND; + + /* precompute sky texture */ + if (handle.empty()) { + SkyLoader *loader = new SkyLoader( + sun_elevation, altitude, air_density, dust_density, ozone_density); + handle = image_manager->add_image(loader, impar); + } + } else assert(false); @@ -787,38 +847,52 @@ void SkyTextureNode::compile(SVMCompiler &compiler) compiler.stack_assign(color_out); compiler.add_node(NODE_TEX_SKY, vector_offset, compiler.stack_assign(color_out), type); - compiler.add_node(__float_as_uint(sunsky.phi), - __float_as_uint(sunsky.theta), - __float_as_uint(sunsky.radiance_x), - __float_as_uint(sunsky.radiance_y)); - compiler.add_node(__float_as_uint(sunsky.radiance_z), - __float_as_uint(sunsky.config_x[0]), - __float_as_uint(sunsky.config_x[1]), - __float_as_uint(sunsky.config_x[2])); - compiler.add_node(__float_as_uint(sunsky.config_x[3]), - __float_as_uint(sunsky.config_x[4]), - __float_as_uint(sunsky.config_x[5]), - __float_as_uint(sunsky.config_x[6])); - compiler.add_node(__float_as_uint(sunsky.config_x[7]), - __float_as_uint(sunsky.config_x[8]), - __float_as_uint(sunsky.config_y[0]), - __float_as_uint(sunsky.config_y[1])); - compiler.add_node(__float_as_uint(sunsky.config_y[2]), - __float_as_uint(sunsky.config_y[3]), - __float_as_uint(sunsky.config_y[4]), - __float_as_uint(sunsky.config_y[5])); - compiler.add_node(__float_as_uint(sunsky.config_y[6]), - __float_as_uint(sunsky.config_y[7]), - __float_as_uint(sunsky.config_y[8]), - __float_as_uint(sunsky.config_z[0])); - compiler.add_node(__float_as_uint(sunsky.config_z[1]), - __float_as_uint(sunsky.config_z[2]), - __float_as_uint(sunsky.config_z[3]), - __float_as_uint(sunsky.config_z[4])); - compiler.add_node(__float_as_uint(sunsky.config_z[5]), - __float_as_uint(sunsky.config_z[6]), - __float_as_uint(sunsky.config_z[7]), - __float_as_uint(sunsky.config_z[8])); + /* nishita doesn't need this data */ + if (type != NODE_SKY_NISHITA) { + compiler.add_node(__float_as_uint(sunsky.phi), + __float_as_uint(sunsky.theta), + __float_as_uint(sunsky.radiance_x), + __float_as_uint(sunsky.radiance_y)); + compiler.add_node(__float_as_uint(sunsky.radiance_z), + __float_as_uint(sunsky.config_x[0]), + __float_as_uint(sunsky.config_x[1]), + __float_as_uint(sunsky.config_x[2])); + compiler.add_node(__float_as_uint(sunsky.config_x[3]), + __float_as_uint(sunsky.config_x[4]), + __float_as_uint(sunsky.config_x[5]), + __float_as_uint(sunsky.config_x[6])); + compiler.add_node(__float_as_uint(sunsky.config_x[7]), + __float_as_uint(sunsky.config_x[8]), + __float_as_uint(sunsky.config_y[0]), + __float_as_uint(sunsky.config_y[1])); + compiler.add_node(__float_as_uint(sunsky.config_y[2]), + __float_as_uint(sunsky.config_y[3]), + __float_as_uint(sunsky.config_y[4]), + __float_as_uint(sunsky.config_y[5])); + compiler.add_node(__float_as_uint(sunsky.config_y[6]), + __float_as_uint(sunsky.config_y[7]), + __float_as_uint(sunsky.config_y[8]), + __float_as_uint(sunsky.config_z[0])); + compiler.add_node(__float_as_uint(sunsky.config_z[1]), + __float_as_uint(sunsky.config_z[2]), + __float_as_uint(sunsky.config_z[3]), + __float_as_uint(sunsky.config_z[4])); + compiler.add_node(__float_as_uint(sunsky.config_z[5]), + __float_as_uint(sunsky.config_z[6]), + __float_as_uint(sunsky.config_z[7]), + __float_as_uint(sunsky.config_z[8])); + } + else { + compiler.add_node(__float_as_uint(sunsky.nishita_data[0]), + __float_as_uint(sunsky.nishita_data[1]), + __float_as_uint(sunsky.nishita_data[2]), + __float_as_uint(sunsky.nishita_data[3])); + compiler.add_node(__float_as_uint(sunsky.nishita_data[4]), + __float_as_uint(sunsky.nishita_data[5]), + __float_as_uint(sunsky.nishita_data[6]), + __float_as_uint(sunsky.nishita_data[7])); + compiler.add_node(__float_as_uint(sunsky.nishita_data[8]), handle.svm_slot(), 0, 0); + } tex_mapping.compile_end(compiler, vector_in, vector_offset); } @@ -828,10 +902,32 @@ void SkyTextureNode::compile(OSLCompiler &compiler) tex_mapping.compile(compiler); SunSky sunsky; - if (type == NODE_SKY_OLD) - sky_texture_precompute_old(&sunsky, sun_direction, turbidity); - else if (type == NODE_SKY_NEW) - sky_texture_precompute_new(&sunsky, sun_direction, turbidity, ground_albedo); + if (type == NODE_SKY_PREETHAM) + sky_texture_precompute_preetham(&sunsky, sun_direction, turbidity); + else if (type == NODE_SKY_HOSEK) + sky_texture_precompute_hosek(&sunsky, sun_direction, turbidity, ground_albedo); + else if (type == NODE_SKY_NISHITA) { + sky_texture_precompute_nishita(&sunsky, + sun_disc, + sun_size, + sun_elevation, + sun_rotation, + altitude, + air_density, + dust_density); + /* precomputed texture image parameters */ + ImageManager *image_manager = compiler.scene->image_manager; + ImageParams impar; + impar.interpolation = INTERPOLATION_LINEAR; + impar.extension = EXTENSION_EXTEND; + + /* precompute sky texture */ + if (handle.empty()) { + SkyLoader *loader = new SkyLoader( + sun_elevation, altitude, air_density, dust_density, ozone_density); + handle = image_manager->add_image(loader, impar); + } + } else assert(false); @@ -843,6 +939,11 @@ void SkyTextureNode::compile(OSLCompiler &compiler) compiler.parameter_array("config_x", sunsky.config_x, 9); compiler.parameter_array("config_y", sunsky.config_y, 9); compiler.parameter_array("config_z", sunsky.config_z, 9); + compiler.parameter_array("nishita_data", sunsky.nishita_data, 9); + /* nishita texture */ + if (type == NODE_SKY_NISHITA) { + compiler.parameter_texture("filename", handle.svm_slot()); + } compiler.add(this, "node_sky_texture"); } diff --git a/intern/cycles/render/nodes.h b/intern/cycles/render/nodes.h index 83c3ad071ae..846ba7423e5 100644 --- a/intern/cycles/render/nodes.h +++ b/intern/cycles/render/nodes.h @@ -168,7 +168,16 @@ class SkyTextureNode : public TextureNode { float3 sun_direction; float turbidity; float ground_albedo; + bool sun_disc; + float sun_size; + float sun_elevation; + float sun_rotation; + int altitude; + float air_density; + float dust_density; + float ozone_density; float3 vector; + ImageHandle handle; }; class OutputNode : public ShaderNode { diff --git a/intern/cycles/util/CMakeLists.txt b/intern/cycles/util/CMakeLists.txt index c1f71461dfd..16d47d57e69 100644 --- a/intern/cycles/util/CMakeLists.txt +++ b/intern/cycles/util/CMakeLists.txt @@ -100,6 +100,7 @@ set(SRC_HEADERS util_sky_model.cpp util_sky_model.h util_sky_model_data.h + util_sky_nishita.cpp util_avxf.h util_avxb.h util_semaphore.h diff --git a/intern/cycles/util/util_sky_model.h b/intern/cycles/util/util_sky_model.h index 84340614b2c..36f1079a16d 100644 --- a/intern/cycles/util/util_sky_model.h +++ b/intern/cycles/util/util_sky_model.h @@ -298,6 +298,8 @@ HINT #1: if you want to model the sky of an earth-like planet that orbits previous paragraph. */ +#include "util/util_types.h" + CCL_NAMESPACE_BEGIN #ifndef _SKY_MODEL_H_ @@ -426,4 +428,26 @@ double arhosekskymodel_solar_radiance(ArHosekSkyModelState *state, #endif // _SKY_MODEL_H_ +/* Nishita improved sky model */ + +void nishita_skymodel_precompute_texture(float *pixels, + int stride, + int start_y, + int end_y, + int width, + int height, + float sun_elevation, + float altitude, + float air_density, + float dust_density, + float ozone_density); + +void nishita_skymodel_precompute_sun(float sun_elevation, + float angular_diameter, + float altitude, + float air_density, + float dust_density, + float *pixel_bottom, + float *pixel_top); + CCL_NAMESPACE_END diff --git a/intern/cycles/util/util_sky_nishita.cpp b/intern/cycles/util/util_sky_nishita.cpp new file mode 100644 index 00000000000..92397804d43 --- /dev/null +++ b/intern/cycles/util/util_sky_nishita.cpp @@ -0,0 +1,371 @@ +/* + * Copyright 2011-2020 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "util/util_math.h" +#include "util/util_sky_model.h" + +CCL_NAMESPACE_BEGIN + +/* Constants */ +static const float rayleigh_scale = 8000.0f; // Rayleigh scale height (m) +static const float mie_scale = 1200.0f; // Mie scale height (m) +static const float mie_coeff = 2e-5f; // Mie scattering coefficient +static const float mie_G = 0.76f; // aerosols anisotropy +static const float earth_radius = 6360000.0f; // radius of Earth (m) +static const float atmosphere_radius = 6420000.0f; // radius of atmosphere (m) +static const int steps = 32; // segments per primary ray +static const int steps_light = 16; // segments per sun connection ray +static const int num_wavelengths = 21; // number of wavelengths +/* irradiance at top of atmosphere */ +static const float irradiance[] = { + 1.45756829855592995315f, 1.56596305559738380175f, 1.65148449067670455293f, + 1.71496242737209314555f, 1.75797983805020541226f, 1.78256407885924539336f, + 1.79095108475838560302f, 1.78541550133410664714f, 1.76815554864306845317f, + 1.74122069647250410362f, 1.70647127164943679389f, 1.66556087452739887134f, + 1.61993437242451854274f, 1.57083597368892080581f, 1.51932335059305478886f, + 1.46628494965214395407f, 1.41245852740172450623f, 1.35844961970384092709f, + 1.30474913844739281998f, 1.25174963272610817455f, 1.19975998755420620867f}; +/* Rayleigh scattering coefficient */ +static const float rayleigh_coeff[] = { + 0.00005424820087636473f, 0.00004418549866505454f, 0.00003635151910165377f, + 0.00003017929012024763f, 0.00002526320226989157f, 0.00002130859310621843f, + 0.00001809838025320633f, 0.00001547057129129042f, 0.00001330284977336850f, + 0.00001150184784075764f, 0.00000999557429990163f, 0.00000872799973630707f, + 0.00000765513700977967f, 0.00000674217203751443f, 0.00000596134125832052f, + 0.00000529034598065810f, 0.00000471115687557433f, 0.00000420910481110487f, + 0.00000377218381260133f, 0.00000339051255477280f, 0.00000305591531679811f}; +/* Ozone absorption coefficient */ +static const float ozone_coeff[] = { + 0.00000000325126849861f, 0.00000000585395365047f, 0.00000001977191155085f, + 0.00000007309568762914f, 0.00000020084561514287f, 0.00000040383958096161f, + 0.00000063551335912363f, 0.00000096707041180970f, 0.00000154797400424410f, + 0.00000209038647223331f, 0.00000246128056164565f, 0.00000273551299461512f, + 0.00000215125863128643f, 0.00000159051840791988f, 0.00000112356197979857f, + 0.00000073527551487574f, 0.00000046450130357806f, 0.00000033096079921048f, + 0.00000022512612292678f, 0.00000014879129266490f, 0.00000016828623364192f}; +/* CIE XYZ color matching functions */ +static const float cmf_xyz[][3] = {{0.00136800000f, 0.00003900000f, 0.00645000100f}, + {0.01431000000f, 0.00039600000f, 0.06785001000f}, + {0.13438000000f, 0.00400000000f, 0.64560000000f}, + {0.34828000000f, 0.02300000000f, 1.74706000000f}, + {0.29080000000f, 0.06000000000f, 1.66920000000f}, + {0.09564000000f, 0.13902000000f, 0.81295010000f}, + {0.00490000000f, 0.32300000000f, 0.27200000000f}, + {0.06327000000f, 0.71000000000f, 0.07824999000f}, + {0.29040000000f, 0.95400000000f, 0.02030000000f}, + {0.59450000000f, 0.99500000000f, 0.00390000000f}, + {0.91630000000f, 0.87000000000f, 0.00165000100f}, + {1.06220000000f, 0.63100000000f, 0.00080000000f}, + {0.85444990000f, 0.38100000000f, 0.00019000000f}, + {0.44790000000f, 0.17500000000f, 0.00002000000f}, + {0.16490000000f, 0.06100000000f, 0.00000000000f}, + {0.04677000000f, 0.01700000000f, 0.00000000000f}, + {0.01135916000f, 0.00410200000f, 0.00000000000f}, + {0.00289932700f, 0.00104700000f, 0.00000000000f}, + {0.00069007860f, 0.00024920000f, 0.00000000000f}, + {0.00016615050f, 0.00006000000f, 0.00000000000f}, + {0.00004150994f, 0.00001499000f, 0.00000000000f}}; + +static float3 geographical_to_direction(float lat, float lon) +{ + return make_float3(cosf(lat) * cosf(lon), cosf(lat) * sinf(lon), sinf(lat)); +} + +static float3 spec_to_xyz(float *spectrum) +{ + float3 xyz = make_float3(0.0f, 0.0f, 0.0f); + for (int i = 0; i < num_wavelengths; i++) { + xyz.x += cmf_xyz[i][0] * spectrum[i]; + xyz.y += cmf_xyz[i][1] * spectrum[i]; + xyz.z += cmf_xyz[i][2] * spectrum[i]; + } + return xyz * (20 * 683 * 1e-9f); +} + +/* Atmosphere volume models */ + +static float density_rayleigh(float height) +{ + return expf(-height / rayleigh_scale); +} + +static float density_mie(float height) +{ + return expf(-height / mie_scale); +} + +static float density_ozone(float height) +{ + float den = 0.0f; + if (height >= 10000.0f && height < 25000.0f) + den = 1.0f / 15000.0f * height - 2.0f / 3.0f; + else if (height >= 25000 && height < 40000) + den = -(1.0f / 15000.0f * height - 8.0f / 3.0f); + return den; +} + +static float phase_rayleigh(float mu) +{ + return 3.0f / (16.0f * M_PI_F) * (1.0f + sqr(mu)); +} + +static float phase_mie(float mu) +{ + static const float sqr_G = mie_G * mie_G; + + return (3.0f * (1.0f - sqr_G) * (1.0f + sqr(mu))) / + (8.0f * M_PI_F * (2.0f + sqr_G) * powf((1.0f + sqr_G - 2.0f * mie_G * mu), 1.5)); +} + +/* Intersection helpers */ +static bool surface_intersection(float3 pos, float3 dir) +{ + if (dir.z >= 0) + return false; + float t = dot(dir, -pos) / len_squared(dir); + float D = pos.x * pos.x - 2.0f * (-pos.x) * dir.x * t + dir.x * t * dir.x * t + pos.y * pos.y - + 2.0f * (-pos.y) * dir.y * t + (dir.y * t) * (dir.y * t) + pos.z * pos.z - + 2.0f * (-pos.z) * dir.z * t + dir.z * t * dir.z * t; + return (D <= sqr(earth_radius)); +} + +static float3 atmosphere_intersection(float3 pos, float3 dir) +{ + float b = -2.0f * dot(dir, -pos); + float c = len_squared(pos) - sqr(atmosphere_radius); + float t = (-b + sqrtf(b * b - 4.0f * c)) / 2.0f; + return make_float3(pos.x + dir.x * t, pos.y + dir.y * t, pos.z + dir.z * t); +} + +static float3 ray_optical_depth(float3 ray_origin, float3 ray_dir) +{ + /* This code computes the optical depth along a ray through the atmosphere. */ + float3 ray_end = atmosphere_intersection(ray_origin, ray_dir); + float ray_length = distance(ray_origin, ray_end); + + /* To compute the optical depth, we step along the ray in segments and + * accumulate the optical depth along each segment. */ + float segment_length = ray_length / steps_light; + float3 segment = segment_length * ray_dir; + + /* Instead of tracking the transmission spectrum across all wavelengths directly, + * we use the fact that the density always has the same spectrum for each type of + * scattering, so we split the density into a constant spectrum and a factor and + * only track the factors. */ + float3 optical_depth = make_float3(0.0f, 0.0f, 0.0f); + + /* The density of each segment is evaluated at its middle. */ + float3 P = ray_origin + 0.5f * segment; + for (int i = 0; i < steps_light; i++) { + /* Compute height above sea level. */ + float height = len(P) - earth_radius; + + /* Accumulate optical depth of this segment (density is assumed to be constant along it). */ + float3 density = make_float3( + density_rayleigh(height), density_mie(height), density_ozone(height)); + optical_depth += segment_length * density; + + /* Advance along ray. */ + P += segment; + } + + return optical_depth; +} + +/* Single Scattering implementation */ +static void single_scattering(float3 ray_dir, + float3 sun_dir, + float3 ray_origin, + float air_density, + float dust_density, + float ozone_density, + float *r_spectrum) +{ + /* This code computes single-inscattering along a ray through the atmosphere. */ + float3 ray_end = atmosphere_intersection(ray_origin, ray_dir); + float ray_length = distance(ray_origin, ray_end); + + /* To compute the inscattering, we step along the ray in segments and accumulate + * the inscattering as well as the optical depth along each segment. */ + float segment_length = ray_length / steps; + float3 segment = segment_length * ray_dir; + + /* Instead of tracking the transmission spectrum across all wavelengths directly, + * we use the fact that the density always has the same spectrum for each type of + * scattering, so we split the density into a constant spectrum and a factor and + * only track the factors. */ + float3 optical_depth = make_float3(0.0f, 0.0f, 0.0f); + + /* Zero out light accumulation. */ + for (int wl = 0; wl < num_wavelengths; wl++) { + r_spectrum[wl] = 0.0f; + } + + /* Compute phase function for scattering and the density scale factor. */ + float mu = dot(ray_dir, sun_dir); + float3 phase_function = make_float3(phase_rayleigh(mu), phase_mie(mu), 0.0f); + float3 density_scale = make_float3(air_density, dust_density, ozone_density); + + /* The density and in-scattering of each segment is evaluated at its middle. */ + float3 P = ray_origin + 0.5f * segment; + for (int i = 0; i < steps; i++) { + /* Compute height above sea level. */ + float height = len(P) - earth_radius; + + /* Evaluate and accumulate optical depth along the ray. */ + float3 density = density_scale * make_float3(density_rayleigh(height), + density_mie(height), + density_ozone(height)); + optical_depth += segment_length * density; + + /* If the earth isn't in the way, evaluate inscattering from the sun. */ + if (!surface_intersection(P, sun_dir)) { + float3 light_optical_depth = density_scale * ray_optical_depth(P, sun_dir); + float3 total_optical_depth = optical_depth + light_optical_depth; + + /* attenuation of light */ + for (int wl = 0; wl < num_wavelengths; wl++) { + float3 extinction_density = total_optical_depth * make_float3(rayleigh_coeff[wl], + 1.11f * mie_coeff, + ozone_coeff[wl]); + float attenuation = expf(-reduce_add(extinction_density)); + + float3 scattering_density = density * make_float3(rayleigh_coeff[wl], mie_coeff, 0.0f); + + /* The total inscattered radiance from one segment is: + * Tr(A<->B) * Tr(B<->C) * sigma_s * phase * L * segment_length + * + * These terms are: + * Tr(A<->B): Transmission from start to scattering position (tracked in optical_depth) + * Tr(B<->C): Transmission from scattering position to light (computed in + * ray_optical_depth) sigma_s: Scattering density phase: Phase function of the scattering + * type (Rayleigh or Mie) L: Radiance coming from the light source segment_length: The + * length of the segment + * + * The code here is just that, with a bit of additional optimization to not store full + * spectra for the optical depth. + */ + r_spectrum[wl] += attenuation * reduce_add(phase_function * scattering_density) * + irradiance[wl] * segment_length; + } + } + + /* Advance along ray. */ + P += segment; + } +} + +/* calculate texture array */ +void nishita_skymodel_precompute_texture(float *pixels, + int stride, + int start_y, + int end_y, + int width, + int height, + float sun_elevation, + float altitude, + float air_density, + float dust_density, + float ozone_density) +{ + /* calculate texture pixels */ + float spectrum[num_wavelengths]; + int half_width = width / 2; + float3 cam_pos = make_float3(0, 0, earth_radius + altitude); + float3 sun_dir = geographical_to_direction(sun_elevation, 0.0f); + + float latitude_step = M_PI_2_F / height; + float longitude_step = M_2PI_F / width; + + for (int y = start_y; y < end_y; y++) { + float latitude = latitude_step * y; + + float *pixel_row = pixels + (y * width) * stride; + for (int x = 0; x < half_width; x++) { + float longitude = longitude_step * x - M_PI_F; + + float3 dir = geographical_to_direction(latitude, longitude); + single_scattering(dir, sun_dir, cam_pos, air_density, dust_density, ozone_density, spectrum); + float3 xyz = spec_to_xyz(spectrum); + + pixel_row[x * stride + 0] = xyz.x; + pixel_row[x * stride + 1] = xyz.y; + pixel_row[x * stride + 2] = xyz.z; + int mirror_x = width - x - 1; + pixel_row[mirror_x * stride + 0] = xyz.x; + pixel_row[mirror_x * stride + 1] = xyz.y; + pixel_row[mirror_x * stride + 2] = xyz.z; + } + } +} + +/* Sun disc */ +static void sun_radiation(float3 cam_dir, + float altitude, + float air_density, + float dust_density, + float solid_angle, + float *r_spectrum) +{ + float3 cam_pos = make_float3(0, 0, earth_radius + altitude); + float3 optical_depth = ray_optical_depth(cam_pos, cam_dir); + + /* Compute final spectrum. */ + for (int i = 0; i < num_wavelengths; i++) { + /* Combine spectra and the optical depth into transmittance. */ + float transmittance = rayleigh_coeff[i] * optical_depth.x * air_density + + 1.11f * mie_coeff * optical_depth.y * dust_density; + r_spectrum[i] = (irradiance[i] / solid_angle) * expf(-transmittance); + } +} + +void nishita_skymodel_precompute_sun(float sun_elevation, + float angular_diameter, + float altitude, + float air_density, + float dust_density, + float *pixel_bottom, + float *pixel_top) +{ + /* definitions */ + float half_angular = angular_diameter / 2.0f; + float solid_angle = M_2PI_F * (1.0f - cosf(half_angular)); + float spectrum[num_wavelengths]; + float bottom = sun_elevation - half_angular; + float top = sun_elevation + half_angular; + float elevation_bottom, elevation_top; + float3 pix_bottom, pix_top, sun_dir; + + /* compute 2 pixels for sun disc */ + elevation_bottom = (bottom > 0.0f) ? bottom : 0.0f; + elevation_top = (top > 0.0f) ? top : 0.0f; + sun_dir = geographical_to_direction(elevation_bottom, 0.0f); + sun_radiation(sun_dir, altitude, air_density, dust_density, solid_angle, spectrum); + pix_bottom = spec_to_xyz(spectrum); + sun_dir = geographical_to_direction(elevation_top, 0.0f); + sun_radiation(sun_dir, altitude, air_density, dust_density, solid_angle, spectrum); + pix_top = spec_to_xyz(spectrum); + + /* store pixels */ + pixel_bottom[0] = pix_bottom.x; + pixel_bottom[1] = pix_bottom.y; + pixel_bottom[2] = pix_bottom.z; + pixel_top[0] = pix_top.x; + pixel_top[1] = pix_top.y; + pixel_top[2] = pix_top.z; +} + +CCL_NAMESPACE_END diff --git a/intern/mantaflow/intern/strings/liquid_script.h b/intern/mantaflow/intern/strings/liquid_script.h index 26b6644f231..65733aad736 100644 --- a/intern/mantaflow/intern/strings/liquid_script.h +++ b/intern/mantaflow/intern/strings/liquid_script.h @@ -131,20 +131,20 @@ curvature_s$ID$ = s$ID$.create(RealGrid, name='$NAME_CURVATURE$')\n"; const std::string liquid_alloc_particles = "\n\ -ppSnd_sp$ID$ = sp$ID$.create(BasicParticleSystem, name='$FLUID_NAME_PP_PARTICLES$')\n\ -pVelSnd_pp$ID$ = ppSnd_sp$ID$.create(PdataVec3, name='$FLUID_NAME_PVEL_PARTICLES$')\n\ -pForceSnd_pp$ID$ = ppSnd_sp$ID$.create(PdataVec3, name='$FLUID_NAME_PFORCE_PARTICLES$')\n\ -pLifeSnd_pp$ID$ = ppSnd_sp$ID$.create(PdataReal, name='$FLUID_NAME_PLIFE_PARTICLES$')\n\ -vel_sp$ID$ = sp$ID$.create(MACGrid, name='$FLUID_NAME_VELOCITY_PARTICLES$')\n\ -flags_sp$ID$ = sp$ID$.create(FlagGrid, name='$FLUID_NAME_FLAGS_PARTICLES$')\n\ -phi_sp$ID$ = sp$ID$.create(LevelsetGrid, name='$FLUID_NAME_PHI_PARTICLES$')\n\ -phiObs_sp$ID$ = sp$ID$.create(LevelsetGrid, name='$FLUID_NAME_PHIOBS_PARTICLES$')\n\ -phiOut_sp$ID$ = sp$ID$.create(LevelsetGrid, name='$FLUID_NAME_PHIOUT_PARTICLES$')\n\ -normal_sp$ID$ = sp$ID$.create(VecGrid, name='$FLUID_NAME_NORMAL_PARTICLES$')\n\ -neighborRatio_sp$ID$ = sp$ID$.create(RealGrid, name='$FLUID_NAME_NEIGHBORRATIO_PARTICLES$')\n\ -trappedAir_sp$ID$ = sp$ID$.create(RealGrid, name='$FLUID_NAME_TRAPPEDAIR_PARTICLES$')\n\ -waveCrest_sp$ID$ = sp$ID$.create(RealGrid, name='$FLUID_NAME_WAVECREST_PARTICLES$')\n\ -kineticEnergy_sp$ID$ = sp$ID$.create(RealGrid, name='$FLUID_NAME_KINETICENERGY_PARTICLES$')\n\ +ppSnd_sp$ID$ = sp$ID$.create(BasicParticleSystem, name='$NAME_PP_PARTICLES$')\n\ +pVelSnd_pp$ID$ = ppSnd_sp$ID$.create(PdataVec3, name='$NAME_PVEL_PARTICLES$')\n\ +pForceSnd_pp$ID$ = ppSnd_sp$ID$.create(PdataVec3, name='$NAME_PFORCE_PARTICLES$')\n\ +pLifeSnd_pp$ID$ = ppSnd_sp$ID$.create(PdataReal, name='$NAME_PLIFE_PARTICLES$')\n\ +vel_sp$ID$ = sp$ID$.create(MACGrid, name='$NAME_VELOCITY_PARTICLES$')\n\ +flags_sp$ID$ = sp$ID$.create(FlagGrid, name='$NAME_FLAGS_PARTICLES$')\n\ +phi_sp$ID$ = sp$ID$.create(LevelsetGrid, name='$NAME_PHI_PARTICLES$')\n\ +phiObs_sp$ID$ = sp$ID$.create(LevelsetGrid, name='$NAME_PHIOBS_PARTICLES$')\n\ +phiOut_sp$ID$ = sp$ID$.create(LevelsetGrid, name='$NAME_PHIOUT_PARTICLES$')\n\ +normal_sp$ID$ = sp$ID$.create(VecGrid, name='$NAME_NORMAL_PARTICLES$')\n\ +neighborRatio_sp$ID$ = sp$ID$.create(RealGrid, name='$NAME_NEIGHBORRATIO_PARTICLES$')\n\ +trappedAir_sp$ID$ = sp$ID$.create(RealGrid, name='$NAME_TRAPPEDAIR_PARTICLES$')\n\ +waveCrest_sp$ID$ = sp$ID$.create(RealGrid, name='$NAME_WAVECREST_PARTICLES$')\n\ +kineticEnergy_sp$ID$ = sp$ID$.create(RealGrid, name='$NAME_KINETICENERGY_PARTICLES$')\n\ \n\ # Set some initial values\n\ phi_sp$ID$.setConst(9999)\n\ diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index bbbe520441c..c0f99bdb8a1 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -695,9 +695,12 @@ def km_user_interface(_params): ("ui.copy_data_path_button", {"type": 'C', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, {"properties": [("full_path", True)]}), # Keyframes and drivers - ("anim.keyframe_insert_button", {"type": 'I', "value": 'PRESS'}, None), - ("anim.keyframe_delete_button", {"type": 'I', "value": 'PRESS', "alt": True}, None), - ("anim.keyframe_clear_button", {"type": 'I', "value": 'PRESS', "shift": True, "alt": True}, None), + ("anim.keyframe_insert_button", {"type": 'I', "value": 'PRESS'}, + {"properties": [("all", True)]}), + ("anim.keyframe_delete_button", {"type": 'I', "value": 'PRESS', "alt": True}, + {"properties": [("all", True)]}), + ("anim.keyframe_clear_button", {"type": 'I', "value": 'PRESS', "shift": True, "alt": True}, + {"properties": [("all", True)]}), ("anim.driver_button_add", {"type": 'D', "value": 'PRESS', "ctrl": True}, None), ("anim.driver_button_remove", {"type": 'D', "value": 'PRESS', "ctrl": True, "alt": True}, None), ("anim.keyingset_button_add", {"type": 'K', "value": 'PRESS'}, None), @@ -2464,7 +2467,7 @@ def km_sequencer(params): {"properties": [("all", False)]}), ("sequencer.gap_remove", {"type": 'BACK_SPACE', "value": 'PRESS', "shift": True}, {"properties": [("all", True)]}), - ("sequencer.gap_insert", {"type": 'BACK_SPACE', "value": 'PRESS', "ctrl": True}, None), + ("sequencer.gap_insert", {"type": 'EQUAL', "value": 'PRESS', "shift": True}, None), ("sequencer.snap", {"type": 'S', "value": 'PRESS', "shift": True}, None), ("sequencer.swap_inputs", {"type": 'S', "value": 'PRESS', "alt": True}, None), *( @@ -2520,14 +2523,6 @@ def km_sequencer(params): {"properties": [("side", 'LEFT')]}), ("sequencer.select_side_of_frame", {"type": 'RIGHT_BRACKET', "value": 'PRESS'}, {"properties": [("side", 'RIGHT')]}), - ("sequencer.select_side_of_frame", {"type": 'EQUAL', "value": 'PRESS'}, - {"properties": [("side", 'OVERLAP')]}), - ("sequencer.select_side_of_frame", {"type": 'LEFT_BRACKET', "value": 'PRESS', "shift": True}, - {"properties": [("side", 'LEFT'), ("extend", True)]}), - ("sequencer.select_side_of_frame", {"type": 'RIGHT_BRACKET', "value": 'PRESS', "shift": True}, - {"properties": [("side", 'RIGHT'), ("extend", True)]}), - ("sequencer.select_side_of_frame", {"type": 'EQUAL', "value": 'PRESS', "shift": True}, - {"properties": [("side", 'OVERLAP'), ("extend", True)]}), *_template_items_context_menu("SEQUENCER_MT_context_menu", params.context_menu_event), ]) diff --git a/release/scripts/startup/bl_operators/sequencer.py b/release/scripts/startup/bl_operators/sequencer.py index af071561232..e0aaea3c085 100644 --- a/release/scripts/startup/bl_operators/sequencer.py +++ b/release/scripts/startup/bl_operators/sequencer.py @@ -164,6 +164,7 @@ class SequencerFadesClear(Operator): if curve: fcurves.remove(curve) setattr(sequence, animated_property, 1.0) + sequence.invalidate('COMPOSITE') return {'FINISHED'} @@ -230,6 +231,7 @@ class SequencerFadesAdd(Operator): self.fade_animation_clear(fade_fcurve, fades) self.fade_animation_create(fade_fcurve, fades) faded_sequences.append(sequence) + sequence.invalidate('COMPOSITE') sequence_string = "sequence" if len(faded_sequences) == 1 else "sequences" self.report({'INFO'}, "Added fade animation to {} {}.".format(len(faded_sequences), sequence_string)) diff --git a/release/scripts/startup/bl_operators/vertexpaint_dirt.py b/release/scripts/startup/bl_operators/vertexpaint_dirt.py index 7024582ad30..62ef3cb34b5 100644 --- a/release/scripts/startup/bl_operators/vertexpaint_dirt.py +++ b/release/scripts/startup/bl_operators/vertexpaint_dirt.py @@ -32,7 +32,7 @@ def get_vcolor_layer_data(me): return lay.data -def applyVertexDirt(me, blur_iterations, blur_strength, clamp_dirt, clamp_clean, dirt_only): +def applyVertexDirt(me, blur_iterations, blur_strength, clamp_dirt, clamp_clean, dirt_only, normalize): from mathutils import Vector from math import acos import array @@ -74,14 +74,14 @@ def applyVertexDirt(me, blur_iterations, blur_strength, clamp_dirt, clamp_clean, tot_con = len(con[i]) if tot_con == 0: - continue + ang = pi / 2.0 # assume 90°, i. e. flat + else: + vec /= tot_con - vec /= tot_con - - # angle is the acos() of the dot product between normal and connected verts. - # > 90 degrees: convex - # < 90 degrees: concave - ang = acos(no.dot(vec)) + # angle is the acos() of the dot product between normal and connected verts. + # > 90 degrees: convex + # < 90 degrees: concave + ang = acos(no.dot(vec)) # enforce min/max ang = max(clamp_dirt, ang) @@ -104,8 +104,12 @@ def applyVertexDirt(me, blur_iterations, blur_strength, clamp_dirt, clamp_clean, vert_tone[j] /= len(c) * blur_strength + 1 del orig_vert_tone - min_tone = min(vert_tone) - max_tone = max(vert_tone) + if normalize: + min_tone = min(vert_tone) + max_tone = max(vert_tone) + else: + min_tone = clamp_dirt + max_tone = clamp_clean tone_range = max_tone - min_tone @@ -181,6 +185,11 @@ class VertexPaintDirt(Operator): description="Don't calculate cleans for convex areas", default=False, ) + normalize: BoolProperty( + name="Normalize", + description="Normalize the colors, increasing the contrast", + default=True, + ) @classmethod def poll(cls, context): @@ -198,6 +207,7 @@ class VertexPaintDirt(Operator): self.dirt_angle, self.clean_angle, self.dirt_only, + self.normalize, ) return ret diff --git a/release/scripts/startup/bl_ui/properties_data_camera.py b/release/scripts/startup/bl_ui/properties_data_camera.py index 62dffa3b6ba..3de144c5a15 100644 --- a/release/scripts/startup/bl_ui/properties_data_camera.py +++ b/release/scripts/startup/bl_ui/properties_data_camera.py @@ -84,7 +84,6 @@ class DATA_PT_lens(CameraButtonsPanel, Panel): col.separator() if cam.type == 'PERSP': - col = layout.column() if cam.lens_unit == 'MILLIMETERS': col.prop(cam, "lens") elif cam.lens_unit == 'FOV': diff --git a/release/scripts/startup/bl_ui/space_outliner.py b/release/scripts/startup/bl_ui/space_outliner.py index ee8015df273..aa4d0b94b7f 100644 --- a/release/scripts/startup/bl_ui/space_outliner.py +++ b/release/scripts/startup/bl_ui/space_outliner.py @@ -213,7 +213,7 @@ class OUTLINER_MT_collection(Menu): layout.separator() layout.operator("outliner.delete", text="Delete", icon='X') - layout.operator("outliner.collection_hierarchy_delete") + layout.operator("outliner.delete", text="Delete Hierarchy").hierarchy = True layout.separator() @@ -279,9 +279,7 @@ class OUTLINER_MT_object(Menu): layout.separator() layout.operator("outliner.delete", text="Delete", icon='X') - - if space.display_mode == 'VIEW_LAYER' and not space.use_filter_collection: - layout.operator("outliner.object_operation", text="Delete Hierarchy").type = 'DELETE_HIERARCHY' + layout.operator("outliner.delete", text="Delete Hierarchy").hierarchy = True layout.separator() diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 20fa0b9165b..0b1dfe7dc83 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -6809,7 +6809,10 @@ class VIEW3D_PT_overlay_gpencil_options(Panel): if context.object.mode in {'PAINT_GPENCIL', 'VERTEX_GPENCIL'}: layout.label(text="Vertex Paint") - layout.prop(overlay, "gpencil_vertex_paint_opacity", text="Opacity", slider=True) + row = layout.row() + shading = VIEW3D_PT_shading.get_shading(context) + row.enabled = shading.type not in {'WIREFRAME', 'RENDERED'} + row.prop(overlay, "gpencil_vertex_paint_opacity", text="Opacity", slider=True) class VIEW3D_PT_quad_view(Panel): diff --git a/source/blender/blenkernel/BKE_blendfile.h b/source/blender/blenkernel/BKE_blendfile.h index 2bff684948d..e835137bfa1 100644 --- a/source/blender/blenkernel/BKE_blendfile.h +++ b/source/blender/blenkernel/BKE_blendfile.h @@ -74,6 +74,7 @@ void BKE_blendfile_write_partial_begin(struct Main *bmain_src); bool BKE_blendfile_write_partial(struct Main *bmain_src, const char *filepath, const int write_flags, + const int remap_mode, struct ReportList *reports); void BKE_blendfile_write_partial_end(struct Main *bmain_src); diff --git a/source/blender/blenkernel/BKE_collection.h b/source/blender/blenkernel/BKE_collection.h index 2db2be131df..4cf33640ebd 100644 --- a/source/blender/blenkernel/BKE_collection.h +++ b/source/blender/blenkernel/BKE_collection.h @@ -62,8 +62,8 @@ bool BKE_collection_delete(struct Main *bmain, struct Collection *collection, bo struct Collection *BKE_collection_duplicate(struct Main *bmain, struct Collection *parent, struct Collection *collection, - const bool do_objects, - const bool do_obdata); + const uint duplicate_flags, + const uint duplicate_options); /* Master Collection for Scene */ diff --git a/source/blender/blenkernel/BKE_global.h b/source/blender/blenkernel/BKE_global.h index f6cae6d8a9c..a134f29228f 100644 --- a/source/blender/blenkernel/BKE_global.h +++ b/source/blender/blenkernel/BKE_global.h @@ -177,19 +177,20 @@ enum { /** On read, use #FileGlobal.filename instead of the real location on-disk, * needed for recovering temp files so relative paths resolve */ G_FILE_RECOVER = (1 << 23), - /** On write, remap relative file paths to the new file location. */ - G_FILE_RELATIVE_REMAP = (1 << 24), /** On write, make backup `.blend1`, `.blend2` ... files, when the users preference is enabled */ G_FILE_HISTORY = (1 << 25), /** BMesh option to save as older mesh format */ /* #define G_FILE_MESH_COMPAT (1 << 26) */ - /** On write, restore paths after editing them (G_FILE_RELATIVE_REMAP) */ + /** On write, restore paths after editing them (see #BLO_WRITE_PATH_REMAP_RELATIVE). */ G_FILE_SAVE_COPY = (1 << 27), /* #define G_FILE_GLSL_NO_ENV_LIGHTING (1 << 28) */ /* deprecated */ }; -/** Don't overwrite these flags when reading a file. */ -#define G_FILE_FLAG_ALL_RUNTIME (G_FILE_NO_UI | G_FILE_RELATIVE_REMAP | G_FILE_SAVE_COPY) +/** + * Run-time only #G.fileflags which are never read or written to/from Blend files. + * This means we can change the values without worrying about do-versions. + */ +#define G_FILE_FLAG_ALL_RUNTIME (G_FILE_NO_UI | G_FILE_HISTORY | G_FILE_SAVE_COPY) /** ENDIAN_ORDER: indicates what endianness the platform where the file was written had. */ #if !defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__) diff --git a/source/blender/blenkernel/BKE_lib_id.h b/source/blender/blenkernel/BKE_lib_id.h index 7f5a6e3e36a..e6a711732c9 100644 --- a/source/blender/blenkernel/BKE_lib_id.h +++ b/source/blender/blenkernel/BKE_lib_id.h @@ -144,6 +144,16 @@ struct ID *BKE_libblock_find_name(struct Main *bmain, const short type, const char *name) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +/** + * Duplicate (a.k.a. deep copy) common processing options. + * See also eDupli_ID_Flags for options controlling what kind of IDs to duplicate. + */ +typedef enum eLibIDDuplicateFlags { + /** This call to a duplicate function is part of another call for some parent ID. + * Therefore, this sub-process should not clear `newid` pointers, nor handle remapping itself. */ + LIB_ID_DUPLICATE_IS_SUBPROCESS = 1 << 0, +} eLibIDDuplicateFlags; + /* lib_remap.c (keep here since they're general functions) */ /** * New freeing logic options. @@ -217,6 +227,10 @@ bool id_single_user(struct bContext *C, bool BKE_id_copy_is_allowed(const struct ID *id); bool BKE_id_copy(struct Main *bmain, const struct ID *id, struct ID **newid); bool BKE_id_copy_ex(struct Main *bmain, const struct ID *id, struct ID **r_newid, const int flag); +struct ID *BKE_id_copy_for_duplicate(struct Main *bmain, + struct ID *id, + const bool is_owner_id_liboverride, + const uint duplicate_flags); void BKE_lib_id_swap(struct Main *bmain, struct ID *id_a, struct ID *id_b); void BKE_lib_id_swap_full(struct Main *bmain, struct ID *id_a, struct ID *id_b); diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index 46c6f68384e..7d989bfcf69 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -484,6 +484,7 @@ void BKE_mesh_poly_edgebitmap_insert(unsigned int *edge_bitmap, const struct MLoop *mloop); bool BKE_mesh_center_median(const struct Mesh *me, float r_cent[3]); +bool BKE_mesh_center_median_from_polys(const struct Mesh *me, float r_cent[3]); bool BKE_mesh_center_bounds(const struct Mesh *me, float r_cent[3]); bool BKE_mesh_center_of_surface(const struct Mesh *me, float r_cent[3]); bool BKE_mesh_center_of_volume(const struct Mesh *me, float r_cent[3]); diff --git a/source/blender/blenkernel/BKE_modifier.h b/source/blender/blenkernel/BKE_modifier.h index 766ca78dc50..d99b4fddf2d 100644 --- a/source/blender/blenkernel/BKE_modifier.h +++ b/source/blender/blenkernel/BKE_modifier.h @@ -48,25 +48,28 @@ typedef enum { /* Should not be used, only for None modifier type */ eModifierTypeType_None, - /* Modifier only does deformation, implies that modifier + /** + * Modifier only does deformation, implies that modifier * type should have a valid deformVerts function. OnlyDeform * style modifiers implicitly accept either mesh or CV * input but should still declare flags appropriately. */ eModifierTypeType_OnlyDeform, - /* Modifier adds geometry. */ + /** Modifier adds geometry. */ eModifierTypeType_Constructive, /* Modifier can add and remove geometry. */ eModifierTypeType_Nonconstructive, - /* both deformVerts & applyModifier are valid calls + /** + * Both deformVerts & applyModifier are valid calls * used for particles modifier that doesn't actually modify the object * unless it's a mesh and can be exploded -> curve can also emit particles */ eModifierTypeType_DeformOrConstruct, - /* Like eModifierTypeType_Nonconstructive, but does not affect the geometry + /** + * Like eModifierTypeType_Nonconstructive, but does not affect the geometry * of the object, rather some of its CustomData layers. * E.g. UVProject and WeightVG modifiers. */ eModifierTypeType_NonGeometrical, @@ -78,7 +81,8 @@ typedef enum { eModifierTypeFlag_SupportsMapping = (1 << 2), eModifierTypeFlag_SupportsEditmode = (1 << 3), - /* For modifiers that support editmode this determines if the + /** + * For modifiers that support editmode this determines if the * modifier should be enabled by default in editmode. This should * only be used by modifiers that are relatively speedy and * also generally used in editmode, otherwise let the user enable @@ -86,22 +90,25 @@ typedef enum { */ eModifierTypeFlag_EnableInEditmode = (1 << 4), - /* For modifiers that require original data and so cannot + /** + * For modifiers that require original data and so cannot * be placed after any non-deformative modifier. */ eModifierTypeFlag_RequiresOriginalData = (1 << 5), - /* For modifiers that support pointcache, - * so we can check to see if it has files we need to deal with. */ + /** + * For modifiers that support pointcache, + * so we can check to see if it has files we need to deal with. + */ eModifierTypeFlag_UsesPointCache = (1 << 6), - /* For physics modifiers, max one per type */ + /** For physics modifiers, max one per type */ eModifierTypeFlag_Single = (1 << 7), - /* Some modifier can't be added manually by user */ + /** Some modifier can't be added manually by user */ eModifierTypeFlag_NoUserAdd = (1 << 8), - /* For modifiers that use CD_PREVIEW_MCOL for preview. */ + /** For modifiers that use CD_PREVIEW_MCOL for preview. */ eModifierTypeFlag_UsesPreview = (1 << 9), eModifierTypeFlag_AcceptsVertexCosOnly = (1 << 10), @@ -169,7 +176,8 @@ typedef struct ModifierTypeInfo { /********************* Non-optional functions *********************/ - /* Copy instance data for this modifier type. Should copy all user + /** + * Copy instance data for this modifier type. Should copy all user * level settings to the target modifier. * * \param flag: Copying options (see BKE_lib_id.h's LIB_ID_COPY_... flags for more). @@ -178,7 +186,8 @@ typedef struct ModifierTypeInfo { /********************* Deform modifier functions *********************/ - /* Only for deform types, should apply the deformation + /** + * Only for deform types, should apply the deformation * to the given vertex array. If the deformer requires information from * the object it can obtain it from the mesh argument if non-NULL, * and otherwise the ob argument. @@ -189,15 +198,17 @@ typedef struct ModifierTypeInfo { float (*vertexCos)[3], int numVerts); - /* Like deformMatricesEM but called from object mode (for supporting modifiers in sculpt mode) */ + /** + * Like deformMatricesEM but called from object mode (for supporting modifiers in sculpt mode). + */ void (*deformMatrices)(struct ModifierData *md, const struct ModifierEvalContext *ctx, struct Mesh *mesh, float (*vertexCos)[3], float (*defMats)[3][3], int numVerts); - - /* Like deformVerts but called during editmode (for supporting modifiers) + /** + * Like deformVerts but called during editmode (for supporting modifiers) */ void (*deformVertsEM)(struct ModifierData *md, const struct ModifierEvalContext *ctx, @@ -217,7 +228,8 @@ typedef struct ModifierTypeInfo { /********************* Non-deform modifier functions *********************/ - /* For non-deform types: apply the modifier and return a mesh data-block. + /** + * For non-deform types: apply the modifier and return a mesh data-block. * * The mesh argument should always be non-NULL; the modifier should use the * passed in mesh data-block rather than object->data, as it contains the mesh @@ -242,14 +254,16 @@ typedef struct ModifierTypeInfo { /********************* Optional functions *********************/ - /* Initialize new instance data for this modifier type, this function + /** + * Initialize new instance data for this modifier type, this function * should set modifier variables to their default values. * * This function is optional. */ void (*initData)(struct ModifierData *md); - /* Should add to passed \a r_cddata_masks the data types that this + /** + * Should add to passed \a r_cddata_masks the data types that this * modifier needs. If (mask & (1 << (layer type))) != 0, this modifier * needs that custom data layer. It can change required layers * depending on the modifier's settings. @@ -266,7 +280,8 @@ typedef struct ModifierTypeInfo { struct ModifierData *md, struct CustomData_MeshMasks *r_cddata_masks); - /* Free internal modifier data variables, this function should + /** + * Free internal modifier data variables, this function should * not free the md variable itself. * * This function is responsible for freeing the runtime data as well. @@ -275,7 +290,8 @@ typedef struct ModifierTypeInfo { */ void (*freeData)(struct ModifierData *md); - /* Return a boolean value indicating if this modifier is able to be + /** + * Return a boolean value indicating if this modifier is able to be * calculated based on the modifier data. This is *not* regarding the * md->flag, that is tested by the system, this is just if the data * validates (for example, a lattice will return false if the lattice @@ -285,29 +301,33 @@ typedef struct ModifierTypeInfo { */ bool (*isDisabled)(const struct Scene *scene, struct ModifierData *md, bool userRenderParams); - /* Add the appropriate relations to the dependency graph. + /** + * Add the appropriate relations to the dependency graph. * * This function is optional. */ void (*updateDepsgraph)(struct ModifierData *md, const ModifierUpdateDepsgraphContext *ctx); - /* Should return true if the modifier needs to be recalculated on time + /** + * Should return true if the modifier needs to be recalculated on time * changes. * * This function is optional (assumes false if not present). */ bool (*dependsOnTime)(struct ModifierData *md); - /* True when a deform modifier uses normals, the requiredDataMask + /** + * True when a deform modifier uses normals, the requiredDataMask * cant be used here because that refers to a normal layer whereas * in this case we need to know if the deform modifier uses normals. * * this is needed because applying 2 deform modifiers will give the * second modifier bogus normals. - * */ + */ bool (*dependsOnNormals)(struct ModifierData *md); - /* Should call the given walk function on with a pointer to each Object + /** + * Should call the given walk function on with a pointer to each Object * pointer that the modifier data stores. This is used for linking on file * load and for unlinking objects or forwarding object references. * @@ -318,7 +338,8 @@ typedef struct ModifierTypeInfo { ObjectWalkFunc walk, void *userData); - /* Should call the given walk function with a pointer to each ID + /** + * Should call the given walk function with a pointer to each ID * pointer (i.e. each data-block pointer) that the modifier data * stores. This is used for linking on file load and for * unlinking data-blocks or forwarding data-block references. @@ -331,7 +352,8 @@ typedef struct ModifierTypeInfo { IDWalkFunc walk, void *userData); - /* Should call the given walk function for each texture that the + /** + * Should call the given walk function for each texture that the * modifier data stores. This is used for finding all textures in * the context for the UI. * @@ -343,7 +365,8 @@ typedef struct ModifierTypeInfo { TexWalkFunc walk, void *userData); - /* Free given run-time data. + /** + * Free given run-time data. * * This data is coming from a modifier of the corresponding type, but actual * modifier data is not known here. @@ -355,10 +378,11 @@ typedef struct ModifierTypeInfo { */ void (*freeRuntimeData)(void *runtime_data); - /* Register the panel types for the modifier's UI. */ + /** Register the panel types for the modifier's UI. */ void (*panelRegister)(struct ARegionType *region_type); - /* Is called when the modifier is written to a file. The modifier data struct itself is written + /** + * Is called when the modifier is written to a file. The modifier data struct itself is written * already. * * This method should write any additional arrays and referenced structs that should be @@ -366,7 +390,8 @@ typedef struct ModifierTypeInfo { */ void (*blendWrite)(struct BlendWriter *writer, const struct ModifierData *md); - /* Is called when the modifier is read from a file. + /** + * Is called when the modifier is read from a file. * * It can be used to update pointers to arrays and other structs. Furthermore, fields that have * not been written (e.g. runtime data) can be reset. @@ -447,7 +472,8 @@ typedef struct CDMaskLink { struct CustomData_MeshMasks mask; } CDMaskLink; -/* Calculates and returns a linked list of CustomData_MeshMasks and modified +/** + * Calculates and returns a linked list of CustomData_MeshMasks and modified * final datamask, indicating the data required by each modifier in the stack * pointed to by md for correct evaluation, assuming the data indicated by * final_datamask is required at the end of the stack. @@ -473,7 +499,7 @@ typedef struct VirtualModifierData { struct ModifierData *BKE_modifiers_get_virtual_modifierlist(const struct Object *ob, struct VirtualModifierData *data); -/* ensure modifier correctness when changing ob->data */ +/** Ensure modifier correctness when changing ob->data. */ void BKE_modifiers_test_object(struct Object *ob); /* here for do_versions */ diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h index 342e48f5016..d830a35dda0 100644 --- a/source/blender/blenkernel/BKE_object.h +++ b/source/blender/blenkernel/BKE_object.h @@ -138,8 +138,9 @@ bool BKE_object_is_libdata(const struct Object *ob); bool BKE_object_obdata_is_libdata(const struct Object *ob); struct Object *BKE_object_duplicate(struct Main *bmain, - const struct Object *ob, - const uint dupflag); + struct Object *ob, + const uint dupflag, + const uint duplicate_options); void BKE_object_obdata_size_init(struct Object *ob, const float scale); diff --git a/source/blender/blenkernel/intern/anim_data.c b/source/blender/blenkernel/intern/anim_data.c index cbb34cbee30..841fdaa3b2c 100644 --- a/source/blender/blenkernel/intern/anim_data.c +++ b/source/blender/blenkernel/intern/anim_data.c @@ -399,6 +399,8 @@ void BKE_animdata_copy_id_action(Main *bmain, ID *id, const bool set_newid) if (ntree) { BKE_animdata_copy_id_action(bmain, &ntree->id, set_newid); } + /* Note that collections are not animatable currently, so no need to handle scenes' master + * collection here. */ } /* Merge copies of the data from the src AnimData into the destination AnimData */ diff --git a/source/blender/blenkernel/intern/blender_copybuffer.c b/source/blender/blenkernel/intern/blender_copybuffer.c index 4d27621a861..bd133ce9ea6 100644 --- a/source/blender/blenkernel/intern/blender_copybuffer.c +++ b/source/blender/blenkernel/intern/blender_copybuffer.c @@ -72,9 +72,10 @@ void BKE_copybuffer_tag_ID(ID *id) */ bool BKE_copybuffer_save(Main *bmain_src, const char *filename, ReportList *reports) { - const int write_flags = G_FILE_RELATIVE_REMAP; + const int write_flags = 0; + const eBLO_WritePathRemap remap_mode = BLO_WRITE_PATH_REMAP_RELATIVE; - bool retval = BKE_blendfile_write_partial(bmain_src, filename, write_flags, reports); + bool retval = BKE_blendfile_write_partial(bmain_src, filename, write_flags, remap_mode, reports); BKE_blendfile_write_partial_end(bmain_src); diff --git a/source/blender/blenkernel/intern/blender_undo.c b/source/blender/blenkernel/intern/blender_undo.c index ab382d0e8ff..e19a4935698 100644 --- a/source/blender/blenkernel/intern/blender_undo.c +++ b/source/blender/blenkernel/intern/blender_undo.c @@ -109,7 +109,8 @@ MemFileUndoData *BKE_memfile_undo_encode(Main *bmain, MemFileUndoData *mfu_prev) static int counter = 0; char filename[FILE_MAX]; char numstr[32]; - int fileflags = G.fileflags & ~(G_FILE_HISTORY); /* don't do file history on undo */ + /* Don't do file history on undo. */ + const int fileflags = G.fileflags & ~G_FILE_HISTORY; /* Calculate current filename. */ counter++; @@ -118,7 +119,7 @@ MemFileUndoData *BKE_memfile_undo_encode(Main *bmain, MemFileUndoData *mfu_prev) BLI_snprintf(numstr, sizeof(numstr), "%d.blend", counter); BLI_join_dirfile(filename, sizeof(filename), BKE_tempdir_session(), numstr); - /* success = */ /* UNUSED */ BLO_write_file(bmain, filename, fileflags, NULL, NULL); + /* success = */ /* UNUSED */ BLO_write_file(bmain, filename, fileflags, NULL); BLI_strncpy(mfu->filename, filename, sizeof(mfu->filename)); } diff --git a/source/blender/blenkernel/intern/blendfile.c b/source/blender/blenkernel/intern/blendfile.c index ef474022f19..a3031e9047f 100644 --- a/source/blender/blenkernel/intern/blendfile.c +++ b/source/blender/blenkernel/intern/blendfile.c @@ -646,7 +646,7 @@ bool BKE_blendfile_userdef_write(const char *filepath, ReportList *reports) Main *mainb = MEM_callocN(sizeof(Main), "empty main"); bool ok = false; - if (BLO_write_file(mainb, filepath, G_FILE_USERPREFS, reports, NULL)) { + if (BLO_write_file(mainb, filepath, G_FILE_USERPREFS, reports)) { ok = true; } @@ -777,7 +777,8 @@ bool BKE_blendfile_workspace_config_write(Main *bmain, const char *filepath, Rep BKE_blendfile_write_partial_tag_ID(&workspace->id, true); } - if (BKE_blendfile_write_partial(bmain, filepath, fileflags, reports)) { + if (BKE_blendfile_write_partial( + bmain, filepath, fileflags, BLO_WRITE_PATH_REMAP_NONE, reports)) { retval = true; } @@ -829,11 +830,13 @@ static void blendfile_write_partial_cb(void *UNUSED(handle), Main *UNUSED(bmain) } /** + * \param remap_mode: Choose the kind of path remapping or none #eBLO_FilePathRemap. * \return Success. */ bool BKE_blendfile_write_partial(Main *bmain_src, const char *filepath, const int write_flags, + const int remap_mode, ReportList *reports) { Main *bmain_dst = MEM_callocN(sizeof(Main), "copybuffer"); @@ -875,12 +878,12 @@ bool BKE_blendfile_write_partial(Main *bmain_src, * This happens because id_sort_by_name does not take into account * string case or the library name, so the order is not strictly * defined for two linked data-blocks with the same name! */ - if (write_flags & G_FILE_RELATIVE_REMAP) { + if (remap_mode != BLO_WRITE_PATH_REMAP_NONE) { path_list_backup = BKE_bpath_list_backup(bmain_dst, path_list_flag); } /* save the buffer */ - retval = BLO_write_file(bmain_dst, filepath, write_flags, reports, NULL); + retval = BLO_write_file_ex(bmain_dst, filepath, write_flags, reports, remap_mode, NULL); if (path_list_backup) { BKE_bpath_list_restore(bmain_dst, path_list_flag, path_list_backup); diff --git a/source/blender/blenkernel/intern/boids.c b/source/blender/blenkernel/intern/boids.c index 6197b9dbefd..639437f8251 100644 --- a/source/blender/blenkernel/intern/boids.c +++ b/source/blender/blenkernel/intern/boids.c @@ -218,7 +218,7 @@ static int rule_avoid_collision(BoidRule *rule, BoidValues *val, ParticleData *pa) { - const int raycast_flag = BVH_RAYCAST_DEFAULT & ~(BVH_RAYCAST_WATERTIGHT); + const int raycast_flag = BVH_RAYCAST_DEFAULT & ~BVH_RAYCAST_WATERTIGHT; BoidRuleAvoidCollision *acbr = (BoidRuleAvoidCollision *)rule; KDTreeNearest_3d *ptn = NULL; ParticleTarget *pt; @@ -854,7 +854,7 @@ static Object *boid_find_ground(BoidBrainData *bbd, float ground_co[3], float ground_nor[3]) { - const int raycast_flag = BVH_RAYCAST_DEFAULT & ~(BVH_RAYCAST_WATERTIGHT); + const int raycast_flag = BVH_RAYCAST_DEFAULT & ~BVH_RAYCAST_WATERTIGHT; BoidParticle *bpa = pa->boid; if (bpa->data.mode == eBoidMode_Climbing) { diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index 3241518cae5..b66df6b1da6 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -493,7 +493,7 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) brush->gpencil_settings->preset_type = type; /* Set vertex mix factor. */ - brush->gpencil_settings->vertex_mode = GPPAINT_MODE_STROKE; + brush->gpencil_settings->vertex_mode = GPPAINT_MODE_BOTH; brush->gpencil_settings->vertex_factor = 1.0f; switch (type) { diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index 9abcce7c38f..dddbf7d45b2 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -28,6 +28,7 @@ #include "BLI_threads.h" #include "BLT_translation.h" +#include "BKE_anim_data.h" #include "BKE_collection.h" #include "BKE_icons.h" #include "BKE_idprop.h" @@ -326,15 +327,16 @@ bool BKE_collection_delete(Main *bmain, Collection *collection, bool hierarchy) static Collection *collection_duplicate_recursive(Main *bmain, Collection *parent, Collection *collection_old, - const bool do_objects, - const bool do_obdata) + const eDupli_ID_Flags duplicate_flags, + const eLibIDDuplicateFlags duplicate_options) { Collection *collection_new; bool do_full_process = false; - const int object_dupflag = (do_obdata) ? U.dupflag : 0; const bool is_collection_master = (collection_old->flag & COLLECTION_IS_MASTER) != 0; const bool is_collection_liboverride = ID_IS_OVERRIDE_LIBRARY(collection_old); + const bool do_objects = (duplicate_flags & USER_DUP_OBJECT) != 0; + if (is_collection_master) { /* We never duplicate master collections here, but we can still deep-copy their objects and * collections. */ @@ -343,12 +345,8 @@ static Collection *collection_duplicate_recursive(Main *bmain, do_full_process = true; } else if (collection_old->id.newid == NULL) { - BKE_id_copy(bmain, &collection_old->id, (ID **)&collection_new); - - /* Copying add one user by default, need to get rid of that one. */ - id_us_min(&collection_new->id); - - ID_NEW_SET(collection_old, collection_new); + collection_new = (Collection *)BKE_id_copy_for_duplicate( + bmain, (ID *)collection_old, is_collection_liboverride, duplicate_flags); do_full_process = true; } else { @@ -391,8 +389,8 @@ static Collection *collection_duplicate_recursive(Main *bmain, } if (ob_new == NULL) { - ob_new = BKE_object_duplicate(bmain, ob_old, (eDupli_ID_Flags)object_dupflag); - ID_NEW_SET(ob_old, ob_new); + ob_new = BKE_object_duplicate( + bmain, ob_old, duplicate_flags, duplicate_options | LIB_ID_DUPLICATE_IS_SUBPROCESS); } collection_object_add(bmain, collection_new, ob_new, 0, true); @@ -410,7 +408,7 @@ static Collection *collection_duplicate_recursive(Main *bmain, } collection_duplicate_recursive( - bmain, collection_new, child_collection_old, do_objects, do_obdata); + bmain, collection_new, child_collection_old, duplicate_flags, duplicate_options); collection_child_remove(collection_new, child_collection_old); } @@ -420,7 +418,9 @@ static Collection *collection_duplicate_recursive(Main *bmain, /** * Make a deep copy (aka duplicate) of the given collection and all of its children, recusrsively. * - * \warning This functions will clear all \a bmain id.idnew pointers. + * \warning This functions will clear all \a bmain #ID.idnew pointers, unless \a + * LIB_ID_DUPLICATE_IS_SUBPROCESS duplicate option is passed on, in which case caller is reponsible + * to reconstruct collection dependencies informations (i.e. call #BKE_main_collection_sync). * * \param do_objects: If true, it will also make copies of objects. * \param do_obdata: If true, it will also make duplicates of objects, @@ -430,23 +430,44 @@ static Collection *collection_duplicate_recursive(Main *bmain, Collection *BKE_collection_duplicate(Main *bmain, Collection *parent, Collection *collection, - const bool do_objects, - const bool do_obdata) + eDupli_ID_Flags duplicate_flags, + eLibIDDuplicateFlags duplicate_options) { - BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false); - BKE_main_id_clear_newpoins(bmain); + const bool is_subprocess = (duplicate_options & LIB_ID_DUPLICATE_IS_SUBPROCESS) != 0; - Collection *collection_new = collection_duplicate_recursive( - bmain, parent, collection, do_objects, do_obdata); + if (!is_subprocess) { + BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false); + BKE_main_id_clear_newpoins(bmain); + } - /* This code will follow into all ID links using an ID tagged with LIB_TAG_NEW.*/ - BKE_libblock_relink_to_newid(&collection_new->id); + Collection *collection_new = collection_duplicate_recursive( + bmain, parent, collection, duplicate_flags, duplicate_options); + + if (!is_subprocess) { + /* `collection_duplicate_recursive` will also tag our 'root' collection, whic is not required + * unless its duplication is a subprocess of another one. */ + collection_new->id.tag &= ~LIB_TAG_NEW; + + /* This code will follow into all ID links using an ID tagged with LIB_TAG_NEW.*/ + BKE_libblock_relink_to_newid(&collection_new->id); + +#ifndef NDEBUG + /* Call to `BKE_libblock_relink_to_newid` above is supposed to have cleared all those flags. */ + ID *id_iter; + FOREACH_MAIN_ID_BEGIN (bmain, id_iter) { + if (id_iter->tag & LIB_TAG_NEW) { + BLI_assert((id_iter->tag & LIB_TAG_NEW) == 0); + } + } + FOREACH_MAIN_ID_END; +#endif - /* Cleanup. */ - BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false); - BKE_main_id_clear_newpoins(bmain); + /* Cleanup. */ + BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false); + BKE_main_id_clear_newpoins(bmain); - BKE_main_collection_sync(bmain); + BKE_main_collection_sync(bmain); + } return collection_new; } diff --git a/source/blender/blenkernel/intern/effect.c b/source/blender/blenkernel/intern/effect.c index fe2c9ed51b8..7ed04c6976a 100644 --- a/source/blender/blenkernel/intern/effect.c +++ b/source/blender/blenkernel/intern/effect.c @@ -428,7 +428,7 @@ static float eff_calc_visibility(ListBase *colliders, EffectorData *efd, EffectedPoint *point) { - const int raycast_flag = BVH_RAYCAST_DEFAULT & ~(BVH_RAYCAST_WATERTIGHT); + const int raycast_flag = BVH_RAYCAST_DEFAULT & ~BVH_RAYCAST_WATERTIGHT; ListBase *colls = colliders; ColliderCache *col; float norm[3], len = 0.0; diff --git a/source/blender/blenkernel/intern/gpencil_curve.c b/source/blender/blenkernel/intern/gpencil_curve.c index 3591894d36a..14e2a9836c4 100644 --- a/source/blender/blenkernel/intern/gpencil_curve.c +++ b/source/blender/blenkernel/intern/gpencil_curve.c @@ -392,7 +392,8 @@ static void gpencil_convert_spline(Main *bmain, BKE_gpencil_stroke_geometry_update(gps); } -/* Convert a curve object to grease pencil stroke. +/** + * Convert a curve object to grease pencil stroke. * * \param bmain: Main thread pointer * \param scene: Original scene. diff --git a/source/blender/blenkernel/intern/gpencil_geom.c b/source/blender/blenkernel/intern/gpencil_geom.c index 378d94015ef..9883abe19a5 100644 --- a/source/blender/blenkernel/intern/gpencil_geom.c +++ b/source/blender/blenkernel/intern/gpencil_geom.c @@ -1517,7 +1517,9 @@ void BKE_gpencil_dissolve_points(bGPDframe *gpf, bGPDstroke *gps, const short ta } /* Merge by distance ------------------------------------- */ -/* Reduce a series of points when the distance is below a threshold. + +/** + * Reduce a series of points when the distance is below a threshold. * Special case for first and last points (both are keeped) for other points, * the merge point always is at first point. * \param gpf: Grease Pencil frame @@ -1820,7 +1822,7 @@ static void gpencil_generate_edgeloops(Object *ob, /* Helper: Add gpencil material using material as base. */ static Material *gpencil_add_material(Main *bmain, Object *ob_gp, - char *name, + const char *name, const float color[4], const bool use_stroke, const bool use_fill, @@ -1854,9 +1856,10 @@ static Material *gpencil_add_material(Main *bmain, return mat_gp; } -/* Convert a mesh object to grease pencil stroke. +/** + * Convert a mesh object to grease pencil stroke. * - * \param bmain: Main thread pointer + * \param bmain: Main thread pointer. * \param depsgraph: Original depsgraph. * \param scene: Original scene. * \param ob_gp: Grease pencil object to add strokes. diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index ab9b11f436a..4d36530fccc 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -608,6 +608,49 @@ bool BKE_id_copy(Main *bmain, const ID *id, ID **newid) } /** + * Invokes the appropriate copy method for the block and returns the result in + * newid, unless test. Returns true if the block can be copied. + */ +ID *BKE_id_copy_for_duplicate(Main *bmain, + ID *id, + const bool is_owner_id_liboverride, + const eDupli_ID_Flags duplicate_flags) +{ + if (id == NULL) { + return NULL; + } + if (id->newid == NULL) { + if (!is_owner_id_liboverride || !ID_IS_LINKED(id)) { + ID *id_new; + BKE_id_copy(bmain, id, &id_new); + /* Copying add one user by default, need to get rid of that one. */ + id_us_min(id_new); + ID_NEW_SET(id, id_new); + + /* Shape keys are always copied with their owner ID, by default. */ + ID *key_new = (ID *)BKE_key_from_id(id_new); + ID *key = (ID *)BKE_key_from_id(id); + if (key != NULL) { + ID_NEW_SET(key, key_new); + } + + /* Note: embedded data (root nodetrees and master collections) should never be referenced by + * anything else, so we do not need to set their newid pointer and flag. */ + + if (duplicate_flags & USER_DUP_ACT) { + BKE_animdata_copy_id_action(bmain, id_new, true); + if (key_new != NULL) { + BKE_animdata_copy_id_action(bmain, key_new, true); + } + /* Note that actions of embedded data (root nodetrees and master collections) are handled + * by `BKE_animdata_copy_id_action` as well. */ + } + } + } + return id->newid; +} + +/** * Does a mere memory swap over the whole IDs data (including type-specific memory). * \note Most internal ID data itself is not swapped (only IDProperties are). */ diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index d6f037f64a4..2da562ec2ed 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -310,7 +310,8 @@ BLI_INLINE IDOverrideLibraryRuntime *override_library_rna_path_mapping_ensure( IDOverrideLibrary *override) { if (override->runtime == NULL) { - override->runtime = BLI_ghash_new(BLI_ghashutil_strhash_p, BLI_ghashutil_strcmp, __func__); + override->runtime = BLI_ghash_new( + BLI_ghashutil_strhash_p_murmur, BLI_ghashutil_strcmp, __func__); for (IDOverrideLibraryProperty *op = override->properties.first; op != NULL; op = op->next) { BLI_ghash_insert(override->runtime, op->rna_path, op); } @@ -649,6 +650,7 @@ bool BKE_lib_override_library_status_check_local(Main *bmain, ID *local) &rnaptr_local, &rnaptr_reference, NULL, + 0, local->override_library, RNA_OVERRIDE_COMPARE_IGNORE_NON_OVERRIDABLE | RNA_OVERRIDE_COMPARE_IGNORE_OVERRIDDEN, @@ -712,6 +714,7 @@ bool BKE_lib_override_library_status_check_reference(Main *bmain, ID *local) &rnaptr_local, &rnaptr_reference, NULL, + 0, local->override_library, RNA_OVERRIDE_COMPARE_IGNORE_OVERRIDDEN, NULL)) { @@ -771,6 +774,7 @@ bool BKE_lib_override_library_operations_create(Main *bmain, &rnaptr_local, &rnaptr_reference, NULL, + 0, local->override_library, RNA_OVERRIDE_COMPARE_CREATE | RNA_OVERRIDE_COMPARE_RESTORE, &report_flags); diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c index ba986b1661b..d4246056efe 100644 --- a/source/blender/blenkernel/intern/lib_remap.c +++ b/source/blender/blenkernel/intern/lib_remap.c @@ -666,9 +666,10 @@ static int id_relink_to_newid_looper(LibraryIDLinkCallbackData *cb_data) /* See: NEW_ID macro */ if (id->newid) { BKE_library_update_ID_link_user(id->newid, id, cb_flag); - *id_pointer = id->newid; + id = id->newid; + *id_pointer = id; } - else if (id->tag & LIB_TAG_NEW) { + if (id->tag & LIB_TAG_NEW) { id->tag &= ~LIB_TAG_NEW; BKE_libblock_relink_to_newid(id); } diff --git a/source/blender/blenkernel/intern/mesh_evaluate.c b/source/blender/blenkernel/intern/mesh_evaluate.c index 433db26ded8..0ffe2ee2a41 100644 --- a/source/blender/blenkernel/intern/mesh_evaluate.c +++ b/source/blender/blenkernel/intern/mesh_evaluate.c @@ -2386,10 +2386,10 @@ float BKE_mesh_calc_poly_uv_area(const MPoly *mpoly, const MLoopUV *uv_array) * - The resulting volume will only be correct if the mesh is manifold and has consistent * face winding (non-contiguous face normals or holes in the mesh surface). */ -static float mesh_calc_poly_volume_centroid(const MPoly *mpoly, - const MLoop *loopstart, - const MVert *mvarray, - float r_cent[3]) +static float UNUSED_FUNCTION(mesh_calc_poly_volume_centroid)(const MPoly *mpoly, + const MLoop *loopstart, + const MVert *mvarray, + float r_cent[3]) { const float *v_pivot, *v_step1; float total_volume = 0.0f; @@ -2424,6 +2424,36 @@ static float mesh_calc_poly_volume_centroid(const MPoly *mpoly, } /** + * A version of mesh_calc_poly_volume_centroid that takes an initial reference center, + * use this to increase numeric stability as the quality of the result becomes + * very low quality as the value moves away from 0.0, see: T65986. + */ +static float mesh_calc_poly_volume_centroid_with_reference_center(const MPoly *mpoly, + const MLoop *loopstart, + const MVert *mvarray, + const float reference_center[3], + float r_cent[3]) +{ + /* See: mesh_calc_poly_volume_centroid for comments. */ + float v_pivot[3], v_step1[3]; + float total_volume = 0.0f; + zero_v3(r_cent); + sub_v3_v3v3(v_pivot, mvarray[loopstart[0].v].co, reference_center); + sub_v3_v3v3(v_step1, mvarray[loopstart[1].v].co, reference_center); + for (int i = 2; i < mpoly->totloop; i++) { + float v_step2[3]; + sub_v3_v3v3(v_step2, mvarray[loopstart[i].v].co, reference_center); + const float tetra_volume = volume_tri_tetrahedron_signed_v3_6x(v_pivot, v_step1, v_step2); + total_volume += tetra_volume; + for (uint j = 0; j < 3; j++) { + r_cent[j] += tetra_volume * (v_pivot[j] + v_step1[j] + v_step2[j]); + } + copy_v3_v3(v_step1, v_step2); + } + return total_volume; +} + +/** * \note * - Results won't be correct if polygon is non-planar. * - This has the advantage over #mesh_calc_poly_volume_centroid @@ -2536,10 +2566,35 @@ bool BKE_mesh_center_median(const Mesh *me, float r_cent[3]) if (me->totvert) { mul_v3_fl(r_cent, 1.0f / (float)me->totvert); } - return (me->totvert != 0); } +/** + * Calculate the center from polygons, + * use when we want to ignore vertex locations that don't have connected faces. + */ +bool BKE_mesh_center_median_from_polys(const Mesh *me, float r_cent[3]) +{ + int i = me->totpoly; + int tot = 0; + const MPoly *mpoly = me->mpoly; + const MLoop *mloop = me->mloop; + const MVert *mvert = me->mvert; + zero_v3(r_cent); + for (mpoly = me->mpoly; i--; mpoly++) { + int loopend = mpoly->loopstart + mpoly->totloop; + for (int j = mpoly->loopstart; j < loopend; j++) { + add_v3_v3(r_cent, mvert[mloop[j].v].co); + } + tot += mpoly->totloop; + } + /* otherwise we get NAN for 0 verts */ + if (me->totpoly) { + mul_v3_fl(r_cent, 1.0f / (float)tot); + } + return (me->totpoly != 0); +} + bool BKE_mesh_center_bounds(const Mesh *me, float r_cent[3]) { float min[3], max[3]; @@ -2595,12 +2650,16 @@ bool BKE_mesh_center_of_volume(const Mesh *me, float r_cent[3]) float total_volume = 0.0f; float poly_cent[3]; + /* Use an initial center to avoid numeric instability of geometry far away from the center. */ + float init_cent[3]; + const bool init_cent_result = BKE_mesh_center_median_from_polys(me, init_cent); + zero_v3(r_cent); /* calculate a weighted average of polyhedron centroids */ for (mpoly = me->mpoly; i--; mpoly++) { - poly_volume = mesh_calc_poly_volume_centroid( - mpoly, me->mloop + mpoly->loopstart, me->mvert, poly_cent); + poly_volume = mesh_calc_poly_volume_centroid_with_reference_center( + mpoly, me->mloop + mpoly->loopstart, me->mvert, init_cent, poly_cent); /* poly_cent is already volume-weighted, so no need to multiply by the volume */ add_v3_v3(r_cent, poly_cent); @@ -2616,9 +2675,10 @@ bool BKE_mesh_center_of_volume(const Mesh *me, float r_cent[3]) /* this can happen for non-manifold objects, fallback to median */ if (UNLIKELY(!is_finite_v3(r_cent))) { - return BKE_mesh_center_median(me, r_cent); + copy_v3_v3(r_cent, init_cent); + return init_cent_result; } - + add_v3_v3(r_cent, init_cent); return (me->totpoly != 0); } diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index fd7ddc9eb6d..6d9dd573b3c 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -1758,300 +1758,171 @@ Object *BKE_object_copy(Main *bmain, const Object *ob) * \note Caller MUST free \a newid pointers itself (#BKE_main_id_clear_newpoins()) and call updates * of DEG too (#DAG_relations_tag_update()). */ -Object *BKE_object_duplicate(Main *bmain, const Object *ob, const uint dupflag) +Object *BKE_object_duplicate(Main *bmain, + Object *ob, + const eDupli_ID_Flags dupflag, + const eLibIDDuplicateFlags duplicate_options) { + const bool is_subprocess = (duplicate_options & LIB_ID_DUPLICATE_IS_SUBPROCESS) != 0; + + if (!is_subprocess) { + BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false); + BKE_main_id_clear_newpoins(bmain); + } + Material ***matarar; - ID *id; - int a, didit; const bool is_object_liboverride = ID_IS_OVERRIDE_LIBRARY(ob); - Object *obn = BKE_object_copy(bmain, ob); + Object *obn; + BKE_id_copy(bmain, &ob->id, (ID **)&obn); + id_us_min(&obn->id); + if (is_subprocess) { + ID_NEW_SET(ob, obn); + } /* 0 == full linked. */ if (dupflag == 0) { return obn; } -#define ID_NEW_REMAP_US(a) \ - if ((a)->id.newid) { \ - (a) = (void *)(a)->id.newid; \ - (a)->id.us++; \ - } -#define ID_NEW_REMAP_US2(a) \ - if (((ID *)a)->newid) { \ - (a) = ((ID *)a)->newid; \ - ((ID *)a)->us++; \ - } - /* duplicates using userflags */ if (dupflag & USER_DUP_ACT) { BKE_animdata_copy_id_action(bmain, &obn->id, true); } if (dupflag & USER_DUP_MAT) { - for (a = 0; a < obn->totcol; a++) { - id = (ID *)obn->mat[a]; - if (id) { - if (is_object_liboverride && ID_IS_LINKED(id)) { - continue; - } - ID_NEW_REMAP_US(obn->mat[a]) - else - { - obn->mat[a] = ID_NEW_SET(obn->mat[a], BKE_material_copy(bmain, obn->mat[a])); - if (dupflag & USER_DUP_ACT) { - BKE_animdata_copy_id_action(bmain, &obn->mat[a]->id, true); - } - } - id_us_min(id); - } + for (int i = 0; i < obn->totcol; i++) { + BKE_id_copy_for_duplicate(bmain, (ID *)obn->mat[i], is_object_liboverride, dupflag); } } if (dupflag & USER_DUP_PSYS) { ParticleSystem *psys; for (psys = obn->particlesystem.first; psys; psys = psys->next) { - id = (ID *)psys->part; - if (id) { - if (is_object_liboverride && ID_IS_LINKED(id)) { - continue; - } - ID_NEW_REMAP_US(psys->part) - else - { - psys->part = ID_NEW_SET(psys->part, BKE_particlesettings_copy(bmain, psys->part)); - if (dupflag & USER_DUP_ACT) { - BKE_animdata_copy_id_action(bmain, &psys->part->id, true); - } - } - id_us_min(id); - } + BKE_id_copy_for_duplicate(bmain, (ID *)psys->part, is_object_liboverride, dupflag); } } - id = obn->data; - didit = 0; + ID *id = obn->data; + ID *id_new = NULL; + const bool need_to_duplicate_obdata = (id != NULL) && (id->newid == NULL); - if (!is_object_liboverride || !ID_IS_LINKED(id)) { - switch (obn->type) { - case OB_MESH: - if (dupflag & USER_DUP_MESH) { - ID_NEW_REMAP_US2(obn->data) - else - { - obn->data = ID_NEW_SET(obn->data, BKE_mesh_copy(bmain, obn->data)); - didit = 1; - } - id_us_min(id); - } - break; - case OB_CURVE: - if (dupflag & USER_DUP_CURVE) { - ID_NEW_REMAP_US2(obn->data) - else - { - obn->data = ID_NEW_SET(obn->data, BKE_curve_copy(bmain, obn->data)); - didit = 1; - } - id_us_min(id); - } - break; - case OB_SURF: - if (dupflag & USER_DUP_SURF) { - ID_NEW_REMAP_US2(obn->data) - else - { - obn->data = ID_NEW_SET(obn->data, BKE_curve_copy(bmain, obn->data)); - didit = 1; - } - id_us_min(id); - } - break; - case OB_FONT: - if (dupflag & USER_DUP_FONT) { - ID_NEW_REMAP_US2(obn->data) - else - { - obn->data = ID_NEW_SET(obn->data, BKE_curve_copy(bmain, obn->data)); - didit = 1; - } - id_us_min(id); - } - break; - case OB_MBALL: - if (dupflag & USER_DUP_MBALL) { - ID_NEW_REMAP_US2(obn->data) - else - { - obn->data = ID_NEW_SET(obn->data, BKE_mball_copy(bmain, obn->data)); - didit = 1; - } - id_us_min(id); - } - break; - case OB_LAMP: - if (dupflag & USER_DUP_LAMP) { - ID_NEW_REMAP_US2(obn->data) - else - { - obn->data = ID_NEW_SET(obn->data, BKE_light_copy(bmain, obn->data)); - didit = 1; - } - id_us_min(id); - } - break; - case OB_ARMATURE: - if (dupflag != 0) { - DEG_id_tag_update(&obn->id, ID_RECALC_GEOMETRY); - if (obn->pose) { - BKE_pose_tag_recalc(bmain, obn->pose); - } - if (dupflag & USER_DUP_ARM) { - ID_NEW_REMAP_US2(obn->data) - else - { - obn->data = ID_NEW_SET(obn->data, BKE_armature_copy(bmain, obn->data)); - BKE_pose_rebuild(bmain, obn, obn->data, true); - didit = 1; - } - id_us_min(id); - } - } - break; - case OB_LATTICE: - if (dupflag != 0) { - ID_NEW_REMAP_US2(obn->data) - else - { - obn->data = ID_NEW_SET(obn->data, BKE_lattice_copy(bmain, obn->data)); - didit = 1; - } - id_us_min(id); - } - break; - case OB_CAMERA: - if (dupflag != 0) { - ID_NEW_REMAP_US2(obn->data) - else - { - obn->data = ID_NEW_SET(obn->data, BKE_camera_copy(bmain, obn->data)); - didit = 1; - } - id_us_min(id); - } - break; - case OB_LIGHTPROBE: - if (dupflag & USER_DUP_LIGHTPROBE) { - ID_NEW_REMAP_US2(obn->data) - else - { - obn->data = ID_NEW_SET(obn->data, BKE_lightprobe_copy(bmain, obn->data)); - didit = 1; - } - id_us_min(id); - } - break; - case OB_SPEAKER: - if (dupflag != 0) { - ID_NEW_REMAP_US2(obn->data) - else - { - obn->data = ID_NEW_SET(obn->data, BKE_speaker_copy(bmain, obn->data)); - didit = 1; - } - id_us_min(id); - } - break; - case OB_GPENCIL: - if (dupflag & USER_DUP_GPENCIL) { - ID_NEW_REMAP_US2(obn->data) - else - { - obn->data = ID_NEW_SET(obn->data, BKE_gpencil_copy(bmain, obn->data)); - didit = 1; - } - id_us_min(id); - } - break; - case OB_HAIR: - if (dupflag & USER_DUP_HAIR) { - ID_NEW_REMAP_US2(obn->data) - else - { - obn->data = ID_NEW_SET(obn->data, BKE_hair_copy(bmain, obn->data)); - didit = 1; - } - id_us_min(id); - } - break; - case OB_POINTCLOUD: - if (dupflag & USER_DUP_POINTCLOUD) { - ID_NEW_REMAP_US2(obn->data) - else - { - obn->data = ID_NEW_SET(obn->data, BKE_pointcloud_copy(bmain, obn->data)); - didit = 1; - } - id_us_min(id); - } - break; - case OB_VOLUME: - if (dupflag & USER_DUP_VOLUME) { - ID_NEW_REMAP_US2(obn->data) - else - { - obn->data = ID_NEW_SET(obn->data, BKE_volume_copy(bmain, obn->data)); - didit = 1; - } - id_us_min(id); - } - break; - } - } - - /* Check if obdata is copied. */ - if (didit) { - Key *key = BKE_key_from_object(obn); - - Key *oldkey = BKE_key_from_object(ob); - if (oldkey != NULL) { - ID_NEW_SET(oldkey, key); - } - - if (dupflag & USER_DUP_ACT) { - BKE_animdata_copy_id_action(bmain, (ID *)obn->data, true); - if (key) { - BKE_animdata_copy_id_action(bmain, (ID *)key, true); + switch (obn->type) { + case OB_MESH: + if (dupflag & USER_DUP_MESH) { + id_new = BKE_id_copy_for_duplicate(bmain, id, is_object_liboverride, dupflag); } - } + break; + case OB_CURVE: + if (dupflag & USER_DUP_CURVE) { + id_new = BKE_id_copy_for_duplicate(bmain, id, is_object_liboverride, dupflag); + } + break; + case OB_SURF: + if (dupflag & USER_DUP_SURF) { + id_new = BKE_id_copy_for_duplicate(bmain, id, is_object_liboverride, dupflag); + } + break; + case OB_FONT: + if (dupflag & USER_DUP_FONT) { + id_new = BKE_id_copy_for_duplicate(bmain, id, is_object_liboverride, dupflag); + } + break; + case OB_MBALL: + if (dupflag & USER_DUP_MBALL) { + id_new = BKE_id_copy_for_duplicate(bmain, id, is_object_liboverride, dupflag); + } + break; + case OB_LAMP: + if (dupflag & USER_DUP_LAMP) { + id_new = BKE_id_copy_for_duplicate(bmain, id, is_object_liboverride, dupflag); + } + break; + case OB_ARMATURE: + if (dupflag & USER_DUP_ARM) { + id_new = BKE_id_copy_for_duplicate(bmain, id, is_object_liboverride, dupflag); + } + break; + case OB_LATTICE: + if (dupflag != 0) { + id_new = BKE_id_copy_for_duplicate(bmain, id, is_object_liboverride, dupflag); + } + break; + case OB_CAMERA: + if (dupflag != 0) { + id_new = BKE_id_copy_for_duplicate(bmain, id, is_object_liboverride, dupflag); + } + break; + case OB_LIGHTPROBE: + if (dupflag & USER_DUP_LIGHTPROBE) { + id_new = BKE_id_copy_for_duplicate(bmain, id, is_object_liboverride, dupflag); + } + break; + case OB_SPEAKER: + if (dupflag != 0) { + id_new = BKE_id_copy_for_duplicate(bmain, id, is_object_liboverride, dupflag); + } + break; + case OB_GPENCIL: + if (dupflag & USER_DUP_GPENCIL) { + id_new = BKE_id_copy_for_duplicate(bmain, id, is_object_liboverride, dupflag); + } + break; + case OB_HAIR: + if (dupflag & USER_DUP_HAIR) { + id_new = BKE_id_copy_for_duplicate(bmain, id, is_object_liboverride, dupflag); + } + break; + case OB_POINTCLOUD: + if (dupflag & USER_DUP_POINTCLOUD) { + id_new = BKE_id_copy_for_duplicate(bmain, id, is_object_liboverride, dupflag); + } + break; + case OB_VOLUME: + if (dupflag & USER_DUP_VOLUME) { + id_new = BKE_id_copy_for_duplicate(bmain, id, is_object_liboverride, dupflag); + } + break; + } + /* If obdata has been copied, we may also have to duplicate the materials assigned to it. */ + if (need_to_duplicate_obdata && id_new != NULL) { if (dupflag & USER_DUP_MAT) { matarar = BKE_object_material_array_p(obn); if (matarar) { - for (a = 0; a < obn->totcol; a++) { - id = (ID *)(*matarar)[a]; - if (id) { - if (is_object_liboverride && ID_IS_LINKED(id)) { - continue; - } - ID_NEW_REMAP_US((*matarar)[a]) - else - { - (*matarar)[a] = ID_NEW_SET((*matarar)[a], BKE_material_copy(bmain, (*matarar)[a])); - if (dupflag & USER_DUP_ACT) { - BKE_animdata_copy_id_action(bmain, &(*matarar)[a]->id, true); - } - } - id_us_min(id); - } + for (int i = 0; i < obn->totcol; i++) { + BKE_id_copy_for_duplicate(bmain, (ID *)(*matarar)[i], is_object_liboverride, dupflag); } } } } -#undef ID_NEW_REMAP_US -#undef ID_NEW_REMAP_US2 + if (!is_subprocess) { + /* This code will follow into all ID links using an ID tagged with LIB_TAG_NEW.*/ + BKE_libblock_relink_to_newid(&obn->id); + +#ifndef NDEBUG + /* Call to `BKE_libblock_relink_to_newid` above is supposed to have cleared all those flags. */ + ID *id_iter; + FOREACH_MAIN_ID_BEGIN (bmain, id_iter) { + BLI_assert((id_iter->tag & LIB_TAG_NEW) == 0); + } + FOREACH_MAIN_ID_END; +#endif + + /* Cleanup. */ + BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false); + BKE_main_id_clear_newpoins(bmain); + } + + if (obn->type == OB_ARMATURE) { + DEG_id_tag_update(&obn->id, ID_RECALC_GEOMETRY); + if (obn->pose) { + BKE_pose_tag_recalc(bmain, obn->pose); + } + // BKE_pose_rebuild(bmain, obn, obn->data, true); + } - if (ob->data != NULL) { + if (obn->data != NULL) { DEG_id_tag_update_ex(bmain, (ID *)obn->data, ID_RECALC_EDITORS); } diff --git a/source/blender/blenkernel/intern/rigidbody.c b/source/blender/blenkernel/intern/rigidbody.c index c9911d2cf85..02ce0c6f996 100644 --- a/source/blender/blenkernel/intern/rigidbody.c +++ b/source/blender/blenkernel/intern/rigidbody.c @@ -1348,9 +1348,10 @@ void BKE_rigidbody_main_collection_object_add(Main *bmain, Collection *collectio /* ************************************** */ /* Utilities API */ -/* Get RigidBody world for the given scene, creating one if needed +/** + * Get RigidBody world for the given scene, creating one if needed * - * \param scene: Scene to find active Rigid Body world for + * \param scene: Scene to find active Rigid Body world for. */ RigidBodyWorld *BKE_rigidbody_get_world(Scene *scene) { diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index d3f1efb5975..0537f1a65ad 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -822,48 +822,70 @@ Scene *BKE_scene_duplicate(Main *bmain, Scene *sce, eSceneCopyMethod type) return sce_copy; } else { - BKE_id_copy_ex(bmain, (ID *)sce, (ID **)&sce_copy, LIB_ID_COPY_ACTIONS); + const eDupli_ID_Flags duplicate_flags = U.dupflag | USER_DUP_OBJECT; + + BKE_id_copy(bmain, (ID *)sce, (ID **)&sce_copy); id_us_min(&sce_copy->id); id_us_ensure_real(&sce_copy->id); + if (duplicate_flags & USER_DUP_ACT) { + BKE_animdata_copy_id_action(bmain, &sce_copy->id, true); + } + /* Extra actions, most notably SCE_FULL_COPY also duplicates several 'children' datablocks. */ if (type == SCE_COPY_FULL) { + /* Scene duplication is always root of duplication currently. */ + const bool is_subprocess = false; + + if (!is_subprocess) { + BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false); + BKE_main_id_clear_newpoins(bmain); + } + /* Copy Freestyle LineStyle datablocks. */ LISTBASE_FOREACH (ViewLayer *, view_layer_dst, &sce_copy->view_layers) { LISTBASE_FOREACH ( FreestyleLineSet *, lineset, &view_layer_dst->freestyle_config.linesets) { - if (lineset->linestyle) { - if (is_scene_liboverride && ID_IS_LINKED(lineset->linestyle)) { - continue; - } - id_us_min(&lineset->linestyle->id); - BKE_id_copy_ex( - bmain, (ID *)lineset->linestyle, (ID **)&lineset->linestyle, LIB_ID_COPY_ACTIONS); - } + BKE_id_copy_for_duplicate( + bmain, &lineset->linestyle->id, is_scene_liboverride, duplicate_flags); } } /* Full copy of world (included animations) */ - if (sce_copy->world) { - if (!is_scene_liboverride || !ID_IS_LINKED(sce_copy->world)) { - id_us_min(&sce_copy->world->id); - BKE_id_copy_ex( - bmain, (ID *)sce_copy->world, (ID **)&sce_copy->world, LIB_ID_COPY_ACTIONS); - } - } + BKE_id_copy_for_duplicate(bmain, &sce->world->id, is_scene_liboverride, duplicate_flags); /* Full copy of GreasePencil. */ - if (sce_copy->gpd) { - if (!is_scene_liboverride || !ID_IS_LINKED(sce_copy->gpd)) { - id_us_min(&sce_copy->gpd->id); - BKE_id_copy_ex(bmain, (ID *)sce_copy->gpd, (ID **)&sce_copy->gpd, LIB_ID_COPY_ACTIONS); - } - } + BKE_id_copy_for_duplicate(bmain, &sce->gpd->id, is_scene_liboverride, duplicate_flags); /* Deep-duplicate collections and objects (using preferences' settings for which sub-data to * duplicate along the object itself). */ - BKE_collection_duplicate(bmain, NULL, sce_copy->master_collection, true, true); + BKE_collection_duplicate(bmain, + NULL, + sce_copy->master_collection, + duplicate_flags, + LIB_ID_DUPLICATE_IS_SUBPROCESS); + + if (!is_subprocess) { + /* This code will follow into all ID links using an ID tagged with LIB_TAG_NEW.*/ + BKE_libblock_relink_to_newid(&sce_copy->id); + +#ifndef NDEBUG + /* Call to `BKE_libblock_relink_to_newid` above is supposed to have cleared all those + * flags. */ + ID *id_iter; + FOREACH_MAIN_ID_BEGIN (bmain, id_iter) { + BLI_assert((id_iter->tag & LIB_TAG_NEW) == 0); + } + FOREACH_MAIN_ID_END; +#endif + + /* Cleanup. */ + BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false); + BKE_main_id_clear_newpoins(bmain); + + BKE_main_collection_sync(bmain); + } } else { /* Remove sequencer if not full copy */ diff --git a/source/blender/blenkernel/intern/seqmodifier.c b/source/blender/blenkernel/intern/seqmodifier.c index 75f7ed82165..604cbf476a8 100644 --- a/source/blender/blenkernel/intern/seqmodifier.c +++ b/source/blender/blenkernel/intern/seqmodifier.c @@ -1126,3 +1126,5 @@ int BKE_sequence_supports_modifiers(Sequence *seq) { return !ELEM(seq->type, SEQ_TYPE_SOUND_RAM, SEQ_TYPE_SOUND_HD); } + +/** \} */ diff --git a/source/blender/blenkernel/intern/sequencer.c b/source/blender/blenkernel/intern/sequencer.c index 90edebfaa97..5da6435436d 100644 --- a/source/blender/blenkernel/intern/sequencer.c +++ b/source/blender/blenkernel/intern/sequencer.c @@ -842,30 +842,22 @@ void BKE_sequence_calc(Scene *scene, Sequence *seq) } /* effects and meta: automatic start and end */ - if (seq->type & SEQ_TYPE_EFFECT) { - /* pointers */ - if (seq->seq2 == NULL) { - seq->seq2 = seq->seq1; - } - if (seq->seq3 == NULL) { - seq->seq3 = seq->seq1; - } - - /* effecten go from seq1 -> seq2: test */ - - /* we take the largest start and smallest end */ - - // seq->start = seq->startdisp = MAX2(seq->seq1->startdisp, seq->seq2->startdisp); - // seq->enddisp = MIN2(seq->seq1->enddisp, seq->seq2->enddisp); - if (seq->seq1) { - /* XXX These resets should not be necessary, but users used to be able to - * edit effect's length, leading to strange results. See [#29190] */ seq->startofs = seq->endofs = seq->startstill = seq->endstill = 0; - seq->start = seq->startdisp = max_iii( - seq->seq1->startdisp, seq->seq2->startdisp, seq->seq3->startdisp); - seq->enddisp = min_iii(seq->seq1->enddisp, seq->seq2->enddisp, seq->seq3->enddisp); + if (seq->seq3) { + seq->start = seq->startdisp = max_iii( + seq->seq1->startdisp, seq->seq2->startdisp, seq->seq3->startdisp); + seq->enddisp = min_iii(seq->seq1->enddisp, seq->seq2->enddisp, seq->seq3->enddisp); + } + else if (seq->seq2) { + seq->start = seq->startdisp = max_ii(seq->seq1->startdisp, seq->seq2->startdisp); + seq->enddisp = min_ii(seq->seq1->enddisp, seq->seq2->enddisp); + } + else { + seq->start = seq->startdisp = seq->seq1->startdisp; + seq->enddisp = seq->seq1->enddisp; + } /* we cant help if strips don't overlap, it wont give useful results. * but at least ensure 'len' is never negative which causes bad bugs elsewhere. */ if (seq->enddisp < seq->startdisp) { @@ -1188,7 +1180,7 @@ static void seqbase_unique_name(ListBase *seqbasep, SeqUniqueInfo *sui) Sequence *seq; for (seq = seqbasep->first; seq; seq = seq->next) { if ((sui->seq != seq) && STREQ(sui->name_dest, seq->name + 2)) { - /* SEQ_NAME_MAXSTR -4 for the number, -1 for \0, - 2 for prefix */ + /* SEQ_NAME_MAXSTR -4 for the number, -1 for \0, - 2 for r_prefix */ BLI_snprintf(sui->name_dest, sizeof(sui->name_dest), "%.*s.%03d", @@ -1783,6 +1775,33 @@ static void seq_open_anim_file(Scene *scene, Sequence *seq, bool openfile) } } +static bool seq_proxy_get_custom_file_fname(Sequence *seq, char *name, const int view_id) +{ + char fname[FILE_MAXFILE]; + char suffix[24]; + StripProxy *proxy = seq->strip->proxy; + + if (proxy == NULL) { + return false; + } + + BLI_join_dirfile(fname, PROXY_MAXFILE, proxy->dir, proxy->file); + BLI_path_abs(fname, BKE_main_blendfile_path_from_global()); + + if (view_id > 0) { + BLI_snprintf(suffix, sizeof(suffix), "_%d", view_id); + /* TODO(sergey): This will actually append suffix after extension + * which is weird but how was originally coded in multiview branch. + */ + BLI_snprintf(name, PROXY_MAXFILE, "%s_%s", fname, suffix); + } + else { + BLI_strncpy(name, fname, PROXY_MAXFILE); + } + + return true; +} + static bool seq_proxy_get_fname(Editing *ed, Sequence *seq, int cfra, @@ -1790,118 +1809,58 @@ static bool seq_proxy_get_fname(Editing *ed, char *name, const int view_id) { - int frameno; char dir[PROXY_MAXFILE]; - StripAnim *sanim; char suffix[24] = {'\0'}; - StripProxy *proxy = seq->strip->proxy; - if (!proxy) { + + if (proxy == NULL) { return false; } - /* MOVIE tracks (only exception: custom files) are now handled - * internally by ImBuf module for various reasons: proper time code - * support, quicker index build, using one file instead - * of a full directory of jpeg files, etc. Trying to support old - * and new method at once could lead to funny effects, if people - * have both, a directory full of jpeg files and proxy avis, so - * sorry folks, please rebuild your proxies... */ + /* Multiview suffix. */ + if (view_id > 0) { + BLI_snprintf(suffix, sizeof(suffix), "_%d", view_id); + } - sanim = BLI_findlink(&seq->anims, view_id); + /* Per strip with Custom file situation is handled separately. */ + if (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_FILE && + ed->proxy_storage != SEQ_EDIT_PROXY_DIR_STORAGE) { + if (seq_proxy_get_custom_file_fname(seq, name, view_id)) { + return true; + } + } if (ed->proxy_storage == SEQ_EDIT_PROXY_DIR_STORAGE) { - char fname[FILE_MAXFILE]; + /* Per project default. */ if (ed->proxy_dir[0] == 0) { BLI_strncpy(dir, "//BL_proxy", sizeof(dir)); } - else { + else { /* Per project with custom dir. */ BLI_strncpy(dir, ed->proxy_dir, sizeof(dir)); } - - if (sanim && sanim->anim) { - IMB_anim_get_fname(sanim->anim, fname, FILE_MAXFILE); - } - else if (seq->type == SEQ_TYPE_IMAGE) { - fname[0] = 0; - } - else { - /* We could make a name here, except non-movie's don't generate proxies, - * cancel until other types of sequence strips are supported. */ - return false; - } - BLI_path_append(dir, sizeof(dir), fname); BLI_path_abs(name, BKE_main_blendfile_path_from_global()); } - else if ((proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_DIR) && - (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_FILE)) { - BLI_strncpy(dir, seq->strip->proxy->dir, sizeof(dir)); - } - else if (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_FILE) { - BLI_strncpy(dir, seq->strip->proxy->dir, sizeof(dir)); - } - else if (sanim && sanim->anim && (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_DIR)) { - char fname[FILE_MAXFILE]; - BLI_strncpy(dir, seq->strip->proxy->dir, sizeof(dir)); - IMB_anim_get_fname(sanim->anim, fname, FILE_MAXFILE); - BLI_path_append(dir, sizeof(dir), fname); - } - else if (seq->type == SEQ_TYPE_IMAGE) { + else { + /* Pre strip with custom dir. */ if (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_DIR) { BLI_strncpy(dir, seq->strip->proxy->dir, sizeof(dir)); } - else { + else { /* Per strip default. */ BLI_snprintf(dir, PROXY_MAXFILE, "%s/BL_proxy", seq->strip->dir); } } - else { - return false; - } - - if (view_id > 0) { - BLI_snprintf(suffix, sizeof(suffix), "_%d", view_id); - } - - if (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_FILE && - ed->proxy_storage != SEQ_EDIT_PROXY_DIR_STORAGE) { - char fname[FILE_MAXFILE]; - BLI_join_dirfile(fname, PROXY_MAXFILE, dir, proxy->file); - BLI_path_abs(fname, BKE_main_blendfile_path_from_global()); - if (suffix[0] != '\0') { - /* TODO(sergey): This will actually append suffix after extension - * which is weird but how was originally coded in multiview branch. - */ - BLI_snprintf(name, PROXY_MAXFILE, "%s_%s", fname, suffix); - } - else { - BLI_strncpy(name, fname, PROXY_MAXFILE); - } - - return true; - } - - /* generate a separate proxy directory for each preview size */ + /* Proxy size number to be used in path. */ int proxy_size_number = BKE_sequencer_rendersize_to_scale_factor(render_size) * 100; - if (seq->type == SEQ_TYPE_IMAGE) { - BLI_snprintf(name, - PROXY_MAXFILE, - "%s/images/%d/%s_proxy%s", - dir, - proxy_size_number, - BKE_sequencer_give_stripelem(seq, cfra)->name, - suffix); - frameno = 1; - } - else { - frameno = (int)BKE_sequencer_give_stripelem_index(seq, cfra) + seq->anim_startofs; - BLI_snprintf(name, PROXY_MAXFILE, "%s/proxy_misc/%d/####%s", dir, proxy_size_number, suffix); - } - + BLI_snprintf(name, + PROXY_MAXFILE, + "%s/images/%d/%s_proxy%s", + dir, + proxy_size_number, + BKE_sequencer_give_stripelem(seq, cfra)->name, + suffix); BLI_path_abs(name, BKE_main_blendfile_path_from_global()); - BLI_path_frame(name, frameno, 0); - strcat(name, ".jpg"); return true; @@ -2283,6 +2242,15 @@ void BKE_sequencer_proxy_set(struct Sequence *seq, bool value) } } +static bool seq_can_use_proxy(Sequence *seq, IMB_Proxy_Size psize) +{ + if (seq->strip->proxy == NULL) { + return false; + } + short size_flags = seq->strip->proxy->build_size_flags; + return (seq->flag & SEQ_USE_PROXY) != 0 && psize != IMB_PROXY_NONE && (size_flags & psize) != 0; +} + /*********************** color balance *************************/ static StripColorBalance calc_cb(StripColorBalance *cb_) @@ -2342,7 +2310,9 @@ MINLINE float color_balance_fl( x = 0.f; } - return powf(x, gamma) * mul; + x = powf(x, gamma) * mul; + CLAMP(x, FLT_MIN, FLT_MAX); + return x; } static void make_cb_table_float(float lift, float gain, float gamma, float *table, float mul) @@ -2977,9 +2947,9 @@ static ImBuf *seq_render_effect_strip_impl(const SeqRenderData *context, case EARLY_DO_EFFECT: for (i = 0; i < 3; i++) { /* Speed effect requires time remapping of cfra for input(s). */ - if (input[1] && seq->type == SEQ_TYPE_SPEED) { + if (input[0] && seq->type == SEQ_TYPE_SPEED) { float target_frame = BKE_sequencer_speed_effect_target_frame_get(context, seq, cfra, i); - ibuf[i] = seq_render_strip(context, state, input[i], target_frame); + ibuf[i] = seq_render_strip(context, state, input[0], target_frame); } else { /* Other effects. */ if (input[i]) { @@ -2988,7 +2958,7 @@ static ImBuf *seq_render_effect_strip_impl(const SeqRenderData *context, } } - if (ibuf[0] && ibuf[1]) { + if (ibuf[0] && (ibuf[1] || BKE_sequence_effect_get_num_inputs(seq->type) == 1)) { if (sh.multithreaded) { out = BKE_sequencer_effect_execute_threaded( &sh, context, seq, cfra, fac, facf, ibuf[0], ibuf[1], ibuf[2]); @@ -3021,97 +2991,114 @@ static ImBuf *seq_render_effect_strip_impl(const SeqRenderData *context, return out; } -static ImBuf *seq_render_image_strip(const SeqRenderData *context, - Sequence *seq, - float UNUSED(nr), - float cfra) +/* Render individual view for multiview or single (default view) for monoview. */ +static ImBuf *seq_render_image_strip_view(const SeqRenderData *context, + Sequence *seq, + char *name, + char *prefix, + const char *ext, + int view_id) { - ImBuf *ibuf = NULL; - char name[FILE_MAX]; - bool is_multiview = (seq->flag & SEQ_USE_VIEWS) != 0 && - (context->scene->r.scemode & R_MULTIVIEW) != 0; - StripElem *s_elem = BKE_sequencer_give_stripelem(seq, cfra); - int flag; - if (s_elem) { - BLI_join_dirfile(name, sizeof(name), seq->strip->dir, s_elem->name); - BLI_path_abs(name, BKE_main_blendfile_path_from_global()); - } + ImBuf *ibuf = NULL; - flag = IB_rect | IB_metadata; + int flag = IB_rect | IB_metadata; if (seq->alpha_mode == SEQ_ALPHA_PREMUL) { flag |= IB_alphamode_premul; } - if (!s_elem) { - /* don't do anything */ + if (prefix[0] == '\0') { + ibuf = IMB_loadiffname(name, flag, seq->strip->colorspace_settings.name); + } + else { + char str[FILE_MAX]; + seq_multiview_name(context->scene, view_id, prefix, ext, str, FILE_MAX); + ibuf = IMB_loadiffname(str, flag, seq->strip->colorspace_settings.name); } - else if (is_multiview) { - const int totfiles = seq_num_files(context->scene, seq->views_format, true); - int totviews; - struct ImBuf **ibufs_arr; - char prefix[FILE_MAX]; - const char *ext = NULL; - if (totfiles > 1) { - BKE_scene_multiview_view_prefix_get(context->scene, name, prefix, &ext); - if (prefix[0] == '\0') { - goto monoview_image; - } - } - else { - prefix[0] = '\0'; + if (ibuf == NULL) { + return NULL; + } + + /* We don't need both (speed reasons)! */ + if (ibuf->rect_float != NULL && ibuf->rect != NULL) { + imb_freerectImBuf(ibuf); + } + + /* All sequencer color is done in SRGB space, linear gives odd crossfades. */ + BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibuf, false); + + return ibuf; +} + +static bool seq_image_strip_is_multiview_render( + Scene *scene, Sequence *seq, int totfiles, char *name, char *r_prefix, const char *r_ext) +{ + if (totfiles > 1) { + BKE_scene_multiview_view_prefix_get(scene, name, r_prefix, &r_ext); + if (r_prefix[0] == '\0') { + return false; } + } + else { + r_prefix[0] = '\0'; + } - totviews = BKE_scene_multiview_num_views_get(&context->scene->r); - ibufs_arr = MEM_callocN(sizeof(ImBuf *) * totviews, "Sequence Image Views Imbufs"); + return (seq->flag & SEQ_USE_VIEWS) != 0 && (scene->r.scemode & R_MULTIVIEW) != 0; +} - for (int view_id = 0; view_id < totfiles; view_id++) { +static ImBuf *seq_render_image_strip(const SeqRenderData *context, + Sequence *seq, + float UNUSED(nr), + float cfra) +{ + char name[FILE_MAX]; + const char *ext = NULL; + char prefix[FILE_MAX]; + ImBuf *ibuf = NULL; - if (prefix[0] == '\0') { - ibufs_arr[view_id] = IMB_loadiffname(name, flag, seq->strip->colorspace_settings.name); - } - else { - char str[FILE_MAX]; - seq_multiview_name(context->scene, view_id, prefix, ext, str, FILE_MAX); - ibufs_arr[view_id] = IMB_loadiffname(str, flag, seq->strip->colorspace_settings.name); - } + StripElem *s_elem = BKE_sequencer_give_stripelem(seq, cfra); + if (s_elem == NULL) { + return NULL; + } - if (ibufs_arr[view_id]) { - /* we don't need both (speed reasons)! */ - if (ibufs_arr[view_id]->rect_float && ibufs_arr[view_id]->rect) { - imb_freerectImBuf(ibufs_arr[view_id]); - } - } + BLI_join_dirfile(name, sizeof(name), seq->strip->dir, s_elem->name); + BLI_path_abs(name, BKE_main_blendfile_path_from_global()); + + const int totfiles = seq_num_files(context->scene, seq->views_format, true); + bool is_multiview_render = seq_image_strip_is_multiview_render( + context->scene, seq, totfiles, name, prefix, ext); + + if (is_multiview_render) { + int totviews = BKE_scene_multiview_num_views_get(&context->scene->r); + ImBuf **ibufs_arr = MEM_callocN(sizeof(ImBuf *) * totviews, "Sequence Image Views Imbufs"); + + for (int view_id = 0; view_id < totfiles; view_id++) { + ibufs_arr[view_id] = seq_render_image_strip_view(context, seq, name, prefix, ext, view_id); } - if (seq->views_format == R_IMF_VIEWS_STEREO_3D && ibufs_arr[0]) { + if (ibufs_arr[0] == NULL) { + return NULL; + } + + if (seq->views_format == R_IMF_VIEWS_STEREO_3D) { IMB_ImBufFromStereo3d(seq->stereo3d_format, ibufs_arr[0], &ibufs_arr[0], &ibufs_arr[1]); } for (int view_id = 0; view_id < totviews; view_id++) { - if (ibufs_arr[view_id]) { - SeqRenderData localcontext = *context; - localcontext.view_id = view_id; - - /* all sequencer color is done in SRGB space, linear gives odd crossfades */ - BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibufs_arr[view_id], false); + SeqRenderData localcontext = *context; + localcontext.view_id = view_id; - if (view_id != context->view_id) { - ibufs_arr[view_id] = seq_render_preprocess_ibuf( - &localcontext, seq, ibufs_arr[view_id], cfra, clock(), true, false, false); - } + if (view_id != context->view_id) { + ibufs_arr[view_id] = seq_render_preprocess_ibuf( + &localcontext, seq, ibufs_arr[view_id], cfra, clock(), true, false, false); } } - /* return the original requested ImBuf */ + /* Return the original requested ImBuf. */ ibuf = ibufs_arr[context->view_id]; - if (ibuf) { - s_elem->orig_width = ibufs_arr[0]->x; - s_elem->orig_height = ibufs_arr[0]->y; - } - /* "remove" the others (decrease their refcount) */ + /* Remove the others (decrease their refcount). */ for (int view_id = 0; view_id < totviews; view_id++) { if (ibufs_arr[view_id] != ibuf) { IMB_freeImBuf(ibufs_arr[view_id]); @@ -3121,116 +3108,111 @@ static ImBuf *seq_render_image_strip(const SeqRenderData *context, MEM_freeN(ibufs_arr); } else { - monoview_image: - if ((ibuf = IMB_loadiffname(name, flag, seq->strip->colorspace_settings.name))) { - /* we don't need both (speed reasons)! */ - if (ibuf->rect_float && ibuf->rect) { - imb_freerectImBuf(ibuf); - } - - /* all sequencer color is done in SRGB space, linear gives odd crossfades */ - BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibuf, false); + ibuf = seq_render_image_strip_view(context, seq, name, prefix, ext, context->view_id); + } - s_elem->orig_width = ibuf->x; - s_elem->orig_height = ibuf->y; - } + if (ibuf == NULL) { + return NULL; } + s_elem->orig_width = ibuf->x; + s_elem->orig_height = ibuf->y; + return ibuf; } -static ImBuf *seq_render_movie_strip(const SeqRenderData *context, - Sequence *seq, - float nr, - float cfra) +/** + * Render individual view for multi-view or single (default view) for mono-view. + */ +static ImBuf *seq_render_movie_strip_view(const SeqRenderData *context, + Sequence *seq, + float nr, + StripAnim *sanim) { ImBuf *ibuf = NULL; - StripAnim *sanim; + IMB_Proxy_Size psize = seq_rendersize_to_proxysize(context->preview_render_size); - bool is_multiview = (seq->flag & SEQ_USE_VIEWS) != 0 && - (context->scene->r.scemode & R_MULTIVIEW) != 0; + IMB_anim_set_preseek(sanim->anim, seq->anim_preseek); - IMB_Proxy_Size psize = seq_rendersize_to_proxysize(context->preview_render_size); + if (seq_can_use_proxy(seq, psize)) { + ibuf = IMB_anim_absolute(sanim->anim, + nr + seq->anim_startofs, + seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN, + psize); + } - if ((seq->flag & SEQ_USE_PROXY) == 0) { - psize = IMB_PROXY_NONE; + /* Fetching for requested proxy size failed, try fetching the original instead. */ + if (ibuf == NULL) { + ibuf = IMB_anim_absolute(sanim->anim, + nr + seq->anim_startofs, + seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN, + IMB_PROXY_NONE); + } + if (ibuf == NULL) { + return NULL; } - /* load all the videos */ - seq_open_anim_file(context->scene, seq, false); - if (is_multiview) { - ImBuf **ibuf_arr; - const int totfiles = seq_num_files(context->scene, seq->views_format, true); - int totviews; - int ibuf_view_id; + BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibuf, false); - if (totfiles != BLI_listbase_count_at_most(&seq->anims, totfiles + 1)) { - goto monoview_movie; - } + /* We don't need both (speed reasons)! */ + if (ibuf->rect_float != NULL && ibuf->rect != NULL) { + imb_freerectImBuf(ibuf); + } + + return ibuf; +} + +static ImBuf *seq_render_movie_strip(const SeqRenderData *context, + Sequence *seq, + float nr, + float cfra) +{ + /* Load all the videos. */ + seq_open_anim_file(context->scene, seq, false); - totviews = BKE_scene_multiview_num_views_get(&context->scene->r); + ImBuf *ibuf = NULL; + StripAnim *sanim = seq->anims.first; + const int totfiles = seq_num_files(context->scene, seq->views_format, true); + bool is_multiview_render = (seq->flag & SEQ_USE_VIEWS) != 0 && + (context->scene->r.scemode & R_MULTIVIEW) != 0 && + BLI_listbase_count_at_most(&seq->anims, totfiles + 1) == totfiles; + + if (is_multiview_render) { + ImBuf **ibuf_arr; + int totviews = BKE_scene_multiview_num_views_get(&context->scene->r); ibuf_arr = MEM_callocN(sizeof(ImBuf *) * totviews, "Sequence Image Views Imbufs"); + int ibuf_view_id; for (ibuf_view_id = 0, sanim = seq->anims.first; sanim; sanim = sanim->next, ibuf_view_id++) { if (sanim->anim) { - IMB_anim_set_preseek(sanim->anim, seq->anim_preseek); - - ibuf_arr[ibuf_view_id] = IMB_anim_absolute(sanim->anim, - nr + seq->anim_startofs, - seq->strip->proxy ? seq->strip->proxy->tc : - IMB_TC_RECORD_RUN, - psize); - - /* fetching for requested proxy size failed, try fetching the original instead */ - if (!ibuf_arr[ibuf_view_id] && psize != IMB_PROXY_NONE) { - ibuf_arr[ibuf_view_id] = IMB_anim_absolute(sanim->anim, - nr + seq->anim_startofs, - seq->strip->proxy ? seq->strip->proxy->tc : - IMB_TC_RECORD_RUN, - IMB_PROXY_NONE); - } - if (ibuf_arr[ibuf_view_id]) { - /* we don't need both (speed reasons)! */ - if (ibuf_arr[ibuf_view_id]->rect_float && ibuf_arr[ibuf_view_id]->rect) { - imb_freerectImBuf(ibuf_arr[ibuf_view_id]); - } - } + ibuf_arr[ibuf_view_id] = seq_render_movie_strip_view(context, seq, nr, sanim); } } if (seq->views_format == R_IMF_VIEWS_STEREO_3D) { - if (ibuf_arr[0]) { - IMB_ImBufFromStereo3d(seq->stereo3d_format, ibuf_arr[0], &ibuf_arr[0], &ibuf_arr[1]); - } - else { - /* probably proxy hasn't been created yet */ + if (ibuf_arr[0] == NULL) { + /* Probably proxy hasn't been created yet. */ MEM_freeN(ibuf_arr); return NULL; } + + IMB_ImBufFromStereo3d(seq->stereo3d_format, ibuf_arr[0], &ibuf_arr[0], &ibuf_arr[1]); } for (int view_id = 0; view_id < totviews; view_id++) { SeqRenderData localcontext = *context; localcontext.view_id = view_id; - if (ibuf_arr[view_id]) { - /* all sequencer color is done in SRGB space, linear gives odd crossfades */ - BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibuf_arr[view_id], false); - } if (view_id != context->view_id) { ibuf_arr[view_id] = seq_render_preprocess_ibuf( &localcontext, seq, ibuf_arr[view_id], cfra, clock(), true, false, false); } } - /* return the original requested ImBuf */ + /* Return the original requested ImBuf. */ ibuf = ibuf_arr[context->view_id]; - if (ibuf) { - seq->strip->stripdata->orig_width = ibuf->x; - seq->strip->stripdata->orig_height = ibuf->y; - } - /* "remove" the others (decrease their refcount) */ + /* Remove the others (decrease their refcount). */ for (int view_id = 0; view_id < totviews; view_id++) { if (ibuf_arr[view_id] != ibuf) { IMB_freeImBuf(ibuf_arr[view_id]); @@ -3240,36 +3222,16 @@ static ImBuf *seq_render_movie_strip(const SeqRenderData *context, MEM_freeN(ibuf_arr); } else { - monoview_movie: - sanim = seq->anims.first; - if (sanim && sanim->anim) { - IMB_anim_set_preseek(sanim->anim, seq->anim_preseek); - - ibuf = IMB_anim_absolute(sanim->anim, - nr + seq->anim_startofs, - seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN, - psize); - - /* fetching for requested proxy size failed, try fetching the original instead */ - if (!ibuf && psize != IMB_PROXY_NONE) { - ibuf = IMB_anim_absolute(sanim->anim, - nr + seq->anim_startofs, - seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN, - IMB_PROXY_NONE); - } - if (ibuf) { - BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibuf, false); - - /* we don't need both (speed reasons)! */ - if (ibuf->rect_float && ibuf->rect) { - imb_freerectImBuf(ibuf); - } + ibuf = seq_render_movie_strip_view(context, seq, nr, sanim); + } - seq->strip->stripdata->orig_width = ibuf->x; - seq->strip->stripdata->orig_height = ibuf->y; - } - } + if (ibuf == NULL) { + return NULL; } + + seq->strip->stripdata->orig_width = ibuf->x; + seq->strip->stripdata->orig_height = ibuf->y; + return ibuf; } @@ -5052,7 +5014,7 @@ int BKE_sequence_swap(Sequence *seq_a, Sequence *seq_b, const char **error_str) return 1; } -/* prefix + [" + escaped_name + "] + \0 */ +/* r_prefix + [" + escaped_name + "] + \0 */ #define SEQ_RNAPATH_MAXSTR ((30 + 2 + (SEQ_NAME_MAXSTR * 2) + 2) + 1) static size_t sequencer_rna_path_prefix(char str[SEQ_RNAPATH_MAXSTR], const char *name) diff --git a/source/blender/blenlib/BLI_math_geom.h b/source/blender/blenlib/BLI_math_geom.h index 563bcad5d14..f51486c5e7b 100644 --- a/source/blender/blenlib/BLI_math_geom.h +++ b/source/blender/blenlib/BLI_math_geom.h @@ -190,6 +190,10 @@ float dist_squared_to_projected_aabb_simple(const float projmat[4][4], const float bbmin[3], const float bbmax[3]); +float closest_to_ray_v3(float r_close[3], + const float p[3], + const float ray_orig[3], + const float ray_dir[3]); float closest_to_line_v2(float r_close[2], const float p[2], const float l1[2], const float l2[2]); double closest_to_line_v2_db(double r_close[2], const double p[2], diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c index e7c1fc8c2d9..d3dc4729617 100644 --- a/source/blender/blenlib/intern/math_geom.c +++ b/source/blender/blenlib/intern/math_geom.c @@ -3248,19 +3248,27 @@ bool isect_ray_aabb_v3_simple(const float orig[3], } } -/* find closest point to p on line through (l1, l2) and return lambda, - * where (0 <= lambda <= 1) when cp is in the line segment (l1, l2) +float closest_to_ray_v3(float r_close[3], + const float p[3], + const float ray_orig[3], + const float ray_dir[3]) +{ + float h[3], lambda; + sub_v3_v3v3(h, p, ray_orig); + lambda = dot_v3v3(ray_dir, h) / dot_v3v3(ray_dir, ray_dir); + madd_v3_v3v3fl(r_close, ray_orig, ray_dir, lambda); + return lambda; +} + +/** + * Find closest point to p on line through (l1, l2) and return lambda, + * where (0 <= lambda <= 1) when cp is in the line segment (l1, l2). */ float closest_to_line_v3(float r_close[3], const float p[3], const float l1[3], const float l2[3]) { - float h[3], u[3], lambda; + float u[3]; sub_v3_v3v3(u, l2, l1); - sub_v3_v3v3(h, p, l1); - lambda = dot_v3v3(u, h) / dot_v3v3(u, u); - r_close[0] = l1[0] + u[0] * lambda; - r_close[1] = l1[1] + u[1] * lambda; - r_close[2] = l1[2] + u[2] * lambda; - return lambda; + return closest_to_ray_v3(r_close, p, l1, u); } float closest_to_line_v2(float r_close[2], const float p[2], const float l1[2], const float l2[2]) diff --git a/source/blender/blenlib/intern/math_matrix.c b/source/blender/blenlib/intern/math_matrix.c index 9e398239bc7..bc06882b67a 100644 --- a/source/blender/blenlib/intern/math_matrix.c +++ b/source/blender/blenlib/intern/math_matrix.c @@ -2388,6 +2388,22 @@ void interp_m3_m3m3(float R[3][3], const float A[3][3], const float B[3][3], con mat3_polar_decompose(A, U_A, P_A); mat3_polar_decompose(B, U_B, P_B); + /* Quaterions cannot represent an axis flip. If such a singularity is detected, choose a + * different decomposition of the matrix that still satisfies A = U_A * P_A but which has a + * positive determinant and thus no axis flips. This resolves T77154. + * + * Note that a flip of two axes is just a rotation of 180 degrees around the third axis, and + * three flipped axes are just an 180 degree rotation + a single axis flip. It is thus sufficient + * to solve this problem for single axis flips. */ + if (determinant_m3_array(U_A) < 0) { + mul_m3_fl(U_A, -1.0f); + mul_m3_fl(P_A, -1.0f); + } + if (determinant_m3_array(U_B) < 0) { + mul_m3_fl(U_B, -1.0f); + mul_m3_fl(P_B, -1.0f); + } + mat3_to_quat(quat_A, U_A); mat3_to_quat(quat_B, U_B); interp_qt_qtqt(quat, quat_A, quat_B, t); diff --git a/source/blender/blenloader/BLO_writefile.h b/source/blender/blenloader/BLO_writefile.h index d83abf7f9ed..18783474392 100644 --- a/source/blender/blenloader/BLO_writefile.h +++ b/source/blender/blenloader/BLO_writefile.h @@ -30,11 +30,32 @@ struct Main; struct MemFile; struct ReportList; +/** + * Adjust paths when saving (kept unless #G_FILE_SAVE_COPY is set). + */ +typedef enum eBLO_WritePathRemap { + /** No path manipulation. */ + BLO_WRITE_PATH_REMAP_NONE = 1, + /** Remap existing relative paths (default). */ + BLO_WRITE_PATH_REMAP_RELATIVE = 2, + /** Remap paths making all paths relative to the new location. */ + BLO_WRITE_PATH_REMAP_RELATIVE_ALL = 3, + /** Make all paths absolute. */ + BLO_WRITE_PATH_REMAP_ABSOLUTE = 4, +} eBLO_WritePathRemap; + +extern bool BLO_write_file_ex(struct Main *mainvar, + const char *filepath, + const int write_flags, + struct ReportList *reports, + /* Extra arguments. */ + eBLO_WritePathRemap remap_mode, + const struct BlendThumbnail *thumb); extern bool BLO_write_file(struct Main *mainvar, const char *filepath, - int write_flags, - struct ReportList *reports, - const struct BlendThumbnail *thumb); + const int write_flags, + struct ReportList *reports); + extern bool BLO_write_file_mem(struct Main *mainvar, struct MemFile *compare, struct MemFile *current, diff --git a/source/blender/blenloader/intern/versioning_250.c b/source/blender/blenloader/intern/versioning_250.c index eaeef0d52c1..2c3b047af46 100644 --- a/source/blender/blenloader/intern/versioning_250.c +++ b/source/blender/blenloader/intern/versioning_250.c @@ -308,7 +308,7 @@ static void area_add_window_regions(ScrArea *area, SpaceLink *sl, ListBase *lb) region->v2d.tot.ymax = 0.0f; region->v2d.scroll |= (V2D_SCROLL_BOTTOM | V2D_SCROLL_HORIZONTAL_HANDLES); - region->v2d.scroll |= (V2D_SCROLL_RIGHT); + region->v2d.scroll |= V2D_SCROLL_RIGHT; region->v2d.align = V2D_ALIGN_NO_POS_Y; region->v2d.flag |= V2D_VIEWSYNC_AREA_VERTICAL; break; @@ -334,7 +334,7 @@ static void area_add_window_regions(ScrArea *area, SpaceLink *sl, ListBase *lb) region->v2d.minzoom = 0.01f; region->v2d.maxzoom = 50; region->v2d.scroll = (V2D_SCROLL_BOTTOM | V2D_SCROLL_HORIZONTAL_HANDLES); - region->v2d.scroll |= (V2D_SCROLL_RIGHT); + region->v2d.scroll |= V2D_SCROLL_RIGHT; region->v2d.keepzoom = V2D_LOCKZOOM_Y; region->v2d.align = V2D_ALIGN_NO_POS_Y; region->v2d.flag = V2D_VIEWSYNC_AREA_VERTICAL; diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 7bba23c28d7..f053739b710 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -4407,11 +4407,13 @@ static bool do_history(const char *name, ReportList *reports) /** * \return Success. */ -bool BLO_write_file(Main *mainvar, - const char *filepath, - int write_flags, - ReportList *reports, - const BlendThumbnail *thumb) +bool BLO_write_file_ex(Main *mainvar, + const char *filepath, + const int write_flags, + ReportList *reports, + /* Extra arguments. */ + eBLO_WritePathRemap remap_mode, + const BlendThumbnail *thumb) { char tempname[FILE_MAX + 1]; eWriteWrapType ww_type; @@ -4446,7 +4448,15 @@ bool BLO_write_file(Main *mainvar, } /* Remapping of relative paths to new file location. */ - if (write_flags & G_FILE_RELATIVE_REMAP) { + if (remap_mode != BLO_WRITE_PATH_REMAP_NONE) { + + if (remap_mode == BLO_WRITE_PATH_REMAP_RELATIVE) { + /* Make all relative as none of the existing paths can be relative in an unsaved document. */ + if (G.relbase_valid == false) { + remap_mode = BLO_WRITE_PATH_REMAP_RELATIVE_ALL; + } + } + char dir_src[FILE_MAX]; char dir_dst[FILE_MAX]; BLI_split_dir_part(mainvar->name, dir_src, sizeof(dir_src)); @@ -4456,23 +4466,43 @@ bool BLO_write_file(Main *mainvar, BLI_path_normalize(mainvar->name, dir_dst); BLI_path_normalize(mainvar->name, dir_src); - if (G.relbase_valid && (BLI_path_cmp(dir_dst, dir_src) == 0)) { - /* Saved to same path. Nothing to do. */ - write_flags &= ~G_FILE_RELATIVE_REMAP; + /* Only for relative, not relative-all, as this means making existing paths relative. */ + if (remap_mode == BLO_WRITE_PATH_REMAP_RELATIVE) { + if (G.relbase_valid && (BLI_path_cmp(dir_dst, dir_src) == 0)) { + /* Saved to same path. Nothing to do. */ + remap_mode = BLO_WRITE_PATH_REMAP_NONE; + } } - else { + else if (remap_mode == BLO_WRITE_PATH_REMAP_ABSOLUTE) { + if (G.relbase_valid == false) { + /* Unsaved, all paths are absolute.Even if the user manages to set a relative path, + * there is no base-path that can be used to make it absolute. */ + remap_mode = BLO_WRITE_PATH_REMAP_NONE; + } + } + + if (remap_mode != BLO_WRITE_PATH_REMAP_NONE) { /* Check if we need to backup and restore paths. */ if (UNLIKELY(G_FILE_SAVE_COPY & write_flags)) { path_list_backup = BKE_bpath_list_backup(mainvar, path_list_flag); } - if (G.relbase_valid) { - /* Saved, make relative paths relative to new location (if possible). */ - BKE_bpath_relative_rebase(mainvar, dir_src, dir_dst, NULL); - } - else { - /* Unsaved, make all relative. */ - BKE_bpath_relative_convert(mainvar, dir_dst, NULL); + switch (remap_mode) { + case BLO_WRITE_PATH_REMAP_RELATIVE: + /* Saved, make relative paths relative to new location (if possible). */ + BKE_bpath_relative_rebase(mainvar, dir_src, dir_dst, NULL); + break; + case BLO_WRITE_PATH_REMAP_RELATIVE_ALL: + /* Make all relative (when requested or unsaved). */ + BKE_bpath_relative_convert(mainvar, dir_dst, NULL); + break; + case BLO_WRITE_PATH_REMAP_ABSOLUTE: + /* Make all absolute (when requested or unsaved). */ + BKE_bpath_absolute_convert(mainvar, dir_src, NULL); + break; + case BLO_WRITE_PATH_REMAP_NONE: + BLI_assert(0); /* Unreachable. */ + break; } } } @@ -4517,6 +4547,15 @@ bool BLO_write_file(Main *mainvar, return 1; } +bool BLO_write_file(Main *mainvar, + const char *filepath, + const int write_flags, + ReportList *reports) +{ + return BLO_write_file_ex( + mainvar, filepath, write_flags, reports, BLO_WRITE_PATH_REMAP_NONE, NULL); +} + /** * \return Success. */ diff --git a/source/blender/bmesh/intern/bmesh_polygon_edgenet.c b/source/blender/bmesh/intern/bmesh_polygon_edgenet.c index 284f5265b90..a738b2139c4 100644 --- a/source/blender/bmesh/intern/bmesh_polygon_edgenet.c +++ b/source/blender/bmesh/intern/bmesh_polygon_edgenet.c @@ -1242,7 +1242,8 @@ bool BM_face_split_edgenet_connect_islands(BMesh *bm, uint *r_edge_net_new_len) { /* -------------------------------------------------------------------- */ - /* This function has 2 main parts. + /** + * This function has 2 main parts. * * - Check if there are any holes. * - Connect the holes with edges (if any are found). diff --git a/source/blender/bmesh/operators/bmo_connect.c b/source/blender/bmesh/operators/bmo_connect.c index 9969d31a4a5..b52c26a65f0 100644 --- a/source/blender/bmesh/operators/bmo_connect.c +++ b/source/blender/bmesh/operators/bmo_connect.c @@ -50,6 +50,7 @@ static int bm_face_connect_verts(BMesh *bm, BMFace *f, const bool check_degenera BMLoop *l_tag_prev = NULL, *l_tag_first = NULL; BMLoop *l_iter, *l_first; uint i; + int result = 1; STACK_INIT(loops_split, pair_split_max); STACK_INIT(verts_pair, pair_split_max); @@ -109,30 +110,60 @@ static int bm_face_connect_verts(BMesh *bm, BMFace *f, const bool check_degenera v_pair[1] = loops_split[i][1]->v; } + /* Clear and re-use to store duplicate faces, to remove after splitting is finished. */ + STACK_CLEAR(loops_split); + for (i = 0; i < STACK_SIZE(verts_pair); i++) { BMFace *f_new; BMLoop *l_new; - BMLoop *l_a, *l_b; - - if ((l_a = BM_face_vert_share_loop(f, verts_pair[i][0])) && - (l_b = BM_face_vert_share_loop(f, verts_pair[i][1]))) { - f_new = BM_face_split(bm, f, l_a, l_b, &l_new, NULL, false); + BMLoop *l_pair[2]; + + /* Note that duplicate edges in this case is very unlikely but it can happen, see T70287. */ + bool edge_exists = (BM_edge_exists(verts_pair[i][0], verts_pair[i][1]) != NULL); + if ((l_pair[0] = BM_face_vert_share_loop(f, verts_pair[i][0])) && + (l_pair[1] = BM_face_vert_share_loop(f, verts_pair[i][1]))) { + f_new = BM_face_split(bm, f, l_pair[0], l_pair[1], &l_new, NULL, edge_exists); + + /* Check if duplicate faces have been created, store the loops for removal in this case. + * Note that this matches how triangulate works (newly created duplicates get removed). */ + if (UNLIKELY(edge_exists)) { + BMLoop **l_pair_deferred_remove = NULL; + for (int j = 0; j < 2; j++) { + if (BM_face_find_double(l_pair[j]->f)) { + if (l_pair_deferred_remove == NULL) { + l_pair_deferred_remove = STACK_PUSH_RET(loops_split); + l_pair_deferred_remove[0] = NULL; + l_pair_deferred_remove[1] = NULL; + } + l_pair_deferred_remove[j] = l_pair[j]; + } + } + } } else { f_new = NULL; l_new = NULL; } - f = f_new; - if (!l_new || !f_new) { - return -1; + result = -1; + break; } + + f = f_new; // BMO_face_flag_enable(bm, f_new, FACE_NEW); BMO_edge_flag_enable(bm, l_new->e, EDGE_OUT); } - return 1; + for (i = 0; i < STACK_SIZE(loops_split); i++) { + for (int j = 0; j < 2; j++) { + if (loops_split[i][j] != NULL) { + BM_face_kill(bm, loops_split[i][j]->f); + } + } + } + + return result; } void bmo_connect_verts_exec(BMesh *bm, BMOperator *op) diff --git a/source/blender/depsgraph/intern/builder/deg_builder_cache.cc b/source/blender/depsgraph/intern/builder/deg_builder_cache.cc index 104676f7ab6..62d9118cbc0 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_cache.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_cache.cc @@ -155,21 +155,16 @@ DepsgraphBuilderCache::DepsgraphBuilderCache() DepsgraphBuilderCache::~DepsgraphBuilderCache() { - for (AnimatedPropertyStorageMap::value_type &iter : animated_property_storage_map_) { - AnimatedPropertyStorage *animated_property_storage = iter.second; + for (AnimatedPropertyStorage *animated_property_storage : + animated_property_storage_map_.values()) { OBJECT_GUARDED_DELETE(animated_property_storage, AnimatedPropertyStorage); } } AnimatedPropertyStorage *DepsgraphBuilderCache::ensureAnimatedPropertyStorage(ID *id) { - AnimatedPropertyStorageMap::iterator it = animated_property_storage_map_.find(id); - if (it != animated_property_storage_map_.end()) { - return it->second; - } - AnimatedPropertyStorage *animated_property_storage = OBJECT_GUARDED_NEW(AnimatedPropertyStorage); - animated_property_storage_map_.insert(make_pair(id, animated_property_storage)); - return animated_property_storage; + return animated_property_storage_map_.lookup_or_add_cb( + id, []() { return OBJECT_GUARDED_NEW(AnimatedPropertyStorage); }); } AnimatedPropertyStorage *DepsgraphBuilderCache::ensureInitializedAnimatedPropertyStorage(ID *id) diff --git a/source/blender/depsgraph/intern/builder/deg_builder_cache.h b/source/blender/depsgraph/intern/builder/deg_builder_cache.h index bb4e1f5c96a..531d1664281 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_cache.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_cache.h @@ -45,8 +45,6 @@ class AnimatedPropertyID { AnimatedPropertyID(ID *id, StructRNA *type, void *data, const char *property_name); uint32_t hash() const; - - bool operator<(const AnimatedPropertyID &other) const; friend bool operator==(const AnimatedPropertyID &a, const AnimatedPropertyID &b); /* Corresponds to PointerRNA.data. */ @@ -73,8 +71,6 @@ class AnimatedPropertyStorage { Set<AnimatedPropertyID> animated_properties_set; }; -typedef map<ID *, AnimatedPropertyStorage *> AnimatedPropertyStorageMap; - /* Cached data which can be re-used by multiple builders. */ class DepsgraphBuilderCache { public: @@ -100,7 +96,7 @@ class DepsgraphBuilderCache { return animated_property_storage->isPropertyAnimated(args...); } - AnimatedPropertyStorageMap animated_property_storage_map_; + Map<ID *, AnimatedPropertyStorage *> animated_property_storage_map_; }; } // namespace DEG diff --git a/source/blender/depsgraph/intern/builder/deg_builder_map.cc b/source/blender/depsgraph/intern/builder/deg_builder_map.cc index 4bca4f037b0..d0bebd8b10a 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_map.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_map.cc @@ -42,33 +42,20 @@ bool BuilderMap::checkIsBuilt(ID *id, int tag) const void BuilderMap::tagBuild(ID *id, int tag) { - IDTagMap::iterator it = id_tags_.find(id); - if (it == id_tags_.end()) { - id_tags_.insert(make_pair(id, tag)); - return; - } - it->second |= tag; + id_tags_.lookup_or_add(id, 0) |= tag; } bool BuilderMap::checkIsBuiltAndTag(ID *id, int tag) { - IDTagMap::iterator it = id_tags_.find(id); - if (it == id_tags_.end()) { - id_tags_.insert(make_pair(id, tag)); - return false; - } - const bool result = (it->second & tag) == tag; - it->second |= tag; + int &id_tag = id_tags_.lookup_or_add(id, 0); + const bool result = (id_tag & tag) == tag; + id_tag |= tag; return result; } int BuilderMap::getIDTag(ID *id) const { - IDTagMap::const_iterator it = id_tags_.find(id); - if (it == id_tags_.end()) { - return 0; - } - return it->second; + return id_tags_.lookup_default(id, 0); } } // namespace DEG diff --git a/source/blender/depsgraph/intern/builder/deg_builder_map.h b/source/blender/depsgraph/intern/builder/deg_builder_map.h index 65b493e2467..54561a0c3d2 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_map.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_map.h @@ -75,8 +75,7 @@ class BuilderMap { protected: int getIDTag(ID *id) const; - typedef map<ID *, int> IDTagMap; - IDTagMap id_tags_; + Map<ID *, int> id_tags_; }; } // namespace DEG diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index 92dff751b8a..e21a83485ad 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -2887,9 +2887,7 @@ void DepsgraphRelationBuilder::build_driver_relations(IDNode *id_node) } // Mapping from RNA prefix -> set of driver evaluation nodes: - typedef Vector<Node *> DriverGroup; - typedef map<string, DriverGroup> DriverGroupMap; - DriverGroupMap driver_groups; + Map<string, Vector<Node *>> driver_groups; LISTBASE_FOREACH (FCurve *, fcu, &adt->drivers) { if (fcu->rna_path == nullptr) { @@ -2897,33 +2895,33 @@ void DepsgraphRelationBuilder::build_driver_relations(IDNode *id_node) } // Get the RNA path except the part after the last dot. char *last_dot = strrchr(fcu->rna_path, '.'); - string rna_prefix; + StringRef rna_prefix; if (last_dot != nullptr) { - rna_prefix = string(fcu->rna_path, last_dot); + rna_prefix = StringRef(fcu->rna_path, last_dot); } // Insert this driver node into the group belonging to the RNA prefix. OperationKey driver_key( id_orig, NodeType::PARAMETERS, OperationCode::DRIVER, fcu->rna_path, fcu->array_index); Node *node_driver = get_node(driver_key); - driver_groups[rna_prefix].append(node_driver); + driver_groups.lookup_or_add_default_as(rna_prefix).append(node_driver); } - for (pair<string, DriverGroup> prefix_group : driver_groups) { + for (Span<Node *> prefix_group : driver_groups.values()) { // For each node in the driver group, try to connect it to another node // in the same group without creating any cycles. - int num_drivers = prefix_group.second.size(); + int num_drivers = prefix_group.size(); if (num_drivers < 2) { // A relation requires two drivers. continue; } for (int from_index = 0; from_index < num_drivers; ++from_index) { - Node *op_from = prefix_group.second[from_index]; + Node *op_from = prefix_group[from_index]; // Start by trying the next node in the group. for (int to_offset = 1; to_offset < num_drivers; ++to_offset) { int to_index = (from_index + to_offset) % num_drivers; - Node *op_to = prefix_group.second[to_index]; + Node *op_to = prefix_group[to_index]; // Investigate whether this relation would create a dependency cycle. // Example graph: diff --git a/source/blender/depsgraph/intern/depsgraph_type.h b/source/blender/depsgraph/intern/depsgraph_type.h index 3d386695e6c..12150320391 100644 --- a/source/blender/depsgraph/intern/depsgraph_type.h +++ b/source/blender/depsgraph/intern/depsgraph_type.h @@ -54,6 +54,7 @@ namespace DEG { /* Commonly used types. */ using blender::Map; +using blender::Optional; using blender::Set; using blender::Span; using blender::StringRef; @@ -61,9 +62,7 @@ using blender::StringRefNull; using blender::Vector; using blender::VectorSet; using std::deque; -using std::map; using std::pair; -using std::set; using std::string; using std::unique_ptr; diff --git a/source/blender/depsgraph/intern/eval/deg_eval_flush.cc b/source/blender/depsgraph/intern/eval/deg_eval_flush.cc index df6c139e916..50469d57a34 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_flush.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_flush.cc @@ -251,7 +251,10 @@ void flush_editors_id_update(Depsgraph *graph, const DEGEditorUpdateContext *upd if (graph->is_active && id_node->is_user_modified) { deg_editors_id_update(update_ctx, id_orig); } - if (ID_IS_OVERRIDE_LIBRARY(id_orig)) { + if (ID_IS_OVERRIDE_LIBRARY(id_orig) && id_orig->recalc != 0) { + /* We only want to tag an ID for liboverride autorefresh if it was actually tagged as + * changed. CoW IDs indirectly modified because of changes in other IDs should never + * require a liboverride diffing. */ id_orig->tag |= LIB_TAG_OVERRIDE_LIBRARY_AUTOREFRESH; } /* Inform draw engines that something was changed. */ diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_modifier.cc b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_modifier.cc index 3361c26a077..2dedba6ac5b 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_modifier.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_modifier.cc @@ -35,15 +35,15 @@ ModifierDataBackupID::ModifierDataBackupID(ModifierData *modifier_data, Modifier { } -bool ModifierDataBackupID::operator<(const ModifierDataBackupID &other) const +bool operator==(const ModifierDataBackupID &a, const ModifierDataBackupID &b) { - if (modifier_data < other.modifier_data) { - return true; - } - if (modifier_data == other.modifier_data) { - return static_cast<int>(type) < static_cast<int>(other.type); - } - return false; + return a.modifier_data == b.modifier_data && a.type == b.type; +} + +uint32_t ModifierDataBackupID::hash() const +{ + uintptr_t ptr = (uintptr_t)modifier_data; + return (ptr >> 4) ^ (uintptr_t)type; } } // namespace DEG diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_modifier.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_modifier.h index 4b3d46126f3..446e6830867 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_modifier.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_modifier.h @@ -46,13 +46,15 @@ class ModifierDataBackupID { ModifierDataBackupID(const Depsgraph *depsgraph); ModifierDataBackupID(ModifierData *modifier_data, ModifierType type); - bool operator<(const ModifierDataBackupID &other) const; + friend bool operator==(const ModifierDataBackupID &a, const ModifierDataBackupID &b); + + uint32_t hash() const; ModifierData *modifier_data; ModifierType type; }; /* Storage for backed up runtime modifier data. */ -typedef map<ModifierDataBackupID, void *> ModifierRuntimeDataBackup; +typedef Map<ModifierDataBackupID, void *> ModifierRuntimeDataBackup; } // namespace DEG diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc index 2b172f824b6..975887ae7bf 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc @@ -75,7 +75,7 @@ void ObjectRuntimeBackup::backup_modifier_runtime_data(Object *object) } BLI_assert(modifier_data->orig_modifier_data != nullptr); ModifierDataBackupID modifier_data_id = create_modifier_data_id(modifier_data); - modifier_runtime_data.insert(make_pair(modifier_data_id, modifier_data->runtime)); + modifier_runtime_data.add(modifier_data_id, modifier_data->runtime); modifier_data->runtime = nullptr; } } @@ -86,7 +86,7 @@ void ObjectRuntimeBackup::backup_pose_channel_runtime_data(Object *object) LISTBASE_FOREACH (bPoseChannel *, pchan, &object->pose->chanbase) { /* This is nullptr in Edit mode. */ if (pchan->orig_pchan != nullptr) { - pose_channel_runtime_data[pchan->orig_pchan] = pchan->runtime; + pose_channel_runtime_data.add(pchan->orig_pchan, pchan->runtime); BKE_pose_channel_runtime_reset(&pchan->runtime); } } @@ -153,22 +153,16 @@ void ObjectRuntimeBackup::restore_modifier_runtime_data(Object *object) LISTBASE_FOREACH (ModifierData *, modifier_data, &object->modifiers) { BLI_assert(modifier_data->orig_modifier_data != nullptr); ModifierDataBackupID modifier_data_id = create_modifier_data_id(modifier_data); - ModifierRuntimeDataBackup::iterator runtime_data_iterator = modifier_runtime_data.find( - modifier_data_id); - if (runtime_data_iterator != modifier_runtime_data.end()) { - modifier_data->runtime = runtime_data_iterator->second; - runtime_data_iterator->second = nullptr; + void *runtime = modifier_runtime_data.pop_default(modifier_data_id, nullptr); + if (runtime != nullptr) { + modifier_data->runtime = runtime; } } - for (ModifierRuntimeDataBackup::value_type value : modifier_runtime_data) { - const ModifierDataBackupID modifier_data_id = value.first; - void *runtime = value.second; - if (value.second == nullptr) { - continue; - } - const ModifierTypeInfo *modifier_type_info = BKE_modifier_get_info(modifier_data_id.type); + + for (ModifierRuntimeDataBackup::Item item : modifier_runtime_data.items()) { + const ModifierTypeInfo *modifier_type_info = BKE_modifier_get_info(item.key.type); BLI_assert(modifier_type_info != nullptr); - modifier_type_info->freeRuntimeData(runtime); + modifier_type_info->freeRuntimeData(item.value); } } @@ -178,17 +172,16 @@ void ObjectRuntimeBackup::restore_pose_channel_runtime_data(Object *object) LISTBASE_FOREACH (bPoseChannel *, pchan, &object->pose->chanbase) { /* This is nullptr in Edit mode. */ if (pchan->orig_pchan != nullptr) { - PoseChannelRuntimeDataBackup::iterator runtime_data_iterator = - pose_channel_runtime_data.find(pchan->orig_pchan); - if (runtime_data_iterator != pose_channel_runtime_data.end()) { - pchan->runtime = runtime_data_iterator->second; - pose_channel_runtime_data.erase(runtime_data_iterator); + Optional<bPoseChannel_Runtime> runtime = pose_channel_runtime_data.pop_try( + pchan->orig_pchan); + if (runtime.has_value()) { + pchan->runtime = runtime.extract(); } } } } - for (PoseChannelRuntimeDataBackup::value_type &value : pose_channel_runtime_data) { - BKE_pose_channel_runtime_free(&value.second); + for (bPoseChannel_Runtime &runtime : pose_channel_runtime_data.values()) { + BKE_pose_channel_runtime_free(&runtime); } } diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.h index e5c3d6a967a..128731ee9e0 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.h @@ -53,7 +53,7 @@ class ObjectRuntimeBackup { short base_flag; unsigned short base_local_view_bits; ModifierRuntimeDataBackup modifier_runtime_data; - PoseChannelRuntimeDataBackup pose_channel_runtime_data; + Map<bPoseChannel *, bPoseChannel_Runtime> pose_channel_runtime_data; }; } // namespace DEG diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_pose.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_pose.h index 53a2c4c0784..c795d9d4b82 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_pose.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_pose.h @@ -27,11 +27,6 @@ #include "DNA_action_types.h" -struct bPoseChannel; - namespace DEG { -/* Storage for backed up pose channel runtime data. */ -typedef map<bPoseChannel *, bPoseChannel_Runtime> PoseChannelRuntimeDataBackup; - } // namespace DEG diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequencer.cc b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequencer.cc index adc7fd570e8..769264f3075 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequencer.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequencer.cc @@ -42,7 +42,7 @@ void SequencerBackup::init_from_scene(Scene *scene) SequenceBackup sequence_backup(depsgraph); sequence_backup.init_from_sequence(sequence); if (!sequence_backup.isEmpty()) { - sequences_backup.insert(make_pair(sequence->orig_sequence, sequence_backup)); + sequences_backup.add(sequence->orig_sequence, sequence_backup); } } SEQ_END; @@ -52,17 +52,14 @@ void SequencerBackup::restore_to_scene(Scene *scene) { Sequence *sequence; SEQ_BEGIN (scene->ed, sequence) { - SequencesBackupMap::iterator it = sequences_backup.find(sequence->orig_sequence); - if (it == sequences_backup.end()) { - continue; + SequenceBackup *sequence_backup = sequences_backup.lookup_ptr(sequence->orig_sequence); + if (sequence_backup != nullptr) { + sequence_backup->restore_to_sequence(sequence); } - SequenceBackup &sequence_backup = it->second; - sequence_backup.restore_to_sequence(sequence); } SEQ_END; /* Cleanup audio while the scene is still known. */ - for (SequencesBackupMap::value_type &it : sequences_backup) { - SequenceBackup &sequence_backup = it.second; + for (SequenceBackup &sequence_backup : sequences_backup.values()) { if (sequence_backup.scene_sound != nullptr) { BKE_sound_remove_scene_sound(scene, sequence_backup.scene_sound); } diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequencer.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequencer.h index 05f37b45dc4..5ffa7c24859 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequencer.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequencer.h @@ -42,8 +42,7 @@ class SequencerBackup { const Depsgraph *depsgraph; - typedef map<Sequence *, SequenceBackup> SequencesBackupMap; - SequencesBackupMap sequences_backup; + Map<Sequence *, SequenceBackup> sequences_backup; }; } // namespace DEG diff --git a/source/blender/draw/DRW_select_buffer.h b/source/blender/draw/DRW_select_buffer.h index 6ebc30d0382..66dee3a9aa9 100644 --- a/source/blender/draw/DRW_select_buffer.h +++ b/source/blender/draw/DRW_select_buffer.h @@ -56,16 +56,16 @@ struct ObjectOffsets { uint vert; }; -struct SELECTID_Context { +typedef struct SELECTID_Context { /* All context objects */ struct Object **objects; - uint objects_len; /* Array with only drawn objects. When a new object is found within the rect, * it is added to the end of the list. * The list is reset to any viewport or context update. */ - struct ObjectOffsets *index_offsets; struct Object **objects_drawn; + struct ObjectOffsets *index_offsets; + uint objects_len; uint objects_drawn_len; /** Total number of element indices `index_offsets[object_drawn_len - 1].vert`. */ @@ -73,13 +73,13 @@ struct SELECTID_Context { short select_mode; + /* rect is used to check which objects whose indexes need to be drawn. */ + rcti last_rect; + /* To check for updates. */ float persmat[4][4]; bool is_dirty; - - /* rect is used to check which objects whose indexes need to be drawn. */ - rcti last_rect; -}; +} SELECTID_Context; /* draw_select_buffer.c */ bool DRW_select_buffer_elem_get(const uint sel_id, diff --git a/source/blender/draw/engines/eevee/eevee_engine.c b/source/blender/draw/engines/eevee/eevee_engine.c index b698574f9d7..083d0a82214 100644 --- a/source/blender/draw/engines/eevee/eevee_engine.c +++ b/source/blender/draw/engines/eevee/eevee_engine.c @@ -370,7 +370,7 @@ static void eevee_id_object_update(void *UNUSED(vedata), Object *object) { EEVEE_LightProbeEngineData *ped = EEVEE_lightprobe_data_get(object); if (ped != NULL && ped->dd.recalc != 0) { - ped->need_update = (ped->dd.recalc & (ID_RECALC_TRANSFORM)) != 0; + ped->need_update = (ped->dd.recalc & ID_RECALC_TRANSFORM) != 0; ped->dd.recalc = 0; } EEVEE_LightEngineData *led = EEVEE_light_data_get(object); diff --git a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c index 2b811f1d52e..15dc4aa1860 100644 --- a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c +++ b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c @@ -265,8 +265,10 @@ GPENCIL_tLayer *gpencil_layer_cache_add(GPENCIL_PrivateData *pd, GPENCIL_VERTEX_MODE(gpd) || pd->is_render; bool is_masked = (gpl->flag & GP_LAYER_USE_MASK) && !BLI_listbase_is_empty(&gpl->mask_layers); - float vert_col_opacity = (overide_vertcol) ? (is_vert_col_mode ? 1.0f : 0.0f) : - gpl->vertex_paint_opacity; + float vert_col_opacity = (overide_vertcol) ? + (is_vert_col_mode ? pd->vertex_paint_opacity : 0.0f) : + pd->is_render ? gpl->vertex_paint_opacity : + pd->vertex_paint_opacity; /* Negate thickness sign to tag that strokes are in screen space. * Convert to world units (by default, 1 meter = 2000 px). */ float thickness_scale = (is_screenspace) ? -1.0f : (gpd->pixfactor / GPENCIL_PIXEL_FACTOR); diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.c b/source/blender/draw/engines/gpencil/gpencil_engine.c index 495de7ef10b..16416596784 100644 --- a/source/blender/draw/engines/gpencil/gpencil_engine.c +++ b/source/blender/draw/engines/gpencil/gpencil_engine.c @@ -224,6 +224,7 @@ void GPENCIL_cache_init(void *ved) const bool is_fade_layer = ((!hide_overlay) && (!pd->is_render) && (draw_ctx->v3d->gp_flag & V3D_GP_FADE_NOACTIVE_LAYERS)); pd->fade_layer_opacity = (is_fade_layer) ? draw_ctx->v3d->overlay.gpencil_fade_layer : -1.0f; + pd->vertex_paint_opacity = draw_ctx->v3d->overlay.gpencil_vertex_paint_opacity; /* Fade GPencil Objects. */ const bool is_fade_object = ((!hide_overlay) && (!pd->is_render) && (draw_ctx->v3d->gp_flag & V3D_GP_FADE_OBJECTS) && diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.h b/source/blender/draw/engines/gpencil/gpencil_engine.h index cedd75af813..7baca28dca3 100644 --- a/source/blender/draw/engines/gpencil/gpencil_engine.h +++ b/source/blender/draw/engines/gpencil/gpencil_engine.h @@ -363,6 +363,8 @@ typedef struct GPENCIL_PrivateData { float xray_alpha; /* Mask invert uniform. */ int mask_invert; + /* Vertex Paint opacity. */ + float vertex_paint_opacity; } GPENCIL_PrivateData; /* geometry batch cache functions */ diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 10ef8d9c4c8..16dd17590f8 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -2374,6 +2374,17 @@ void DRW_draw_depth_loop_gpencil(struct Depsgraph *depsgraph, void DRW_draw_select_id(Depsgraph *depsgraph, ARegion *region, View3D *v3d, const rcti *rect) { + SELECTID_Context *sel_ctx = DRW_select_engine_context_get(); + GPUViewport *viewport = WM_draw_region_get_viewport(region); + if (!viewport) { + /* Selection engine requires a viewport. + * TODO (germano): This should be done internally in the engine. */ + sel_ctx->is_dirty = true; + sel_ctx->objects_drawn_len = 0; + sel_ctx->index_drawn_len = 1; + return; + } + Scene *scene = DEG_get_evaluated_scene(depsgraph); ViewLayer *view_layer = DEG_get_evaluated_view_layer(depsgraph); @@ -2394,14 +2405,13 @@ void DRW_draw_select_id(Depsgraph *depsgraph, ARegion *region, View3D *v3d, cons drw_context_state_init(); /* Setup viewport */ - DST.viewport = WM_draw_region_get_viewport(region); + DST.viewport = viewport; drw_viewport_var_init(); /* Update ubos */ DRW_globals_update(); /* Init Select Engine */ - struct SELECTID_Context *sel_ctx = DRW_select_engine_context_get(); sel_ctx->last_rect = *rect; use_drw_engine(&draw_engine_select_type); diff --git a/source/blender/editors/armature/armature_relations.c b/source/blender/editors/armature/armature_relations.c index d3d00fc44f2..8dfb8177ddb 100644 --- a/source/blender/editors/armature/armature_relations.c +++ b/source/blender/editors/armature/armature_relations.c @@ -270,7 +270,7 @@ static void joined_armature_fix_links( } /* join armature exec is exported for use in object->join objects operator... */ -int join_armature_exec(bContext *C, wmOperator *op) +int ED_armature_join_objects_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); @@ -754,7 +754,7 @@ static void bone_connect_to_new_parent(ListBase *edbo, float offset[3]; if ((selbone->parent) && (selbone->flag & BONE_CONNECTED)) { - selbone->parent->flag &= ~(BONE_TIPSEL); + selbone->parent->flag &= ~BONE_TIPSEL; } /* make actbone the parent of selbone */ @@ -956,7 +956,7 @@ static void editbone_clear_parent(EditBone *ebone, int mode) { if (ebone->parent) { /* for nice selection */ - ebone->parent->flag &= ~(BONE_TIPSEL); + ebone->parent->flag &= ~BONE_TIPSEL; } if (mode == 1) { diff --git a/source/blender/editors/armature/armature_select.c b/source/blender/editors/armature/armature_select.c index eb7c1bc74ea..ccd39429704 100644 --- a/source/blender/editors/armature/armature_select.c +++ b/source/blender/editors/armature/armature_select.c @@ -188,7 +188,7 @@ static void *ed_armature_pick_bone_from_selectbuffer_impl(const bool is_editmode Base *base = NULL; bool sel; - hitresult &= ~(BONESEL_ANY); + hitresult &= ~BONESEL_ANY; /* Determine what the current bone is */ if (is_editmode == false) { base = ED_armature_base_and_pchan_from_select_buffer(bases, bases_len, hitresult, &pchan); @@ -1302,7 +1302,7 @@ static int armature_de_select_all_exec(bContext *C, wmOperator *op) if ((ebone->flag & BONE_UNSELECTABLE) == 0) { ebone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); if (ebone->parent) { - ebone->parent->flag |= (BONE_TIPSEL); + ebone->parent->flag |= BONE_TIPSEL; } } break; @@ -1317,7 +1317,7 @@ static int armature_de_select_all_exec(bContext *C, wmOperator *op) if ((ebone->flag & BONE_UNSELECTABLE) == 0) { ebone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); if (ebone->parent) { - ebone->parent->flag |= (BONE_TIPSEL); + ebone->parent->flag |= BONE_TIPSEL; } } } diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index 82f5d99aa47..a39c8261b32 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -6895,7 +6895,7 @@ void CURVE_OT_shade_flat(wmOperatorType *ot) * This is used externally, by #OBJECT_OT_join. * TODO: shape keys - as with meshes. */ -int join_curve_exec(bContext *C, wmOperator *op) +int ED_curve_join_objects_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); diff --git a/source/blender/editors/curve/editcurve_select.c b/source/blender/editors/curve/editcurve_select.c index 9294bc6e91b..73f970876b1 100644 --- a/source/blender/editors/curve/editcurve_select.c +++ b/source/blender/editors/curve/editcurve_select.c @@ -988,7 +988,7 @@ void CURVE_OT_select_more(wmOperatorType *ot) /* identifiers */ ot->name = "Select More"; ot->idname = "CURVE_OT_select_more"; - ot->description = "Select control points directly linked to already selected ones"; + ot->description = "Select control points at the boundary of each selection region"; /* api callbacks */ ot->exec = curve_select_more_exec; @@ -1203,7 +1203,7 @@ void CURVE_OT_select_less(wmOperatorType *ot) /* identifiers */ ot->name = "Select Less"; ot->idname = "CURVE_OT_select_less"; - ot->description = "Reduce current selection by deselecting boundary elements"; + ot->description = "Deselect control points at the boundary of each selection region"; /* api callbacks */ ot->exec = curve_select_less_exec; diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c index 5c5adb32a97..2bd83475f00 100644 --- a/source/blender/editors/gpencil/annotate_paint.c +++ b/source/blender/editors/gpencil/annotate_paint.c @@ -381,10 +381,11 @@ static void gp_stroke_convertcoords(tGPsdata *p, const float mval[2], float out[ } } -/* Apply smooth to buffer while drawing +/** + * Apply smooth to buffer while drawing * to smooth point C, use 2 before (A, B) and current point (D): * - * A----B-----C------D + * `A----B-----C------D` * * \param p: Temp data * \param inf: Influence factor @@ -2053,7 +2054,7 @@ static void annotation_draw_apply_event( /* Key to toggle stabilization. */ if (event->shift > 0 && p->paintmode == GP_PAINTMODE_DRAW) { /* Using permanent stabilization, shift will deactivate the flag. */ - if (p->flags & (GP_PAINTFLAG_USE_STABILIZER)) { + if (p->flags & GP_PAINTFLAG_USE_STABILIZER) { if (p->flags & GP_PAINTFLAG_USE_STABILIZER_TEMP) { gpencil_draw_toggle_stabilizer_cursor(p, false); p->flags &= ~GP_PAINTFLAG_USE_STABILIZER_TEMP; diff --git a/source/blender/editors/gpencil/editaction_gpencil.c b/source/blender/editors/gpencil/editaction_gpencil.c index d2b1eba7d86..5db2e904dee 100644 --- a/source/blender/editors/gpencil/editaction_gpencil.c +++ b/source/blender/editors/gpencil/editaction_gpencil.c @@ -282,8 +282,10 @@ void ED_gplayer_frames_duplicate(bGPDlayer *gpl) } } -/* Set keyframe type for selected frames from given gp-layer - * \param type: The type of keyframe (eBezTriple_KeyframeType) to set selected frames to +/** + * Set keyframe type for selected frames from given gp-layer + * + * \param type: The type of keyframe (#eBezTriple_KeyframeType) to set selected frames to. */ void ED_gplayer_frames_keytype_set(bGPDlayer *gpl, short type) { diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c index 502097a0678..ab52a484d2f 100644 --- a/source/blender/editors/gpencil/gpencil_fill.c +++ b/source/blender/editors/gpencil/gpencil_fill.c @@ -472,15 +472,16 @@ static void set_pixel(ImBuf *ibuf, int idx, const float col[4]) } } -/* check if the size of the leak is narrow to determine if the stroke is closed +/** + * Check if the size of the leak is narrow to determine if the stroke is closed * this is used for strokes with small gaps between them to get a full fill * and do not get a full screen fill. * - * \param ibuf: Image pixel data - * \param maxpixel: Maximum index - * \param limit: Limit of pixels to analyze - * \param index: Index of current pixel - * \param type: 0-Horizontal 1-Vertical + * \param ibuf: Image pixel data. + * \param maxpixel: Maximum index. + * \param limit: Limit of pixels to analyze. + * \param index: Index of current pixel. + * \param type: 0-Horizontal 1-Vertical. */ static bool is_leak_narrow(ImBuf *ibuf, const int maxpixel, int limit, int index, int type) { @@ -576,11 +577,12 @@ static bool is_leak_narrow(ImBuf *ibuf, const int maxpixel, int limit, int index return (bool)(t_a && t_b); } -/* Boundary fill inside strokes +/** + * Boundary fill inside strokes * Fills the space created by a set of strokes using the stroke color as the boundary * of the shape to fill. * - * \param tgpf: Temporary fill data + * \param tgpf: Temporary fill data. */ static void gpencil_boundaryfill_area(tGPDfill *tgpf) { diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index a9eb94498ad..e3512b8660f 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -551,10 +551,11 @@ static void gp_brush_angle(bGPdata *gpd, Brush *brush, tGPspoint *pt, const floa } } -/* Apply smooth to buffer while drawing +/** + * Apply smooth to buffer while drawing * to smooth point C, use 2 before (A, B) and current point (D): * - * A----B-----C------D + * `A----B-----C------D` * * \param p: Temp data * \param inf: Influence factor diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c index 52274520176..fd5131ce867 100644 --- a/source/blender/editors/gpencil/gpencil_utils.c +++ b/source/blender/editors/gpencil/gpencil_utils.c @@ -286,7 +286,8 @@ bGPdata *ED_gpencil_data_get_active(const bContext *C) return ob->data; } -/* Get the active Grease Pencil datablock +/** + * Get the active Grease Pencil datablock * \note This is the original (bmain) copy of the datablock, stored in files. * Do not use for reading evaluated copies of GP Objects data */ diff --git a/source/blender/editors/include/ED_armature.h b/source/blender/editors/include/ED_armature.h index 541e2633512..eef431c40fa 100644 --- a/source/blender/editors/include/ED_armature.h +++ b/source/blender/editors/include/ED_armature.h @@ -174,7 +174,7 @@ void ED_operatormacros_armature(void); void ED_keymap_armature(struct wmKeyConfig *keyconf); /* armature_relations.c */ -int join_armature_exec(struct bContext *C, struct wmOperator *op); +int ED_armature_join_objects_exec(struct bContext *C, struct wmOperator *op); /* armature_select.c */ struct Base *ED_armature_base_and_ebone_from_select_buffer(struct Base **bases, diff --git a/source/blender/editors/include/ED_curve.h b/source/blender/editors/include/ED_curve.h index 95c454043da..79f5f62f293 100644 --- a/source/blender/editors/include/ED_curve.h +++ b/source/blender/editors/include/ED_curve.h @@ -66,7 +66,7 @@ int ED_curve_nurb_select_count(struct View3D *v3d, struct Nurb *nu); bool ED_curve_nurb_select_all(const struct Nurb *nu); bool ED_curve_nurb_deselect_all(const struct Nurb *nu); -int join_curve_exec(struct bContext *C, struct wmOperator *op); +int ED_curve_join_objects_exec(struct bContext *C, struct wmOperator *op); /* editcurve_select.c */ bool ED_curve_select_check(struct View3D *v3d, struct EditNurb *editnurb); diff --git a/source/blender/editors/include/ED_mball.h b/source/blender/editors/include/ED_mball.h index 938d1059f90..5c2106b934c 100644 --- a/source/blender/editors/include/ED_mball.h +++ b/source/blender/editors/include/ED_mball.h @@ -38,8 +38,12 @@ void ED_operatortypes_metaball(void); void ED_operatormacros_metaball(void); void ED_keymap_metaball(struct wmKeyConfig *keyconf); -struct MetaElem *ED_mball_add_primitive( - struct bContext *C, struct Object *obedit, float mat[4][4], float dia, int type); +struct MetaElem *ED_mball_add_primitive(struct bContext *C, + struct Object *obedit, + bool obedit_is_new, + float mat[4][4], + float dia, + int type); bool ED_mball_select_pick( struct bContext *C, const int mval[2], bool extend, bool deselect, bool toggle); diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h index 20e54df1ccb..17e2043a7a8 100644 --- a/source/blender/editors/include/ED_mesh.h +++ b/source/blender/editors/include/ED_mesh.h @@ -445,8 +445,8 @@ void EDBM_redo_state_restore(struct BMBackup, struct BMEditMesh *em, int recalct void EDBM_redo_state_free(struct BMBackup *, struct BMEditMesh *em, int recalctess); /* *** meshtools.c *** */ -int join_mesh_exec(struct bContext *C, struct wmOperator *op); -int join_mesh_shapes_exec(struct bContext *C, struct wmOperator *op); +int ED_mesh_join_objects_exec(struct bContext *C, struct wmOperator *op); +int ED_mesh_shapes_join_objects_exec(struct bContext *C, struct wmOperator *op); /* mirror lookup api */ /* Spatial Mirror */ diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 42feda0e1bc..3f3f0513184 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -4872,7 +4872,7 @@ static void ui_numedit_set_active(uiBut *but) /* Don't change the cursor once pressed. */ if ((but->flag & UI_SELECT) == 0) { - if ((but->drawflag & (UI_BUT_ACTIVE_LEFT)) || (but->drawflag & (UI_BUT_ACTIVE_RIGHT))) { + if ((but->drawflag & UI_BUT_ACTIVE_LEFT) || (but->drawflag & UI_BUT_ACTIVE_RIGHT)) { if (data->changed_cursor) { WM_cursor_modal_restore(data->window); data->changed_cursor = false; @@ -9947,7 +9947,7 @@ static int ui_handle_menu_event(bContext *C, if (ELEM(event->val, KM_PRESS, KM_DBL_CLICK)) { if ((is_parent_menu == false) && (U.uiflag & USER_MENUOPENAUTO) == 0) { /* for root menus, allow clicking to close */ - if (block->flag & (UI_BLOCK_OUT_1)) { + if (block->flag & UI_BLOCK_OUT_1) { menu->menuretval = UI_RETURN_OK; } else { @@ -9955,7 +9955,7 @@ static int ui_handle_menu_event(bContext *C, } } else if (saferct && !BLI_rctf_isect_pt(&saferct->parent, event->x, event->y)) { - if (block->flag & (UI_BLOCK_OUT_1)) { + if (block->flag & UI_BLOCK_OUT_1) { menu->menuretval = UI_RETURN_OK; } else { @@ -10049,7 +10049,7 @@ static int ui_handle_menu_event(bContext *C, /* strict check, and include the parent rect */ if (!menu->dotowards && !saferct) { - if (block->flag & (UI_BLOCK_OUT_1)) { + if (block->flag & UI_BLOCK_OUT_1) { menu->menuretval = UI_RETURN_OK; } else { diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c index 55657d7297a..4fff80d5def 100644 --- a/source/blender/editors/interface/interface_panel.c +++ b/source/blender/editors/interface/interface_panel.c @@ -583,6 +583,26 @@ static void set_panels_list_data_expand_flag(const bContext *C, ARegion *region) /****************************** panels ******************************/ +/** + * Set flag state for a panel and its subpanels. + * + * \return True if this function changed any of the flags, false if it didn't. + */ +static bool panel_set_flag_recursive(Panel *panel, int flag, bool value) +{ + short flag_original = panel->flag; + + SET_FLAG_FROM_TEST(panel->flag, value, flag); + + bool changed = (flag_original != panel->flag); + + LISTBASE_FOREACH (Panel *, child, &panel->children) { + changed |= panel_set_flag_recursive(child, flag, value); + } + + return changed; +} + static void panels_collapse_all(const bContext *C, ScrArea *area, ARegion *region, @@ -980,7 +1000,7 @@ void ui_draw_aligned_panel(uiStyle *style, * can't be dragged. This may be changed in future. */ show_background); const int panel_col = is_subpanel ? TH_PANEL_SUB_BACK : TH_PANEL_BACK; - const bool draw_box_style = (panel->type && panel->type->flag & (PNL_DRAW_BOX)); + const bool draw_box_style = (panel->type && panel->type->flag & PNL_DRAW_BOX); /* Use the theme for box widgets for box-style panels. */ uiWidgetColors *box_wcol = NULL; @@ -2017,11 +2037,25 @@ static void ui_handle_panel_header( if (button == 2) { /* close */ ED_region_tag_redraw(region); } - else { /* collapse */ + else { + /* Collapse and expand panels. */ + if (ctrl) { /* Only collapse all for parent panels. */ if (block->panel->type != NULL && block->panel->type->parent == NULL) { - panels_collapse_all(C, area, region, block->panel); + if (block->panel->flag & PNL_CLOSED || BLI_listbase_is_empty(&block->panel->children)) { + panels_collapse_all(C, area, region, block->panel); + } + else { + const int closed_flag = (align == BUT_HORIZONTAL) ? PNL_CLOSEDX : PNL_CLOSEDY; + /* If a panel has subpanels and it's open, toggle the expansion + * of the subpanels (based on the expansion of the first subpanel). */ + Panel *first_child = block->panel->children.first; + BLI_assert(first_child != NULL); + panel_set_flag_recursive( + block->panel, closed_flag, (first_child->flag & PNL_CLOSED) == 0); + block->panel->flag |= closed_flag; + } /* reset the view - we don't want to display a view without content */ UI_view2d_offset(®ion->v2d, 0.0f, 1.0f); @@ -2899,24 +2933,6 @@ static void ui_handler_remove_panel(bContext *C, void *userdata) panel_activate_state(C, panel, PANEL_STATE_EXIT); } -/** - * Set selection state for a panel and its subpanels. The subpanels need to know they are selected - * too so they can be drawn above their parent when it is dragged. - */ -static void set_panel_selection(Panel *panel, bool value) -{ - if (value) { - panel->flag |= PNL_SELECT; - } - else { - panel->flag &= ~PNL_SELECT; - } - - LISTBASE_FOREACH (Panel *, child, &panel->children) { - set_panel_selection(child, value); - } -} - static void panel_activate_state(const bContext *C, Panel *panel, uiHandlePanelState state) { uiHandlePanelData *data = panel->activedata; @@ -2929,6 +2945,8 @@ static void panel_activate_state(const bContext *C, Panel *panel, uiHandlePanelS bool was_drag_drop = (data && data->state == PANEL_STATE_DRAG); + /* Set selection state for the panel and its subpanels, which need to know they are selected + * too so they can be drawn above their parent when it's dragged. */ if (state == PANEL_STATE_EXIT || state == PANEL_STATE_ANIMATION) { if (data && data->state != PANEL_STATE_ANIMATION) { /* XXX: @@ -2941,10 +2959,10 @@ static void panel_activate_state(const bContext *C, Panel *panel, uiHandlePanelS check_panel_overlap(region, NULL); /* clears */ } - set_panel_selection(panel, false); + panel_set_flag_recursive(panel, PNL_SELECT, false); } else { - set_panel_selection(panel, true); + panel_set_flag_recursive(panel, PNL_SELECT, true); } if (data && data->animtimer) { diff --git a/source/blender/editors/interface/interface_region_menu_popup.c b/source/blender/editors/interface/interface_region_menu_popup.c index 3e34b7f3f8a..65f5798bdce 100644 --- a/source/blender/editors/interface/interface_region_menu_popup.c +++ b/source/blender/editors/interface/interface_region_menu_popup.c @@ -184,7 +184,12 @@ static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, voi pup->block->handle = NULL; } - if (pup->but) { + /* Find block minimum width. */ + if (uiLayoutGetUnitsX(pup->layout) != 0.0f) { + /* Use the minimum width from the layout if it's set. */ + minwidth = uiLayoutGetUnitsX(pup->layout) * UI_UNIT_X; + } + else if (pup->but) { /* minimum width to enforece */ if (pup->but->drawstr[0]) { minwidth = BLI_rctf_size_x(&pup->but->rect); @@ -193,7 +198,13 @@ static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, voi /* For buttons with no text, use the minimum (typically icon only). */ minwidth = UI_MENU_WIDTH_MIN; } + } + else { + minwidth = UI_MENU_WIDTH_MIN; + } + /* Find block direction. */ + if (pup->but) { if (pup->block->direction != 0) { /* allow overriding the direction from menu_func */ direction = pup->block->direction; @@ -203,7 +214,6 @@ static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, voi } } else { - minwidth = UI_MENU_WIDTH_MIN; direction = UI_DIR_DOWN; } diff --git a/source/blender/editors/interface/view2d.c b/source/blender/editors/interface/view2d.c index f8419ba3eba..8fee9df8706 100644 --- a/source/blender/editors/interface/view2d.c +++ b/source/blender/editors/interface/view2d.c @@ -113,10 +113,10 @@ BLI_INLINE void clamp_rctf_to_rcti(rcti *dst, const rctf *src) static int view2d_scroll_mapped(int scroll) { if (scroll & V2D_SCROLL_HORIZONTAL_FULLR) { - scroll &= ~(V2D_SCROLL_HORIZONTAL); + scroll &= ~V2D_SCROLL_HORIZONTAL; } if (scroll & V2D_SCROLL_VERTICAL_FULLR) { - scroll &= ~(V2D_SCROLL_VERTICAL); + scroll &= ~V2D_SCROLL_VERTICAL; } return scroll; } @@ -198,7 +198,7 @@ static void view2d_masks(View2D *v2d, const rcti *mask_scroll) } /* horizontal scroller */ - if (scroll & (V2D_SCROLL_BOTTOM)) { + if (scroll & V2D_SCROLL_BOTTOM) { /* on bottom edge of region */ v2d->hor = *mask_scroll; v2d->hor.ymax = scroll_height; @@ -211,7 +211,7 @@ static void view2d_masks(View2D *v2d, const rcti *mask_scroll) /* adjust vertical scroller if there's a horizontal scroller, to leave corner free */ if (scroll & V2D_SCROLL_VERTICAL) { - if (scroll & (V2D_SCROLL_BOTTOM)) { + if (scroll & V2D_SCROLL_BOTTOM) { /* on bottom edge of region */ v2d->vert.ymin = v2d->hor.ymax; } diff --git a/source/blender/editors/interface/view2d_ops.c b/source/blender/editors/interface/view2d_ops.c index 98bbd7af943..34108853c61 100644 --- a/source/blender/editors/interface/view2d_ops.c +++ b/source/blender/editors/interface/view2d_ops.c @@ -2299,8 +2299,8 @@ static int scroller_activate_invoke(bContext *C, wmOperator *op, const wmEvent * } /* zone is also inappropriate if scroller is not visible... */ - if (((vsm->scroller == 'h') && (v2d->scroll & (V2D_SCROLL_HORIZONTAL_FULLR))) || - ((vsm->scroller == 'v') && (v2d->scroll & (V2D_SCROLL_VERTICAL_FULLR)))) { + if (((vsm->scroller == 'h') && (v2d->scroll & V2D_SCROLL_HORIZONTAL_FULLR)) || + ((vsm->scroller == 'v') && (v2d->scroll & V2D_SCROLL_VERTICAL_FULLR))) { /* free customdata initialized */ scroller_activate_exit(C, op); diff --git a/source/blender/editors/io/io_alembic.c b/source/blender/editors/io/io_alembic.c index fc2c55ffeda..2de03363a77 100644 --- a/source/blender/editors/io/io_alembic.c +++ b/source/blender/editors/io/io_alembic.c @@ -410,8 +410,11 @@ void WM_OT_alembic_export(wmOperatorType *ot) "Use Subdivision Schema", "Export meshes using Alembic's subdivision schema"); - RNA_def_boolean( - ot->srna, "apply_subdiv", 0, "Apply Subdivision Surface", "Export subdivision surfaces as meshes"); + RNA_def_boolean(ot->srna, + "apply_subdiv", + 0, + "Apply Subdivision Surface", + "Export subdivision surfaces as meshes"); RNA_def_boolean(ot->srna, "curves_as_mesh", diff --git a/source/blender/editors/mesh/meshtools.c b/source/blender/editors/mesh/meshtools.c index 917bbe61e3d..1bdf2ede22a 100644 --- a/source/blender/editors/mesh/meshtools.c +++ b/source/blender/editors/mesh/meshtools.c @@ -297,7 +297,7 @@ static void join_mesh_single(Depsgraph *depsgraph, *mpoly_pp += me->totpoly; } -int join_mesh_exec(bContext *C, wmOperator *op) +int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); @@ -706,12 +706,14 @@ int join_mesh_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -/*********************** JOIN AS SHAPES ***************************/ +/* -------------------------------------------------------------------- */ +/** \name Join as Shapes + * \{ */ /* Append selected meshes vertex locations as shapes of the active mesh, * return 0 if no join is made (error) and 1 of the join is done */ -int join_mesh_shapes_exec(bContext *C, wmOperator *op) +int ED_mesh_shapes_join_objects_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); @@ -796,6 +798,8 @@ int join_mesh_shapes_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Mesh Topology Mirror API * \{ */ diff --git a/source/blender/editors/metaball/mball_edit.c b/source/blender/editors/metaball/mball_edit.c index a25175510cd..094011ebef1 100644 --- a/source/blender/editors/metaball/mball_edit.c +++ b/source/blender/editors/metaball/mball_edit.c @@ -58,6 +58,10 @@ #include "mball_intern.h" +/* -------------------------------------------------------------------- */ +/** \name Edit Mode Functions + * \{ */ + /* This function is used to free all MetaElems from MetaBall */ void ED_mball_editmball_free(Object *obedit) { @@ -93,9 +97,36 @@ void ED_mball_editmball_load(Object *UNUSED(obedit)) { } -/* Add metaelem primitive to metaball object (which is in edit mode) */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Selection + * \{ */ + +bool ED_mball_deselect_all_multi(bContext *C) +{ + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + ViewContext vc; + ED_view3d_viewcontext_init(C, &vc, depsgraph); + uint bases_len = 0; + Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data( + vc.view_layer, vc.v3d, &bases_len); + bool changed_multi = BKE_mball_deselect_all_multi_ex(bases, bases_len); + MEM_freeN(bases); + return changed_multi; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Add Meta Primitive Utility + * \{ */ + +/** + * Add meta-element primitive to meta-ball object (which is in edit mode). + */ MetaElem *ED_mball_add_primitive( - bContext *UNUSED(C), Object *obedit, float mat[4][4], float dia, int type) + bContext *UNUSED(C), Object *obedit, bool obedit_is_new, float mat[4][4], float dia, int type) { MetaBall *mball = (MetaBall *)obedit->data; MetaElem *ml; @@ -109,16 +140,28 @@ MetaElem *ED_mball_add_primitive( ml = BKE_mball_element_add(mball, type); ml->rad *= dia; - mball->wiresize *= dia; - mball->rendersize *= dia; + + if (obedit_is_new) { + mball->wiresize *= dia; + mball->rendersize *= dia; + } copy_v3_v3(&ml->x, mat[3]); + /* MB_ELIPSOID works differently (intentional?). Whatever the case, + * on testing this needs to be skipped otherwise it doesn't behave like other types. */ + if (type != MB_ELIPSOID) { + mul_v3_fl(&ml->expx, dia); + } ml->flag |= SELECT; mball->lastelem = ml; return ml; } -/***************************** Select/Deselect operator *****************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Select/Deselect Operator + * \{ */ /* Select or deselect all MetaElements */ static int mball_select_all_exec(bContext *C, wmOperator *op) @@ -175,8 +218,11 @@ void MBALL_OT_select_all(wmOperatorType *ot) WM_operator_properties_select_all(ot); } +/** \} */ + /* -------------------------------------------------------------------- */ -/* Select Similar */ +/** \name Select Similar Operator + * \{ */ enum { SIMMBALL_TYPE = 1, @@ -428,9 +474,12 @@ void MBALL_OT_select_similar(wmOperatorType *ot) RNA_def_float(ot->srna, "threshold", 0.1, 0.0, FLT_MAX, "Threshold", "", 0.01, 1.0); } -/***************************** Select random operator *****************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Select Random Operator + * \{ */ -/* Random metaball selection */ static int select_random_metaelems_exec(bContext *C, wmOperator *op) { const bool select = (RNA_enum_get(op->ptr, "action") == SEL_SELECT); @@ -494,7 +543,11 @@ void MBALL_OT_select_random_metaelems(struct wmOperatorType *ot) WM_operator_properties_select_random(ot); } -/***************************** Duplicate operator *****************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Duplicate Meta-Ball Operator + * \{ */ /* Duplicate selected MetaElements */ static int duplicate_metaelems_exec(bContext *C, wmOperator *UNUSED(op)) @@ -546,9 +599,14 @@ void MBALL_OT_duplicate_metaelems(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/***************************** Delete operator *****************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Delete Meta-Ball Operator + * + * Delete all selected MetaElems (not MetaBall). + * \{ */ -/* Delete all selected MetaElems (not MetaBall) */ static int delete_metaelems_exec(bContext *C, wmOperator *UNUSED(op)) { ViewLayer *view_layer = CTX_data_view_layer(C); @@ -601,9 +659,12 @@ void MBALL_OT_delete_metaelems(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/***************************** Hide operator *****************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Hide Meta-Elements Operator + * \{ */ -/* Hide selected MetaElems */ static int hide_metaelems_exec(bContext *C, wmOperator *op) { Object *obedit = CTX_data_edit_object(C); @@ -646,9 +707,12 @@ void MBALL_OT_hide_metaelems(wmOperatorType *ot) ot->srna, "unselected", false, "Unselected", "Hide unselected rather than selected"); } -/***************************** Unhide operator *****************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Un-Hide Meta-Elements Operator + * \{ */ -/* Unhide all edited MetaElems */ static int reveal_metaelems_exec(bContext *C, wmOperator *op) { Object *obedit = CTX_data_edit_object(C); @@ -689,6 +753,12 @@ void MBALL_OT_reveal_metaelems(wmOperatorType *ot) RNA_def_boolean(ot->srna, "select", true, "Select", ""); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Select Pick Utility + * \{ */ + /* Select MetaElement with mouse click (user can select radius circle or * stiffness circle) */ bool ED_mball_select_pick(bContext *C, const int mval[2], bool extend, bool deselect, bool toggle) @@ -740,7 +810,7 @@ bool ED_mball_select_pick(bContext *C, const int mval[2], bool extend, bool dese continue; } - if (metaelem_id != (hitresult & 0xFFFF0000 & ~(MBALLSEL_ANY))) { + if (metaelem_id != (hitresult & 0xFFFF0000 & ~MBALLSEL_ANY)) { continue; } @@ -831,15 +901,4 @@ bool ED_mball_select_pick(bContext *C, const int mval[2], bool extend, bool dese return false; } -bool ED_mball_deselect_all_multi(bContext *C) -{ - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - ViewContext vc; - ED_view3d_viewcontext_init(C, &vc, depsgraph); - uint bases_len = 0; - Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data( - vc.view_layer, vc.v3d, &bases_len); - bool changed_multi = BKE_mball_deselect_all_multi_ex(bases, bases_len); - MEM_freeN(bases); - return changed_multi; -} +/** \} */ diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 5af586942dc..b60ce459ba6 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -860,7 +860,7 @@ static int object_metaball_add_exec(bContext *C, wmOperator *op) * we want to pass in 1 so other values such as resolution are scaled by 1.0. */ dia = RNA_float_get(op->ptr, "radius") / 2; - ED_mball_add_primitive(C, obedit, mat, dia, RNA_enum_get(op->ptr, "type")); + ED_mball_add_primitive(C, obedit, newob, mat, dia, RNA_enum_get(op->ptr, "type")); /* userdef */ if (newob && !enter_editmode) { @@ -2136,7 +2136,7 @@ static const EnumPropertyItem convert_target_items[] = { {0, NULL, 0, NULL, NULL}, }; -static void convert_ensure_curve_cache(Depsgraph *depsgraph, Scene *scene, Object *ob) +static void object_data_convert_ensure_curve_cache(Depsgraph *depsgraph, Scene *scene, Object *ob) { if (ob->runtime.curve_cache == NULL) { /* Force creation. This is normally not needed but on operator @@ -2155,7 +2155,7 @@ static void convert_ensure_curve_cache(Depsgraph *depsgraph, Scene *scene, Objec } } -static void curvetomesh(Main *bmain, Depsgraph *depsgraph, Object *ob) +static void object_data_convert_curve_to_mesh(Main *bmain, Depsgraph *depsgraph, Object *ob) { Object *object_eval = DEG_get_evaluated_object(depsgraph, ob); Curve *curve = ob->data; @@ -2188,7 +2188,7 @@ static void curvetomesh(Main *bmain, Depsgraph *depsgraph, Object *ob) } } -static bool convert_poll(bContext *C) +static bool object_convert_poll(bContext *C) { Scene *scene = CTX_data_scene(C); Base *base_act = CTX_data_active_base(C); @@ -2198,7 +2198,7 @@ static bool convert_poll(bContext *C) (base_act->flag & BASE_SELECTED) && !ID_IS_LINKED(obact)); } -/* Helper for convert_exec */ +/* Helper for object_convert_exec */ static Base *duplibase_for_convert( Main *bmain, Depsgraph *depsgraph, Scene *scene, ViewLayer *view_layer, Base *base, Object *ob) { @@ -2233,7 +2233,7 @@ static Base *duplibase_for_convert( * time we need to duplicate an object to convert it. Even worse, this is not 100% correct, since * we do not yet have duplicated obdata. * However, that is a safe solution for now. Proper, longer-term solution is to refactor - * convert_exec to: + * object_convert_exec to: * - duplicate all data it needs to in a first loop. * - do a single update. * - convert data in a second loop. */ @@ -2251,7 +2251,7 @@ static Base *duplibase_for_convert( return basen; } -static int convert_exec(bContext *C, wmOperator *op) +static int object_convert_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); @@ -2528,7 +2528,7 @@ static int convert_exec(bContext *C, wmOperator *op) if (target == OB_MESH) { /* No assumption should be made that the resulting objects is a mesh, as conversion can * fail. */ - curvetomesh(bmain, depsgraph, newob); + object_data_convert_curve_to_mesh(bmain, depsgraph, newob); /* meshes doesn't use displist */ BKE_object_free_curve_cache(newob); } @@ -2553,7 +2553,7 @@ static int convert_exec(bContext *C, wmOperator *op) /* No assumption should be made that the resulting objects is a mesh, as conversion can * fail. */ - curvetomesh(bmain, depsgraph, newob); + object_data_convert_curve_to_mesh(bmain, depsgraph, newob); /* meshes doesn't use displist */ BKE_object_free_curve_cache(newob); } @@ -2607,7 +2607,7 @@ static int convert_exec(bContext *C, wmOperator *op) } } - convert_ensure_curve_cache(depsgraph, scene, baseob); + object_data_convert_ensure_curve_cache(depsgraph, scene, baseob); BKE_mesh_from_metaball(&baseob->runtime.curve_cache->disp, newob->data); if (obact->type == OB_MBALL) { @@ -2710,7 +2710,7 @@ static int convert_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -static void convert_ui(bContext *C, wmOperator *op) +static void object_convert_ui(bContext *UNUSED(C), wmOperator *op) { uiLayout *layout = op->layout; PointerRNA ptr; @@ -2739,9 +2739,9 @@ void OBJECT_OT_convert(wmOperatorType *ot) /* api callbacks */ ot->invoke = WM_menu_invoke; - ot->exec = convert_exec; - ot->poll = convert_poll; - ot->ui = convert_ui; + ot->exec = object_convert_exec; + ot->poll = object_convert_poll; + ot->ui = object_convert_ui; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -2797,8 +2797,12 @@ void OBJECT_OT_convert(wmOperatorType *ot) /* used below, assumes id.new is correct */ /* leaves selection of base/object unaltered */ /* Does set ID->newid pointers. */ -static Base *object_add_duplicate_internal( - Main *bmain, Scene *scene, ViewLayer *view_layer, Object *ob, const eDupli_ID_Flags dupflag) +static Base *object_add_duplicate_internal(Main *bmain, + Scene *scene, + ViewLayer *view_layer, + Object *ob, + const eDupli_ID_Flags dupflag, + const eLibIDDuplicateFlags duplicate_options) { Base *base, *basen = NULL; Object *obn; @@ -2807,7 +2811,7 @@ static Base *object_add_duplicate_internal( /* nothing? */ } else { - obn = ID_NEW_SET(ob, BKE_object_duplicate(bmain, ob, dupflag)); + obn = ID_NEW_SET(ob, BKE_object_duplicate(bmain, ob, dupflag, duplicate_options)); DEG_id_tag_update(&obn->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); base = BKE_view_layer_base_find(view_layer, ob); @@ -2851,7 +2855,8 @@ Base *ED_object_add_duplicate( Base *basen; Object *ob; - basen = object_add_duplicate_internal(bmain, scene, view_layer, base->object, dupflag); + basen = object_add_duplicate_internal( + bmain, scene, view_layer, base->object, dupflag, LIB_ID_DUPLICATE_IS_SUBPROCESS); if (basen == NULL) { return NULL; } @@ -2882,7 +2887,8 @@ static int duplicate_exec(bContext *C, wmOperator *op) const eDupli_ID_Flags dupflag = (linked) ? 0 : (eDupli_ID_Flags)U.dupflag; CTX_DATA_BEGIN (C, Base *, base, selected_bases) { - Base *basen = object_add_duplicate_internal(bmain, scene, view_layer, base->object, dupflag); + Base *basen = object_add_duplicate_internal( + bmain, scene, view_layer, base->object, dupflag, 0); /* note that this is safe to do with this context iterator, * the list is made in advance */ @@ -2953,7 +2959,7 @@ void OBJECT_OT_duplicate(wmOperatorType *ot) * Use for drag & drop. * \{ */ -static int add_named_exec(bContext *C, wmOperator *op) +static int object_add_named_exec(bContext *C, wmOperator *op) { wmWindow *win = CTX_wm_window(C); const wmEvent *event = win ? win->eventstate : NULL; @@ -2976,7 +2982,7 @@ static int add_named_exec(bContext *C, wmOperator *op) } /* prepare dupli */ - basen = object_add_duplicate_internal(bmain, scene, view_layer, ob, dupflag); + basen = object_add_duplicate_internal(bmain, scene, view_layer, ob, dupflag, 0); if (basen == NULL) { BKE_report(op->reports, RPT_ERROR, "Object could not be duplicated"); @@ -3016,7 +3022,7 @@ void OBJECT_OT_add_named(wmOperatorType *ot) ot->idname = "OBJECT_OT_add_named"; /* api callbacks */ - ot->exec = add_named_exec; + ot->exec = object_add_named_exec; ot->poll = ED_operator_objectmode; /* flags */ @@ -3037,7 +3043,7 @@ void OBJECT_OT_add_named(wmOperatorType *ot) * * \{ */ -static bool join_poll(bContext *C) +static bool object_join_poll(bContext *C) { Object *ob = CTX_data_active_object(C); @@ -3053,7 +3059,7 @@ static bool join_poll(bContext *C) } } -static int join_exec(bContext *C, wmOperator *op) +static int object_join_exec(bContext *C, wmOperator *op) { Object *ob = CTX_data_active_object(C); @@ -3074,13 +3080,13 @@ static int join_exec(bContext *C, wmOperator *op) } if (ob->type == OB_MESH) { - return join_mesh_exec(C, op); + return ED_mesh_join_objects_exec(C, op); } else if (ELEM(ob->type, OB_CURVE, OB_SURF)) { - return join_curve_exec(C, op); + return ED_curve_join_objects_exec(C, op); } else if (ob->type == OB_ARMATURE) { - return join_armature_exec(C, op); + return ED_armature_join_objects_exec(C, op); } else if (ob->type == OB_GPENCIL) { return ED_gpencil_join_objects_exec(C, op); @@ -3097,8 +3103,8 @@ void OBJECT_OT_join(wmOperatorType *ot) ot->idname = "OBJECT_OT_join"; /* api callbacks */ - ot->exec = join_exec; - ot->poll = join_poll; + ot->exec = object_join_exec; + ot->poll = object_join_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -3141,7 +3147,7 @@ static int join_shapes_exec(bContext *C, wmOperator *op) } if (ob->type == OB_MESH) { - return join_mesh_shapes_exec(C, op); + return ED_mesh_shapes_join_objects_exec(C, op); } return OPERATOR_CANCELLED; diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index d522dcabae3..90dd16ea393 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -1771,15 +1771,8 @@ static void move_to_collection_menu_create(bContext *UNUSED(C), uiLayout *layout const char *name = BKE_collection_ui_name_get(menu->collection); UI_block_flag_enable(uiLayoutGetBlock(layout), UI_BLOCK_IS_FLIP); - uiItemIntO(layout, name, ICON_NONE, menu->ot->idname, "collection_index", menu->index); - uiItemS(layout); - - for (MoveToCollectionData *submenu = menu->submenus.first; submenu != NULL; - submenu = submenu->next) { - move_to_collection_menus_items(layout, submenu); - } - uiItemS(layout); + // uiItemS(layout); WM_operator_properties_create_ptr(&menu->ptr, menu->ot); RNA_int_set(&menu->ptr, "collection_index", menu->index); @@ -1787,6 +1780,15 @@ static void move_to_collection_menu_create(bContext *UNUSED(C), uiLayout *layout uiItemFullO_ptr( layout, menu->ot, "New Collection", ICON_ADD, menu->ptr.data, WM_OP_INVOKE_DEFAULT, 0, NULL); + + uiItemS(layout); + + uiItemIntO(layout, name, ICON_SCENE_DATA, menu->ot->idname, "collection_index", menu->index); + + for (MoveToCollectionData *submenu = menu->submenus.first; submenu != NULL; + submenu = submenu->next) { + move_to_collection_menus_items(layout, submenu); + } } static void move_to_collection_menus_items(uiLayout *layout, MoveToCollectionData *menu) diff --git a/source/blender/editors/object/object_modes.c b/source/blender/editors/object/object_modes.c index c518fd32c7f..5d4476ecb8c 100644 --- a/source/blender/editors/object/object_modes.c +++ b/source/blender/editors/object/object_modes.c @@ -116,7 +116,7 @@ bool ED_object_mode_compat_test(const Object *ob, eObjectMode mode) case OB_SURF: case OB_FONT: case OB_MBALL: - if (mode & (OB_MODE_EDIT)) { + if (mode & OB_MODE_EDIT) { return true; } break; diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index c04122edd36..5ea02de1e45 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -1145,7 +1145,7 @@ static void region_overlap_fix(ScrArea *area, ARegion *region) /* find overlapping previous region on same place */ for (ar1 = region->prev; ar1; ar1 = ar1->prev) { - if (ar1->flag & (RGN_FLAG_HIDDEN)) { + if (ar1->flag & RGN_FLAG_HIDDEN) { continue; } @@ -1194,7 +1194,7 @@ static void region_overlap_fix(ScrArea *area, ARegion *region) /* At this point, 'region' is in its final position and still open. * Make a final check it does not overlap any previous 'other side' region. */ for (ar1 = region->prev; ar1; ar1 = ar1->prev) { - if (ar1->flag & (RGN_FLAG_HIDDEN)) { + if (ar1->flag & RGN_FLAG_HIDDEN) { continue; } if (ELEM(ar1->alignment, RGN_ALIGN_FLOAT)) { @@ -2566,16 +2566,16 @@ void ED_region_panels_layout_ex(const bContext *C, /* only allow scrolling in vertical direction */ v2d->keepofs |= V2D_LOCKOFS_X | V2D_KEEPOFS_Y; v2d->keepofs &= ~(V2D_LOCKOFS_Y | V2D_KEEPOFS_X); - v2d->scroll &= ~(V2D_SCROLL_BOTTOM); - v2d->scroll |= (V2D_SCROLL_RIGHT); + v2d->scroll &= ~V2D_SCROLL_BOTTOM; + v2d->scroll |= V2D_SCROLL_RIGHT; } else { /* for now, allow scrolling in both directions (since layouts are optimized for vertical, * they often don't fit in horizontal layout) */ v2d->keepofs &= ~(V2D_LOCKOFS_X | V2D_LOCKOFS_Y | V2D_KEEPOFS_X | V2D_KEEPOFS_Y); - v2d->scroll |= (V2D_SCROLL_BOTTOM); - v2d->scroll &= ~(V2D_SCROLL_RIGHT); + v2d->scroll |= V2D_SCROLL_BOTTOM; + v2d->scroll &= ~V2D_SCROLL_RIGHT; } /* collect categories */ diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index b2243f2ccb9..5fcd10d0feb 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -4311,7 +4311,7 @@ static int match_region_with_redraws(int spacetype, } break; case SPACE_NODE: - if (redraws & (TIME_NODES)) { + if (redraws & TIME_NODES) { return 1; } break; diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index 2c6f708d82a..447d5373a48 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -909,7 +909,7 @@ PaintStroke *paint_stroke_new(bContext *C, stroke->zoom_2d = max_ff(zoomx, zoomy); if (stroke->stroke_mode == BRUSH_STROKE_INVERT) { - if (br->flag & (BRUSH_CURVE)) { + if (br->flag & BRUSH_CURVE) { RNA_enum_set(op->ptr, "mode", BRUSH_STROKE_NORMAL); } } @@ -1467,8 +1467,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) } else if (first_modal || /* regular dabs */ - (!(br->flag & (BRUSH_AIRBRUSH)) && - (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE))) || + (!(br->flag & BRUSH_AIRBRUSH) && (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE))) || /* airbrush */ ((br->flag & BRUSH_AIRBRUSH) && event->type == TIMER && event->customdata == stroke->timer)) { diff --git a/source/blender/editors/space_action/space_action.c b/source/blender/editors/space_action/space_action.c index e92ea906237..1e4919761a1 100644 --- a/source/blender/editors/space_action/space_action.c +++ b/source/blender/editors/space_action/space_action.c @@ -128,7 +128,7 @@ static SpaceLink *action_new(const ScrArea *area, const Scene *scene) region->v2d.minzoom = 0.01f; region->v2d.maxzoom = 50; region->v2d.scroll = (V2D_SCROLL_BOTTOM | V2D_SCROLL_HORIZONTAL_HANDLES); - region->v2d.scroll |= (V2D_SCROLL_RIGHT); + region->v2d.scroll |= V2D_SCROLL_RIGHT; region->v2d.keepzoom = V2D_LOCKZOOM_Y; region->v2d.keepofs = V2D_KEEPOFS_Y; region->v2d.align = V2D_ALIGN_NO_POS_Y; diff --git a/source/blender/editors/space_clip/clip_dopesheet_draw.c b/source/blender/editors/space_clip/clip_dopesheet_draw.c index 84ab5e6524b..c3aca95910b 100644 --- a/source/blender/editors/space_clip/clip_dopesheet_draw.c +++ b/source/blender/editors/space_clip/clip_dopesheet_draw.c @@ -118,7 +118,7 @@ void clip_draw_dopesheet_main(SpaceClip *sc, ARegion *region, Scene *scene) MovieTrackingDopesheet *dopesheet = &tracking->dopesheet; MovieTrackingDopesheetChannel *channel; float strip[4], selected_strip[4]; - float height = (dopesheet->tot_channel * CHANNEL_STEP) + (CHANNEL_HEIGHT); + float height = (dopesheet->tot_channel * CHANNEL_STEP) + CHANNEL_HEIGHT; uint keyframe_len = 0; @@ -305,7 +305,7 @@ void clip_draw_dopesheet_channels(const bContext *C, ARegion *region) MovieTracking *tracking = &clip->tracking; MovieTrackingDopesheet *dopesheet = &tracking->dopesheet; - int height = (dopesheet->tot_channel * CHANNEL_STEP) + (CHANNEL_HEIGHT); + int height = (dopesheet->tot_channel * CHANNEL_STEP) + CHANNEL_HEIGHT; if (height > BLI_rcti_size_y(&v2d->mask)) { /* don't use totrect set, as the width stays the same diff --git a/source/blender/editors/space_clip/space_clip.c b/source/blender/editors/space_clip/space_clip.c index a68e06951f7..6ec99730e09 100644 --- a/source/blender/editors/space_clip/space_clip.c +++ b/source/blender/editors/space_clip/space_clip.c @@ -96,7 +96,7 @@ static void init_preview_region(const Scene *scene, region->v2d.minzoom = 0.01f; region->v2d.maxzoom = 50; region->v2d.scroll = (V2D_SCROLL_BOTTOM | V2D_SCROLL_HORIZONTAL_HANDLES); - region->v2d.scroll |= (V2D_SCROLL_RIGHT); + region->v2d.scroll |= V2D_SCROLL_RIGHT; region->v2d.keepzoom = V2D_LOCKZOOM_Y; region->v2d.keepofs = V2D_KEEPOFS_Y; region->v2d.align = V2D_ALIGN_NO_POS_Y; diff --git a/source/blender/editors/space_console/space_console.c b/source/blender/editors/space_console/space_console.c index d8c097cad37..775159fbd79 100644 --- a/source/blender/editors/space_console/space_console.c +++ b/source/blender/editors/space_console/space_console.c @@ -70,7 +70,7 @@ static SpaceLink *console_new(const ScrArea *UNUSED(area), const Scene *UNUSED(s region->regiontype = RGN_TYPE_WINDOW; /* keep in sync with info */ - region->v2d.scroll |= (V2D_SCROLL_RIGHT); + region->v2d.scroll |= V2D_SCROLL_RIGHT; region->v2d.align |= V2D_ALIGN_NO_NEG_X | V2D_ALIGN_NO_NEG_Y; /* align bottom left */ region->v2d.keepofs |= V2D_LOCKOFS_X; region->v2d.keepzoom = (V2D_LOCKZOOM_X | V2D_LOCKZOOM_Y | V2D_LIMITZOOM | V2D_KEEPASPECT); diff --git a/source/blender/editors/space_image/image_edit.c b/source/blender/editors/space_image/image_edit.c index c9f2ec38354..cb0fdcf23ca 100644 --- a/source/blender/editors/space_image/image_edit.c +++ b/source/blender/editors/space_image/image_edit.c @@ -191,7 +191,7 @@ int ED_space_image_get_display_channel_mask(ImBuf *ibuf) result &= ~(SI_USE_ALPHA | SI_SHOW_ALPHA); } if (!zbuf) { - result &= ~(SI_SHOW_ZBUF); + result &= ~SI_SHOW_ZBUF; } if (!color) { result &= ~(SI_SHOW_R | SI_SHOW_G | SI_SHOW_B); diff --git a/source/blender/editors/space_info/space_info.c b/source/blender/editors/space_info/space_info.c index 04df0f0d4f0..ddf45eb4dce 100644 --- a/source/blender/editors/space_info/space_info.c +++ b/source/blender/editors/space_info/space_info.c @@ -77,7 +77,7 @@ static SpaceLink *info_new(const ScrArea *UNUSED(area), const Scene *UNUSED(scen region->regiontype = RGN_TYPE_WINDOW; /* keep in sync with console */ - region->v2d.scroll |= (V2D_SCROLL_RIGHT); + region->v2d.scroll |= V2D_SCROLL_RIGHT; region->v2d.align |= V2D_ALIGN_NO_NEG_X | V2D_ALIGN_NO_NEG_Y; /* align bottom left */ region->v2d.keepofs |= V2D_LOCKOFS_X; region->v2d.keepzoom = (V2D_LOCKZOOM_X | V2D_LOCKZOOM_Y | V2D_LIMITZOOM | V2D_KEEPASPECT); diff --git a/source/blender/editors/space_nla/space_nla.c b/source/blender/editors/space_nla/space_nla.c index f060693d9f4..9ec38a2e9eb 100644 --- a/source/blender/editors/space_nla/space_nla.c +++ b/source/blender/editors/space_nla/space_nla.c @@ -120,7 +120,7 @@ static SpaceLink *nla_new(const ScrArea *area, const Scene *scene) region->v2d.minzoom = 0.01f; region->v2d.maxzoom = 50; region->v2d.scroll = (V2D_SCROLL_BOTTOM | V2D_SCROLL_HORIZONTAL_HANDLES); - region->v2d.scroll |= (V2D_SCROLL_RIGHT); + region->v2d.scroll |= V2D_SCROLL_RIGHT; region->v2d.keepzoom = V2D_LOCKZOOM_Y; region->v2d.keepofs = V2D_KEEPOFS_Y; region->v2d.align = V2D_ALIGN_NO_POS_Y; diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index 01ac3a80871..ab50158c8bc 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -849,12 +849,35 @@ static void node_shader_buts_tex_environment_ex(uiLayout *layout, bContext *C, P static void node_shader_buts_tex_sky(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "sky_type", DEFAULT_FLAGS, "", ICON_NONE); - uiItemR(layout, ptr, "sun_direction", DEFAULT_FLAGS, "", ICON_NONE); - uiItemR(layout, ptr, "turbidity", DEFAULT_FLAGS, NULL, ICON_NONE); - if (RNA_enum_get(ptr, "sky_type") == SHD_SKY_NEW) { + if (RNA_enum_get(ptr, "sky_type") == SHD_SKY_PREETHAM) { + uiItemR(layout, ptr, "sun_direction", DEFAULT_FLAGS, "", ICON_NONE); + uiItemR(layout, ptr, "turbidity", DEFAULT_FLAGS, NULL, ICON_NONE); + } + if (RNA_enum_get(ptr, "sky_type") == SHD_SKY_HOSEK) { + uiItemR(layout, ptr, "sun_direction", DEFAULT_FLAGS, "", ICON_NONE); + uiItemR(layout, ptr, "turbidity", DEFAULT_FLAGS, NULL, ICON_NONE); uiItemR(layout, ptr, "ground_albedo", DEFAULT_FLAGS, NULL, ICON_NONE); } + if (RNA_enum_get(ptr, "sky_type") == SHD_SKY_NISHITA) { + uiItemR(layout, ptr, "sun_disc", DEFAULT_FLAGS, NULL, 0); + + if (RNA_boolean_get(ptr, "sun_disc")) { + uiItemR(layout, ptr, "sun_size", DEFAULT_FLAGS, NULL, ICON_NONE); + } + + uiLayout *col; + col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "sun_elevation", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "sun_rotation", DEFAULT_FLAGS, NULL, ICON_NONE); + + uiItemR(layout, ptr, "altitude", DEFAULT_FLAGS, NULL, ICON_NONE); + + col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "air_density", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "dust_density", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "ozone_density", DEFAULT_FLAGS, NULL, ICON_NONE); + } } static void node_shader_buts_tex_gradient(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/editors/space_node/node_draw.c b/source/blender/editors/space_node/node_draw.c index bd8950c5085..43d844df900 100644 --- a/source/blender/editors/space_node/node_draw.c +++ b/source/blender/editors/space_node/node_draw.c @@ -1323,7 +1323,7 @@ static void node_draw_basis(const bContext *C, UI_BTYPE_LABEL, 0, showname, - (int)(rct->xmin + (NODE_MARGIN_X)), + (int)(rct->xmin + NODE_MARGIN_X), (int)(rct->ymax - NODE_DY), (short)(iconofs - rct->xmin - 18.0f), (short)NODE_DY, diff --git a/source/blender/editors/space_outliner/outliner_collections.c b/source/blender/editors/space_outliner/outliner_collections.c index 6ff3ccc5bb4..131491fcc40 100644 --- a/source/blender/editors/space_outliner/outliner_collections.c +++ b/source/blender/editors/space_outliner/outliner_collections.c @@ -581,7 +581,8 @@ static int collection_duplicate_exec(bContext *C, wmOperator *op) "it won't be linked to any view layer"); } - BKE_collection_duplicate(bmain, parent, collection, true, !linked); + const eDupli_ID_Flags dupli_flags = USER_DUP_OBJECT | (linked ? 0 : U.dupflag); + BKE_collection_duplicate(bmain, parent, collection, dupli_flags, 0); DEG_relations_tag_update(bmain); WM_main_add_notifier(NC_SCENE | ND_LAYER, CTX_data_scene(C)); diff --git a/source/blender/editors/space_outliner/outliner_intern.h b/source/blender/editors/space_outliner/outliner_intern.h index f2b64bc2a4b..3a928485711 100644 --- a/source/blender/editors/space_outliner/outliner_intern.h +++ b/source/blender/editors/space_outliner/outliner_intern.h @@ -495,6 +495,7 @@ TreeElement *outliner_find_parent_element(ListBase *lb, TreeElement *outliner_find_id(struct SpaceOutliner *soops, ListBase *lb, const struct ID *id); TreeElement *outliner_find_posechannel(ListBase *lb, const struct bPoseChannel *pchan); TreeElement *outliner_find_editbone(ListBase *lb, const struct EditBone *ebone); +TreeElement *outliner_search_back_te(TreeElement *te, short idcode); struct ID *outliner_search_back(TreeElement *te, short idcode); bool outliner_tree_traverse(const SpaceOutliner *soops, ListBase *tree, diff --git a/source/blender/editors/space_outliner/outliner_select.c b/source/blender/editors/space_outliner/outliner_select.c index fa8422573ab..40fb5c7be3a 100644 --- a/source/blender/editors/space_outliner/outliner_select.c +++ b/source/blender/editors/space_outliner/outliner_select.c @@ -302,28 +302,32 @@ static void do_outliner_ebone_select_recursive(bArmature *arm, EditBone *ebone_p static eOLDrawState tree_element_set_active_object(bContext *C, Scene *scene, ViewLayer *view_layer, - SpaceOutliner *soops, + SpaceOutliner *UNUSED(soops), TreeElement *te, const eOLSetState set, bool recursive) { TreeStoreElem *tselem = TREESTORE(te); TreeStoreElem *parent_tselem = NULL; + TreeElement *parent_te = NULL; Scene *sce; Base *base; Object *ob = NULL; - TreeElement *te_ob = NULL; /* if id is not object, we search back */ - if (te->idcode == ID_OB) { + if (tselem->type == 0 && te->idcode == ID_OB) { ob = (Object *)tselem->id; } else { - ob = (Object *)outliner_search_back(te, ID_OB); - - /* Don't return when activating children of the previous active object. */ - if (ob == OBACT(view_layer) && set == OL_SETSEL_NONE) { - return OL_DRAWSEL_NONE; + parent_te = outliner_search_back_te(te, ID_OB); + if (parent_te) { + parent_tselem = TREESTORE(parent_te); + ob = (Object *)parent_tselem->id; + + /* Don't return when activating children of the previous active object. */ + if (ob == OBACT(view_layer) && set == OL_SETSEL_NONE) { + return OL_DRAWSEL_NONE; + } } } if (ob == NULL) { @@ -356,11 +360,6 @@ static eOLDrawState tree_element_set_active_object(bContext *C, } } - te_ob = outliner_find_id(soops, &soops->tree, (ID *)ob); - if (te_ob != NULL && te_ob != te) { - parent_tselem = TREESTORE(te_ob); - } - if (base) { if (set == OL_SETSEL_EXTEND) { /* swap select */ diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c index 80a63af3f42..4b780df1cbf 100644 --- a/source/blender/editors/space_outliner/outliner_tools.c +++ b/source/blender/editors/space_outliner/outliner_tools.c @@ -44,6 +44,7 @@ #include "DNA_world_types.h" #include "BLI_blenlib.h" +#include "BLI_ghash.h" #include "BLI_utildefines.h" #include "BKE_anim_data.h" @@ -689,12 +690,8 @@ static void object_deselect_cb(bContext *C, } } -static void outliner_object_delete(bContext *C, - ReportList *reports, - Scene *scene, - TreeStoreElem *tselem) +static void outliner_object_delete_fn(bContext *C, ReportList *reports, Scene *scene, Object *ob) { - Object *ob = (Object *)tselem->id; if (ob) { Main *bmain = CTX_data_main(C); if (ob->id.tag & LIB_TAG_INDIRECT) { @@ -860,7 +857,6 @@ void outliner_do_object_operation_ex(bContext *C, bool select_recurse) { TreeElement *te; - for (te = lb->first; te; te = te->next) { TreeStoreElem *tselem = TREESTORE(te); bool select_handled = false; @@ -1175,82 +1171,6 @@ static void outliner_do_data_operation( } } -static Base *outline_delete_hierarchy(bContext *C, ReportList *reports, Scene *scene, Base *base) -{ - Base *child_base, *base_next; - Object *parent; - ViewLayer *view_layer = CTX_data_view_layer(C); - - if (!base) { - return NULL; - } - - for (child_base = view_layer->object_bases.first; child_base; child_base = base_next) { - base_next = child_base->next; - for (parent = child_base->object->parent; parent && (parent != base->object); - parent = parent->parent) { - /* pass */ - } - if (parent) { - base_next = outline_delete_hierarchy(C, reports, scene, child_base); - } - } - - base_next = base->next; - - Main *bmain = CTX_data_main(C); - if (base->object->id.tag & LIB_TAG_INDIRECT) { - BKE_reportf(reports, - RPT_WARNING, - "Cannot delete indirectly linked object '%s'", - base->object->id.name + 2); - return base_next; - } - else if (BKE_library_ID_is_indirectly_used(bmain, base->object) && - ID_REAL_USERS(base->object) <= 1 && ID_EXTRA_USERS(base->object) == 0) { - BKE_reportf(reports, - RPT_WARNING, - "Cannot delete object '%s' from scene '%s', indirectly used objects need at least " - "one user", - base->object->id.name + 2, - scene->id.name + 2); - return base_next; - } - ED_object_base_free_and_unlink(CTX_data_main(C), scene, base->object); - return base_next; -} - -static void object_delete_hierarchy_cb(bContext *C, - ReportList *reports, - Scene *scene, - TreeElement *te, - TreeStoreElem *UNUSED(tsep), - TreeStoreElem *tselem, - void *UNUSED(user_data)) -{ - ViewLayer *view_layer = CTX_data_view_layer(C); - Base *base = (Base *)te->directdata; - Object *obedit = CTX_data_edit_object(C); - - if (!base) { - base = BKE_view_layer_base_find(view_layer, (Object *)tselem->id); - } - if (base) { - /* Check also library later. */ - for (; obedit && (obedit != base->object); obedit = obedit->parent) { - /* pass */ - } - if (obedit == base->object) { - ED_object_editmode_exit(C, EM_FREEDATA); - } - - outline_delete_hierarchy(C, reports, scene, base); - } - - DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); - WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); -} - static Base *outline_batch_delete_hierarchy( ReportList *reports, Main *bmain, ViewLayer *view_layer, Scene *scene, Base *base) { @@ -1303,21 +1223,16 @@ static Base *outline_batch_delete_hierarchy( return base_next; } -static void object_batch_delete_hierarchy_cb(bContext *C, +static void object_batch_delete_hierarchy_fn(bContext *C, ReportList *reports, Scene *scene, - TreeElement *te, - TreeStoreElem *UNUSED(tsep), - TreeStoreElem *tselem, - void *UNUSED(user_data)) + Object *ob) { ViewLayer *view_layer = CTX_data_view_layer(C); - Base *base = (Base *)te->directdata; Object *obedit = CTX_data_edit_object(C); - if (!base) { - base = BKE_view_layer_base_find(view_layer, (Object *)tselem->id); - } + Base *base = BKE_view_layer_base_find(view_layer, ob); + if (base) { /* Check also library later. */ for (; obedit && (obedit != base->object); obedit = obedit->parent) { @@ -1341,7 +1256,6 @@ enum { OL_OP_SELECT = 1, OL_OP_DESELECT, OL_OP_SELECT_HIERARCHY, - OL_OP_DELETE_HIERARCHY, OL_OP_REMAP, OL_OP_LOCALIZED, /* disabled, see below */ OL_OP_TOGVIS, @@ -1356,7 +1270,6 @@ static const EnumPropertyItem prop_object_op_types[] = { {OL_OP_SELECT, "SELECT", ICON_RESTRICT_SELECT_OFF, "Select", ""}, {OL_OP_DESELECT, "DESELECT", 0, "Deselect", ""}, {OL_OP_SELECT_HIERARCHY, "SELECT_HIERARCHY", 0, "Select Hierarchy", ""}, - {OL_OP_DELETE_HIERARCHY, "DELETE_HIERARCHY", 0, "Delete Hierarchy", ""}, {OL_OP_REMAP, "REMAP", 0, @@ -1370,7 +1283,6 @@ static const EnumPropertyItem prop_object_op_types[] = { static int outliner_object_operation_exec(bContext *C, wmOperator *op) { - struct wmMsgBus *mbus = CTX_wm_message_bus(C); Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); wmWindow *win = CTX_wm_window(C); @@ -1411,43 +1323,6 @@ static int outliner_object_operation_exec(bContext *C, wmOperator *op) str = "Deselect Objects"; selection_changed = true; } - else if (event == OL_OP_DELETE_HIERARCHY) { - ViewLayer *view_layer = CTX_data_view_layer(C); - const Base *basact_prev = BASACT(view_layer); - - /* Keeping old 'safe and slow' code for a bit (new one enabled on 28/01/2019). */ - if (G.debug_value == 666) { - outliner_do_object_operation_ex( - C, op->reports, scene, soops, &soops->tree, object_delete_hierarchy_cb, NULL, false); - } - else { - BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); - - outliner_do_object_operation_ex(C, - op->reports, - scene, - soops, - &soops->tree, - object_batch_delete_hierarchy_cb, - NULL, - false); - - BKE_id_multi_tagged_delete(bmain); - } - - /* XXX: See outliner_delete_exec comment below. */ - outliner_cleanup_tree(soops); - - DEG_relations_tag_update(bmain); - str = "Delete Object Hierarchy"; - DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); - WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); - if (basact_prev != BASACT(view_layer)) { - WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); - WM_msg_publish_rna_prop(mbus, &scene->id, view_layer, LayerObjects, active); - } - selection_changed = true; - } else if (event == OL_OP_REMAP) { outliner_do_libdata_operation(C, op->reports, scene, soops, &soops->tree, id_remap_cb, NULL); /* No undo push here, operator does it itself (since it's a modal one, the op_undo_depth trick @@ -1511,22 +1386,38 @@ void OUTLINER_OT_object_operation(wmOperatorType *ot) /** \name Delete Object/Collection Operator * \{ */ -static void outliner_objects_delete( - bContext *C, Scene *scene, SpaceOutliner *soops, ReportList *reports, ListBase *lb) +typedef void (*OutlinerDeleteFunc)(bContext *C, ReportList *reports, Scene *scene, Object *ob); + +static void outliner_do_object_delete(bContext *C, + ReportList *reports, + Scene *scene, + GSet *objects_to_delete, + OutlinerDeleteFunc delete_fn) { - LISTBASE_FOREACH (TreeElement *, te, lb) { - TreeStoreElem *tselem = TREESTORE(te); + GSetIterator objects_to_delete_iter; + GSET_ITER (objects_to_delete_iter, objects_to_delete) { + Object *ob = (Object *)BLI_gsetIterator_getKey(&objects_to_delete_iter); - if (tselem->flag & TSE_SELECTED) { - if (tselem->type == 0 && te->idcode == ID_OB) { - outliner_object_delete(C, reports, scene, tselem); - } - } + delete_fn(C, reports, scene, ob); + } +} - if (TSELEM_OPEN(tselem, soops)) { - outliner_objects_delete(C, scene, soops, reports, &te->subtree); - } +static TreeTraversalAction outliner_find_objects_to_delete(TreeElement *te, void *customdata) +{ + GSet *objects_to_delete = (GSet *)customdata; + TreeStoreElem *tselem = TREESTORE(te); + + if (outliner_is_collection_tree_element(te)) { + return TRAVERSE_CONTINUE; } + + if (tselem->type || (tselem->id == NULL) || (GS(tselem->id->name) != ID_OB)) { + return TRAVERSE_SKIP_CHILDS; + } + + BLI_gset_add(objects_to_delete, tselem->id); + + return TRAVERSE_CONTINUE; } static int outliner_delete_exec(bContext *C, wmOperator *op) @@ -1535,12 +1426,32 @@ static int outliner_delete_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); SpaceOutliner *soops = CTX_wm_space_outliner(C); struct wmMsgBus *mbus = CTX_wm_message_bus(C); - ViewLayer *view_layer = CTX_data_view_layer(C); const Base *basact_prev = BASACT(view_layer); - outliner_collection_delete(C, bmain, scene, op->reports, false); - outliner_objects_delete(C, scene, soops, op->reports, &soops->tree); + const bool delete_hierarchy = RNA_boolean_get(op->ptr, "hierarchy"); + + /* Get selected objects skipping duplicates to prevent deleting objects linked to multiple + * collections twice */ + GSet *objects_to_delete = BLI_gset_ptr_new(__func__); + outliner_tree_traverse( + soops, &soops->tree, 0, TSE_SELECTED, outliner_find_objects_to_delete, objects_to_delete); + + if (delete_hierarchy) { + BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); + + outliner_do_object_delete( + C, op->reports, scene, objects_to_delete, object_batch_delete_hierarchy_fn); + + BKE_id_multi_tagged_delete(bmain); + } + else { + outliner_do_object_delete(C, op->reports, scene, objects_to_delete, outliner_object_delete_fn); + } + + BLI_gset_free(objects_to_delete, NULL); + + outliner_collection_delete(C, bmain, scene, op->reports, delete_hierarchy); /* Tree management normally happens from draw_outliner(), but when * you're clicking too fast on Delete object from context menu in @@ -1577,6 +1488,11 @@ void OUTLINER_OT_delete(wmOperatorType *ot) /* flags */ ot->flag |= OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + PropertyRNA *prop = RNA_def_boolean( + ot->srna, "hierarchy", false, "Hierarchy", "Delete child objects and collections"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); } /** \} */ diff --git a/source/blender/editors/space_outliner/outliner_utils.c b/source/blender/editors/space_outliner/outliner_utils.c index a058c30cef2..5f19d8b8757 100644 --- a/source/blender/editors/space_outliner/outliner_utils.c +++ b/source/blender/editors/space_outliner/outliner_utils.c @@ -256,7 +256,7 @@ TreeElement *outliner_find_editbone(ListBase *lb, const EditBone *ebone) return NULL; } -ID *outliner_search_back(TreeElement *te, short idcode) +TreeElement *outliner_search_back_te(TreeElement *te, short idcode) { TreeStoreElem *tselem; te = te->parent; @@ -264,13 +264,26 @@ ID *outliner_search_back(TreeElement *te, short idcode) while (te) { tselem = TREESTORE(te); if (tselem->type == 0 && te->idcode == idcode) { - return tselem->id; + return te; } te = te->parent; } return NULL; } +ID *outliner_search_back(TreeElement *te, short idcode) +{ + TreeElement *search_te; + TreeStoreElem *tselem; + + search_te = outliner_search_back_te(te, idcode); + if (search_te) { + tselem = TREESTORE(search_te); + return tselem->id; + } + return NULL; +} + /** * Iterate over all tree elements (pre-order traversal), executing \a func callback for * each tree element matching the optional filters. diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index 9311cbed265..dc1fcfca8b4 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -673,6 +673,14 @@ int seq_effect_find_selected(Scene *scene, *r_selseq2 = seq2; *r_selseq3 = seq3; + /* TODO(Richard): This function needs some refactoring, this is just quick hack for T73828. */ + if (BKE_sequence_effect_get_num_inputs(type) < 3) { + *r_selseq3 = NULL; + } + if (BKE_sequence_effect_get_num_inputs(type) < 2) { + *r_selseq2 = NULL; + } + return 1; } @@ -1403,14 +1411,21 @@ static int sequencer_snap_exec(bContext *C, wmOperator *op) BKE_sequence_base_shuffle(ed->seqbasep, seq, scene); } } - else if (seq->type & SEQ_TYPE_EFFECT) { + } + + /* Recalculate bounds of effect strips. */ + for (seq = ed->seqbasep->first; seq; seq = seq->next) { + if (seq->type & SEQ_TYPE_EFFECT) { if (seq->seq1 && (seq->seq1->flag & SELECT)) { + BKE_sequencer_offset_animdata(scene, seq, (snap_frame - seq->startdisp)); BKE_sequence_calc(scene, seq); } else if (seq->seq2 && (seq->seq2->flag & SELECT)) { + BKE_sequencer_offset_animdata(scene, seq, (snap_frame - seq->startdisp)); BKE_sequence_calc(scene, seq); } else if (seq->seq3 && (seq->seq3->flag & SELECT)) { + BKE_sequencer_offset_animdata(scene, seq, (snap_frame - seq->startdisp)); BKE_sequence_calc(scene, seq); } } @@ -2218,6 +2233,11 @@ static int sequencer_reassign_inputs_exec(bContext *C, wmOperator *op) Sequence *seq1, *seq2, *seq3, *last_seq = BKE_sequencer_active_get(scene); const char *error_msg; + if (BKE_sequence_effect_get_num_inputs(last_seq->type) != 0) { + BKE_report(op->reports, RPT_ERROR, "Cannot reassign inputs: strip has no inputs"); + return OPERATOR_CANCELLED; + } + if (!seq_effect_find_selected( scene, last_seq, last_seq->type, &seq1, &seq2, &seq3, &error_msg)) { BKE_report(op->reports, RPT_ERROR, error_msg); diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c index 2686edd58a5..85b70354ab3 100644 --- a/source/blender/editors/space_sequencer/sequencer_select.c +++ b/source/blender/editors/space_sequencer/sequencer_select.c @@ -409,32 +409,15 @@ static int sequencer_select_exec(bContext *C, wmOperator *op) /* Select left, right or overlapping the current frame. */ if (side_of_frame) { /* Use different logic for this. */ - float x; if (extend == false) { ED_sequencer_deselect_all(scene); } - /* 10px margin around current frame to select under the current frame with mouse. */ - float margin = BLI_rctf_size_x(&v2d->cur) / BLI_rcti_size_x(&v2d->mask) * 10; - x = UI_view2d_region_to_view_x(v2d, mval[0]); - if (x >= CFRA - margin && x <= CFRA + margin) { - x = CFRA; - } + const float x = UI_view2d_region_to_view_x(v2d, mval[0]); SEQP_BEGIN (ed, seq) { - bool test = false; - /* FIXME(campbell): this functionality is only in the sequencer, - * either we should support this for all timeline views or remove it. */ - if ((x == CFRA) && (seq->startdisp <= CFRA) && (seq->enddisp >= CFRA)) { - /* Select overlapping the current frame. */ - test = true; - } - else if ((x < CFRA && seq->enddisp <= CFRA) || (x > CFRA && seq->startdisp >= CFRA)) { + if (((x < CFRA) && (seq->enddisp <= CFRA)) || ((x >= CFRA) && (seq->startdisp >= CFRA))) { /* Select left or right. */ - test = true; - } - - if (test) { seq->flag |= SELECT; recurs_sel_seq(seq); } @@ -1023,7 +1006,6 @@ static int sequencer_select_side_of_frame_exec(bContext *C, wmOperator *op) void SEQUENCER_OT_select_side_of_frame(wmOperatorType *ot) { static const EnumPropertyItem sequencer_select_left_right_types[] = { - {0, "OVERLAP", 0, "Overlap", "Select overlapping the current frame"}, {-1, "LEFT", 0, "Left", "Select to the left of the current frame"}, {1, "RIGHT", 0, "Right", "Select to the right of the current frame"}, {0, NULL, 0, NULL, NULL}, diff --git a/source/blender/editors/space_view3d/view3d_fly.c b/source/blender/editors/space_view3d/view3d_fly.c index 06d1a033a0d..506969443fd 100644 --- a/source/blender/editors/space_view3d/view3d_fly.c +++ b/source/blender/editors/space_view3d/view3d_fly.c @@ -55,6 +55,10 @@ #include "view3d_intern.h" /* own include */ +/* -------------------------------------------------------------------- */ +/** \name Modal Key-map + * \{ */ + /* NOTE: these defines are saved in keymap files, * do not change values but just add new ones */ enum { @@ -138,6 +142,12 @@ void fly_modal_keymap(wmKeyConfig *keyconf) WM_modalkeymap_assign(keymap, "VIEW3D_OT_fly"); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Internal Fly Structs + * \{ */ + typedef struct FlyInfo { /* context stuff */ RegionView3D *rv3d; @@ -205,6 +215,12 @@ typedef struct FlyInfo { } FlyInfo; +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Internal Fly Drawing + * \{ */ + /* prototypes */ #ifdef WITH_INPUT_NDOF static void flyApply_ndof(bContext *C, FlyInfo *fly, bool is_confirm); @@ -278,6 +294,12 @@ static void drawFlyPixel(const struct bContext *UNUSED(C), ARegion *UNUSED(regio immUnbindProgram(); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Internal Fly Logic + * \{ */ + /* FlyInfo->state */ enum { FLY_RUNNING = 0, @@ -1034,6 +1056,12 @@ static void flyApply_ndof(bContext *C, FlyInfo *fly, bool is_confirm) } #endif /* WITH_INPUT_NDOF */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Fly Operator + * \{ */ + static int fly_invoke(bContext *C, wmOperator *op, const wmEvent *event) { RegionView3D *rv3d = CTX_wm_region_view3d(C); @@ -1128,3 +1156,5 @@ void VIEW3D_OT_fly(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_BLOCKING; } + +/** \} */ diff --git a/source/blender/editors/space_view3d/view3d_header.c b/source/blender/editors/space_view3d/view3d_header.c index 829d793333e..f2e42cd1725 100644 --- a/source/blender/editors/space_view3d/view3d_header.c +++ b/source/blender/editors/space_view3d/view3d_header.c @@ -209,7 +209,7 @@ static void uiTemplatePaintModeSelection(uiLayout *layout, struct bContext *C) PointerRNA meshptr; RNA_pointer_create(ob->data, &RNA_Mesh, ob->data, &meshptr); - if (ob->mode & (OB_MODE_TEXTURE_PAINT)) { + if (ob->mode & OB_MODE_TEXTURE_PAINT) { uiItemR(layout, &meshptr, "use_paint_mask", UI_ITEM_R_ICON_ONLY, "", ICON_NONE); } else { diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index 1bdb8268c23..9e235d72f26 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -3007,7 +3007,7 @@ static bool do_meta_box_select(ViewContext *vc, const rcti *rect, const eSelectO continue; } - if (metaelem_id != (hitresult & 0xFFFF0000 & ~(MBALLSEL_ANY))) { + if (metaelem_id != (hitresult & 0xFFFF0000 & ~MBALLSEL_ANY)) { continue; } diff --git a/source/blender/editors/space_view3d/view3d_walk.c b/source/blender/editors/space_view3d/view3d_walk.c index 7aefd173953..50fa573423a 100644 --- a/source/blender/editors/space_view3d/view3d_walk.c +++ b/source/blender/editors/space_view3d/view3d_walk.c @@ -65,6 +65,10 @@ /* ensure the target position is one we can reach, see: T45771 */ #define USE_PIXELSIZE_NATIVE_SUPPORT +/* -------------------------------------------------------------------- */ +/** \name Modal Key-map + * \{ */ + /* NOTE: these defines are saved in keymap files, * do not change values but just add new ones */ enum { @@ -173,6 +177,12 @@ void walk_modal_keymap(wmKeyConfig *keyconf) WM_modalkeymap_assign(keymap, "VIEW3D_OT_walk"); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Internal Walk Structs + * \{ */ + typedef struct WalkTeleport { eWalkTeleportState state; float duration; /* from user preferences */ @@ -283,6 +293,12 @@ typedef struct WalkInfo { } WalkInfo; +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Internal Walk Drawing + * \{ */ + /* prototypes */ #ifdef WITH_INPUT_NDOF static void walkApply_ndof(bContext *C, WalkInfo *walk, bool is_confirm); @@ -340,6 +356,12 @@ static void drawWalkPixel(const struct bContext *UNUSED(C), ARegion *region, voi immUnbindProgram(); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Internal Walk Logic + * \{ */ + static void walk_navigation_mode_set(WalkInfo *walk, eWalkMethod mode) { if (mode == WALK_MODE_FREE) { @@ -1343,7 +1365,12 @@ static void walkApply_ndof(bContext *C, WalkInfo *walk, bool is_confirm) } #endif /* WITH_INPUT_NDOF */ -/****** walk operator ******/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Walk Operator + * \{ */ + static int walk_invoke(bContext *C, wmOperator *op, const wmEvent *event) { RegionView3D *rv3d = CTX_wm_region_view3d(C); @@ -1438,3 +1465,5 @@ void VIEW3D_OT_walk(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_BLOCKING; } + +/** \} */ diff --git a/source/blender/editors/transform/transform_constraints.c b/source/blender/editors/transform/transform_constraints.c index 838c1880881..97b14d5ddd2 100644 --- a/source/blender/editors/transform/transform_constraints.c +++ b/source/blender/editors/transform/transform_constraints.c @@ -79,6 +79,27 @@ static void projection_matrix_calc(const TransInfo *t, float r_pmtx[3][3]) } /* ************************** CONSTRAINTS ************************* */ +#define CONSTRAIN_EPSILON 0.0001f + +static void constraint_plane_calc(TransInfo *t, float r_plane[4]) +{ + const float *constraint_vector[2]; + int n = 0; + for (int i = 0; i < 3; i++) { + if (t->con.mode & (CON_AXIS0 << i)) { + constraint_vector[n++] = t->spacemtx[i]; + if (n == 2) { + break; + } + } + } + BLI_assert(n == 2); + + cross_v3_v3v3(r_plane, constraint_vector[0], constraint_vector[1]); + normalize_v3(r_plane); + r_plane[3] = -dot_v3v3(r_plane, t->center_global); +} + static void constraintValuesFinal(TransInfo *t, float vec[3]) { int mode = t->con.mode; @@ -297,32 +318,79 @@ static void axisProjection(const TransInfo *t, } /** - * Return true if the 2x axis are both aligned when projected into the view. - * In this case, we can't usefully project the cursor onto the plane. + * Snap to the intersection between the edge direction and the constraint plane. */ -static bool isPlaneProjectionViewAligned(const TransInfo *t) +static void constraint_plane_to_edge(const TransInfo *t, const float plane[4], float r_out[3]) { - const float eps = 0.001f; - const float *constraint_vector[2]; - int n = 0; - for (int i = 0; i < 3; i++) { - if (t->con.mode & (CON_AXIS0 << i)) { - constraint_vector[n++] = t->spacemtx[i]; - if (n == 2) { - break; - } - } + float lambda; + const float *edge_snap_point = t->tsnap.snapPoint; + const float *edge_dir = t->tsnap.snapNormal; + bool is_aligned = fabsf(dot_v3v3(edge_dir, plane)) < CONSTRAIN_EPSILON; + if (!is_aligned && isect_ray_plane_v3(edge_snap_point, edge_dir, plane, &lambda, false)) { + madd_v3_v3v3fl(r_out, edge_snap_point, edge_dir, lambda); + sub_v3_v3(r_out, t->tsnap.snapTarget); } - BLI_assert(n == 2); +} + +/** + * Snap to the nearest point between the snap point and the line that + * intersects the face plane with the constraint plane. + */ +static void constraint_plane_to_face(const TransInfo *t, const float plane[4], float r_out[3]) +{ + float face_plane[4], isect_orig[3], isect_dir[3]; + const float *face_snap_point = t->tsnap.snapPoint; + const float *face_normal = t->tsnap.snapNormal; + plane_from_point_normal_v3(face_plane, face_snap_point, face_normal); + bool is_aligned = fabsf(dot_v3v3(plane, face_plane)) > (1.0f - CONSTRAIN_EPSILON); + if (!is_aligned && isect_plane_plane_v3(plane, face_plane, isect_orig, isect_dir)) { + closest_to_ray_v3(r_out, face_snap_point, isect_orig, isect_dir); + sub_v3_v3(r_out, t->tsnap.snapTarget); + } +} - float view_to_plane[3], plane_normal[3]; +/** + * Snap to the nearest point on the axis to the edge/line element. + */ +static void constraint_axis_to_edge(const TransInfo *t, const float axis[3], float r_out[3]) +{ + float lambda; + const float *edge_snap_point = t->tsnap.snapPoint; + const float *edge_dir = t->tsnap.snapNormal; + bool is_aligned = fabsf(dot_v3v3(axis, edge_dir)) > (1.0f - CONSTRAIN_EPSILON); + if (!is_aligned && + isect_ray_ray_v3(t->tsnap.snapTarget, axis, edge_snap_point, edge_dir, &lambda, NULL)) { + mul_v3_v3fl(r_out, axis, lambda); + } +} - getViewVector(t, t->center_global, view_to_plane); +/** + * Snap to the intersection of the axis and the plane defined by the face. + */ +static void constraint_axis_to_face(const TransInfo *t, const float axis[3], float r_out[3]) +{ + float lambda; + float face_plane[4]; + const float *face_snap_point = t->tsnap.snapPoint; + const float *face_normal = t->tsnap.snapNormal; + plane_from_point_normal_v3(face_plane, face_snap_point, face_normal); + bool is_aligned = fabsf(dot_v3v3(face_normal, face_plane)) < CONSTRAIN_EPSILON; + if (!is_aligned && isect_ray_plane_v3(t->tsnap.snapTarget, axis, face_plane, &lambda, false)) { + mul_v3_v3fl(r_out, axis, lambda); + } +} - cross_v3_v3v3(plane_normal, constraint_vector[0], constraint_vector[1]); - normalize_v3(plane_normal); +/** + * Return true if the 2x axis are both aligned when projected into the view. + * In this case, we can't usefully project the cursor onto the plane. + */ +static bool isPlaneProjectionViewAligned(const TransInfo *t, float plane[4]) +{ + const float eps = 0.001f; + float view_to_plane[3]; + getViewVector(t, t->center_global, view_to_plane); - float factor = dot_v3v3(plane_normal, view_to_plane); + float factor = dot_v3v3(plane, view_to_plane); return fabsf(factor) < eps; } @@ -361,14 +429,31 @@ static void applyAxisConstraintVec( copy_v3_v3(out, in); if (!td && t->con.mode & CON_APPLY) { mul_m3_v3(t->con.pmtx, out); + bool is_snap_to_edge = false, is_snap_to_face = false; + if (activeSnap(t)) { + is_snap_to_edge = (t->tsnap.snapElem & SCE_SNAP_MODE_EDGE) != 0; + is_snap_to_face = (t->tsnap.snapElem & SCE_SNAP_MODE_FACE) != 0; + } - // With snap, a projection is alright, no need to correct for view alignment - if (!validSnap(t)) { + /* With snap points, a projection is alright, no adjustments needed. */ + if (!validSnap(t) || is_snap_to_edge || is_snap_to_face) { const int dims = getConstraintSpaceDimension(t); if (dims == 2) { if (!is_zero_v3(out)) { - if (!isPlaneProjectionViewAligned(t)) { - planeProjection(t, in, out); + float plane[4]; + constraint_plane_calc(t, plane); + + if (is_snap_to_edge) { + constraint_plane_to_edge(t, plane, out); + } + else if (is_snap_to_face) { + constraint_plane_to_face(t, plane, out); + } + else { + /* View alignment correction. */ + if (!isPlaneProjectionViewAligned(t, plane)) { + planeProjection(t, in, out); + } } } } @@ -384,7 +469,17 @@ static void applyAxisConstraintVec( else if (t->con.mode & CON_AXIS2) { copy_v3_v3(c, t->spacemtx[2]); } - axisProjection(t, c, in, out); + + if (is_snap_to_edge) { + constraint_axis_to_edge(t, c, out); + } + else if (is_snap_to_face) { + constraint_axis_to_face(t, c, out); + } + else { + /* View alignment correction. */ + axisProjection(t, c, in, out); + } } } postConstraintChecks(t, out); diff --git a/source/blender/freestyle/intern/python/BPy_Freestyle.cpp b/source/blender/freestyle/intern/python/BPy_Freestyle.cpp index e7fe660cf93..33078e6ba6a 100644 --- a/source/blender/freestyle/intern/python/BPy_Freestyle.cpp +++ b/source/blender/freestyle/intern/python/BPy_Freestyle.cpp @@ -267,7 +267,7 @@ static PyObject *Freestyle_evaluateCurveMappingF(PyObject * /*self*/, PyObject * BKE_curvemapping_initialize(cumap); /* disable extrapolation if enabled */ if ((cumap->flag & CUMA_EXTEND_EXTRAPOLATE)) { - cumap->flag &= ~(CUMA_EXTEND_EXTRAPOLATE); + cumap->flag &= ~CUMA_EXTEND_EXTRAPOLATE; BKE_curvemapping_changed(cumap, 0); } return PyFloat_FromDouble(BKE_curvemapping_evaluateF(cumap, cur, value)); diff --git a/source/blender/gpu/intern/gpu_draw.c b/source/blender/gpu/intern/gpu_draw.c index b03b0fc0b1e..d768ce373b6 100644 --- a/source/blender/gpu/intern/gpu_draw.c +++ b/source/blender/gpu/intern/gpu_draw.c @@ -1384,7 +1384,7 @@ static void gpu_free_image_immediate(Image *ima) } } - ima->gpuflag &= ~(IMA_GPU_MIPMAP_COMPLETE); + ima->gpuflag &= ~IMA_GPU_MIPMAP_COMPLETE; } void GPU_free_image(Image *ima) diff --git a/source/blender/io/collada/AnimationImporter.cpp b/source/blender/io/collada/AnimationImporter.cpp index edac84e2aaa..1de86425521 100644 --- a/source/blender/io/collada/AnimationImporter.cpp +++ b/source/blender/io/collada/AnimationImporter.cpp @@ -344,9 +344,11 @@ bool AnimationImporter::write_animation_list(const COLLADAFW::AnimationList *ani return true; } -/* \todo refactor read_node_transform to not automatically apply anything, +/** + * \todo refactor read_node_transform to not automatically apply anything, * but rather return the transform matrix, so caller can do with it what is - * necessary. Same for \ref get_node_mat */ + * necessary. Same for \ref get_node_mat + */ void AnimationImporter::read_node_transform(COLLADAFW::Node *node, Object *ob) { float mat[4][4]; diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h index e6800cad8f1..b27ee0ac7c1 100644 --- a/source/blender/makesdna/DNA_gpencil_types.h +++ b/source/blender/makesdna/DNA_gpencil_types.h @@ -808,10 +808,10 @@ typedef enum eGP_DrawMode { #define GPENCIL_ANY_EDIT_MODE(gpd) \ ((gpd) && ((gpd)->flag & \ (GP_DATA_STROKE_EDITMODE | GP_DATA_STROKE_SCULPTMODE | GP_DATA_STROKE_WEIGHTMODE))) -#define GPENCIL_PAINT_MODE(gpd) ((gpd) && (gpd->flag & (GP_DATA_STROKE_PAINTMODE))) +#define GPENCIL_PAINT_MODE(gpd) ((gpd) && (gpd->flag & GP_DATA_STROKE_PAINTMODE)) #define GPENCIL_SCULPT_MODE(gpd) ((gpd) && (gpd->flag & GP_DATA_STROKE_SCULPTMODE)) #define GPENCIL_WEIGHT_MODE(gpd) ((gpd) && (gpd->flag & GP_DATA_STROKE_WEIGHTMODE)) -#define GPENCIL_VERTEX_MODE(gpd) ((gpd) && (gpd->flag & (GP_DATA_STROKE_VERTEXMODE))) +#define GPENCIL_VERTEX_MODE(gpd) ((gpd) && (gpd->flag & GP_DATA_STROKE_VERTEXMODE)) #define GPENCIL_SCULPT_OR_WEIGHT_MODE(gpd) \ ((gpd) && ((gpd)->flag & (GP_DATA_STROKE_SCULPTMODE | GP_DATA_STROKE_WEIGHTMODE))) #define GPENCIL_NONE_EDIT_MODE(gpd) \ diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 4ff0e531168..993aad92564 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -844,6 +844,15 @@ typedef struct NodeTexSky { float sun_direction[3]; float turbidity; float ground_albedo; + float sun_size; + float sun_elevation; + float sun_rotation; + int altitude; + float air_density; + float dust_density; + float ozone_density; + char sun_disc; + char _pad[3]; } NodeTexSky; typedef struct NodeTexImage { @@ -1171,8 +1180,9 @@ enum { }; /* sky texture */ -#define SHD_SKY_OLD 0 -#define SHD_SKY_NEW 1 +#define SHD_SKY_PREETHAM 0 +#define SHD_SKY_HOSEK 1 +#define SHD_SKY_NISHITA 2 /* environment texture */ #define SHD_PROJ_EQUIRECTANGULAR 0 diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 4b6d78a1d14..ab33b3a58f2 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -1150,6 +1150,13 @@ typedef enum eDupli_ID_Flags { USER_DUP_HAIR = (1 << 14), USER_DUP_POINTCLOUD = (1 << 15), USER_DUP_VOLUME = (1 << 16), + + USER_DUP_OBDATA = (~0) & ((1 << 24) - 1), + + /* Those are not exposed as user preferences, only used internaly. */ + USER_DUP_OBJECT = (1 << 24), + /* USER_DUP_COLLECTION = (1 << 25), */ /* UNUSED, keep because we may implement. */ + } eDupli_ID_Flags; /** diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index 65c43ebc151..7247f468245 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -1465,6 +1465,7 @@ bool RNA_struct_override_matches(struct Main *bmain, struct PointerRNA *ptr_local, struct PointerRNA *ptr_reference, const char *root_path, + const size_t root_path_len, struct IDOverrideLibrary *override, const eRNAOverrideMatch flags, eRNAOverrideMatchResult *r_report_flags); diff --git a/source/blender/makesrna/intern/rna_access_compare_override.c b/source/blender/makesrna/intern/rna_access_compare_override.c index 265e83ddcba..28ee5eca723 100644 --- a/source/blender/makesrna/intern/rna_access_compare_override.c +++ b/source/blender/makesrna/intern/rna_access_compare_override.c @@ -18,6 +18,8 @@ * \ingroup RNA */ +#include <string.h> + #include "MEM_guardedalloc.h" #include "DNA_ID.h" @@ -28,7 +30,7 @@ #include "BLI_string.h" #include "BLI_utildefines.h" -// #define DEBUG_OVERRIDE_TIMEIT +//#define DEBUG_OVERRIDE_TIMEIT #ifdef DEBUG_OVERRIDE_TIMEIT # include "PIL_time_utildefines.h" @@ -180,6 +182,7 @@ static int rna_property_override_diff(Main *bmain, PropertyRNA *prop_a, PropertyRNA *prop_b, const char *rna_path, + const size_t rna_path_len, eRNACompareMode mode, IDOverrideLibrary *override, const int flags, @@ -191,7 +194,7 @@ bool RNA_property_equals( BLI_assert(ELEM(mode, RNA_EQ_STRICT, RNA_EQ_UNSET_MATCH_ANY, RNA_EQ_UNSET_MATCH_NONE)); return (rna_property_override_diff( - bmain, ptr_a, ptr_b, prop, NULL, NULL, NULL, mode, NULL, 0, NULL) == 0); + bmain, ptr_a, ptr_b, prop, NULL, NULL, NULL, 0, mode, NULL, 0, NULL) == 0); } bool RNA_struct_equals(Main *bmain, PointerRNA *ptr_a, PointerRNA *ptr_b, eRNACompareMode mode) @@ -249,6 +252,7 @@ static int rna_property_override_diff(Main *bmain, PropertyRNA *prop_a, PropertyRNA *prop_b, const char *rna_path, + const size_t rna_path_len, eRNACompareMode mode, IDOverrideLibrary *override, const int flags, @@ -363,6 +367,7 @@ static int rna_property_override_diff(Main *bmain, mode, override, rna_path, + rna_path_len, diff_flags, &override_changed); if (override_changed && r_report_flags) { @@ -568,6 +573,7 @@ bool RNA_struct_override_matches(Main *bmain, PointerRNA *ptr_local, PointerRNA *ptr_reference, const char *root_path, + const size_t root_path_len, IDOverrideLibrary *override, const eRNAOverrideMatch flags, eRNAOverrideMatchResult *r_report_flags) @@ -649,31 +655,51 @@ bool RNA_struct_override_matches(Main *bmain, #endif #define RNA_PATH_BUFFSIZE 8192 -#define RNA_PATH_PRINTF(_str, ...) \ - if (BLI_snprintf(rna_path, RNA_PATH_BUFFSIZE, (_str), __VA_ARGS__) >= RNA_PATH_BUFFSIZE) { \ - rna_path = BLI_sprintfN((_str), __VA_ARGS__); \ - } \ - (void)0 -#define RNA_PATH_FREE \ - if (rna_path != rna_path_buffer) \ - MEM_freeN(rna_path) char rna_path_buffer[RNA_PATH_BUFFSIZE]; char *rna_path = rna_path_buffer; + size_t rna_path_len = 0; /* XXX TODO this will have to be refined to handle collections insertions, and array items */ if (root_path) { + BLI_assert(strlen(root_path) == root_path_len); + + const char *prop_name = RNA_property_identifier(prop_local); + const size_t prop_name_len = strlen(prop_name); + /* Inlined building, much much more efficient. */ if (prop_local->magic == RNA_MAGIC) { - RNA_PATH_PRINTF("%s.%s", root_path, RNA_property_identifier(prop_local)); + rna_path_len = root_path_len + 1 + prop_name_len; + if (rna_path_len >= RNA_PATH_BUFFSIZE) { + rna_path = MEM_mallocN(rna_path_len + 1, __func__); + } + + memcpy(rna_path, root_path, root_path_len); + rna_path[root_path_len] = '.'; + memcpy(rna_path + root_path_len + 1, prop_name, prop_name_len); + rna_path[rna_path_len] = '\0'; } else { - RNA_PATH_PRINTF("%s[\"%s\"]", root_path, RNA_property_identifier(prop_local)); + rna_path_len = root_path_len + 2 + prop_name_len + 2; + if (rna_path_len >= RNA_PATH_BUFFSIZE) { + rna_path = MEM_mallocN(rna_path_len + 1, __func__); + } + + memcpy(rna_path, root_path, root_path_len); + rna_path[root_path_len] = '['; + rna_path[root_path_len + 1] = '"'; + memcpy(rna_path + root_path_len + 2, prop_name, prop_name_len); + rna_path[root_path_len + 2 + prop_name_len] = '"'; + rna_path[root_path_len + 2 + prop_name_len + 1] = ']'; + rna_path[rna_path_len] = '\0'; } } else { /* This is rather slow, but is not much called, so not really worth optimizing. */ rna_path = RNA_path_from_ID_to_property(ptr_local, prop_local); + if (rna_path != NULL) { + rna_path_len = strlen(rna_path); + } } if (rna_path == NULL) { continue; @@ -684,7 +710,10 @@ bool RNA_struct_override_matches(Main *bmain, IDOverrideLibraryProperty *op = BKE_lib_override_library_property_find(override, rna_path); if (ignore_overridden && op != NULL) { BKE_lib_override_library_operations_tag(op, IDOVERRIDE_LIBRARY_TAG_UNUSED, false); - RNA_PATH_FREE; + + if (rna_path != rna_path_buffer) { + MEM_freeN(rna_path); + } continue; } @@ -702,6 +731,7 @@ bool RNA_struct_override_matches(Main *bmain, prop_local, prop_reference, rna_path, + rna_path_len, RNA_EQ_STRICT, override, flags, @@ -769,17 +799,18 @@ bool RNA_struct_override_matches(Main *bmain, matching = false; if (!(do_create || do_restore)) { /* Since we have no 'changing' action allowed, we can break here. */ - MEM_SAFE_FREE(rna_path); + if (rna_path != rna_path_buffer) { + MEM_freeN(rna_path); + } break; } } } - RNA_PATH_FREE; - + if (rna_path != rna_path_buffer) { + MEM_freeN(rna_path); + } #undef RNA_PATH_BUFFSIZE -#undef RNA_PATH_PRINTF -#undef RNA_PATH_FREE } RNA_property_collection_end(&iter); diff --git a/source/blender/makesrna/intern/rna_define.c b/source/blender/makesrna/intern/rna_define.c index d6bedc61424..7919c014bb2 100644 --- a/source/blender/makesrna/intern/rna_define.c +++ b/source/blender/makesrna/intern/rna_define.c @@ -1324,7 +1324,7 @@ PropertyRNA *RNA_def_property(StructOrFunctionRNA *cont_, switch (type) { case PROP_BOOLEAN: if (DefRNA.preprocess) { - if ((subtype & ~(PROP_LAYER_MEMBER)) != PROP_NONE) { + if ((subtype & ~PROP_LAYER_MEMBER) != PROP_NONE) { CLOG_ERROR(&LOG, "subtype does not apply to 'PROP_BOOLEAN' \"%s.%s\"", CONTAINER_RNA_ID(cont), diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index 44f118a8744..0783addd78b 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -498,6 +498,7 @@ int rna_property_override_diff_default(struct Main *bmain, const int mode, struct IDOverrideLibrary *override, const char *rna_path, + const size_t rna_path_len, const int flags, bool *r_override_changed); diff --git a/source/blender/makesrna/intern/rna_internal_types.h b/source/blender/makesrna/intern/rna_internal_types.h index 0e3fc851a9b..bc83ed25ce5 100644 --- a/source/blender/makesrna/intern/rna_internal_types.h +++ b/source/blender/makesrna/intern/rna_internal_types.h @@ -176,6 +176,7 @@ typedef int (*RNAPropOverrideDiff)(struct Main *bmain, const int mode, struct IDOverrideLibrary *override, const char *rna_path, + const size_t rna_path_len, const int flags, bool *r_override_changed); diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index ffd9bb772cc..4d909ee2874 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -5021,7 +5021,7 @@ static void rna_def_modifier_weightvgedit(BlenderRNA *brna) {MOD_WVG_MAPPING_RANDOM, "RANDOM", ICON_RNDCURVE, "Random", ""}, {MOD_WVG_MAPPING_STEP, "STEP", - ICON_NOCURVE /* Would need a better icon... */, + ICON_IPO_CONSTANT, "Median Step", "Map all values below 0.5 to 0.0, and all others to 1.0"}, {0, NULL, 0, NULL, NULL}, @@ -5272,7 +5272,7 @@ static void rna_def_modifier_weightvgproximity(BlenderRNA *brna) {MOD_WVG_MAPPING_RANDOM, "RANDOM", ICON_RNDCURVE, "Random", ""}, {MOD_WVG_MAPPING_STEP, "STEP", - ICON_NOCURVE /* Would need a better icon... */, + ICON_IPO_CONSTANT, "Median Step", "Map all values below 0.5 to 0.0, and all others to 1.0"}, {0, NULL, 0, NULL, NULL}, diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 32999c91fad..13dee92c1cb 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -4405,8 +4405,9 @@ static void def_sh_tex(StructRNA *srna) static void def_sh_tex_sky(StructRNA *srna) { static const EnumPropertyItem prop_sky_type[] = { - {SHD_SKY_OLD, "PREETHAM", 0, "Preetham", ""}, - {SHD_SKY_NEW, "HOSEK_WILKIE", 0, "Hosek / Wilkie", ""}, + {SHD_SKY_PREETHAM, "PREETHAM", 0, "Preetham", "Preetham 1999"}, + {SHD_SKY_HOSEK, "HOSEK_WILKIE", 0, "Hosek / Wilkie", "Hosek / Wilkie 2012"}, + {SHD_SKY_NISHITA, "NISHITA", 0, "Nishita", "Nishita 1993 improved"}, {0, NULL, 0, NULL, NULL}, }; static float default_dir[3] = {0.0f, 0.0f, 1.0f}; @@ -4419,7 +4420,7 @@ static void def_sh_tex_sky(StructRNA *srna) prop = RNA_def_property(srna, "sky_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "sky_model"); RNA_def_property_enum_items(prop, prop_sky_type); - RNA_def_property_ui_text(prop, "Sky Type", ""); + RNA_def_property_ui_text(prop, "Sky Type", "Which sky model should be used"); RNA_def_property_update(prop, 0, "rna_Node_update"); prop = RNA_def_property(srna, "sun_direction", PROP_FLOAT, PROP_DIRECTION); @@ -4439,6 +4440,54 @@ static void def_sh_tex_sky(StructRNA *srna) RNA_def_property_ui_text( prop, "Ground Albedo", "Ground color that is subtly reflected in the sky"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + + prop = RNA_def_property(srna, "sun_disc", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_ui_text(prop, "Sun Disc", "Include the sun itself in the output"); + RNA_def_property_boolean_sdna(prop, NULL, "sun_disc", 1); + RNA_def_property_boolean_default(prop, true); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + + prop = RNA_def_property(srna, "sun_size", PROP_FLOAT, PROP_ANGLE); + RNA_def_property_ui_text(prop, "Sun Size", "Size of sun disc (angular diameter)"); + RNA_def_property_range(prop, 0.0f, M_PI_2); + RNA_def_property_float_default(prop, DEG2RADF(0.545)); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + + prop = RNA_def_property(srna, "sun_elevation", PROP_FLOAT, PROP_ANGLE); + RNA_def_property_ui_text(prop, "Sun Elevation", "Angle between sun and horizon"); + RNA_def_property_range(prop, -M_PI_2, M_PI_2); + RNA_def_property_float_default(prop, M_PI_2); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + + prop = RNA_def_property(srna, "sun_rotation", PROP_FLOAT, PROP_ANGLE); + RNA_def_property_ui_text(prop, "Sun Rotation", "Rotation of sun around zenith"); + RNA_def_property_range(prop, 0.0f, 2.0f * M_PI); + RNA_def_property_float_default(prop, 0.0f); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + + prop = RNA_def_property(srna, "altitude", PROP_INT, PROP_NONE); + RNA_def_property_ui_text(prop, "Altitude", "Altitude height from sea level in meters"); + RNA_def_property_range(prop, 0, 60000); + RNA_def_property_int_default(prop, 0); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + + prop = RNA_def_property(srna, "air_density", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_ui_text(prop, "Air", "Density of air molecules (Rayleigh scattering)"); + RNA_def_property_range(prop, 0.0f, 10.0f); + RNA_def_property_float_default(prop, 1.0f); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + + prop = RNA_def_property(srna, "dust_density", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_ui_text(prop, "Dust", "Density of dust and water molecules (Mie scattering)"); + RNA_def_property_range(prop, 0.0f, 10.0f); + RNA_def_property_float_default(prop, 1.0f); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + + prop = RNA_def_property(srna, "ozone_density", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_ui_text(prop, "Ozone", "Density of Ozone layer (Ozone absorption)"); + RNA_def_property_range(prop, 0.0f, 10.0f); + RNA_def_property_float_default(prop, 1.0f); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } static const EnumPropertyItem sh_tex_prop_interpolation_items[] = { diff --git a/source/blender/makesrna/intern/rna_rna.c b/source/blender/makesrna/intern/rna_rna.c index d7e93975d31..bf1dcfbbf25 100644 --- a/source/blender/makesrna/intern/rna_rna.c +++ b/source/blender/makesrna/intern/rna_rna.c @@ -1248,6 +1248,7 @@ static int rna_property_override_diff_propptr(Main *bmain, const bool no_prop_name, IDOverrideLibrary *override, const char *rna_path, + size_t rna_path_len, const char *rna_itemname_a, const char *rna_itemname_b, const int rna_itemindex_a, @@ -1324,16 +1325,7 @@ static int rna_property_override_diff_propptr(Main *bmain, char extended_rna_path_buffer[RNA_PATH_BUFFSIZE]; char *extended_rna_path = extended_rna_path_buffer; - -# define RNA_PATH_PRINTF(_str, ...) \ - if (BLI_snprintf(extended_rna_path_buffer, RNA_PATH_BUFFSIZE, (_str), __VA_ARGS__) >= \ - RNA_PATH_BUFFSIZE - 1) { \ - extended_rna_path = BLI_sprintfN((_str), __VA_ARGS__); \ - } \ - (void)0 -# define RNA_PATH_FREE() \ - if (extended_rna_path != extended_rna_path_buffer && extended_rna_path != rna_path) \ - MEM_freeN(extended_rna_path) + size_t extended_rna_path_len = 0; /* There may be a propname defined in some cases, while no actual name set * (e.g. happens with point cache), in that case too we want to fall back to index. @@ -1342,31 +1334,82 @@ static int rna_property_override_diff_propptr(Main *bmain, if ((rna_itemname_a != NULL && rna_itemname_a[0] != '\0') && (rna_itemname_b != NULL && rna_itemname_b[0] != '\0')) { BLI_assert(STREQ(rna_itemname_a, rna_itemname_b)); + char esc_item_name[RNA_PATH_BUFFSIZE]; - BLI_strescape(esc_item_name, rna_itemname_a, RNA_PATH_BUFFSIZE); - RNA_PATH_PRINTF("%s[\"%s\"]", rna_path, esc_item_name); + const size_t esc_item_name_len = BLI_strescape( + esc_item_name, rna_itemname_a, RNA_PATH_BUFFSIZE); + extended_rna_path_len = rna_path_len + 2 + esc_item_name_len + 2; + if (extended_rna_path_len >= RNA_PATH_BUFFSIZE) { + extended_rna_path = MEM_mallocN(extended_rna_path_len + 1, __func__); + } + + memcpy(extended_rna_path, rna_path, rna_path_len); + extended_rna_path[rna_path_len] = '['; + extended_rna_path[rna_path_len + 1] = '"'; + memcpy(extended_rna_path + rna_path_len + 2, esc_item_name, esc_item_name_len); + extended_rna_path[rna_path_len + 2 + esc_item_name_len] = '"'; + extended_rna_path[rna_path_len + 2 + esc_item_name_len + 1] = ']'; + extended_rna_path[extended_rna_path_len] = '\0'; } else if (rna_itemindex_a != -1) { /* Based on index... */ BLI_assert(rna_itemindex_a == rna_itemindex_b); - RNA_PATH_PRINTF("%s[%d]", rna_path, rna_itemindex_a); + + /* low-level specific highly-efficient conversion of positive integer to string. */ + char item_index_buff[32]; + size_t item_index_buff_len = 0; + if (rna_itemindex_a == 0) { + item_index_buff[0] = '0'; + item_index_buff_len = 1; + } + else { + uint index; + for (index = rna_itemindex_a; + index > 0 && item_index_buff_len < sizeof(item_index_buff); + index /= 10) { + item_index_buff[item_index_buff_len++] = '0' + (char)(index % 10); + } + BLI_assert(index == 0); + } + + extended_rna_path_len = rna_path_len + item_index_buff_len + 2; + if (extended_rna_path_len >= RNA_PATH_BUFFSIZE) { + extended_rna_path = MEM_mallocN(extended_rna_path_len + 1, __func__); + } + + memcpy(extended_rna_path, rna_path, rna_path_len); + extended_rna_path[rna_path_len] = '['; + for (size_t i = 1; i <= item_index_buff_len; i++) { + /* The first loop above generated inverted string representation of our index number. + */ + extended_rna_path[rna_path_len + i] = item_index_buff[item_index_buff_len - i]; + } + extended_rna_path[rna_path_len + 1 + item_index_buff_len] = ']'; + extended_rna_path[extended_rna_path_len] = '\0'; } else { extended_rna_path = (char *)rna_path; + extended_rna_path_len = rna_path_len; } } eRNAOverrideMatchResult report_flags = 0; - const bool match = RNA_struct_override_matches( - bmain, propptr_a, propptr_b, extended_rna_path, override, flags, &report_flags); + const bool match = RNA_struct_override_matches(bmain, + propptr_a, + propptr_b, + extended_rna_path, + extended_rna_path_len, + override, + flags, + &report_flags); if (r_override_changed && (report_flags & RNA_OVERRIDE_MATCH_RESULT_CREATED) != 0) { *r_override_changed = true; } - RNA_PATH_FREE(); + if (extended_rna_path != extended_rna_path_buffer && extended_rna_path != rna_path) { + MEM_freeN(extended_rna_path); + } # undef RNA_PATH_BUFFSIZE -# undef RNA_PATH_PRINTF -# undef RNA_PATH_FREE return !match; } @@ -1395,6 +1438,7 @@ int rna_property_override_diff_default(Main *bmain, const int mode, IDOverrideLibrary *override, const char *rna_path, + const size_t rna_path_len, const int flags, bool *r_override_changed) { @@ -1684,6 +1728,7 @@ int rna_property_override_diff_default(Main *bmain, no_prop_name, override, rna_path, + rna_path_len, NULL, NULL, -1, @@ -1844,6 +1889,7 @@ int rna_property_override_diff_default(Main *bmain, no_prop_name, override, rna_path, + rna_path_len, propname_a, propname_b, idx_a, diff --git a/source/blender/makesrna/intern/rna_sequencer_api.c b/source/blender/makesrna/intern/rna_sequencer_api.c index 6c8f51f97a1..e04dcd34663 100644 --- a/source/blender/makesrna/intern/rna_sequencer_api.c +++ b/source/blender/makesrna/intern/rna_sequencer_api.c @@ -447,6 +447,21 @@ static void rna_SequenceElements_pop(ID *id, Sequence *seq, ReportList *reports, WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, scene); } +static void rna_Sequence_invalidate_cache_rnafunc(ID *id, Sequence *self, int type) +{ + switch (type) { + case SEQ_CACHE_STORE_RAW: + BKE_sequence_invalidate_cache_raw((Scene *)id, self); + break; + case SEQ_CACHE_STORE_PREPROCESSED: + BKE_sequence_invalidate_cache_preprocessed((Scene *)id, self); + break; + case SEQ_CACHE_STORE_COMPOSITE: + BKE_sequence_invalidate_cache_composite((Scene *)id, self); + break; + } +} + #else void RNA_api_sequence_strip(StructRNA *srna) @@ -454,6 +469,13 @@ void RNA_api_sequence_strip(StructRNA *srna) FunctionRNA *func; PropertyRNA *parm; + static const EnumPropertyItem seq_cahce_type_items[] = { + {SEQ_CACHE_STORE_RAW, "RAW", 0, "Raw", ""}, + {SEQ_CACHE_STORE_PREPROCESSED, "PREPROCESSED", 0, "Preprocessed", ""}, + {SEQ_CACHE_STORE_COMPOSITE, "COMPOSITE", 0, "Composite", ""}, + {0, NULL, 0, NULL, NULL}, + }; + func = RNA_def_function(srna, "update", "rna_Sequence_update_rnafunc"); RNA_def_function_flag(func, FUNC_USE_SELF_ID); RNA_def_function_ui_description(func, "Update the strip dimensions"); @@ -479,6 +501,12 @@ void RNA_api_sequence_strip(StructRNA *srna) RNA_def_function_flag(func, FUNC_USE_REPORTS); parm = RNA_def_pointer(func, "other", "Sequence", "Other", ""); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + + func = RNA_def_function(srna, "invalidate_cache", "rna_Sequence_invalidate_cache_rnafunc"); + RNA_def_function_flag(func, FUNC_USE_SELF_ID); + RNA_def_function_ui_description(func, "Invalidate Cached images for strip and all dependant strips. "); + parm = RNA_def_enum(func, "type", seq_cahce_type_items, 0, "Type", "Cache Type"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); } void RNA_api_sequence_elements(BlenderRNA *brna, PropertyRNA *cprop) diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 49a0121dadb..c18c08a87dc 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -5044,7 +5044,7 @@ static void rna_def_userdef_edit(BlenderRNA *brna) prop = RNA_def_property(srna, "use_duplicate_action", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "dupflag", USER_DUP_ACT); RNA_def_property_ui_text( - prop, "Duplicate Action", "Causes actions to be duplicated with the object"); + prop, "Duplicate Action", "Causes actions to be duplicated with the data-blocks"); prop = RNA_def_property(srna, "use_duplicate_particle", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "dupflag", USER_DUP_PSYS); diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c index 6749aa9495a..bfd99c01551 100644 --- a/source/blender/makesrna/intern/rna_wm.c +++ b/source/blender/makesrna/intern/rna_wm.c @@ -251,7 +251,7 @@ const EnumPropertyItem rna_enum_event_type_items[] = { {EVT_RETKEY, "RET", 0, "Return", "Enter"}, {EVT_SPACEKEY, "SPACE", 0, "Spacebar", "Space"}, {EVT_LINEFEEDKEY, "LINE_FEED", 0, "Line Feed", ""}, - {EVT_BACKSPACEKEY, "BACK_SPACE", 0, "Back Space", "BkSpace"}, + {EVT_BACKSPACEKEY, "BACK_SPACE", 0, "Backspace", "BkSpace"}, {EVT_DELKEY, "DEL", 0, "Delete", "Del"}, {EVT_SEMICOLONKEY, "SEMI_COLON", 0, ";", ""}, {EVT_PERIODKEY, "PERIOD", 0, ".", ""}, diff --git a/source/blender/modifiers/intern/MOD_ui_common.c b/source/blender/modifiers/intern/MOD_ui_common.c index cc0d3d8ccee..0d2c9e4af2c 100644 --- a/source/blender/modifiers/intern/MOD_ui_common.c +++ b/source/blender/modifiers/intern/MOD_ui_common.c @@ -222,19 +222,29 @@ static bool modifier_can_delete(ModifierData *md) return true; } -static void modifier_ops_extra_draw(bContext *UNUSED(C), uiLayout *layout, void *md_v) +static void modifier_ops_extra_draw(bContext *C, uiLayout *layout, void *md_v) { + PointerRNA op_ptr; + uiLayout *row; ModifierData *md = (ModifierData *)md_v; + PointerRNA ptr; + Object *ob = get_modifier_object(C); + RNA_pointer_create(&ob->id, &RNA_Modifier, md, &ptr); + uiLayoutSetContextPointer(layout, "modifier", &ptr); uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_DEFAULT); + uiLayoutSetUnitsX(layout, 4.0f); + + /* Apply. */ uiItemEnumO(layout, "OBJECT_OT_modifier_apply", CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Apply"), - 0, + ICON_CHECKMARK, "apply_as", MODIFIER_APPLY_DATA); + /* Apply as shapekey. */ if (BKE_modifier_is_same_topology(md) && !BKE_modifier_is_non_geometrical(md)) { uiItemEnumO(layout, "OBJECT_OT_modifier_apply", @@ -244,6 +254,7 @@ static void modifier_ops_extra_draw(bContext *UNUSED(C), uiLayout *layout, void MODIFIER_APPLY_SHAPE); } + /* Duplicate. */ if (!ELEM(md->type, eModifierType_Fluidsim, eModifierType_Softbody, @@ -256,17 +267,42 @@ static void modifier_ops_extra_draw(bContext *UNUSED(C), uiLayout *layout, void "OBJECT_OT_modifier_copy"); } - if (modifier_can_delete(md) && !modifier_is_simulation(md)) { - uiItemO(layout, - CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Delete"), - ICON_X, - "OBJECT_OT_modifier_remove"); + uiItemS(layout); + + /* Move to first. */ + row = uiLayoutColumn(layout, false); + uiItemFullO(row, + "OBJECT_OT_modifier_move_to_index", + IFACE_("Move to First"), + ICON_TRIA_UP, + NULL, + WM_OP_INVOKE_DEFAULT, + 0, + &op_ptr); + RNA_int_set(&op_ptr, "index", 0); + if (!md->prev) { + uiLayoutSetEnabled(row, false); + } + + /* Move to last. */ + row = uiLayoutColumn(layout, false); + uiItemFullO(row, + "OBJECT_OT_modifier_move_to_index", + IFACE_("Move to Last"), + ICON_TRIA_DOWN, + NULL, + WM_OP_INVOKE_DEFAULT, + 0, + &op_ptr); + RNA_int_set(&op_ptr, "index", BLI_listbase_count(&ob->modifiers) - 1); + if (!md->next) { + uiLayoutSetEnabled(row, false); } } static void modifier_panel_header(const bContext *C, Panel *panel) { - uiLayout *row, *sub; + uiLayout *row, *sub, *name_row; uiLayout *layout = panel->layout; PointerRNA ptr; @@ -280,32 +316,22 @@ static void modifier_panel_header(const bContext *C, Panel *panel) const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type); Scene *scene = CTX_data_scene(C); int index = panel->runtime.list_index; - bool narrow_panel = (panel->sizex < UI_UNIT_X * 8 && panel->sizex != 0); /* Modifier Icon. */ - row = uiLayoutRow(layout, false); + sub = uiLayoutRow(layout, true); if (mti->isDisabled && mti->isDisabled(scene, md, 0)) { - uiLayoutSetRedAlert(row, true); + uiLayoutSetRedAlert(sub, true); } - uiItemL(row, "", RNA_struct_ui_icon(ptr.type)); + uiItemL(sub, "", RNA_struct_ui_icon(ptr.type)); - /* Modifier Name. */ - if (!narrow_panel) { - uiItemR(layout, &ptr, "name", 0, "", ICON_NONE); - } + row = uiLayoutRow(layout, true); - /* Switch context buttons. */ - if (modifier_is_simulation(md) == 1) { - uiItemStringO( - layout, "", ICON_PROPERTIES, "WM_OT_properties_context_change", "context", "PHYSICS"); - } - else if (modifier_is_simulation(md) == 2) { - uiItemStringO( - layout, "", ICON_PROPERTIES, "WM_OT_properties_context_change", "context", "PARTICLES"); - } + /* Modifier Name. + * Count how many buttons are added to the header to check if there is enough space. */ + int buttons_number = 0; + name_row = uiLayoutRow(row, true); - /* Mode switching buttons. */ - row = uiLayoutRow(layout, true); + /* Display mode switching buttons. */ if (ob->type == OB_MESH) { int last_cage_index; int cage_index = BKE_modifiers_get_cage_index(scene, ob, &last_cage_index, 0); @@ -315,12 +341,14 @@ static void modifier_panel_header(const bContext *C, Panel *panel) uiLayoutSetActive(sub, false); } uiItemR(sub, &ptr, "show_on_cage", 0, "", ICON_NONE); + buttons_number++; } } /* Tessellation point for curve-typed objects. */ else if (ELEM(ob->type, OB_CURVE, OB_SURF, OB_FONT)) { if (mti->type != eModifierTypeType_Constructive) { /* Constructive modifiers tessellates curve before applying. */ - uiItemR(layout, &ptr, "use_apply_on_spline", 0, "", ICON_NONE); + uiItemR(row, &ptr, "use_apply_on_spline", 0, "", ICON_NONE); + buttons_number++; } } /* Collision and Surface are always enabled, hide buttons. */ @@ -330,15 +358,45 @@ static void modifier_panel_header(const bContext *C, Panel *panel) sub = uiLayoutRow(row, true); uiLayoutSetActive(sub, (md->mode & eModifierMode_Realtime)); uiItemR(sub, &ptr, "show_in_editmode", 0, "", ICON_NONE); + buttons_number++; } uiItemR(row, &ptr, "show_viewport", 0, "", ICON_NONE); uiItemR(row, &ptr, "show_render", 0, "", ICON_NONE); + buttons_number += 2; } - row = uiLayoutRow(layout, false); + /* Extra operators menu. */ uiItemMenuF(row, "", ICON_DOWNARROW_HLT, modifier_ops_extra_draw, md); - /* Some padding at the end, so the buttons aren't too close to the drag button. */ + /* Delete button. */ + if (modifier_can_delete(md) && !modifier_is_simulation(md)) { + sub = uiLayoutRow(row, false); + uiLayoutSetEmboss(sub, UI_EMBOSS_NONE); + uiItemO(sub, "", ICON_X, "OBJECT_OT_modifier_remove"); + buttons_number++; + } + + /* Switch context buttons. */ + if (modifier_is_simulation(md) == 1) { + uiItemStringO( + row, "", ICON_PROPERTIES, "WM_OT_properties_context_change", "context", "PHYSICS"); + buttons_number++; + } + else if (modifier_is_simulation(md) == 2) { + uiItemStringO( + row, "", ICON_PROPERTIES, "WM_OT_properties_context_change", "context", "PARTICLES"); + buttons_number++; + } + + bool display_name = (panel->sizex / UI_UNIT_X - buttons_number > 5) || (panel->sizex == 0); + if (display_name) { + uiItemR(name_row, &ptr, "name", 0, "", ICON_NONE); + } + else { + uiLayoutSetAlignment(row, UI_LAYOUT_ALIGN_RIGHT); + } + + /* Extra padding for delete button. */ uiItemS(layout); } diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_sky.c b/source/blender/nodes/shader/nodes/node_shader_tex_sky.c index cb91e086cf4..0daa948c139 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_sky.c +++ b/source/blender/nodes/shader/nodes/node_shader_tex_sky.c @@ -41,8 +41,15 @@ static void node_shader_init_tex_sky(bNodeTree *UNUSED(ntree), bNode *node) tex->sun_direction[2] = 1.0f; tex->turbidity = 2.2f; tex->ground_albedo = 0.3f; - tex->sky_model = SHD_SKY_NEW; - + tex->sun_disc = true; + tex->sun_size = DEG2RADF(0.545); + tex->sun_elevation = M_PI_2; + tex->sun_rotation = 0.0f; + tex->altitude = 0; + tex->air_density = 1.0f; + tex->dust_density = 1.0f; + tex->ozone_density = 1.0f; + tex->sky_model = SHD_SKY_NISHITA; node->storage = tex; } diff --git a/source/blender/python/bmesh/bmesh_py_types_customdata.c b/source/blender/python/bmesh/bmesh_py_types_customdata.c index 340286191b8..8615da653ae 100644 --- a/source/blender/python/bmesh/bmesh_py_types_customdata.c +++ b/source/blender/python/bmesh/bmesh_py_types_customdata.c @@ -98,7 +98,8 @@ PyDoc_STRVAR( PyDoc_STRVAR(bpy_bmlayeraccess_collection__bevel_weight_doc, "Bevel weight float in [0 - 1].\n\n:type: :class:`BMLayerCollection`"); PyDoc_STRVAR(bpy_bmlayeraccess_collection__crease_doc, - "Edge crease for subdivision surface - float in [0 - 1].\n\n:type: :class:`BMLayerCollection`"); + "Edge crease for subdivision surface - float in [0 - 1].\n\n:type: " + ":class:`BMLayerCollection`"); PyDoc_STRVAR( bpy_bmlayeraccess_collection__uv_doc, "Accessor for :class:`BMLoopUV` UV (as a 2D Vector).\n\ntype: :class:`BMLayerCollection`"); diff --git a/source/blender/python/intern/bpy_library_write.c b/source/blender/python/intern/bpy_library_write.c index fec0cef7b05..66d20dd357f 100644 --- a/source/blender/python/intern/bpy_library_write.c +++ b/source/blender/python/intern/bpy_library_write.c @@ -35,6 +35,8 @@ #include "BKE_main.h" #include "BKE_report.h" +#include "BLO_writefile.h" + #include "RNA_types.h" #include "bpy_capi_utils.h" @@ -45,8 +47,7 @@ PyDoc_STRVAR( bpy_lib_write_doc, - ".. method:: write(filepath, datablocks, relative_remap=False, fake_user=False, " - "compress=False)\n" + ".. method:: write(filepath, datablocks, path_remap=False, fake_user=False, compress=False)\n" "\n" " Write data-blocks into a blend file.\n" "\n" @@ -58,8 +59,14 @@ PyDoc_STRVAR( " :type filepath: string\n" " :arg datablocks: set of data-blocks (:class:`bpy.types.ID` instances).\n" " :type datablocks: set\n" - " :arg relative_remap: When True, make paths relative to the current blend-file.\n" - " :type relative_remap: bool\n" + " :arg path_remap: Optionally remap paths when writing the file:\n" + "\n" + " - ``NONE`` No path manipulation (default).\n" + " - ``RELATIVE`` Remap paths that are already relative to the new location.\n" + " - ``RELATIVE_ALL`` Remap all paths to be relative to the new location.\n" + " - ``ABSOLUTE`` Make all paths absolute on writing.\n" + "\n" + " :type path_remap: string\n" " :arg fake_user: When True, data-blocks will be written with fake-user flag enabled.\n" " :type fake_user: bool\n" " :arg compress: When True, write a compressed blend file.\n" @@ -70,13 +77,23 @@ static PyObject *bpy_lib_write(PyObject *UNUSED(self), PyObject *args, PyObject const char *filepath; char filepath_abs[FILE_MAX]; PyObject *datablocks = NULL; - bool use_relative_remap = false, use_fake_user = false, use_compress = false; + + const struct PyC_StringEnumItems path_remap_items[] = { + {BLO_WRITE_PATH_REMAP_NONE, "NONE"}, + {BLO_WRITE_PATH_REMAP_RELATIVE, "RELATIVE"}, + {BLO_WRITE_PATH_REMAP_RELATIVE_ALL, "RELATIVE_ALL"}, + {BLO_WRITE_PATH_REMAP_ABSOLUTE, "ABSOLUTE"}, + {0, NULL}, + }; + struct PyC_StringEnum path_remap = {path_remap_items, BLO_WRITE_PATH_REMAP_NONE}; + + bool use_fake_user = false, use_compress = false; static const char *_keywords[] = { "filepath", "datablocks", /* optional */ - "relative_remap", + "path_remap", "fake_user", "compress", NULL, @@ -88,8 +105,8 @@ static PyObject *bpy_lib_write(PyObject *UNUSED(self), PyObject *args, PyObject &filepath, &PySet_Type, &datablocks, - PyC_ParseBool, - &use_relative_remap, + PyC_ParseStringEnum, + &path_remap, PyC_ParseBool, &use_fake_user, PyC_ParseBool, @@ -100,10 +117,6 @@ static PyObject *bpy_lib_write(PyObject *UNUSED(self), PyObject *args, PyObject Main *bmain_src = G_MAIN; int write_flags = 0; - if (use_relative_remap) { - write_flags |= G_FILE_RELATIVE_REMAP; - } - if (use_compress) { write_flags |= G_FILE_COMPRESS; } @@ -162,8 +175,8 @@ static PyObject *bpy_lib_write(PyObject *UNUSED(self), PyObject *args, PyObject ReportList reports; BKE_reports_init(&reports, RPT_STORE); - - retval = BKE_blendfile_write_partial(bmain_src, filepath_abs, write_flags, &reports); + retval = BKE_blendfile_write_partial( + bmain_src, filepath_abs, write_flags, path_remap.value_found, &reports); /* cleanup state */ BKE_blendfile_write_partial_end(bmain_src); diff --git a/source/blender/python/mathutils/mathutils_Matrix.c b/source/blender/python/mathutils/mathutils_Matrix.c index 7896b939d77..327ee4dd1c3 100644 --- a/source/blender/python/mathutils/mathutils_Matrix.c +++ b/source/blender/python/mathutils/mathutils_Matrix.c @@ -1634,16 +1634,17 @@ static PyObject *Matrix_inverted_noargs(MatrixObject *self) Py_RETURN_NONE; } -PyDoc_STRVAR(Matrix_invert_safe_doc, - ".. method:: invert_safe()\n" - "\n" - " Set the matrix to its inverse, will never error.\n" - " If degenerated (e.g. zero scale on an axis), add some epsilon to its diagonal, " - "to get an invertible one.\n" - " If tweaked matrix is still degenerated, set to the identity matrix instead.\n" - "\n" - " .. seealso:: `Inverse Matrix <https://en.wikipedia.org/wiki/Inverse_matrix>`__ on " - "Wikipedia.\n"); +PyDoc_STRVAR( + Matrix_invert_safe_doc, + ".. method:: invert_safe()\n" + "\n" + " Set the matrix to its inverse, will never error.\n" + " If degenerated (e.g. zero scale on an axis), add some epsilon to its diagonal, " + "to get an invertible one.\n" + " If tweaked matrix is still degenerated, set to the identity matrix instead.\n" + "\n" + " .. seealso:: `Inverse Matrix <https://en.wikipedia.org/wiki/Inverse_matrix>`__ on " + "Wikipedia.\n"); static PyObject *Matrix_invert_safe(MatrixObject *self) { if (BaseMath_ReadCallback_ForWrite(self) == -1) { diff --git a/source/blender/render/intern/source/pipeline.c b/source/blender/render/intern/source/pipeline.c index c66c43ec467..b335862abe0 100644 --- a/source/blender/render/intern/source/pipeline.c +++ b/source/blender/render/intern/source/pipeline.c @@ -1844,7 +1844,7 @@ static void validate_render_settings(Render *re) { if (RE_engine_is_external(re)) { /* not supported yet */ - re->r.scemode &= ~(R_FULL_SAMPLE); + re->r.scemode &= ~R_FULL_SAMPLE; } } diff --git a/source/blender/render/intern/source/render_texture.c b/source/blender/render/intern/source/render_texture.c index ee484924bf9..9926e08c968 100644 --- a/source/blender/render/intern/source/render_texture.c +++ b/source/blender/render/intern/source/render_texture.c @@ -1322,7 +1322,7 @@ static int multitex_nodes_intern(Tex *tex, texnode_preview, use_nodes); - if (mtex->mapto & (MAP_COL)) { + if (mtex->mapto & MAP_COL) { ImBuf *ibuf = BKE_image_pool_acquire_ibuf(tex->ima, &tex->iuser, pool); /* don't linearize float buffers, assumed to be linear */ diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index ed1b29d61ce..b2753886f69 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -1110,11 +1110,6 @@ void wm_homefile_read(bContext *C, } if (use_data) { - /* Prevent buggy files that had G_FILE_RELATIVE_REMAP written out by mistake. - * Screws up autosaves otherwise can remove this eventually, - * only in a 2.53 and older, now its not written. */ - G.fileflags &= ~G_FILE_RELATIVE_REMAP; - if (reset_app_template) { /* Always load UI when switching to another template. */ G.fileflags &= ~G_FILE_NO_UI; @@ -1408,11 +1403,13 @@ static ImBuf *blend_file_thumb(const bContext *C, bool write_crash_blend(void) { char path[FILE_MAX]; - int fileflags = G.fileflags & ~(G_FILE_HISTORY); /* don't do file history on crash file */ + + /* Don't do file history on crash file. */ + const int fileflags = G.fileflags & ~G_FILE_HISTORY; BLI_strncpy(path, BKE_main_blendfile_path_from_global(), sizeof(path)); BLI_path_extension_replace(path, sizeof(path), "_crash.blend"); - if (BLO_write_file(G_MAIN, path, fileflags, NULL, NULL)) { + if (BLO_write_file(G_MAIN, path, fileflags, NULL)) { printf("written: %s\n", path); return 1; } @@ -1425,7 +1422,11 @@ bool write_crash_blend(void) /** * \see #wm_homefile_write_exec wraps #BLO_write_file in a similar way. */ -static bool wm_file_write(bContext *C, const char *filepath, int fileflags, ReportList *reports) +static bool wm_file_write(bContext *C, + const char *filepath, + int fileflags, + eBLO_WritePathRemap remap_mode, + ReportList *reports) { Main *bmain = CTX_data_main(C); Library *li; @@ -1502,7 +1503,7 @@ static bool wm_file_write(bContext *C, const char *filepath, int fileflags, Repo /* XXX temp solution to solve bug, real fix coming (ton) */ bmain->recovered = 0; - if (BLO_write_file(CTX_data_main(C), filepath, fileflags, reports, thumb)) { + if (BLO_write_file_ex(CTX_data_main(C), filepath, fileflags, reports, remap_mode, thumb)) { const bool do_history = (G.background == false) && (CTX_wm_manager(C)->op_undo_depth == 0); if (!(fileflags & G_FILE_SAVE_COPY)) { @@ -1635,7 +1636,7 @@ void wm_autosave_timer(Main *bmain, wmWindowManager *wm, wmTimer *UNUSED(wt)) ED_editors_flush_edits(bmain); /* Error reporting into console */ - BLO_write_file(bmain, filepath, fileflags, NULL, NULL); + BLO_write_file(bmain, filepath, fileflags, NULL); } /* do timer after file write, just in case file write takes a long time */ wm->autosavetimer = WM_event_add_timer(wm, NULL, TIMERAUTOSAVE, U.savetime * 60.0); @@ -1755,7 +1756,8 @@ static int wm_homefile_write_exec(bContext *C, wmOperator *op) /* Force save as regular blend file. */ fileflags = G.fileflags & ~(G_FILE_COMPRESS | G_FILE_HISTORY); - if (BLO_write_file(bmain, filepath, fileflags, op->reports, NULL) == 0) { + if (BLO_write_file_ex( + bmain, filepath, fileflags, op->reports, BLO_WRITE_PATH_REMAP_RELATIVE, NULL) == 0) { printf("fail\n"); return OPERATOR_CANCELLED; } @@ -2671,6 +2673,12 @@ static int wm_save_as_mainfile_exec(bContext *C, wmOperator *op) char path[FILE_MAX]; const bool is_save_as = (op->type->invoke == wm_save_as_mainfile_invoke); + /* We could expose all options to the users however in most cases remapping + * existing relative paths is a good default. + * Users can manually make their paths relative & absolute if they wish. */ + const eBLO_WritePathRemap remap_mode = RNA_boolean_get(op->ptr, "relative_remap") ? + BLO_WRITE_PATH_REMAP_RELATIVE : + BLO_WRITE_PATH_REMAP_NONE; save_set_compress(op); if (RNA_struct_property_is_set(op->ptr, "filepath")) { @@ -2686,13 +2694,12 @@ static int wm_save_as_mainfile_exec(bContext *C, wmOperator *op) /* set compression flag */ SET_FLAG_FROM_TEST(fileflags, RNA_boolean_get(op->ptr, "compress"), G_FILE_COMPRESS); - SET_FLAG_FROM_TEST(fileflags, RNA_boolean_get(op->ptr, "relative_remap"), G_FILE_RELATIVE_REMAP); SET_FLAG_FROM_TEST( fileflags, (RNA_struct_property_is_set(op->ptr, "copy") && RNA_boolean_get(op->ptr, "copy")), G_FILE_SAVE_COPY); - const bool ok = wm_file_write(C, path, fileflags, op->reports); + const bool ok = wm_file_write(C, path, fileflags, remap_mode, op->reports); if ((op->flag & OP_IS_INVOKE) == 0) { /* OP_IS_INVOKE is set when the operator is called from the GUI. diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index 001acc459c2..27aa9c532e5 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -500,7 +500,7 @@ void WM_exit_ex(bContext *C, const bool do_python) has_edited = ED_editors_flush_edits(bmain); - if ((has_edited && BLO_write_file(bmain, filename, fileflags, NULL, NULL)) || + if ((has_edited && BLO_write_file(bmain, filename, fileflags, NULL)) || (undo_memfile && BLO_memfile_write_file(undo_memfile, filename))) { printf("Saved session recovery to '%s'\n", filename); } diff --git a/source/creator/creator.c b/source/creator/creator.c index ea64184c826..b96b33f7a27 100644 --- a/source/creator/creator.c +++ b/source/creator/creator.c @@ -248,8 +248,12 @@ int main(int argc, /* Unbuffered stdout makes stdout and stderr better synchronized, and helps * when stepping through code in a debugger (prints are immediately - * visible). */ + * visible). However disabling buffering causes lock contention on windows + * see T76767 for detais, since this is a debugging aid, we do not enable + * the unbuffered behavior for release builds. */ +#ifndef NDEBUG setvbuf(stdout, NULL, _IONBF, 0); +#endif #ifdef WIN32 /* We delay loading of openmp so we can set the policy here. */ diff --git a/tests/gtests/blenlib/BLI_math_matrix_test.cc b/tests/gtests/blenlib/BLI_math_matrix_test.cc new file mode 100644 index 00000000000..9c47c02ceaf --- /dev/null +++ b/tests/gtests/blenlib/BLI_math_matrix_test.cc @@ -0,0 +1,99 @@ +/* Apache License, Version 2.0 */ + +#include "testing/testing.h" + +#include "BLI_math_matrix.h" + +TEST(math_matrix, interp_m4_m4m4_regular) +{ + /* Test 4x4 matrix interpolation without singularity, i.e. without axis flip. */ + + /* Transposed matrix, so that the code here is written in the same way as print_m4() outputs. */ + /* This matrix represents T=(0.1, 0.2, 0.3), R=(40, 50, 60) degrees, S=(0.7, 0.8, 0.9) */ + float matrix_a[4][4] = { + {0.224976f, -0.333770f, 0.765074f, 0.100000f}, + {0.389669f, 0.647565f, 0.168130f, 0.200000f}, + {-0.536231f, 0.330541f, 0.443163f, 0.300000f}, + {0.000000f, 0.000000f, 0.000000f, 1.000000f}, + }; + transpose_m4(matrix_a); + + float matrix_i[4][4]; + unit_m4(matrix_i); + + float result[4][4]; + const float epsilon = 1e-6; + interp_m4_m4m4(result, matrix_i, matrix_a, 0.0f); + EXPECT_M4_NEAR(result, matrix_i, epsilon); + + interp_m4_m4m4(result, matrix_i, matrix_a, 1.0f); + EXPECT_M4_NEAR(result, matrix_a, epsilon); + + /* This matrix is based on the current implementation of the code, and isn't guaranteed to be + * correct. It's just consistent with the current implementation. */ + float matrix_halfway[4][4] = { + {0.690643f, -0.253244f, 0.484996f, 0.050000f}, + {0.271924f, 0.852623f, 0.012348f, 0.100000f}, + {-0.414209f, 0.137484f, 0.816778f, 0.150000f}, + {0.000000f, 0.000000f, 0.000000f, 1.000000f}, + }; + + transpose_m4(matrix_halfway); + interp_m4_m4m4(result, matrix_i, matrix_a, 0.5f); + EXPECT_M4_NEAR(result, matrix_halfway, epsilon); +} + +TEST(math_matrix, interp_m3_m3m3_singularity) +{ + /* A singluarity means that there is an axis mirror in the rotation component of the matrix. This + * is reflected in its negative determinant. + * + * The interpolation of 4x4 matrices performs linear interpolation on the translation component, + * and then uses the 3x3 interpolation function to handle rotation and scale. As a result, this + * test for a singularity in the rotation matrix only needs to test the 3x3 case. */ + + /* Transposed matrix, so that the code here is written in the same way as print_m4() outputs. */ + /* This matrix represents R=(4, 5, 6) degrees, S=(-1, 1, 1) */ + float matrix_a[3][3] = { + {-0.990737f, -0.098227f, 0.093759f}, + {-0.104131f, 0.992735f, -0.060286f}, + {0.087156f, 0.069491f, 0.993768f}, + }; + transpose_m3(matrix_a); + EXPECT_NEAR(-1.0f, determinant_m3_array(matrix_a), 1e-6); + + /* This matrix represents R=(0, 0, 0), S=(-1, 0, 0) */ + float matrix_b[3][3] = { + {-1.0f, 0.0f, 0.0f}, + {0.0f, 1.0f, 0.0f}, + {0.0f, 0.0f, 1.0f}, + }; + transpose_m3(matrix_b); + + float result[3][3]; + interp_m3_m3m3(result, matrix_a, matrix_b, 0.0f); + EXPECT_M3_NEAR(result, matrix_a, 1e-5); + + interp_m3_m3m3(result, matrix_a, matrix_b, 1.0f); + EXPECT_M3_NEAR(result, matrix_b, 1e-5); + + interp_m3_m3m3(result, matrix_a, matrix_b, 0.5f); + float expect[3][3] = { + {-0.997681f, -0.049995f, 0.046186f}, + {-0.051473f, 0.998181f, -0.031385f}, + {0.044533f, 0.033689f, 0.998440f}, + }; + transpose_m3(expect); + EXPECT_M3_NEAR(result, expect, 1e-5); + + /* Interpolating between a matrix with and without axis flip can cause it to go through a zero + * point. The determinant det(A) of a matrix represents the change in volume; interpolating + * between matrices with det(A)=-1 and det(B)=1 will have to go through a point where + * det(result)=0, so where the volume becomes zero. */ + float matrix_i[3][3]; + unit_m3(matrix_i); + zero_m3(expect); + interp_m3_m3m3(result, matrix_a, matrix_i, 0.5f); + EXPECT_NEAR(0.0f, determinant_m3_array(result), 1e-5); + EXPECT_M3_NEAR(result, expect, 1e-5); +} diff --git a/tests/gtests/blenlib/CMakeLists.txt b/tests/gtests/blenlib/CMakeLists.txt index 8ddb2702b83..31c8e983292 100644 --- a/tests/gtests/blenlib/CMakeLists.txt +++ b/tests/gtests/blenlib/CMakeLists.txt @@ -60,6 +60,7 @@ BLENDER_TEST(BLI_math_base "bf_blenlib") BLENDER_TEST(BLI_math_bits "bf_blenlib") BLENDER_TEST(BLI_math_color "bf_blenlib") BLENDER_TEST(BLI_math_geom "bf_blenlib") +BLENDER_TEST(BLI_math_matrix "bf_blenlib") BLENDER_TEST(BLI_math_vector "bf_blenlib") BLENDER_TEST(BLI_memiter "bf_blenlib") BLENDER_TEST(BLI_optional "bf_blenlib") diff --git a/tests/python/bl_pyapi_idprop_datablock.py b/tests/python/bl_pyapi_idprop_datablock.py index 44fec6a9043..ca52c1b01fe 100644 --- a/tests/python/bl_pyapi_idprop_datablock.py +++ b/tests/python/bl_pyapi_idprop_datablock.py @@ -157,10 +157,10 @@ def check_linked_scene_copying(): extern_sce = get_scene("lib.blend", "Scene_lib") # check node's props - # we made full copy from linked scene, so pointers must equal each other + # must point to own scene camera abort_if_false(intern_sce.node_tree.nodes['Render Layers']["prop"] and - intern_sce.node_tree.nodes['Render Layers']["prop"] == - extern_sce.node_tree.nodes['Render Layers']["prop"]) + not (intern_sce.node_tree.nodes['Render Layers']["prop"] == + extern_sce.node_tree.nodes['Render Layers']["prop"])) def check_scene_copying(): |