Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAntonio Vazquez <blendergit@gmail.com>2020-06-18 21:35:31 +0300
committerAntonio Vazquez <blendergit@gmail.com>2020-06-18 21:35:31 +0300
commit3536a2aac43d705d6efc53936f194563031343e4 (patch)
tree43f9343eed4eb894d3ef4edba9490914b27ca4dd
parent362618ec0b11764acc8748be8e0c622b6be4540d (diff)
parentd56444392f41e5115bb001af9fa50747f6295834 (diff)
Merge branch 'master' into greasepencil-edit-curve
-rw-r--r--build_files/buildbot/README.md2
-rw-r--r--build_files/buildbot/slave_rsync.py37
-rwxr-xr-xbuild_files/buildbot/worker_bundle_dmg.py (renamed from build_files/buildbot/slave_bundle_dmg.py)2
-rw-r--r--build_files/buildbot/worker_codesign.cmake (renamed from build_files/buildbot/slave_codesign.cmake)2
-rwxr-xr-xbuild_files/buildbot/worker_codesign.py (renamed from build_files/buildbot/slave_codesign.py)0
-rw-r--r--build_files/buildbot/worker_compile.py (renamed from build_files/buildbot/slave_compile.py)2
-rw-r--r--build_files/buildbot/worker_pack.py (renamed from build_files/buildbot/slave_pack.py)4
-rw-r--r--build_files/buildbot/worker_test.py (renamed from build_files/buildbot/slave_test.py)0
-rw-r--r--build_files/buildbot/worker_update.py (renamed from build_files/buildbot/slave_update.py)0
-rw-r--r--doc/python_api/sphinx_doc_gen.py19
-rw-r--r--intern/cycles/blender/blender_shader.cpp8
-rw-r--r--intern/cycles/device/cuda/device_cuda.h30
-rw-r--r--intern/cycles/device/opencl/device_opencl_impl.cpp10
-rw-r--r--intern/cycles/kernel/CMakeLists.txt2
-rw-r--r--intern/cycles/kernel/kernel_emission.h4
-rw-r--r--intern/cycles/kernel/kernel_light.h500
-rw-r--r--intern/cycles/kernel/kernel_light_background.h448
-rw-r--r--intern/cycles/kernel/kernel_light_common.h159
-rw-r--r--intern/cycles/kernel/kernel_montecarlo.h10
-rw-r--r--intern/cycles/kernel/kernel_types.h27
-rw-r--r--intern/cycles/kernel/osl/osl_closures.cpp79
-rw-r--r--intern/cycles/kernel/osl/osl_closures.h2
-rw-r--r--intern/cycles/kernel/shaders/node_sky_texture.osl123
-rw-r--r--intern/cycles/kernel/svm/svm_sky.h301
-rw-r--r--intern/cycles/kernel/svm/svm_types.h2
-rw-r--r--intern/cycles/render/CMakeLists.txt2
-rw-r--r--intern/cycles/render/image_sky.cpp95
-rw-r--r--intern/cycles/render/image_sky.h49
-rw-r--r--intern/cycles/render/light.cpp109
-rw-r--r--intern/cycles/render/nodes.cpp199
-rw-r--r--intern/cycles/render/nodes.h9
-rw-r--r--intern/cycles/util/CMakeLists.txt1
-rw-r--r--intern/cycles/util/util_sky_model.h24
-rw-r--r--intern/cycles/util/util_sky_nishita.cpp371
-rw-r--r--intern/mantaflow/intern/strings/liquid_script.h28
-rw-r--r--release/scripts/presets/keyconfig/keymap_data/blender_default.py19
-rw-r--r--release/scripts/startup/bl_operators/sequencer.py2
-rw-r--r--release/scripts/startup/bl_operators/vertexpaint_dirt.py30
-rw-r--r--release/scripts/startup/bl_ui/properties_data_camera.py1
-rw-r--r--release/scripts/startup/bl_ui/space_outliner.py6
-rw-r--r--release/scripts/startup/bl_ui/space_view3d.py5
-rw-r--r--source/blender/blenkernel/BKE_blendfile.h1
-rw-r--r--source/blender/blenkernel/BKE_collection.h4
-rw-r--r--source/blender/blenkernel/BKE_global.h11
-rw-r--r--source/blender/blenkernel/BKE_lib_id.h14
-rw-r--r--source/blender/blenkernel/BKE_mesh.h1
-rw-r--r--source/blender/blenkernel/BKE_modifier.h94
-rw-r--r--source/blender/blenkernel/BKE_object.h5
-rw-r--r--source/blender/blenkernel/intern/anim_data.c2
-rw-r--r--source/blender/blenkernel/intern/blender_copybuffer.c5
-rw-r--r--source/blender/blenkernel/intern/blender_undo.c5
-rw-r--r--source/blender/blenkernel/intern/blendfile.c11
-rw-r--r--source/blender/blenkernel/intern/boids.c4
-rw-r--r--source/blender/blenkernel/intern/brush.c2
-rw-r--r--source/blender/blenkernel/intern/collection.c71
-rw-r--r--source/blender/blenkernel/intern/effect.c2
-rw-r--r--source/blender/blenkernel/intern/gpencil_curve.c3
-rw-r--r--source/blender/blenkernel/intern/gpencil_geom.c11
-rw-r--r--source/blender/blenkernel/intern/lib_id.c43
-rw-r--r--source/blender/blenkernel/intern/lib_override.c6
-rw-r--r--source/blender/blenkernel/intern/lib_remap.c5
-rw-r--r--source/blender/blenkernel/intern/mesh_evaluate.c78
-rw-r--r--source/blender/blenkernel/intern/object.c387
-rw-r--r--source/blender/blenkernel/intern/rigidbody.c5
-rw-r--r--source/blender/blenkernel/intern/scene.c68
-rw-r--r--source/blender/blenkernel/intern/seqmodifier.c2
-rw-r--r--source/blender/blenkernel/intern/sequencer.c514
-rw-r--r--source/blender/blenlib/BLI_math_geom.h4
-rw-r--r--source/blender/blenlib/intern/math_geom.c26
-rw-r--r--source/blender/blenlib/intern/math_matrix.c16
-rw-r--r--source/blender/blenloader/BLO_writefile.h27
-rw-r--r--source/blender/blenloader/intern/versioning_250.c4
-rw-r--r--source/blender/blenloader/intern/writefile.c73
-rw-r--r--source/blender/bmesh/intern/bmesh_polygon_edgenet.c3
-rw-r--r--source/blender/bmesh/operators/bmo_connect.c49
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_cache.cc13
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_cache.h6
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_map.cc23
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_map.h3
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations.cc18
-rw-r--r--source/blender/depsgraph/intern/depsgraph_type.h3
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_flush.cc5
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_modifier.cc16
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_modifier.h6
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc37
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.h2
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_pose.h5
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequencer.cc13
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequencer.h3
-rw-r--r--source/blender/draw/DRW_select_buffer.h14
-rw-r--r--source/blender/draw/engines/eevee/eevee_engine.c2
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_cache_utils.c6
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_engine.c1
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_engine.h2
-rw-r--r--source/blender/draw/intern/draw_manager.c14
-rw-r--r--source/blender/editors/armature/armature_relations.c6
-rw-r--r--source/blender/editors/armature/armature_select.c6
-rw-r--r--source/blender/editors/curve/editcurve.c2
-rw-r--r--source/blender/editors/curve/editcurve_select.c4
-rw-r--r--source/blender/editors/gpencil/annotate_paint.c7
-rw-r--r--source/blender/editors/gpencil/editaction_gpencil.c6
-rw-r--r--source/blender/editors/gpencil/gpencil_fill.c18
-rw-r--r--source/blender/editors/gpencil/gpencil_paint.c5
-rw-r--r--source/blender/editors/gpencil/gpencil_utils.c3
-rw-r--r--source/blender/editors/include/ED_armature.h2
-rw-r--r--source/blender/editors/include/ED_curve.h2
-rw-r--r--source/blender/editors/include/ED_mball.h8
-rw-r--r--source/blender/editors/include/ED_mesh.h4
-rw-r--r--source/blender/editors/interface/interface_handlers.c8
-rw-r--r--source/blender/editors/interface/interface_panel.c64
-rw-r--r--source/blender/editors/interface/interface_region_menu_popup.c14
-rw-r--r--source/blender/editors/interface/view2d.c8
-rw-r--r--source/blender/editors/interface/view2d_ops.c4
-rw-r--r--source/blender/editors/io/io_alembic.c7
-rw-r--r--source/blender/editors/mesh/meshtools.c10
-rw-r--r--source/blender/editors/metaball/mball_edit.c115
-rw-r--r--source/blender/editors/object/object_add.c66
-rw-r--r--source/blender/editors/object/object_edit.c18
-rw-r--r--source/blender/editors/object/object_modes.c2
-rw-r--r--source/blender/editors/screen/area.c12
-rw-r--r--source/blender/editors/screen/screen_ops.c2
-rw-r--r--source/blender/editors/sculpt_paint/paint_stroke.c5
-rw-r--r--source/blender/editors/space_action/space_action.c2
-rw-r--r--source/blender/editors/space_clip/clip_dopesheet_draw.c4
-rw-r--r--source/blender/editors/space_clip/space_clip.c2
-rw-r--r--source/blender/editors/space_console/space_console.c2
-rw-r--r--source/blender/editors/space_image/image_edit.c2
-rw-r--r--source/blender/editors/space_info/space_info.c2
-rw-r--r--source/blender/editors/space_nla/space_nla.c2
-rw-r--r--source/blender/editors/space_node/drawnode.c29
-rw-r--r--source/blender/editors/space_node/node_draw.c2
-rw-r--r--source/blender/editors/space_outliner/outliner_collections.c3
-rw-r--r--source/blender/editors/space_outliner/outliner_intern.h1
-rw-r--r--source/blender/editors/space_outliner/outliner_select.c25
-rw-r--r--source/blender/editors/space_outliner/outliner_tools.c208
-rw-r--r--source/blender/editors/space_outliner/outliner_utils.c17
-rw-r--r--source/blender/editors/space_sequencer/sequencer_edit.c22
-rw-r--r--source/blender/editors/space_sequencer/sequencer_select.c22
-rw-r--r--source/blender/editors/space_view3d/view3d_fly.c30
-rw-r--r--source/blender/editors/space_view3d/view3d_header.c2
-rw-r--r--source/blender/editors/space_view3d/view3d_select.c2
-rw-r--r--source/blender/editors/space_view3d/view3d_walk.c31
-rw-r--r--source/blender/editors/transform/transform_constraints.c143
-rw-r--r--source/blender/freestyle/intern/python/BPy_Freestyle.cpp2
-rw-r--r--source/blender/gpu/intern/gpu_draw.c2
-rw-r--r--source/blender/io/collada/AnimationImporter.cpp6
-rw-r--r--source/blender/makesdna/DNA_gpencil_types.h4
-rw-r--r--source/blender/makesdna/DNA_node_types.h14
-rw-r--r--source/blender/makesdna/DNA_userdef_types.h7
-rw-r--r--source/blender/makesrna/RNA_access.h1
-rw-r--r--source/blender/makesrna/intern/rna_access_compare_override.c67
-rw-r--r--source/blender/makesrna/intern/rna_define.c2
-rw-r--r--source/blender/makesrna/intern/rna_internal.h1
-rw-r--r--source/blender/makesrna/intern/rna_internal_types.h1
-rw-r--r--source/blender/makesrna/intern/rna_modifier.c4
-rw-r--r--source/blender/makesrna/intern/rna_nodetree.c55
-rw-r--r--source/blender/makesrna/intern/rna_rna.c82
-rw-r--r--source/blender/makesrna/intern/rna_sequencer_api.c28
-rw-r--r--source/blender/makesrna/intern/rna_userdef.c2
-rw-r--r--source/blender/makesrna/intern/rna_wm.c2
-rw-r--r--source/blender/modifiers/intern/MOD_ui_common.c118
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_sky.c11
-rw-r--r--source/blender/python/bmesh/bmesh_py_types_customdata.c3
-rw-r--r--source/blender/python/intern/bpy_library_write.c41
-rw-r--r--source/blender/python/mathutils/mathutils_Matrix.c21
-rw-r--r--source/blender/render/intern/source/pipeline.c2
-rw-r--r--source/blender/render/intern/source/render_texture.c2
-rw-r--r--source/blender/windowmanager/intern/wm_files.c33
-rw-r--r--source/blender/windowmanager/intern/wm_init_exit.c2
-rw-r--r--source/creator/creator.c6
-rw-r--r--tests/gtests/blenlib/BLI_math_matrix_test.cc99
-rw-r--r--tests/gtests/blenlib/CMakeLists.txt1
-rw-r--r--tests/python/bl_pyapi_idprop_datablock.py6
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, &params);
+
+ 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(&region->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():