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:
authorJacques Lucke <jacques@blender.org>2021-06-14 13:44:13 +0300
committerJacques Lucke <jacques@blender.org>2021-06-14 13:44:13 +0300
commitdddcf1e9bbf4a6d1f4ff03eaf0cb7e9228b18ec5 (patch)
treec20defa7efd54c933d20a296abefe567909bb6c0
parent3b162b7c185d089e93d892169a458d552196b7b6 (diff)
parentc9dc55301cd7903b7ef7c045d337ada29aa809a1 (diff)
Merge branch 'master' into temp-compact-node-prototypetemp-compact-node-prototype
-rw-r--r--.clang-format11
-rw-r--r--.clang-tidy2
-rw-r--r--.gitignore3
-rw-r--r--CMakeLists.txt25
-rw-r--r--GNUmakefile21
-rw-r--r--build_files/build_environment/CMakeLists.txt2
-rw-r--r--build_files/build_environment/cmake/blosc.cmake2
-rw-r--r--build_files/build_environment/cmake/boost.cmake9
-rw-r--r--build_files/build_environment/cmake/download.cmake2
-rw-r--r--build_files/build_environment/cmake/embree.cmake8
-rw-r--r--build_files/build_environment/cmake/gmp.cmake26
-rw-r--r--build_files/build_environment/cmake/harvest.cmake6
-rw-r--r--build_files/build_environment/cmake/llvm.cmake2
-rw-r--r--build_files/build_environment/cmake/opencolorio.cmake2
-rw-r--r--build_files/build_environment/cmake/openimageio.cmake1
-rw-r--r--build_files/build_environment/cmake/options.cmake21
-rw-r--r--build_files/build_environment/cmake/png.cmake4
-rw-r--r--build_files/build_environment/cmake/sse2neon.cmake22
-rw-r--r--build_files/build_environment/cmake/ssl.cmake4
-rw-r--r--build_files/build_environment/cmake/ssl.conf5
-rw-r--r--build_files/build_environment/cmake/tbb.cmake30
-rw-r--r--build_files/build_environment/cmake/versions.cmake35
-rw-r--r--build_files/build_environment/cmake/x264.cmake18
-rwxr-xr-xbuild_files/build_environment/install_deps.sh37
-rw-r--r--build_files/build_environment/patches/cmake/modules/FindIlmBase.cmake2
-rw-r--r--build_files/build_environment/patches/cmake/modules/FindOpenEXR.cmake2
-rw-r--r--build_files/build_environment/patches/cmakelists_tbb.txt636
-rw-r--r--build_files/build_environment/patches/oidn.diff30
-rw-r--r--build_files/build_environment/patches/osl.diff16
-rw-r--r--build_files/build_environment/patches/tbb.diff13
-rw-r--r--build_files/build_environment/patches/theora.diff4
-rw-r--r--build_files/build_environment/patches/usd.diff144
-rw-r--r--build_files/buildbot/README.md72
-rw-r--r--build_files/buildbot/buildbot_utils.py127
-rw-r--r--build_files/buildbot/codesign/absolute_and_relative_filename.py81
-rw-r--r--build_files/buildbot/codesign/archive_with_indicator.py245
-rw-r--r--build_files/buildbot/codesign/base_code_signer.py501
-rw-r--r--build_files/buildbot/codesign/config_builder.py62
-rw-r--r--build_files/buildbot/codesign/config_common.py36
-rw-r--r--build_files/buildbot/codesign/config_server_template.py101
-rw-r--r--build_files/buildbot/codesign/exception.py26
-rw-r--r--build_files/buildbot/codesign/linux_code_signer.py72
-rw-r--r--build_files/buildbot/codesign/macos_code_signer.py456
-rw-r--r--build_files/buildbot/codesign/simple_code_signer.py52
-rw-r--r--build_files/buildbot/codesign/util.py54
-rw-r--r--build_files/buildbot/codesign/windows_code_signer.py117
-rwxr-xr-xbuild_files/buildbot/codesign_server_linux.py37
-rwxr-xr-xbuild_files/buildbot/codesign_server_macos.py41
-rw-r--r--build_files/buildbot/codesign_server_windows.bat11
-rwxr-xr-xbuild_files/buildbot/codesign_server_windows.py54
-rwxr-xr-xbuild_files/buildbot/worker_bundle_dmg.py551
-rw-r--r--build_files/buildbot/worker_codesign.cmake44
-rwxr-xr-xbuild_files/buildbot/worker_codesign.py74
-rw-r--r--build_files/buildbot/worker_compile.py135
-rw-r--r--build_files/buildbot/worker_pack.py208
-rw-r--r--build_files/buildbot/worker_test.py42
-rw-r--r--build_files/buildbot/worker_update.py31
-rw-r--r--build_files/cmake/Modules/FindClang.cmake16
-rw-r--r--build_files/cmake/Modules/FindEmbree.cmake2
-rw-r--r--build_files/cmake/Modules/FindOSL.cmake2
-rw-r--r--build_files/cmake/buildinfo.cmake2
-rwxr-xr-xbuild_files/cmake/cmake_consistency_check.py63
-rw-r--r--build_files/cmake/cmake_static_check_clang_array.py25
-rw-r--r--build_files/cmake/cmake_static_check_cppcheck.py25
-rw-r--r--build_files/cmake/config/blender_release.cmake8
-rwxr-xr-xbuild_files/cmake/example_scripts/make_quicky.py119
-rw-r--r--build_files/cmake/macros.cmake8
-rw-r--r--build_files/cmake/packaging.cmake4
-rw-r--r--build_files/cmake/platform/platform_apple.cmake19
-rw-r--r--build_files/cmake/platform/platform_apple_xcode.cmake12
-rw-r--r--build_files/cmake/platform/platform_unix.cmake16
-rw-r--r--build_files/cmake/platform/platform_win32.cmake64
-rw-r--r--build_files/cmake/platform/platform_win32_bundle_crt.cmake9
-rwxr-xr-xbuild_files/cmake/project_info.py51
-rw-r--r--build_files/cmake/project_source_info.py106
-rw-r--r--build_files/config/README.md8
-rw-r--r--build_files/config/pipeline_config.json87
-rw-r--r--build_files/utils/README.md5
-rwxr-xr-xbuild_files/utils/make_source_archive.py132
-rw-r--r--build_files/windows/configure_msbuild.cmd10
-rw-r--r--build_files/windows/configure_ninja.cmd8
-rw-r--r--doc/blender_file_format/BlendFileReader.py4
-rw-r--r--doc/doxygen/Doxyfile215
-rwxr-xr-xdoc/manpage/blender.1.py191
-rw-r--r--doc/python_api/examples/bpy.types.RenderEngine.py72
-rw-r--r--doc/python_api/examples/gpu.4.py6
-rw-r--r--doc/python_api/examples/gpu.6.py11
-rw-r--r--doc/python_api/examples/gpu.7.py12
-rw-r--r--doc/python_api/examples/gpu.8.py17
-rw-r--r--doc/python_api/examples/gpu.9.py5
-rw-r--r--doc/python_api/examples/mathutils.Matrix.LocRotScale.py5
-rw-r--r--doc/python_api/examples/mathutils.Matrix.py6
-rw-r--r--doc/python_api/requirements.txt4
-rw-r--r--doc/python_api/rst/info_gotcha.rst3
-rw-r--r--doc/python_api/sphinx_doc_gen.py37
-rw-r--r--doc/python_api/static/css/theme_overrides.css15
-rw-r--r--extern/CMakeLists.txt4
-rw-r--r--extern/audaspace/CMakeLists.txt2
-rw-r--r--extern/audaspace/include/devices/SoftwareDevice.h1
-rw-r--r--extern/audaspace/include/devices/ThreadedDevice.h95
-rw-r--r--extern/audaspace/plugins/ffmpeg/FFMPEGWriter.cpp1
-rw-r--r--extern/audaspace/plugins/pulseaudio/PulseAudioDevice.cpp62
-rw-r--r--extern/audaspace/plugins/pulseaudio/PulseAudioDevice.h19
-rw-r--r--extern/audaspace/plugins/pulseaudio/PulseAudioSymbols.h14
-rw-r--r--extern/audaspace/plugins/wasapi/WASAPIDevice.cpp169
-rw-r--r--extern/audaspace/plugins/wasapi/WASAPIDevice.h29
-rw-r--r--extern/audaspace/src/devices/SoftwareDevice.cpp6
-rw-r--r--extern/audaspace/src/devices/ThreadedDevice.cpp65
-rw-r--r--extern/mantaflow/preprocessed/fileio/iovdb.cpp13
-rw-r--r--extern/mantaflow/preprocessed/fluidsolver.h1
-rw-r--r--extern/mantaflow/preprocessed/general.h2
-rw-r--r--extern/mantaflow/preprocessed/gitinfo.h2
-rw-r--r--extern/mantaflow/preprocessed/grid.h1
-rw-r--r--extern/mantaflow/preprocessed/grid4d.h2
-rw-r--r--extern/mantaflow/preprocessed/levelset.h1
-rw-r--r--extern/mantaflow/preprocessed/mesh.h3
-rw-r--r--extern/mantaflow/preprocessed/movingobs.h1
-rw-r--r--extern/mantaflow/preprocessed/noisefield.h1
-rw-r--r--extern/mantaflow/preprocessed/particle.h6
-rw-r--r--extern/mantaflow/preprocessed/plugin/meshplugins.cpp8
-rw-r--r--extern/mantaflow/preprocessed/plugin/secondaryparticles.cpp7
-rw-r--r--extern/mantaflow/preprocessed/shapes.h6
-rw-r--r--extern/mantaflow/preprocessed/turbulencepart.h1
-rw-r--r--extern/mantaflow/preprocessed/vortexpart.h1
-rw-r--r--extern/mantaflow/preprocessed/vortexsheet.h1
-rw-r--r--extern/smaa_areatex/CMakeLists.txt26
-rw-r--r--extern/smaa_areatex/README.blender5
-rw-r--r--extern/smaa_areatex/smaa_areatex.cpp1208
-rw-r--r--intern/clog/CLG_log.h1
-rw-r--r--intern/clog/clog.c16
-rw-r--r--intern/cycles/app/CMakeLists.txt10
-rw-r--r--intern/cycles/blender/addon/engine.py8
-rw-r--r--intern/cycles/blender/addon/presets.py3
-rw-r--r--intern/cycles/blender/addon/properties.py13
-rw-r--r--intern/cycles/blender/addon/ui.py59
-rw-r--r--intern/cycles/blender/addon/version_update.py12
-rw-r--r--intern/cycles/blender/blender_camera.cpp27
-rw-r--r--intern/cycles/blender/blender_image.cpp4
-rw-r--r--intern/cycles/blender/blender_image.h4
-rw-r--r--intern/cycles/blender/blender_light.cpp10
-rw-r--r--intern/cycles/blender/blender_object.cpp100
-rw-r--r--intern/cycles/blender/blender_python.cpp12
-rw-r--r--intern/cycles/blender/blender_session.cpp54
-rw-r--r--intern/cycles/blender/blender_shader.cpp16
-rw-r--r--intern/cycles/blender/blender_sync.cpp73
-rw-r--r--intern/cycles/blender/blender_sync.h15
-rw-r--r--intern/cycles/blender/blender_viewport.cpp55
-rw-r--r--intern/cycles/blender/blender_viewport.h28
-rw-r--r--intern/cycles/blender/blender_volume.cpp6
-rw-r--r--intern/cycles/bvh/bvh_optix.cpp7
-rw-r--r--intern/cycles/bvh/bvh_optix.h1
-rw-r--r--intern/cycles/device/device.cpp2
-rw-r--r--intern/cycles/device/device.h6
-rw-r--r--intern/cycles/device/device_cpu.cpp1
-rw-r--r--intern/cycles/device/device_cuda.cpp1
-rw-r--r--intern/cycles/device/device_memory.cpp46
-rw-r--r--intern/cycles/device/device_memory.h5
-rw-r--r--intern/cycles/device/device_multi.cpp16
-rw-r--r--intern/cycles/device/device_opencl.cpp3
-rw-r--r--intern/cycles/device/device_optix.cpp83
-rw-r--r--intern/cycles/device/opencl/device_opencl.h9
-rw-r--r--intern/cycles/device/opencl/device_opencl_impl.cpp99
-rw-r--r--intern/cycles/graph/node.cpp78
-rw-r--r--intern/cycles/graph/node.h36
-rw-r--r--intern/cycles/graph/node_type.h7
-rw-r--r--intern/cycles/kernel/CMakeLists.txt1
-rw-r--r--intern/cycles/kernel/bvh/bvh.h98
-rw-r--r--intern/cycles/kernel/bvh/bvh_shadow_all.h19
-rw-r--r--intern/cycles/kernel/bvh/bvh_util.h162
-rw-r--r--intern/cycles/kernel/closure/alloc.h48
-rw-r--r--intern/cycles/kernel/kernel_light.h49
-rw-r--r--intern/cycles/kernel/kernel_light_common.h64
-rw-r--r--intern/cycles/kernel/kernel_montecarlo.h117
-rw-r--r--intern/cycles/kernel/kernel_path.h17
-rw-r--r--intern/cycles/kernel/kernel_subsurface.h43
-rw-r--r--intern/cycles/kernel/kernel_types.h44
-rw-r--r--intern/cycles/kernel/shaders/node_noise_texture.osl2
-rw-r--r--intern/cycles/kernel/shaders/node_vector_math.osl3
-rw-r--r--intern/cycles/kernel/shaders/stdcycles.h69
-rw-r--r--intern/cycles/kernel/svm/svm_math.h3
-rw-r--r--intern/cycles/kernel/svm/svm_math_util.h6
-rw-r--r--intern/cycles/kernel/svm/svm_noisetex.h2
-rw-r--r--intern/cycles/kernel/svm/svm_tex_coord.h9
-rw-r--r--intern/cycles/kernel/svm/svm_types.h1
-rw-r--r--intern/cycles/render/CMakeLists.txt2
-rw-r--r--intern/cycles/render/alembic.cpp997
-rw-r--r--intern/cycles/render/alembic.h69
-rw-r--r--intern/cycles/render/alembic_read.cpp1008
-rw-r--r--intern/cycles/render/alembic_read.h134
-rw-r--r--intern/cycles/render/attribute.cpp64
-rw-r--r--intern/cycles/render/attribute.h33
-rw-r--r--intern/cycles/render/background.cpp9
-rw-r--r--intern/cycles/render/geometry.cpp140
-rw-r--r--intern/cycles/render/geometry.h25
-rw-r--r--intern/cycles/render/hair.cpp61
-rw-r--r--intern/cycles/render/hair.h5
-rw-r--r--intern/cycles/render/image.cpp18
-rw-r--r--intern/cycles/render/image.h12
-rw-r--r--intern/cycles/render/image_oiio.cpp4
-rw-r--r--intern/cycles/render/image_oiio.h2
-rw-r--r--intern/cycles/render/image_sky.cpp2
-rw-r--r--intern/cycles/render/image_sky.h2
-rw-r--r--intern/cycles/render/image_vdb.cpp179
-rw-r--r--intern/cycles/render/image_vdb.h3
-rw-r--r--intern/cycles/render/light.cpp13
-rw-r--r--intern/cycles/render/light.h1
-rw-r--r--intern/cycles/render/mesh.cpp36
-rw-r--r--intern/cycles/render/mesh.h5
-rw-r--r--intern/cycles/render/nodes.cpp4
-rw-r--r--intern/cycles/render/nodes.h14
-rw-r--r--intern/cycles/render/object.cpp12
-rw-r--r--intern/cycles/render/object.h1
-rw-r--r--intern/cycles/render/osl.cpp14
-rw-r--r--intern/cycles/render/osl.h11
-rw-r--r--intern/cycles/render/scene.cpp66
-rw-r--r--intern/cycles/render/scene.h4
-rw-r--r--intern/cycles/render/session.cpp10
-rw-r--r--intern/cycles/render/shader.cpp62
-rw-r--r--intern/cycles/render/shader.h11
-rw-r--r--intern/cycles/render/svm.cpp10
-rw-r--r--intern/cycles/render/svm.h9
-rw-r--r--intern/cycles/test/util_avxf_avx2_test.cpp3
-rw-r--r--intern/cycles/test/util_avxf_avx_test.cpp3
-rw-r--r--intern/cycles/util/util_math.h2
-rw-r--r--intern/cycles/util/util_math_fast.h2
-rw-r--r--intern/cycles/util/util_simd.h8
-rw-r--r--intern/cycles/util/util_sseb.h12
-rw-r--r--intern/cycles/util/util_ssef.h4
-rw-r--r--intern/cycles/util/util_ssei.h13
-rw-r--r--intern/cycles/util/util_system.cpp41
-rw-r--r--intern/cycles/util/util_task.h4
-rw-r--r--intern/cycles/util/util_texture.h12
-rw-r--r--intern/cycles/util/util_vector.h4
-rw-r--r--intern/ffmpeg/ffmpeg_compat.h589
-rw-r--r--intern/ghost/CMakeLists.txt9
-rw-r--r--intern/ghost/GHOST_C-api.h107
-rw-r--r--intern/ghost/GHOST_IXrContext.h6
-rw-r--r--intern/ghost/GHOST_Types.h60
-rw-r--r--intern/ghost/intern/GHOST_C-api.cpp148
-rw-r--r--intern/ghost/intern/GHOST_DropTargetX11.cpp3
-rw-r--r--intern/ghost/intern/GHOST_IconX11.h4
-rw-r--r--intern/ghost/intern/GHOST_SystemCocoa.mm2
-rw-r--r--intern/ghost/intern/GHOST_SystemWayland.cpp285
-rw-r--r--intern/ghost/intern/GHOST_SystemWayland.h17
-rw-r--r--intern/ghost/intern/GHOST_SystemWin32.cpp99
-rw-r--r--intern/ghost/intern/GHOST_Util.h45
-rw-r--r--intern/ghost/intern/GHOST_WaylandCursorSettings.h130
-rw-r--r--intern/ghost/intern/GHOST_WindowCocoa.mm5
-rw-r--r--intern/ghost/intern/GHOST_WindowWayland.cpp130
-rw-r--r--intern/ghost/intern/GHOST_WindowWayland.h17
-rw-r--r--intern/ghost/intern/GHOST_WindowWin32.cpp84
-rw-r--r--intern/ghost/intern/GHOST_WindowWin32.h174
-rw-r--r--intern/ghost/intern/GHOST_XrAction.cpp477
-rw-r--r--intern/ghost/intern/GHOST_XrAction.h145
-rw-r--r--intern/ghost/intern/GHOST_XrContext.cpp20
-rw-r--r--intern/ghost/intern/GHOST_XrContext.h5
-rw-r--r--intern/ghost/intern/GHOST_XrException.h5
-rw-r--r--intern/ghost/intern/GHOST_XrSession.cpp365
-rw-r--r--intern/ghost/intern/GHOST_XrSession.h37
-rw-r--r--intern/ghost/intern/GHOST_Xr_intern.h24
-rw-r--r--intern/ghost/test/gears/GHOST_C-Test.c2
-rw-r--r--intern/ghost/test/multitest/MultiTest.c2
-rw-r--r--intern/guardedalloc/MEM_guardedalloc.h35
-rw-r--r--intern/itasc/kdl/frames.hpp2
-rw-r--r--intern/itasc/kdl/utilities/error.h2
-rw-r--r--intern/libmv/libmv/base/scoped_ptr.h2
-rw-r--r--intern/libmv/third_party/.clang-format2
-rw-r--r--intern/mikktspace/README.md4
-rw-r--r--intern/opencolorio/ocio_impl_glsl.cc2
-rw-r--r--intern/opensubdiv/internal/topology/mesh_topology.cc2
-rw-r--r--intern/rigidbody/RBI_hull_api.h4
-rw-r--r--intern/rigidbody/rb_convex_hull_api.cpp32
-rw-r--r--readme.rst2
-rw-r--r--release/darwin/README.md5
-rw-r--r--release/darwin/README.txt55
-rw-r--r--release/darwin/blender.applescript18
-rwxr-xr-xrelease/darwin/bundle.sh212
m---------release/datafiles/locale0
-rw-r--r--release/datafiles/startup.blendbin690452 -> 804804 bytes
-rw-r--r--release/freedesktop/org.blender.Blender.appdata.xml19
-rw-r--r--release/freedesktop/snap/README.md17
-rw-r--r--release/freedesktop/snap/README.txt38
-rw-r--r--release/freedesktop/snap/blender-snapcraft-template.yaml (renamed from release/freedesktop/snap/snapcraft.yaml.in)16
-rwxr-xr-xrelease/freedesktop/snap/bundle.py21
-rwxr-xr-xrelease/lts/create_download_urls.py9
m---------release/scripts/addons0
m---------release/scripts/addons_contrib0
-rw-r--r--release/scripts/freestyle/styles/ignore_small_occlusions.py2
-rw-r--r--release/scripts/modules/addon_utils.py32
-rw-r--r--release/scripts/modules/animsys_refactor.py8
-rw-r--r--release/scripts/modules/bl_app_template_utils.py10
-rw-r--r--release/scripts/modules/bl_i18n_utils/utils_spell_check.py2
-rw-r--r--release/scripts/modules/bl_keymap_utils/keymap_from_toolbar.py2
-rw-r--r--release/scripts/modules/bl_keymap_utils/platform_helpers.py4
-rw-r--r--release/scripts/modules/bl_previews_utils/bl_previews_render.py14
-rw-r--r--release/scripts/modules/bl_rna_utils/__init__.py0
-rw-r--r--release/scripts/modules/bl_rna_utils/data_path.py91
-rw-r--r--release/scripts/modules/bl_ui_utils/bug_report_url.py10
-rw-r--r--release/scripts/modules/bpy/path.py15
-rw-r--r--release/scripts/modules/bpy/utils/__init__.py42
-rw-r--r--release/scripts/modules/bpy_extras/anim_utils.py2
-rw-r--r--release/scripts/modules/bpy_extras/asset_utils.py2
-rw-r--r--release/scripts/modules/bpy_extras/io_utils.py14
-rw-r--r--release/scripts/modules/bpy_extras/view3d_utils.py4
-rw-r--r--release/scripts/modules/bpy_types.py36
-rw-r--r--release/scripts/modules/console/complete_calltip.py2
-rw-r--r--release/scripts/modules/console/complete_import.py4
-rw-r--r--release/scripts/modules/console/complete_namespace.py6
-rw-r--r--release/scripts/modules/console/intellisense.py6
-rw-r--r--release/scripts/modules/gpu_extras/batch.py2
-rw-r--r--release/scripts/modules/gpu_extras/presets.py23
-rw-r--r--release/scripts/modules/keyingsets_utils.py34
-rw-r--r--release/scripts/modules/nodeitems_utils.py6
-rw-r--r--release/scripts/modules/rna_keymap_ui.py2
-rw-r--r--release/scripts/modules/rna_manual_reference.py176
-rw-r--r--release/scripts/modules/rna_prop_ui.py18
-rw-r--r--release/scripts/modules/rna_xml.py9
-rw-r--r--release/scripts/modules/sys_info.py52
-rw-r--r--release/scripts/presets/camera/1_colon_2.3_inch.py4
-rw-r--r--release/scripts/presets/camera/1_inch.py4
-rw-r--r--release/scripts/presets/camera/1_slash_1.8_inch.py4
-rw-r--r--release/scripts/presets/camera/1_slash_2.3_inch.py4
-rw-r--r--release/scripts/presets/camera/1_slash_2.5_inch.py (renamed from release/scripts/presets/camera/1_colon_2.5_inch.py)2
-rw-r--r--release/scripts/presets/camera/1_slash_2.7_inch.py4
-rw-r--r--release/scripts/presets/camera/1_slash_3.2_inch.py (renamed from release/scripts/presets/camera/iPhone_4.py)3
-rw-r--r--release/scripts/presets/camera/2_colon_3_inch.py4
-rw-r--r--release/scripts/presets/camera/2_slash_3_inch.py4
-rw-r--r--release/scripts/presets/camera/4_colon_3_inch.py4
-rw-r--r--release/scripts/presets/camera/APS-C.py4
-rw-r--r--release/scripts/presets/camera/APS-C_(Canon).py4
-rw-r--r--release/scripts/presets/camera/APS-H_(Canon).py (renamed from release/scripts/presets/camera/Canon_APS-H.py)2
-rw-r--r--release/scripts/presets/camera/Analog_16mm.py4
-rw-r--r--release/scripts/presets/camera/Analog_35mm.py4
-rw-r--r--release/scripts/presets/camera/Analog_65mm.py4
-rw-r--r--release/scripts/presets/camera/Analog_IMAX.py4
-rw-r--r--release/scripts/presets/camera/Analog_Super_16.py4
-rw-r--r--release/scripts/presets/camera/Analog_Super_35.py (renamed from release/scripts/presets/camera/Super_35_Film.py)2
-rw-r--r--release/scripts/presets/camera/Arri_Alexa.py4
-rw-r--r--release/scripts/presets/camera/Arri_Alexa_65.py4
-rw-r--r--release/scripts/presets/camera/Arri_Alexa_LF.py4
-rw-r--r--release/scripts/presets/camera/Arri_Alexa_Mini_&_SXT.py4
-rw-r--r--release/scripts/presets/camera/Blackmagic_Cinema_Camera.py4
-rw-r--r--release/scripts/presets/camera/Blackmagic_Pocket_&_Studio.py (renamed from release/scripts/presets/camera/Blackmagic_Pocket_Cinema_Camera.py)2
-rw-r--r--release/scripts/presets/camera/Blackmagic_Pocket_4K.py4
-rw-r--r--release/scripts/presets/camera/Blackmagic_Pocket_6k.py4
-rw-r--r--release/scripts/presets/camera/Blackmagic_Production_Camera_4K.py4
-rw-r--r--release/scripts/presets/camera/Blackmagic_URSA_4.6K.py4
-rw-r--r--release/scripts/presets/camera/Blender.py4
-rw-r--r--release/scripts/presets/camera/Canon_1100D.py4
-rw-r--r--release/scripts/presets/camera/Canon_APS-C.py4
-rw-r--r--release/scripts/presets/camera/Canon_C300.py4
-rw-r--r--release/scripts/presets/camera/Foveon_(Sigma).py4
-rw-r--r--release/scripts/presets/camera/Fullframe.py (renamed from release/scripts/presets/camera/Full_Frame_35mm_Camera.py)2
-rw-r--r--release/scripts/presets/camera/GoPro_Hero3_Black.py6
-rw-r--r--release/scripts/presets/camera/GoPro_Hero3_Silver.py6
-rw-r--r--release/scripts/presets/camera/MFT.py4
-rw-r--r--release/scripts/presets/camera/Medium-format_(Hasselblad).py4
-rw-r--r--release/scripts/presets/camera/Nexus_5.py5
-rw-r--r--release/scripts/presets/camera/Nikon_D3100.py4
-rw-r--r--release/scripts/presets/camera/Nikon_DX.py4
-rw-r--r--release/scripts/presets/camera/Panasonic_AG-HVX200.py4
-rw-r--r--release/scripts/presets/camera/Panasonic_LX2.py4
-rw-r--r--release/scripts/presets/camera/RED_Dragon_5K.py4
-rw-r--r--release/scripts/presets/camera/RED_Dragon_6K.py4
-rw-r--r--release/scripts/presets/camera/RED_Helium_8K.py4
-rw-r--r--release/scripts/presets/camera/RED_Monstro_8K.py4
-rw-r--r--release/scripts/presets/camera/Red_Epic.py4
-rw-r--r--release/scripts/presets/camera/Red_One_2K.py4
-rw-r--r--release/scripts/presets/camera/Red_One_3K.py4
-rw-r--r--release/scripts/presets/camera/Red_One_4K.py4
-rw-r--r--release/scripts/presets/camera/Samsung_Galaxy_S3.py5
-rw-r--r--release/scripts/presets/camera/Samsung_Galaxy_S4.py5
-rw-r--r--release/scripts/presets/camera/Sony_A55.py4
-rw-r--r--release/scripts/presets/camera/Sony_EX1.py4
-rw-r--r--release/scripts/presets/camera/Sony_F65.py4
-rw-r--r--release/scripts/presets/camera/Super_16_Film.py4
-rw-r--r--release/scripts/presets/camera/iPhone_5.py5
-rw-r--r--release/scripts/presets/cycles/integrator/Default.py14
-rw-r--r--release/scripts/presets/cycles/integrator/Direct_Light.py3
-rw-r--r--release/scripts/presets/cycles/integrator/Fast_Global_Illumination.py14
-rw-r--r--release/scripts/presets/cycles/integrator/Full_Global_Illumination.py15
-rw-r--r--release/scripts/presets/cycles/integrator/Limited_Global_Illumination.py5
-rw-r--r--release/scripts/presets/keyconfig/Blender.py6
-rw-r--r--release/scripts/presets/keyconfig/keymap_data/blender_default.py125
-rw-r--r--release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py9
-rw-r--r--release/scripts/presets/tracking_camera/1__colon__2.3_inch.py9
-rw-r--r--release/scripts/presets/tracking_camera/1__colon__2.5_inch.py9
-rw-r--r--release/scripts/presets/tracking_camera/1_inch.py4
-rw-r--r--release/scripts/presets/tracking_camera/1_slash_1.8_inch.py4
-rw-r--r--release/scripts/presets/tracking_camera/1_slash_2.3_inch.py4
-rw-r--r--release/scripts/presets/tracking_camera/1_slash_2.5_inch.py (renamed from release/scripts/presets/camera/GoPro_Hero3_White.py)4
-rw-r--r--release/scripts/presets/tracking_camera/1_slash_2.7_inch.py4
-rw-r--r--release/scripts/presets/tracking_camera/1_slash_3.2_inch.py (renamed from release/scripts/presets/camera/iPhone_4S.py)3
-rw-r--r--release/scripts/presets/tracking_camera/2__colon__3_inch.py9
-rw-r--r--release/scripts/presets/tracking_camera/2_slash_3_inch.py4
-rw-r--r--release/scripts/presets/tracking_camera/4__colon__3_inch.py9
-rw-r--r--release/scripts/presets/tracking_camera/APS-C.py4
-rw-r--r--release/scripts/presets/tracking_camera/APS-C_(Canon).py4
-rw-r--r--release/scripts/presets/tracking_camera/APS-H_(Canon).py4
-rw-r--r--release/scripts/presets/tracking_camera/Analog_16mm.py4
-rw-r--r--release/scripts/presets/tracking_camera/Analog_35mm.py4
-rw-r--r--release/scripts/presets/tracking_camera/Analog_65mm.py4
-rw-r--r--release/scripts/presets/tracking_camera/Analog_IMAX.py4
-rw-r--r--release/scripts/presets/tracking_camera/Analog_Super_16.py4
-rw-r--r--release/scripts/presets/tracking_camera/Analog_Super_35.py4
-rw-r--r--release/scripts/presets/tracking_camera/Arri_Alexa.py9
-rw-r--r--release/scripts/presets/tracking_camera/Arri_Alexa_65.py4
-rw-r--r--release/scripts/presets/tracking_camera/Arri_Alexa_LF.py4
-rw-r--r--release/scripts/presets/tracking_camera/Arri_Alexa_Mini_&_SXT.py4
-rw-r--r--release/scripts/presets/tracking_camera/Blackmagic_Cinema_Camera.py9
-rw-r--r--release/scripts/presets/tracking_camera/Blackmagic_Pocket_&_Studio.py4
-rw-r--r--release/scripts/presets/tracking_camera/Blackmagic_Pocket_4K.py4
-rw-r--r--release/scripts/presets/tracking_camera/Blackmagic_Pocket_6k.py4
-rw-r--r--release/scripts/presets/tracking_camera/Blackmagic_Pocket_Cinema_Camera.py9
-rw-r--r--release/scripts/presets/tracking_camera/Blackmagic_Production_Camera_4K.py9
-rw-r--r--release/scripts/presets/tracking_camera/Blackmagic_URSA_4.6K.py4
-rw-r--r--release/scripts/presets/tracking_camera/Blender.py10
-rw-r--r--release/scripts/presets/tracking_camera/Canon_1100D.py9
-rw-r--r--release/scripts/presets/tracking_camera/Canon_APS-C.py9
-rw-r--r--release/scripts/presets/tracking_camera/Canon_APS-H.py9
-rw-r--r--release/scripts/presets/tracking_camera/Canon_C300.py9
-rw-r--r--release/scripts/presets/tracking_camera/Foveon_(Sigma).py4
-rw-r--r--release/scripts/presets/tracking_camera/Full_Frame_35mm_Camera.py9
-rw-r--r--release/scripts/presets/tracking_camera/Fullframe.py4
-rw-r--r--release/scripts/presets/tracking_camera/GoPro_Hero3_Black.py10
-rw-r--r--release/scripts/presets/tracking_camera/GoPro_Hero3_Silver.py10
-rw-r--r--release/scripts/presets/tracking_camera/GoPro_Hero3_White.py10
-rw-r--r--release/scripts/presets/tracking_camera/MFT.py4
-rw-r--r--release/scripts/presets/tracking_camera/Medium-format_(Hasselblad).py4
-rw-r--r--release/scripts/presets/tracking_camera/Nexus_5.py10
-rw-r--r--release/scripts/presets/tracking_camera/Nikon_D3100.py9
-rw-r--r--release/scripts/presets/tracking_camera/Nikon_DX.py9
-rw-r--r--release/scripts/presets/tracking_camera/Panasonic_AG-HVX200.py9
-rw-r--r--release/scripts/presets/tracking_camera/Panasonic_LX2.py9
-rw-r--r--release/scripts/presets/tracking_camera/RED_Dragon_5K.py4
-rw-r--r--release/scripts/presets/tracking_camera/RED_Dragon_6K.py4
-rw-r--r--release/scripts/presets/tracking_camera/RED_Helium_8K.py4
-rw-r--r--release/scripts/presets/tracking_camera/RED_Monstro_8K.py4
-rw-r--r--release/scripts/presets/tracking_camera/Red_Epic.py9
-rw-r--r--release/scripts/presets/tracking_camera/Red_One_2K.py9
-rw-r--r--release/scripts/presets/tracking_camera/Red_One_3K.py9
-rw-r--r--release/scripts/presets/tracking_camera/Red_One_4K.py9
-rw-r--r--release/scripts/presets/tracking_camera/Samsung_Galaxy_S3.py10
-rw-r--r--release/scripts/presets/tracking_camera/Samsung_Galaxy_S4.py10
-rw-r--r--release/scripts/presets/tracking_camera/Sony_A55.py9
-rw-r--r--release/scripts/presets/tracking_camera/Sony_EX1.py9
-rw-r--r--release/scripts/presets/tracking_camera/Sony_F65.py9
-rw-r--r--release/scripts/presets/tracking_camera/Super_16.py9
-rw-r--r--release/scripts/presets/tracking_camera/Super_35.py9
-rw-r--r--release/scripts/presets/tracking_camera/iPhone_4.py10
-rw-r--r--release/scripts/presets/tracking_camera/iPhone_4S.py10
-rw-r--r--release/scripts/presets/tracking_camera/iPhone_5.py10
-rw-r--r--release/scripts/startup/bl_operators/anim.py3
-rw-r--r--release/scripts/startup/bl_operators/clip.py64
-rw-r--r--release/scripts/startup/bl_operators/constraint.py15
-rw-r--r--release/scripts/startup/bl_operators/geometry_nodes.py4
-rw-r--r--release/scripts/startup/bl_operators/mesh.py4
-rw-r--r--release/scripts/startup/bl_operators/node.py72
-rw-r--r--release/scripts/startup/bl_operators/object.py2
-rw-r--r--release/scripts/startup/bl_operators/presets.py4
-rw-r--r--release/scripts/startup/bl_operators/screen_play_rendered_anim.py3
-rw-r--r--release/scripts/startup/bl_operators/spreadsheet.py40
-rw-r--r--release/scripts/startup/bl_operators/userpref.py42
-rw-r--r--release/scripts/startup/bl_operators/uvcalc_follow_active.py2
-rw-r--r--release/scripts/startup/bl_operators/uvcalc_lightmap.py2
-rw-r--r--release/scripts/startup/bl_operators/view3d.py3
-rw-r--r--release/scripts/startup/bl_operators/wm.py200
-rw-r--r--release/scripts/startup/bl_ui/properties_collection.py1
-rw-r--r--release/scripts/startup/bl_ui/properties_constraint.py1
-rw-r--r--release/scripts/startup/bl_ui/properties_data_armature.py9
-rw-r--r--release/scripts/startup/bl_ui/properties_data_bone.py7
-rw-r--r--release/scripts/startup/bl_ui/properties_data_curve.py1
-rw-r--r--release/scripts/startup/bl_ui/properties_data_gpencil.py3
-rw-r--r--release/scripts/startup/bl_ui/properties_data_pointcloud.py8
-rw-r--r--release/scripts/startup/bl_ui/properties_grease_pencil_common.py88
-rw-r--r--release/scripts/startup/bl_ui/properties_material.py13
-rw-r--r--release/scripts/startup/bl_ui/properties_material_gpencil.py14
-rw-r--r--release/scripts/startup/bl_ui/properties_object.py5
-rw-r--r--release/scripts/startup/bl_ui/properties_paint_common.py8
-rw-r--r--release/scripts/startup/bl_ui/properties_particle.py20
-rw-r--r--release/scripts/startup/bl_ui/properties_physics_common.py9
-rw-r--r--release/scripts/startup/bl_ui/space_filebrowser.py4
-rw-r--r--release/scripts/startup/bl_ui/space_image.py3
-rw-r--r--release/scripts/startup/bl_ui/space_info.py13
-rw-r--r--release/scripts/startup/bl_ui/space_node.py7
-rw-r--r--release/scripts/startup/bl_ui/space_outliner.py43
-rw-r--r--release/scripts/startup/bl_ui/space_sequencer.py17
-rw-r--r--release/scripts/startup/bl_ui/space_spreadsheet.py66
-rw-r--r--release/scripts/startup/bl_ui/space_text.py4
-rw-r--r--release/scripts/startup/bl_ui/space_time.py4
-rw-r--r--release/scripts/startup/bl_ui/space_toolsystem_common.py2
-rw-r--r--release/scripts/startup/bl_ui/space_toolsystem_toolbar.py34
-rw-r--r--release/scripts/startup/bl_ui/space_topbar.py10
-rw-r--r--release/scripts/startup/bl_ui/space_userpref.py7
-rw-r--r--release/scripts/startup/bl_ui/space_view3d.py93
-rw-r--r--release/scripts/startup/bl_ui/space_view3d_toolbar.py82
-rw-r--r--release/scripts/startup/keyingsets_builtins.py125
-rw-r--r--release/scripts/startup/nodeitems_builtins.py67
-rw-r--r--release/scripts/templates_osl/basic_shader.osl10
-rw-r--r--release/scripts/templates_py/bmesh_simple_editmode.py2
-rw-r--r--release/scripts/templates_py/gizmo_custom_geometry.py2
-rw-r--r--release/scripts/templates_py/image_processing.py35
-rw-r--r--release/scripts/templates_py/operator_modal_draw.py9
-rw-r--r--release/steam/README.md70
-rw-r--r--release/steam/blender_app_build.vdf.template17
-rw-r--r--release/steam/create_steam_builds.py397
-rw-r--r--release/steam/depot_build_linux.vdf.template31
-rw-r--r--release/steam/depot_build_macos.vdf.template30
-rw-r--r--release/steam/depot_build_win.vdf.template31
-rw-r--r--release/windows/manifest/blender.exe.manifest.in6
-rw-r--r--release/windows/msix/README.md84
-rw-r--r--release/windows/msix/create_msix_package.py197
-rw-r--r--source/blender/blendthumb/CMakeLists.txt1
-rw-r--r--source/blender/blenfont/intern/blf_font.c38
-rw-r--r--source/blender/blenfont/intern/blf_font_win32_compat.c6
-rw-r--r--source/blender/blenfont/intern/blf_glyph.c2
-rw-r--r--source/blender/blenkernel/BKE_action.h11
-rw-r--r--source/blender/blenkernel/BKE_anim_path.h28
-rw-r--r--source/blender/blenkernel/BKE_armature.h3
-rw-r--r--source/blender/blenkernel/BKE_attribute_access.hh423
-rw-r--r--source/blender/blenkernel/BKE_attribute_math.hh109
-rw-r--r--source/blender/blenkernel/BKE_blender_version.h4
-rw-r--r--source/blender/blenkernel/BKE_callbacks.h60
-rw-r--r--source/blender/blenkernel/BKE_collection.h8
-rw-r--r--source/blender/blenkernel/BKE_context.h19
-rw-r--r--source/blender/blenkernel/BKE_curve.h39
-rw-r--r--source/blender/blenkernel/BKE_displist.h45
-rw-r--r--source/blender/blenkernel/BKE_editmesh.h3
-rw-r--r--source/blender/blenkernel/BKE_fcurve.h21
-rw-r--r--source/blender/blenkernel/BKE_geometry_set.h19
-rw-r--r--source/blender/blenkernel/BKE_geometry_set.hh395
-rw-r--r--source/blender/blenkernel/BKE_geometry_set_instances.hh15
-rw-r--r--source/blender/blenkernel/BKE_global.h14
-rw-r--r--source/blender/blenkernel/BKE_gpencil.h5
-rw-r--r--source/blender/blenkernel/BKE_gpencil_geom.h7
-rw-r--r--source/blender/blenkernel/BKE_image.h1
-rw-r--r--source/blender/blenkernel/BKE_lib_id.h17
-rw-r--r--source/blender/blenkernel/BKE_lib_override.h16
-rw-r--r--source/blender/blenkernel/BKE_lib_query.h5
-rw-r--r--source/blender/blenkernel/BKE_lib_remap.h4
-rw-r--r--source/blender/blenkernel/BKE_material.h10
-rw-r--r--source/blender/blenkernel/BKE_mesh.h9
-rw-r--r--source/blender/blenkernel/BKE_mesh_boolean_convert.hh43
-rw-r--r--source/blender/blenkernel/BKE_mesh_sample.hh55
-rw-r--r--source/blender/blenkernel/BKE_modifier.h8
-rw-r--r--source/blender/blenkernel/BKE_nla.h9
-rw-r--r--source/blender/blenkernel/BKE_node.h78
-rw-r--r--source/blender/blenkernel/BKE_node_ui_storage.hh20
-rw-r--r--source/blender/blenkernel/BKE_object.h12
-rw-r--r--source/blender/blenkernel/BKE_paint.h6
-rw-r--r--source/blender/blenkernel/BKE_persistent_data_handle.hh153
-rw-r--r--source/blender/blenkernel/BKE_scene.h3
-rw-r--r--source/blender/blenkernel/BKE_softbody.h2
-rw-r--r--source/blender/blenkernel/BKE_spline.hh551
-rw-r--r--source/blender/blenkernel/BKE_unit.h13
-rw-r--r--source/blender/blenkernel/BKE_volume.h42
-rw-r--r--source/blender/blenkernel/BKE_volume_render.h8
-rw-r--r--source/blender/blenkernel/CMakeLists.txt28
-rw-r--r--source/blender/blenkernel/intern/DerivedMesh.cc24
-rw-r--r--source/blender/blenkernel/intern/action.c42
-rw-r--r--source/blender/blenkernel/intern/action_mirror.c457
-rw-r--r--source/blender/blenkernel/intern/anim_data.c14
-rw-r--r--source/blender/blenkernel/intern/anim_path.c410
-rw-r--r--source/blender/blenkernel/intern/anim_sys.c548
-rw-r--r--source/blender/blenkernel/intern/appdir.c2
-rw-r--r--source/blender/blenkernel/intern/armature.c29
-rw-r--r--source/blender/blenkernel/intern/armature_deform.c16
-rw-r--r--source/blender/blenkernel/intern/armature_pose.cc10
-rw-r--r--source/blender/blenkernel/intern/armature_update.c589
-rw-r--r--source/blender/blenkernel/intern/attribute_access.cc907
-rw-r--r--source/blender/blenkernel/intern/attribute_access_intern.hh230
-rw-r--r--source/blender/blenkernel/intern/attribute_math.cc15
-rw-r--r--source/blender/blenkernel/intern/blender.c2
-rw-r--r--source/blender/blenkernel/intern/blendfile.c5
-rw-r--r--source/blender/blenkernel/intern/brush.c13
-rw-r--r--source/blender/blenkernel/intern/bvhutils.c4
-rw-r--r--source/blender/blenkernel/intern/collection.c141
-rw-r--r--source/blender/blenkernel/intern/constraint.c59
-rw-r--r--source/blender/blenkernel/intern/context.c51
-rw-r--r--source/blender/blenkernel/intern/cryptomatte.cc9
-rw-r--r--source/blender/blenkernel/intern/curve.c114
-rw-r--r--source/blender/blenkernel/intern/curve_bevel.c28
-rw-r--r--source/blender/blenkernel/intern/curve_convert.c2
-rw-r--r--source/blender/blenkernel/intern/curve_deform.c86
-rw-r--r--source/blender/blenkernel/intern/curve_eval.cc272
-rw-r--r--source/blender/blenkernel/intern/curveprofile.c3
-rw-r--r--source/blender/blenkernel/intern/customdata.c4
-rw-r--r--source/blender/blenkernel/intern/customdata_file.c10
-rw-r--r--source/blender/blenkernel/intern/deform.c11
-rw-r--r--source/blender/blenkernel/intern/displist.cc (renamed from source/blender/blenkernel/intern/displist.c)892
-rw-r--r--source/blender/blenkernel/intern/dynamicpaint.c2
-rw-r--r--source/blender/blenkernel/intern/editmesh.c11
-rw-r--r--source/blender/blenkernel/intern/editmesh_tangent.c2
-rw-r--r--source/blender/blenkernel/intern/effect.c8
-rw-r--r--source/blender/blenkernel/intern/fcurve.c74
-rw-r--r--source/blender/blenkernel/intern/fcurve_cache.c189
-rw-r--r--source/blender/blenkernel/intern/fluid.c6
-rw-r--r--source/blender/blenkernel/intern/font.c10
-rw-r--r--source/blender/blenkernel/intern/geometry_component_curve.cc1199
-rw-r--r--source/blender/blenkernel/intern/geometry_component_instances.cc110
-rw-r--r--source/blender/blenkernel/intern/geometry_component_mesh.cc594
-rw-r--r--source/blender/blenkernel/intern/geometry_component_pointcloud.cc73
-rw-r--r--source/blender/blenkernel/intern/geometry_component_volume.cc14
-rw-r--r--source/blender/blenkernel/intern/geometry_set.cc94
-rw-r--r--source/blender/blenkernel/intern/geometry_set_instances.cc347
-rw-r--r--source/blender/blenkernel/intern/gpencil.c45
-rw-r--r--source/blender/blenkernel/intern/gpencil_curve.c4
-rw-r--r--source/blender/blenkernel/intern/gpencil_geom.c184
-rw-r--r--source/blender/blenkernel/intern/gpencil_modifier.c10
-rw-r--r--source/blender/blenkernel/intern/idprop.c2
-rw-r--r--source/blender/blenkernel/intern/image.c31
-rw-r--r--source/blender/blenkernel/intern/image_gen.c21
-rw-r--r--source/blender/blenkernel/intern/image_gpu.c24
-rw-r--r--source/blender/blenkernel/intern/key.c9
-rw-r--r--source/blender/blenkernel/intern/lattice.c62
-rw-r--r--source/blender/blenkernel/intern/lattice_deform.c22
-rw-r--r--source/blender/blenkernel/intern/lattice_deform_test.cc1
-rw-r--r--source/blender/blenkernel/intern/lib_id.c120
-rw-r--r--source/blender/blenkernel/intern/lib_id_test.cc173
-rw-r--r--source/blender/blenkernel/intern/lib_override.c951
-rw-r--r--source/blender/blenkernel/intern/lib_query.c46
-rw-r--r--source/blender/blenkernel/intern/lib_remap.c22
-rw-r--r--source/blender/blenkernel/intern/main.c4
-rw-r--r--source/blender/blenkernel/intern/mask.c9
-rw-r--r--source/blender/blenkernel/intern/material.c166
-rw-r--r--source/blender/blenkernel/intern/mball_tessellate.c9
-rw-r--r--source/blender/blenkernel/intern/mesh.c4
-rw-r--r--source/blender/blenkernel/intern/mesh_boolean_convert.cc219
-rw-r--r--source/blender/blenkernel/intern/mesh_convert.c90
-rw-r--r--source/blender/blenkernel/intern/mesh_evaluate.c7
-rw-r--r--source/blender/blenkernel/intern/mesh_fair.cc2
-rw-r--r--source/blender/blenkernel/intern/mesh_mirror.c15
-rw-r--r--source/blender/blenkernel/intern/mesh_remap.c3
-rw-r--r--source/blender/blenkernel/intern/mesh_sample.cc158
-rw-r--r--source/blender/blenkernel/intern/mesh_tangent.c2
-rw-r--r--source/blender/blenkernel/intern/modifier.c6
-rw-r--r--source/blender/blenkernel/intern/movieclip.c2
-rw-r--r--source/blender/blenkernel/intern/nla.c62
-rw-r--r--source/blender/blenkernel/intern/node.cc200
-rw-r--r--source/blender/blenkernel/intern/node_ui_storage.cc29
-rw-r--r--source/blender/blenkernel/intern/object.c131
-rw-r--r--source/blender/blenkernel/intern/object_dupli.cc (renamed from source/blender/blenkernel/intern/object_dupli.c)442
-rw-r--r--source/blender/blenkernel/intern/object_update.c7
-rw-r--r--source/blender/blenkernel/intern/ocean.c10
-rw-r--r--source/blender/blenkernel/intern/particle.c9
-rw-r--r--source/blender/blenkernel/intern/particle_distribute.c2
-rw-r--r--source/blender/blenkernel/intern/particle_system.c16
-rw-r--r--source/blender/blenkernel/intern/pbvh_intern.h28
-rw-r--r--source/blender/blenkernel/intern/pointcache.c2
-rw-r--r--source/blender/blenkernel/intern/rigidbody.c6
-rw-r--r--source/blender/blenkernel/intern/scene.c89
-rw-r--r--source/blender/blenkernel/intern/screen.c70
-rw-r--r--source/blender/blenkernel/intern/softbody.c37
-rw-r--r--source/blender/blenkernel/intern/spline_base.cc380
-rw-r--r--source/blender/blenkernel/intern/spline_bezier.cc592
-rw-r--r--source/blender/blenkernel/intern/spline_nurbs.cc444
-rw-r--r--source/blender/blenkernel/intern/spline_poly.cc124
-rw-r--r--source/blender/blenkernel/intern/subdiv_ccg.c154
-rw-r--r--source/blender/blenkernel/intern/subsurf_ccg.c3
-rw-r--r--source/blender/blenkernel/intern/text.c19
-rw-r--r--source/blender/blenkernel/intern/tracking.c5
-rw-r--r--source/blender/blenkernel/intern/unit.c10
-rw-r--r--source/blender/blenkernel/intern/volume.cc280
-rw-r--r--source/blender/blenkernel/intern/volume_render.cc6
-rw-r--r--source/blender/blenkernel/intern/writeffmpeg.c551
-rw-r--r--source/blender/blenkernel/nla_private.h11
-rw-r--r--source/blender/blenlib/BLI_asan.h2
-rw-r--r--source/blender/blenlib/BLI_color.hh302
-rw-r--r--source/blender/blenlib/BLI_compiler_attrs.h7
-rw-r--r--source/blender/blenlib/BLI_endian_defines.h38
-rw-r--r--source/blender/blenlib/BLI_enumerable_thread_specific.hh73
-rw-r--r--source/blender/blenlib/BLI_fileops.h20
-rw-r--r--source/blender/blenlib/BLI_float3.hh7
-rw-r--r--source/blender/blenlib/BLI_float4x4.hh44
-rw-r--r--source/blender/blenlib/BLI_function_ref.hh24
-rw-r--r--source/blender/blenlib/BLI_hash.hh17
-rw-r--r--source/blender/blenlib/BLI_index_mask.hh2
-rw-r--r--source/blender/blenlib/BLI_iterator.h10
-rw-r--r--source/blender/blenlib/BLI_linear_allocator.hh18
-rw-r--r--source/blender/blenlib/BLI_map.hh195
-rw-r--r--source/blender/blenlib/BLI_map_slots.hh12
-rw-r--r--source/blender/blenlib/BLI_math_base.h3
-rw-r--r--source/blender/blenlib/BLI_math_color.h5
-rw-r--r--source/blender/blenlib/BLI_math_geom.h10
-rw-r--r--source/blender/blenlib/BLI_math_matrix.h5
-rw-r--r--source/blender/blenlib/BLI_math_solvers.h2
-rw-r--r--source/blender/blenlib/BLI_math_vector.h8
-rw-r--r--source/blender/blenlib/BLI_memarena.h3
-rw-r--r--source/blender/blenlib/BLI_memiter.h21
-rw-r--r--source/blender/blenlib/BLI_mempool.h16
-rw-r--r--source/blender/blenlib/BLI_mesh_intersect.hh61
-rw-r--r--source/blender/blenlib/BLI_mpq3.hh16
-rw-r--r--source/blender/blenlib/BLI_multi_value_map.hh6
-rw-r--r--source/blender/blenlib/BLI_resource_scope.hh (renamed from source/blender/blenlib/BLI_resource_collector.hh)62
-rw-r--r--source/blender/blenlib/BLI_span.hh14
-rw-r--r--source/blender/blenlib/BLI_stack.hh5
-rw-r--r--source/blender/blenlib/BLI_string.h5
-rw-r--r--source/blender/blenlib/BLI_task.h59
-rw-r--r--source/blender/blenlib/BLI_vector.hh48
-rw-r--r--source/blender/blenlib/BLI_vector_set.hh77
-rw-r--r--source/blender/blenlib/BLI_virtual_array.hh427
-rw-r--r--source/blender/blenlib/CMakeLists.txt9
-rw-r--r--source/blender/blenlib/intern/BLI_color.cc55
-rw-r--r--source/blender/blenlib/intern/BLI_dial_2d.c2
-rw-r--r--source/blender/blenlib/intern/BLI_mempool.c105
-rw-r--r--source/blender/blenlib/intern/BLI_mempool_private.h52
-rw-r--r--source/blender/blenlib/intern/array_utils.c4
-rw-r--r--source/blender/blenlib/intern/delaunay_2d.cc3
-rw-r--r--source/blender/blenlib/intern/fileops.c4
-rw-r--r--source/blender/blenlib/intern/freetypefont.c1
-rw-r--r--source/blender/blenlib/intern/list_sort_impl.h6
-rw-r--r--source/blender/blenlib/intern/math_base_inline.c16
-rw-r--r--source/blender/blenlib/intern/math_color.c72
-rw-r--r--source/blender/blenlib/intern/math_geom.c4
-rw-r--r--source/blender/blenlib/intern/math_matrix.c151
-rw-r--r--source/blender/blenlib/intern/math_vector.c2
-rw-r--r--source/blender/blenlib/intern/memory_utils.c2
-rw-r--r--source/blender/blenlib/intern/mesh_boolean.cc413
-rw-r--r--source/blender/blenlib/intern/mesh_intersect.cc703
-rw-r--r--source/blender/blenlib/intern/noise.c6
-rw-r--r--source/blender/blenlib/intern/path_util.c16
-rw-r--r--source/blender/blenlib/intern/polyfill_2d_beautify.c2
-rw-r--r--source/blender/blenlib/intern/storage.c18
-rw-r--r--source/blender/blenlib/intern/string.c23
-rw-r--r--source/blender/blenlib/intern/task_iterator.c136
-rw-r--r--source/blender/blenlib/intern/task_pool.cc85
-rw-r--r--source/blender/blenlib/intern/timecode.c4
-rw-r--r--source/blender/blenlib/intern/uvproject.c2
-rw-r--r--source/blender/blenlib/intern/winstuff.c4
-rw-r--r--source/blender/blenlib/tests/BLI_color_test.cc133
-rw-r--r--source/blender/blenlib/tests/BLI_function_ref_test.cc25
-rw-r--r--source/blender/blenlib/tests/BLI_linear_allocator_test.cc13
-rw-r--r--source/blender/blenlib/tests/BLI_linklist_lockfree_test.cc2
-rw-r--r--source/blender/blenlib/tests/BLI_map_test.cc49
-rw-r--r--source/blender/blenlib/tests/BLI_stack_cxx_test.cc9
-rw-r--r--source/blender/blenlib/tests/BLI_task_test.cc103
-rw-r--r--source/blender/blenlib/tests/BLI_vector_set_test.cc39
-rw-r--r--source/blender/blenlib/tests/BLI_vector_test.cc9
-rw-r--r--source/blender/blenlib/tests/BLI_virtual_array_test.cc133
-rw-r--r--source/blender/blenloader/BLO_readfile.h5
-rw-r--r--source/blender/blenloader/CMakeLists.txt1
-rw-r--r--source/blender/blenloader/intern/readfile.c24
-rw-r--r--source/blender/blenloader/intern/readfile.h2
-rw-r--r--source/blender/blenloader/intern/readfile_tempload.c21
-rw-r--r--source/blender/blenloader/intern/versioning_250.c3
-rw-r--r--source/blender/blenloader/intern/versioning_280.c14
-rw-r--r--source/blender/blenloader/intern/versioning_290.c174
-rw-r--r--source/blender/blenloader/intern/versioning_300.c263
-rw-r--r--source/blender/blenloader/intern/versioning_legacy.c11
-rw-r--r--source/blender/blenloader/intern/versioning_userdef.c43
-rw-r--r--source/blender/blenloader/intern/writefile.c1
-rw-r--r--source/blender/blenloader/tests/blendfile_loading_base_test.cc4
-rw-r--r--source/blender/blenloader/tests/blendfile_loading_base_test.h2
-rw-r--r--source/blender/bmesh/CMakeLists.txt14
-rw-r--r--source/blender/bmesh/bmesh.h3
-rw-r--r--source/blender/bmesh/intern/bmesh_core.c26
-rw-r--r--source/blender/bmesh/intern/bmesh_core.h3
-rw-r--r--source/blender/bmesh/intern/bmesh_iterators_inline.h8
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh.c1659
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh.h38
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh_normals.c1859
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh_normals.h62
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh_partial_update.c254
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh_partial_update.h64
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh_tessellate.c457
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh_tessellate.h30
-rw-r--r--source/blender/bmesh/intern/bmesh_mods.c28
-rw-r--r--source/blender/bmesh/intern/bmesh_mods.h6
-rw-r--r--source/blender/bmesh/intern/bmesh_operators.c4
-rw-r--r--source/blender/bmesh/intern/bmesh_polygon.c289
-rw-r--r--source/blender/bmesh/intern/bmesh_polygon.h4
-rw-r--r--source/blender/bmesh/intern/bmesh_polygon_edgenet.c22
-rw-r--r--source/blender/bmesh/operators/bmo_connect_pair.c10
-rw-r--r--source/blender/bmesh/operators/bmo_dissolve.c22
-rw-r--r--source/blender/bmesh/operators/bmo_offset_edgeloops.c2
-rw-r--r--source/blender/bmesh/operators/bmo_primitive.c29
-rw-r--r--source/blender/bmesh/tools/bmesh_bevel.c4
-rw-r--r--source/blender/bmesh/tools/bmesh_bisect_plane.c188
-rw-r--r--source/blender/bmesh/tools/bmesh_boolean.cc3
-rw-r--r--source/blender/bmesh/tools/bmesh_decimate_dissolve.c4
-rw-r--r--source/blender/bmesh/tools/bmesh_decimate_unsubdivide.c2
-rw-r--r--source/blender/bmesh/tools/bmesh_intersect.c6
-rw-r--r--source/blender/bmesh/tools/bmesh_intersect.h8
-rw-r--r--source/blender/compositor/CMakeLists.txt43
-rw-r--r--source/blender/compositor/COM_compositor.h6
-rw-r--r--source/blender/compositor/COM_defines.h62
-rw-r--r--source/blender/compositor/intern/COM_BufferOperation.cc65
-rw-r--r--source/blender/compositor/intern/COM_BufferOperation.h37
-rw-r--r--source/blender/compositor/intern/COM_CPUDevice.cc33
-rw-r--r--source/blender/compositor/intern/COM_CPUDevice.h4
-rw-r--r--source/blender/compositor/intern/COM_ChunkOrder.cc4
-rw-r--r--source/blender/compositor/intern/COM_ChunkOrder.h4
-rw-r--r--source/blender/compositor/intern/COM_ChunkOrderHotspot.cc4
-rw-r--r--source/blender/compositor/intern/COM_ChunkOrderHotspot.h4
-rw-r--r--source/blender/compositor/intern/COM_CompositorContext.cc31
-rw-r--r--source/blender/compositor/intern/COM_CompositorContext.h24
-rw-r--r--source/blender/compositor/intern/COM_Converter.cc36
-rw-r--r--source/blender/compositor/intern/COM_Converter.h7
-rw-r--r--source/blender/compositor/intern/COM_Debug.cc185
-rw-r--r--source/blender/compositor/intern/COM_Debug.h105
-rw-r--r--source/blender/compositor/intern/COM_Device.h12
-rw-r--r--source/blender/compositor/intern/COM_Enums.cc61
-rw-r--r--source/blender/compositor/intern/COM_Enums.h91
-rw-r--r--source/blender/compositor/intern/COM_ExecutionGroup.cc217
-rw-r--r--source/blender/compositor/intern/COM_ExecutionGroup.h159
-rw-r--r--source/blender/compositor/intern/COM_ExecutionModel.cc48
-rw-r--r--source/blender/compositor/intern/COM_ExecutionModel.h84
-rw-r--r--source/blender/compositor/intern/COM_ExecutionSystem.cc155
-rw-r--r--source/blender/compositor/intern/COM_ExecutionSystem.h49
-rw-r--r--source/blender/compositor/intern/COM_FullFrameExecutionModel.cc362
-rw-r--r--source/blender/compositor/intern/COM_FullFrameExecutionModel.h88
-rw-r--r--source/blender/compositor/intern/COM_MemoryBuffer.cc104
-rw-r--r--source/blender/compositor/intern/COM_MemoryBuffer.h159
-rw-r--r--source/blender/compositor/intern/COM_MemoryProxy.cc8
-rw-r--r--source/blender/compositor/intern/COM_MemoryProxy.h19
-rw-r--r--source/blender/compositor/intern/COM_MetaData.cc6
-rw-r--r--source/blender/compositor/intern/COM_MetaData.h6
-rw-r--r--source/blender/compositor/intern/COM_MultiThreadedOperation.cc26
-rw-r--r--source/blender/compositor/intern/COM_MultiThreadedOperation.h73
-rw-r--r--source/blender/compositor/intern/COM_Node.cc24
-rw-r--r--source/blender/compositor/intern/COM_Node.h73
-rw-r--r--source/blender/compositor/intern/COM_NodeConverter.cc4
-rw-r--r--source/blender/compositor/intern/COM_NodeConverter.h4
-rw-r--r--source/blender/compositor/intern/COM_NodeGraph.cc46
-rw-r--r--source/blender/compositor/intern/COM_NodeGraph.h21
-rw-r--r--source/blender/compositor/intern/COM_NodeOperation.cc343
-rw-r--r--source/blender/compositor/intern/COM_NodeOperation.h622
-rw-r--r--source/blender/compositor/intern/COM_NodeOperationBuilder.cc119
-rw-r--r--source/blender/compositor/intern/COM_NodeOperationBuilder.h30
-rw-r--r--source/blender/compositor/intern/COM_OpenCLDevice.cc26
-rw-r--r--source/blender/compositor/intern/COM_OpenCLDevice.h7
-rw-r--r--source/blender/compositor/intern/COM_SharedOperationBuffers.cc129
-rw-r--r--source/blender/compositor/intern/COM_SharedOperationBuffers.h71
-rw-r--r--source/blender/compositor/intern/COM_SingleThreadedOperation.cc7
-rw-r--r--source/blender/compositor/intern/COM_SingleThreadedOperation.h9
-rw-r--r--source/blender/compositor/intern/COM_SocketReader.h153
-rw-r--r--source/blender/compositor/intern/COM_TiledExecutionModel.cc158
-rw-r--r--source/blender/compositor/intern/COM_TiledExecutionModel.h54
-rw-r--r--source/blender/compositor/intern/COM_WorkPackage.cc18
-rw-r--r--source/blender/compositor/intern/COM_WorkPackage.h38
-rw-r--r--source/blender/compositor/intern/COM_WorkScheduler.cc42
-rw-r--r--source/blender/compositor/intern/COM_WorkScheduler.h12
-rw-r--r--source/blender/compositor/intern/COM_compositor.cc16
-rw-r--r--source/blender/compositor/nodes/COM_AlphaOverNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_AlphaOverNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_AntiAliasingNode.cc60
-rw-r--r--source/blender/compositor/nodes/COM_AntiAliasingNode.h40
-rw-r--r--source/blender/compositor/nodes/COM_BilateralBlurNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_BilateralBlurNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_BlurNode.cc6
-rw-r--r--source/blender/compositor/nodes/COM_BlurNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_BokehBlurNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_BokehBlurNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_BokehImageNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_BokehImageNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_BoxMaskNode.cc6
-rw-r--r--source/blender/compositor/nodes/COM_BoxMaskNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_BrightnessNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_BrightnessNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_ChannelMatteNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_ChannelMatteNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_ChromaMatteNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_ChromaMatteNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_ColorBalanceNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_ColorBalanceNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_ColorCorrectionNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_ColorCorrectionNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_ColorCurveNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_ColorCurveNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_ColorExposureNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_ColorExposureNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_ColorMatteNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_ColorMatteNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_ColorNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_ColorNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_ColorRampNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_ColorRampNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_ColorSpillNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_ColorSpillNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_ColorToBWNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_ColorToBWNode.h5
-rw-r--r--source/blender/compositor/nodes/COM_CombineColorNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_CombineColorNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_CompositorNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_CompositorNode.h5
-rw-r--r--source/blender/compositor/nodes/COM_ConvertAlphaNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_ConvertAlphaNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_CornerPinNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_CornerPinNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_CropNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_CropNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_CryptomatteNode.cc41
-rw-r--r--source/blender/compositor/nodes/COM_CryptomatteNode.h22
-rw-r--r--source/blender/compositor/nodes/COM_DefocusNode.cc6
-rw-r--r--source/blender/compositor/nodes/COM_DefocusNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_DenoiseNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_DenoiseNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_DespeckleNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_DespeckleNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_DifferenceMatteNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_DifferenceMatteNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_DilateErodeNode.cc6
-rw-r--r--source/blender/compositor/nodes/COM_DilateErodeNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_DirectionalBlurNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_DirectionalBlurNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_DisplaceNode.cc6
-rw-r--r--source/blender/compositor/nodes/COM_DisplaceNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_DistanceMatteNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_DistanceMatteNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_DoubleEdgeMaskNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_DoubleEdgeMaskNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_EllipseMaskNode.cc6
-rw-r--r--source/blender/compositor/nodes/COM_EllipseMaskNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_FilterNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_FilterNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_FlipNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_FlipNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_GammaNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_GammaNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_GlareNode.cc8
-rw-r--r--source/blender/compositor/nodes/COM_GlareNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_HueSaturationValueNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_HueSaturationValueNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_IDMaskNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_IDMaskNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_ImageNode.cc22
-rw-r--r--source/blender/compositor/nodes/COM_ImageNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_InpaintNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_InpaintNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_InvertNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_InvertNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_KeyingNode.cc6
-rw-r--r--source/blender/compositor/nodes/COM_KeyingNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_KeyingScreenNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_KeyingScreenNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_LensDistortionNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_LensDistortionNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_LuminanceMatteNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_LuminanceMatteNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_MapRangeNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_MapRangeNode.h5
-rw-r--r--source/blender/compositor/nodes/COM_MapUVNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_MapUVNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_MapValueNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_MapValueNode.h5
-rw-r--r--source/blender/compositor/nodes/COM_MaskNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_MaskNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_MathNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_MathNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_MixNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_MixNode.h5
-rw-r--r--source/blender/compositor/nodes/COM_MovieClipNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_MovieClipNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_MovieDistortionNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_MovieDistortionNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_NormalNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_NormalNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_NormalizeNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_NormalizeNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_OutputFileNode.cc56
-rw-r--r--source/blender/compositor/nodes/COM_OutputFileNode.h12
-rw-r--r--source/blender/compositor/nodes/COM_PixelateNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_PixelateNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_PlaneTrackDeformNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_PlaneTrackDeformNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_RenderLayersNode.cc13
-rw-r--r--source/blender/compositor/nodes/COM_RenderLayersNode.h3
-rw-r--r--source/blender/compositor/nodes/COM_RotateNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_RotateNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_ScaleNode.cc6
-rw-r--r--source/blender/compositor/nodes/COM_ScaleNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_SeparateColorNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_SeparateColorNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_SetAlphaNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_SetAlphaNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_SocketProxyNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_SocketProxyNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_SplitViewerNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_SplitViewerNode.h5
-rw-r--r--source/blender/compositor/nodes/COM_Stabilize2dNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_Stabilize2dNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_SunBeamsNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_SunBeamsNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_SwitchNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_SwitchNode.h5
-rw-r--r--source/blender/compositor/nodes/COM_SwitchViewNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_SwitchViewNode.h5
-rw-r--r--source/blender/compositor/nodes/COM_TextureNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_TextureNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_TimeNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_TimeNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_TonemapNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_TonemapNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_TrackPositionNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_TrackPositionNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_TransformNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_TransformNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_TranslateNode.cc9
-rw-r--r--source/blender/compositor/nodes/COM_TranslateNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_ValueNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_ValueNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_VectorBlurNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_VectorBlurNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_VectorCurveNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_VectorCurveNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_ViewLevelsNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_ViewLevelsNode.h4
-rw-r--r--source/blender/compositor/nodes/COM_ViewerNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_ViewerNode.h5
-rw-r--r--source/blender/compositor/nodes/COM_ZCombineNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_ZCombineNode.h4
-rw-r--r--source/blender/compositor/operations/COM_AlphaOverKeyOperation.cc7
-rw-r--r--source/blender/compositor/operations/COM_AlphaOverKeyOperation.h9
-rw-r--r--source/blender/compositor/operations/COM_AlphaOverMixedOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_AlphaOverMixedOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc7
-rw-r--r--source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.h9
-rw-r--r--source/blender/compositor/operations/COM_AntiAliasOperation.cc6
-rw-r--r--source/blender/compositor/operations/COM_AntiAliasOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_BilateralBlurOperation.cc6
-rw-r--r--source/blender/compositor/operations/COM_BilateralBlurOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_BlurBaseOperation.cc8
-rw-r--r--source/blender/compositor/operations/COM_BlurBaseOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_BokehBlurOperation.cc27
-rw-r--r--source/blender/compositor/operations/COM_BokehBlurOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_BokehImageOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_BokehImageOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_BoxMaskOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_BoxMaskOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_BrightnessOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_BrightnessOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_CalculateMeanOperation.cc8
-rw-r--r--source/blender/compositor/operations/COM_CalculateMeanOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.cc7
-rw-r--r--source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.h7
-rw-r--r--source/blender/compositor/operations/COM_ChangeHSVOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_ChangeHSVOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_ChannelMatteOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_ChannelMatteOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_ChromaMatteOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_ChromaMatteOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_ColorBalanceLGGOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_ColorCorrectionOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_ColorCorrectionOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_ColorCurveOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_ColorCurveOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_ColorExposureOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_ColorExposureOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_ColorMatteOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_ColorMatteOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_ColorRampOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_ColorRampOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_ColorSpillOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_ColorSpillOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_CompositorOperation.cc18
-rw-r--r--source/blender/compositor/operations/COM_CompositorOperation.h8
-rw-r--r--source/blender/compositor/operations/COM_ConvertColorProfileOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_ConvertColorProfileOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.h5
-rw-r--r--source/blender/compositor/operations/COM_ConvertOperation.cc39
-rw-r--r--source/blender/compositor/operations/COM_ConvertOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.cc7
-rw-r--r--source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.h5
-rw-r--r--source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc6
-rw-r--r--source/blender/compositor/operations/COM_ConvolutionFilterOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_CropOperation.cc6
-rw-r--r--source/blender/compositor/operations/COM_CropOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_CryptomatteOperation.cc16
-rw-r--r--source/blender/compositor/operations/COM_CryptomatteOperation.h8
-rw-r--r--source/blender/compositor/operations/COM_CurveBaseOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_CurveBaseOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_DenoiseOperation.cc13
-rw-r--r--source/blender/compositor/operations/COM_DenoiseOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_DespeckleOperation.cc6
-rw-r--r--source/blender/compositor/operations/COM_DespeckleOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_DifferenceMatteOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_DifferenceMatteOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_DilateErodeOperation.cc12
-rw-r--r--source/blender/compositor/operations/COM_DilateErodeOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_DirectionalBlurOperation.cc13
-rw-r--r--source/blender/compositor/operations/COM_DirectionalBlurOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_DisplaceOperation.cc14
-rw-r--r--source/blender/compositor/operations/COM_DisplaceOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_DisplaceSimpleOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_DistanceRGBMatteOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_DistanceRGBMatteOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_DistanceYCCMatteOperation.cc7
-rw-r--r--source/blender/compositor/operations/COM_DistanceYCCMatteOperation.h10
-rw-r--r--source/blender/compositor/operations/COM_DotproductOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_DotproductOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc6
-rw-r--r--source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_EllipseMaskOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_EllipseMaskOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc16
-rw-r--r--source/blender/compositor/operations/COM_FastGaussianBlurOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_FlipOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_FlipOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_GammaCorrectOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_GammaCorrectOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_GammaOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_GammaOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc10
-rw-r--r--source/blender/compositor/operations/COM_GaussianBokehBlurOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_GaussianXBlurOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_GaussianXBlurOperation.h6
-rw-r--r--source/blender/compositor/operations/COM_GaussianYBlurOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_GaussianYBlurOperation.h6
-rw-r--r--source/blender/compositor/operations/COM_GlareBaseOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_GlareBaseOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_GlareFogGlowOperation.cc21
-rw-r--r--source/blender/compositor/operations/COM_GlareFogGlowOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_GlareGhostOperation.cc10
-rw-r--r--source/blender/compositor/operations/COM_GlareGhostOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_GlareSimpleStarOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_GlareSimpleStarOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_GlareStreaksOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_GlareStreaksOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_GlareThresholdOperation.cc6
-rw-r--r--source/blender/compositor/operations/COM_GlareThresholdOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_IDMaskOperation.cc6
-rw-r--r--source/blender/compositor/operations/COM_IDMaskOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_ImageOperation.cc16
-rw-r--r--source/blender/compositor/operations/COM_ImageOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_InpaintOperation.cc9
-rw-r--r--source/blender/compositor/operations/COM_InpaintOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_InvertOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_InvertOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_KeyingBlurOperation.cc6
-rw-r--r--source/blender/compositor/operations/COM_KeyingBlurOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_KeyingClipOperation.cc6
-rw-r--r--source/blender/compositor/operations/COM_KeyingClipOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_KeyingDespillOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_KeyingDespillOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_KeyingOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_KeyingOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_KeyingScreenOperation.cc6
-rw-r--r--source/blender/compositor/operations/COM_KeyingScreenOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_LuminanceMatteOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_LuminanceMatteOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_MapRangeOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_MapRangeOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_MapUVOperation.cc10
-rw-r--r--source/blender/compositor/operations/COM_MapUVOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_MapValueOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_MapValueOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_MaskOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_MaskOperation.h7
-rw-r--r--source/blender/compositor/operations/COM_MathBaseOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_MathBaseOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_MixOperation.cc126
-rw-r--r--source/blender/compositor/operations/COM_MixOperation.h23
-rw-r--r--source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_MovieClipAttributeOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_MovieClipOperation.cc10
-rw-r--r--source/blender/compositor/operations/COM_MovieClipOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_MovieDistortionOperation.cc8
-rw-r--r--source/blender/compositor/operations/COM_MovieDistortionOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_MultilayerImageOperation.cc12
-rw-r--r--source/blender/compositor/operations/COM_MultilayerImageOperation.h6
-rw-r--r--source/blender/compositor/operations/COM_NormalizeOperation.cc6
-rw-r--r--source/blender/compositor/operations/COM_NormalizeOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_OutputFileOperation.cc18
-rw-r--r--source/blender/compositor/operations/COM_OutputFileOperation.h26
-rw-r--r--source/blender/compositor/operations/COM_PixelateOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_PixelateOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc8
-rw-r--r--source/blender/compositor/operations/COM_PlaneCornerPinOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc76
-rw-r--r--source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h62
-rw-r--r--source/blender/compositor/operations/COM_PlaneTrackOperation.cc57
-rw-r--r--source/blender/compositor/operations/COM_PlaneTrackOperation.h9
-rw-r--r--source/blender/compositor/operations/COM_PreviewOperation.cc14
-rw-r--r--source/blender/compositor/operations/COM_PreviewOperation.h10
-rw-r--r--source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc8
-rw-r--r--source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_QualityStepHelper.cc18
-rw-r--r--source/blender/compositor/operations/COM_QualityStepHelper.h10
-rw-r--r--source/blender/compositor/operations/COM_ReadBufferOperation.cc13
-rw-r--r--source/blender/compositor/operations/COM_ReadBufferOperation.h12
-rw-r--r--source/blender/compositor/operations/COM_RenderLayersProg.cc12
-rw-r--r--source/blender/compositor/operations/COM_RenderLayersProg.h6
-rw-r--r--source/blender/compositor/operations/COM_RotateOperation.cc6
-rw-r--r--source/blender/compositor/operations/COM_RotateOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_SMAAOperation.cc868
-rw-r--r--source/blender/compositor/operations/COM_SMAAOperation.h149
-rw-r--r--source/blender/compositor/operations/COM_ScaleOperation.cc16
-rw-r--r--source/blender/compositor/operations/COM_ScaleOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc10
-rw-r--r--source/blender/compositor/operations/COM_ScreenLensDistortionOperation.h6
-rw-r--r--source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_SetAlphaReplaceOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_SetAlphaReplaceOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_SetColorOperation.cc5
-rw-r--r--source/blender/compositor/operations/COM_SetColorOperation.h8
-rw-r--r--source/blender/compositor/operations/COM_SetSamplerOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_SetSamplerOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_SetValueOperation.cc5
-rw-r--r--source/blender/compositor/operations/COM_SetValueOperation.h9
-rw-r--r--source/blender/compositor/operations/COM_SetVectorOperation.cc5
-rw-r--r--source/blender/compositor/operations/COM_SetVectorOperation.h8
-rw-r--r--source/blender/compositor/operations/COM_SocketProxyOperation.cc9
-rw-r--r--source/blender/compositor/operations/COM_SocketProxyOperation.h26
-rw-r--r--source/blender/compositor/operations/COM_SplitOperation.cc8
-rw-r--r--source/blender/compositor/operations/COM_SplitOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_SunBeamsOperation.cc14
-rw-r--r--source/blender/compositor/operations/COM_SunBeamsOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_TextureOperation.cc40
-rw-r--r--source/blender/compositor/operations/COM_TextureOperation.h6
-rw-r--r--source/blender/compositor/operations/COM_TonemapOperation.cc8
-rw-r--r--source/blender/compositor/operations/COM_TonemapOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_TrackPositionOperation.cc5
-rw-r--r--source/blender/compositor/operations/COM_TrackPositionOperation.h9
-rw-r--r--source/blender/compositor/operations/COM_TranslateOperation.cc6
-rw-r--r--source/blender/compositor/operations/COM_TranslateOperation.h8
-rw-r--r--source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc22
-rw-r--r--source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_VectorBlurOperation.cc8
-rw-r--r--source/blender/compositor/operations/COM_VectorBlurOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_VectorCurveOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_VectorCurveOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_ViewerOperation.cc18
-rw-r--r--source/blender/compositor/operations/COM_ViewerOperation.h10
-rw-r--r--source/blender/compositor/operations/COM_WrapOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_WrapOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_WriteBufferOperation.cc9
-rw-r--r--source/blender/compositor/operations/COM_WriteBufferOperation.h13
-rw-r--r--source/blender/compositor/operations/COM_ZCombineOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_ZCombineOperation.h4
-rw-r--r--source/blender/datatoc/datatoc_icon.c49
-rw-r--r--source/blender/depsgraph/DEG_depsgraph.h21
-rw-r--r--source/blender/depsgraph/DEG_depsgraph_build.h9
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder.cc4
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder.h2
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_cache.cc4
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_cache.h1
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_map.cc8
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_map.h3
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes.cc113
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes.h5
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc4
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_pchanmap.cc8
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_pchanmap.h4
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations.cc39
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_rna.cc4
-rw-r--r--source/blender/depsgraph/intern/builder/pipeline.cc6
-rw-r--r--source/blender/depsgraph/intern/builder/pipeline.h2
-rw-r--r--source/blender/depsgraph/intern/depsgraph.cc7
-rw-r--r--source/blender/depsgraph/intern/depsgraph.h3
-rw-r--r--source/blender/depsgraph/intern/depsgraph_build.cc31
-rw-r--r--source/blender/depsgraph/intern/depsgraph_query.cc2
-rw-r--r--source/blender/depsgraph/intern/depsgraph_query_iter.cc23
-rw-r--r--source/blender/depsgraph/intern/depsgraph_tag.cc33
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval.cc3
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc35
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc5
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.h5
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.cc8
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.h3
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc4
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequence.cc6
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequence.h3
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_id.cc1
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_id.h3
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_operation.cc4
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_operation.h1
-rw-r--r--source/blender/draw/CMakeLists.txt18
-rw-r--r--source/blender/draw/DRW_engine.h3
-rw-r--r--source/blender/draw/engines/eevee/eevee_cryptomatte.c10
-rw-r--r--source/blender/draw/engines/eevee/eevee_effects.c3
-rw-r--r--source/blender/draw/engines/eevee/eevee_engine.c20
-rw-r--r--source/blender/draw/engines/eevee/eevee_lights.c6
-rw-r--r--source/blender/draw/engines/eevee/eevee_materials.c84
-rw-r--r--source/blender/draw/engines/eevee/eevee_mist.c17
-rw-r--r--source/blender/draw/engines/eevee/eevee_occlusion.c17
-rw-r--r--source/blender/draw/engines/eevee/eevee_renderpasses.c2
-rw-r--r--source/blender/draw/engines/eevee/eevee_screen_raytrace.c18
-rw-r--r--source/blender/draw/engines/eevee/eevee_shadows.c18
-rw-r--r--source/blender/draw/engines/eevee/eevee_volumes.c22
-rw-r--r--source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl11
-rw-r--r--source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl6
-rw-r--r--source/blender/draw/engines/eevee/shaders/closure_eval_lib.glsl93
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_dof_lib.glsl2
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_dof_resolve_frag.glsl2
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl60
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_cache_utils.c11
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_draw_data.c24
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_engine.c5
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_engine.h4
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_shader_fx.c10
-rw-r--r--source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl4
-rw-r--r--source/blender/draw/engines/gpencil/shaders/gpencil_vfx_frag.glsl4
-rw-r--r--source/blender/draw/engines/image/image_engine.c2
-rw-r--r--source/blender/draw/engines/overlay/overlay_armature.c31
-rw-r--r--source/blender/draw/engines/overlay/overlay_edit_mesh.c4
-rw-r--r--source/blender/draw/engines/overlay/overlay_engine.c12
-rw-r--r--source/blender/draw/engines/overlay/overlay_extra.c7
-rw-r--r--source/blender/draw/engines/overlay/overlay_gpencil.c2
-rw-r--r--source/blender/draw/engines/overlay/overlay_mode_transfer.c159
-rw-r--r--source/blender/draw/engines/overlay/overlay_motion_path.c4
-rw-r--r--source/blender/draw/engines/overlay/overlay_paint.c2
-rw-r--r--source/blender/draw/engines/overlay/overlay_particle.c2
-rw-r--r--source/blender/draw/engines/overlay/overlay_private.h12
-rw-r--r--source/blender/draw/engines/overlay/overlay_wireframe.c30
-rw-r--r--source/blender/draw/engines/workbench/workbench_materials.c2
-rw-r--r--source/blender/draw/engines/workbench/workbench_volume.c4
-rw-r--r--source/blender/draw/intern/DRW_render.h7
-rw-r--r--source/blender/draw/intern/draw_cache.h3
-rw-r--r--source/blender/draw/intern/draw_cache_extract.h50
-rw-r--r--source/blender/draw/intern/draw_cache_extract_mesh.c6165
-rw-r--r--source/blender/draw/intern/draw_cache_extract_mesh.cc813
-rw-r--r--source/blender/draw/intern/draw_cache_extract_mesh_extractors.c3687
-rw-r--r--source/blender/draw/intern/draw_cache_extract_mesh_private.h295
-rw-r--r--source/blender/draw/intern/draw_cache_extract_mesh_render_data.c371
-rw-r--r--source/blender/draw/intern/draw_cache_impl.h7
-rw-r--r--source/blender/draw/intern/draw_cache_impl_curve.cc (renamed from source/blender/draw/intern/draw_cache_impl_curve.c)242
-rw-r--r--source/blender/draw/intern/draw_cache_impl_displist.c10
-rw-r--r--source/blender/draw/intern/draw_cache_impl_gpencil.c11
-rw-r--r--source/blender/draw/intern/draw_cache_impl_hair.c3
-rw-r--r--source/blender/draw/intern/draw_cache_impl_mesh.c78
-rw-r--r--source/blender/draw/intern/draw_cache_impl_volume.c12
-rw-r--r--source/blender/draw/intern/draw_cache_inline.h8
-rw-r--r--source/blender/draw/intern/draw_hair.c200
-rw-r--r--source/blender/draw/intern/draw_manager.c17
-rw-r--r--source/blender/draw/intern/draw_manager.h13
-rw-r--r--source/blender/draw/intern/draw_manager_data.c36
-rw-r--r--source/blender/draw/intern/draw_manager_exec.c10
-rw-r--r--source/blender/draw/intern/draw_manager_profiling.c6
-rw-r--r--source/blender/draw/intern/draw_manager_shader.c7
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc392
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc119
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc256
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc198
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc127
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc182
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc269
-rw-r--r--source/blender/draw/intern/shaders/common_hair_lib.glsl78
-rw-r--r--source/blender/draw/intern/shaders/common_hair_refine_comp.glsl24
-rw-r--r--source/blender/draw/intern/shaders/common_hair_refine_vert.glsl45
-rw-r--r--source/blender/draw/intern/shaders/common_math_lib.glsl7
-rw-r--r--source/blender/draw/intern/shaders/common_pointcloud_lib.glsl2
-rw-r--r--source/blender/draw/tests/draw_testing.cc18
-rw-r--r--source/blender/draw/tests/draw_testing.hh13
-rw-r--r--source/blender/draw/tests/shaders_test.cc12
-rw-r--r--source/blender/editors/animation/anim_channels_defines.c5
-rw-r--r--source/blender/editors/animation/anim_channels_edit.c10
-rw-r--r--source/blender/editors/animation/keyframing.c13
-rw-r--r--source/blender/editors/armature/CMakeLists.txt1
-rw-r--r--source/blender/editors/armature/armature_add.c14
-rw-r--r--source/blender/editors/armature/armature_naming.c4
-rw-r--r--source/blender/editors/armature/armature_select.c206
-rw-r--r--source/blender/editors/armature/pose_edit.c4
-rw-r--r--source/blender/editors/armature/pose_lib.c11
-rw-r--r--source/blender/editors/armature/pose_select.c188
-rw-r--r--source/blender/editors/armature/pose_slide.c1224
-rw-r--r--source/blender/editors/armature/pose_transform.c13
-rw-r--r--source/blender/editors/armature/pose_utils.c5
-rw-r--r--source/blender/editors/curve/curve_intern.h5
-rw-r--r--source/blender/editors/curve/editcurve.c25
-rw-r--r--source/blender/editors/curve/editcurve_add.c12
-rw-r--r--source/blender/editors/curve/editcurve_paint.c18
-rw-r--r--source/blender/editors/curve/editcurve_select.c2
-rw-r--r--source/blender/editors/curve/editfont.c3
-rw-r--r--source/blender/editors/geometry/geometry_attributes.c10
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c40
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c2
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c484
-rw-r--r--source/blender/editors/gpencil/CMakeLists.txt2
-rw-r--r--source/blender/editors/gpencil/annotate_paint.c26
-rw-r--r--source/blender/editors/gpencil/gpencil_add_blank.c101
-rw-r--r--source/blender/editors/gpencil/gpencil_add_lineart.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_add_monkey.c4
-rw-r--r--source/blender/editors/gpencil/gpencil_add_stroke.c4
-rw-r--r--source/blender/editors/gpencil/gpencil_bake_animation.c448
-rw-r--r--source/blender/editors/gpencil/gpencil_convert.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_data.c360
-rw-r--r--source/blender/editors/gpencil/gpencil_edit.c441
-rw-r--r--source/blender/editors/gpencil/gpencil_fill.c22
-rw-r--r--source/blender/editors/gpencil/gpencil_intern.h7
-rw-r--r--source/blender/editors/gpencil/gpencil_interpolate.c77
-rw-r--r--source/blender/editors/gpencil/gpencil_mesh.c26
-rw-r--r--source/blender/editors/gpencil/gpencil_ops.c4
-rw-r--r--source/blender/editors/gpencil/gpencil_paint.c56
-rw-r--r--source/blender/editors/gpencil/gpencil_primitive.c44
-rw-r--r--source/blender/editors/gpencil/gpencil_select.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_trace_ops.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_trace_utils.c10
-rw-r--r--source/blender/editors/gpencil/gpencil_utils.c40
-rw-r--r--source/blender/editors/include/ED_armature.h14
-rw-r--r--source/blender/editors/include/ED_fileselect.h2
-rw-r--r--source/blender/editors/include/ED_gizmo_library.h21
-rw-r--r--source/blender/editors/include/ED_gpencil.h3
-rw-r--r--source/blender/editors/include/ED_keyframing.h1
-rw-r--r--source/blender/editors/include/ED_mesh.h13
-rw-r--r--source/blender/editors/include/ED_numinput.h5
-rw-r--r--source/blender/editors/include/ED_object.h18
-rw-r--r--source/blender/editors/include/ED_render.h3
-rw-r--r--source/blender/editors/include/ED_screen.h9
-rw-r--r--source/blender/editors/include/ED_sequencer.h1
-rw-r--r--source/blender/editors/include/ED_spreadsheet.h43
-rw-r--r--source/blender/editors/include/ED_text.h2
-rw-r--r--source/blender/editors/include/ED_transform.h6
-rw-r--r--source/blender/editors/include/ED_transform_snap_object_context.h17
-rw-r--r--source/blender/editors/include/ED_uvedit.h1
-rw-r--r--source/blender/editors/include/ED_view3d.h43
-rw-r--r--source/blender/editors/include/UI_interface.h31
-rw-r--r--source/blender/editors/include/UI_resources.h2
-rw-r--r--source/blender/editors/interface/interface.c57
-rw-r--r--source/blender/editors/interface/interface_context_menu.c10
-rw-r--r--source/blender/editors/interface/interface_eyedropper.c11
-rw-r--r--source/blender/editors/interface/interface_eyedropper_color.c105
-rw-r--r--source/blender/editors/interface/interface_handlers.c355
-rw-r--r--source/blender/editors/interface/interface_icons.c14
-rw-r--r--source/blender/editors/interface/interface_intern.h100
-rw-r--r--source/blender/editors/interface/interface_layout.c8
-rw-r--r--source/blender/editors/interface/interface_ops.c224
-rw-r--r--source/blender/editors/interface/interface_panel.c22
-rw-r--r--source/blender/editors/interface/interface_region_menu_popup.c13
-rw-r--r--source/blender/editors/interface/interface_region_search.c3
-rw-r--r--source/blender/editors/interface/interface_region_tooltip.c25
-rw-r--r--source/blender/editors/interface/interface_template_search_menu.c12
-rw-r--r--source/blender/editors/interface/interface_template_search_operator.c1
-rw-r--r--source/blender/editors/interface/interface_templates.c14
-rw-r--r--source/blender/editors/interface/interface_widgets.c101
-rw-r--r--source/blender/editors/interface/view2d_ops.c2
-rw-r--r--source/blender/editors/io/CMakeLists.txt2
-rw-r--r--source/blender/editors/io/io_alembic.c27
-rw-r--r--source/blender/editors/io/io_collada.c10
-rw-r--r--source/blender/editors/io/io_gpencil.h1
-rw-r--r--source/blender/editors/io/io_gpencil_export.c14
-rw-r--r--source/blender/editors/io/io_gpencil_import.c5
-rw-r--r--source/blender/editors/mask/mask_add.c40
-rw-r--r--source/blender/editors/mask/mask_shapekey.c4
-rw-r--r--source/blender/editors/mesh/editmesh_bevel.c48
-rw-r--r--source/blender/editors/mesh/editmesh_bisect.c20
-rw-r--r--source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c1
-rw-r--r--source/blender/editors/mesh/editmesh_intersect.c28
-rw-r--r--source/blender/editors/mesh/editmesh_knife.c2116
-rw-r--r--source/blender/editors/mesh/editmesh_path.c5
-rw-r--r--source/blender/editors/mesh/editmesh_select.c173
-rw-r--r--source/blender/editors/mesh/editmesh_select_similar.c4
-rw-r--r--source/blender/editors/mesh/editmesh_tools.c31
-rw-r--r--source/blender/editors/mesh/editmesh_undo.c104
-rw-r--r--source/blender/editors/mesh/editmesh_utils.c6
-rw-r--r--source/blender/editors/mesh/meshtools.c2
-rw-r--r--source/blender/editors/object/CMakeLists.txt2
-rw-r--r--source/blender/editors/object/object_add.c409
-rw-r--r--source/blender/editors/object/object_bake_api.c129
-rw-r--r--source/blender/editors/object/object_data_transfer.c3
-rw-r--r--source/blender/editors/object/object_data_transform.c9
-rw-r--r--source/blender/editors/object/object_edit.c142
-rw-r--r--source/blender/editors/object/object_intern.h6
-rw-r--r--source/blender/editors/object/object_modes.c220
-rw-r--r--source/blender/editors/object/object_modifier.c158
-rw-r--r--source/blender/editors/object/object_ops.c2
-rw-r--r--source/blender/editors/object/object_relations.c15
-rw-r--r--source/blender/editors/object/object_remesh.c4
-rw-r--r--source/blender/editors/object/object_transform.c25
-rw-r--r--source/blender/editors/object/object_vgroup.c6
-rw-r--r--source/blender/editors/physics/particle_edit.c27
-rw-r--r--source/blender/editors/physics/rigidbody_object.c23
-rw-r--r--source/blender/editors/render/render_internal.c7
-rw-r--r--source/blender/editors/render/render_opengl.c2
-rw-r--r--source/blender/editors/render/render_preview.c45
-rw-r--r--source/blender/editors/render/render_update.c2
-rw-r--r--source/blender/editors/render/render_view.c1
-rw-r--r--source/blender/editors/screen/area.c25
-rw-r--r--source/blender/editors/screen/screen_draw.c313
-rw-r--r--source/blender/editors/screen/screen_edit.c246
-rw-r--r--source/blender/editors/screen/screen_geometry.c28
-rw-r--r--source/blender/editors/screen/screen_intern.h47
-rw-r--r--source/blender/editors/screen/screen_ops.c388
-rw-r--r--source/blender/editors/screen/screendump.c3
-rw-r--r--source/blender/editors/sculpt_paint/paint_cursor.c41
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_proj.c4
-rw-r--r--source/blender/editors/sculpt_paint/paint_intern.h4
-rw-r--r--source/blender/editors/sculpt_paint/paint_mask.c21
-rw-r--r--source/blender/editors/sculpt_paint/paint_ops.c9
-rw-r--r--source/blender/editors/sculpt_paint/paint_stroke.c64
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex.c19
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c7
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex_weight_utils.c2
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c10
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_expand.c49
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_face_set.c4
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_intern.h2
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_pose.c5
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_undo.c4
-rw-r--r--source/blender/editors/sound/sound_ops.c3
-rw-r--r--source/blender/editors/space_action/action_edit.c14
-rw-r--r--source/blender/editors/space_action/space_action.c5
-rw-r--r--source/blender/editors/space_buttons/CMakeLists.txt2
-rw-r--r--source/blender/editors/space_buttons/buttons_context.c14
-rw-r--r--source/blender/editors/space_buttons/buttons_intern.h2
-rw-r--r--source/blender/editors/space_buttons/buttons_texture.c50
-rw-r--r--source/blender/editors/space_clip/clip_buttons.c6
-rw-r--r--source/blender/editors/space_clip/clip_editor.c2
-rw-r--r--source/blender/editors/space_clip/clip_ops.c10
-rw-r--r--source/blender/editors/space_clip/tracking_ops_track.c26
-rw-r--r--source/blender/editors/space_file/file_draw.c39
-rw-r--r--source/blender/editors/space_file/file_intern.h1
-rw-r--r--source/blender/editors/space_file/file_ops.c129
-rw-r--r--source/blender/editors/space_file/filelist.c94
-rw-r--r--source/blender/editors/space_file/filesel.c1
-rw-r--r--source/blender/editors/space_file/space_file.c10
-rw-r--r--source/blender/editors/space_graph/graph_edit.c14
-rw-r--r--source/blender/editors/space_graph/graph_select.c190
-rw-r--r--source/blender/editors/space_image/image_buttons.c5
-rw-r--r--source/blender/editors/space_image/image_draw.c2
-rw-r--r--source/blender/editors/space_image/image_intern.h1
-rw-r--r--source/blender/editors/space_image/image_ops.c221
-rw-r--r--source/blender/editors/space_image/space_image.c1
-rw-r--r--source/blender/editors/space_info/info_draw.c4
-rw-r--r--source/blender/editors/space_info/info_ops.c90
-rw-r--r--source/blender/editors/space_info/info_stats.c127
-rw-r--r--source/blender/editors/space_info/space_info.c9
-rw-r--r--source/blender/editors/space_nla/nla_draw.c8
-rw-r--r--source/blender/editors/space_node/CMakeLists.txt18
-rw-r--r--source/blender/editors/space_node/drawnode.cc (renamed from source/blender/editors/space_node/drawnode.c)976
-rw-r--r--source/blender/editors/space_node/node_add.cc (renamed from source/blender/editors/space_node/node_add.c)95
-rw-r--r--source/blender/editors/space_node/node_buttons.c5
-rw-r--r--source/blender/editors/space_node/node_draw.cc64
-rw-r--r--source/blender/editors/space_node/node_edit.cc (renamed from source/blender/editors/space_node/node_edit.c)278
-rw-r--r--source/blender/editors/space_node/node_geometry_attribute_search.cc130
-rw-r--r--source/blender/editors/space_node/node_gizmo.c1
-rw-r--r--source/blender/editors/space_node/node_group.cc (renamed from source/blender/editors/space_node/node_group.c)91
-rw-r--r--source/blender/editors/space_node/node_intern.h10
-rw-r--r--source/blender/editors/space_node/node_relationships.cc (renamed from source/blender/editors/space_node/node_relationships.c)435
-rw-r--r--source/blender/editors/space_node/node_select.cc (renamed from source/blender/editors/space_node/node_select.c)166
-rw-r--r--source/blender/editors/space_node/node_templates.cc (renamed from source/blender/editors/space_node/node_templates.c)124
-rw-r--r--source/blender/editors/space_node/node_toolbar.cc (renamed from source/blender/editors/space_node/node_toolbar.c)0
-rw-r--r--source/blender/editors/space_node/node_view.cc (renamed from source/blender/editors/space_node/node_view.c)68
-rw-r--r--source/blender/editors/space_node/space_node.c17
-rw-r--r--source/blender/editors/space_outliner/CMakeLists.txt1
-rw-r--r--source/blender/editors/space_outliner/outliner_dragdrop.c13
-rw-r--r--source/blender/editors/space_outliner/outliner_draw.c107
-rw-r--r--source/blender/editors/space_outliner/outliner_edit.c16
-rw-r--r--source/blender/editors/space_outliner/outliner_intern.h2
-rw-r--r--source/blender/editors/space_outliner/outliner_sync.c7
-rw-r--r--source/blender/editors/space_outliner/outliner_tools.c33
-rw-r--r--source/blender/editors/space_outliner/outliner_tree.c9
-rw-r--r--source/blender/editors/space_outliner/outliner_utils.c1
-rw-r--r--source/blender/editors/space_outliner/space_outliner.c4
-rw-r--r--source/blender/editors/space_outliner/tree/tree_display.cc3
-rw-r--r--source/blender/editors/space_outliner/tree/tree_display.hh18
-rw-r--r--source/blender/editors/space_outliner/tree/tree_display_override_library.cc204
-rw-r--r--source/blender/editors/space_outliner/tree/tree_element_overrides.cc41
-rw-r--r--source/blender/editors/space_outliner/tree/tree_element_overrides.hh1
-rw-r--r--source/blender/editors/space_sequencer/sequencer_add.c91
-rw-r--r--source/blender/editors/space_sequencer/sequencer_buttons.c2
-rw-r--r--source/blender/editors/space_sequencer/sequencer_draw.c95
-rw-r--r--source/blender/editors/space_sequencer/sequencer_edit.c194
-rw-r--r--source/blender/editors/space_sequencer/sequencer_modifier.c4
-rw-r--r--source/blender/editors/space_sequencer/sequencer_proxy.c14
-rw-r--r--source/blender/editors/space_sequencer/sequencer_select.c150
-rw-r--r--source/blender/editors/space_spreadsheet/CMakeLists.txt18
-rw-r--r--source/blender/editors/space_spreadsheet/space_spreadsheet.cc251
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh58
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_column.cc72
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_column.hh48
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_column_layout.cc230
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_column_layout.hh115
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh92
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_context.cc306
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_context.hh (renamed from source/blender/editors/space_spreadsheet/spreadsheet_from_geometry.hh)15
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_data_source.cc24
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh65
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc445
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh94
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_draw.cc18
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_draw.hh1
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_from_geometry.cc447
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_layout.cc256
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_layout.hh42
-rw-r--r--source/blender/editors/space_text/space_text.c13
-rw-r--r--source/blender/editors/space_text/text_draw.c4
-rw-r--r--source/blender/editors/space_text/text_intern.h1
-rw-r--r--source/blender/editors/space_text/text_ops.c10
-rw-r--r--source/blender/editors/space_userpref/space_userpref.c3
-rw-r--r--source/blender/editors/space_view3d/drawobject.c2
-rw-r--r--source/blender/editors/space_view3d/space_view3d.c16
-rw-r--r--source/blender/editors/space_view3d/view3d_buttons.c4
-rw-r--r--source/blender/editors/space_view3d/view3d_draw.c202
-rw-r--r--source/blender/editors/space_view3d/view3d_edit.c16
-rw-r--r--source/blender/editors/space_view3d/view3d_gizmo_navigate.c19
-rw-r--r--source/blender/editors/space_view3d/view3d_gizmo_navigate_type.c3
-rw-r--r--source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c4
-rw-r--r--source/blender/editors/space_view3d/view3d_gizmo_ruler.c15
-rw-r--r--source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c2
-rw-r--r--source/blender/editors/space_view3d/view3d_intern.h11
-rw-r--r--source/blender/editors/space_view3d/view3d_ops.c1
-rw-r--r--source/blender/editors/space_view3d/view3d_placement.c42
-rw-r--r--source/blender/editors/space_view3d/view3d_project.c15
-rw-r--r--source/blender/editors/space_view3d/view3d_select.c259
-rw-r--r--source/blender/editors/space_view3d/view3d_utils.c106
-rw-r--r--source/blender/editors/space_view3d/view3d_walk.c2
-rw-r--r--source/blender/editors/transform/transform.c179
-rw-r--r--source/blender/editors/transform/transform.h64
-rw-r--r--source/blender/editors/transform/transform_constraints.c16
-rw-r--r--source/blender/editors/transform/transform_convert.c244
-rw-r--r--source/blender/editors/transform/transform_convert.h8
-rw-r--r--source/blender/editors/transform/transform_convert_armature.c14
-rw-r--r--source/blender/editors/transform/transform_convert_cursor.c26
-rw-r--r--source/blender/editors/transform/transform_convert_curve.c14
-rw-r--r--source/blender/editors/transform/transform_convert_gpencil.c10
-rw-r--r--source/blender/editors/transform/transform_convert_lattice.c2
-rw-r--r--source/blender/editors/transform/transform_convert_mball.c20
-rw-r--r--source/blender/editors/transform/transform_convert_mesh.c1330
-rw-r--r--source/blender/editors/transform/transform_convert_mesh_edge.c7
-rw-r--r--source/blender/editors/transform/transform_convert_mesh_skin.c30
-rw-r--r--source/blender/editors/transform/transform_convert_mesh_uv.c2
-rw-r--r--source/blender/editors/transform/transform_convert_object.c12
-rw-r--r--source/blender/editors/transform/transform_convert_sequencer.c25
-rw-r--r--source/blender/editors/transform/transform_generics.c82
-rw-r--r--source/blender/editors/transform/transform_gizmo_3d.c50
-rw-r--r--source/blender/editors/transform/transform_gizmo_extrude_3d.c15
-rw-r--r--source/blender/editors/transform/transform_input.c55
-rw-r--r--source/blender/editors/transform/transform_mode.c137
-rw-r--r--source/blender/editors/transform/transform_mode.h5
-rw-r--r--source/blender/editors/transform/transform_mode_curveshrinkfatten.c1
-rw-r--r--source/blender/editors/transform/transform_mode_edge_rotate_normal.c4
-rw-r--r--source/blender/editors/transform/transform_mode_edge_seq_slide.c10
-rw-r--r--source/blender/editors/transform/transform_mode_edge_slide.c8
-rw-r--r--source/blender/editors/transform/transform_mode_gpopacity.c1
-rw-r--r--source/blender/editors/transform/transform_mode_gpshrinkfatten.c1
-rw-r--r--source/blender/editors/transform/transform_mode_maskshrinkfatten.c3
-rw-r--r--source/blender/editors/transform/transform_mode_mirror.c3
-rw-r--r--source/blender/editors/transform/transform_mode_resize.c7
-rw-r--r--source/blender/editors/transform/transform_mode_rotate.c4
-rw-r--r--source/blender/editors/transform/transform_mode_shear.c2
-rw-r--r--source/blender/editors/transform/transform_mode_shrink_fatten.c6
-rw-r--r--source/blender/editors/transform/transform_mode_skin_resize.c3
-rw-r--r--source/blender/editors/transform/transform_mode_timetranslate.c4
-rw-r--r--source/blender/editors/transform/transform_mode_trackball.c26
-rw-r--r--source/blender/editors/transform/transform_mode_translate.c107
-rw-r--r--source/blender/editors/transform/transform_mode_vert_slide.c8
-rw-r--r--source/blender/editors/transform/transform_orientations.c61
-rw-r--r--source/blender/editors/transform/transform_orientations.h2
-rw-r--r--source/blender/editors/transform/transform_snap.c8
-rw-r--r--source/blender/editors/transform/transform_snap_object.c685
-rw-r--r--source/blender/editors/undo/ed_undo.c24
-rw-r--r--source/blender/editors/util/CMakeLists.txt3
-rw-r--r--source/blender/editors/util/ed_draw.c2
-rw-r--r--source/blender/editors/util/ed_transverts.c6
-rw-r--r--source/blender/editors/util/ed_util.c24
-rw-r--r--source/blender/editors/util/ed_util_ops.cc2
-rw-r--r--source/blender/editors/util/numinput.c25
-rw-r--r--source/blender/editors/util/select_utils.c6
-rw-r--r--source/blender/editors/uvedit/uvedit_ops.c4
-rw-r--r--source/blender/editors/uvedit/uvedit_parametrizer.c24
-rw-r--r--source/blender/editors/uvedit/uvedit_parametrizer.h7
-rw-r--r--source/blender/editors/uvedit/uvedit_select.c3
-rw-r--r--source/blender/editors/uvedit/uvedit_unwrap_ops.c79
-rw-r--r--source/blender/freestyle/intern/geometry/Bezier.cpp8
-rw-r--r--source/blender/freestyle/intern/geometry/Bezier.h1
-rw-r--r--source/blender/freestyle/intern/geometry/FitCurve.cpp4
-rw-r--r--source/blender/freestyle/intern/geometry/FitCurve.h1
-rw-r--r--source/blender/freestyle/intern/geometry/GridHelpers.cpp4
-rw-r--r--source/blender/freestyle/intern/geometry/normal_cycle.cpp4
-rw-r--r--source/blender/freestyle/intern/geometry/normal_cycle.h1
-rw-r--r--source/blender/freestyle/intern/scene_graph/NodeCamera.cpp11
-rw-r--r--source/blender/freestyle/intern/scene_graph/NodeCamera.h2
-rw-r--r--source/blender/freestyle/intern/stroke/ChainingIterators.h2
-rw-r--r--source/blender/freestyle/intern/stroke/Curve.h4
-rw-r--r--source/blender/freestyle/intern/stroke/Stroke.cpp4
-rw-r--r--source/blender/freestyle/intern/stroke/Stroke.h3
-rw-r--r--source/blender/freestyle/intern/stroke/StrokeRenderer.cpp8
-rw-r--r--source/blender/freestyle/intern/stroke/StrokeRenderer.h1
-rw-r--r--source/blender/freestyle/intern/system/PseudoNoise.cpp4
-rw-r--r--source/blender/freestyle/intern/system/PseudoNoise.h2
-rw-r--r--source/blender/freestyle/intern/view_map/ArbitraryGridDensityProvider.cpp8
-rw-r--r--source/blender/freestyle/intern/view_map/ArbitraryGridDensityProvider.h2
-rw-r--r--source/blender/freestyle/intern/view_map/AverageAreaGridDensityProvider.cpp8
-rw-r--r--source/blender/freestyle/intern/view_map/AverageAreaGridDensityProvider.h2
-rw-r--r--source/blender/freestyle/intern/view_map/BoxGrid.cpp20
-rw-r--r--source/blender/freestyle/intern/view_map/BoxGrid.h6
-rw-r--r--source/blender/freestyle/intern/view_map/CulledOccluderSource.cpp4
-rw-r--r--source/blender/freestyle/intern/view_map/CulledOccluderSource.h1
-rw-r--r--source/blender/freestyle/intern/view_map/HeuristicGridDensityProviderFactory.cpp4
-rw-r--r--source/blender/freestyle/intern/view_map/HeuristicGridDensityProviderFactory.h1
-rw-r--r--source/blender/freestyle/intern/view_map/OccluderSource.cpp4
-rw-r--r--source/blender/freestyle/intern/view_map/Pow23GridDensityProvider.cpp8
-rw-r--r--source/blender/freestyle/intern/view_map/Pow23GridDensityProvider.h2
-rw-r--r--source/blender/freestyle/intern/view_map/SphericalGrid.cpp20
-rw-r--r--source/blender/freestyle/intern/view_map/SphericalGrid.h6
-rw-r--r--source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp4
-rw-r--r--source/blender/functions/FN_cpp_type.hh11
-rw-r--r--source/blender/functions/FN_generic_pointer.hh10
-rw-r--r--source/blender/functions/FN_generic_span.hh20
-rw-r--r--source/blender/functions/FN_generic_value_map.hh5
-rw-r--r--source/blender/functions/FN_generic_vector_array.hh6
-rw-r--r--source/blender/functions/FN_generic_virtual_array.hh729
-rw-r--r--source/blender/functions/FN_generic_virtual_vector_array.hh16
-rw-r--r--source/blender/functions/FN_multi_function_network_optimization.hh4
-rw-r--r--source/blender/functions/FN_multi_function_params.hh25
-rw-r--r--source/blender/functions/intern/cpp_types.cc4
-rw-r--r--source/blender/functions/intern/generic_vector_array.cc6
-rw-r--r--source/blender/functions/intern/generic_virtual_array.cc309
-rw-r--r--source/blender/functions/intern/generic_virtual_vector_array.cc26
-rw-r--r--source/blender/functions/intern/multi_function_network_evaluation.cc109
-rw-r--r--source/blender/functions/intern/multi_function_network_optimization.cc27
-rw-r--r--source/blender/functions/tests/FN_multi_function_network_test.cc2
-rw-r--r--source/blender/gpencil_modifiers/CMakeLists.txt10
-rw-r--r--source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h1
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c14
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.h2
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c1
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c19
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencillength.c223
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c63
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c66
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c73
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c61
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h155
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c590
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c1736
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h50
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c4
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_util.c49
-rw-r--r--source/blender/gpu/CMakeLists.txt12
-rw-r--r--source/blender/gpu/GPU_capabilities.h13
-rw-r--r--source/blender/gpu/GPU_common.h1
-rw-r--r--source/blender/gpu/GPU_compute.h (renamed from source/blender/blenkernel/BKE_mesh_boolean_convert.h)24
-rw-r--r--source/blender/gpu/GPU_framebuffer.h5
-rw-r--r--source/blender/gpu/GPU_index_buffer.h21
-rw-r--r--source/blender/gpu/GPU_matrix.h36
-rw-r--r--source/blender/gpu/GPU_platform.h3
-rw-r--r--source/blender/gpu/GPU_shader.h7
-rw-r--r--source/blender/gpu/GPU_state.h1
-rw-r--r--source/blender/gpu/GPU_texture.h6
-rw-r--r--source/blender/gpu/GPU_vertex_buffer.h10
-rw-r--r--source/blender/gpu/intern/gpu_backend.hh1
-rw-r--r--source/blender/gpu/intern/gpu_batch_private.hh3
-rw-r--r--source/blender/gpu/intern/gpu_capabilities.cc60
-rw-r--r--source/blender/gpu/intern/gpu_capabilities_private.hh13
-rw-r--r--source/blender/gpu/intern/gpu_compute.cc41
-rw-r--r--source/blender/gpu/intern/gpu_context.cc2
-rw-r--r--source/blender/gpu/intern/gpu_debug.cc4
-rw-r--r--source/blender/gpu/intern/gpu_framebuffer.cc19
-rw-r--r--source/blender/gpu/intern/gpu_framebuffer_private.hh14
-rw-r--r--source/blender/gpu/intern/gpu_index_buffer.cc93
-rw-r--r--source/blender/gpu/intern/gpu_index_buffer_private.hh14
-rw-r--r--source/blender/gpu/intern/gpu_material_library.c7
-rw-r--r--source/blender/gpu/intern/gpu_matrix.cc49
-rw-r--r--source/blender/gpu/intern/gpu_platform.cc73
-rw-r--r--source/blender/gpu/intern/gpu_platform_private.hh16
-rw-r--r--source/blender/gpu/intern/gpu_shader.cc85
-rw-r--r--source/blender/gpu/intern/gpu_shader_interface.cc15
-rw-r--r--source/blender/gpu/intern/gpu_shader_interface.hh6
-rw-r--r--source/blender/gpu/intern/gpu_shader_private.hh1
-rw-r--r--source/blender/gpu/intern/gpu_state.cc6
-rw-r--r--source/blender/gpu/intern/gpu_texture.cc19
-rw-r--r--source/blender/gpu/intern/gpu_texture_private.hh7
-rw-r--r--source/blender/gpu/intern/gpu_vertex_buffer.cc15
-rw-r--r--source/blender/gpu/intern/gpu_vertex_buffer_private.hh3
-rw-r--r--source/blender/gpu/opengl/gl_backend.cc107
-rw-r--r--source/blender/gpu/opengl/gl_backend.hh7
-rw-r--r--source/blender/gpu/opengl/gl_batch.cc16
-rw-r--r--source/blender/gpu/opengl/gl_batch.hh5
-rw-r--r--source/blender/gpu/opengl/gl_compute.cc35
-rw-r--r--source/blender/gpu/opengl/gl_compute.hh30
-rw-r--r--source/blender/gpu/opengl/gl_drawlist.cc1
-rw-r--r--source/blender/gpu/opengl/gl_immediate.hh2
-rw-r--r--source/blender/gpu/opengl/gl_index_buffer.cc34
-rw-r--r--source/blender/gpu/opengl/gl_index_buffer.hh7
-rw-r--r--source/blender/gpu/opengl/gl_shader.cc38
-rw-r--r--source/blender/gpu/opengl/gl_shader.hh4
-rw-r--r--source/blender/gpu/opengl/gl_shader_interface.cc45
-rw-r--r--source/blender/gpu/opengl/gl_state.hh3
-rw-r--r--source/blender/gpu/opengl/gl_texture.cc6
-rw-r--r--source/blender/gpu/opengl/gl_vertex_buffer.cc45
-rw-r--r--source/blender/gpu/opengl/gl_vertex_buffer.hh8
-rw-r--r--source/blender/gpu/shaders/gpu_shader_2D_widget_base_vert.glsl26
-rw-r--r--source/blender/gpu/shaders/gpu_shader_text_frag.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_geometry.glsl5
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl1
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl4
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl6
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_wavelength.glsl15
-rw-r--r--source/blender/gpu/tests/gpu_index_buffer_test.cc47
-rw-r--r--source/blender/gpu/tests/gpu_shader_test.cc301
-rw-r--r--source/blender/gpu/tests/gpu_testing.cc6
-rw-r--r--source/blender/imbuf/CMakeLists.txt6
-rw-r--r--source/blender/imbuf/IMB_colormanagement.h6
-rw-r--r--source/blender/imbuf/IMB_imbuf.h16
-rw-r--r--source/blender/imbuf/intern/IMB_anim.h11
-rw-r--r--source/blender/imbuf/intern/IMB_indexer.h11
-rw-r--r--source/blender/imbuf/intern/anim_movie.c827
-rw-r--r--source/blender/imbuf/intern/bmp.c65
-rw-r--r--source/blender/imbuf/intern/cineon/cineon_dpx.c12
-rw-r--r--source/blender/imbuf/intern/colormanagement.c9
-rw-r--r--source/blender/imbuf/intern/colormanagement_inline.c4
-rw-r--r--source/blender/imbuf/intern/dds/ColorBlock.cpp5
-rw-r--r--source/blender/imbuf/intern/dds/ColorBlock.h2
-rw-r--r--source/blender/imbuf/intern/dds/DirectDrawSurface.cpp4
-rw-r--r--source/blender/imbuf/intern/dds/DirectDrawSurface.h1
-rw-r--r--source/blender/imbuf/intern/divers.c9
-rw-r--r--source/blender/imbuf/intern/imageprocess.c278
-rw-r--r--source/blender/imbuf/intern/indexer.c417
-rw-r--r--source/blender/imbuf/intern/iris.c6
-rw-r--r--source/blender/imbuf/intern/jpeg.c3
-rw-r--r--source/blender/imbuf/intern/metadata.c6
-rw-r--r--source/blender/imbuf/intern/openexr/openexr_api.cpp4
-rw-r--r--source/blender/imbuf/intern/radiance_hdr.c138
-rw-r--r--source/blender/imbuf/intern/rectop.c9
-rw-r--r--source/blender/imbuf/intern/tiff.c17
-rw-r--r--source/blender/imbuf/intern/util.c18
-rw-r--r--source/blender/io/alembic/ABC_alembic.h1
-rw-r--r--source/blender/io/alembic/exporter/abc_custom_props.cc4
-rw-r--r--source/blender/io/alembic/exporter/abc_custom_props.h2
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_abstract.cc4
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_abstract.h1
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_instance.cc4
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_instance.h1
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_mball.cc2
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_mesh.cc17
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_mesh.h1
-rw-r--r--source/blender/io/alembic/intern/abc_customdata.cc72
-rw-r--r--source/blender/io/alembic/intern/abc_customdata.h18
-rw-r--r--source/blender/io/alembic/intern/abc_reader_mesh.cc5
-rw-r--r--source/blender/io/alembic/intern/abc_reader_object.cc4
-rw-r--r--source/blender/io/alembic/intern/abc_reader_object.h2
-rw-r--r--source/blender/io/collada/DocumentImporter.cpp2
-rw-r--r--source/blender/io/collada/ErrorHandler.cpp5
-rw-r--r--source/blender/io/collada/ErrorHandler.h2
-rw-r--r--source/blender/io/collada/ExtraHandler.cpp4
-rw-r--r--source/blender/io/collada/ExtraHandler.h3
-rw-r--r--source/blender/io/collada/ExtraTags.cpp4
-rw-r--r--source/blender/io/collada/Materials.cpp82
-rw-r--r--source/blender/io/collada/Materials.h3
-rw-r--r--source/blender/io/collada/SkinInfo.cpp5
-rw-r--r--source/blender/io/common/IO_abstract_hierarchy_iterator.h5
-rw-r--r--source/blender/io/common/intern/abstract_hierarchy_iterator.cc4
-rw-r--r--source/blender/io/common/intern/dupli_parent_finder.cc8
-rw-r--r--source/blender/io/common/intern/dupli_parent_finder.hh3
-rw-r--r--source/blender/io/common/intern/object_identifier.cc9
-rw-r--r--source/blender/io/gpencil/CMakeLists.txt31
-rw-r--r--source/blender/io/gpencil/gpencil_io.h2
-rw-r--r--source/blender/io/gpencil/intern/gpencil_io_base.cc43
-rw-r--r--source/blender/io/gpencil/intern/gpencil_io_base.hh (renamed from source/blender/io/gpencil/intern/gpencil_io_base.h)5
-rw-r--r--source/blender/io/gpencil/intern/gpencil_io_capi.cc16
-rw-r--r--source/blender/io/gpencil/intern/gpencil_io_export_base.hh (renamed from source/blender/io/gpencil/intern/gpencil_io_export_base.h)2
-rw-r--r--source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc46
-rw-r--r--source/blender/io/gpencil/intern/gpencil_io_export_pdf.hh (renamed from source/blender/io/gpencil/intern/gpencil_io_export_pdf.h)4
-rw-r--r--source/blender/io/gpencil/intern/gpencil_io_export_svg.cc9
-rw-r--r--source/blender/io/gpencil/intern/gpencil_io_export_svg.hh (renamed from source/blender/io/gpencil/intern/gpencil_io_export_svg.h)10
-rw-r--r--source/blender/io/gpencil/intern/gpencil_io_import_base.cc2
-rw-r--r--source/blender/io/gpencil/intern/gpencil_io_import_base.hh (renamed from source/blender/io/gpencil/intern/gpencil_io_import_base.h)2
-rw-r--r--source/blender/io/gpencil/intern/gpencil_io_import_svg.cc20
-rw-r--r--source/blender/io/gpencil/intern/gpencil_io_import_svg.hh (renamed from source/blender/io/gpencil/intern/gpencil_io_import_svg.h)2
-rw-r--r--source/blender/io/gpencil/nanosvg/nanosvg.h32
-rw-r--r--source/blender/io/usd/intern/usd_writer_abstract.cc4
-rw-r--r--source/blender/io/usd/intern/usd_writer_abstract.h1
-rw-r--r--source/blender/io/usd/intern/usd_writer_metaball.cc2
-rw-r--r--source/blender/makesdna/DNA_ID.h22
-rw-r--r--source/blender/makesdna/DNA_action_types.h13
-rw-r--r--source/blender/makesdna/DNA_armature_types.h7
-rw-r--r--source/blender/makesdna/DNA_boid_types.h5
-rw-r--r--source/blender/makesdna/DNA_brush_types.h3
-rw-r--r--source/blender/makesdna/DNA_constraint_types.h2
-rw-r--r--source/blender/makesdna/DNA_curve_types.h31
-rw-r--r--source/blender/makesdna/DNA_effect_types.h9
-rw-r--r--source/blender/makesdna/DNA_genfile.h1
-rw-r--r--source/blender/makesdna/DNA_gpencil_modifier_defaults.h16
-rw-r--r--source/blender/makesdna/DNA_gpencil_modifier_types.h65
-rw-r--r--source/blender/makesdna/DNA_gpencil_types.h6
-rw-r--r--source/blender/makesdna/DNA_ipo_types.h5
-rw-r--r--source/blender/makesdna/DNA_light_defaults.h1
-rw-r--r--source/blender/makesdna/DNA_light_types.h2
-rw-r--r--source/blender/makesdna/DNA_lineart_types.h3
-rw-r--r--source/blender/makesdna/DNA_mesh_defaults.h1
-rw-r--r--source/blender/makesdna/DNA_mesh_types.h8
-rw-r--r--source/blender/makesdna/DNA_modifier_defaults.h4
-rw-r--r--source/blender/makesdna/DNA_modifier_types.h2
-rw-r--r--source/blender/makesdna/DNA_movieclip_defaults.h2
-rw-r--r--source/blender/makesdna/DNA_node_types.h153
-rw-r--r--source/blender/makesdna/DNA_object_force_types.h9
-rw-r--r--source/blender/makesdna/DNA_object_types.h9
-rw-r--r--source/blender/makesdna/DNA_particle_types.h10
-rw-r--r--source/blender/makesdna/DNA_pointcache_types.h3
-rw-r--r--source/blender/makesdna/DNA_scene_types.h6
-rw-r--r--source/blender/makesdna/DNA_sequence_types.h9
-rw-r--r--source/blender/makesdna/DNA_space_types.h88
-rw-r--r--source/blender/makesdna/DNA_texture_types.h3
-rw-r--r--source/blender/makesdna/DNA_userdef_types.h5
-rw-r--r--source/blender/makesdna/DNA_view3d_defaults.h1
-rw-r--r--source/blender/makesdna/DNA_view3d_types.h4
-rw-r--r--source/blender/makesdna/DNA_windowmanager_types.h15
-rw-r--r--source/blender/makesdna/DNA_xr_types.h15
-rw-r--r--source/blender/makesdna/intern/dna_defaults.c2
-rw-r--r--source/blender/makesdna/intern/dna_genfile.c15
-rw-r--r--source/blender/makesdna/intern/dna_utils.c3
-rw-r--r--source/blender/makesdna/intern/makesdna.c5
-rw-r--r--source/blender/makesrna/RNA_access.h6
-rw-r--r--source/blender/makesrna/RNA_define.h3
-rw-r--r--source/blender/makesrna/RNA_enum_types.h1
-rw-r--r--source/blender/makesrna/RNA_types.h58
-rw-r--r--source/blender/makesrna/intern/CMakeLists.txt2
-rw-r--r--source/blender/makesrna/intern/makesrna.c33
-rw-r--r--source/blender/makesrna/intern/rna_ID.c356
-rw-r--r--source/blender/makesrna/intern/rna_access.c134
-rw-r--r--source/blender/makesrna/intern/rna_action.c6
-rw-r--r--source/blender/makesrna/intern/rna_action_api.c24
-rw-r--r--source/blender/makesrna/intern/rna_armature.c12
-rw-r--r--source/blender/makesrna/intern/rna_armature_api.c20
-rw-r--r--source/blender/makesrna/intern/rna_attribute.c16
-rw-r--r--source/blender/makesrna/intern/rna_brush.c9
-rw-r--r--source/blender/makesrna/intern/rna_cloth.c1
-rw-r--r--source/blender/makesrna/intern/rna_color.c2
-rw-r--r--source/blender/makesrna/intern/rna_constraint.c9
-rw-r--r--source/blender/makesrna/intern/rna_curve.c23
-rw-r--r--source/blender/makesrna/intern/rna_curve_api.c2
-rw-r--r--source/blender/makesrna/intern/rna_curveprofile.c2
-rw-r--r--source/blender/makesrna/intern/rna_define.c26
-rw-r--r--source/blender/makesrna/intern/rna_depsgraph.c11
-rw-r--r--source/blender/makesrna/intern/rna_fcurve.c54
-rw-r--r--source/blender/makesrna/intern/rna_gpencil.c17
-rw-r--r--source/blender/makesrna/intern/rna_gpencil_modifier.c616
-rw-r--r--source/blender/makesrna/intern/rna_internal.h2
-rw-r--r--source/blender/makesrna/intern/rna_internal_types.h2
-rw-r--r--source/blender/makesrna/intern/rna_key.c12
-rw-r--r--source/blender/makesrna/intern/rna_lattice_api.c2
-rw-r--r--source/blender/makesrna/intern/rna_light.c9
-rw-r--r--source/blender/makesrna/intern/rna_main.c4
-rw-r--r--source/blender/makesrna/intern/rna_main_api.c38
-rw-r--r--source/blender/makesrna/intern/rna_material.c2
-rw-r--r--source/blender/makesrna/intern/rna_mesh.c24
-rw-r--r--source/blender/makesrna/intern/rna_mesh_api.c2
-rw-r--r--source/blender/makesrna/intern/rna_meta_api.c2
-rw-r--r--source/blender/makesrna/intern/rna_modifier.c17
-rw-r--r--source/blender/makesrna/intern/rna_movieclip.c4
-rw-r--r--source/blender/makesrna/intern/rna_nodetree.c637
-rw-r--r--source/blender/makesrna/intern/rna_object.c217
-rw-r--r--source/blender/makesrna/intern/rna_object_api.c4
-rw-r--r--source/blender/makesrna/intern/rna_object_force.c7
-rw-r--r--source/blender/makesrna/intern/rna_particle.c12
-rw-r--r--source/blender/makesrna/intern/rna_pose.c21
-rw-r--r--source/blender/makesrna/intern/rna_pose_api.c2
-rw-r--r--source/blender/makesrna/intern/rna_render.c6
-rw-r--r--source/blender/makesrna/intern/rna_rna.c25
-rw-r--r--source/blender/makesrna/intern/rna_scene.c35
-rw-r--r--source/blender/makesrna/intern/rna_scene_api.c2
-rw-r--r--source/blender/makesrna/intern/rna_screen.c46
-rw-r--r--source/blender/makesrna/intern/rna_sculpt_paint.c3
-rw-r--r--source/blender/makesrna/intern/rna_sequencer.c62
-rw-r--r--source/blender/makesrna/intern/rna_sequencer_api.c48
-rw-r--r--source/blender/makesrna/intern/rna_shader_fx.c38
-rw-r--r--source/blender/makesrna/intern/rna_space.c282
-rw-r--r--source/blender/makesrna/intern/rna_tracking.c4
-rw-r--r--source/blender/makesrna/intern/rna_ui.c2
-rw-r--r--source/blender/makesrna/intern/rna_userdef.c30
-rw-r--r--source/blender/makesrna/intern/rna_volume.c16
-rw-r--r--source/blender/makesrna/intern/rna_wm.c118
-rw-r--r--source/blender/makesrna/intern/rna_wm_gizmo.c16
-rw-r--r--source/blender/modifiers/CMakeLists.txt30
-rw-r--r--source/blender/modifiers/intern/MOD_armature.c1
-rw-r--r--source/blender/modifiers/intern/MOD_array.c6
-rw-r--r--source/blender/modifiers/intern/MOD_bevel.c1
-rw-r--r--source/blender/modifiers/intern/MOD_boolean.c967
-rw-r--r--source/blender/modifiers/intern/MOD_boolean.cc651
-rw-r--r--source/blender/modifiers/intern/MOD_build.c1
-rw-r--r--source/blender/modifiers/intern/MOD_cast.c1
-rw-r--r--source/blender/modifiers/intern/MOD_cloth.c1
-rw-r--r--source/blender/modifiers/intern/MOD_collision.c1
-rw-r--r--source/blender/modifiers/intern/MOD_correctivesmooth.c1
-rw-r--r--source/blender/modifiers/intern/MOD_curve.c1
-rw-r--r--source/blender/modifiers/intern/MOD_datatransfer.c1
-rw-r--r--source/blender/modifiers/intern/MOD_decimate.c1
-rw-r--r--source/blender/modifiers/intern/MOD_displace.c1
-rw-r--r--source/blender/modifiers/intern/MOD_dynamicpaint.c1
-rw-r--r--source/blender/modifiers/intern/MOD_edgesplit.c1
-rw-r--r--source/blender/modifiers/intern/MOD_explode.c1
-rw-r--r--source/blender/modifiers/intern/MOD_fluid.c1
-rw-r--r--source/blender/modifiers/intern/MOD_hook.c1
-rw-r--r--source/blender/modifiers/intern/MOD_laplaciandeform.c1
-rw-r--r--source/blender/modifiers/intern/MOD_laplaciansmooth.c1
-rw-r--r--source/blender/modifiers/intern/MOD_lattice.c1
-rw-r--r--source/blender/modifiers/intern/MOD_mask.cc38
-rw-r--r--source/blender/modifiers/intern/MOD_mesh_to_volume.cc19
-rw-r--r--source/blender/modifiers/intern/MOD_meshcache.c1
-rw-r--r--source/blender/modifiers/intern/MOD_meshdeform.c1
-rw-r--r--source/blender/modifiers/intern/MOD_meshsequencecache.c1
-rw-r--r--source/blender/modifiers/intern/MOD_mirror.c8
-rw-r--r--source/blender/modifiers/intern/MOD_multires.c1
-rw-r--r--source/blender/modifiers/intern/MOD_nodes.cc888
-rw-r--r--source/blender/modifiers/intern/MOD_nodes_evaluator.cc1553
-rw-r--r--source/blender/modifiers/intern/MOD_nodes_evaluator.hh52
-rw-r--r--source/blender/modifiers/intern/MOD_none.c1
-rw-r--r--source/blender/modifiers/intern/MOD_normal_edit.c1
-rw-r--r--source/blender/modifiers/intern/MOD_ocean.c1
-rw-r--r--source/blender/modifiers/intern/MOD_particleinstance.c1
-rw-r--r--source/blender/modifiers/intern/MOD_particlesystem.c1
-rw-r--r--source/blender/modifiers/intern/MOD_remesh.c1
-rw-r--r--source/blender/modifiers/intern/MOD_screw.c1
-rw-r--r--source/blender/modifiers/intern/MOD_shapekey.c1
-rw-r--r--source/blender/modifiers/intern/MOD_shrinkwrap.c1
-rw-r--r--source/blender/modifiers/intern/MOD_simpledeform.c198
-rw-r--r--source/blender/modifiers/intern/MOD_skin.c64
-rw-r--r--source/blender/modifiers/intern/MOD_smooth.c1
-rw-r--r--source/blender/modifiers/intern/MOD_softbody.c1
-rw-r--r--source/blender/modifiers/intern/MOD_solidify.c1
-rw-r--r--source/blender/modifiers/intern/MOD_solidify_extrude.c2
-rw-r--r--source/blender/modifiers/intern/MOD_subsurf.c1
-rw-r--r--source/blender/modifiers/intern/MOD_surface.c17
-rw-r--r--source/blender/modifiers/intern/MOD_surfacedeform.c1
-rw-r--r--source/blender/modifiers/intern/MOD_triangulate.c1
-rw-r--r--source/blender/modifiers/intern/MOD_ui_common.c29
-rw-r--r--source/blender/modifiers/intern/MOD_uvproject.c1
-rw-r--r--source/blender/modifiers/intern/MOD_uvwarp.c1
-rw-r--r--source/blender/modifiers/intern/MOD_volume_displace.cc22
-rw-r--r--source/blender/modifiers/intern/MOD_volume_to_mesh.cc5
-rw-r--r--source/blender/modifiers/intern/MOD_warp.c1
-rw-r--r--source/blender/modifiers/intern/MOD_wave.c1
-rw-r--r--source/blender/modifiers/intern/MOD_weighted_normal.c1
-rw-r--r--source/blender/modifiers/intern/MOD_weightvgedit.c1
-rw-r--r--source/blender/modifiers/intern/MOD_weightvgmix.c1
-rw-r--r--source/blender/modifiers/intern/MOD_weightvgproximity.c1
-rw-r--r--source/blender/modifiers/intern/MOD_weld.c1
-rw-r--r--source/blender/modifiers/intern/MOD_wireframe.c1
-rw-r--r--source/blender/nodes/CMakeLists.txt59
-rw-r--r--source/blender/nodes/NOD_composite.h1
-rw-r--r--source/blender/nodes/NOD_derived_node_tree.hh47
-rw-r--r--source/blender/nodes/NOD_geometry.h22
-rw-r--r--source/blender/nodes/NOD_geometry_exec.hh236
-rw-r--r--source/blender/nodes/NOD_math_functions.hh5
-rw-r--r--source/blender/nodes/NOD_node_tree_multi_function.hh46
-rw-r--r--source/blender/nodes/NOD_node_tree_ref.hh70
-rw-r--r--source/blender/nodes/NOD_socket.h2
-rw-r--r--source/blender/nodes/NOD_static_types.h84
-rw-r--r--source/blender/nodes/NOD_type_conversions.hh83
-rw-r--r--source/blender/nodes/composite/node_composite_tree.c7
-rw-r--r--source/blender/nodes/composite/node_composite_util.c10
-rw-r--r--source/blender/nodes/composite/node_composite_util.h4
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_antialiasing.c65
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc133
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_image.c34
-rw-r--r--source/blender/nodes/function/node_function_util.cc10
-rw-r--r--source/blender/nodes/function/node_function_util.hh1
-rw-r--r--source/blender/nodes/geometry/node_geometry_tree.cc32
-rw-r--r--source/blender/nodes/geometry/node_geometry_util.cc10
-rw-r--r--source/blender/nodes/geometry/node_geometry_util.hh14
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc192
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc284
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc55
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc31
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc118
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc93
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc232
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc39
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc437
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc106
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc166
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc38
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc96
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc62
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc68
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc597
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc268
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc352
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_boolean.cc170
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc177
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_collection_info.cc43
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_common.cc14
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc321
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_length.cc58
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc211
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc313
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc678
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_edge_split.cc5
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_material.cc51
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc123
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_material_assign.cc107
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_material_replace.cc75
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc3
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc372
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc1
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_plane.cc)66
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc10
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc252
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc316
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_object_info.cc20
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc190
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_instance.cc222
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc101
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_scale.cc78
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_separate.cc53
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_translate.cc30
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc78
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_select_by_material.cc106
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_subdivide.cc6
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc23
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_switch.cc191
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_transform.cc60
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_triangulate.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc16
-rw-r--r--source/blender/nodes/intern/derived_node_tree.cc50
-rw-r--r--source/blender/nodes/intern/math_functions.cc2
-rw-r--r--source/blender/nodes/intern/node_common.c20
-rw-r--r--source/blender/nodes/intern/node_common.h4
-rw-r--r--source/blender/nodes/intern/node_exec.cc (renamed from source/blender/nodes/intern/node_exec.c)49
-rw-r--r--source/blender/nodes/intern/node_geometry_exec.cc101
-rw-r--r--source/blender/nodes/intern/node_socket.cc137
-rw-r--r--source/blender/nodes/intern/node_tree_multi_function.cc128
-rw-r--r--source/blender/nodes/intern/node_tree_ref.cc223
-rw-r--r--source/blender/nodes/intern/node_util.c125
-rw-r--r--source/blender/nodes/intern/type_callbacks.cc3
-rw-r--r--source/blender/nodes/intern/type_conversions.cc349
-rw-r--r--source/blender/nodes/shader/node_shader_tree.c38
-rw-r--r--source/blender/nodes/shader/node_shader_util.c32
-rw-r--r--source/blender/nodes/shader/node_shader_util.h10
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_curves.cc (renamed from source/blender/nodes/shader/nodes/node_shader_curves.c)112
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_map_range.cc182
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_mixRgb.cc (renamed from source/blender/nodes/shader/nodes/node_shader_mixRgb.c)69
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_output_aov.c5
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc13
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_sky.c19
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_valToRgb.cc8
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_vector_math.cc199
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_wavelength.c28
-rw-r--r--source/blender/nodes/texture/node_texture_tree.c7
-rw-r--r--source/blender/nodes/texture/node_texture_util.c10
-rw-r--r--source/blender/nodes/texture/node_texture_util.h4
-rw-r--r--source/blender/python/BPY_extern_run.h24
-rw-r--r--source/blender/python/bmesh/bmesh_py_api.c2
-rw-r--r--source/blender/python/bmesh/bmesh_py_types.c65
-rw-r--r--source/blender/python/bmesh/bmesh_py_types.h27
-rw-r--r--source/blender/python/bmesh/bmesh_py_types_customdata.h9
-rw-r--r--source/blender/python/bmesh/bmesh_py_types_meshdata.c9
-rw-r--r--source/blender/python/bmesh/bmesh_py_types_meshdata.h3
-rw-r--r--source/blender/python/bmesh/bmesh_py_types_select.h6
-rw-r--r--source/blender/python/bmesh/bmesh_py_utils.c6
-rw-r--r--source/blender/python/generic/bgl.h3
-rw-r--r--source/blender/python/generic/idprop_py_api.c567
-rw-r--r--source/blender/python/generic/idprop_py_api.h61
-rw-r--r--source/blender/python/generic/imbuf_py_api.c18
-rw-r--r--source/blender/python/generic/py_capi_utils.c1
-rw-r--r--source/blender/python/gpu/CMakeLists.txt4
-rw-r--r--source/blender/python/gpu/gpu_py_api.c8
-rw-r--r--source/blender/python/gpu/gpu_py_batch.h4
-rw-r--r--source/blender/python/gpu/gpu_py_buffer.c176
-rw-r--r--source/blender/python/gpu/gpu_py_buffer.h3
-rw-r--r--source/blender/python/gpu/gpu_py_capabilities.c148
-rw-r--r--source/blender/python/gpu/gpu_py_capabilities.h23
-rw-r--r--source/blender/python/gpu/gpu_py_element.h3
-rw-r--r--source/blender/python/gpu/gpu_py_framebuffer.c164
-rw-r--r--source/blender/python/gpu/gpu_py_framebuffer.h7
-rw-r--r--source/blender/python/gpu/gpu_py_offscreen.c20
-rw-r--r--source/blender/python/gpu/gpu_py_platform.c81
-rw-r--r--source/blender/python/gpu/gpu_py_platform.h (renamed from source/blender/compositor/intern/COM_SocketReader.cc)10
-rw-r--r--source/blender/python/gpu/gpu_py_shader.h3
-rw-r--r--source/blender/python/gpu/gpu_py_state.c41
-rw-r--r--source/blender/python/gpu/gpu_py_texture.c43
-rw-r--r--source/blender/python/gpu/gpu_py_texture.h3
-rw-r--r--source/blender/python/gpu/gpu_py_vertex_buffer.h4
-rw-r--r--source/blender/python/gpu/gpu_py_vertex_format.h3
-rw-r--r--source/blender/python/intern/CMakeLists.txt2
-rw-r--r--source/blender/python/intern/bpy.c10
-rw-r--r--source/blender/python/intern/bpy_app.c5
-rw-r--r--source/blender/python/intern/bpy_app_handlers.c4
-rw-r--r--source/blender/python/intern/bpy_capi_utils.h5
-rw-r--r--source/blender/python/intern/bpy_interface.c13
-rw-r--r--source/blender/python/intern/bpy_interface_run.c67
-rw-r--r--source/blender/python/intern/bpy_library_load.c2
-rw-r--r--source/blender/python/intern/bpy_msgbus.c2
-rw-r--r--source/blender/python/intern/bpy_operator.c8
-rw-r--r--source/blender/python/intern/bpy_props.c32
-rw-r--r--source/blender/python/intern/bpy_rna.c45
-rw-r--r--source/blender/python/intern/bpy_rna_anim.c4
-rw-r--r--source/blender/python/intern/bpy_rna_id_collection.c2
-rw-r--r--source/blender/python/intern/bpy_rna_operator.c152
-rw-r--r--source/blender/python/intern/bpy_rna_operator.h31
-rw-r--r--source/blender/python/intern/bpy_rna_types_capi.c18
-rw-r--r--source/blender/python/intern/bpy_utils_units.c4
-rw-r--r--source/blender/python/mathutils/mathutils.c12
-rw-r--r--source/blender/python/mathutils/mathutils.h3
-rw-r--r--source/blender/python/mathutils/mathutils_Euler.c2
-rw-r--r--source/blender/python/mathutils/mathutils_Matrix.c148
-rw-r--r--source/blender/python/mathutils/mathutils_bvhtree.c7
-rw-r--r--source/blender/python/mathutils/mathutils_geometry.c2
-rw-r--r--source/blender/python/mathutils/mathutils_interpolate.c15
-rw-r--r--source/blender/python/mathutils/mathutils_kdtree.c4
-rw-r--r--source/blender/python/rna_dump.py2
-rw-r--r--source/blender/render/RE_engine.h4
-rw-r--r--source/blender/render/RE_pipeline.h19
-rw-r--r--source/blender/render/intern/engine.c137
-rw-r--r--source/blender/render/intern/pipeline.c273
-rw-r--r--source/blender/render/intern/render_result.c2
-rw-r--r--source/blender/render/intern/render_types.h2
-rw-r--r--source/blender/render/intern/zbuf.c2
-rw-r--r--source/blender/sequencer/CMakeLists.txt2
-rw-r--r--source/blender/sequencer/SEQ_add.h1
-rw-r--r--source/blender/sequencer/SEQ_iterator.h80
-rw-r--r--source/blender/sequencer/SEQ_sequencer.h5
-rw-r--r--source/blender/sequencer/SEQ_time.h1
-rw-r--r--source/blender/sequencer/SEQ_transform.h4
-rw-r--r--source/blender/sequencer/SEQ_utils.h9
-rw-r--r--source/blender/sequencer/intern/effects.c5
-rw-r--r--source/blender/sequencer/intern/image_cache.c39
-rw-r--r--source/blender/sequencer/intern/iterator.c300
-rw-r--r--source/blender/sequencer/intern/proxy.c24
-rw-r--r--source/blender/sequencer/intern/render.c416
-rw-r--r--source/blender/sequencer/intern/render.h4
-rw-r--r--source/blender/sequencer/intern/sequencer.c48
-rw-r--r--source/blender/sequencer/intern/strip_add.c43
-rw-r--r--source/blender/sequencer/intern/strip_edit.c114
-rw-r--r--source/blender/sequencer/intern/strip_relations.c5
-rw-r--r--source/blender/sequencer/intern/strip_time.c79
-rw-r--r--source/blender/sequencer/intern/strip_transform.c40
-rw-r--r--source/blender/sequencer/intern/utils.c94
-rw-r--r--source/blender/shader_fx/intern/FX_shader_glow.c7
-rw-r--r--source/blender/windowmanager/CMakeLists.txt1
-rw-r--r--source/blender/windowmanager/WM_api.h85
-rw-r--r--source/blender/windowmanager/WM_types.h30
-rw-r--r--source/blender/windowmanager/gizmo/WM_gizmo_api.h6
-rw-r--r--source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c6
-rw-r--r--source/blender/windowmanager/gizmo/intern/wm_gizmo_group_type.c6
-rw-r--r--source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c17
-rw-r--r--source/blender/windowmanager/gizmo/intern/wm_gizmo_type.c8
-rw-r--r--source/blender/windowmanager/gizmo/wm_gizmo_fn.h4
-rw-r--r--source/blender/windowmanager/intern/wm.c1
-rw-r--r--source/blender/windowmanager/intern/wm_cursors.c2
-rw-r--r--source/blender/windowmanager/intern/wm_dragdrop.c17
-rw-r--r--source/blender/windowmanager/intern/wm_draw.c8
-rw-r--r--source/blender/windowmanager/intern/wm_event_query.c17
-rw-r--r--source/blender/windowmanager/intern/wm_event_system.c86
-rw-r--r--source/blender/windowmanager/intern/wm_files.c81
-rw-r--r--source/blender/windowmanager/intern/wm_files_link.c50
-rw-r--r--source/blender/windowmanager/intern/wm_gesture.c4
-rw-r--r--source/blender/windowmanager/intern/wm_gesture_ops.c1
-rw-r--r--source/blender/windowmanager/intern/wm_init_exit.c11
-rw-r--r--source/blender/windowmanager/intern/wm_keymap.c9
-rw-r--r--source/blender/windowmanager/intern/wm_operator_type.c2
-rw-r--r--source/blender/windowmanager/intern/wm_operators.c11
-rw-r--r--source/blender/windowmanager/intern/wm_playanim.c484
-rw-r--r--source/blender/windowmanager/intern/wm_window.c34
-rw-r--r--source/blender/windowmanager/wm.h1
-rw-r--r--source/blender/windowmanager/wm_files.h3
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr.c5
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr_actions.c480
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr_draw.c6
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr_intern.h68
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr_session.c148
-rw-r--r--source/creator/CMakeLists.txt95
-rw-r--r--source/creator/blender_launcher_win32.c92
-rw-r--r--source/creator/creator.c40
-rw-r--r--source/creator/creator_args.c34
-rw-r--r--source/creator/creator_intern.h2
m---------source/tools0
-rw-r--r--tests/CMakeLists.txt7
-rw-r--r--tests/blender_as_python_module/CMakeLists.txt (renamed from source/blender/python/simple_enum_gen.py)56
-rw-r--r--tests/blender_as_python_module/import_bpy.py19
-rw-r--r--tests/python/CMakeLists.txt27
-rw-r--r--tests/python/bl_blendfile_library_overrides.py105
-rw-r--r--tests/python/bl_load_addons.py3
-rw-r--r--tests/python/bl_pyapi_bpy_path.py12
-rw-r--r--tests/python/bl_pyapi_bpy_utils_units.py4
-rw-r--r--tests/python/bl_pyapi_idprop.py52
-rw-r--r--tests/python/bl_pyapi_mathutils.py31
-rw-r--r--tests/python/bl_run_operators_event_simulate.py597
-rw-r--r--tests/python/bl_test.py13
-rw-r--r--tests/python/compositor_render_tests.py68
-rw-r--r--tests/python/operators.py122
2283 files changed, 82935 insertions, 42187 deletions
diff --git a/.clang-format b/.clang-format
index 79aa893cada..9cc3cdeaaf8 100644
--- a/.clang-format
+++ b/.clang-format
@@ -2,7 +2,7 @@
# Configuration of clang-format
# =============================
#
-# Tested to work with versions: 6 to 8.
+# Tested to work with versions: 8 to 11.
# This causes parameters on continuations to align to the opening brace.
#
@@ -255,7 +255,7 @@ ForEachMacros:
- SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN
- SEQ_ALL_BEGIN
- - SEQ_CURRENT_BEGIN
+ - SEQ_ITERATOR_FOREACH
- SURFACE_QUAD_ITER_BEGIN
- foreach
- ED_screen_areas_iter
@@ -264,8 +264,5 @@ ForEachMacros:
- MAP_SLOT_PROBING_BEGIN
- VECTOR_SET_SLOT_PROBING_BEGIN
-# Use once we bump the minimum version to version 8.
-# # Without this string literals that in-line 'STRINGIFY' behave strangely (a bug?).
-# StatementMacros:
-# - PyObject_VAR_HEAD
-# - STRINGIFY
+StatementMacros:
+ - PyObject_VAR_HEAD
diff --git a/.clang-tidy b/.clang-tidy
index 98fa36db790..b03163b54b9 100644
--- a/.clang-tidy
+++ b/.clang-tidy
@@ -35,7 +35,6 @@ Checks: >
-modernize-use-auto,
-modernize-use-trailing-return-type,
-modernize-avoid-c-arrays,
- -modernize-use-equals-default,
-modernize-use-nodiscard,
-modernize-loop-convert,
-modernize-pass-by-value,
@@ -43,7 +42,6 @@ Checks: >
# the windows compiler currently.
-modernize-raw-string-literal
-WarningsAsErrors: '*'
CheckOptions:
- key: modernize-use-default-member-init.UseAssignment
value: 1
diff --git a/.gitignore b/.gitignore
index b1f351255b7..96b70c2be70 100644
--- a/.gitignore
+++ b/.gitignore
@@ -46,3 +46,6 @@ Desktop.ini
# smoke simulation noise tile (generated)
waveletNoiseTile.bin
+
+# testing environment
+/Testing
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2743d2d334c..f315fa87236 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -236,9 +236,7 @@ option(WITH_SYSTEM_AUDASPACE "Build with external audaspace library installed on
mark_as_advanced(WITH_AUDASPACE)
mark_as_advanced(WITH_SYSTEM_AUDASPACE)
-if(NOT WITH_AUDASPACE)
- set(WITH_SYSTEM_AUDASPACE OFF)
-endif()
+set_and_warn_dependency(WITH_AUDASPACE WITH_SYSTEM_AUDASPACE OFF)
option(WITH_OPENMP "Enable OpenMP (has to be supported by the compiler)" ON)
if(UNIX AND NOT APPLE)
@@ -522,10 +520,10 @@ if(CMAKE_COMPILER_IS_GNUCC)
mark_as_advanced(WITH_LINKER_LLD)
endif()
-if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES "Clang")
- option(WITH_COMPILER_ASAN "Build and link against address sanitizer (only for Debug & RelWithDebInfo targets)." OFF)
- mark_as_advanced(WITH_COMPILER_ASAN)
+option(WITH_COMPILER_ASAN "Build and link against address sanitizer (only for Debug & RelWithDebInfo targets)." OFF)
+mark_as_advanced(WITH_COMPILER_ASAN)
+if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES "Clang")
if(WITH_COMPILER_ASAN)
set(_asan_defaults "\
-fsanitize=address \
@@ -704,10 +702,8 @@ if(WITH_PYTHON_MODULE AND WITH_PYTHON_INSTALL)
message(FATAL_ERROR "WITH_PYTHON_MODULE requires WITH_PYTHON_INSTALL to be OFF")
endif()
-if(NOT WITH_PYTHON)
- set(WITH_CYCLES OFF)
- set(WITH_DRACO OFF)
-endif()
+set_and_warn_dependency(WITH_PYTHON WITH_CYCLES OFF)
+set_and_warn_dependency(WITH_PYTHON WITH_DRACO OFF)
if(WITH_DRACO AND NOT WITH_PYTHON_INSTALL)
message(STATUS "WITH_DRACO requires WITH_PYTHON_INSTALL to be ON, disabling WITH_DRACO for now")
@@ -779,6 +775,7 @@ if(WITH_INSTALL_PORTABLE)
endif()
if(WITH_GHOST_SDL OR WITH_HEADLESS)
+ message(STATUS "Disabling Ghost Wayland, X11, Input IME, and OpenXR")
set(WITH_GHOST_WAYLAND OFF)
set(WITH_GHOST_X11 OFF)
set(WITH_X11_XINPUT OFF)
@@ -809,7 +806,7 @@ endif()
if(NOT WITH_CUDA_DYNLOAD)
find_package(CUDA)
if(NOT CUDA_FOUND)
- message("CUDA toolkit not found, using dynamic runtime loading of libraries instead")
+ message(STATUS "CUDA toolkit not found, using dynamic runtime loading of libraries (WITH_CUDA_DYNLOAD) instead")
set(WITH_CUDA_DYNLOAD ON)
endif()
endif()
@@ -1235,6 +1232,7 @@ if(WITH_OPENMP)
string(APPEND CMAKE_C_FLAGS " ${OpenMP_C_FLAGS}")
string(APPEND CMAKE_CXX_FLAGS " ${OpenMP_CXX_FLAGS}")
string(APPEND CMAKE_EXE_LINKER_FLAGS " ${OpenMP_LINKER_FLAGS}")
+ string(APPEND CMAKE_MODULE_LINKER_FLAGS " ${OpenMP_LINKER_FLAGS}")
else()
# Typically avoid adding flags as defines but we can't
# pass OpenMP flags to the linker for static builds, meaning
@@ -1245,6 +1243,7 @@ if(WITH_OPENMP)
find_library_static(OpenMP_LIBRARIES gomp ${CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES})
endif()
else()
+ message(STATUS "OpenMP not found, disabling WITH_OPENMP")
set(WITH_OPENMP OFF)
endif()
@@ -1320,6 +1319,7 @@ list(APPEND GL_DEFINITIONS -DGLEW_NO_GLU)
if(WITH_BULLET AND WITH_SYSTEM_BULLET)
find_package(Bullet)
if(NOT BULLET_FOUND)
+ message(STATUS "Bullet not found, disabling WITH_BULLET")
set(WITH_BULLET OFF)
endif()
else()
@@ -1526,6 +1526,7 @@ if(CMAKE_COMPILER_IS_GNUCC)
ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_WARN_UNDEF -Wundef)
ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_WARN_FORMAT_SIGN -Wformat-signedness)
ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_WARN_RESTRICT -Wrestrict)
+ ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_WARN_NO_SUGGEST_OVERRIDE -Wno-suggest-override)
# gcc 4.2 gives annoying warnings on every file with this
if(NOT "${CMAKE_C_COMPILER_VERSION}" VERSION_LESS "4.3")
@@ -1589,6 +1590,8 @@ elseif(CMAKE_C_COMPILER_ID MATCHES "Clang")
ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_WARN_NO_OVERLOADED_VIRTUAL -Wno-overloaded-virtual) # we get a lot of these, if its a problem a dev needs to look into it.
ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_WARN_NO_SIGN_COMPARE -Wno-sign-compare)
ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_WARN_NO_INVALID_OFFSETOF -Wno-invalid-offsetof)
+ # Apple Clang (tested on version 12) doesn't support this flag while LLVM Clang 11 does.
+ ADD_CHECK_CXX_COMPILER_FLAG(CXX_WARNINGS CXX_WARN_NO_SUGGEST_OVERRIDE -Wno-suggest-override)
# gives too many unfixable warnings
# ADD_CHECK_C_COMPILER_FLAG(C_WARNINGS C_WARN_UNUSED_MACROS -Wunused-macros)
diff --git a/GNUmakefile b/GNUmakefile
index 3fe1850ca73..7df561ed34f 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -128,8 +128,14 @@ Utilities
* source_archive:
Create a compressed archive of the source code.
+ * source_archive_complete:
+ Create a compressed archive of the source code and all the libraries of dependencies.
+
* update:
- updates git and all submodules
+ Updates git and all submodules and svn.
+
+ * update_code:
+ Updates git and all submodules but not svn.
* format:
Format source code using clang (uses PATHS if passed in). For example::
@@ -474,6 +480,9 @@ check_smatch: .FORCE
cd "$(BUILD_DIR)" ; \
$(PYTHON) "$(BLENDER_DIR)/build_files/cmake/cmake_static_check_smatch.py"
+check_mypy: .FORCE
+ $(PYTHON) "$(BLENDER_DIR)/source/tools/check_source/check_mypy.py"
+
check_spelling_py: .FORCE
cd "$(BUILD_DIR)" ; \
PYTHONIOENCODING=utf_8 $(PYTHON) \
@@ -508,6 +517,13 @@ check_descriptions: .FORCE
source_archive: .FORCE
python3 ./build_files/utils/make_source_archive.py
+source_archive_complete: .FORCE
+ cmake -S "$(BLENDER_DIR)/build_files/build_environment" -B"$(BUILD_DIR)/source_archive" \
+ -DCMAKE_BUILD_TYPE_INIT:STRING=$(BUILD_TYPE) -DPACKAGE_USE_UPSTREAM_SOURCES=OFF
+# This assumes CMake is still using a default `PACKAGE_DIR` variable:
+ python3 ./build_files/utils/make_source_archive.py --include-packages "$(BUILD_DIR)/source_archive/packages"
+
+
INKSCAPE_BIN?="inkscape"
icons: .FORCE
BLENDER_BIN=$(BLENDER_BIN) INKSCAPE_BIN=$(INKSCAPE_BIN) \
@@ -522,6 +538,9 @@ icons_geom: .FORCE
update: .FORCE
$(PYTHON) ./build_files/utils/make_update.py
+update_code: .FORCE
+ $(PYTHON) ./build_files/utils/make_update.py --no-libraries
+
format: .FORCE
PATH="../lib/${OS_NCASE}_${CPU}/llvm/bin/:../lib/${OS_NCASE}_centos7_${CPU}/llvm/bin/:../lib/${OS_NCASE}/llvm/bin/:$(PATH)" \
$(PYTHON) source/tools/utils_maintenance/clang_format_paths.py $(PATHS)
diff --git a/build_files/build_environment/CMakeLists.txt b/build_files/build_environment/CMakeLists.txt
index a3d694b4bc3..fb79eee62be 100644
--- a/build_files/build_environment/CMakeLists.txt
+++ b/build_files/build_environment/CMakeLists.txt
@@ -113,7 +113,7 @@ include(cmake/expat.cmake)
include(cmake/yamlcpp.cmake)
include(cmake/opencolorio.cmake)
-if(APPLE AND ("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64"))
+if(BLENDER_PLATFORM_ARM)
include(cmake/sse2neon.cmake)
endif()
diff --git a/build_files/build_environment/cmake/blosc.cmake b/build_files/build_environment/cmake/blosc.cmake
index 89df67b7d58..ce88f081722 100644
--- a/build_files/build_environment/cmake/blosc.cmake
+++ b/build_files/build_environment/cmake/blosc.cmake
@@ -29,7 +29,7 @@ set(BLOSC_EXTRA_ARGS
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
)
-# Prevent blosc from including it's own local copy of zlib in the object file
+# Prevent blosc from including its own local copy of zlib in the object file
# and cause linker errors with everybody else.
set(BLOSC_EXTRA_ARGS ${BLOSC_EXTRA_ARGS}
-DPREFER_EXTERNAL_ZLIB=ON
diff --git a/build_files/build_environment/cmake/boost.cmake b/build_files/build_environment/cmake/boost.cmake
index 8b36af7dc41..5170a3a123e 100644
--- a/build_files/build_environment/cmake/boost.cmake
+++ b/build_files/build_environment/cmake/boost.cmake
@@ -18,6 +18,12 @@
set(BOOST_ADDRESS_MODEL 64)
+if(BLENDER_PLATFORM_ARM)
+ set(BOOST_ARCHITECTURE arm)
+else()
+ set(BOOST_ARCHITECTURE x86)
+endif()
+
if(WIN32)
set(BOOST_TOOLSET toolset=msvc-14.1)
set(BOOST_COMPILER_STRING -vc141)
@@ -29,7 +35,6 @@ if(WIN32)
if(BUILD_MODE STREQUAL Release)
set(BOOST_HARVEST_CMD ${BOOST_HARVEST_CMD} && ${CMAKE_COMMAND} -E copy_directory ${LIBDIR}/boost/include/boost-${BOOST_VERSION_NODOTS_SHORT}/ ${HARVEST_TARGET}/boost/include/)
endif()
-
elseif(APPLE)
set(BOOST_CONFIGURE_COMMAND ./bootstrap.sh)
set(BOOST_BUILD_COMMAND ./b2)
@@ -93,7 +98,7 @@ ExternalProject_Add(external_boost
UPDATE_COMMAND ""
PATCH_COMMAND ${BOOST_PATCH_COMMAND}
CONFIGURE_COMMAND ${BOOST_CONFIGURE_COMMAND}
- BUILD_COMMAND ${BOOST_BUILD_COMMAND} ${BOOST_BUILD_OPTIONS} -j${MAKE_THREADS} architecture=x86 address-model=${BOOST_ADDRESS_MODEL} link=static threading=multi ${BOOST_OPTIONS} --prefix=${LIBDIR}/boost install
+ BUILD_COMMAND ${BOOST_BUILD_COMMAND} ${BOOST_BUILD_OPTIONS} -j${MAKE_THREADS} architecture=${BOOST_ARCHITECTURE} address-model=${BOOST_ADDRESS_MODEL} link=static threading=multi ${BOOST_OPTIONS} --prefix=${LIBDIR}/boost install
BUILD_IN_SOURCE 1
INSTALL_COMMAND "${BOOST_HARVEST_CMD}"
)
diff --git a/build_files/build_environment/cmake/download.cmake b/build_files/build_environment/cmake/download.cmake
index f52957afeab..27351ddee45 100644
--- a/build_files/build_environment/cmake/download.cmake
+++ b/build_files/build_environment/cmake/download.cmake
@@ -12,7 +12,7 @@ function(download_source dep)
if(NOT EXISTS ${TARGET_FILE})
message("Checking source : ${dep} - source not found downloading from ${TARGET_URI}")
file(DOWNLOAD ${TARGET_URI} ${TARGET_FILE}
- TIMEOUT 60 # seconds
+ TIMEOUT 1800 # seconds
EXPECTED_HASH ${TARGET_HASH_TYPE}=${TARGET_HASH}
TLS_VERIFY ON
SHOW_PROGRESS
diff --git a/build_files/build_environment/cmake/embree.cmake b/build_files/build_environment/cmake/embree.cmake
index 4830630def0..b1d5dff91a0 100644
--- a/build_files/build_environment/cmake/embree.cmake
+++ b/build_files/build_environment/cmake/embree.cmake
@@ -43,11 +43,17 @@ endif()
if(WIN32)
set(EMBREE_BUILD_DIR ${BUILD_MODE}/)
+ if(BUILD_MODE STREQUAL Debug)
+ list(APPEND EMBREE_EXTRA_ARGS
+ -DEMBREE_TBBMALLOC_LIBRARY_NAME=tbbmalloc_debug
+ -DEMBREE_TBB_LIBRARY_NAME=tbb_debug
+ )
+ endif()
else()
set(EMBREE_BUILD_DIR)
endif()
-if(APPLE AND ("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64"))
+if(BLENDER_PLATFORM_ARM)
ExternalProject_Add(external_embree
GIT_REPOSITORY ${EMBREE_ARM_GIT}
GIT_TAG "blender-arm"
diff --git a/build_files/build_environment/cmake/gmp.cmake b/build_files/build_environment/cmake/gmp.cmake
index 323630a63aa..6ca81678a32 100644
--- a/build_files/build_environment/cmake/gmp.cmake
+++ b/build_files/build_environment/cmake/gmp.cmake
@@ -25,19 +25,12 @@ else()
set(GMP_OPTIONS --enable-static --disable-shared )
endif()
-if(APPLE)
- if("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")
- set(GMP_OPTIONS
- ${GMP_OPTIONS}
- --disable-assembly
- )
- else()
- set(GMP_OPTIONS
- ${GMP_OPTIONS}
- --with-pic
- )
- endif()
-elseif(UNIX)
+if(APPLE AND NOT BLENDER_PLATFORM_ARM)
+ set(GMP_OPTIONS
+ ${GMP_OPTIONS}
+ --with-pic
+ )
+elseif(UNIX AND NOT APPLE)
set(GMP_OPTIONS
${GMP_OPTIONS}
--with-pic
@@ -45,6 +38,13 @@ elseif(UNIX)
)
endif()
+if(BLENDER_PLATFORM_ARM)
+ set(GMP_OPTIONS
+ ${GMP_OPTIONS}
+ --disable-assembly
+ )
+endif()
+
ExternalProject_Add(external_gmp
URL file://${PACKAGE_DIR}/${GMP_FILE}
DOWNLOAD_DIR ${DOWNLOAD_DIR}
diff --git a/build_files/build_environment/cmake/harvest.cmake b/build_files/build_environment/cmake/harvest.cmake
index 23d0dcbab7b..fc7e652a028 100644
--- a/build_files/build_environment/cmake/harvest.cmake
+++ b/build_files/build_environment/cmake/harvest.cmake
@@ -109,9 +109,9 @@ harvest(llvm/lib llvm/lib "libclang*.a")
if(APPLE)
harvest(openmp/lib openmp/lib "*")
harvest(openmp/include openmp/include "*.h")
- if("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")
- harvest(sse2neon sse2neon "*.h")
- endif()
+endif()
+if(BLENDER_PLATFORM_ARM)
+ harvest(sse2neon sse2neon "*.h")
endif()
harvest(ogg/lib ffmpeg/lib "*.a")
harvest(openal/include openal/include "*.h")
diff --git a/build_files/build_environment/cmake/llvm.cmake b/build_files/build_environment/cmake/llvm.cmake
index f067267a416..cbb986410aa 100644
--- a/build_files/build_environment/cmake/llvm.cmake
+++ b/build_files/build_environment/cmake/llvm.cmake
@@ -16,7 +16,7 @@
#
# ***** END GPL LICENSE BLOCK *****
-if(APPLE AND "${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")
+if(BLENDER_PLATFORM_ARM)
set(LLVM_TARGETS AArch64$<SEMICOLON>ARM)
else()
set(LLVM_TARGETS X86)
diff --git a/build_files/build_environment/cmake/opencolorio.cmake b/build_files/build_environment/cmake/opencolorio.cmake
index bd03be5ebff..28c93973cf5 100644
--- a/build_files/build_environment/cmake/opencolorio.cmake
+++ b/build_files/build_environment/cmake/opencolorio.cmake
@@ -36,7 +36,7 @@ set(OPENCOLORIO_EXTRA_ARGS
-Dyaml-cpp_ROOT=${LIBDIR}/yamlcpp
)
-if(APPLE AND NOT("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "x86_64"))
+if(BLENDER_PLATFORM_ARM)
set(OPENCOLORIO_EXTRA_ARGS
${OPENCOLORIO_EXTRA_ARGS}
-DOCIO_USE_SSE=OFF
diff --git a/build_files/build_environment/cmake/openimageio.cmake b/build_files/build_environment/cmake/openimageio.cmake
index 68d9bd96db3..a8f4025b0ba 100644
--- a/build_files/build_environment/cmake/openimageio.cmake
+++ b/build_files/build_environment/cmake/openimageio.cmake
@@ -68,7 +68,6 @@ set(OPENIMAGEIO_EXTRA_ARGS
-DBOOST_LIBRARYDIR=${LIBDIR}/boost/lib/
-DBoost_NO_SYSTEM_PATHS=ON
-DBoost_NO_BOOST_CMAKE=ON
- -OIIO_BUILD_CPP11=ON
-DUSE_LIBSQUISH=OFF
-DUSE_QT5=OFF
-DUSE_NUKE=OFF
diff --git a/build_files/build_environment/cmake/options.cmake b/build_files/build_environment/cmake/options.cmake
index 5091a5d9496..8930bd70fab 100644
--- a/build_files/build_environment/cmake/options.cmake
+++ b/build_files/build_environment/cmake/options.cmake
@@ -21,7 +21,8 @@ if(WIN32)
endif()
option(WITH_WEBP "Enable building of oiio with webp support" OFF)
option(WITH_BOOST_PYTHON "Enable building of boost with python support" OFF)
-set(MAKE_THREADS 1 CACHE STRING "Number of threads to run make with")
+cmake_host_system_information(RESULT NUM_CORES QUERY NUMBER_OF_LOGICAL_CORES)
+set(MAKE_THREADS ${NUM_CORES} CACHE STRING "Number of threads to run make with")
if(NOT BUILD_MODE)
set(BUILD_MODE "Release")
@@ -36,14 +37,8 @@ else(BUILD_MODE STREQUAL "Debug")
endif()
set(DOWNLOAD_DIR "${CMAKE_CURRENT_BINARY_DIR}/downloads" CACHE STRING "Path for downloaded files")
-# look in blenders source folder for packages directory, if that exists
-# it will our package folder, otherwise it will be in the build folder
-if(EXISTS "${CMAKE_SOURCE_DIR}/../../packages")
- set(PACKAGE_DIR_DEFAULT "${CMAKE_SOURCE_DIR}/../../packages")
-else()
- set(PACKAGE_DIR_DEFAULT "${CMAKE_CURRENT_BINARY_DIR}/packages")
-endif()
-set(PACKAGE_DIR ${PACKAGE_DIR_DEFAULT} CACHE STRING "Path for downloaded source files")
+# This path must be hard-coded like this, so that the GNUmakefile knows where it is and can pass it to make_source_archive.py:
+set(PACKAGE_DIR "${CMAKE_CURRENT_BINARY_DIR}/packages")
option(PACKAGE_USE_UPSTREAM_SOURCES "Use soures upstream to download the package sources, when OFF the blender mirror will be used" ON)
file(TO_CMAKE_PATH ${DOWNLOAD_DIR} DOWNLOAD_DIR)
@@ -142,6 +137,10 @@ else()
endif()
set(OSX_SYSROOT ${XCODE_DEV_PATH}/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk)
+ if("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")
+ set(BLENDER_PLATFORM_ARM ON)
+ endif()
+
set(PLATFORM_CFLAGS "-isysroot ${OSX_SYSROOT} -mmacosx-version-min=${OSX_DEPLOYMENT_TARGET} -arch ${CMAKE_OSX_ARCHITECTURES}")
set(PLATFORM_CXXFLAGS "-isysroot ${OSX_SYSROOT} -mmacosx-version-min=${OSX_DEPLOYMENT_TARGET} -std=c++11 -stdlib=libc++ -arch ${CMAKE_OSX_ARCHITECTURES}")
set(PLATFORM_LDFLAGS "-isysroot ${OSX_SYSROOT} -mmacosx-version-min=${OSX_DEPLOYMENT_TARGET} -arch ${CMAKE_OSX_ARCHITECTURES}")
@@ -156,6 +155,10 @@ else()
-DCMAKE_OSX_SYSROOT:PATH=${OSX_SYSROOT}
)
else()
+ if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "aarch64")
+ set(BLENDER_PLATFORM_ARM ON)
+ endif()
+
set(PLATFORM_CFLAGS "-fPIC")
set(PLATFORM_CXXFLAGS "-std=c++11 -fPIC")
set(PLATFORM_LDFLAGS)
diff --git a/build_files/build_environment/cmake/png.cmake b/build_files/build_environment/cmake/png.cmake
index d9248b61c98..458d3a1fd98 100644
--- a/build_files/build_environment/cmake/png.cmake
+++ b/build_files/build_environment/cmake/png.cmake
@@ -22,8 +22,8 @@ set(PNG_EXTRA_ARGS
-DPNG_STATIC=ON
)
-if(APPLE AND ("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64"))
- set(PNG_EXTRA_ARGS ${PNG_EXTRA_ARGS} -DPNG_HARDWARE_OPTIMIZATIONS=ON -DPNG_ARM_NEON=on -DCMAKE_SYSTEM_PROCESSOR="aarch64")
+if(BLENDER_PLATFORM_ARM)
+ set(PNG_EXTRA_ARGS ${PNG_EXTRA_ARGS} -DPNG_HARDWARE_OPTIMIZATIONS=ON -DPNG_ARM_NEON=ON -DCMAKE_SYSTEM_PROCESSOR="aarch64")
endif()
ExternalProject_Add(external_png
diff --git a/build_files/build_environment/cmake/sse2neon.cmake b/build_files/build_environment/cmake/sse2neon.cmake
index dca2d94f913..d7987fdfd2f 100644
--- a/build_files/build_environment/cmake/sse2neon.cmake
+++ b/build_files/build_environment/cmake/sse2neon.cmake
@@ -16,15 +16,13 @@
#
# ***** END GPL LICENSE BLOCK *****
-if(APPLE AND ("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64"))
- ExternalProject_Add(external_sse2neon
- GIT_REPOSITORY ${SSE2NEON_GIT}
- GIT_TAG ${SSE2NEON_GIT_HASH}
- DOWNLOAD_DIR ${DOWNLOAD_DIR}
- PREFIX ${BUILD_DIR}/sse2neon
- CONFIGURE_COMMAND echo sse2neon - Nothing to configure
- BUILD_COMMAND echo sse2neon - nothing to build
- INSTALL_COMMAND mkdir -p ${LIBDIR}/sse2neon && cp ${BUILD_DIR}/sse2neon/src/external_sse2neon/sse2neon.h ${LIBDIR}/sse2neon
- INSTALL_DIR ${LIBDIR}/sse2neon
- )
-endif()
+ExternalProject_Add(external_sse2neon
+ GIT_REPOSITORY ${SSE2NEON_GIT}
+ GIT_TAG ${SSE2NEON_GIT_HASH}
+ DOWNLOAD_DIR ${DOWNLOAD_DIR}
+ PREFIX ${BUILD_DIR}/sse2neon
+ CONFIGURE_COMMAND echo sse2neon - Nothing to configure
+ BUILD_COMMAND echo sse2neon - nothing to build
+ INSTALL_COMMAND mkdir -p ${LIBDIR}/sse2neon && cp ${BUILD_DIR}/sse2neon/src/external_sse2neon/sse2neon.h ${LIBDIR}/sse2neon
+ INSTALL_DIR ${LIBDIR}/sse2neon
+)
diff --git a/build_files/build_environment/cmake/ssl.cmake b/build_files/build_environment/cmake/ssl.cmake
index 4426cc876c6..615b88167ec 100644
--- a/build_files/build_environment/cmake/ssl.cmake
+++ b/build_files/build_environment/cmake/ssl.cmake
@@ -22,7 +22,9 @@ set(SSL_PATCH_CMD echo .)
if(APPLE)
set(SSL_OS_COMPILER "blender-darwin-${CMAKE_OSX_ARCHITECTURES}")
else()
- if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
+ if(BLENDER_PLATFORM_ARM)
+ set(SSL_OS_COMPILER "blender-linux-aarch64")
+ elseif("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
set(SSL_EXTRA_ARGS enable-ec_nistp_64_gcc_128)
set(SSL_OS_COMPILER "blender-linux-x86_64")
else()
diff --git a/build_files/build_environment/cmake/ssl.conf b/build_files/build_environment/cmake/ssl.conf
index 8a9c9dcab4c..fa59bcf2825 100644
--- a/build_files/build_environment/cmake/ssl.conf
+++ b/build_files/build_environment/cmake/ssl.conf
@@ -8,6 +8,11 @@ my %targets = (
inherit_from => [ "linux-x86_64" ],
cflags => add("-fPIC"),
},
+ "blender-linux-aarch64" => {
+ inherit_from => [ "linux-aarch64" ],
+ cxxflags => add("-fPIC"),
+ cflags => add("-fPIC"),
+ },
"blender-darwin-x86_64" => {
inherit_from => [ "darwin64-x86_64-cc" ],
cflags => add("-fPIC"),
diff --git a/build_files/build_environment/cmake/tbb.cmake b/build_files/build_environment/cmake/tbb.cmake
index b006898e6fd..c08ecebc069 100644
--- a/build_files/build_environment/cmake/tbb.cmake
+++ b/build_files/build_environment/cmake/tbb.cmake
@@ -21,6 +21,8 @@ if(WIN32)
-DTBB_BUILD_TBBMALLOC=On
-DTBB_BUILD_TBBMALLOC_PROXY=On
-DTBB_BUILD_STATIC=Off
+ -DTBB_BUILD_TESTS=Off
+ -DCMAKE_DEBUG_POSTFIX=_debug
)
set(TBB_LIBRARY tbb)
set(TBB_STATIC_LIBRARY Off)
@@ -30,6 +32,7 @@ else()
-DTBB_BUILD_TBBMALLOC=On
-DTBB_BUILD_TBBMALLOC_PROXY=Off
-DTBB_BUILD_STATIC=On
+ -DTBB_BUILD_TESTS=Off
)
set(TBB_LIBRARY tbb_static)
set(TBB_STATIC_LIBRARY On)
@@ -42,7 +45,7 @@ ExternalProject_Add(external_tbb
URL_HASH ${TBB_HASH_TYPE}=${TBB_HASH}
PREFIX ${BUILD_DIR}/tbb
PATCH_COMMAND COMMAND ${CMAKE_COMMAND} -E copy ${PATCH_DIR}/cmakelists_tbb.txt ${BUILD_DIR}/tbb/src/external_tbb/CMakeLists.txt &&
- ${CMAKE_COMMAND} -E copy ${BUILD_DIR}/tbb/src/external_tbb/build/vs2013/version_string.ver ${BUILD_DIR}/tbb/src/external_tbb/src/tbb/version_string.ver &&
+ ${CMAKE_COMMAND} -E copy ${BUILD_DIR}/tbb/src/external_tbb/build/vs2013/version_string.ver ${BUILD_DIR}/tbb/src/external_tbb/build/version_string.ver.in &&
${PATCH_CMD} -p 1 -d ${BUILD_DIR}/tbb/src/external_tbb < ${PATCH_DIR}/tbb.diff
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${LIBDIR}/tbb ${DEFAULT_CMAKE_FLAGS} ${TBB_EXTRA_ARGS}
INSTALL_DIR ${LIBDIR}/tbb
@@ -53,17 +56,17 @@ if(WIN32)
ExternalProject_Add_Step(external_tbb after_install
# findtbb.cmake in some deps *NEEDS* to find tbb_debug.lib even if they are not going to use it
# to make that test pass, we place a copy with the right name in the lib folder.
- COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbb.lib ${HARVEST_TARGET}/tbb/lib/tbb_debug.lib
- COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbbmalloc.lib ${HARVEST_TARGET}/tbb/lib/tbbmalloc_debug.lib
- COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbb.dll ${HARVEST_TARGET}/tbb/lib/tbb_debug.dll
- COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbbmalloc.dll ${HARVEST_TARGET}/tbb/lib/tbbmalloc_debug.dll
+ COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbb.lib ${LIBDIR}/tbb/lib/tbb_debug.lib
+ COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbbmalloc.lib ${LIBDIR}/tbb/lib/tbbmalloc_debug.lib
+ COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/bin/tbb.dll ${LIBDIR}/tbb/bin/tbb_debug.dll
+ COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/bin/tbbmalloc.dll ${LIBDIR}/tbb/bin/tbbmalloc_debug.dll
# Normal collection of build artifacts
COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbb.lib ${HARVEST_TARGET}/tbb/lib/tbb.lib
- COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbb.dll ${HARVEST_TARGET}/tbb/lib/tbb.dll
+ COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/bin/tbb.dll ${HARVEST_TARGET}/tbb/bin/tbb.dll
COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbbmalloc.lib ${HARVEST_TARGET}/tbb/lib/tbbmalloc.lib
- COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbbmalloc.dll ${HARVEST_TARGET}/tbb/lib/tbbmalloc.dll
+ COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/bin/tbbmalloc.dll ${HARVEST_TARGET}/tbb/bin/tbbmalloc.dll
COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbbmalloc_proxy.lib ${HARVEST_TARGET}/tbb/lib/tbbmalloc_proxy.lib
- COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbbmalloc_proxy.dll ${HARVEST_TARGET}/tbb/lib/tbbmalloc_proxy.dll
+ COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/bin/tbbmalloc_proxy.dll ${HARVEST_TARGET}/tbb/bin/tbbmalloc_proxy.dll
COMMAND ${CMAKE_COMMAND} -E copy_directory ${LIBDIR}/tbb/include/ ${HARVEST_TARGET}/tbb/include/
DEPENDEES install
)
@@ -74,11 +77,12 @@ if(WIN32)
# to make that test pass, we place a copy with the right name in the lib folder.
COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbb_debug.lib ${LIBDIR}/tbb/lib/tbb.lib
# Normal collection of build artifacts
- COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbb_debug.lib ${HARVEST_TARGET}/tbb/lib/debug/tbb_debug.lib
- COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbb_debug.dll ${HARVEST_TARGET}/tbb/lib/debug/tbb_debug.dll
- COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbbmalloc_proxy.lib ${HARVEST_TARGET}/tbb/lib/tbbmalloc_proxy_debug.lib
- COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbbmalloc.dll ${HARVEST_TARGET}/tbb/lib/debug/tbbmalloc.dll
- COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbbmalloc_proxy.dll ${HARVEST_TARGET}/tbb/lib/debug/tbbmalloc_proxy.dll
+ COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbb_debug.lib ${HARVEST_TARGET}/tbb/lib/tbb_debug.lib
+ COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/bin/tbb_debug.dll ${HARVEST_TARGET}/tbb/bin/tbb_debug.dll
+ COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbbmalloc_debug.lib ${HARVEST_TARGET}/tbb/lib/tbbmalloc_debug.lib
+ COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbbmalloc_proxy_debug.lib ${HARVEST_TARGET}/tbb/lib/tbbmalloc_proxy_debug.lib
+ COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/bin/tbbmalloc_debug.dll ${HARVEST_TARGET}/tbb/bin/tbbmalloc_debug.dll
+ COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/bin/tbbmalloc_proxy_debug.dll ${HARVEST_TARGET}/tbb/bin/tbbmalloc_proxy_debug.dll
DEPENDEES install
)
endif()
diff --git a/build_files/build_environment/cmake/versions.cmake b/build_files/build_environment/cmake/versions.cmake
index 97da5d54d48..d93b8463b4b 100644
--- a/build_files/build_environment/cmake/versions.cmake
+++ b/build_files/build_environment/cmake/versions.cmake
@@ -43,7 +43,7 @@ set(JPEG_FILE libjpeg-turbo-${JPEG_VERSION}.tar.gz)
set(BOOST_VERSION 1.73.0)
set(BOOST_VERSION_NODOTS 1_73_0)
set(BOOST_VERSION_NODOTS_SHORT 1_73)
-set(BOOST_URI https://dl.bintray.com/boostorg/release/${BOOST_VERSION}/source/boost_${BOOST_VERSION_NODOTS}.tar.gz)
+set(BOOST_URI https://boostorg.jfrog.io/artifactory/main/release/${BOOST_VERSION}/source/boost_${BOOST_VERSION_NODOTS}.tar.gz)
set(BOOST_HASH 4036cd27ef7548b8d29c30ea10956196)
set(BOOST_HASH_TYPE MD5)
set(BOOST_FILE boost_${BOOST_VERSION_NODOTS}.tar.gz)
@@ -152,7 +152,7 @@ set(OPENCOLORIO_HASH 1a2e3478b6cd9a1549f24e1b2205e3f0)
set(OPENCOLORIO_HASH_TYPE MD5)
set(OPENCOLORIO_FILE OpenColorIO-${OPENCOLORIO_VERSION}.tar.gz)
-if(APPLE AND ("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64"))
+if(BLENDER_PLATFORM_ARM)
# Newer version required by ISPC with arm support.
set(LLVM_VERSION 11.0.1)
set(LLVM_URI https://github.com/llvm/llvm-project/releases/download/llvmorg-${LLVM_VERSION}/llvm-project-${LLVM_VERSION}.src.tar.xz)
@@ -297,10 +297,10 @@ set(OPENJPEG_HASH 63f5a4713ecafc86de51bfad89cc07bb788e9bba24ebbf0c4ca637621aadb6
set(OPENJPEG_HASH_TYPE SHA256)
set(OPENJPEG_FILE openjpeg-v${OPENJPEG_VERSION}.tar.gz)
-set(FFMPEG_VERSION 4.2.3)
+set(FFMPEG_VERSION 4.4)
set(FFMPEG_URI http://ffmpeg.org/releases/ffmpeg-${FFMPEG_VERSION}.tar.bz2)
-set(FFMPEG_HASH 695fad11f3baf27784e24cb0e977b65a)
-set(FFMPEG_HASH_TYPE MD5)
+set(FFMPEG_HASH 42093549751b582cf0f338a21a3664f52e0a9fbe0d238d3c992005e493607d0e)
+set(FFMPEG_HASH_TYPE SHA256)
set(FFMPEG_FILE ffmpeg-${FFMPEG_VERSION}.tar.bz2)
set(FFTW_VERSION 3.3.8)
@@ -398,11 +398,20 @@ set(LZMA_HASH 5117f930900b341493827d63aa910ff5e011e0b994197c3b71c08a20228a42df)
set(LZMA_HASH_TYPE SHA256)
set(LZMA_FILE xz-${LZMA_VERSION}.tar.bz2)
-set(SSL_VERSION 1.1.1g)
-set(SSL_URI https://www.openssl.org/source/openssl-${SSL_VERSION}.tar.gz)
-set(SSL_HASH ddb04774f1e32f0c49751e21b67216ac87852ceb056b75209af2443400636d46)
-set(SSL_HASH_TYPE SHA256)
-set(SSL_FILE openssl-${SSL_VERSION}.tar.gz)
+if(BLENDER_PLATFORM_ARM)
+ # Need at least 1.1.1i for aarch64 support (https://github.com/openssl/openssl/pull/13218)
+ set(SSL_VERSION 1.1.1i)
+ set(SSL_URI https://www.openssl.org/source/openssl-${SSL_VERSION}.tar.gz)
+ set(SSL_HASH e8be6a35fe41d10603c3cc635e93289ed00bf34b79671a3a4de64fcee00d5242)
+ set(SSL_HASH_TYPE SHA256)
+ set(SSL_FILE openssl-${SSL_VERSION}.tar.gz)
+else()
+ set(SSL_VERSION 1.1.1g)
+ set(SSL_URI https://www.openssl.org/source/openssl-${SSL_VERSION}.tar.gz)
+ set(SSL_HASH ddb04774f1e32f0c49751e21b67216ac87852ceb056b75209af2443400636d46)
+ set(SSL_HASH_TYPE SHA256)
+ set(SSL_FILE openssl-${SSL_VERSION}.tar.gz)
+endif()
set(SQLITE_VERSION 3.31.1)
set(SQLITE_URI https://www.sqlite.org/2018/sqlite-src-3240000.zip)
@@ -423,9 +432,9 @@ set(USD_HASH 1dd1e2092d085ed393c1f7c450a4155a)
set(USD_HASH_TYPE MD5)
set(USD_FILE usd-v${USD_VERSION}.tar.gz)
-set(OIDN_VERSION 1.3.0)
+set(OIDN_VERSION 1.4.0)
set(OIDN_URI https://github.com/OpenImageDenoise/oidn/releases/download/v${OIDN_VERSION}/oidn-${OIDN_VERSION}.src.tar.gz)
-set(OIDN_HASH 301a5a0958d375a942014df0679b9270)
+set(OIDN_HASH 421824019becc5b664a22a2b98332bc5)
set(OIDN_HASH_TYPE MD5)
set(OIDN_FILE oidn-${OIDN_VERSION}.src.tar.gz)
@@ -453,7 +462,7 @@ set(XR_OPENXR_SDK_HASH 0df6b2fd6045423451a77ff6bc3e1a75)
set(XR_OPENXR_SDK_HASH_TYPE MD5)
set(XR_OPENXR_SDK_FILE OpenXR-SDK-${XR_OPENXR_SDK_VERSION}.tar.gz)
-if(APPLE AND ("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64"))
+if(BLENDER_PLATFORM_ARM)
# Unreleased version with macOS arm support.
set(ISPC_URI https://github.com/ispc/ispc/archive/f5949c055eb9eeb93696978a3da4bfb3a6a30b35.zip)
set(ISPC_HASH d382fea18d01dbd0cd05d9e1ede36d7d)
diff --git a/build_files/build_environment/cmake/x264.cmake b/build_files/build_environment/cmake/x264.cmake
index a32f119d184..08d698cc3ad 100644
--- a/build_files/build_environment/cmake/x264.cmake
+++ b/build_files/build_environment/cmake/x264.cmake
@@ -20,24 +20,16 @@ if(WIN32)
set(X264_EXTRA_ARGS --enable-win32thread --cross-prefix=${MINGW_HOST}- --host=${MINGW_HOST})
endif()
-
-if(APPLE)
- if("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")
- set(X264_EXTRA_ARGS ${X264_EXTRA_ARGS} "--disable-asm")
- set(X264_CONFIGURE_ENV echo .)
- else()
- set(X264_CONFIGURE_ENV
- export AS=${LIBDIR}/nasm/bin/nasm
- )
- endif()
-else()
- set(X264_CONFIGURE_ENV echo .)
+if(BLENDER_PLATFORM_ARM)
+ set(X264_EXTRA_ARGS ${X264_EXTRA_ARGS} "--disable-asm")
endif()
-if(UNIX AND NOT APPLE)
+if((APPLE AND NOT BLENDER_PLATFORM_ARM) OR (UNIX AND NOT APPLE))
set(X264_CONFIGURE_ENV
export AS=${LIBDIR}/nasm/bin/nasm
)
+else()
+ set(X264_CONFIGURE_ENV echo .)
endif()
ExternalProject_Add(external_x264
diff --git a/build_files/build_environment/install_deps.sh b/build_files/build_environment/install_deps.sh
index 7cd21b2885c..808e154955d 100755
--- a/build_files/build_environment/install_deps.sh
+++ b/build_files/build_environment/install_deps.sh
@@ -37,7 +37,7 @@ if [ $USE_DEBUG_TRAP -ne 0 ]; then
trap 'err_report $LINENO' ERR
fi
-# Noisy, show every line that runs with it's line number.
+# Noisy, show every line that runs with its line number.
if [ $USE_DEBUG_LOG -ne 0 ]; then
PS4='\e[0;33m$(printf %4d ${LINENO}):\e\033[0m '
set -x
@@ -553,18 +553,18 @@ EMBREE_FORCE_BUILD=false
EMBREE_FORCE_REBUILD=false
EMBREE_SKIP=false
-OIDN_VERSION="1.3.0"
-OIDN_VERSION_SHORT="1.3"
-OIDN_VERSION_MIN="1.3.0"
-OIDN_VERSION_MAX="1.4"
+OIDN_VERSION="1.4.0"
+OIDN_VERSION_SHORT="1.4"
+OIDN_VERSION_MIN="1.4.0"
+OIDN_VERSION_MAX="1.5"
OIDN_FORCE_BUILD=false
OIDN_FORCE_REBUILD=false
OIDN_SKIP=false
ISPC_VERSION="1.14.1"
-FFMPEG_VERSION="4.2.3"
-FFMPEG_VERSION_SHORT="4.2"
+FFMPEG_VERSION="4.4"
+FFMPEG_VERSION_SHORT="4.4"
FFMPEG_VERSION_MIN="3.0"
FFMPEG_VERSION_MAX="5.0"
FFMPEG_FORCE_BUILD=false
@@ -1797,6 +1797,10 @@ compile_OCIO() {
cmake_d="$cmake_d -D OCIO_BUILD_PYTHON=OFF"
cmake_d="$cmake_d -D OCIO_BUILD_GPU_TESTS=OFF"
+ if [ $(uname -m) == "aarch64" ]; then
+ cmake_d="$cmake_d -D OCIO_USE_SSE=OFF"
+ fi
+
if file /bin/cp | grep -q '32-bit'; then
cflags="-fPIC -m32 -march=i686"
else
@@ -2059,7 +2063,10 @@ compile_OIIO() {
cmake_d="$cmake_d -D CMAKE_INSTALL_PREFIX=$_inst"
cmake_d="$cmake_d -D STOP_ON_WARNING=OFF"
cmake_d="$cmake_d -D LINKSTATIC=OFF"
- cmake_d="$cmake_d -D USE_SIMD=sse2"
+
+ if [ $(uname -m) != "aarch64" ]; then
+ cmake_d="$cmake_d -D USE_SIMD=sse2"
+ fi
cmake_d="$cmake_d -D OPENEXR_VERSION=$OPENEXR_VERSION"
@@ -2079,7 +2086,7 @@ compile_OIIO() {
cmake_d="$cmake_d -D USE_OPENCV=OFF"
cmake_d="$cmake_d -D BUILD_TESTING=OFF"
cmake_d="$cmake_d -D OIIO_BUILD_TESTS=OFF"
- cmake_d="$cmake_d -D OIIO_BUILD_TOOLS=ON"
+ cmake_d="$cmake_d -D OIIO_BUILD_TOOLS=OFF"
cmake_d="$cmake_d -D TXT2MAN="
#cmake_d="$cmake_d -D CMAKE_EXPORT_COMPILE_COMMANDS=ON"
#cmake_d="$cmake_d -D CMAKE_VERBOSE_MAKEFILE=ON"
@@ -2209,10 +2216,15 @@ compile_LLVM() {
mkdir build
cd build
+ LLVM_TARGETS="X86"
+ if [ $(uname -m) == "aarch64" ]; then
+ LLVM_TARGETS="AArch64"
+ fi
+
cmake_d="-D CMAKE_BUILD_TYPE=Release"
cmake_d="$cmake_d -D CMAKE_INSTALL_PREFIX=$_inst"
cmake_d="$cmake_d -D LLVM_ENABLE_FFI=ON"
- cmake_d="$cmake_d -D LLVM_TARGETS_TO_BUILD=X86"
+ cmake_d="$cmake_d -D LLVM_TARGETS_TO_BUILD=$LLVM_TARGETS"
cmake_d="$cmake_d -D LLVM_ENABLE_TERMINFO=OFF"
if [ -d $_FFI_INCLUDE_DIR ]; then
@@ -2329,13 +2341,16 @@ compile_OSL() {
cmake_d="$cmake_d -D STOP_ON_WARNING=OFF"
cmake_d="$cmake_d -D OSL_BUILD_PLUGINS=OFF"
cmake_d="$cmake_d -D OSL_BUILD_TESTS=OFF"
- cmake_d="$cmake_d -D USE_SIMD=sse2"
cmake_d="$cmake_d -D USE_LLVM_BITCODE=OFF"
cmake_d="$cmake_d -D USE_PARTIO=OFF"
cmake_d="$cmake_d -D OSL_BUILD_MATERIALX=OFF"
cmake_d="$cmake_d -D USE_QT=OFF"
cmake_d="$cmake_d -D USE_PYTHON=OFF"
+ if [ $(uname -m) != "aarch64" ]; then
+ cmake_d="$cmake_d -D USE_SIMD=sse2"
+ fi
+
cmake_d="$cmake_d -D CMAKE_CXX_STANDARD=14"
#~ cmake_d="$cmake_d -D ILMBASE_VERSION=$ILMBASE_VERSION"
diff --git a/build_files/build_environment/patches/cmake/modules/FindIlmBase.cmake b/build_files/build_environment/patches/cmake/modules/FindIlmBase.cmake
index 731cf514a91..ffd70ac155c 100644
--- a/build_files/build_environment/patches/cmake/modules/FindIlmBase.cmake
+++ b/build_files/build_environment/patches/cmake/modules/FindIlmBase.cmake
@@ -20,7 +20,7 @@
# ILMBASE_LIBRARIES - list of libraries to link against when using IlmBase.
# ILMBASE_FOUND - True if IlmBase was found.
-# Other standarnd issue macros
+# Other standard issue macros
include(FindPackageHandleStandardArgs)
include(FindPackageMessage)
include(SelectLibraryConfigurations)
diff --git a/build_files/build_environment/patches/cmake/modules/FindOpenEXR.cmake b/build_files/build_environment/patches/cmake/modules/FindOpenEXR.cmake
index a0390e3311a..330038f022d 100644
--- a/build_files/build_environment/patches/cmake/modules/FindOpenEXR.cmake
+++ b/build_files/build_environment/patches/cmake/modules/FindOpenEXR.cmake
@@ -22,7 +22,7 @@
# These are defined by the FindIlmBase module.
# OPENEXR_FOUND - True if OpenEXR was found.
-# Other standarnd issue macros
+# Other standard issue macros
include(SelectLibraryConfigurations)
include(FindPackageHandleStandardArgs)
include(FindPackageMessage)
diff --git a/build_files/build_environment/patches/cmakelists_tbb.txt b/build_files/build_environment/patches/cmakelists_tbb.txt
index 4032e5d6f83..e05f27afdd6 100644
--- a/build_files/build_environment/patches/cmakelists_tbb.txt
+++ b/build_files/build_environment/patches/cmakelists_tbb.txt
@@ -1,5 +1,32 @@
-cmake_minimum_required (VERSION 2.8)
-project(tbb CXX)
+cmake_minimum_required(VERSION 3.1 FATAL_ERROR)
+
+if (POLICY CMP0048)
+ # cmake warns if loaded from a min-3.0-required parent dir, so silence the warning:
+ cmake_policy(SET CMP0048 NEW)
+endif()
+
+project (tbb CXX)
+
+include(CheckCXXCompilerFlag)
+include(CheckCXXSourceRuns)
+
+if(POLICY CMP0058)
+ cmake_policy(SET CMP0058 NEW)
+endif()
+
+if(POLICY CMP0068)
+ cmake_policy(SET CMP0068 NEW)
+endif()
+
+if (POLICY CMP0078)
+ # swig standard target names
+ cmake_policy(SET CMP0078 NEW)
+endif ()
+
+if (POLICY CMP0086)
+ # UseSWIG honors SWIG_MODULE_NAME via -module flag
+ cmake_policy(SET CMP0086 NEW)
+endif ()
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
message(STATUS "Setting build type to 'Release' as none was specified.")
@@ -8,12 +35,36 @@ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
"MinSizeRel" "RelWithDebInfo")
endif()
-include_directories(include src src/rml/include )
+if(NOT TBB_INSTALL_RUNTIME_DIR)
+ set(TBB_INSTALL_RUNTIME_DIR bin)
+endif()
+if(NOT TBB_INSTALL_LIBRARY_DIR)
+ set(TBB_INSTALL_LIBRARY_DIR lib)
+endif()
+if(NOT TBB_INSTALL_ARCHIVE_DIR)
+ set(TBB_INSTALL_ARCHIVE_DIR lib)
+endif()
+if(NOT TBB_INSTALL_INCLUDE_DIR)
+ set(TBB_INSTALL_INCLUDE_DIR include)
+endif()
+if(NOT TBB_CMAKE_PACKAGE_INSTALL_DIR)
+ set(TBB_CMAKE_PACKAGE_INSTALL_DIR lib/cmake/tbb)
+endif()
+
+include_directories(include src src/rml/include ${CMAKE_CURRENT_BINARY_DIR})
option(TBB_BUILD_SHARED "Build TBB shared library" ON)
option(TBB_BUILD_STATIC "Build TBB static library" ON)
option(TBB_BUILD_TBBMALLOC "Build TBB malloc library" ON)
option(TBB_BUILD_TBBMALLOC_PROXY "Build TBB malloc proxy library" ON)
+option(TBB_BUILD_TESTS "Build TBB tests and enable testing infrastructure" ON)
+option(TBB_NO_DATE "Do not save the configure date in the version string" OFF)
+option(TBB_BUILD_PYTHON "Build TBB Python bindings" OFF)
+option(TBB_SET_SOVERSION "Set the SOVERSION (shared library build version suffix)?" OFF)
+
+# When this repository is part of a larger build system of a parent project
+# we may not want TBB to set up default installation targets
+option(TBB_INSTALL_TARGETS "Include build targets for 'make install'" ON)
if(APPLE)
set(CMAKE_MACOSX_RPATH ON)
@@ -39,66 +90,143 @@ set(tbbmalloc_proxy_src
src/tbbmalloc/proxy.cpp
src/tbbmalloc/tbb_function_replacement.cpp)
-if (NOT APPLE)
- add_definitions(-DDO_ITT_NOTIFY)
-else()
+add_library (tbb_interface INTERFACE)
+add_definitions(-DTBB_SUPPRESS_DEPRECATED_MESSAGES=1)
+
+if (CMAKE_SYSTEM_PROCESSOR MATCHES "(i386|x86_64)")
+ if (NOT APPLE AND NOT MINGW)
+ target_compile_definitions(tbb_interface INTERFACE DO_ITT_NOTIFY)
+ endif()
+endif()
+
+if (APPLE)
# Disable annoying "has no symbols" warnings
- set(CMAKE_C_ARCHIVE_CREATE "<CMAKE_AR> Scr <TARGET> <LINK_FLAGS> <OBJECTS>")
- set(CMAKE_CXX_ARCHIVE_CREATE "<CMAKE_AR> Scr <TARGET> <LINK_FLAGS> <OBJECTS>")
- set(CMAKE_C_ARCHIVE_FINISH "<CMAKE_RANLIB> -no_warning_for_no_symbols -c <TARGET>")
- set(CMAKE_CXX_ARCHIVE_FINISH "<CMAKE_RANLIB> -no_warning_for_no_symbols -c <TARGET>")
+ set(CMAKE_C_ARCHIVE_CREATE "<CMAKE_AR> Scr <TARGET> <LINK_FLAGS> <OBJECTS>")
+ set(CMAKE_CXX_ARCHIVE_CREATE "<CMAKE_AR> Scr <TARGET> <LINK_FLAGS> <OBJECTS>")
+ set(CMAKE_C_ARCHIVE_FINISH "<CMAKE_RANLIB> -no_warning_for_no_symbols -c <TARGET>")
+ set(CMAKE_CXX_ARCHIVE_FINISH "<CMAKE_RANLIB> -no_warning_for_no_symbols -c <TARGET>")
endif()
-if (UNIX)
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -DUSE_PTHREAD")
- if(NOT CMAKE_CXX_FLAGS MATCHES "-mno-rtm")
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mrtm")
- endif()
- if (APPLE)
- set(ARCH_PREFIX "mac")
- else()
- set(ARCH_PREFIX "lin")
- endif()
- if (CMAKE_SIZEOF_VOID_P EQUAL 8)
- set(ARCH_PREFIX "${ARCH_PREFIX}64")
+macro(CHECK_CXX_COMPILER_AND_LINKER_FLAGS _RESULT _CXX_FLAGS _LINKER_FLAGS)
+ set(CMAKE_REQUIRED_FLAGS ${_CXX_FLAGS})
+ set(CMAKE_REQUIRED_LIBRARIES ${_LINKER_FLAGS})
+ set(CMAKE_REQUIRED_QUIET TRUE)
+ check_cxx_source_runs("#include <iostream>\nint main(int argc, char **argv) { std::cout << \"test\"; return 0; }" ${_RESULT})
+ set(CMAKE_REQUIRED_FLAGS "")
+ set(CMAKE_REQUIRED_LIBRARIES "")
+endmacro()
+
+# Prefer libc++ in conjunction with Clang
+if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+ if (CMAKE_CXX_FLAGS MATCHES "-stdlib=libc\\+\\+")
+ message(STATUS "TBB: using libc++.")
else()
- set(ARCH_PREFIX "${ARCH_PREFIX}32")
+ CHECK_CXX_COMPILER_AND_LINKER_FLAGS(HAS_LIBCPP "-stdlib=libc++" "-stdlib=libc++")
+ if (HAS_LIBCPP)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++ -D_LIBCPP_VERSION")
+ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++")
+ set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -stdlib=libc++")
+ message(STATUS "TBB: using libc++.")
+ else()
+ message(STATUS "TBB: NOT using libc++.")
+ endif()
endif()
- set(ENABLE_RTTI "-frtti -fexceptions ")
- set(DISABLE_RTTI "-fno-rtti -fno-exceptions ")
+endif()
+
+set (CMAKE_CXX_STANDARD 11)
+
+if (UNIX)
+ target_compile_definitions(tbb_interface INTERFACE USE_PTHREAD)
+
+ check_cxx_compiler_flag ("-mrtm -Werror" SUPPORTS_MRTM)
+ if (SUPPORTS_MRTM)
+ target_compile_options(tbb_interface INTERFACE "-mrtm")
+ endif ()
+
elseif(WIN32)
- cmake_minimum_required (VERSION 3.1)
- enable_language(ASM_MASM)
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /GS- /Zc:wchar_t /Zc:forScope /DUSE_WINTHREAD")
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D_CRT_SECURE_NO_DEPRECATE /D_WIN32_WINNT=0x0600 /volatile:iso")
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4267 /wd4800 /wd4146 /wd4244 /wd4018")
-
- if (CMAKE_SIZEOF_VOID_P EQUAL 8)
- list(APPEND tbb_src src/tbb/intel64-masm/atomic_support.asm
- src/tbb/intel64-masm/itsx.asm src/tbb/intel64-masm/intel64_misc.asm)
- list(APPEND tbbmalloc_src src/tbb/intel64-masm/atomic_support.asm)
- set(CMAKE_ASM_MASM_FLAGS "/DEM64T=1")
- set(ARCH_PREFIX "win64")
- else()
- list(APPEND tbb_src src/tbb/ia32-masm/atomic_support.asm
- src/tbb/ia32-masm/itsx.asm src/tbb/ia32-masm/lock_byte.asm)
- list(APPEND tbbmalloc_src src/tbb/ia32-masm/atomic_support.asm)
- set(ARCH_PREFIX "win32")
- endif()
+ target_compile_definitions(tbb_interface INTERFACE USE_WINTHREAD _WIN32_WINNT=0x0600)
+ if (MSVC)
+ enable_language(ASM_MASM)
+ target_compile_options(tbb_interface INTERFACE /GS- /Zc:wchar_t /Zc:forScope)
+ check_cxx_compiler_flag ("/volatile:iso" SUPPORTS_VOLATILE_FLAG)
+ if (SUPPORTS_VOLATILE_FLAG)
+ target_compile_options(tbb_interface INTERFACE /volatile:iso)
+ endif ()
+ target_compile_options(tbb_interface INTERFACE $<$<COMPILE_LANGUAGE:CXX>:/wd4267 /wd4800 /wd4146 /wd4244 /wd4577 /wd4018>)
+ if (NOT CMAKE_SIZEOF_VOID_P)
+ message(FATAL_ERROR "'CMAKE_SIZEOF_VOID_P' is undefined. Please delete your build directory and rerun CMake again!")
+ endif()
+
+ if (CMAKE_SIZEOF_VOID_P EQUAL 8)
+ list(APPEND tbb_src src/tbb/intel64-masm/atomic_support.asm
+ src/tbb/intel64-masm/itsx.asm src/tbb/intel64-masm/intel64_misc.asm)
+ list(APPEND tbbmalloc_src src/tbb/intel64-masm/atomic_support.asm)
+ set(CMAKE_ASM_MASM_FLAGS "/DEM64T=1 ${CMAKE_ASM_MASM_FLAGS}")
+ else()
+ list(APPEND tbb_src src/tbb/ia32-masm/atomic_support.asm
+ src/tbb/ia32-masm/itsx.asm src/tbb/ia32-masm/lock_byte.asm)
+ # Enable SAFESEH feature for assembly (x86 builds only).
+ set(CMAKE_ASM_MASM_FLAGS "/safeseh ${CMAKE_ASM_MASM_FLAGS}")
+ endif()
+ elseif (MINGW)
+ target_compile_options(tbb_interface INTERFACE "-mthreads")
+ endif ()
+endif()
+
+if (MSVC)
set(ENABLE_RTTI "/EHsc /GR ")
set(DISABLE_RTTI "/EHs- /GR- ")
+elseif (UNIX)
+ set(ENABLE_RTTI "-frtti -fexceptions ")
+ set(DISABLE_RTTI "-fno-rtti -fno-exceptions ")
+endif ()
+
+##--------
+# - Added TBB_USE_GLIBCXX_VERSION macro to specify the version of GNU
+# libstdc++ when it cannot be properly recognized, e.g. when used
+# with Clang on Linux* OS. Inspired by a contribution from David A.
+if (NOT TBB_USE_GLIBCXX_VERSION AND UNIX AND NOT APPLE)
+ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
+ # using Clang
+ string(REPLACE "." "0" TBB_USE_GLIBCXX_VERSION ${CMAKE_CXX_COMPILER_VERSION})
+ endif()
endif()
+if (TBB_USE_GLIBCXX_VERSION)
+ target_compile_definitions(tbb_interface INTERFACE TBB_USE_GLIBCXX_VERSION=${TBB_USE_GLIBCXX_VERSION})
+endif()
+
+##-------
+
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
- include(CheckCXXCompilerFlag)
- check_cxx_compiler_flag("-flifetime-dse=1" SUPPORTS_FLIFETIME)
- if (SUPPORTS_FLIFETIME)
- add_definitions(-flifetime-dse=1)
- endif()
+ check_cxx_compiler_flag ("-flifetime-dse=1" SUPPORTS_FLIFETIME)
+ if (SUPPORTS_FLIFETIME)
+ target_compile_options(tbb_interface INTERFACE -flifetime-dse=1)
+ endif()
endif()
# Linker export definitions
-if (WIN32)
+if (APPLE)
+ set (ARCH_PREFIX "mac")
+elseif(WIN32)
+ set (ARCH_PREFIX "win")
+else()
+ set (ARCH_PREFIX "lin")
+endif()
+
+if (CMAKE_SIZEOF_VOID_P EQUAL 8)
+ set(ARCH_PREFIX "${ARCH_PREFIX}64")
+else()
+ set(ARCH_PREFIX "${ARCH_PREFIX}32")
+endif()
+
+if (MINGW)
+ set (ARCH_PREFIX "${ARCH_PREFIX}-gcc")
+ # there's no win32-gcc-tbb-export.def, use lin32-tbb-export.def
+ execute_process (COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/lin32-tbb-export.def ${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/win32-gcc-tbb-export.def)
+endif()
+
+if (MSVC)
add_custom_command(OUTPUT tbb.def
COMMAND ${CMAKE_CXX_COMPILER} /TC /EP ${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/${ARCH_PREFIX}-tbb-export.def -I ${CMAKE_CURRENT_SOURCE_DIR}/include > tbb.def
MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/${ARCH_PREFIX}-tbb-export.def
@@ -110,18 +238,15 @@ if (WIN32)
MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/src/tbbmalloc/${ARCH_PREFIX}-tbbmalloc-export.def
COMMENT "Preprocessing tbbmalloc.def"
)
- list(APPEND tbb_src ${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/tbb_resource.rc)
- list(APPEND tbbmalloc_src ${CMAKE_CURRENT_SOURCE_DIR}/src/tbbmalloc/tbbmalloc.rc)
- list(APPEND tbbmalloc_proxy_src ${CMAKE_CURRENT_SOURCE_DIR}/src/tbbmalloc/tbbmalloc.rc)
else()
add_custom_command(OUTPUT tbb.def
- COMMAND ${CMAKE_CXX_COMPILER} -xc++ -E ${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/${ARCH_PREFIX}-tbb-export.def -I ${CMAKE_CURRENT_SOURCE_DIR}/include -o tbb.def
+ COMMAND ${CMAKE_CXX_COMPILER} -xc++ -std=c++11 -E ${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/${ARCH_PREFIX}-tbb-export.def -I ${CMAKE_CURRENT_SOURCE_DIR}/include -o tbb.def
MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/${ARCH_PREFIX}-tbb-export.def
COMMENT "Preprocessing tbb.def"
)
add_custom_command(OUTPUT tbbmalloc.def
- COMMAND ${CMAKE_CXX_COMPILER} -xc++ -E ${CMAKE_CURRENT_SOURCE_DIR}/src/tbbmalloc/${ARCH_PREFIX}-tbbmalloc-export.def -I ${CMAKE_CURRENT_SOURCE_DIR}/include -o tbbmalloc.def
+ COMMAND ${CMAKE_CXX_COMPILER} -xc++ -std=c++11 -E ${CMAKE_CURRENT_SOURCE_DIR}/src/tbbmalloc/${ARCH_PREFIX}-tbbmalloc-export.def -I ${CMAKE_CURRENT_SOURCE_DIR}/include -o tbbmalloc.def
MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/src/tbbmalloc/${ARCH_PREFIX}-tbbmalloc-export.def
COMMENT "Preprocessing tbbmalloc.def"
)
@@ -132,34 +257,80 @@ add_custom_target(tbb_def_files DEPENDS tbb.def tbbmalloc.def)
# TBB library
if (TBB_BUILD_STATIC)
add_library(tbb_static STATIC ${tbb_src})
- set_property(TARGET tbb_static APPEND PROPERTY COMPILE_DEFINITIONS "__TBB_BUILD=1")
- set_property(TARGET tbb_static APPEND PROPERTY COMPILE_DEFINITIONS "__TBB_SOURCE_DIRECTLY_INCLUDED=1")
+ target_link_libraries(tbb_static PRIVATE tbb_interface)
+ target_include_directories(tbb_static INTERFACE "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>" "$<INSTALL_INTERFACE:${TBB_INSTALL_INCLUDE_DIR}>")
set_property(TARGET tbb_static APPEND_STRING PROPERTY COMPILE_FLAGS ${ENABLE_RTTI})
- install(TARGETS tbb_static ARCHIVE DESTINATION lib)
+ if (TBB_INSTALL_TARGETS)
+ install(TARGETS tbb_static ARCHIVE DESTINATION ${TBB_INSTALL_ARCHIVE_DIR})
+ endif()
+
+ target_compile_definitions(tbb_static
+ PRIVATE
+ -D__TBB_BUILD=1
+ -D__TBB_DYNAMIC_LOAD_ENABLED=0
+ -D__TBB_SOURCE_DIRECTLY_INCLUDED=1)
+
+ if (MSVC)
+ target_compile_definitions(tbb_static
+ PUBLIC -D__TBB_NO_IMPLICIT_LINKAGE=1
+ PRIVATE -D_CRT_SECURE_NO_WARNINGS)
+ endif()
+
+ if (UNIX AND NOT APPLE)
+ target_link_libraries(tbb_static PUBLIC pthread dl)
+ endif()
endif()
if (TBB_BUILD_SHARED)
add_library(tbb SHARED ${tbb_src})
- set_property(TARGET tbb APPEND PROPERTY COMPILE_DEFINITIONS "__TBB_BUILD=1")
+ target_link_libraries(tbb PRIVATE tbb_interface)
+ target_include_directories(tbb INTERFACE "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>" "$<INSTALL_INTERFACE:${TBB_INSTALL_INCLUDE_DIR}>")
set_property(TARGET tbb APPEND_STRING PROPERTY COMPILE_FLAGS ${ENABLE_RTTI})
+ if (TBB_SET_SOVERSION)
+ set_property(TARGET tbb PROPERTY SOVERSION 2)
+ endif ()
+
+ target_compile_definitions(tbb
+ PRIVATE -D__TBB_BUILD=1)
+
+ if (MSVC)
+ target_compile_definitions(tbb
+ PUBLIC -D__TBB_NO_IMPLICIT_LINKAGE=1
+ PRIVATE -D_CRT_SECURE_NO_WARNINGS)
+ endif()
+
add_dependencies(tbb tbb_def_files)
+
if (APPLE)
- set_property(TARGET tbb APPEND PROPERTY LINK_FLAGS "-Wl,-exported_symbols_list,${CMAKE_CURRENT_BINARY_DIR}/tbb.def")
- elseif(UNIX)
- set_property(TARGET tbb APPEND PROPERTY LINK_FLAGS "-Wl,-version-script,${CMAKE_CURRENT_BINARY_DIR}/tbb.def")
- elseif(WIN32)
- set_property(TARGET tbb APPEND PROPERTY LINK_FLAGS "/DEF:${CMAKE_CURRENT_BINARY_DIR}/tbb.def")
-
+ set_property(TARGET tbb APPEND PROPERTY LINK_FLAGS "-Wl,-exported_symbols_list,\"${CMAKE_CURRENT_BINARY_DIR}/tbb.def\"")
+ elseif (MSVC)
+ set_property(TARGET tbb APPEND PROPERTY LINK_FLAGS "/DEF:\"${CMAKE_CURRENT_BINARY_DIR}/tbb.def\"")
+ else ()
+ set_property(TARGET tbb APPEND PROPERTY LINK_FLAGS "-Wl,-version-script,\"${CMAKE_CURRENT_BINARY_DIR}/tbb.def\"")
endif()
- install(TARGETS tbb DESTINATION lib)
- if(WIN32)
- set_target_properties(tbb PROPERTIES OUTPUT_NAME "tbb$<$<CONFIG:Debug>:_debug>")
+
+ if (TBB_INSTALL_TARGETS)
+ install(TARGETS tbb EXPORT TBB
+ LIBRARY DESTINATION ${TBB_INSTALL_LIBRARY_DIR}
+ ARCHIVE DESTINATION ${TBB_INSTALL_ARCHIVE_DIR}
+ RUNTIME DESTINATION ${TBB_INSTALL_RUNTIME_DIR})
+ if (MSVC)
+ install(FILES $<TARGET_PDB_FILE:tbb> DESTINATION ${TBB_INSTALL_RUNTIME_DIR} OPTIONAL)
+ endif()
+ endif()
+
+ if (UNIX AND NOT APPLE)
+ target_link_libraries(tbb PUBLIC pthread dl)
endif()
endif()
-if(CMAKE_COMPILER_IS_GNUCC)
+
+if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
# Quench a warning on GCC
set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/governor.cpp COMPILE_FLAGS "-Wno-missing-field-initializers ")
+elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
+ # Quench a warning on Clang
+ set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/itt_notify.cpp COMPILE_FLAGS "-Wno-varargs ")
elseif(MSVC)
# Quench a warning on MSVC
set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/scheduler.cpp COMPILE_FLAGS "/wd4458 ")
@@ -169,24 +340,50 @@ if(TBB_BUILD_TBBMALLOC)
# TBB malloc library
if (TBB_BUILD_STATIC)
add_library(tbbmalloc_static STATIC ${tbbmalloc_static_src})
+ target_link_libraries(tbbmalloc_static PRIVATE tbb_interface)
set_property(TARGET tbbmalloc_static APPEND PROPERTY COMPILE_DEFINITIONS "__TBBMALLOC_BUILD=1")
+ set_property(TARGET tbbmalloc_static APPEND PROPERTY COMPILE_DEFINITIONS "__TBB_DYNAMIC_LOAD_ENABLED=0")
+ set_property(TARGET tbbmalloc_static APPEND PROPERTY COMPILE_DEFINITIONS "__TBB_SOURCE_DIRECTLY_INCLUDED=1")
set_property(TARGET tbbmalloc_static APPEND_STRING PROPERTY COMPILE_FLAGS ${DISABLE_RTTI})
- install(TARGETS tbbmalloc_static ARCHIVE DESTINATION lib)
+ if (MSVC)
+ target_compile_definitions(tbbmalloc_static PUBLIC __TBB_NO_IMPLICIT_LINKAGE=1 __TBBMALLOC_NO_IMPLICIT_LINKAGE=1)
+ endif()
+ if (TBB_INSTALL_TARGETS)
+ install(TARGETS tbbmalloc_static ARCHIVE DESTINATION ${TBB_INSTALL_ARCHIVE_DIR})
+ endif()
endif()
if (TBB_BUILD_SHARED)
add_library(tbbmalloc SHARED ${tbbmalloc_src})
+ target_link_libraries(tbbmalloc PRIVATE tbb_interface)
set_property(TARGET tbbmalloc APPEND PROPERTY COMPILE_DEFINITIONS "__TBBMALLOC_BUILD=1")
set_property(TARGET tbbmalloc APPEND_STRING PROPERTY COMPILE_FLAGS ${DISABLE_RTTI})
+ if (TBB_SET_SOVERSION)
+ set_property(TARGET tbbmalloc PROPERTY SOVERSION 2)
+ endif ()
add_dependencies(tbbmalloc tbb_def_files)
if (APPLE)
- set_property(TARGET tbbmalloc APPEND PROPERTY LINK_FLAGS "-Wl,-exported_symbols_list,${CMAKE_CURRENT_BINARY_DIR}/tbbmalloc.def")
- elseif(UNIX)
- set_property(TARGET tbbmalloc APPEND PROPERTY LINK_FLAGS "-Wl,-version-script,${CMAKE_CURRENT_BINARY_DIR}/tbbmalloc.def")
- elseif(WIN32)
- set_property(TARGET tbbmalloc APPEND PROPERTY LINK_FLAGS "/DEF:${CMAKE_CURRENT_BINARY_DIR}/tbbmalloc.def")
+ set_property(TARGET tbbmalloc APPEND PROPERTY LINK_FLAGS "-Wl,-exported_symbols_list,\"${CMAKE_CURRENT_BINARY_DIR}/tbbmalloc.def\"")
+ elseif (MSVC)
+ set_property(TARGET tbbmalloc APPEND PROPERTY LINK_FLAGS "/DEF:\"${CMAKE_CURRENT_BINARY_DIR}/tbbmalloc.def\"")
+ else ()
+ set_property(TARGET tbbmalloc APPEND PROPERTY LINK_FLAGS "-Wl,-version-script,\"${CMAKE_CURRENT_BINARY_DIR}/tbbmalloc.def\"")
+ endif()
+ if (MSVC)
+ target_compile_definitions(tbbmalloc PUBLIC __TBB_NO_IMPLICIT_LINKAGE=1 __TBBMALLOC_NO_IMPLICIT_LINKAGE=1)
+ endif()
+ if (TBB_INSTALL_TARGETS)
+ install(TARGETS tbbmalloc EXPORT TBB
+ LIBRARY DESTINATION ${TBB_INSTALL_LIBRARY_DIR}
+ ARCHIVE DESTINATION ${TBB_INSTALL_ARCHIVE_DIR}
+ RUNTIME DESTINATION ${TBB_INSTALL_RUNTIME_DIR})
+ if (MSVC)
+ install(FILES $<TARGET_PDB_FILE:tbbmalloc> DESTINATION ${TBB_INSTALL_RUNTIME_DIR} OPTIONAL)
+ endif()
+ endif()
+ if (UNIX AND NOT APPLE)
+ target_link_libraries(tbbmalloc PUBLIC pthread dl)
endif()
- install(TARGETS tbbmalloc DESTINATION lib)
endif()
endif()
@@ -194,19 +391,298 @@ if(TBB_BUILD_TBBMALLOC_PROXY)
# TBB malloc proxy library
if (TBB_BUILD_STATIC)
add_library(tbbmalloc_proxy_static STATIC ${tbbmalloc_proxy_src})
+ target_link_libraries(tbbmalloc_proxy_static PRIVATE tbb_interface)
set_property(TARGET tbbmalloc_proxy_static APPEND PROPERTY COMPILE_DEFINITIONS "__TBBMALLOC_BUILD=1")
+ set_property(TARGET tbbmalloc_proxy_static APPEND PROPERTY COMPILE_DEFINITIONS "__TBB_DYNAMIC_LOAD_ENABLED=0")
+ set_property(TARGET tbbmalloc_proxy_static APPEND PROPERTY COMPILE_DEFINITIONS "__TBB_SOURCE_DIRECTLY_INCLUDED=1")
set_property(TARGET tbbmalloc_proxy_static APPEND_STRING PROPERTY COMPILE_FLAGS ${DISABLE_RTTI})
- link_libraries(tbbmalloc_proxy_static tbbmalloc)
- install(TARGETS tbbmalloc_proxy_static ARCHIVE DESTINATION lib)
+ if (TBB_INSTALL_TARGETS)
+ install(TARGETS tbbmalloc_proxy_static ARCHIVE DESTINATION ${TBB_INSTALL_ARCHIVE_DIR})
+ endif()
endif()
if (TBB_BUILD_SHARED)
add_library(tbbmalloc_proxy SHARED ${tbbmalloc_proxy_src})
+ target_link_libraries(tbbmalloc_proxy PRIVATE tbb_interface)
set_property(TARGET tbbmalloc_proxy APPEND PROPERTY COMPILE_DEFINITIONS "__TBBMALLOC_BUILD=1")
set_property(TARGET tbbmalloc_proxy APPEND_STRING PROPERTY COMPILE_FLAGS ${DISABLE_RTTI})
- target_link_libraries(tbbmalloc_proxy tbbmalloc)
- install(TARGETS tbbmalloc_proxy DESTINATION lib)
+ if (TBB_SET_SOVERSION)
+ set_property(TARGET tbbmalloc_proxy PROPERTY SOVERSION 2)
+ endif ()
+ target_link_libraries(tbbmalloc_proxy PUBLIC tbbmalloc)
+ if (TBB_INSTALL_TARGETS)
+ install(TARGETS tbbmalloc_proxy EXPORT TBB
+ LIBRARY DESTINATION ${TBB_INSTALL_LIBRARY_DIR}
+ ARCHIVE DESTINATION ${TBB_INSTALL_ARCHIVE_DIR}
+ RUNTIME DESTINATION ${TBB_INSTALL_RUNTIME_DIR})
+ if (MSVC)
+ install(FILES $<TARGET_PDB_FILE:tbbmalloc_proxy> DESTINATION ${TBB_INSTALL_RUNTIME_DIR} OPTIONAL)
+ endif()
+ endif()
+ if (UNIX AND NOT APPLE)
+ target_link_libraries(tbbmalloc_proxy PUBLIC pthread dl)
+ endif()
endif()
endif()
-install(DIRECTORY include/tbb DESTINATION include)
+if (TBB_INSTALL_TARGETS)
+ install(DIRECTORY include/tbb DESTINATION ${TBB_INSTALL_INCLUDE_DIR})
+ if (TBB_BUILD_SHARED)
+ install(EXPORT TBB DESTINATION ${TBB_CMAKE_PACKAGE_INSTALL_DIR} NAMESPACE TBB:: FILE TBBConfig.cmake)
+ endif()
+endif()
+
+# version file
+if (TBB_INSTALL_TARGETS)
+ set (_VERSION_FILE ${CMAKE_CURRENT_SOURCE_DIR}/include/tbb/tbb_stddef.h)
+ file (STRINGS ${_VERSION_FILE} _VERSION_MAJOR_STRING REGEX ".*define[ ]+TBB_VERSION_MAJOR[ ]+[0-9]+.*")
+ file (STRINGS ${_VERSION_FILE} _VERSION_MINOR_STRING REGEX ".*define[ ]+TBB_VERSION_MINOR[ ]+[0-9]+.*")
+ string (REGEX REPLACE ".*TBB_VERSION_MAJOR[ ]+([0-9]+)" "\\1" TBB_MAJOR_VERSION ${_VERSION_MAJOR_STRING})
+ string (REGEX REPLACE ".*TBB_VERSION_MINOR[ ]+([0-9]+)" "\\1" TBB_MINOR_VERSION ${_VERSION_MINOR_STRING})
+ set (TBB_VERSION_STRING "${TBB_MAJOR_VERSION}.${TBB_MINOR_VERSION}")
+ include (CMakePackageConfigHelpers)
+ write_basic_package_version_file (TBBConfigVersion.cmake VERSION "${TBB_VERSION_STRING}" COMPATIBILITY AnyNewerVersion)
+ install (FILES ${CMAKE_CURRENT_BINARY_DIR}/TBBConfigVersion.cmake DESTINATION "${TBB_CMAKE_PACKAGE_INSTALL_DIR}")
+endif()
+
+# version_string.ver
+if (UNIX AND NOT TBB_NO_DATE)
+ execute_process (COMMAND date "+%a, %d %b %Y %H:%M:%S %z"
+ OUTPUT_VARIABLE _configure_date
+ OUTPUT_STRIP_TRAILING_WHITESPACE)
+elseif (WIN32 AND NOT TBB_NO_DATE)
+ execute_process (COMMAND cmd " /C date /T"
+ OUTPUT_VARIABLE _configure_date
+ OUTPUT_STRIP_TRAILING_WHITESPACE)
+else ()
+ set (_configure_date "Unknown")
+endif()
+set (TBB_CONFIG_DATE "${_configure_date}" CACHE STRING "First time that TBB was configured")
+set (_configure_date "${TBB_CONFIG_DATE}")
+include_directories (${CMAKE_BINARY_DIR})
+configure_file (build/version_string.ver.in version_string.ver @ONLY)
+
+if (TBB_BUILD_TESTS)
+ enable_language (C)
+ enable_testing ()
+
+ find_library (LIBRT_LIBRARIES rt)
+ find_library (LIDL_LIBRARIES dl)
+ find_package (Threads)
+ if (NOT APPLE)
+ find_package (OpenMP)
+ endif()
+
+ macro (tbb_add_test testname)
+ set (full_testname tbb_test_${testname})
+ add_executable (${full_testname} src/test/test_${testname}.cpp)
+ target_link_libraries(${full_testname} PRIVATE tbb_interface)
+ if (TBB_BUILD_SHARED)
+ target_link_libraries (${full_testname} PRIVATE tbb tbbmalloc)
+ target_compile_definitions (${full_testname} PRIVATE __TBB_LIB_NAME=tbb)
+ else ()
+ target_link_libraries (${full_testname} PRIVATE tbb_static tbbmalloc_static)
+ target_compile_definitions (${full_testname} PRIVATE __TBB_LIB_NAME=tbb_static)
+ endif ()
+ if (LIBRT_LIBRARIES)
+ target_link_libraries (${full_testname} PRIVATE ${LIBRT_LIBRARIES})
+ endif ()
+ if (LIDL_LIBRARIES)
+ target_link_libraries (${full_testname} PRIVATE ${LIDL_LIBRARIES})
+ endif ()
+ if (Threads_FOUND)
+ target_link_libraries (${full_testname} PRIVATE ${CMAKE_THREAD_LIBS_INIT})
+ endif ()
+ if (OPENMP_FOUND AND "${testname}" MATCHES "openmp")
+ set_target_properties (${full_testname} PROPERTIES COMPILE_FLAGS "${OpenMP_CXX_FLAGS}")
+ set_target_properties (${full_testname} PROPERTIES LINK_FLAGS "${OpenMP_CXX_FLAGS}")
+ endif()
+ if (MINGW)
+ target_link_libraries (${full_testname} PRIVATE psapi)
+ endif ()
+ add_test (NAME ${full_testname} COMMAND ${full_testname})
+ endmacro ()
+
+ tbb_add_test (aggregator)
+ tbb_add_test (aligned_space)
+ tbb_add_test (assembly)
+ if (NOT WIN32)
+ tbb_add_test (async_msg) # msvc64/debug timeouts
+ endif()
+ tbb_add_test (async_node)
+ # tbb_add_test (atomic) # msvc64/debug timeouts: Compile-time initialization fails for static tbb::atomic variables
+ tbb_add_test (blocked_range2d)
+ tbb_add_test (blocked_range3d)
+ tbb_add_test (blocked_range)
+ tbb_add_test (broadcast_node)
+ tbb_add_test (buffer_node)
+ tbb_add_test (cache_aligned_allocator)
+ if (NOT WIN32)
+ tbb_add_test (cache_aligned_allocator_STL)
+ endif()
+ tbb_add_test (cilk_dynamic_load)
+ tbb_add_test (cilk_interop)
+ tbb_add_test (combinable)
+ tbb_add_test (composite_node)
+ tbb_add_test (concurrent_hash_map)
+ tbb_add_test (concurrent_lru_cache)
+ # tbb_add_test (concurrent_monitor) # too long
+ # tbb_add_test (concurrent_priority_queue)
+ if (NOT WIN32)
+ tbb_add_test (concurrent_queue) # msvc64/debug timeouts
+ endif()
+ # tbb_add_test (concurrent_queue_whitebox)
+ tbb_add_test (concurrent_unordered_map)
+ # tbb_add_test (concurrent_unordered_set)
+ tbb_add_test (concurrent_vector)
+ tbb_add_test (continue_node)
+ tbb_add_test (critical_section)
+ tbb_add_test (dynamic_link)
+ # tbb_add_test (eh_algorithms)
+ tbb_add_test (eh_flow_graph)
+ # tbb_add_test (eh_tasks)
+ tbb_add_test (enumerable_thread_specific)
+ tbb_add_test (examples_common_utility)
+ # tbb_add_test (fast_random)
+ tbb_add_test (flow_graph)
+ tbb_add_test (flow_graph_whitebox)
+ # tbb_add_test (fp) # mingw: harness_fp.h:66, assertion !checkConsistency || (ctl.mxcsr & SSE_RND_MODE_MASK) >> 3 == (ctl.x87cw & FE_RND_MODE_MASK): failed
+ # tbb_add_test (function_node) # mingw:random timeout
+ # tbb_add_test (global_control)
+ # tbb_add_test (global_control_whitebox)
+ tbb_add_test (halt)
+ tbb_add_test (handle_perror)
+ # tbb_add_test (hw_concurrency)
+ tbb_add_test (indexer_node)
+ tbb_add_test (inits_loop)
+ tbb_add_test (intrusive_list)
+ tbb_add_test (ittnotify)
+ # tbb_add_test (join_node) #msvc/64: fatal error C1128: number of sections exceeded object file format limit: compile with /bigob
+ tbb_add_test (lambda)
+ tbb_add_test (limiter_node)
+ # tbb_add_test (malloc_atexit)
+ # tbb_add_test (malloc_compliance) #mingw: Limits should be decreased for the test to work
+ tbb_add_test (malloc_init_shutdown)
+ # tbb_add_test (malloc_lib_unload)
+ # tbb_add_test (malloc_overload)
+ tbb_add_test (malloc_pools)
+ tbb_add_test (malloc_regression)
+ # tbb_add_test (malloc_used_by_lib)
+ # tbb_add_test (malloc_whitebox)
+ tbb_add_test (model_plugin)
+ # tbb_add_test (multifunction_node) # too long
+ tbb_add_test (mutex)
+ tbb_add_test (mutex_native_threads)
+ # tbb_add_test (opencl_node)
+ if (OPENMP_FOUND)
+ tbb_add_test (openmp)
+ endif ()
+ tbb_add_test (overwrite_node)
+ # tbb_add_test (parallel_do)
+ # This seems to fail on CI platforms (AppVeyor/Travis), perhaps because the VM exposes just 1 core?
+ tbb_add_test (parallel_for)
+ tbb_add_test (parallel_for_each)
+ tbb_add_test (parallel_for_vectorization)
+ tbb_add_test (parallel_invoke)
+ tbb_add_test (parallel_pipeline)
+ tbb_add_test (parallel_reduce)
+ tbb_add_test (parallel_scan)
+ tbb_add_test (parallel_sort)
+ tbb_add_test (parallel_while)
+ # tbb_add_test (partitioner_whitebox) # too long
+ tbb_add_test (pipeline)
+ # tbb_add_test (pipeline_with_tbf) # takes forever on appveyor
+ tbb_add_test (priority_queue_node)
+ tbb_add_test (queue_node)
+ tbb_add_test (reader_writer_lock)
+ # tbb_add_test (runtime_loader) # LINK : fatal error LNK1104: cannot open file 'tbbproxy.lib' [C:\projects\tbb\test_runtime_loader.vcxproj]
+ tbb_add_test (rwm_upgrade_downgrade)
+ # tbb_add_test (ScalableAllocator)
+ if (NOT WIN32)
+ tbb_add_test (ScalableAllocator_STL)
+ endif()
+ tbb_add_test (semaphore)
+ # tbb_add_test (sequencer_node) # msvc: timeout
+ tbb_add_test (source_node)
+ tbb_add_test (split_node)
+ tbb_add_test (static_assert)
+ tbb_add_test (std_thread)
+ tbb_add_test (tagged_msg)
+ # tbb_add_test (task_arena) # LINK : fatal error LNK1104: cannot open file '__TBB_LIB_NAME.lib' [C:\projects\tbb\test_task_arena.vcxproj]
+ # tbb_add_test (task_assertions)
+ tbb_add_test (task_auto_init)
+ tbb_add_test (task)
+ # tbb_add_test (task_enqueue) # too long
+ tbb_add_test (task_group)
+ # tbb_add_test (task_leaks)
+ # tbb_add_test (task_priority)
+ # tbb_add_test (task_scheduler_init) # msvc: test_task_scheduler_init.cpp:68, assertion !test_mandatory_parallelism || Harness::CanReachConcurrencyLevel(threads): failed
+ tbb_add_test (task_scheduler_observer)
+ tbb_add_test (task_steal_limit)
+ tbb_add_test (tbb_condition_variable)
+ tbb_add_test (tbb_fork)
+ # tbb_add_test (tbb_header)
+ tbb_add_test (tbb_thread)
+ # tbb_add_test (tbb_version)
+ tbb_add_test (tick_count)
+ tbb_add_test (tuple)
+ tbb_add_test (write_once_node)
+ tbb_add_test (yield)
+endif ()
+
+if (TBB_BUILD_PYTHON)
+ find_package(PythonInterp)
+ find_package(PythonLibs ${PYTHON_VERSION_STRING} EXACT)
+ find_package(SWIG 3)
+ if (PythonLibs_FOUND AND SWIG_FOUND AND TBB_BUILD_SHARED)
+ include (${SWIG_USE_FILE})
+ set_source_files_properties (python/tbb/api.i PROPERTIES CPLUSPLUS ON)
+ set (CMAKE_SWIG_FLAGS "-threads")
+
+ # swig_add_module is deprecated
+ if (CMAKE_VERSION VERSION_LESS 3.8)
+ swig_add_module (api python python/tbb/api.i)
+ else ()
+ swig_add_library (api LANGUAGE python SOURCES python/tbb/api.i)
+ endif ()
+
+ # UseSWIG generates now standard target names
+ if (CMAKE_VERSION VERSION_LESS 3.13)
+ set (module_target ${SWIG_MODULE_api_REAL_NAME})
+ else ()
+ set (module_target api)
+ endif ()
+
+ target_include_directories(${module_target} PRIVATE ${PYTHON_INCLUDE_DIRS})
+ target_link_libraries(${module_target} PRIVATE tbb)
+ if(WIN32)
+ target_link_libraries(${module_target} ${PYTHON_LIBRARIES})
+ elseif(APPLE)
+ set_target_properties(${module_target} PROPERTIES LINK_FLAGS "-undefined dynamic_lookup")
+ endif()
+
+ if (WIN32)
+ set (PYTHON_SITE_PACKAGES Lib/site-packages)
+ else ()
+ set (PYTHON_SITE_PACKAGES lib/python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}/site-packages)
+ endif ()
+ if (TBB_INSTALL_TARGETS)
+ install(FILES python/TBB.py
+ DESTINATION ${PYTHON_SITE_PACKAGES})
+ install(FILES python/tbb/__init__.py python/tbb/pool.py python/tbb/test.py python/tbb/__main__.py ${CMAKE_CURRENT_BINARY_DIR}/api.py
+ DESTINATION ${PYTHON_SITE_PACKAGES}/tbb)
+ install(TARGETS ${module_target}
+ DESTINATION ${PYTHON_SITE_PACKAGES}/tbb)
+ endif()
+
+ if(UNIX AND NOT APPLE)
+ add_library(irml SHARED python/rml/ipc_server.cpp python/rml/ipc_utils.cpp src/tbb/cache_aligned_allocator.cpp src/tbb/dynamic_link.cpp src/tbb/tbb_misc_ex.cpp src/tbb/tbb_misc.cpp)
+ target_compile_definitions(irml PRIVATE DO_ITT_NOTIFY=0 USE_PTHREAD=1)
+ target_link_libraries(irml PRIVATE tbb)
+ set_target_properties(irml PROPERTIES VERSION 1)
+ if (TBB_INSTALL_TARGETS)
+ install(TARGETS irml DESTINATION ${TBB_INSTALL_LIBRARY_DIR})
+ endif()
+ endif ()
+ endif ()
+endif ()
diff --git a/build_files/build_environment/patches/oidn.diff b/build_files/build_environment/patches/oidn.diff
index 10d21d7764b..db1015b8958 100644
--- a/build_files/build_environment/patches/oidn.diff
+++ b/build_files/build_environment/patches/oidn.diff
@@ -1,33 +1,3 @@
-diff -Naur oidn-1.3.0/cmake/FindTBB.cmake external_openimagedenoise/cmake/FindTBB.cmake
---- oidn-1.3.0/cmake/FindTBB.cmake 2021-02-04 16:20:26 -0700
-+++ external_openimagedenoise/cmake/FindTBB.cmake 2021-02-12 09:35:53 -0700
-@@ -332,20 +332,22 @@
- ${TBB_ROOT}/lib/${TBB_ARCH}/${TBB_VCVER}
- ${TBB_ROOT}/lib
- )
--
- # On Windows, also search the DLL so that the client may install it.
- file(GLOB DLL_NAMES
- ${TBB_ROOT}/bin/${TBB_ARCH}/${TBB_VCVER}/${LIB_NAME}.dll
- ${TBB_ROOT}/bin/${LIB_NAME}.dll
-+ ${TBB_ROOT}/lib/${LIB_NAME}.dll
- ${TBB_ROOT}/redist/${TBB_ARCH}/${TBB_VCVER}/${LIB_NAME}.dll
- ${TBB_ROOT}/redist/${TBB_ARCH}/${TBB_VCVER}/${LIB_NAME_GLOB1}.dll
- ${TBB_ROOT}/redist/${TBB_ARCH}/${TBB_VCVER}/${LIB_NAME_GLOB2}.dll
- ${TBB_ROOT}/../redist/${TBB_ARCH}/tbb/${TBB_VCVER}/${LIB_NAME}.dll
- ${TBB_ROOT}/../redist/${TBB_ARCH}_win/tbb/${TBB_VCVER}/${LIB_NAME}.dll
- )
-- list(GET DLL_NAMES 0 DLL_NAME)
-- get_filename_component(${BIN_DIR_VAR} "${DLL_NAME}" DIRECTORY)
-- set(${DLL_VAR} "${DLL_NAME}" CACHE PATH "${COMPONENT_NAME} ${BUILD_CONFIG} dll path")
-+ if (DLL_NAMES)
-+ list(GET DLL_NAMES 0 DLL_NAME)
-+ get_filename_component(${BIN_DIR_VAR} "${DLL_NAME}" DIRECTORY)
-+ set(${DLL_VAR} "${DLL_NAME}" CACHE PATH "${COMPONENT_NAME} ${BUILD_CONFIG} dll path")
-+ endif()
- elseif(APPLE)
- set(LIB_PATHS ${TBB_ROOT}/lib)
- else()
--- external_openimagedenoise/cmake/oidn_ispc.cmake 2021-02-15 17:29:34.000000000 +0100
+++ external_openimagedenoise/cmake/oidn_ispc.cmake2 2021-02-15 17:29:28.000000000 +0100
@@ -98,7 +98,7 @@
diff --git a/build_files/build_environment/patches/osl.diff b/build_files/build_environment/patches/osl.diff
index badb6c0d9b3..cd1b58bf580 100644
--- a/build_files/build_environment/patches/osl.diff
+++ b/build_files/build_environment/patches/osl.diff
@@ -63,3 +63,19 @@ diff -Naur org/CMakeLists.txt external_osl/CMakeLists.txt
set (OSL_NO_DEFAULT_TEXTURESYSTEM OFF CACHE BOOL "Do not use create a raw OIIO::TextureSystem")
if (OSL_NO_DEFAULT_TEXTURESYSTEM)
+diff --git a/src/liboslexec/llvm_util.cpp b/src/liboslexec/llvm_util.cpp
+index 445f6400..3d468de2 100644
+--- a/src/liboslexec/llvm_util.cpp
++++ b/src/liboslexec/llvm_util.cpp
+@@ -3430,8 +3430,9 @@ LLVM_Util::call_function (llvm::Value *func, cspan<llvm::Value *> args)
+ #endif
+ //llvm_gen_debug_printf (std::string("start ") + std::string(name));
+ #if OSL_LLVM_VERSION >= 110
+- OSL_DASSERT(llvm::isa<llvm::Function>(func));
+- llvm::Value *r = builder().CreateCall(llvm::cast<llvm::Function>(func), llvm::ArrayRef<llvm::Value *>(args.data(), args.size()));
++ llvm::Value* r = builder().CreateCall(
++ llvm::cast<llvm::FunctionType>(func->getType()->getPointerElementType()), func,
++ llvm::ArrayRef<llvm::Value*>(args.data(), args.size()));
+ #else
+ llvm::Value *r = builder().CreateCall (func, llvm::ArrayRef<llvm::Value *>(args.data(), args.size()));
+ #endif
diff --git a/build_files/build_environment/patches/tbb.diff b/build_files/build_environment/patches/tbb.diff
index c05c35bca95..07f70aa7007 100644
--- a/build_files/build_environment/patches/tbb.diff
+++ b/build_files/build_environment/patches/tbb.diff
@@ -10,4 +10,15 @@ index 7a8d06a0..886699d8 100644
+ #if (__cplusplus >= 201402L && (!defined(_MSC_VER) || _MSC_VER >= 1920))
#define __TBB_DEPRECATED [[deprecated]]
#define __TBB_DEPRECATED_MSG(msg) [[deprecated(msg)]]
- #elif _MSC_VER \ No newline at end of file
+ #elif _MSC_VER
+--- a/src/tbb/tools_api/ittnotify_config.h
++++ b/src/tbb/tools_api/ittnotify_config.h
+@@ -162,7 +162,7 @@
+ # define ITT_ARCH ITT_ARCH_IA32E
+ # elif defined _M_IA64 || defined __ia64__
+ # define ITT_ARCH ITT_ARCH_IA64
+-# elif defined _M_ARM || defined __arm__
++# elif defined _M_ARM || defined __arm__ || defined __aarch64__
+ # define ITT_ARCH ITT_ARCH_ARM
+ # elif defined __powerpc64__
+ # define ITT_ARCH ITT_ARCH_PPC64
diff --git a/build_files/build_environment/patches/theora.diff b/build_files/build_environment/patches/theora.diff
index 3abadb66be9..4436577816e 100644
--- a/build_files/build_environment/patches/theora.diff
+++ b/build_files/build_environment/patches/theora.diff
@@ -4,7 +4,7 @@
# Some are omitted here because they have special meanings below.
1750a | 580 \
| a29k \
-+ | aarch64 \
++ | aarch64 \
| alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
| alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
| arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \
@@ -12,7 +12,7 @@
# Recognize the basic CPU types with company name.
580-* \
| a29k-* \
-+ | aarch64-* \
++ | aarch64-* \
| alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
| alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
| alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
diff --git a/build_files/build_environment/patches/usd.diff b/build_files/build_environment/patches/usd.diff
index 42527a4f443..dc4982ad114 100644
--- a/build_files/build_environment/patches/usd.diff
+++ b/build_files/build_environment/patches/usd.diff
@@ -53,3 +53,147 @@ diff -ru USD-20.11/pxr/base/tf/pxrLZ4/lz4.cpp external_usd/pxr/base/tf/pxrLZ4/lz
/*-******************************
* Compression functions
+
+From 442d087962f762deeb8b6e49a0955753fcf9aeb9 Mon Sep 17 00:00:00 2001
+From: Tsahi Zidenberg <tsahee@amazon.com>
+Date: Sun, 15 Nov 2020 15:18:24 +0000
+Subject: [PATCH 1/2] stackTrace: support aarch64/linux
+
+stacktrace calls syscall directly via assembler. Create compatible
+aarch64 code.
+---
+ pxr/base/arch/stackTrace.cpp | 30 ++++++++++++++++++++++++------
+ 1 file changed, 24 insertions(+), 6 deletions(-)
+
+diff --git a/pxr/base/arch/stackTrace.cpp b/pxr/base/arch/stackTrace.cpp
+index dcc1dfd46..c11aabeb1 100644
+--- a/pxr/base/arch/stackTrace.cpp
++++ b/pxr/base/arch/stackTrace.cpp
+@@ -583,7 +583,6 @@ nonLockingLinux__execve (const char *file,
+ char *const argv[],
+ char *const envp[])
+ {
+-#if defined(ARCH_BITS_64)
+ /*
+ * We make a direct system call here, because we can't find an
+ * execve which corresponds with the non-locking fork we call
+@@ -594,7 +593,27 @@ nonLockingLinux__execve (const char *file,
+ * hangs in a threaded app. (We use the non-locking fork to get
+ * around problems with forking when we have had memory
+ * corruption.) whew.
+- *
++ */
++
++ unsigned long result;
++
++#if defined (__aarch64__)
++ {
++ register long __file_result asm ("x0") = (long)file;
++ register char* const* __argv asm ("x1") = argv;
++ register char* const* __envp asm ("x2") = envp;
++ register long __num_execve asm ("x8") = 221;
++ __asm__ __volatile__ (
++ "svc 0"
++ : "=r" (__file_result)
++ : "r"(__num_execve), "r" (__file_result), "r" (__argv), "r" (__envp)
++ : "memory"
++ );
++ result = __file_result;
++ }
++#elif defined(ARCH_CPU_INTEL) && defined(ARCH_BITS_64)
++
++ /*
+ * %rdi, %rsi, %rdx, %rcx, %r8, %r9 are args 0-5
+ * syscall clobbers %rcx and %r11
+ *
+@@ -603,7 +622,6 @@ nonLockingLinux__execve (const char *file,
+ * constraints to gcc.
+ */
+
+- unsigned long result;
+ __asm__ __volatile__ (
+ "mov %0, %%rdi \n\t"
+ "mov %%rcx, %%rsi \n\t"
+@@ -614,6 +632,9 @@ nonLockingLinux__execve (const char *file,
+ : "0" (file), "c" (argv), "d" (envp)
+ : "memory", "cc", "r11"
+ );
++#else
++#error Unknown architecture
++#endif
+
+ if (result >= 0xfffffffffffff000) {
+ errno = -result;
+@@ -621,9 +642,6 @@ nonLockingLinux__execve (const char *file,
+ }
+
+ return result;
+-#else
+-#error Unknown architecture
+-#endif
+ }
+
+ #endif
+
+From a1dffe02519bb3c6ccbbe8c6c58304da5db98995 Mon Sep 17 00:00:00 2001
+From: Tsahi Zidenberg <tsahee@amazon.com>
+Date: Sun, 15 Nov 2020 15:22:52 +0000
+Subject: [PATCH 2/2] timing: support aarch64/linux
+
+The aarch64 arch-timer is directly accessible to userspace via two
+registers:
+CNTVCT_EL0 - holds the current counter value
+CNTFRQ_EL0 - holds the counter frequency (in Hz)
+---
+ pxr/base/arch/timing.cpp | 6 ++++++
+ pxr/base/arch/timing.h | 6 +++++-
+ 2 files changed, 11 insertions(+), 1 deletion(-)
+
+diff --git a/pxr/base/arch/timing.cpp b/pxr/base/arch/timing.cpp
+index 27ad58fed..9022950c1 100644
+--- a/pxr/base/arch/timing.cpp
++++ b/pxr/base/arch/timing.cpp
+@@ -59,6 +59,11 @@ ARCH_HIDDEN
+ void
+ Arch_InitTickTimer()
+ {
++#ifdef __aarch64__
++ uint64_t counter_hz;
++ __asm __volatile("mrs %0, CNTFRQ_EL0" : "=&r" (counter_hz));
++ Arch_NanosecondsPerTick = double(1e9) / double(counter_hz);
++#else
+ // NOTE: Normally ifstream would be cleaner, but it causes crashes when
+ // used in conjunction with DSOs and the Intel Compiler.
+ FILE *in;
+@@ -135,6 +140,7 @@ Arch_InitTickTimer()
+ }
+
+ Arch_NanosecondsPerTick = double(1e9) / double(cpuHz);
++#endif
+ }
+ #elif defined(ARCH_OS_WINDOWS)
+
+diff --git a/pxr/base/arch/timing.h b/pxr/base/arch/timing.h
+index 67ec0d15f..6dc3e85a0 100644
+--- a/pxr/base/arch/timing.h
++++ b/pxr/base/arch/timing.h
+@@ -36,7 +36,7 @@
+ /// \addtogroup group_arch_SystemFunctions
+ ///@{
+
+-#if defined(ARCH_OS_LINUX)
++#if defined(ARCH_OS_LINUX) && defined(ARCH_CPU_INTEL)
+ #include <x86intrin.h>
+ #elif defined(ARCH_OS_DARWIN)
+ #include <mach/mach_time.h>
+@@ -69,6 +69,10 @@ ArchGetTickTime()
+ #elif defined(ARCH_CPU_INTEL)
+ // On Intel we'll use the rdtsc instruction.
+ return __rdtsc();
++#elif defined (__aarch64__)
++ uint64_t result;
++ __asm __volatile("mrs %0, CNTVCT_EL0" : "=&r" (result));
++ return result;
+ #else
+ #error Unknown architecture.
+ #endif
diff --git a/build_files/buildbot/README.md b/build_files/buildbot/README.md
index 06733c9a42d..f6fd07d9246 100644
--- a/build_files/buildbot/README.md
+++ b/build_files/buildbot/README.md
@@ -1,70 +1,4 @@
-Blender Buildbot
-================
+Buildbot Configuration
+=====================
-Code signing
-------------
-
-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 `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.
-
-### Configuration
-
-Client configuration doesn't need anything special, other than variable
-`SHARED_STORAGE_DIR` pointing to a location which is watched by a server.
-This is done in `config_builder.py` file and is stored in Git (which makes it
-possible to have almost zero-configuration buildbot machines).
-
-Server configuration requires copying `config_server_template.py` under the
-name of `config_server.py` and tweaking values, which are platform-specific.
-
-#### Windows configuration
-
-There are two things which are needed on Windows in order to have code signing
-to work:
-
-- `TIMESTAMP_AUTHORITY_URL` which is most likely set http://timestamp.digicert.com
-- `CERTIFICATE_FILEPATH` which is a full file path to a PKCS #12 key (.pfx).
-
-## Tips
-
-### Self-signed certificate on Windows
-
-It is easiest to test configuration using self-signed certificate.
-
-The certificate manipulation utilities are coming with Windows SDK.
-Unfortunately, they are not added to PATH. Here is an example of how to make
-sure they are easily available:
-
-```
-set PATH=C:\Program Files (x86)\Windows Kits\10\App Certification Kit;%PATH%
-set PATH=C:\Program Files (x86)\Windows Kits\10\bin\10.0.18362.0\x64;%PATH%
-```
-
-Generate CA:
-
-```
-makecert -r -pe -n "CN=Blender Test CA" -ss CA -sr CurrentUser -a sha256 ^
- -cy authority -sky signature -sv BlenderTestCA.pvk BlenderTestCA.cer
-```
-
-Import the generated CA:
-
-```
-certutil -user -addstore Root BlenderTestCA.cer
-```
-
-Create self-signed certificate and pack it into PKCS #12:
-
-```
-makecert -pe -n "CN=Blender Test SPC" -a sha256 -cy end ^
- -sky signature ^
- -ic BlenderTestCA.cer -iv BlenderTestCA.pvk ^
- -sv BlenderTestSPC.pvk BlenderTestSPC.cer
-
-pvk2pfx -pvk BlenderTestSPC.pvk -spc BlenderTestSPC.cer -pfx BlenderTestSPC.pfx
-``` \ No newline at end of file
+Files used by Buildbot's `compile-code` step.
diff --git a/build_files/buildbot/buildbot_utils.py b/build_files/buildbot/buildbot_utils.py
deleted file mode 100644
index 057c2155fb6..00000000000
--- a/build_files/buildbot/buildbot_utils.py
+++ /dev/null
@@ -1,127 +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>
-
-import argparse
-import os
-import re
-import subprocess
-import sys
-
-
-def is_tool(name):
- """Check whether `name` is on PATH and marked as executable."""
-
- # from whichcraft import which
- from shutil import which
-
- return which(name) is not None
-
-
-class Builder:
- def __init__(self, name, branch, codesign):
- self.name = name
- self.branch = branch
- self.is_release_branch = re.match("^blender-v(.*)-release$", branch) is not None
- self.codesign = codesign
-
- # Buildbot runs from build/ directory
- self.blender_dir = os.path.abspath(os.path.join('..', 'blender.git'))
- self.build_dir = os.path.abspath(os.path.join('..', 'build'))
- self.install_dir = os.path.abspath(os.path.join('..', 'install'))
- self.upload_dir = os.path.abspath(os.path.join('..', 'install'))
-
- # Detect platform
- if name.startswith('mac'):
- self.platform = 'mac'
- self.command_prefix = []
- elif name.startswith('linux'):
- self.platform = 'linux'
- if is_tool('scl'):
- self.command_prefix = ['scl', 'enable', 'devtoolset-9', '--']
- else:
- self.command_prefix = []
- elif name.startswith('win'):
- self.platform = 'win'
- self.command_prefix = []
- else:
- raise ValueError('Unkonw platform for builder ' + self.platform)
-
- # Always 64 bit now
- self.bits = 64
-
-
-def create_builder_from_arguments():
- parser = argparse.ArgumentParser()
- parser.add_argument('builder_name')
- parser.add_argument('branch', default='master', nargs='?')
- parser.add_argument("--codesign", action="store_true")
- args = parser.parse_args()
- return Builder(args.builder_name, args.branch, args.codesign)
-
-
-class VersionInfo:
- def __init__(self, builder):
- # Get version information
- buildinfo_h = os.path.join(builder.build_dir, "source", "creator", "buildinfo.h")
- blender_h = os.path.join(builder.blender_dir, "source", "blender", "blenkernel", "BKE_blender_version.h")
-
- version_number = int(self._parse_header_file(blender_h, 'BLENDER_VERSION'))
- version_number_patch = int(self._parse_header_file(blender_h, 'BLENDER_VERSION_PATCH'))
- version_numbers = (version_number // 100, version_number % 100, version_number_patch)
- self.short_version = "%d.%02d" % (version_numbers[0], version_numbers[1])
- self.version = "%d.%02d.%d" % version_numbers
- self.version_cycle = self._parse_header_file(blender_h, 'BLENDER_VERSION_CYCLE')
- self.hash = self._parse_header_file(buildinfo_h, 'BUILD_HASH')[1:-1]
-
- if self.version_cycle == "release":
- # Final release
- self.full_version = self.version
- self.is_development_build = False
- elif self.version_cycle == "rc":
- # Release candidate
- self.full_version = self.version + self.version_cycle
- self.is_development_build = False
- else:
- # Development build
- self.full_version = self.version + '-' + self.hash
- self.is_development_build = True
-
- def _parse_header_file(self, filename, define):
- import re
- regex = re.compile(r"^#\s*define\s+%s\s+(.*)" % define)
- with open(filename, "r") as file:
- for l in file:
- match = regex.match(l)
- if match:
- return match.group(1)
- return None
-
-
-def call(cmd, env=None, exit_on_error=True):
- print(' '.join(cmd))
-
- # Flush to ensure correct order output on Windows.
- sys.stdout.flush()
- sys.stderr.flush()
-
- retcode = subprocess.call(cmd, env=env)
- if exit_on_error and retcode != 0:
- sys.exit(retcode)
- return retcode
diff --git a/build_files/buildbot/codesign/absolute_and_relative_filename.py b/build_files/buildbot/codesign/absolute_and_relative_filename.py
deleted file mode 100644
index cb42710e785..00000000000
--- a/build_files/buildbot/codesign/absolute_and_relative_filename.py
+++ /dev/null
@@ -1,81 +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>
-
-from dataclasses import dataclass
-from pathlib import Path
-from typing import List
-
-
-@dataclass
-class AbsoluteAndRelativeFileName:
- """
- Helper class which keeps track of absolute file path for a direct access and
- corresponding relative path against given base.
-
- The relative part is used to construct a file name within an archive which
- contains files which are to be signed or which has been signed already
- (depending on whether the archive is addressed to signing server or back
- to the buildbot worker).
- """
-
- # Base directory which is where relative_filepath is relative to.
- base_dir: Path
-
- # Full absolute path of the corresponding file.
- absolute_filepath: Path
-
- # Derived from full file path, contains part of the path which is relative
- # to a desired base path.
- relative_filepath: Path
-
- def __init__(self, base_dir: Path, filepath: Path):
- self.base_dir = base_dir
- self.absolute_filepath = filepath.resolve()
- self.relative_filepath = self.absolute_filepath.relative_to(
- self.base_dir)
-
- @classmethod
- def from_path(cls, path: Path) -> 'AbsoluteAndRelativeFileName':
- assert path.is_absolute()
- assert path.is_file()
-
- base_dir = path.parent
- return AbsoluteAndRelativeFileName(base_dir, path)
-
- @classmethod
- def recursively_from_directory(cls, base_dir: Path) \
- -> List['AbsoluteAndRelativeFileName']:
- """
- Create list of AbsoluteAndRelativeFileName for all the files in the
- given directory.
-
- NOTE: Result will be pointing to a resolved paths.
- """
- assert base_dir.is_absolute()
- assert base_dir.is_dir()
-
- base_dir = base_dir.resolve()
-
- result = []
- for filename in base_dir.glob('**/*'):
- if not filename.is_file():
- continue
- result.append(AbsoluteAndRelativeFileName(base_dir, filename))
- return result
diff --git a/build_files/buildbot/codesign/archive_with_indicator.py b/build_files/buildbot/codesign/archive_with_indicator.py
deleted file mode 100644
index aebf5a15417..00000000000
--- a/build_files/buildbot/codesign/archive_with_indicator.py
+++ /dev/null
@@ -1,245 +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>
-
-import dataclasses
-import json
-import os
-
-from pathlib import Path
-from typing import Optional
-
-import codesign.util as util
-
-
-class ArchiveStateError(Exception):
- message: str
-
- def __init__(self, message):
- self.message = message
- super().__init__(self.message)
-
-
-@dataclasses.dataclass
-class ArchiveState:
- """
- Additional information (state) of the archive
-
- Includes information like expected file size of the archive file in the case
- the archive file is expected to be successfully created.
-
- If the archive can not be created, this state will contain error message
- indicating details of error.
- """
-
- # Size in bytes of the corresponding archive.
- file_size: Optional[int] = None
-
- # Non-empty value indicates that error has happenned.
- error_message: str = ''
-
- def has_error(self) -> bool:
- """
- Check whether the archive is at error state
- """
-
- return self.error_message
-
- def serialize_to_string(self) -> str:
- payload = dataclasses.asdict(self)
- return json.dumps(payload, sort_keys=True, indent=4)
-
- def serialize_to_file(self, filepath: Path) -> None:
- string = self.serialize_to_string()
- filepath.write_text(string)
-
- @classmethod
- def deserialize_from_string(cls, string: str) -> 'ArchiveState':
- try:
- object_as_dict = json.loads(string)
- except json.decoder.JSONDecodeError:
- raise ArchiveStateError('Error parsing JSON')
-
- return cls(**object_as_dict)
-
- @classmethod
- def deserialize_from_file(cls, filepath: Path):
- string = filepath.read_text()
- return cls.deserialize_from_string(string)
-
-
-class ArchiveWithIndicator:
- """
- The idea of this class is to wrap around logic which takes care of keeping
- track of a name of an archive and synchronization routines between buildbot
- worker and signing server.
-
- The synchronization is done based on creating a special file after the
- archive file is knowingly ready for access.
- """
-
- # Base directory where the archive is stored (basically, a basename() of
- # the absolute archive file name).
- #
- # For example, 'X:\\TEMP\\'.
- base_dir: Path
-
- # Absolute file name of the archive.
- #
- # For example, 'X:\\TEMP\\FOO.ZIP'.
- archive_filepath: Path
-
- # Absolute name of a file which acts as an indication of the fact that the
- # archive is ready and is available for access.
- #
- # This is how synchronization between buildbot worker and signing server is
- # done:
- # - First, the archive is created under archive_filepath name.
- # - Second, the indication file is created under ready_indicator_filepath
- # name.
- # - Third, the colleague of whoever created the indicator name watches for
- # the indication file to appear, and once it's there it access the
- # archive.
- ready_indicator_filepath: Path
-
- def __init__(
- self, base_dir: Path, archive_name: str, ready_indicator_name: str):
- """
- Construct the object from given base directory and name of the archive
- file:
- ArchiveWithIndicator(Path('X:\\TEMP'), 'FOO.ZIP', 'INPUT_READY')
- """
-
- self.base_dir = base_dir
- self.archive_filepath = self.base_dir / archive_name
- self.ready_indicator_filepath = self.base_dir / ready_indicator_name
-
- def is_ready_unsafe(self) -> bool:
- """
- Check whether the archive is ready for access.
-
- No guarding about possible network failres is done here.
- """
- if not self.ready_indicator_filepath.exists():
- return False
-
- try:
- archive_state = ArchiveState.deserialize_from_file(
- self.ready_indicator_filepath)
- except ArchiveStateError as error:
- print(f'Error deserializing archive state: {error.message}')
- return False
-
- if archive_state.has_error():
- # If the error did happen during codesign procedure there will be no
- # corresponding archive file.
- # The caller code will deal with the error check further.
- return True
-
- # Sometimes on macOS indicator file appears prior to the actual archive
- # despite the order of creation and os.sync() used in tag_ready().
- # So consider archive not ready if there is an indicator without an
- # actual archive.
- if not self.archive_filepath.exists():
- print('Found indicator without actual archive, waiting for archive '
- f'({self.archive_filepath}) to appear.')
- return False
-
- # Wait for until archive is fully stored.
- actual_archive_size = self.archive_filepath.stat().st_size
- if actual_archive_size != archive_state.file_size:
- print('Partial/invalid archive size (expected '
- f'{archive_state.file_size} got {actual_archive_size})')
- return False
-
- return True
-
- def is_ready(self) -> bool:
- """
- Check whether the archive is ready for access.
-
- Will tolerate possible network failures: if there is a network failure
- or if there is still no proper permission on a file False is returned.
- """
-
- # There are some intermitten problem happening at a random which is
- # translates to "OSError : [WinError 59] An unexpected network error occurred".
- # Some reports suggests it might be due to lack of permissions to the file,
- # which might be applicable in our case since it's possible that file is
- # initially created with non-accessible permissions and gets chmod-ed
- # after initial creation.
- try:
- return self.is_ready_unsafe()
- except OSError as e:
- print(f'Exception checking archive: {e}')
- return False
-
- def tag_ready(self, error_message='') -> None:
- """
- Tag the archive as ready by creating the corresponding indication file.
-
- NOTE: It is expected that the archive was never tagged as ready before
- and that there are no subsequent tags of the same archive.
- If it is violated, an assert will fail.
- """
- assert not self.is_ready()
-
- # Try the best to make sure everything is synced to the file system,
- # to avoid any possibility of stamp appearing on a network share prior to
- # an actual file.
- if util.get_current_platform() != util.Platform.WINDOWS:
- os.sync()
-
- archive_size = -1
- if self.archive_filepath.exists():
- archive_size = self.archive_filepath.stat().st_size
-
- archive_info = ArchiveState(
- file_size=archive_size, error_message=error_message)
-
- self.ready_indicator_filepath.write_text(
- archive_info.serialize_to_string())
-
- def get_state(self) -> ArchiveState:
- """
- Get state object for this archive
-
- The state is read from the corresponding state file.
- """
-
- try:
- return ArchiveState.deserialize_from_file(self.ready_indicator_filepath)
- except ArchiveStateError as error:
- return ArchiveState(error_message=f'Error in information format: {error}')
-
- def clean(self) -> None:
- """
- Remove both archive and the ready indication file.
- """
- util.ensure_file_does_not_exist_or_die(self.ready_indicator_filepath)
- util.ensure_file_does_not_exist_or_die(self.archive_filepath)
-
- def is_fully_absent(self) -> bool:
- """
- Check whether both archive and its ready indicator are absent.
- Is used for a sanity check during code signing process by both
- buildbot worker and signing server.
- """
- return (not self.archive_filepath.exists() and
- not self.ready_indicator_filepath.exists())
diff --git a/build_files/buildbot/codesign/base_code_signer.py b/build_files/buildbot/codesign/base_code_signer.py
deleted file mode 100644
index f045e9c8242..00000000000
--- a/build_files/buildbot/codesign/base_code_signer.py
+++ /dev/null
@@ -1,501 +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>
-
-# Signing process overview.
-#
-# From buildbot worker side:
-# - Files which needs to be signed are collected from either a directory to
-# sign all signable files in there, or by filename of a single file to sign.
-# - Those files gets packed into an archive and stored in a location location
-# which is watched by the signing server.
-# - A marker READY file is created which indicates the archive is ready for
-# access.
-# - Wait for the server to provide an archive with signed files.
-# This is done by watching for the READY file which corresponds to an archive
-# coming from the signing server.
-# - Unpack the signed signed files from the archives and replace original ones.
-#
-# From code sign server:
-# - Watch special location for a READY file which indicates the there is an
-# archive with files which are to be signed.
-# - Unpack the archive to a temporary location.
-# - Run codesign tool and make sure all the files are signed.
-# - Pack the signed files and store them in a location which is watched by
-# the buildbot worker.
-# - Create a READY file which indicates that the archive with signed files is
-# ready.
-
-import abc
-import logging
-import shutil
-import subprocess
-import time
-import tarfile
-import uuid
-
-from pathlib import Path
-from tempfile import TemporaryDirectory
-from typing import Iterable, List
-
-import codesign.util as util
-
-from codesign.absolute_and_relative_filename import AbsoluteAndRelativeFileName
-from codesign.archive_with_indicator import ArchiveWithIndicator
-from codesign.exception import CodeSignException
-
-
-logger = logging.getLogger(__name__)
-logger_builder = logger.getChild('builder')
-logger_server = logger.getChild('server')
-
-
-def pack_files(files: Iterable[AbsoluteAndRelativeFileName],
- archive_filepath: Path) -> None:
- """
- Create tar archive from given files for the signing pipeline.
- Is used by buildbot worker to create an archive of files which are to be
- signed, and by signing server to send signed files back to the worker.
- """
- with tarfile.TarFile.open(archive_filepath, 'w') as tar_file_handle:
- for file_info in files:
- tar_file_handle.add(file_info.absolute_filepath,
- arcname=file_info.relative_filepath)
-
-
-def extract_files(archive_filepath: Path,
- extraction_dir: Path) -> None:
- """
- Extract all files form the given archive into the given direcotry.
- """
-
- # TODO(sergey): Verify files in the archive have relative path.
-
- with tarfile.TarFile.open(archive_filepath, mode='r') as tar_file_handle:
- tar_file_handle.extractall(path=extraction_dir)
-
-
-class BaseCodeSigner(metaclass=abc.ABCMeta):
- """
- Base class for a platform-specific signer of binaries.
-
- Contains all the logic shared across platform-specific implementations, such
- as synchronization and notification logic.
-
- Platform specific bits (such as actual command for signing the binary) are
- to be implemented as a subclass.
-
- Provides utilities code signing as a whole, including functionality needed
- by a signing server and a buildbot worker.
-
- The signer and builder may run on separate machines, the only requirement is
- that they have access to a directory which is shared between them. For the
- security concerns this is to be done as a separate machine (or as a Shared
- Folder configuration in VirtualBox configuration). This directory might be
- mounted under different base paths, but its underlying storage is to be
- the same.
-
- The code signer is short-lived on a buildbot worker side, and is living
- forever on a code signing server side.
- """
-
- # TODO(sergey): Find a neat way to have config annotated.
- # config: Config
-
- # Storage directory where builder puts files which are requested to be
- # signed.
- # Consider this an input of the code signing server.
- unsigned_storage_dir: Path
-
- # Storage where signed files are stored.
- # Consider this an output of the code signer server.
- signed_storage_dir: Path
-
- # Platform the code is currently executing on.
- platform: util.Platform
-
- def __init__(self, config):
- self.config = config
-
- absolute_shared_storage_dir = config.SHARED_STORAGE_DIR.resolve()
-
- # Unsigned (signing server input) configuration.
- self.unsigned_storage_dir = absolute_shared_storage_dir / 'unsigned'
-
- # Signed (signing server output) configuration.
- self.signed_storage_dir = absolute_shared_storage_dir / 'signed'
-
- self.platform = util.get_current_platform()
-
- def cleanup_environment_for_builder(self) -> None:
- # TODO(sergey): Revisit need of cleaning up the existing files.
- # In practice it wasn't so helpful, and with multiple clients
- # talking to the same server it becomes even more tricky.
- pass
-
- def cleanup_environment_for_signing_server(self) -> None:
- # TODO(sergey): Revisit need of cleaning up the existing files.
- # In practice it wasn't so helpful, and with multiple clients
- # talking to the same server it becomes even more tricky.
- pass
-
- def generate_request_id(self) -> str:
- """
- Generate an unique identifier for code signing request.
- """
- return str(uuid.uuid4())
-
- def archive_info_for_request_id(
- self, path: Path, request_id: str) -> ArchiveWithIndicator:
- return ArchiveWithIndicator(
- path, f'{request_id}.tar', f'{request_id}.ready')
-
- def signed_archive_info_for_request_id(
- self, request_id: str) -> ArchiveWithIndicator:
- return self.archive_info_for_request_id(
- self.signed_storage_dir, request_id)
-
- def unsigned_archive_info_for_request_id(
- self, request_id: str) -> ArchiveWithIndicator:
- return self.archive_info_for_request_id(
- self.unsigned_storage_dir, request_id)
-
- ############################################################################
- # Buildbot worker side helpers.
-
- @abc.abstractmethod
- def check_file_is_to_be_signed(
- self, file: AbsoluteAndRelativeFileName) -> bool:
- """
- Check whether file is to be signed.
-
- Is used by both single file signing pipeline and recursive directory
- signing pipeline.
-
- This is where code signer is to check whether file is to be signed or
- not. This check might be based on a simple extension test or on actual
- test whether file have a digital signature already or not.
- """
-
- def collect_files_to_sign(self, path: Path) \
- -> List[AbsoluteAndRelativeFileName]:
- """
- Get all files which need to be signed from the given path.
-
- NOTE: The path might either be a file or directory.
-
- This function is run from the buildbot worker side.
- """
-
- # If there is a single file provided trust the buildbot worker that it
- # is eligible for signing.
- if path.is_file():
- file = AbsoluteAndRelativeFileName.from_path(path)
- if not self.check_file_is_to_be_signed(file):
- return []
- return [file]
-
- all_files = AbsoluteAndRelativeFileName.recursively_from_directory(
- path)
- files_to_be_signed = [file for file in all_files
- if self.check_file_is_to_be_signed(file)]
- return files_to_be_signed
-
- def wait_for_signed_archive_or_die(self, request_id) -> None:
- """
- Wait until archive with signed files is available.
-
- Will only return if the archive with signed files is available. If there
- was an error during code sign procedure the SystemExit exception is
- raised, with the message set to the error reported by the codesign
- server.
-
- Will only wait for the configured time. If that time exceeds and there
- is still no responce from the signing server the application will exit
- with a non-zero exit code.
-
- """
-
- signed_archive_info = self.signed_archive_info_for_request_id(
- request_id)
- unsigned_archive_info = self.unsigned_archive_info_for_request_id(
- request_id)
-
- timeout_in_seconds = self.config.TIMEOUT_IN_SECONDS
- time_start = time.monotonic()
- while not signed_archive_info.is_ready():
- time.sleep(1)
- time_slept_in_seconds = time.monotonic() - time_start
- if time_slept_in_seconds > timeout_in_seconds:
- signed_archive_info.clean()
- unsigned_archive_info.clean()
- raise SystemExit("Signing server didn't finish signing in "
- f'{timeout_in_seconds} seconds, dying :(')
-
- archive_state = signed_archive_info.get_state()
- if archive_state.has_error():
- signed_archive_info.clean()
- unsigned_archive_info.clean()
- raise SystemExit(
- f'Error happenned during codesign procedure: {archive_state.error_message}')
-
- def copy_signed_files_to_directory(
- self, signed_dir: Path, destination_dir: Path) -> None:
- """
- Copy all files from signed_dir to destination_dir.
-
- This function will overwrite any existing file. Permissions are copied
- from the source files, but other metadata, such as timestamps, are not.
- """
- for signed_filepath in signed_dir.glob('**/*'):
- if not signed_filepath.is_file():
- continue
-
- relative_filepath = signed_filepath.relative_to(signed_dir)
- destination_filepath = destination_dir / relative_filepath
- destination_filepath.parent.mkdir(parents=True, exist_ok=True)
-
- shutil.copy(signed_filepath, destination_filepath)
-
- def run_buildbot_path_sign_pipeline(self, path: Path) -> None:
- """
- Run all steps needed to make given path signed.
-
- Path points to an unsigned file or a directory which contains unsigned
- files.
-
- If the path points to a single file then this file will be signed.
- This is used to sign a final bundle such as .msi on Windows or .dmg on
- macOS.
-
- NOTE: The code signed implementation might actually reject signing the
- file, in which case the file will be left unsigned. This isn't anything
- to be considered a failure situation, just might happen when buildbot
- worker can not detect whether signing is really required in a specific
- case or not.
-
- If the path points to a directory then code signer will sign all
- signable files from it (finding them recursively).
- """
-
- self.cleanup_environment_for_builder()
-
- # Make sure storage directory exists.
- self.unsigned_storage_dir.mkdir(parents=True, exist_ok=True)
-
- # Collect all files which needs to be signed and pack them into a single
- # archive which will be sent to the signing server.
- logger_builder.info('Collecting files which are to be signed...')
- files = self.collect_files_to_sign(path)
- if not files:
- logger_builder.info('No files to be signed, ignoring.')
- return
- logger_builder.info('Found %d files to sign.', len(files))
-
- request_id = self.generate_request_id()
- signed_archive_info = self.signed_archive_info_for_request_id(
- request_id)
- unsigned_archive_info = self.unsigned_archive_info_for_request_id(
- request_id)
-
- pack_files(files=files,
- archive_filepath=unsigned_archive_info.archive_filepath)
- unsigned_archive_info.tag_ready()
-
- # Wait for the signing server to finish signing.
- logger_builder.info('Waiting signing server to sign the files...')
- self.wait_for_signed_archive_or_die(request_id)
-
- # Extract signed files from archive and move files to final location.
- with TemporaryDirectory(prefix='blender-buildbot-') as temp_dir_str:
- unpacked_signed_files_dir = Path(temp_dir_str)
-
- logger_builder.info('Extracting signed files from archive...')
- extract_files(
- archive_filepath=signed_archive_info.archive_filepath,
- extraction_dir=unpacked_signed_files_dir)
-
- destination_dir = path
- if destination_dir.is_file():
- destination_dir = destination_dir.parent
- self.copy_signed_files_to_directory(
- unpacked_signed_files_dir, destination_dir)
-
- logger_builder.info('Removing archive with signed files...')
- signed_archive_info.clean()
-
- ############################################################################
- # Signing server side helpers.
-
- def wait_for_sign_request(self) -> str:
- """
- Wait for the buildbot to request signing of an archive.
-
- Returns an identifier of signing request.
- """
-
- # TOOD(sergey): Support graceful shutdown on Ctrl-C.
-
- logger_server.info(
- f'Waiting for a request directory {self.unsigned_storage_dir} to appear.')
- while not self.unsigned_storage_dir.exists():
- time.sleep(1)
-
- logger_server.info(
- 'Waiting for a READY indicator of any signing request.')
- request_id = None
- while request_id is None:
- for file in self.unsigned_storage_dir.iterdir():
- if file.suffix != '.ready':
- continue
- request_id = file.stem
- logger_server.info(f'Found READY for request ID {request_id}.')
- if request_id is None:
- time.sleep(1)
-
- unsigned_archive_info = self.unsigned_archive_info_for_request_id(
- request_id)
- while not unsigned_archive_info.is_ready():
- time.sleep(1)
-
- return request_id
-
- @abc.abstractmethod
- def sign_all_files(self, files: List[AbsoluteAndRelativeFileName]) -> None:
- """
- Sign all files in the given directory.
-
- NOTE: Signing should happen in-place.
- """
-
- def run_signing_pipeline(self, request_id: str):
- """
- Run the full signing pipeline starting from the point when buildbot
- worker have requested signing.
- """
-
- # Make sure storage directory exists.
- self.signed_storage_dir.mkdir(parents=True, exist_ok=True)
-
- with TemporaryDirectory(prefix='blender-codesign-') as temp_dir_str:
- temp_dir = Path(temp_dir_str)
-
- signed_archive_info = self.signed_archive_info_for_request_id(
- request_id)
- unsigned_archive_info = self.unsigned_archive_info_for_request_id(
- request_id)
-
- logger_server.info('Extracting unsigned files from archive...')
- extract_files(
- archive_filepath=unsigned_archive_info.archive_filepath,
- extraction_dir=temp_dir)
-
- logger_server.info('Collecting all files which needs signing...')
- files = AbsoluteAndRelativeFileName.recursively_from_directory(
- temp_dir)
-
- logger_server.info('Signing all requested files...')
- try:
- self.sign_all_files(files)
- except CodeSignException as error:
- signed_archive_info.tag_ready(error_message=error.message)
- unsigned_archive_info.clean()
- logger_server.info('Signing is complete with errors.')
- return
-
- logger_server.info('Packing signed files...')
- pack_files(files=files,
- archive_filepath=signed_archive_info.archive_filepath)
- signed_archive_info.tag_ready()
-
- logger_server.info('Removing signing request...')
- unsigned_archive_info.clean()
-
- logger_server.info('Signing is complete.')
-
- def run_signing_server(self):
- logger_server.info('Starting new code signing server...')
- self.cleanup_environment_for_signing_server()
- logger_server.info('Code signing server is ready')
- while True:
- logger_server.info('Waiting for the signing request in %s...',
- self.unsigned_storage_dir)
- request_id = self.wait_for_sign_request()
-
- logger_server.info(
- f'Beging signign procedure for request ID {request_id}.')
- self.run_signing_pipeline(request_id)
-
- ############################################################################
- # Command executing.
- #
- # Abstracted to a degree that allows to run commands from a foreign
- # platform.
- # The goal with this is to allow performing dry-run tests of code signer
- # server from other platforms (for example, to test that macOS code signer
- # does what it is supposed to after doing a refactor on Linux).
-
- # TODO(sergey): What is the type annotation for the command?
- def run_command_or_mock(self, command, platform: util.Platform) -> None:
- """
- Run given command if current platform matches given one
-
- If the platform is different then it will only be printed allowing
- to verify logic of the code signing process.
- """
-
- if platform != self.platform:
- logger_server.info(
- f'Will run command for {platform}: {command}')
- return
-
- logger_server.info(f'Running command: {command}')
- subprocess.run(command)
-
- # TODO(sergey): What is the type annotation for the command?
- def check_output_or_mock(self, command,
- platform: util.Platform,
- allow_nonzero_exit_code=False) -> str:
- """
- Run given command if current platform matches given one
-
- If the platform is different then it will only be printed allowing
- to verify logic of the code signing process.
-
- If allow_nonzero_exit_code is truth then the output will be returned
- even if application quit with non-zero exit code.
- Otherwise an subprocess.CalledProcessError exception will be raised
- in such case.
- """
-
- if platform != self.platform:
- logger_server.info(
- f'Will run command for {platform}: {command}')
- return
-
- if allow_nonzero_exit_code:
- process = subprocess.Popen(command,
- stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT)
- output = process.communicate()[0]
- return output.decode()
-
- logger_server.info(f'Running command: {command}')
- return subprocess.check_output(
- command, stderr=subprocess.STDOUT).decode()
diff --git a/build_files/buildbot/codesign/config_builder.py b/build_files/buildbot/codesign/config_builder.py
deleted file mode 100644
index 1f41619ba13..00000000000
--- a/build_files/buildbot/codesign/config_builder.py
+++ /dev/null
@@ -1,62 +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>
-
-# Configuration of a code signer which is specific to the code running from
-# buildbot's worker.
-
-import sys
-
-from pathlib import Path
-
-import codesign.util as util
-
-from codesign.config_common import *
-
-platform = util.get_current_platform()
-if platform == util.Platform.LINUX:
- SHARED_STORAGE_DIR = Path('/data/codesign')
-elif platform == util.Platform.WINDOWS:
- SHARED_STORAGE_DIR = Path('Z:\\codesign')
-elif platform == util.Platform.MACOS:
- SHARED_STORAGE_DIR = Path('/Volumes/codesign_macos/codesign')
-
-# https://docs.python.org/3/library/logging.config.html#configuration-dictionary-schema
-LOGGING = {
- 'version': 1,
- 'formatters': {
- 'default': {'format': '%(asctime)-15s %(levelname)8s %(name)s %(message)s'}
- },
- 'handlers': {
- 'console': {
- 'class': 'logging.StreamHandler',
- 'formatter': 'default',
- 'stream': 'ext://sys.stderr',
- }
- },
- 'loggers': {
- 'codesign': {'level': 'INFO'},
- },
- 'root': {
- 'level': 'WARNING',
- 'handlers': [
- 'console',
- ],
- }
-}
diff --git a/build_files/buildbot/codesign/config_common.py b/build_files/buildbot/codesign/config_common.py
deleted file mode 100644
index a37bc731dc0..00000000000
--- a/build_files/buildbot/codesign/config_common.py
+++ /dev/null
@@ -1,36 +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>
-
-from pathlib import Path
-
-# Timeout in seconds for the signing process.
-#
-# This is how long buildbot packing step will wait signing server to
-# perform signing.
-#
-# NOTE: Notarization could take a long time, hence the rather high value
-# here. Might consider using different timeout for different platforms.
-TIMEOUT_IN_SECONDS = 45 * 60 * 60
-
-# Directory which is shared across buildbot worker and signing server.
-#
-# This is where worker puts files requested for signing as well as where
-# server puts signed files.
-SHARED_STORAGE_DIR: Path
diff --git a/build_files/buildbot/codesign/config_server_template.py b/build_files/buildbot/codesign/config_server_template.py
deleted file mode 100644
index ff97ed15fa5..00000000000
--- a/build_files/buildbot/codesign/config_server_template.py
+++ /dev/null
@@ -1,101 +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>
-
-# Configuration of a code signer which is specific to the code signing server.
-#
-# NOTE: DO NOT put any sensitive information here, put it in an actual
-# configuration on the signing machine.
-
-from pathlib import Path
-
-from codesign.config_common import *
-
-CODESIGN_DIRECTORY = Path(__file__).absolute().parent
-BLENDER_GIT_ROOT_DIRECTORY = CODESIGN_DIRECTORY.parent.parent.parent
-
-################################################################################
-# Common configuration.
-
-# Directory where folders for codesign requests and signed result are stored.
-# For example, /data/codesign
-SHARED_STORAGE_DIR: Path
-
-################################################################################
-# macOS-specific configuration.
-
-MACOS_ENTITLEMENTS_FILE = \
- BLENDER_GIT_ROOT_DIRECTORY / 'release' / 'darwin' / 'entitlements.plist'
-
-# Identity of the Developer ID Application certificate which is to be used for
-# codesign tool.
-# Use `security find-identity -v -p codesigning` to find the identity.
-#
-# NOTE: This identity is just an example from release/darwin/README.txt.
-MACOS_CODESIGN_IDENTITY = 'AE825E26F12D08B692F360133210AF46F4CF7B97'
-
-# User name (Apple ID) which will be used to request notarization.
-MACOS_XCRUN_USERNAME = 'me@example.com'
-
-# One-time application password which will be used to request notarization.
-MACOS_XCRUN_PASSWORD = '@keychain:altool-password'
-
-# Timeout in seconds within which the notarial office is supposed to reply.
-MACOS_NOTARIZE_TIMEOUT_IN_SECONDS = 60 * 60
-
-################################################################################
-# Windows-specific configuration.
-
-# URL to the timestamping authority.
-WIN_TIMESTAMP_AUTHORITY_URL = 'http://timestamp.digicert.com'
-
-# Full path to the certificate used for signing.
-#
-# The path and expected file format might vary depending on a platform.
-#
-# On Windows it is usually is a PKCS #12 key (.pfx), so the path will look
-# like Path('C:\\Secret\\Blender.pfx').
-WIN_CERTIFICATE_FILEPATH: Path
-
-################################################################################
-# Logging configuration, common for all platforms.
-
-# https://docs.python.org/3/library/logging.config.html#configuration-dictionary-schema
-LOGGING = {
- 'version': 1,
- 'formatters': {
- 'default': {'format': '%(asctime)-15s %(levelname)8s %(name)s %(message)s'}
- },
- 'handlers': {
- 'console': {
- 'class': 'logging.StreamHandler',
- 'formatter': 'default',
- 'stream': 'ext://sys.stderr',
- }
- },
- 'loggers': {
- 'codesign': {'level': 'INFO'},
- },
- 'root': {
- 'level': 'WARNING',
- 'handlers': [
- 'console',
- ],
- }
-}
diff --git a/build_files/buildbot/codesign/exception.py b/build_files/buildbot/codesign/exception.py
deleted file mode 100644
index 6c8a9f262a5..00000000000
--- a/build_files/buildbot/codesign/exception.py
+++ /dev/null
@@ -1,26 +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>
-
-class CodeSignException(Exception):
- message: str
-
- def __init__(self, message):
- self.message = message
- super().__init__(self.message)
diff --git a/build_files/buildbot/codesign/linux_code_signer.py b/build_files/buildbot/codesign/linux_code_signer.py
deleted file mode 100644
index 04935f67832..00000000000
--- a/build_files/buildbot/codesign/linux_code_signer.py
+++ /dev/null
@@ -1,72 +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>
-
-# NOTE: This is a no-op signer (since there isn't really a procedure to sign
-# Linux binaries yet). Used to debug and verify the code signing routines on
-# a Linux environment.
-
-import logging
-
-from pathlib import Path
-from typing import List
-
-from codesign.absolute_and_relative_filename import AbsoluteAndRelativeFileName
-from codesign.base_code_signer import BaseCodeSigner
-
-logger = logging.getLogger(__name__)
-logger_server = logger.getChild('server')
-
-
-class LinuxCodeSigner(BaseCodeSigner):
- def is_active(self) -> bool:
- """
- Check whether this signer is active.
-
- if it is inactive, no files will be signed.
-
- Is used to be able to debug code signing pipeline on Linux, where there
- is no code signing happening in the actual buildbot and release
- environment.
- """
- return False
-
- def check_file_is_to_be_signed(
- self, file: AbsoluteAndRelativeFileName) -> bool:
- if file.relative_filepath == Path('blender'):
- return True
- if (file.relative_filepath.parts[-3:-1] == ('python', 'bin') and
- file.relative_filepath.name.startwith('python')):
- return True
- if file.relative_filepath.suffix == '.so':
- return True
- return False
-
- def collect_files_to_sign(self, path: Path) \
- -> List[AbsoluteAndRelativeFileName]:
- if not self.is_active():
- return []
-
- return super().collect_files_to_sign(path)
-
- def sign_all_files(self, files: List[AbsoluteAndRelativeFileName]) -> None:
- num_files = len(files)
- for file_index, file in enumerate(files):
- logger.info('Server: Signed file [%d/%d] %s',
- file_index + 1, num_files, file.relative_filepath)
diff --git a/build_files/buildbot/codesign/macos_code_signer.py b/build_files/buildbot/codesign/macos_code_signer.py
deleted file mode 100644
index f03dad8e1d6..00000000000
--- a/build_files/buildbot/codesign/macos_code_signer.py
+++ /dev/null
@@ -1,456 +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>
-
-import logging
-import re
-import stat
-import subprocess
-import time
-
-from pathlib import Path
-from typing import List
-
-import codesign.util as util
-
-from buildbot_utils import Builder
-
-from codesign.absolute_and_relative_filename import AbsoluteAndRelativeFileName
-from codesign.base_code_signer import BaseCodeSigner
-from codesign.exception import CodeSignException
-
-logger = logging.getLogger(__name__)
-logger_server = logger.getChild('server')
-
-# NOTE: Check is done as filename.endswith(), so keep the dot
-EXTENSIONS_TO_BE_SIGNED = {'.dylib', '.so', '.dmg'}
-
-# Prefixes of a file (not directory) name which are to be signed.
-# Used to sign extra executable files in Contents/Resources.
-NAME_PREFIXES_TO_BE_SIGNED = {'python'}
-
-
-class NotarizationException(CodeSignException):
- pass
-
-
-def is_file_from_bundle(file: AbsoluteAndRelativeFileName) -> bool:
- """
- Check whether file is coming from an .app bundle
- """
- parts = file.relative_filepath.parts
- if not parts:
- return False
- if not parts[0].endswith('.app'):
- return False
- return True
-
-
-def get_bundle_from_file(
- file: AbsoluteAndRelativeFileName) -> AbsoluteAndRelativeFileName:
- """
- Get AbsoluteAndRelativeFileName descriptor of bundle
- """
- assert(is_file_from_bundle(file))
-
- parts = file.relative_filepath.parts
- bundle_name = parts[0]
-
- base_dir = file.base_dir
- bundle_filepath = file.base_dir / bundle_name
- return AbsoluteAndRelativeFileName(base_dir, bundle_filepath)
-
-
-def is_bundle_executable_file(file: AbsoluteAndRelativeFileName) -> bool:
- """
- Check whether given file is an executable within an app bundle
- """
- if not is_file_from_bundle(file):
- return False
-
- parts = file.relative_filepath.parts
- num_parts = len(parts)
- if num_parts < 3:
- return False
-
- if parts[1:3] != ('Contents', 'MacOS'):
- return False
-
- return True
-
-
-def xcrun_field_value_from_output(field: str, output: str) -> str:
- """
- Get value of a given field from xcrun output.
-
- If field is not found empty string is returned.
- """
-
- field_prefix = field + ': '
- for line in output.splitlines():
- line = line.strip()
- if line.startswith(field_prefix):
- return line[len(field_prefix):]
- return ''
-
-
-class MacOSCodeSigner(BaseCodeSigner):
- def check_file_is_to_be_signed(
- self, file: AbsoluteAndRelativeFileName) -> bool:
- if file.relative_filepath.name.startswith('.'):
- return False
-
- if is_bundle_executable_file(file):
- return True
-
- base_name = file.relative_filepath.name
- if any(base_name.startswith(prefix)
- for prefix in NAME_PREFIXES_TO_BE_SIGNED):
- return True
-
- mode = file.absolute_filepath.lstat().st_mode
- if mode & stat.S_IXUSR != 0:
- file_output = subprocess.check_output(
- ("file", file.absolute_filepath)).decode()
- if "64-bit executable" in file_output:
- return True
-
- return file.relative_filepath.suffix in EXTENSIONS_TO_BE_SIGNED
-
- def collect_files_to_sign(self, path: Path) \
- -> List[AbsoluteAndRelativeFileName]:
- # Include all files when signing app or dmg bundle: all the files are
- # needed to do valid signature of bundle.
- if path.name.endswith('.app'):
- return AbsoluteAndRelativeFileName.recursively_from_directory(path)
- if path.is_dir():
- files = []
- for child in path.iterdir():
- if child.name.endswith('.app'):
- current_files = AbsoluteAndRelativeFileName.recursively_from_directory(
- child)
- else:
- current_files = super().collect_files_to_sign(child)
- for current_file in current_files:
- files.append(AbsoluteAndRelativeFileName(
- path, current_file.absolute_filepath))
- return files
- return super().collect_files_to_sign(path)
-
- ############################################################################
- # Codesign.
-
- def codesign_remove_signature(
- self, file: AbsoluteAndRelativeFileName) -> None:
- """
- Make sure given file does not have codesign signature
-
- This is needed because codesigning is not possible for file which has
- signature already.
- """
-
- logger_server.info(
- 'Removing codesign signature from %s...', file.relative_filepath)
-
- command = ['codesign', '--remove-signature', file.absolute_filepath]
- self.run_command_or_mock(command, util.Platform.MACOS)
-
- def codesign_file(
- self, file: AbsoluteAndRelativeFileName) -> None:
- """
- Sign given file
-
- NOTE: File must not have any signatures.
- """
-
- logger_server.info(
- 'Codesigning %s...', file.relative_filepath)
-
- entitlements_file = self.config.MACOS_ENTITLEMENTS_FILE
- command = ['codesign',
- '--timestamp',
- '--options', 'runtime',
- f'--entitlements={entitlements_file}',
- '--sign', self.config.MACOS_CODESIGN_IDENTITY,
- file.absolute_filepath]
- self.run_command_or_mock(command, util.Platform.MACOS)
-
- def codesign_all_files(self, files: List[AbsoluteAndRelativeFileName]) -> None:
- """
- Run codesign tool on all eligible files in the given list.
-
- Will ignore all files which are not to be signed. For the rest will
- remove possible existing signature and add a new signature.
- """
-
- num_files = len(files)
- have_ignored_files = False
- signed_files = []
- for file_index, file in enumerate(files):
- # Ignore file if it is not to be signed.
- # Allows to manually construct ZIP of a bundle and get it signed.
- if not self.check_file_is_to_be_signed(file):
- logger_server.info(
- 'Ignoring file [%d/%d] %s',
- file_index + 1, num_files, file.relative_filepath)
- have_ignored_files = True
- continue
-
- logger_server.info(
- 'Running codesigning routines for file [%d/%d] %s...',
- file_index + 1, num_files, file.relative_filepath)
-
- self.codesign_remove_signature(file)
- self.codesign_file(file)
-
- signed_files.append(file)
-
- if have_ignored_files:
- logger_server.info('Signed %d files:', len(signed_files))
- num_signed_files = len(signed_files)
- for file_index, signed_file in enumerate(signed_files):
- logger_server.info(
- '- [%d/%d] %s',
- file_index + 1, num_signed_files,
- signed_file.relative_filepath)
-
- def codesign_bundles(
- self, files: List[AbsoluteAndRelativeFileName]) -> None:
- """
- Codesign all .app bundles in the given list of files.
-
- Bundle is deducted from paths of the files, and every bundle is only
- signed once.
- """
-
- signed_bundles = set()
- extra_files = []
-
- for file in files:
- if not is_file_from_bundle(file):
- continue
- bundle = get_bundle_from_file(file)
- bundle_name = bundle.relative_filepath
- if bundle_name in signed_bundles:
- continue
-
- logger_server.info('Running codesign routines on bundle %s',
- bundle_name)
-
- # It is not possible to remove signature from DMG.
- if bundle.relative_filepath.name.endswith('.app'):
- self.codesign_remove_signature(bundle)
- self.codesign_file(bundle)
-
- signed_bundles.add(bundle_name)
-
- # Codesign on a bundle adds an extra folder with information.
- # It needs to be compied to the source.
- code_signature_directory = \
- bundle.absolute_filepath / 'Contents' / '_CodeSignature'
- code_signature_files = \
- AbsoluteAndRelativeFileName.recursively_from_directory(
- code_signature_directory)
- for code_signature_file in code_signature_files:
- bundle_relative_file = AbsoluteAndRelativeFileName(
- bundle.base_dir,
- code_signature_directory /
- code_signature_file.relative_filepath)
- extra_files.append(bundle_relative_file)
-
- files.extend(extra_files)
-
- ############################################################################
- # Notarization.
-
- def notarize_get_bundle_id(self, file: AbsoluteAndRelativeFileName) -> str:
- """
- Get bundle ID which will be used to notarize DMG
- """
- name = file.relative_filepath.name
- app_name = name.split('-', 2)[0].lower()
-
- app_name_words = app_name.split()
- if len(app_name_words) > 1:
- app_name_id = ''.join(word.capitalize() for word in app_name_words)
- else:
- app_name_id = app_name_words[0]
-
- # TODO(sergey): Consider using "alpha" for buildbot builds.
- return f'org.blenderfoundation.{app_name_id}.release'
-
- def notarize_request(self, file) -> str:
- """
- Request notarization of the given file.
-
- Returns UUID of the notarization request. If error occurred None is
- returned instead of UUID.
- """
-
- bundle_id = self.notarize_get_bundle_id(file)
- logger_server.info('Bundle ID: %s', bundle_id)
-
- logger_server.info('Submitting file to the notarial office.')
- command = [
- 'xcrun', 'altool', '--notarize-app', '--verbose',
- '-f', file.absolute_filepath,
- '--primary-bundle-id', bundle_id,
- '--username', self.config.MACOS_XCRUN_USERNAME,
- '--password', self.config.MACOS_XCRUN_PASSWORD]
-
- output = self.check_output_or_mock(
- command, util.Platform.MACOS, allow_nonzero_exit_code=True)
-
- for line in output.splitlines():
- line = line.strip()
- if line.startswith('RequestUUID = '):
- request_uuid = line[14:]
- return request_uuid
-
- # Check whether the package has been already submitted.
- if 'The software asset has already been uploaded.' in line:
- request_uuid = re.sub(
- '.*The upload ID is ([A-Fa-f0-9\-]+).*', '\\1', line)
- logger_server.warning(
- f'The package has been already submitted under UUID {request_uuid}')
- return request_uuid
-
- logger_server.error(output)
- logger_server.error('xcrun command did not report RequestUUID')
- return None
-
- def notarize_review_status(self, xcrun_output: str) -> bool:
- """
- Review status returned by xcrun's notarization info
-
- Returns truth if the notarization process has finished.
- If there are errors during notarization, a NotarizationException()
- exception is thrown with status message from the notarial office.
- """
-
- # Parse status and message
- status = xcrun_field_value_from_output('Status', xcrun_output)
- status_message = xcrun_field_value_from_output(
- 'Status Message', xcrun_output)
-
- if status == 'success':
- logger_server.info(
- 'Package successfully notarized: %s', status_message)
- return True
-
- if status == 'invalid':
- logger_server.error(xcrun_output)
- logger_server.error(
- 'Package notarization has failed: %s', status_message)
- raise NotarizationException(status_message)
-
- if status == 'in progress':
- return False
-
- logger_server.info(
- 'Unknown notarization status %s (%s)', status, status_message)
-
- return False
-
- def notarize_wait_result(self, request_uuid: str) -> None:
- """
- Wait for until notarial office have a reply
- """
-
- logger_server.info(
- 'Waiting for a result from the notarization office.')
-
- command = ['xcrun', 'altool',
- '--notarization-info', request_uuid,
- '--username', self.config.MACOS_XCRUN_USERNAME,
- '--password', self.config.MACOS_XCRUN_PASSWORD]
-
- time_start = time.monotonic()
- timeout_in_seconds = self.config.MACOS_NOTARIZE_TIMEOUT_IN_SECONDS
-
- while True:
- xcrun_output = self.check_output_or_mock(
- command, util.Platform.MACOS, allow_nonzero_exit_code=True)
-
- if self.notarize_review_status(xcrun_output):
- break
-
- logger_server.info('Keep waiting for notarization office.')
- time.sleep(30)
-
- time_slept_in_seconds = time.monotonic() - time_start
- if time_slept_in_seconds > timeout_in_seconds:
- logger_server.error(
- "Notarial office didn't reply in %f seconds.",
- timeout_in_seconds)
-
- def notarize_staple(self, file: AbsoluteAndRelativeFileName) -> bool:
- """
- Staple notarial label on the file
- """
-
- logger_server.info('Stapling notarial stamp.')
-
- command = ['xcrun', 'stapler', 'staple', '-v', file.absolute_filepath]
- self.check_output_or_mock(command, util.Platform.MACOS)
-
- def notarize_dmg(self, file: AbsoluteAndRelativeFileName) -> bool:
- """
- Run entire pipeline to get DMG notarized.
- """
- logger_server.info('Begin notarization routines on %s',
- file.relative_filepath)
-
- # Submit file for notarization.
- request_uuid = self.notarize_request(file)
- if not request_uuid:
- return False
- logger_server.info('Received Request UUID: %s', request_uuid)
-
- # Wait for the status from the notarization office.
- if not self.notarize_wait_result(request_uuid):
- return False
-
- # Staple.
- self.notarize_staple(file)
-
- def notarize_all_dmg(
- self, files: List[AbsoluteAndRelativeFileName]) -> bool:
- """
- Notarize all DMG images from the input.
-
- Images are supposed to be codesigned already.
- """
- for file in files:
- if not file.relative_filepath.name.endswith('.dmg'):
- continue
- if not self.check_file_is_to_be_signed(file):
- continue
-
- self.notarize_dmg(file)
-
- ############################################################################
- # Entry point.
-
- def sign_all_files(self, files: List[AbsoluteAndRelativeFileName]) -> None:
- # TODO(sergey): Handle errors somehow.
-
- self.codesign_all_files(files)
- self.codesign_bundles(files)
- self.notarize_all_dmg(files)
diff --git a/build_files/buildbot/codesign/simple_code_signer.py b/build_files/buildbot/codesign/simple_code_signer.py
deleted file mode 100644
index 674d9e9ce9e..00000000000
--- a/build_files/buildbot/codesign/simple_code_signer.py
+++ /dev/null
@@ -1,52 +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>
-
-
-import logging.config
-import sys
-
-from pathlib import Path
-from typing import Optional
-
-import codesign.config_builder
-import codesign.util as util
-from codesign.base_code_signer import BaseCodeSigner
-
-
-class SimpleCodeSigner:
- code_signer: Optional[BaseCodeSigner]
-
- def __init__(self):
- platform = util.get_current_platform()
- if platform == util.Platform.LINUX:
- from codesign.linux_code_signer import LinuxCodeSigner
- self.code_signer = LinuxCodeSigner(codesign.config_builder)
- elif platform == util.Platform.MACOS:
- from codesign.macos_code_signer import MacOSCodeSigner
- self.code_signer = MacOSCodeSigner(codesign.config_builder)
- elif platform == util.Platform.WINDOWS:
- from codesign.windows_code_signer import WindowsCodeSigner
- self.code_signer = WindowsCodeSigner(codesign.config_builder)
- else:
- self.code_signer = None
-
- def sign_file_or_directory(self, path: Path) -> None:
- logging.config.dictConfig(codesign.config_builder.LOGGING)
- self.code_signer.run_buildbot_path_sign_pipeline(path)
diff --git a/build_files/buildbot/codesign/util.py b/build_files/buildbot/codesign/util.py
deleted file mode 100644
index e67292dd049..00000000000
--- a/build_files/buildbot/codesign/util.py
+++ /dev/null
@@ -1,54 +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>
-
-import sys
-
-from enum import Enum
-from pathlib import Path
-
-
-class Platform(Enum):
- LINUX = 1
- MACOS = 2
- WINDOWS = 3
-
-
-def get_current_platform() -> Platform:
- if sys.platform == 'linux':
- return Platform.LINUX
- elif sys.platform == 'darwin':
- return Platform.MACOS
- elif sys.platform == 'win32':
- return Platform.WINDOWS
- raise Exception(f'Unknown platform {sys.platform}')
-
-
-def ensure_file_does_not_exist_or_die(filepath: Path) -> None:
- """
- If the file exists, unlink it.
- If the file path exists and is not a file an assert will trigger.
- If the file path does not exists nothing happens.
- """
- if not filepath.exists():
- return
- if not filepath.is_file():
- # TODO(sergey): Provide information about what the filepath actually is.
- raise SystemExit(f'{filepath} is expected to be a file, but is not')
- filepath.unlink()
diff --git a/build_files/buildbot/codesign/windows_code_signer.py b/build_files/buildbot/codesign/windows_code_signer.py
deleted file mode 100644
index db185788a56..00000000000
--- a/build_files/buildbot/codesign/windows_code_signer.py
+++ /dev/null
@@ -1,117 +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>
-
-import logging
-
-from pathlib import Path
-from typing import List
-
-import codesign.util as util
-
-from buildbot_utils import Builder
-
-from codesign.absolute_and_relative_filename import AbsoluteAndRelativeFileName
-from codesign.base_code_signer import BaseCodeSigner
-from codesign.exception import CodeSignException
-
-logger = logging.getLogger(__name__)
-logger_server = logger.getChild('server')
-
-# NOTE: Check is done as filename.endswith(), so keep the dot
-EXTENSIONS_TO_BE_SIGNED = {'.exe', '.dll', '.pyd', '.msi'}
-
-BLACKLIST_FILE_PREFIXES = (
- 'api-ms-', 'concrt', 'msvcp', 'ucrtbase', 'vcomp', 'vcruntime')
-
-
-class SigntoolException(CodeSignException):
- pass
-
-class WindowsCodeSigner(BaseCodeSigner):
- def check_file_is_to_be_signed(
- self, file: AbsoluteAndRelativeFileName) -> bool:
- base_name = file.relative_filepath.name
- if any(base_name.startswith(prefix)
- for prefix in BLACKLIST_FILE_PREFIXES):
- return False
-
- return file.relative_filepath.suffix in EXTENSIONS_TO_BE_SIGNED
-
-
- def get_sign_command_prefix(self) -> List[str]:
- return [
- 'signtool', 'sign', '/v',
- '/f', self.config.WIN_CERTIFICATE_FILEPATH,
- '/tr', self.config.WIN_TIMESTAMP_AUTHORITY_URL]
-
-
- def run_codesign_tool(self, filepath: Path) -> None:
- command = self.get_sign_command_prefix() + [filepath]
-
- try:
- codesign_output = self.check_output_or_mock(command, util.Platform.WINDOWS)
- except subprocess.CalledProcessError as e:
- raise SigntoolException(f'Error running signtool {e}')
-
- logger_server.info(f'signtool output:\n{codesign_output}')
-
- got_number_of_success = False
-
- for line in codesign_output.split('\n'):
- line_clean = line.strip()
- line_clean_lower = line_clean.lower()
-
- if line_clean_lower.startswith('number of warnings') or \
- line_clean_lower.startswith('number of errors'):
- number = int(line_clean_lower.split(':')[1])
- if number != 0:
- raise SigntoolException('Non-clean success of signtool')
-
- if line_clean_lower.startswith('number of files successfully signed'):
- got_number_of_success = True
- number = int(line_clean_lower.split(':')[1])
- if number != 1:
- raise SigntoolException('Signtool did not consider codesign a success')
-
- if not got_number_of_success:
- raise SigntoolException('Signtool did not report number of files signed')
-
-
- def sign_all_files(self, files: List[AbsoluteAndRelativeFileName]) -> None:
- # NOTE: Sign files one by one to avoid possible command line length
- # overflow (which could happen if we ever decide to sign every binary
- # in the install folder, for example).
- #
- # TODO(sergey): Consider doing batched signing of handful of files in
- # one go (but only if this actually known to be much faster).
- num_files = len(files)
- for file_index, file in enumerate(files):
- # Ignore file if it is not to be signed.
- # Allows to manually construct ZIP of package and get it signed.
- if not self.check_file_is_to_be_signed(file):
- logger_server.info(
- 'Ignoring file [%d/%d] %s',
- file_index + 1, num_files, file.relative_filepath)
- continue
-
- logger_server.info(
- 'Running signtool command for file [%d/%d] %s...',
- file_index + 1, num_files, file.relative_filepath)
- self.run_codesign_tool(file.absolute_filepath)
diff --git a/build_files/buildbot/codesign_server_linux.py b/build_files/buildbot/codesign_server_linux.py
deleted file mode 100755
index be3065e640d..00000000000
--- a/build_files/buildbot/codesign_server_linux.py
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/usr/bin/env python3
-
-# ##### 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>
-
-# NOTE: This is a no-op signer (since there isn't really a procedure to sign
-# Linux binaries yet). Used to debug and verify the code signing routines on
-# a Linux environment.
-
-import logging.config
-from pathlib import Path
-from typing import List
-
-from codesign.linux_code_signer import LinuxCodeSigner
-import codesign.config_server
-
-if __name__ == "__main__":
- logging.config.dictConfig(codesign.config_server.LOGGING)
- code_signer = LinuxCodeSigner(codesign.config_server)
- code_signer.run_signing_server()
diff --git a/build_files/buildbot/codesign_server_macos.py b/build_files/buildbot/codesign_server_macos.py
deleted file mode 100755
index 1bdb012fe67..00000000000
--- a/build_files/buildbot/codesign_server_macos.py
+++ /dev/null
@@ -1,41 +0,0 @@
-#!/usr/bin/env python3
-
-# ##### 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>
-
-import logging.config
-from pathlib import Path
-from typing import List
-
-from codesign.macos_code_signer import MacOSCodeSigner
-import codesign.config_server
-
-if __name__ == "__main__":
- entitlements_file = codesign.config_server.MACOS_ENTITLEMENTS_FILE
- if not entitlements_file.exists():
- raise SystemExit(
- 'Entitlements file {entitlements_file} does not exist.')
- if not entitlements_file.is_file():
- raise SystemExit(
- 'Entitlements file {entitlements_file} is not a file.')
-
- logging.config.dictConfig(codesign.config_server.LOGGING)
- code_signer = MacOSCodeSigner(codesign.config_server)
- code_signer.run_signing_server()
diff --git a/build_files/buildbot/codesign_server_windows.bat b/build_files/buildbot/codesign_server_windows.bat
deleted file mode 100644
index 82680f30eb4..00000000000
--- a/build_files/buildbot/codesign_server_windows.bat
+++ /dev/null
@@ -1,11 +0,0 @@
-@echo off
-
-rem This is an entry point of the codesign server for Windows.
-rem It makes sure that signtool.exe is within the current PATH and can be
-rem used by the Python script.
-
-SETLOCAL
-
-set PATH=C:\Program Files (x86)\Windows Kits\10\App Certification Kit;%PATH%
-
-codesign_server_windows.py
diff --git a/build_files/buildbot/codesign_server_windows.py b/build_files/buildbot/codesign_server_windows.py
deleted file mode 100755
index 97ea4fd6756..00000000000
--- a/build_files/buildbot/codesign_server_windows.py
+++ /dev/null
@@ -1,54 +0,0 @@
-#!/usr/bin/env python3
-
-# ##### 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>
-
-# Implementation of codesign server for Windows.
-#
-# NOTE: If signtool.exe is not in the PATH use codesign_server_windows.bat
-
-import logging.config
-import shutil
-
-from pathlib import Path
-from typing import List
-
-import codesign.util as util
-
-from codesign.windows_code_signer import WindowsCodeSigner
-import codesign.config_server
-
-if __name__ == "__main__":
- logging.config.dictConfig(codesign.config_server.LOGGING)
-
- logger = logging.getLogger(__name__)
- logger_server = logger.getChild('server')
-
- # TODO(sergey): Consider moving such sanity checks into
- # CodeSigner.check_environment_or_die().
- if not shutil.which('signtool.exe'):
- if util.get_current_platform() == util.Platform.WINDOWS:
- raise SystemExit("signtool.exe is not found in %PATH%")
- logger_server.info(
- 'signtool.exe not found, '
- 'but will not be used on this foreign platform')
-
- code_signer = WindowsCodeSigner(codesign.config_server)
- code_signer.run_signing_server()
diff --git a/build_files/buildbot/worker_bundle_dmg.py b/build_files/buildbot/worker_bundle_dmg.py
deleted file mode 100755
index 56e0d7da88e..00000000000
--- a/build_files/buildbot/worker_bundle_dmg.py
+++ /dev/null
@@ -1,551 +0,0 @@
-#!/usr/bin/env python3
-
-# ##### 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 #####
-
-import argparse
-import re
-import shutil
-import subprocess
-import sys
-import time
-
-from pathlib import Path
-from tempfile import TemporaryDirectory, NamedTemporaryFile
-from typing import List
-
-BUILDBOT_DIRECTORY = Path(__file__).absolute().parent
-CODESIGN_SCRIPT = BUILDBOT_DIRECTORY / 'worker_codesign.py'
-BLENDER_GIT_ROOT_DIRECTORY = BUILDBOT_DIRECTORY.parent.parent
-DARWIN_DIRECTORY = BLENDER_GIT_ROOT_DIRECTORY / 'release' / 'darwin'
-
-
-# Extra size which is added on top of actual files size when estimating size
-# of destination DNG.
-EXTRA_DMG_SIZE_IN_BYTES = 800 * 1024 * 1024
-
-################################################################################
-# Common utilities
-
-
-def get_directory_size(root_directory: Path) -> int:
- """
- Get size of directory on disk
- """
-
- total_size = 0
- for file in root_directory.glob('**/*'):
- total_size += file.lstat().st_size
- return total_size
-
-
-################################################################################
-# DMG bundling specific logic
-
-def create_argument_parser():
- parser = argparse.ArgumentParser()
- parser.add_argument(
- 'source_dir',
- type=Path,
- help='Source directory which points to either existing .app bundle'
- 'or to a directory with .app bundles.')
- parser.add_argument(
- '--background-image',
- type=Path,
- help="Optional background picture which will be set on the DMG."
- "If not provided default Blender's one is used.")
- parser.add_argument(
- '--volume-name',
- type=str,
- help='Optional name of a volume which will be used for DMG.')
- parser.add_argument(
- '--dmg',
- type=Path,
- help='Optional argument which points to a final DMG file name.')
- parser.add_argument(
- '--applescript',
- type=Path,
- help="Optional path to applescript to set up folder looks of DMG."
- "If not provided default Blender's one is used.")
- parser.add_argument(
- '--codesign',
- action="store_true",
- help="Code sign and notarize DMG contents.")
- return parser
-
-
-def collect_app_bundles(source_dir: Path) -> List[Path]:
- """
- Collect all app bundles which are to be put into DMG
-
- If the source directory points to FOO.app it will be the only app bundle
- packed.
-
- Otherwise all .app bundles from given directory are placed to a single
- DMG.
- """
-
- if source_dir.name.endswith('.app'):
- return [source_dir]
-
- app_bundles = []
- for filename in source_dir.glob('*'):
- if not filename.is_dir():
- continue
- if not filename.name.endswith('.app'):
- continue
-
- app_bundles.append(filename)
-
- return app_bundles
-
-
-def collect_and_log_app_bundles(source_dir: Path) -> List[Path]:
- app_bundles = collect_app_bundles(source_dir)
-
- if not app_bundles:
- print('No app bundles found for packing')
- return
-
- print(f'Found {len(app_bundles)} to pack:')
- for app_bundle in app_bundles:
- print(f'- {app_bundle}')
-
- return app_bundles
-
-
-def estimate_dmg_size(app_bundles: List[Path]) -> int:
- """
- Estimate size of DMG to hold requested app bundles
-
- The size is based on actual size of all files in all bundles plus some
- space to compensate for different size-on-disk plus some space to hold
- codesign signatures.
-
- Is better to be on a high side since the empty space is compressed, but
- lack of space might cause silent failures later on.
- """
-
- app_bundles_size = 0
- for app_bundle in app_bundles:
- app_bundles_size += get_directory_size(app_bundle)
-
- return app_bundles_size + EXTRA_DMG_SIZE_IN_BYTES
-
-
-def copy_app_bundles_to_directory(app_bundles: List[Path],
- directory: Path) -> None:
- """
- Copy all bundles to a given directory
-
- This directory is what the DMG will be created from.
- """
- for app_bundle in app_bundles:
- print(f'Copying {app_bundle.name}...')
- shutil.copytree(app_bundle, directory / app_bundle.name)
-
-
-def get_main_app_bundle(app_bundles: List[Path]) -> Path:
- """
- Get application bundle main for the installation
- """
- return app_bundles[0]
-
-
-def create_dmg_image(app_bundles: List[Path],
- dmg_filepath: Path,
- volume_name: str) -> None:
- """
- Create DMG disk image and put app bundles in it
-
- No DMG configuration or codesigning is happening here.
- """
-
- if dmg_filepath.exists():
- print(f'Removing existing writable DMG {dmg_filepath}...')
- dmg_filepath.unlink()
-
- print('Preparing directory with app bundles for the DMG...')
- with TemporaryDirectory(prefix='blender-dmg-content-') as content_dir_str:
- # Copy all bundles to a clean directory.
- content_dir = Path(content_dir_str)
- copy_app_bundles_to_directory(app_bundles, content_dir)
-
- # Estimate size of the DMG.
- dmg_size = estimate_dmg_size(app_bundles)
- print(f'Estimated DMG size: {dmg_size:,} bytes.')
-
- # Create the DMG.
- print(f'Creating writable DMG {dmg_filepath}')
- command = ('hdiutil',
- 'create',
- '-size', str(dmg_size),
- '-fs', 'HFS+',
- '-srcfolder', content_dir,
- '-volname', volume_name,
- '-format', 'UDRW',
- dmg_filepath)
- subprocess.run(command)
-
-
-def get_writable_dmg_filepath(dmg_filepath: Path):
- """
- Get file path for writable DMG image
- """
- parent = dmg_filepath.parent
- return parent / (dmg_filepath.stem + '-temp.dmg')
-
-
-def mount_readwrite_dmg(dmg_filepath: Path) -> None:
- """
- Mount writable DMG
-
- Mounting point would be /Volumes/<volume name>
- """
-
- print(f'Mounting read-write DMG ${dmg_filepath}')
- command = ('hdiutil',
- 'attach', '-readwrite',
- '-noverify',
- '-noautoopen',
- dmg_filepath)
- subprocess.run(command)
-
-
-def get_mount_directory_for_volume_name(volume_name: str) -> Path:
- """
- Get directory under which the volume will be mounted
- """
-
- return Path('/Volumes') / volume_name
-
-
-def eject_volume(volume_name: str) -> None:
- """
- Eject given volume, if mounted
- """
- mount_directory = get_mount_directory_for_volume_name(volume_name)
- if not mount_directory.exists():
- return
- mount_directory_str = str(mount_directory)
-
- print(f'Ejecting volume {volume_name}')
-
- # Figure out which device to eject.
- mount_output = subprocess.check_output(['mount']).decode()
- device = ''
- for line in mount_output.splitlines():
- if f'on {mount_directory_str} (' not in line:
- continue
- tokens = line.split(' ', 3)
- if len(tokens) < 3:
- continue
- if tokens[1] != 'on':
- continue
- if device:
- raise Exception(
- f'Multiple devices found for mounting point {mount_directory}')
- device = tokens[0]
-
- if not device:
- raise Exception(
- f'No device found for mounting point {mount_directory}')
-
- print(f'{mount_directory} is mounted as device {device}, ejecting...')
- subprocess.run(['diskutil', 'eject', device])
-
-
-def copy_background_if_needed(background_image_filepath: Path,
- mount_directory: Path) -> None:
- """
- Copy background to the DMG
-
- If the background image is not specified it will not be copied.
- """
-
- if not background_image_filepath:
- print('No background image provided.')
- return
-
- print(f'Copying background image {background_image_filepath}')
-
- destination_dir = mount_directory / '.background'
- destination_dir.mkdir(exist_ok=True)
-
- destination_filepath = destination_dir / background_image_filepath.name
- shutil.copy(background_image_filepath, destination_filepath)
-
-
-def create_applications_link(mount_directory: Path) -> None:
- """
- Create link to /Applications in the given location
- """
-
- print('Creating link to /Applications')
-
- command = ('ln', '-s', '/Applications', mount_directory / ' ')
- subprocess.run(command)
-
-
-def run_applescript(applescript: Path,
- volume_name: str,
- app_bundles: List[Path],
- background_image_filepath: Path) -> None:
- """
- Run given applescript to adjust look and feel of the DMG
- """
-
- main_app_bundle = get_main_app_bundle(app_bundles)
-
- with NamedTemporaryFile(
- mode='w', suffix='.applescript') as temp_applescript:
- print('Adjusting applescript for volume name...')
- # Adjust script to the specific volume name.
- with open(applescript, mode='r') as input:
- for line in input.readlines():
- stripped_line = line.strip()
- if stripped_line.startswith('tell disk'):
- line = re.sub('tell disk ".*"',
- f'tell disk "{volume_name}"',
- line)
- elif stripped_line.startswith('set background picture'):
- if not background_image_filepath:
- continue
- else:
- background_image_short = \
- '.background:' + background_image_filepath.name
- line = re.sub('to file ".*"',
- f'to file "{background_image_short}"',
- line)
- line = line.replace('blender.app', main_app_bundle.name)
- temp_applescript.write(line)
-
- temp_applescript.flush()
-
- print('Running applescript...')
- command = ('osascript', temp_applescript.name)
- subprocess.run(command)
-
- print('Waiting for applescript...')
-
- # NOTE: This is copied from bundle.sh. The exact reason for sleep is
- # still remained a mystery.
- time.sleep(5)
-
-
-def codesign(subject: Path):
- """
- Codesign file or directory
-
- NOTE: For DMG it will also notarize.
- """
-
- command = (CODESIGN_SCRIPT, subject)
- subprocess.run(command)
-
-
-def codesign_app_bundles_in_dmg(mount_directory: str) -> None:
- """
- Code sign all binaries and bundles in the mounted directory
- """
-
- print(f'Codesigning all app bundles in {mount_directory}')
- codesign(mount_directory)
-
-
-def codesign_and_notarize_dmg(dmg_filepath: Path) -> None:
- """
- Run codesign and notarization pipeline on the DMG
- """
-
- print(f'Codesigning and notarizing DMG {dmg_filepath}')
- codesign(dmg_filepath)
-
-
-def compress_dmg(writable_dmg_filepath: Path,
- final_dmg_filepath: Path) -> None:
- """
- Compress temporary read-write DMG
- """
- command = ('hdiutil', 'convert',
- writable_dmg_filepath,
- '-format', 'UDZO',
- '-o', final_dmg_filepath)
-
- if final_dmg_filepath.exists():
- print(f'Removing old compressed DMG {final_dmg_filepath}')
- final_dmg_filepath.unlink()
-
- print('Compressing disk image...')
- subprocess.run(command)
-
-
-def create_final_dmg(app_bundles: List[Path],
- dmg_filepath: Path,
- background_image_filepath: Path,
- volume_name: str,
- applescript: Path,
- codesign: bool) -> None:
- """
- Create DMG with all app bundles
-
- Will take care configuring background, signing all binaries and app bundles
- and notarizing the DMG.
- """
-
- print('Running all routines to create final DMG')
-
- writable_dmg_filepath = get_writable_dmg_filepath(dmg_filepath)
- mount_directory = get_mount_directory_for_volume_name(volume_name)
-
- # Make sure volume is not mounted.
- # If it is mounted it will prevent removing old DMG files and could make
- # it so app bundles are copied to the wrong place.
- eject_volume(volume_name)
-
- create_dmg_image(app_bundles, writable_dmg_filepath, volume_name)
-
- mount_readwrite_dmg(writable_dmg_filepath)
-
- # Run codesign first, prior to copying amything else.
- #
- # This allows to recurs into the content of bundles without worrying about
- # possible interfereice of Application symlink.
- if codesign:
- codesign_app_bundles_in_dmg(mount_directory)
-
- copy_background_if_needed(background_image_filepath, mount_directory)
- create_applications_link(mount_directory)
- run_applescript(applescript, volume_name, app_bundles,
- background_image_filepath)
-
- print('Ejecting read-write DMG image...')
- eject_volume(volume_name)
-
- compress_dmg(writable_dmg_filepath, dmg_filepath)
- writable_dmg_filepath.unlink()
-
- if codesign:
- codesign_and_notarize_dmg(dmg_filepath)
-
-
-def ensure_dmg_extension(filepath: Path) -> Path:
- """
- Make sure given file have .dmg extension
- """
-
- if filepath.suffix != '.dmg':
- return filepath.with_suffix(f'{filepath.suffix}.dmg')
- return filepath
-
-
-def get_dmg_filepath(requested_name: Path, app_bundles: List[Path]) -> Path:
- """
- Get full file path for the final DMG image
-
- Will use the provided one when possible, otherwise will deduct it from
- app bundles.
-
- If the name is deducted, the DMG is stored in the current directory.
- """
-
- if requested_name:
- return ensure_dmg_extension(requested_name.absolute())
-
- # TODO(sergey): This is not necessarily the main one.
- main_bundle = app_bundles[0]
- # Strip .app from the name
- return Path(main_bundle.name[:-4] + '.dmg').absolute()
-
-
-def get_background_image(requested_background_image: Path) -> Path:
- """
- Get effective filepath for the background image
- """
-
- if requested_background_image:
- return requested_background_image.absolute()
-
- return DARWIN_DIRECTORY / 'background.tif'
-
-
-def get_applescript(requested_applescript: Path) -> Path:
- """
- Get effective filepath for the applescript
- """
-
- if requested_applescript:
- return requested_applescript.absolute()
-
- return DARWIN_DIRECTORY / 'blender.applescript'
-
-
-def get_volume_name_from_dmg_filepath(dmg_filepath: Path) -> str:
- """
- Deduct volume name from the DMG path
-
- Will use first part of the DMG file name prior to dash.
- """
-
- tokens = dmg_filepath.stem.split('-')
- words = tokens[0].split()
-
- return ' '.join(word.capitalize() for word in words)
-
-
-def get_volume_name(requested_volume_name: str,
- dmg_filepath: Path) -> str:
- """
- Get effective name for DMG volume
- """
-
- if requested_volume_name:
- return requested_volume_name
-
- return get_volume_name_from_dmg_filepath(dmg_filepath)
-
-
-def main():
- parser = create_argument_parser()
- args = parser.parse_args()
-
- # Get normalized input parameters.
- source_dir = args.source_dir.absolute()
- background_image_filepath = get_background_image(args.background_image)
- applescript = get_applescript(args.applescript)
- codesign = args.codesign
-
- app_bundles = collect_and_log_app_bundles(source_dir)
- if not app_bundles:
- return
-
- dmg_filepath = get_dmg_filepath(args.dmg, app_bundles)
- volume_name = get_volume_name(args.volume_name, dmg_filepath)
-
- print(f'Will produce DMG "{dmg_filepath.name}" (without quotes)')
-
- create_final_dmg(app_bundles,
- dmg_filepath,
- background_image_filepath,
- volume_name,
- applescript,
- codesign)
-
-
-if __name__ == "__main__":
- main()
diff --git a/build_files/buildbot/worker_codesign.cmake b/build_files/buildbot/worker_codesign.cmake
deleted file mode 100644
index f37feaef407..00000000000
--- a/build_files/buildbot/worker_codesign.cmake
+++ /dev/null
@@ -1,44 +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 #####
-
-# This is a script which is used as POST-INSTALL one for regular CMake's
-# INSTALL target.
-# It is used by buildbot workers to sign every binary which is going into
-# the final buundle.
-
-# On Windows Python 3 there only is python.exe, no python3.exe.
-#
-# On other platforms it is possible to have python2 and python3, and a
-# symbolic link to python to either of them. So on those platforms use
-# an explicit Python version.
-if(WIN32)
- set(PYTHON_EXECUTABLE python)
-else()
- set(PYTHON_EXECUTABLE python3)
-endif()
-
-execute_process(
- COMMAND ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_LIST_DIR}/worker_codesign.py"
- "${CMAKE_INSTALL_PREFIX}"
- WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
- RESULT_VARIABLE exit_code
-)
-
-if(NOT exit_code EQUAL "0")
- message(FATAL_ERROR "Non-zero exit code of codesign tool")
-endif()
diff --git a/build_files/buildbot/worker_codesign.py b/build_files/buildbot/worker_codesign.py
deleted file mode 100755
index a82ee98b1b5..00000000000
--- a/build_files/buildbot/worker_codesign.py
+++ /dev/null
@@ -1,74 +0,0 @@
-#!/usr/bin/env python3
-
-# ##### 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 #####
-
-# Helper script which takes care of signing provided location.
-#
-# The location can either be a directory (in which case all eligible binaries
-# will be signed) or a single file (in which case a single file will be signed).
-#
-# This script takes care of all the complexity of communicating between process
-# which requests file to be signed and the code signing server.
-#
-# NOTE: Signing happens in-place.
-
-import argparse
-import sys
-
-from pathlib import Path
-
-from codesign.simple_code_signer import SimpleCodeSigner
-
-
-def create_argument_parser():
- parser = argparse.ArgumentParser()
- parser.add_argument('path_to_sign', type=Path)
- return parser
-
-
-def main():
- parser = create_argument_parser()
- args = parser.parse_args()
- path_to_sign = args.path_to_sign.absolute()
-
- if sys.platform == 'win32':
- # When WIX packed is used to generate .msi on Windows the CPack will
- # install two different projects and install them to different
- # installation prefix:
- #
- # - C:\b\build\_CPack_Packages\WIX\Blender
- # - C:\b\build\_CPack_Packages\WIX\Unspecified
- #
- # Annoying part is: CMake's post-install script will only be run
- # once, with the install prefix which corresponds to a project which
- # was installed last. But we want to sign binaries from all projects.
- # So in order to do so we detect that we are running for a CPack's
- # project used for WIX and force parent directory (which includes both
- # projects) to be signed.
- #
- # Here we force both projects to be signed.
- if path_to_sign.name == 'Unspecified' and 'WIX' in str(path_to_sign):
- path_to_sign = path_to_sign.parent
-
- code_signer = SimpleCodeSigner()
- code_signer.sign_file_or_directory(path_to_sign)
-
-
-if __name__ == "__main__":
- main()
diff --git a/build_files/buildbot/worker_compile.py b/build_files/buildbot/worker_compile.py
deleted file mode 100644
index 8c6b44c5866..00000000000
--- a/build_files/buildbot/worker_compile.py
+++ /dev/null
@@ -1,135 +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>
-
-import os
-import shutil
-
-import buildbot_utils
-
-
-def get_cmake_options(builder):
- codesign_script = os.path.join(
- builder.blender_dir, 'build_files', 'buildbot', 'worker_codesign.cmake')
-
- config_file = "build_files/cmake/config/blender_release.cmake"
- options = ['-DCMAKE_BUILD_TYPE:STRING=Release',
- '-DWITH_GTESTS=ON']
-
- if builder.platform == 'mac':
- options.append('-DCMAKE_OSX_ARCHITECTURES:STRING=x86_64')
- options.append('-DCMAKE_OSX_DEPLOYMENT_TARGET=10.9')
- elif builder.platform == 'win':
- options.extend(['-G', 'Visual Studio 16 2019', '-A', 'x64'])
- if builder.codesign:
- options.extend(['-DPOSTINSTALL_SCRIPT:PATH=' + codesign_script])
- elif builder.platform == 'linux':
- config_file = "build_files/buildbot/config/blender_linux.cmake"
-
- optix_sdk_dir = os.path.join(builder.blender_dir, '..', '..', 'NVIDIA-Optix-SDK-7.1')
- options.append('-DOPTIX_ROOT_DIR:PATH=' + optix_sdk_dir)
-
- # Workaround to build sm_30 kernels with CUDA 10, since CUDA 11 no longer supports that architecture
- if builder.platform == 'win':
- options.append('-DCUDA10_TOOLKIT_ROOT_DIR:PATH=C:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v10.1')
- options.append('-DCUDA10_NVCC_EXECUTABLE:FILEPATH=C:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v10.1/bin/nvcc.exe')
- options.append('-DCUDA11_TOOLKIT_ROOT_DIR:PATH=C:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v11.1')
- options.append('-DCUDA11_NVCC_EXECUTABLE:FILEPATH=C:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v11.1/bin/nvcc.exe')
- elif builder.platform == 'linux':
- options.append('-DCUDA10_TOOLKIT_ROOT_DIR:PATH=/usr/local/cuda-10.1')
- options.append('-DCUDA10_NVCC_EXECUTABLE:FILEPATH=/usr/local/cuda-10.1/bin/nvcc')
- options.append('-DCUDA11_TOOLKIT_ROOT_DIR:PATH=/usr/local/cuda-11.1')
- options.append('-DCUDA11_NVCC_EXECUTABLE:FILEPATH=/usr/local/cuda-11.1/bin/nvcc')
-
- options.append("-C" + os.path.join(builder.blender_dir, config_file))
- options.append("-DCMAKE_INSTALL_PREFIX=%s" % (builder.install_dir))
-
- return options
-
-
-def update_git(builder):
- # Do extra git fetch because not all platform/git/buildbot combinations
- # update the origin remote, causing buildinfo to detect local changes.
- os.chdir(builder.blender_dir)
-
- print("Fetching remotes")
- command = ['git', 'fetch', '--all']
- buildbot_utils.call(builder.command_prefix + command)
-
-
-def clean_directories(builder):
- # Make sure no garbage remained from the previous run
- if os.path.isdir(builder.install_dir):
- shutil.rmtree(builder.install_dir)
-
- # Make sure build directory exists and enter it
- os.makedirs(builder.build_dir, exist_ok=True)
-
- # Remove buildinfo files to force buildbot to re-generate them.
- for buildinfo in ('buildinfo.h', 'buildinfo.h.txt', ):
- full_path = os.path.join(builder.build_dir, 'source', 'creator', buildinfo)
- if os.path.exists(full_path):
- print("Removing {}" . format(buildinfo))
- os.remove(full_path)
-
-
-def cmake_configure(builder):
- # CMake configuration
- os.chdir(builder.build_dir)
-
- cmake_cache = os.path.join(builder.build_dir, 'CMakeCache.txt')
- if os.path.exists(cmake_cache):
- print("Removing CMake cache")
- os.remove(cmake_cache)
-
- print("CMake configure:")
- cmake_options = get_cmake_options(builder)
- command = ['cmake', builder.blender_dir] + cmake_options
- buildbot_utils.call(builder.command_prefix + command)
-
-
-def cmake_build(builder):
- # CMake build
- os.chdir(builder.build_dir)
-
- # NOTE: CPack will build an INSTALL target, which would mean that code
- # signing will happen twice when using `make install` and CPack.
- # The tricky bit here is that it is not possible to know whether INSTALL
- # target is used by CPack or by a buildbot itaself. Extra level on top of
- # this is that on Windows it is required to build INSTALL target in order
- # to have unit test binaries to run.
- # So on the one hand we do an extra unneeded code sign on Windows, but on
- # a positive side we don't add complexity and don't make build process more
- # fragile trying to avoid this. The signing process is way faster than just
- # a clean build of buildbot, especially with regression tests enabled.
- if builder.platform == 'win':
- command = ['cmake', '--build', '.', '--target', 'install', '--config', 'Release']
- else:
- command = ['make', '-s', '-j16', 'install']
-
- print("CMake build:")
- buildbot_utils.call(builder.command_prefix + command)
-
-
-if __name__ == "__main__":
- builder = buildbot_utils.create_builder_from_arguments()
- update_git(builder)
- clean_directories(builder)
- cmake_configure(builder)
- cmake_build(builder)
diff --git a/build_files/buildbot/worker_pack.py b/build_files/buildbot/worker_pack.py
deleted file mode 100644
index a5727a66e09..00000000000
--- a/build_files/buildbot/worker_pack.py
+++ /dev/null
@@ -1,208 +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 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.
-
-import os
-import sys
-
-from pathlib import Path
-
-import buildbot_utils
-
-
-def get_package_name(builder, platform=None):
- info = buildbot_utils.VersionInfo(builder)
-
- package_name = 'blender-' + info.full_version
- if platform:
- package_name += '-' + platform
- if not (builder.branch == 'master' or builder.is_release_branch):
- if info.is_development_build:
- package_name = builder.branch + "-" + package_name
-
- return package_name
-
-
-def sign_file_or_directory(path):
- from codesign.simple_code_signer import SimpleCodeSigner
- code_signer = SimpleCodeSigner()
- code_signer.sign_file_or_directory(Path(path))
-
-
-def create_buildbot_upload_zip(builder, package_files):
- import zipfile
-
- buildbot_upload_zip = os.path.join(builder.upload_dir, "buildbot_upload.zip")
- if os.path.exists(buildbot_upload_zip):
- os.remove(buildbot_upload_zip)
-
- try:
- z = zipfile.ZipFile(buildbot_upload_zip, "w", compression=zipfile.ZIP_STORED)
- for filepath, filename in package_files:
- print("Packaged", filename)
- z.write(filepath, arcname=filename)
- z.close()
- except Exception as ex:
- sys.stderr.write('Create buildbot_upload.zip failed: ' + str(ex) + '\n')
- sys.exit(1)
-
-
-def create_tar_xz(src, dest, package_name):
- # One extra to remove leading os.sep when cleaning root for package_root
- ln = len(src) + 1
- flist = list()
-
- # Create list of tuples containing file and archive name
- for root, dirs, files in os.walk(src):
- package_root = os.path.join(package_name, root[ln:])
- flist.extend([(os.path.join(root, file), os.path.join(package_root, file)) for file in files])
-
- import tarfile
-
- # Set UID/GID of archived files to 0, otherwise they'd be owned by whatever
- # user compiled the package. If root then unpacks it to /usr/local/ you get
- # a security issue.
- def _fakeroot(tarinfo):
- tarinfo.gid = 0
- tarinfo.gname = "root"
- tarinfo.uid = 0
- tarinfo.uname = "root"
- return tarinfo
-
- package = tarfile.open(dest, 'w:xz', preset=9)
- for entry in flist:
- package.add(entry[0], entry[1], recursive=False, filter=_fakeroot)
- package.close()
-
-
-def cleanup_files(dirpath, extension):
- for f in os.listdir(dirpath):
- filepath = os.path.join(dirpath, f)
- if os.path.isfile(filepath) and f.endswith(extension):
- os.remove(filepath)
-
-
-def pack_mac(builder):
- info = buildbot_utils.VersionInfo(builder)
-
- os.chdir(builder.build_dir)
- cleanup_files(builder.build_dir, '.dmg')
-
- package_name = get_package_name(builder, 'macOS')
- package_filename = package_name + '.dmg'
- package_filepath = os.path.join(builder.build_dir, package_filename)
-
- 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, 'worker_bundle_dmg.py')
-
- command = [bundle_script]
- command += ['--dmg', package_filepath]
- if info.is_development_build:
- background_image = os.path.join(release_dir, 'buildbot', 'background.tif')
- command += ['--background-image', background_image]
- if builder.codesign:
- command += ['--codesign']
- command += [builder.install_dir]
- buildbot_utils.call(command)
-
- create_buildbot_upload_zip(builder, [(package_filepath, package_filename)])
-
-
-def pack_win(builder):
- info = buildbot_utils.VersionInfo(builder)
-
- os.chdir(builder.build_dir)
- cleanup_files(builder.build_dir, '.zip')
-
- # CPack will add the platform name
- cpack_name = get_package_name(builder, None)
- package_name = get_package_name(builder, 'windows' + str(builder.bits))
-
- command = ['cmake', '-DCPACK_OVERRIDE_PACKAGENAME:STRING=' + cpack_name, '.']
- buildbot_utils.call(builder.command_prefix + command)
- command = ['cpack', '-G', 'ZIP']
- buildbot_utils.call(builder.command_prefix + command)
-
- package_filename = package_name + '.zip'
- package_filepath = os.path.join(builder.build_dir, package_filename)
- package_files = [(package_filepath, package_filename)]
-
- if info.version_cycle == 'release':
- # Installer only for final release builds, otherwise will get
- # 'this product is already installed' messages.
- command = ['cpack', '-G', 'WIX']
- buildbot_utils.call(builder.command_prefix + command)
-
- package_filename = package_name + '.msi'
- package_filepath = os.path.join(builder.build_dir, package_filename)
- if builder.codesign:
- sign_file_or_directory(package_filepath)
-
- package_files += [(package_filepath, package_filename)]
-
- create_buildbot_upload_zip(builder, package_files)
-
-
-def pack_linux(builder):
- blender_executable = os.path.join(builder.install_dir, 'blender')
-
- info = buildbot_utils.VersionInfo(builder)
-
- # Strip all unused symbols from the binaries
- print("Stripping binaries...")
- buildbot_utils.call(builder.command_prefix + ['strip', '--strip-all', blender_executable])
-
- print("Stripping python...")
- py_target = os.path.join(builder.install_dir, info.short_version)
- buildbot_utils.call(
- builder.command_prefix + [
- 'find', py_target, '-iname', '*.so', '-exec', 'strip', '-s', '{}', ';',
- ],
- )
-
- # Construct package name
- platform_name = 'linux64'
- package_name = get_package_name(builder, platform_name)
- package_filename = package_name + ".tar.xz"
-
- print("Creating .tar.xz archive")
- package_filepath = builder.install_dir + '.tar.xz'
- create_tar_xz(builder.install_dir, package_filepath, package_name)
-
- # Create buildbot_upload.zip
- create_buildbot_upload_zip(builder, [(package_filepath, package_filename)])
-
-
-if __name__ == "__main__":
- builder = buildbot_utils.create_builder_from_arguments()
-
- # Make sure install directory always exists
- os.makedirs(builder.install_dir, exist_ok=True)
-
- if builder.platform == 'mac':
- pack_mac(builder)
- elif builder.platform == 'win':
- pack_win(builder)
- elif builder.platform == 'linux':
- pack_linux(builder)
diff --git a/build_files/buildbot/worker_test.py b/build_files/buildbot/worker_test.py
deleted file mode 100644
index 8a6e7d4bb69..00000000000
--- a/build_files/buildbot/worker_test.py
+++ /dev/null
@@ -1,42 +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>
-
-import buildbot_utils
-import os
-import sys
-
-
-def get_ctest_arguments(builder):
- args = ['--output-on-failure']
- if builder.platform == 'win':
- args += ['-C', 'Release']
- return args
-
-
-def test(builder):
- os.chdir(builder.build_dir)
-
- command = builder.command_prefix + ['ctest'] + get_ctest_arguments(builder)
- buildbot_utils.call(command)
-
-
-if __name__ == "__main__":
- builder = buildbot_utils.create_builder_from_arguments()
- test(builder)
diff --git a/build_files/buildbot/worker_update.py b/build_files/buildbot/worker_update.py
deleted file mode 100644
index 36a7ae31c84..00000000000
--- a/build_files/buildbot/worker_update.py
+++ /dev/null
@@ -1,31 +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>
-
-import buildbot_utils
-import os
-import sys
-
-if __name__ == "__main__":
- builder = buildbot_utils.create_builder_from_arguments()
- os.chdir(builder.blender_dir)
-
- # Run make update which handles all libraries and submodules.
- make_update = os.path.join(builder.blender_dir, "build_files", "utils", "make_update.py")
- buildbot_utils.call([sys.executable, make_update, '--no-blender', "--use-tests", "--use-centos-libraries"])
diff --git a/build_files/cmake/Modules/FindClang.cmake b/build_files/cmake/Modules/FindClang.cmake
index b5c2cfbc28d..41df6e106b2 100644
--- a/build_files/cmake/Modules/FindClang.cmake
+++ b/build_files/cmake/Modules/FindClang.cmake
@@ -20,8 +20,24 @@ if(NOT CLANG_ROOT_DIR AND NOT $ENV{CLANG_ROOT_DIR} STREQUAL "")
set(CLANG_ROOT_DIR $ENV{CLANG_ROOT_DIR})
endif()
+if(NOT LLVM_ROOT_DIR)
+ if(DEFINED LLVM_VERSION)
+ message(running llvm-config-${LLVM_VERSION})
+ find_program(LLVM_CONFIG llvm-config-${LLVM_VERSION})
+ endif()
+ if(NOT LLVM_CONFIG)
+ find_program(LLVM_CONFIG llvm-config)
+ endif()
+
+ execute_process(COMMAND ${LLVM_CONFIG} --prefix
+ OUTPUT_VARIABLE LLVM_ROOT_DIR
+ OUTPUT_STRIP_TRAILING_WHITESPACE)
+ set(LLVM_ROOT_DIR ${LLVM_ROOT_DIR} CACHE PATH "Path to the LLVM installation")
+endif()
+
set(_CLANG_SEARCH_DIRS
${CLANG_ROOT_DIR}
+ ${LLVM_ROOT_DIR}
/opt/lib/clang
)
diff --git a/build_files/cmake/Modules/FindEmbree.cmake b/build_files/cmake/Modules/FindEmbree.cmake
index 7f7f2ae0fb3..e6105a885f3 100644
--- a/build_files/cmake/Modules/FindEmbree.cmake
+++ b/build_files/cmake/Modules/FindEmbree.cmake
@@ -34,7 +34,7 @@ FIND_PATH(EMBREE_INCLUDE_DIR
include
)
-IF(NOT (APPLE AND ("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")))
+IF(NOT (("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "aarch64") OR (APPLE AND ("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64"))))
SET(_embree_SIMD_COMPONENTS
embree_sse42
embree_avx
diff --git a/build_files/cmake/Modules/FindOSL.cmake b/build_files/cmake/Modules/FindOSL.cmake
index d184215ac9f..b21c7ad50a3 100644
--- a/build_files/cmake/Modules/FindOSL.cmake
+++ b/build_files/cmake/Modules/FindOSL.cmake
@@ -75,7 +75,7 @@ FIND_PATH(OSL_SHADER_DIR
/usr/share/OSL/
/usr/include/OSL/
PATH_SUFFIXES
- shaders
+ share/OSL/shaders
)
# handle the QUIETLY and REQUIRED arguments and set OSL_FOUND to TRUE if
diff --git a/build_files/cmake/buildinfo.cmake b/build_files/cmake/buildinfo.cmake
index a349ffc1b56..2b39c36a4e9 100644
--- a/build_files/cmake/buildinfo.cmake
+++ b/build_files/cmake/buildinfo.cmake
@@ -79,7 +79,7 @@ if(EXISTS ${SOURCE_DIR}/.git)
ERROR_QUIET)
if(NOT _git_below_check STREQUAL "")
# If there're commits between HEAD and upstream this means
- # that we're reset-ed to older revision. Use it's hash then.
+ # that we're reset-ed to older revision. Use its hash then.
execute_process(COMMAND git rev-parse --short=12 HEAD
WORKING_DIRECTORY ${SOURCE_DIR}
OUTPUT_VARIABLE MY_WC_HASH
diff --git a/build_files/cmake/cmake_consistency_check.py b/build_files/cmake/cmake_consistency_check.py
index c613d4d799b..c62e5fc0b59 100755
--- a/build_files/cmake/cmake_consistency_check.py
+++ b/build_files/cmake/cmake_consistency_check.py
@@ -28,6 +28,14 @@ if sys.version_info.major < 3:
sys.version.partition(" ")[0])
sys.exit(1)
+import os
+from os.path import (
+ dirname,
+ join,
+ normpath,
+ splitext,
+)
+
from cmake_consistency_check_config import (
IGNORE_SOURCE,
IGNORE_SOURCE_MISSING,
@@ -37,32 +45,35 @@ from cmake_consistency_check_config import (
BUILD_DIR,
)
-
-import os
-from os.path import (
- dirname,
- join,
- normpath,
- splitext,
+from typing import (
+ Callable,
+ Dict,
+ Generator,
+ Iterator,
+ List,
+ Optional,
+ Tuple,
)
+
global_h = set()
global_c = set()
-global_refs = {}
+global_refs: Dict[str, List[Tuple[str, int]]] = {}
# Flatten `IGNORE_SOURCE_MISSING` to avoid nested looping.
-IGNORE_SOURCE_MISSING = [
+IGNORE_SOURCE_MISSING_FLAT = [
(k, ignore_path) for k, ig_list in IGNORE_SOURCE_MISSING
for ignore_path in ig_list
]
# Ignore cmake file, path pairs.
-global_ignore_source_missing = {}
-for k, v in IGNORE_SOURCE_MISSING:
+global_ignore_source_missing: Dict[str, List[str]] = {}
+for k, v in IGNORE_SOURCE_MISSING_FLAT:
global_ignore_source_missing.setdefault(k, []).append(v)
+del IGNORE_SOURCE_MISSING_FLAT
-def replace_line(f, i, text, keep_indent=True):
+def replace_line(f: str, i: int, text: str, keep_indent: bool = True) -> None:
file_handle = open(f, 'r')
data = file_handle.readlines()
file_handle.close()
@@ -77,7 +88,10 @@ def replace_line(f, i, text, keep_indent=True):
file_handle.close()
-def source_list(path, filename_check=None):
+def source_list(
+ path: str,
+ filename_check: Optional[Callable[[str], bool]] = None,
+) -> Generator[str, None, None]:
for dirpath, dirnames, filenames in os.walk(path):
# skip '.git'
dirnames[:] = [d for d in dirnames if not d.startswith(".")]
@@ -88,37 +102,37 @@ def source_list(path, filename_check=None):
# extension checking
-def is_cmake(filename):
+def is_cmake(filename: str) -> bool:
ext = splitext(filename)[1]
return (ext == ".cmake") or (filename == "CMakeLists.txt")
-def is_c_header(filename):
+def is_c_header(filename: str) -> bool:
ext = splitext(filename)[1]
return (ext in {".h", ".hpp", ".hxx", ".hh"})
-def is_c(filename):
+def is_c(filename: str) -> bool:
ext = splitext(filename)[1]
return (ext in {".c", ".cpp", ".cxx", ".m", ".mm", ".rc", ".cc", ".inl"})
-def is_c_any(filename):
+def is_c_any(filename: str) -> bool:
return is_c(filename) or is_c_header(filename)
-def cmake_get_src(f):
+def cmake_get_src(f: str) -> None:
sources_h = []
sources_c = []
filen = open(f, "r", encoding="utf8")
- it = iter(filen)
+ it: Optional[Iterator[str]] = iter(filen)
found = False
i = 0
# print(f)
- def is_definition(l, f, i, name):
+ def is_definition(l: str, f: str, i: int, name: str) -> bool:
if l.startswith("unset("):
return False
@@ -131,6 +145,7 @@ def cmake_get_src(f):
if l.endswith(")"):
raise Exception("strict formatting not kept 'list(APPEND %s...)' on 1 line %s:%d" % (name, f, i))
return True
+ return False
while it is not None:
context_name = ""
@@ -269,7 +284,7 @@ def cmake_get_src(f):
filen.close()
-def is_ignore_source(f, ignore_used):
+def is_ignore_source(f: str, ignore_used: List[bool]) -> bool:
for index, ignore_path in enumerate(IGNORE_SOURCE):
if ignore_path in f:
ignore_used[index] = True
@@ -277,7 +292,7 @@ def is_ignore_source(f, ignore_used):
return False
-def is_ignore_cmake(f, ignore_used):
+def is_ignore_cmake(f: str, ignore_used: List[bool]) -> bool:
for index, ignore_path in enumerate(IGNORE_CMAKE):
if ignore_path in f:
ignore_used[index] = True
@@ -285,7 +300,7 @@ def is_ignore_cmake(f, ignore_used):
return False
-def main():
+def main() -> None:
print("Scanning:", SOURCE_DIR)
@@ -359,7 +374,7 @@ def main():
if "extern" not in f:
i = 1
try:
- for l in open(f, "r", encoding="utf8"):
+ for _ in open(f, "r", encoding="utf8"):
i += 1
except UnicodeDecodeError:
print("Non utf8: %s:%d" % (f, i))
diff --git a/build_files/cmake/cmake_static_check_clang_array.py b/build_files/cmake/cmake_static_check_clang_array.py
index 59743f0244b..76aeb9e194e 100644
--- a/build_files/cmake/cmake_static_check_clang_array.py
+++ b/build_files/cmake/cmake_static_check_clang_array.py
@@ -25,6 +25,14 @@ import subprocess
import sys
import os
+from typing import (
+ Any,
+ Callable,
+ List,
+ Tuple,
+)
+
+
USE_QUIET = (os.environ.get("QUIET", None) is not None)
CHECKER_IGNORE_PREFIX = [
@@ -43,7 +51,7 @@ CHECKER_ARGS = [
]
-def main():
+def main() -> None:
source_info = project_source_info.build_info(ignore_prefix_list=CHECKER_IGNORE_PREFIX)
check_commands = []
@@ -52,18 +60,19 @@ def main():
# ~if "source/blender" not in c:
# ~ continue
- cmd = ([CHECKER_BIN] +
- CHECKER_ARGS +
- [c] +
- [("-I%s" % i) for i in inc_dirs] +
- [("-D%s" % d) for d in defs]
- )
+ cmd = (
+ [CHECKER_BIN] +
+ CHECKER_ARGS +
+ [c] +
+ [("-I%s" % i) for i in inc_dirs] +
+ [("-D%s" % d) for d in defs]
+ )
check_commands.append((c, cmd))
process_functions = []
- def my_process(i, c, cmd):
+ def my_process(i: int, c: str, cmd: str) -> subprocess.Popen[Any]:
if not USE_QUIET:
percent = 100.0 * (i / (len(check_commands) - 1))
percent_str = "[" + ("%.2f]" % percent).rjust(7) + " %:"
diff --git a/build_files/cmake/cmake_static_check_cppcheck.py b/build_files/cmake/cmake_static_check_cppcheck.py
index c26ebb4cb84..1eef2efe2b5 100644
--- a/build_files/cmake/cmake_static_check_cppcheck.py
+++ b/build_files/cmake/cmake_static_check_cppcheck.py
@@ -25,6 +25,12 @@ import subprocess
import sys
import os
+from typing import (
+ Any,
+ List,
+)
+
+
USE_QUIET = (os.environ.get("QUIET", None) is not None)
CHECKER_IGNORE_PREFIX = [
@@ -47,25 +53,26 @@ if USE_QUIET:
CHECKER_ARGS.append("--quiet")
-def main():
+def main() -> None:
source_info = project_source_info.build_info(ignore_prefix_list=CHECKER_IGNORE_PREFIX)
source_defines = project_source_info.build_defines_as_args()
check_commands = []
for c, inc_dirs, defs in source_info:
- cmd = ([CHECKER_BIN] +
- CHECKER_ARGS +
- [c] +
- [("-I%s" % i) for i in inc_dirs] +
- [("-D%s" % d) for d in defs] +
- source_defines
- )
+ cmd = (
+ [CHECKER_BIN] +
+ CHECKER_ARGS +
+ [c] +
+ [("-I%s" % i) for i in inc_dirs] +
+ [("-D%s" % d) for d in defs] +
+ source_defines
+ )
check_commands.append((c, cmd))
process_functions = []
- def my_process(i, c, cmd):
+ def my_process(i: int, c: str, cmd: List[str]) -> subprocess.Popen[Any]:
if not USE_QUIET:
percent = 100.0 * (i / len(check_commands))
percent_str = "[" + ("%.2f]" % percent).rjust(7) + " %:"
diff --git a/build_files/cmake/config/blender_release.cmake b/build_files/cmake/config/blender_release.cmake
index d4fd3779665..75aafd45c82 100644
--- a/build_files/cmake/config/blender_release.cmake
+++ b/build_files/cmake/config/blender_release.cmake
@@ -56,10 +56,6 @@ set(WITH_TBB ON CACHE BOOL "" FORCE)
set(WITH_USD ON CACHE BOOL "" FORCE)
set(WITH_MEM_JEMALLOC ON CACHE BOOL "" FORCE)
-set(WITH_CYCLES_CUDA_BINARIES ON CACHE BOOL "" FORCE)
-set(WITH_CYCLES_CUBIN_COMPILER OFF CACHE BOOL "" FORCE)
-set(CYCLES_CUDA_BINARIES_ARCH sm_30;sm_35;sm_37;sm_50;sm_52;sm_60;sm_61;sm_70;sm_75;sm_86;compute_75 CACHE STRING "" FORCE)
-set(WITH_CYCLES_DEVICE_OPTIX ON CACHE BOOL "" FORCE)
# platform dependent options
if(APPLE)
@@ -80,4 +76,8 @@ if(UNIX AND NOT APPLE)
endif()
if(NOT APPLE)
set(WITH_XR_OPENXR ON CACHE BOOL "" FORCE)
+
+ set(WITH_CYCLES_DEVICE_OPTIX ON CACHE BOOL "" FORCE)
+ set(WITH_CYCLES_CUDA_BINARIES ON CACHE BOOL "" FORCE)
+ set(WITH_CYCLES_CUBIN_COMPILER OFF CACHE BOOL "" FORCE)
endif()
diff --git a/build_files/cmake/example_scripts/make_quicky.py b/build_files/cmake/example_scripts/make_quicky.py
deleted file mode 100755
index 51985e930ae..00000000000
--- a/build_files/cmake/example_scripts/make_quicky.py
+++ /dev/null
@@ -1,119 +0,0 @@
-#!/usr/bin/env python3
-
-# ##### 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>
-
-
-def print_help(targets):
- print("CMake quicky wrapper, no valid targets given.")
- print(" * targets can contain a subset of the full target name.")
- print(" * arguments with a '-' prefix are passed onto make.")
- print(" * this must run from the cmake build dir")
- print(" * alias this with a short command for speedy access, in bash:")
- print(" alias mk='../blender/build_files/cmake/example_scripts/make_quicky.py'")
- print("")
- print(" eg: make_quicky.py -j3 extern python")
- print(" ...will execute")
- print(" make -j3 extern_binreloc extern_glew bf_python bf_python_ext blender/fast")
- print("")
- print("Target List:")
- for t in targets:
- print(" %s" % t)
- print("...exiting")
-
-
-def main():
- targets = set()
-
- # collect targets
- makefile = open("Makefile", "r")
- for line in makefile:
- line = line.rstrip()
- if not line or line[0] in ". \t@$#":
- continue
-
- line = line.split("#", 1)[0]
- if ":" not in line:
- continue
-
- line = line.split(":", 1)[0]
-
- if "/" in line: # cmake terget options, dont need these
- continue
-
- targets.add(line)
- makefile.close()
-
- # remove cmake targets
- bad = set([
- "help",
- "clean",
- "all",
- "preinstall",
- "install",
- "default_target",
- "edit_cache",
- "cmake_force",
- "rebuild_cache",
- "depend",
- "cmake_check_build_system",
- ])
-
- targets -= set(bad)
-
- # parse args
- targets = list(targets)
- targets.sort()
-
- import sys
- if len(sys.argv) == 1:
- print_help(targets)
- return
-
- targets_new = []
- args = []
- for arg in sys.argv[1:]:
- if arg[0] in "/-":
- args.append(arg)
- else:
- found = False
- for t in targets:
- if arg in t and t not in targets_new:
- targets_new.append(t)
- found = True
-
- if not found:
- print("Error '%s' not found in...")
- for t in targets:
- print(" %s" % t)
- print("...aborting.")
- return
-
- # execute
- cmd = ["make"] + args + targets_new + ["blender/fast"]
- print("cmake building with targets: %s" % " ".join(targets_new))
- print("executing: %s" % " ".join(cmd))
-
- import subprocess
- subprocess.call(cmd)
-
-
-if __name__ == "__main__":
- main()
diff --git a/build_files/cmake/macros.cmake b/build_files/cmake/macros.cmake
index 813ac013cdf..5ae5ed7c29e 100644
--- a/build_files/cmake/macros.cmake
+++ b/build_files/cmake/macros.cmake
@@ -596,14 +596,6 @@ function(SETUP_LIBDIRS)
link_directories(${GMP_LIBPATH})
endif()
- if(WITH_GHOST_WAYLAND)
- link_directories(
- ${wayland-client_LIBRARY_DIRS}
- ${wayland-egl_LIBRARY_DIRS}
- ${xkbcommon_LIBRARY_DIRS}
- ${wayland-cursor_LIBRARY_DIRS})
- endif()
-
if(WIN32 AND NOT UNIX)
link_directories(${PTHREADS_LIBPATH})
endif()
diff --git a/build_files/cmake/packaging.cmake b/build_files/cmake/packaging.cmake
index 4a0a4f2493d..265b3c0e2ab 100644
--- a/build_files/cmake/packaging.cmake
+++ b/build_files/cmake/packaging.cmake
@@ -104,8 +104,8 @@ if(WIN32)
set(CPACK_WIX_LIGHT_EXTRA_FLAGS -dcl:medium)
endif()
-set(CPACK_PACKAGE_EXECUTABLES "blender" "blender")
-set(CPACK_CREATE_DESKTOP_LINKS "blender" "blender")
+set(CPACK_PACKAGE_EXECUTABLES "blender-launcher" "blender")
+set(CPACK_CREATE_DESKTOP_LINKS "blender-launcher" "blender")
include(CPack)
diff --git a/build_files/cmake/platform/platform_apple.cmake b/build_files/cmake/platform/platform_apple.cmake
index 6276032d85c..fe9dd6a58de 100644
--- a/build_files/cmake/platform/platform_apple.cmake
+++ b/build_files/cmake/platform/platform_apple.cmake
@@ -20,12 +20,6 @@
# Libraries configuration for Apple.
-if("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")
- set(MACOSX_DEPLOYMENT_TARGET 11.00)
-else()
- set(MACOSX_DEPLOYMENT_TARGET 10.13)
-endif()
-
macro(find_package_wrapper)
# do nothing, just satisfy the macro
endmacro()
@@ -56,6 +50,7 @@ list(APPEND ZLIB_LIBRARIES ${BZIP2_LIBRARIES})
if(WITH_OPENAL)
find_package(OpenAL)
if(NOT OPENAL_FOUND)
+ message(WARNING "OpenAL not found, disabling WITH_OPENAL")
set(WITH_OPENAL OFF)
endif()
endif()
@@ -65,6 +60,7 @@ if(WITH_JACK)
NAMES jackmp
)
if(NOT JACK_FRAMEWORK)
+ message(STATUS "JACK not found, disabling WITH_JACK")
set(WITH_JACK OFF)
else()
set(JACK_INCLUDE_DIRS ${JACK_FRAMEWORK}/headers)
@@ -102,6 +98,7 @@ endif()
if(WITH_USD)
find_package(USD)
if(NOT USD_FOUND)
+ message(STATUS "USD not found, disabling WITH_USD")
set(WITH_USD OFF)
endif()
endif()
@@ -146,7 +143,7 @@ if(WITH_PYTHON)
set(PYTHON_INCLUDE_DIR "${_py_framework}/include/python${PYTHON_VERSION}")
set(PYTHON_EXECUTABLE "${_py_framework}/bin/python${PYTHON_VERSION}")
- set(PYTHON_LIBPATH "${_py_framework}/lib/python${PYTHON_VERSION}/config-${PYTHON_VERSION}")
+ set(PYTHON_LIBPATH "${_py_framework}/lib/python${PYTHON_VERSION}")
# set(PYTHON_LIBRARY python${PYTHON_VERSION})
# set(PYTHON_LINKFLAGS "-u _PyMac_Error -framework Python") # won't build with this enabled
@@ -308,7 +305,7 @@ if(WITH_OPENCOLORIO)
if(NOT OPENCOLORIO_FOUND)
set(WITH_OPENCOLORIO OFF)
- message(STATUS "OpenColorIO not found")
+ message(STATUS "OpenColorIO not found, disabling WITH_OPENCOLORIO")
endif()
endif()
@@ -357,7 +354,7 @@ if(WITH_CYCLES_OSL)
if(OSL_INCLUDE_DIR AND OSL_LIBRARIES AND OSL_COMPILER AND OSL_SHADER_DIR)
set(OSL_FOUND TRUE)
else()
- message(STATUS "OSL not found")
+ message(WARNING "OSL not found, disabling WITH_CYCLES_OSL")
set(WITH_CYCLES_OSL OFF)
endif()
endif()
@@ -385,7 +382,7 @@ if(WITH_OPENIMAGEDENOISE)
if(NOT OPENIMAGEDENOISE_FOUND)
set(WITH_OPENIMAGEDENOISE OFF)
- message(STATUS "OpenImageDenoise not found")
+ message(STATUS "OpenImageDenoise not found, disabling WITH_OPENIMAGEDENOISE")
endif()
endif()
@@ -413,7 +410,7 @@ if(WITH_OPENMP)
set(OpenMP_LINKER_FLAGS "-L'${LIBDIR}/openmp/lib' -lomp")
# Copy libomp.dylib to allow executables like datatoc and tests to work.
- # `@executable_path/../Resources/lib/` `LC_ID_DYLIB`is added by the deps builder.
+ # `@executable_path/../Resources/lib/` `LC_ID_DYLIB` is added by the deps builder.
# For single config generator datatoc, tests etc.
execute_process(
COMMAND mkdir -p ${CMAKE_BINARY_DIR}/Resources/lib
diff --git a/build_files/cmake/platform/platform_apple_xcode.cmake b/build_files/cmake/platform/platform_apple_xcode.cmake
index 4d15fee75b7..639d7e43afd 100644
--- a/build_files/cmake/platform/platform_apple_xcode.cmake
+++ b/build_files/cmake/platform/platform_apple_xcode.cmake
@@ -168,21 +168,15 @@ endif()
unset(OSX_SDKROOT)
-# 10.13 is our min. target, if you use higher sdk, weak linking happens
if("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")
+ # M1 chips run Big Sur onwards.
set(OSX_MIN_DEPLOYMENT_TARGET 11.00)
else()
+ # 10.13 is our min. target, if you use higher sdk, weak linking happens
set(OSX_MIN_DEPLOYMENT_TARGET 10.13)
endif()
-if(CMAKE_OSX_DEPLOYMENT_TARGET)
- if(${CMAKE_OSX_DEPLOYMENT_TARGET} VERSION_LESS ${OSX_MIN_DEPLOYMENT_TARGET})
- message(STATUS "Setting deployment target to ${OSX_MIN_DEPLOYMENT_TARGET}, lower versions are not supported")
- set(CMAKE_OSX_DEPLOYMENT_TARGET "${OSX_MIN_DEPLOYMENT_TARGET}" CACHE STRING "" FORCE)
- endif()
-else()
- set(CMAKE_OSX_DEPLOYMENT_TARGET "${OSX_MIN_DEPLOYMENT_TARGET}" CACHE STRING "" FORCE)
-endif()
+set(CMAKE_OSX_DEPLOYMENT_TARGET "${OSX_MIN_DEPLOYMENT_TARGET}" CACHE STRING "" FORCE)
if(NOT ${CMAKE_GENERATOR} MATCHES "Xcode")
# Force CMAKE_OSX_DEPLOYMENT_TARGET for makefiles, will not work else (CMake bug?)
diff --git a/build_files/cmake/platform/platform_unix.cmake b/build_files/cmake/platform/platform_unix.cmake
index 7791112c6ab..fcfde5a07ba 100644
--- a/build_files/cmake/platform/platform_unix.cmake
+++ b/build_files/cmake/platform/platform_unix.cmake
@@ -575,17 +575,17 @@ if(WITH_GHOST_WAYLAND)
pkg_check_modules(wayland-scanner REQUIRED wayland-scanner)
pkg_check_modules(xkbcommon REQUIRED xkbcommon)
pkg_check_modules(wayland-cursor REQUIRED wayland-cursor)
+ pkg_check_modules(dbus REQUIRED dbus-1)
set(WITH_GL_EGL ON)
- if(WITH_GHOST_WAYLAND)
- list(APPEND PLATFORM_LINKLIBS
- ${wayland-client_LIBRARIES}
- ${wayland-egl_LIBRARIES}
- ${xkbcommon_LIBRARIES}
- ${wayland-cursor_LIBRARIES}
- )
- endif()
+ list(APPEND PLATFORM_LINKLIBS
+ ${wayland-client_LINK_LIBRARIES}
+ ${wayland-egl_LINK_LIBRARIES}
+ ${xkbcommon_LINK_LIBRARIES}
+ ${wayland-cursor_LINK_LIBRARIES}
+ ${dbus_LINK_LIBRARIES}
+ )
endif()
if(WITH_GHOST_X11)
diff --git a/build_files/cmake/platform/platform_win32.cmake b/build_files/cmake/platform/platform_win32.cmake
index acd6028e2ae..99a2cb7966f 100644
--- a/build_files/cmake/platform/platform_win32.cmake
+++ b/build_files/cmake/platform/platform_win32.cmake
@@ -119,7 +119,7 @@ string(APPEND CMAKE_MODULE_LINKER_FLAGS " /SAFESEH:NO /ignore:4099")
list(APPEND PLATFORM_LINKLIBS
ws2_32 vfw32 winmm kernel32 user32 gdi32 comdlg32 Comctl32 version
advapi32 shfolder shell32 ole32 oleaut32 uuid psapi Dbghelp Shlwapi
- pathcch
+ pathcch Shcore
)
if(WITH_INPUT_IME)
@@ -144,16 +144,11 @@ add_definitions(-D_ALLOW_KEYWORD_MACROS)
# that both /GR and /GR- are specified.
remove_cc_flag("/GR")
-# We want to support Windows 7 level ABI
-add_definitions(-D_WIN32_WINNT=0x601)
+# Make the Windows 8.1 API available for use.
+add_definitions(-D_WIN32_WINNT=0x603)
include(build_files/cmake/platform/platform_win32_bundle_crt.cmake)
remove_cc_flag("/MDd" "/MD" "/Zi")
-if(WITH_WINDOWS_PDB)
- set(PDB_INFO_OVERRIDE_FLAGS "/Z7")
- set(PDB_INFO_OVERRIDE_LINKER_FLAGS "/DEBUG /OPT:REF /OPT:ICF /INCREMENTAL:NO")
-endif()
-
if(MSVC_CLANG) # Clangs version of cl doesn't support all flags
string(APPEND CMAKE_CXX_FLAGS " ${CXX_WARN_FLAGS} /nologo /J /Gd /EHsc -Wno-unused-command-line-argument -Wno-microsoft-enum-forward-reference ")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /nologo /J /Gd -Wno-unused-command-line-argument -Wno-microsoft-enum-forward-reference")
@@ -162,6 +157,21 @@ else()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /nologo /J /Gd /MP /bigobj")
endif()
+# X64 ASAN is available and usable on MSVC 16.9 preview 4 and up)
+if(WITH_COMPILER_ASAN AND MSVC AND NOT MSVC_CLANG)
+ if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.28.29828)
+ #set a flag so we don't have to do this comparison all the time
+ SET(MSVC_ASAN On)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address")
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address")
+ string(APPEND CMAKE_EXE_LINKER_FLAGS_DEBUG " /INCREMENTAL:NO")
+ string(APPEND CMAKE_SHARED_LINKER_FLAGS_DEBUG " /INCREMENTAL:NO")
+ else()
+ message("-- ASAN not supported on MSVC ${CMAKE_CXX_COMPILER_VERSION}")
+ endif()
+endif()
+
+
# C++ standards conformace (/permissive-) is available on msvc 15.5 (1912) and up
if(MSVC_VERSION GREATER 1911 AND NOT MSVC_CLANG)
string(APPEND CMAKE_CXX_FLAGS " /permissive-")
@@ -174,14 +184,41 @@ if(WITH_WINDOWS_SCCACHE AND CMAKE_VS_MSBUILD_COMMAND)
set(WITH_WINDOWS_SCCACHE Off)
endif()
+# Debug Symbol format
+# sccache # MSVC_ASAN # format # why
+# On # On # Z7 # sccache will only play nice with Z7
+# On # Off # Z7 # sccache will only play nice with Z7
+# Off # On # Zi # Asan will not play nice with Edit and Continue
+# Off # Off # ZI # Neither asan nor sscache is enabled Edit and Continue is available
+
+# Release Symbol format
+# sccache # MSVC_ASAN # format # why
+# On # On # Z7 # sccache will only play nice with Z7
+# On # Off # Z7 # sccache will only play nice with Z7
+# Off # On # Zi # Asan will not play nice with Edit and Continue
+# Off # Off # Zi # Edit and Continue disables some optimizations
+
+
if(WITH_WINDOWS_SCCACHE)
set(CMAKE_C_COMPILER_LAUNCHER sccache)
set(CMAKE_CXX_COMPILER_LAUNCHER sccache)
set(SYMBOL_FORMAT /Z7)
+ set(SYMBOL_FORMAT_RELEASE /Z7)
else()
unset(CMAKE_C_COMPILER_LAUNCHER)
unset(CMAKE_CXX_COMPILER_LAUNCHER)
- set(SYMBOL_FORMAT /ZI)
+ if(MSVC_ASAN)
+ set(SYMBOL_FORMAT /Z7)
+ set(SYMBOL_FORMAT_RELEASE /Z7)
+ else()
+ set(SYMBOL_FORMAT /ZI)
+ set(SYMBOL_FORMAT_RELEASE /Zi)
+ endif()
+endif()
+
+if(WITH_WINDOWS_PDB)
+ set(PDB_INFO_OVERRIDE_FLAGS "${SYMBOL_FORMAT_RELEASE}")
+ set(PDB_INFO_OVERRIDE_LINKER_FLAGS "/DEBUG /OPT:REF /OPT:ICF /INCREMENTAL:NO")
endif()
string(APPEND CMAKE_CXX_FLAGS_DEBUG " /MDd ${SYMBOL_FORMAT}")
@@ -190,9 +227,11 @@ string(APPEND CMAKE_CXX_FLAGS_RELEASE " /MD ${PDB_INFO_OVERRIDE_FLAGS}")
string(APPEND CMAKE_C_FLAGS_RELEASE " /MD ${PDB_INFO_OVERRIDE_FLAGS}")
string(APPEND CMAKE_CXX_FLAGS_MINSIZEREL " /MD ${PDB_INFO_OVERRIDE_FLAGS}")
string(APPEND CMAKE_C_FLAGS_MINSIZEREL " /MD ${PDB_INFO_OVERRIDE_FLAGS}")
-string(APPEND CMAKE_CXX_FLAGS_RELWITHDEBINFO " /MD ${SYMBOL_FORMAT}")
-string(APPEND CMAKE_C_FLAGS_RELWITHDEBINFO " /MD ${SYMBOL_FORMAT}")
+string(APPEND CMAKE_CXX_FLAGS_RELWITHDEBINFO " /MD ${SYMBOL_FORMAT_RELEASE}")
+string(APPEND CMAKE_C_FLAGS_RELWITHDEBINFO " /MD ${SYMBOL_FORMAT_RELEASE}")
unset(SYMBOL_FORMAT)
+unset(SYMBOL_FORMAT_RELEASE)
+
# JMC is available on msvc 15.8 (1915) and up
if(MSVC_VERSION GREATER 1914 AND NOT MSVC_CLANG)
string(APPEND CMAKE_CXX_FLAGS_DEBUG " /JMC")
@@ -636,10 +675,11 @@ if(WITH_SYSTEM_AUDASPACE)
endif()
if(WITH_TBB)
- set(TBB_LIBRARIES optimized ${LIBDIR}/tbb/lib/tbb.lib debug ${LIBDIR}/tbb/lib/debug/tbb_debug.lib)
+ set(TBB_LIBRARIES optimized ${LIBDIR}/tbb/lib/tbb.lib debug ${LIBDIR}/tbb/lib/tbb_debug.lib)
set(TBB_INCLUDE_DIR ${LIBDIR}/tbb/include)
set(TBB_INCLUDE_DIRS ${TBB_INCLUDE_DIR})
if(WITH_TBB_MALLOC_PROXY)
+ set(TBB_MALLOC_LIBRARIES optimized ${LIBDIR}/tbb/lib/tbbmalloc.lib debug ${LIBDIR}/tbb/lib/tbbmalloc_debug.lib)
add_definitions(-DWITH_TBB_MALLOC)
endif()
endif()
diff --git a/build_files/cmake/platform/platform_win32_bundle_crt.cmake b/build_files/cmake/platform/platform_win32_bundle_crt.cmake
index f5bc024e4e0..7b2e1edb1b3 100644
--- a/build_files/cmake/platform/platform_win32_bundle_crt.cmake
+++ b/build_files/cmake/platform/platform_win32_bundle_crt.cmake
@@ -15,6 +15,15 @@ if(WITH_WINDOWS_BUNDLE_CRT)
include(InstallRequiredSystemLibraries)
+ # ucrtbase(d).dll cannot be in the manifest, due to the way windows 10 handles
+ # redirects for this dll, for details see T88813.
+ foreach(lib ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS})
+ string(FIND ${lib} "ucrtbase" pos)
+ if(NOT pos EQUAL -1)
+ list(REMOVE_ITEM CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS ${lib})
+ install(FILES ${lib} DESTINATION . COMPONENT Libraries)
+ endif()
+ endforeach()
# Install the CRT to the blender.crt Sub folder.
install(FILES ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS} DESTINATION ./blender.crt COMPONENT Libraries)
diff --git a/build_files/cmake/project_info.py b/build_files/cmake/project_info.py
index dd81602630e..13b07f49079 100755
--- a/build_files/cmake/project_info.py
+++ b/build_files/cmake/project_info.py
@@ -44,6 +44,15 @@ __all__ = (
"init",
)
+from typing import (
+ Callable,
+ Generator,
+ List,
+ Optional,
+ Union,
+ Tuple,
+)
+
import sys
if sys.version_info.major < 3:
@@ -70,10 +79,11 @@ SOURCE_DIR = abspath(SOURCE_DIR)
SIMPLE_PROJECTFILE = False
# must initialize from 'init'
-CMAKE_DIR = None
+CMAKE_DIR = ""
+PROJECT_DIR = ""
-def init(cmake_path):
+def init(cmake_path: str) -> bool:
global CMAKE_DIR, PROJECT_DIR
# get cmake path
@@ -91,7 +101,10 @@ def init(cmake_path):
return True
-def source_list(path, filename_check=None):
+def source_list(
+ path: str,
+ filename_check: Optional[Callable[[str], bool]] = None,
+) -> Generator[str, None, None]:
for dirpath, dirnames, filenames in os.walk(path):
# skip '.git'
dirnames[:] = [d for d in dirnames if not d.startswith(".")]
@@ -103,53 +116,57 @@ def source_list(path, filename_check=None):
# extension checking
-def is_cmake(filename):
+def is_cmake(filename: str) -> bool:
ext = splitext(filename)[1]
return (ext == ".cmake") or (filename.endswith("CMakeLists.txt"))
-def is_c_header(filename):
+def is_c_header(filename: str) -> bool:
ext = splitext(filename)[1]
return (ext in {".h", ".hpp", ".hxx", ".hh"})
-def is_py(filename):
+def is_py(filename: str) -> bool:
ext = splitext(filename)[1]
return (ext == ".py")
-def is_glsl(filename):
+def is_glsl(filename: str) -> bool:
ext = splitext(filename)[1]
return (ext == ".glsl")
-def is_c(filename):
+def is_c(filename: str) -> bool:
ext = splitext(filename)[1]
return (ext in {".c", ".cpp", ".cxx", ".m", ".mm", ".rc", ".cc", ".inl", ".osl"})
-def is_c_any(filename):
+def is_c_any(filename: str) -> bool:
return is_c(filename) or is_c_header(filename)
-def is_svn_file(filename):
+def is_svn_file(filename: str) -> bool:
dn, fn = os.path.split(filename)
filename_svn = join(dn, ".svn", "text-base", "%s.svn-base" % fn)
return exists(filename_svn)
-def is_project_file(filename):
+def is_project_file(filename: str) -> bool:
return (is_c_any(filename) or is_cmake(filename) or is_glsl(filename)) # and is_svn_file(filename)
-def cmake_advanced_info():
+def cmake_advanced_info() -> Union[Tuple[List[str], List[Tuple[str, str]]], Tuple[None, None]]:
""" Extract includes and defines from cmake.
"""
make_exe = cmake_cache_var("CMAKE_MAKE_PROGRAM")
+ if make_exe is None:
+ print("Make command not found in: %r not found" % project_path)
+ return None, None
+
make_exe_basename = os.path.basename(make_exe)
- def create_eclipse_project():
+ def create_eclipse_project() -> str:
print("CMAKE_DIR %r" % CMAKE_DIR)
if sys.platform == "win32":
raise Exception("Error: win32 is not supported")
@@ -219,7 +236,7 @@ def cmake_advanced_info():
return includes, defines
-def cmake_cache_var(var):
+def cmake_cache_var(var: str) -> Optional[str]:
with open(os.path.join(CMAKE_DIR, "CMakeCache.txt"), encoding='utf-8') as cache_file:
lines = [
l_strip for l in cache_file
@@ -233,12 +250,12 @@ def cmake_cache_var(var):
return None
-def cmake_compiler_defines():
+def cmake_compiler_defines() -> Optional[List[str]]:
compiler = cmake_cache_var("CMAKE_C_COMPILER") # could do CXX too
if compiler is None:
print("Couldn't find the compiler, os defines will be omitted...")
- return
+ return None
import tempfile
temp_c = tempfile.mkstemp(suffix=".c")[1]
@@ -255,5 +272,5 @@ def cmake_compiler_defines():
return lines
-def project_name_get():
+def project_name_get() -> Optional[str]:
return cmake_cache_var("CMAKE_PROJECT_NAME")
diff --git a/build_files/cmake/project_source_info.py b/build_files/cmake/project_source_info.py
index eb14eea18ef..d2ed80022ca 100644
--- a/build_files/cmake/project_source_info.py
+++ b/build_files/cmake/project_source_info.py
@@ -34,30 +34,45 @@ if sys.version_info.major < 3:
import os
from os.path import join, dirname, normpath, abspath
+import subprocess
+
+from typing import (
+ Any,
+ Callable,
+ Generator,
+ List,
+ Optional,
+ Sequence,
+ Tuple,
+ Union,
+ cast,
+)
+
+
SOURCE_DIR = join(dirname(__file__), "..", "..")
SOURCE_DIR = normpath(SOURCE_DIR)
SOURCE_DIR = abspath(SOURCE_DIR)
-def is_c_header(filename):
+def is_c_header(filename: str) -> bool:
ext = os.path.splitext(filename)[1]
return (ext in {".h", ".hpp", ".hxx", ".hh"})
-def is_c(filename):
+def is_c(filename: str) -> bool:
ext = os.path.splitext(filename)[1]
return (ext in {".c", ".cpp", ".cxx", ".m", ".mm", ".rc", ".cc", ".inl", ".osl"})
-def is_c_any(filename):
- return os.path.s_c(filename) or is_c_header(filename)
+def is_c_any(filename: str) -> bool:
+ return is_c(filename) or is_c_header(filename)
# copied from project_info.py
CMAKE_DIR = "."
-def cmake_cache_var_iter():
+def cmake_cache_var_iter() -> Generator[Tuple[str, str, str], None, None]:
import re
re_cache = re.compile(r'([A-Za-z0-9_\-]+)?:?([A-Za-z0-9_\-]+)?=(.*)$')
with open(join(CMAKE_DIR, "CMakeCache.txt"), 'r', encoding='utf-8') as cache_file:
@@ -68,14 +83,22 @@ def cmake_cache_var_iter():
yield (var, type_ or "", val)
-def cmake_cache_var(var):
+def cmake_cache_var(var: str) -> Optional[str]:
for var_iter, type_iter, value_iter in cmake_cache_var_iter():
if var == var_iter:
return value_iter
return None
-def do_ignore(filepath, ignore_prefix_list):
+def cmake_cache_var_or_exit(var: str) -> str:
+ value = cmake_cache_var(var)
+ if value is None:
+ print("Unable to find %r exiting!" % value)
+ sys.exit(1)
+ return value
+
+
+def do_ignore(filepath: str, ignore_prefix_list: Optional[Sequence[str]]) -> bool:
if ignore_prefix_list is None:
return False
@@ -83,12 +106,13 @@ def do_ignore(filepath, ignore_prefix_list):
return any([relpath.startswith(prefix) for prefix in ignore_prefix_list])
-def makefile_log():
+def makefile_log() -> List[str]:
import subprocess
import time
# support both make and ninja
- make_exe = cmake_cache_var("CMAKE_MAKE_PROGRAM")
+ make_exe = cmake_cache_var_or_exit("CMAKE_MAKE_PROGRAM")
+
make_exe_basename = os.path.basename(make_exe)
if make_exe_basename.startswith(("make", "gmake")):
@@ -102,26 +126,37 @@ def makefile_log():
stdout=subprocess.PIPE,
)
+ if process is None:
+ print("Can't execute process")
+ sys.exit(1)
+
+
while process.poll():
time.sleep(1)
- out = process.stdout.read()
- process.stdout.close()
- print("done!", len(out), "bytes")
- return out.decode("utf-8", errors="ignore").split("\n")
+ # We know this is always true based on the input arguments to `Popen`.
+ stdout: IO[bytes] = process.stdout # type: ignore
+ out = stdout.read()
+ stdout.close()
+ print("done!", len(out), "bytes")
+ return cast(List[str], out.decode("utf-8", errors="ignore").split("\n"))
-def build_info(use_c=True, use_cxx=True, ignore_prefix_list=None):
+def build_info(
+ use_c: bool = True,
+ use_cxx: bool = True,
+ ignore_prefix_list: Optional[List[str]] = None,
+) -> List[Tuple[str, List[str], List[str]]]:
makelog = makefile_log()
source = []
compilers = []
if use_c:
- compilers.append(cmake_cache_var("CMAKE_C_COMPILER"))
+ compilers.append(cmake_cache_var_or_exit("CMAKE_C_COMPILER"))
if use_cxx:
- compilers.append(cmake_cache_var("CMAKE_CXX_COMPILER"))
+ compilers.append(cmake_cache_var_or_exit("CMAKE_CXX_COMPILER"))
print("compilers:", " ".join(compilers))
@@ -131,7 +166,7 @@ def build_info(use_c=True, use_cxx=True, ignore_prefix_list=None):
for line in makelog:
- args = line.split()
+ args: Union[str, List[str]] = line.split()
if not any([(c in args) for c in compilers]):
continue
@@ -176,29 +211,40 @@ def build_info(use_c=True, use_cxx=True, ignore_prefix_list=None):
return source
-def build_defines_as_source():
+def build_defines_as_source() -> str:
"""
Returns a string formatted as an include:
'#defines A=B\n#define....'
"""
import subprocess
# works for both gcc and clang
- cmd = (cmake_cache_var("CMAKE_C_COMPILER"), "-dM", "-E", "-")
- return subprocess.Popen(cmd,
- stdout=subprocess.PIPE,
- stdin=subprocess.DEVNULL,
- ).stdout.read().strip().decode('ascii')
+ cmd = (cmake_cache_var_or_exit("CMAKE_C_COMPILER"), "-dM", "-E", "-")
+ process = subprocess.Popen(
+ cmd,
+ stdout=subprocess.PIPE,
+ stdin=subprocess.DEVNULL,
+ )
+
+ # We know this is always true based on the input arguments to `Popen`.
+ stdout: IO[bytes] = process.stdout # type: ignore
+
+ return cast(str, stdout.read().strip().decode('ascii'))
-def build_defines_as_args():
- return [("-D" + "=".join(l.split(maxsplit=2)[1:]))
- for l in build_defines_as_source().split("\n")
- if l.startswith('#define')]
+def build_defines_as_args() -> List[str]:
+ return [
+ ("-D" + "=".join(l.split(maxsplit=2)[1:]))
+ for l in build_defines_as_source().split("\n")
+ if l.startswith('#define')
+ ]
# could be moved elsewhere!, this just happens to be used by scripts that also
# use this module.
-def queue_processes(process_funcs, job_total=-1):
+def queue_processes(
+ process_funcs: Sequence[Tuple[Callable[..., subprocess.Popen[Any]], Tuple[Any, ...]]],
+ job_total: int =-1,
+) -> None:
""" Takes a list of function arg pairs, each function must return a process
"""
@@ -217,7 +263,7 @@ def queue_processes(process_funcs, job_total=-1):
else:
import time
- processes = []
+ processes: List[subprocess.Popen[Any]] = []
for func, args in process_funcs:
# wait until a thread is free
while 1:
@@ -234,7 +280,7 @@ def queue_processes(process_funcs, job_total=-1):
processes.append(func(*args))
-def main():
+def main() -> None:
if not os.path.exists(join(CMAKE_DIR, "CMakeCache.txt")):
print("This script must run from the cmake build dir")
return
diff --git a/build_files/config/README.md b/build_files/config/README.md
new file mode 100644
index 00000000000..6c429e4e58d
--- /dev/null
+++ b/build_files/config/README.md
@@ -0,0 +1,8 @@
+Pipeline Config
+===============
+
+This configuration file is used by buildbot new pipeline for the `update-code` step.
+
+It will soon be used by the ../utils/make_update.py script.
+
+Both buildbot and developers will eventually use the same configuration file.
diff --git a/build_files/config/pipeline_config.json b/build_files/config/pipeline_config.json
new file mode 100644
index 00000000000..c9a5e25eaaf
--- /dev/null
+++ b/build_files/config/pipeline_config.json
@@ -0,0 +1,87 @@
+{
+ "update-code":
+ {
+ "git" :
+ {
+ "submodules":
+ [
+ { "path": "release/scripts/addons", "branch": "master", "commit_id": "HEAD" },
+ { "path": "release/scripts/addons_contrib", "branch": "master", "commit_id": "HEAD" },
+ { "path": "release/datafiles/locale", "branch": "master", "commit_id": "HEAD" },
+ { "path": "source/tools", "branch": "master", "commit_id": "HEAD" }
+ ]
+ },
+ "svn":
+ {
+ "tests": { "path": "lib/tests", "branch": "trunk", "commit_id": "HEAD" },
+ "libraries":
+ {
+ "darwin-x86_64": { "path": "lib/darwin", "branch": "trunk", "commit_id": "HEAD" },
+ "darwin-arm64": { "path": "lib/darwin_arm64", "branch": "trunk", "commit_id": "HEAD" },
+ "linux-x86_64": { "path": "lib/linux_centos7_x86_64", "branch": "trunk", "commit_id": "HEAD" },
+ "windows-amd64": { "path": "lib/win64_vc15", "branch": "trunk", "commit_id": "HEAD" }
+ }
+ }
+ },
+ "buildbot":
+ {
+ "gcc":
+ {
+ "version": "9.0"
+ },
+ "sdks":
+ {
+ "optix":
+ {
+ "version": "7.1.0"
+ },
+ "cuda10":
+ {
+ "version": "10.1"
+ },
+ "cuda11":
+ {
+ "version": "11.3"
+ }
+ },
+ "cmake":
+ {
+ "default":
+ {
+ "version": "any",
+ "overrides":
+ {
+
+ }
+ },
+ "darwin-x86_64":
+ {
+ "overrides":
+ {
+
+ }
+ },
+ "darwin-arm64":
+ {
+ "overrides":
+ {
+
+ }
+ },
+ "linux-x86_64":
+ {
+ "overrides":
+ {
+
+ }
+ },
+ "windows-amd64":
+ {
+ "overrides":
+ {
+
+ }
+ }
+ }
+ }
+}
diff --git a/build_files/utils/README.md b/build_files/utils/README.md
new file mode 100644
index 00000000000..e78d05b0c69
--- /dev/null
+++ b/build_files/utils/README.md
@@ -0,0 +1,5 @@
+Make Utility Scripts
+====================
+
+Scripts used only by developers for now
+
diff --git a/build_files/utils/make_source_archive.py b/build_files/utils/make_source_archive.py
index 271ca358f7e..2ef6dccd5be 100755
--- a/build_files/utils/make_source_archive.py
+++ b/build_files/utils/make_source_archive.py
@@ -1,11 +1,12 @@
#!/usr/bin/env python3
+import argparse
import dataclasses
import os
import re
import subprocess
from pathlib import Path
-from typing import Iterable, TextIO
+from typing import Iterable, TextIO, Optional, Any, Union
# This script can run from any location,
# output is created in the $CWD
@@ -18,21 +19,43 @@ SKIP_NAMES = {
".gitignore",
".gitmodules",
".arcconfig",
+ ".svn",
}
def main() -> None:
- output_dir = Path(".").absolute()
blender_srcdir = Path(__file__).absolute().parent.parent.parent
+
+ cli_parser = argparse.ArgumentParser(
+ description=f"Create a tarball of the Blender sources, optionally including sources of dependencies.",
+ epilog="This script is intended to be run by `make source_archive_complete`.",
+ )
+ cli_parser.add_argument(
+ "-p",
+ "--include-packages",
+ type=Path,
+ default=None,
+ metavar="PACKAGE_PATH",
+ help="Include all source files from the given package directory as well.",
+ )
+
+ cli_args = cli_parser.parse_args()
+
print(f"Source dir: {blender_srcdir}")
+ curdir = blender_srcdir.parent
+ os.chdir(curdir)
+ blender_srcdir = blender_srcdir.relative_to(curdir)
+
+ print(f"Output dir: {curdir}")
+
version = parse_blender_version(blender_srcdir)
- manifest = output_dir / f"blender-{version}-manifest.txt"
- tarball = output_dir / f"blender-{version}.tar.xz"
+ tarball = tarball_path(curdir, version, cli_args)
+ manifest = manifest_path(tarball)
+ packages_dir = packages_path(curdir, cli_args)
- os.chdir(blender_srcdir)
- create_manifest(version, manifest)
- create_tarball(version, tarball, manifest)
+ create_manifest(version, manifest, blender_srcdir, packages_dir)
+ create_tarball(version, tarball, manifest, blender_srcdir, packages_dir)
create_checksum_file(tarball)
cleanup(manifest)
print("Done!")
@@ -84,43 +107,109 @@ def parse_blender_version(blender_srcdir: Path) -> BlenderVersion:
)
+def tarball_path(output_dir: Path, version: BlenderVersion, cli_args: Any) -> Path:
+ extra = ""
+ if cli_args.include_packages:
+ extra = "-with-libraries"
+
+ return output_dir / f"blender{extra}-{version}.tar.xz"
+
+
+def manifest_path(tarball: Path) -> Path:
+ """Return the manifest path for the given tarball path.
+
+ >>> from pathlib import Path
+ >>> tarball = Path("/home/sybren/workspace/blender-git/blender-test.tar.gz")
+ >>> manifest_path(tarball).as_posix()
+ '/home/sybren/workspace/blender-git/blender-test-manifest.txt'
+ """
+ # ".tar.gz" is seen as two suffixes.
+ without_suffix = tarball.with_suffix("").with_suffix("")
+ name = without_suffix.name
+ return without_suffix.with_name(f"{name}-manifest.txt")
+
+
+def packages_path(current_directory: Path, cli_args: Any) -> Optional[Path]:
+ if not cli_args.include_packages:
+ return None
+
+ abspath = cli_args.include_packages.absolute()
+
+ # os.path.relpath() can return paths like "../../packages", where
+ # Path.relative_to() will not go up directories (so its return value never
+ # has "../" in there).
+ relpath = os.path.relpath(abspath, current_directory)
+
+ return Path(relpath)
+
+
### Manifest creation
-def create_manifest(version: BlenderVersion, outpath: Path) -> None:
+def create_manifest(
+ version: BlenderVersion,
+ outpath: Path,
+ blender_srcdir: Path,
+ packages_dir: Optional[Path],
+) -> None:
print(f'Building manifest of files: "{outpath}"...', end="", flush=True)
with outpath.open("w", encoding="utf-8") as outfile:
- main_files_to_manifest(outfile)
- submodules_to_manifest(version, outfile)
+ main_files_to_manifest(blender_srcdir, outfile)
+ submodules_to_manifest(blender_srcdir, version, outfile)
+
+ if packages_dir:
+ packages_to_manifest(outfile, packages_dir)
print("OK")
-def main_files_to_manifest(outfile: TextIO) -> None:
- for path in git_ls_files():
+def main_files_to_manifest(blender_srcdir: Path, outfile: TextIO) -> None:
+ assert not blender_srcdir.is_absolute()
+ for path in git_ls_files(blender_srcdir):
print(path, file=outfile)
-def submodules_to_manifest(version: BlenderVersion, outfile: TextIO) -> None:
+def submodules_to_manifest(
+ blender_srcdir: Path, version: BlenderVersion, outfile: TextIO
+) -> None:
skip_addon_contrib = version.is_release
+ assert not blender_srcdir.is_absolute()
- for line in git_command("submodule"):
+ for line in git_command("-C", blender_srcdir, "submodule"):
submodule = line.split()[1]
# Don't use native slashes as GIT for MS-Windows outputs forward slashes.
if skip_addon_contrib and submodule == "release/scripts/addons_contrib":
continue
- for path in git_ls_files(Path(submodule)):
+ for path in git_ls_files(blender_srcdir / submodule):
print(path, file=outfile)
-def create_tarball(version: BlenderVersion, tarball: Path, manifest: Path) -> None:
+def packages_to_manifest(outfile: TextIO, packages_dir: Path) -> None:
+ for path in packages_dir.glob("*"):
+ if not path.is_file():
+ continue
+ if path.name in SKIP_NAMES:
+ continue
+ print(path, file=outfile)
+
+
+### Higher-level functions
+
+
+def create_tarball(
+ version: BlenderVersion, tarball: Path, manifest: Path, blender_srcdir: Path, packages_dir: Optional[Path]
+) -> None:
print(f'Creating archive: "{tarball}" ...', end="", flush=True)
+ command = ["tar"]
+
# Requires GNU `tar`, since `--transform` is used.
- command = [
- "tar",
+ if packages_dir:
+ command += ["--transform", f"s,{packages_dir}/,packages/,g"]
+
+ command += [
"--transform",
- f"s,^,blender-{version}/,g",
+ f"s,^{blender_srcdir.name}/,blender-{version}/,g",
"--use-compress-program=xz -9",
"--create",
f"--file={tarball}",
@@ -130,7 +219,8 @@ def create_tarball(version: BlenderVersion, tarball: Path, manifest: Path) -> No
"--owner=0",
"--group=0",
]
- subprocess.run(command, check=True, timeout=300)
+
+ subprocess.run(command, check=True, timeout=3600)
print("OK")
@@ -174,7 +264,7 @@ def git_ls_files(directory: Path = Path(".")) -> Iterable[Path]:
yield path
-def git_command(*cli_args) -> Iterable[str]:
+def git_command(*cli_args: Union[bytes, str, Path] ) -> Iterable[str]:
"""Generator, yields lines of output from a Git command."""
command = ("git", *cli_args)
diff --git a/build_files/windows/configure_msbuild.cmd b/build_files/windows/configure_msbuild.cmd
index 4956f1e3ea1..be69f99a8e8 100644
--- a/build_files/windows/configure_msbuild.cmd
+++ b/build_files/windows/configure_msbuild.cmd
@@ -9,14 +9,10 @@ if "%BUILD_WITH_SCCACHE%"=="1" (
if "%WITH_CLANG%"=="1" (
set CLANG_CMAKE_ARGS=-T"llvm"
- if "%WITH_ASAN%"=="1" (
+)
+
+if "%WITH_ASAN%"=="1" (
set ASAN_CMAKE_ARGS=-DWITH_COMPILER_ASAN=On
- )
-) else (
- if "%WITH_ASAN%"=="1" (
- echo ASAN is only supported with clang.
- exit /b 1
- )
)
if "%WITH_PYDEBUG%"=="1" (
diff --git a/build_files/windows/configure_ninja.cmd b/build_files/windows/configure_ninja.cmd
index d68d94a3bcf..90085feb2bd 100644
--- a/build_files/windows/configure_ninja.cmd
+++ b/build_files/windows/configure_ninja.cmd
@@ -46,16 +46,10 @@ set LLVM_DIR=
set CFLAGS=-m64 -fmsc-version=1914
set CXXFLAGS=-m64 -fmsc-version=1914
)
- if "%WITH_ASAN%"=="1" (
- set BUILD_CMAKE_ARGS=%BUILD_CMAKE_ARGS% -DWITH_COMPILER_ASAN=On
- )
)
if "%WITH_ASAN%"=="1" (
- if "%WITH_CLANG%" == "" (
- echo ASAN is only supported with clang.
- exit /b 1
- )
+ set BUILD_CMAKE_ARGS=%BUILD_CMAKE_ARGS% -DWITH_COMPILER_ASAN=On
)
if NOT "%verbose%" == "" (
diff --git a/doc/blender_file_format/BlendFileReader.py b/doc/blender_file_format/BlendFileReader.py
index d4aed722578..a2f214bc4fc 100644
--- a/doc/blender_file_format/BlendFileReader.py
+++ b/doc/blender_file_format/BlendFileReader.py
@@ -85,7 +85,7 @@ def openBlendFile(filename):
'''
handle = open(filename, 'rb')
magic = ReadString(handle, 7)
- if magic in ("BLENDER", "BULLETf"):
+ if magic in {"BLENDER", "BULLETf"}:
log.debug("normal blendfile detected")
handle.seek(0, os.SEEK_SET)
return handle
@@ -137,7 +137,7 @@ class BlendFile:
fileblock = BlendFileBlock(handle, self)
found_dna_block = False
while not found_dna_block:
- if fileblock.Header.Code in ("DNA1", "SDNA"):
+ if fileblock.Header.Code in {"DNA1", "SDNA"}:
self.Catalog = DNACatalog(self.Header, handle)
found_dna_block = True
else:
diff --git a/doc/doxygen/Doxyfile b/doc/doxygen/Doxyfile
index f9055f02bcf..96eb30a852e 100644
--- a/doc/doxygen/Doxyfile
+++ b/doc/doxygen/Doxyfile
@@ -1,4 +1,4 @@
-# Doxyfile 1.8.16
+# Doxyfile 1.9.1
# This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project.
@@ -38,7 +38,7 @@ PROJECT_NAME = Blender
# could be handy for archiving the generated documentation or if some version
# control system is used.
-PROJECT_NUMBER = "V2.93"
+PROJECT_NUMBER = V3.0
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
@@ -227,6 +227,14 @@ QT_AUTOBRIEF = NO
MULTILINE_CPP_IS_BRIEF = NO
+# By default Python docstrings are displayed as preformatted text and doxygen's
+# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the
+# doxygen's special commands can be used and the contents of the docstring
+# documentation blocks is shown as doxygen documentation.
+# The default value is: YES.
+
+PYTHON_DOCSTRING = YES
+
# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
# documentation from any documented member that it re-implements.
# The default value is: YES.
@@ -263,12 +271,6 @@ TAB_SIZE = 4
ALIASES =
-# This tag can be used to specify a number of word-keyword mappings (TCL only).
-# A mapping has the form "name=value". For example adding "class=itcl::class"
-# will allow you to use the command class in the itcl::class meaning.
-
-TCL_SUBST =
-
# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
# only. Doxygen will then generate output that is more tailored for C. For
# instance, some of the names that are used will be different. The list of all
@@ -309,19 +311,22 @@ OPTIMIZE_OUTPUT_SLICE = NO
# parses. With this tag you can assign which parser to use for a given
# extension. Doxygen has a built-in mapping, but you can override or extend it
# using this tag. The format is ext=language, where ext is a file extension, and
-# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
-# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice,
+# language is one of the parsers supported by doxygen: IDL, Java, JavaScript,
+# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL,
# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser
# tries to guess whether the code is fixed or free formatted code, this is the
-# default for Fortran type files), VHDL, tcl. For instance to make doxygen treat
-# .inc files as Fortran files (default is PHP), and .f files as C (default is
-# Fortran), use: inc=Fortran f=C.
+# default for Fortran type files). For instance to make doxygen treat .inc files
+# as Fortran files (default is PHP), and .f files as C (default is Fortran),
+# use: inc=Fortran f=C.
#
# Note: For files without extension you can use no_extension as a placeholder.
#
# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
-# the files are not read by doxygen.
+# the files are not read by doxygen. When specifying no_extension you should add
+# * to the FILE_PATTERNS.
+#
+# Note see also the list of default file extension mappings.
EXTENSION_MAPPING =
@@ -455,6 +460,19 @@ TYPEDEF_HIDES_STRUCT = NO
LOOKUP_CACHE_SIZE = 3
+# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use
+# during processing. When set to 0 doxygen will based this on the number of
+# cores available in the system. You can set it explicitly to a value larger
+# than 0 to get more control over the balance between CPU load and processing
+# speed. At this moment only the input processing can be done using multiple
+# threads. Since this is still an experimental feature the default is set to 1,
+# which efficively disables parallel processing. Please report any issues you
+# encounter. Generating dot graphs in parallel is controlled by the
+# DOT_NUM_THREADS setting.
+# Minimum value: 0, maximum value: 32, default value: 1.
+
+NUM_PROC_THREADS = 1
+
#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------
@@ -518,6 +536,13 @@ EXTRACT_LOCAL_METHODS = NO
EXTRACT_ANON_NSPACES = NO
+# If this flag is set to YES, the name of an unnamed parameter in a declaration
+# will be determined by the corresponding definition. By default unnamed
+# parameters remain unnamed in the output.
+# The default value is: YES.
+
+RESOLVE_UNNAMED_PARAMS = YES
+
# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
# undocumented members inside documented classes or files. If set to NO these
# members will be included in the various overviews, but no documentation
@@ -535,8 +560,8 @@ HIDE_UNDOC_MEMBERS = NO
HIDE_UNDOC_CLASSES = NO
# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
-# (class|struct|union) declarations. If set to NO, these declarations will be
-# included in the documentation.
+# declarations. If set to NO, these declarations will be included in the
+# documentation.
# The default value is: NO.
HIDE_FRIEND_COMPOUNDS = NO
@@ -555,11 +580,18 @@ HIDE_IN_BODY_DOCS = NO
INTERNAL_DOCS = YES
-# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
-# names in lower-case letters. If set to YES, upper-case letters are also
-# allowed. This is useful if you have classes or files whose names only differ
-# in case and if your file system supports case sensitive file names. Windows
-# (including Cygwin) ands Mac users are advised to set this option to NO.
+# With the correct setting of option CASE_SENSE_NAMES doxygen will better be
+# able to match the capabilities of the underlying filesystem. In case the
+# filesystem is case sensitive (i.e. it supports files in the same directory
+# whose names only differ in casing), the option must be set to YES to properly
+# deal with such files in case they appear in the input. For filesystems that
+# are not case sensitive the option should be be set to NO to properly deal with
+# output files written for symbols that only differ in casing, such as for two
+# classes, one named CLASS and the other named Class, and to also support
+# references to files without having to specify the exact matching casing. On
+# Windows (including Cygwin) and MacOS, users should typically set this option
+# to NO, whereas on Linux or other Unix flavors it should typically be set to
+# YES.
# The default value is: system dependent.
CASE_SENSE_NAMES = YES
@@ -798,7 +830,10 @@ WARN_IF_DOC_ERROR = YES
WARN_NO_PARAMDOC = NO
# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
-# a warning is encountered.
+# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS
+# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but
+# at the end of the doxygen process doxygen will return with a non-zero status.
+# Possible values are: NO, YES and FAIL_ON_WARNINGS.
# The default value is: NO.
WARN_AS_ERROR = NO
@@ -840,8 +875,8 @@ INPUT = doxygen.main.h \
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
-# documentation (see: https://www.gnu.org/software/libiconv/) for the list of
-# possible encodings.
+# documentation (see:
+# https://www.gnu.org/software/libiconv/) for the list of possible encodings.
# The default value is: UTF-8.
INPUT_ENCODING = UTF-8
@@ -854,11 +889,15 @@ INPUT_ENCODING = UTF-8
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
# read by doxygen.
#
+# Note the list of default checked file patterns might differ from the list of
+# default file extension mappings.
+#
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
-# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
-# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice.
+# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment),
+# *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, *.vhdl,
+# *.ucf, *.qsf and *.ice.
FILE_PATTERNS =
@@ -1086,13 +1125,6 @@ VERBATIM_HEADERS = YES
ALPHABETICAL_INDEX = YES
-# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
-# which the alphabetical index list will be split.
-# Minimum value: 1, maximum value: 20, default value: 5.
-# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
-
-COLS_IN_ALPHA_INDEX = 5
-
# In case all classes in a project start with a common prefix, all classes will
# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
# can be used to specify a prefix (or a list of prefixes) that should be ignored
@@ -1231,9 +1263,9 @@ HTML_TIMESTAMP = YES
# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
# documentation will contain a main index with vertical navigation menus that
-# are dynamically created via Javascript. If disabled, the navigation index will
+# are dynamically created via JavaScript. If disabled, the navigation index will
# consists of multiple levels of tabs that are statically embedded in every HTML
-# page. Disable this option to support browsers that do not have Javascript,
+# page. Disable this option to support browsers that do not have JavaScript,
# like the Qt help browser.
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
@@ -1263,10 +1295,11 @@ HTML_INDEX_NUM_ENTRIES = 100
# If the GENERATE_DOCSET tag is set to YES, additional index files will be
# generated that can be used as input for Apple's Xcode 3 integrated development
-# environment (see: https://developer.apple.com/xcode/), introduced with OSX
-# 10.5 (Leopard). To create a documentation set, doxygen will generate a
-# Makefile in the HTML output directory. Running make will produce the docset in
-# that directory and running make install will install the docset in
+# environment (see:
+# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To
+# create a documentation set, doxygen will generate a Makefile in the HTML
+# output directory. Running make will produce the docset in that directory and
+# running make install will install the docset in
# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy
# genXcode/_index.html for more information.
@@ -1308,8 +1341,8 @@ DOCSET_PUBLISHER_NAME = Publisher
# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
-# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on
-# Windows.
+# (see:
+# https://www.microsoft.com/en-us/download/details.aspx?id=21138) on Windows.
#
# The HTML Help Workshop contains a compiler that can convert all HTML output
# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
@@ -1339,7 +1372,7 @@ CHM_FILE = blender.chm
HHC_LOCATION = "C:/Program Files (x86)/HTML Help Workshop/hhc.exe"
# The GENERATE_CHI flag controls if a separate .chi index file is generated
-# (YES) or that it should be included in the master .chm file (NO).
+# (YES) or that it should be included in the main .chm file (NO).
# The default value is: NO.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
@@ -1384,7 +1417,8 @@ QCH_FILE =
# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
# Project output. For more information please see Qt Help Project / Namespace
-# (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
+# (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
# The default value is: org.doxygen.Project.
# This tag requires that the tag GENERATE_QHP is set to YES.
@@ -1392,8 +1426,8 @@ QHP_NAMESPACE = org.doxygen.Project
# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
# Help Project output. For more information please see Qt Help Project / Virtual
-# Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-
-# folders).
+# Folders (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders).
# The default value is: doc.
# This tag requires that the tag GENERATE_QHP is set to YES.
@@ -1401,16 +1435,16 @@ QHP_VIRTUAL_FOLDER = doc
# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
# filter to add. For more information please see Qt Help Project / Custom
-# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
-# filters).
+# Filters (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_CUST_FILTER_NAME =
# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
# custom filter to add. For more information please see Qt Help Project / Custom
-# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
-# filters).
+# Filters (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_CUST_FILTER_ATTRS =
@@ -1422,9 +1456,9 @@ QHP_CUST_FILTER_ATTRS =
QHP_SECT_FILTER_ATTRS =
-# The QHG_LOCATION tag can be used to specify the location of Qt's
-# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
-# generated .qhp file.
+# The QHG_LOCATION tag can be used to specify the location (absolute path
+# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to
+# run qhelpgenerator on the generated .qhp file.
# This tag requires that the tag GENERATE_QHP is set to YES.
QHG_LOCATION =
@@ -1501,6 +1535,17 @@ TREEVIEW_WIDTH = 246
EXT_LINKS_IN_WINDOW = NO
+# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg
+# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see
+# https://inkscape.org) to generate formulas as SVG images instead of PNGs for
+# the HTML output. These images will generally look nicer at scaled resolutions.
+# Possible values are: png (the default) and svg (looks nicer but requires the
+# pdf2svg or inkscape tool).
+# The default value is: png.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FORMULA_FORMAT = png
+
# Use this tag to change the font size of LaTeX formulas included as images in
# the HTML documentation. When you change the font size after a successful
# doxygen run you need to manually remove any form_*.png images from the HTML
@@ -1521,8 +1566,14 @@ FORMULA_FONTSIZE = 10
FORMULA_TRANSPARENT = YES
+# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands
+# to create new LaTeX commands to be used in formulas as building blocks. See
+# the section "Including formulas" for details.
+
+FORMULA_MACROFILE =
+
# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
-# https://www.mathjax.org) which uses client side Javascript for the rendering
+# https://www.mathjax.org) which uses client side JavaScript for the rendering
# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
# installed or if you want to formulas look prettier in the HTML output. When
# enabled you may also need to install MathJax separately and configure the path
@@ -1534,7 +1585,7 @@ USE_MATHJAX = NO
# When MathJax is enabled you can set the default output format to be used for
# the MathJax output. See the MathJax site (see:
-# http://docs.mathjax.org/en/latest/output.html) for more details.
+# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details.
# Possible values are: HTML-CSS (which is slower, but has the best
# compatibility), NativeMML (i.e. MathML) and SVG.
# The default value is: HTML-CSS.
@@ -1550,7 +1601,7 @@ MATHJAX_FORMAT = HTML-CSS
# Content Delivery Network so you can quickly see the result without installing
# MathJax. However, it is strongly recommended to install a local copy of
# MathJax from https://www.mathjax.org before deployment.
-# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/.
+# The default value is: https://cdn.jsdelivr.net/npm/mathjax@2.
# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_RELPATH = http://www.mathjax.org/mathjax
@@ -1564,7 +1615,8 @@ MATHJAX_EXTENSIONS =
# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
# of code that will be used on startup of the MathJax code. See the MathJax site
-# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# (see:
+# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an
# example see the documentation.
# This tag requires that the tag USE_MATHJAX is set to YES.
@@ -1592,7 +1644,7 @@ MATHJAX_CODEFILE =
SEARCHENGINE = NO
# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
-# implemented using a web server instead of a web client using Javascript. There
+# implemented using a web server instead of a web client using JavaScript. There
# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
# setting. When disabled, doxygen will generate a PHP script for searching and
# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
@@ -1611,7 +1663,8 @@ SERVER_BASED_SEARCH = NO
#
# Doxygen ships with an example indexer (doxyindexer) and search engine
# (doxysearch.cgi) which are based on the open source search engine library
-# Xapian (see: https://xapian.org/).
+# Xapian (see:
+# https://xapian.org/).
#
# See the section "External Indexing and Searching" for details.
# The default value is: NO.
@@ -1624,8 +1677,9 @@ EXTERNAL_SEARCH = NO
#
# Doxygen ships with an example indexer (doxyindexer) and search engine
# (doxysearch.cgi) which are based on the open source search engine library
-# Xapian (see: https://xapian.org/). See the section "External Indexing and
-# Searching" for details.
+# Xapian (see:
+# https://xapian.org/). See the section "External Indexing and Searching" for
+# details.
# This tag requires that the tag SEARCHENGINE is set to YES.
SEARCHENGINE_URL =
@@ -1789,9 +1843,11 @@ LATEX_EXTRA_FILES =
PDF_HYPERLINKS = NO
-# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
-# the PDF file directly from the LaTeX files. Set this option to YES, to get a
-# higher quality PDF documentation.
+# If the USE_PDFLATEX tag is set to YES, doxygen will use the engine as
+# specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX
+# files. Set this option to YES, to get a higher quality PDF documentation.
+#
+# See also section LATEX_CMD_NAME for selecting the engine.
# The default value is: YES.
# This tag requires that the tag GENERATE_LATEX is set to YES.
@@ -2126,7 +2182,8 @@ INCLUDE_FILE_PATTERNS =
# recursively expanded use the := operator instead of the = operator.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
-PREDEFINED = BUILD_DATE
+PREDEFINED = BUILD_DATE \
+ DOXYGEN=1
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
# tag can be used to specify a list of macro names that should be expanded. The
@@ -2303,10 +2360,32 @@ UML_LOOK = YES
# but if the number exceeds 15, the total amount of fields shown is limited to
# 10.
# Minimum value: 0, maximum value: 100, default value: 10.
-# This tag requires that the tag HAVE_DOT is set to YES.
+# This tag requires that the tag UML_LOOK is set to YES.
UML_LIMIT_NUM_FIELDS = 10
+# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and
+# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS
+# tag is set to YES, doxygen will add type and arguments for attributes and
+# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen
+# will not generate fields with class member information in the UML graphs. The
+# class diagrams will look similar to the default class diagrams but using UML
+# notation for the relationships.
+# Possible values are: NO, YES and NONE.
+# The default value is: NO.
+# This tag requires that the tag UML_LOOK is set to YES.
+
+DOT_UML_DETAILS = NO
+
+# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters
+# to display on a single line. If the actual line length exceeds this threshold
+# significantly it will wrapped across multiple lines. Some heuristics are apply
+# to avoid ugly line breaks.
+# Minimum value: 0, maximum value: 1000, default value: 17.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_WRAP_THRESHOLD = 17
+
# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
# collaboration graphs will show the relations between templates and their
# instances.
@@ -2496,9 +2575,11 @@ DOT_MULTI_TARGETS = YES
GENERATE_LEGEND = YES
-# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot
+# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate
# files that are used to generate the various graphs.
+#
+# Note: This setting is not only used for dot files but also for msc and
+# plantuml temporary files.
# The default value is: YES.
-# This tag requires that the tag HAVE_DOT is set to YES.
DOT_CLEANUP = YES
diff --git a/doc/manpage/blender.1.py b/doc/manpage/blender.1.py
index 150548072d5..950a2119f91 100755
--- a/doc/manpage/blender.1.py
+++ b/doc/manpage/blender.1.py
@@ -22,7 +22,7 @@
This script generates the blender.1 man page, embedding the help text
from the Blender executable itself. Invoke it as follows:
- blender.1.py <path-to-blender> <output-filename>
+ blender.1.py --blender <path-to-blender> --output <output-filename>
where <path-to-blender> is the path to the Blender executable,
and <output-filename> is where to write the generated man page.
@@ -30,108 +30,147 @@ and <output-filename> is where to write the generated man page.
# <pep8 compliant>
+import argparse
import os
import subprocess
import sys
-
import time
+from typing import (
+ Dict,
+ TextIO,
+)
+
-def man_format(data):
+def man_format(data: str) -> str:
data = data.replace("-", "\\-")
data = data.replace("\t", " ")
return data
-if len(sys.argv) != 3:
- import getopt
- raise getopt.GetoptError("Usage: %s <path-to-blender> <output-filename>" % sys.argv[0])
+def blender_extract_info(blender_bin: str) -> Dict[str, str]:
+
+ blender_env = {
+ "ASAN_OPTIONS": "exitcode=0:" + os.environ.get("ASAN_OPTIONS", ""),
+ }
+
+ blender_help = subprocess.run(
+ [blender_bin, "--help"],
+ env=blender_env,
+ check=True,
+ stdout=subprocess.PIPE,
+ ).stdout.decode(encoding="utf-8")
+
+ blender_version_ouput = subprocess.run(
+ [blender_bin, "--version"],
+ env=blender_env,
+ check=True,
+ stdout=subprocess.PIPE,
+ ).stdout.decode(encoding="utf-8")
+
+ # Extract information from the version string.
+ # Note that some internal modules may print errors (e.g. color management),
+ # check for each lines prefix to ensure these aren't included.
+ blender_version = ""
+ blender_date = ""
+ for l in blender_version_ouput.split("\n"):
+ if l.startswith("Blender "):
+ # Remove 'Blender' prefix.
+ blender_version = l.split(" ", 1)[1].strip()
+ elif l.lstrip().startswith("build date:"):
+ # Remove 'build date:' prefix.
+ blender_date = l.split(":", 1)[1].strip()
+ if blender_version and blender_date:
+ break
+
+ if not blender_date:
+ # Happens when built without WITH_BUILD_INFO e.g.
+ date_string = time.strftime("%B %d, %Y", time.gmtime(int(os.environ.get('SOURCE_DATE_EPOCH', time.time()))))
+ else:
+ date_string = time.strftime("%B %d, %Y", time.strptime(blender_date, "%Y-%m-%d"))
-blender_bin = sys.argv[1]
-outfilename = sys.argv[2]
+ return {
+ "help": blender_help,
+ "version": blender_version,
+ "date": date_string,
+ }
-cmd = [blender_bin, "--help"]
-print(" executing:", " ".join(cmd))
-ASAN_OPTIONS = "exitcode=0:" + os.environ.get("ASAN_OPTIONS", "")
-blender_help = subprocess.run(
- cmd, env={"ASAN_OPTIONS": ASAN_OPTIONS}, check=True, stdout=subprocess.PIPE).stdout.decode(encoding="utf-8")
-blender_version = subprocess.run(
- [blender_bin, "--version"], env={"ASAN_OPTIONS": ASAN_OPTIONS}, check=True, stdout=subprocess.PIPE).stdout.decode(encoding="utf-8").strip()
-blender_version, blender_date = (blender_version.split("build") + [None, None])[0:2]
-blender_version = blender_version.rstrip().partition(" ")[2] # remove 'Blender' prefix.
-if blender_date is None:
- # Happens when built without WITH_BUILD_INFO e.g.
- date_string = time.strftime("%B %d, %Y", time.gmtime(int(os.environ.get('SOURCE_DATE_EPOCH', time.time()))))
-else:
- blender_date = blender_date.strip().partition(" ")[2] # remove 'date:' prefix
- date_string = time.strftime("%B %d, %Y", time.strptime(blender_date, "%Y-%m-%d"))
-outfile = open(outfilename, "w")
-fw = outfile.write
+def man_page_from_blender_help(fh: TextIO, blender_bin: str) -> None:
+ blender_info = blender_extract_info(blender_bin)
-fw('.TH "BLENDER" "1" "%s" "Blender %s"\n' % (date_string, blender_version.replace(".", "\\&.")))
+ # Header Content.
+ fh.write(
+ '.TH "BLENDER" "1" "%s" "Blender %s"\n' %
+ (blender_info["date"], blender_info["version"].replace(".", "\\&."))
+ )
-fw('''
+ fh.write(r'''
.SH NAME
blender \- a full-featured 3D application''')
-fw('''
+ fh.write(r'''
.SH SYNOPSIS
.B blender [args ...] [file] [args ...]''')
-fw('''
+ fh.write(r'''
.br
.SH DESCRIPTION
.PP
.B blender
-is a full-featured 3D application. It supports the entirety of the 3D pipeline - modeling, rigging, animation, simulation, rendering, compositing, motion tracking, and video editing.
+is a full-featured 3D application. It supports the entirety of the 3D pipeline - '''
+'''modeling, rigging, animation, simulation, rendering, compositing, motion tracking, and video editing.
-Use Blender to create 3D images and animations, films and commercials, content for games, architectural and industrial visualizatons, and scientific visualizations.
+Use Blender to create 3D images and animations, films and commercials, content for games, '''
+r'''architectural and industrial visualizatons, and scientific visualizations.
https://www.blender.org''')
-fw('''
+ fh.write(r'''
.SH OPTIONS''')
-fw("\n\n")
+ fh.write("\n\n")
-lines = [line.rstrip() for line in blender_help.split("\n")]
+ # Body Content.
-while lines:
- l = lines.pop(0)
- if l.startswith("Environment Variables:"):
- fw('.SH "ENVIRONMENT VARIABLES"\n')
- elif l.endswith(":"): # one line
- fw('.SS "%s"\n\n' % l)
- elif l.startswith("-") or l.startswith("/"): # can be multi line
+ lines = [line.rstrip() for line in blender_info["help"].split("\n")]
- fw('.TP\n')
- fw('.B %s\n' % man_format(l))
+ while lines:
+ l = lines.pop(0)
+ if l.startswith("Environment Variables:"):
+ fh.write('.SH "ENVIRONMENT VARIABLES"\n')
+ elif l.endswith(":"): # One line.
+ fh.write('.SS "%s"\n\n' % l)
+ elif l.startswith("-") or l.startswith("/"): # Can be multi line.
+ fh.write('.TP\n')
+ fh.write('.B %s\n' % man_format(l))
- while lines:
- # line with no
- if lines[0].strip() and len(lines[0].lstrip()) == len(lines[0]): # no white space
- break
+ while lines:
+ # line with no
+ if lines[0].strip() and len(lines[0].lstrip()) == len(lines[0]): # No white space.
+ break
- if not l: # second blank line
- fw('.IP\n')
- else:
- fw('.br\n')
+ if not l: # Second blank line.
+ fh.write('.IP\n')
+ else:
+ fh.write('.br\n')
- l = lines.pop(0)
- l = l[1:] # remove first whitespace (tab)
+ l = lines.pop(0)
+ if l:
+ assert(l.startswith('\t'))
+ l = l[1:] # Remove first white-space (tab).
- fw('%s\n' % man_format(l))
+ fh.write('%s\n' % man_format(l))
- else:
- if not l.strip():
- fw('.br\n')
else:
- fw('%s\n' % man_format(l))
+ if not l.strip():
+ fh.write('.br\n')
+ else:
+ fh.write('%s\n' % man_format(l))
-# footer
+ # Footer Content.
-fw('''
+ fh.write(r'''
.br
.SH SEE ALSO
.B luxrender(1)
@@ -143,5 +182,33 @@ This manpage was written for a Debian GNU/Linux system by Daniel Mester
<cyril.brulebois@enst-bretagne.fr> and Dan Eicher <dan@trollwerks.org>.
''')
-outfile.close()
-print("written:", outfilename)
+
+def create_argparse() -> argparse.ArgumentParser:
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ "--output",
+ required=True,
+ help="The man page to write to."
+ )
+ parser.add_argument(
+ "--blender",
+ required=True,
+ help="Path to the blender binary."
+ )
+
+ return parser
+
+
+def main() -> None:
+ parser = create_argparse()
+ args = parser.parse_args()
+
+ blender_bin = args.blender
+ output_filename = args.output
+
+ with open(output_filename, "w", encoding="utf-8") as fh:
+ man_page_from_blender_help(fh, blender_bin)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/doc/python_api/examples/bpy.types.RenderEngine.py b/doc/python_api/examples/bpy.types.RenderEngine.py
index 45910194244..0b8795340ad 100644
--- a/doc/python_api/examples/bpy.types.RenderEngine.py
+++ b/doc/python_api/examples/bpy.types.RenderEngine.py
@@ -4,7 +4,9 @@ Simple Render Engine
"""
import bpy
-import bgl
+import array
+import gpu
+from gpu_extras.presets import draw_texture_2d
class CustomRenderEngine(bpy.types.RenderEngine):
@@ -100,8 +102,7 @@ class CustomRenderEngine(bpy.types.RenderEngine):
dimensions = region.width, region.height
# Bind shader that converts from scene linear to display space,
- bgl.glEnable(bgl.GL_BLEND)
- bgl.glBlendFunc(bgl.GL_ONE, bgl.GL_ONE_MINUS_SRC_ALPHA)
+ gpu.state.blend_set('ALPHA_PREMULT')
self.bind_display_space_shader(scene)
if not self.draw_data or self.draw_data.dimensions != dimensions:
@@ -110,7 +111,7 @@ class CustomRenderEngine(bpy.types.RenderEngine):
self.draw_data.draw()
self.unbind_display_space_shader()
- bgl.glDisable(bgl.GL_BLEND)
+ gpu.state.blend_set('NONE')
class CustomDrawData:
@@ -119,68 +120,21 @@ class CustomDrawData:
self.dimensions = dimensions
width, height = dimensions
- pixels = [0.1, 0.2, 0.1, 1.0] * width * height
- pixels = bgl.Buffer(bgl.GL_FLOAT, width * height * 4, pixels)
+ pixels = width * height * array.array('f', [0.1, 0.2, 0.1, 1.0])
+ pixels = gpu.types.Buffer('FLOAT', width * height * 4, pixels)
# Generate texture
- self.texture = bgl.Buffer(bgl.GL_INT, 1)
- bgl.glGenTextures(1, self.texture)
- bgl.glActiveTexture(bgl.GL_TEXTURE0)
- bgl.glBindTexture(bgl.GL_TEXTURE_2D, self.texture[0])
- bgl.glTexImage2D(bgl.GL_TEXTURE_2D, 0, bgl.GL_RGBA16F, width, height, 0, bgl.GL_RGBA, bgl.GL_FLOAT, pixels)
- bgl.glTexParameteri(bgl.GL_TEXTURE_2D, bgl.GL_TEXTURE_MIN_FILTER, bgl.GL_LINEAR)
- bgl.glTexParameteri(bgl.GL_TEXTURE_2D, bgl.GL_TEXTURE_MAG_FILTER, bgl.GL_LINEAR)
- bgl.glBindTexture(bgl.GL_TEXTURE_2D, 0)
+ self.texture = gpu.types.GPUTexture((width, height), format='RGBA16F', data=pixels)
- # Bind shader that converts from scene linear to display space,
- # use the scene's color management settings.
- shader_program = bgl.Buffer(bgl.GL_INT, 1)
- bgl.glGetIntegerv(bgl.GL_CURRENT_PROGRAM, shader_program)
-
- # Generate vertex array
- self.vertex_array = bgl.Buffer(bgl.GL_INT, 1)
- bgl.glGenVertexArrays(1, self.vertex_array)
- bgl.glBindVertexArray(self.vertex_array[0])
-
- texturecoord_location = bgl.glGetAttribLocation(shader_program[0], "texCoord")
- position_location = bgl.glGetAttribLocation(shader_program[0], "pos")
-
- bgl.glEnableVertexAttribArray(texturecoord_location)
- bgl.glEnableVertexAttribArray(position_location)
-
- # Generate geometry buffers for drawing textured quad
- position = [0.0, 0.0, width, 0.0, width, height, 0.0, height]
- position = bgl.Buffer(bgl.GL_FLOAT, len(position), position)
- texcoord = [0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0]
- texcoord = bgl.Buffer(bgl.GL_FLOAT, len(texcoord), texcoord)
-
- self.vertex_buffer = bgl.Buffer(bgl.GL_INT, 2)
-
- bgl.glGenBuffers(2, self.vertex_buffer)
- bgl.glBindBuffer(bgl.GL_ARRAY_BUFFER, self.vertex_buffer[0])
- bgl.glBufferData(bgl.GL_ARRAY_BUFFER, 32, position, bgl.GL_STATIC_DRAW)
- bgl.glVertexAttribPointer(position_location, 2, bgl.GL_FLOAT, bgl.GL_FALSE, 0, None)
-
- bgl.glBindBuffer(bgl.GL_ARRAY_BUFFER, self.vertex_buffer[1])
- bgl.glBufferData(bgl.GL_ARRAY_BUFFER, 32, texcoord, bgl.GL_STATIC_DRAW)
- bgl.glVertexAttribPointer(texturecoord_location, 2, bgl.GL_FLOAT, bgl.GL_FALSE, 0, None)
-
- bgl.glBindBuffer(bgl.GL_ARRAY_BUFFER, 0)
- bgl.glBindVertexArray(0)
+ # Note: This is just a didactic example.
+ # In this case it would be more convenient to fill the texture with:
+ # self.texture.clear('FLOAT', value=[0.1, 0.2, 0.1, 1.0])
def __del__(self):
- bgl.glDeleteBuffers(2, self.vertex_buffer)
- bgl.glDeleteVertexArrays(1, self.vertex_array)
- bgl.glBindTexture(bgl.GL_TEXTURE_2D, 0)
- bgl.glDeleteTextures(1, self.texture)
+ del self.texture
def draw(self):
- bgl.glActiveTexture(bgl.GL_TEXTURE0)
- bgl.glBindTexture(bgl.GL_TEXTURE_2D, self.texture[0])
- bgl.glBindVertexArray(self.vertex_array[0])
- bgl.glDrawArrays(bgl.GL_TRIANGLE_FAN, 0, 4)
- bgl.glBindVertexArray(0)
- bgl.glBindTexture(bgl.GL_TEXTURE_2D, 0)
+ draw_texture_2d(self.texture, (0, 0), self.texture.width, self.texture.height)
# RenderEngines also need to tell UI Panels that they are compatible with.
diff --git a/doc/python_api/examples/gpu.4.py b/doc/python_api/examples/gpu.4.py
index e05290a9442..b1a1ba827db 100644
--- a/doc/python_api/examples/gpu.4.py
+++ b/doc/python_api/examples/gpu.4.py
@@ -4,7 +4,6 @@ Mesh with Random Vertex Colors
"""
import bpy
import gpu
-import bgl
import numpy as np
from random import random
from gpu_extras.batch import batch_for_shader
@@ -31,9 +30,10 @@ batch = batch_for_shader(
def draw():
- bgl.glEnable(bgl.GL_DEPTH_TEST)
+ gpu.state.depth_test_set('LESS_EQUAL')
+ gpu.state.depth_mask_set(True)
batch.draw(shader)
- bgl.glDisable(bgl.GL_DEPTH_TEST)
+ gpu.state.depth_mask_set(False)
bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_VIEW')
diff --git a/doc/python_api/examples/gpu.6.py b/doc/python_api/examples/gpu.6.py
index 69af65e163e..334a606055a 100644
--- a/doc/python_api/examples/gpu.6.py
+++ b/doc/python_api/examples/gpu.6.py
@@ -6,11 +6,11 @@ To use this example you have to provide an image that should be displayed.
"""
import bpy
import gpu
-import bgl
from gpu_extras.batch import batch_for_shader
IMAGE_NAME = "Untitled"
image = bpy.data.images[IMAGE_NAME]
+texture = gpu.texture.from_image(image)
shader = gpu.shader.from_builtin('2D_IMAGE')
batch = batch_for_shader(
@@ -21,16 +21,9 @@ batch = batch_for_shader(
},
)
-if image.gl_load():
- raise Exception()
-
-
def draw():
- bgl.glActiveTexture(bgl.GL_TEXTURE0)
- bgl.glBindTexture(bgl.GL_TEXTURE_2D, image.bindcode)
-
shader.bind()
- shader.uniform_int("image", 0)
+ shader.uniform_sampler("image", texture)
batch.draw(shader)
diff --git a/doc/python_api/examples/gpu.7.py b/doc/python_api/examples/gpu.7.py
index 80cda45e690..9d881831c21 100644
--- a/doc/python_api/examples/gpu.7.py
+++ b/doc/python_api/examples/gpu.7.py
@@ -9,7 +9,6 @@ Generate a texture using Offscreen Rendering
"""
import bpy
import gpu
-import bgl
from mathutils import Matrix
from gpu_extras.batch import batch_for_shader
from gpu_extras.presets import draw_circle_2d
@@ -20,8 +19,8 @@ from gpu_extras.presets import draw_circle_2d
offscreen = gpu.types.GPUOffScreen(512, 512)
with offscreen.bind():
- bgl.glClearColor(0.0, 0.0, 0.0, 0.0)
- bgl.glClear(bgl.GL_COLOR_BUFFER_BIT)
+ fb = gpu.state.active_framebuffer_get()
+ fb.clear(color=(0.0, 0.0, 0.0, 0.0))
with gpu.matrix.push_pop():
# reset matrices -> use normalized device coordinates [-1, 1]
gpu.matrix.load_matrix(Matrix.Identity(4))
@@ -30,7 +29,7 @@ with offscreen.bind():
amount = 10
for i in range(-amount, amount + 1):
x_pos = i / amount
- draw_circle_2d((x_pos, 0.0), (1, 1, 1, 1), 0.5, 200)
+ draw_circle_2d((x_pos, 0.0), (1, 1, 1, 1), 0.5, segments=200)
# Drawing the generated texture in 3D space
@@ -75,13 +74,10 @@ batch = batch_for_shader(
def draw():
- bgl.glActiveTexture(bgl.GL_TEXTURE0)
- bgl.glBindTexture(bgl.GL_TEXTURE_2D, offscreen.color_texture)
-
shader.bind()
shader.uniform_float("modelMatrix", Matrix.Translation((1, 2, 3)) @ Matrix.Scale(3, 4))
shader.uniform_float("viewProjectionMatrix", bpy.context.region_data.perspective_matrix)
- shader.uniform_float("image", 0)
+ shader.uniform_sampler("image", offscreen.texture_color)
batch.draw(shader)
diff --git a/doc/python_api/examples/gpu.8.py b/doc/python_api/examples/gpu.8.py
index e67c601def9..86a242b0258 100644
--- a/doc/python_api/examples/gpu.8.py
+++ b/doc/python_api/examples/gpu.8.py
@@ -7,11 +7,10 @@ If it already exists, it will override the existing one.
Currently almost all of the execution time is spent in the last line.
In the future this will hopefully be solved by implementing the Python buffer protocol
-for :class:`bgl.Buffer` and :class:`bpy.types.Image.pixels` (aka ``bpy_prop_array``).
+for :class:`gpu.types.Buffer` and :class:`bpy.types.Image.pixels` (aka ``bpy_prop_array``).
"""
import bpy
import gpu
-import bgl
import random
from mathutils import Matrix
from gpu_extras.presets import draw_circle_2d
@@ -25,8 +24,8 @@ RING_AMOUNT = 10
offscreen = gpu.types.GPUOffScreen(WIDTH, HEIGHT)
with offscreen.bind():
- bgl.glClearColor(0.0, 0.0, 0.0, 0.0)
- bgl.glClear(bgl.GL_COLOR_BUFFER_BIT)
+ fb = gpu.state.active_framebuffer_get()
+ fb.clear(color=(0.0, 0.0, 0.0, 0.0))
with gpu.matrix.push_pop():
# reset matrices -> use normalized device coordinates [-1, 1]
gpu.matrix.load_matrix(Matrix.Identity(4))
@@ -35,11 +34,11 @@ with offscreen.bind():
for i in range(RING_AMOUNT):
draw_circle_2d(
(random.uniform(-1, 1), random.uniform(-1, 1)),
- (1, 1, 1, 1), random.uniform(0.1, 1), 20)
+ (1, 1, 1, 1), random.uniform(0.1, 1),
+ segments=20,
+ )
- buffer = bgl.Buffer(bgl.GL_BYTE, WIDTH * HEIGHT * 4)
- bgl.glReadBuffer(bgl.GL_BACK)
- bgl.glReadPixels(0, 0, WIDTH, HEIGHT, bgl.GL_RGBA, bgl.GL_UNSIGNED_BYTE, buffer)
+ buffer = fb.read_color(0, 0, WIDTH, HEIGHT, 4, 0, 'UBYTE')
offscreen.free()
@@ -48,4 +47,6 @@ if not IMAGE_NAME in bpy.data.images:
bpy.data.images.new(IMAGE_NAME, WIDTH, HEIGHT)
image = bpy.data.images[IMAGE_NAME]
image.scale(WIDTH, HEIGHT)
+
+buffer.dimensions = WIDTH * HEIGHT * 4
image.pixels = [v / 255 for v in buffer]
diff --git a/doc/python_api/examples/gpu.9.py b/doc/python_api/examples/gpu.9.py
index a4db576ecc0..e358cb517bd 100644
--- a/doc/python_api/examples/gpu.9.py
+++ b/doc/python_api/examples/gpu.9.py
@@ -7,7 +7,6 @@ You could also make this independent of a specific camera,
but Blender does not expose good functions to create view and projection matrices yet.
"""
import bpy
-import bgl
import gpu
from gpu_extras.presets import draw_texture_2d
@@ -34,8 +33,8 @@ def draw():
view_matrix,
projection_matrix)
- bgl.glDisable(bgl.GL_DEPTH_TEST)
- draw_texture_2d(offscreen.color_texture, (10, 10), WIDTH, HEIGHT)
+ gpu.state.depth_mask_set(False)
+ draw_texture_2d(offscreen.texture_color, (10, 10), WIDTH, HEIGHT)
bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_PIXEL')
diff --git a/doc/python_api/examples/mathutils.Matrix.LocRotScale.py b/doc/python_api/examples/mathutils.Matrix.LocRotScale.py
new file mode 100644
index 00000000000..016a5002e82
--- /dev/null
+++ b/doc/python_api/examples/mathutils.Matrix.LocRotScale.py
@@ -0,0 +1,5 @@
+# Compute local object transformation matrix:
+if obj.rotation_mode == 'QUATERNION':
+ matrix = mathutils.Matrix.LocRotScale(obj.location, obj.rotation_quaternion, obj.scale)
+else:
+ matrix = mathutils.Matrix.LocRotScale(obj.location, obj.rotation_euler, obj.scale)
diff --git a/doc/python_api/examples/mathutils.Matrix.py b/doc/python_api/examples/mathutils.Matrix.py
index 26c7ccba27c..84e09b97c15 100644
--- a/doc/python_api/examples/mathutils.Matrix.py
+++ b/doc/python_api/examples/mathutils.Matrix.py
@@ -14,10 +14,14 @@ mat_rot = mathutils.Matrix.Rotation(math.radians(45.0), 4, 'X')
mat_out = mat_loc @ mat_rot @ mat_sca
print(mat_out)
-# extract components back out of the matrix
+# extract components back out of the matrix as two vectors and a quaternion
loc, rot, sca = mat_out.decompose()
print(loc, rot, sca)
+# recombine extracted components
+mat_out2 = mathutils.Matrix.LocRotScale(loc, rot, sca)
+print(mat_out2)
+
# it can also be useful to access components of a matrix directly
mat = mathutils.Matrix()
mat[0][0], mat[1][0], mat[2][0] = 0.0, 1.0, 2.0
diff --git a/doc/python_api/requirements.txt b/doc/python_api/requirements.txt
index 11423966f06..7040fd190f3 100644
--- a/doc/python_api/requirements.txt
+++ b/doc/python_api/requirements.txt
@@ -1,2 +1,2 @@
-Sphinx==3.5.1
-sphinx_rtd_theme==0.5.1
+Sphinx==3.5.3
+sphinx_rtd_theme==0.5.2
diff --git a/doc/python_api/rst/info_gotcha.rst b/doc/python_api/rst/info_gotcha.rst
index e5ff56063b5..df6e7297e26 100644
--- a/doc/python_api/rst/info_gotcha.rst
+++ b/doc/python_api/rst/info_gotcha.rst
@@ -50,7 +50,8 @@ you should be able to find the poll function with no knowledge of C.
Blender does have the functionality for poll functions to describe why they fail,
but its currently not used much, if you're interested to help improve the API
- feel free to add calls to ``CTX_wm_operator_poll_msg_set`` where its not obvious why poll fails, e.g:
+ feel free to add calls to :class:`bpy.types.Operator.poll_message_set` (``CTX_wm_operator_poll_msg_set`` in C)
+ where its not obvious why poll fails, e.g:
>>> bpy.ops.gpencil.draw()
RuntimeError: Operator bpy.ops.gpencil.draw.poll() Failed to find Grease Pencil data to draw into
diff --git a/doc/python_api/sphinx_doc_gen.py b/doc/python_api/sphinx_doc_gen.py
index 5c6cf24a178..4be27e0f0e8 100644
--- a/doc/python_api/sphinx_doc_gen.py
+++ b/doc/python_api/sphinx_doc_gen.py
@@ -545,6 +545,13 @@ def range_str(val):
def example_extract_docstring(filepath):
+ '''
+ Return (text, line_no, line_no_has_content) where:
+ - ``text`` is the doc-string text.
+ - ``line_no`` is the line the doc-string text ends.
+ - ``line_no_has_content`` when False, this file only contains a doc-string.
+ There is no need to include the remainder.
+ '''
file = open(filepath, "r", encoding="utf-8")
line = file.readline()
line_no = 0
@@ -553,7 +560,7 @@ def example_extract_docstring(filepath):
line_no += 1
else:
file.close()
- return "", 0
+ return "", 0, True
for line in file:
line_no += 1
@@ -563,15 +570,17 @@ def example_extract_docstring(filepath):
text.append(line.rstrip())
line_no += 1
+ line_no_has_content = False
# Skip over blank lines so the Python code doesn't have blank lines at the top.
for line in file:
if line.strip():
+ line_no_has_content = True
break
line_no += 1
file.close()
- return "\n".join(text), line_no
+ return "\n".join(text), line_no, line_no_has_content
def title_string(text, heading_char, double=False):
@@ -590,16 +599,18 @@ def write_example_ref(ident, fw, example_id, ext="py"):
filepath = os.path.join("..", "examples", "%s.%s" % (example_id, ext))
filepath_full = os.path.join(os.path.dirname(fw.__self__.name), filepath)
- text, line_no = example_extract_docstring(filepath_full)
+ text, line_no, line_no_has_content = example_extract_docstring(filepath_full)
for line in text.split("\n"):
fw("%s\n" % (ident + line).rstrip())
fw("\n")
- fw("%s.. literalinclude:: %s\n" % (ident, filepath))
- if line_no > 0:
- fw("%s :lines: %d-\n" % (ident, line_no))
- fw("\n")
+ # Some files only contain a doc-string.
+ if line_no_has_content:
+ fw("%s.. literalinclude:: %s\n" % (ident, filepath))
+ if line_no > 0:
+ fw("%s :lines: %d-\n" % (ident, line_no))
+ fw("\n")
EXAMPLE_SET_USED.add(example_id)
else:
if bpy.app.debug:
@@ -943,7 +954,7 @@ def pymodule2sphinx(basepath, module_name, module, title, module_all_extra):
# constant, not much fun we can do here except to list it.
# TODO, figure out some way to document these!
fw(".. data:: %s\n\n" % attribute)
- write_indented_lines(" ", fw, "constant value %s" % repr(value), False)
+ write_indented_lines(" ", fw, "Constant value %s" % repr(value), False)
fw("\n")
else:
BPY_LOGGER.debug("\tnot documenting %s.%s of %r type" % (module_name, attribute, value_type.__name__))
@@ -1025,7 +1036,6 @@ def pymodule2sphinx(basepath, module_name, module, title, module_all_extra):
context_type_map = {
# context_member: (RNA type, is_collection)
"active_annotation_layer": ("GPencilLayer", False),
- "active_base": ("ObjectBase", False),
"active_bone": ("EditBone", False),
"active_gpencil_frame": ("GreasePencilLayer", True),
"active_gpencil_layer": ("GPencilLayer", True),
@@ -1236,7 +1246,7 @@ def pyrna_enum2sphinx(prop, use_empty_descriptions=False):
"%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", " "),
+ indent(" -- ".join(escape_rst(val) for val in (name, description) if val) or "Undocumented", " "),
)
for identifier, name, description in prop.enum_items
])
@@ -1408,7 +1418,8 @@ def pyrna2sphinx(basepath):
else:
fw(" .. attribute:: %s\n\n" % prop.identifier)
if prop.description:
- fw(" %s\n\n" % prop.description)
+ write_indented_lines(" ", fw, prop.description, False)
+ fw("\n")
# special exception, can't use generic code here for enums
if prop.type == "enum":
@@ -1544,8 +1555,8 @@ def pyrna2sphinx(basepath):
fw(".. hlist::\n")
fw(" :columns: 2\n\n")
- # context does its own thing
- # "active_base": ("ObjectBase", False),
+ # Context does its own thing.
+ # "active_object": ("Object", False),
for ref_attr, (ref_type, ref_is_seq) in sorted(context_type_map.items()):
if ref_type == struct_id:
fw(" * :mod:`bpy.context.%s`\n" % ref_attr)
diff --git a/doc/python_api/static/css/theme_overrides.css b/doc/python_api/static/css/theme_overrides.css
index c237a958f5f..0fea27a8ebd 100644
--- a/doc/python_api/static/css/theme_overrides.css
+++ b/doc/python_api/static/css/theme_overrides.css
@@ -1,7 +1,8 @@
/* T76453: Prevent Long enum lists */
-.field-list li {
+.field-list > dd p {
max-height: 245px;
overflow-y: auto !important;
+ word-break: break-word;
}
/* Hide home icon in search area */
@@ -11,3 +12,15 @@
.wy-nav-content {
max-width: 1000px !important;
}
+
+/* Fix long titles on mobile */
+h1, h2, h3, h4, h5, h6 {word-break: break-all}
+
+/* Temp fix for https://github.com/readthedocs/sphinx_rtd_theme/pull/1109 */
+.hlist tr {
+ display: -ms-flexbox;
+ display: flex;
+ flex-flow: row wrap;
+ }
+
+.hlist td {margin-right: auto}
diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt
index 235c2fa931a..824b2fb0e9c 100644
--- a/extern/CMakeLists.txt
+++ b/extern/CMakeLists.txt
@@ -109,3 +109,7 @@ endif()
if(WITH_MOD_FLUID)
add_subdirectory(mantaflow)
endif()
+
+if (WITH_COMPOSITOR)
+ add_subdirectory(smaa_areatex)
+endif()
diff --git a/extern/audaspace/CMakeLists.txt b/extern/audaspace/CMakeLists.txt
index fe1bf5dc742..1599c03cbad 100644
--- a/extern/audaspace/CMakeLists.txt
+++ b/extern/audaspace/CMakeLists.txt
@@ -42,6 +42,7 @@ set(SRC
src/devices/NULLDevice.cpp
src/devices/ReadDevice.cpp
src/devices/SoftwareDevice.cpp
+ src/devices/ThreadedDevice.cpp
src/Exception.cpp
src/file/File.cpp
src/file/FileManager.cpp
@@ -148,6 +149,7 @@ set(PUBLIC_HDR
include/devices/NULLDevice.h
include/devices/ReadDevice.h
include/devices/SoftwareDevice.h
+ include/devices/ThreadedDevice.h
include/Exception.h
include/file/File.h
include/file/FileManager.h
diff --git a/extern/audaspace/include/devices/SoftwareDevice.h b/extern/audaspace/include/devices/SoftwareDevice.h
index a350550048b..209be9941b1 100644
--- a/extern/audaspace/include/devices/SoftwareDevice.h
+++ b/extern/audaspace/include/devices/SoftwareDevice.h
@@ -255,6 +255,7 @@ protected:
/**
* This function tells the device, to start or pause playback.
* \param playing True if device should playback.
+ * \note This method is only called when the device is locked.
*/
virtual void playing(bool playing)=0;
diff --git a/extern/audaspace/include/devices/ThreadedDevice.h b/extern/audaspace/include/devices/ThreadedDevice.h
new file mode 100644
index 00000000000..36c2e68e36f
--- /dev/null
+++ b/extern/audaspace/include/devices/ThreadedDevice.h
@@ -0,0 +1,95 @@
+/*******************************************************************************
+ * Copyright 2009-2016 Jörg Müller
+ *
+ * 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.
+ ******************************************************************************/
+
+#pragma once
+
+/**
+ * @file ThreadedDevice.h
+ * @ingroup plugin
+ * The ThreadedDevice class.
+ */
+
+#include "devices/SoftwareDevice.h"
+
+#include <thread>
+
+AUD_NAMESPACE_BEGIN
+
+/**
+ * This device extends the SoftwareDevice with code for running mixing in a separate thread.
+ */
+class AUD_PLUGIN_API ThreadedDevice : public SoftwareDevice
+{
+private:
+ /**
+ * Whether there is currently playback.
+ */
+ bool m_playing;
+
+ /**
+ * Whether the current playback should stop.
+ */
+ bool m_stop;
+
+ /**
+ * The streaming thread.
+ */
+ std::thread m_thread;
+
+ /**
+ * Starts the streaming thread.
+ */
+ AUD_LOCAL void start();
+
+ /**
+ * Streaming thread main function.
+ */
+ AUD_LOCAL virtual void runMixingThread()=0;
+
+ // delete copy constructor and operator=
+ ThreadedDevice(const ThreadedDevice&) = delete;
+ ThreadedDevice& operator=(const ThreadedDevice&) = delete;
+
+protected:
+ virtual void playing(bool playing);
+
+ /**
+ * Empty default constructor. To setup the device call the function create()
+ * and to uninitialize call destroy().
+ */
+ ThreadedDevice();
+
+ /**
+ * Indicates that the mixing thread should be stopped.
+ * \return Whether the mixing thread should be stopping.
+ * \warning For thread safety, the device needs to be locked, when this method is called.
+ */
+ inline bool shouldStop() { return m_stop; }
+
+ /**
+ * This method needs to be called when the mixing thread is stopping.
+ * \warning For thread safety, the device needs to be locked, when this method is called.
+ */
+ inline void doStop() { m_stop = m_playing = false; }
+
+ /**
+ * Stops all playback and notifies the mixing thread to stop.
+ * \warning The device has to be unlocked to not run into a deadlock.
+ */
+ void stopMixingThread();
+};
+
+AUD_NAMESPACE_END
diff --git a/extern/audaspace/plugins/ffmpeg/FFMPEGWriter.cpp b/extern/audaspace/plugins/ffmpeg/FFMPEGWriter.cpp
index 2cd3261bd20..10517d1d596 100644
--- a/extern/audaspace/plugins/ffmpeg/FFMPEGWriter.cpp
+++ b/extern/audaspace/plugins/ffmpeg/FFMPEGWriter.cpp
@@ -75,6 +75,7 @@ void FFMPEGWriter::encode()
m_frame->nb_samples = m_input_samples;
m_frame->format = m_codecCtx->sample_fmt;
m_frame->channel_layout = m_codecCtx->channel_layout;
+ m_frame->channels = m_specs.channels;
if(avcodec_fill_audio_frame(m_frame, m_specs.channels, m_codecCtx->sample_fmt, reinterpret_cast<data_t*>(data), m_input_buffer.getSize(), 0) < 0)
AUD_THROW(FileException, "File couldn't be written, filling the audio frame failed with ffmpeg.");
diff --git a/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.cpp b/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.cpp
index 0a50d5db2c7..3ffe97661d8 100644
--- a/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.cpp
+++ b/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.cpp
@@ -27,9 +27,9 @@ void PulseAudioDevice::PulseAudio_state_callback(pa_context *context, void *data
{
PulseAudioDevice* device = (PulseAudioDevice*)data;
- device->m_state = AUD_pa_context_get_state(context);
+ std::lock_guard<ILockable> lock(*device);
- AUD_pa_threaded_mainloop_signal(device->m_mainloop, 0);
+ device->m_state = AUD_pa_context_get_state(context);
}
void PulseAudioDevice::PulseAudio_request(pa_stream *stream, size_t num_bytes, void *data)
@@ -68,29 +68,40 @@ void PulseAudioDevice::PulseAudio_underflow(pa_stream *stream, void *data)
}
}
-void PulseAudioDevice::playing(bool playing)
+void PulseAudioDevice::runMixingThread()
{
- m_playback = playing;
+ for(;;)
+ {
+ {
+ std::lock_guard<ILockable> lock(*this);
+
+ if(shouldStop())
+ {
+ AUD_pa_stream_cork(m_stream, 1, nullptr, nullptr);
+ doStop();
+ return;
+ }
+ }
+
+ if(AUD_pa_stream_is_corked(m_stream))
+ AUD_pa_stream_cork(m_stream, 0, nullptr, nullptr);
- AUD_pa_stream_cork(m_stream, playing ? 0 : 1, nullptr, nullptr);
+ AUD_pa_mainloop_iterate(m_mainloop, true, nullptr);
+ }
}
PulseAudioDevice::PulseAudioDevice(std::string name, DeviceSpecs specs, int buffersize) :
- m_playback(false),
m_state(PA_CONTEXT_UNCONNECTED),
m_buffersize(buffersize),
m_underflows(0)
{
- m_mainloop = AUD_pa_threaded_mainloop_new();
+ m_mainloop = AUD_pa_mainloop_new();
- AUD_pa_threaded_mainloop_lock(m_mainloop);
-
- m_context = AUD_pa_context_new(AUD_pa_threaded_mainloop_get_api(m_mainloop), name.c_str());
+ m_context = AUD_pa_context_new(AUD_pa_mainloop_get_api(m_mainloop), name.c_str());
if(!m_context)
{
- AUD_pa_threaded_mainloop_unlock(m_mainloop);
- AUD_pa_threaded_mainloop_free(m_mainloop);
+ AUD_pa_mainloop_free(m_mainloop);
AUD_THROW(DeviceException, "Could not connect to PulseAudio.");
}
@@ -99,26 +110,21 @@ PulseAudioDevice::PulseAudioDevice(std::string name, DeviceSpecs specs, int buff
AUD_pa_context_connect(m_context, nullptr, PA_CONTEXT_NOFLAGS, nullptr);
- AUD_pa_threaded_mainloop_start(m_mainloop);
-
while(m_state != PA_CONTEXT_READY)
{
switch(m_state)
{
case PA_CONTEXT_FAILED:
case PA_CONTEXT_TERMINATED:
- AUD_pa_threaded_mainloop_unlock(m_mainloop);
- AUD_pa_threaded_mainloop_stop(m_mainloop);
-
AUD_pa_context_disconnect(m_context);
AUD_pa_context_unref(m_context);
- AUD_pa_threaded_mainloop_free(m_mainloop);
+ AUD_pa_mainloop_free(m_mainloop);
AUD_THROW(DeviceException, "Could not connect to PulseAudio.");
break;
default:
- AUD_pa_threaded_mainloop_wait(m_mainloop);
+ AUD_pa_mainloop_iterate(m_mainloop, true, nullptr);
break;
}
}
@@ -166,13 +172,10 @@ PulseAudioDevice::PulseAudioDevice(std::string name, DeviceSpecs specs, int buff
if(!m_stream)
{
- AUD_pa_threaded_mainloop_unlock(m_mainloop);
- AUD_pa_threaded_mainloop_stop(m_mainloop);
-
AUD_pa_context_disconnect(m_context);
AUD_pa_context_unref(m_context);
- AUD_pa_threaded_mainloop_free(m_mainloop);
+ AUD_pa_mainloop_free(m_mainloop);
AUD_THROW(DeviceException, "Could not create PulseAudio stream.");
}
@@ -188,32 +191,27 @@ PulseAudioDevice::PulseAudioDevice(std::string name, DeviceSpecs specs, int buff
buffer_attr.prebuf = -1U;
buffer_attr.tlength = buffersize;
- if(AUD_pa_stream_connect_playback(m_stream, nullptr, &buffer_attr, static_cast<pa_stream_flags_t>(PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE), nullptr, nullptr) < 0)
+ if(AUD_pa_stream_connect_playback(m_stream, nullptr, &buffer_attr, static_cast<pa_stream_flags_t>(PA_STREAM_START_CORKED | PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE), nullptr, nullptr) < 0)
{
- AUD_pa_threaded_mainloop_unlock(m_mainloop);
- AUD_pa_threaded_mainloop_stop(m_mainloop);
-
AUD_pa_context_disconnect(m_context);
AUD_pa_context_unref(m_context);
- AUD_pa_threaded_mainloop_free(m_mainloop);
+ AUD_pa_mainloop_free(m_mainloop);
AUD_THROW(DeviceException, "Could not connect PulseAudio stream.");
}
- AUD_pa_threaded_mainloop_unlock(m_mainloop);
-
create();
}
PulseAudioDevice::~PulseAudioDevice()
{
- AUD_pa_threaded_mainloop_stop(m_mainloop);
+ stopMixingThread();
AUD_pa_context_disconnect(m_context);
AUD_pa_context_unref(m_context);
- AUD_pa_threaded_mainloop_free(m_mainloop);
+ AUD_pa_mainloop_free(m_mainloop);
destroy();
}
diff --git a/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.h b/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.h
index 9efae5128b1..be34cc9032b 100644
--- a/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.h
+++ b/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.h
@@ -26,7 +26,7 @@
* The PulseAudioDevice class.
*/
-#include "devices/SoftwareDevice.h"
+#include "devices/ThreadedDevice.h"
#include <pulse/pulseaudio.h>
@@ -35,15 +35,10 @@ AUD_NAMESPACE_BEGIN
/**
* This device plays back through PulseAudio, the simple direct media layer.
*/
-class AUD_PLUGIN_API PulseAudioDevice : public SoftwareDevice
+class AUD_PLUGIN_API PulseAudioDevice : public ThreadedDevice
{
private:
- /**
- * Whether there is currently playback.
- */
- volatile bool m_playback;
-
- pa_threaded_mainloop* m_mainloop;
+ pa_mainloop* m_mainloop;
pa_context* m_context;
pa_stream* m_stream;
pa_context_state_t m_state;
@@ -74,13 +69,15 @@ private:
*/
AUD_LOCAL static void PulseAudio_underflow(pa_stream* stream, void* data);
+ /**
+ * Streaming thread main function.
+ */
+ AUD_LOCAL void runMixingThread();
+
// delete copy constructor and operator=
PulseAudioDevice(const PulseAudioDevice&) = delete;
PulseAudioDevice& operator=(const PulseAudioDevice&) = delete;
-protected:
- virtual void playing(bool playing);
-
public:
/**
* Opens the PulseAudio audio device for playback.
diff --git a/extern/audaspace/plugins/pulseaudio/PulseAudioSymbols.h b/extern/audaspace/plugins/pulseaudio/PulseAudioSymbols.h
index 9cefbc0c7e2..4b9e1ffea2b 100644
--- a/extern/audaspace/plugins/pulseaudio/PulseAudioSymbols.h
+++ b/extern/audaspace/plugins/pulseaudio/PulseAudioSymbols.h
@@ -24,18 +24,14 @@ PULSEAUDIO_SYMBOL(pa_context_unref);
PULSEAUDIO_SYMBOL(pa_stream_begin_write);
PULSEAUDIO_SYMBOL(pa_stream_connect_playback);
PULSEAUDIO_SYMBOL(pa_stream_cork);
+PULSEAUDIO_SYMBOL(pa_stream_is_corked);
PULSEAUDIO_SYMBOL(pa_stream_new);
PULSEAUDIO_SYMBOL(pa_stream_set_buffer_attr);
PULSEAUDIO_SYMBOL(pa_stream_set_underflow_callback);
PULSEAUDIO_SYMBOL(pa_stream_set_write_callback);
PULSEAUDIO_SYMBOL(pa_stream_write);
-PULSEAUDIO_SYMBOL(pa_threaded_mainloop_free);
-PULSEAUDIO_SYMBOL(pa_threaded_mainloop_get_api);
-PULSEAUDIO_SYMBOL(pa_threaded_mainloop_lock);
-PULSEAUDIO_SYMBOL(pa_threaded_mainloop_new);
-PULSEAUDIO_SYMBOL(pa_threaded_mainloop_signal);
-PULSEAUDIO_SYMBOL(pa_threaded_mainloop_start);
-PULSEAUDIO_SYMBOL(pa_threaded_mainloop_stop);
-PULSEAUDIO_SYMBOL(pa_threaded_mainloop_unlock);
-PULSEAUDIO_SYMBOL(pa_threaded_mainloop_wait);
+PULSEAUDIO_SYMBOL(pa_mainloop_free);
+PULSEAUDIO_SYMBOL(pa_mainloop_get_api);
+PULSEAUDIO_SYMBOL(pa_mainloop_new);
+PULSEAUDIO_SYMBOL(pa_mainloop_iterate);
diff --git a/extern/audaspace/plugins/wasapi/WASAPIDevice.cpp b/extern/audaspace/plugins/wasapi/WASAPIDevice.cpp
index 4f213dc8468..b4632ebb83e 100644
--- a/extern/audaspace/plugins/wasapi/WASAPIDevice.cpp
+++ b/extern/audaspace/plugins/wasapi/WASAPIDevice.cpp
@@ -31,159 +31,83 @@ template <class T> void SafeRelease(T **ppT)
}
}
-void WASAPIDevice::start()
-{
- lock();
-
- // thread is still running, we can abort stopping it
- if(m_stop)
- m_stop = false;
- // thread is not running, let's start it
- else if(!m_playing)
- {
- if(m_thread.joinable())
- m_thread.join();
-
- m_playing = true;
-
- m_thread = std::thread(&WASAPIDevice::updateStream, this);
- }
-
- unlock();
-}
-
-void WASAPIDevice::updateStream()
+void WASAPIDevice::runMixingThread()
{
UINT32 buffer_size;
+ UINT32 padding;
+ UINT32 length;
data_t* buffer;
- lock();
+ IAudioRenderClient* render_client = nullptr;
- if(FAILED(m_audio_client->GetBufferSize(&buffer_size)))
{
- m_playing = false;
- m_stop = false;
- unlock();
- return;
- }
+ std::lock_guard<ILockable> lock(*this);
- IAudioRenderClient* render_client = nullptr;
- const IID IID_IAudioRenderClient = __uuidof(IAudioRenderClient);
+ const IID IID_IAudioRenderClient = __uuidof(IAudioRenderClient);
- if(FAILED(m_audio_client->GetService(IID_IAudioRenderClient, reinterpret_cast<void**>(&render_client))))
- {
- m_playing = false;
- m_stop = false;
- unlock();
- return;
- }
+ if(FAILED(m_audio_client->GetBufferSize(&buffer_size)))
+ goto init_error;
- UINT32 padding;
+ if(FAILED(m_audio_client->GetService(IID_IAudioRenderClient, reinterpret_cast<void**>(&render_client))))
+ goto init_error;
- if(FAILED(m_audio_client->GetCurrentPadding(&padding)))
- {
- SafeRelease(&render_client);
- m_playing = false;
- m_stop = false;
- unlock();
- return;
- }
+ if(FAILED(m_audio_client->GetCurrentPadding(&padding)))
+ goto init_error;
- UINT32 length = buffer_size - padding;
+ length = buffer_size - padding;
- if(FAILED(render_client->GetBuffer(length, &buffer)))
- {
- SafeRelease(&render_client);
- m_playing = false;
- m_stop = false;
- unlock();
- return;
- }
+ if(FAILED(render_client->GetBuffer(length, &buffer)))
+ goto init_error;
- mix((data_t*)buffer, length);
+ mix((data_t*)buffer, length);
- if(FAILED(render_client->ReleaseBuffer(length, 0)))
- {
- SafeRelease(&render_client);
- m_playing = false;
- m_stop = false;
- unlock();
- return;
+ if(FAILED(render_client->ReleaseBuffer(length, 0)))
+ {
+ init_error:
+ SafeRelease(&render_client);
+ doStop();
+ return;
+ }
}
- unlock();
-
m_audio_client->Start();
auto sleepDuration = std::chrono::milliseconds(buffer_size * 1000 / int(m_specs.rate) / 2);
for(;;)
{
- lock();
-
- if(FAILED(m_audio_client->GetCurrentPadding(&padding)))
{
- m_audio_client->Stop();
- SafeRelease(&render_client);
- m_playing = false;
- m_stop = false;
- unlock();
- return;
- }
+ std::lock_guard<ILockable> lock(*this);
- length = buffer_size - padding;
+ if(FAILED(m_audio_client->GetCurrentPadding(&padding)))
+ goto stop_thread;
- if(FAILED(render_client->GetBuffer(length, &buffer)))
- {
- m_audio_client->Stop();
- SafeRelease(&render_client);
- m_playing = false;
- m_stop = false;
- unlock();
- return;
- }
+ length = buffer_size - padding;
- mix((data_t*)buffer, length);
+ if(FAILED(render_client->GetBuffer(length, &buffer)))
+ goto stop_thread;
- if(FAILED(render_client->ReleaseBuffer(length, 0)))
- {
- m_audio_client->Stop();
- SafeRelease(&render_client);
- m_playing = false;
- m_stop = false;
- unlock();
- return;
- }
+ mix((data_t*)buffer, length);
- // stop thread
- if(m_stop)
- {
- m_audio_client->Stop();
- SafeRelease(&render_client);
- m_playing = false;
- m_stop = false;
- unlock();
- return;
- }
+ if(FAILED(render_client->ReleaseBuffer(length, 0)))
+ goto stop_thread;
- unlock();
+ // stop thread
+ if(shouldStop())
+ {
+ stop_thread:
+ m_audio_client->Stop();
+ SafeRelease(&render_client);
+ doStop();
+ return;
+ }
+ }
std::this_thread::sleep_for(sleepDuration);
}
}
-void WASAPIDevice::playing(bool playing)
-{
- if((!m_playing || m_stop) && playing)
- start();
- else
- m_stop = true;
-}
-
WASAPIDevice::WASAPIDevice(DeviceSpecs specs, int buffersize) :
- m_playing(false),
- m_stop(false),
-
m_imm_device_enumerator(nullptr),
m_imm_device(nullptr),
m_audio_client(nullptr),
@@ -361,14 +285,7 @@ WASAPIDevice::WASAPIDevice(DeviceSpecs specs, int buffersize) :
WASAPIDevice::~WASAPIDevice()
{
- lock();
-
- stopAll();
-
- unlock();
-
- if(m_thread.joinable())
- m_thread.join();
+ stopMixingThread();
SafeRelease(&m_audio_client);
SafeRelease(&m_imm_device);
diff --git a/extern/audaspace/plugins/wasapi/WASAPIDevice.h b/extern/audaspace/plugins/wasapi/WASAPIDevice.h
index ae25a09c432..375f03bd255 100644
--- a/extern/audaspace/plugins/wasapi/WASAPIDevice.h
+++ b/extern/audaspace/plugins/wasapi/WASAPIDevice.h
@@ -26,7 +26,7 @@
* The WASAPIDevice class.
*/
-#include "devices/SoftwareDevice.h"
+#include "devices/ThreadedDevice.h"
#include <thread>
@@ -40,46 +40,23 @@ AUD_NAMESPACE_BEGIN
/**
* This device plays back through WASAPI, the Windows audio API.
*/
-class AUD_PLUGIN_API WASAPIDevice : public SoftwareDevice
+class AUD_PLUGIN_API WASAPIDevice : public ThreadedDevice
{
private:
- /**
- * Whether there is currently playback.
- */
- bool m_playing;
-
- /**
- * Whether the current playback should stop.
- */
- bool m_stop;
-
IMMDeviceEnumerator* m_imm_device_enumerator;
IMMDevice* m_imm_device;
IAudioClient* m_audio_client;
WAVEFORMATEXTENSIBLE m_wave_format_extensible;
/**
- * The streaming thread.
- */
- std::thread m_thread;
-
- /**
- * Starts the streaming thread.
- */
- AUD_LOCAL void start();
-
- /**
* Streaming thread main function.
*/
- AUD_LOCAL void updateStream();
+ AUD_LOCAL void runMixingThread();
// delete copy constructor and operator=
WASAPIDevice(const WASAPIDevice&) = delete;
WASAPIDevice& operator=(const WASAPIDevice&) = delete;
-protected:
- virtual void playing(bool playing);
-
public:
/**
* Opens the WASAPI audio device for playback.
diff --git a/extern/audaspace/src/devices/SoftwareDevice.cpp b/extern/audaspace/src/devices/SoftwareDevice.cpp
index c8c1c6081c2..7a2561515f4 100644
--- a/extern/audaspace/src/devices/SoftwareDevice.cpp
+++ b/extern/audaspace/src/devices/SoftwareDevice.cpp
@@ -737,7 +737,7 @@ void SoftwareDevice::mix(data_t* buffer, int length)
{
m_buffer.assureSize(length * AUD_SAMPLE_SIZE(m_specs));
- std::lock_guard<std::recursive_mutex> lock(m_mutex);
+ std::lock_guard<ILockable> lock(*this);
{
std::shared_ptr<SoftwareDevice::SoftwareHandle> sound;
@@ -880,7 +880,7 @@ std::shared_ptr<IHandle> SoftwareDevice::play(std::shared_ptr<IReader> reader, b
// play sound
std::shared_ptr<SoftwareDevice::SoftwareHandle> sound = std::shared_ptr<SoftwareDevice::SoftwareHandle>(new SoftwareDevice::SoftwareHandle(this, reader, pitch, resampler, mapper, keep));
- std::lock_guard<std::recursive_mutex> lock(m_mutex);
+ std::lock_guard<ILockable> lock(*this);
m_playingSounds.push_back(sound);
@@ -897,7 +897,7 @@ std::shared_ptr<IHandle> SoftwareDevice::play(std::shared_ptr<ISound> sound, boo
void SoftwareDevice::stopAll()
{
- std::lock_guard<std::recursive_mutex> lock(m_mutex);
+ std::lock_guard<ILockable> lock(*this);
while(!m_playingSounds.empty())
m_playingSounds.front()->stop();
diff --git a/extern/audaspace/src/devices/ThreadedDevice.cpp b/extern/audaspace/src/devices/ThreadedDevice.cpp
new file mode 100644
index 00000000000..44ceccb8a60
--- /dev/null
+++ b/extern/audaspace/src/devices/ThreadedDevice.cpp
@@ -0,0 +1,65 @@
+/*******************************************************************************
+ * Copyright 2009-2016 Jörg Müller
+ *
+ * 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 "devices/ThreadedDevice.h"
+
+#include <mutex>
+
+AUD_NAMESPACE_BEGIN
+
+void ThreadedDevice::start()
+{
+ std::lock_guard<ILockable> lock(*this);
+
+ // thread is still running, we can abort stopping it
+ if(m_stop)
+ m_stop = false;
+
+ // thread is not running, let's start it
+ else if(!m_playing)
+ {
+ if(m_thread.joinable())
+ m_thread.join();
+
+ m_playing = true;
+
+ m_thread = std::thread(&ThreadedDevice::runMixingThread, this);
+ }
+}
+
+void ThreadedDevice::playing(bool playing)
+{
+ if((!m_playing || m_stop) && playing)
+ start();
+ else
+ m_stop = true;
+}
+
+ThreadedDevice::ThreadedDevice() :
+ m_playing(false),
+ m_stop(false)
+{
+}
+
+void aud::ThreadedDevice::stopMixingThread()
+{
+ stopAll();
+
+ if(m_thread.joinable())
+ m_thread.join();
+}
+
+AUD_NAMESPACE_END
diff --git a/extern/mantaflow/preprocessed/fileio/iovdb.cpp b/extern/mantaflow/preprocessed/fileio/iovdb.cpp
index e615741e0f7..1846ef7ecbb 100644
--- a/extern/mantaflow/preprocessed/fileio/iovdb.cpp
+++ b/extern/mantaflow/preprocessed/fileio/iovdb.cpp
@@ -29,10 +29,10 @@
#if OPENVDB == 1
# include "openvdb/openvdb.h"
-# include <openvdb/points/PointConversion.h>
-# include <openvdb/points/PointCount.h>
-# include <openvdb/tools/Clip.h>
-# include <openvdb/tools/Dense.h>
+# include "openvdb/points/PointConversion.h"
+# include "openvdb/points/PointCount.h"
+# include "openvdb/tools/Clip.h"
+# include "openvdb/tools/Dense.h"
#endif
#define POSITION_NAME "P"
@@ -519,7 +519,7 @@ int writeObjectsVDB(const string &filename,
}
}
- // Write only if the is at least one grid, optionally write with compression.
+ // Write only if there is at least one grid, optionally write with compression.
if (gridsVDB.size()) {
int vdb_flags = openvdb::io::COMPRESS_ACTIVE_MASK;
switch (compression) {
@@ -534,7 +534,8 @@ int writeObjectsVDB(const string &filename,
}
case COMPRESSION_BLOSC: {
# if OPENVDB_BLOSC == 1
- vdb_flags |= openvdb::io::COMPRESS_BLOSC;
+ // Cannot use |= here, causes segfault with blosc 1.5.0 (== recommended version)
+ vdb_flags = openvdb::io::COMPRESS_BLOSC;
# else
debMsg("OpenVDB was built without Blosc support, using Zip compression instead", 1);
vdb_flags |= openvdb::io::COMPRESS_ZIP;
diff --git a/extern/mantaflow/preprocessed/fluidsolver.h b/extern/mantaflow/preprocessed/fluidsolver.h
index 0c871bca3a1..6770f8b7b05 100644
--- a/extern/mantaflow/preprocessed/fluidsolver.h
+++ b/extern/mantaflow/preprocessed/fluidsolver.h
@@ -384,6 +384,7 @@ class FluidSolver : public PbClass {
GridStorage<Real> mGrids4dReal;
GridStorage<Vec3> mGrids4dVec;
GridStorage<Vec4> mGrids4dVec4;
+
public:
PbArgs _args;
}
diff --git a/extern/mantaflow/preprocessed/general.h b/extern/mantaflow/preprocessed/general.h
index 50eac71e87e..8bf1c2e25de 100644
--- a/extern/mantaflow/preprocessed/general.h
+++ b/extern/mantaflow/preprocessed/general.h
@@ -42,7 +42,7 @@ inline void updateQtGui(bool full, int frame, float time, const std::string &cur
# ifdef _DEBUG
# define DEBUG 1
# endif // _DEBUG
-#endif // DEBUG
+#endif // DEBUG
// Standard exception
class Error : public std::exception {
diff --git a/extern/mantaflow/preprocessed/gitinfo.h b/extern/mantaflow/preprocessed/gitinfo.h
index 1bb96fe3baa..03fd0112095 100644
--- a/extern/mantaflow/preprocessed/gitinfo.h
+++ b/extern/mantaflow/preprocessed/gitinfo.h
@@ -1,3 +1,3 @@
-#define MANTA_GIT_VERSION "commit 39b7a415721ecbf6643612a24e8eadd221aeb934"
+#define MANTA_GIT_VERSION "commit 9c505cd22e289b98c9aa717efba8ef3201c7e458"
diff --git a/extern/mantaflow/preprocessed/grid.h b/extern/mantaflow/preprocessed/grid.h
index 9c3d954771e..2c4296e78dd 100644
--- a/extern/mantaflow/preprocessed/grid.h
+++ b/extern/mantaflow/preprocessed/grid.h
@@ -389,6 +389,7 @@ class GridBase : public PbClass {
Real mDx;
bool m3D; // precomputed Z shift: to ensure 2D compatibility, always use this instead of sx*sy !
IndexInt mStrideZ;
+
public:
PbArgs _args;
}
diff --git a/extern/mantaflow/preprocessed/grid4d.h b/extern/mantaflow/preprocessed/grid4d.h
index 9b64116fc9d..1741db590b7 100644
--- a/extern/mantaflow/preprocessed/grid4d.h
+++ b/extern/mantaflow/preprocessed/grid4d.h
@@ -326,6 +326,7 @@ class Grid4dBase : public PbClass {
// precomputed Z,T shift: to ensure 2D compatibility, always use this instead of sx*sy !
IndexInt mStrideZ;
IndexInt mStrideT;
+
public:
PbArgs _args;
}
@@ -950,6 +951,7 @@ template<class T> class Grid4d : public Grid4dBase {
protected:
T *mData;
+
public:
PbArgs _args;
}
diff --git a/extern/mantaflow/preprocessed/levelset.h b/extern/mantaflow/preprocessed/levelset.h
index db542bb1fe6..ae162f73c3d 100644
--- a/extern/mantaflow/preprocessed/levelset.h
+++ b/extern/mantaflow/preprocessed/levelset.h
@@ -266,6 +266,7 @@ class LevelsetGrid : public Grid<Real> {
}
static Real invalidTimeValue();
+
public:
PbArgs _args;
}
diff --git a/extern/mantaflow/preprocessed/mesh.h b/extern/mantaflow/preprocessed/mesh.h
index d3a69abc4ea..b5de66ce095 100644
--- a/extern/mantaflow/preprocessed/mesh.h
+++ b/extern/mantaflow/preprocessed/mesh.h
@@ -796,6 +796,7 @@ class Mesh : public PbClass {
std::vector<MeshDataImpl<int> *>
mMdataInt; //! indicate that mdata of this mesh is copied, and needs to be freed
bool mFreeMdata;
+
public:
PbArgs _args;
}
@@ -881,6 +882,7 @@ class MeshDataBase : public PbClass {
protected:
Mesh *mMesh;
+
public:
PbArgs _args;
}
@@ -1645,6 +1647,7 @@ template<class T> class MeshDataImpl : public MeshDataBase {
//! optionally , we might have an associated grid from which to grab new data
Grid<T> *mpGridSource; //! unfortunately , we need to distinguish mac vs regular vec3
bool mGridSourceMAC;
+
public:
PbArgs _args;
}
diff --git a/extern/mantaflow/preprocessed/movingobs.h b/extern/mantaflow/preprocessed/movingobs.h
index 0661ddf5b37..83ef6ed0c9f 100644
--- a/extern/mantaflow/preprocessed/movingobs.h
+++ b/extern/mantaflow/preprocessed/movingobs.h
@@ -154,6 +154,7 @@ class MovingObstacle : public PbClass {
int mEmptyType;
int mID;
static int sIDcnt;
+
public:
PbArgs _args;
}
diff --git a/extern/mantaflow/preprocessed/noisefield.h b/extern/mantaflow/preprocessed/noisefield.h
index 73c9de779ef..6ed8ac0012d 100644
--- a/extern/mantaflow/preprocessed/noisefield.h
+++ b/extern/mantaflow/preprocessed/noisefield.h
@@ -236,6 +236,7 @@ class WaveletNoiseField : public PbClass {
static int randomSeed;
// global reference count for noise tile
static std::atomic<int> mNoiseReferenceCount;
+
public:
PbArgs _args;
}
diff --git a/extern/mantaflow/preprocessed/particle.h b/extern/mantaflow/preprocessed/particle.h
index 7fcc7e5ca32..7e0c64e6d03 100644
--- a/extern/mantaflow/preprocessed/particle.h
+++ b/extern/mantaflow/preprocessed/particle.h
@@ -205,6 +205,7 @@ class ParticleBase : public PbClass {
//! custom seed for particle systems, used by plugins
int mSeed; //! fix global random seed storage, used mainly by functions in this class
static int globalSeed;
+
public:
PbArgs _args;
}
@@ -628,6 +629,7 @@ template<class S> class ParticleSystem : public ParticleBase {
std::vector<S> mData;
//! reduce storage , called by doCompress
virtual void compress();
+
public:
PbArgs _args;
}
@@ -918,6 +920,7 @@ class ParticleIndexSystem : public ParticleSystem<ParticleIndexData> {
return -1;
}
};
+
public:
PbArgs _args;
}
@@ -982,6 +985,7 @@ template<class DATA, class CON> class ConnectedParticleSystem : public ParticleS
protected:
std::vector<CON> mSegments;
virtual void compress();
+
public:
PbArgs _args;
}
@@ -1071,6 +1075,7 @@ class ParticleDataBase : public PbClass {
protected:
ParticleBase *mpParticleSys;
+
public:
PbArgs _args;
}
@@ -1843,6 +1848,7 @@ template<class T> class ParticleDataImpl : public ParticleDataBase {
//! optionally , we might have an associated grid from which to grab new data
Grid<T> *mpGridSource; //! unfortunately , we need to distinguish mac vs regular vec3
bool mGridSourceMAC;
+
public:
PbArgs _args;
}
diff --git a/extern/mantaflow/preprocessed/plugin/meshplugins.cpp b/extern/mantaflow/preprocessed/plugin/meshplugins.cpp
index 043660db20b..cf315429d65 100644
--- a/extern/mantaflow/preprocessed/plugin/meshplugins.cpp
+++ b/extern/mantaflow/preprocessed/plugin/meshplugins.cpp
@@ -234,10 +234,10 @@ void subdivideMesh(
normalize(ne2);
// Real thisArea = sqrMag(cross(-e2,e0));
- // small angle approximation says sin(x) = arcsin(x) = x,
- // arccos(x) = pi/2 - arcsin(x),
- // cos(x) = dot(A,B),
- // so angle is approximately 1 - dot(A,B).
+ // small angle approximation says sin(x) = arcsin(x) = x,
+ // arccos(x) = pi/2 - arcsin(x),
+ // cos(x) = dot(A,B),
+ // so angle is approximately 1 - dot(A,B).
Real angle[3];
angle[0] = 1.0 - dot(ne0, -ne2);
angle[1] = 1.0 - dot(ne1, -ne0);
diff --git a/extern/mantaflow/preprocessed/plugin/secondaryparticles.cpp b/extern/mantaflow/preprocessed/plugin/secondaryparticles.cpp
index 7a1d8224d94..2f876376f53 100644
--- a/extern/mantaflow/preprocessed/plugin/secondaryparticles.cpp
+++ b/extern/mantaflow/preprocessed/plugin/secondaryparticles.cpp
@@ -2287,9 +2287,10 @@ struct knFlipComputePotentialTrappedAir : public KernelBase {
const Vec3 &vj = scaleFromManta * v.getCentered(x, y, z);
const Vec3 xij = xi - xj;
const Vec3 vij = vi - vj;
- Real h = !pot.is3D() ? 1.414 * radius :
- 1.732 * radius; // estimate sqrt(2)*radius resp. sqrt(3)*radius
- // for h, due to squared resp. cubic neighbor area
+ Real h = !pot.is3D() ?
+ 1.414 * radius :
+ 1.732 * radius; // estimate sqrt(2)*radius resp. sqrt(3)*radius for h, due
+ // to squared resp. cubic neighbor area
vdiff += norm(vij) * (1 - dot(getNormalized(vij), getNormalized(xij))) *
(1 - norm(xij) / h);
}
diff --git a/extern/mantaflow/preprocessed/shapes.h b/extern/mantaflow/preprocessed/shapes.h
index fa645389bfe..5a400eaed09 100644
--- a/extern/mantaflow/preprocessed/shapes.h
+++ b/extern/mantaflow/preprocessed/shapes.h
@@ -269,6 +269,7 @@ class Shape : public PbClass {
protected:
GridType mType;
+
public:
PbArgs _args;
}
@@ -319,6 +320,7 @@ class NullShape : public Shape {
{
gridSetConst<Real>(phi, 1000.0f);
}
+
public:
PbArgs _args;
}
@@ -394,6 +396,7 @@ class Box : public Shape {
protected:
Vec3 mP0, mP1;
+
public:
PbArgs _args;
}
@@ -455,6 +458,7 @@ class Sphere : public Shape {
protected:
Vec3 mCenter, mScale;
Real mRadius;
+
public:
PbArgs _args;
}
@@ -579,6 +583,7 @@ class Cylinder : public Shape {
protected:
Vec3 mCenter, mZDir;
Real mRadius, mZ;
+
public:
PbArgs _args;
}
@@ -655,6 +660,7 @@ class Slope : public Shape {
Real mAnglexy, mAngleyz;
Real mOrigin;
Vec3 mGs;
+
public:
PbArgs _args;
}
diff --git a/extern/mantaflow/preprocessed/turbulencepart.h b/extern/mantaflow/preprocessed/turbulencepart.h
index 81c94d77722..5177aeb2d96 100644
--- a/extern/mantaflow/preprocessed/turbulencepart.h
+++ b/extern/mantaflow/preprocessed/turbulencepart.h
@@ -199,6 +199,7 @@ class TurbulenceParticleSystem : public ParticleSystem<TurbulenceParticleData> {
private:
WaveletNoiseField &noise;
+
public:
PbArgs _args;
}
diff --git a/extern/mantaflow/preprocessed/vortexpart.h b/extern/mantaflow/preprocessed/vortexpart.h
index e48fbc7f507..8f80cf910eb 100644
--- a/extern/mantaflow/preprocessed/vortexpart.h
+++ b/extern/mantaflow/preprocessed/vortexpart.h
@@ -127,6 +127,7 @@ class VortexParticleSystem : public ParticleSystem<VortexParticleData> {
}
virtual ParticleBase *clone();
+
public:
PbArgs _args;
}
diff --git a/extern/mantaflow/preprocessed/vortexsheet.h b/extern/mantaflow/preprocessed/vortexsheet.h
index 01c32e4e806..0fc0f3a1258 100644
--- a/extern/mantaflow/preprocessed/vortexsheet.h
+++ b/extern/mantaflow/preprocessed/vortexsheet.h
@@ -240,6 +240,7 @@ class VortexSheetMesh : public Mesh {
VorticityChannel mVorticity;
TexCoord3Channel mTex1, mTex2;
TurbulenceChannel mTurb;
+
public:
PbArgs _args;
}
diff --git a/extern/smaa_areatex/CMakeLists.txt b/extern/smaa_areatex/CMakeLists.txt
new file mode 100644
index 00000000000..2386b0e7b79
--- /dev/null
+++ b/extern/smaa_areatex/CMakeLists.txt
@@ -0,0 +1,26 @@
+# ***** 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.
+#
+# The Original Code is Copyright (C) 2017, Blender Foundation
+# All rights reserved.
+#
+# The Original Code is: all of this file.
+#
+# Contributor(s): IRIE Shinsuke
+#
+# ***** END GPL LICENSE BLOCK *****
+
+add_executable(smaa_areatex smaa_areatex.cpp)
diff --git a/extern/smaa_areatex/README.blender b/extern/smaa_areatex/README.blender
new file mode 100644
index 00000000000..9c409142ae8
--- /dev/null
+++ b/extern/smaa_areatex/README.blender
@@ -0,0 +1,5 @@
+Project: smaa-cpp
+URL: https://github.com/iRi-E/smaa-cpp
+License: MIT
+Upstream version: 0.4.0
+Local modifications:
diff --git a/extern/smaa_areatex/smaa_areatex.cpp b/extern/smaa_areatex/smaa_areatex.cpp
new file mode 100644
index 00000000000..7a4ff3a9831
--- /dev/null
+++ b/extern/smaa_areatex/smaa_areatex.cpp
@@ -0,0 +1,1208 @@
+/**
+ * Copyright (C) 2016-2017 IRIE Shinsuke
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/*
+ * smaa_areatex.cpp version 0.4.0
+ *
+ * This is a part of smaa-cpp that is an implementation of
+ * Enhanced Subpixel Morphological Antialiasing (SMAA) written in C++.
+ *
+ * This program is C++ rewrite of AreaTex.py included in the original
+ * SMAA ditribution:
+ *
+ * https://github.com/iryoku/smaa/tree/master/Scripts
+ */
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+
+#include <cmath>
+
+/*------------------------------------------------------------------------------*/
+/* Type Definitions */
+
+class Int2;
+class Dbl2;
+
+class Int2 {
+public:
+ int x, y;
+
+ Int2() { this->x = this->y = 0; }
+ Int2(int x) { this->x = this->y = x; }
+ Int2(int x, int y) { this->x = x; this->y = y; }
+
+ operator Dbl2();
+
+ Int2 operator + (Int2 other) { return Int2(x + other.x, y + other.y); }
+ Int2 operator * (Int2 other) { return Int2(x * other.x, y * other.y); }
+};
+
+class Dbl2 {
+public:
+ double x, y;
+
+ Dbl2() { this->x = this->y = 0.0; }
+ Dbl2(double x) { this->x = this->y = x; }
+ Dbl2(double x, double y) { this->x = x; this->y = y; }
+
+ Dbl2 apply(double (* func)(double)) { return Dbl2(func(x), func(y)); }
+
+ operator Int2();
+
+ Dbl2 operator + (Dbl2 other) { return Dbl2(x + other.x, y + other.y); }
+ Dbl2 operator - (Dbl2 other) { return Dbl2(x - other.x, y - other.y); }
+ Dbl2 operator * (Dbl2 other) { return Dbl2(x * other.x, y * other.y); }
+ Dbl2 operator / (Dbl2 other) { return Dbl2(x / other.x, y / other.y); }
+ Dbl2 operator += (Dbl2 other) { return Dbl2(x += other.x, y += other.y); }
+ bool operator == (Dbl2 other) { return (x == other.x && y == other.y); }
+};
+
+Int2::operator Dbl2() { return Dbl2((double)x, (double)y); }
+Dbl2::operator Int2() { return Int2((int)x, (int)y); }
+
+/*------------------------------------------------------------------------------*/
+/* Data to Calculate Areatex */
+
+/* Texture sizes: */
+/* (it's quite possible that this is not easily configurable) */
+static const int SUBSAMPLES_ORTHO = 7;
+static const int SUBSAMPLES_DIAG = 5;
+static const int MAX_DIST_ORTHO_COMPAT = 16;
+static const int MAX_DIST_ORTHO = 20;
+static const int MAX_DIST_DIAG = 20;
+static const int TEX_SIZE_ORTHO = 80; /* 16 * 5 slots = 80 */
+static const int TEX_SIZE_DIAG = 80; /* 20 * 4 slots = 80 */
+
+/* Number of samples for calculating areas in the diagonal textures: */
+/* (diagonal areas are calculated using brute force sampling) */
+static const int SAMPLES_DIAG = 30;
+
+/* Maximum distance for smoothing u-shapes: */
+static const int SMOOTH_MAX_DISTANCE = 32;
+
+/*------------------------------------------------------------------------------*/
+/* Offset Tables */
+
+/* Offsets for subsample rendering */
+static const double subsample_offsets_ortho[SUBSAMPLES_ORTHO] = {
+ 0.0, /* 0 */
+ -0.25, /* 1 */
+ 0.25, /* 2 */
+ -0.125, /* 3 */
+ 0.125, /* 4 */
+ -0.375, /* 5 */
+ 0.375 /* 6 */
+};
+
+static const Dbl2 subsample_offsets_diag[SUBSAMPLES_DIAG] = {
+ { 0.00, 0.00}, /* 0 */
+ { 0.25, -0.25}, /* 1 */
+ {-0.25, 0.25}, /* 2 */
+ { 0.125, -0.125}, /* 3 */
+ {-0.125, 0.125} /* 4 */
+};
+
+/* Mapping offsets for placing each pattern subtexture into its place */
+enum edgesorthoIndices
+{
+ EDGESORTHO_NONE_NONE = 0,
+ EDGESORTHO_NONE_NEGA = 1,
+ EDGESORTHO_NONE_POSI = 2,
+ EDGESORTHO_NONE_BOTH = 3,
+ EDGESORTHO_NEGA_NONE = 4,
+ EDGESORTHO_NEGA_NEGA = 5,
+ EDGESORTHO_NEGA_POSI = 6,
+ EDGESORTHO_NEGA_BOTH = 7,
+ EDGESORTHO_POSI_NONE = 8,
+ EDGESORTHO_POSI_NEGA = 9,
+ EDGESORTHO_POSI_POSI = 10,
+ EDGESORTHO_POSI_BOTH = 11,
+ EDGESORTHO_BOTH_NONE = 12,
+ EDGESORTHO_BOTH_NEGA = 13,
+ EDGESORTHO_BOTH_POSI = 14,
+ EDGESORTHO_BOTH_BOTH = 15,
+};
+
+static const Int2 edgesortho_compat[16] = {
+ {0, 0}, {0, 1}, {0, 3}, {0, 4}, {1, 0}, {1, 1}, {1, 3}, {1, 4},
+ {3, 0}, {3, 1}, {3, 3}, {3, 4}, {4, 0}, {4, 1}, {4, 3}, {4, 4}
+};
+
+static const Int2 edgesortho[16] = {
+ {0, 0}, {0, 1}, {0, 2}, {0, 3}, {1, 0}, {1, 1}, {1, 2}, {1, 3},
+ {2, 0}, {2, 1}, {2, 2}, {2, 3}, {3, 0}, {3, 1}, {3, 2}, {3, 3}
+};
+
+enum edgesdiagIndices
+{
+ EDGESDIAG_NONE_NONE = 0,
+ EDGESDIAG_NONE_VERT = 1,
+ EDGESDIAG_NONE_HORZ = 2,
+ EDGESDIAG_NONE_BOTH = 3,
+ EDGESDIAG_VERT_NONE = 4,
+ EDGESDIAG_VERT_VERT = 5,
+ EDGESDIAG_VERT_HORZ = 6,
+ EDGESDIAG_VERT_BOTH = 7,
+ EDGESDIAG_HORZ_NONE = 8,
+ EDGESDIAG_HORZ_VERT = 9,
+ EDGESDIAG_HORZ_HORZ = 10,
+ EDGESDIAG_HORZ_BOTH = 11,
+ EDGESDIAG_BOTH_NONE = 12,
+ EDGESDIAG_BOTH_VERT = 13,
+ EDGESDIAG_BOTH_HORZ = 14,
+ EDGESDIAG_BOTH_BOTH = 15,
+};
+
+static const Int2 edgesdiag[16] = {
+ {0, 0}, {0, 1}, {0, 2}, {0, 3}, {1, 0}, {1, 1}, {1, 2}, {1, 3},
+ {2, 0}, {2, 1}, {2, 2}, {2, 3}, {3, 0}, {3, 1}, {3, 2}, {3, 3}
+};
+
+/*------------------------------------------------------------------------------*/
+/* Miscellaneous Utility Functions */
+
+/* Linear interpolation: */
+static Dbl2 lerp(Dbl2 a, Dbl2 b, double p)
+{
+ return a + (b - a) * Dbl2(p);
+}
+
+/* Saturates a value to [0..1] range: */
+static double saturate(double x)
+{
+ return 0.0 < x ? (x < 1.0 ? x : 1.0) : 0.0;
+}
+
+/*------------------------------------------------------------------------------*/
+/* Horizontal/Vertical Areas */
+
+class AreaOrtho {
+ double m_data[SUBSAMPLES_ORTHO][TEX_SIZE_ORTHO][TEX_SIZE_ORTHO][2];
+ bool m_compat;
+ bool m_orig_u;
+public:
+ AreaOrtho(bool compat, bool orig_u) : m_compat(compat), m_orig_u(orig_u) {}
+
+ double *getData() { return (double *)&m_data; }
+ Dbl2 getPixel(int offset_index, Int2 coords) {
+ return Dbl2(m_data[offset_index][coords.y][coords.x][0],
+ m_data[offset_index][coords.y][coords.x][1]);
+ }
+
+ void areaTex(int offset_index);
+private:
+ void putPixel(int offset_index, Int2 coords, Dbl2 pixel) {
+ m_data[offset_index][coords.y][coords.x][0] = pixel.x;
+ m_data[offset_index][coords.y][coords.x][1] = pixel.y;
+ }
+
+ Dbl2 smoothArea(double d, Dbl2 a1, Dbl2 a2);
+ Dbl2 makeQuad(int x, double d, double o);
+ Dbl2 area(Dbl2 p1, Dbl2 p2, int x);
+ Dbl2 calculate(int pattern, int left, int right, double offset);
+};
+
+/* Smoothing function for small u-patterns: */
+Dbl2 AreaOrtho::smoothArea(double d, Dbl2 a1, Dbl2 a2)
+{
+ Dbl2 b1 = (a1 * Dbl2(2.0)).apply(sqrt) * Dbl2(0.5);
+ Dbl2 b2 = (a2 * Dbl2(2.0)).apply(sqrt) * Dbl2(0.5);
+ double p = saturate(d / (double)SMOOTH_MAX_DISTANCE);
+ return lerp(b1, a1, p) + lerp(b2, a2, p);
+}
+
+/* Smoothing u-patterns by quadratic function: */
+Dbl2 AreaOrtho::makeQuad(int x, double d, double o)
+{
+ double r = (double)x;
+
+ /* fmin() below is a trick to smooth tiny u-patterns: */
+ return Dbl2(r, (1.0 - fmin(4.0, d) * r * (d - r) / (d * d)) * o);
+}
+
+/* Calculates the area under the line p1->p2, for the pixel x..x+1: */
+Dbl2 AreaOrtho::area(Dbl2 p1, Dbl2 p2, int x)
+{
+ Dbl2 d = p2 - p1;
+ double x1 = (double)x;
+ double x2 = x1 + 1.0;
+
+ if ((x1 >= p1.x && x1 < p2.x) || (x2 > p1.x && x2 <= p2.x)) { /* inside? */
+ double y1 = p1.y + (x1 - p1.x) * d.y / d.x;
+ double y2 = p1.y + (x2 - p1.x) * d.y / d.x;
+
+ if ((copysign(1.0, y1) == copysign(1.0, y2) ||
+ fabs(y1) < 1e-4 || fabs(y2) < 1e-4)) { /* trapezoid? */
+ double a = (y1 + y2) / 2.0;
+ if (a < 0.0)
+ return Dbl2(fabs(a), 0.0);
+ else
+ return Dbl2(0.0, fabs(a));
+ }
+ else { /* Then, we got two triangles: */
+ double x = p1.x - p1.y * d.x / d.y, xi;
+ double a1 = x > p1.x ? y1 * modf(x, &xi) / 2.0 : 0.0;
+ double a2 = x < p2.x ? y2 * (1.0 - modf(x, &xi)) / 2.0 : 0.0;
+ double a = fabs(a1) > fabs(a2) ? a1 : -a2;
+ if (a < 0.0)
+ return Dbl2(fabs(a1), fabs(a2));
+ else
+ return Dbl2(fabs(a2), fabs(a1));
+ }
+ }
+ else
+ return Dbl2(0.0, 0.0);
+}
+
+/* Calculates the area for a given pattern and distances to the left and to the */
+/* right, biased by an offset: */
+Dbl2 AreaOrtho::calculate(int pattern, int left, int right, double offset)
+{
+ Dbl2 a1, a2;
+
+ /*
+ * o1 |
+ * .-------´
+ * o2 |
+ *
+ * <---d--->
+ */
+ double d = (double)(left + right + 1);
+
+ double o1 = 0.5 + offset;
+ double o2 = 0.5 + offset - 1.0;
+
+ switch (pattern) {
+ case EDGESORTHO_NONE_NONE:
+ {
+ /*
+ *
+ * ------
+ *
+ */
+ return Dbl2(0.0, 0.0);
+ break;
+ }
+ case EDGESORTHO_POSI_NONE:
+ {
+ /*
+ *
+ * .------
+ * |
+ *
+ * We only offset L patterns in the crossing edge side, to make it
+ * converge with the unfiltered pattern 0 (we don't want to filter the
+ * pattern 0 to avoid artifacts).
+ */
+ if (left <= right)
+ return area(Dbl2(0.0, o2), Dbl2(d / 2.0, 0.0), left);
+ else
+ return Dbl2(0.0, 0.0);
+ break;
+ }
+ case EDGESORTHO_NONE_POSI:
+ {
+ /*
+ *
+ * ------.
+ * |
+ */
+ if (left >= right)
+ return area(Dbl2(d / 2.0, 0.0), Dbl2(d, o2), left);
+ else
+ return Dbl2(0.0, 0.0);
+ break;
+ }
+ case EDGESORTHO_POSI_POSI:
+ {
+ /*
+ *
+ * .------.
+ * | |
+ */
+ if (m_orig_u) {
+ a1 = area(Dbl2(0.0, o2), Dbl2(d / 2.0, 0.0), left);
+ a2 = area(Dbl2(d / 2.0, 0.0), Dbl2(d, o2), left);
+ return smoothArea(d, a1, a2);
+ }
+ else
+ return area(makeQuad(left, d, o2), makeQuad(left + 1, d, o2), left);
+ break;
+ }
+ case EDGESORTHO_NEGA_NONE:
+ {
+ /*
+ * |
+ * `------
+ *
+ */
+ if (left <= right)
+ return area(Dbl2(0.0, o1), Dbl2(d / 2.0, 0.0), left);
+ else
+ return Dbl2(0.0, 0.0);
+ break;
+ }
+ case EDGESORTHO_BOTH_NONE:
+ {
+ /*
+ * |
+ * +------
+ * |
+ */
+ return Dbl2(0.0, 0.0);
+ break;
+ }
+ case EDGESORTHO_NEGA_POSI:
+ {
+ /*
+ * |
+ * `------.
+ * |
+ *
+ * A problem of not offseting L patterns (see above), is that for certain
+ * max search distances, the pixels in the center of a Z pattern will
+ * detect the full Z pattern, while the pixels in the sides will detect a
+ * L pattern. To avoid discontinuities, we blend the full offsetted Z
+ * revectorization with partially offsetted L patterns.
+ */
+ if (fabs(offset) > 0.0) {
+ a1 = area(Dbl2(0.0, o1), Dbl2(d, o2), left);
+ a2 = area(Dbl2(0.0, o1), Dbl2(d / 2.0, 0.0), left);
+ a2 += area(Dbl2(d / 2.0, 0.0), Dbl2(d, o2), left);
+ return (a1 + a2) / Dbl2(2.0);
+ }
+ else
+ return area(Dbl2(0.0, o1), Dbl2(d, o2), left);
+ break;
+ }
+ case EDGESORTHO_BOTH_POSI:
+ {
+ /*
+ * |
+ * +------.
+ * | |
+ */
+ return area(Dbl2(0.0, o1), Dbl2(d, o2), left);
+ break;
+ }
+ case EDGESORTHO_NONE_NEGA:
+ {
+ /*
+ * |
+ * ------´
+ *
+ */
+ if (left >= right)
+ return area(Dbl2(d / 2.0, 0.0), Dbl2(d, o1), left);
+ else
+ return Dbl2(0.0, 0.0);
+ break;
+ }
+ case EDGESORTHO_POSI_NEGA:
+ {
+ /*
+ * |
+ * .------´
+ * |
+ */
+ if (fabs(offset) > 0.0) {
+ a1 = area(Dbl2(0.0, o2), Dbl2(d, o1), left);
+ a2 = area(Dbl2(0.0, o2), Dbl2(d / 2.0, 0.0), left);
+ a2 += area(Dbl2(d / 2.0, 0.0), Dbl2(d, o1), left);
+ return (a1 + a2) / Dbl2(2.0);
+ }
+ else
+ return area(Dbl2(0.0, o2), Dbl2(d, o1), left);
+ break;
+ }
+ case EDGESORTHO_NONE_BOTH:
+ {
+ /*
+ * |
+ * ------+
+ * |
+ */
+ return Dbl2(0.0, 0.0);
+ break;
+ }
+ case EDGESORTHO_POSI_BOTH:
+ {
+ /*
+ * |
+ * .------+
+ * | |
+ */
+ return area(Dbl2(0.0, o2), Dbl2(d, o1), left);
+ break;
+ }
+ case EDGESORTHO_NEGA_NEGA:
+ {
+ /*
+ * | |
+ * `------´
+ *
+ */
+ if (m_orig_u) {
+ a1 = area(Dbl2(0.0, o1), Dbl2(d / 2.0, 0.0), left);
+ a2 = area(Dbl2(d / 2.0, 0.0), Dbl2(d, o1), left);
+ return smoothArea(d, a1, a2);
+ }
+ else
+ return area(makeQuad(left, d, o1), makeQuad(left + 1, d, o1), left);
+ break;
+ }
+ case EDGESORTHO_BOTH_NEGA:
+ {
+ /*
+ * | |
+ * +------´
+ * |
+ */
+ return area(Dbl2(0.0, o2), Dbl2(d, o1), left);
+ break;
+ }
+ case EDGESORTHO_NEGA_BOTH:
+ {
+ /*
+ * | |
+ * `------+
+ * |
+ */
+ return area(Dbl2(0.0, o1), Dbl2(d, o2), left);
+ break;
+ }
+ case EDGESORTHO_BOTH_BOTH:
+ {
+ /*
+ * | |
+ * +------+
+ * | |
+ */
+ return Dbl2(0.0, 0.0);
+ break;
+ }
+ }
+
+ return Dbl2(0.0, 0.0);
+}
+
+/*------------------------------------------------------------------------------*/
+/* Diagonal Areas */
+
+class AreaDiag {
+ double m_data[SUBSAMPLES_DIAG][TEX_SIZE_DIAG][TEX_SIZE_DIAG][2];
+ bool m_numeric;
+ bool m_orig_u;
+public:
+ AreaDiag(bool numeric, bool orig_u) : m_numeric(numeric), m_orig_u(orig_u) {}
+
+ double *getData() { return (double *)&m_data; }
+ Dbl2 getPixel(int offset_index, Int2 coords) {
+ return Dbl2(m_data[offset_index][coords.y][coords.x][0],
+ m_data[offset_index][coords.y][coords.x][1]);
+ }
+
+ void areaTex(int offset_index);
+private:
+ void putPixel(int offset_index, Int2 coords, Dbl2 pixel) {
+ m_data[offset_index][coords.y][coords.x][0] = pixel.x;
+ m_data[offset_index][coords.y][coords.x][1] = pixel.y;
+ }
+
+ double area1(Dbl2 p1, Dbl2 p2, Int2 p);
+ Dbl2 area(Dbl2 p1, Dbl2 p2, int left);
+ Dbl2 areaTriangle(Dbl2 p1L, Dbl2 p2L, Dbl2 p1R, Dbl2 p2R, int left);
+ Dbl2 calculate(int pattern, int left, int right, Dbl2 offset);
+};
+
+/* Calculates the area under the line p1->p2 for the pixel 'p' using brute */
+/* force sampling: */
+/* (quick and dirty solution, but it works) */
+double AreaDiag::area1(Dbl2 p1, Dbl2 p2, Int2 p)
+{
+ if (p1 == p2)
+ return 1.0;
+
+ double xm = (p1.x + p2.x) / 2.0, ym = (p1.y + p2.y) / 2.0;
+ double a = p2.y - p1.y;
+ double b = p1.x - p2.x;
+ int count = 0;
+
+ for (int ix = 0; ix < SAMPLES_DIAG; ix++) {
+ double x = (double)p.x + (double)ix / (double)(SAMPLES_DIAG - 1);
+ for (int iy = 0; iy < SAMPLES_DIAG; iy++) {
+ double y = (double)p.y + (double)iy / (double)(SAMPLES_DIAG - 1);
+ if (a * (x - xm) + b * (y - ym) > 0.0) /* inside? */
+ count++;
+ }
+ }
+ return (double)count / (double)(SAMPLES_DIAG * SAMPLES_DIAG);
+}
+
+/* Calculates the area under the line p1->p2: */
+/* (includes the pixel and its opposite) */
+Dbl2 AreaDiag::area(Dbl2 p1, Dbl2 p2, int left)
+{
+ if (m_numeric) {
+ double a1 = area1(p1, p2, Int2(1, 0) + Int2(left));
+ double a2 = area1(p1, p2, Int2(1, 1) + Int2(left));
+ return Dbl2(1.0 - a1, a2);
+ }
+
+ /* Calculates the area under the line p1->p2 for the pixel 'p' analytically */
+ Dbl2 d = p2 - p1;
+ if (d.x == 0.0)
+ return Dbl2(0.0, 1.0);
+
+ double x1 = (double)(1 + left);
+ double x2 = x1 + 1.0;
+ double ymid = x1;
+ double xtop = p1.x + (ymid + 1.0 - p1.y) * d.x / d.y;
+ double xmid = p1.x + (ymid - p1.y) * d.x / d.y;
+ double xbot = p1.x + (ymid - 1.0 - p1.y) * d.x / d.y;
+
+ double y1 = p1.y + (x1 - p1.x) * d.y / d.x;
+ double y2 = p1.y + (x2 - p1.x) * d.y / d.x;
+ double fy1 = y1 - floor(y1);
+ double fy2 = y2 - floor(y2);
+ int iy1 = (int)floor(y1 - ymid);
+ int iy2 = (int)floor(y2 - ymid);
+
+ if (iy1 <= -2) {
+ if (iy2 == -1)
+ return Dbl2(1.0 - (x2 - xbot) * fy2 * 0.5, 0.0);
+ else if (iy2 == 0)
+ return Dbl2((xmid + xbot) * 0.5 - x1, (x2 - xmid) * fy2 * 0.5);
+ else if (iy2 >= 1)
+ return Dbl2((xmid + xbot) * 0.5 - x1, x2 - (xtop + xmid) * 0.5);
+ else /* iy2 < -1 */
+ return Dbl2(1.0, 0.0);
+ }
+ else if (iy1 == -1) {
+ if (iy2 == -1)
+ return Dbl2(1.0 - (fy1 + fy2) * 0.5, 0.0);
+ else if (iy2 == 0)
+ return Dbl2((xmid - x1) * (1.0 - fy1) * 0.5, (x2 - xmid) * fy2 * 0.5);
+ else if (iy2 >= 1)
+ return Dbl2((xmid - x1) * (1.0 - fy1) * 0.5, x2 - (xtop + xmid) * 0.5);
+ else /* iy2 < -1 */
+ return Dbl2(1.0 - (xbot - x1) * fy1 * 0.5, 0.0);
+ }
+ else if (iy1 == 0) {
+ if (iy2 == -1)
+ return Dbl2((x2 - xmid) * (1.0 - fy2) * 0.5, (xmid - x1) * fy1 * 0.5);
+ else if (iy2 == 0)
+ return Dbl2(0.0, (fy1 + fy2) * 0.5);
+ else if (iy2 >= 1)
+ return Dbl2(0.0, 1.0 - (xtop - x1) * (1.0 - fy1) * 0.5);
+ else /* iy2 < -1 */
+ return Dbl2(x2 - (xmid + xbot) * 0.5, (xmid - x1) * fy1 * 0.5);
+ }
+ else { /* iy1 > 0 */
+ if (iy2 == -1)
+ return Dbl2((x2 - xtop) * (1.0 - fy2) * 0.5, (xtop + xmid) * 0.5 - x1);
+ else if (iy2 == 0)
+ return Dbl2(0.0, 1.0 - (x1 - xtop) * (1.0 - fy2) * 0.5);
+ else if (iy2 >= 1)
+ return Dbl2(0.0, 1.0);
+ else /* iy2 < -1 */
+ return Dbl2(x2 - (xmid + xbot) * 0.5, (xtop + xmid) * 0.5 - x1);
+ }
+}
+
+/* Calculate u-patterns using a triangle: */
+Dbl2 AreaDiag::areaTriangle(Dbl2 p1L, Dbl2 p2L, Dbl2 p1R, Dbl2 p2R, int left)
+{
+ double x1 = (double)(1 + left);
+ double x2 = x1 + 1.0;
+
+ Dbl2 dL = p2L - p1L;
+ Dbl2 dR = p2R - p1R;
+ double xm = ((p1L.x * dL.y / dL.x - p1L.y) - (p1R.x * dR.y / dR.x - p1R.y)) / (dL.y / dL.x - dR.y / dR.x);
+
+ double y1 = (x1 < xm) ? p1L.y + (x1 - p1L.x) * dL.y / dL.x : p1R.y + (x1 - p1R.x) * dR.y / dR.x;
+ double y2 = (x2 < xm) ? p1L.y + (x2 - p1L.x) * dL.y / dL.x : p1R.y + (x2 - p1R.x) * dR.y / dR.x;
+
+ return area(Dbl2(x1, y1), Dbl2(x2, y2), left);
+}
+
+/* Calculates the area for a given pattern and distances to the left and to the */
+/* right, biased by an offset: */
+Dbl2 AreaDiag::calculate(int pattern, int left, int right, Dbl2 offset)
+{
+ Dbl2 a1, a2;
+
+ double d = (double)(left + right + 1);
+
+ /*
+ * There is some Black Magic around diagonal area calculations. Unlike
+ * orthogonal patterns, the 'null' pattern (one without crossing edges) must be
+ * filtered, and the ends of both the 'null' and L patterns are not known: L
+ * and U patterns have different endings, and we don't know what is the
+ * adjacent pattern. So, what we do is calculate a blend of both possibilites.
+ */
+ switch (pattern) {
+ case EDGESDIAG_NONE_NONE:
+ {
+ /*
+ *
+ * .-´
+ * .-´
+ * .-´
+ * .-´
+ * ´
+ *
+ */
+ a1 = area(Dbl2(1.0, 1.0), Dbl2(1.0, 1.0) + Dbl2(d), left); /* 1st possibility */
+ a2 = area(Dbl2(1.0, 0.0), Dbl2(1.0, 0.0) + Dbl2(d), left); /* 2nd possibility */
+ return (a1 + a2) / Dbl2(2.0); /* Blend them */
+ break;
+ }
+ case EDGESDIAG_VERT_NONE:
+ {
+ /*
+ *
+ * .-´
+ * .-´
+ * .-´
+ * .-´
+ * |
+ * |
+ */
+ a1 = area(Dbl2(1.0, 0.0) + offset, Dbl2(0.0, 0.0) + Dbl2(d), left);
+ a2 = area(Dbl2(1.0, 0.0) + offset, Dbl2(1.0, 0.0) + Dbl2(d), left);
+ return (a1 + a2) / Dbl2(2.0);
+ break;
+ }
+ case EDGESDIAG_NONE_HORZ:
+ {
+ /*
+ *
+ * .----
+ * .-´
+ * .-´
+ * .-´
+ * ´
+ *
+ */
+ a1 = area(Dbl2(0.0, 0.0), Dbl2(1.0, 0.0) + Dbl2(d) + offset, left);
+ a2 = area(Dbl2(1.0, 0.0), Dbl2(1.0, 0.0) + Dbl2(d) + offset, left);
+ return (a1 + a2) / Dbl2(2.0);
+ break;
+ }
+ case EDGESDIAG_VERT_HORZ:
+ {
+ /*
+ *
+ * .----
+ * .-´
+ * .-´
+ * .-´
+ * |
+ * |
+ */
+ if (m_orig_u)
+ return area(Dbl2(1.0, 0.0) + offset, Dbl2(1.0, 0.0) + Dbl2(d) + offset, left);
+ else
+ return areaTriangle(Dbl2(1.0, 0.0) + offset, Dbl2(1.0, 1.0) + Dbl2(d),
+ Dbl2(0.0, 0.0), Dbl2(1.0, 0.0) + Dbl2(d) + offset, left);
+ break;
+ }
+ case EDGESDIAG_HORZ_NONE:
+ {
+ /*
+ *
+ * .-´
+ * .-´
+ * .-´
+ * ----´
+ *
+ *
+ */
+ a1 = area(Dbl2(1.0, 1.0) + offset, Dbl2(0.0, 0.0) + Dbl2(d), left);
+ a2 = area(Dbl2(1.0, 1.0) + offset, Dbl2(1.0, 0.0) + Dbl2(d), left);
+ return (a1 + a2) / Dbl2(2.0);
+ break;
+ }
+ case EDGESDIAG_BOTH_NONE:
+ {
+ /*
+ *
+ * .-´
+ * .-´
+ * .-´
+ * --.-´
+ * |
+ * |
+ */
+ a1 = area(Dbl2(1.0, 1.0) + offset, Dbl2(0.0, 0.0) + Dbl2(d), left);
+ a2 = area(Dbl2(1.0, 0.0) + offset, Dbl2(1.0, 0.0) + Dbl2(d), left);
+ return (a1 + a2) / Dbl2(2.0);
+ break;
+ }
+ case EDGESDIAG_HORZ_HORZ:
+ {
+ /*
+ *
+ * .----
+ * .-´
+ * .-´
+ * ----´
+ *
+ *
+ */
+ return area(Dbl2(1.0, 1.0) + offset, Dbl2(1.0, 0.0) + Dbl2(d) + offset, left);
+ break;
+ }
+ case EDGESDIAG_BOTH_HORZ:
+ {
+ /*
+ *
+ * .----
+ * .-´
+ * .-´
+ * --.-´
+ * |
+ * |
+ */
+ a1 = area(Dbl2(1.0, 1.0) + offset, Dbl2(1.0, 0.0) + Dbl2(d) + offset, left);
+ a2 = area(Dbl2(1.0, 0.0) + offset, Dbl2(1.0, 0.0) + Dbl2(d) + offset, left);
+ return (a1 + a2) / Dbl2(2.0);
+ break;
+ }
+ case EDGESDIAG_NONE_VERT:
+ {
+ /*
+ * |
+ * |
+ * .-´
+ * .-´
+ * .-´
+ * ´
+ *
+ */
+ a1 = area(Dbl2(0.0, 0.0), Dbl2(1.0, 1.0) + Dbl2(d) + offset, left);
+ a2 = area(Dbl2(1.0, 0.0), Dbl2(1.0, 1.0) + Dbl2(d) + offset, left);
+ return (a1 + a2) / Dbl2(2.0);
+ break;
+ }
+ case EDGESDIAG_VERT_VERT:
+ {
+ /*
+ * |
+ * |
+ * .-´
+ * .-´
+ * .-´
+ * |
+ * |
+ */
+ return area(Dbl2(1.0, 0.0) + offset, Dbl2(1.0, 1.0) + Dbl2(d) + offset, left);
+ break;
+ }
+ case EDGESDIAG_NONE_BOTH:
+ {
+ /*
+ * |
+ * .----
+ * .-´
+ * .-´
+ * .-´
+ * ´
+ *
+ */
+ a1 = area(Dbl2(0.0, 0.0), Dbl2(1.0, 1.0) + Dbl2(d) + offset, left);
+ a2 = area(Dbl2(1.0, 0.0), Dbl2(1.0, 0.0) + Dbl2(d) + offset, left);
+ return (a1 + a2) / Dbl2(2.0);
+ break;
+ }
+ case EDGESDIAG_VERT_BOTH:
+ {
+ /*
+ * |
+ * .----
+ * .-´
+ * .-´
+ * .-´
+ * |
+ * |
+ */
+ a1 = area(Dbl2(1.0, 0.0) + offset, Dbl2(1.0, 1.0) + Dbl2(d) + offset, left);
+ a2 = area(Dbl2(1.0, 0.0) + offset, Dbl2(1.0, 0.0) + Dbl2(d) + offset, left);
+ return (a1 + a2) / Dbl2(2.0);
+ break;
+ }
+ case EDGESDIAG_HORZ_VERT:
+ {
+ /*
+ * |
+ * |
+ * .-´
+ * .-´
+ * ----´
+ *
+ *
+ */
+ if (m_orig_u)
+ return area(Dbl2(1.0, 1.0) + offset, Dbl2(1.0, 1.0) + Dbl2(d) + offset, left);
+ else
+ return areaTriangle(Dbl2(1.0, 1.0) + offset, Dbl2(2.0, 1.0) + Dbl2(d),
+ Dbl2(1.0, 0.0), Dbl2(1.0, 1.0) + Dbl2(d) + offset, left);
+ break;
+ }
+ case EDGESDIAG_BOTH_VERT:
+ {
+ /*
+ * |
+ * |
+ * .-´
+ * .-´
+ * --.-´
+ * |
+ * |
+ */
+ a1 = area(Dbl2(1.0, 1.0) + offset, Dbl2(1.0, 1.0) + Dbl2(d) + offset, left);
+ a2 = area(Dbl2(1.0, 0.0) + offset, Dbl2(1.0, 1.0) + Dbl2(d) + offset, left);
+ return (a1 + a2) / Dbl2(2.0);
+ break;
+ }
+ case EDGESDIAG_HORZ_BOTH:
+ {
+ /*
+ * |
+ * .----
+ * .-´
+ * .-´
+ * ----´
+ *
+ *
+ */
+ a1 = area(Dbl2(1.0, 1.0) + offset, Dbl2(1.0, 1.0) + Dbl2(d) + offset, left);
+ a2 = area(Dbl2(1.0, 1.0) + offset, Dbl2(1.0, 0.0) + Dbl2(d) + offset, left);
+ return (a1 + a2) / Dbl2(2.0);
+ break;
+ }
+ case EDGESDIAG_BOTH_BOTH:
+ {
+ /*
+ * |
+ * .----
+ * .-´
+ * .-´
+ * --.-´
+ * |
+ * |
+ */
+ a1 = area(Dbl2(1.0, 1.0) + offset, Dbl2(1.0, 1.0) + Dbl2(d) + offset, left);
+ a2 = area(Dbl2(1.0, 0.0) + offset, Dbl2(1.0, 0.0) + Dbl2(d) + offset, left);
+ return (a1 + a2) / Dbl2(2.0);
+ break;
+ }
+ }
+
+ return Dbl2(0.0, 0.0);
+}
+
+/*------------------------------------------------------------------------------*/
+/* Main Loops */
+
+void AreaOrtho::areaTex(int offset_index)
+{
+ double offset = subsample_offsets_ortho[offset_index];
+ int max_dist = m_compat ? MAX_DIST_ORTHO_COMPAT : MAX_DIST_ORTHO;
+
+ for (int pattern = 0; pattern < 16; pattern++) {
+ Int2 e = Int2(max_dist) * (m_compat ? edgesortho_compat : edgesortho)[pattern];
+ for (int left = 0; left < max_dist; left++) {
+ for (int right = 0; right < max_dist; right++) {
+ Dbl2 p = calculate(pattern, left * left, right * right, offset);
+ Int2 coords = e + Int2(left, right);
+
+ putPixel(offset_index, coords, p);
+ }
+ }
+ }
+ return;
+}
+
+void AreaDiag::areaTex(int offset_index)
+{
+ Dbl2 offset = subsample_offsets_diag[offset_index];
+
+ for (int pattern = 0; pattern < 16; pattern++) {
+ Int2 e = Int2(MAX_DIST_DIAG) * edgesdiag[pattern];
+ for (int left = 0; left < MAX_DIST_DIAG; left++) {
+ for (int right = 0; right < MAX_DIST_DIAG; right++) {
+ Dbl2 p = calculate(pattern, left, right, offset);
+ Int2 coords = e + Int2(left, right);
+
+ putPixel(offset_index, coords, p);
+ }
+ }
+ }
+ return;
+}
+
+/*------------------------------------------------------------------------------*/
+/* Write File to Specified Location on Disk */
+
+/* C/C++ source code (arrays of floats) */
+static void write_double_array(FILE *fp, const double *ptr, int length, const char *array_name, bool quantize)
+{
+ fprintf(fp, "static const float %s[%d] = {", array_name, length);
+
+ for (int n = 0; n < length; n++) {
+ if (n > 0)
+ fprintf(fp, ",");
+ fprintf(fp, (n % 8 != 0) ? " " : "\n\t");
+
+ if (quantize)
+ fprintf(fp, "%3d / 255.0", (int)(*(ptr++) * 255.0));
+ else
+ fprintf(fp, "%1.8lf", *(ptr++));
+ }
+
+ fprintf(fp, "\n};\n");
+}
+
+static void write_csource(AreaOrtho *ortho, AreaDiag *diag, FILE *fp, bool subsampling, bool quantize)
+{
+ fprintf(fp, "/* This file was generated by smaa_areatex.cpp */\n");
+
+ fprintf(fp, "\n/* Horizontal/Vertical Areas */\n");
+ write_double_array(fp, ortho->getData(),
+ TEX_SIZE_ORTHO * TEX_SIZE_ORTHO * 2 * (subsampling ? SUBSAMPLES_ORTHO : 1),
+ "areatex", quantize);
+
+ fprintf(fp, "\n/* Diagonal Areas */\n");
+ write_double_array(fp, diag->getData(),
+ TEX_SIZE_DIAG * TEX_SIZE_DIAG * 2 * (subsampling ? SUBSAMPLES_DIAG : 1),
+ "areatex_diag", quantize);
+}
+
+/* .tga File (RGBA 32bit uncompressed) */
+static void write_tga(AreaOrtho *ortho, AreaDiag *diag, FILE *fp, bool subsampling)
+{
+ int subsamples = subsampling ? SUBSAMPLES_ORTHO : 1;
+ unsigned char header[18] = {0, 0,
+ 2, /* uncompressed RGB */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 32, /* 32bit */
+ 8}; /* 8bit alpha, left to right, bottom to top */
+
+ /* Set width and height */
+ header[12] = (TEX_SIZE_ORTHO + TEX_SIZE_DIAG) & 0xff;
+ header[13] = ((TEX_SIZE_ORTHO + TEX_SIZE_DIAG) >> 8) & 0xff;
+ header[14] = (subsamples * TEX_SIZE_ORTHO) & 0xff;
+ header[15] = ((subsamples * TEX_SIZE_ORTHO) >> 8) & 0xff;
+
+ /* Write .tga header */
+ fwrite(header, sizeof(unsigned char), sizeof(header) / sizeof(unsigned char), fp);
+
+ /* Write pixel data */
+ for (int i = subsamples - 1; i >= 0; i--) {
+ for (int y = TEX_SIZE_ORTHO - 1; y >= 0; y--) {
+ for (int x = 0; x < TEX_SIZE_ORTHO; x++) {
+ Dbl2 p = ortho->getPixel(i, Int2(x, y));
+ fputc(0, fp); /* B */
+ fputc((unsigned char)(p.y * 255.0), fp); /* G */
+ fputc((unsigned char)(p.x * 255.0), fp); /* R */
+ fputc(0, fp); /* A */
+ }
+
+ for (int x = 0; x < TEX_SIZE_DIAG; x++) {
+ if (i < SUBSAMPLES_DIAG) {
+ Dbl2 p = diag->getPixel(i, Int2(x, y));
+ fputc(0, fp); /* B */
+ fputc((unsigned char)(p.y * 255.0), fp); /* G */
+ fputc((unsigned char)(p.x * 255.0), fp); /* R */
+ fputc(0, fp); /* A */
+ }
+ else {
+ fputc(0, fp);
+ fputc(0, fp);
+ fputc(0, fp);
+ fputc(0, fp);
+ }
+ }
+ }
+ }
+}
+
+/* .raw File (R8G8 raw data) */
+static void write_raw(AreaOrtho *ortho, AreaDiag *diag, FILE *fp, bool subsampling)
+{
+ int subsamples = subsampling ? SUBSAMPLES_ORTHO : 1;
+
+ /* Write pixel data */
+ for (int i = 0; i < subsamples; i++) {
+ for (int y = 0; y < TEX_SIZE_ORTHO; y++) {
+ for (int x = 0; x < TEX_SIZE_ORTHO; x++) {
+ Dbl2 p = ortho->getPixel(i, Int2(x, y));
+ fputc((unsigned char)(p.x * 255.0), fp); /* R */
+ fputc((unsigned char)(p.y * 255.0), fp); /* G */
+ }
+
+ for (int x = 0; x < TEX_SIZE_DIAG; x++) {
+ if (i < SUBSAMPLES_DIAG) {
+ Dbl2 p = diag->getPixel(i, Int2(x, y));
+ fputc((unsigned char)(p.x * 255.0), fp); /* R */
+ fputc((unsigned char)(p.y * 255.0), fp); /* G */
+ }
+ else {
+ fputc(0, fp);
+ fputc(0, fp);
+ }
+ }
+ }
+ }
+}
+
+static int generate_file(AreaOrtho *ortho, AreaDiag *diag, const char *path, bool subsampling, bool quantize, bool tga, bool raw)
+{
+ FILE *fp = fopen(path, tga ? "wb" : "w");
+
+ if (!fp) {
+ fprintf(stderr, "Unable to open file: %s\n", path);
+ return 1;
+ }
+
+ // fprintf(stderr, "Generating %s\n", path);
+
+ if (tga)
+ write_tga(ortho, diag, fp, subsampling);
+ else if (raw)
+ write_raw(ortho, diag, fp, subsampling);
+ else
+ write_csource(ortho, diag, fp, subsampling, quantize);
+
+ fclose(fp);
+
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ bool subsampling = false;
+ bool quantize = false;
+ bool tga = false;
+ bool raw = false;
+ bool compat = false;
+ bool numeric = false;
+ bool orig_u = false;
+ bool help = false;
+ char *outfile = NULL;
+ int status = 0;
+
+ for (int i = 1; i < argc; i++) {
+ char *ptr = argv[i];
+ if (*ptr++ == '-' && *ptr != '\0') {
+ char c;
+ while ((c = *ptr++) != '\0') {
+ if (c == 's')
+ subsampling = true;
+ else if (c == 'q')
+ quantize = true;
+ else if (c == 't')
+ tga = true;
+ else if (c == 'r')
+ raw = true;
+ else if (c == 'c')
+ compat = true;
+ else if (c == 'n')
+ numeric = true;
+ else if (c == 'u')
+ orig_u = true;
+ else if (c == 'h')
+ help = true;
+ else {
+ fprintf(stderr, "Unknown option: -%c\n", c);
+ status = 1;
+ break;
+ }
+ }
+ }
+ else if (outfile) {
+ fprintf(stderr, "Too much file names: %s, %s\n", outfile, argv[i]);
+ status = 1;
+ }
+ else
+ outfile = argv[i];
+
+ if (status != 0)
+ break;
+ }
+
+ if (status == 0 && !help && !outfile) {
+ fprintf(stderr, "File name was not specified.\n");
+ status = 1;
+ }
+
+ if (status != 0 || help) {
+ fprintf(stderr, "Usage: %s [OPTION]... OUTFILE\n", argv[0]);
+ fprintf(stderr, "Options:\n");
+ fprintf(stderr, " -s Calculate data for subpixel rendering\n");
+ fprintf(stderr, " -q Quantize data to 256 levels\n");
+ fprintf(stderr, " -t Write TGA image instead of C/C++ source\n");
+ fprintf(stderr, " -r Write R8G8 raw image instead of C/C++ source\n");
+ fprintf(stderr, " -c Generate compatible orthogonal data that subtexture size is 16\n");
+ fprintf(stderr, " -n Numerically calculate diagonal data using brute force sampling\n");
+ fprintf(stderr, " -u Process orthogonal / diagonal U patterns in older ways\n");
+ fprintf(stderr, " -h Print this help and exit\n");
+ fprintf(stderr, "File name OUTFILE usually should have an extension such as .c, .h, or .tga,\n");
+ fprintf(stderr, "except for a special name '-' that means standard output.\n\n");
+ fprintf(stderr, "Example:\n");
+ fprintf(stderr, " Generate TGA file exactly same as AreaTexDX10.tga bundled with the\n");
+ fprintf(stderr, " original implementation:\n\n");
+ fprintf(stderr, " $ smaa_areatex -stcnu AreaTexDX10.tga\n\n");
+ return status;
+ }
+
+ AreaOrtho *ortho = new AreaOrtho(compat, orig_u);
+ AreaDiag *diag = new AreaDiag(numeric, orig_u);
+
+ /* Calculate areatex data */
+ for (int i = 0; i < (subsampling ? SUBSAMPLES_ORTHO : 1); i++)
+ ortho->areaTex(i);
+
+ for (int i = 0; i < (subsampling ? SUBSAMPLES_DIAG : 1); i++)
+ diag->areaTex(i);
+
+ /* Generate .tga, .raw, or C/C++ source file, or write the data to stdout */
+ if (strcmp(outfile, "-") != 0)
+ status = generate_file(ortho, diag, outfile, subsampling, quantize, tga, raw);
+ else if (tga)
+ write_tga(ortho, diag, stdout, subsampling);
+ else if (raw)
+ write_raw(ortho, diag, stdout, subsampling);
+ else
+ write_csource(ortho, diag, stdout, subsampling, quantize);
+
+ delete ortho;
+ delete diag;
+
+ return status;
+}
+
+/* smaa_areatex.cpp ends here */
diff --git a/intern/clog/CLG_log.h b/intern/clog/CLG_log.h
index 3e51e228bac..8a26eb035cf 100644
--- a/intern/clog/CLG_log.h
+++ b/intern/clog/CLG_log.h
@@ -118,6 +118,7 @@ typedef struct CLG_LogType {
typedef struct CLG_LogRef {
const char *identifier;
CLG_LogType *type;
+ struct CLG_LogRef *next;
} CLG_LogRef;
void CLG_log_str(CLG_LogType *lg,
diff --git a/intern/clog/clog.c b/intern/clog/clog.c
index 01d1c0a1770..416ea25ee0c 100644
--- a/intern/clog/clog.c
+++ b/intern/clog/clog.c
@@ -81,6 +81,8 @@ typedef struct CLG_IDFilter {
typedef struct CLogContext {
/** Single linked list of types. */
CLG_LogType *types;
+ /** Single linked list of references. */
+ CLG_LogRef *refs;
#ifdef WITH_CLOG_PTHREADS
pthread_mutex_t types_lock;
#endif
@@ -320,7 +322,9 @@ static bool clg_ctx_filter_check(CLogContext *ctx, const char *identifier)
if (flt->match[0] == '*' && flt->match[len - 1] == '*') {
char *match = MEM_callocN(sizeof(char) * len - 1, __func__);
memcpy(match, flt->match + 1, len - 2);
- if (strstr(identifier, match) != NULL) {
+ const bool success = (strstr(identifier, match) != NULL);
+ MEM_freeN(match);
+ if (success) {
return (bool)i;
}
}
@@ -673,6 +677,12 @@ static void CLG_ctx_free(CLogContext *ctx)
MEM_freeN(item);
}
+ while (ctx->refs != NULL) {
+ CLG_LogRef *item = ctx->refs;
+ ctx->refs = item->next;
+ item->type = NULL;
+ }
+
for (uint i = 0; i < 2; i++) {
while (ctx->filters[i] != NULL) {
CLG_IDFilter *item = ctx->filters[i];
@@ -769,6 +779,10 @@ void CLG_logref_init(CLG_LogRef *clg_ref)
pthread_mutex_lock(&g_ctx->types_lock);
#endif
if (clg_ref->type == NULL) {
+ /* Add to the refs list so we can NULL the pointers to 'type' when CLG_exit() is called. */
+ clg_ref->next = g_ctx->refs;
+ g_ctx->refs = clg_ref;
+
CLG_LogType *clg_ty = clg_ctx_type_find_by_name(g_ctx, clg_ref->identifier);
if (clg_ty == NULL) {
clg_ty = clg_ctx_type_register(g_ctx, clg_ref->identifier);
diff --git a/intern/cycles/app/CMakeLists.txt b/intern/cycles/app/CMakeLists.txt
index 67b852013f3..7a1e5d62dd2 100644
--- a/intern/cycles/app/CMakeLists.txt
+++ b/intern/cycles/app/CMakeLists.txt
@@ -71,6 +71,16 @@ if(WITH_CYCLES_STANDALONE)
target_link_libraries(cycles ${LIBRARIES})
cycles_target_link_libraries(cycles)
+ if(APPLE)
+ if(WITH_OPENCOLORIO)
+ set_property(TARGET cycles APPEND_STRING PROPERTY LINK_FLAGS " -framework IOKit")
+ endif()
+ if(WITH_OPENIMAGEDENOISE AND "${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64")
+ # OpenImageDenoise uses BNNS from the Accelerate framework.
+ set_property(TARGET cycles APPEND_STRING PROPERTY LINK_FLAGS " -framework Accelerate")
+ endif()
+ endif()
+
if(UNIX AND NOT APPLE)
set_target_properties(cycles PROPERTIES INSTALL_RPATH $ORIGIN/lib)
endif()
diff --git a/intern/cycles/blender/addon/engine.py b/intern/cycles/blender/addon/engine.py
index dfa696714fb..dc53c4db48f 100644
--- a/intern/cycles/blender/addon/engine.py
+++ b/intern/cycles/blender/addon/engine.py
@@ -19,16 +19,16 @@ from __future__ import annotations
def _is_using_buggy_driver():
- import bgl
+ import gpu
# We need to be conservative here because in multi-GPU systems display card
# might be quite old, but others one might be just good.
#
# So We shouldn't disable possible good dedicated cards just because display
# card seems weak. And instead we only blacklist configurations which are
# proven to cause problems.
- if bgl.glGetString(bgl.GL_VENDOR) == "ATI Technologies Inc.":
+ if gpu.platform.vendor_get() == "ATI Technologies Inc.":
import re
- version = bgl.glGetString(bgl.GL_VERSION)
+ version = gpu.platform.version_get()
if version.endswith("Compatibility Profile Context"):
# Old HD 4xxx and 5xxx series drivers did not have driver version
# in the version string, but those cards do not quite work and
@@ -132,7 +132,7 @@ def init():
_workaround_buggy_drivers()
path = os.path.dirname(__file__)
- user_path = os.path.dirname(os.path.abspath(bpy.utils.user_resource('CONFIG', '')))
+ user_path = os.path.dirname(os.path.abspath(bpy.utils.user_resource('CONFIG', path='')))
_cycles.init(path, user_path, bpy.app.background)
_parse_command_line()
diff --git a/intern/cycles/blender/addon/presets.py b/intern/cycles/blender/addon/presets.py
index 04b18b38927..bf33e5dc010 100644
--- a/intern/cycles/blender/addon/presets.py
+++ b/intern/cycles/blender/addon/presets.py
@@ -41,6 +41,9 @@ class AddPresetIntegrator(AddPresetBase, Operator):
"cycles.caustics_reflective",
"cycles.caustics_refractive",
"cycles.blur_glossy"
+ "cycles.use_fast_gi"
+ "cycles.ao_bounces"
+ "cycles.ao_bounces_render"
]
preset_subdir = "cycles/integrator"
diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py
index dc4437bdc52..cda1355eb2d 100644
--- a/intern/cycles/blender/addon/properties.py
+++ b/intern/cycles/blender/addon/properties.py
@@ -801,17 +801,22 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
items=enum_texture_limit
)
+ use_fast_gi: BoolProperty(
+ name="Fast GI Approximation",
+ description="Approximate diffuse indirect light with background tinted ambient occlusion. This provides fast alternative to full global illumination, for interactive viewport rendering or final renders with reduced quality",
+ default=False,
+ )
ao_bounces: IntProperty(
name="AO Bounces",
- default=0,
- description="Approximate indirect light with background tinted ambient occlusion at the specified bounce, 0 disables this feature",
+ default=1,
+ description="After this number of light bounces, use approximate global illumination. 0 disables this feature",
min=0, max=1024,
)
ao_bounces_render: IntProperty(
name="AO Bounces Render",
- default=0,
- description="Approximate indirect light with background tinted ambient occlusion at the specified bounce, 0 disables this feature",
+ default=1,
+ description="After this number of light bounces, use approximate global illumination. 0 disables this feature",
min=0, max=1024,
)
diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py
index c9b4dc25cf2..ce93bd96bd5 100644
--- a/intern/cycles/blender/addon/ui.py
+++ b/intern/cycles/blender/addon/ui.py
@@ -526,6 +526,37 @@ class CYCLES_RENDER_PT_light_paths_caustics(CyclesButtonsPanel, Panel):
col.prop(cscene, "caustics_refractive", text="Refractive")
+class CYCLES_RENDER_PT_light_paths_fast_gi(CyclesButtonsPanel, Panel):
+ bl_label = "Fast GI Approximation"
+ bl_options = {'DEFAULT_CLOSED'}
+ bl_parent_id = "CYCLES_RENDER_PT_light_paths"
+
+ def draw_header(self, context):
+ scene = context.scene
+ cscene = scene.cycles
+
+ self.layout.prop(cscene, "use_fast_gi", text="")
+
+ def draw(self, context):
+ scene = context.scene
+ cscene = scene.cycles
+ world = scene.world
+
+ layout = self.layout
+ layout.use_property_split = True
+ layout.use_property_decorate = False
+
+ col = layout.column(align=True)
+ col.prop(cscene, "ao_bounces", text="Viewport Bounces")
+ col.prop(cscene, "ao_bounces_render", text="Render Bounces")
+
+ if world:
+ light = world.light_settings
+ col = layout.column(align=True)
+ col.prop(light, "ao_factor", text="AO Factor")
+ col.prop(light, "distance", text="AO Distance")
+
+
class CYCLES_RENDER_PT_motion_blur(CyclesButtonsPanel, Panel):
bl_label = "Motion Blur"
bl_options = {'DEFAULT_CLOSED'}
@@ -694,7 +725,7 @@ class CYCLES_RENDER_PT_performance_tiles(CyclesButtonsPanel, Panel):
col.prop(cscene, "tile_order", text="Order")
sub = col.column()
- sub.active = not rd.use_save_buffers
+ sub.active = not rd.use_save_buffers and not cscene.use_adaptive_sampling
sub.prop(cscene, "use_progressive_refine")
@@ -746,7 +777,7 @@ class CYCLES_RENDER_PT_performance_final_render(CyclesButtonsPanel, Panel):
col = layout.column()
col.prop(rd, "use_save_buffers")
- col.prop(rd, "use_persistent_data", text="Persistent Images")
+ col.prop(rd, "use_persistent_data", text="Persistent Data")
class CYCLES_RENDER_PT_performance_viewport(CyclesButtonsPanel, Panel):
@@ -1409,15 +1440,15 @@ class CYCLES_LIGHT_PT_nodes(CyclesButtonsPanel, Panel):
panel_node_draw(layout, light, 'OUTPUT_LIGHT', 'Surface')
-class CYCLES_LIGHT_PT_spot(CyclesButtonsPanel, Panel):
- bl_label = "Spot Shape"
+class CYCLES_LIGHT_PT_beam_shape(CyclesButtonsPanel, Panel):
+ bl_label = "Beam Shape"
bl_parent_id = "CYCLES_LIGHT_PT_light"
bl_context = "data"
@classmethod
def poll(cls, context):
- light = context.light
- return (light and light.type == 'SPOT') and CyclesButtonsPanel.poll(context)
+ if context.light.type in {'SPOT', 'AREA'}:
+ return context.light and CyclesButtonsPanel.poll(context)
def draw(self, context):
layout = self.layout
@@ -1425,9 +1456,12 @@ class CYCLES_LIGHT_PT_spot(CyclesButtonsPanel, Panel):
layout.use_property_split = True
col = layout.column()
- col.prop(light, "spot_size", text="Size")
- col.prop(light, "spot_blend", text="Blend", slider=True)
- col.prop(light, "show_cone")
+ if light.type == 'SPOT':
+ col.prop(light, "spot_size", text="Spot Size")
+ col.prop(light, "spot_blend", text="Blend", slider=True)
+ col.prop(light, "show_cone")
+ elif light.type == 'AREA':
+ col.prop(light, "spread", text="Spread")
class CYCLES_WORLD_PT_preview(CyclesButtonsPanel, Panel):
@@ -2038,7 +2072,6 @@ class CYCLES_RENDER_PT_simplify_viewport(CyclesButtonsPanel, Panel):
col.prop(rd, "simplify_subdivision", text="Max Subdivision")
col.prop(rd, "simplify_child_particles", text="Child Particles")
col.prop(cscene, "texture_limit", text="Texture Limit")
- col.prop(cscene, "ao_bounces", text="AO Bounces")
col.prop(rd, "simplify_volumes", text="Volume Resolution")
@@ -2064,7 +2097,6 @@ class CYCLES_RENDER_PT_simplify_render(CyclesButtonsPanel, Panel):
col.prop(rd, "simplify_subdivision_render", text="Max Subdivision")
col.prop(rd, "simplify_child_particles_render", text="Child Particles")
col.prop(cscene, "texture_limit_render", text="Texture Limit")
- col.prop(cscene, "ao_bounces_render", text="AO Bounces")
class CYCLES_RENDER_PT_simplify_culling(CyclesButtonsPanel, Panel):
@@ -2242,6 +2274,7 @@ classes = (
CYCLES_RENDER_PT_light_paths_max_bounces,
CYCLES_RENDER_PT_light_paths_clamping,
CYCLES_RENDER_PT_light_paths_caustics,
+ CYCLES_RENDER_PT_light_paths_fast_gi,
CYCLES_RENDER_PT_volumes,
CYCLES_RENDER_PT_subdivision,
CYCLES_RENDER_PT_hair,
@@ -2284,7 +2317,7 @@ classes = (
CYCLES_LIGHT_PT_preview,
CYCLES_LIGHT_PT_light,
CYCLES_LIGHT_PT_nodes,
- CYCLES_LIGHT_PT_spot,
+ CYCLES_LIGHT_PT_beam_shape,
CYCLES_WORLD_PT_preview,
CYCLES_WORLD_PT_surface,
CYCLES_WORLD_PT_volume,
@@ -2314,7 +2347,7 @@ classes = (
node_panel(CYCLES_WORLD_PT_settings_surface),
node_panel(CYCLES_WORLD_PT_settings_volume),
node_panel(CYCLES_LIGHT_PT_light),
- node_panel(CYCLES_LIGHT_PT_spot),
+ node_panel(CYCLES_LIGHT_PT_beam_shape)
)
diff --git a/intern/cycles/blender/addon/version_update.py b/intern/cycles/blender/addon/version_update.py
index aeecc265399..827f84b9873 100644
--- a/intern/cycles/blender/addon/version_update.py
+++ b/intern/cycles/blender/addon/version_update.py
@@ -217,6 +217,18 @@ def do_versions(self):
baov.name = caov.get("name", "AOV")
baov.type = "COLOR" if caov.get("type", 1) == 1 else "VALUE"
+ if version <= (2, 93, 16):
+ cscene = scene.cycles
+ ao_bounces = cscene.get("ao_bounces", 0)
+ ao_bounces_render = cscene.get("ao_bounces_render", 0)
+ if scene.render.use_simplify and (ao_bounces or ao_bounces_render):
+ cscene.use_fast_gi = True
+ cscene.ao_bounces = ao_bounces
+ cscene.ao_bounces_render = ao_bounces_render
+ else:
+ cscene.ao_bounces = 1
+ cscene.ao_bounces_render = 1
+
# Lamps
for light in bpy.data.lights:
if light.library not in libraries:
diff --git a/intern/cycles/blender/blender_camera.cpp b/intern/cycles/blender/blender_camera.cpp
index b31841801d8..6954c5c2f26 100644
--- a/intern/cycles/blender/blender_camera.cpp
+++ b/intern/cycles/blender/blender_camera.cpp
@@ -83,6 +83,8 @@ struct BlenderCamera {
BoundBox2D pano_viewplane;
BoundBox2D viewport_camera_border;
+ float passepartout_alpha;
+
Transform matrix;
float offscreen_dicing_scale;
@@ -125,6 +127,7 @@ static void blender_camera_init(BlenderCamera *bcam, BL::RenderSettings &b_rende
bcam->pano_viewplane.top = 1.0f;
bcam->viewport_camera_border.right = 1.0f;
bcam->viewport_camera_border.top = 1.0f;
+ bcam->passepartout_alpha = 0.5f;
bcam->offscreen_dicing_scale = 1.0f;
bcam->matrix = transform_identity();
@@ -212,6 +215,8 @@ static void blender_camera_from_object(BlenderCamera *bcam,
bcam->lens = b_camera.lens();
+ bcam->passepartout_alpha = b_camera.show_passepartout() ? b_camera.passepartout_alpha() : 0.0f;
+
if (b_camera.dof().use_dof()) {
/* allow f/stop number to change aperture_size but still
* give manual control over aperture radius */
@@ -834,15 +839,19 @@ static void blender_camera_border(BlenderCamera *bcam,
full_border,
&bcam->viewport_camera_border);
- if (!b_render.use_border()) {
+ if (b_render.use_border()) {
+ bcam->border.left = b_render.border_min_x();
+ bcam->border.right = b_render.border_max_x();
+ bcam->border.bottom = b_render.border_min_y();
+ bcam->border.top = b_render.border_max_y();
+ }
+ else if (bcam->passepartout_alpha == 1.0f) {
+ bcam->border = full_border;
+ }
+ else {
return;
}
- bcam->border.left = b_render.border_min_x();
- bcam->border.right = b_render.border_max_x();
- bcam->border.bottom = b_render.border_min_y();
- bcam->border.top = b_render.border_max_y();
-
/* Determine viewport subset matching camera border. */
blender_camera_border_subset(b_engine,
b_render,
@@ -885,8 +894,7 @@ void BlenderSync::sync_view(BL::SpaceView3D &b_v3d,
}
}
-BufferParams BlenderSync::get_buffer_params(BL::RenderSettings &b_render,
- BL::SpaceView3D &b_v3d,
+BufferParams BlenderSync::get_buffer_params(BL::SpaceView3D &b_v3d,
BL::RegionView3D &b_rv3d,
Camera *cam,
int width,
@@ -902,7 +910,8 @@ BufferParams BlenderSync::get_buffer_params(BL::RenderSettings &b_render,
if (b_v3d && b_rv3d && b_rv3d.view_perspective() != BL::RegionView3D::view_perspective_CAMERA)
use_border = b_v3d.use_render_border();
else
- use_border = b_render.use_border();
+ /* the camera can always have a passepartout */
+ use_border = true;
if (use_border) {
/* border render */
diff --git a/intern/cycles/blender/blender_image.cpp b/intern/cycles/blender/blender_image.cpp
index 459dc1779fb..3a9d159e461 100644
--- a/intern/cycles/blender/blender_image.cpp
+++ b/intern/cycles/blender/blender_image.cpp
@@ -29,7 +29,7 @@ BlenderImageLoader::BlenderImageLoader(BL::Image b_image, int frame)
{
}
-bool BlenderImageLoader::load_metadata(ImageMetaData &metadata)
+bool BlenderImageLoader::load_metadata(const ImageDeviceFeatures &, ImageMetaData &metadata)
{
metadata.width = b_image.size()[0];
metadata.height = b_image.size()[1];
@@ -171,7 +171,7 @@ BlenderPointDensityLoader::BlenderPointDensityLoader(BL::Depsgraph b_depsgraph,
{
}
-bool BlenderPointDensityLoader::load_metadata(ImageMetaData &metadata)
+bool BlenderPointDensityLoader::load_metadata(const ImageDeviceFeatures &, ImageMetaData &metadata)
{
metadata.channels = 4;
metadata.width = b_node.resolution();
diff --git a/intern/cycles/blender/blender_image.h b/intern/cycles/blender/blender_image.h
index b58a159a6ba..fddbbfd9c37 100644
--- a/intern/cycles/blender/blender_image.h
+++ b/intern/cycles/blender/blender_image.h
@@ -27,7 +27,7 @@ class BlenderImageLoader : public ImageLoader {
public:
BlenderImageLoader(BL::Image b_image, int frame);
- bool load_metadata(ImageMetaData &metadata) override;
+ bool load_metadata(const ImageDeviceFeatures &features, ImageMetaData &metadata) override;
bool load_pixels(const ImageMetaData &metadata,
void *pixels,
const size_t pixels_size,
@@ -44,7 +44,7 @@ class BlenderPointDensityLoader : public ImageLoader {
public:
BlenderPointDensityLoader(BL::Depsgraph depsgraph, BL::ShaderNodeTexPointDensity b_node);
- bool load_metadata(ImageMetaData &metadata) override;
+ bool load_metadata(const ImageDeviceFeatures &features, ImageMetaData &metadata) override;
bool load_pixels(const ImageMetaData &metadata,
void *pixels,
const size_t pixels_size,
diff --git a/intern/cycles/blender/blender_light.cpp b/intern/cycles/blender/blender_light.cpp
index ff4ecc5a3f9..ae353b32633 100644
--- a/intern/cycles/blender/blender_light.cpp
+++ b/intern/cycles/blender/blender_light.cpp
@@ -34,12 +34,17 @@ void BlenderSync::sync_light(BL::Object &b_parent,
bool *use_portal)
{
/* test if we need to sync */
- Light *light;
ObjectKey key(b_parent, persistent_id, b_ob_instance, false);
BL::Light b_light(b_ob.data());
+ Light *light = light_map.find(key);
+
+ /* Check if the transform was modified, in case a linked collection is moved we do not get a
+ * specific depsgraph update (T88515). This also mimics the behavior for Objects. */
+ const bool tfm_updated = (light && light->get_tfm() != tfm);
+
/* Update if either object or light data changed. */
- if (!light_map.add_or_update(&light, b_ob, b_parent, key)) {
+ if (!tfm_updated && !light_map.add_or_update(&light, b_ob, b_parent, key)) {
Shader *shader;
if (!shader_map.add_or_update(&shader, b_light)) {
if (light->get_is_portal())
@@ -82,6 +87,7 @@ void BlenderSync::sync_light(BL::Object &b_parent,
light->set_axisu(transform_get_column(&tfm, 0));
light->set_axisv(transform_get_column(&tfm, 1));
light->set_sizeu(b_area_light.size());
+ light->set_spread(b_area_light.spread());
switch (b_area_light.shape()) {
case BL::AreaLight::shape_SQUARE:
light->set_sizev(light->get_sizeu());
diff --git a/intern/cycles/blender/blender_object.cpp b/intern/cycles/blender/blender_object.cpp
index 54128cf82fc..cb84013c551 100644
--- a/intern/cycles/blender/blender_object.cpp
+++ b/intern/cycles/blender/blender_object.cpp
@@ -96,7 +96,49 @@ bool BlenderSync::object_is_light(BL::Object &b_ob)
return (b_ob_data && b_ob_data.is_a(&RNA_Light));
}
-/* Object */
+void BlenderSync::sync_object_motion_init(BL::Object &b_parent, BL::Object &b_ob, Object *object)
+{
+ /* Initialize motion blur for object, detecting if it's enabled and creating motion
+ * steps array if so. */
+ array<Transform> motion;
+ object->set_motion(motion);
+
+ Scene::MotionType need_motion = scene->need_motion();
+ if (need_motion == Scene::MOTION_NONE || !object->get_geometry()) {
+ return;
+ }
+
+ Geometry *geom = object->get_geometry();
+ geom->set_use_motion_blur(false);
+ geom->set_motion_steps(0);
+
+ uint motion_steps;
+
+ if (need_motion == Scene::MOTION_BLUR) {
+ motion_steps = object_motion_steps(b_parent, b_ob, Object::MAX_MOTION_STEPS);
+ geom->set_motion_steps(motion_steps);
+ if (motion_steps && object_use_deform_motion(b_parent, b_ob)) {
+ geom->set_use_motion_blur(true);
+ }
+ }
+ else {
+ motion_steps = 3;
+ geom->set_motion_steps(motion_steps);
+ }
+
+ motion.resize(motion_steps, transform_empty());
+
+ if (motion_steps) {
+ motion[motion_steps / 2] = object->get_tfm();
+
+ /* update motion socket before trying to access object->motion_time */
+ object->set_motion(motion);
+
+ for (size_t step = 0; step < motion_steps; step++) {
+ motion_times.insert(object->motion_time(step));
+ }
+ }
+}
Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph,
BL::ViewLayer &b_view_layer,
@@ -219,10 +261,8 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph,
}
/* test if we need to sync */
- bool object_updated = false;
-
- if (object_map.add_or_update(&object, b_ob, b_parent, key))
- object_updated = true;
+ bool object_updated = object_map.add_or_update(&object, b_ob, b_parent, key) ||
+ (tfm != object->get_tfm());
/* mesh sync */
/* b_ob is owned by the iterator and will go out of scope at the end of the block.
@@ -271,49 +311,11 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph,
* transform comparison should not be needed, but duplis don't work perfect
* in the depsgraph and may not signal changes, so this is a workaround */
if (object->is_modified() || object_updated ||
- (object->get_geometry() && object->get_geometry()->is_modified()) ||
- tfm != object->get_tfm()) {
+ (object->get_geometry() && object->get_geometry()->is_modified())) {
object->name = b_ob.name().c_str();
object->set_pass_id(b_ob.pass_index());
object->set_color(get_float3(b_ob.color()));
object->set_tfm(tfm);
- array<Transform> motion;
- object->set_motion(motion);
-
- /* motion blur */
- Scene::MotionType need_motion = scene->need_motion();
- if (need_motion != Scene::MOTION_NONE && object->get_geometry()) {
- Geometry *geom = object->get_geometry();
- geom->set_use_motion_blur(false);
- geom->set_motion_steps(0);
-
- uint motion_steps;
-
- if (need_motion == Scene::MOTION_BLUR) {
- motion_steps = object_motion_steps(b_parent, b_ob, Object::MAX_MOTION_STEPS);
- geom->set_motion_steps(motion_steps);
- if (motion_steps && object_use_deform_motion(b_parent, b_ob)) {
- geom->set_use_motion_blur(true);
- }
- }
- else {
- motion_steps = 3;
- geom->set_motion_steps(motion_steps);
- }
-
- motion.resize(motion_steps, transform_empty());
-
- if (motion_steps) {
- motion[motion_steps / 2] = tfm;
-
- /* update motion socket before trying to access object->motion_time */
- object->set_motion(motion);
-
- for (size_t step = 0; step < motion_steps; step++) {
- motion_times.insert(object->motion_time(step));
- }
- }
- }
/* dupli texture coordinates and random_id */
if (is_instance) {
@@ -331,6 +333,8 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph,
object->tag_update(scene);
}
+ sync_object_motion_init(b_parent, b_ob, object);
+
if (is_instance) {
/* Sync possible particle data. */
sync_dupli_particle(b_parent, b_instance, object);
@@ -560,10 +564,12 @@ void BlenderSync::sync_objects(BL::Depsgraph &b_depsgraph,
if (!cancel && !motion) {
sync_background_light(b_v3d, use_portal);
- /* handle removed data and modified pointers */
+ /* Handle removed data and modified pointers, as this may free memory, delete Nodes in the
+ * right order to ensure that dependent data is freed after their users. Objects should be
+ * freed before particle systems and geometries. */
light_map.post_sync();
- geometry_map.post_sync();
object_map.post_sync();
+ geometry_map.post_sync();
particle_system_map.post_sync();
}
@@ -611,7 +617,7 @@ void BlenderSync::sync_motion(BL::RenderSettings &b_render,
if (b_cam) {
sync_camera_motion(b_render, b_cam, width, height, 0.0f);
}
- sync_objects(b_depsgraph, b_v3d, 0.0f);
+ sync_objects(b_depsgraph, b_v3d);
}
/* Insert motion times from camera. Motion times from other objects
diff --git a/intern/cycles/blender/blender_python.cpp b/intern/cycles/blender/blender_python.cpp
index 0daad310543..785ca6b4e01 100644
--- a/intern/cycles/blender/blender_python.cpp
+++ b/intern/cycles/blender/blender_python.cpp
@@ -35,6 +35,7 @@
#include "util/util_path.h"
#include "util/util_string.h"
#include "util/util_task.h"
+#include "util/util_tbb.h"
#include "util/util_types.h"
#ifdef WITH_OSL
@@ -288,9 +289,11 @@ static PyObject *render_func(PyObject * /*self*/, PyObject *args)
RNA_pointer_create(NULL, &RNA_Depsgraph, (ID *)PyLong_AsVoidPtr(pydepsgraph), &depsgraphptr);
BL::Depsgraph b_depsgraph(depsgraphptr);
+ /* Allow Blender to execute other Python scripts, and isolate TBB tasks so we
+ * don't get deadlocks with Blender threads accessing shared data like images. */
python_thread_state_save(&session->python_thread_state);
- session->render(b_depsgraph);
+ tbb::this_task_arena::isolate([&] { session->render(b_depsgraph); });
python_thread_state_restore(&session->python_thread_state);
@@ -327,7 +330,8 @@ static PyObject *bake_func(PyObject * /*self*/, PyObject *args)
python_thread_state_save(&session->python_thread_state);
- session->bake(b_depsgraph, b_object, pass_type, pass_filter, width, height);
+ tbb::this_task_arena::isolate(
+ [&] { session->bake(b_depsgraph, b_object, pass_type, pass_filter, width, height); });
python_thread_state_restore(&session->python_thread_state);
@@ -373,7 +377,7 @@ static PyObject *reset_func(PyObject * /*self*/, PyObject *args)
python_thread_state_save(&session->python_thread_state);
- session->reset_session(b_data, b_depsgraph);
+ tbb::this_task_arena::isolate([&] { session->reset_session(b_data, b_depsgraph); });
python_thread_state_restore(&session->python_thread_state);
@@ -395,7 +399,7 @@ static PyObject *sync_func(PyObject * /*self*/, PyObject *args)
python_thread_state_save(&session->python_thread_state);
- session->synchronize(b_depsgraph);
+ tbb::this_task_arena::isolate([&] { session->synchronize(b_depsgraph); });
python_thread_state_restore(&session->python_thread_state);
diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp
index ae13310789e..29de886e4ff 100644
--- a/intern/cycles/blender/blender_session.cpp
+++ b/intern/cycles/blender/blender_session.cpp
@@ -143,12 +143,6 @@ void BlenderSession::create_session()
session->scene = scene;
- /* There is no single depsgraph to use for the entire render.
- * So we need to handle this differently.
- *
- * We could loop over the final render result render layers in pipeline and keep Cycles unaware
- * of multiple layers, or perhaps move syncing further down in the pipeline.
- */
/* create sync */
sync = new BlenderSync(b_engine, b_data, b_scene, scene, !background, session->progress);
BL::Object b_camera_override(b_engine.camera_override());
@@ -161,7 +155,7 @@ void BlenderSession::create_session()
/* set buffer parameters */
BufferParams buffer_params = BlenderSync::get_buffer_params(
- b_render, b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use);
+ b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use);
session->reset(buffer_params, session_params.samples);
b_engine.use_highlight_tiles(session_params.progressive_refine == false);
@@ -213,7 +207,7 @@ void BlenderSession::reset_session(BL::BlendData &b_data, BL::Depsgraph &b_depsg
SceneParams scene_params = BlenderSync::get_scene_params(b_scene, background);
if (scene->params.modified(scene_params) || session->params.modified(session_params) ||
- !scene_params.persistent_data) {
+ !this->b_render.use_persistent_data()) {
/* if scene or session parameters changed, it's easier to simply re-create
* them rather than trying to distinguish which settings need to be updated
*/
@@ -225,7 +219,6 @@ void BlenderSession::reset_session(BL::BlendData &b_data, BL::Depsgraph &b_depsg
}
session->progress.reset();
- scene->reset();
session->tile_manager.set_tile_order(session_params.tile_order);
@@ -234,17 +227,22 @@ void BlenderSession::reset_session(BL::BlendData &b_data, BL::Depsgraph &b_depsg
*/
session->stats.mem_peak = session->stats.mem_used;
- /* There is no single depsgraph to use for the entire render.
- * See note on create_session().
- */
- /* sync object should be re-created */
- delete sync;
- sync = new BlenderSync(b_engine, b_data, b_scene, scene, !background, session->progress);
+ if (is_new_session) {
+ /* Sync object should be re-created for new scene. */
+ delete sync;
+ sync = new BlenderSync(b_engine, b_data, b_scene, scene, !background, session->progress);
+ }
+ else {
+ /* Sync recalculations to do just the required updates. */
+ sync->sync_recalc(b_depsgraph, b_v3d);
+ }
+
+ BL::Object b_camera_override(b_engine.camera_override());
+ sync->sync_camera(b_render, b_camera_override, width, height, "");
BL::SpaceView3D b_null_space_view3d(PointerRNA_NULL);
BL::RegionView3D b_null_region_view3d(PointerRNA_NULL);
- BufferParams buffer_params = BlenderSync::get_buffer_params(b_render,
- b_null_space_view3d,
+ BufferParams buffer_params = BlenderSync::get_buffer_params(b_null_space_view3d,
b_null_region_view3d,
scene->camera,
width,
@@ -487,7 +485,7 @@ void BlenderSession::render(BL::Depsgraph &b_depsgraph_)
SessionParams session_params = BlenderSync::get_session_params(
b_engine, b_userpref, b_scene, background, b_view_layer);
BufferParams buffer_params = BlenderSync::get_buffer_params(
- b_render, b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use);
+ b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use);
/* temporary render result to find needed passes and views */
BL::RenderResult b_rr = begin_render_result(
@@ -502,7 +500,7 @@ void BlenderSession::render(BL::Depsgraph &b_depsgraph_)
/* Compute render passes and film settings. */
vector<Pass> passes = sync->sync_render_passes(
- b_rlay, b_view_layer, session_params.adaptive_sampling, session_params.denoising);
+ b_scene, b_rlay, b_view_layer, session_params.adaptive_sampling, session_params.denoising);
/* Set buffer params, using film settings from sync_render_passes. */
buffer_params.passes = passes;
@@ -598,18 +596,6 @@ void BlenderSession::render(BL::Depsgraph &b_depsgraph_)
/* clear callback */
session->write_render_tile_cb = function_null;
session->update_render_tile_cb = function_null;
-
- /* TODO: find a way to clear this data for persistent data render */
-#if 0
- /* free all memory used (host and device), so we wouldn't leave render
- * engine with extra memory allocated
- */
-
- session->device_free();
-
- delete sync;
- sync = NULL;
-#endif
}
static int bake_pass_filter_get(const int pass_filter)
@@ -823,7 +809,7 @@ void BlenderSession::synchronize(BL::Depsgraph &b_depsgraph_)
/* get buffer parameters */
BufferParams buffer_params = BlenderSync::get_buffer_params(
- b_render, b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use);
+ b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use);
if (!buffer_params.denoising_data_pass) {
session_params.denoising.use = false;
@@ -902,7 +888,7 @@ bool BlenderSession::draw(int w, int h)
SessionParams session_params = BlenderSync::get_session_params(
b_engine, b_userpref, b_scene, background);
BufferParams buffer_params = BlenderSync::get_buffer_params(
- b_render, b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use);
+ b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use);
bool session_pause = BlenderSync::get_session_pause(b_scene, background);
if (session_pause == false) {
@@ -920,7 +906,7 @@ bool BlenderSession::draw(int w, int h)
/* draw */
BufferParams buffer_params = BlenderSync::get_buffer_params(
- b_render, b_v3d, b_rv3d, scene->camera, width, height, session->params.denoising.use);
+ b_v3d, b_rv3d, scene->camera, width, height, session->params.denoising.use);
DeviceDrawParams draw_params;
if (session->params.display_buffer_linear) {
diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp
index 72328333732..7f129310736 100644
--- a/intern/cycles/blender/blender_shader.cpp
+++ b/intern/cycles/blender/blender_shader.cpp
@@ -1373,7 +1373,7 @@ void BlenderSync::sync_world(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d,
BlenderViewportParameters new_viewport_parameters(b_v3d);
if (world_recalc || update_all || b_world.ptr.data != world_map ||
- viewport_parameters.modified(new_viewport_parameters)) {
+ viewport_parameters.shader_modified(new_viewport_parameters)) {
Shader *shader = scene->default_background;
ShaderGraph *graph = new ShaderGraph();
@@ -1501,8 +1501,8 @@ void BlenderSync::sync_world(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d,
background->set_transparent_roughness_threshold(0.0f);
}
- background->set_use_shader(view_layer.use_background_shader |
- viewport_parameters.custom_viewport_parameters());
+ background->set_use_shader(view_layer.use_background_shader ||
+ viewport_parameters.use_custom_shader());
background->set_use_ao(background->get_use_ao() && view_layer.use_background_ao);
background->tag_update(scene);
@@ -1553,13 +1553,9 @@ void BlenderSync::sync_lights(BL::Depsgraph &b_depsgraph, bool update_all)
void BlenderSync::sync_shaders(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d)
{
/* for auto refresh images */
- bool auto_refresh_update = false;
-
- if (preview) {
- ImageManager *image_manager = scene->image_manager;
- int frame = b_scene.frame_current();
- auto_refresh_update = image_manager->set_animation_frame_update(frame);
- }
+ ImageManager *image_manager = scene->image_manager;
+ const int frame = b_scene.frame_current();
+ const bool auto_refresh_update = image_manager->set_animation_frame_update(frame);
shader_map.pre_sync();
diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/blender_sync.cpp
index 0e61f4f2615..82b3abd4432 100644
--- a/intern/cycles/blender/blender_sync.cpp
+++ b/intern/cycles/blender/blender_sync.cpp
@@ -69,7 +69,8 @@ BlenderSync::BlenderSync(BL::RenderEngine &b_engine,
experimental(false),
dicing_rate(1.0f),
max_subdivisions(12),
- progress(progress)
+ progress(progress),
+ has_updates_(true)
{
PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
dicing_rate = preview ? RNA_float_get(&cscene, "preview_dicing_rate") :
@@ -84,7 +85,9 @@ BlenderSync::~BlenderSync()
void BlenderSync::reset(BL::BlendData &b_data, BL::Scene &b_scene)
{
/* Update data and scene pointers in case they change in session reset,
- * for example after undo. */
+ * for example after undo.
+ * Note that we do not modify the `has_updates_` flag here because the sync
+ * reset is also used during viewport navigation. */
this->b_data = b_data;
this->b_scene = b_scene;
}
@@ -117,6 +120,8 @@ void BlenderSync::sync_recalc(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d
}
if (dicing_prop_changed) {
+ has_updates_ = true;
+
for (const pair<const GeometryKey, Geometry *> &iter : geometry_map.key_to_scene_data()) {
Geometry *geom = iter.second;
if (geom->is_mesh()) {
@@ -133,6 +138,12 @@ void BlenderSync::sync_recalc(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d
/* Iterate over all IDs in this depsgraph. */
for (BL::DepsgraphUpdate &b_update : b_depsgraph.updates) {
+ /* TODO(sergey): Can do more selective filter here. For example, ignore changes made to
+ * screen datablock. Note that sync_data() needs to be called after object deletion, and
+ * currently this is ensured by the scene ID tagged for update, which sets the `has_updates_`
+ * flag. */
+ has_updates_ = true;
+
BL::ID b_id(b_update.id());
/* Material */
@@ -211,9 +222,15 @@ void BlenderSync::sync_recalc(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d
}
}
- BlenderViewportParameters new_viewport_parameters(b_v3d);
- if (viewport_parameters.modified(new_viewport_parameters)) {
- world_recalc = true;
+ if (b_v3d) {
+ BlenderViewportParameters new_viewport_parameters(b_v3d);
+
+ if (viewport_parameters.shader_modified(new_viewport_parameters)) {
+ world_recalc = true;
+ has_updates_ = true;
+ }
+
+ has_updates_ |= viewport_parameters.modified(new_viewport_parameters);
}
}
@@ -225,11 +242,15 @@ void BlenderSync::sync_data(BL::RenderSettings &b_render,
int height,
void **python_thread_state)
{
+ if (!has_updates_) {
+ return;
+ }
+
scoped_timer timer;
BL::ViewLayer b_view_layer = b_depsgraph.view_layer_eval();
- sync_view_layer(b_v3d, b_view_layer);
+ sync_view_layer(b_view_layer);
sync_integrator();
sync_film(b_v3d);
sync_shaders(b_depsgraph, b_v3d);
@@ -252,6 +273,8 @@ void BlenderSync::sync_data(BL::RenderSettings &b_render,
free_data_after_sync(b_depsgraph);
VLOG(1) << "Total time spent synchronizing data: " << timer.get_time();
+
+ has_updates_ = false;
}
/* Integrator */
@@ -358,7 +381,7 @@ void BlenderSync::sync_integrator()
integrator->set_adaptive_min_samples(adaptive_min_samples);
- if (b_scene.render().use_simplify()) {
+ if (get_boolean(cscene, "use_fast_gi")) {
if (preview) {
integrator->set_ao_bounces(get_int(cscene, "ao_bounces"));
}
@@ -422,7 +445,7 @@ void BlenderSync::sync_film(BL::SpaceView3D &b_v3d)
/* Render Layer */
-void BlenderSync::sync_view_layer(BL::SpaceView3D & /*b_v3d*/, BL::ViewLayer &b_view_layer)
+void BlenderSync::sync_view_layer(BL::ViewLayer &b_view_layer)
{
view_layer.name = b_view_layer.name();
@@ -567,7 +590,8 @@ int BlenderSync::get_denoising_pass(BL::RenderPass &b_pass)
return -1;
}
-vector<Pass> BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay,
+vector<Pass> BlenderSync::sync_render_passes(BL::Scene &b_scene,
+ BL::RenderLayer &b_rlay,
BL::ViewLayer &b_view_layer,
bool adaptive_sampling,
const DenoiseParams &denoising)
@@ -578,7 +602,7 @@ vector<Pass> BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay,
for (BL::RenderPass &b_pass : b_rlay.passes) {
PassType pass_type = get_pass_type(b_pass);
- if (pass_type == PASS_MOTION && scene->integrator->get_motion_blur())
+ if (pass_type == PASS_MOTION && b_scene.render().use_motion_blur())
continue;
if (pass_type != PASS_NONE)
Pass::add(pass_type, passes, b_pass.name().c_str());
@@ -736,12 +760,18 @@ void BlenderSync::free_data_after_sync(BL::Depsgraph &b_depsgraph)
* caches to be releases from blender side in order to reduce peak memory
* footprint during synchronization process.
*/
+
const bool is_interface_locked = b_engine.render() && b_engine.render().use_lock_interface();
- const bool can_free_caches = (BlenderSession::headless || is_interface_locked) &&
- /* Baking re-uses the depsgraph multiple times, clearing crashes
- * reading un-evaluated mesh data which isn't aligned with the
- * geometry we're baking, see T71012. */
- !scene->bake_manager->get_baking();
+ const bool is_persistent_data = b_engine.render() && b_engine.render().use_persistent_data();
+ const bool can_free_caches =
+ (BlenderSession::headless || is_interface_locked) &&
+ /* Baking re-uses the depsgraph multiple times, clearing crashes
+ * reading un-evaluated mesh data which isn't aligned with the
+ * geometry we're baking, see T71012. */
+ !scene->bake_manager->get_baking() &&
+ /* Persistent data must main caches for performance and correctness. */
+ !is_persistent_data;
+
if (!can_free_caches) {
return;
}
@@ -757,7 +787,6 @@ void BlenderSync::free_data_after_sync(BL::Depsgraph &b_depsgraph)
SceneParams BlenderSync::get_scene_params(BL::Scene &b_scene, bool background)
{
- BL::RenderSettings r = b_scene.render();
SceneParams params;
PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
const bool shadingsystem = RNA_boolean_get(&cscene, "shading_system");
@@ -781,11 +810,6 @@ SceneParams BlenderSync::get_scene_params(BL::Scene &b_scene, bool background)
params.hair_shape = (CurveShapeType)get_enum(
csscene, "shape", CURVE_NUM_SHAPE_TYPES, CURVE_THICK);
- if (background && params.shadingsystem != SHADINGSYSTEM_OSL)
- params.persistent_data = r.use_persistent_data();
- else
- params.persistent_data = false;
-
int texture_limit;
if (background) {
texture_limit = RNA_enum_get(&cscene, "texture_limit_render");
@@ -872,6 +896,9 @@ SessionParams BlenderSync::get_session_params(BL::RenderEngine &b_engine,
/* Clamp samples. */
params.samples = min(params.samples, Integrator::MAX_SAMPLES);
+ /* Adaptive sampling. */
+ params.adaptive_sampling = RNA_boolean_get(&cscene, "use_adaptive_sampling");
+
/* tiles */
const bool is_cpu = (params.device.type == DEVICE_CPU);
if (!is_cpu && !background) {
@@ -924,7 +951,7 @@ SessionParams BlenderSync::get_session_params(BL::RenderEngine &b_engine,
BL::RenderSettings b_r = b_scene.render();
params.progressive_refine = b_engine.is_preview() ||
get_boolean(cscene, "use_progressive_refine");
- if (b_r.use_save_buffers())
+ if (b_r.use_save_buffers() || params.adaptive_sampling)
params.progressive_refine = false;
if (background) {
@@ -960,8 +987,6 @@ SessionParams BlenderSync::get_session_params(BL::RenderEngine &b_engine,
params.use_profiling = params.device.has_profiling && !b_engine.is_preview() && background &&
BlenderSession::print_render_stats;
- params.adaptive_sampling = RNA_boolean_get(&cscene, "use_adaptive_sampling");
-
return params;
}
diff --git a/intern/cycles/blender/blender_sync.h b/intern/cycles/blender/blender_sync.h
index 1c43522a57e..1c98e529190 100644
--- a/intern/cycles/blender/blender_sync.h
+++ b/intern/cycles/blender/blender_sync.h
@@ -73,8 +73,9 @@ class BlenderSync {
int width,
int height,
void **python_thread_state);
- void sync_view_layer(BL::SpaceView3D &b_v3d, BL::ViewLayer &b_view_layer);
- vector<Pass> sync_render_passes(BL::RenderLayer &b_render_layer,
+ void sync_view_layer(BL::ViewLayer &b_view_layer);
+ vector<Pass> sync_render_passes(BL::Scene &b_scene,
+ BL::RenderLayer &b_render_layer,
BL::ViewLayer &b_view_layer,
bool adaptive_sampling,
const DenoiseParams &denoising);
@@ -103,8 +104,7 @@ class BlenderSync {
bool background,
BL::ViewLayer b_view_layer = BL::ViewLayer(PointerRNA_NULL));
static bool get_session_pause(BL::Scene &b_scene, bool background);
- static BufferParams get_buffer_params(BL::RenderSettings &b_render,
- BL::SpaceView3D &b_v3d,
+ static BufferParams get_buffer_params(BL::SpaceView3D &b_v3d,
BL::RegionView3D &b_rv3d,
Camera *cam,
int width,
@@ -149,6 +149,7 @@ class BlenderSync {
BlenderObjectCulling &culling,
bool *use_portal,
TaskPool *geom_task_pool);
+ void sync_object_motion_init(BL::Object &b_parent, BL::Object &b_ob, Object *object);
bool sync_object_attributes(BL::DepsgraphObjectInstance &b_instance, Object *object);
@@ -262,6 +263,12 @@ class BlenderSync {
} view_layer;
Progress &progress;
+
+ protected:
+ /* Indicates that `sync_recalc()` detected changes in the scene.
+ * If this flag is false then the data is considered to be up-to-date and will not be
+ * synchronized at all. */
+ bool has_updates_ = true;
};
CCL_NAMESPACE_END
diff --git a/intern/cycles/blender/blender_viewport.cpp b/intern/cycles/blender/blender_viewport.cpp
index 73ef5f94720..07408fee218 100644
--- a/intern/cycles/blender/blender_viewport.cpp
+++ b/intern/cycles/blender/blender_viewport.cpp
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
#include "blender_viewport.h"
#include "blender_util.h"
@@ -25,29 +26,39 @@ BlenderViewportParameters::BlenderViewportParameters()
studiolight_rotate_z(0.0f),
studiolight_intensity(1.0f),
studiolight_background_alpha(1.0f),
- studiolight_path(ustring())
+ display_pass(PASS_COMBINED)
{
}
BlenderViewportParameters::BlenderViewportParameters(BL::SpaceView3D &b_v3d)
: BlenderViewportParameters()
{
- /* We only copy the parameters if we are in look dev mode. otherwise
+ if (!b_v3d) {
+ return;
+ }
+
+ BL::View3DShading shading = b_v3d.shading();
+ PointerRNA cshading = RNA_pointer_get(&shading.ptr, "cycles");
+
+ /* We only copy the shading parameters if we are in look dev mode. otherwise
* defaults are being used. These defaults mimic normal render settings */
- if (b_v3d && b_v3d.shading().type() == BL::View3DShading::type_RENDERED) {
- use_scene_world = b_v3d.shading().use_scene_world_render();
- use_scene_lights = b_v3d.shading().use_scene_lights_render();
+ if (shading.type() == BL::View3DShading::type_RENDERED) {
+ use_scene_world = shading.use_scene_world_render();
+ use_scene_lights = shading.use_scene_lights_render();
+
if (!use_scene_world) {
- studiolight_rotate_z = b_v3d.shading().studiolight_rotate_z();
- studiolight_intensity = b_v3d.shading().studiolight_intensity();
- studiolight_background_alpha = b_v3d.shading().studiolight_background_alpha();
- studiolight_path = b_v3d.shading().selected_studio_light().path();
+ studiolight_rotate_z = shading.studiolight_rotate_z();
+ studiolight_intensity = shading.studiolight_intensity();
+ studiolight_background_alpha = shading.studiolight_background_alpha();
+ studiolight_path = shading.selected_studio_light().path();
}
}
+
+ /* Film. */
+ display_pass = (PassType)get_enum(cshading, "render_pass", -1, -1);
}
-/* Check if two instances are different. */
-const bool BlenderViewportParameters::modified(const BlenderViewportParameters &other) const
+bool BlenderViewportParameters::shader_modified(const BlenderViewportParameters &other) const
{
return use_scene_world != other.use_scene_world || use_scene_lights != other.use_scene_lights ||
studiolight_rotate_z != other.studiolight_rotate_z ||
@@ -56,26 +67,26 @@ const bool BlenderViewportParameters::modified(const BlenderViewportParameters &
studiolight_path != other.studiolight_path;
}
-const bool BlenderViewportParameters::custom_viewport_parameters() const
+bool BlenderViewportParameters::film_modified(const BlenderViewportParameters &other) const
{
- return !(use_scene_world && use_scene_lights);
+ return display_pass != other.display_pass;
}
-PassType BlenderViewportParameters::get_viewport_display_render_pass(BL::SpaceView3D &b_v3d)
+bool BlenderViewportParameters::modified(const BlenderViewportParameters &other) const
{
- PassType display_pass = PASS_NONE;
- if (b_v3d) {
- BL::View3DShading b_view3dshading = b_v3d.shading();
- PointerRNA cshading = RNA_pointer_get(&b_view3dshading.ptr, "cycles");
- display_pass = (PassType)get_enum(cshading, "render_pass", -1, -1);
- }
- return display_pass;
+ return shader_modified(other) || film_modified(other);
+}
+
+bool BlenderViewportParameters::use_custom_shader() const
+{
+ return !(use_scene_world && use_scene_lights);
}
PassType update_viewport_display_passes(BL::SpaceView3D &b_v3d, vector<Pass> &passes)
{
if (b_v3d) {
- PassType display_pass = BlenderViewportParameters::get_viewport_display_render_pass(b_v3d);
+ const BlenderViewportParameters viewport_parameters(b_v3d);
+ const PassType display_pass = viewport_parameters.display_pass;
passes.clear();
Pass::add(display_pass, passes);
diff --git a/intern/cycles/blender/blender_viewport.h b/intern/cycles/blender/blender_viewport.h
index 7c6c9c4d274..d6518597053 100644
--- a/intern/cycles/blender/blender_viewport.h
+++ b/intern/cycles/blender/blender_viewport.h
@@ -18,17 +18,18 @@
#define __BLENDER_VIEWPORT_H__
#include "MEM_guardedalloc.h"
+
#include "RNA_access.h"
#include "RNA_blender_cpp.h"
#include "RNA_types.h"
#include "render/film.h"
-#include "util/util_param.h"
CCL_NAMESPACE_BEGIN
class BlenderViewportParameters {
- private:
+ public:
+ /* Shader. */
bool use_scene_world;
bool use_scene_lights;
float studiolight_rotate_z;
@@ -36,17 +37,24 @@ class BlenderViewportParameters {
float studiolight_background_alpha;
ustring studiolight_path;
+ /* Film. */
+ PassType display_pass;
+
BlenderViewportParameters();
- BlenderViewportParameters(BL::SpaceView3D &b_v3d);
+ explicit BlenderViewportParameters(BL::SpaceView3D &b_v3d);
- const bool modified(const BlenderViewportParameters &other) const;
- const bool custom_viewport_parameters() const;
- friend class BlenderSync;
+ /* Check whether any of shading related settings are different from the given parameters. */
+ bool shader_modified(const BlenderViewportParameters &other) const;
- public:
- /* Retrieve the render pass that needs to be displayed on the given `SpaceView3D`
- * When the `b_v3d` parameter is not given `PASS_NONE` will be returned. */
- static PassType get_viewport_display_render_pass(BL::SpaceView3D &b_v3d);
+ /* Check whether any of film related settings are different from the given parameters. */
+ bool film_modified(const BlenderViewportParameters &other) const;
+
+ /* Check whether any of settings are different from the given parameters. */
+ bool modified(const BlenderViewportParameters &other) const;
+
+ /* Returns truth when a custom shader defined by the viewport is to be used instead of the
+ * regular background shader or scene light. */
+ bool use_custom_shader() const;
};
PassType update_viewport_display_passes(BL::SpaceView3D &b_v3d, vector<Pass> &passes);
diff --git a/intern/cycles/blender/blender_volume.cpp b/intern/cycles/blender/blender_volume.cpp
index 410f7a72cf5..772ab9f5c8a 100644
--- a/intern/cycles/blender/blender_volume.cpp
+++ b/intern/cycles/blender/blender_volume.cpp
@@ -26,7 +26,7 @@
#ifdef WITH_OPENVDB
# include <openvdb/openvdb.h>
openvdb::GridBase::ConstPtr BKE_volume_grid_openvdb_for_read(const struct Volume *volume,
- struct VolumeGrid *grid);
+ const struct VolumeGrid *grid);
#endif
CCL_NAMESPACE_BEGIN
@@ -41,7 +41,7 @@ class BlenderSmokeLoader : public ImageLoader {
mesh_texture_space(b_mesh, texspace_loc, texspace_size);
}
- bool load_metadata(ImageMetaData &metadata) override
+ bool load_metadata(const ImageDeviceFeatures &, ImageMetaData &metadata) override
{
if (!b_domain) {
return false;
@@ -227,7 +227,7 @@ class BlenderVolumeLoader : public VDBImageLoader {
const bool unload = !b_volume_grid.is_loaded();
::Volume *volume = (::Volume *)b_volume.ptr.data;
- VolumeGrid *volume_grid = (VolumeGrid *)b_volume_grid.ptr.data;
+ const VolumeGrid *volume_grid = (VolumeGrid *)b_volume_grid.ptr.data;
grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid);
if (unload) {
diff --git a/intern/cycles/bvh/bvh_optix.cpp b/intern/cycles/bvh/bvh_optix.cpp
index d630e8965dc..cd266f72f89 100644
--- a/intern/cycles/bvh/bvh_optix.cpp
+++ b/intern/cycles/bvh/bvh_optix.cpp
@@ -17,6 +17,8 @@
#ifdef WITH_OPTIX
+# include "device/device.h"
+
# include "bvh/bvh_optix.h"
CCL_NAMESPACE_BEGIN
@@ -26,6 +28,7 @@ BVHOptiX::BVHOptiX(const BVHParams &params_,
const vector<Object *> &objects_,
Device *device)
: BVH(params_, geometry_, objects_),
+ device(device),
traversable_handle(0),
as_data(device, params_.top_level ? "optix tlas" : "optix blas", false),
motion_transform_data(device, "optix motion transform", false)
@@ -34,7 +37,9 @@ BVHOptiX::BVHOptiX(const BVHParams &params_,
BVHOptiX::~BVHOptiX()
{
- // Acceleration structure memory is freed via the 'as_data' destructor
+ // Acceleration structure memory is delayed freed on device, since deleting the
+ // BVH may happen while still being used for rendering.
+ device->release_optix_bvh(this);
}
CCL_NAMESPACE_END
diff --git a/intern/cycles/bvh/bvh_optix.h b/intern/cycles/bvh/bvh_optix.h
index aa514beae0d..ba5d90471d1 100644
--- a/intern/cycles/bvh/bvh_optix.h
+++ b/intern/cycles/bvh/bvh_optix.h
@@ -28,6 +28,7 @@ CCL_NAMESPACE_BEGIN
class BVHOptiX : public BVH {
public:
+ Device *device;
uint64_t traversable_handle;
device_only_memory<char> as_data;
device_only_memory<char> motion_transform_data;
diff --git a/intern/cycles/device/device.cpp b/intern/cycles/device/device.cpp
index 94732cd1855..ed53fbb54ae 100644
--- a/intern/cycles/device/device.cpp
+++ b/intern/cycles/device/device.cpp
@@ -619,6 +619,7 @@ DeviceInfo Device::get_multi_device(const vector<DeviceInfo> &subdevices,
info.num = 0;
info.has_half_images = true;
+ info.has_nanovdb = true;
info.has_volume_decoupled = true;
info.has_branched_path = true;
info.has_adaptive_stop_per_sample = true;
@@ -665,6 +666,7 @@ DeviceInfo Device::get_multi_device(const vector<DeviceInfo> &subdevices,
/* Accumulate device info. */
info.has_half_images &= device.has_half_images;
+ info.has_nanovdb &= device.has_nanovdb;
info.has_volume_decoupled &= device.has_volume_decoupled;
info.has_branched_path &= device.has_branched_path;
info.has_adaptive_stop_per_sample &= device.has_adaptive_stop_per_sample;
diff --git a/intern/cycles/device/device.h b/intern/cycles/device/device.h
index 0a731969c79..ecf79bcdfa6 100644
--- a/intern/cycles/device/device.h
+++ b/intern/cycles/device/device.h
@@ -61,7 +61,6 @@ enum DeviceTypeMask {
};
enum DeviceKernelStatus {
- DEVICE_KERNEL_WAITING_FOR_FEATURE_KERNEL = 0,
DEVICE_KERNEL_FEATURE_KERNEL_AVAILABLE,
DEVICE_KERNEL_USING_FEATURE_KERNEL,
DEVICE_KERNEL_FEATURE_KERNEL_INVALID,
@@ -78,6 +77,7 @@ class DeviceInfo {
int num;
bool display_device; /* GPU is used as a display device. */
bool has_half_images; /* Support half-float textures. */
+ bool has_nanovdb; /* Support NanoVDB volumes. */
bool has_volume_decoupled; /* Decoupled volume shading. */
bool has_branched_path; /* Supports branched path tracing. */
bool has_adaptive_stop_per_sample; /* Per-sample adaptive sampling stopping. */
@@ -99,6 +99,7 @@ class DeviceInfo {
cpu_threads = 0;
display_device = false;
has_half_images = false;
+ has_nanovdb = false;
has_volume_decoupled = false;
has_branched_path = true;
has_adaptive_stop_per_sample = false;
@@ -425,6 +426,9 @@ class Device {
/* acceleration structure building */
virtual void build_bvh(BVH *bvh, Progress &progress, bool refit);
+ /* OptiX specific destructor. */
+ virtual void release_optix_bvh(BVH * /*bvh*/){};
+
#ifdef WITH_NETWORK
/* networking */
void server_run();
diff --git a/intern/cycles/device/device_cpu.cpp b/intern/cycles/device/device_cpu.cpp
index e2f9c7391da..0e3c771dbc3 100644
--- a/intern/cycles/device/device_cpu.cpp
+++ b/intern/cycles/device/device_cpu.cpp
@@ -1654,6 +1654,7 @@ void device_cpu_info(vector<DeviceInfo> &devices)
info.has_adaptive_stop_per_sample = true;
info.has_osl = true;
info.has_half_images = true;
+ info.has_nanovdb = true;
info.has_profiling = true;
info.denoisers = DENOISER_NLM;
if (openimagedenoise_supported()) {
diff --git a/intern/cycles/device/device_cuda.cpp b/intern/cycles/device/device_cuda.cpp
index d9ffcceb06e..2e225ecfaf8 100644
--- a/intern/cycles/device/device_cuda.cpp
+++ b/intern/cycles/device/device_cuda.cpp
@@ -128,6 +128,7 @@ void device_cuda_info(vector<DeviceInfo> &devices)
info.num = num;
info.has_half_images = (major >= 3);
+ info.has_nanovdb = true;
info.has_volume_decoupled = false;
info.has_adaptive_stop_per_sample = false;
info.denoisers = DENOISER_NLM;
diff --git a/intern/cycles/device/device_memory.cpp b/intern/cycles/device/device_memory.cpp
index 9eee86b0814..80a05fc32fe 100644
--- a/intern/cycles/device/device_memory.cpp
+++ b/intern/cycles/device/device_memory.cpp
@@ -35,10 +35,54 @@ device_memory::device_memory(Device *device, const char *name, MemoryType type)
device_pointer(0),
host_pointer(0),
shared_pointer(0),
- shared_counter(0)
+ shared_counter(0),
+ original_device_ptr(0),
+ original_device_size(0),
+ original_device(0),
+ need_realloc_(false),
+ modified(false)
{
}
+device_memory::device_memory(device_memory &&other) noexcept
+ : data_type(other.data_type),
+ data_elements(other.data_elements),
+ data_size(other.data_size),
+ device_size(other.device_size),
+ data_width(other.data_width),
+ data_height(other.data_height),
+ data_depth(other.data_depth),
+ type(other.type),
+ name(other.name),
+ device(other.device),
+ device_pointer(other.device_pointer),
+ host_pointer(other.host_pointer),
+ shared_pointer(other.shared_pointer),
+ shared_counter(other.shared_counter),
+ original_device_ptr(other.original_device_ptr),
+ original_device_size(other.original_device_size),
+ original_device(other.original_device),
+ need_realloc_(other.need_realloc_),
+ modified(other.modified)
+{
+ other.data_elements = 0;
+ other.data_size = 0;
+ other.device_size = 0;
+ other.data_width = 0;
+ other.data_height = 0;
+ other.data_depth = 0;
+ other.device = 0;
+ other.device_pointer = 0;
+ other.host_pointer = 0;
+ other.shared_pointer = 0;
+ other.shared_counter = 0;
+ other.original_device_ptr = 0;
+ other.original_device_size = 0;
+ other.original_device = 0;
+ other.need_realloc_ = false;
+ other.modified = false;
+}
+
device_memory::~device_memory()
{
assert(shared_pointer == 0);
diff --git a/intern/cycles/device/device_memory.h b/intern/cycles/device/device_memory.h
index 97459b9ae6a..80f4d7b0468 100644
--- a/intern/cycles/device/device_memory.h
+++ b/intern/cycles/device/device_memory.h
@@ -238,6 +238,7 @@ class device_memory {
/* Only create through subclasses. */
device_memory(Device *device, const char *name, MemoryType type);
+ device_memory(device_memory &&other) noexcept;
/* No copying allowed. */
device_memory(const device_memory &) = delete;
@@ -277,6 +278,10 @@ template<typename T> class device_only_memory : public device_memory {
data_elements = max(device_type_traits<T>::num_elements, 1);
}
+ device_only_memory(device_only_memory &&other) noexcept : device_memory(std::move(other))
+ {
+ }
+
virtual ~device_only_memory()
{
free();
diff --git a/intern/cycles/device/device_multi.cpp b/intern/cycles/device/device_multi.cpp
index b272e59f99d..85ffa5fcd52 100644
--- a/intern/cycles/device/device_multi.cpp
+++ b/intern/cycles/device/device_multi.cpp
@@ -46,10 +46,13 @@ class MultiDevice : public Device {
list<SubDevice> devices, denoising_devices;
device_ptr unique_key;
vector<vector<SubDevice *>> peer_islands;
+ bool use_denoising;
bool matching_rendering_and_denoising_devices;
MultiDevice(DeviceInfo &info, Stats &stats, Profiler &profiler, bool background_)
- : Device(info, stats, profiler, background_), unique_key(1)
+ : Device(info, stats, profiler, background_),
+ unique_key(1),
+ use_denoising(!info.denoising_devices.empty())
{
foreach (DeviceInfo &subinfo, info.multi_devices) {
/* Always add CPU devices at the back since GPU devices can change
@@ -194,6 +197,7 @@ class MultiDevice : public Device {
if (!sub.device->load_kernels(requested_features))
return false;
+ use_denoising = requested_features.use_denoising;
if (requested_features.use_denoising) {
/* Only need denoising feature, everything else is unused. */
DeviceRequestedFeatures denoising_features;
@@ -228,10 +232,6 @@ class MultiDevice : public Device {
foreach (SubDevice &sub, devices) {
DeviceKernelStatus subresult = sub.device->get_active_kernel_switch_state();
switch (subresult) {
- case DEVICE_KERNEL_WAITING_FOR_FEATURE_KERNEL:
- result = subresult;
- break;
-
case DEVICE_KERNEL_FEATURE_KERNEL_INVALID:
case DEVICE_KERNEL_FEATURE_KERNEL_AVAILABLE:
return subresult;
@@ -400,7 +400,7 @@ class MultiDevice : public Device {
size_t existing_size = mem.device_size;
/* The tile buffers are allocated on each device (see below), so copy to all of them */
- if (strcmp(mem.name, "RenderBuffers") == 0) {
+ if (strcmp(mem.name, "RenderBuffers") == 0 && use_denoising) {
foreach (SubDevice &sub, devices) {
mem.device = sub.device;
mem.device_pointer = (existing_key) ? sub.ptr_map[existing_key] : 0;
@@ -466,7 +466,7 @@ class MultiDevice : public Device {
/* This is a hack to only allocate the tile buffers on denoising devices
* Similarly the tile buffers also need to be allocated separately on all devices so any
* overlap rendered for denoising does not interfere with each other */
- if (strcmp(mem.name, "RenderBuffers") == 0) {
+ if (strcmp(mem.name, "RenderBuffers") == 0 && use_denoising) {
vector<device_ptr> device_pointers;
device_pointers.reserve(devices.size());
@@ -518,7 +518,7 @@ class MultiDevice : public Device {
size_t existing_size = mem.device_size;
/* Free memory that was allocated for all devices (see above) on each device */
- if (strcmp(mem.name, "RenderBuffers") == 0 || mem.type == MEM_PIXELS) {
+ if (mem.type == MEM_PIXELS || (strcmp(mem.name, "RenderBuffers") == 0 && use_denoising)) {
foreach (SubDevice &sub, devices) {
mem.device = sub.device;
mem.device_pointer = sub.ptr_map[key];
diff --git a/intern/cycles/device/device_opencl.cpp b/intern/cycles/device/device_opencl.cpp
index 11376ee4883..9abb7cfb7fe 100644
--- a/intern/cycles/device/device_opencl.cpp
+++ b/intern/cycles/device/device_opencl.cpp
@@ -126,6 +126,9 @@ void device_opencl_info(vector<DeviceInfo> &devices)
/* Check OpenCL extensions */
info.has_half_images = platform_device.device_extensions.find("cl_khr_fp16") != string::npos;
+ /* Disabled for now due to apparent AMD driver bug. */
+ info.has_nanovdb = platform_name != "AMD Accelerated Parallel Processing";
+
devices.push_back(info);
num_devices++;
}
diff --git a/intern/cycles/device/device_optix.cpp b/intern/cycles/device/device_optix.cpp
index 51e1a0033ba..b008dfa376f 100644
--- a/intern/cycles/device/device_optix.cpp
+++ b/intern/cycles/device/device_optix.cpp
@@ -193,6 +193,9 @@ class OptiXDevice : public CUDADevice {
device_only_memory<unsigned char> denoiser_state;
int denoiser_input_passes = 0;
+ vector<device_only_memory<char>> delayed_free_bvh_memory;
+ thread_mutex delayed_free_bvh_mutex;
+
public:
OptiXDevice(DeviceInfo &info_, Stats &stats_, Profiler &profiler_, bool background_)
: CUDADevice(info_, stats_, profiler_, background_),
@@ -258,6 +261,8 @@ class OptiXDevice : public CUDADevice {
// Make CUDA context current
const CUDAContextScope scope(cuContext);
+ free_bvh_memory_delayed();
+
sbt_data.free();
texture_info.free();
launch_params.free();
@@ -362,7 +367,7 @@ class OptiXDevice : public CUDADevice {
}
}
- OptixModuleCompileOptions module_options;
+ OptixModuleCompileOptions module_options = {};
module_options.maxRegisterCount = 0; // Do not set an explicit register limit
# ifdef WITH_CYCLES_DEBUG
module_options.optLevel = OPTIX_COMPILE_OPTIMIZATION_LEVEL_0;
@@ -377,7 +382,7 @@ class OptiXDevice : public CUDADevice {
module_options.numBoundValues = 0;
# endif
- OptixPipelineCompileOptions pipeline_options;
+ OptixPipelineCompileOptions pipeline_options = {};
// Default to no motion blur and two-level graph, since it is the fastest option
pipeline_options.usesMotionBlur = false;
pipeline_options.traversableGraphFlags =
@@ -477,7 +482,7 @@ class OptiXDevice : public CUDADevice {
# if OPTIX_ABI_VERSION >= 36
if (DebugFlags().optix.curves_api && requested_features.use_hair_thick) {
- OptixBuiltinISOptions builtin_options;
+ OptixBuiltinISOptions builtin_options = {};
builtin_options.builtinISModuleType = OPTIX_PRIMITIVE_TYPE_ROUND_CUBIC_BSPLINE;
builtin_options.usesMotionBlur = false;
@@ -571,7 +576,7 @@ class OptiXDevice : public CUDADevice {
stack_size[PG_HITS_MOTION].cssIS + stack_size[PG_HITS_MOTION].cssAH);
# endif
- OptixPipelineLinkOptions link_options;
+ OptixPipelineLinkOptions link_options = {};
link_options.maxTraceDepth = 1;
# ifdef WITH_CYCLES_DEBUG
link_options.debugLevel = OPTIX_COMPILE_DEBUG_LEVEL_FULL;
@@ -721,7 +726,11 @@ class OptiXDevice : public CUDADevice {
}
}
else if (task.type == DeviceTask::SHADER) {
- launch_shader_eval(task, thread_index);
+ // CUDA kernels are used when doing baking
+ if (optix_module == NULL)
+ CUDADevice::shader(task);
+ else
+ launch_shader_eval(task, thread_index);
}
else if (task.type == DeviceTask::DENOISE_BUFFER) {
// Set up a single tile that covers the whole task and denoise it
@@ -953,16 +962,23 @@ class OptiXDevice : public CUDADevice {
}
// Create OptiX denoiser handle on demand when it is first used
- OptixDenoiserOptions denoiser_options;
+ OptixDenoiserOptions denoiser_options = {};
assert(task.denoising.input_passes >= 1 && task.denoising.input_passes <= 3);
+# if OPTIX_ABI_VERSION >= 47
+ denoiser_options.guideAlbedo = task.denoising.input_passes >= 2;
+ denoiser_options.guideNormal = task.denoising.input_passes >= 3;
+ check_result_optix_ret(optixDenoiserCreate(
+ context, OPTIX_DENOISER_MODEL_KIND_HDR, &denoiser_options, &denoiser));
+# else
denoiser_options.inputKind = static_cast<OptixDenoiserInputKind>(
OPTIX_DENOISER_INPUT_RGB + (task.denoising.input_passes - 1));
-# if OPTIX_ABI_VERSION < 28
+# if OPTIX_ABI_VERSION < 28
denoiser_options.pixelFormat = OPTIX_PIXEL_FORMAT_FLOAT3;
-# endif
+# endif
check_result_optix_ret(optixDenoiserCreate(context, &denoiser_options, &denoiser));
check_result_optix_ret(
optixDenoiserSetModel(denoiser, OPTIX_DENOISER_MODEL_KIND_HDR, NULL, 0));
+# endif
// OptiX denoiser handle was created with the requested number of input passes
denoiser_input_passes = task.denoising.input_passes;
@@ -1032,10 +1048,34 @@ class OptiXDevice : public CUDADevice {
# endif
output_layers[0].format = OPTIX_PIXEL_FORMAT_FLOAT3;
+# if OPTIX_ABI_VERSION >= 47
+ OptixDenoiserLayer image_layers = {};
+ image_layers.input = input_layers[0];
+ image_layers.output = output_layers[0];
+
+ OptixDenoiserGuideLayer guide_layers = {};
+ guide_layers.albedo = input_layers[1];
+ guide_layers.normal = input_layers[2];
+# endif
+
// Finally run denonising
OptixDenoiserParams params = {}; // All parameters are disabled/zero
+# if OPTIX_ABI_VERSION >= 47
check_result_optix_ret(optixDenoiserInvoke(denoiser,
- 0,
+ NULL,
+ &params,
+ denoiser_state.device_pointer,
+ scratch_offset,
+ &guide_layers,
+ &image_layers,
+ 1,
+ overlap_offset.x,
+ overlap_offset.y,
+ denoiser_state.device_pointer + scratch_offset,
+ scratch_size));
+# else
+ check_result_optix_ret(optixDenoiserInvoke(denoiser,
+ NULL,
&params,
denoiser_state.device_pointer,
scratch_offset,
@@ -1046,6 +1086,7 @@ class OptiXDevice : public CUDADevice {
output_layers,
denoiser_state.device_pointer + scratch_offset,
scratch_size));
+# endif
# if OPTIX_DENOISER_NO_PIXEL_STRIDE
void *output_args[] = {&input_ptr,
@@ -1157,7 +1198,7 @@ class OptiXDevice : public CUDADevice {
// Compute memory usage
OptixAccelBufferSizes sizes = {};
- OptixAccelBuildOptions options;
+ OptixAccelBuildOptions options = {};
options.operation = operation;
if (background) {
// Prefer best performance and lowest memory consumption in background
@@ -1195,7 +1236,7 @@ class OptiXDevice : public CUDADevice {
}
// Finally build the acceleration structure
- OptixAccelEmitDesc compacted_size_prop;
+ OptixAccelEmitDesc compacted_size_prop = {};
compacted_size_prop.type = OPTIX_PROPERTY_TYPE_COMPACTED_SIZE;
// A tiny space was allocated for this property at the end of the temporary buffer above
// Make sure this pointer is 8-byte aligned
@@ -1265,6 +1306,8 @@ class OptiXDevice : public CUDADevice {
return;
}
+ free_bvh_memory_delayed();
+
BVHOptiX *const bvh_optix = static_cast<BVHOptiX *>(bvh);
progress.set_substatus("Building OptiX acceleration structure");
@@ -1735,6 +1778,24 @@ class OptiXDevice : public CUDADevice {
}
}
+ void release_optix_bvh(BVH *bvh) override
+ {
+ thread_scoped_lock lock(delayed_free_bvh_mutex);
+ /* Do delayed free of BVH memory, since geometry holding BVH might be deleted
+ * while GPU is still rendering. */
+ BVHOptiX *const bvh_optix = static_cast<BVHOptiX *>(bvh);
+
+ delayed_free_bvh_memory.emplace_back(std::move(bvh_optix->as_data));
+ delayed_free_bvh_memory.emplace_back(std::move(bvh_optix->motion_transform_data));
+ bvh_optix->traversable_handle = 0;
+ }
+
+ void free_bvh_memory_delayed()
+ {
+ thread_scoped_lock lock(delayed_free_bvh_mutex);
+ delayed_free_bvh_memory.free_memory();
+ }
+
void const_copy_to(const char *name, void *host, size_t size) override
{
// Set constant memory for CUDA module
diff --git a/intern/cycles/device/opencl/device_opencl.h b/intern/cycles/device/opencl/device_opencl.h
index 2d6c6d04214..a65e764b0d4 100644
--- a/intern/cycles/device/opencl/device_opencl.h
+++ b/intern/cycles/device/opencl/device_opencl.h
@@ -269,7 +269,6 @@ class OpenCLDevice : public Device {
cl_device_id cdDevice;
cl_int ciErr;
int device_num;
- bool use_preview_kernels;
class OpenCLProgram {
public:
@@ -369,8 +368,7 @@ class OpenCLDevice : public Device {
/* Load the kernels and put the created kernels in the given
* `programs` parameter. */
void load_kernels(vector<OpenCLProgram *> &programs,
- const DeviceRequestedFeatures &requested_features,
- bool is_preview = false);
+ const DeviceRequestedFeatures &requested_features);
};
DeviceSplitKernel *split_kernel;
@@ -382,7 +380,6 @@ class OpenCLDevice : public Device {
OpenCLProgram denoising_program;
OpenCLSplitPrograms kernel_programs;
- OpenCLSplitPrograms preview_programs;
typedef map<string, device_vector<uchar> *> ConstMemMap;
typedef map<string, device_ptr> MemMap;
@@ -412,7 +409,6 @@ class OpenCLDevice : public Device {
string device_md5_hash(string kernel_custom_build_options = "");
bool load_kernels(const DeviceRequestedFeatures &requested_features);
void load_required_kernels(const DeviceRequestedFeatures &requested_features);
- void load_preview_kernels();
bool wait_for_availability(const DeviceRequestedFeatures &requested_features);
DeviceKernelStatus get_active_kernel_switch_state();
@@ -422,8 +418,7 @@ class OpenCLDevice : public Device {
/* Get the program file name to compile (*.cl) for the given kernel */
const string get_opencl_program_filename(const string &kernel_name);
string get_build_options(const DeviceRequestedFeatures &requested_features,
- const string &opencl_program_name,
- bool preview_kernel = false);
+ const string &opencl_program_name);
/* Enable the default features to reduce recompilation events */
void enable_default_features(DeviceRequestedFeatures &features);
diff --git a/intern/cycles/device/opencl/device_opencl_impl.cpp b/intern/cycles/device/opencl/device_opencl_impl.cpp
index aee3b0fb64f..715213175c9 100644
--- a/intern/cycles/device/opencl/device_opencl_impl.cpp
+++ b/intern/cycles/device/opencl/device_opencl_impl.cpp
@@ -107,8 +107,7 @@ void OpenCLDevice::enable_default_features(DeviceRequestedFeatures &features)
}
string OpenCLDevice::get_build_options(const DeviceRequestedFeatures &requested_features,
- const string &opencl_program_name,
- bool preview_kernel)
+ const string &opencl_program_name)
{
/* first check for non-split kernel programs */
if (opencl_program_name == "base" || opencl_program_name == "denoising") {
@@ -185,13 +184,7 @@ string OpenCLDevice::get_build_options(const DeviceRequestedFeatures &requested_
enable_default_features(nofeatures);
/* Add program specific optimized compile directives */
- if (preview_kernel) {
- DeviceRequestedFeatures preview_features;
- preview_features.use_hair = true;
- build_options += "-D__KERNEL_AO_PREVIEW__ ";
- build_options += preview_features.get_build_options();
- }
- else if (opencl_program_name == "split_do_volume" && !requested_features.use_volume) {
+ if (opencl_program_name == "split_do_volume" && !requested_features.use_volume) {
build_options += nofeatures.get_build_options();
}
else {
@@ -238,9 +231,7 @@ OpenCLDevice::OpenCLSplitPrograms::~OpenCLSplitPrograms()
}
void OpenCLDevice::OpenCLSplitPrograms::load_kernels(
- vector<OpenCLProgram *> &programs,
- const DeviceRequestedFeatures &requested_features,
- bool is_preview)
+ vector<OpenCLProgram *> &programs, const DeviceRequestedFeatures &requested_features)
{
if (!requested_features.use_baking) {
# define ADD_SPLIT_KERNEL_BUNDLE_PROGRAM(kernel_name) \
@@ -251,7 +242,7 @@ void OpenCLDevice::OpenCLSplitPrograms::load_kernels(
device, \
program_name_##kernel_name, \
"kernel_" #kernel_name ".cl", \
- device->get_build_options(requested_features, program_name_##kernel_name, is_preview)); \
+ device->get_build_options(requested_features, program_name_##kernel_name)); \
program_##kernel_name.add_kernel(ustring("path_trace_" #kernel_name)); \
programs.push_back(&program_##kernel_name);
@@ -259,7 +250,7 @@ void OpenCLDevice::OpenCLSplitPrograms::load_kernels(
ADD_SPLIT_KERNEL_PROGRAM(subsurface_scatter);
ADD_SPLIT_KERNEL_PROGRAM(direct_lighting);
ADD_SPLIT_KERNEL_PROGRAM(indirect_background);
- if (requested_features.use_volume || is_preview) {
+ if (requested_features.use_volume) {
ADD_SPLIT_KERNEL_PROGRAM(do_volume);
}
ADD_SPLIT_KERNEL_PROGRAM(shader_eval);
@@ -274,7 +265,7 @@ void OpenCLDevice::OpenCLSplitPrograms::load_kernels(
device,
"split_bundle",
"kernel_split_bundle.cl",
- device->get_build_options(requested_features, "split_bundle", is_preview));
+ device->get_build_options(requested_features, "split_bundle"));
ADD_SPLIT_KERNEL_BUNDLE_PROGRAM(data_init);
ADD_SPLIT_KERNEL_BUNDLE_PROGRAM(state_buffer_size);
@@ -403,7 +394,7 @@ class OpenCLSplitKernel : public DeviceSplitKernel {
device,
program_name,
device->get_opencl_program_filename(kernel_name),
- device->get_build_options(requested_features, program_name, device->use_preview_kernels));
+ device->get_build_options(requested_features, program_name));
kernel->program.add_kernel(ustring("path_trace_" + kernel_name));
kernel->program.load();
@@ -569,6 +560,11 @@ class OpenCLSplitKernel : public DeviceSplitKernel {
size_t num_elements = max_elements_for_max_buffer_size(kg, data, max_buffer_size);
int2 global_size = make_int2(max(round_down((int)sqrt(num_elements), 64), 64),
(int)sqrt(num_elements));
+
+ if (device->info.description.find("Intel") != string::npos) {
+ global_size = make_int2(min(512, global_size.x), min(512, global_size.y));
+ }
+
VLOG(1) << "Global size: " << global_size << ".";
return global_size;
}
@@ -612,7 +608,6 @@ OpenCLDevice::OpenCLDevice(DeviceInfo &info, Stats &stats, Profiler &profiler, b
: Device(info, stats, profiler, background),
load_kernel_num_compiling(0),
kernel_programs(this),
- preview_programs(this),
memory_manager(this),
texture_info(this, "__texture_info", MEM_GLOBAL)
{
@@ -622,7 +617,6 @@ OpenCLDevice::OpenCLDevice(DeviceInfo &info, Stats &stats, Profiler &profiler, b
cqCommandQueue = NULL;
device_initialized = false;
textures_need_update = true;
- use_preview_kernels = !background;
vector<OpenCLPlatformDevice> usable_devices;
OpenCLInfo::get_usable_devices(&usable_devices);
@@ -678,9 +672,6 @@ OpenCLDevice::OpenCLDevice(DeviceInfo &info, Stats &stats, Profiler &profiler, b
device_initialized = true;
split_kernel = new OpenCLSplitKernel(this);
- if (use_preview_kernels) {
- load_preview_kernels();
- }
}
OpenCLDevice::~OpenCLDevice()
@@ -771,7 +762,7 @@ bool OpenCLDevice::load_kernels(const DeviceRequestedFeatures &requested_feature
load_required_kernels(requested_features);
vector<OpenCLProgram *> programs;
- kernel_programs.load_kernels(programs, requested_features, false);
+ kernel_programs.load_kernels(programs, requested_features);
if (!requested_features.use_baking && requested_features.use_denoising) {
denoising_program = OpenCLProgram(
@@ -849,19 +840,6 @@ void OpenCLDevice::load_required_kernels(const DeviceRequestedFeatures &requeste
}
}
-void OpenCLDevice::load_preview_kernels()
-{
- DeviceRequestedFeatures no_features;
- vector<OpenCLProgram *> programs;
- preview_programs.load_kernels(programs, no_features, true);
-
- foreach (OpenCLProgram *program, programs) {
- if (!program->load()) {
- load_required_kernel_task_pool.push(function_bind(&OpenCLProgram::compile, program));
- }
- }
-}
-
bool OpenCLDevice::wait_for_availability(const DeviceRequestedFeatures &requested_features)
{
if (requested_features.use_baking) {
@@ -869,59 +847,18 @@ bool OpenCLDevice::wait_for_availability(const DeviceRequestedFeatures &requeste
return true;
}
- if (background) {
- load_kernel_task_pool.wait_work();
- use_preview_kernels = false;
- }
- else {
- /* We use a device setting to determine to load preview kernels or not
- * Better to check on device level than per kernel as mixing preview and
- * non-preview kernels does not work due to different data types */
- if (use_preview_kernels) {
- use_preview_kernels = load_kernel_num_compiling.load() > 0;
- }
- }
+ load_kernel_task_pool.wait_work();
return split_kernel->load_kernels(requested_features);
}
OpenCLDevice::OpenCLSplitPrograms *OpenCLDevice::get_split_programs()
{
- return use_preview_kernels ? &preview_programs : &kernel_programs;
+ return &kernel_programs;
}
DeviceKernelStatus OpenCLDevice::get_active_kernel_switch_state()
{
- /* Do not switch kernels for background renderings
- * We do foreground rendering but use the preview kernels
- * Check for the optimized kernels
- *
- * This works also the other way around, where we are using
- * optimized kernels but new ones are being compiled due
- * to other features that are needed */
- if (background) {
- /* The if-statements below would find the same result,
- * But as the `finished` method uses a mutex we added
- * this as an early exit */
- return DEVICE_KERNEL_USING_FEATURE_KERNEL;
- }
-
- bool other_kernels_finished = load_kernel_num_compiling.load() == 0;
- if (use_preview_kernels) {
- if (other_kernels_finished) {
- return DEVICE_KERNEL_FEATURE_KERNEL_AVAILABLE;
- }
- else {
- return DEVICE_KERNEL_WAITING_FOR_FEATURE_KERNEL;
- }
- }
- else {
- if (other_kernels_finished) {
- return DEVICE_KERNEL_USING_FEATURE_KERNEL;
- }
- else {
- return DEVICE_KERNEL_FEATURE_KERNEL_INVALID;
- }
- }
+ return DEVICE_KERNEL_USING_FEATURE_KERNEL;
}
void OpenCLDevice::mem_alloc(device_memory &mem)
@@ -2036,7 +1973,9 @@ string OpenCLDevice::kernel_build_options(const string *debug_src)
# endif
# ifdef WITH_NANOVDB
- build_options += "-DWITH_NANOVDB ";
+ if (info.has_nanovdb) {
+ build_options += "-DWITH_NANOVDB ";
+ }
# endif
return build_options;
diff --git a/intern/cycles/graph/node.cpp b/intern/cycles/graph/node.cpp
index c926f6ab8ef..57f25283f85 100644
--- a/intern/cycles/graph/node.cpp
+++ b/intern/cycles/graph/node.cpp
@@ -367,9 +367,17 @@ void Node::copy_value(const SocketType &socket, const Node &other, const SocketT
case SocketType::TRANSFORM_ARRAY:
copy_array<Transform>(this, socket, &other, other_socket);
break;
- case SocketType::NODE_ARRAY:
+ case SocketType::NODE_ARRAY: {
copy_array<void *>(this, socket, &other, other_socket);
+
+ array<Node *> &node_array = get_socket_value<array<Node *>>(this, socket);
+
+ for (Node *node : node_array) {
+ node->reference();
+ }
+
break;
+ }
default:
assert(0);
break;
@@ -379,6 +387,14 @@ void Node::copy_value(const SocketType &socket, const Node &other, const SocketT
const void *src = ((char *)&other) + other_socket.struct_offset;
void *dst = ((char *)this) + socket.struct_offset;
memcpy(dst, src, socket.size());
+
+ if (socket.type == SocketType::NODE) {
+ Node *node = get_socket_value<Node *>(this, socket);
+
+ if (node) {
+ node->reference();
+ }
+ }
}
}
@@ -773,6 +789,26 @@ void Node::set_owner(const NodeOwner *owner_)
owner = owner_;
}
+void Node::dereference_all_used_nodes()
+{
+ foreach (const SocketType &socket, type->inputs) {
+ if (socket.type == SocketType::NODE) {
+ Node *node = get_socket_value<Node *>(this, socket);
+
+ if (node) {
+ node->dereference();
+ }
+ }
+ else if (socket.type == SocketType::NODE_ARRAY) {
+ const array<Node *> &nodes = get_socket_value<array<Node *>>(this, socket);
+
+ for (Node *node : nodes) {
+ node->dereference();
+ }
+ }
+ }
+}
+
bool Node::socket_is_modified(const SocketType &input) const
{
return (socket_modified & input.modified_flag_bit) != 0;
@@ -803,6 +839,25 @@ template<typename T> void Node::set_if_different(const SocketType &input, T valu
socket_modified |= input.modified_flag_bit;
}
+void Node::set_if_different(const SocketType &input, Node *value)
+{
+ if (get_socket_value<Node *>(this, input) == value) {
+ return;
+ }
+
+ Node *old_node = get_socket_value<Node *>(this, input);
+ if (old_node) {
+ old_node->dereference();
+ }
+
+ if (value) {
+ value->reference();
+ }
+
+ get_socket_value<Node *>(this, input) = value;
+ socket_modified |= input.modified_flag_bit;
+}
+
template<typename T> void Node::set_if_different(const SocketType &input, array<T> &value)
{
if (!socket_is_modified(input)) {
@@ -815,6 +870,27 @@ template<typename T> void Node::set_if_different(const SocketType &input, array<
socket_modified |= input.modified_flag_bit;
}
+void Node::set_if_different(const SocketType &input, array<Node *> &value)
+{
+ if (!socket_is_modified(input)) {
+ if (get_socket_value<array<Node *>>(this, input) == value) {
+ return;
+ }
+ }
+
+ array<Node *> &old_nodes = get_socket_value<array<Node *>>(this, input);
+ for (Node *old_node : old_nodes) {
+ old_node->dereference();
+ }
+
+ for (Node *new_node : value) {
+ new_node->reference();
+ }
+
+ get_socket_value<array<Node *>>(this, input).steal_data(value);
+ socket_modified |= input.modified_flag_bit;
+}
+
void Node::print_modified_sockets() const
{
printf("Node : %s\n", name.c_str());
diff --git a/intern/cycles/graph/node.h b/intern/cycles/graph/node.h
index 2fc9a1e0281..aa365baeccd 100644
--- a/intern/cycles/graph/node.h
+++ b/intern/cycles/graph/node.h
@@ -177,8 +177,32 @@ struct Node {
const NodeOwner *get_owner() const;
void set_owner(const NodeOwner *owner_);
+ int reference_count() const
+ {
+ return ref_count;
+ }
+
+ void reference()
+ {
+ ref_count += 1;
+ }
+
+ void dereference()
+ {
+ ref_count -= 1;
+ }
+
+ /* Set the reference count to zero. This should only be called when we know for sure that the
+ * Node is not used by anyone else. For now, this is only the case when "deleting" shaders, as
+ * they are never actually deleted. */
+ void clear_reference_count()
+ {
+ ref_count = 0;
+ }
+
protected:
const NodeOwner *owner;
+ int ref_count{0};
template<typename T> static T &get_socket_value(const Node *node, const SocketType &socket)
{
@@ -189,7 +213,19 @@ struct Node {
template<typename T> void set_if_different(const SocketType &input, T value);
+ /* Explicit overload for Node sockets so we can handle reference counting. The old Node is
+ * dereferenced, and the new one is referenced. */
+ void set_if_different(const SocketType &input, Node *value);
+
template<typename T> void set_if_different(const SocketType &input, array<T> &value);
+
+ /* Explicit overload for Node sockets so we can handle reference counting. The old Nodes are
+ * dereferenced, and the new ones are referenced. */
+ void set_if_different(const SocketType &input, array<Node *> &value);
+
+ /* Call this function in derived classes' destructors to ensure that used Nodes are dereferenced
+ * properly. */
+ void dereference_all_used_nodes();
};
CCL_NAMESPACE_END
diff --git a/intern/cycles/graph/node_type.h b/intern/cycles/graph/node_type.h
index 2a741d9b06f..8b37398fa17 100644
--- a/intern/cycles/graph/node_type.h
+++ b/intern/cycles/graph/node_type.h
@@ -148,16 +148,17 @@ struct NodeType {
#define NODE_DECLARE \
static const NodeType *get_node_type(); \
template<typename T> static const NodeType *register_type(); \
- static Node *create(const NodeType *type);
+ static Node *create(const NodeType *type); \
+ static const NodeType *node_type;
#define NODE_DEFINE(structname) \
+ const NodeType *structname::node_type = structname::register_type<structname>(); \
Node *structname::create(const NodeType *) \
{ \
return new structname(); \
} \
const NodeType *structname::get_node_type() \
{ \
- static const NodeType *node_type = register_type<structname>(); \
return node_type; \
} \
template<typename T> const NodeType *structname::register_type()
@@ -169,6 +170,8 @@ struct NodeType {
#define NODE_ABSTRACT_DEFINE(structname) \
const NodeType *structname::get_node_base_type() \
{ \
+ /* Base types constructed in this getter to ensure correct initialization \
+ * order. Regular types are not so they are auto-registered for XML parsing. */ \
static const NodeType *node_base_type = register_base_type<structname>(); \
return node_base_type; \
} \
diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt
index f6b4b963a7a..ea0f16c9233 100644
--- a/intern/cycles/kernel/CMakeLists.txt
+++ b/intern/cycles/kernel/CMakeLists.txt
@@ -93,6 +93,7 @@ set(SRC_BVH_HEADERS
bvh/bvh_local.h
bvh/bvh_traversal.h
bvh/bvh_types.h
+ bvh/bvh_util.h
bvh/bvh_volume.h
bvh/bvh_volume_all.h
bvh/bvh_embree.h
diff --git a/intern/cycles/kernel/bvh/bvh.h b/intern/cycles/kernel/bvh/bvh.h
index 3049f243ae9..3a3f38539c5 100644
--- a/intern/cycles/kernel/bvh/bvh.h
+++ b/intern/cycles/kernel/bvh/bvh.h
@@ -29,9 +29,10 @@
# include "kernel/bvh/bvh_embree.h"
#endif
-CCL_NAMESPACE_BEGIN
-
#include "kernel/bvh/bvh_types.h"
+#include "kernel/bvh/bvh_util.h"
+
+CCL_NAMESPACE_BEGIN
#ifndef __KERNEL_OPTIX__
@@ -533,97 +534,4 @@ ccl_device_intersect uint scene_intersect_volume_all(KernelGlobals *kg,
}
#endif /* __VOLUME_RECORD_ALL__ */
-/* Ray offset to avoid self intersection.
- *
- * This function should be used to compute a modified ray start position for
- * rays leaving from a surface. */
-
-ccl_device_inline float3 ray_offset(float3 P, float3 Ng)
-{
-#ifdef __INTERSECTION_REFINE__
- const float epsilon_f = 1e-5f;
- /* ideally this should match epsilon_f, but instancing and motion blur
- * precision makes it problematic */
- const float epsilon_test = 1.0f;
- const int epsilon_i = 32;
-
- float3 res;
-
- /* x component */
- if (fabsf(P.x) < epsilon_test) {
- res.x = P.x + Ng.x * epsilon_f;
- }
- else {
- uint ix = __float_as_uint(P.x);
- ix += ((ix ^ __float_as_uint(Ng.x)) >> 31) ? -epsilon_i : epsilon_i;
- res.x = __uint_as_float(ix);
- }
-
- /* y component */
- if (fabsf(P.y) < epsilon_test) {
- res.y = P.y + Ng.y * epsilon_f;
- }
- else {
- uint iy = __float_as_uint(P.y);
- iy += ((iy ^ __float_as_uint(Ng.y)) >> 31) ? -epsilon_i : epsilon_i;
- res.y = __uint_as_float(iy);
- }
-
- /* z component */
- if (fabsf(P.z) < epsilon_test) {
- res.z = P.z + Ng.z * epsilon_f;
- }
- else {
- uint iz = __float_as_uint(P.z);
- iz += ((iz ^ __float_as_uint(Ng.z)) >> 31) ? -epsilon_i : epsilon_i;
- res.z = __uint_as_float(iz);
- }
-
- return res;
-#else
- const float epsilon_f = 1e-4f;
- return P + epsilon_f * Ng;
-#endif
-}
-
-#if defined(__VOLUME_RECORD_ALL__) || (defined(__SHADOW_RECORD_ALL__) && defined(__KERNEL_CPU__))
-/* ToDo: Move to another file? */
-ccl_device int intersections_compare(const void *a, const void *b)
-{
- const Intersection *isect_a = (const Intersection *)a;
- const Intersection *isect_b = (const Intersection *)b;
-
- if (isect_a->t < isect_b->t)
- return -1;
- else if (isect_a->t > isect_b->t)
- return 1;
- else
- return 0;
-}
-#endif
-
-#if defined(__SHADOW_RECORD_ALL__)
-ccl_device_inline void sort_intersections(Intersection *hits, uint num_hits)
-{
-# ifdef __KERNEL_GPU__
- /* Use bubble sort which has more friendly memory pattern on GPU. */
- bool swapped;
- do {
- swapped = false;
- for (int j = 0; j < num_hits - 1; ++j) {
- if (hits[j].t > hits[j + 1].t) {
- struct Intersection tmp = hits[j];
- hits[j] = hits[j + 1];
- hits[j + 1] = tmp;
- swapped = true;
- }
- }
- --num_hits;
- } while (swapped);
-# else
- qsort(hits, num_hits, sizeof(Intersection), intersections_compare);
-# endif
-}
-#endif /* __SHADOW_RECORD_ALL__ | __VOLUME_RECORD_ALL__ */
-
CCL_NAMESPACE_END
diff --git a/intern/cycles/kernel/bvh/bvh_shadow_all.h b/intern/cycles/kernel/bvh/bvh_shadow_all.h
index dccd257d2de..2e94b1d7c37 100644
--- a/intern/cycles/kernel/bvh/bvh_shadow_all.h
+++ b/intern/cycles/kernel/bvh/bvh_shadow_all.h
@@ -180,25 +180,10 @@ ccl_device_inline
/* todo: optimize so primitive visibility flag indicates if
* the primitive has a transparent shadow shader? */
- int prim = kernel_tex_fetch(__prim_index, isect_array->prim);
- int shader = 0;
-
-#ifdef __HAIR__
- if (kernel_tex_fetch(__prim_type, isect_array->prim) & PRIMITIVE_ALL_TRIANGLE)
-#endif
- {
- shader = kernel_tex_fetch(__tri_shader, prim);
- }
-#ifdef __HAIR__
- else {
- float4 str = kernel_tex_fetch(__curves, prim);
- shader = __float_as_int(str.z);
- }
-#endif
- int flag = kernel_tex_fetch(__shaders, (shader & SHADER_MASK)).flags;
+ const int flags = intersection_get_shader_flags(kg, isect_array);
/* if no transparent shadows, all light is blocked */
- if (!(flag & SD_HAS_TRANSPARENT_SHADOW)) {
+ if (!(flags & SD_HAS_TRANSPARENT_SHADOW)) {
return true;
}
/* if maximum number of hits reached, block all light */
diff --git a/intern/cycles/kernel/bvh/bvh_util.h b/intern/cycles/kernel/bvh/bvh_util.h
new file mode 100644
index 00000000000..a694e4dc259
--- /dev/null
+++ b/intern/cycles/kernel/bvh/bvh_util.h
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2011-2013 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.
+ */
+
+#pragma once
+
+CCL_NAMESPACE_BEGIN
+
+/* Ray offset to avoid self intersection.
+ *
+ * This function should be used to compute a modified ray start position for
+ * rays leaving from a surface. */
+
+ccl_device_inline float3 ray_offset(float3 P, float3 Ng)
+{
+#ifdef __INTERSECTION_REFINE__
+ const float epsilon_f = 1e-5f;
+ /* ideally this should match epsilon_f, but instancing and motion blur
+ * precision makes it problematic */
+ const float epsilon_test = 1.0f;
+ const int epsilon_i = 32;
+
+ float3 res;
+
+ /* x component */
+ if (fabsf(P.x) < epsilon_test) {
+ res.x = P.x + Ng.x * epsilon_f;
+ }
+ else {
+ uint ix = __float_as_uint(P.x);
+ ix += ((ix ^ __float_as_uint(Ng.x)) >> 31) ? -epsilon_i : epsilon_i;
+ res.x = __uint_as_float(ix);
+ }
+
+ /* y component */
+ if (fabsf(P.y) < epsilon_test) {
+ res.y = P.y + Ng.y * epsilon_f;
+ }
+ else {
+ uint iy = __float_as_uint(P.y);
+ iy += ((iy ^ __float_as_uint(Ng.y)) >> 31) ? -epsilon_i : epsilon_i;
+ res.y = __uint_as_float(iy);
+ }
+
+ /* z component */
+ if (fabsf(P.z) < epsilon_test) {
+ res.z = P.z + Ng.z * epsilon_f;
+ }
+ else {
+ uint iz = __float_as_uint(P.z);
+ iz += ((iz ^ __float_as_uint(Ng.z)) >> 31) ? -epsilon_i : epsilon_i;
+ res.z = __uint_as_float(iz);
+ }
+
+ return res;
+#else
+ const float epsilon_f = 1e-4f;
+ return P + epsilon_f * Ng;
+#endif
+}
+
+#if defined(__VOLUME_RECORD_ALL__) || (defined(__SHADOW_RECORD_ALL__) && defined(__KERNEL_CPU__))
+/* ToDo: Move to another file? */
+ccl_device int intersections_compare(const void *a, const void *b)
+{
+ const Intersection *isect_a = (const Intersection *)a;
+ const Intersection *isect_b = (const Intersection *)b;
+
+ if (isect_a->t < isect_b->t)
+ return -1;
+ else if (isect_a->t > isect_b->t)
+ return 1;
+ else
+ return 0;
+}
+#endif
+
+#if defined(__SHADOW_RECORD_ALL__)
+ccl_device_inline void sort_intersections(Intersection *hits, uint num_hits)
+{
+ kernel_assert(num_hits > 0);
+
+# ifdef __KERNEL_GPU__
+ /* Use bubble sort which has more friendly memory pattern on GPU. */
+ bool swapped;
+ do {
+ swapped = false;
+ for (int j = 0; j < num_hits - 1; ++j) {
+ if (hits[j].t > hits[j + 1].t) {
+ struct Intersection tmp = hits[j];
+ hits[j] = hits[j + 1];
+ hits[j + 1] = tmp;
+ swapped = true;
+ }
+ }
+ --num_hits;
+ } while (swapped);
+# else
+ qsort(hits, num_hits, sizeof(Intersection), intersections_compare);
+# endif
+}
+#endif /* __SHADOW_RECORD_ALL__ | __VOLUME_RECORD_ALL__ */
+
+/* Utility to quickly get a shader flags from an intersection. */
+
+ccl_device_forceinline int intersection_get_shader_flags(KernelGlobals *ccl_restrict kg,
+ const Intersection *isect)
+{
+ const int prim = kernel_tex_fetch(__prim_index, isect->prim);
+ int shader = 0;
+
+#ifdef __HAIR__
+ if (kernel_tex_fetch(__prim_type, isect->prim) & PRIMITIVE_ALL_TRIANGLE)
+#endif
+ {
+ shader = kernel_tex_fetch(__tri_shader, prim);
+ }
+#ifdef __HAIR__
+ else {
+ float4 str = kernel_tex_fetch(__curves, prim);
+ shader = __float_as_int(str.z);
+ }
+#endif
+
+ return kernel_tex_fetch(__shaders, (shader & SHADER_MASK)).flags;
+}
+
+ccl_device_forceinline int intersection_get_shader(KernelGlobals *ccl_restrict kg,
+ const Intersection *isect)
+{
+ const int prim = kernel_tex_fetch(__prim_index, isect->prim);
+ int shader = 0;
+
+#ifdef __HAIR__
+ if (kernel_tex_fetch(__prim_type, isect->prim) & PRIMITIVE_ALL_TRIANGLE)
+#endif
+ {
+ shader = kernel_tex_fetch(__tri_shader, prim);
+ }
+#ifdef __HAIR__
+ else {
+ float4 str = kernel_tex_fetch(__curves, prim);
+ shader = __float_as_int(str.z);
+ }
+#endif
+
+ return shader & SHADER_MASK;
+}
+
+CCL_NAMESPACE_END
diff --git a/intern/cycles/kernel/closure/alloc.h b/intern/cycles/kernel/closure/alloc.h
index 341d1e16eb1..99a5a675976 100644
--- a/intern/cycles/kernel/closure/alloc.h
+++ b/intern/cycles/kernel/closure/alloc.h
@@ -57,14 +57,24 @@ ccl_device ccl_addr_space void *closure_alloc_extra(ShaderData *sd, int size)
ccl_device_inline ShaderClosure *bsdf_alloc(ShaderData *sd, int size, float3 weight)
{
- ShaderClosure *sc = closure_alloc(sd, size, CLOSURE_NONE_ID, weight);
+ kernel_assert(isfinite3_safe(weight));
- if (sc == NULL)
- return NULL;
+ const float sample_weight = fabsf(average(weight));
+
+ /* Use comparison this way to help dealing with non-finite weight: if the average is not finite
+ * we will not allocate new closure. */
+ if (sample_weight >= CLOSURE_WEIGHT_CUTOFF) {
+ ShaderClosure *sc = closure_alloc(sd, size, CLOSURE_NONE_ID, weight);
+ if (sc == NULL) {
+ return NULL;
+ }
+
+ sc->sample_weight = sample_weight;
- float sample_weight = fabsf(average(weight));
- sc->sample_weight = sample_weight;
- return (sample_weight >= CLOSURE_WEIGHT_CUTOFF) ? sc : NULL;
+ return sc;
+ }
+
+ return NULL;
}
#ifdef __OSL__
@@ -73,17 +83,27 @@ ccl_device_inline ShaderClosure *bsdf_alloc_osl(ShaderData *sd,
float3 weight,
void *data)
{
- ShaderClosure *sc = closure_alloc(sd, size, CLOSURE_NONE_ID, weight);
+ kernel_assert(isfinite3_safe(weight));
- if (!sc)
- return NULL;
+ const float sample_weight = fabsf(average(weight));
- memcpy((void *)sc, data, size);
+ /* Use comparison this way to help dealing with non-finite weight: if the average is not finite
+ * we will not allocate new closure. */
+ if (sample_weight >= CLOSURE_WEIGHT_CUTOFF) {
+ ShaderClosure *sc = closure_alloc(sd, size, CLOSURE_NONE_ID, weight);
+ if (!sc) {
+ return NULL;
+ }
- float sample_weight = fabsf(average(weight));
- sc->weight = weight;
- sc->sample_weight = sample_weight;
- return (sample_weight >= CLOSURE_WEIGHT_CUTOFF) ? sc : NULL;
+ memcpy((void *)sc, data, size);
+
+ sc->weight = weight;
+ sc->sample_weight = sample_weight;
+
+ return sc;
+ }
+
+ return NULL;
}
#endif
diff --git a/intern/cycles/kernel/kernel_light.h b/intern/cycles/kernel/kernel_light.h
index 9650b85a5c2..42a834d2ce3 100644
--- a/intern/cycles/kernel/kernel_light.h
+++ b/intern/cycles/kernel/kernel_light.h
@@ -119,11 +119,11 @@ ccl_device_inline bool lamp_light_sample(
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]);
- float3 D = make_float3(klight->area.dir[0], klight->area.dir[1], klight->area.dir[2]);
+ float3 Ng = make_float3(klight->area.dir[0], klight->area.dir[1], klight->area.dir[2]);
float invarea = fabsf(klight->area.invarea);
bool is_round = (klight->area.invarea < 0.0f);
- if (dot(ls->P - P, D) > 0.0f) {
+ if (dot(ls->P - P, Ng) > 0.0f) {
return false;
}
@@ -136,19 +136,37 @@ ccl_device_inline bool lamp_light_sample(
}
else {
inplane = ls->P;
- ls->pdf = rect_light_sample(P, &ls->P, axisu, axisv, randu, randv, true);
+
+ float3 sample_axisu = axisu;
+ float3 sample_axisv = axisv;
+
+ if (klight->area.tan_spread > 0.0f) {
+ if (!light_spread_clamp_area_light(
+ P, Ng, &ls->P, &sample_axisu, &sample_axisv, klight->area.tan_spread)) {
+ return false;
+ }
+ }
+
+ ls->pdf = rect_light_sample(P, &ls->P, sample_axisu, sample_axisv, randu, randv, true);
inplane = ls->P - inplane;
}
ls->u = dot(inplane, axisu) * (1.0f / dot(axisu, axisu)) + 0.5f;
ls->v = dot(inplane, axisv) * (1.0f / dot(axisv, axisv)) + 0.5f;
- ls->Ng = D;
+ ls->Ng = Ng;
ls->D = normalize_len(ls->P - P, &ls->t);
ls->eval_fac = 0.25f * invarea;
+
+ if (klight->area.tan_spread > 0.0f) {
+ /* Area Light spread angle attenuation */
+ ls->eval_fac *= light_spread_attenuation(
+ ls->D, ls->Ng, klight->area.tan_spread, klight->area.normalize_spread);
+ }
+
if (is_round) {
- ls->pdf *= lamp_light_pdf(kg, D, -ls->D, ls->t);
+ ls->pdf *= lamp_light_pdf(kg, Ng, -ls->D, ls->t);
}
}
}
@@ -283,9 +301,28 @@ ccl_device bool lamp_light_eval(
ls->pdf = invarea * lamp_light_pdf(kg, Ng, -D, ls->t);
}
else {
- ls->pdf = rect_light_sample(P, &light_P, axisu, axisv, 0, 0, false);
+ float3 sample_axisu = axisu;
+ float3 sample_axisv = axisv;
+
+ if (klight->area.tan_spread > 0.0f) {
+ if (!light_spread_clamp_area_light(
+ P, Ng, &light_P, &sample_axisu, &sample_axisv, klight->area.tan_spread)) {
+ return false;
+ }
+ }
+
+ ls->pdf = rect_light_sample(P, &light_P, sample_axisu, sample_axisv, 0, 0, false);
}
ls->eval_fac = 0.25f * invarea;
+
+ if (klight->area.tan_spread > 0.0f) {
+ /* Area Light spread angle attenuation */
+ ls->eval_fac *= light_spread_attenuation(
+ ls->D, ls->Ng, klight->area.tan_spread, klight->area.normalize_spread);
+ if (ls->eval_fac == 0.0f) {
+ return false;
+ }
+ }
}
else {
return false;
diff --git a/intern/cycles/kernel/kernel_light_common.h b/intern/cycles/kernel/kernel_light_common.h
index 39503a4b479..4a683d36226 100644
--- a/intern/cycles/kernel/kernel_light_common.h
+++ b/intern/cycles/kernel/kernel_light_common.h
@@ -146,6 +146,70 @@ ccl_device float spot_light_attenuation(float3 dir, float spot_angle, float spot
return attenuation;
}
+ccl_device float light_spread_attenuation(const float3 D,
+ const float3 lightNg,
+ const float tan_spread,
+ const float normalize_spread)
+{
+ /* Model a soft-box grid, computing the ratio of light not hidden by the
+ * slats of the grid at a given angle. (see D10594). */
+ const float cos_a = -dot(D, lightNg);
+ const float sin_a = safe_sqrtf(1.0f - sqr(cos_a));
+ const float tan_a = sin_a / cos_a;
+ return max((1.0f - (tan_spread * tan_a)) * normalize_spread, 0.0f);
+}
+
+/* Compute subset of area light that actually has an influence on the shading point, to
+ * reduce noise with low spread. */
+ccl_device bool light_spread_clamp_area_light(const float3 P,
+ const float3 lightNg,
+ float3 *lightP,
+ float3 *axisu,
+ float3 *axisv,
+ const float tan_spread)
+{
+ /* Closest point in area light plane and distance to that plane. */
+ const float3 closest_P = P - dot(lightNg, P - *lightP) * lightNg;
+ const float t = len(closest_P - P);
+
+ /* Radius of circle on area light that actually affects the shading point. */
+ const float radius = t / tan_spread;
+
+ /* TODO: would be faster to store as normalized vector + length, also in rect_light_sample. */
+ float len_u, len_v;
+ const float3 u = normalize_len(*axisu, &len_u);
+ const float3 v = normalize_len(*axisv, &len_v);
+
+ /* Local uv coordinates of closest point. */
+ const float closest_u = dot(u, closest_P - *lightP);
+ const float closest_v = dot(v, closest_P - *lightP);
+
+ /* Compute rectangle encompassing the circle that affects the shading point,
+ * clamped to the bounds of the area light. */
+ const float min_u = max(closest_u - radius, -len_u * 0.5f);
+ const float max_u = min(closest_u + radius, len_u * 0.5f);
+ const float min_v = max(closest_v - radius, -len_v * 0.5f);
+ const float max_v = min(closest_v + radius, len_v * 0.5f);
+
+ /* Skip if rectangle is empty. */
+ if (min_u >= max_u || min_v >= max_v) {
+ return false;
+ }
+
+ /* Compute new area light center position and axes from rectangle in local
+ * uv coordinates. */
+ const float new_center_u = 0.5f * (min_u + max_u);
+ const float new_center_v = 0.5f * (min_v + max_v);
+ const float new_len_u = max_u - min_u;
+ const float new_len_v = max_v - min_v;
+
+ *lightP = *lightP + new_center_u * u + new_center_v * v;
+ *axisu = u * new_len_u;
+ *axisv = v * new_len_v;
+
+ return true;
+}
+
ccl_device float lamp_light_pdf(KernelGlobals *kg, const float3 Ng, const float3 I, float t)
{
float cos_pi = dot(Ng, I);
diff --git a/intern/cycles/kernel/kernel_montecarlo.h b/intern/cycles/kernel/kernel_montecarlo.h
index ba25c0e24e4..ce37bd0b15e 100644
--- a/intern/cycles/kernel/kernel_montecarlo.h
+++ b/intern/cycles/kernel/kernel_montecarlo.h
@@ -195,31 +195,108 @@ ccl_device float2 regular_polygon_sample(float corners, float rotation, float u,
ccl_device float3 ensure_valid_reflection(float3 Ng, float3 I, float3 N)
{
- float3 R;
- float NI = dot(N, I);
- float NgR, threshold;
-
- /* Check if the incident ray is coming from behind normal N. */
- if (NI > 0) {
- /* Normal reflection */
- R = (2 * NI) * N - I;
- NgR = dot(Ng, R);
-
- /* Reflection rays may always be at least as shallow as the incoming ray. */
- threshold = min(0.9f * dot(Ng, I), 0.01f);
- if (NgR >= threshold) {
- return N;
+ float3 R = 2 * dot(N, I) * N - I;
+
+ /* Reflection rays may always be at least as shallow as the incoming ray. */
+ float threshold = min(0.9f * dot(Ng, I), 0.01f);
+ if (dot(Ng, R) >= threshold) {
+ return N;
+ }
+
+ /* Form coordinate system with Ng as the Z axis and N inside the X-Z-plane.
+ * The X axis is found by normalizing the component of N that's orthogonal to Ng.
+ * The Y axis isn't actually needed.
+ */
+ float NdotNg = dot(N, Ng);
+ float3 X = normalize(N - NdotNg * Ng);
+
+ /* Keep math expressions. */
+ /* clang-format off */
+ /* Calculate N.z and N.x in the local coordinate system.
+ *
+ * The goal of this computation is to find a N' that is rotated towards Ng just enough
+ * to lift R' above the threshold (here called t), therefore dot(R', Ng) = t.
+ *
+ * According to the standard reflection equation,
+ * this means that we want dot(2*dot(N', I)*N' - I, Ng) = t.
+ *
+ * Since the Z axis of our local coordinate system is Ng, dot(x, Ng) is just x.z, so we get
+ * 2*dot(N', I)*N'.z - I.z = t.
+ *
+ * The rotation is simple to express in the coordinate system we formed -
+ * since N lies in the X-Z-plane, we know that N' will also lie in the X-Z-plane,
+ * so N'.y = 0 and therefore dot(N', I) = N'.x*I.x + N'.z*I.z .
+ *
+ * Furthermore, we want N' to be normalized, so N'.x = sqrt(1 - N'.z^2).
+ *
+ * With these simplifications,
+ * we get the final equation 2*(sqrt(1 - N'.z^2)*I.x + N'.z*I.z)*N'.z - I.z = t.
+ *
+ * The only unknown here is N'.z, so we can solve for that.
+ *
+ * The equation has four solutions in general:
+ *
+ * N'.z = +-sqrt(0.5*(+-sqrt(I.x^2*(I.x^2 + I.z^2 - t^2)) + t*I.z + I.x^2 + I.z^2)/(I.x^2 + I.z^2))
+ * We can simplify this expression a bit by grouping terms:
+ *
+ * a = I.x^2 + I.z^2
+ * b = sqrt(I.x^2 * (a - t^2))
+ * c = I.z*t + a
+ * N'.z = +-sqrt(0.5*(+-b + c)/a)
+ *
+ * Two solutions can immediately be discarded because they're negative so N' would lie in the
+ * lower hemisphere.
+ */
+ /* clang-format on */
+
+ float Ix = dot(I, X), Iz = dot(I, Ng);
+ float Ix2 = sqr(Ix), Iz2 = sqr(Iz);
+ float a = Ix2 + Iz2;
+
+ float b = safe_sqrtf(Ix2 * (a - sqr(threshold)));
+ float c = Iz * threshold + a;
+
+ /* Evaluate both solutions.
+ * In many cases one can be immediately discarded (if N'.z would be imaginary or larger than
+ * one), so check for that first. If no option is viable (might happen in extreme cases like N
+ * being in the wrong hemisphere), give up and return Ng. */
+ float fac = 0.5f / a;
+ float N1_z2 = fac * (b + c), N2_z2 = fac * (-b + c);
+ bool valid1 = (N1_z2 > 1e-5f) && (N1_z2 <= (1.0f + 1e-5f));
+ bool valid2 = (N2_z2 > 1e-5f) && (N2_z2 <= (1.0f + 1e-5f));
+
+ float2 N_new;
+ if (valid1 && valid2) {
+ /* If both are possible, do the expensive reflection-based check. */
+ float2 N1 = make_float2(safe_sqrtf(1.0f - N1_z2), safe_sqrtf(N1_z2));
+ float2 N2 = make_float2(safe_sqrtf(1.0f - N2_z2), safe_sqrtf(N2_z2));
+
+ float R1 = 2 * (N1.x * Ix + N1.y * Iz) * N1.y - Iz;
+ float R2 = 2 * (N2.x * Ix + N2.y * Iz) * N2.y - Iz;
+
+ valid1 = (R1 >= 1e-5f);
+ valid2 = (R2 >= 1e-5f);
+ if (valid1 && valid2) {
+ /* If both solutions are valid, return the one with the shallower reflection since it will be
+ * closer to the input (if the original reflection wasn't shallow, we would not be in this
+ * part of the function). */
+ N_new = (R1 < R2) ? N1 : N2;
}
+ else {
+ /* If only one reflection is valid (= positive), pick that one. */
+ N_new = (R1 > R2) ? N1 : N2;
+ }
+ }
+ else if (valid1 || valid2) {
+ /* Only one solution passes the N'.z criterium, so pick that one. */
+ float Nz2 = valid1 ? N1_z2 : N2_z2;
+ N_new = make_float2(safe_sqrtf(1.0f - Nz2), safe_sqrtf(Nz2));
}
else {
- /* Bad incident */
- R = -I;
- NgR = dot(Ng, R);
- threshold = 0.01f;
+ return Ng;
}
- R = R + Ng * (threshold - NgR); /* Lift the reflection above the threshold. */
- return normalize(I * len(R) + R * len(I)); /* Find a bisector. */
+ return N_new.x * X + N_new.y * Ng;
}
CCL_NAMESPACE_END
diff --git a/intern/cycles/kernel/kernel_path.h b/intern/cycles/kernel/kernel_path.h
index 5681510fc25..dd2390808ea 100644
--- a/intern/cycles/kernel/kernel_path.h
+++ b/intern/cycles/kernel/kernel_path.h
@@ -65,7 +65,6 @@ ccl_device_forceinline bool kernel_path_scene_intersect(KernelGlobals *kg,
uint visibility = path_state_ray_visibility(kg, state);
if (path_state_ao_bounce(kg, state)) {
- visibility = PATH_RAY_SHADOW;
ray->t = kernel_data.background.ao_distance;
}
@@ -416,7 +415,13 @@ ccl_device void kernel_path_indirect(KernelGlobals *kg,
break;
}
else if (path_state_ao_bounce(kg, state)) {
- break;
+ if (intersection_get_shader_flags(kg, &isect) &
+ (SD_HAS_TRANSPARENT_SHADOW | SD_HAS_EMISSION)) {
+ state->flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT;
+ }
+ else {
+ break;
+ }
}
/* Setup shader data. */
@@ -554,7 +559,13 @@ ccl_device_forceinline void kernel_path_integrate(KernelGlobals *kg,
break;
}
else if (path_state_ao_bounce(kg, state)) {
- break;
+ if (intersection_get_shader_flags(kg, &isect) &
+ (SD_HAS_TRANSPARENT_SHADOW | SD_HAS_EMISSION)) {
+ state->flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT;
+ }
+ else {
+ break;
+ }
}
/* Setup shader data. */
diff --git a/intern/cycles/kernel/kernel_subsurface.h b/intern/cycles/kernel/kernel_subsurface.h
index c75958e79c5..dd922b86722 100644
--- a/intern/cycles/kernel/kernel_subsurface.h
+++ b/intern/cycles/kernel/kernel_subsurface.h
@@ -25,8 +25,9 @@ CCL_NAMESPACE_BEGIN
ccl_device_inline float3
subsurface_scatter_eval(ShaderData *sd, const ShaderClosure *sc, float disk_r, float r, bool all)
{
- /* this is the veach one-sample model with balance heuristic, some pdf
- * factors drop out when using balance heuristic weighting */
+ /* This is the Veach one-sample model with balance heuristic, some pdf
+ * factors drop out when using balance heuristic weighting. For branched
+ * path tracing (all) we sample all closure and don't use MIS. */
float3 eval_sum = zero_float3();
float pdf_sum = 0.0f;
float sample_weight_inv = 0.0f;
@@ -65,6 +66,30 @@ subsurface_scatter_eval(ShaderData *sd, const ShaderClosure *sc, float disk_r, f
return (pdf_sum > 0.0f) ? eval_sum / pdf_sum : zero_float3();
}
+ccl_device_inline float3 subsurface_scatter_walk_eval(ShaderData *sd,
+ const ShaderClosure *sc,
+ float3 throughput,
+ bool all)
+{
+ /* This is the Veach one-sample model with balance heuristic, some pdf
+ * factors drop out when using balance heuristic weighting. For branched
+ * path tracing (all) we sample all closure and don't use MIS. */
+ if (!all) {
+ float bssrdf_weight = 0.0f;
+ float weight = sc->sample_weight;
+
+ for (int i = 0; i < sd->num_closure; i++) {
+ sc = &sd->closure[i];
+
+ if (CLOSURE_IS_BSSRDF(sc->type)) {
+ bssrdf_weight += sc->sample_weight;
+ }
+ }
+ throughput *= bssrdf_weight / weight;
+ }
+ return throughput;
+}
+
/* replace closures with a single diffuse bsdf closure after scatter step */
ccl_device void subsurface_scatter_setup_diffuse_bsdf(
KernelGlobals *kg, ShaderData *sd, ClosureType type, float roughness, float3 weight, float3 N)
@@ -437,7 +462,8 @@ ccl_device_noinline
ccl_addr_space PathState *state,
const ShaderClosure *sc,
const float bssrdf_u,
- const float bssrdf_v)
+ const float bssrdf_v,
+ bool all)
{
/* Sample diffuse surface scatter into the object. */
float3 D;
@@ -605,6 +631,13 @@ ccl_device_noinline
if (hit) {
t = ray->t;
}
+ else if (bounce == 0) {
+ /* Restore original position if nothing was hit after the first bounce,
+ * without the ray_offset() that was added to avoid self-intersection.
+ * Otherwise if that offset is relatively large compared to the scattering
+ * radius, we never go back up high enough to exit the surface. */
+ ray->P = sd->P;
+ }
/* Advance to new scatter location. */
ray->P += t * ray->D;
@@ -662,7 +695,7 @@ ccl_device_noinline
/* TODO: gain back performance lost from merging with disk BSSRDF. We
* only need to return on hit so this indirect ray push/pop overhead
* is not actually needed, but it does keep the code simpler. */
- ss_isect->weight[0] = throughput;
+ ss_isect->weight[0] = subsurface_scatter_walk_eval(sd, sc, throughput, all);
#ifdef __SPLIT_KERNEL__
ss_isect->ray = *ray;
#endif
@@ -684,7 +717,7 @@ ccl_device_inline int subsurface_scatter_multi_intersect(KernelGlobals *kg,
return subsurface_scatter_disk(kg, ss_isect, sd, sc, lcg_state, bssrdf_u, bssrdf_v, all);
}
else {
- return subsurface_random_walk(kg, ss_isect, sd, state, sc, bssrdf_u, bssrdf_v);
+ return subsurface_random_walk(kg, ss_isect, sd, state, sc, bssrdf_u, bssrdf_v, all);
}
}
diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h
index df56360b1df..74fa2826cd4 100644
--- a/intern/cycles/kernel/kernel_types.h
+++ b/intern/cycles/kernel/kernel_types.h
@@ -99,27 +99,23 @@ CCL_NAMESPACE_BEGIN
#define __AO__
#define __PASSES__
#define __HAIR__
-
-/* Without these we get an AO render, used by OpenCL preview kernel. */
-#ifndef __KERNEL_AO_PREVIEW__
-# define __SVM__
-# define __EMISSION__
-# define __HOLDOUT__
-# define __MULTI_CLOSURE__
-# define __TRANSPARENT_SHADOWS__
-# define __BACKGROUND_MIS__
-# define __LAMP_MIS__
-# define __CAMERA_MOTION__
-# define __OBJECT_MOTION__
-# define __BAKING__
-# define __PRINCIPLED__
-# define __SUBSURFACE__
-# define __VOLUME__
-# define __VOLUME_SCATTER__
-# define __CMJ__
-# define __SHADOW_RECORD_ALL__
-# define __BRANCHED_PATH__
-#endif
+#define __SVM__
+#define __EMISSION__
+#define __HOLDOUT__
+#define __MULTI_CLOSURE__
+#define __TRANSPARENT_SHADOWS__
+#define __BACKGROUND_MIS__
+#define __LAMP_MIS__
+#define __CAMERA_MOTION__
+#define __OBJECT_MOTION__
+#define __BAKING__
+#define __PRINCIPLED__
+#define __SUBSURFACE__
+#define __VOLUME__
+#define __VOLUME_SCATTER__
+#define __CMJ__
+#define __SHADOW_RECORD_ALL__
+#define __BRANCHED_PATH__
/* Device specific features */
#ifdef __KERNEL_CPU__
@@ -895,6 +891,8 @@ enum ShaderDataFlag {
SD_HAS_CONSTANT_EMISSION = (1 << 27),
/* Needs to access attributes for volume rendering */
SD_NEED_VOLUME_ATTRIBUTES = (1 << 28),
+ /* Shader has emission */
+ SD_HAS_EMISSION = (1 << 29),
SD_SHADER_FLAGS = (SD_USE_MIS | SD_HAS_TRANSPARENT_SHADOW | SD_HAS_VOLUME | SD_HAS_ONLY_VOLUME |
SD_HETEROGENEOUS_VOLUME | SD_HAS_BSSRDF_BUMP | SD_VOLUME_EQUIANGULAR |
@@ -1501,9 +1499,9 @@ typedef struct KernelAreaLight {
float axisu[3];
float invarea;
float axisv[3];
- float pad1;
+ float tan_spread;
float dir[3];
- float pad2;
+ float normalize_spread;
} KernelAreaLight;
typedef struct KernelDistantLight {
diff --git a/intern/cycles/kernel/shaders/node_noise_texture.osl b/intern/cycles/kernel/shaders/node_noise_texture.osl
index 61c0216910b..01196ab633a 100644
--- a/intern/cycles/kernel/shaders/node_noise_texture.osl
+++ b/intern/cycles/kernel/shaders/node_noise_texture.osl
@@ -25,7 +25,7 @@
* coordinates to act as a seed since the noise functions don't have seed values.
* A seed value is needed for generating distortion textures and color outputs.
* The offset's components are in the range [100, 200], not too high to cause
- * bad precision and not to small to be noticeable. We use float seed because
+ * bad precision and not too small to be noticeable. We use float seed because
* OSL only support float hashes.
*/
diff --git a/intern/cycles/kernel/shaders/node_vector_math.osl b/intern/cycles/kernel/shaders/node_vector_math.osl
index 3963c23ea9c..c08d75b99ef 100644
--- a/intern/cycles/kernel/shaders/node_vector_math.osl
+++ b/intern/cycles/kernel/shaders/node_vector_math.osl
@@ -52,6 +52,9 @@ shader node_vector_math(string math_type = "add",
else if (math_type == "faceforward") {
Vector = compatible_faceforward(Vector1, Vector2, Vector3);
}
+ else if (math_type == "multiply_add") {
+ Vector = Vector1 * Vector2 + Vector3;
+ }
else if (math_type == "dot_product") {
Value = dot(Vector1, Vector2);
}
diff --git a/intern/cycles/kernel/shaders/stdcycles.h b/intern/cycles/kernel/shaders/stdcycles.h
index af7b645d9a2..dd604da68ce 100644
--- a/intern/cycles/kernel/shaders/stdcycles.h
+++ b/intern/cycles/kernel/shaders/stdcycles.h
@@ -84,30 +84,67 @@ closure color principled_hair(normal N,
closure color henyey_greenstein(float g) BUILTIN;
closure color absorption() BUILTIN;
-normal ensure_valid_reflection(normal Ng, normal I, normal N)
+normal ensure_valid_reflection(normal Ng, vector I, normal N)
{
/* The implementation here mirrors the one in kernel_montecarlo.h,
* check there for an explanation of the algorithm. */
- vector R;
- float NI = dot(N, I);
- float NgR, threshold;
-
- if (NI > 0) {
- R = (2 * NI) * N - I;
- NgR = dot(Ng, R);
- threshold = min(0.9 * dot(Ng, I), 0.01);
- if (NgR >= threshold) {
- return N;
+
+ float sqr(float x)
+ {
+ return x * x;
+ }
+
+ vector R = 2 * dot(N, I) * N - I;
+
+ float threshold = min(0.9 * dot(Ng, I), 0.01);
+ if (dot(Ng, R) >= threshold) {
+ return N;
+ }
+
+ float NdotNg = dot(N, Ng);
+ vector X = normalize(N - NdotNg * Ng);
+
+ float Ix = dot(I, X), Iz = dot(I, Ng);
+ float Ix2 = sqr(Ix), Iz2 = sqr(Iz);
+ float a = Ix2 + Iz2;
+
+ float b = sqrt(Ix2 * (a - sqr(threshold)));
+ float c = Iz * threshold + a;
+
+ float fac = 0.5 / a;
+ float N1_z2 = fac * (b + c), N2_z2 = fac * (-b + c);
+ int valid1 = (N1_z2 > 1e-5) && (N1_z2 <= (1.0 + 1e-5));
+ int valid2 = (N2_z2 > 1e-5) && (N2_z2 <= (1.0 + 1e-5));
+
+ float N_new_x, N_new_z;
+ if (valid1 && valid2) {
+ float N1_x = sqrt(1.0 - N1_z2), N1_z = sqrt(N1_z2);
+ float N2_x = sqrt(1.0 - N2_z2), N2_z = sqrt(N2_z2);
+
+ float R1 = 2 * (N1_x * Ix + N1_z * Iz) * N1_z - Iz;
+ float R2 = 2 * (N2_x * Ix + N2_z * Iz) * N2_z - Iz;
+
+ valid1 = (R1 >= 1e-5);
+ valid2 = (R2 >= 1e-5);
+ if (valid1 && valid2) {
+ N_new_x = (R1 < R2) ? N1_x : N2_x;
+ N_new_z = (R1 < R2) ? N1_z : N2_z;
+ }
+ else {
+ N_new_x = (R1 > R2) ? N1_x : N2_x;
+ N_new_z = (R1 > R2) ? N1_z : N2_z;
}
}
+ else if (valid1 || valid2) {
+ float Nz2 = valid1 ? N1_z2 : N2_z2;
+ N_new_x = sqrt(1.0 - Nz2);
+ N_new_z = sqrt(Nz2);
+ }
else {
- R = -I;
- NgR = dot(Ng, R);
- threshold = 0.01;
+ return Ng;
}
- R = R + Ng * (threshold - NgR);
- return normalize(I * length(R) + R * length(I));
+ return N_new_x * X + N_new_z * Ng;
}
#endif /* CCL_STDOSL_H */
diff --git a/intern/cycles/kernel/svm/svm_math.h b/intern/cycles/kernel/svm/svm_math.h
index dda2e50f916..733ea28f9e5 100644
--- a/intern/cycles/kernel/svm/svm_math.h
+++ b/intern/cycles/kernel/svm/svm_math.h
@@ -58,7 +58,8 @@ ccl_device void svm_node_vector_math(KernelGlobals *kg,
float3 vector;
/* 3 Vector Operators */
- if (type == NODE_VECTOR_MATH_WRAP || type == NODE_VECTOR_MATH_FACEFORWARD) {
+ if (type == NODE_VECTOR_MATH_WRAP || type == NODE_VECTOR_MATH_FACEFORWARD ||
+ type == NODE_VECTOR_MATH_MULTIPLY_ADD) {
uint4 extra_node = read_node(kg, offset);
c = stack_load_float3(stack, extra_node.x);
}
diff --git a/intern/cycles/kernel/svm/svm_math_util.h b/intern/cycles/kernel/svm/svm_math_util.h
index 389c44ab1da..9e654f2247f 100644
--- a/intern/cycles/kernel/svm/svm_math_util.h
+++ b/intern/cycles/kernel/svm/svm_math_util.h
@@ -52,6 +52,9 @@ ccl_device void svm_vector_math(float *value,
case NODE_VECTOR_MATH_FACEFORWARD:
*vector = faceforward(a, b, c);
break;
+ case NODE_VECTOR_MATH_MULTIPLY_ADD:
+ *vector = a * b + c;
+ break;
case NODE_VECTOR_MATH_DOT_PRODUCT:
*value = dot(a, b);
break;
@@ -242,12 +245,15 @@ ccl_device float3 svm_math_blackbody_color(float t)
return make_float3(4.70366907f, 0.0f, 0.0f);
}
+ /* Manually align for readability. */
+ /* clang-format off */
int i = (t >= 6365.0f) ? 5 :
(t >= 3315.0f) ? 4 :
(t >= 1902.0f) ? 3 :
(t >= 1449.0f) ? 2 :
(t >= 1167.0f) ? 1 :
0;
+ /* clang-format on */
ccl_constant float *r = blackbody_table_r[i];
ccl_constant float *g = blackbody_table_g[i];
diff --git a/intern/cycles/kernel/svm/svm_noisetex.h b/intern/cycles/kernel/svm/svm_noisetex.h
index 920dd7d9d02..61fd9553802 100644
--- a/intern/cycles/kernel/svm/svm_noisetex.h
+++ b/intern/cycles/kernel/svm/svm_noisetex.h
@@ -20,7 +20,7 @@ CCL_NAMESPACE_BEGIN
* coordinates to act as a seed since the noise functions don't have seed values.
* A seed value is needed for generating distortion textures and color outputs.
* The offset's components are in the range [100, 200], not too high to cause
- * bad precision and not to small to be noticeable. We use float seed because
+ * bad precision and not too small to be noticeable. We use float seed because
* OSL only support float hashes.
*/
diff --git a/intern/cycles/kernel/svm/svm_tex_coord.h b/intern/cycles/kernel/svm/svm_tex_coord.h
index 4fe940f1a67..fc46bb584be 100644
--- a/intern/cycles/kernel/svm/svm_tex_coord.h
+++ b/intern/cycles/kernel/svm/svm_tex_coord.h
@@ -370,10 +370,13 @@ ccl_device void svm_node_tangent(KernelGlobals *kg, ShaderData *sd, float *stack
if (direction_type == NODE_TANGENT_UVMAP) {
/* UV map */
- if (desc.offset == ATTR_STD_NOT_FOUND)
- tangent = make_float3(0.0f, 0.0f, 0.0f);
- else
+ if (desc.offset == ATTR_STD_NOT_FOUND) {
+ stack_store_float3(stack, tangent_offset, zero_float3());
+ return;
+ }
+ else {
tangent = attribute_value;
+ }
}
else {
/* radial */
diff --git a/intern/cycles/kernel/svm/svm_types.h b/intern/cycles/kernel/svm/svm_types.h
index 64a8f82a094..062afcfa5ac 100644
--- a/intern/cycles/kernel/svm/svm_types.h
+++ b/intern/cycles/kernel/svm/svm_types.h
@@ -341,6 +341,7 @@ typedef enum NodeVectorMathType {
NODE_VECTOR_MATH_TANGENT,
NODE_VECTOR_MATH_REFRACT,
NODE_VECTOR_MATH_FACEFORWARD,
+ NODE_VECTOR_MATH_MULTIPLY_ADD,
} NodeVectorMathType;
typedef enum NodeClampType {
diff --git a/intern/cycles/render/CMakeLists.txt b/intern/cycles/render/CMakeLists.txt
index c67919b375a..feead27c5ca 100644
--- a/intern/cycles/render/CMakeLists.txt
+++ b/intern/cycles/render/CMakeLists.txt
@@ -24,6 +24,7 @@ set(INC_SYS
set(SRC
alembic.cpp
+ alembic_read.cpp
attribute.cpp
background.cpp
bake.cpp
@@ -67,6 +68,7 @@ set(SRC
set(SRC_HEADERS
alembic.h
+ alembic_read.h
attribute.h
bake.h
background.h
diff --git a/intern/cycles/render/alembic.cpp b/intern/cycles/render/alembic.cpp
index 1336f81896a..dcb456dc1ce 100644
--- a/intern/cycles/render/alembic.cpp
+++ b/intern/cycles/render/alembic.cpp
@@ -16,6 +16,7 @@
#include "render/alembic.h"
+#include "render/alembic_read.h"
#include "render/camera.h"
#include "render/curves.h"
#include "render/mesh.h"
@@ -34,7 +35,48 @@ using namespace Alembic::AbcGeom;
CCL_NAMESPACE_BEGIN
-/* TODO(@kevindietrich): motion blur support */
+/* TODO(kevindietrich): motion blur support. */
+
+template<typename SchemaType>
+static vector<FaceSetShaderIndexPair> parse_face_sets_for_shader_assignment(
+ SchemaType &schema, const array<Node *> &used_shaders)
+{
+ vector<FaceSetShaderIndexPair> result;
+
+ std::vector<std::string> face_set_names;
+ schema.getFaceSetNames(face_set_names);
+
+ if (face_set_names.empty()) {
+ return result;
+ }
+
+ for (const std::string &face_set_name : face_set_names) {
+ int shader_index = 0;
+
+ for (Node *node : used_shaders) {
+ if (node->name == face_set_name) {
+ break;
+ }
+
+ ++shader_index;
+ }
+
+ if (shader_index >= used_shaders.size()) {
+ /* use the first shader instead if none was found */
+ shader_index = 0;
+ }
+
+ const Alembic::AbcGeom::IFaceSet face_set = schema.getFaceSet(face_set_name);
+
+ if (!face_set.valid()) {
+ continue;
+ }
+
+ result.push_back({face_set, shader_index});
+ }
+
+ return result;
+}
void CachedData::clear()
{
@@ -54,7 +96,7 @@ void CachedData::clear()
subd_start_corner.clear();
transforms.clear();
triangles.clear();
- triangles_loops.clear();
+ uv_loops.clear();
vertices.clear();
for (CachedAttribute &attr : attributes) {
@@ -101,7 +143,7 @@ bool CachedData::is_constant() const
CHECK_IF_CONSTANT(subd_start_corner)
CHECK_IF_CONSTANT(transforms)
CHECK_IF_CONSTANT(triangles)
- CHECK_IF_CONSTANT(triangles_loops)
+ CHECK_IF_CONSTANT(uv_loops)
CHECK_IF_CONSTANT(vertices)
for (const CachedAttribute &attr : attributes) {
@@ -140,7 +182,7 @@ void CachedData::invalidate_last_loaded_time(bool attributes_only)
subd_start_corner.invalidate_last_loaded_time();
transforms.invalidate_last_loaded_time();
triangles.invalidate_last_loaded_time();
- triangles_loops.invalidate_last_loaded_time();
+ uv_loops.invalidate_last_loaded_time();
vertices.invalidate_last_loaded_time();
}
@@ -161,7 +203,7 @@ void CachedData::set_time_sampling(TimeSampling time_sampling)
subd_start_corner.set_time_sampling(time_sampling);
transforms.set_time_sampling(time_sampling);
triangles.set_time_sampling(time_sampling);
- triangles_loops.set_time_sampling(time_sampling);
+ uv_loops.set_time_sampling(time_sampling);
vertices.set_time_sampling(time_sampling);
for (CachedAttribute &attr : attributes) {
@@ -169,36 +211,6 @@ void CachedData::set_time_sampling(TimeSampling time_sampling)
}
}
-/* get the sample times to load data for the given the start and end frame of the procedural */
-static set<chrono_t> get_relevant_sample_times(AlembicProcedural *proc,
- const TimeSampling &time_sampling,
- size_t num_samples)
-{
- set<chrono_t> result;
-
- if (num_samples < 2) {
- result.insert(0.0);
- return result;
- }
-
- double start_frame = (double)(proc->get_start_frame() / proc->get_frame_rate());
- double end_frame = (double)((proc->get_end_frame() + 1) / proc->get_frame_rate());
-
- size_t start_index = time_sampling.getFloorIndex(start_frame, num_samples).first;
- size_t end_index = time_sampling.getCeilIndex(end_frame, num_samples).first;
-
- for (size_t i = start_index; i < end_index; ++i) {
- result.insert(time_sampling.getSampleTime(i));
- }
-
- return result;
-}
-
-static float3 make_float3_from_yup(const V3f &v)
-{
- return make_float3(v.x, -v.z, v.y);
-}
-
static M44d convert_yup_zup(const M44d &mtx, float scale_mult)
{
V3d scale, shear, rotation, translation;
@@ -366,249 +378,6 @@ static Transform make_transform(const M44d &a, float scale)
return trans;
}
-static void add_uvs(AlembicProcedural *proc,
- const IV2fGeomParam &uvs,
- CachedData &cached_data,
- Progress &progress)
-{
- if (uvs.getScope() != kFacevaryingScope) {
- return;
- }
-
- const TimeSamplingPtr time_sampling_ptr = uvs.getTimeSampling();
-
- TimeSampling time_sampling;
- if (time_sampling_ptr) {
- time_sampling = *time_sampling_ptr;
- }
-
- std::string name = Alembic::Abc::GetSourceName(uvs.getMetaData());
-
- /* According to the convention, primary UVs should have had their name
- * set using Alembic::Abc::SetSourceName, but you can't expect everyone
- * to follow it! :) */
- if (name.empty()) {
- name = uvs.getName();
- }
-
- CachedData::CachedAttribute &attr = cached_data.add_attribute(ustring(name), time_sampling);
- attr.std = ATTR_STD_UV;
-
- ccl::set<chrono_t> times = get_relevant_sample_times(proc, time_sampling, uvs.getNumSamples());
-
- /* Keys used to determine if the UVs do actually change over time. */
- ArraySample::Key previous_indices_key;
- ArraySample::Key previous_values_key;
-
- foreach (chrono_t time, times) {
- if (progress.get_cancel()) {
- return;
- }
-
- const ISampleSelector iss = ISampleSelector(time);
- const IV2fGeomParam::Sample uvsample = uvs.getIndexedValue(iss);
-
- if (!uvsample.valid()) {
- continue;
- }
-
- const array<int3> *triangles =
- cached_data.triangles.data_for_time_no_check(time).get_data_or_null();
- const array<int3> *triangles_loops =
- cached_data.triangles_loops.data_for_time_no_check(time).get_data_or_null();
-
- if (!triangles || !triangles_loops) {
- continue;
- }
-
- array<char> data;
- data.resize(triangles->size() * 3 * sizeof(float2));
-
- float2 *data_float2 = reinterpret_cast<float2 *>(data.data());
-
- const ArraySample::Key indices_key = uvsample.getIndices()->getKey();
- const ArraySample::Key values_key = uvsample.getVals()->getKey();
-
- if (indices_key == previous_indices_key && values_key == previous_values_key) {
- attr.data.reuse_data_for_last_time(time);
- }
- else {
- const unsigned int *indices = uvsample.getIndices()->get();
- const V2f *values = uvsample.getVals()->get();
-
- for (const int3 &loop : *triangles_loops) {
- unsigned int v0 = indices[loop.x];
- unsigned int v1 = indices[loop.y];
- unsigned int v2 = indices[loop.z];
-
- data_float2[0] = make_float2(values[v0][0], values[v0][1]);
- data_float2[1] = make_float2(values[v1][0], values[v1][1]);
- data_float2[2] = make_float2(values[v2][0], values[v2][1]);
- data_float2 += 3;
- }
-
- attr.data.add_data(data, time);
- }
-
- previous_indices_key = indices_key;
- previous_values_key = values_key;
- }
-}
-
-static void add_normals(const Int32ArraySamplePtr face_indices,
- const IN3fGeomParam &normals,
- double time,
- CachedData &cached_data)
-{
- switch (normals.getScope()) {
- case kFacevaryingScope: {
- const ISampleSelector iss = ISampleSelector(time);
- const IN3fGeomParam::Sample sample = normals.getExpandedValue(iss);
-
- if (!sample.valid()) {
- return;
- }
-
- CachedData::CachedAttribute &attr = cached_data.add_attribute(ustring(normals.getName()),
- *normals.getTimeSampling());
- attr.std = ATTR_STD_VERTEX_NORMAL;
-
- const array<float3> *vertices =
- cached_data.vertices.data_for_time_no_check(time).get_data_or_null();
-
- if (!vertices) {
- return;
- }
-
- array<char> data;
- data.resize(vertices->size() * sizeof(float3));
-
- float3 *data_float3 = reinterpret_cast<float3 *>(data.data());
-
- const int *face_indices_array = face_indices->get();
- const N3fArraySamplePtr values = sample.getVals();
-
- for (size_t i = 0; i < face_indices->size(); ++i) {
- int point_index = face_indices_array[i];
- data_float3[point_index] = make_float3_from_yup(values->get()[i]);
- }
-
- attr.data.add_data(data, time);
- break;
- }
- case kVaryingScope:
- case kVertexScope: {
- const ISampleSelector iss = ISampleSelector(time);
- const IN3fGeomParam::Sample sample = normals.getExpandedValue(iss);
-
- if (!sample.valid()) {
- return;
- }
-
- CachedData::CachedAttribute &attr = cached_data.add_attribute(ustring(normals.getName()),
- *normals.getTimeSampling());
- attr.std = ATTR_STD_VERTEX_NORMAL;
-
- const array<float3> *vertices =
- cached_data.vertices.data_for_time_no_check(time).get_data_or_null();
-
- if (!vertices) {
- return;
- }
-
- array<char> data;
- data.resize(vertices->size() * sizeof(float3));
-
- float3 *data_float3 = reinterpret_cast<float3 *>(data.data());
-
- const Imath::V3f *values = sample.getVals()->get();
-
- for (size_t i = 0; i < vertices->size(); ++i) {
- data_float3[i] = make_float3_from_yup(values[i]);
- }
-
- attr.data.add_data(data, time);
-
- break;
- }
- default: {
- break;
- }
- }
-}
-
-static void add_positions(const P3fArraySamplePtr positions, double time, CachedData &cached_data)
-{
- if (!positions) {
- return;
- }
-
- array<float3> vertices;
- vertices.reserve(positions->size());
-
- for (size_t i = 0; i < positions->size(); i++) {
- V3f f = positions->get()[i];
- vertices.push_back_reserved(make_float3_from_yup(f));
- }
-
- cached_data.vertices.add_data(vertices, time);
-}
-
-static void add_triangles(const Int32ArraySamplePtr face_counts,
- const Int32ArraySamplePtr face_indices,
- double time,
- CachedData &cached_data,
- const array<int> &polygon_to_shader)
-{
- if (!face_counts || !face_indices) {
- return;
- }
-
- const size_t num_faces = face_counts->size();
- const int *face_counts_array = face_counts->get();
- const int *face_indices_array = face_indices->get();
-
- size_t num_triangles = 0;
- for (size_t i = 0; i < face_counts->size(); i++) {
- num_triangles += face_counts_array[i] - 2;
- }
-
- array<int> shader;
- array<int3> triangles;
- array<int3> triangles_loops;
- shader.reserve(num_triangles);
- triangles.reserve(num_triangles);
- triangles_loops.reserve(num_triangles);
- int index_offset = 0;
-
- for (size_t i = 0; i < num_faces; i++) {
- int current_shader = 0;
-
- if (!polygon_to_shader.empty()) {
- current_shader = polygon_to_shader[i];
- }
-
- for (int j = 0; j < face_counts_array[i] - 2; j++) {
- int v0 = face_indices_array[index_offset];
- int v1 = face_indices_array[index_offset + j + 1];
- int v2 = face_indices_array[index_offset + j + 2];
-
- shader.push_back_reserved(current_shader);
-
- /* Alembic orders the loops following the RenderMan convention, so need to go in reverse. */
- triangles.push_back_reserved(make_int3(v2, v1, v0));
- triangles_loops.push_back_reserved(
- make_int3(index_offset + j + 2, index_offset + j + 1, index_offset));
- }
-
- index_offset += face_counts_array[i];
- }
-
- cached_data.triangles.add_data(triangles, time);
- cached_data.triangles_loops.add_data(triangles_loops, time);
- cached_data.shader.add_data(shader, time);
-}
-
NODE_DEFINE(AlembicObject)
{
NodeType *type = NodeType::add("alembic_object", create);
@@ -648,398 +417,137 @@ bool AlembicObject::has_data_loaded() const
return data_loaded;
}
-void AlembicObject::update_shader_attributes(const ICompoundProperty &arb_geom_params,
- Progress &progress)
-{
- AttributeRequestSet requested_attributes = get_requested_attributes();
-
- foreach (const AttributeRequest &attr, requested_attributes.requests) {
- if (progress.get_cancel()) {
- return;
- }
-
- bool attr_exists = false;
- foreach (CachedData::CachedAttribute &cached_attr, cached_data.attributes) {
- if (cached_attr.name == attr.name) {
- attr_exists = true;
- break;
- }
- }
-
- if (attr_exists) {
- continue;
- }
-
- read_attribute(arb_geom_params, attr.name, progress);
- }
-
- cached_data.invalidate_last_loaded_time(true);
- need_shader_update = false;
-}
-
-template<typename SchemaType>
-void AlembicObject::read_face_sets(SchemaType &schema,
- array<int> &polygon_to_shader,
- ISampleSelector sample_sel)
-{
- std::vector<std::string> face_sets;
- schema.getFaceSetNames(face_sets);
-
- if (face_sets.empty()) {
- return;
- }
-
- const Int32ArraySamplePtr face_counts = schema.getFaceCountsProperty().getValue();
-
- polygon_to_shader.resize(face_counts->size());
-
- foreach (const std::string &face_set_name, face_sets) {
- int shader_index = 0;
-
- foreach (Node *node, get_used_shaders()) {
- if (node->name == face_set_name) {
- break;
- }
-
- ++shader_index;
- }
-
- if (shader_index >= get_used_shaders().size()) {
- /* use the first shader instead if none was found */
- shader_index = 0;
- }
-
- const IFaceSet face_set = schema.getFaceSet(face_set_name);
-
- if (!face_set.valid()) {
- continue;
- }
-
- const IFaceSetSchema face_schem = face_set.getSchema();
- const IFaceSetSchema::Sample face_sample = face_schem.getValue(sample_sel);
- const Int32ArraySamplePtr group_faces = face_sample.getFaces();
- const size_t num_group_faces = group_faces->size();
-
- for (size_t l = 0; l < num_group_faces; l++) {
- size_t pos = (*group_faces)[l];
-
- if (pos >= polygon_to_shader.size()) {
- continue;
- }
-
- polygon_to_shader[pos] = shader_index;
- }
- }
-}
-
-void AlembicObject::load_all_data(AlembicProcedural *proc,
- IPolyMeshSchema &schema,
- Progress &progress)
+void AlembicObject::load_data_in_cache(CachedData &cached_data,
+ AlembicProcedural *proc,
+ IPolyMeshSchema &schema,
+ Progress &progress)
{
- cached_data.clear();
-
/* Only load data for the original Geometry. */
if (instance_of) {
return;
}
- const TimeSamplingPtr time_sampling = schema.getTimeSampling();
- cached_data.set_time_sampling(*time_sampling);
-
- const IN3fGeomParam &normals = schema.getNormalsParam();
-
- ccl::set<chrono_t> times = get_relevant_sample_times(
- proc, *time_sampling, schema.getNumSamples());
-
- /* Key used to determine if the triangles change over time, if the key is the same as the
- * last one, we can avoid creating a new entry in the cache and simply point to the last
- * frame. */
- ArraySample::Key previous_key;
-
- /* read topology */
- foreach (chrono_t time, times) {
- if (progress.get_cancel()) {
- return;
- }
-
- const ISampleSelector iss = ISampleSelector(time);
- const IPolyMeshSchema::Sample sample = schema.getValue(iss);
-
- add_positions(sample.getPositions(), time, cached_data);
-
- /* Only copy triangles for other frames if the topology is changing over time as well. */
- if (schema.getTopologyVariance() != kHomogenousTopology || cached_data.triangles.size() == 0) {
- const ArraySample::Key key = sample.getFaceIndices()->getKey();
-
- if (key == previous_key) {
- cached_data.triangles.reuse_data_for_last_time(time);
- cached_data.triangles_loops.reuse_data_for_last_time(time);
- cached_data.shader.reuse_data_for_last_time(time);
- }
- else {
- /* start by reading the face sets (per face shader), as we directly split polygons to
- * triangles
- */
- array<int> polygon_to_shader;
- read_face_sets(schema, polygon_to_shader, iss);
-
- add_triangles(
- sample.getFaceCounts(), sample.getFaceIndices(), time, cached_data, polygon_to_shader);
- }
+ cached_data.clear();
- previous_key = key;
- }
+ PolyMeshSchemaData data;
+ data.topology_variance = schema.getTopologyVariance();
+ data.time_sampling = schema.getTimeSampling();
+ data.positions = schema.getPositionsProperty();
+ data.face_counts = schema.getFaceCountsProperty();
+ data.face_indices = schema.getFaceIndicesProperty();
+ data.normals = schema.getNormalsParam();
+ data.num_samples = schema.getNumSamples();
+ data.shader_face_sets = parse_face_sets_for_shader_assignment(schema, get_used_shaders());
- if (normals.valid()) {
- add_normals(sample.getFaceIndices(), normals, time, cached_data);
- }
- }
+ read_geometry_data(proc, cached_data, data, progress);
if (progress.get_cancel()) {
return;
}
- update_shader_attributes(schema.getArbGeomParams(), progress);
+ /* Use the schema as the base compound property to also be able to look for top level properties.
+ */
+ read_attributes(
+ proc, cached_data, schema, schema.getUVsParam(), get_requested_attributes(), progress);
if (progress.get_cancel()) {
return;
}
- const IV2fGeomParam &uvs = schema.getUVsParam();
-
- if (uvs.valid()) {
- add_uvs(proc, uvs, cached_data, progress);
- }
-
+ cached_data.invalidate_last_loaded_time(true);
data_loaded = true;
}
-void AlembicObject::load_all_data(AlembicProcedural *proc, ISubDSchema &schema, Progress &progress)
+void AlembicObject::load_data_in_cache(CachedData &cached_data,
+ AlembicProcedural *proc,
+ ISubDSchema &schema,
+ Progress &progress)
{
- cached_data.clear();
-
/* Only load data for the original Geometry. */
if (instance_of) {
return;
}
- AttributeRequestSet requested_attributes = get_requested_attributes();
-
- const TimeSamplingPtr time_sampling = schema.getTimeSampling();
- cached_data.set_time_sampling(*time_sampling);
-
- ccl::set<chrono_t> times = get_relevant_sample_times(
- proc, *time_sampling, schema.getNumSamples());
-
- /* read topology */
- foreach (chrono_t time, times) {
- if (progress.get_cancel()) {
- return;
- }
-
- const ISampleSelector iss = ISampleSelector(time);
- const ISubDSchema::Sample sample = schema.getValue(iss);
-
- add_positions(sample.getPositions(), time, cached_data);
-
- const Int32ArraySamplePtr face_counts = sample.getFaceCounts();
- const Int32ArraySamplePtr face_indices = sample.getFaceIndices();
-
- /* start by reading the face sets (per face shader) */
- array<int> polygon_to_shader;
- read_face_sets(schema, polygon_to_shader, iss);
-
- /* read faces */
- array<int> subd_start_corner;
- array<int> shader;
- array<int> subd_num_corners;
- array<bool> subd_smooth;
- array<int> subd_ptex_offset;
- array<int> subd_face_corners;
-
- const size_t num_faces = face_counts->size();
- const int *face_counts_array = face_counts->get();
- const int *face_indices_array = face_indices->get();
-
- int num_ngons = 0;
- int num_corners = 0;
- for (size_t i = 0; i < face_counts->size(); i++) {
- num_ngons += (face_counts_array[i] == 4 ? 0 : 1);
- num_corners += face_counts_array[i];
- }
-
- subd_start_corner.reserve(num_faces);
- subd_num_corners.reserve(num_faces);
- subd_smooth.reserve(num_faces);
- subd_ptex_offset.reserve(num_faces);
- shader.reserve(num_faces);
- subd_face_corners.reserve(num_corners);
-
- int start_corner = 0;
- int current_shader = 0;
- int ptex_offset = 0;
-
- for (size_t i = 0; i < face_counts->size(); i++) {
- num_corners = face_counts_array[i];
-
- if (!polygon_to_shader.empty()) {
- current_shader = polygon_to_shader[i];
- }
-
- subd_start_corner.push_back_reserved(start_corner);
- subd_num_corners.push_back_reserved(num_corners);
-
- for (int j = 0; j < num_corners; ++j) {
- subd_face_corners.push_back_reserved(face_indices_array[start_corner + j]);
- }
-
- shader.push_back_reserved(current_shader);
- subd_smooth.push_back_reserved(1);
- subd_ptex_offset.push_back_reserved(ptex_offset);
-
- ptex_offset += (num_corners == 4 ? 1 : num_corners);
-
- start_corner += num_corners;
- }
-
- cached_data.shader.add_data(shader, time);
- cached_data.subd_start_corner.add_data(subd_start_corner, time);
- cached_data.subd_num_corners.add_data(subd_num_corners, time);
- cached_data.subd_smooth.add_data(subd_smooth, time);
- cached_data.subd_ptex_offset.add_data(subd_ptex_offset, time);
- cached_data.subd_face_corners.add_data(subd_face_corners, time);
- cached_data.num_ngons.add_data(num_ngons, time);
-
- /* read creases */
- Int32ArraySamplePtr creases_length = sample.getCreaseLengths();
- Int32ArraySamplePtr creases_indices = sample.getCreaseIndices();
- FloatArraySamplePtr creases_sharpnesses = sample.getCreaseSharpnesses();
-
- if (creases_length && creases_indices && creases_sharpnesses) {
- array<int> creases_edge;
- array<float> creases_weight;
-
- creases_edge.reserve(creases_sharpnesses->size() * 2);
- creases_weight.reserve(creases_sharpnesses->size());
-
- int length_offset = 0;
- int weight_offset = 0;
- for (size_t c = 0; c < creases_length->size(); ++c) {
- const int crease_length = creases_length->get()[c];
-
- for (size_t j = 0; j < crease_length - 1; ++j) {
- creases_edge.push_back_reserved(creases_indices->get()[length_offset + j]);
- creases_edge.push_back_reserved(creases_indices->get()[length_offset + j + 1]);
- creases_weight.push_back_reserved(creases_sharpnesses->get()[weight_offset++]);
- }
-
- length_offset += crease_length;
- }
-
- cached_data.subd_creases_edge.add_data(creases_edge, time);
- cached_data.subd_creases_weight.add_data(creases_weight, time);
- }
- }
+ cached_data.clear();
- /* TODO(@kevindietrich) : attributes, need test files */
+ SubDSchemaData data;
+ data.time_sampling = schema.getTimeSampling();
+ data.num_samples = schema.getNumSamples();
+ data.topology_variance = schema.getTopologyVariance();
+ data.face_counts = schema.getFaceCountsProperty();
+ data.face_indices = schema.getFaceIndicesProperty();
+ data.positions = schema.getPositionsProperty();
+ data.face_varying_interpolate_boundary = schema.getFaceVaryingInterpolateBoundaryProperty();
+ data.face_varying_propagate_corners = schema.getFaceVaryingPropagateCornersProperty();
+ data.interpolate_boundary = schema.getInterpolateBoundaryProperty();
+ data.crease_indices = schema.getCreaseIndicesProperty();
+ data.crease_lengths = schema.getCreaseLengthsProperty();
+ data.crease_sharpnesses = schema.getCreaseSharpnessesProperty();
+ data.corner_indices = schema.getCornerIndicesProperty();
+ data.corner_sharpnesses = schema.getCornerSharpnessesProperty();
+ data.holes = schema.getHolesProperty();
+ data.subdivision_scheme = schema.getSubdivisionSchemeProperty();
+ data.velocities = schema.getVelocitiesProperty();
+ data.shader_face_sets = parse_face_sets_for_shader_assignment(schema, get_used_shaders());
+
+ read_geometry_data(proc, cached_data, data, progress);
if (progress.get_cancel()) {
return;
}
+ /* Use the schema as the base compound property to also be able to look for top level properties.
+ */
+ read_attributes(
+ proc, cached_data, schema, schema.getUVsParam(), get_requested_attributes(), progress);
+
+ cached_data.invalidate_last_loaded_time(true);
data_loaded = true;
}
-void AlembicObject::load_all_data(AlembicProcedural *proc,
- const ICurvesSchema &schema,
- Progress &progress,
- float default_radius)
+void AlembicObject::load_data_in_cache(CachedData &cached_data,
+ AlembicProcedural *proc,
+ const ICurvesSchema &schema,
+ Progress &progress)
{
- cached_data.clear();
-
/* Only load data for the original Geometry. */
if (instance_of) {
return;
}
- const TimeSamplingPtr time_sampling = schema.getTimeSampling();
- cached_data.set_time_sampling(*time_sampling);
-
- ccl::set<chrono_t> times = get_relevant_sample_times(
- proc, *time_sampling, schema.getNumSamples());
-
- foreach (chrono_t time, times) {
- if (progress.get_cancel()) {
- return;
- }
-
- const ISampleSelector iss = ISampleSelector(time);
- const ICurvesSchema::Sample sample = schema.getValue(iss);
-
- const Int32ArraySamplePtr curves_num_vertices = sample.getCurvesNumVertices();
- const P3fArraySamplePtr position = sample.getPositions();
-
- const IFloatGeomParam widths_param = schema.getWidthsParam();
- FloatArraySamplePtr radiuses;
-
- if (widths_param.valid()) {
- IFloatGeomParam::Sample wsample = widths_param.getExpandedValue(iss);
- radiuses = wsample.getVals();
- }
-
- const bool do_radius = (radiuses != nullptr) && (radiuses->size() > 1);
- float radius = (radiuses && radiuses->size() == 1) ? (*radiuses)[0] : default_radius;
-
- array<float3> curve_keys;
- array<float> curve_radius;
- array<int> curve_first_key;
- array<int> curve_shader;
-
- const bool is_homogenous = schema.getTopologyVariance() == kHomogenousTopology;
-
- curve_keys.reserve(position->size());
- curve_radius.reserve(position->size());
- curve_first_key.reserve(curves_num_vertices->size());
- curve_shader.reserve(curves_num_vertices->size());
-
- int offset = 0;
- for (size_t i = 0; i < curves_num_vertices->size(); i++) {
- const int num_vertices = curves_num_vertices->get()[i];
-
- for (int j = 0; j < num_vertices; j++) {
- const V3f &f = position->get()[offset + j];
- curve_keys.push_back_reserved(make_float3_from_yup(f));
-
- if (do_radius) {
- radius = (*radiuses)[offset + j];
- }
-
- curve_radius.push_back_reserved(radius * radius_scale);
- }
-
- if (!is_homogenous || cached_data.curve_first_key.size() == 0) {
- curve_first_key.push_back_reserved(offset);
- curve_shader.push_back_reserved(0);
- }
-
- offset += num_vertices;
- }
+ cached_data.clear();
- cached_data.curve_keys.add_data(curve_keys, time);
- cached_data.curve_radius.add_data(curve_radius, time);
+ CurvesSchemaData data;
+ data.positions = schema.getPositionsProperty();
+ data.position_weights = schema.getPositionWeightsProperty();
+ data.normals = schema.getNormalsParam();
+ data.knots = schema.getKnotsProperty();
+ data.orders = schema.getOrdersProperty();
+ data.widths = schema.getWidthsParam();
+ data.velocities = schema.getVelocitiesProperty();
+ data.time_sampling = schema.getTimeSampling();
+ data.topology_variance = schema.getTopologyVariance();
+ data.num_samples = schema.getNumSamples();
+ data.num_vertices = schema.getNumVerticesProperty();
+ data.default_radius = proc->get_default_radius();
+ data.radius_scale = get_radius_scale();
+
+ read_geometry_data(proc, cached_data, data, progress);
- if (!is_homogenous || cached_data.curve_first_key.size() == 0) {
- cached_data.curve_first_key.add_data(curve_first_key, time);
- cached_data.curve_shader.add_data(curve_shader, time);
- }
+ if (progress.get_cancel()) {
+ return;
}
- // TODO(@kevindietrich): attributes, need example files
+ /* Use the schema as the base compound property to also be able to look for top level properties.
+ */
+ read_attributes(
+ proc, cached_data, schema, schema.getUVsParam(), get_requested_attributes(), progress);
+ cached_data.invalidate_last_loaded_time(true);
data_loaded = true;
}
-void AlembicObject::setup_transform_cache(float scale)
+void AlembicObject::setup_transform_cache(CachedData &cached_data, float scale)
{
cached_data.transforms.clear();
cached_data.transforms.invalidate_last_loaded_time();
@@ -1102,188 +610,6 @@ AttributeRequestSet AlembicObject::get_requested_attributes()
return requested_attributes;
}
-void AlembicObject::read_attribute(const ICompoundProperty &arb_geom_params,
- const ustring &attr_name,
- Progress &progress)
-{
- const PropertyHeader *prop = arb_geom_params.getPropertyHeader(attr_name.c_str());
-
- if (prop == nullptr) {
- return;
- }
-
- if (IV2fProperty::matches(prop->getMetaData()) && Alembic::AbcGeom::isUV(*prop)) {
- const IV2fGeomParam &param = IV2fGeomParam(arb_geom_params, prop->getName());
-
- CachedData::CachedAttribute &attribute = cached_data.add_attribute(attr_name,
- *param.getTimeSampling());
-
- for (size_t i = 0; i < param.getNumSamples(); ++i) {
- if (progress.get_cancel()) {
- return;
- }
-
- ISampleSelector iss = ISampleSelector(index_t(i));
-
- IV2fGeomParam::Sample sample;
- param.getIndexed(sample, iss);
-
- const chrono_t time = param.getTimeSampling()->getSampleTime(index_t(i));
-
- if (param.getScope() == kFacevaryingScope) {
- V2fArraySamplePtr values = sample.getVals();
- UInt32ArraySamplePtr indices = sample.getIndices();
-
- attribute.std = ATTR_STD_NONE;
- attribute.element = ATTR_ELEMENT_CORNER;
- attribute.type_desc = TypeFloat2;
-
- const array<int3> *triangles =
- cached_data.triangles.data_for_time_no_check(time).get_data_or_null();
- const array<int3> *triangles_loops =
- cached_data.triangles_loops.data_for_time_no_check(time).get_data_or_null();
-
- if (!triangles || !triangles_loops) {
- return;
- }
-
- array<char> data;
- data.resize(triangles->size() * 3 * sizeof(float2));
-
- float2 *data_float2 = reinterpret_cast<float2 *>(data.data());
-
- for (const int3 &loop : *triangles_loops) {
- unsigned int v0 = (*indices)[loop.x];
- unsigned int v1 = (*indices)[loop.y];
- unsigned int v2 = (*indices)[loop.z];
-
- data_float2[0] = make_float2((*values)[v0][0], (*values)[v0][1]);
- data_float2[1] = make_float2((*values)[v1][0], (*values)[v1][1]);
- data_float2[2] = make_float2((*values)[v2][0], (*values)[v2][1]);
- data_float2 += 3;
- }
-
- attribute.data.set_time_sampling(*param.getTimeSampling());
- attribute.data.add_data(data, time);
- }
- }
- }
- else if (IC3fProperty::matches(prop->getMetaData())) {
- const IC3fGeomParam &param = IC3fGeomParam(arb_geom_params, prop->getName());
-
- CachedData::CachedAttribute &attribute = cached_data.add_attribute(attr_name,
- *param.getTimeSampling());
-
- for (size_t i = 0; i < param.getNumSamples(); ++i) {
- if (progress.get_cancel()) {
- return;
- }
-
- ISampleSelector iss = ISampleSelector(index_t(i));
-
- IC3fGeomParam::Sample sample;
- param.getIndexed(sample, iss);
-
- const chrono_t time = param.getTimeSampling()->getSampleTime(index_t(i));
-
- C3fArraySamplePtr values = sample.getVals();
-
- attribute.std = ATTR_STD_NONE;
-
- if (param.getScope() == kVaryingScope) {
- attribute.element = ATTR_ELEMENT_CORNER_BYTE;
- attribute.type_desc = TypeRGBA;
-
- const array<int3> *triangles =
- cached_data.triangles.data_for_time_no_check(time).get_data_or_null();
-
- if (!triangles) {
- return;
- }
-
- array<char> data;
- data.resize(triangles->size() * 3 * sizeof(uchar4));
-
- uchar4 *data_uchar4 = reinterpret_cast<uchar4 *>(data.data());
-
- int offset = 0;
- for (const int3 &tri : *triangles) {
- Imath::C3f v = (*values)[tri.x];
- data_uchar4[offset + 0] = color_float_to_byte(make_float3(v.x, v.y, v.z));
-
- v = (*values)[tri.y];
- data_uchar4[offset + 1] = color_float_to_byte(make_float3(v.x, v.y, v.z));
-
- v = (*values)[tri.z];
- data_uchar4[offset + 2] = color_float_to_byte(make_float3(v.x, v.y, v.z));
-
- offset += 3;
- }
-
- attribute.data.set_time_sampling(*param.getTimeSampling());
- attribute.data.add_data(data, time);
- }
- }
- }
- else if (IC4fProperty::matches(prop->getMetaData())) {
- const IC4fGeomParam &param = IC4fGeomParam(arb_geom_params, prop->getName());
-
- CachedData::CachedAttribute &attribute = cached_data.add_attribute(attr_name,
- *param.getTimeSampling());
-
- for (size_t i = 0; i < param.getNumSamples(); ++i) {
- if (progress.get_cancel()) {
- return;
- }
-
- ISampleSelector iss = ISampleSelector(index_t(i));
-
- IC4fGeomParam::Sample sample;
- param.getIndexed(sample, iss);
-
- const chrono_t time = param.getTimeSampling()->getSampleTime(index_t(i));
-
- C4fArraySamplePtr values = sample.getVals();
-
- attribute.std = ATTR_STD_NONE;
-
- if (param.getScope() == kVaryingScope) {
- attribute.element = ATTR_ELEMENT_CORNER_BYTE;
- attribute.type_desc = TypeRGBA;
-
- const array<int3> *triangles =
- cached_data.triangles.data_for_time_no_check(time).get_data_or_null();
-
- if (!triangles) {
- return;
- }
-
- array<char> data;
- data.resize(triangles->size() * 3 * sizeof(uchar4));
-
- uchar4 *data_uchar4 = reinterpret_cast<uchar4 *>(data.data());
-
- int offset = 0;
- for (const int3 &tri : *triangles) {
- Imath::C4f v = (*values)[tri.x];
- data_uchar4[offset + 0] = color_float4_to_uchar4(make_float4(v.r, v.g, v.b, v.a));
-
- v = (*values)[tri.y];
- data_uchar4[offset + 1] = color_float4_to_uchar4(make_float4(v.r, v.g, v.b, v.a));
-
- v = (*values)[tri.z];
- data_uchar4[offset + 2] = color_float4_to_uchar4(make_float4(v.r, v.g, v.b, v.a));
-
- offset += 3;
- }
-
- attribute.data.set_time_sampling(*param.getTimeSampling());
- attribute.data.add_data(data, time);
- }
- }
- }
-}
-
/* Update existing attributes and remove any attribute not in the cached_data, those attributes
* were added by Cycles (e.g. face normals) */
static void update_attributes(AttributeSet &attributes, CachedData &cached_data, double frame_time)
@@ -1291,7 +617,11 @@ static void update_attributes(AttributeSet &attributes, CachedData &cached_data,
set<Attribute *> cached_attributes;
for (CachedData::CachedAttribute &attribute : cached_data.attributes) {
- const array<char> *attr_data = attribute.data.data_for_time(frame_time).get_data_or_null();
+ const CacheLookupResult<array<char>> result = attribute.data.data_for_time(frame_time);
+
+ if (result.has_no_data_for_time()) {
+ continue;
+ }
Attribute *attr = nullptr;
if (attribute.std != ATTR_STD_NONE) {
@@ -1304,18 +634,19 @@ static void update_attributes(AttributeSet &attributes, CachedData &cached_data,
cached_attributes.insert(attr);
- if (!attr_data) {
- /* no new data */
+ if (!result.has_new_data()) {
continue;
}
+ const ccl::array<char> &attr_data = result.get_data();
+
/* weak way of detecting if the topology has changed
* todo: reuse code from device_update patch */
- if (attr->buffer.size() != attr_data->size()) {
- attr->buffer.resize(attr_data->size());
+ if (attr->buffer.size() != attr_data.size()) {
+ attr->buffer.resize(attr_data.size());
}
- memcpy(attr->data(), attr_data->data(), attr_data->size());
+ memcpy(attr->data(), attr_data.data(), attr_data.size());
attr->modified = true;
}
@@ -1323,8 +654,7 @@ static void update_attributes(AttributeSet &attributes, CachedData &cached_data,
list<Attribute>::iterator it;
for (it = attributes.attributes.begin(); it != attributes.attributes.end();) {
if (cached_attributes.find(&(*it)) == cached_attributes.end()) {
- attributes.attributes.erase(it++);
- attributes.modified = true;
+ attributes.remove(it++);
continue;
}
@@ -1407,6 +737,15 @@ void AlembicProcedural::generate(Scene *scene, Progress &progress)
need_data_updates = true;
}
+ /* Check if the shaders were modified. */
+ if (object->used_shaders_is_modified() && object->get_object() &&
+ object->get_object()->get_geometry()) {
+ Geometry *geometry = object->get_object()->get_geometry();
+ array<Node *> used_shaders = object->get_used_shaders();
+ geometry->set_used_shaders(used_shaders);
+ need_shader_updates = true;
+ }
+
/* Check for changes in shaders (e.g. newly requested attributes). */
foreach (Node *shader_node, object->get_used_shaders()) {
Shader *shader = static_cast<Shader *>(shader_node);
@@ -1467,6 +806,7 @@ void AlembicProcedural::generate(Scene *scene, Progress &progress)
read_subd(object, frame_time);
}
+ object->need_shader_update = false;
object->clear_modified();
}
@@ -1586,6 +926,11 @@ void AlembicProcedural::read_mesh(AlembicObject *abc_object, Abc::chrono_t frame
Mesh *mesh = static_cast<Mesh *>(object->get_geometry());
+ /* Make sure shader ids are also updated. */
+ if (mesh->used_shaders_is_modified()) {
+ mesh->tag_shader_modified();
+ }
+
cached_data.vertices.copy_to_socket(frame_time, mesh, mesh->get_verts_socket());
cached_data.shader.copy_to_socket(frame_time, mesh, mesh->get_shader_socket());
@@ -1653,6 +998,11 @@ void AlembicProcedural::read_subd(AlembicObject *abc_object, Abc::chrono_t frame
Mesh *mesh = static_cast<Mesh *>(object->get_geometry());
+ /* Make sure shader ids are also updated. */
+ if (mesh->used_shaders_is_modified()) {
+ mesh->tag_shader_modified();
+ }
+
/* Cycles overwrites the original triangles when computing displacement, so we always have to
* repass the data if something is animated (vertices most likely) to avoid buffer overflows. */
if (!cached_data.is_constant()) {
@@ -1743,6 +1093,11 @@ void AlembicProcedural::read_curves(AlembicObject *abc_object, Abc::chrono_t fra
Hair *hair = static_cast<Hair *>(object->get_geometry());
+ /* Make sure shader ids are also updated. */
+ if (hair->used_shaders_is_modified()) {
+ hair->tag_curve_shader_modified();
+ }
+
cached_data.curve_keys.copy_to_socket(frame_time, hair, hair->get_curve_keys_socket());
cached_data.curve_radius.copy_to_socket(frame_time, hair, hair->get_curve_radius_socket());
@@ -1936,12 +1291,17 @@ void AlembicProcedural::build_caches(Progress &progress)
if (!object->has_data_loaded()) {
IPolyMesh polymesh(object->iobject, Alembic::Abc::kWrapExisting);
IPolyMeshSchema schema = polymesh.getSchema();
- object->load_all_data(this, schema, progress);
+ object->load_data_in_cache(object->get_cached_data(), this, schema, progress);
}
else if (object->need_shader_update) {
IPolyMesh polymesh(object->iobject, Alembic::Abc::kWrapExisting);
IPolyMeshSchema schema = polymesh.getSchema();
- object->update_shader_attributes(schema.getArbGeomParams(), progress);
+ read_attributes(this,
+ object->get_cached_data(),
+ schema,
+ schema.getUVsParam(),
+ object->get_requested_attributes(),
+ progress);
}
}
else if (object->schema_type == AlembicObject::CURVES) {
@@ -1949,24 +1309,29 @@ void AlembicProcedural::build_caches(Progress &progress)
object->radius_scale_is_modified()) {
ICurves curves(object->iobject, Alembic::Abc::kWrapExisting);
ICurvesSchema schema = curves.getSchema();
- object->load_all_data(this, schema, progress, default_radius);
+ object->load_data_in_cache(object->get_cached_data(), this, schema, progress);
}
}
else if (object->schema_type == AlembicObject::SUBD) {
if (!object->has_data_loaded()) {
ISubD subd_mesh(object->iobject, Alembic::Abc::kWrapExisting);
ISubDSchema schema = subd_mesh.getSchema();
- object->load_all_data(this, schema, progress);
+ object->load_data_in_cache(object->get_cached_data(), this, schema, progress);
}
else if (object->need_shader_update) {
ISubD subd_mesh(object->iobject, Alembic::Abc::kWrapExisting);
ISubDSchema schema = subd_mesh.getSchema();
- object->update_shader_attributes(schema.getArbGeomParams(), progress);
+ read_attributes(this,
+ object->get_cached_data(),
+ schema,
+ schema.getUVsParam(),
+ object->get_requested_attributes(),
+ progress);
}
}
if (scale_is_modified() || object->get_cached_data().transforms.size() == 0) {
- object->setup_transform_cache(scale);
+ object->setup_transform_cache(object->get_cached_data(), scale);
}
}
}
diff --git a/intern/cycles/render/alembic.h b/intern/cycles/render/alembic.h
index d0c5856a353..61c0e40fe4a 100644
--- a/intern/cycles/render/alembic.h
+++ b/intern/cycles/render/alembic.h
@@ -152,6 +152,10 @@ template<typename T> class DataStore {
double last_loaded_time = std::numeric_limits<double>::max();
public:
+ /* Keys used to compare values. */
+ Alembic::AbcCoreAbstract::ArraySample::Key key1;
+ Alembic::AbcCoreAbstract::ArraySample::Key key2;
+
void set_time_sampling(Alembic::AbcCoreAbstract::TimeSampling time_sampling_)
{
time_sampling = time_sampling_;
@@ -225,6 +229,11 @@ template<typename T> class DataStore {
index_data_map.push_back({time, data_index.source_time, data_index.index});
}
+ void add_no_data(double time)
+ {
+ index_data_map.push_back({time, time, -1ul});
+ }
+
bool is_constant() const
{
return data.size() <= 1;
@@ -239,6 +248,7 @@ template<typename T> class DataStore {
{
invalidate_last_loaded_time();
data.clear();
+ index_data_map.clear();
}
void invalidate_last_loaded_time()
@@ -283,7 +293,7 @@ struct CachedData {
DataStore<array<int3>> triangles{};
/* triangle "loops" are the polygons' vertices indices used for indexing face varying attributes
* (like UVs) */
- DataStore<array<int3>> triangles_loops{};
+ DataStore<array<int>> uv_loops{};
DataStore<array<int>> shader{};
/* subd data */
@@ -361,16 +371,18 @@ class AlembicObject : public Node {
void set_object(Object *object);
Object *get_object();
- void load_all_data(AlembicProcedural *proc,
- Alembic::AbcGeom::IPolyMeshSchema &schema,
- Progress &progress);
- void load_all_data(AlembicProcedural *proc,
- Alembic::AbcGeom::ISubDSchema &schema,
- Progress &progress);
- void load_all_data(AlembicProcedural *proc,
- const Alembic::AbcGeom::ICurvesSchema &schema,
- Progress &progress,
- float default_radius);
+ void load_data_in_cache(CachedData &cached_data,
+ AlembicProcedural *proc,
+ Alembic::AbcGeom::IPolyMeshSchema &schema,
+ Progress &progress);
+ void load_data_in_cache(CachedData &cached_data,
+ AlembicProcedural *proc,
+ Alembic::AbcGeom::ISubDSchema &schema,
+ Progress &progress);
+ void load_data_in_cache(CachedData &cached_data,
+ AlembicProcedural *proc,
+ const Alembic::AbcGeom::ICurvesSchema &schema,
+ Progress &progress);
bool has_data_loaded() const;
@@ -396,33 +408,21 @@ class AlembicObject : public Node {
CachedData &get_cached_data()
{
- return cached_data;
+ return cached_data_;
}
bool is_constant() const
{
- return cached_data.is_constant();
+ return cached_data_.is_constant();
}
Object *object = nullptr;
bool data_loaded = false;
- CachedData cached_data;
-
- void update_shader_attributes(const Alembic::AbcGeom::ICompoundProperty &arb_geom_params,
- Progress &progress);
-
- void read_attribute(const Alembic::AbcGeom::ICompoundProperty &arb_geom_params,
- const ustring &attr_name,
- Progress &progress);
-
- template<typename SchemaType>
- void read_face_sets(SchemaType &schema,
- array<int> &polygon_to_shader,
- Alembic::AbcGeom::ISampleSelector sample_sel);
+ CachedData cached_data_;
- void setup_transform_cache(float scale);
+ void setup_transform_cache(CachedData &cached_data, float scale);
AttributeRequestSet get_requested_attributes();
};
@@ -480,16 +480,23 @@ class AlembicProcedural : public Procedural {
* invocation, and updates the data on subsequent invocations if the frame changed. */
void generate(Scene *scene, Progress &progress);
- /* Add an object to our list of objects, and tag the socket as modified. */
- void add_object(AlembicObject *object);
-
/* Tag for an update only if something was modified. */
void tag_update(Scene *scene);
- /* Returns a pointer to an existing or a newly created AlembicObject for the given path. */
+ /* This should be called by scene exporters to request the rendering of an object located
+ * in the Alembic archive at the given path.
+ *
+ * Since we lazily load object, the function does not validate the existence of the object
+ * in the archive. If no objects with such path if found in the archive during the next call
+ * to `generate`, it will be ignored.
+ *
+ * Returns a pointer to an existing or a newly created AlembicObject for the given path. */
AlembicObject *get_or_create_object(const ustring &path);
private:
+ /* Add an object to our list of objects, and tag the socket as modified. */
+ void add_object(AlembicObject *object);
+
/* Load the data for all the objects whose data has not yet been loaded. */
void load_objects(Progress &progress);
diff --git a/intern/cycles/render/alembic_read.cpp b/intern/cycles/render/alembic_read.cpp
new file mode 100644
index 00000000000..33ce3502879
--- /dev/null
+++ b/intern/cycles/render/alembic_read.cpp
@@ -0,0 +1,1008 @@
+/*
+ * Copyright 2021 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/alembic_read.h"
+
+#include "render/alembic.h"
+#include "render/mesh.h"
+
+#include "util/util_progress.h"
+
+#ifdef WITH_ALEMBIC
+
+using namespace Alembic::AbcGeom;
+
+CCL_NAMESPACE_BEGIN
+
+static float3 make_float3_from_yup(const V3f &v)
+{
+ return make_float3(v.x, -v.z, v.y);
+}
+
+/* get the sample times to load data for the given the start and end frame of the procedural */
+static set<chrono_t> get_relevant_sample_times(AlembicProcedural *proc,
+ const TimeSampling &time_sampling,
+ size_t num_samples)
+{
+ set<chrono_t> result;
+
+ if (num_samples < 2) {
+ result.insert(0.0);
+ return result;
+ }
+
+ // load the data for the entire animation
+ const double start_frame = static_cast<double>(proc->get_start_frame());
+ const double end_frame = static_cast<double>(proc->get_end_frame());
+
+ const double frame_rate = static_cast<double>(proc->get_frame_rate());
+ const double start_time = start_frame / frame_rate;
+ const double end_time = (end_frame + 1) / frame_rate;
+
+ const size_t start_index = time_sampling.getFloorIndex(start_time, num_samples).first;
+ const size_t end_index = time_sampling.getCeilIndex(end_time, num_samples).first;
+
+ for (size_t i = start_index; i < end_index; ++i) {
+ result.insert(time_sampling.getSampleTime(i));
+ }
+
+ return result;
+}
+
+/* Main function to read data, this will iterate over all the relevant sample times for the
+ * duration of the requested animation, and call the DataReadingFunc for each of those sample time.
+ */
+template<typename Params, typename DataReadingFunc>
+static void read_data_loop(AlembicProcedural *proc,
+ CachedData &cached_data,
+ const Params &params,
+ DataReadingFunc &&func,
+ Progress &progress)
+{
+ const std::set<chrono_t> times = get_relevant_sample_times(
+ proc, *params.time_sampling, params.num_samples);
+
+ cached_data.set_time_sampling(*params.time_sampling);
+
+ for (chrono_t time : times) {
+ if (progress.get_cancel()) {
+ return;
+ }
+
+ func(cached_data, params, time);
+ }
+}
+
+/* Polygon Mesh Geometries. */
+
+/* Compute the vertex normals in case none are present in the IPolyMeshSchema, this is mostly used
+ * to avoid computing them in the GeometryManager in order to speed up data updates. */
+static void compute_vertex_normals(CachedData &cache, double current_time)
+{
+ if (cache.vertices.size() == 0) {
+ return;
+ }
+
+ CachedData::CachedAttribute &attr_normal = cache.add_attribute(
+ ustring("N"), cache.vertices.get_time_sampling());
+ attr_normal.std = ATTR_STD_VERTEX_NORMAL;
+ attr_normal.element = ATTR_ELEMENT_VERTEX;
+ attr_normal.type_desc = TypeNormal;
+
+ const array<float3> *vertices =
+ cache.vertices.data_for_time_no_check(current_time).get_data_or_null();
+ const array<int3> *triangles =
+ cache.triangles.data_for_time_no_check(current_time).get_data_or_null();
+
+ if (!vertices || !triangles) {
+ attr_normal.data.add_no_data(current_time);
+ return;
+ }
+
+ array<char> attr_data(vertices->size() * sizeof(float3));
+ float3 *attr_ptr = reinterpret_cast<float3 *>(attr_data.data());
+ memset(attr_ptr, 0, vertices->size() * sizeof(float3));
+
+ for (size_t t = 0; t < triangles->size(); ++t) {
+ const int3 tri_int3 = triangles->data()[t];
+ Mesh::Triangle tri{};
+ tri.v[0] = tri_int3[0];
+ tri.v[1] = tri_int3[1];
+ tri.v[2] = tri_int3[2];
+
+ const float3 tri_N = tri.compute_normal(vertices->data());
+
+ for (int v = 0; v < 3; ++v) {
+ attr_ptr[tri_int3[v]] += tri_N;
+ }
+ }
+
+ for (size_t v = 0; v < vertices->size(); ++v) {
+ attr_ptr[v] = normalize(attr_ptr[v]);
+ }
+
+ attr_normal.data.add_data(attr_data, current_time);
+}
+
+static void add_normals(const Int32ArraySamplePtr face_indices,
+ const IN3fGeomParam &normals,
+ double time,
+ CachedData &cached_data)
+{
+ switch (normals.getScope()) {
+ case kFacevaryingScope: {
+ const ISampleSelector iss = ISampleSelector(time);
+ const IN3fGeomParam::Sample sample = normals.getExpandedValue(iss);
+
+ if (!sample.valid()) {
+ return;
+ }
+
+ CachedData::CachedAttribute &attr = cached_data.add_attribute(ustring(normals.getName()),
+ *normals.getTimeSampling());
+ attr.std = ATTR_STD_VERTEX_NORMAL;
+
+ const array<float3> *vertices =
+ cached_data.vertices.data_for_time_no_check(time).get_data_or_null();
+
+ if (!vertices) {
+ return;
+ }
+
+ array<char> data;
+ data.resize(vertices->size() * sizeof(float3));
+
+ float3 *data_float3 = reinterpret_cast<float3 *>(data.data());
+
+ const int *face_indices_array = face_indices->get();
+ const N3fArraySamplePtr values = sample.getVals();
+
+ for (size_t i = 0; i < face_indices->size(); ++i) {
+ int point_index = face_indices_array[i];
+ data_float3[point_index] = make_float3_from_yup(values->get()[i]);
+ }
+
+ attr.data.add_data(data, time);
+ break;
+ }
+ case kVaryingScope:
+ case kVertexScope: {
+ const ISampleSelector iss = ISampleSelector(time);
+ const IN3fGeomParam::Sample sample = normals.getExpandedValue(iss);
+
+ if (!sample.valid()) {
+ return;
+ }
+
+ CachedData::CachedAttribute &attr = cached_data.add_attribute(ustring(normals.getName()),
+ *normals.getTimeSampling());
+ attr.std = ATTR_STD_VERTEX_NORMAL;
+
+ const array<float3> *vertices =
+ cached_data.vertices.data_for_time_no_check(time).get_data_or_null();
+
+ if (!vertices) {
+ return;
+ }
+
+ array<char> data;
+ data.resize(vertices->size() * sizeof(float3));
+
+ float3 *data_float3 = reinterpret_cast<float3 *>(data.data());
+
+ const Imath::V3f *values = sample.getVals()->get();
+
+ for (size_t i = 0; i < vertices->size(); ++i) {
+ data_float3[i] = make_float3_from_yup(values[i]);
+ }
+
+ attr.data.add_data(data, time);
+
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+}
+
+static void add_positions(const P3fArraySamplePtr positions, double time, CachedData &cached_data)
+{
+ if (!positions) {
+ return;
+ }
+
+ array<float3> vertices;
+ vertices.reserve(positions->size());
+
+ for (size_t i = 0; i < positions->size(); i++) {
+ V3f f = positions->get()[i];
+ vertices.push_back_reserved(make_float3_from_yup(f));
+ }
+
+ cached_data.vertices.add_data(vertices, time);
+}
+
+static void add_triangles(const Int32ArraySamplePtr face_counts,
+ const Int32ArraySamplePtr face_indices,
+ double time,
+ CachedData &cached_data,
+ const array<int> &polygon_to_shader)
+{
+ if (!face_counts || !face_indices) {
+ return;
+ }
+
+ const size_t num_faces = face_counts->size();
+ const int *face_counts_array = face_counts->get();
+ const int *face_indices_array = face_indices->get();
+
+ size_t num_triangles = 0;
+ for (size_t i = 0; i < face_counts->size(); i++) {
+ num_triangles += face_counts_array[i] - 2;
+ }
+
+ array<int> shader;
+ array<int3> triangles;
+ array<int> uv_loops;
+ shader.reserve(num_triangles);
+ triangles.reserve(num_triangles);
+ uv_loops.reserve(num_triangles * 3);
+ int index_offset = 0;
+
+ for (size_t i = 0; i < num_faces; i++) {
+ int current_shader = 0;
+
+ if (!polygon_to_shader.empty()) {
+ current_shader = polygon_to_shader[i];
+ }
+
+ for (int j = 0; j < face_counts_array[i] - 2; j++) {
+ int v0 = face_indices_array[index_offset];
+ int v1 = face_indices_array[index_offset + j + 1];
+ int v2 = face_indices_array[index_offset + j + 2];
+
+ shader.push_back_reserved(current_shader);
+
+ /* Alembic orders the loops following the RenderMan convention, so need to go in reverse. */
+ triangles.push_back_reserved(make_int3(v2, v1, v0));
+ uv_loops.push_back_reserved(index_offset + j + 2);
+ uv_loops.push_back_reserved(index_offset + j + 1);
+ uv_loops.push_back_reserved(index_offset);
+ }
+
+ index_offset += face_counts_array[i];
+ }
+
+ cached_data.triangles.add_data(triangles, time);
+ cached_data.uv_loops.add_data(uv_loops, time);
+ cached_data.shader.add_data(shader, time);
+}
+
+static array<int> compute_polygon_to_shader_map(
+ const Int32ArraySamplePtr &face_counts,
+ const vector<FaceSetShaderIndexPair> &face_set_shader_index,
+ ISampleSelector sample_sel)
+{
+ if (face_set_shader_index.empty()) {
+ return {};
+ }
+
+ if (!face_counts) {
+ return {};
+ }
+
+ if (face_counts->size() == 0) {
+ return {};
+ }
+
+ array<int> polygon_to_shader(face_counts->size());
+
+ for (const FaceSetShaderIndexPair &pair : face_set_shader_index) {
+ const IFaceSet &face_set = pair.face_set;
+ const IFaceSetSchema face_schem = face_set.getSchema();
+ const IFaceSetSchema::Sample face_sample = face_schem.getValue(sample_sel);
+ const Int32ArraySamplePtr group_faces = face_sample.getFaces();
+ const size_t num_group_faces = group_faces->size();
+
+ for (size_t l = 0; l < num_group_faces; l++) {
+ size_t pos = (*group_faces)[l];
+
+ if (pos >= polygon_to_shader.size()) {
+ continue;
+ }
+
+ polygon_to_shader[pos] = pair.shader_index;
+ }
+ }
+
+ return polygon_to_shader;
+}
+
+static void read_poly_mesh_geometry(CachedData &cached_data,
+ const PolyMeshSchemaData &data,
+ chrono_t time)
+{
+ const ISampleSelector iss = ISampleSelector(time);
+
+ add_positions(data.positions.getValue(iss), time, cached_data);
+
+ const Int32ArraySamplePtr face_counts = data.face_counts.getValue(iss);
+ const Int32ArraySamplePtr face_indices = data.face_indices.getValue(iss);
+
+ /* Only copy triangles for other frames if the topology is changing over time as well. */
+ if (data.topology_variance != kHomogeneousTopology || cached_data.triangles.size() == 0) {
+ bool do_triangles = true;
+
+ /* Compare key with last one to check whether the topology changed. */
+ if (cached_data.triangles.size() > 0) {
+ const ArraySample::Key key = face_indices->getKey();
+
+ if (key == cached_data.triangles.key1) {
+ do_triangles = false;
+ }
+
+ cached_data.triangles.key1 = key;
+ }
+
+ if (do_triangles) {
+ const array<int> polygon_to_shader = compute_polygon_to_shader_map(
+ face_counts, data.shader_face_sets, iss);
+ add_triangles(face_counts, face_indices, time, cached_data, polygon_to_shader);
+ }
+ else {
+ cached_data.triangles.reuse_data_for_last_time(time);
+ cached_data.uv_loops.reuse_data_for_last_time(time);
+ cached_data.shader.reuse_data_for_last_time(time);
+ }
+
+ /* Initialize the first key. */
+ if (data.topology_variance != kHomogeneousTopology && cached_data.triangles.size() == 1) {
+ cached_data.triangles.key1 = face_indices->getKey();
+ }
+ }
+
+ if (data.normals.valid()) {
+ add_normals(face_indices, data.normals, time, cached_data);
+ }
+ else {
+ compute_vertex_normals(cached_data, time);
+ }
+}
+
+void read_geometry_data(AlembicProcedural *proc,
+ CachedData &cached_data,
+ const PolyMeshSchemaData &data,
+ Progress &progress)
+{
+ read_data_loop(proc, cached_data, data, read_poly_mesh_geometry, progress);
+}
+
+/* Subdivision Geometries */
+
+static void add_subd_polygons(CachedData &cached_data, const SubDSchemaData &data, chrono_t time)
+{
+ const ISampleSelector iss = ISampleSelector(time);
+
+ const Int32ArraySamplePtr face_counts = data.face_counts.getValue(iss);
+ const Int32ArraySamplePtr face_indices = data.face_indices.getValue(iss);
+
+ array<int> subd_start_corner;
+ array<int> shader;
+ array<int> subd_num_corners;
+ array<bool> subd_smooth;
+ array<int> subd_ptex_offset;
+ array<int> subd_face_corners;
+ array<int> uv_loops;
+
+ const size_t num_faces = face_counts->size();
+ const int *face_counts_array = face_counts->get();
+ const int *face_indices_array = face_indices->get();
+
+ int num_ngons = 0;
+ int num_corners = 0;
+ for (size_t i = 0; i < face_counts->size(); i++) {
+ num_ngons += (face_counts_array[i] == 4 ? 0 : 1);
+ num_corners += face_counts_array[i];
+ }
+
+ subd_start_corner.reserve(num_faces);
+ subd_num_corners.reserve(num_faces);
+ subd_smooth.reserve(num_faces);
+ subd_ptex_offset.reserve(num_faces);
+ shader.reserve(num_faces);
+ subd_face_corners.reserve(num_corners);
+ uv_loops.reserve(num_corners);
+
+ int start_corner = 0;
+ int current_shader = 0;
+ int ptex_offset = 0;
+
+ const array<int> polygon_to_shader = compute_polygon_to_shader_map(
+ face_counts, data.shader_face_sets, iss);
+
+ for (size_t i = 0; i < face_counts->size(); i++) {
+ num_corners = face_counts_array[i];
+
+ if (!polygon_to_shader.empty()) {
+ current_shader = polygon_to_shader[i];
+ }
+
+ subd_start_corner.push_back_reserved(start_corner);
+ subd_num_corners.push_back_reserved(num_corners);
+
+ for (int j = 0; j < num_corners; ++j) {
+ subd_face_corners.push_back_reserved(face_indices_array[start_corner + j]);
+ uv_loops.push_back_reserved(start_corner + j);
+ }
+
+ shader.push_back_reserved(current_shader);
+ subd_smooth.push_back_reserved(1);
+ subd_ptex_offset.push_back_reserved(ptex_offset);
+
+ ptex_offset += (num_corners == 4 ? 1 : num_corners);
+
+ start_corner += num_corners;
+ }
+
+ cached_data.shader.add_data(shader, time);
+ cached_data.subd_start_corner.add_data(subd_start_corner, time);
+ cached_data.subd_num_corners.add_data(subd_num_corners, time);
+ cached_data.subd_smooth.add_data(subd_smooth, time);
+ cached_data.subd_ptex_offset.add_data(subd_ptex_offset, time);
+ cached_data.subd_face_corners.add_data(subd_face_corners, time);
+ cached_data.num_ngons.add_data(num_ngons, time);
+ cached_data.uv_loops.add_data(uv_loops, time);
+}
+
+static void add_subd_creases(CachedData &cached_data, const SubDSchemaData &data, chrono_t time)
+{
+ if (!(data.crease_indices.valid() && data.crease_indices.valid() &&
+ data.crease_sharpnesses.valid())) {
+ return;
+ }
+
+ const ISampleSelector iss = ISampleSelector(time);
+
+ const Int32ArraySamplePtr creases_length = data.crease_lengths.getValue(iss);
+ const Int32ArraySamplePtr creases_indices = data.crease_indices.getValue(iss);
+ const FloatArraySamplePtr creases_sharpnesses = data.crease_sharpnesses.getValue(iss);
+
+ if (creases_length && creases_indices && creases_sharpnesses) {
+ array<int> creases_edge;
+ array<float> creases_weight;
+
+ creases_edge.reserve(creases_sharpnesses->size() * 2);
+ creases_weight.reserve(creases_sharpnesses->size());
+
+ int length_offset = 0;
+ int weight_offset = 0;
+ for (size_t c = 0; c < creases_length->size(); ++c) {
+ const int crease_length = creases_length->get()[c];
+
+ for (size_t j = 0; j < crease_length - 1; ++j) {
+ creases_edge.push_back_reserved(creases_indices->get()[length_offset + j]);
+ creases_edge.push_back_reserved(creases_indices->get()[length_offset + j + 1]);
+ creases_weight.push_back_reserved(creases_sharpnesses->get()[weight_offset++]);
+ }
+
+ length_offset += crease_length;
+ }
+
+ cached_data.subd_creases_edge.add_data(creases_edge, time);
+ cached_data.subd_creases_weight.add_data(creases_weight, time);
+ }
+}
+
+static void read_subd_geometry(CachedData &cached_data, const SubDSchemaData &data, chrono_t time)
+{
+ const ISampleSelector iss = ISampleSelector(time);
+
+ add_positions(data.positions.getValue(iss), time, cached_data);
+
+ if (data.topology_variance != kHomogenousTopology || cached_data.shader.size() == 0) {
+ add_subd_polygons(cached_data, data, time);
+ add_subd_creases(cached_data, data, time);
+ }
+}
+
+void read_geometry_data(AlembicProcedural *proc,
+ CachedData &cached_data,
+ const SubDSchemaData &data,
+ Progress &progress)
+{
+ read_data_loop(proc, cached_data, data, read_subd_geometry, progress);
+}
+
+/* Curve Geometries. */
+
+static void read_curves_data(CachedData &cached_data, const CurvesSchemaData &data, chrono_t time)
+{
+ const ISampleSelector iss = ISampleSelector(time);
+
+ const Int32ArraySamplePtr curves_num_vertices = data.num_vertices.getValue(iss);
+ const P3fArraySamplePtr position = data.positions.getValue(iss);
+
+ FloatArraySamplePtr radiuses;
+
+ if (data.widths.valid()) {
+ IFloatGeomParam::Sample wsample = data.widths.getExpandedValue(iss);
+ radiuses = wsample.getVals();
+ }
+
+ const bool do_radius = (radiuses != nullptr) && (radiuses->size() > 1);
+ float radius = (radiuses && radiuses->size() == 1) ? (*radiuses)[0] : data.default_radius;
+
+ array<float3> curve_keys;
+ array<float> curve_radius;
+ array<int> curve_first_key;
+ array<int> curve_shader;
+
+ const bool is_homogenous = data.topology_variance == kHomogenousTopology;
+
+ curve_keys.reserve(position->size());
+ curve_radius.reserve(position->size());
+ curve_first_key.reserve(curves_num_vertices->size());
+ curve_shader.reserve(curves_num_vertices->size());
+
+ int offset = 0;
+ for (size_t i = 0; i < curves_num_vertices->size(); i++) {
+ const int num_vertices = curves_num_vertices->get()[i];
+
+ for (int j = 0; j < num_vertices; j++) {
+ const V3f &f = position->get()[offset + j];
+ // todo(@kevindietrich): we are reading too much data?
+ curve_keys.push_back_slow(make_float3_from_yup(f));
+
+ if (do_radius) {
+ radius = (*radiuses)[offset + j];
+ }
+
+ curve_radius.push_back_slow(radius * data.radius_scale);
+ }
+
+ if (!is_homogenous || cached_data.curve_first_key.size() == 0) {
+ curve_first_key.push_back_reserved(offset);
+ curve_shader.push_back_reserved(0);
+ }
+
+ offset += num_vertices;
+ }
+
+ cached_data.curve_keys.add_data(curve_keys, time);
+ cached_data.curve_radius.add_data(curve_radius, time);
+
+ if (!is_homogenous || cached_data.curve_first_key.size() == 0) {
+ cached_data.curve_first_key.add_data(curve_first_key, time);
+ cached_data.curve_shader.add_data(curve_shader, time);
+ }
+}
+
+void read_geometry_data(AlembicProcedural *proc,
+ CachedData &cached_data,
+ const CurvesSchemaData &data,
+ Progress &progress)
+{
+ read_data_loop(proc, cached_data, data, read_curves_data, progress);
+}
+
+/* Attributes conversions. */
+
+/* Type traits for converting between Alembic and Cycles types.
+ */
+
+template<typename T> struct value_type_converter {
+ using cycles_type = float;
+ /* Use `TypeDesc::FLOAT` instead of `TypeFloat` to work around a compiler bug in gcc 11. */
+ static constexpr TypeDesc type_desc = TypeDesc::FLOAT;
+ static constexpr const char *type_name = "float (default)";
+
+ static cycles_type convert_value(T value)
+ {
+ return static_cast<float>(value);
+ }
+};
+
+template<> struct value_type_converter<Imath::V2f> {
+ using cycles_type = float2;
+ static constexpr TypeDesc type_desc = TypeFloat2;
+ static constexpr const char *type_name = "float2";
+
+ static cycles_type convert_value(Imath::V2f value)
+ {
+ return make_float2(value.x, value.y);
+ }
+};
+
+template<> struct value_type_converter<Imath::V3f> {
+ using cycles_type = float3;
+ static constexpr TypeDesc type_desc = TypeVector;
+ static constexpr const char *type_name = "float3";
+
+ static cycles_type convert_value(Imath::V3f value)
+ {
+ return make_float3_from_yup(value);
+ }
+};
+
+template<> struct value_type_converter<Imath::C3f> {
+ using cycles_type = uchar4;
+ static constexpr TypeDesc type_desc = TypeRGBA;
+ static constexpr const char *type_name = "rgb";
+
+ static cycles_type convert_value(Imath::C3f value)
+ {
+ return color_float_to_byte(make_float3(value.x, value.y, value.z));
+ }
+};
+
+template<> struct value_type_converter<Imath::C4f> {
+ using cycles_type = uchar4;
+ static constexpr TypeDesc type_desc = TypeRGBA;
+ static constexpr const char *type_name = "rgba";
+
+ static cycles_type convert_value(Imath::C4f value)
+ {
+ return color_float4_to_uchar4(make_float4(value.r, value.g, value.b, value.a));
+ }
+};
+
+/* Main function used to read attributes of any type. */
+template<typename TRAIT>
+static void process_attribute(CachedData &cache,
+ CachedData::CachedAttribute &attribute,
+ GeometryScope scope,
+ const typename ITypedGeomParam<TRAIT>::Sample &sample,
+ double time)
+{
+ using abc_type = typename TRAIT::value_type;
+ using cycles_type = typename value_type_converter<abc_type>::cycles_type;
+
+ const TypedArraySample<TRAIT> &values = *sample.getVals();
+
+ switch (scope) {
+ case kConstantScope:
+ case kVertexScope: {
+ const array<float3> *vertices =
+ cache.vertices.data_for_time_no_check(time).get_data_or_null();
+
+ if (!vertices) {
+ attribute.data.add_no_data(time);
+ return;
+ }
+
+ if (vertices->size() != values.size()) {
+ attribute.data.add_no_data(time);
+ return;
+ }
+
+ array<char> data(vertices->size() * sizeof(cycles_type));
+
+ cycles_type *pod_typed_data = reinterpret_cast<cycles_type *>(data.data());
+
+ for (size_t i = 0; i < values.size(); ++i) {
+ *pod_typed_data++ = value_type_converter<abc_type>::convert_value(values[i]);
+ }
+
+ attribute.data.add_data(data, time);
+ break;
+ }
+ case kVaryingScope: {
+ const array<int3> *triangles =
+ cache.triangles.data_for_time_no_check(time).get_data_or_null();
+
+ if (!triangles) {
+ attribute.data.add_no_data(time);
+ return;
+ }
+
+ array<char> data(triangles->size() * 3 * sizeof(cycles_type));
+
+ cycles_type *pod_typed_data = reinterpret_cast<cycles_type *>(data.data());
+
+ for (const int3 &tri : *triangles) {
+ *pod_typed_data++ = value_type_converter<abc_type>::convert_value(values[tri.x]);
+ *pod_typed_data++ = value_type_converter<abc_type>::convert_value(values[tri.y]);
+ *pod_typed_data++ = value_type_converter<abc_type>::convert_value(values[tri.z]);
+ }
+
+ attribute.data.add_data(data, time);
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+}
+
+/* UVs are processed separately as their indexing is based on loops, instead of vertices or
+ * corners. */
+static void process_uvs(CachedData &cache,
+ CachedData::CachedAttribute &attribute,
+ GeometryScope scope,
+ const IV2fGeomParam::Sample &sample,
+ double time)
+{
+ if (scope != kFacevaryingScope) {
+ return;
+ }
+
+ const array<int> *uv_loops = cache.uv_loops.data_for_time_no_check(time).get_data_or_null();
+
+ if (!uv_loops) {
+ return;
+ }
+
+ const array<int3> *triangles = cache.triangles.data_for_time_no_check(time).get_data_or_null();
+ const array<int> *corners =
+ cache.subd_face_corners.data_for_time_no_check(time).get_data_or_null();
+
+ array<char> data;
+ if (triangles) {
+ data.resize(triangles->size() * 3 * sizeof(float2));
+ }
+ else if (corners) {
+ data.resize(corners->size() * sizeof(float2));
+ }
+ else {
+ return;
+ }
+
+ float2 *data_float2 = reinterpret_cast<float2 *>(data.data());
+
+ const uint32_t *indices = sample.getIndices()->get();
+ const V2f *values = sample.getVals()->get();
+
+ for (const int uv_loop_index : *uv_loops) {
+ const uint32_t index = indices[uv_loop_index];
+ *data_float2++ = make_float2(values[index][0], values[index][1]);
+ }
+
+ attribute.data.add_data(data, time);
+}
+
+/* Type of the function used to parse one time worth of data, either process_uvs or
+ * process_attribute_generic. */
+template<typename TRAIT>
+using process_callback_type = void (*)(CachedData &,
+ CachedData::CachedAttribute &,
+ GeometryScope,
+ const typename ITypedGeomParam<TRAIT>::Sample &,
+ double);
+
+/* Main loop to process the attributes, this will look at the given param's TimeSampling and
+ * extract data based on which frame time is requested by the procedural and execute the callback
+ * for each of those requested time. */
+template<typename TRAIT>
+static void read_attribute_loop(AlembicProcedural *proc,
+ CachedData &cache,
+ const ITypedGeomParam<TRAIT> &param,
+ process_callback_type<TRAIT> callback,
+ Progress &progress,
+ AttributeStandard std = ATTR_STD_NONE)
+{
+ const std::set<chrono_t> times = get_relevant_sample_times(
+ proc, *param.getTimeSampling(), param.getNumSamples());
+
+ if (times.empty()) {
+ return;
+ }
+
+ std::string name = param.getName();
+
+ if (std == ATTR_STD_UV) {
+ std::string uv_source_name = Alembic::Abc::GetSourceName(param.getMetaData());
+
+ /* According to the convention, primary UVs should have had their name
+ * set using Alembic::Abc::SetSourceName, but you can't expect everyone
+ * to follow it! :) */
+ if (!uv_source_name.empty()) {
+ name = uv_source_name;
+ }
+ }
+
+ CachedData::CachedAttribute &attribute = cache.add_attribute(ustring(name),
+ *param.getTimeSampling());
+
+ using abc_type = typename TRAIT::value_type;
+
+ attribute.data.set_time_sampling(*param.getTimeSampling());
+ attribute.std = std;
+ attribute.type_desc = value_type_converter<abc_type>::type_desc;
+
+ if (attribute.type_desc == TypeRGBA) {
+ attribute.element = ATTR_ELEMENT_CORNER_BYTE;
+ }
+ else {
+ if (param.getScope() == kVaryingScope || param.getScope() == kFacevaryingScope) {
+ attribute.element = ATTR_ELEMENT_CORNER;
+ }
+ else {
+ attribute.element = ATTR_ELEMENT_VERTEX;
+ }
+ }
+
+ for (const chrono_t time : times) {
+ if (progress.get_cancel()) {
+ return;
+ }
+
+ ISampleSelector iss = ISampleSelector(time);
+ typename ITypedGeomParam<TRAIT>::Sample sample;
+ param.getIndexed(sample, iss);
+
+ if (!sample.valid()) {
+ continue;
+ }
+
+ if (!sample.getVals()) {
+ attribute.data.add_no_data(time);
+ continue;
+ }
+
+ /* Check whether we already loaded constant data. */
+ if (attribute.data.size() != 0) {
+ if (param.isConstant()) {
+ return;
+ }
+
+ const ArraySample::Key indices_key = sample.getIndices()->getKey();
+ const ArraySample::Key values_key = sample.getVals()->getKey();
+
+ const bool is_same_as_last_time = (indices_key == attribute.data.key1 &&
+ values_key == attribute.data.key2);
+
+ attribute.data.key1 = indices_key;
+ attribute.data.key2 = values_key;
+
+ if (is_same_as_last_time) {
+ attribute.data.reuse_data_for_last_time(time);
+ continue;
+ }
+ }
+
+ callback(cache, attribute, param.getScope(), sample, time);
+ }
+}
+
+/* Attributes requests. */
+
+/* This structure is used to tell which ICoumpoundProperty the PropertyHeader comes from, as we
+ * need the parent when downcasting to the proper type. */
+struct PropHeaderAndParent {
+ const PropertyHeader *prop;
+ ICompoundProperty parent;
+};
+
+/* Parse the ICompoundProperty to look for properties whose names appear in the
+ * AttributeRequestSet. This also looks into any child ICompoundProperty of the given
+ * ICompoundProperty. If no property of the given name is found, let it be that way, Cycles will
+ * use a zero value for the missing attribute. */
+static void parse_requested_attributes_recursive(const AttributeRequestSet &requested_attributes,
+ const ICompoundProperty &arb_geom_params,
+ vector<PropHeaderAndParent> &requested_properties)
+{
+ if (!arb_geom_params.valid()) {
+ return;
+ }
+
+ for (const AttributeRequest &req : requested_attributes.requests) {
+ const PropertyHeader *property_header = arb_geom_params.getPropertyHeader(req.name.c_str());
+
+ if (!property_header) {
+ continue;
+ }
+
+ requested_properties.push_back({property_header, arb_geom_params});
+ }
+
+ /* Look into children compound properties. */
+ for (size_t i = 0; i < arb_geom_params.getNumProperties(); ++i) {
+ const PropertyHeader &property_header = arb_geom_params.getPropertyHeader(i);
+
+ if (property_header.isCompound()) {
+ ICompoundProperty compound_property = ICompoundProperty(arb_geom_params,
+ property_header.getName());
+ parse_requested_attributes_recursive(
+ requested_attributes, compound_property, requested_properties);
+ }
+ }
+}
+
+/* Main entry point for parsing requested attributes from an ICompoundProperty, this exists so that
+ * we can simply return the list of properties instead of allocating it on the stack and passing it
+ * as a parameter. */
+static vector<PropHeaderAndParent> parse_requested_attributes(
+ const AttributeRequestSet &requested_attributes, const ICompoundProperty &arb_geom_params)
+{
+ vector<PropHeaderAndParent> requested_properties;
+ parse_requested_attributes_recursive(
+ requested_attributes, arb_geom_params, requested_properties);
+ return requested_properties;
+}
+
+/* Read the attributes requested by the shaders from the archive. This will recursively find named
+ * attributes from the AttributeRequestSet in the ICompoundProperty and any of its compound child.
+ * The attributes are added to the CachedData's attribute list. For each attribute we will try to
+ * deduplicate data across consecutive frames. */
+void read_attributes(AlembicProcedural *proc,
+ CachedData &cache,
+ const ICompoundProperty &arb_geom_params,
+ const IV2fGeomParam &default_uvs_param,
+ const AttributeRequestSet &requested_attributes,
+ Progress &progress)
+{
+ if (default_uvs_param.valid()) {
+ /* Only the default UVs should be treated as the standard UV attribute. */
+ read_attribute_loop(proc, cache, default_uvs_param, process_uvs, progress, ATTR_STD_UV);
+ }
+
+ vector<PropHeaderAndParent> requested_properties = parse_requested_attributes(
+ requested_attributes, arb_geom_params);
+
+ for (const PropHeaderAndParent &prop_and_parent : requested_properties) {
+ if (progress.get_cancel()) {
+ return;
+ }
+
+ const PropertyHeader *prop = prop_and_parent.prop;
+ const ICompoundProperty &parent = prop_and_parent.parent;
+
+ if (IBoolGeomParam::matches(*prop)) {
+ const IBoolGeomParam &param = IBoolGeomParam(parent, prop->getName());
+ read_attribute_loop(proc, cache, param, process_attribute<BooleanTPTraits>, progress);
+ }
+ else if (IInt32GeomParam::matches(*prop)) {
+ const IInt32GeomParam &param = IInt32GeomParam(parent, prop->getName());
+ read_attribute_loop(proc, cache, param, process_attribute<Int32TPTraits>, progress);
+ }
+ else if (IFloatGeomParam::matches(*prop)) {
+ const IFloatGeomParam &param = IFloatGeomParam(parent, prop->getName());
+ read_attribute_loop(proc, cache, param, process_attribute<Float32TPTraits>, progress);
+ }
+ else if (IV2fGeomParam::matches(*prop)) {
+ const IV2fGeomParam &param = IV2fGeomParam(parent, prop->getName());
+ if (Alembic::AbcGeom::isUV(*prop)) {
+ read_attribute_loop(proc, cache, param, process_uvs, progress);
+ }
+ else {
+ read_attribute_loop(proc, cache, param, process_attribute<V2fTPTraits>, progress);
+ }
+ }
+ else if (IV3fGeomParam::matches(*prop)) {
+ const IV3fGeomParam &param = IV3fGeomParam(parent, prop->getName());
+ read_attribute_loop(proc, cache, param, process_attribute<V3fTPTraits>, progress);
+ }
+ else if (IN3fGeomParam::matches(*prop)) {
+ const IN3fGeomParam &param = IN3fGeomParam(parent, prop->getName());
+ read_attribute_loop(proc, cache, param, process_attribute<N3fTPTraits>, progress);
+ }
+ else if (IC3fGeomParam::matches(*prop)) {
+ const IC3fGeomParam &param = IC3fGeomParam(parent, prop->getName());
+ read_attribute_loop(proc, cache, param, process_attribute<C3fTPTraits>, progress);
+ }
+ else if (IC4fGeomParam::matches(*prop)) {
+ const IC4fGeomParam &param = IC4fGeomParam(parent, prop->getName());
+ read_attribute_loop(proc, cache, param, process_attribute<C4fTPTraits>, progress);
+ }
+ }
+
+ cache.invalidate_last_loaded_time(true);
+}
+
+CCL_NAMESPACE_END
+
+#endif
diff --git a/intern/cycles/render/alembic_read.h b/intern/cycles/render/alembic_read.h
new file mode 100644
index 00000000000..9cc8622a1ba
--- /dev/null
+++ b/intern/cycles/render/alembic_read.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2021 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.
+ */
+
+#pragma once
+
+#ifdef WITH_ALEMBIC
+
+# include <Alembic/AbcCoreFactory/All.h>
+# include <Alembic/AbcGeom/All.h>
+
+# include "util/util_vector.h"
+
+CCL_NAMESPACE_BEGIN
+
+class AlembicProcedural;
+class AttributeRequestSet;
+class Progress;
+struct CachedData;
+
+/* Maps a FaceSet whose name matches that of a Shader to the index of said shader in the Geometry's
+ * used_shaders list. */
+struct FaceSetShaderIndexPair {
+ Alembic::AbcGeom::IFaceSet face_set;
+ int shader_index;
+};
+
+/* Data of an IPolyMeshSchema that we need to read. */
+struct PolyMeshSchemaData {
+ Alembic::AbcGeom::TimeSamplingPtr time_sampling;
+ size_t num_samples;
+ Alembic::AbcGeom::MeshTopologyVariance topology_variance;
+
+ Alembic::AbcGeom::IP3fArrayProperty positions;
+ Alembic::AbcGeom::IInt32ArrayProperty face_indices;
+ Alembic::AbcGeom::IInt32ArrayProperty face_counts;
+
+ Alembic::AbcGeom::IN3fGeomParam normals;
+
+ vector<FaceSetShaderIndexPair> shader_face_sets;
+
+ // Unsupported for now.
+ Alembic::AbcGeom::IV3fArrayProperty velocities;
+};
+
+void read_geometry_data(AlembicProcedural *proc,
+ CachedData &cached_data,
+ const PolyMeshSchemaData &data,
+ Progress &progress);
+
+/* Data of an ISubDSchema that we need to read. */
+struct SubDSchemaData {
+ Alembic::AbcGeom::TimeSamplingPtr time_sampling;
+ size_t num_samples;
+ Alembic::AbcGeom::MeshTopologyVariance topology_variance;
+
+ Alembic::AbcGeom::IInt32ArrayProperty face_counts;
+ Alembic::AbcGeom::IInt32ArrayProperty face_indices;
+ Alembic::AbcGeom::IP3fArrayProperty positions;
+
+ Alembic::AbcGeom::IInt32ArrayProperty crease_indices;
+ Alembic::AbcGeom::IInt32ArrayProperty crease_lengths;
+ Alembic::AbcGeom::IFloatArrayProperty crease_sharpnesses;
+
+ vector<FaceSetShaderIndexPair> shader_face_sets;
+
+ // Those are unsupported for now.
+ Alembic::AbcGeom::IInt32ArrayProperty corner_indices;
+ Alembic::AbcGeom::IFloatArrayProperty corner_sharpnesses;
+ Alembic::AbcGeom::IInt32Property face_varying_interpolate_boundary;
+ Alembic::AbcGeom::IInt32Property face_varying_propagate_corners;
+ Alembic::AbcGeom::IInt32Property interpolate_boundary;
+ Alembic::AbcGeom::IInt32ArrayProperty holes;
+ Alembic::AbcGeom::IStringProperty subdivision_scheme;
+ Alembic::AbcGeom::IV3fArrayProperty velocities;
+};
+
+void read_geometry_data(AlembicProcedural *proc,
+ CachedData &cached_data,
+ const SubDSchemaData &data,
+ Progress &progress);
+
+/* Data of a ICurvesSchema that we need to read. */
+struct CurvesSchemaData {
+ Alembic::AbcGeom::TimeSamplingPtr time_sampling;
+ size_t num_samples;
+ Alembic::AbcGeom::MeshTopologyVariance topology_variance;
+
+ Alembic::AbcGeom::IP3fArrayProperty positions;
+
+ Alembic::AbcGeom::IInt32ArrayProperty num_vertices;
+
+ float default_radius;
+ float radius_scale;
+
+ // Those are unsupported for now.
+ Alembic::AbcGeom::IV3fArrayProperty velocities;
+ // if this property is invalid then the weight for every point is 1
+ Alembic::AbcGeom::IFloatArrayProperty position_weights;
+ Alembic::AbcGeom::IN3fGeomParam normals;
+ Alembic::AbcGeom::IFloatGeomParam widths;
+ Alembic::AbcGeom::IUcharArrayProperty orders;
+ Alembic::AbcGeom::IFloatArrayProperty knots;
+
+ // TODO(@kevindietrich): type, basis, wrap
+};
+
+void read_geometry_data(AlembicProcedural *proc,
+ CachedData &cached_data,
+ const CurvesSchemaData &data,
+ Progress &progress);
+
+void read_attributes(AlembicProcedural *proc,
+ CachedData &cache,
+ const Alembic::AbcGeom::ICompoundProperty &arb_geom_params,
+ const Alembic::AbcGeom::IV2fGeomParam &default_uvs_param,
+ const AttributeRequestSet &requested_attributes,
+ Progress &progress);
+
+CCL_NAMESPACE_END
+
+#endif
diff --git a/intern/cycles/render/attribute.cpp b/intern/cycles/render/attribute.cpp
index 331d30802f1..bf9d69cb47e 100644
--- a/intern/cycles/render/attribute.cpp
+++ b/intern/cycles/render/attribute.cpp
@@ -383,6 +383,23 @@ AttributeStandard Attribute::name_standard(const char *name)
return ATTR_STD_NONE;
}
+AttrKernelDataType Attribute::kernel_type(const Attribute &attr)
+{
+ if (attr.element == ATTR_ELEMENT_CORNER) {
+ return AttrKernelDataType::UCHAR4;
+ }
+
+ if (attr.type == TypeDesc::TypeFloat) {
+ return AttrKernelDataType::FLOAT;
+ }
+
+ if (attr.type == TypeFloat2) {
+ return AttrKernelDataType::FLOAT2;
+ }
+
+ return AttrKernelDataType::FLOAT3;
+}
+
void Attribute::get_uv_tiles(Geometry *geom,
AttributePrimitive prim,
unordered_set<int> &tiles) const
@@ -417,7 +434,7 @@ void Attribute::get_uv_tiles(Geometry *geom,
/* Attribute Set */
AttributeSet::AttributeSet(Geometry *geometry, AttributePrimitive prim)
- : geometry(geometry), prim(prim)
+ : modified_flag(~0u), geometry(geometry), prim(prim)
{
}
@@ -440,7 +457,7 @@ Attribute *AttributeSet::add(ustring name, TypeDesc type, AttributeElement eleme
Attribute new_attr(name, type, element, geometry, prim);
attributes.emplace_back(std::move(new_attr));
- modified = true;
+ tag_modified(attributes.back());
return &attributes.back();
}
@@ -462,8 +479,7 @@ void AttributeSet::remove(ustring name)
for (it = attributes.begin(); it != attributes.end(); it++) {
if (&*it == attr) {
- modified = true;
- attributes.erase(it);
+ remove(it);
return;
}
}
@@ -608,8 +624,7 @@ void AttributeSet::remove(AttributeStandard std)
for (it = attributes.begin(); it != attributes.end(); it++) {
if (&*it == attr) {
- modified = true;
- attributes.erase(it);
+ remove(it);
return;
}
}
@@ -634,6 +649,12 @@ void AttributeSet::remove(Attribute *attribute)
}
}
+void AttributeSet::remove(list<Attribute>::iterator it)
+{
+ tag_modified(*it);
+ attributes.erase(it);
+}
+
void AttributeSet::resize(bool reserve_only)
{
foreach (Attribute &attr, attributes) {
@@ -674,21 +695,22 @@ void AttributeSet::update(AttributeSet &&new_attributes)
for (it = attributes.begin(); it != attributes.end();) {
if (it->std != ATTR_STD_NONE) {
if (new_attributes.find(it->std) == nullptr) {
- modified = true;
- attributes.erase(it++);
+ remove(it++);
continue;
}
}
else if (it->name != "") {
if (new_attributes.find(it->name) == nullptr) {
- modified = true;
- attributes.erase(it++);
+ remove(it++);
continue;
}
}
it++;
}
+
+ /* If all attributes were replaced, transform is no longer applied. */
+ geometry->transform_applied = false;
}
void AttributeSet::clear_modified()
@@ -696,7 +718,27 @@ void AttributeSet::clear_modified()
foreach (Attribute &attr, attributes) {
attr.modified = false;
}
- modified = false;
+
+ modified_flag = 0;
+}
+
+void AttributeSet::tag_modified(const Attribute &attr)
+{
+ /* Some attributes are not stored in the various kernel attribute arrays
+ * (DeviceScene::attribute_*), so the modified flags are only set if the associated standard
+ * corresponds to an attribute which will be stored in the kernel's attribute arrays. */
+ const bool modifies_device_array = (attr.std != ATTR_STD_FACE_NORMAL &&
+ attr.std != ATTR_STD_VERTEX_NORMAL);
+
+ if (modifies_device_array) {
+ AttrKernelDataType kernel_type = Attribute::kernel_type(attr);
+ modified_flag |= (1u << kernel_type);
+ }
+}
+
+bool AttributeSet::modified(AttrKernelDataType kernel_type) const
+{
+ return (modified_flag & (1u << kernel_type)) != 0;
}
/* AttributeRequest */
diff --git a/intern/cycles/render/attribute.h b/intern/cycles/render/attribute.h
index 18c9e5ab83a..004c267cabc 100644
--- a/intern/cycles/render/attribute.h
+++ b/intern/cycles/render/attribute.h
@@ -39,6 +39,21 @@ class Hair;
class Mesh;
struct Transform;
+/* AttrKernelDataType.
+ *
+ * The data type of the device arrays storing the attribute's data. Those data types are different
+ * than the ones for attributes as some attribute types are stored in the same array, e.g. Point,
+ * Vector, and Transform are all stored as float3 in the kernel.
+ *
+ * The values of this enumeration are also used as flags to detect changes in AttributeSet. */
+
+enum AttrKernelDataType {
+ FLOAT = 0,
+ FLOAT2 = 1,
+ FLOAT3 = 2,
+ UCHAR4 = 3,
+};
+
/* Attribute
*
* Arbitrary data layers on meshes.
@@ -167,6 +182,8 @@ class Attribute {
static const char *standard_name(AttributeStandard std);
static AttributeStandard name_standard(const char *name);
+ static AttrKernelDataType kernel_type(const Attribute &attr);
+
void get_uv_tiles(Geometry *geom, AttributePrimitive prim, unordered_set<int> &tiles) const;
};
@@ -175,11 +192,12 @@ class Attribute {
* Set of attributes on a mesh. */
class AttributeSet {
+ uint32_t modified_flag;
+
public:
Geometry *geometry;
AttributePrimitive prim;
list<Attribute> attributes;
- bool modified = true;
AttributeSet(Geometry *geometry, AttributePrimitive prim);
AttributeSet(AttributeSet &&) = default;
@@ -197,6 +215,8 @@ class AttributeSet {
void remove(Attribute *attribute);
+ void remove(list<Attribute>::iterator it);
+
void resize(bool reserve_only = false);
void clear(bool preserve_voxel_data = false);
@@ -204,7 +224,18 @@ class AttributeSet {
* and remove any attribute not found on the new set from this. */
void update(AttributeSet &&new_attributes);
+ /* Return whether the attributes of the given kernel_type are modified, where "modified" means
+ * that some attributes of the given type were added or removed from this AttributeSet. This does
+ * not mean that the data of the remaining attributes in this AttributeSet were also modified. To
+ * check this, use Attribute.modified. */
+ bool modified(AttrKernelDataType kernel_type) const;
+
void clear_modified();
+
+ private:
+ /* Set the relevant modified flag for the attribute. Only attributes that are stored in device
+ * arrays will be considered for tagging this AttributeSet as modified. */
+ void tag_modified(const Attribute &attr);
};
/* AttributeRequest
diff --git a/intern/cycles/render/background.cpp b/intern/cycles/render/background.cpp
index b3d383afae4..b925e755434 100644
--- a/intern/cycles/render/background.cpp
+++ b/intern/cycles/render/background.cpp
@@ -59,6 +59,7 @@ Background::Background() : Node(get_node_type())
Background::~Background()
{
+ dereference_all_used_nodes();
}
void Background::device_update(Device *device, DeviceScene *dscene, Scene *scene)
@@ -130,6 +131,14 @@ void Background::device_free(Device * /*device*/, DeviceScene * /*dscene*/)
void Background::tag_update(Scene *scene)
{
+ Shader *bg_shader = get_shader(scene);
+ if (bg_shader && bg_shader->is_modified()) {
+ /* Tag as modified to update the KernelBackground visibility information.
+ * We only tag the use_shader socket as modified as it is related to the shader
+ * and to avoid doing unnecessary updates anywhere else. */
+ tag_use_shader_modified();
+ }
+
if (ao_factor_is_modified() || use_ao_is_modified()) {
scene->integrator->tag_update(scene, Integrator::BACKGROUND_AO_MODIFIED);
}
diff --git a/intern/cycles/render/geometry.cpp b/intern/cycles/render/geometry.cpp
index 124a41db21e..ce76658acb6 100644
--- a/intern/cycles/render/geometry.cpp
+++ b/intern/cycles/render/geometry.cpp
@@ -46,6 +46,12 @@ CCL_NAMESPACE_BEGIN
/* Geometry */
+PackFlags operator|=(PackFlags &pack_flags, uint32_t value)
+{
+ pack_flags = (PackFlags)((uint32_t)pack_flags | value);
+ return pack_flags;
+}
+
NODE_ABSTRACT_DEFINE(Geometry)
{
NodeType *type = NodeType::add("geometry_base", NULL);
@@ -79,6 +85,7 @@ Geometry::Geometry(const NodeType *node_type, const Type type)
Geometry::~Geometry()
{
+ dereference_all_used_nodes();
delete bvh;
}
@@ -823,10 +830,13 @@ void GeometryManager::device_update_attributes(Device *device,
dscene->attributes_float3.alloc(attr_float3_size);
dscene->attributes_uchar4.alloc(attr_uchar4_size);
- const bool copy_all_data = dscene->attributes_float.need_realloc() ||
- dscene->attributes_float2.need_realloc() ||
- dscene->attributes_float3.need_realloc() ||
- dscene->attributes_uchar4.need_realloc();
+ /* The order of those flags needs to match that of AttrKernelDataType. */
+ const bool attributes_need_realloc[4] = {
+ dscene->attributes_float.need_realloc(),
+ dscene->attributes_float2.need_realloc(),
+ dscene->attributes_float3.need_realloc(),
+ dscene->attributes_uchar4.need_realloc(),
+ };
size_t attr_float_offset = 0;
size_t attr_float2_offset = 0;
@@ -845,7 +855,7 @@ void GeometryManager::device_update_attributes(Device *device,
if (attr) {
/* force a copy if we need to reallocate all the data */
- attr->modified |= copy_all_data;
+ attr->modified |= attributes_need_realloc[Attribute::kernel_type(*attr)];
}
update_attribute_element_offset(geom,
@@ -868,7 +878,7 @@ void GeometryManager::device_update_attributes(Device *device,
if (subd_attr) {
/* force a copy if we need to reallocate all the data */
- subd_attr->modified |= copy_all_data;
+ subd_attr->modified |= attributes_need_realloc[Attribute::kernel_type(*subd_attr)];
}
update_attribute_element_offset(mesh,
@@ -899,6 +909,10 @@ void GeometryManager::device_update_attributes(Device *device,
foreach (AttributeRequest &req, attributes.requests) {
Attribute *attr = values.find(req);
+ if (attr) {
+ attr->modified |= attributes_need_realloc[Attribute::kernel_type(*attr)];
+ }
+
update_attribute_element_offset(object->geometry,
dscene->attributes_float,
attr_float_offset,
@@ -934,10 +948,10 @@ void GeometryManager::device_update_attributes(Device *device,
/* copy to device */
progress.set_status("Updating Mesh", "Copying Attributes to device");
- dscene->attributes_float.copy_to_device();
- dscene->attributes_float2.copy_to_device();
- dscene->attributes_float3.copy_to_device();
- dscene->attributes_uchar4.copy_to_device();
+ dscene->attributes_float.copy_to_device_if_modified();
+ dscene->attributes_float2.copy_to_device_if_modified();
+ dscene->attributes_float3.copy_to_device_if_modified();
+ dscene->attributes_uchar4.copy_to_device_if_modified();
if (progress.get_cancel())
return;
@@ -1235,7 +1249,16 @@ void GeometryManager::device_update_bvh(Device *device,
const bool can_refit = scene->bvh != nullptr &&
(bparams.bvh_layout == BVHLayout::BVH_LAYOUT_OPTIX);
- const bool pack_all = scene->bvh == nullptr;
+
+ PackFlags pack_flags = PackFlags::PACK_NONE;
+
+ if (scene->bvh == nullptr) {
+ pack_flags |= PackFlags::PACK_ALL;
+ }
+
+ if (dscene->prim_visibility.is_modified()) {
+ pack_flags |= PackFlags::PACK_VISIBILITY;
+ }
BVH *bvh = scene->bvh;
if (!scene->bvh) {
@@ -1273,10 +1296,14 @@ void GeometryManager::device_update_bvh(Device *device,
pack.root_index = -1;
- if (!pack_all) {
+ if (pack_flags != PackFlags::PACK_ALL) {
/* if we do not need to recreate the BVH, then only the vertices are updated, so we can
* safely retake the memory */
dscene->prim_tri_verts.give_data(pack.prim_tri_verts);
+
+ if ((pack_flags & PackFlags::PACK_VISIBILITY) != 0) {
+ dscene->prim_visibility.give_data(pack.prim_visibility);
+ }
}
else {
/* It is not strictly necessary to skip those resizes we if do not have to repack, as the OS
@@ -1305,13 +1332,21 @@ void GeometryManager::device_update_bvh(Device *device,
// Iterate over scene mesh list instead of objects, since 'optix_prim_offset' was calculated
// based on that list, which may be ordered differently from the object list.
foreach (Geometry *geom, scene->geometry) {
- if (!pack_all && !geom->is_modified()) {
+ /* Make a copy of the pack_flags so the current geometry's flags do not pollute the others'.
+ */
+ PackFlags geom_pack_flags = pack_flags;
+
+ if (geom->is_modified()) {
+ geom_pack_flags |= PackFlags::PACK_VERTICES;
+ }
+
+ if (geom_pack_flags == PACK_NONE) {
continue;
}
const pair<int, uint> &info = geometry_to_object_info[geom];
pool.push(function_bind(
- &Geometry::pack_primitives, geom, &pack, info.first, info.second, pack_all));
+ &Geometry::pack_primitives, geom, &pack, info.first, info.second, geom_pack_flags));
}
pool.wait_work();
}
@@ -1346,7 +1381,7 @@ void GeometryManager::device_update_bvh(Device *device,
dscene->prim_type.steal_data(pack.prim_type);
dscene->prim_type.copy_to_device();
}
- if (pack.prim_visibility.size() && (dscene->prim_visibility.need_realloc() || has_bvh2_layout)) {
+ if (pack.prim_visibility.size() && (dscene->prim_visibility.is_modified() || has_bvh2_layout)) {
dscene->prim_visibility.steal_data(pack.prim_visibility);
dscene->prim_visibility.copy_to_device();
}
@@ -1364,7 +1399,6 @@ void GeometryManager::device_update_bvh(Device *device,
}
dscene->data.bvh.root = pack.root_index;
- dscene->data.bvh.bvh_layout = bparams.bvh_layout;
dscene->data.bvh.use_bvh_steps = (scene->params.num_bvh_time_steps != 0);
dscene->data.bvh.curve_subdivisions = scene->params.curve_subdivisions();
/* The scene handle is set in 'CPUDevice::const_copy_to' and 'OptiXDevice::const_copy_to' */
@@ -1404,24 +1438,46 @@ static void update_device_flags_attribute(uint32_t &device_update_flags,
continue;
}
- if (attr.element == ATTR_ELEMENT_CORNER) {
- device_update_flags |= ATTR_UCHAR4_MODIFIED;
- }
- else if (attr.type == TypeDesc::TypeFloat) {
- device_update_flags |= ATTR_FLOAT_MODIFIED;
- }
- else if (attr.type == TypeFloat2) {
- device_update_flags |= ATTR_FLOAT2_MODIFIED;
- }
- else if (attr.type == TypeDesc::TypeMatrix) {
- device_update_flags |= ATTR_FLOAT3_MODIFIED;
- }
- else if (attr.element != ATTR_ELEMENT_VOXEL) {
- device_update_flags |= ATTR_FLOAT3_MODIFIED;
+ AttrKernelDataType kernel_type = Attribute::kernel_type(attr);
+
+ switch (kernel_type) {
+ case AttrKernelDataType::FLOAT: {
+ device_update_flags |= ATTR_FLOAT_MODIFIED;
+ break;
+ }
+ case AttrKernelDataType::FLOAT2: {
+ device_update_flags |= ATTR_FLOAT2_MODIFIED;
+ break;
+ }
+ case AttrKernelDataType::FLOAT3: {
+ device_update_flags |= ATTR_FLOAT3_MODIFIED;
+ break;
+ }
+ case AttrKernelDataType::UCHAR4: {
+ device_update_flags |= ATTR_UCHAR4_MODIFIED;
+ break;
+ }
}
}
}
+static void update_attribute_realloc_flags(uint32_t &device_update_flags,
+ const AttributeSet &attributes)
+{
+ if (attributes.modified(AttrKernelDataType::FLOAT)) {
+ device_update_flags |= ATTR_FLOAT_NEEDS_REALLOC;
+ }
+ if (attributes.modified(AttrKernelDataType::FLOAT2)) {
+ device_update_flags |= ATTR_FLOAT2_NEEDS_REALLOC;
+ }
+ if (attributes.modified(AttrKernelDataType::FLOAT3)) {
+ device_update_flags |= ATTR_FLOAT3_NEEDS_REALLOC;
+ }
+ if (attributes.modified(AttrKernelDataType::UCHAR4)) {
+ device_update_flags |= ATTR_UCHAR4_NEEDS_REALLOC;
+ }
+}
+
void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Progress &progress)
{
if (!need_update() && !need_flags_update) {
@@ -1444,16 +1500,11 @@ void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Pro
foreach (Geometry *geom, scene->geometry) {
geom->has_volume = false;
- if (geom->attributes.modified) {
- device_update_flags |= ATTRS_NEED_REALLOC;
- }
+ update_attribute_realloc_flags(device_update_flags, geom->attributes);
if (geom->is_mesh()) {
Mesh *mesh = static_cast<Mesh *>(geom);
-
- if (mesh->subd_attributes.modified) {
- device_update_flags |= ATTRS_NEED_REALLOC;
- }
+ update_attribute_realloc_flags(device_update_flags, mesh->subd_attributes);
}
foreach (Node *node, geom->get_used_shaders()) {
@@ -1584,7 +1635,6 @@ void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Pro
dscene->tri_vnormal.tag_realloc();
dscene->tri_vindex.tag_realloc();
dscene->tri_patch.tag_realloc();
- dscene->tri_vnormal.tag_realloc();
dscene->tri_patch_uv.tag_realloc();
dscene->tri_shader.tag_realloc();
dscene->patches.tag_realloc();
@@ -1596,6 +1646,10 @@ void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Pro
}
}
+ if ((update_flags & VISIBILITY_MODIFIED) != 0) {
+ dscene->prim_visibility.tag_modified();
+ }
+
if (device_update_flags & ATTR_FLOAT_NEEDS_REALLOC) {
dscene->attributes_map.tag_realloc();
dscene->attributes_float.tag_realloc();
@@ -1922,7 +1976,8 @@ void GeometryManager::device_update(Device *device,
* Also update the BVH if the transformations change, we cannot rely on tagging the Geometry
* as modified in this case, as we may accumulate displacement if the vertices do not also
* change. */
- bool need_update_scene_bvh = (scene->bvh == nullptr || (update_flags & TRANSFORM_MODIFIED) != 0);
+ bool need_update_scene_bvh = (scene->bvh == nullptr ||
+ (update_flags & (TRANSFORM_MODIFIED | VISIBILITY_MODIFIED)) != 0);
{
scoped_callback_timer timer([scene](double time) {
if (scene->update_stats) {
@@ -1985,6 +2040,11 @@ void GeometryManager::device_update(Device *device,
}
}
+ /* Always set BVH layout again after displacement where it was set to none,
+ * to avoid ray-tracing at that stage. */
+ dscene->data.bvh.bvh_layout = BVHParams::best_bvh_layout(scene->params.bvh_layout,
+ device->get_bvh_layout_mask());
+
{
scoped_callback_timer timer([scene](double time) {
if (scene->update_stats) {
@@ -2003,7 +2063,7 @@ void GeometryManager::device_update(Device *device,
* for meshes with correct bounding boxes.
*
* This wouldn't cause wrong results, just true
- * displacement might be less optimal ot calculate.
+ * displacement might be less optimal to calculate.
*/
scene->object_manager->need_flags_update = old_need_object_flags_update;
}
diff --git a/intern/cycles/render/geometry.h b/intern/cycles/render/geometry.h
index abdd851a089..7db122f69cb 100644
--- a/intern/cycles/render/geometry.h
+++ b/intern/cycles/render/geometry.h
@@ -43,6 +43,24 @@ class Shader;
class Volume;
struct PackedBVH;
+/* Flags used to determine which geometry data need to be packed. */
+enum PackFlags : uint32_t {
+ PACK_NONE = 0u,
+
+ /* Pack the geometry information (e.g. triangle or curve keys indices). */
+ PACK_GEOMETRY = (1u << 0),
+
+ /* Pack the vertices, for Meshes and Volumes' bounding meshes. */
+ PACK_VERTICES = (1u << 1),
+
+ /* Pack the visibility flags for each triangle or curve. */
+ PACK_VISIBILITY = (1u << 2),
+
+ PACK_ALL = (PACK_GEOMETRY | PACK_VERTICES | PACK_VISIBILITY),
+};
+
+PackFlags operator|=(PackFlags &pack_flags, uint32_t value);
+
/* Geometry
*
* Base class for geometric types like Mesh and Hair. */
@@ -126,7 +144,10 @@ class Geometry : public Node {
int n,
int total);
- virtual void pack_primitives(PackedBVH *pack, int object, uint visibility, bool pack_all) = 0;
+ virtual void pack_primitives(PackedBVH *pack,
+ int object,
+ uint visibility,
+ PackFlags pack_flags) = 0;
/* Check whether the geometry should have own BVH built separately. Briefly,
* own BVH is needed for geometry, if:
@@ -191,6 +212,8 @@ class GeometryManager {
TRANSFORM_MODIFIED = (1 << 10),
+ VISIBILITY_MODIFIED = (1 << 11),
+
/* tag everything in the manager for an update */
UPDATE_ALL = ~0u,
diff --git a/intern/cycles/render/hair.cpp b/intern/cycles/render/hair.cpp
index dad235aa340..72fc612c0c0 100644
--- a/intern/cycles/render/hair.cpp
+++ b/intern/cycles/render/hair.cpp
@@ -494,38 +494,47 @@ void Hair::pack_curves(Scene *scene,
}
}
-void Hair::pack_primitives(PackedBVH *pack, int object, uint visibility, bool pack_all)
+void Hair::pack_primitives(PackedBVH *pack, int object, uint visibility, PackFlags pack_flags)
{
if (curve_first_key.empty())
return;
- /* If the BVH does not have to be recreated, we can bail out. */
- if (!pack_all) {
- return;
+ /* Separate loop as other arrays are not initialized if their packing is not required. */
+ if ((pack_flags & PACK_VISIBILITY) != 0) {
+ unsigned int *prim_visibility = &pack->prim_visibility[optix_prim_offset];
+
+ size_t index = 0;
+ for (size_t j = 0; j < num_curves(); ++j) {
+ Curve curve = get_curve(j);
+ for (size_t k = 0; k < curve.num_segments(); ++k, ++index) {
+ prim_visibility[index] = visibility;
+ }
+ }
}
- unsigned int *prim_tri_index = &pack->prim_tri_index[optix_prim_offset];
- int *prim_type = &pack->prim_type[optix_prim_offset];
- unsigned int *prim_visibility = &pack->prim_visibility[optix_prim_offset];
- int *prim_index = &pack->prim_index[optix_prim_offset];
- int *prim_object = &pack->prim_object[optix_prim_offset];
- // 'pack->prim_time' is unused by Embree and OptiX
-
- uint type = has_motion_blur() ?
- ((curve_shape == CURVE_RIBBON) ? PRIMITIVE_MOTION_CURVE_RIBBON :
- PRIMITIVE_MOTION_CURVE_THICK) :
- ((curve_shape == CURVE_RIBBON) ? PRIMITIVE_CURVE_RIBBON : PRIMITIVE_CURVE_THICK);
-
- size_t index = 0;
- for (size_t j = 0; j < num_curves(); ++j) {
- Curve curve = get_curve(j);
- for (size_t k = 0; k < curve.num_segments(); ++k, ++index) {
- prim_tri_index[index] = -1;
- prim_type[index] = PRIMITIVE_PACK_SEGMENT(type, k);
- prim_visibility[index] = visibility;
- // Each curve segment points back to its curve index
- prim_index[index] = j + prim_offset;
- prim_object[index] = object;
+ if ((pack_flags & PACK_GEOMETRY) != 0) {
+ unsigned int *prim_tri_index = &pack->prim_tri_index[optix_prim_offset];
+ int *prim_type = &pack->prim_type[optix_prim_offset];
+ int *prim_index = &pack->prim_index[optix_prim_offset];
+ int *prim_object = &pack->prim_object[optix_prim_offset];
+ // 'pack->prim_time' is unused by Embree and OptiX
+
+ uint type = has_motion_blur() ?
+ ((curve_shape == CURVE_RIBBON) ? PRIMITIVE_MOTION_CURVE_RIBBON :
+ PRIMITIVE_MOTION_CURVE_THICK) :
+ ((curve_shape == CURVE_RIBBON) ? PRIMITIVE_CURVE_RIBBON :
+ PRIMITIVE_CURVE_THICK);
+
+ size_t index = 0;
+ for (size_t j = 0; j < num_curves(); ++j) {
+ Curve curve = get_curve(j);
+ for (size_t k = 0; k < curve.num_segments(); ++k, ++index) {
+ prim_tri_index[index] = -1;
+ prim_type[index] = PRIMITIVE_PACK_SEGMENT(type, k);
+ // Each curve segment points back to its curve index
+ prim_index[index] = j + prim_offset;
+ prim_object[index] = object;
+ }
}
}
}
diff --git a/intern/cycles/render/hair.h b/intern/cycles/render/hair.h
index 4b949f984e5..1a8f422e8c4 100644
--- a/intern/cycles/render/hair.h
+++ b/intern/cycles/render/hair.h
@@ -146,7 +146,10 @@ class Hair : public Geometry {
/* BVH */
void pack_curves(Scene *scene, float4 *curve_key_co, float4 *curve_data, size_t curvekey_offset);
- void pack_primitives(PackedBVH *pack, int object, uint visibility, bool pack_all) override;
+ void pack_primitives(PackedBVH *pack,
+ int object,
+ uint visibility,
+ PackFlags pack_flags) override;
};
CCL_NAMESPACE_END
diff --git a/intern/cycles/render/image.cpp b/intern/cycles/render/image.cpp
index 29a95beaf7e..27f9b7df1dd 100644
--- a/intern/cycles/render/image.cpp
+++ b/intern/cycles/render/image.cpp
@@ -303,7 +303,8 @@ ImageManager::ImageManager(const DeviceInfo &info)
animation_frame = 0;
/* Set image limits */
- has_half_images = info.has_half_images;
+ features.has_half_float = info.has_half_images;
+ features.has_nanovdb = info.has_nanovdb;
}
ImageManager::~ImageManager()
@@ -347,7 +348,7 @@ void ImageManager::load_image_metadata(Image *img)
metadata = ImageMetaData();
metadata.colorspace = img->params.colorspace;
- if (img->loader->load_metadata(metadata)) {
+ if (img->loader->load_metadata(features, metadata)) {
assert(metadata.type != IMAGE_DATA_NUM_TYPES);
}
else {
@@ -356,15 +357,10 @@ void ImageManager::load_image_metadata(Image *img)
metadata.detect_colorspace();
- /* No half textures on OpenCL, use full float instead. */
- if (!has_half_images) {
- if (metadata.type == IMAGE_DATA_TYPE_HALF4) {
- metadata.type = IMAGE_DATA_TYPE_FLOAT4;
- }
- else if (metadata.type == IMAGE_DATA_TYPE_HALF) {
- metadata.type = IMAGE_DATA_TYPE_FLOAT;
- }
- }
+ assert(features.has_half_float ||
+ (metadata.type != IMAGE_DATA_TYPE_HALF4 && metadata.type != IMAGE_DATA_TYPE_HALF));
+ assert(features.has_nanovdb || (metadata.type != IMAGE_DATA_TYPE_NANOVDB_FLOAT ||
+ metadata.type != IMAGE_DATA_TYPE_NANOVDB_FLOAT3));
img->need_metadata = false;
}
diff --git a/intern/cycles/render/image.h b/intern/cycles/render/image.h
index c802521db56..dede9513d5f 100644
--- a/intern/cycles/render/image.h
+++ b/intern/cycles/render/image.h
@@ -97,6 +97,13 @@ class ImageMetaData {
void detect_colorspace();
};
+/* Information about supported features that Image loaders can use. */
+class ImageDeviceFeatures {
+ public:
+ bool has_half_float;
+ bool has_nanovdb;
+};
+
/* Image loader base class, that can be subclassed to load image data
* from custom sources (file, memory, procedurally generated, etc). */
class ImageLoader {
@@ -105,7 +112,7 @@ class ImageLoader {
virtual ~ImageLoader(){};
/* Load metadata without actual image yet, should be fast. */
- virtual bool load_metadata(ImageMetaData &metadata) = 0;
+ virtual bool load_metadata(const ImageDeviceFeatures &features, ImageMetaData &metadata) = 0;
/* Load actual image contents. */
virtual bool load_pixels(const ImageMetaData &metadata,
@@ -212,7 +219,8 @@ class ImageManager {
private:
bool need_update_;
- bool has_half_images;
+
+ ImageDeviceFeatures features;
thread_mutex device_mutex;
thread_mutex images_mutex;
diff --git a/intern/cycles/render/image_oiio.cpp b/intern/cycles/render/image_oiio.cpp
index e9c87461822..4867efe6ac0 100644
--- a/intern/cycles/render/image_oiio.cpp
+++ b/intern/cycles/render/image_oiio.cpp
@@ -30,7 +30,7 @@ OIIOImageLoader::~OIIOImageLoader()
{
}
-bool OIIOImageLoader::load_metadata(ImageMetaData &metadata)
+bool OIIOImageLoader::load_metadata(const ImageDeviceFeatures &features, ImageMetaData &metadata)
{
/* Perform preliminary checks, with meaningful logging. */
if (!path_exists(filepath.string())) {
@@ -76,7 +76,7 @@ bool OIIOImageLoader::load_metadata(ImageMetaData &metadata)
}
/* check if it's half float */
- if (spec.format == TypeDesc::HALF) {
+ if (spec.format == TypeDesc::HALF && features.has_half_float) {
is_half = true;
}
diff --git a/intern/cycles/render/image_oiio.h b/intern/cycles/render/image_oiio.h
index a234b968557..a6dbb168b65 100644
--- a/intern/cycles/render/image_oiio.h
+++ b/intern/cycles/render/image_oiio.h
@@ -26,7 +26,7 @@ class OIIOImageLoader : public ImageLoader {
OIIOImageLoader(const string &filepath);
~OIIOImageLoader();
- bool load_metadata(ImageMetaData &metadata) override;
+ bool load_metadata(const ImageDeviceFeatures &features, ImageMetaData &metadata) override;
bool load_pixels(const ImageMetaData &metadata,
void *pixels,
diff --git a/intern/cycles/render/image_sky.cpp b/intern/cycles/render/image_sky.cpp
index 0560907c63e..7f9b85836f8 100644
--- a/intern/cycles/render/image_sky.cpp
+++ b/intern/cycles/render/image_sky.cpp
@@ -40,7 +40,7 @@ SkyLoader::SkyLoader(float sun_elevation,
SkyLoader::~SkyLoader(){};
-bool SkyLoader::load_metadata(ImageMetaData &metadata)
+bool SkyLoader::load_metadata(const ImageDeviceFeatures &, ImageMetaData &metadata)
{
metadata.width = 512;
metadata.height = 128;
diff --git a/intern/cycles/render/image_sky.h b/intern/cycles/render/image_sky.h
index 686f4e5b885..89ff586e7fd 100644
--- a/intern/cycles/render/image_sky.h
+++ b/intern/cycles/render/image_sky.h
@@ -34,7 +34,7 @@ class SkyLoader : public ImageLoader {
float ozone_density);
~SkyLoader();
- bool load_metadata(ImageMetaData &metadata) override;
+ bool load_metadata(const ImageDeviceFeatures &features, ImageMetaData &metadata) override;
bool load_pixels(const ImageMetaData &metadata,
void *pixels,
diff --git a/intern/cycles/render/image_vdb.cpp b/intern/cycles/render/image_vdb.cpp
index 70b3de5a939..fb6394e8917 100644
--- a/intern/cycles/render/image_vdb.cpp
+++ b/intern/cycles/render/image_vdb.cpp
@@ -34,7 +34,7 @@ VDBImageLoader::~VDBImageLoader()
{
}
-bool VDBImageLoader::load_metadata(ImageMetaData &metadata)
+bool VDBImageLoader::load_metadata(const ImageDeviceFeatures &features, ImageMetaData &metadata)
{
#ifdef WITH_OPENVDB
if (!grid) {
@@ -56,55 +56,71 @@ bool VDBImageLoader::load_metadata(ImageMetaData &metadata)
if (grid->isType<openvdb::FloatGrid>()) {
metadata.channels = 1;
# ifdef WITH_NANOVDB
- nanogrid = nanovdb::openToNanoVDB(*openvdb::gridConstPtrCast<openvdb::FloatGrid>(grid));
+ if (features.has_nanovdb) {
+ nanogrid = nanovdb::openToNanoVDB(*openvdb::gridConstPtrCast<openvdb::FloatGrid>(grid));
+ }
# endif
}
else if (grid->isType<openvdb::Vec3fGrid>()) {
metadata.channels = 3;
# ifdef WITH_NANOVDB
- nanogrid = nanovdb::openToNanoVDB(*openvdb::gridConstPtrCast<openvdb::Vec3fGrid>(grid));
+ if (features.has_nanovdb) {
+ nanogrid = nanovdb::openToNanoVDB(*openvdb::gridConstPtrCast<openvdb::Vec3fGrid>(grid));
+ }
# endif
}
else if (grid->isType<openvdb::BoolGrid>()) {
metadata.channels = 1;
# ifdef WITH_NANOVDB
- nanogrid = nanovdb::openToNanoVDB(
- openvdb::FloatGrid(*openvdb::gridConstPtrCast<openvdb::BoolGrid>(grid)));
+ if (features.has_nanovdb) {
+ nanogrid = nanovdb::openToNanoVDB(
+ openvdb::FloatGrid(*openvdb::gridConstPtrCast<openvdb::BoolGrid>(grid)));
+ }
# endif
}
else if (grid->isType<openvdb::DoubleGrid>()) {
metadata.channels = 1;
# ifdef WITH_NANOVDB
- nanogrid = nanovdb::openToNanoVDB(
- openvdb::FloatGrid(*openvdb::gridConstPtrCast<openvdb::DoubleGrid>(grid)));
+ if (features.has_nanovdb) {
+ nanogrid = nanovdb::openToNanoVDB(
+ openvdb::FloatGrid(*openvdb::gridConstPtrCast<openvdb::DoubleGrid>(grid)));
+ }
# endif
}
else if (grid->isType<openvdb::Int32Grid>()) {
metadata.channels = 1;
# ifdef WITH_NANOVDB
- nanogrid = nanovdb::openToNanoVDB(
- openvdb::FloatGrid(*openvdb::gridConstPtrCast<openvdb::Int32Grid>(grid)));
+ if (features.has_nanovdb) {
+ nanogrid = nanovdb::openToNanoVDB(
+ openvdb::FloatGrid(*openvdb::gridConstPtrCast<openvdb::Int32Grid>(grid)));
+ }
# endif
}
else if (grid->isType<openvdb::Int64Grid>()) {
metadata.channels = 1;
# ifdef WITH_NANOVDB
- nanogrid = nanovdb::openToNanoVDB(
- openvdb::FloatGrid(*openvdb::gridConstPtrCast<openvdb::Int64Grid>(grid)));
+ if (features.has_nanovdb) {
+ nanogrid = nanovdb::openToNanoVDB(
+ openvdb::FloatGrid(*openvdb::gridConstPtrCast<openvdb::Int64Grid>(grid)));
+ }
# endif
}
else if (grid->isType<openvdb::Vec3IGrid>()) {
metadata.channels = 3;
# ifdef WITH_NANOVDB
- nanogrid = nanovdb::openToNanoVDB(
- openvdb::Vec3fGrid(*openvdb::gridConstPtrCast<openvdb::Vec3IGrid>(grid)));
+ if (features.has_nanovdb) {
+ nanogrid = nanovdb::openToNanoVDB(
+ openvdb::Vec3fGrid(*openvdb::gridConstPtrCast<openvdb::Vec3IGrid>(grid)));
+ }
# endif
}
else if (grid->isType<openvdb::Vec3dGrid>()) {
metadata.channels = 3;
# ifdef WITH_NANOVDB
- nanogrid = nanovdb::openToNanoVDB(
- openvdb::Vec3fGrid(*openvdb::gridConstPtrCast<openvdb::Vec3dGrid>(grid)));
+ if (features.has_nanovdb) {
+ nanogrid = nanovdb::openToNanoVDB(
+ openvdb::Vec3fGrid(*openvdb::gridConstPtrCast<openvdb::Vec3dGrid>(grid)));
+ }
# endif
}
else if (grid->isType<openvdb::MaskGrid>()) {
@@ -118,21 +134,25 @@ bool VDBImageLoader::load_metadata(ImageMetaData &metadata)
}
# ifdef WITH_NANOVDB
- metadata.byte_size = nanogrid.size();
- if (metadata.channels == 1) {
- metadata.type = IMAGE_DATA_TYPE_NANOVDB_FLOAT;
- }
- else {
- metadata.type = IMAGE_DATA_TYPE_NANOVDB_FLOAT3;
- }
-# else
- if (metadata.channels == 1) {
- metadata.type = IMAGE_DATA_TYPE_FLOAT;
- }
- else {
- metadata.type = IMAGE_DATA_TYPE_FLOAT4;
+ if (nanogrid) {
+ metadata.byte_size = nanogrid.size();
+ if (metadata.channels == 1) {
+ metadata.type = IMAGE_DATA_TYPE_NANOVDB_FLOAT;
+ }
+ else {
+ metadata.type = IMAGE_DATA_TYPE_NANOVDB_FLOAT3;
+ }
}
+ else
# endif
+ {
+ if (metadata.channels == 1) {
+ metadata.type = IMAGE_DATA_TYPE_FLOAT;
+ }
+ else {
+ metadata.type = IMAGE_DATA_TYPE_FLOAT4;
+ }
+ }
/* Set transform from object space to voxel index. */
openvdb::math::Mat4f grid_matrix = grid->transform().baseMap()->getAffineMap()->getMat4();
@@ -143,20 +163,29 @@ bool VDBImageLoader::load_metadata(ImageMetaData &metadata)
}
}
+ Transform texture_to_index;
# ifdef WITH_NANOVDB
- Transform texture_to_index = transform_identity();
-# else
- openvdb::Coord min = bbox.min();
- Transform texture_to_index = transform_translate(min.x(), min.y(), min.z()) *
- transform_scale(dim.x(), dim.y(), dim.z());
+ if (nanogrid) {
+ texture_to_index = transform_identity();
+ }
+ else
# endif
+ {
+ openvdb::Coord min = bbox.min();
+ texture_to_index = transform_translate(min.x(), min.y(), min.z()) *
+ transform_scale(dim.x(), dim.y(), dim.z());
+ }
metadata.transform_3d = transform_inverse(index_to_object * texture_to_index);
metadata.use_transform_3d = true;
+# ifndef WITH_NANOVDB
+ (void)features;
+# endif
return true;
#else
(void)metadata;
+ (void)features;
return false;
#endif
}
@@ -165,48 +194,52 @@ bool VDBImageLoader::load_pixels(const ImageMetaData &, void *pixels, const size
{
#ifdef WITH_OPENVDB
# ifdef WITH_NANOVDB
- memcpy(pixels, nanogrid.data(), nanogrid.size());
-# else
- if (grid->isType<openvdb::FloatGrid>()) {
- openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, (float *)pixels);
- openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::FloatGrid>(grid), dense);
- }
- else if (grid->isType<openvdb::Vec3fGrid>()) {
- openvdb::tools::Dense<openvdb::Vec3f, openvdb::tools::LayoutXYZ> dense(
- bbox, (openvdb::Vec3f *)pixels);
- openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Vec3fGrid>(grid), dense);
- }
- else if (grid->isType<openvdb::BoolGrid>()) {
- openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, (float *)pixels);
- openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::BoolGrid>(grid), dense);
- }
- else if (grid->isType<openvdb::DoubleGrid>()) {
- openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, (float *)pixels);
- openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::DoubleGrid>(grid), dense);
- }
- else if (grid->isType<openvdb::Int32Grid>()) {
- openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, (float *)pixels);
- openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Int32Grid>(grid), dense);
- }
- else if (grid->isType<openvdb::Int64Grid>()) {
- openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, (float *)pixels);
- openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Int64Grid>(grid), dense);
- }
- else if (grid->isType<openvdb::Vec3IGrid>()) {
- openvdb::tools::Dense<openvdb::Vec3f, openvdb::tools::LayoutXYZ> dense(
- bbox, (openvdb::Vec3f *)pixels);
- openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Vec3IGrid>(grid), dense);
- }
- else if (grid->isType<openvdb::Vec3dGrid>()) {
- openvdb::tools::Dense<openvdb::Vec3f, openvdb::tools::LayoutXYZ> dense(
- bbox, (openvdb::Vec3f *)pixels);
- openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Vec3dGrid>(grid), dense);
- }
- else if (grid->isType<openvdb::MaskGrid>()) {
- openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, (float *)pixels);
- openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::MaskGrid>(grid), dense);
+ if (nanogrid) {
+ memcpy(pixels, nanogrid.data(), nanogrid.size());
}
+ else
# endif
+ {
+ if (grid->isType<openvdb::FloatGrid>()) {
+ openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, (float *)pixels);
+ openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::FloatGrid>(grid), dense);
+ }
+ else if (grid->isType<openvdb::Vec3fGrid>()) {
+ openvdb::tools::Dense<openvdb::Vec3f, openvdb::tools::LayoutXYZ> dense(
+ bbox, (openvdb::Vec3f *)pixels);
+ openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Vec3fGrid>(grid), dense);
+ }
+ else if (grid->isType<openvdb::BoolGrid>()) {
+ openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, (float *)pixels);
+ openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::BoolGrid>(grid), dense);
+ }
+ else if (grid->isType<openvdb::DoubleGrid>()) {
+ openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, (float *)pixels);
+ openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::DoubleGrid>(grid), dense);
+ }
+ else if (grid->isType<openvdb::Int32Grid>()) {
+ openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, (float *)pixels);
+ openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Int32Grid>(grid), dense);
+ }
+ else if (grid->isType<openvdb::Int64Grid>()) {
+ openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, (float *)pixels);
+ openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Int64Grid>(grid), dense);
+ }
+ else if (grid->isType<openvdb::Vec3IGrid>()) {
+ openvdb::tools::Dense<openvdb::Vec3f, openvdb::tools::LayoutXYZ> dense(
+ bbox, (openvdb::Vec3f *)pixels);
+ openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Vec3IGrid>(grid), dense);
+ }
+ else if (grid->isType<openvdb::Vec3dGrid>()) {
+ openvdb::tools::Dense<openvdb::Vec3f, openvdb::tools::LayoutXYZ> dense(
+ bbox, (openvdb::Vec3f *)pixels);
+ openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Vec3dGrid>(grid), dense);
+ }
+ else if (grid->isType<openvdb::MaskGrid>()) {
+ openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, (float *)pixels);
+ openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::MaskGrid>(grid), dense);
+ }
+ }
return true;
#else
(void)pixels;
diff --git a/intern/cycles/render/image_vdb.h b/intern/cycles/render/image_vdb.h
index 71d10cc39f5..763196f2a15 100644
--- a/intern/cycles/render/image_vdb.h
+++ b/intern/cycles/render/image_vdb.h
@@ -33,7 +33,8 @@ class VDBImageLoader : public ImageLoader {
VDBImageLoader(const string &grid_name);
~VDBImageLoader();
- virtual bool load_metadata(ImageMetaData &metadata) override;
+ virtual bool load_metadata(const ImageDeviceFeatures &features,
+ ImageMetaData &metadata) override;
virtual bool load_pixels(const ImageMetaData &metadata,
void *pixels,
diff --git a/intern/cycles/render/light.cpp b/intern/cycles/render/light.cpp
index 72450e2c546..5290d68e75a 100644
--- a/intern/cycles/render/light.cpp
+++ b/intern/cycles/render/light.cpp
@@ -129,6 +129,7 @@ NODE_DEFINE(Light)
SOCKET_VECTOR(axisv, "Axis V", zero_float3());
SOCKET_FLOAT(sizev, "Size V", 1.0f);
SOCKET_BOOLEAN(round, "Round", false);
+ SOCKET_FLOAT(spread, "Spread", M_PI_F);
SOCKET_INT(map_resolution, "Map Resolution", 0);
@@ -158,6 +159,7 @@ NODE_DEFINE(Light)
Light::Light() : Node(get_node_type())
{
+ dereference_all_used_nodes();
}
void Light::tag_update(Scene *scene)
@@ -858,6 +860,15 @@ void LightManager::device_update_points(Device *, DeviceScene *dscene, Scene *sc
float invarea = (area != 0.0f) ? 1.0f / area : 1.0f;
float3 dir = light->dir;
+ /* Convert from spread angle 0..180 to 90..0, clamping to a minimum
+ * angle to avoid excessive noise. */
+ const float min_spread_angle = 1.0f * M_PI_F / 180.0f;
+ const float spread_angle = 0.5f * (M_PI_F - max(light->spread, min_spread_angle));
+ /* Normalization computed using:
+ * integrate cos(x) * (1 - tan(x) * tan(a)) * sin(x) from x = 0 to pi/2 - a. */
+ const float tan_spread = tanf(spread_angle);
+ const float normalize_spread = 2.0f / (2.0f + (2.0f * spread_angle - M_PI_F) * tan_spread);
+
dir = safe_normalize(dir);
if (light->use_mis && area != 0.0f)
@@ -877,6 +888,8 @@ void LightManager::device_update_points(Device *, DeviceScene *dscene, Scene *sc
klights[light_index].area.dir[0] = dir.x;
klights[light_index].area.dir[1] = dir.y;
klights[light_index].area.dir[2] = dir.z;
+ klights[light_index].area.tan_spread = tan_spread;
+ klights[light_index].area.normalize_spread = normalize_spread;
}
else if (light->light_type == LIGHT_SPOT) {
shader_id &= ~SHADER_AREA_LIGHT;
diff --git a/intern/cycles/render/light.h b/intern/cycles/render/light.h
index 39014b5d667..fbd709125ff 100644
--- a/intern/cycles/render/light.h
+++ b/intern/cycles/render/light.h
@@ -58,6 +58,7 @@ class Light : public Node {
NODE_SOCKET_API(float3, axisv)
NODE_SOCKET_API(float, sizev)
NODE_SOCKET_API(bool, round)
+ NODE_SOCKET_API(float, spread)
NODE_SOCKET_API(Transform, tfm)
diff --git a/intern/cycles/render/mesh.cpp b/intern/cycles/render/mesh.cpp
index d5e5b960665..fd9879dd5dd 100644
--- a/intern/cycles/render/mesh.cpp
+++ b/intern/cycles/render/mesh.cpp
@@ -805,7 +805,7 @@ void Mesh::pack_patches(uint *patch_data, uint vert_offset, uint face_offset, ui
}
}
-void Mesh::pack_primitives(ccl::PackedBVH *pack, int object, uint visibility, bool pack_all)
+void Mesh::pack_primitives(ccl::PackedBVH *pack, int object, uint visibility, PackFlags pack_flags)
{
if (triangles.empty())
return;
@@ -819,28 +819,38 @@ void Mesh::pack_primitives(ccl::PackedBVH *pack, int object, uint visibility, bo
uint type = has_motion_blur() ? PRIMITIVE_MOTION_TRIANGLE : PRIMITIVE_TRIANGLE;
- if (pack_all) {
+ /* Separate loop as other arrays are not initialized if their packing is not required. */
+ if ((pack_flags & PackFlags::PACK_VISIBILITY) != 0) {
+ unsigned int *prim_visibility = &pack->prim_visibility[optix_prim_offset];
+ for (size_t k = 0; k < num_prims; ++k) {
+ prim_visibility[k] = visibility;
+ }
+ }
+
+ if ((pack_flags & PackFlags::PACK_GEOMETRY) != 0) {
/* Use optix_prim_offset for indexing as those arrays also contain data for Hair geometries. */
unsigned int *prim_tri_index = &pack->prim_tri_index[optix_prim_offset];
int *prim_type = &pack->prim_type[optix_prim_offset];
- unsigned int *prim_visibility = &pack->prim_visibility[optix_prim_offset];
int *prim_index = &pack->prim_index[optix_prim_offset];
int *prim_object = &pack->prim_object[optix_prim_offset];
for (size_t k = 0; k < num_prims; ++k) {
- prim_tri_index[k] = (prim_offset + k) * 3;
- prim_type[k] = type;
- prim_index[k] = prim_offset + k;
- prim_object[k] = object;
- prim_visibility[k] = visibility;
+ if ((pack_flags & PackFlags::PACK_GEOMETRY) != 0) {
+ prim_tri_index[k] = (prim_offset + k) * 3;
+ prim_type[k] = type;
+ prim_index[k] = prim_offset + k;
+ prim_object[k] = object;
+ }
}
}
- for (size_t k = 0; k < num_prims; ++k) {
- const Mesh::Triangle t = get_triangle(k);
- prim_tri_verts[k * 3] = float3_to_float4(verts[t.v[0]]);
- prim_tri_verts[k * 3 + 1] = float3_to_float4(verts[t.v[1]]);
- prim_tri_verts[k * 3 + 2] = float3_to_float4(verts[t.v[2]]);
+ if ((pack_flags & PackFlags::PACK_VERTICES) != 0) {
+ for (size_t k = 0; k < num_prims; ++k) {
+ const Mesh::Triangle t = get_triangle(k);
+ prim_tri_verts[k * 3] = float3_to_float4(verts[t.v[0]]);
+ prim_tri_verts[k * 3 + 1] = float3_to_float4(verts[t.v[1]]);
+ prim_tri_verts[k * 3 + 2] = float3_to_float4(verts[t.v[2]]);
+ }
}
}
diff --git a/intern/cycles/render/mesh.h b/intern/cycles/render/mesh.h
index 2b0ff92ab62..e9e79f7f20d 100644
--- a/intern/cycles/render/mesh.h
+++ b/intern/cycles/render/mesh.h
@@ -232,7 +232,10 @@ class Mesh : public Geometry {
size_t tri_offset);
void pack_patches(uint *patch_data, uint vert_offset, uint face_offset, uint corner_offset);
- void pack_primitives(PackedBVH *pack, int object, uint visibility, bool pack_all) override;
+ void pack_primitives(PackedBVH *pack,
+ int object,
+ uint visibility,
+ PackFlags pack_flags) override;
void tessellate(DiagSplit *split);
diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp
index f3d420c6fcb..5792d2458d1 100644
--- a/intern/cycles/render/nodes.cpp
+++ b/intern/cycles/render/nodes.cpp
@@ -6093,6 +6093,7 @@ NODE_DEFINE(VectorMathNode)
type_enum.insert("reflect", NODE_VECTOR_MATH_REFLECT);
type_enum.insert("refract", NODE_VECTOR_MATH_REFRACT);
type_enum.insert("faceforward", NODE_VECTOR_MATH_FACEFORWARD);
+ type_enum.insert("multiply_add", NODE_VECTOR_MATH_MULTIPLY_ADD);
type_enum.insert("dot_product", NODE_VECTOR_MATH_DOT_PRODUCT);
@@ -6165,7 +6166,8 @@ void VectorMathNode::compile(SVMCompiler &compiler)
int vector_stack_offset = compiler.stack_assign_if_linked(vector_out);
/* 3 Vector Operators */
- if (math_type == NODE_VECTOR_MATH_WRAP || math_type == NODE_VECTOR_MATH_FACEFORWARD) {
+ if (math_type == NODE_VECTOR_MATH_WRAP || math_type == NODE_VECTOR_MATH_FACEFORWARD ||
+ math_type == NODE_VECTOR_MATH_MULTIPLY_ADD) {
ShaderInput *vector3_in = input("Vector3");
int vector3_stack_offset = compiler.stack_assign(vector3_in);
compiler.add_node(
diff --git a/intern/cycles/render/nodes.h b/intern/cycles/render/nodes.h
index fb9cf0c9836..99cb0b779b8 100644
--- a/intern/cycles/render/nodes.h
+++ b/intern/cycles/render/nodes.h
@@ -1600,11 +1600,23 @@ class SetNormalNode : public ShaderNode {
NODE_SOCKET_API(float3, direction)
};
-class OSLNode : public ShaderNode {
+class OSLNode final : public ShaderNode {
public:
static OSLNode *create(ShaderGraph *graph, size_t num_inputs, const OSLNode *from = NULL);
~OSLNode();
+ static void operator delete(void *ptr)
+ {
+ /* Override delete operator to silence new-delete-type-mismatch ASAN warnings
+ * regarding size mismatch in the destructor. This is intentional as we allocate
+ * extra space at the end of the node. */
+ ::operator delete(ptr);
+ }
+ static void operator delete(void *, void *)
+ {
+ /* Deliberately empty placement delete operator, to avoid MSVC warning C4291. */
+ }
+
ShaderNode *clone(ShaderGraph *graph) const;
char *input_default_value();
diff --git a/intern/cycles/render/object.cpp b/intern/cycles/render/object.cpp
index 52f63685aeb..f65f8bc6e90 100644
--- a/intern/cycles/render/object.cpp
+++ b/intern/cycles/render/object.cpp
@@ -153,10 +153,6 @@ void Object::update_motion()
void Object::compute_bounds(bool motion_blur)
{
- if (!is_modified() && !geometry->is_modified()) {
- return;
- }
-
BoundBox mbounds = geometry->bounds;
if (motion_blur && use_motion()) {
@@ -224,6 +220,10 @@ void Object::tag_update(Scene *scene)
flag |= ObjectManager::TRANSFORM_MODIFIED;
}
+ if (visibility_is_modified()) {
+ flag |= ObjectManager::VISIBILITY_MODIFIED;
+ }
+
foreach (Node *node, geometry->get_used_shaders()) {
Shader *shader = static_cast<Shader *>(node);
if (shader->get_use_mis() && shader->has_surface_emission)
@@ -918,6 +918,10 @@ void ObjectManager::tag_update(Scene *scene, uint32_t flag)
geometry_flag |= GeometryManager::TRANSFORM_MODIFIED;
}
+ if ((flag & VISIBILITY_MODIFIED) != 0) {
+ geometry_flag |= GeometryManager::VISIBILITY_MODIFIED;
+ }
+
scene->geometry_manager->tag_update(scene, geometry_flag);
}
diff --git a/intern/cycles/render/object.h b/intern/cycles/render/object.h
index 23682270fd1..e4bc7ac3d8e 100644
--- a/intern/cycles/render/object.h
+++ b/intern/cycles/render/object.h
@@ -134,6 +134,7 @@ class ObjectManager {
OBJECT_MODIFIED = (1 << 5),
HOLDOUT_MODIFIED = (1 << 6),
TRANSFORM_MODIFIED = (1 << 7),
+ VISIBILITY_MODIFIED = (1 << 8),
/* tag everything in the manager for an update */
UPDATE_ALL = ~0u,
diff --git a/intern/cycles/render/osl.cpp b/intern/cycles/render/osl.cpp
index 53c67049571..9bd6e3a5e2d 100644
--- a/intern/cycles/render/osl.cpp
+++ b/intern/cycles/render/osl.cpp
@@ -91,10 +91,10 @@ void OSLShaderManager::reset(Scene * /*scene*/)
shading_system_init();
}
-void OSLShaderManager::device_update(Device *device,
- DeviceScene *dscene,
- Scene *scene,
- Progress &progress)
+void OSLShaderManager::device_update_specific(Device *device,
+ DeviceScene *dscene,
+ Scene *scene,
+ Progress &progress)
{
if (!need_update())
return;
@@ -1149,7 +1149,7 @@ void OSLCompiler::compile(OSLGlobals *og, Shader *shader)
shader->has_integrator_dependency = false;
/* generate surface shader */
- if (shader->used && graph && output->input("Surface")->link) {
+ if (shader->reference_count() && graph && output->input("Surface")->link) {
shader->osl_surface_ref = compile_type(shader, shader->graph, SHADER_TYPE_SURFACE);
if (has_bump)
@@ -1165,7 +1165,7 @@ void OSLCompiler::compile(OSLGlobals *og, Shader *shader)
}
/* generate volume shader */
- if (shader->used && graph && output->input("Volume")->link) {
+ if (shader->reference_count() && graph && output->input("Volume")->link) {
shader->osl_volume_ref = compile_type(shader, shader->graph, SHADER_TYPE_VOLUME);
shader->has_volume = true;
}
@@ -1173,7 +1173,7 @@ void OSLCompiler::compile(OSLGlobals *og, Shader *shader)
shader->osl_volume_ref = OSL::ShaderGroupRef();
/* generate displacement shader */
- if (shader->used && graph && output->input("Displacement")->link) {
+ if (shader->reference_count() && graph && output->input("Displacement")->link) {
shader->osl_displacement_ref = compile_type(shader, shader->graph, SHADER_TYPE_DISPLACEMENT);
shader->has_displacement = true;
}
diff --git a/intern/cycles/render/osl.h b/intern/cycles/render/osl.h
index ea2d0ca492c..f6aa98d867a 100644
--- a/intern/cycles/render/osl.h
+++ b/intern/cycles/render/osl.h
@@ -72,15 +72,18 @@ class OSLShaderManager : public ShaderManager {
static void free_memory();
- void reset(Scene *scene);
+ void reset(Scene *scene) override;
- bool use_osl()
+ bool use_osl() override
{
return true;
}
- void device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress);
- void device_free(Device *device, DeviceScene *dscene, Scene *scene);
+ void device_update_specific(Device *device,
+ DeviceScene *dscene,
+ Scene *scene,
+ Progress &progress) override;
+ void device_free(Device *device, DeviceScene *dscene, Scene *scene) override;
/* osl compile and query */
static bool osl_compile(const string &inputfile, const string &outputfile);
diff --git a/intern/cycles/render/scene.cpp b/intern/cycles/render/scene.cpp
index d3dfe1c5be6..c4e7d2c79d6 100644
--- a/intern/cycles/render/scene.cpp
+++ b/intern/cycles/render/scene.cpp
@@ -143,21 +143,27 @@ void Scene::free_memory(bool final)
delete bvh;
bvh = NULL;
- foreach (Shader *s, shaders)
- delete s;
- /* delete procedurals before other types as they may hold pointers to those types */
+ /* The order of deletion is important to make sure data is freed based on possible dependencies
+ * as the Nodes' reference counts are decremented in the destructors:
+ *
+ * - Procedurals can create and hold pointers to any other types.
+ * - Objects can hold pointers to Geometries and ParticleSystems
+ * - Lights and Geometries can hold pointers to Shaders.
+ *
+ * Similarly, we first delete all nodes and their associated device data, and then the managers
+ * and their associated device data.
+ */
foreach (Procedural *p, procedurals)
delete p;
- foreach (Geometry *g, geometry)
- delete g;
foreach (Object *o, objects)
delete o;
- foreach (Light *l, lights)
- delete l;
+ foreach (Geometry *g, geometry)
+ delete g;
foreach (ParticleSystem *p, particle_systems)
delete p;
+ foreach (Light *l, lights)
+ delete l;
- shaders.clear();
geometry.clear();
objects.clear();
lights.clear();
@@ -169,7 +175,25 @@ void Scene::free_memory(bool final)
film->device_free(device, &dscene, this);
background->device_free(device, &dscene);
integrator->device_free(device, &dscene, true);
+ }
+ if (final) {
+ delete camera;
+ delete dicing_camera;
+ delete film;
+ delete background;
+ delete integrator;
+ }
+
+ /* Delete Shaders after every other nodes to ensure that we do not try to decrement the reference
+ * count on some dangling pointer. */
+ foreach (Shader *s, shaders)
+ delete s;
+
+ shaders.clear();
+
+ /* Now that all nodes have been deleted, we can safely delete managers and device data. */
+ if (device) {
object_manager->device_free(device, &dscene, true);
geometry_manager->device_free(device, &dscene, true);
shader_manager->device_free(device, &dscene, this);
@@ -179,7 +203,7 @@ void Scene::free_memory(bool final)
bake_manager->device_free(device, &dscene);
- if (!params.persistent_data || final)
+ if (final)
image_manager->device_free(device);
else
image_manager->device_free_builtin(device);
@@ -189,11 +213,6 @@ void Scene::free_memory(bool final)
if (final) {
delete lookup_tables;
- delete camera;
- delete dicing_camera;
- delete film;
- delete background;
- delete integrator;
delete object_manager;
delete geometry_manager;
delete shader_manager;
@@ -504,9 +523,6 @@ bool Scene::update(Progress &progress, bool &kernel_switch_needed)
{
/* update scene */
if (need_update()) {
- /* Updated used shader tag so we know which features are need for the kernel. */
- shader_manager->update_shaders_used(this);
-
/* Update max_closures. */
KernelIntegrator *kintegrator = &dscene.data.integrator;
if (params.background) {
@@ -526,9 +542,6 @@ bool Scene::update(Progress &progress, bool &kernel_switch_needed)
DeviceKernelStatus kernel_switch_status = device->get_active_kernel_switch_state();
kernel_switch_needed = kernel_switch_status == DEVICE_KERNEL_FEATURE_KERNEL_AVAILABLE ||
kernel_switch_status == DEVICE_KERNEL_FEATURE_KERNEL_INVALID;
- if (kernel_switch_status == DEVICE_KERNEL_WAITING_FOR_FEATURE_KERNEL) {
- progress.set_kernel_status("Compiling render kernels");
- }
if (new_kernels_needed || kernel_switch_needed) {
progress.set_kernel_status("Compiling render kernels");
device->wait_for_availability(loaded_kernel_features);
@@ -566,9 +579,6 @@ bool Scene::load_kernels(Progress &progress, bool lock_scene)
return false;
}
- progress.add_skip_time(timer, false);
- VLOG(1) << "Total time spent loading kernels: " << time_dt() - timer.get_start();
-
kernels_loaded = true;
loaded_kernel_features = requested_features;
return true;
@@ -587,7 +597,7 @@ int Scene::get_max_closure_count()
int max_closures = 0;
for (int i = 0; i < shaders.size(); i++) {
Shader *shader = shaders[i];
- if (shader->used) {
+ if (shader->reference_count()) {
int num_closures = shader->graph->get_num_closures();
max_closures = max(max_closures, num_closures);
}
@@ -748,9 +758,10 @@ template<> void Scene::delete_node_impl(ParticleSystem *node)
particle_system_manager->tag_update(this);
}
-template<> void Scene::delete_node_impl(Shader * /*node*/)
+template<> void Scene::delete_node_impl(Shader *shader)
{
/* don't delete unused shaders, not supported */
+ shader->clear_reference_count();
}
template<> void Scene::delete_node_impl(Procedural *node)
@@ -817,9 +828,12 @@ template<> void Scene::delete_nodes(const set<ParticleSystem *> &nodes, const No
particle_system_manager->tag_update(this);
}
-template<> void Scene::delete_nodes(const set<Shader *> & /*nodes*/, const NodeOwner * /*owner*/)
+template<> void Scene::delete_nodes(const set<Shader *> &nodes, const NodeOwner * /*owner*/)
{
/* don't delete unused shaders, not supported */
+ for (Shader *shader : nodes) {
+ shader->clear_reference_count();
+ }
}
template<> void Scene::delete_nodes(const set<Procedural *> &nodes, const NodeOwner *owner)
diff --git a/intern/cycles/render/scene.h b/intern/cycles/render/scene.h
index 61c753e411c..a6dab06f8f2 100644
--- a/intern/cycles/render/scene.h
+++ b/intern/cycles/render/scene.h
@@ -178,7 +178,6 @@ class SceneParams {
int num_bvh_time_steps;
int hair_subdivisions;
CurveShapeType hair_shape;
- bool persistent_data;
int texture_limit;
bool background;
@@ -193,7 +192,6 @@ class SceneParams {
num_bvh_time_steps = 0;
hair_subdivisions = 3;
hair_shape = CURVE_RIBBON;
- persistent_data = false;
texture_limit = 0;
background = true;
}
@@ -206,7 +204,7 @@ class SceneParams {
use_bvh_unaligned_nodes == params.use_bvh_unaligned_nodes &&
num_bvh_time_steps == params.num_bvh_time_steps &&
hair_subdivisions == params.hair_subdivisions && hair_shape == params.hair_shape &&
- persistent_data == params.persistent_data && texture_limit == params.texture_limit);
+ texture_limit == params.texture_limit);
}
int curve_subdivisions()
diff --git a/intern/cycles/render/session.cpp b/intern/cycles/render/session.cpp
index 3c601e18126..7830ca2293a 100644
--- a/intern/cycles/render/session.cpp
+++ b/intern/cycles/render/session.cpp
@@ -243,11 +243,6 @@ void Session::run_gpu()
}
}
- /* Don't go in pause mode when image was rendered with preview kernels
- * When feature kernels become available the session will be reset. */
- else if (no_tiles && kernel_state == DEVICE_KERNEL_WAITING_FOR_FEATURE_KERNEL) {
- time_sleep(0.1);
- }
else if (no_tiles && kernel_state == DEVICE_KERNEL_FEATURE_KERNEL_AVAILABLE) {
reset_gpu(tile_manager.params, params.samples);
}
@@ -762,11 +757,6 @@ void Session::run_cpu()
}
}
- /* Don't go in pause mode when preview kernels are used
- * When feature kernels become available the session will be reset. */
- else if (no_tiles && kernel_state == DEVICE_KERNEL_WAITING_FOR_FEATURE_KERNEL) {
- time_sleep(0.1);
- }
else if (no_tiles && kernel_state == DEVICE_KERNEL_FEATURE_KERNEL_AVAILABLE) {
reset_cpu(tile_manager.params, params.samples);
}
diff --git a/intern/cycles/render/shader.cpp b/intern/cycles/render/shader.cpp
index 5ecbd92d96d..59b60904746 100644
--- a/intern/cycles/render/shader.cpp
+++ b/intern/cycles/render/shader.cpp
@@ -16,7 +16,6 @@
#include "device/device.h"
-#include "render/alembic.h"
#include "render/background.h"
#include "render/camera.h"
#include "render/colorspace.h"
@@ -27,6 +26,7 @@
#include "render/nodes.h"
#include "render/object.h"
#include "render/osl.h"
+#include "render/procedural.h"
#include "render/scene.h"
#include "render/shader.h"
#include "render/svm.h"
@@ -218,7 +218,6 @@ Shader::Shader() : Node(get_node_type())
displacement_method = DISPLACE_BUMP;
id = -1;
- used = false;
need_update_uvs = true;
need_update_attribute = true;
@@ -382,8 +381,9 @@ void Shader::tag_used(Scene *scene)
{
/* if an unused shader suddenly gets used somewhere, it needs to be
* recompiled because it was skipped for compilation before */
- if (!used) {
+ if (!reference_count()) {
tag_modified();
+ /* We do not reference here as the shader will be referenced when added to a socket. */
scene->shader_manager->tag_update(scene, ShaderManager::SHADER_MODIFIED);
}
}
@@ -461,52 +461,28 @@ int ShaderManager::get_shader_id(Shader *shader, bool smooth)
return id;
}
-void ShaderManager::update_shaders_used(Scene *scene)
+void ShaderManager::device_update(Device *device,
+ DeviceScene *dscene,
+ Scene *scene,
+ Progress &progress)
{
if (!need_update()) {
return;
}
- /* figure out which shaders are in use, so SVM/OSL can skip compiling them
- * for speed and avoid loading image textures into memory */
uint id = 0;
foreach (Shader *shader, scene->shaders) {
- shader->used = false;
shader->id = id++;
}
- scene->default_surface->used = true;
- scene->default_light->used = true;
- scene->default_background->used = true;
- scene->default_empty->used = true;
+ /* Those shaders should always be compiled as they are used as fallback if a shader cannot be
+ * found, e.g. bad shader index for the triangle shaders on a Mesh. */
+ assert(scene->default_surface->reference_count() != 0);
+ assert(scene->default_light->reference_count() != 0);
+ assert(scene->default_background->reference_count() != 0);
+ assert(scene->default_empty->reference_count() != 0);
- if (scene->background->get_shader())
- scene->background->get_shader()->used = true;
-
-#ifdef WITH_ALEMBIC
- foreach (Procedural *procedural, scene->procedurals) {
- AlembicProcedural *abc_proc = static_cast<AlembicProcedural *>(procedural);
-
- foreach (Node *abc_node, abc_proc->get_objects()) {
- AlembicObject *abc_object = static_cast<AlembicObject *>(abc_node);
-
- foreach (Node *node, abc_object->get_used_shaders()) {
- Shader *shader = static_cast<Shader *>(node);
- shader->used = true;
- }
- }
- }
-#endif
-
- foreach (Geometry *geom, scene->geometry)
- foreach (Node *node, geom->get_used_shaders()) {
- Shader *shader = static_cast<Shader *>(node);
- shader->used = true;
- }
-
- foreach (Light *light, scene->lights)
- if (light->get_shader())
- const_cast<Shader *>(light->get_shader())->used = true;
+ device_update_specific(device, dscene, scene, progress);
}
void ShaderManager::device_update_common(Device *device,
@@ -528,6 +504,8 @@ void ShaderManager::device_update_common(Device *device,
if (shader->get_use_mis())
flag |= SD_USE_MIS;
+ if (shader->has_surface_emission)
+ flag |= SD_HAS_EMISSION;
if (shader->has_surface_transparent && shader->get_use_transparent_shadow())
flag |= SD_HAS_TRANSPARENT_SHADOW;
if (shader->has_volume) {
@@ -637,6 +615,7 @@ void ShaderManager::add_default(Scene *scene)
Shader *shader = scene->create_node<Shader>();
shader->name = "default_surface";
shader->set_graph(graph);
+ shader->reference();
scene->default_surface = shader;
shader->tag_update(scene);
}
@@ -655,6 +634,8 @@ void ShaderManager::add_default(Scene *scene)
shader->set_graph(graph);
scene->default_volume = shader;
shader->tag_update(scene);
+ /* No default reference for the volume to avoid compiling volume kernels if there are no actual
+ * volumes in the scene */
}
/* default light */
@@ -671,6 +652,7 @@ void ShaderManager::add_default(Scene *scene)
Shader *shader = scene->create_node<Shader>();
shader->name = "default_light";
shader->set_graph(graph);
+ shader->reference();
scene->default_light = shader;
shader->tag_update(scene);
}
@@ -682,6 +664,7 @@ void ShaderManager::add_default(Scene *scene)
Shader *shader = scene->create_node<Shader>();
shader->name = "default_background";
shader->set_graph(graph);
+ shader->reference();
scene->default_background = shader;
shader->tag_update(scene);
}
@@ -693,6 +676,7 @@ void ShaderManager::add_default(Scene *scene)
Shader *shader = scene->create_node<Shader>();
shader->name = "default_empty";
shader->set_graph(graph);
+ shader->reference();
scene->default_empty = shader;
shader->tag_update(scene);
}
@@ -733,7 +717,7 @@ void ShaderManager::get_requested_features(Scene *scene,
requested_features->nodes_features = 0;
for (int i = 0; i < scene->shaders.size(); i++) {
Shader *shader = scene->shaders[i];
- if (!shader->used) {
+ if (!shader->reference_count()) {
continue;
}
diff --git a/intern/cycles/render/shader.h b/intern/cycles/render/shader.h
index f47d64f346c..50c8bed4669 100644
--- a/intern/cycles/render/shader.h
+++ b/intern/cycles/render/shader.h
@@ -132,7 +132,6 @@ class Shader : public Node {
/* determined before compiling */
uint id;
- bool used;
#ifdef WITH_OSL
/* osl shading state references */
@@ -187,10 +186,11 @@ class ShaderManager {
}
/* device update */
- virtual void device_update(Device *device,
- DeviceScene *dscene,
- Scene *scene,
- Progress &progress) = 0;
+ void device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress);
+ virtual void device_update_specific(Device *device,
+ DeviceScene *dscene,
+ Scene *scene,
+ Progress &progress) = 0;
virtual void device_free(Device *device, DeviceScene *dscene, Scene *scene) = 0;
void device_update_common(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress);
@@ -208,7 +208,6 @@ class ShaderManager {
static void add_default(Scene *scene);
/* Selective nodes compilation. */
- void update_shaders_used(Scene *scene);
void get_requested_features(Scene *scene, DeviceRequestedFeatures *requested_features);
static void free_memory();
diff --git a/intern/cycles/render/svm.cpp b/intern/cycles/render/svm.cpp
index fce604234f1..5c793c5c016 100644
--- a/intern/cycles/render/svm.cpp
+++ b/intern/cycles/render/svm.cpp
@@ -69,10 +69,10 @@ void SVMShaderManager::device_update_shader(Scene *scene,
<< summary.full_report();
}
-void SVMShaderManager::device_update(Device *device,
- DeviceScene *dscene,
- Scene *scene,
- Progress &progress)
+void SVMShaderManager::device_update_specific(Device *device,
+ DeviceScene *dscene,
+ Scene *scene,
+ Progress &progress)
{
if (!need_update())
return;
@@ -776,7 +776,7 @@ void SVMCompiler::compile_type(Shader *shader, ShaderGraph *graph, ShaderType ty
add_node(NODE_ENTER_BUMP_EVAL, bump_state_offset);
}
- if (shader->used) {
+ if (shader->reference_count()) {
CompilerState state(graph);
if (clin->link) {
bool generate = false;
diff --git a/intern/cycles/render/svm.h b/intern/cycles/render/svm.h
index a4ca68e1d8d..d23ff3e2a47 100644
--- a/intern/cycles/render/svm.h
+++ b/intern/cycles/render/svm.h
@@ -44,10 +44,13 @@ class SVMShaderManager : public ShaderManager {
SVMShaderManager();
~SVMShaderManager();
- void reset(Scene *scene);
+ void reset(Scene *scene) override;
- void device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress);
- void device_free(Device *device, DeviceScene *dscene, Scene *scene);
+ void device_update_specific(Device *device,
+ DeviceScene *dscene,
+ Scene *scene,
+ Progress &progress) override;
+ void device_free(Device *device, DeviceScene *dscene, Scene *scene) override;
protected:
void device_update_shader(Scene *scene,
diff --git a/intern/cycles/test/util_avxf_avx2_test.cpp b/intern/cycles/test/util_avxf_avx2_test.cpp
index c5365a81a51..4a98a6fca73 100644
--- a/intern/cycles/test/util_avxf_avx2_test.cpp
+++ b/intern/cycles/test/util_avxf_avx2_test.cpp
@@ -18,6 +18,7 @@
#define TEST_CATEGORY_NAME util_avx2
-#if defined(i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64)
+#if (defined(i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64)) && \
+ defined(__AVX2__)
# include "util_avxf_test.h"
#endif
diff --git a/intern/cycles/test/util_avxf_avx_test.cpp b/intern/cycles/test/util_avxf_avx_test.cpp
index dbac20c69b3..2ae8afd52ea 100644
--- a/intern/cycles/test/util_avxf_avx_test.cpp
+++ b/intern/cycles/test/util_avxf_avx_test.cpp
@@ -18,6 +18,7 @@
#define TEST_CATEGORY_NAME util_avx
-#if defined(i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64)
+#if (defined(i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64)) && \
+ defined(__AVX__)
# include "util_avxf_test.h"
#endif
diff --git a/intern/cycles/util/util_math.h b/intern/cycles/util/util_math.h
index ad04340231e..c5996ebfcb6 100644
--- a/intern/cycles/util/util_math.h
+++ b/intern/cycles/util/util_math.h
@@ -788,7 +788,7 @@ ccl_device_inline float compare_floats(float a, float b, float abs_diff, int ulp
}
/* Calculate the angle between the two vectors a and b.
- * The usual approach acos(dot(a, b)) has severe precision issues for small angles,
+ * The usual approach `acos(dot(a, b))` has severe precision issues for small angles,
* which are avoided by this method.
* Based on "Mangled Angles" from https://people.eecs.berkeley.edu/~wkahan/Mindless.pdf
*/
diff --git a/intern/cycles/util/util_math_fast.h b/intern/cycles/util/util_math_fast.h
index 107b36ce6cd..5ae56290f05 100644
--- a/intern/cycles/util/util_math_fast.h
+++ b/intern/cycles/util/util_math_fast.h
@@ -362,7 +362,7 @@ ccl_device float fast_atan2f(float y, float x)
ccl_device float fast_log2f(float x)
{
/* NOTE: clamp to avoid special cases and make result "safe" from large
- * negative values/nans. */
+ * negative values/NAN's. */
x = clamp(x, FLT_MIN, FLT_MAX);
unsigned bits = __float_as_uint(x);
int exponent = (int)(bits >> 23) - 127;
diff --git a/intern/cycles/util/util_simd.h b/intern/cycles/util/util_simd.h
index 718ec9266b1..8e8caa98a1b 100644
--- a/intern/cycles/util/util_simd.h
+++ b/intern/cycles/util/util_simd.h
@@ -124,7 +124,7 @@ static struct StepTy {
template<class type, int i0, int i1, int i2, int i3> type shuffle_neon(const type &a)
{
if (i0 == i1 && i0 == i2 && i0 == i3) {
- return vdupq_laneq_s32(a, i0);
+ return type(vdupq_laneq_s32(int32x4_t(a), i0));
}
static const uint8_t tbl[16] = {(i0 * 4) + 0,
(i0 * 4) + 1,
@@ -143,7 +143,7 @@ template<class type, int i0, int i1, int i2, int i3> type shuffle_neon(const typ
(i3 * 4) + 2,
(i3 * 4) + 3};
- return vqtbl1q_s8(int8x16_t(a), *(int8x16_t *)tbl);
+ return type(vqtbl1q_s8(int8x16_t(a), *(uint8x16_t *)tbl));
}
template<class type, int i0, int i1, int i2, int i3>
@@ -167,7 +167,7 @@ type shuffle_neon(const type &a, const type &b)
(i3 * 4) + 2,
(i3 * 4) + 3};
- return vqtbl1q_s8(int8x16_t(b), *(int8x16_t *)tbl);
+ return type(vqtbl1q_s8(int8x16_t(b), *(uint8x16_t *)tbl));
}
else {
@@ -188,7 +188,7 @@ type shuffle_neon(const type &a, const type &b)
(i3 * 4) + 2 + 16,
(i3 * 4) + 3 + 16};
- return vqtbl2q_s8((int8x16x2_t){a, b}, *(int8x16_t *)tbl);
+ return type(vqtbl2q_s8((int8x16x2_t){int8x16_t(a), int8x16_t(b)}, *(uint8x16_t *)tbl));
}
}
#endif /* __KERNEL_NEON */
diff --git a/intern/cycles/util/util_sseb.h b/intern/cycles/util/util_sseb.h
index 1488da46b09..4dbd5b8046e 100644
--- a/intern/cycles/util/util_sseb.h
+++ b/intern/cycles/util/util_sseb.h
@@ -283,7 +283,7 @@ __forceinline uint32_t popcnt(const sseb &a)
{
# if defined(__KERNEL_NEON__)
const int32x4_t mask = {1, 1, 1, 1};
- int32x4_t t = vandq_s32(a.m128, mask);
+ int32x4_t t = vandq_s32(vreinterpretq_s32_m128(a.m128), mask);
return vaddvq_s32(t);
# else
return _mm_popcnt_u32(_mm_movemask_ps(a));
@@ -299,7 +299,7 @@ __forceinline uint32_t popcnt(const sseb &a)
__forceinline bool reduce_and(const sseb &a)
{
# if defined(__KERNEL_NEON__)
- return vaddvq_s32(a.m128) == -4;
+ return vaddvq_s32(vreinterpretq_s32_m128(a.m128)) == -4;
# else
return _mm_movemask_ps(a) == 0xf;
# endif
@@ -307,7 +307,7 @@ __forceinline bool reduce_and(const sseb &a)
__forceinline bool reduce_or(const sseb &a)
{
# if defined(__KERNEL_NEON__)
- return vaddvq_s32(a.m128) != 0x0;
+ return vaddvq_s32(vreinterpretq_s32_m128(a.m128)) != 0x0;
# else
return _mm_movemask_ps(a) != 0x0;
# endif
@@ -315,7 +315,7 @@ __forceinline bool reduce_or(const sseb &a)
__forceinline bool all(const sseb &b)
{
# if defined(__KERNEL_NEON__)
- return vaddvq_s32(b.m128) == -4;
+ return vaddvq_s32(vreinterpretq_s32_m128(b.m128)) == -4;
# else
return _mm_movemask_ps(b) == 0xf;
# endif
@@ -323,7 +323,7 @@ __forceinline bool all(const sseb &b)
__forceinline bool any(const sseb &b)
{
# if defined(__KERNEL_NEON__)
- return vaddvq_s32(b.m128) != 0x0;
+ return vaddvq_s32(vreinterpretq_s32_m128(b.m128)) != 0x0;
# else
return _mm_movemask_ps(b) != 0x0;
# endif
@@ -331,7 +331,7 @@ __forceinline bool any(const sseb &b)
__forceinline bool none(const sseb &b)
{
# if defined(__KERNEL_NEON__)
- return vaddvq_s32(b.m128) == 0x0;
+ return vaddvq_s32(vreinterpretq_s32_m128(b.m128)) == 0x0;
# else
return _mm_movemask_ps(b) == 0x0;
# endif
diff --git a/intern/cycles/util/util_ssef.h b/intern/cycles/util/util_ssef.h
index d039b50a7d2..0c81ed87553 100644
--- a/intern/cycles/util/util_ssef.h
+++ b/intern/cycles/util/util_ssef.h
@@ -596,7 +596,7 @@ template<size_t i0, size_t i1, size_t i2, size_t i3>
__forceinline const ssef shuffle(const ssef &b)
{
# ifdef __KERNEL_NEON__
- return shuffle_neon<ssef, i0, i1, i2, i3>(b.m128);
+ return shuffle_neon<float32x4_t, i0, i1, i2, i3>(b.m128);
# else
return _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(b), _MM_SHUFFLE(i3, i2, i1, i0)));
# endif
@@ -625,7 +625,7 @@ __forceinline const ssef shuffle(const ssef &a, const ssef &b)
template<size_t i0> __forceinline const ssef shuffle(const ssef &a, const ssef &b)
{
# ifdef __KERNEL_NEON__
- return shuffle<float32x4_t, i0, i0, i0, i0>(a, b);
+ return shuffle_neon<float32x4_t, i0, i0, i0, i0>(a, b);
# else
return _mm_shuffle_ps(a, b, _MM_SHUFFLE(i0, i0, i0, i0));
# endif
diff --git a/intern/cycles/util/util_ssei.h b/intern/cycles/util/util_ssei.h
index 3ec69ab3700..cd51dbff2f1 100644
--- a/intern/cycles/util/util_ssei.h
+++ b/intern/cycles/util/util_ssei.h
@@ -446,7 +446,8 @@ template<size_t i0, size_t i1, size_t i2, size_t i3>
__forceinline const ssei shuffle(const ssei &a)
{
# ifdef __KERNEL_NEON__
- return shuffle_neon<ssei, i0, i1, i2, i3>(a);
+ int32x4_t result = shuffle_neon<int32x4_t, i0, i1, i2, i3>(vreinterpretq_s32_m128i(a));
+ return vreinterpretq_m128i_s32(result);
# else
return _mm_shuffle_epi32(a, _MM_SHUFFLE(i3, i2, i1, i0));
# endif
@@ -456,7 +457,9 @@ template<size_t i0, size_t i1, size_t i2, size_t i3>
__forceinline const ssei shuffle(const ssei &a, const ssei &b)
{
# ifdef __KERNEL_NEON__
- return shuffle_neon<ssei, i0, i1, i2, i3>(a, b);
+ int32x4_t result = shuffle_neon<int32x4_t, i0, i1, i2, i3>(vreinterpretq_s32_m128i(a),
+ vreinterpretq_s32_m128i(b));
+ return vreinterpretq_m128i_s32(result);
# else
return _mm_castps_si128(
_mm_shuffle_ps(_mm_castsi128_ps(a), _mm_castsi128_ps(b), _MM_SHUFFLE(i3, i2, i1, i0)));
@@ -514,7 +517,7 @@ __forceinline const ssei vreduce_add(const ssei &v)
__forceinline int reduce_min(const ssei &v)
{
# ifdef __KERNEL_NEON__
- return vminvq_s32(v);
+ return vminvq_s32(vreinterpretq_s32_m128i(v));
# else
return extract<0>(vreduce_min(v));
# endif
@@ -522,7 +525,7 @@ __forceinline int reduce_min(const ssei &v)
__forceinline int reduce_max(const ssei &v)
{
# ifdef __KERNEL_NEON__
- return vmaxvq_s32(v);
+ return vmaxvq_s32(vreinterpretq_s32_m128i(v));
# else
return extract<0>(vreduce_max(v));
# endif
@@ -530,7 +533,7 @@ __forceinline int reduce_max(const ssei &v)
__forceinline int reduce_add(const ssei &v)
{
# ifdef __KERNEL_NEON__
- return vaddvq_s32(v);
+ return vaddvq_s32(vreinterpretq_s32_m128i(v));
# else
return extract<0>(vreduce_add(v));
# endif
diff --git a/intern/cycles/util/util_system.cpp b/intern/cycles/util/util_system.cpp
index 2c1716ce515..03bc5aea1dd 100644
--- a/intern/cycles/util/util_system.cpp
+++ b/intern/cycles/util/util_system.cpp
@@ -145,7 +145,8 @@ int system_cpu_num_active_group_processors()
return numaAPI_GetNumCurrentNodesProcessors();
}
-#if !defined(_WIN32) || defined(FREE_WINDOWS)
+/* Equivalent of Windows __cpuid for x86 processors on other platforms. */
+#if (!defined(_WIN32) || defined(FREE_WINDOWS)) && (defined(__x86_64__) || defined(__i386__))
static void __cpuid(int data[4], int selector)
{
# if defined(__x86_64__)
@@ -166,24 +167,54 @@ static void __cpuid(int data[4], int selector)
string system_cpu_brand_string()
{
+#if defined(__APPLE__)
+ /* Get from system on macOS. */
+ char modelname[512] = "";
+ size_t bufferlen = 512;
+ if (sysctlbyname("machdep.cpu.brand_string", &modelname, &bufferlen, NULL, 0) == 0) {
+ return modelname;
+ }
+#elif defined(WIN32) || defined(__x86_64__) || defined(__i386__)
+ /* Get from intrinsics on Windows and x86. */
char buf[49] = {0};
int result[4] = {0};
__cpuid(result, 0x80000000);
- if (result[0] >= (int)0x80000004) {
+ if (result[0] != 0 && result[0] >= (int)0x80000004) {
__cpuid((int *)(buf + 0), 0x80000002);
__cpuid((int *)(buf + 16), 0x80000003);
__cpuid((int *)(buf + 32), 0x80000004);
string brand = buf;
- /* make it a bit more presentable */
+ /* Make it a bit more presentable. */
brand = string_remove_trademark(brand);
return brand;
}
-
+#else
+ /* Get from /proc/cpuinfo on Unix systems. */
+ FILE *cpuinfo = fopen("/proc/cpuinfo", "r");
+ if (cpuinfo != nullptr) {
+ char cpuinfo_buf[513] = "";
+ fread(cpuinfo_buf, sizeof(cpuinfo_buf) - 1, 1, cpuinfo);
+ fclose(cpuinfo);
+
+ char *modelname = strstr(cpuinfo_buf, "model name");
+ if (modelname != nullptr) {
+ modelname = strchr(modelname, ':');
+ if (modelname != nullptr) {
+ modelname += 2;
+ char *modelname_end = strchr(modelname, '\n');
+ if (modelname_end != nullptr) {
+ *modelname_end = '\0';
+ return modelname;
+ }
+ }
+ }
+ }
+#endif
return "Unknown CPU";
}
@@ -192,7 +223,7 @@ int system_cpu_bits()
return (sizeof(void *) * 8);
}
-#if defined(__x86_64__) || defined(_M_X64) || defined(i386) || defined(_M_IX86)
+#if defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || defined(_M_IX86)
struct CPUCapabilities {
bool x64;
diff --git a/intern/cycles/util/util_task.h b/intern/cycles/util/util_task.h
index 7c39ed675b5..906bf420756 100644
--- a/intern/cycles/util/util_task.h
+++ b/intern/cycles/util/util_task.h
@@ -32,7 +32,7 @@ typedef function<void(void)> TaskRunFunction;
/* Task Pool
*
- * Pool of tasks that will be executed by the central TaskScheduler.For each
+ * Pool of tasks that will be executed by the central TaskScheduler. For each
* pool, we can wait for all tasks to be done, or cancel them before they are
* done.
*
@@ -77,7 +77,7 @@ class TaskPool {
/* Task Scheduler
*
- * Central scheduler that holds running threads ready to execute tasks. A singe
+ * Central scheduler that holds running threads ready to execute tasks. A single
* queue holds the task from all pools. */
class TaskScheduler {
diff --git a/intern/cycles/util/util_texture.h b/intern/cycles/util/util_texture.h
index b445ab1488f..71bf9c65911 100644
--- a/intern/cycles/util/util_texture.h
+++ b/intern/cycles/util/util_texture.h
@@ -21,18 +21,12 @@
CCL_NAMESPACE_BEGIN
-/* Texture limits on devices. */
-#define TEX_NUM_MAX (INT_MAX >> 4)
-
/* Color to use when textures are not found. */
#define TEX_IMAGE_MISSING_R 1
#define TEX_IMAGE_MISSING_G 0
#define TEX_IMAGE_MISSING_B 1
#define TEX_IMAGE_MISSING_A 1
-/* Texture type. */
-#define kernel_tex_type(tex) (tex & IMAGE_DATA_TYPE_MASK)
-
/* Interpolation types for textures
* cuda also use texture space to store other objects */
typedef enum InterpolationType {
@@ -45,9 +39,6 @@ typedef enum InterpolationType {
INTERPOLATION_NUM_TYPES,
} InterpolationType;
-/* Texture types
- * Since we store the type in the lower bits of a flat index,
- * the shift and bit mask constant below need to be kept in sync. */
typedef enum ImageDataType {
IMAGE_DATA_TYPE_FLOAT4 = 0,
IMAGE_DATA_TYPE_BYTE4 = 1,
@@ -75,9 +66,6 @@ typedef enum ImageAlphaType {
IMAGE_ALPHA_NUM_TYPES,
} ImageAlphaType;
-#define IMAGE_DATA_TYPE_SHIFT 4
-#define IMAGE_DATA_TYPE_MASK 0xF
-
/* Extension types for textures.
*
* Defines how the image is extrapolated past its original bounds. */
diff --git a/intern/cycles/util/util_vector.h b/intern/cycles/util/util_vector.h
index 04fb33368d9..87cd4de8438 100644
--- a/intern/cycles/util/util_vector.h
+++ b/intern/cycles/util/util_vector.h
@@ -43,8 +43,8 @@ class vector : public std::vector<value_type, allocator_type> {
/* Try as hard as possible to use zero memory. */
void free_memory()
{
- BaseClass::resize(0);
- BaseClass::shrink_to_fit();
+ vector<value_type, allocator_type> empty;
+ BaseClass::swap(empty);
}
/* Some external API might demand working with std::vector. */
diff --git a/intern/ffmpeg/ffmpeg_compat.h b/intern/ffmpeg/ffmpeg_compat.h
index 727fd4b9601..17e2a7dc05b 100644
--- a/intern/ffmpeg/ffmpeg_compat.h
+++ b/intern/ffmpeg/ffmpeg_compat.h
@@ -22,10 +22,17 @@
#include <libavformat/avformat.h>
-/* check our ffmpeg is new enough, avoids user complaints */
-#if (LIBAVFORMAT_VERSION_MAJOR < 52) || \
- ((LIBAVFORMAT_VERSION_MAJOR == 52) && (LIBAVFORMAT_VERSION_MINOR <= 64))
-# error "FFmpeg 0.7 or newer is needed, Upgrade your FFmpeg or disable it"
+/* Check if our ffmpeg is new enough, avoids user complaints.
+ * Minimum supported version is currently 3.2.0 which mean the following library versions:
+ * libavutil > 55.30
+ * libavcodec > 57.60
+ * libavformat > 57.50
+ *
+ * We only check for one of these as they are usually updated in tandem.
+ */
+#if (LIBAVFORMAT_VERSION_MAJOR < 57) || \
+ ((LIBAVFORMAT_VERSION_MAJOR == 57) && (LIBAVFORMAT_VERSION_MINOR <= 50))
+# error "FFmpeg 3.2.0 or newer is needed, Upgrade your FFmpeg or disable it"
#endif
/* end sanity check */
@@ -36,520 +43,83 @@
# define FFMPEG_INLINE static inline
#endif
-#include <libavcodec/avcodec.h>
-#include <libavutil/mathematics.h>
-#include <libavutil/opt.h>
-#include <libavutil/rational.h>
-
-#if (LIBAVFORMAT_VERSION_MAJOR > 52) || \
- ((LIBAVFORMAT_VERSION_MAJOR >= 52) && (LIBAVFORMAT_VERSION_MINOR >= 101))
-# define FFMPEG_HAVE_PARSE_UTILS 1
-# include <libavutil/parseutils.h>
-#endif
-
-#include <libswscale/swscale.h>
-
-#if (LIBAVFORMAT_VERSION_MAJOR > 52) || \
- ((LIBAVFORMAT_VERSION_MAJOR >= 52) && (LIBAVFORMAT_VERSION_MINOR >= 105))
-# define FFMPEG_HAVE_AVIO 1
-#endif
-
-#if (LIBAVCODEC_VERSION_MAJOR > 53) || \
- ((LIBAVCODEC_VERSION_MAJOR == 53) && (LIBAVCODEC_VERSION_MINOR > 1)) || \
- ((LIBAVCODEC_VERSION_MAJOR == 53) && (LIBAVCODEC_VERSION_MINOR == 1) && \
- (LIBAVCODEC_VERSION_MICRO >= 1)) || \
- ((LIBAVCODEC_VERSION_MAJOR == 52) && (LIBAVCODEC_VERSION_MINOR >= 121))
-# define FFMPEG_HAVE_DEFAULT_VAL_UNION 1
-#endif
-
-#if (LIBAVFORMAT_VERSION_MAJOR > 52) || \
- ((LIBAVFORMAT_VERSION_MAJOR >= 52) && (LIBAVFORMAT_VERSION_MINOR >= 101))
-# define FFMPEG_HAVE_AV_DUMP_FORMAT 1
-#endif
-
-#if (LIBAVFORMAT_VERSION_MAJOR > 52) || \
- ((LIBAVFORMAT_VERSION_MAJOR >= 52) && (LIBAVFORMAT_VERSION_MINOR >= 45))
-# define FFMPEG_HAVE_AV_GUESS_FORMAT 1
-#endif
-
-#if (LIBAVCODEC_VERSION_MAJOR > 52) || \
- ((LIBAVCODEC_VERSION_MAJOR >= 52) && (LIBAVCODEC_VERSION_MINOR >= 23))
-# define FFMPEG_HAVE_DECODE_AUDIO3 1
-# define FFMPEG_HAVE_DECODE_VIDEO2 1
-#endif
-
-#if (LIBAVCODEC_VERSION_MAJOR > 52) || \
- ((LIBAVCODEC_VERSION_MAJOR >= 52) && (LIBAVCODEC_VERSION_MINOR >= 64))
-# define FFMPEG_HAVE_AVMEDIA_TYPES 1
-#endif
-
-#if ((LIBAVCODEC_VERSION_MAJOR > 52) || \
- (LIBAVCODEC_VERSION_MAJOR >= 52) && (LIBAVCODEC_VERSION_MINOR >= 29)) && \
- ((LIBSWSCALE_VERSION_MAJOR > 0) || \
- (LIBSWSCALE_VERSION_MAJOR >= 0) && (LIBSWSCALE_VERSION_MINOR >= 10))
-# define FFMPEG_SWSCALE_COLOR_SPACE_SUPPORT
-#endif
-
-#if ((LIBAVCODEC_VERSION_MAJOR > 54) || \
- (LIBAVCODEC_VERSION_MAJOR >= 54) && (LIBAVCODEC_VERSION_MINOR > 14))
-# define FFMPEG_HAVE_CANON_H264_RESOLUTION_FIX
-#endif
-
-#if ((LIBAVCODEC_VERSION_MAJOR > 53) || \
- (LIBAVCODEC_VERSION_MAJOR >= 53) && (LIBAVCODEC_VERSION_MINOR >= 60))
-# define FFMPEG_HAVE_ENCODE_AUDIO2
-#endif
-
-#if ((LIBAVCODEC_VERSION_MAJOR > 53) || \
- (LIBAVCODEC_VERSION_MAJOR >= 53) && (LIBAVCODEC_VERSION_MINOR >= 42))
-# define FFMPEG_HAVE_DECODE_AUDIO4
-#endif
-
-#if ((LIBAVCODEC_VERSION_MAJOR > 54) || \
- (LIBAVCODEC_VERSION_MAJOR >= 54) && (LIBAVCODEC_VERSION_MINOR >= 13))
-# define FFMPEG_HAVE_AVFRAME_SAMPLE_RATE
-#endif
-
-#if ((LIBAVUTIL_VERSION_MAJOR > 51) || \
- (LIBAVUTIL_VERSION_MAJOR == 51) && (LIBAVUTIL_VERSION_MINOR >= 21))
-# define FFMPEG_FFV1_ALPHA_SUPPORTED
-# define FFMPEG_SAMPLE_FMT_S16P_SUPPORTED
-#else
-
-FFMPEG_INLINE
-int av_sample_fmt_is_planar(enum AVSampleFormat sample_fmt)
-{
- /* no planar formats in FFmpeg < 0.9 */
- (void)sample_fmt;
- return 0;
-}
-
-#endif
-
-/* XXX TODO Probably fix to correct modern flags in code? Not sure how old FFMPEG we want to
- * support though, so for now this will do. */
-
-#ifndef FF_MIN_BUFFER_SIZE
-# ifdef AV_INPUT_BUFFER_MIN_SIZE
-# define FF_MIN_BUFFER_SIZE AV_INPUT_BUFFER_MIN_SIZE
-# endif
-#endif
-
-#ifndef FF_INPUT_BUFFER_PADDING_SIZE
-# ifdef AV_INPUT_BUFFER_PADDING_SIZE
-# define FF_INPUT_BUFFER_PADDING_SIZE AV_INPUT_BUFFER_PADDING_SIZE
-# endif
-#endif
-
-#ifndef CODEC_FLAG_GLOBAL_HEADER
-# ifdef AV_CODEC_FLAG_GLOBAL_HEADER
-# define CODEC_FLAG_GLOBAL_HEADER AV_CODEC_FLAG_GLOBAL_HEADER
-# endif
-#endif
-
-#ifndef CODEC_FLAG_GLOBAL_HEADER
-# ifdef AV_CODEC_FLAG_GLOBAL_HEADER
-# define CODEC_FLAG_GLOBAL_HEADER AV_CODEC_FLAG_GLOBAL_HEADER
-# endif
-#endif
-
-#ifndef CODEC_FLAG_INTERLACED_DCT
-# ifdef AV_CODEC_FLAG_INTERLACED_DCT
-# define CODEC_FLAG_INTERLACED_DCT AV_CODEC_FLAG_INTERLACED_DCT
-# endif
-#endif
-
-#ifndef CODEC_FLAG_INTERLACED_ME
-# ifdef AV_CODEC_FLAG_INTERLACED_ME
-# define CODEC_FLAG_INTERLACED_ME AV_CODEC_FLAG_INTERLACED_ME
-# endif
-#endif
-
-/* FFmpeg upstream 1.0 is the first who added AV_ prefix. */
-#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(54, 59, 100)
-# define AV_CODEC_ID_NONE CODEC_ID_NONE
-# define AV_CODEC_ID_MPEG4 CODEC_ID_MPEG4
-# define AV_CODEC_ID_MJPEG CODEC_ID_MJPEG
-# define AV_CODEC_ID_DNXHD CODEC_ID_DNXHD
-# define AV_CODEC_ID_MPEG2VIDEO CODEC_ID_MPEG2VIDEO
-# define AV_CODEC_ID_MPEG1VIDEO CODEC_ID_MPEG1VIDEO
-# define AV_CODEC_ID_DVVIDEO CODEC_ID_DVVIDEO
-# define AV_CODEC_ID_THEORA CODEC_ID_THEORA
-# define AV_CODEC_ID_PNG CODEC_ID_PNG
-# define AV_CODEC_ID_QTRLE CODEC_ID_QTRLE
-# define AV_CODEC_ID_FFV1 CODEC_ID_FFV1
-# define AV_CODEC_ID_HUFFYUV CODEC_ID_HUFFYUV
-# define AV_CODEC_ID_H264 CODEC_ID_H264
-# define AV_CODEC_ID_FLV1 CODEC_ID_FLV1
+#if (LIBAVFORMAT_VERSION_MAJOR < 58) || \
+ ((LIBAVFORMAT_VERSION_MAJOR == 58) && (LIBAVFORMAT_VERSION_MINOR < 76))
+# define FFMPEG_USE_DURATION_WORKAROUND 1
-# define AV_CODEC_ID_AAC CODEC_ID_AAC
-# define AV_CODEC_ID_AC3 CODEC_ID_AC3
-# define AV_CODEC_ID_MP3 CODEC_ID_MP3
-# define AV_CODEC_ID_MP2 CODEC_ID_MP2
-# define AV_CODEC_ID_FLAC CODEC_ID_FLAC
-# define AV_CODEC_ID_PCM_U8 CODEC_ID_PCM_U8
-# define AV_CODEC_ID_PCM_S16LE CODEC_ID_PCM_S16LE
-# define AV_CODEC_ID_PCM_S24LE CODEC_ID_PCM_S24LE
-# define AV_CODEC_ID_PCM_S32LE CODEC_ID_PCM_S32LE
-# define AV_CODEC_ID_PCM_F32LE CODEC_ID_PCM_F32LE
-# define AV_CODEC_ID_PCM_F64LE CODEC_ID_PCM_F64LE
-# define AV_CODEC_ID_VORBIS CODEC_ID_VORBIS
-#endif
+/* Before ffmpeg 4.4, package duration calculation used depricated variables to calculate the
+ * packet duration. Use the function from commit
+ * github.com/FFmpeg/FFmpeg/commit/1c0885334dda9ee8652e60c586fa2e3674056586
+ * to calculate the correct framerate for ffmpeg < 4.4.
+ */
FFMPEG_INLINE
-int av_get_cropped_height_from_codec(AVCodecContext *pCodecCtx)
+void my_guess_pkt_duration(AVFormatContext *s, AVStream *st, AVPacket *pkt)
{
- int y = pCodecCtx->height;
-
-#ifndef FFMPEG_HAVE_CANON_H264_RESOLUTION_FIX
- /* really bad hack to remove this dreadfull black bar at the bottom
- with Canon footage and old ffmpeg versions.
- (to fix this properly in older ffmpeg versions one has to write a new
- demuxer...)
-
- see the actual fix here for reference:
-
- http://git.libav.org/?p=libav.git;a=commit;h=30f515091c323da59c0f1b533703dedca2f4b95d
-
- We do our best to apply this only to matching footage.
-*/
- if (pCodecCtx->width == 1920 && pCodecCtx->height == 1088 &&
- pCodecCtx->pix_fmt == PIX_FMT_YUVJ420P && pCodecCtx->codec_id == AV_CODEC_ID_H264) {
- y = 1080;
+ if (pkt->duration < 0 && st->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE) {
+ av_log(s,
+ AV_LOG_WARNING,
+ "Packet with invalid duration %" PRId64 " in stream %d\n",
+ pkt->duration,
+ pkt->stream_index);
+ pkt->duration = 0;
}
-#endif
-
- return y;
-}
-
-#if ((LIBAVUTIL_VERSION_MAJOR < 51) || \
- (LIBAVUTIL_VERSION_MAJOR == 51) && (LIBAVUTIL_VERSION_MINOR < 22))
-FFMPEG_INLINE
-int av_opt_set(void *obj, const char *name, const char *val, int search_flags)
-{
- const AVOption *rv = NULL;
- (void)search_flags;
- av_set_string3(obj, name, val, 1, &rv);
- return rv != NULL;
-}
-
-FFMPEG_INLINE
-int av_opt_set_int(void *obj, const char *name, int64_t val, int search_flags)
-{
- const AVOption *rv = NULL;
- (void)search_flags;
- rv = av_set_int(obj, name, val);
- return rv != NULL;
-}
-
-FFMPEG_INLINE
-int av_opt_set_double(void *obj, const char *name, double val, int search_flags)
-{
- const AVOption *rv = NULL;
- (void)search_flags;
- rv = av_set_double(obj, name, val);
- return rv != NULL;
-}
-
-# define AV_OPT_TYPE_INT FF_OPT_TYPE_INT
-# define AV_OPT_TYPE_INT64 FF_OPT_TYPE_INT64
-# define AV_OPT_TYPE_STRING FF_OPT_TYPE_STRING
-# define AV_OPT_TYPE_CONST FF_OPT_TYPE_CONST
-# define AV_OPT_TYPE_DOUBLE FF_OPT_TYPE_DOUBLE
-# define AV_OPT_TYPE_FLOAT FF_OPT_TYPE_FLOAT
-#endif
-
-#if ((LIBAVUTIL_VERSION_MAJOR < 51) || \
- (LIBAVUTIL_VERSION_MAJOR == 51) && (LIBAVUTIL_VERSION_MINOR < 54))
-FFMPEG_INLINE
-enum AVSampleFormat av_get_packed_sample_fmt(enum AVSampleFormat sample_fmt)
-{
- if (sample_fmt < 0 || sample_fmt >= AV_SAMPLE_FMT_NB)
- return AV_SAMPLE_FMT_NONE;
- return sample_fmt;
-}
-#endif
-#if ((LIBAVCODEC_VERSION_MAJOR < 53) || \
- (LIBAVCODEC_VERSION_MAJOR == 53 && LIBAVCODEC_VERSION_MINOR < 35))
-FFMPEG_INLINE
-int avcodec_open2(AVCodecContext *avctx, AVCodec *codec, AVDictionary **options)
-{
- /* TODO: no options are taking into account */
- (void)options;
- return avcodec_open(avctx, codec);
-}
-#endif
-
-#if ((LIBAVFORMAT_VERSION_MAJOR < 53) || \
- (LIBAVFORMAT_VERSION_MAJOR == 53 && LIBAVFORMAT_VERSION_MINOR < 21))
-FFMPEG_INLINE
-AVStream *avformat_new_stream(AVFormatContext *s, AVCodec *c)
-{
- /* TODO: no codec is taking into account */
- (void)c;
- return av_new_stream(s, 0);
-}
-
-FFMPEG_INLINE
-int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options)
-{
- /* TODO: no options are taking into account */
- (void)options;
- return av_find_stream_info(ic);
-}
-#endif
-
-#if ((LIBAVFORMAT_VERSION_MAJOR > 53) || \
- ((LIBAVFORMAT_VERSION_MAJOR == 53) && (LIBAVFORMAT_VERSION_MINOR > 32)) || \
- ((LIBAVFORMAT_VERSION_MAJOR == 53) && (LIBAVFORMAT_VERSION_MINOR == 24) && \
- (LIBAVFORMAT_VERSION_MICRO >= 100)))
-FFMPEG_INLINE
-void my_update_cur_dts(AVFormatContext *s, AVStream *ref_st, int64_t timestamp)
-{
- int i;
-
- for (i = 0; i < s->nb_streams; i++) {
- AVStream *st = s->streams[i];
-
- st->cur_dts = av_rescale(timestamp,
- st->time_base.den * (int64_t)ref_st->time_base.num,
- st->time_base.num * (int64_t)ref_st->time_base.den);
+ if (pkt->duration) {
+ return;
}
-}
-FFMPEG_INLINE
-void av_update_cur_dts(AVFormatContext *s, AVStream *ref_st, int64_t timestamp)
-{
- my_update_cur_dts(s, ref_st, timestamp);
-}
-#endif
-
-#if ((LIBAVCODEC_VERSION_MAJOR < 54) || \
- (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR < 28))
-FFMPEG_INLINE
-void avcodec_free_frame(AVFrame **frame)
-{
- /* don't need to do anything with old AVFrame
- * since it does not have malloced members */
- (void)frame;
-}
-#endif
-
-#if ((LIBAVCODEC_VERSION_MAJOR > 54) || \
- (LIBAVCODEC_VERSION_MAJOR >= 54) && (LIBAVCODEC_VERSION_MINOR >= 13))
-# define FFMPEG_HAVE_AVFRAME_SAMPLE_RATE
-#endif
-
-#if ((LIBAVCODEC_VERSION_MAJOR > 54) || \
- (LIBAVCODEC_VERSION_MAJOR == 54 && LIBAVCODEC_VERSION_MINOR >= 13))
-# define FFMPEG_HAVE_FRAME_CHANNEL_LAYOUT
-#endif
-
-#ifndef FFMPEG_HAVE_AVIO
-# define AVIO_FLAG_WRITE URL_WRONLY
-# define avio_open url_fopen
-# define avio_tell url_ftell
-# define avio_close url_fclose
-# define avio_size url_fsize
-#endif
-
-/* There are some version in between, which have avio_... functions but no
- * AVIO_FLAG_... */
-#ifndef AVIO_FLAG_WRITE
-# define AVIO_FLAG_WRITE URL_WRONLY
-#endif
-
-#ifndef AV_PKT_FLAG_KEY
-# define AV_PKT_FLAG_KEY PKT_FLAG_KEY
-#endif
-
-#ifndef FFMPEG_HAVE_AV_DUMP_FORMAT
-# define av_dump_format dump_format
-#endif
-
-#ifndef FFMPEG_HAVE_AV_GUESS_FORMAT
-# define av_guess_format guess_format
-#endif
-
-#ifndef FFMPEG_HAVE_PARSE_UTILS
-# define av_parse_video_rate av_parse_video_frame_rate
-#endif
-
-#ifdef FFMPEG_HAVE_DEFAULT_VAL_UNION
-# define FFMPEG_DEF_OPT_VAL_INT(OPT) OPT->default_val.i64
-# define FFMPEG_DEF_OPT_VAL_DOUBLE(OPT) OPT->default_val.dbl
-#else
-# define FFMPEG_DEF_OPT_VAL_INT(OPT) OPT->default_val
-# define FFMPEG_DEF_OPT_VAL_DOUBLE(OPT) OPT->default_val
-#endif
-
-#ifndef FFMPEG_HAVE_AVMEDIA_TYPES
-# define AVMEDIA_TYPE_VIDEO CODEC_TYPE_VIDEO
-# define AVMEDIA_TYPE_AUDIO CODEC_TYPE_AUDIO
-#endif
-
-#ifndef FFMPEG_HAVE_DECODE_AUDIO3
-FFMPEG_INLINE
-int avcodec_decode_audio3(AVCodecContext *avctx,
- int16_t *samples,
- int *frame_size_ptr,
- AVPacket *avpkt)
-{
- return avcodec_decode_audio2(avctx, samples, frame_size_ptr, avpkt->data, avpkt->size);
-}
-#endif
-
-#ifndef FFMPEG_HAVE_DECODE_VIDEO2
-FFMPEG_INLINE
-int avcodec_decode_video2(AVCodecContext *avctx,
- AVFrame *picture,
- int *got_picture_ptr,
- AVPacket *avpkt)
-{
- return avcodec_decode_video(avctx, picture, got_picture_ptr, avpkt->data, avpkt->size);
-}
-#endif
-
-FFMPEG_INLINE
-int64_t av_get_pts_from_frame(AVFormatContext *avctx, AVFrame *picture)
-{
- int64_t pts;
-#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(55, 34, 100)
- pts = picture->pts;
-#else
- pts = picture->pkt_pts;
-#endif
-
- if (pts == AV_NOPTS_VALUE) {
- pts = picture->pkt_dts;
- }
- if (pts == AV_NOPTS_VALUE) {
- pts = 0;
- }
-
- (void)avctx;
- return pts;
-}
-
-/* obsolete constant formerly defined in FFMpeg libavcodec/avcodec.h */
-#ifndef AVCODEC_MAX_AUDIO_FRAME_SIZE
-# define AVCODEC_MAX_AUDIO_FRAME_SIZE 192000 // 1 second of 48khz 32bit audio
-#endif
-
-#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(54, 1, 0)
-FFMPEG_INLINE
-int avcodec_encode_video2(AVCodecContext *avctx,
- AVPacket *pkt,
- const AVFrame *frame,
- int *got_output)
-{
- int outsize, ret;
-
- ret = av_new_packet(pkt, avctx->width * avctx->height * 7 + 10000);
- if (ret < 0)
- return ret;
-
- outsize = avcodec_encode_video(avctx, pkt->data, pkt->size, frame);
- if (outsize <= 0) {
- *got_output = 0;
- av_free_packet(pkt);
- }
- else {
- *got_output = 1;
- av_shrink_packet(pkt, outsize);
- if (avctx->coded_frame) {
- pkt->pts = avctx->coded_frame->pts;
- if (avctx->coded_frame->key_frame)
- pkt->flags |= AV_PKT_FLAG_KEY;
+ switch (st->codecpar->codec_type) {
+ case AVMEDIA_TYPE_VIDEO:
+ if (st->avg_frame_rate.num > 0 && st->avg_frame_rate.den > 0) {
+ pkt->duration = av_rescale_q(1, av_inv_q(st->avg_frame_rate), st->time_base);
+ }
+ else if (st->time_base.num * 1000LL > st->time_base.den) {
+ pkt->duration = 1;
+ }
+ break;
+ case AVMEDIA_TYPE_AUDIO: {
+ int frame_size = av_get_audio_frame_duration2(st->codecpar, pkt->size);
+ if (frame_size && st->codecpar->sample_rate) {
+ pkt->duration = av_rescale_q(
+ frame_size, (AVRational){1, st->codecpar->sample_rate}, st->time_base);
+ }
+ break;
}
+ default:
+ break;
}
-
- return outsize >= 0 ? 0 : outsize;
}
-
#endif
-#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 17, 0)
FFMPEG_INLINE
-void avformat_close_input(AVFormatContext **ctx)
+int64_t timestamp_from_pts_or_dts(int64_t pts, int64_t dts)
{
- av_close_input_file(*ctx);
- *ctx = NULL;
-}
-#endif
-
-#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(52, 8, 0)
-FFMPEG_INLINE
-AVFrame *av_frame_alloc(void)
-{
- return avcodec_alloc_frame();
-}
-
-FFMPEG_INLINE
-void av_frame_free(AVFrame **frame)
-{
- av_freep(frame);
-}
-#endif
-
-FFMPEG_INLINE
-const char *av_get_metadata_key_value(AVDictionary *metadata, const char *key)
-{
- if (metadata == NULL) {
- return NULL;
- }
- AVDictionaryEntry *tag = NULL;
- while ((tag = av_dict_get(metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) {
- if (!strcmp(tag->key, key)) {
- return tag->value;
- }
+ /* Some videos do not have any pts values, use dts instead in those cases if
+ * possible. Usually when this happens dts can act as pts because as all frames
+ * should then be presented in their decoded in order. IE pts == dts. */
+ if (pts == AV_NOPTS_VALUE) {
+ return dts;
}
- return NULL;
+ return pts;
}
FFMPEG_INLINE
-bool av_check_encoded_with_ffmpeg(AVFormatContext *ctx)
+int64_t av_get_pts_from_frame(AVFrame *picture)
{
- const char *encoder = av_get_metadata_key_value(ctx->metadata, "ENCODER");
- if (encoder != NULL && !strncmp(encoder, "Lavf", 4)) {
- return true;
- }
- return false;
+ return timestamp_from_pts_or_dts(picture->pts, picture->pkt_dts);
}
-#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(51, 32, 0)
-# define AV_OPT_SEARCH_FAKE_OBJ 0
-#endif
-
-#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(54, 59, 100)
-# define FFMPEG_HAVE_DEPRECATED_FLAGS2
-#endif
-
-/* Since FFmpeg-1.1 this constant have AV_ prefix. */
-#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(52, 3, 100)
-# define AV_PIX_FMT_BGR32 PIX_FMT_BGR32
-# define AV_PIX_FMT_YUV422P PIX_FMT_YUV422P
-# define AV_PIX_FMT_BGRA PIX_FMT_BGRA
-# define AV_PIX_FMT_ARGB PIX_FMT_ARGB
-# define AV_PIX_FMT_RGBA PIX_FMT_RGBA
-#endif
-
-/* New API from FFmpeg-2.0 which soon became recommended one. */
-#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(52, 38, 100)
-# define av_frame_alloc avcodec_alloc_frame
-# define av_frame_free avcodec_free_frame
-# define av_frame_unref avcodec_get_frame_defaults
-#endif
-
-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 24, 102)
-
-/* NOTE: The code in this block are from FFmpeg 2.6.4, which is licensed by LGPL. */
+/* -------------------------------------------------------------------- */
+/** \name Deinterlace code block
+ *
+ * NOTE: The code in this block are from FFmpeg 2.6.4, which is licensed by LGPL.
+ * \{ */
-# define MAX_NEG_CROP 1024
+#define MAX_NEG_CROP 1024
-# define times4(x) x, x, x, x
-# define times256(x) times4(times4(times4(times4(times4(x)))))
+#define times4(x) x, x, x, x
+#define times256(x) times4(times4(times4(times4(times4(x)))))
static const uint8_t ff_compat_crop_tab[256 + 2 * MAX_NEG_CROP] = {
times256(0x00), 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A,
@@ -575,8 +145,8 @@ static const uint8_t ff_compat_crop_tab[256 + 2 * MAX_NEG_CROP] = {
0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA,
0xFB, 0xFC, 0xFD, 0xFE, 0xFF, times256(0xFF)};
-# undef times4
-# undef times256
+#undef times4
+#undef times256
/* filter parameters: [-1 4 2 4 -1] // 8 */
FFMPEG_INLINE
@@ -668,8 +238,9 @@ int deinterlace_bottom_field_inplace(uint8_t *src1, int src_wrap, int width, int
uint8_t *src_m1, *src_0, *src_p1, *src_p2;
int y;
uint8_t *buf = (uint8_t *)av_malloc(width);
- if (!buf)
+ if (!buf) {
return AVERROR(ENOMEM);
+ }
src_m1 = src1;
memcpy(buf, src_m1, width);
@@ -689,24 +260,21 @@ int deinterlace_bottom_field_inplace(uint8_t *src1, int src_wrap, int width, int
return 0;
}
-# ifdef __GNUC__
-# pragma GCC diagnostic push
-# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-# endif
-
FFMPEG_INLINE
-int avpicture_deinterlace(
- AVPicture *dst, const AVPicture *src, enum AVPixelFormat pix_fmt, int width, int height)
+int av_image_deinterlace(
+ AVFrame *dst, const AVFrame *src, enum AVPixelFormat pix_fmt, int width, int height)
{
int i, ret;
if (pix_fmt != AV_PIX_FMT_YUV420P && pix_fmt != AV_PIX_FMT_YUVJ420P &&
pix_fmt != AV_PIX_FMT_YUV422P && pix_fmt != AV_PIX_FMT_YUVJ422P &&
pix_fmt != AV_PIX_FMT_YUV444P && pix_fmt != AV_PIX_FMT_YUV411P &&
- pix_fmt != AV_PIX_FMT_GRAY8)
+ pix_fmt != AV_PIX_FMT_GRAY8) {
return -1;
- if ((width & 3) != 0 || (height & 3) != 0)
+ }
+ if ((width & 3) != 0 || (height & 3) != 0) {
return -1;
+ }
for (i = 0; i < 3; i++) {
if (i == 1) {
@@ -732,8 +300,9 @@ int avpicture_deinterlace(
}
if (src == dst) {
ret = deinterlace_bottom_field_inplace(dst->data[i], dst->linesize[i], width, height);
- if (ret < 0)
+ if (ret < 0) {
return ret;
+ }
}
else {
deinterlace_bottom_field(
@@ -743,10 +312,6 @@ int avpicture_deinterlace(
return 0;
}
-# ifdef __GNUC__
-# pragma GCC diagnostic pop
-# endif
-
-#endif
+/** \} Deinterlace code block */
#endif
diff --git a/intern/ghost/CMakeLists.txt b/intern/ghost/CMakeLists.txt
index 1739659ab88..1b5cdb3cda0 100644
--- a/intern/ghost/CMakeLists.txt
+++ b/intern/ghost/CMakeLists.txt
@@ -79,6 +79,7 @@ set(SRC
intern/GHOST_SystemPaths.h
intern/GHOST_TimerManager.h
intern/GHOST_TimerTask.h
+ intern/GHOST_Util.h
intern/GHOST_Window.h
intern/GHOST_WindowManager.h
)
@@ -281,6 +282,7 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND)
${wayland-egl_INCLUDE_DIRS}
${xkbcommon_INCLUDE_DIRS}
${wayland-cursor_INCLUDE_DIRS}
+ ${dbus_INCLUDE_DIRS}
)
list(APPEND SRC
@@ -320,6 +322,11 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND)
xdg-shell
"${WAYLAND_PROTOCOLS_DIR}/stable/xdg-shell/xdg-shell.xml"
)
+ # xdg-decoration.
+ generate_protocol_bindings(
+ xdg-decoration
+ "${WAYLAND_PROTOCOLS_DIR}/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml"
+ )
# Pointer-constraints.
generate_protocol_bindings(
pointer-constraints
@@ -438,6 +445,7 @@ endif()
if(WITH_XR_OPENXR)
list(APPEND SRC
intern/GHOST_Xr.cpp
+ intern/GHOST_XrAction.cpp
intern/GHOST_XrContext.cpp
intern/GHOST_XrEvent.cpp
intern/GHOST_XrGraphicsBinding.cpp
@@ -446,6 +454,7 @@ if(WITH_XR_OPENXR)
GHOST_IXrContext.h
intern/GHOST_IXrGraphicsBinding.h
+ intern/GHOST_XrAction.h
intern/GHOST_XrContext.h
intern/GHOST_XrException.h
intern/GHOST_XrSession.h
diff --git a/intern/ghost/GHOST_C-api.h b/intern/ghost/GHOST_C-api.h
index d79111b742f..2bd9af6df5c 100644
--- a/intern/ghost/GHOST_C-api.h
+++ b/intern/ghost/GHOST_C-api.h
@@ -276,7 +276,7 @@ extern int GHOST_GetFullScreen(GHOST_SystemHandle systemhandle);
* wait (block) until the next event before returning.
* \return Indication of the presence of events.
*/
-extern int GHOST_ProcessEvents(GHOST_SystemHandle systemhandle, int waitForEvent);
+extern bool GHOST_ProcessEvents(GHOST_SystemHandle systemhandle, bool waitForEvent);
/**
* Retrieves events from the queue and send them to the event consumers.
@@ -1059,7 +1059,110 @@ int GHOST_XrSessionNeedsUpsideDownDrawing(const GHOST_XrContextHandle xr_context
* \returns GHOST_kSuccess if any event was handled, otherwise GHOST_kFailure.
*/
GHOST_TSuccess GHOST_XrEventsHandle(GHOST_XrContextHandle xr_context);
-#endif
+
+/* actions */
+/**
+ * Create an OpenXR action set for input/output.
+ */
+int GHOST_XrCreateActionSet(GHOST_XrContextHandle xr_context, const GHOST_XrActionSetInfo *info);
+
+/**
+ * Destroy a previously created OpenXR action set.
+ */
+void GHOST_XrDestroyActionSet(GHOST_XrContextHandle xr_context, const char *action_set_name);
+
+/**
+ * Create OpenXR input/output actions.
+ */
+int GHOST_XrCreateActions(GHOST_XrContextHandle xr_context,
+ const char *action_set_name,
+ GHOST_TUns32 count,
+ const GHOST_XrActionInfo *infos);
+
+/**
+ * Destroy previously created OpenXR actions.
+ */
+void GHOST_XrDestroyActions(GHOST_XrContextHandle xr_context,
+ const char *action_set_name,
+ GHOST_TUns32 count,
+ const char *const *action_names);
+
+/**
+ * Create spaces for pose-based OpenXR actions.
+ */
+int GHOST_XrCreateActionSpaces(GHOST_XrContextHandle xr_context,
+ const char *action_set_name,
+ GHOST_TUns32 count,
+ const GHOST_XrActionSpaceInfo *infos);
+
+/**
+ * Destroy previously created spaces for OpenXR actions.
+ */
+void GHOST_XrDestroyActionSpaces(GHOST_XrContextHandle xr_context,
+ const char *action_set_name,
+ GHOST_TUns32 count,
+ const GHOST_XrActionSpaceInfo *infos);
+
+/**
+ * Create input/output path bindings for OpenXR actions.
+ */
+int GHOST_XrCreateActionBindings(GHOST_XrContextHandle xr_context,
+ const char *action_set_name,
+ GHOST_TUns32 count,
+ const GHOST_XrActionProfileInfo *infos);
+
+/**
+ * Destroy previously created bindings for OpenXR actions.
+ */
+void GHOST_XrDestroyActionBindings(GHOST_XrContextHandle xr_context,
+ const char *action_set_name,
+ GHOST_TUns32 count,
+ const GHOST_XrActionProfileInfo *infos);
+
+/**
+ * Attach all created action sets to the current OpenXR session.
+ */
+int GHOST_XrAttachActionSets(GHOST_XrContextHandle xr_context);
+
+/**
+ * Update button/tracking states for OpenXR actions.
+ *
+ * \param action_set_name: The name of the action set to sync. If NULL, all action sets
+ * attached to the session will be synced.
+ */
+int GHOST_XrSyncActions(GHOST_XrContextHandle xr_context, const char *action_set_name);
+
+/**
+ * Apply an OpenXR haptic output action.
+ */
+int GHOST_XrApplyHapticAction(GHOST_XrContextHandle xr_context,
+ const char *action_set_name,
+ const char *action_name,
+ const GHOST_TInt64 *duration,
+ const float *frequency,
+ const float *amplitude);
+
+/**
+ * Stop a previously applied OpenXR haptic output action.
+ */
+void GHOST_XrStopHapticAction(GHOST_XrContextHandle xr_context,
+ const char *action_set_name,
+ const char *action_name);
+
+/**
+ * Get action set custom data (owned by Blender, not GHOST).
+ */
+void *GHOST_XrGetActionSetCustomdata(GHOST_XrContextHandle xr_context,
+ const char *action_set_name);
+
+/**
+ * Get action custom data (owned by Blender, not GHOST).
+ */
+void *GHOST_XrGetActionCustomdata(GHOST_XrContextHandle xr_context,
+ const char *action_set_name,
+ const char *action_name);
+
+#endif /* WITH_XR_OPENXR */
#ifdef __cplusplus
}
diff --git a/intern/ghost/GHOST_IXrContext.h b/intern/ghost/GHOST_IXrContext.h
index dd266a3b6ae..86fe78814a7 100644
--- a/intern/ghost/GHOST_IXrContext.h
+++ b/intern/ghost/GHOST_IXrContext.h
@@ -22,6 +22,8 @@
#include "GHOST_Types.h"
+class GHOST_XrSession;
+
class GHOST_IXrContext {
public:
virtual ~GHOST_IXrContext() = default;
@@ -31,6 +33,10 @@ class GHOST_IXrContext {
virtual bool isSessionRunning() const = 0;
virtual void drawSessionViews(void *draw_customdata) = 0;
+ /* Needed for the GHOST C api. */
+ virtual GHOST_XrSession *getSession() = 0;
+ virtual const GHOST_XrSession *getSession() const = 0;
+
virtual void dispatchErrorMessage(const class GHOST_XrException *) const = 0;
virtual void setGraphicsContextBindFuncs(GHOST_XrGraphicsContextBindFn bind_fn,
diff --git a/intern/ghost/GHOST_Types.h b/intern/ghost/GHOST_Types.h
index bd7ba6657f0..3a8d0fbfecf 100644
--- a/intern/ghost/GHOST_Types.h
+++ b/intern/ghost/GHOST_Types.h
@@ -416,7 +416,10 @@ typedef enum {
GHOST_kGrabNormal,
/** Wrap the mouse location to prevent limiting screen bounds. */
GHOST_kGrabWrap,
- /** Hide the mouse while grabbing and restore the original location on release (numbuts). */
+ /**
+ * Hide the mouse while grabbing and restore the original location on release
+ * (used for number buttons and some other draggable UI elements).
+ */
GHOST_kGrabHide,
} GHOST_TGrabCursorMode;
@@ -631,7 +634,9 @@ typedef enum GHOST_TXrGraphicsBinding {
typedef void (*GHOST_XrErrorHandlerFn)(const struct GHOST_XrError *);
+typedef void (*GHOST_XrSessionCreateFn)(void);
typedef void (*GHOST_XrSessionExitFn)(void *customdata);
+typedef void (*GHOST_XrCustomdataFreeFn)(void *customdata);
typedef void *(*GHOST_XrGraphicsContextBindFn)(void);
typedef void (*GHOST_XrGraphicsContextUnbindFn)(GHOST_ContextHandle graphics_context);
@@ -662,6 +667,7 @@ typedef struct {
typedef struct {
GHOST_XrPose base_pose;
+ GHOST_XrSessionCreateFn create_fn;
GHOST_XrSessionExitFn exit_fn;
void *exit_customdata;
} GHOST_XrSessionBeginInfo;
@@ -688,4 +694,54 @@ typedef struct GHOST_XrError {
void *customdata;
} GHOST_XrError;
-#endif
+typedef struct GHOST_XrActionSetInfo {
+ const char *name;
+
+ GHOST_XrCustomdataFreeFn customdata_free_fn;
+ void *customdata; /* wmXrActionSet */
+} GHOST_XrActionSetInfo;
+
+/** XR action type. Enum values match those in OpenXR's
+ * XrActionType enum for consistency. */
+typedef enum GHOST_XrActionType {
+ GHOST_kXrActionTypeBooleanInput = 1,
+ GHOST_kXrActionTypeFloatInput = 2,
+ GHOST_kXrActionTypeVector2fInput = 3,
+ GHOST_kXrActionTypePoseInput = 4,
+ GHOST_kXrActionTypeVibrationOutput = 100,
+} GHOST_XrActionType;
+
+typedef struct GHOST_XrActionInfo {
+ const char *name;
+ GHOST_XrActionType type;
+ GHOST_TUns32 count_subaction_paths;
+ const char **subaction_paths;
+ /** States for each subaction path. */
+ void *states;
+
+ GHOST_XrCustomdataFreeFn customdata_free_fn;
+ void *customdata; /* wmXrAction */
+} GHOST_XrActionInfo;
+
+typedef struct GHOST_XrActionSpaceInfo {
+ const char *action_name;
+ GHOST_TUns32 count_subaction_paths;
+ const char **subaction_paths;
+ /** Poses for each subaction path. */
+ const GHOST_XrPose *poses;
+} GHOST_XrActionSpaceInfo;
+
+typedef struct GHOST_XrActionBindingInfo {
+ const char *action_name;
+ GHOST_TUns32 count_interaction_paths;
+ /** Interaction path: User (subaction) path + component path. */
+ const char **interaction_paths;
+} GHOST_XrActionBindingInfo;
+
+typedef struct GHOST_XrActionProfileInfo {
+ const char *profile_path;
+ GHOST_TUns32 count_bindings;
+ const GHOST_XrActionBindingInfo *bindings;
+} GHOST_XrActionProfileInfo;
+
+#endif /* WITH_XR_OPENXR */
diff --git a/intern/ghost/intern/GHOST_C-api.cpp b/intern/ghost/intern/GHOST_C-api.cpp
index af65e1cd4d2..955f35274ea 100644
--- a/intern/ghost/intern/GHOST_C-api.cpp
+++ b/intern/ghost/intern/GHOST_C-api.cpp
@@ -33,6 +33,7 @@
#include "intern/GHOST_Debug.h"
#ifdef WITH_XR_OPENXR
# include "GHOST_IXrContext.h"
+# include "intern/GHOST_XrSession.h"
#endif
#include "intern/GHOST_CallbackEventConsumer.h"
#include "intern/GHOST_XrException.h"
@@ -248,11 +249,11 @@ int GHOST_GetFullScreen(GHOST_SystemHandle systemhandle)
return (int)system->getFullScreen();
}
-int GHOST_ProcessEvents(GHOST_SystemHandle systemhandle, int waitForEvent)
+bool GHOST_ProcessEvents(GHOST_SystemHandle systemhandle, bool waitForEvent)
{
GHOST_ISystem *system = (GHOST_ISystem *)systemhandle;
- return (int)system->processEvents(waitForEvent ? true : false);
+ return system->processEvents(waitForEvent);
}
void GHOST_DispatchEvents(GHOST_SystemHandle systemhandle)
@@ -953,4 +954,145 @@ int GHOST_XrSessionNeedsUpsideDownDrawing(const GHOST_XrContextHandle xr_context
return 0; /* Only reached if exception is thrown. */
}
-#endif
+int GHOST_XrCreateActionSet(GHOST_XrContextHandle xr_contexthandle,
+ const GHOST_XrActionSetInfo *info)
+{
+ GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
+ GHOST_XrSession *xr_session = xr_context->getSession();
+ GHOST_XR_CAPI_CALL_RET(xr_session->createActionSet(*info), xr_context);
+ return 0;
+}
+
+void GHOST_XrDestroyActionSet(GHOST_XrContextHandle xr_contexthandle, const char *action_set_name)
+{
+ GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
+ GHOST_XrSession *xr_session = xr_context->getSession();
+ GHOST_XR_CAPI_CALL(xr_session->destroyActionSet(action_set_name), xr_context);
+}
+
+int GHOST_XrCreateActions(GHOST_XrContextHandle xr_contexthandle,
+ const char *action_set_name,
+ GHOST_TUns32 count,
+ const GHOST_XrActionInfo *infos)
+{
+ GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
+ GHOST_XrSession *xr_session = xr_context->getSession();
+ GHOST_XR_CAPI_CALL_RET(xr_session->createActions(action_set_name, count, infos), xr_context);
+ return 0;
+}
+
+void GHOST_XrDestroyActions(GHOST_XrContextHandle xr_contexthandle,
+ const char *action_set_name,
+ GHOST_TUns32 count,
+ const char *const *action_names)
+{
+ GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
+ GHOST_XrSession *xr_session = xr_context->getSession();
+ GHOST_XR_CAPI_CALL(xr_session->destroyActions(action_set_name, count, action_names), xr_context);
+}
+
+int GHOST_XrCreateActionSpaces(GHOST_XrContextHandle xr_contexthandle,
+ const char *action_set_name,
+ GHOST_TUns32 count,
+ const GHOST_XrActionSpaceInfo *infos)
+{
+ GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
+ GHOST_XrSession *xr_session = xr_context->getSession();
+ GHOST_XR_CAPI_CALL_RET(xr_session->createActionSpaces(action_set_name, count, infos),
+ xr_context);
+ return 0;
+}
+
+void GHOST_XrDestroyActionSpaces(GHOST_XrContextHandle xr_contexthandle,
+ const char *action_set_name,
+ GHOST_TUns32 count,
+ const GHOST_XrActionSpaceInfo *infos)
+{
+ GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
+ GHOST_XrSession *xr_session = xr_context->getSession();
+ GHOST_XR_CAPI_CALL(xr_session->destroyActionSpaces(action_set_name, count, infos), xr_context);
+}
+
+int GHOST_XrCreateActionBindings(GHOST_XrContextHandle xr_contexthandle,
+ const char *action_set_name,
+ GHOST_TUns32 count,
+ const GHOST_XrActionProfileInfo *infos)
+{
+ GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
+ GHOST_XrSession *xr_session = xr_context->getSession();
+ GHOST_XR_CAPI_CALL_RET(xr_session->createActionBindings(action_set_name, count, infos),
+ xr_context);
+ return 0;
+}
+
+void GHOST_XrDestroyActionBindings(GHOST_XrContextHandle xr_contexthandle,
+ const char *action_set_name,
+ GHOST_TUns32 count,
+ const GHOST_XrActionProfileInfo *infos)
+{
+ GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
+ GHOST_XrSession *xr_session = xr_context->getSession();
+ GHOST_XR_CAPI_CALL(xr_session->destroyActionBindings(action_set_name, count, infos), xr_context);
+}
+
+int GHOST_XrAttachActionSets(GHOST_XrContextHandle xr_contexthandle)
+{
+ GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
+ GHOST_XrSession *xr_session = xr_context->getSession();
+ GHOST_XR_CAPI_CALL_RET(xr_session->attachActionSets(), xr_context);
+ return 0;
+}
+
+int GHOST_XrSyncActions(GHOST_XrContextHandle xr_contexthandle, const char *action_set_name)
+{
+ GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
+ GHOST_XrSession *xr_session = xr_context->getSession();
+ GHOST_XR_CAPI_CALL_RET(xr_session->syncActions(action_set_name), xr_context);
+ return 0;
+}
+
+int GHOST_XrApplyHapticAction(GHOST_XrContextHandle xr_contexthandle,
+ const char *action_set_name,
+ const char *action_name,
+ const GHOST_TInt64 *duration,
+ const float *frequency,
+ const float *amplitude)
+{
+ GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
+ GHOST_XrSession *xr_session = xr_context->getSession();
+ GHOST_XR_CAPI_CALL_RET(xr_session->applyHapticAction(
+ action_set_name, action_name, *duration, *frequency, *amplitude),
+ xr_context);
+ return 0;
+}
+
+void GHOST_XrStopHapticAction(GHOST_XrContextHandle xr_contexthandle,
+ const char *action_set_name,
+ const char *action_name)
+{
+ GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
+ GHOST_XrSession *xr_session = xr_context->getSession();
+ GHOST_XR_CAPI_CALL(xr_session->stopHapticAction(action_set_name, action_name), xr_context);
+}
+
+void *GHOST_XrGetActionSetCustomdata(GHOST_XrContextHandle xr_contexthandle,
+ const char *action_set_name)
+{
+ GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
+ GHOST_XrSession *xr_session = xr_context->getSession();
+ GHOST_XR_CAPI_CALL_RET(xr_session->getActionSetCustomdata(action_set_name), xr_context);
+ return 0;
+}
+
+void *GHOST_XrGetActionCustomdata(GHOST_XrContextHandle xr_contexthandle,
+ const char *action_set_name,
+ const char *action_name)
+{
+ GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle;
+ GHOST_XrSession *xr_session = xr_context->getSession();
+ GHOST_XR_CAPI_CALL_RET(xr_session->getActionCustomdata(action_set_name, action_name),
+ xr_context);
+ return 0;
+}
+
+#endif /* WITH_XR_OPENXR */
diff --git a/intern/ghost/intern/GHOST_DropTargetX11.cpp b/intern/ghost/intern/GHOST_DropTargetX11.cpp
index 82ed9de230d..a62db31c952 100644
--- a/intern/ghost/intern/GHOST_DropTargetX11.cpp
+++ b/intern/ghost/intern/GHOST_DropTargetX11.cpp
@@ -112,8 +112,7 @@ GHOST_DropTargetX11::~GHOST_DropTargetX11()
}
}
-/* based on a code from Saul Rennison
- * http://stackoverflow.com/questions/2673207/c-c-url-decode-library */
+/* Based on: https://stackoverflow.com/a/2766963/432509 */
typedef enum DecodeState_e {
STATE_SEARCH = 0, ///< searching for an ampersand to convert
diff --git a/intern/ghost/intern/GHOST_IconX11.h b/intern/ghost/intern/GHOST_IconX11.h
index 615a7dae6b5..403d611b71d 100644
--- a/intern/ghost/intern/GHOST_IconX11.h
+++ b/intern/ghost/intern/GHOST_IconX11.h
@@ -24,7 +24,8 @@
#pragma once
-/*
+/**
+ * \code{.py}
* import bpy
* import textwrap
*
@@ -42,6 +43,7 @@
* print("%d,%d," % (w, h))
* text = ", ".join(["0x%x" % p for p in pixels])
* print(textwrap.fill(text, width=120), end=",\n")
+ * \endcode
*/
/**
diff --git a/intern/ghost/intern/GHOST_SystemCocoa.mm b/intern/ghost/intern/GHOST_SystemCocoa.mm
index 050c90097c5..6ddb7f031f5 100644
--- a/intern/ghost/intern/GHOST_SystemCocoa.mm
+++ b/intern/ghost/intern/GHOST_SystemCocoa.mm
@@ -1711,7 +1711,7 @@ GHOST_TSuccess GHOST_SystemCocoa::handleMouseEvent(void *eventPtr)
dx = [event scrollingDeltaX];
dy = [event scrollingDeltaY];
- /* However, wacom tablet (intuos5) needs old deltas,
+ /* However, Wacom tablet (intuos5) needs old deltas,
* it then has momentum and phase at zero. */
if (phase == NSEventPhaseNone && momentumPhase == NSEventPhaseNone) {
dx = [event deltaX];
diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp
index 81d9824ed26..83b9b2ba36b 100644
--- a/intern/ghost/intern/GHOST_SystemWayland.cpp
+++ b/intern/ghost/intern/GHOST_SystemWayland.cpp
@@ -40,6 +40,7 @@
#include <unordered_map>
#include <unordered_set>
+#include "GHOST_WaylandCursorSettings.h"
#include <pointer-constraints-client-protocol.h>
#include <relative-pointer-client-protocol.h>
#include <wayland-cursor.h>
@@ -52,15 +53,6 @@
#include <cstring>
-struct output_t {
- struct wl_output *output;
- int32_t width, height;
- int transform;
- int scale;
- std::string make;
- std::string model;
-};
-
struct buffer_t {
void *data;
size_t size;
@@ -72,6 +64,12 @@ struct cursor_t {
struct wl_buffer *buffer;
struct wl_cursor_image image;
struct buffer_t *file_buffer = nullptr;
+ struct wl_cursor_theme *theme = nullptr;
+ int size;
+ std::string theme_name;
+ // outputs on which the cursor is visible
+ std::unordered_set<const output_t *> outputs;
+ int scale = 1;
};
struct data_offer_t {
@@ -142,10 +140,14 @@ struct display_t {
struct wl_display *display;
struct wl_compositor *compositor = nullptr;
struct xdg_wm_base *xdg_shell = nullptr;
+ struct zxdg_decoration_manager_v1 *xdg_decoration_manager = nullptr;
struct wl_shm *shm = nullptr;
std::vector<output_t *> outputs;
std::vector<input_t *> inputs;
- struct wl_cursor_theme *cursor_theme = nullptr;
+ struct {
+ std::string theme;
+ int size;
+ } cursor;
struct wl_data_device_manager *data_device_manager = nullptr;
struct zwp_relative_pointer_manager_v1 *relative_pointer_manager = nullptr;
struct zwp_pointer_constraints_v1 *pointer_constraints = nullptr;
@@ -154,6 +156,8 @@ struct display_t {
std::vector<struct wl_egl_window *> os_egl_windows;
};
+static GHOST_WindowManager *window_manager = nullptr;
+
static void display_destroy(display_t *d)
{
if (d->data_device_manager) {
@@ -188,6 +192,9 @@ static void display_destroy(display_t *d)
if (input->cursor.surface) {
wl_surface_destroy(input->cursor.surface);
}
+ if (input->cursor.theme) {
+ wl_cursor_theme_destroy(input->cursor.theme);
+ }
if (input->pointer) {
wl_pointer_destroy(input->pointer);
}
@@ -210,10 +217,6 @@ static void display_destroy(display_t *d)
delete input;
}
- if (d->cursor_theme) {
- wl_cursor_theme_destroy(d->cursor_theme);
- }
-
if (d->shm) {
wl_shm_destroy(d->shm);
}
@@ -238,6 +241,10 @@ static void display_destroy(display_t *d)
wl_compositor_destroy(d->compositor);
}
+ if (d->xdg_decoration_manager) {
+ zxdg_decoration_manager_v1_destroy(d->xdg_decoration_manager);
+ }
+
if (d->xdg_shell) {
xdg_wm_base_destroy(d->xdg_shell);
}
@@ -478,7 +485,9 @@ static void dnd_events(const input_t *const input, const GHOST_TEventType event)
static std::string read_pipe(data_offer_t *data_offer, const std::string mime_receive)
{
int pipefd[2];
- pipe(pipefd);
+ if (pipe(pipefd) != 0) {
+ return {};
+ }
wl_data_offer_receive(data_offer->id, mime_receive.c_str(), pipefd[1]);
close(pipefd[1]);
@@ -513,7 +522,9 @@ static void data_source_send(void *data,
int32_t fd)
{
const char *const buffer = static_cast<char *>(data);
- write(fd, buffer, strlen(buffer) + 1);
+ if (write(fd, buffer, strlen(buffer) + 1) < 0) {
+ GHOST_PRINT("error writing to clipboard: " << std::strerror(errno) << std::endl);
+ }
close(fd);
}
@@ -789,13 +800,80 @@ static void cursor_buffer_release(void *data, struct wl_buffer *wl_buffer)
cursor_t *cursor = static_cast<cursor_t *>(data);
wl_buffer_destroy(wl_buffer);
- cursor->buffer = nullptr;
+
+ if (wl_buffer == cursor->buffer) {
+ /* the mapped buffer was from a custom cursor */
+ cursor->buffer = nullptr;
+ }
}
const struct wl_buffer_listener cursor_buffer_listener = {
cursor_buffer_release,
};
+static GHOST_IWindow *get_window(struct wl_surface *surface)
+{
+ if (!surface) {
+ return nullptr;
+ }
+
+ for (GHOST_IWindow *win : window_manager->getWindows()) {
+ if (surface == static_cast<const GHOST_WindowWayland *>(win)->surface()) {
+ return win;
+ }
+ }
+ return nullptr;
+}
+
+static bool update_cursor_scale(cursor_t &cursor, wl_shm *shm)
+{
+ int scale = 0;
+ for (const output_t *output : cursor.outputs) {
+ if (output->scale > scale)
+ scale = output->scale;
+ }
+
+ if (scale > 0 && cursor.scale != scale) {
+ cursor.scale = scale;
+ wl_surface_set_buffer_scale(cursor.surface, scale);
+ wl_cursor_theme_destroy(cursor.theme);
+ cursor.theme = wl_cursor_theme_load(cursor.theme_name.c_str(), scale * cursor.size, shm);
+ return true;
+ }
+ return false;
+}
+
+static void cursor_surface_enter(void *data,
+ struct wl_surface * /*wl_surface*/,
+ struct wl_output *output)
+{
+ input_t *input = static_cast<input_t *>(data);
+ for (const output_t *reg_output : input->system->outputs()) {
+ if (reg_output->output == output) {
+ input->cursor.outputs.insert(reg_output);
+ }
+ }
+ update_cursor_scale(input->cursor, input->system->shm());
+}
+
+static void cursor_surface_leave(void *data,
+ struct wl_surface * /*wl_surface*/,
+ struct wl_output *output)
+{
+ input_t *input = static_cast<input_t *>(data);
+ for (const output_t *reg_output : input->system->outputs()) {
+ if (reg_output->output == output) {
+ input->cursor.outputs.erase(reg_output);
+ }
+ }
+ update_cursor_scale(input->cursor, input->system->shm());
+}
+
+struct wl_surface_listener cursor_surface_listener = {
+ cursor_surface_enter,
+ cursor_surface_leave,
+};
+
static void pointer_enter(void *data,
struct wl_pointer * /*wl_pointer*/,
uint32_t serial,
@@ -803,22 +881,28 @@ static void pointer_enter(void *data,
wl_fixed_t surface_x,
wl_fixed_t surface_y)
{
- if (!surface) {
+ GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(get_window(surface));
+
+ if (!win) {
return;
}
+
+ win->activate();
+
input_t *input = static_cast<input_t *>(data);
input->pointer_serial = serial;
- input->x = wl_fixed_to_int(surface_x);
- input->y = wl_fixed_to_int(surface_y);
+ input->x = win->scale() * wl_fixed_to_int(surface_x);
+ input->y = win->scale() * wl_fixed_to_int(surface_y);
input->focus_pointer = surface;
- input->system->pushEvent(
- new GHOST_EventCursor(input->system->getMilliSeconds(),
- GHOST_kEventCursorMove,
- static_cast<GHOST_WindowWayland *>(wl_surface_get_user_data(surface)),
- input->x,
- input->y,
- GHOST_TABLET_DATA_NONE));
+ win->setCursorShape(win->getCursorShape());
+
+ input->system->pushEvent(new GHOST_EventCursor(input->system->getMilliSeconds(),
+ GHOST_kEventCursorMove,
+ static_cast<GHOST_WindowWayland *>(win),
+ input->x,
+ input->y,
+ GHOST_TABLET_DATA_NONE));
}
static void pointer_leave(void *data,
@@ -826,9 +910,14 @@ static void pointer_leave(void *data,
uint32_t /*serial*/,
struct wl_surface *surface)
{
- if (surface != nullptr) {
- static_cast<input_t *>(data)->focus_pointer = nullptr;
+ GHOST_IWindow *win = get_window(surface);
+
+ if (!win) {
+ return;
}
+
+ static_cast<input_t *>(data)->focus_pointer = nullptr;
+ static_cast<GHOST_WindowWayland *>(win)->deactivate();
}
static void pointer_motion(void *data,
@@ -839,21 +928,20 @@ static void pointer_motion(void *data,
{
input_t *input = static_cast<input_t *>(data);
- GHOST_IWindow *win = static_cast<GHOST_WindowWayland *>(
- wl_surface_get_user_data(input->focus_pointer));
+ GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(get_window(input->focus_pointer));
if (!win) {
return;
}
- input->x = wl_fixed_to_int(surface_x);
- input->y = wl_fixed_to_int(surface_y);
+ input->x = win->scale() * wl_fixed_to_int(surface_x);
+ input->y = win->scale() * wl_fixed_to_int(surface_y);
input->system->pushEvent(new GHOST_EventCursor(input->system->getMilliSeconds(),
GHOST_kEventCursorMove,
win,
- wl_fixed_to_int(surface_x),
- wl_fixed_to_int(surface_y),
+ input->x,
+ input->y,
GHOST_TABLET_DATA_NONE));
}
@@ -864,6 +952,14 @@ static void pointer_button(void *data,
uint32_t button,
uint32_t state)
{
+ input_t *input = static_cast<input_t *>(data);
+
+ GHOST_IWindow *win = get_window(input->focus_pointer);
+
+ if (!win) {
+ return;
+ }
+
GHOST_TEventType etype = GHOST_kEventUnknown;
switch (state) {
case WL_POINTER_BUTTON_STATE_RELEASED:
@@ -887,9 +983,6 @@ static void pointer_button(void *data,
break;
}
- input_t *input = static_cast<input_t *>(data);
- GHOST_IWindow *win = static_cast<GHOST_WindowWayland *>(
- wl_surface_get_user_data(input->focus_pointer));
input->data_source->source_serial = serial;
input->buttons.set(ebutton, state == WL_POINTER_BUTTON_STATE_PRESSED);
input->system->pushEvent(new GHOST_EventButton(
@@ -902,12 +995,18 @@ static void pointer_axis(void *data,
uint32_t axis,
wl_fixed_t value)
{
+ input_t *input = static_cast<input_t *>(data);
+
+ GHOST_IWindow *win = get_window(input->focus_pointer);
+
+ if (!win) {
+ return;
+ }
+
if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL) {
return;
}
- input_t *input = static_cast<input_t *>(data);
- GHOST_IWindow *win = static_cast<GHOST_WindowWayland *>(
- wl_surface_get_user_data(input->focus_pointer));
+
input->system->pushEvent(
new GHOST_EventWheel(input->system->getMilliSeconds(), win, std::signbit(value) ? +1 : -1));
}
@@ -1137,7 +1236,12 @@ static void seat_capabilities(void *data, struct wl_seat *wl_seat, uint32_t capa
input->cursor.visible = true;
input->cursor.buffer = nullptr;
input->cursor.file_buffer = new buffer_t;
+ if (!get_cursor_settings(input->cursor.theme_name, input->cursor.size)) {
+ input->cursor.theme_name = std::string();
+ input->cursor.size = default_cursor_size;
+ }
wl_pointer_add_listener(input->pointer, &pointer_listener, data);
+ wl_surface_add_listener(input->cursor.surface, &cursor_surface_listener, data);
}
if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) {
@@ -1160,8 +1264,8 @@ static void output_geometry(void *data,
struct wl_output * /*wl_output*/,
int32_t /*x*/,
int32_t /*y*/,
- int32_t /*physical_width*/,
- int32_t /*physical_height*/,
+ int32_t physical_width,
+ int32_t physical_height,
int32_t /*subpixel*/,
const char *make,
const char *model,
@@ -1171,6 +1275,8 @@ static void output_geometry(void *data,
output->transform = transform;
output->make = std::string(make);
output->model = std::string(model);
+ output->width_mm = physical_width;
+ output->height_mm = physical_height;
}
static void output_mode(void *data,
@@ -1181,8 +1287,8 @@ static void output_mode(void *data,
int32_t /*refresh*/)
{
output_t *output = static_cast<output_t *>(data);
- output->width = width;
- output->height = height;
+ output->width_pxl = width;
+ output->height_pxl = height;
}
/**
@@ -1227,13 +1333,17 @@ static void global_add(void *data,
struct display_t *display = static_cast<struct display_t *>(data);
if (!strcmp(interface, wl_compositor_interface.name)) {
display->compositor = static_cast<wl_compositor *>(
- wl_registry_bind(wl_registry, name, &wl_compositor_interface, 1));
+ wl_registry_bind(wl_registry, name, &wl_compositor_interface, 3));
}
else if (!strcmp(interface, xdg_wm_base_interface.name)) {
display->xdg_shell = static_cast<xdg_wm_base *>(
wl_registry_bind(wl_registry, name, &xdg_wm_base_interface, 1));
xdg_wm_base_add_listener(display->xdg_shell, &shell_listener, nullptr);
}
+ else if (!strcmp(interface, zxdg_decoration_manager_v1_interface.name)) {
+ display->xdg_decoration_manager = static_cast<zxdg_decoration_manager_v1 *>(
+ wl_registry_bind(wl_registry, name, &zxdg_decoration_manager_v1_interface, 1));
+ }
else if (!strcmp(interface, wl_output_interface.name)) {
output_t *output = new output_t;
output->scale = 1;
@@ -1335,16 +1445,6 @@ GHOST_SystemWayland::GHOST_SystemWayland() : GHOST_System(), d(new display_t)
wl_data_device_add_listener(input->data_device, &data_device_listener, input);
}
}
-
- const char *theme = std::getenv("XCURSOR_THEME");
- const char *size = std::getenv("XCURSOR_SIZE");
- const int sizei = size ? std::stoi(size) : default_cursor_size;
-
- d->cursor_theme = wl_cursor_theme_load(theme, sizei, d->shm);
- if (!d->cursor_theme) {
- display_destroy(d);
- throw std::runtime_error("Wayland: unable to access cursor themes!");
- }
}
GHOST_SystemWayland::~GHOST_SystemWayland()
@@ -1471,8 +1571,8 @@ void GHOST_SystemWayland::getMainDisplayDimensions(GHOST_TUns32 &width, GHOST_TU
{
if (getNumDisplays() > 0) {
/* We assume first output as main. */
- width = uint32_t(d->outputs[0]->width);
- height = uint32_t(d->outputs[0]->height);
+ width = uint32_t(d->outputs[0]->width_pxl) / d->outputs[0]->scale;
+ height = uint32_t(d->outputs[0]->height_pxl) / d->outputs[0]->scale;
}
}
@@ -1481,7 +1581,7 @@ void GHOST_SystemWayland::getAllDisplayDimensions(GHOST_TUns32 &width, GHOST_TUn
getMainDisplayDimensions(width, height);
}
-GHOST_IContext *GHOST_SystemWayland::createOffscreenContext(GHOST_GLSettings glSettings)
+GHOST_IContext *GHOST_SystemWayland::createOffscreenContext(GHOST_GLSettings /*glSettings*/)
{
/* Create new off-screen window. */
wl_surface *os_surface = wl_compositor_create_surface(compositor());
@@ -1530,6 +1630,11 @@ GHOST_IWindow *GHOST_SystemWayland::createWindow(const char *title,
const bool is_dialog,
const GHOST_IWindow *parentWindow)
{
+ /* globally store pointer to window manager */
+ if (!window_manager) {
+ window_manager = getWindowManager();
+ }
+
GHOST_WindowWayland *window = new GHOST_WindowWayland(
this,
title,
@@ -1574,6 +1679,21 @@ xdg_wm_base *GHOST_SystemWayland::shell()
return d->xdg_shell;
}
+zxdg_decoration_manager_v1 *GHOST_SystemWayland::decoration_manager()
+{
+ return d->xdg_decoration_manager;
+}
+
+const std::vector<output_t *> &GHOST_SystemWayland::outputs() const
+{
+ return d->outputs;
+}
+
+wl_shm *GHOST_SystemWayland::shm() const
+{
+ return d->shm;
+}
+
void GHOST_SystemWayland::setSelection(const std::string &selection)
{
this->selection = selection;
@@ -1581,23 +1701,20 @@ void GHOST_SystemWayland::setSelection(const std::string &selection)
static void set_cursor_buffer(input_t *input, wl_buffer *buffer)
{
- input->cursor.visible = (buffer != nullptr);
+ cursor_t *c = &input->cursor;
- wl_surface_attach(input->cursor.surface, buffer, 0, 0);
- wl_surface_commit(input->cursor.surface);
+ c->visible = (buffer != nullptr);
- if (input->cursor.visible) {
- wl_surface_damage(input->cursor.surface,
- 0,
- 0,
- int32_t(input->cursor.image.width),
- int32_t(input->cursor.image.height));
- wl_pointer_set_cursor(input->pointer,
- input->pointer_serial,
- input->cursor.surface,
- int32_t(input->cursor.image.hotspot_x),
- int32_t(input->cursor.image.hotspot_y));
- }
+ wl_surface_attach(c->surface, buffer, 0, 0);
+
+ wl_surface_damage(c->surface, 0, 0, int32_t(c->image.width), int32_t(c->image.height));
+ wl_pointer_set_cursor(input->pointer,
+ input->pointer_serial,
+ c->visible ? c->surface : nullptr,
+ int32_t(c->image.hotspot_x) / c->scale,
+ int32_t(c->image.hotspot_y) / c->scale);
+
+ wl_surface_commit(c->surface);
}
GHOST_TSuccess GHOST_SystemWayland::setCursorShape(GHOST_TStandardCursor shape)
@@ -1608,7 +1725,15 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorShape(GHOST_TStandardCursor shape)
const std::string cursor_name = cursors.count(shape) ? cursors.at(shape) :
cursors.at(GHOST_kStandardCursorDefault);
- wl_cursor *cursor = wl_cursor_theme_get_cursor(d->cursor_theme, cursor_name.c_str());
+ input_t *input = d->inputs[0];
+ cursor_t *c = &input->cursor;
+
+ if (!c->theme) {
+ /* The cursor surface hasn't entered an output yet. Initialize theme with scale 1. */
+ c->theme = wl_cursor_theme_load(c->theme_name.c_str(), c->size, d->inputs[0]->system->shm());
+ }
+
+ wl_cursor *cursor = wl_cursor_theme_get_cursor(c->theme, cursor_name.c_str());
if (!cursor) {
GHOST_PRINT("cursor '" << cursor_name << "' does not exist" << std::endl);
@@ -1620,11 +1745,11 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorShape(GHOST_TStandardCursor shape)
if (!buffer) {
return GHOST_kFailure;
}
- cursor_t *c = &d->inputs[0]->cursor;
+
c->buffer = buffer;
c->image = *image;
- set_cursor_buffer(d->inputs[0], buffer);
+ set_cursor_buffer(input, buffer);
return GHOST_kSuccess;
}
@@ -1735,6 +1860,11 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorVisibility(bool visible)
GHOST_TSuccess GHOST_SystemWayland::setCursorGrab(const GHOST_TGrabCursorMode mode,
wl_surface *surface)
{
+ /* ignore, if the required protocols are not supported */
+ if (!d->relative_pointer_manager || !d->pointer_constraints) {
+ return GHOST_kFailure;
+ }
+
if (d->inputs.empty()) {
return GHOST_kFailure;
}
@@ -1754,6 +1884,7 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorGrab(const GHOST_TGrabCursorMode mo
break;
case GHOST_kGrabNormal:
+ break;
case GHOST_kGrabWrap:
input->relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer(
d->relative_pointer_manager, input->pointer);
diff --git a/intern/ghost/intern/GHOST_SystemWayland.h b/intern/ghost/intern/GHOST_SystemWayland.h
index 10b9ef6bd62..3a08a0d3b16 100644
--- a/intern/ghost/intern/GHOST_SystemWayland.h
+++ b/intern/ghost/intern/GHOST_SystemWayland.h
@@ -26,6 +26,7 @@
#include "GHOST_WindowWayland.h"
#include <wayland-client.h>
+#include <xdg-decoration-client-protocol.h>
#include <xdg-shell-client-protocol.h>
#include <string>
@@ -34,6 +35,16 @@ class GHOST_WindowWayland;
struct display_t;
+struct output_t {
+ struct wl_output *output;
+ int32_t width_pxl, height_pxl; // dimensions in pixel
+ int32_t width_mm, height_mm; // dimensions in millimeter
+ int transform;
+ int scale;
+ std::string make;
+ std::string model;
+};
+
class GHOST_SystemWayland : public GHOST_System {
public:
GHOST_SystemWayland();
@@ -84,6 +95,12 @@ class GHOST_SystemWayland : public GHOST_System {
xdg_wm_base *shell();
+ zxdg_decoration_manager_v1 *decoration_manager();
+
+ const std::vector<output_t *> &outputs() const;
+
+ wl_shm *shm() const;
+
void setSelection(const std::string &selection);
GHOST_TSuccess setCursorShape(GHOST_TStandardCursor shape);
diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp
index 430e8216ae3..c3a243cc22c 100644
--- a/intern/ghost/intern/GHOST_SystemWin32.cpp
+++ b/intern/ghost/intern/GHOST_SystemWin32.cpp
@@ -32,6 +32,7 @@
#include <commctrl.h>
#include <psapi.h>
#include <shellapi.h>
+#include <shellscalingapi.h>
#include <shlobj.h>
#include <tlhelp32.h>
#include <windowsx.h>
@@ -97,41 +98,6 @@
# define VK_GR_LESS 0xE2
#endif // VK_GR_LESS
-#ifndef VK_MEDIA_NEXT_TRACK
-# define VK_MEDIA_NEXT_TRACK 0xB0
-#endif // VK_MEDIA_NEXT_TRACK
-#ifndef VK_MEDIA_PREV_TRACK
-# define VK_MEDIA_PREV_TRACK 0xB1
-#endif // VK_MEDIA_PREV_TRACK
-#ifndef VK_MEDIA_STOP
-# define VK_MEDIA_STOP 0xB2
-#endif // VK_MEDIA_STOP
-#ifndef VK_MEDIA_PLAY_PAUSE
-# define VK_MEDIA_PLAY_PAUSE 0xB3
-#endif // VK_MEDIA_PLAY_PAUSE
-
-// Window message newer than Windows 7
-#ifndef WM_DPICHANGED
-# define WM_DPICHANGED 0x02E0
-#endif // WM_DPICHANGED
-
-// WM_POINTER API messages minimum Windows 7
-#ifndef WM_POINTERENTER
-# define WM_POINTERENTER 0x0249
-#endif // WM_POINTERENTER
-#ifndef WM_POINTERDOWN
-# define WM_POINTERDOWN 0x0246
-#endif // WM_POINTERDOWN
-#ifndef WM_POINTERUPDATE
-# define WM_POINTERUPDATE 0x0245
-#endif // WM_POINTERUPDATE
-#ifndef WM_POINTERUP
-# define WM_POINTERUP 0x0247
-#endif // WM_POINTERUP
-#ifndef WM_POINTERLEAVE
-# define WM_POINTERLEAVE 0x024A
-#endif // WM_POINTERLEAVE
-
/* Workaround for some laptop touchpads, some of which seems to
* have driver issues which makes it so window function receives
* the message, but PeekMessage doesn't pick those messages for
@@ -173,24 +139,6 @@ static void initRawInput()
#undef DEVICE_COUNT
}
-#ifndef DPI_ENUMS_DECLARED
-typedef enum PROCESS_DPI_AWARENESS {
- PROCESS_DPI_UNAWARE = 0,
- PROCESS_SYSTEM_DPI_AWARE = 1,
- PROCESS_PER_MONITOR_DPI_AWARE = 2
-} PROCESS_DPI_AWARENESS;
-
-typedef enum MONITOR_DPI_TYPE {
- MDT_EFFECTIVE_DPI = 0,
- MDT_ANGULAR_DPI = 1,
- MDT_RAW_DPI = 2,
- MDT_DEFAULT = MDT_EFFECTIVE_DPI
-} MONITOR_DPI_TYPE;
-
-# define USER_DEFAULT_SCREEN_DPI 96
-
-# define DPI_ENUMS_DECLARED
-#endif
typedef HRESULT(API *GHOST_WIN32_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS);
typedef BOOL(API *GHOST_WIN32_EnableNonClientDpiScaling)(HWND);
@@ -205,15 +153,7 @@ GHOST_SystemWin32::GHOST_SystemWin32()
// Tell Windows we are per monitor DPI aware. This disables the default
// blurry scaling and enables WM_DPICHANGED to allow us to draw at proper DPI.
- HMODULE m_shcore = ::LoadLibrary("Shcore.dll");
- if (m_shcore) {
- GHOST_WIN32_SetProcessDpiAwareness fpSetProcessDpiAwareness =
- (GHOST_WIN32_SetProcessDpiAwareness)::GetProcAddress(m_shcore, "SetProcessDpiAwareness");
-
- if (fpSetProcessDpiAwareness) {
- fpSetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
- }
- }
+ SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
// Check if current keyboard layout uses AltGr and save keylayout ID for
// specialized handling if keys like VK_OEM_*. I.e. french keylayout
@@ -581,14 +521,7 @@ GHOST_TSuccess GHOST_SystemWin32::init()
InitCommonControls();
/* Disable scaling on high DPI displays on Vista */
- HMODULE
- user32 = ::LoadLibraryA("user32.dll");
- typedef BOOL(WINAPI * LPFNSETPROCESSDPIAWARE)();
- LPFNSETPROCESSDPIAWARE SetProcessDPIAware = (LPFNSETPROCESSDPIAWARE)GetProcAddress(
- user32, "SetProcessDPIAware");
- if (SetProcessDPIAware)
- SetProcessDPIAware();
- FreeLibrary(user32);
+ SetProcessDPIAware();
initRawInput();
m_lfstart = ::GetTickCount();
@@ -1442,12 +1375,23 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
*/
break;
case WM_SYSCOMMAND:
- /* The WM_SYSCHAR message is sent to the window when system commands such as
+ /* The WM_SYSCOMMAND message is sent to the window when system commands such as
* maximize, minimize or close the window are triggered. Also it is sent when ALT
* button is press for menu. To prevent this we must return preventing DefWindowProc.
+ *
+ * Note that the four low-order bits of the wParam parameter are used internally by the
+ * OS. To obtain the correct result when testing the value of wParam, an application
+ * must combine the value 0xFFF0 with the wParam value by using the bitwise AND operator.
*/
- if (wParam == SC_KEYMENU) {
- eventHandled = true;
+ switch (wParam & 0xFFF0) {
+ case SC_KEYMENU:
+ eventHandled = true;
+ break;
+ case SC_RESTORE:
+ ::ShowWindow(hwnd, SW_RESTORE);
+ window->setState(window->getState());
+ eventHandled = true;
+ break;
}
break;
////////////////////////////////////////////////////////////////////////
@@ -1518,14 +1462,7 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
* since DefWindowProc propagates it up the parent chain
* until it finds a window that processes it.
*/
-
- /* Get the window under the mouse and send event to its queue. */
- POINT mouse_pos = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
- HWND mouse_hwnd = ChildWindowFromPoint(HWND_DESKTOP, mouse_pos);
- GHOST_WindowWin32 *mouse_window = (GHOST_WindowWin32 *)::GetWindowLongPtr(mouse_hwnd,
- GWLP_USERDATA);
-
- processWheelEvent(mouse_window ? mouse_window : window, wParam, lParam);
+ processWheelEvent(window, wParam, lParam);
eventHandled = true;
#ifdef BROKEN_PEEK_TOUCHPAD
PostMessage(hwnd, WM_USER, 0, 0);
diff --git a/intern/ghost/intern/GHOST_Util.h b/intern/ghost/intern/GHOST_Util.h
new file mode 100644
index 00000000000..8be5e373b28
--- /dev/null
+++ b/intern/ghost/intern/GHOST_Util.h
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+/** \file
+ * \ingroup GHOST
+ */
+
+#pragma once
+
+#include <functional>
+
+/**
+ * RAII wrapper for typical C `void *` custom data.
+ * Used for exception safe custom-data handling during constructor calls.
+ */
+struct GHOST_C_CustomDataWrapper {
+ using FreeFn = std::function<void(void *)>;
+
+ void *custom_data_;
+ FreeFn free_fn_;
+
+ GHOST_C_CustomDataWrapper(void *custom_data, FreeFn free_fn)
+ : custom_data_(custom_data), free_fn_(free_fn)
+ {
+ }
+ ~GHOST_C_CustomDataWrapper()
+ {
+ if (free_fn_ != nullptr && custom_data_ != nullptr) {
+ free_fn_(custom_data_);
+ }
+ }
+};
diff --git a/intern/ghost/intern/GHOST_WaylandCursorSettings.h b/intern/ghost/intern/GHOST_WaylandCursorSettings.h
new file mode 100644
index 00000000000..c7d2d82ff84
--- /dev/null
+++ b/intern/ghost/intern/GHOST_WaylandCursorSettings.h
@@ -0,0 +1,130 @@
+/*
+ * 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.
+ */
+
+/** \file
+ * \ingroup GHOST
+ */
+
+#pragma once
+#include <dbus/dbus.h>
+#include <string>
+
+static DBusMessage *get_setting_sync(DBusConnection *const connection,
+ const char *key,
+ const char *value)
+{
+ DBusError error;
+ dbus_bool_t success;
+ DBusMessage *message;
+ DBusMessage *reply;
+
+ dbus_error_init(&error);
+
+ message = dbus_message_new_method_call("org.freedesktop.portal.Desktop",
+ "/org/freedesktop/portal/desktop",
+ "org.freedesktop.portal.Settings",
+ "Read");
+
+ success = dbus_message_append_args(
+ message, DBUS_TYPE_STRING, &key, DBUS_TYPE_STRING, &value, DBUS_TYPE_INVALID);
+
+ if (!success) {
+ return NULL;
+ }
+
+ reply = dbus_connection_send_with_reply_and_block(
+ connection, message, DBUS_TIMEOUT_USE_DEFAULT, &error);
+
+ dbus_message_unref(message);
+
+ if (dbus_error_is_set(&error)) {
+ return NULL;
+ }
+
+ return reply;
+}
+
+static bool parse_type(DBusMessage *const reply, const int type, void *value)
+{
+ DBusMessageIter iter[3];
+
+ dbus_message_iter_init(reply, &iter[0]);
+ if (dbus_message_iter_get_arg_type(&iter[0]) != DBUS_TYPE_VARIANT) {
+ return false;
+ }
+
+ dbus_message_iter_recurse(&iter[0], &iter[1]);
+ if (dbus_message_iter_get_arg_type(&iter[1]) != DBUS_TYPE_VARIANT) {
+ return false;
+ }
+
+ dbus_message_iter_recurse(&iter[1], &iter[2]);
+ if (dbus_message_iter_get_arg_type(&iter[2]) != type) {
+ return false;
+ }
+
+ dbus_message_iter_get_basic(&iter[2], value);
+
+ return true;
+}
+
+static bool get_cursor_settings(std::string &theme, int &size)
+{
+ static const char name[] = "org.gnome.desktop.interface";
+ static const char key_theme[] = "cursor-theme";
+ static const char key_size[] = "cursor-size";
+
+ DBusError error;
+ DBusConnection *connection;
+ DBusMessage *reply;
+ const char *value_theme = NULL;
+
+ dbus_error_init(&error);
+
+ connection = dbus_bus_get(DBUS_BUS_SESSION, &error);
+
+ if (dbus_error_is_set(&error)) {
+ return false;
+ }
+
+ reply = get_setting_sync(connection, name, key_theme);
+ if (!reply) {
+ return false;
+ }
+
+ if (!parse_type(reply, DBUS_TYPE_STRING, &value_theme)) {
+ dbus_message_unref(reply);
+ return false;
+ }
+
+ theme = std::string(value_theme);
+
+ dbus_message_unref(reply);
+
+ reply = get_setting_sync(connection, name, key_size);
+ if (!reply) {
+ return false;
+ }
+
+ if (!parse_type(reply, DBUS_TYPE_INT32, &size)) {
+ dbus_message_unref(reply);
+ return false;
+ }
+
+ dbus_message_unref(reply);
+
+ return true;
+}
diff --git a/intern/ghost/intern/GHOST_WindowCocoa.mm b/intern/ghost/intern/GHOST_WindowCocoa.mm
index 6a5a088d163..1776b0d5ce0 100644
--- a/intern/ghost/intern/GHOST_WindowCocoa.mm
+++ b/intern/ghost/intern/GHOST_WindowCocoa.mm
@@ -1131,7 +1131,8 @@ GHOST_TSuccess GHOST_WindowCocoa::hasCursorShape(GHOST_TStandardCursor shape)
return success;
}
-/** Reverse the bits in a GHOST_TUns8
+/* Reverse the bits in a GHOST_TUns8 */
+#if 0
static GHOST_TUns8 uns8ReverseBits(GHOST_TUns8 ch)
{
ch= ((ch >> 1) & 0x55) | ((ch << 1) & 0xAA);
@@ -1139,7 +1140,7 @@ static GHOST_TUns8 uns8ReverseBits(GHOST_TUns8 ch)
ch= ((ch >> 4) & 0x0F) | ((ch << 4) & 0xF0);
return ch;
}
-*/
+#endif
/** Reverse the bits in a GHOST_TUns16 */
static GHOST_TUns16 uns16ReverseBits(GHOST_TUns16 shrt)
diff --git a/intern/ghost/intern/GHOST_WindowWayland.cpp b/intern/ghost/intern/GHOST_WindowWayland.cpp
index 1e1d0814d3a..cbac2d6eaa1 100644
--- a/intern/ghost/intern/GHOST_WindowWayland.cpp
+++ b/intern/ghost/intern/GHOST_WindowWayland.cpp
@@ -29,11 +29,19 @@
#include <wayland-egl.h>
+static constexpr size_t base_dpi = 96;
+
struct window_t {
GHOST_WindowWayland *w;
wl_surface *surface;
+ // outputs on which the window is currently shown on
+ std::unordered_set<const output_t *> outputs;
+ GHOST_TUns16 dpi = 0;
+ int scale = 1;
struct xdg_surface *xdg_surface;
struct xdg_toplevel *xdg_toplevel;
+ struct zxdg_toplevel_decoration_v1 *xdg_toplevel_decoration = nullptr;
+ enum zxdg_toplevel_decoration_v1_mode decoration_mode;
wl_egl_window *egl_window;
int32_t pending_width, pending_height;
bool is_maximised;
@@ -93,17 +101,30 @@ static const xdg_toplevel_listener toplevel_listener = {
toplevel_close,
};
+static void toplevel_decoration_configure(
+ void *data,
+ struct zxdg_toplevel_decoration_v1 * /*zxdg_toplevel_decoration_v1*/,
+ uint32_t mode)
+{
+ static_cast<window_t *>(data)->decoration_mode = zxdg_toplevel_decoration_v1_mode(mode);
+}
+
+static const zxdg_toplevel_decoration_v1_listener toplevel_decoration_v1_listener = {
+ toplevel_decoration_configure,
+};
+
static void surface_configure(void *data, xdg_surface *xdg_surface, uint32_t serial)
{
window_t *win = static_cast<window_t *>(data);
- int w, h;
- wl_egl_window_get_attached_size(win->egl_window, &w, &h);
- if (win->pending_width != 0 && win->pending_height != 0 && win->pending_width != w &&
- win->pending_height != h) {
- win->width = win->pending_width;
- win->height = win->pending_height;
- wl_egl_window_resize(win->egl_window, win->pending_width, win->pending_height, 0, 0);
+ if (win->xdg_surface != xdg_surface) {
+ return;
+ }
+
+ if (win->pending_width != 0 && win->pending_height != 0) {
+ win->width = win->scale * win->pending_width;
+ win->height = win->scale * win->pending_height;
+ wl_egl_window_resize(win->egl_window, win->width, win->height, 0, 0);
win->pending_width = 0;
win->pending_height = 0;
win->w->notify_size();
@@ -123,6 +144,52 @@ static const xdg_surface_listener surface_listener = {
surface_configure,
};
+static bool update_scale(GHOST_WindowWayland *window)
+{
+ int scale = 0;
+ for (const output_t *output : window->outputs_active()) {
+ if (output->scale > scale)
+ scale = output->scale;
+ }
+
+ if (scale > 0 && window->scale() != scale) {
+ window->scale() = scale;
+ // using the real DPI will cause wrong scaling of the UI
+ // use a multiplier for the default DPI as workaround
+ window->dpi() = scale * base_dpi;
+ wl_surface_set_buffer_scale(window->surface(), scale);
+ return true;
+ }
+ return false;
+}
+
+static void surface_enter(void *data, struct wl_surface * /*wl_surface*/, struct wl_output *output)
+{
+ GHOST_WindowWayland *w = static_cast<GHOST_WindowWayland *>(data);
+ for (const output_t *reg_output : w->outputs()) {
+ if (reg_output->output == output) {
+ w->outputs_active().insert(reg_output);
+ }
+ }
+ update_scale(w);
+}
+
+static void surface_leave(void *data, struct wl_surface * /*wl_surface*/, struct wl_output *output)
+{
+ GHOST_WindowWayland *w = static_cast<GHOST_WindowWayland *>(data);
+ for (const output_t *reg_output : w->outputs()) {
+ if (reg_output->output == output) {
+ w->outputs_active().erase(reg_output);
+ }
+ }
+ update_scale(w);
+}
+
+struct wl_surface_listener wl_surface_listener = {
+ surface_enter,
+ surface_leave,
+};
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -161,17 +228,28 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system,
/* Window surfaces. */
w->surface = wl_compositor_create_surface(m_system->compositor());
+ wl_surface_add_listener(w->surface, &wl_surface_listener, this);
+
w->egl_window = wl_egl_window_create(w->surface, int(width), int(height));
w->xdg_surface = xdg_wm_base_get_xdg_surface(m_system->shell(), w->surface);
w->xdg_toplevel = xdg_surface_get_toplevel(w->xdg_surface);
+ if (m_system->decoration_manager()) {
+ w->xdg_toplevel_decoration = zxdg_decoration_manager_v1_get_toplevel_decoration(
+ m_system->decoration_manager(), w->xdg_toplevel);
+ zxdg_toplevel_decoration_v1_add_listener(
+ w->xdg_toplevel_decoration, &toplevel_decoration_v1_listener, w);
+ zxdg_toplevel_decoration_v1_set_mode(w->xdg_toplevel_decoration,
+ ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
+ }
+
wl_surface_set_user_data(w->surface, this);
xdg_surface_add_listener(w->xdg_surface, &surface_listener, w);
xdg_toplevel_add_listener(w->xdg_toplevel, &toplevel_listener, w);
- if (parentWindow) {
+ if (parentWindow && is_dialog) {
xdg_toplevel_set_parent(
w->xdg_toplevel, dynamic_cast<const GHOST_WindowWayland *>(parentWindow)->w->xdg_toplevel);
}
@@ -192,6 +270,9 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system,
if (setDrawingContextType(type) == GHOST_kFailure) {
GHOST_PRINT("Failed to create EGL context" << std::endl);
}
+
+ /* set swap interval to 0 to prevent blocking */
+ setSwapInterval(0);
}
GHOST_TSuccess GHOST_WindowWayland::close()
@@ -226,6 +307,31 @@ GHOST_TSuccess GHOST_WindowWayland::notify_size()
new GHOST_Event(m_system->getMilliSeconds(), GHOST_kEventWindowSize, this));
}
+wl_surface *GHOST_WindowWayland::surface() const
+{
+ return w->surface;
+}
+
+const std::vector<output_t *> &GHOST_WindowWayland::outputs() const
+{
+ return m_system->outputs();
+}
+
+std::unordered_set<const output_t *> &GHOST_WindowWayland::outputs_active()
+{
+ return w->outputs;
+}
+
+uint16_t &GHOST_WindowWayland::dpi()
+{
+ return w->dpi;
+}
+
+int &GHOST_WindowWayland::scale()
+{
+ return w->scale;
+}
+
GHOST_TSuccess GHOST_WindowWayland::setWindowCursorGrab(GHOST_TGrabCursorMode mode)
{
return m_system->setCursorGrab(mode, w->surface);
@@ -310,6 +416,9 @@ GHOST_WindowWayland::~GHOST_WindowWayland()
releaseNativeHandles();
wl_egl_window_destroy(w->egl_window);
+ if (w->xdg_toplevel_decoration) {
+ zxdg_toplevel_decoration_v1_destroy(w->xdg_toplevel_decoration);
+ }
xdg_toplevel_destroy(w->xdg_toplevel);
xdg_surface_destroy(w->xdg_surface);
wl_surface_destroy(w->surface);
@@ -317,6 +426,11 @@ GHOST_WindowWayland::~GHOST_WindowWayland()
delete w;
}
+GHOST_TUns16 GHOST_WindowWayland::getDPIHint()
+{
+ return w->dpi;
+}
+
GHOST_TSuccess GHOST_WindowWayland::setWindowCursorVisibility(bool visible)
{
return m_system->setCursorVisibility(visible);
diff --git a/intern/ghost/intern/GHOST_WindowWayland.h b/intern/ghost/intern/GHOST_WindowWayland.h
index b62b5c24d60..dbddc7c469e 100644
--- a/intern/ghost/intern/GHOST_WindowWayland.h
+++ b/intern/ghost/intern/GHOST_WindowWayland.h
@@ -24,9 +24,14 @@
#include "GHOST_Window.h"
+#include <unordered_set>
+#include <vector>
+
class GHOST_SystemWayland;
struct window_t;
+struct wl_surface;
+struct output_t;
class GHOST_WindowWayland : public GHOST_Window {
public:
@@ -47,6 +52,8 @@ class GHOST_WindowWayland : public GHOST_Window {
~GHOST_WindowWayland() override;
+ GHOST_TUns16 getDPIHint() override;
+
GHOST_TSuccess close();
GHOST_TSuccess activate();
@@ -55,6 +62,16 @@ class GHOST_WindowWayland : public GHOST_Window {
GHOST_TSuccess notify_size();
+ wl_surface *surface() const;
+
+ const std::vector<output_t *> &outputs() const;
+
+ std::unordered_set<const output_t *> &outputs_active();
+
+ uint16_t &dpi();
+
+ int &scale();
+
protected:
GHOST_TSuccess setWindowCursorGrab(GHOST_TGrabCursorMode mode) override;
diff --git a/intern/ghost/intern/GHOST_WindowWin32.cpp b/intern/ghost/intern/GHOST_WindowWin32.cpp
index 70901954df2..eeafe333633 100644
--- a/intern/ghost/intern/GHOST_WindowWin32.cpp
+++ b/intern/ghost/intern/GHOST_WindowWin32.cpp
@@ -84,9 +84,6 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
m_wantAlphaBackground(alphaBackground),
m_normal_state(GHOST_kWindowStateNormal),
m_user32(NULL),
- m_fpGetPointerInfoHistory(NULL),
- m_fpGetPointerPenInfoHistory(NULL),
- m_fpGetPointerTouchInfoHistory(NULL),
m_parentWindowHwnd(parentwindow ? parentwindow->m_hWnd : HWND_DESKTOP),
m_debug_context(is_debug)
{
@@ -121,22 +118,30 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
monitor.dwFlags = 0;
GetMonitorInfo(MonitorFromRect(&win_rect, MONITOR_DEFAULTTONEAREST), &monitor);
- /* Adjust our requested size to allow for caption and borders and constrain to monitor. */
- AdjustWindowRectEx(&win_rect, WS_CAPTION, FALSE, 0);
+ /* Constrain requested size and position to fit within this monitor. */
width = min(monitor.rcWork.right - monitor.rcWork.left, win_rect.right - win_rect.left);
- left = min(max(monitor.rcWork.left, win_rect.left), monitor.rcWork.right - width);
height = min(monitor.rcWork.bottom - monitor.rcWork.top, win_rect.bottom - win_rect.top);
- top = min(max(monitor.rcWork.top, win_rect.top), monitor.rcWork.bottom - height);
-
- m_hWnd = ::CreateWindowExW(extended_style, // window extended style
- s_windowClassName, // pointer to registered class name
- title_16, // pointer to window name
- style, // window style
- left, // horizontal position of window
- top, // vertical position of window
- width, // window width
- height, // window height
- m_parentWindowHwnd, // handle to parent or owner window
+ win_rect.left = min(max(monitor.rcWork.left, win_rect.left), monitor.rcWork.right - width);
+ win_rect.right = win_rect.left + width;
+ win_rect.top = min(max(monitor.rcWork.top, win_rect.top), monitor.rcWork.bottom - height);
+ win_rect.bottom = win_rect.top + height;
+
+ /* Adjust to allow for caption, borders, shadows, scaling, etc. Resulting values can be
+ * correctly outside of monitor bounds. Note: You cannot specify WS_OVERLAPPED when calling. */
+ AdjustWindowRectEx(&win_rect, style & ~WS_OVERLAPPED, FALSE, extended_style);
+
+ /* But never allow a top position that can hide part of the title bar. */
+ win_rect.top = max(monitor.rcWork.top, win_rect.top);
+
+ m_hWnd = ::CreateWindowExW(extended_style, // window extended style
+ s_windowClassName, // pointer to registered class name
+ title_16, // pointer to window name
+ style, // window style
+ win_rect.left, // horizontal position of window
+ win_rect.top, // vertical position of window
+ win_rect.right - win_rect.left, // window width
+ win_rect.bottom - win_rect.top, // window height
+ m_parentWindowHwnd, // handle to parent or owner window
0, // handle to menu or child-window identifier
::GetModuleHandle(0), // handle to application instance
0); // pointer to window-creation data
@@ -145,19 +150,7 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
m_user32 = ::LoadLibrary("user32.dll");
if (m_hWnd) {
- if (m_user32) {
- // Touch enabled screens with pen support by default have gestures
- // enabled, which results in a delay between the pointer down event
- // and the first move when using the stylus. RegisterTouchWindow
- // disables the new gesture architecture enabling the events to be
- // sent immediately to the application rather than being absorbed by
- // the gesture API.
- GHOST_WIN32_RegisterTouchWindow pRegisterTouchWindow = (GHOST_WIN32_RegisterTouchWindow)
- GetProcAddress(m_user32, "RegisterTouchWindow");
- if (pRegisterTouchWindow) {
- pRegisterTouchWindow(m_hWnd, 0);
- }
- }
+ RegisterTouchWindow(m_hWnd, 0);
// Register this window as a droptarget. Requires m_hWnd to be valid.
// Note that OleInitialize(0) has to be called prior to this. Done in GHOST_SystemWin32.
@@ -224,16 +217,6 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
}
}
- // Initialize Windows Ink
- if (m_user32) {
- m_fpGetPointerInfoHistory = (GHOST_WIN32_GetPointerInfoHistory)::GetProcAddress(
- m_user32, "GetPointerInfoHistory");
- m_fpGetPointerPenInfoHistory = (GHOST_WIN32_GetPointerPenInfoHistory)::GetProcAddress(
- m_user32, "GetPointerPenInfoHistory");
- m_fpGetPointerTouchInfoHistory = (GHOST_WIN32_GetPointerTouchInfoHistory)::GetProcAddress(
- m_user32, "GetPointerTouchInfoHistory");
- }
-
// Initialize Wintab
m_wintab.handle = ::LoadLibrary("Wintab32.dll");
if (m_wintab.handle && m_system->getTabletAPI() != GHOST_kTabletNative) {
@@ -318,9 +301,6 @@ GHOST_WindowWin32::~GHOST_WindowWin32()
if (m_user32) {
FreeLibrary(m_user32);
m_user32 = NULL;
- m_fpGetPointerInfoHistory = NULL;
- m_fpGetPointerPenInfoHistory = NULL;
- m_fpGetPointerTouchInfoHistory = NULL;
}
if (m_customCursor) {
@@ -521,7 +501,7 @@ GHOST_TSuccess GHOST_WindowWin32::setState(GHOST_TWindowState state)
switch (state) {
case GHOST_kWindowStateMinimized:
- wp.showCmd = SW_SHOWMINIMIZED;
+ wp.showCmd = SW_MINIMIZE;
break;
case GHOST_kWindowStateMaximized:
wp.showCmd = SW_SHOWMAXIMIZED;
@@ -533,11 +513,18 @@ GHOST_TSuccess GHOST_WindowWin32::setState(GHOST_TWindowState state)
wp.showCmd = SW_SHOWMAXIMIZED;
wp.ptMaxPosition.x = 0;
wp.ptMaxPosition.y = 0;
- style &= ~WS_CAPTION;
+ style &= ~(WS_CAPTION | WS_MAXIMIZE);
break;
case GHOST_kWindowStateNormal:
default:
- wp.showCmd = SW_SHOWNORMAL;
+ if (curstate == GHOST_kWindowStateFullScreen &&
+ m_normal_state == GHOST_kWindowStateMaximized) {
+ wp.showCmd = SW_SHOWMAXIMIZED;
+ m_normal_state = GHOST_kWindowStateNormal;
+ }
+ else {
+ wp.showCmd = SW_SHOWNORMAL;
+ }
break;
}
::SetWindowLongPtr(m_hWnd, GWL_STYLE, style);
@@ -935,15 +922,14 @@ GHOST_TSuccess GHOST_WindowWin32::getPointerInfo(
GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)GHOST_System::getSystem();
GHOST_TUns32 outCount;
- if (!(m_fpGetPointerInfoHistory && m_fpGetPointerInfoHistory(pointerId, &outCount, NULL))) {
+ if (!(GetPointerInfoHistory(pointerId, &outCount, NULL))) {
return GHOST_kFailure;
}
auto pointerPenInfo = std::vector<POINTER_PEN_INFO>(outCount);
outPointerInfo.resize(outCount);
- if (!(m_fpGetPointerPenInfoHistory &&
- m_fpGetPointerPenInfoHistory(pointerId, &outCount, pointerPenInfo.data()))) {
+ if (!(GetPointerPenInfoHistory(pointerId, &outCount, pointerPenInfo.data()))) {
return GHOST_kFailure;
}
diff --git a/intern/ghost/intern/GHOST_WindowWin32.h b/intern/ghost/intern/GHOST_WindowWin32.h
index d86a5c12040..a13bd876667 100644
--- a/intern/ghost/intern/GHOST_WindowWin32.h
+++ b/intern/ghost/intern/GHOST_WindowWin32.h
@@ -53,177 +53,12 @@ typedef BOOL(API *GHOST_WIN32_WTPacket)(HCTX, UINT, LPVOID);
typedef BOOL(API *GHOST_WIN32_WTEnable)(HCTX, BOOL);
typedef BOOL(API *GHOST_WIN32_WTOverlap)(HCTX, BOOL);
-// typedef to user32 functions to disable gestures on windows
-typedef BOOL(API *GHOST_WIN32_RegisterTouchWindow)(HWND hwnd, ULONG ulFlags);
-
// typedefs for user32 functions to allow dynamic loading of Windows 10 DPI scaling functions
typedef UINT(API *GHOST_WIN32_GetDpiForWindow)(HWND);
#ifndef USER_DEFAULT_SCREEN_DPI
# define USER_DEFAULT_SCREEN_DPI 96
#endif // USER_DEFAULT_SCREEN_DPI
-// typedefs for user32 functions to allow pointer functions
-enum tagPOINTER_INPUT_TYPE {
- PT_POINTER = 1, // Generic pointer
- PT_TOUCH = 2, // Touch
- PT_PEN = 3, // Pen
- PT_MOUSE = 4, // Mouse
-#if (WINVER >= 0x0603)
- PT_TOUCHPAD = 5, // Touchpad
-#endif /* WINVER >= 0x0603 */
-};
-
-typedef enum tagPOINTER_BUTTON_CHANGE_TYPE {
- POINTER_CHANGE_NONE,
- POINTER_CHANGE_FIRSTBUTTON_DOWN,
- POINTER_CHANGE_FIRSTBUTTON_UP,
- POINTER_CHANGE_SECONDBUTTON_DOWN,
- POINTER_CHANGE_SECONDBUTTON_UP,
- POINTER_CHANGE_THIRDBUTTON_DOWN,
- POINTER_CHANGE_THIRDBUTTON_UP,
- POINTER_CHANGE_FOURTHBUTTON_DOWN,
- POINTER_CHANGE_FOURTHBUTTON_UP,
- POINTER_CHANGE_FIFTHBUTTON_DOWN,
- POINTER_CHANGE_FIFTHBUTTON_UP,
-} POINTER_BUTTON_CHANGE_TYPE;
-
-typedef DWORD POINTER_INPUT_TYPE;
-typedef UINT32 POINTER_FLAGS;
-
-#define POINTER_FLAG_NONE 0x00000000
-#define POINTER_FLAG_NEW 0x00000001
-#define POINTER_FLAG_INRANGE 0x00000002
-#define POINTER_FLAG_INCONTACT 0x00000004
-#define POINTER_FLAG_FIRSTBUTTON 0x00000010
-#define POINTER_FLAG_SECONDBUTTON 0x00000020
-#define POINTER_FLAG_THIRDBUTTON 0x00000040
-#define POINTER_FLAG_FOURTHBUTTON 0x00000080
-#define POINTER_FLAG_FIFTHBUTTON 0x00000100
-#define POINTER_FLAG_PRIMARY 0x00002000
-#define POINTER_FLAG_CONFIDENCE 0x000004000
-#define POINTER_FLAG_CANCELED 0x000008000
-#define POINTER_FLAG_DOWN 0x00010000
-#define POINTER_FLAG_UPDATE 0x00020000
-#define POINTER_FLAG_UP 0x00040000
-#define POINTER_FLAG_WHEEL 0x00080000
-#define POINTER_FLAG_HWHEEL 0x00100000
-#define POINTER_FLAG_CAPTURECHANGED 0x00200000
-#define POINTER_FLAG_HASTRANSFORM 0x00400000
-
-typedef struct tagPOINTER_INFO {
- POINTER_INPUT_TYPE pointerType;
- UINT32 pointerId;
- UINT32 frameId;
- POINTER_FLAGS pointerFlags;
- HANDLE sourceDevice;
- HWND hwndTarget;
- POINT ptPixelLocation;
- POINT ptHimetricLocation;
- POINT ptPixelLocationRaw;
- POINT ptHimetricLocationRaw;
- DWORD dwTime;
- UINT32 historyCount;
- INT32 InputData;
- DWORD dwKeyStates;
- UINT64 PerformanceCount;
- POINTER_BUTTON_CHANGE_TYPE ButtonChangeType;
-} POINTER_INFO;
-
-typedef UINT32 PEN_FLAGS;
-#define PEN_FLAG_NONE 0x00000000 // Default
-#define PEN_FLAG_BARREL 0x00000001 // The barrel button is pressed
-#define PEN_FLAG_INVERTED 0x00000002 // The pen is inverted
-#define PEN_FLAG_ERASER 0x00000004 // The eraser button is pressed
-
-typedef UINT32 PEN_MASK;
-#define PEN_MASK_NONE 0x00000000 // Default - none of the optional fields are valid
-#define PEN_MASK_PRESSURE 0x00000001 // The pressure field is valid
-#define PEN_MASK_ROTATION 0x00000002 // The rotation field is valid
-#define PEN_MASK_TILT_X 0x00000004 // The tiltX field is valid
-#define PEN_MASK_TILT_Y 0x00000008 // The tiltY field is valid
-
-typedef struct tagPOINTER_PEN_INFO {
- POINTER_INFO pointerInfo;
- PEN_FLAGS penFlags;
- PEN_MASK penMask;
- UINT32 pressure;
- UINT32 rotation;
- INT32 tiltX;
- INT32 tiltY;
-} POINTER_PEN_INFO;
-
-/*
- * Flags that appear in pointer input message parameters
- */
-#define POINTER_MESSAGE_FLAG_NEW 0x00000001 // New pointer
-#define POINTER_MESSAGE_FLAG_INRANGE 0x00000002 // Pointer has not departed
-#define POINTER_MESSAGE_FLAG_INCONTACT 0x00000004 // Pointer is in contact
-#define POINTER_MESSAGE_FLAG_FIRSTBUTTON 0x00000010 // Primary action
-#define POINTER_MESSAGE_FLAG_SECONDBUTTON 0x00000020 // Secondary action
-#define POINTER_MESSAGE_FLAG_THIRDBUTTON 0x00000040 // Third button
-#define POINTER_MESSAGE_FLAG_FOURTHBUTTON 0x00000080 // Fourth button
-#define POINTER_MESSAGE_FLAG_FIFTHBUTTON 0x00000100 // Fifth button
-#define POINTER_MESSAGE_FLAG_PRIMARY 0x00002000 // Pointer is primary
-#define POINTER_MESSAGE_FLAG_CONFIDENCE \
- 0x00004000 // Pointer is considered unlikely to be accidental
-#define POINTER_MESSAGE_FLAG_CANCELED 0x00008000 // Pointer is departing in an abnormal manner
-
-typedef UINT32 TOUCH_FLAGS;
-#define TOUCH_FLAG_NONE 0x00000000 // Default
-
-typedef UINT32 TOUCH_MASK;
-#define TOUCH_MASK_NONE 0x00000000 // Default - none of the optional fields are valid
-#define TOUCH_MASK_CONTACTAREA 0x00000001 // The rcContact field is valid
-#define TOUCH_MASK_ORIENTATION 0x00000002 // The orientation field is valid
-#define TOUCH_MASK_PRESSURE 0x00000004 // The pressure field is valid
-
-typedef struct tagPOINTER_TOUCH_INFO {
- POINTER_INFO pointerInfo;
- TOUCH_FLAGS touchFlags;
- TOUCH_MASK touchMask;
- RECT rcContact;
- RECT rcContactRaw;
- UINT32 orientation;
- UINT32 pressure;
-} POINTER_TOUCH_INFO;
-
-/*
- * Macros to retrieve information from pointer input message parameters
- */
-#define GET_POINTERID_WPARAM(wParam) (LOWORD(wParam))
-#define IS_POINTER_FLAG_SET_WPARAM(wParam, flag) (((DWORD)HIWORD(wParam) & (flag)) == (flag))
-#define IS_POINTER_NEW_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_NEW)
-#define IS_POINTER_INRANGE_WPARAM(wParam) \
- IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_INRANGE)
-#define IS_POINTER_INCONTACT_WPARAM(wParam) \
- IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_INCONTACT)
-#define IS_POINTER_FIRSTBUTTON_WPARAM(wParam) \
- IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_FIRSTBUTTON)
-#define IS_POINTER_SECONDBUTTON_WPARAM(wParam) \
- IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_SECONDBUTTON)
-#define IS_POINTER_THIRDBUTTON_WPARAM(wParam) \
- IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_THIRDBUTTON)
-#define IS_POINTER_FOURTHBUTTON_WPARAM(wParam) \
- IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_FOURTHBUTTON)
-#define IS_POINTER_FIFTHBUTTON_WPARAM(wParam) \
- IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_FIFTHBUTTON)
-#define IS_POINTER_PRIMARY_WPARAM(wParam) \
- IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_PRIMARY)
-#define HAS_POINTER_CONFIDENCE_WPARAM(wParam) \
- IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_CONFIDENCE)
-#define IS_POINTER_CANCELED_WPARAM(wParam) \
- IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_CANCELED)
-
-typedef BOOL(WINAPI *GHOST_WIN32_GetPointerInfoHistory)(UINT32 pointerId,
- UINT32 *entriesCount,
- POINTER_INFO *pointerInfo);
-typedef BOOL(WINAPI *GHOST_WIN32_GetPointerPenInfoHistory)(UINT32 pointerId,
- UINT32 *entriesCount,
- POINTER_PEN_INFO *penInfo);
-typedef BOOL(WINAPI *GHOST_WIN32_GetPointerTouchInfoHistory)(UINT32 pointerId,
- UINT32 *entriesCount,
- POINTER_TOUCH_INFO *touchInfo);
-
struct GHOST_PointerInfoWin32 {
GHOST_TInt32 pointerId;
GHOST_TInt32 isPrimary;
@@ -242,7 +77,7 @@ typedef enum {
} GHOST_MouseCaptureEventWin32;
/**
- * GHOST window on M$ Windows OSs.
+ * GHOST window on MS Windows OSs.
*/
class GHOST_WindowWin32 : public GHOST_Window {
public:
@@ -555,7 +390,7 @@ class GHOST_WindowWin32 : public GHOST_Window {
/* Wintab API */
struct {
- /** WinTab DLL handle. */
+ /** `WinTab.dll` handle. */
HMODULE handle = NULL;
/** API functions */
@@ -574,11 +409,8 @@ class GHOST_WindowWin32 : public GHOST_Window {
GHOST_TWindowState m_normal_state;
- /** user32 dll handle*/
+ /** `user32.dll` handle */
HMODULE m_user32;
- GHOST_WIN32_GetPointerInfoHistory m_fpGetPointerInfoHistory;
- GHOST_WIN32_GetPointerPenInfoHistory m_fpGetPointerPenInfoHistory;
- GHOST_WIN32_GetPointerTouchInfoHistory m_fpGetPointerTouchInfoHistory;
HWND m_parentWindowHwnd;
diff --git a/intern/ghost/intern/GHOST_XrAction.cpp b/intern/ghost/intern/GHOST_XrAction.cpp
new file mode 100644
index 00000000000..172ac40c84f
--- /dev/null
+++ b/intern/ghost/intern/GHOST_XrAction.cpp
@@ -0,0 +1,477 @@
+/*
+ * 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.
+ */
+
+/** \file
+ * \ingroup GHOST
+ */
+
+#include <cassert>
+#include <cstring>
+
+#include "GHOST_Types.h"
+
+#include "GHOST_XrException.h"
+#include "GHOST_Xr_intern.h"
+
+#include "GHOST_XrAction.h"
+
+/* -------------------------------------------------------------------- */
+/** \name GHOST_XrActionSpace
+ *
+ * \{ */
+
+GHOST_XrActionSpace::GHOST_XrActionSpace(XrInstance instance,
+ XrSession session,
+ XrAction action,
+ const GHOST_XrActionSpaceInfo &info,
+ uint32_t subaction_idx)
+{
+ const char *subaction_path = info.subaction_paths[subaction_idx];
+ CHECK_XR(xrStringToPath(instance, subaction_path, &m_subaction_path),
+ (std::string("Failed to get user path \"") + subaction_path + "\".").data());
+
+ XrActionSpaceCreateInfo action_space_info{XR_TYPE_ACTION_SPACE_CREATE_INFO};
+ action_space_info.action = action;
+ action_space_info.subactionPath = m_subaction_path;
+ copy_ghost_pose_to_openxr_pose(info.poses[subaction_idx], action_space_info.poseInActionSpace);
+
+ CHECK_XR(xrCreateActionSpace(session, &action_space_info, &m_space),
+ (std::string("Failed to create space \"") + subaction_path + "\" for action \"" +
+ info.action_name + "\".")
+ .data());
+}
+
+GHOST_XrActionSpace::~GHOST_XrActionSpace()
+{
+ if (m_space != XR_NULL_HANDLE) {
+ CHECK_XR_ASSERT(xrDestroySpace(m_space));
+ }
+}
+
+XrSpace GHOST_XrActionSpace::getSpace() const
+{
+ return m_space;
+}
+
+const XrPath &GHOST_XrActionSpace::getSubactionPath() const
+{
+ return m_subaction_path;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name GHOST_XrActionProfile
+ *
+ * \{ */
+
+GHOST_XrActionProfile::GHOST_XrActionProfile(XrInstance instance,
+ XrAction action,
+ const char *profile_path,
+ const GHOST_XrActionBindingInfo &info)
+{
+ CHECK_XR(
+ xrStringToPath(instance, profile_path, &m_profile),
+ (std::string("Failed to get interaction profile path \"") + profile_path + "\".").data());
+
+ /* Create bindings. */
+ XrInteractionProfileSuggestedBinding bindings_info{
+ XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING};
+ bindings_info.interactionProfile = m_profile;
+ bindings_info.countSuggestedBindings = 1;
+
+ for (uint32_t interaction_idx = 0; interaction_idx < info.count_interaction_paths;
+ ++interaction_idx) {
+ const char *interaction_path = info.interaction_paths[interaction_idx];
+ if (m_bindings.find(interaction_path) != m_bindings.end()) {
+ continue;
+ }
+
+ XrActionSuggestedBinding sbinding;
+ sbinding.action = action;
+ CHECK_XR(xrStringToPath(instance, interaction_path, &sbinding.binding),
+ (std::string("Failed to get interaction path \"") + interaction_path + "\".").data());
+ bindings_info.suggestedBindings = &sbinding;
+
+ /* Although the bindings will be re-suggested in GHOST_XrSession::attachActionSets(), it
+ * greatly improves error checking to suggest them here first. */
+ CHECK_XR(xrSuggestInteractionProfileBindings(instance, &bindings_info),
+ (std::string("Failed to create binding for profile \"") + profile_path +
+ "\" and action \"" + info.action_name +
+ "\". Are the profile and action paths correct?")
+ .data());
+
+ m_bindings.insert({interaction_path, sbinding.binding});
+ }
+}
+
+void GHOST_XrActionProfile::getBindings(
+ XrAction action, std::map<XrPath, std::vector<XrActionSuggestedBinding>> &r_bindings) const
+{
+ std::map<XrPath, std::vector<XrActionSuggestedBinding>>::iterator it = r_bindings.find(
+ m_profile);
+ if (it == r_bindings.end()) {
+ it = r_bindings
+ .emplace(std::piecewise_construct, std::make_tuple(m_profile), std::make_tuple())
+ .first;
+ }
+
+ std::vector<XrActionSuggestedBinding> &sbindings = it->second;
+
+ for (auto &[path, binding] : m_bindings) {
+ XrActionSuggestedBinding sbinding;
+ sbinding.action = action;
+ sbinding.binding = binding;
+
+ sbindings.push_back(std::move(sbinding));
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name GHOST_XrAction
+ *
+ * \{ */
+
+GHOST_XrAction::GHOST_XrAction(XrInstance instance,
+ XrActionSet action_set,
+ const GHOST_XrActionInfo &info)
+ : m_type(info.type),
+ m_states(info.states),
+ m_custom_data_(
+ std::make_unique<GHOST_C_CustomDataWrapper>(info.customdata, info.customdata_free_fn))
+{
+ m_subaction_paths.resize(info.count_subaction_paths);
+
+ for (uint32_t i = 0; i < info.count_subaction_paths; ++i) {
+ CHECK_XR(xrStringToPath(instance, info.subaction_paths[i], &m_subaction_paths[i]),
+ (std::string("Failed to get user path \"") + info.subaction_paths[i] + "\".").data());
+ }
+
+ XrActionCreateInfo action_info{XR_TYPE_ACTION_CREATE_INFO};
+ strcpy(action_info.actionName, info.name);
+ strcpy(action_info.localizedActionName, info.name); /* Just use same name for localized. This can
+ be changed in the future if necessary. */
+
+ switch (info.type) {
+ case GHOST_kXrActionTypeBooleanInput:
+ action_info.actionType = XR_ACTION_TYPE_BOOLEAN_INPUT;
+ break;
+ case GHOST_kXrActionTypeFloatInput:
+ action_info.actionType = XR_ACTION_TYPE_FLOAT_INPUT;
+ break;
+ case GHOST_kXrActionTypeVector2fInput:
+ action_info.actionType = XR_ACTION_TYPE_VECTOR2F_INPUT;
+ break;
+ case GHOST_kXrActionTypePoseInput:
+ action_info.actionType = XR_ACTION_TYPE_POSE_INPUT;
+ break;
+ case GHOST_kXrActionTypeVibrationOutput:
+ action_info.actionType = XR_ACTION_TYPE_VIBRATION_OUTPUT;
+ break;
+ }
+ action_info.countSubactionPaths = info.count_subaction_paths;
+ action_info.subactionPaths = m_subaction_paths.data();
+
+ CHECK_XR(xrCreateAction(action_set, &action_info, &m_action),
+ (std::string("Failed to create action \"") + info.name +
+ "\". Action name and/or paths are invalid. Name must not contain upper "
+ "case letters or special characters other than '-', '_', or '.'.")
+ .data());
+}
+
+GHOST_XrAction::~GHOST_XrAction()
+{
+ if (m_action != XR_NULL_HANDLE) {
+ CHECK_XR_ASSERT(xrDestroyAction(m_action));
+ }
+}
+
+bool GHOST_XrAction::createSpace(XrInstance instance,
+ XrSession session,
+ const GHOST_XrActionSpaceInfo &info)
+{
+ uint32_t subaction_idx = 0;
+ for (; subaction_idx < info.count_subaction_paths; ++subaction_idx) {
+ if (m_spaces.find(info.subaction_paths[subaction_idx]) != m_spaces.end()) {
+ return false;
+ }
+ }
+
+ for (subaction_idx = 0; subaction_idx < info.count_subaction_paths; ++subaction_idx) {
+ m_spaces.emplace(std::piecewise_construct,
+ std::make_tuple(info.subaction_paths[subaction_idx]),
+ std::make_tuple(instance, session, m_action, info, subaction_idx));
+ }
+
+ return true;
+}
+
+void GHOST_XrAction::destroySpace(const char *subaction_path)
+{
+ if (m_spaces.find(subaction_path) != m_spaces.end()) {
+ m_spaces.erase(subaction_path);
+ }
+}
+
+bool GHOST_XrAction::createBinding(XrInstance instance,
+ const char *profile_path,
+ const GHOST_XrActionBindingInfo &info)
+{
+ if (m_profiles.find(profile_path) != m_profiles.end()) {
+ return false;
+ }
+
+ m_profiles.emplace(std::piecewise_construct,
+ std::make_tuple(profile_path),
+ std::make_tuple(instance, m_action, profile_path, info));
+
+ return true;
+}
+
+void GHOST_XrAction::destroyBinding(const char *interaction_profile_path)
+{
+ if (m_profiles.find(interaction_profile_path) != m_profiles.end()) {
+ m_profiles.erase(interaction_profile_path);
+ }
+}
+
+void GHOST_XrAction::updateState(XrSession session,
+ const char *action_name,
+ XrSpace reference_space,
+ const XrTime &predicted_display_time)
+{
+ XrActionStateGetInfo state_info{XR_TYPE_ACTION_STATE_GET_INFO};
+ state_info.action = m_action;
+
+ const size_t count_subaction_paths = m_subaction_paths.size();
+ for (size_t subaction_idx = 0; subaction_idx < count_subaction_paths; ++subaction_idx) {
+ state_info.subactionPath = m_subaction_paths[subaction_idx];
+
+ switch (m_type) {
+ case GHOST_kXrActionTypeBooleanInput: {
+ XrActionStateBoolean state{XR_TYPE_ACTION_STATE_BOOLEAN};
+ CHECK_XR(xrGetActionStateBoolean(session, &state_info, &state),
+ (std::string("Failed to get state for boolean action \"") + action_name + "\".")
+ .data());
+ if (state.isActive) {
+ ((bool *)m_states)[subaction_idx] = state.currentState;
+ }
+ break;
+ }
+ case GHOST_kXrActionTypeFloatInput: {
+ XrActionStateFloat state{XR_TYPE_ACTION_STATE_FLOAT};
+ CHECK_XR(
+ xrGetActionStateFloat(session, &state_info, &state),
+ (std::string("Failed to get state for float action \"") + action_name + "\".").data());
+ if (state.isActive) {
+ ((float *)m_states)[subaction_idx] = state.currentState;
+ }
+ break;
+ }
+ case GHOST_kXrActionTypeVector2fInput: {
+ XrActionStateVector2f state{XR_TYPE_ACTION_STATE_VECTOR2F};
+ CHECK_XR(xrGetActionStateVector2f(session, &state_info, &state),
+ (std::string("Failed to get state for vector2f action \"") + action_name + "\".")
+ .data());
+ if (state.isActive) {
+ memcpy(((float(*)[2])m_states)[subaction_idx], &state.currentState, sizeof(float[2]));
+ }
+ break;
+ }
+ case GHOST_kXrActionTypePoseInput: {
+ XrActionStatePose state{XR_TYPE_ACTION_STATE_POSE};
+ CHECK_XR(
+ xrGetActionStatePose(session, &state_info, &state),
+ (std::string("Failed to get state for pose action \"") + action_name + "\".").data());
+ if (state.isActive) {
+ XrSpace pose_space = XR_NULL_HANDLE;
+ for (auto &[path, space] : m_spaces) {
+ if (space.getSubactionPath() == state_info.subactionPath) {
+ pose_space = space.getSpace();
+ break;
+ }
+ }
+
+ if (pose_space != XR_NULL_HANDLE) {
+ XrSpaceLocation space_location{XR_TYPE_SPACE_LOCATION};
+ CHECK_XR(
+ xrLocateSpace(
+ pose_space, reference_space, predicted_display_time, &space_location),
+ (std::string("Failed to query pose space for action \"") + action_name + "\".")
+ .data());
+ copy_openxr_pose_to_ghost_pose(space_location.pose,
+ ((GHOST_XrPose *)m_states)[subaction_idx]);
+ }
+ }
+ break;
+ }
+ case GHOST_kXrActionTypeVibrationOutput: {
+ break;
+ }
+ }
+ }
+}
+
+void GHOST_XrAction::applyHapticFeedback(XrSession session,
+ const char *action_name,
+ const GHOST_TInt64 &duration,
+ const float &frequency,
+ const float &amplitude)
+{
+ XrHapticVibration vibration{XR_TYPE_HAPTIC_VIBRATION};
+ vibration.duration = (duration == 0) ? XR_MIN_HAPTIC_DURATION :
+ static_cast<XrDuration>(duration);
+ vibration.frequency = frequency;
+ vibration.amplitude = amplitude;
+
+ XrHapticActionInfo haptic_info{XR_TYPE_HAPTIC_ACTION_INFO};
+ haptic_info.action = m_action;
+
+ for (std::vector<XrPath>::iterator it = m_subaction_paths.begin(); it != m_subaction_paths.end();
+ ++it) {
+ haptic_info.subactionPath = *it;
+ CHECK_XR(xrApplyHapticFeedback(session, &haptic_info, (const XrHapticBaseHeader *)&vibration),
+ (std::string("Failed to apply haptic action \"") + action_name + "\".").data());
+ }
+}
+
+void GHOST_XrAction::stopHapticFeedback(XrSession session, const char *action_name)
+{
+ XrHapticActionInfo haptic_info{XR_TYPE_HAPTIC_ACTION_INFO};
+ haptic_info.action = m_action;
+
+ for (std::vector<XrPath>::iterator it = m_subaction_paths.begin(); it != m_subaction_paths.end();
+ ++it) {
+ haptic_info.subactionPath = *it;
+ CHECK_XR(xrStopHapticFeedback(session, &haptic_info),
+ (std::string("Failed to stop haptic action \"") + action_name + "\".").data());
+ }
+}
+
+void *GHOST_XrAction::getCustomdata()
+{
+ if (m_custom_data_ == nullptr) {
+ return nullptr;
+ }
+ return m_custom_data_->custom_data_;
+}
+
+void GHOST_XrAction::getBindings(
+ std::map<XrPath, std::vector<XrActionSuggestedBinding>> &r_bindings) const
+{
+ for (auto &[path, profile] : m_profiles) {
+ profile.getBindings(m_action, r_bindings);
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name GHOST_XrActionSet
+ *
+ * \{ */
+
+GHOST_XrActionSet::GHOST_XrActionSet(XrInstance instance, const GHOST_XrActionSetInfo &info)
+ : m_custom_data_(
+ std::make_unique<GHOST_C_CustomDataWrapper>(info.customdata, info.customdata_free_fn))
+{
+ XrActionSetCreateInfo action_set_info{XR_TYPE_ACTION_SET_CREATE_INFO};
+ strcpy(action_set_info.actionSetName, info.name);
+ strcpy(action_set_info.localizedActionSetName,
+ info.name); /* Just use same name for localized. This can be changed in the future if
+ necessary. */
+ action_set_info.priority = 0; /* Use same (default) priority for all action sets. */
+
+ CHECK_XR(xrCreateActionSet(instance, &action_set_info, &m_action_set),
+ (std::string("Failed to create action set \"") + info.name +
+ "\". Name must not contain upper case letters or special characters "
+ "other than '-', '_', or '.'.")
+ .data());
+}
+
+GHOST_XrActionSet::~GHOST_XrActionSet()
+{
+ /* This needs to be done before xrDestroyActionSet() to avoid an assertion in the GHOST_XrAction
+ * destructor (which calls xrDestroyAction()). */
+ m_actions.clear();
+
+ if (m_action_set != XR_NULL_HANDLE) {
+ CHECK_XR_ASSERT(xrDestroyActionSet(m_action_set));
+ }
+}
+
+bool GHOST_XrActionSet::createAction(XrInstance instance, const GHOST_XrActionInfo &info)
+{
+ if (m_actions.find(info.name) != m_actions.end()) {
+ return false;
+ }
+
+ m_actions.emplace(std::piecewise_construct,
+ std::make_tuple(info.name),
+ std::make_tuple(instance, m_action_set, info));
+
+ return true;
+}
+
+void GHOST_XrActionSet::destroyAction(const char *action_name)
+{
+ if (m_actions.find(action_name) != m_actions.end()) {
+ m_actions.erase(action_name);
+ }
+}
+
+GHOST_XrAction *GHOST_XrActionSet::findAction(const char *action_name)
+{
+ std::map<std::string, GHOST_XrAction>::iterator it = m_actions.find(action_name);
+ if (it == m_actions.end()) {
+ return nullptr;
+ }
+ return &it->second;
+}
+
+void GHOST_XrActionSet::updateStates(XrSession session,
+ XrSpace reference_space,
+ const XrTime &predicted_display_time)
+{
+ for (auto &[name, action] : m_actions) {
+ action.updateState(session, name.data(), reference_space, predicted_display_time);
+ }
+}
+
+XrActionSet GHOST_XrActionSet::getActionSet() const
+{
+ return m_action_set;
+}
+
+void *GHOST_XrActionSet::getCustomdata()
+{
+ if (m_custom_data_ == nullptr) {
+ return nullptr;
+ }
+ return m_custom_data_->custom_data_;
+}
+
+void GHOST_XrActionSet::getBindings(
+ std::map<XrPath, std::vector<XrActionSuggestedBinding>> &r_bindings) const
+{
+ for (auto &[name, action] : m_actions) {
+ action.getBindings(r_bindings);
+ }
+}
+
+/** \} */
diff --git a/intern/ghost/intern/GHOST_XrAction.h b/intern/ghost/intern/GHOST_XrAction.h
new file mode 100644
index 00000000000..bdc6cafb4a9
--- /dev/null
+++ b/intern/ghost/intern/GHOST_XrAction.h
@@ -0,0 +1,145 @@
+/*
+ * 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.
+ */
+
+/** \file
+ * \ingroup GHOST
+ */
+
+/* Note: Requires OpenXR headers to be included before this one for OpenXR types (XrSpace, XrPath,
+ * etc.). */
+
+#pragma once
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include "GHOST_Util.h"
+
+/* -------------------------------------------------------------------- */
+
+class GHOST_XrActionSpace {
+ public:
+ GHOST_XrActionSpace() = delete; /* Default constructor for map storage. */
+ GHOST_XrActionSpace(XrInstance instance,
+ XrSession session,
+ XrAction action,
+ const GHOST_XrActionSpaceInfo &info,
+ uint32_t subaction_idx);
+ ~GHOST_XrActionSpace();
+
+ XrSpace getSpace() const;
+ const XrPath &getSubactionPath() const;
+
+ private:
+ XrSpace m_space = XR_NULL_HANDLE;
+ XrPath m_subaction_path = XR_NULL_PATH;
+};
+
+/* -------------------------------------------------------------------- */
+
+class GHOST_XrActionProfile {
+ public:
+ GHOST_XrActionProfile() = delete; /* Default constructor for map storage. */
+ GHOST_XrActionProfile(XrInstance instance,
+ XrAction action,
+ const char *profile_path,
+ const GHOST_XrActionBindingInfo &info);
+ ~GHOST_XrActionProfile() = default;
+
+ void getBindings(XrAction action,
+ std::map<XrPath, std::vector<XrActionSuggestedBinding>> &r_bindings) const;
+
+ private:
+ XrPath m_profile = XR_NULL_PATH;
+ /* Bindings identified by interaction (user (subaction) + component) path. */
+ std::map<std::string, XrPath> m_bindings;
+};
+
+/* -------------------------------------------------------------------- */
+
+class GHOST_XrAction {
+ public:
+ GHOST_XrAction() = delete; /* Default constructor for map storage. */
+ GHOST_XrAction(XrInstance instance, XrActionSet action_set, const GHOST_XrActionInfo &info);
+ ~GHOST_XrAction();
+
+ bool createSpace(XrInstance instance, XrSession session, const GHOST_XrActionSpaceInfo &info);
+ void destroySpace(const char *subaction_path);
+
+ bool createBinding(XrInstance instance,
+ const char *profile_path,
+ const GHOST_XrActionBindingInfo &info);
+ void destroyBinding(const char *profile_path);
+
+ void updateState(XrSession session,
+ const char *action_name,
+ XrSpace reference_space,
+ const XrTime &predicted_display_time);
+ void applyHapticFeedback(XrSession session,
+ const char *action_name,
+ const GHOST_TInt64 &duration,
+ const float &frequency,
+ const float &amplitude);
+ void stopHapticFeedback(XrSession session, const char *action_name);
+
+ void *getCustomdata();
+ void getBindings(std::map<XrPath, std::vector<XrActionSuggestedBinding>> &r_bindings) const;
+
+ private:
+ XrAction m_action = XR_NULL_HANDLE;
+ GHOST_XrActionType m_type;
+ std::vector<XrPath> m_subaction_paths;
+ /** States for each subaction path. */
+ void *m_states;
+
+ std::unique_ptr<GHOST_C_CustomDataWrapper> m_custom_data_ = nullptr; /* wmXrAction */
+
+ /* Spaces identified by user (subaction) path. */
+ std::map<std::string, GHOST_XrActionSpace> m_spaces;
+ /* Profiles identified by interaction profile path. */
+ std::map<std::string, GHOST_XrActionProfile> m_profiles;
+};
+
+/* -------------------------------------------------------------------- */
+
+class GHOST_XrActionSet {
+ public:
+ GHOST_XrActionSet() = delete; /* Default constructor for map storage. */
+ GHOST_XrActionSet(XrInstance instance, const GHOST_XrActionSetInfo &info);
+ ~GHOST_XrActionSet();
+
+ bool createAction(XrInstance instance, const GHOST_XrActionInfo &info);
+ void destroyAction(const char *action_name);
+ GHOST_XrAction *findAction(const char *action_name);
+
+ void updateStates(XrSession session,
+ XrSpace reference_space,
+ const XrTime &predicted_display_time);
+
+ XrActionSet getActionSet() const;
+ void *getCustomdata();
+ void getBindings(std::map<XrPath, std::vector<XrActionSuggestedBinding>> &r_bindings) const;
+
+ private:
+ XrActionSet m_action_set = XR_NULL_HANDLE;
+
+ std::unique_ptr<GHOST_C_CustomDataWrapper> m_custom_data_ = nullptr; /* wmXrActionSet */
+
+ std::map<std::string, GHOST_XrAction> m_actions;
+};
+
+/* -------------------------------------------------------------------- */
diff --git a/intern/ghost/intern/GHOST_XrContext.cpp b/intern/ghost/intern/GHOST_XrContext.cpp
index 2bf67c121f8..daad0b8190a 100644
--- a/intern/ghost/intern/GHOST_XrContext.cpp
+++ b/intern/ghost/intern/GHOST_XrContext.cpp
@@ -55,7 +55,6 @@ void *GHOST_XrContext::s_error_handler_customdata = nullptr;
/* -------------------------------------------------------------------- */
/** \name Create, Initialize and Destruct
- *
* \{ */
GHOST_XrContext::GHOST_XrContext(const GHOST_XrContextCreateInfo *create_info)
@@ -153,7 +152,6 @@ void GHOST_XrContext::storeInstanceProperties()
/* -------------------------------------------------------------------- */
/** \name Debug Printing
- *
* \{ */
void GHOST_XrContext::printInstanceInfo()
@@ -242,14 +240,13 @@ void GHOST_XrContext::initDebugMessenger()
/* -------------------------------------------------------------------- */
/** \name Error handling
- *
* \{ */
void GHOST_XrContext::dispatchErrorMessage(const GHOST_XrException *exception) const
{
GHOST_XrError error;
- error.user_message = exception->m_msg;
+ error.user_message = exception->m_msg.data();
error.customdata = s_error_handler_customdata;
if (isDebugMode()) {
@@ -273,7 +270,6 @@ void GHOST_XrContext::setErrorHandler(GHOST_XrErrorHandlerFn handler_fn, void *c
/* -------------------------------------------------------------------- */
/** \name OpenXR API-Layers and Extensions
- *
* \{ */
/**
@@ -378,7 +374,7 @@ void GHOST_XrContext::getAPILayersToEnable(std::vector<const char *> &r_ext_name
for (const std::string &layer : try_layers) {
if (openxr_layer_is_available(m_oxr->layers, layer)) {
- r_ext_names.push_back(layer.c_str());
+ r_ext_names.push_back(layer.data());
}
}
}
@@ -488,6 +484,7 @@ GHOST_TXrGraphicsBinding GHOST_XrContext::determineGraphicsBindingTypeToUse(
void GHOST_XrContext::startSession(const GHOST_XrSessionBeginInfo *begin_info)
{
+ m_custom_funcs.session_create_fn = begin_info->create_fn;
m_custom_funcs.session_exit_fn = begin_info->exit_fn;
m_custom_funcs.session_exit_customdata = begin_info->exit_customdata;
@@ -538,6 +535,16 @@ void GHOST_XrContext::handleSessionStateChange(const XrEventDataSessionStateChan
* Public as in, exposed in the Ghost API.
* \{ */
+GHOST_XrSession *GHOST_XrContext::getSession()
+{
+ return m_session.get();
+}
+
+const GHOST_XrSession *GHOST_XrContext::getSession() const
+{
+ return m_session.get();
+}
+
void GHOST_XrContext::setGraphicsContextBindFuncs(GHOST_XrGraphicsContextBindFn bind_fn,
GHOST_XrGraphicsContextUnbindFn unbind_fn)
{
@@ -564,7 +571,6 @@ bool GHOST_XrContext::needsUpsideDownDrawing() const
/* -------------------------------------------------------------------- */
/** \name Ghost Internal Accessors and Mutators
- *
* \{ */
GHOST_TXrOpenXRRuntimeID GHOST_XrContext::getOpenXRRuntimeID() const
diff --git a/intern/ghost/intern/GHOST_XrContext.h b/intern/ghost/intern/GHOST_XrContext.h
index 59c7786ed7a..f29d7349f7e 100644
--- a/intern/ghost/intern/GHOST_XrContext.h
+++ b/intern/ghost/intern/GHOST_XrContext.h
@@ -35,6 +35,7 @@ struct GHOST_XrCustomFuncs {
/** Function to release (possibly free) a graphics context. */
GHOST_XrGraphicsContextUnbindFn gpu_ctx_unbind_fn = nullptr;
+ GHOST_XrSessionCreateFn session_create_fn = nullptr;
GHOST_XrSessionExitFn session_exit_fn = nullptr;
void *session_exit_customdata = nullptr;
@@ -72,6 +73,10 @@ class GHOST_XrContext : public GHOST_IXrContext {
bool isSessionRunning() const override;
void drawSessionViews(void *draw_customdata) override;
+ /** Needed for the GHOST C api. */
+ GHOST_XrSession *getSession() override;
+ const GHOST_XrSession *getSession() const override;
+
static void setErrorHandler(GHOST_XrErrorHandlerFn handler_fn, void *customdata);
void dispatchErrorMessage(const class GHOST_XrException *exception) const override;
diff --git a/intern/ghost/intern/GHOST_XrException.h b/intern/ghost/intern/GHOST_XrException.h
index 30c33eaf98f..e93164e04c8 100644
--- a/intern/ghost/intern/GHOST_XrException.h
+++ b/intern/ghost/intern/GHOST_XrException.h
@@ -21,6 +21,7 @@
#pragma once
#include <exception>
+#include <string>
class GHOST_XrException : public std::exception {
friend class GHOST_XrContext;
@@ -33,10 +34,10 @@ class GHOST_XrException : public std::exception {
const char *what() const noexcept override
{
- return m_msg;
+ return m_msg.data();
}
private:
- const char *m_msg;
+ std::string m_msg;
int m_result;
};
diff --git a/intern/ghost/intern/GHOST_XrSession.cpp b/intern/ghost/intern/GHOST_XrSession.cpp
index a1fd7fc2781..a7438fae13c 100644
--- a/intern/ghost/intern/GHOST_XrSession.cpp
+++ b/intern/ghost/intern/GHOST_XrSession.cpp
@@ -28,6 +28,7 @@
#include "GHOST_C-api.h"
#include "GHOST_IXrGraphicsBinding.h"
+#include "GHOST_XrAction.h"
#include "GHOST_XrContext.h"
#include "GHOST_XrException.h"
#include "GHOST_XrSwapchain.h"
@@ -46,6 +47,8 @@ struct OpenXRSessionData {
XrSpace view_space;
std::vector<XrView> views;
std::vector<GHOST_XrSwapchain> swapchains;
+
+ std::map<std::string, GHOST_XrActionSet> action_sets;
};
struct GHOST_XrDrawInfo {
@@ -59,7 +62,6 @@ struct GHOST_XrDrawInfo {
/* -------------------------------------------------------------------- */
/** \name Create, Initialize and Destruct
- *
* \{ */
GHOST_XrSession::GHOST_XrSession(GHOST_XrContext &xr_context)
@@ -72,6 +74,7 @@ GHOST_XrSession::~GHOST_XrSession()
unbindGraphicsContext();
m_oxr->swapchains.clear();
+ m_oxr->action_sets.clear();
if (m_oxr->reference_space != XR_NULL_HANDLE) {
CHECK_XR_ASSERT(xrDestroySpace(m_oxr->reference_space));
@@ -110,7 +113,6 @@ void GHOST_XrSession::initSystem()
/* -------------------------------------------------------------------- */
/** \name State Management
- *
* \{ */
static void create_reference_spaces(OpenXRSessionData &oxr, const GHOST_XrPose &base_pose)
@@ -179,7 +181,7 @@ void GHOST_XrSession::start(const GHOST_XrSessionBeginInfo *begin_info)
std::ostringstream strstream;
strstream << "Available graphics context version does not meet the following requirements: "
<< requirement_str;
- throw GHOST_XrException(strstream.str().c_str());
+ throw GHOST_XrException(strstream.str().data());
}
m_gpu_binding->initFromGhostContext(*m_gpu_ctx);
@@ -196,6 +198,9 @@ void GHOST_XrSession::start(const GHOST_XrSessionBeginInfo *begin_info)
prepareDrawing();
create_reference_spaces(*m_oxr, begin_info->base_pose);
+
+ /* Create and bind actions here. */
+ m_context->getCustomFuncs().session_create_fn();
}
void GHOST_XrSession::requestEnd()
@@ -225,10 +230,9 @@ GHOST_XrSession::LifeExpectancy GHOST_XrSession::handleStateChangeEvent(
assert(m_oxr->session == XR_NULL_HANDLE || m_oxr->session == lifecycle.session);
switch (lifecycle.state) {
- case XR_SESSION_STATE_READY: {
+ case XR_SESSION_STATE_READY:
beginSession();
break;
- }
case XR_SESSION_STATE_STOPPING:
endSession();
break;
@@ -245,7 +249,6 @@ GHOST_XrSession::LifeExpectancy GHOST_XrSession::handleStateChangeEvent(
/* -------------------------------------------------------------------- */
/** \name Drawing
- *
* \{ */
void GHOST_XrSession::prepareDrawing()
@@ -352,18 +355,6 @@ void GHOST_XrSession::draw(void *draw_customdata)
endFrameDrawing(layers);
}
-static void copy_openxr_pose_to_ghost_pose(const XrPosef &oxr_pose, GHOST_XrPose &r_ghost_pose)
-{
- /* Set and convert to Blender coordinate space. */
- r_ghost_pose.position[0] = oxr_pose.position.x;
- r_ghost_pose.position[1] = oxr_pose.position.y;
- r_ghost_pose.position[2] = oxr_pose.position.z;
- r_ghost_pose.orientation_quat[0] = oxr_pose.orientation.w;
- r_ghost_pose.orientation_quat[1] = oxr_pose.orientation.x;
- r_ghost_pose.orientation_quat[2] = oxr_pose.orientation.y;
- r_ghost_pose.orientation_quat[3] = oxr_pose.orientation.z;
-}
-
static void ghost_xr_draw_view_info_from_view(const XrView &view, GHOST_XrDrawViewInfo &r_info)
{
/* Set and convert to Blender coordinate space. */
@@ -457,7 +448,6 @@ bool GHOST_XrSession::needsUpsideDownDrawing() const
/* -------------------------------------------------------------------- */
/** \name State Queries
- *
* \{ */
bool GHOST_XrSession::isRunning() const
@@ -505,3 +495,340 @@ void GHOST_XrSession::unbindGraphicsContext()
}
/** \} */ /* Graphics Context Injection */
+
+/* -------------------------------------------------------------------- */
+/** \name Actions
+ *
+ * \{ */
+
+static GHOST_XrActionSet *find_action_set(OpenXRSessionData *oxr, const char *action_set_name)
+{
+ std::map<std::string, GHOST_XrActionSet>::iterator it = oxr->action_sets.find(action_set_name);
+ if (it == oxr->action_sets.end()) {
+ return nullptr;
+ }
+ return &it->second;
+}
+
+bool GHOST_XrSession::createActionSet(const GHOST_XrActionSetInfo &info)
+{
+ std::map<std::string, GHOST_XrActionSet> &action_sets = m_oxr->action_sets;
+ if (action_sets.find(info.name) != action_sets.end()) {
+ return false;
+ }
+
+ XrInstance instance = m_context->getInstance();
+
+ action_sets.emplace(
+ std::piecewise_construct, std::make_tuple(info.name), std::make_tuple(instance, info));
+
+ return true;
+}
+
+void GHOST_XrSession::destroyActionSet(const char *action_set_name)
+{
+ std::map<std::string, GHOST_XrActionSet> &action_sets = m_oxr->action_sets;
+ if (action_sets.find(action_set_name) != action_sets.end()) {
+ action_sets.erase(action_set_name);
+ }
+}
+
+bool GHOST_XrSession::createActions(const char *action_set_name,
+ uint32_t count,
+ const GHOST_XrActionInfo *infos)
+{
+ GHOST_XrActionSet *action_set = find_action_set(m_oxr.get(), action_set_name);
+ if (action_set == nullptr) {
+ return false;
+ }
+
+ XrInstance instance = m_context->getInstance();
+
+ for (uint32_t i = 0; i < count; ++i) {
+ if (!action_set->createAction(instance, infos[i])) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void GHOST_XrSession::destroyActions(const char *action_set_name,
+ uint32_t count,
+ const char *const *action_names)
+{
+ GHOST_XrActionSet *action_set = find_action_set(m_oxr.get(), action_set_name);
+ if (action_set == nullptr) {
+ return;
+ }
+
+ for (uint32_t i = 0; i < count; ++i) {
+ action_set->destroyAction(action_names[i]);
+ }
+}
+
+bool GHOST_XrSession::createActionSpaces(const char *action_set_name,
+ uint32_t count,
+ const GHOST_XrActionSpaceInfo *infos)
+{
+ GHOST_XrActionSet *action_set = find_action_set(m_oxr.get(), action_set_name);
+ if (action_set == nullptr) {
+ return false;
+ }
+
+ XrInstance instance = m_context->getInstance();
+ XrSession session = m_oxr->session;
+
+ for (uint32_t action_idx = 0; action_idx < count; ++action_idx) {
+ const GHOST_XrActionSpaceInfo &info = infos[action_idx];
+
+ GHOST_XrAction *action = action_set->findAction(info.action_name);
+ if (action == nullptr) {
+ continue;
+ }
+
+ if (!action->createSpace(instance, session, info)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void GHOST_XrSession::destroyActionSpaces(const char *action_set_name,
+ uint32_t count,
+ const GHOST_XrActionSpaceInfo *infos)
+{
+ GHOST_XrActionSet *action_set = find_action_set(m_oxr.get(), action_set_name);
+ if (action_set == nullptr) {
+ return;
+ }
+
+ for (uint32_t action_idx = 0; action_idx < count; ++action_idx) {
+ const GHOST_XrActionSpaceInfo &info = infos[action_idx];
+
+ GHOST_XrAction *action = action_set->findAction(info.action_name);
+ if (action == nullptr) {
+ continue;
+ }
+
+ for (uint32_t subaction_idx = 0; subaction_idx < info.count_subaction_paths; ++subaction_idx) {
+ action->destroySpace(info.subaction_paths[subaction_idx]);
+ }
+ }
+}
+
+bool GHOST_XrSession::createActionBindings(const char *action_set_name,
+ uint32_t count,
+ const GHOST_XrActionProfileInfo *infos)
+{
+ GHOST_XrActionSet *action_set = find_action_set(m_oxr.get(), action_set_name);
+ if (action_set == nullptr) {
+ return false;
+ }
+
+ XrInstance instance = m_context->getInstance();
+
+ for (uint32_t profile_idx = 0; profile_idx < count; ++profile_idx) {
+ const GHOST_XrActionProfileInfo &info = infos[profile_idx];
+ const char *profile_path = info.profile_path;
+
+ for (uint32_t binding_idx = 0; binding_idx < info.count_bindings; ++binding_idx) {
+ const GHOST_XrActionBindingInfo &binding = info.bindings[binding_idx];
+
+ GHOST_XrAction *action = action_set->findAction(binding.action_name);
+ if (action == nullptr) {
+ continue;
+ }
+
+ action->createBinding(instance, profile_path, binding);
+ }
+ }
+
+ return true;
+}
+
+void GHOST_XrSession::destroyActionBindings(const char *action_set_name,
+ uint32_t count,
+ const GHOST_XrActionProfileInfo *infos)
+{
+ GHOST_XrActionSet *action_set = find_action_set(m_oxr.get(), action_set_name);
+ if (action_set == nullptr) {
+ return;
+ }
+
+ for (uint32_t profile_idx = 0; profile_idx < count; ++profile_idx) {
+ const GHOST_XrActionProfileInfo &info = infos[profile_idx];
+ const char *profile_path = info.profile_path;
+
+ for (uint32_t binding_idx = 0; binding_idx < info.count_bindings; ++binding_idx) {
+ const GHOST_XrActionBindingInfo &binding = info.bindings[binding_idx];
+
+ GHOST_XrAction *action = action_set->findAction(binding.action_name);
+ if (action == nullptr) {
+ continue;
+ }
+
+ action->destroyBinding(profile_path);
+ }
+ }
+}
+
+bool GHOST_XrSession::attachActionSets()
+{
+ /* Suggest action bindings for all action sets. */
+ std::map<XrPath, std::vector<XrActionSuggestedBinding>> profile_bindings;
+ for (auto &[name, action_set] : m_oxr->action_sets) {
+ action_set.getBindings(profile_bindings);
+ }
+
+ if (profile_bindings.size() < 1) {
+ return false;
+ }
+
+ XrInteractionProfileSuggestedBinding bindings_info{
+ XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING};
+ XrInstance instance = m_context->getInstance();
+
+ for (auto &[profile, bindings] : profile_bindings) {
+ bindings_info.interactionProfile = profile;
+ bindings_info.countSuggestedBindings = (uint32_t)bindings.size();
+ bindings_info.suggestedBindings = bindings.data();
+
+ CHECK_XR(xrSuggestInteractionProfileBindings(instance, &bindings_info),
+ "Failed to suggest interaction profile bindings.");
+ }
+
+ /* Attach action sets. */
+ XrSessionActionSetsAttachInfo attach_info{XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO};
+ attach_info.countActionSets = (uint32_t)m_oxr->action_sets.size();
+
+ /* Create an aligned copy of the action sets to pass to xrAttachSessionActionSets(). */
+ std::vector<XrActionSet> action_sets(attach_info.countActionSets);
+ uint32_t i = 0;
+ for (auto &[name, action_set] : m_oxr->action_sets) {
+ action_sets[i++] = action_set.getActionSet();
+ }
+ attach_info.actionSets = action_sets.data();
+
+ CHECK_XR(xrAttachSessionActionSets(m_oxr->session, &attach_info),
+ "Failed to attach XR action sets.");
+
+ return true;
+}
+
+bool GHOST_XrSession::syncActions(const char *action_set_name)
+{
+ std::map<std::string, GHOST_XrActionSet> &action_sets = m_oxr->action_sets;
+
+ XrActionsSyncInfo sync_info{XR_TYPE_ACTIONS_SYNC_INFO};
+ sync_info.countActiveActionSets = (action_set_name != nullptr) ? 1 :
+ (uint32_t)action_sets.size();
+ if (sync_info.countActiveActionSets < 1) {
+ return false;
+ }
+
+ std::vector<XrActiveActionSet> active_action_sets(sync_info.countActiveActionSets);
+ GHOST_XrActionSet *action_set = nullptr;
+
+ if (action_set_name != nullptr) {
+ action_set = find_action_set(m_oxr.get(), action_set_name);
+ if (action_set == nullptr) {
+ return false;
+ }
+
+ XrActiveActionSet &active_action_set = active_action_sets[0];
+ active_action_set.actionSet = action_set->getActionSet();
+ active_action_set.subactionPath = XR_NULL_PATH;
+ }
+ else {
+ uint32_t i = 0;
+ for (auto &[name, action_set] : action_sets) {
+ XrActiveActionSet &active_action_set = active_action_sets[i++];
+ active_action_set.actionSet = action_set.getActionSet();
+ active_action_set.subactionPath = XR_NULL_PATH;
+ }
+ }
+ sync_info.activeActionSets = active_action_sets.data();
+
+ CHECK_XR(xrSyncActions(m_oxr->session, &sync_info), "Failed to synchronize XR actions.");
+
+ /* Update action states (i.e. Blender custom data). */
+ XrSession session = m_oxr->session;
+ XrSpace reference_space = m_oxr->reference_space;
+ const XrTime &predicted_display_time = m_draw_info->frame_state.predictedDisplayTime;
+
+ if (action_set != nullptr) {
+ action_set->updateStates(session, reference_space, predicted_display_time);
+ }
+ else {
+ for (auto &[name, action_set] : action_sets) {
+ action_set.updateStates(session, reference_space, predicted_display_time);
+ }
+ }
+
+ return true;
+}
+
+bool GHOST_XrSession::applyHapticAction(const char *action_set_name,
+ const char *action_name,
+ const GHOST_TInt64 &duration,
+ const float &frequency,
+ const float &amplitude)
+{
+ GHOST_XrActionSet *action_set = find_action_set(m_oxr.get(), action_set_name);
+ if (action_set == nullptr) {
+ return false;
+ }
+
+ GHOST_XrAction *action = action_set->findAction(action_name);
+ if (action == nullptr) {
+ return false;
+ }
+
+ action->applyHapticFeedback(m_oxr->session, action_name, duration, frequency, amplitude);
+
+ return true;
+}
+
+void GHOST_XrSession::stopHapticAction(const char *action_set_name, const char *action_name)
+{
+ GHOST_XrActionSet *action_set = find_action_set(m_oxr.get(), action_set_name);
+ if (action_set == nullptr) {
+ return;
+ }
+
+ GHOST_XrAction *action = action_set->findAction(action_name);
+ if (action == nullptr) {
+ return;
+ }
+
+ action->stopHapticFeedback(m_oxr->session, action_name);
+}
+
+void *GHOST_XrSession::getActionSetCustomdata(const char *action_set_name)
+{
+ GHOST_XrActionSet *action_set = find_action_set(m_oxr.get(), action_set_name);
+ if (action_set == nullptr) {
+ return nullptr;
+ }
+
+ return action_set->getCustomdata();
+}
+
+void *GHOST_XrSession::getActionCustomdata(const char *action_set_name, const char *action_name)
+{
+ GHOST_XrActionSet *action_set = find_action_set(m_oxr.get(), action_set_name);
+ if (action_set == nullptr) {
+ return nullptr;
+ }
+
+ GHOST_XrAction *action = action_set->findAction(action_name);
+ if (action == nullptr) {
+ return nullptr;
+ }
+
+ return action->getCustomdata();
+}
+
+/** \} */ /* Actions */
diff --git a/intern/ghost/intern/GHOST_XrSession.h b/intern/ghost/intern/GHOST_XrSession.h
index 79a586411e9..d09c78e1ea7 100644
--- a/intern/ghost/intern/GHOST_XrSession.h
+++ b/intern/ghost/intern/GHOST_XrSession.h
@@ -52,6 +52,43 @@ class GHOST_XrSession {
void draw(void *draw_customdata);
+ /** Action functions to be called pre-session start.
+ * Note: The "destroy" functions can also be called post-session start. */
+ bool createActionSet(const GHOST_XrActionSetInfo &info);
+ void destroyActionSet(const char *action_set_name);
+ bool createActions(const char *action_set_name, uint32_t count, const GHOST_XrActionInfo *infos);
+ void destroyActions(const char *action_set_name,
+ uint32_t count,
+ const char *const *action_names);
+ bool createActionSpaces(const char *action_set_name,
+ uint32_t count,
+ const GHOST_XrActionSpaceInfo *infos);
+ void destroyActionSpaces(const char *action_set_name,
+ uint32_t count,
+ const GHOST_XrActionSpaceInfo *infos);
+ bool createActionBindings(const char *action_set_name,
+ uint32_t count,
+ const GHOST_XrActionProfileInfo *infos);
+ void destroyActionBindings(const char *action_set_name,
+ uint32_t count,
+ const GHOST_XrActionProfileInfo *infos);
+ bool attachActionSets();
+
+ /** Action functions to be called post-session start. */
+ bool syncActions(
+ const char *action_set_name = nullptr); /* If action_set_name is nullptr, all attached
+ * action sets will be synced. */
+ bool applyHapticAction(const char *action_set_name,
+ const char *action_name,
+ const GHOST_TInt64 &duration,
+ const float &frequency,
+ const float &amplitude);
+ void stopHapticAction(const char *action_set_name, const char *action_name);
+
+ /* Custom data (owned by Blender, not GHOST) accessors. */
+ void *getActionSetCustomdata(const char *action_set_name);
+ void *getActionCustomdata(const char *action_set_name, const char *action_name);
+
private:
/** Pointer back to context managing this session. Would be nice to avoid, but needed to access
* custom callbacks set before session start. */
diff --git a/intern/ghost/intern/GHOST_Xr_intern.h b/intern/ghost/intern/GHOST_Xr_intern.h
index 137541c4528..0616e426da3 100644
--- a/intern/ghost/intern/GHOST_Xr_intern.h
+++ b/intern/ghost/intern/GHOST_Xr_intern.h
@@ -45,3 +45,27 @@
(void)_res; \
} \
(void)0
+
+inline void copy_ghost_pose_to_openxr_pose(const GHOST_XrPose &ghost_pose, XrPosef &r_oxr_pose)
+{
+ /* Set and convert to OpenXR coordinate space. */
+ r_oxr_pose.position.x = ghost_pose.position[0];
+ r_oxr_pose.position.y = ghost_pose.position[1];
+ r_oxr_pose.position.z = ghost_pose.position[2];
+ r_oxr_pose.orientation.w = ghost_pose.orientation_quat[0];
+ r_oxr_pose.orientation.x = ghost_pose.orientation_quat[1];
+ r_oxr_pose.orientation.y = ghost_pose.orientation_quat[2];
+ r_oxr_pose.orientation.z = ghost_pose.orientation_quat[3];
+}
+
+inline void copy_openxr_pose_to_ghost_pose(const XrPosef &oxr_pose, GHOST_XrPose &r_ghost_pose)
+{
+ /* Set and convert to Blender coordinate space. */
+ r_ghost_pose.position[0] = oxr_pose.position.x;
+ r_ghost_pose.position[1] = oxr_pose.position.y;
+ r_ghost_pose.position[2] = oxr_pose.position.z;
+ r_ghost_pose.orientation_quat[0] = oxr_pose.orientation.w;
+ r_ghost_pose.orientation_quat[1] = oxr_pose.orientation.x;
+ r_ghost_pose.orientation_quat[2] = oxr_pose.orientation.y;
+ r_ghost_pose.orientation_quat[3] = oxr_pose.orientation.z;
+}
diff --git a/intern/ghost/test/gears/GHOST_C-Test.c b/intern/ghost/test/gears/GHOST_C-Test.c
index 4283f990cfb..8cd1b5acb89 100644
--- a/intern/ghost/test/gears/GHOST_C-Test.c
+++ b/intern/ghost/test/gears/GHOST_C-Test.c
@@ -477,7 +477,7 @@ int main(int argc, char **argv)
/* Enter main loop */
while (!sExitRequested) {
- if (!GHOST_ProcessEvents(shSystem, 0)) {
+ if (!GHOST_ProcessEvents(shSystem, false)) {
#ifdef WIN32
/* If there were no events, be nice to other applications */
Sleep(10);
diff --git a/intern/ghost/test/multitest/MultiTest.c b/intern/ghost/test/multitest/MultiTest.c
index 8c8858fc6d8..3b424f1ca89 100644
--- a/intern/ghost/test/multitest/MultiTest.c
+++ b/intern/ghost/test/multitest/MultiTest.c
@@ -926,7 +926,7 @@ void multitestapp_exit(MultiTestApp *app)
void multitestapp_run(MultiTestApp *app)
{
while (!app->exit) {
- GHOST_ProcessEvents(app->sys, 1);
+ GHOST_ProcessEvents(app->sys, true);
GHOST_DispatchEvents(app->sys);
}
}
diff --git a/intern/guardedalloc/MEM_guardedalloc.h b/intern/guardedalloc/MEM_guardedalloc.h
index 3d51c04f929..89cb68010fb 100644
--- a/intern/guardedalloc/MEM_guardedalloc.h
+++ b/intern/guardedalloc/MEM_guardedalloc.h
@@ -57,9 +57,11 @@
extern "C" {
#endif
-/** Returns the length of the allocated memory segment pointed at
+/**
+ * Returns the length of the allocated memory segment pointed at
* by vmemh. If the pointer was not previously allocated by this
- * module, the result is undefined.*/
+ * module, the result is undefined.
+ */
extern size_t (*MEM_allocN_len)(const void *vmemh) ATTR_WARN_UNUSED_RESULT;
/**
@@ -103,7 +105,8 @@ extern void *(*MEM_recallocN_id)(void *vmemh,
/**
* Allocate a block of memory of size len, with tag name str. The
* memory is cleared. The name must be static, because only a
- * pointer to it is stored ! */
+ * pointer to it is stored!
+ */
extern void *(*MEM_callocN)(size_t len, const char *str) /* ATTR_MALLOC */ ATTR_WARN_UNUSED_RESULT
ATTR_ALLOC_SIZE(1) ATTR_NONNULL(2);
@@ -143,12 +146,15 @@ extern void *(*MEM_mallocN_aligned)(size_t len,
const char *str) /* ATTR_MALLOC */ ATTR_WARN_UNUSED_RESULT
ATTR_ALLOC_SIZE(1) ATTR_NONNULL(3);
-/** Print a list of the names and sizes of all allocated memory
- * blocks. as a python dict for easy investigation */
+/**
+ * Print a list of the names and sizes of all allocated memory
+ * blocks. as a python dict for easy investigation.
+ */
extern void (*MEM_printmemlist_pydict)(void);
-/** Print a list of the names and sizes of all allocated memory
- * blocks. */
+/**
+ * Print a list of the names and sizes of all allocated memory blocks.
+ */
extern void (*MEM_printmemlist)(void);
/** calls the function on all allocated memory blocks. */
@@ -163,7 +169,8 @@ extern void (*MEM_set_error_callback)(void (*func)(const char *));
/**
* Are the start/end block markers still correct ?
*
- * \retval true for correct memory, false for corrupted memory. */
+ * \retval true for correct memory, false for corrupted memory.
+ */
extern bool (*MEM_consistency_check)(void);
/** Attempt to enforce OSX (or other OS's) to have malloc and stack nonzero */
@@ -209,8 +216,10 @@ extern size_t (*MEM_get_peak_memory)(void) ATTR_WARN_UNUSED_RESULT;
extern const char *(*MEM_name_ptr)(void *vmemh);
#endif
-/** This should be called as early as possible in the program. When it has been called, information
- * about memory leaks will be printed on exit. */
+/**
+ * This should be called as early as possible in the program. When it has been called, information
+ * about memory leaks will be printed on exit.
+ */
void MEM_init_memleak_detection(void);
/**
@@ -219,9 +228,11 @@ void MEM_init_memleak_detection(void);
*/
void MEM_use_memleak_detection(bool enabled);
-/** When this has been called and memory leaks have been detected, the process will have an exit
+/**
+ * When this has been called and memory leaks have been detected, the process will have an exit
* code that indicates failure. This can be used for when checking for memory leaks with automated
- * tests. */
+ * tests.
+ */
void MEM_enable_fail_on_memleak(void);
/* Switch allocator to fast mode, with less tracking.
diff --git a/intern/itasc/kdl/frames.hpp b/intern/itasc/kdl/frames.hpp
index 6cb336801a9..662eb3e857a 100644
--- a/intern/itasc/kdl/frames.hpp
+++ b/intern/itasc/kdl/frames.hpp
@@ -364,7 +364,7 @@ public:
//! Along an arbitrary axes. It is not necessary to normalize rotaxis.
//! returns identity rotation matrix in the case that the norm of rotaxis
- //! is to small to be used.
+ //! is too small to be used.
// @see Rot2 if you want to handle this error in another way.
static Rotation Rot(const Vector& rotaxis,double angle);
diff --git a/intern/itasc/kdl/utilities/error.h b/intern/itasc/kdl/utilities/error.h
index f2377702b9b..c6ab3ebdc69 100644
--- a/intern/itasc/kdl/utilities/error.h
+++ b/intern/itasc/kdl/utilities/error.h
@@ -161,7 +161,7 @@ class Error_MotionPlanning : public Error {};
class Error_MotionPlanning_Circle_ToSmall : public Error_MotionPlanning {
public:
- virtual const char* Description() const { return "Circle : radius is to small";}
+ virtual const char* Description() const { return "Circle : radius is too small";}
virtual int GetType() const {return 3001;}
};
diff --git a/intern/libmv/libmv/base/scoped_ptr.h b/intern/libmv/libmv/base/scoped_ptr.h
index 9bfcfe1d615..45b5722034c 100644
--- a/intern/libmv/libmv/base/scoped_ptr.h
+++ b/intern/libmv/libmv/base/scoped_ptr.h
@@ -77,7 +77,7 @@ class scoped_array {
void reset(T* new_array) {
if (sizeof(T)) {
- delete array_;
+ delete[] array_;
}
array_ = new_array;
}
diff --git a/intern/libmv/third_party/.clang-format b/intern/libmv/third_party/.clang-format
new file mode 100644
index 00000000000..9d159247d51
--- /dev/null
+++ b/intern/libmv/third_party/.clang-format
@@ -0,0 +1,2 @@
+DisableFormat: true
+SortIncludes: false
diff --git a/intern/mikktspace/README.md b/intern/mikktspace/README.md
new file mode 100644
index 00000000000..9fda1559e44
--- /dev/null
+++ b/intern/mikktspace/README.md
@@ -0,0 +1,4 @@
+# MikkTSpace
+A common standard for tangent space used in baking tools to produce normal maps.
+
+More information can be found at http://www.mikktspace.com/.
diff --git a/intern/opencolorio/ocio_impl_glsl.cc b/intern/opencolorio/ocio_impl_glsl.cc
index 590d7c07002..87bc2842539 100644
--- a/intern/opencolorio/ocio_impl_glsl.cc
+++ b/intern/opencolorio/ocio_impl_glsl.cc
@@ -561,6 +561,7 @@ static OCIO_GPUDisplayShader &getGPUDisplayShader(
GpuShaderDescRcPtr shaderdesc_to_scene_linear = GpuShaderDesc::CreateShaderDesc();
shaderdesc_to_scene_linear->setLanguage(GPU_LANGUAGE_GLSL_1_3);
shaderdesc_to_scene_linear->setFunctionName("OCIO_to_scene_linear");
+ shaderdesc_to_scene_linear->setResourcePrefix("to_scene");
(*(ConstProcessorRcPtr *)processor_to_scene_linear)
->getDefaultGPUProcessor()
->extractGpuShaderInfo(shaderdesc_to_scene_linear);
@@ -569,6 +570,7 @@ static OCIO_GPUDisplayShader &getGPUDisplayShader(
GpuShaderDescRcPtr shaderdesc_to_display = GpuShaderDesc::CreateShaderDesc();
shaderdesc_to_display->setLanguage(GPU_LANGUAGE_GLSL_1_3);
shaderdesc_to_display->setFunctionName("OCIO_to_display");
+ shaderdesc_to_scene_linear->setResourcePrefix("to_display");
(*(ConstProcessorRcPtr *)processor_to_display)
->getDefaultGPUProcessor()
->extractGpuShaderInfo(shaderdesc_to_display);
diff --git a/intern/opensubdiv/internal/topology/mesh_topology.cc b/intern/opensubdiv/internal/topology/mesh_topology.cc
index 9197a5c1b54..29c387876ef 100644
--- a/intern/opensubdiv/internal/topology/mesh_topology.cc
+++ b/intern/opensubdiv/internal/topology/mesh_topology.cc
@@ -112,7 +112,7 @@ void MeshTopology::getEdgeVertexIndices(int edge_index, int *v1, int *v2) const
if (edge_index >= edges_.size()) {
*v1 = -1;
- *v1 = -1;
+ *v2 = -1;
return;
}
diff --git a/intern/rigidbody/RBI_hull_api.h b/intern/rigidbody/RBI_hull_api.h
index 9d2dc5034db..3bd216b92cb 100644
--- a/intern/rigidbody/RBI_hull_api.h
+++ b/intern/rigidbody/RBI_hull_api.h
@@ -31,9 +31,13 @@ typedef struct plConvexHull__ {
plConvexHull plConvexHullCompute(float (*coords)[3], int count);
void plConvexHullDelete(plConvexHull hull);
int plConvexHullNumVertices(plConvexHull hull);
+int plConvexHullNumLoops(plConvexHull hull);
int plConvexHullNumFaces(plConvexHull hull);
void plConvexHullGetVertex(plConvexHull hull, int n, float coords[3], int *original_index);
+void plConvexHullGetLoop(plConvexHull hull, int n, int *v_from, int *v_to);
+int plConvexHullGetReversedLoopIndex(plConvexHull hull, int n);
int plConvexHullGetFaceSize(plConvexHull hull, int n);
+void plConvexHullGetFaceLoops(plConvexHull hull, int n, int *loops);
void plConvexHullGetFaceVertices(plConvexHull hull, int n, int *vertices);
#ifdef __cplusplus
diff --git a/intern/rigidbody/rb_convex_hull_api.cpp b/intern/rigidbody/rb_convex_hull_api.cpp
index c5893a4c808..03e7580a12b 100644
--- a/intern/rigidbody/rb_convex_hull_api.cpp
+++ b/intern/rigidbody/rb_convex_hull_api.cpp
@@ -39,6 +39,12 @@ int plConvexHullNumVertices(plConvexHull hull)
return computer->vertices.size();
}
+int plConvexHullNumLoops(plConvexHull hull)
+{
+ btConvexHullComputer *computer(reinterpret_cast<btConvexHullComputer *>(hull));
+ return computer->edges.size();
+}
+
int plConvexHullNumFaces(plConvexHull hull)
{
btConvexHullComputer *computer(reinterpret_cast<btConvexHullComputer *>(hull));
@@ -55,6 +61,19 @@ void plConvexHullGetVertex(plConvexHull hull, int n, float coords[3], int *origi
(*original_index) = computer->original_vertex_index[n];
}
+void plConvexHullGetLoop(plConvexHull hull, int n, int *v_from, int *v_to)
+{
+ btConvexHullComputer *computer(reinterpret_cast<btConvexHullComputer *>(hull));
+ (*v_from) = computer->edges[n].getSourceVertex();
+ (*v_to) = computer->edges[n].getTargetVertex();
+}
+
+int plConvexHullGetReversedLoopIndex(plConvexHull hull, int n)
+{
+ btConvexHullComputer *computer(reinterpret_cast<btConvexHullComputer *>(hull));
+ return computer->edges[n].getReverseEdge() - &computer->edges[0];
+}
+
int plConvexHullGetFaceSize(plConvexHull hull, int n)
{
btConvexHullComputer *computer(reinterpret_cast<btConvexHullComputer *>(hull));
@@ -69,6 +88,19 @@ int plConvexHullGetFaceSize(plConvexHull hull, int n)
return count;
}
+void plConvexHullGetFaceLoops(plConvexHull hull, int n, int *loops)
+{
+ btConvexHullComputer *computer(reinterpret_cast<btConvexHullComputer *>(hull));
+ const btConvexHullComputer::Edge *e_orig, *e;
+ int count;
+
+ for (e_orig = &computer->edges[computer->faces[n]], e = e_orig, count = 0;
+ count == 0 || e != e_orig;
+ e = e->getNextEdgeOfFace(), count++) {
+ loops[count] = e - &computer->edges[0];
+ }
+}
+
void plConvexHullGetFaceVertices(plConvexHull hull, int n, int *vertices)
{
btConvexHullComputer *computer(reinterpret_cast<btConvexHullComputer *>(hull));
diff --git a/readme.rst b/readme.rst
index aadc766b22f..6d03d004be4 100644
--- a/readme.rst
+++ b/readme.rst
@@ -35,7 +35,7 @@ Development
License
-------
-Blender as a whole is licensed under the GNU Public License, Version 3.
+Blender as a whole is licensed under the GNU General Public License, Version 3.
Individual files may have a different, but compatible license.
See `blender.org/about/license <https://www.blender.org/about/license>`__ for details.
diff --git a/release/darwin/README.md b/release/darwin/README.md
new file mode 100644
index 00000000000..f1f02543ff3
--- /dev/null
+++ b/release/darwin/README.md
@@ -0,0 +1,5 @@
+Buildbot Configuration
+======================
+
+Files used by Buildbot's `package-code-binaires` step for the darwin platform.
+
diff --git a/release/darwin/README.txt b/release/darwin/README.txt
deleted file mode 100644
index 626ce8820ab..00000000000
--- a/release/darwin/README.txt
+++ /dev/null
@@ -1,55 +0,0 @@
-
-macOS app bundling guide
-========================
-
-Install Code Signing Certificate
---------------------------------
-
-* Go to https://developer.apple.com/account/resources/certificates/list
-* Download the Developer ID Application certificate.
-* Double click the file and add to key chain (default options).
-* Delete the file from the Downloads folder.
-
-* You will also need to install a .p12 public/private key file for the
- certificate. This is only available for the owner of the Blender account,
- or can be exported and copied from another system that already has code
- signing set up.
-
-Find the codesigning identity by running:
-
-$ security find-identity -v -p codesigning
-
-"Developer ID Application: Stichting Blender Foundation" is the identity needed.
-The long code at the start of the line is used as <identity> below.
-
-Setup Apple ID
---------------
-
-* The Apple ID must have two step verification enabled.
-* Create an app specific password for the code signing app (label can be anything):
-https://support.apple.com/en-us/HT204397
-* Add the app specific password to keychain:
-
-$ security add-generic-password -a <apple-id> -w <app-specific-password> -s altool-password
-
-When running the bundle script, there will be a popup. To avoid that either:
-* Click Always Allow in the popup
-* In the Keychain Access app, change the Access Control settings on altool-password
-
-Bundle
-------
-
-Then the bundle is created as follows:
-
-$ ./bundle.sh --source <sourcedir> --dmg <dmg> --bundle-id <bundleid> --username <apple-id> --password "@keychain:altool-password" --codesign <identity>
-
-<sourcedir> directory where built Blender.app is
-<dmg> location and name of the final disk image
-<bundleid> id on notarization, for example org.blenderfoundation.blender.release
-<apple-id> your appleid email
-<identity> codesigning identity
-
-When specifying only --sourcedir and --dmg, the build will not be signed.
-
-Example :
-$ ./bundle.sh --source /data/build/bin --dmg /data/Blender-2.8-alpha-macOS-10.11.dmg --bundle-id org.blenderfoundation.blender.release --username "foo@mac.com" --password "@keychain:altool-password" --codesign AE825E26F12D08B692F360133210AF46F4CF7B97
diff --git a/release/darwin/blender.applescript b/release/darwin/blender.applescript
deleted file mode 100644
index 130dc2eb64c..00000000000
--- a/release/darwin/blender.applescript
+++ /dev/null
@@ -1,18 +0,0 @@
-tell application "Finder"
- tell disk "Blender"
- open
- set current view of container window to icon view
- set toolbar visible of container window to false
- set statusbar visible of container window to false
- set the bounds of container window to {100, 100, 640, 472}
- set theViewOptions to icon view options of container window
- set arrangement of theViewOptions to not arranged
- set icon size of theViewOptions to 128
- set background picture of theViewOptions to file ".background:background.tif"
- set position of item " " of container window to {400, 190}
- set position of item "blender.app" of container window to {135, 190}
- update without registering applications
- delay 5
- close
- end tell
-end tell
diff --git a/release/darwin/bundle.sh b/release/darwin/bundle.sh
deleted file mode 100755
index 6d8695a441d..00000000000
--- a/release/darwin/bundle.sh
+++ /dev/null
@@ -1,212 +0,0 @@
-#!/usr/bin/env bash
-#
-# Script to create a macOS dmg file for Blender builds, including code
-# signing and notarization for releases.
-
-# Check that we have all needed tools.
-for i in osascript git codesign hdiutil xcrun ; do
- if [ ! -x "$(which ${i})" ]; then
- echo "Unable to execute command $i, macOS broken?"
- exit 1
- fi
-done
-
-# Defaults settings.
-_script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
-_volume_name="Blender"
-_tmp_dir="$(mktemp -d)"
-_tmp_dmg="/tmp/blender-tmp.dmg"
-_background_image="${_script_dir}/background.tif"
-_mount_dir="/Volumes/${_volume_name}"
-_entitlements="${_script_dir}/entitlements.plist"
-
-# Handle arguments.
-while [[ $# -gt 0 ]]; do
- key=$1
- case $key in
- -s|--source)
- SRC_DIR="$2"
- shift
- shift
- ;;
- -d|--dmg)
- DEST_DMG="$2"
- shift
- shift
- ;;
- -b|--bundle-id)
- N_BUNDLE_ID="$2"
- shift
- shift
- ;;
- -u|--username)
- N_USERNAME="$2"
- shift
- shift
- ;;
- -p|--password)
- N_PASSWORD="$2"
- shift
- shift
- ;;
- -c|--codesign)
- C_CERT="$2"
- shift
- shift
- ;;
- --background-image)
- _background_image="$2"
- shift
- shift
- ;;
- -h|--help)
- echo "Usage:"
- echo " $(basename "$0") --source DIR --dmg IMAGENAME "
- echo " optional arguments:"
- echo " --codesign <certname>"
- echo " --username <username>"
- echo " --password <password>"
- echo " --bundle-id <bundleid>"
- echo " Check https://developer.apple.com/documentation/security/notarizing_your_app_before_distribution/customizing_the_notarization_workflow "
- exit 1
- ;;
- esac
-done
-
-if [ ! -d "${SRC_DIR}/Blender.app" ]; then
- echo "use --source parameter to set source directory where Blender.app can be found"
- exit 1
-fi
-
-if [ -z "${DEST_DMG}" ]; then
- echo "use --dmg parameter to set output dmg name"
- exit 1
-fi
-
-# Destroy destination dmg if there is any.
-test -f "${DEST_DMG}" && rm "${DEST_DMG}"
-if [ -d "${_mount_dir}" ]; then
- echo -n "Ejecting existing blender volume.."
- DEV_FILE=$(mount | grep "${_mount_dir}" | awk '{ print $1 }')
- diskutil eject "${DEV_FILE}" || exit 1
- echo
-fi
-
-# Copy dmg contents.
-echo -n "Copying Blender.app..."
-cp -r "${SRC_DIR}/Blender.app" "${_tmp_dir}/" || exit 1
-echo
-
-# Create the disk image.
-_directory_size=$(du -sh ${_tmp_dir} | awk -F'[^0-9]*' '$0=$1')
-_image_size=$(echo "${_directory_size}" + 400 | bc) # extra 400 need for codesign to work (why on earth?)
-
-echo
-echo -n "Creating disk image of size ${_image_size}M.."
-test -f "${_tmp_dmg}" && rm "${_tmp_dmg}"
-hdiutil create -size "${_image_size}m" -fs HFS+ -srcfolder "${_tmp_dir}" -volname "${_volume_name}" -format UDRW "${_tmp_dmg}" -mode 755
-
-echo "Mounting readwrite image..."
-hdiutil attach -readwrite -noverify -noautoopen "${_tmp_dmg}"
-
-echo "Setting background picture.."
-if ! test -z "${_background_image}"; then
- echo "Copying background image ..."
- test -d "${_mount_dir}/.background" || mkdir "${_mount_dir}/.background"
- _background_image_NAME=$(basename "${_background_image}")
- cp "${_background_image}" "${_mount_dir}/.background/${_background_image_NAME}"
-fi
-
-echo "Creating link to /Applications ..."
-ln -s /Applications "${_mount_dir}/Applications"
-echo "Renaming Applications to empty string."
-mv ${_mount_dir}/Applications "${_mount_dir}/ "
-
-echo "Running applescript to set folder looks ..."
-cat "${_script_dir}/blender.applescript" | osascript
-
-echo "Waiting after applescript ..."
-sleep 5
-
-if [ ! -z "${C_CERT}" ]; then
- # Codesigning requires all libs and binaries to be signed separately.
- echo -n "Codesigning Python"
- for f in $(find "${_mount_dir}/Blender.app/Contents/Resources" -name "python*"); do
- if [ -x ${f} ] && [ ! -d ${f} ]; then
- codesign --remove-signature "${f}"
- codesign --timestamp --options runtime --entitlements="${_entitlements}" --sign "${C_CERT}" "${f}"
- fi
- done
- echo ; echo -n "Codesigning .dylib and .so libraries"
- for f in $(find "${_mount_dir}/Blender.app" -name "*.dylib" -o -name "*.so"); do
- codesign --remove-signature "${f}"
- codesign --timestamp --options runtime --entitlements="${_entitlements}" --sign "${C_CERT}" "${f}"
- done
- echo ; echo -n "Codesigning Blender.app"
- codesign --remove-signature "${_mount_dir}/Blender.app"
- codesign --timestamp --options runtime --entitlements="${_entitlements}" --sign "${C_CERT}" "${_mount_dir}/Blender.app"
- echo
-else
- echo "No codesigning cert given, skipping..."
-fi
-
-# Need to eject dev files to remove /dev files and free .dmg for converting
-echo "Unmounting rw disk image ..."
-DEV_FILE=$(mount | grep "${_mount_dir}" | awk '{ print $1 }')
-diskutil eject "${DEV_FILE}"
-
-sleep 3
-
-echo "Compressing disk image ..."
-hdiutil convert "${_tmp_dmg}" -format UDZO -o "${DEST_DMG}"
-
-# Codesign the dmg
-if [ ! -z "${C_CERT}" ]; then
- echo -n "Codesigning dmg..."
- codesign --timestamp --force --sign "${C_CERT}" "${DEST_DMG}"
- echo
-fi
-
-# Cleanup
-rm -rf "${_tmp_dir}"
-rm "${_tmp_dmg}"
-
-# Notarize
-if [ ! -z "${N_USERNAME}" ] && [ ! -z "${N_PASSWORD}" ] && [ ! -z "${N_BUNDLE_ID}" ]; then
- # Send to Apple
- echo "Sending ${DEST_DMG} for notarization..."
- _tmpout=$(mktemp)
- echo xcrun altool --notarize-app --verbose -f "${DEST_DMG}" --primary-bundle-id "${N_BUNDLE_ID}" --username "${N_USERNAME}" --password "${N_PASSWORD}"
- xcrun altool --notarize-app --verbose -f "${DEST_DMG}" --primary-bundle-id "${N_BUNDLE_ID}" --username "${N_USERNAME}" --password "${N_PASSWORD}" >${_tmpout} 2>&1
-
- # Parse request uuid
- _requuid=$(cat "${_tmpout}" | grep "RequestUUID" | awk '{ print $3 }')
- echo "RequestUUID: ${_requuid}"
- if [ ! -z "${_requuid}" ]; then
- # Wait for Apple to confirm notarization is complete
- echo "Waiting for notarization to be complete.."
- for c in {20..0};do
- sleep 600
- xcrun altool --notarization-info "${_requuid}" --username "${N_USERNAME}" --password "${N_PASSWORD}" >${_tmpout} 2>&1
- _status=$(cat "${_tmpout}" | grep "Status:" | awk '{ print $2 }')
- if [ "${_status}" == "invalid" ]; then
- echo "Got invalid notarization!"
- break;
- fi
-
- if [ "${_status}" == "success" ]; then
- echo -n "Notarization successful! Stapling..."
- xcrun stapler staple -v "${DEST_DMG}"
- break;
- fi
- echo "Notarization in progress, waiting..."
- done
- else
- cat ${_tmpout}
- echo "Error getting RequestUUID, notarization unsuccessful"
- fi
-else
- echo "No notarization credentials supplied, skipping..."
-fi
-
-echo "..done. You should have ${DEST_DMG} ready to upload"
diff --git a/release/datafiles/locale b/release/datafiles/locale
-Subproject 2cef4877edc40875978c4e95322bb5193f5815b
+Subproject 4833954c0ac85cc407e1d5a153aa11b1d1823ec
diff --git a/release/datafiles/startup.blend b/release/datafiles/startup.blend
index 9dc7e10986e..20ebf5d9986 100644
--- a/release/datafiles/startup.blend
+++ b/release/datafiles/startup.blend
Binary files differ
diff --git a/release/freedesktop/org.blender.Blender.appdata.xml b/release/freedesktop/org.blender.Blender.appdata.xml
index f6d17834150..7a5a252e4ca 100644
--- a/release/freedesktop/org.blender.Blender.appdata.xml
+++ b/release/freedesktop/org.blender.Blender.appdata.xml
@@ -40,6 +40,25 @@
</screenshot>
</screenshots>
<releases>
+ <release version="2.93" date="2021-06-02">
+ <description>
+ <p>New features:</p>
+ <ul>
+ <li>Mesh primitive nodes</li>
+ <li>Line Art</li>
+ <li>EEVEE Realistic depth of field and volumetrics</li>
+ <li>Spreadsheet editor</li>
+ </ul>
+ <p>Enhancements:</p>
+ <ul>
+ <li>Geometry nodes 22 new nodes and imrpoved attribute search</li>
+ <li>Mask loops, textures and patterns for sculpting</li>
+ <li>Grease pencil interpolate refactored and SVG and PDF support</li>
+ <li>Persistent Data rendering settings for Cycles</li>
+ <li>Video Sequencer Editor auto-proxy system</li>
+ </ul>
+ </description>
+ </release>
<release version="2.92" date="2021-02-25">
<description>
<p>New features:</p>
diff --git a/release/freedesktop/snap/README.md b/release/freedesktop/snap/README.md
new file mode 100644
index 00000000000..742b265ada6
--- /dev/null
+++ b/release/freedesktop/snap/README.md
@@ -0,0 +1,17 @@
+Snap Configuration
+===================
+
+Files used by Buildbot's `package-code-store-snap` and `deliver-code-store-snap` steps.
+
+Build pipeline snap tracks and channels
+
+```
+ <track>/stable
+ - Latest stable release for the specified track
+ <track>/candidate
+ - Test builds for the upcoming stable release - *not used for now*
+ <track>/beta
+ - Nightly automated builds provided by a release branch
+ <track>/egde/<branch>
+ - Nightly or on demand builds - will also make use of branch
+```
diff --git a/release/freedesktop/snap/README.txt b/release/freedesktop/snap/README.txt
deleted file mode 100644
index 2e8822f32dc..00000000000
--- a/release/freedesktop/snap/README.txt
+++ /dev/null
@@ -1,38 +0,0 @@
-
-Snap Package Instructions
-=========================
-
-This folder contains the scripts for creating and uploading the snap on:
-https://snapcraft.io/blender
-
-
-Setup
------
-
-This has only been tested to work on Ubuntu.
-
-# Install required packages
-sudo apt install snapd snapcraft
-
-
-Steps
------
-
-# Build the snap file
-python3 bundle.py --version 2.XX --url https://download.blender.org/release/Blender2.XX/blender-2.XX-x86_64.tar.bz2
-
-# Install snap to test
-# --dangerous is needed since the snap has not been signed yet
-# --classic is required for installing Blender in general
-sudo snap install --dangerous --classic blender_2.XX_amd64.snap
-
-# Upload
-snapcraft push --release=stable blender_2.XX_amd64.snap
-
-
-Release Values
---------------
-
-stable: final release
-candidate: release candidates
-
diff --git a/release/freedesktop/snap/snapcraft.yaml.in b/release/freedesktop/snap/blender-snapcraft-template.yaml
index eb3ef97eba8..882f9081c09 100644
--- a/release/freedesktop/snap/snapcraft.yaml.in
+++ b/release/freedesktop/snap/blender-snapcraft-template.yaml
@@ -10,12 +10,7 @@ description: |
scientists, students, VFX experts, animators, game artists, modders, and
the list goes on.
- The standard snap channels are used in the following way:
-
- stable - Latest stable release.
- candidate - Test builds for the upcoming stable release.
-
-icon: ../icons/scalable/apps/blender.svg
+icon: @ICON_PATH@
passthrough:
license: GPL-3.0
@@ -27,13 +22,14 @@ apps:
command: ./blender-wrapper
desktop: ./blender.desktop
+base: core18
version: '@VERSION@'
grade: @GRADE@
parts:
blender:
plugin: dump
- source: @URL@
+ source: @PACKAGE_PATH@
build-attributes: [keep-execstack, no-patchelf]
override-build: |
snapcraftctl build
@@ -47,7 +43,7 @@ parts:
- libxrender1
- libxxf86vm1
wrapper:
- plugin: copy
+ plugin: dump
source: .
- files:
- blender-wrapper: blender-wrapper
+ stage:
+ - ./blender-wrapper
diff --git a/release/freedesktop/snap/bundle.py b/release/freedesktop/snap/bundle.py
deleted file mode 100755
index c3ecc5af561..00000000000
--- a/release/freedesktop/snap/bundle.py
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/usr/bin/env python3
-
-import argparse
-import os
-import pathlib
-import subprocess
-
-parser = argparse.ArgumentParser()
-parser.add_argument("--version", required=True)
-parser.add_argument("--url", required=True)
-parser.add_argument("--grade", default="stable", choices=["stable", "devel"])
-args = parser.parse_args()
-
-yaml_text = pathlib.Path("snapcraft.yaml.in").read_text()
-yaml_text = yaml_text.replace("@VERSION@", args.version)
-yaml_text = yaml_text.replace("@URL@", args.url)
-yaml_text = yaml_text.replace("@GRADE@", args.grade)
-pathlib.Path("snapcraft.yaml").write_text(yaml_text)
-
-subprocess.call(["snapcraft", "clean"])
-subprocess.call(["snapcraft", "snap"])
diff --git a/release/lts/create_download_urls.py b/release/lts/create_download_urls.py
index 0d0b2554d2a..80613693e33 100755
--- a/release/lts/create_download_urls.py
+++ b/release/lts/create_download_urls.py
@@ -27,12 +27,11 @@ class Version:
def __str__(self) -> str:
return self.version
-
def get_download_file_names(version: Version):
- yield f"blender-{version}-linux64.tar.xz"
- yield f"blender-{version}-macOS.dmg"
- yield f"blender-{version}-windows64.msi"
- yield f"blender-{version}-windows64.zip"
+ yield f"blender-{version}-linux-x64.tar.xz"
+ yield f"blender-{version}-macos-x64.dmg"
+ yield f"blender-{version}-windows-x64.msi"
+ yield f"blender-{version}-windows-x64.zip"
def get_download_url(version: Version, file_name: str) -> str:
diff --git a/release/scripts/addons b/release/scripts/addons
-Subproject bcd08a9506d33bdd7358201031b04d041ef22d9
+Subproject f86f25e62217264495d05f116ccb09d575fe984
diff --git a/release/scripts/addons_contrib b/release/scripts/addons_contrib
-Subproject f948f658ba33eb670a65e0bba058d43138abea7
+Subproject 5a82baad9f986722104280e8354a4427d8e9eab
diff --git a/release/scripts/freestyle/styles/ignore_small_occlusions.py b/release/scripts/freestyle/styles/ignore_small_occlusions.py
index efa228f4663..a5fb4671917 100644
--- a/release/scripts/freestyle/styles/ignore_small_occlusions.py
+++ b/release/scripts/freestyle/styles/ignore_small_occlusions.py
@@ -16,7 +16,7 @@
#
# ##### END GPL LICENSE BLOCK #####
-# Filename : ignore_small_oclusions.py
+# Filename : ignore_small_occlusions.py
# Author : Stephane Grabli
# Date : 04/08/2005
# Purpose : The strokes are drawn through small occlusions
diff --git a/release/scripts/modules/addon_utils.py b/release/scripts/modules/addon_utils.py
index 83bed69d8d2..239dd1cf79b 100644
--- a/release/scripts/modules/addon_utils.py
+++ b/release/scripts/modules/addon_utils.py
@@ -49,16 +49,16 @@ def _initialize():
def paths():
# RELEASE SCRIPTS: official scripts distributed in Blender releases
- addon_paths = _bpy.utils.script_paths("addons")
+ addon_paths = _bpy.utils.script_paths(subdir="addons")
# CONTRIB SCRIPTS: good for testing but not official scripts yet
# if folder addons_contrib/ exists, scripts in there will be loaded too
- addon_paths += _bpy.utils.script_paths("addons_contrib")
+ addon_paths += _bpy.utils.script_paths(subdir="addons_contrib")
return addon_paths
-def modules_refresh(module_cache=addons_fake_modules):
+def modules_refresh(*, module_cache=addons_fake_modules):
global error_encoding
import os
@@ -203,9 +203,9 @@ def modules_refresh(module_cache=addons_fake_modules):
del modules_stale
-def modules(module_cache=addons_fake_modules, *, refresh=True):
+def modules(*, module_cache=addons_fake_modules, refresh=True):
if refresh or ((module_cache is addons_fake_modules) and modules._is_first):
- modules_refresh(module_cache)
+ modules_refresh(module_cache=module_cache)
modules._is_first = False
mod_list = list(module_cache.values())
@@ -349,6 +349,10 @@ def enable(module_name, *, default_set=False, persistent=False, handle_error=Non
# 1) try import
try:
mod = __import__(module_name)
+ if mod.__file__ is None:
+ # This can happen when the addon has been removed but there are
+ # residual `.pyc` files left behind.
+ raise ImportError(name=module_name)
mod.__time__ = os.path.getmtime(mod.__file__)
mod.__addon_enabled__ = False
except Exception as ex:
@@ -508,7 +512,7 @@ def _blender_manual_url_prefix():
return "https://docs.blender.org/manual/en/" + manual_version
-def module_bl_info(mod, info_basis=None):
+def module_bl_info(mod, *, info_basis=None):
if info_basis is None:
info_basis = {
"name": "",
@@ -539,22 +543,6 @@ def module_bl_info(mod, info_basis=None):
if not addon_info["name"]:
addon_info["name"] = mod.__name__
- # Replace 'wiki_url' with 'doc_url'.
- doc_url = addon_info.pop("wiki_url", None)
- if doc_url is not None:
- # Unlikely, but possible that both are set.
- if not addon_info["doc_url"]:
- addon_info["doc_url"] = doc_url
- if _bpy.app.debug:
- print(
- "Warning: add-on \"%s\": 'wiki_url' in 'bl_info' "
- "is deprecated please use 'doc_url' instead!\n"
- " %s" % (
- addon_info['name'],
- getattr(mod, "__file__", None),
- )
- )
-
doc_url = addon_info["doc_url"]
if doc_url:
doc_url_prefix = "{BLENDER_MANUAL_URL}"
diff --git a/release/scripts/modules/animsys_refactor.py b/release/scripts/modules/animsys_refactor.py
index 97e8a8dd144..fd4952e2a53 100644
--- a/release/scripts/modules/animsys_refactor.py
+++ b/release/scripts/modules/animsys_refactor.py
@@ -32,12 +32,6 @@ import bpy
IS_TESTING = False
-def drepr(string):
- # is there a less crappy way to do this in python?, re.escape also escapes
- # single quotes strings so can't use it.
- return '"%s"' % repr(string)[1:-1].replace("\"", "\\\"").replace("\\'", "'")
-
-
def classes_recursive(base_type, clss=None):
if clss is None:
clss = [base_type]
@@ -66,7 +60,7 @@ class DataPathBuilder:
if type(key) is int:
str_value = '[%d]' % key
elif type(key) is str:
- str_value = '[%s]' % drepr(key)
+ str_value = '["%s"]' % bpy.utils.escape_identifier(key)
else:
raise Exception("unsupported accessor %r of type %r (internal error)" % (key, type(key)))
return DataPathBuilder(self.data_path + (str_value, ))
diff --git a/release/scripts/modules/bl_app_template_utils.py b/release/scripts/modules/bl_app_template_utils.py
index 7db084a9a29..43c0c571cb0 100644
--- a/release/scripts/modules/bl_app_template_utils.py
+++ b/release/scripts/modules/bl_app_template_utils.py
@@ -134,7 +134,7 @@ def _disable(template_id, *, handle_error=None):
print("\tapp_template_utils.disable", template_id)
-def import_from_path(path, ignore_not_found=False):
+def import_from_path(path, *, ignore_not_found=False):
import os
from importlib import import_module
base_module, template_id = path.rsplit(os.sep, 2)[-2:]
@@ -148,9 +148,9 @@ def import_from_path(path, ignore_not_found=False):
raise ex
-def import_from_id(template_id, ignore_not_found=False):
+def import_from_id(template_id, *, ignore_not_found=False):
import os
- path = next(iter(_bpy.utils.app_template_paths(template_id)), None)
+ path = next(iter(_bpy.utils.app_template_paths(path=template_id)), None)
if path is None:
if ignore_not_found:
return None
@@ -163,7 +163,7 @@ def import_from_id(template_id, ignore_not_found=False):
return import_from_path(path, ignore_not_found=ignore_not_found)
-def activate(template_id=None):
+def activate(*, template_id=None):
template_id_prev = _app_template["id"]
# not needed but may as well avoids redundant
@@ -190,4 +190,4 @@ def reset(*, reload_scripts=False):
# TODO reload_scripts
- activate(template_id)
+ activate(template_id=template_id)
diff --git a/release/scripts/modules/bl_i18n_utils/utils_spell_check.py b/release/scripts/modules/bl_i18n_utils/utils_spell_check.py
index 6046e8e74d8..c310eee0b14 100644
--- a/release/scripts/modules/bl_i18n_utils/utils_spell_check.py
+++ b/release/scripts/modules/bl_i18n_utils/utils_spell_check.py
@@ -119,6 +119,7 @@ class SpellChecker:
"dirtree",
"editcurve",
"editmesh",
+ "faceforward",
"filebrowser",
"filelist",
"filename", "filenames",
@@ -200,6 +201,7 @@ class SpellChecker:
"selfcollision",
"shadowbuffer", "shadowbuffers",
"singletexture",
+ "softbox",
"spellcheck", "spellchecking",
"startup",
"stateful",
diff --git a/release/scripts/modules/bl_keymap_utils/keymap_from_toolbar.py b/release/scripts/modules/bl_keymap_utils/keymap_from_toolbar.py
index ef398d5e08f..aff1108568b 100644
--- a/release/scripts/modules/bl_keymap_utils/keymap_from_toolbar.py
+++ b/release/scripts/modules/bl_keymap_utils/keymap_from_toolbar.py
@@ -26,7 +26,7 @@ __all__ = (
)
-def generate(context, space_type, use_fallback_keys=True, use_reset=True):
+def generate(context, space_type, *, use_fallback_keys=True, use_reset=True):
"""
Keymap for popup toolbar, currently generated each time.
"""
diff --git a/release/scripts/modules/bl_keymap_utils/platform_helpers.py b/release/scripts/modules/bl_keymap_utils/platform_helpers.py
index a6058b993d5..848eb4e10c4 100644
--- a/release/scripts/modules/bl_keymap_utils/platform_helpers.py
+++ b/release/scripts/modules/bl_keymap_utils/platform_helpers.py
@@ -17,7 +17,7 @@
# ##### END GPL LICENSE BLOCK #####
-def keyconfig_data_oskey_from_ctrl(keyconfig_data_src, filter_fn=None):
+def keyconfig_data_oskey_from_ctrl(keyconfig_data_src, *, filter_fn=None):
keyconfig_data_dst = []
for km_name, km_parms, km_items_data_src in keyconfig_data_src:
km_items_data_dst = km_items_data_src.copy()
@@ -61,4 +61,4 @@ def keyconfig_data_oskey_from_ctrl_for_macos(keyconfig_data_src):
return False
return True
- return keyconfig_data_oskey_from_ctrl(keyconfig_data_src, filter_fn)
+ return keyconfig_data_oskey_from_ctrl(keyconfig_data_src, filter_fn=filter_fn)
diff --git a/release/scripts/modules/bl_previews_utils/bl_previews_render.py b/release/scripts/modules/bl_previews_utils/bl_previews_render.py
index 979b47f7a14..51ea53c0eba 100644
--- a/release/scripts/modules/bl_previews_utils/bl_previews_render.py
+++ b/release/scripts/modules/bl_previews_utils/bl_previews_render.py
@@ -37,6 +37,9 @@ OBJECT_TYPES_RENDER = {'MESH', 'CURVE', 'SURFACE', 'META', 'FONT'}
def ids_nolib(bids):
return (bid for bid in bids if not bid.library)
+def ids_nolib_with_preview(bids):
+ return (bid for bid in bids if (not bid.library and bid.preview))
+
def rna_backup_gen(data, include_props=None, exclude_props=None, root=()):
# only writable properties...
@@ -313,8 +316,9 @@ def do_previews(do_objects, do_collections, do_scenes, do_data_intern):
image = bpy.data.images[render_context.image, None]
item = getattr(bpy.data, item_container)[item_name, None]
image.reload()
- item.preview.image_size = (RENDER_PREVIEW_SIZE, RENDER_PREVIEW_SIZE)
- item.preview.image_pixels_float[:] = image.pixels
+ preview = item.preview_ensure()
+ preview.image_size = (RENDER_PREVIEW_SIZE, RENDER_PREVIEW_SIZE)
+ preview.image_pixels_float[:] = image.pixels
# And now, main code!
do_save = True
@@ -451,15 +455,15 @@ def do_clear_previews(do_objects, do_collections, do_scenes, do_data_intern):
bpy.ops.wm.previews_clear(id_type={'SHADING'})
if do_objects:
- for ob in ids_nolib(bpy.data.objects):
+ for ob in ids_nolib_with_preview(bpy.data.objects):
ob.preview.image_size = (0, 0)
if do_collections:
- for grp in ids_nolib(bpy.data.collections):
+ for grp in ids_nolib_with_preview(bpy.data.collections):
grp.preview.image_size = (0, 0)
if do_scenes:
- for scene in ids_nolib(bpy.data.scenes):
+ for scene in ids_nolib_with_preview(bpy.data.scenes):
scene.preview.image_size = (0, 0)
print("Saving %s..." % bpy.data.filepath)
diff --git a/release/scripts/modules/bl_rna_utils/__init__.py b/release/scripts/modules/bl_rna_utils/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/release/scripts/modules/bl_rna_utils/__init__.py
diff --git a/release/scripts/modules/bl_rna_utils/data_path.py b/release/scripts/modules/bl_rna_utils/data_path.py
new file mode 100644
index 00000000000..42942b7a295
--- /dev/null
+++ b/release/scripts/modules/bl_rna_utils/data_path.py
@@ -0,0 +1,91 @@
+# ##### 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>
+
+__all__ = (
+ "property_definition_from_data_path",
+ "decompose_data_path",
+)
+
+class _TokenizeDataPath:
+ """
+ Class to split up tokens of a data-path.
+
+ Note that almost all access generates new objects with additional paths,
+ with the exception of iteration which is the intended way to access the resulting data."""
+ __slots__ = (
+ "data_path",
+ )
+
+ def __init__(self, attrs):
+ self.data_path = attrs
+
+ def __getattr__(self, attr):
+ return _TokenizeDataPath(self.data_path + ((".%s" % attr),))
+
+ def __getitem__(self, key):
+ return _TokenizeDataPath(self.data_path + (("[%r]" % (key,)),))
+
+ def __call__(self, *args, **kw):
+ value_str = ", ".join([
+ val for val in (
+ ", ".join(repr(value) for value in args),
+ ", ".join(["%s=%r" % (key, value) for key, value in kw.items()]),
+ ) if val])
+ return _TokenizeDataPath(self.data_path + ('(%s)' % value_str, ))
+
+ def __iter__(self):
+ return iter(self.data_path)
+
+
+def decompose_data_path(data_path):
+ """
+ Return the components of a data path split into a list.
+ """
+ ns = {"base": _TokenizeDataPath(())}
+ return list(eval("base" + data_path, ns, ns))
+
+
+def property_definition_from_data_path(base, data_path):
+ """
+ Return an RNA property definition from an object and a data path.
+
+ In Blender this is often used with ``context`` as the base and a
+ path that it references, for example ``.space_data.lock_camera``.
+ """
+ data = decompose_data_path(data_path)
+ while data and (not data[-1].startswith(".")):
+ data.pop()
+
+ if (not data) or (not data[-1].startswith(".")) or (len(data) < 2):
+ return None
+
+ data_path_head = "".join(data[:-1])
+ data_path_tail = data[-1]
+
+ value_head = eval("base" + data_path_head)
+ value_head_rna = getattr(value_head, "bl_rna", None)
+ if value_head_rna is None:
+ return None
+
+ value_tail = value_head.bl_rna.properties.get(data_path_tail[1:])
+ if not value_tail:
+ return None
+
+ return value_tail
diff --git a/release/scripts/modules/bl_ui_utils/bug_report_url.py b/release/scripts/modules/bl_ui_utils/bug_report_url.py
index 5676e0d6815..ef6a261f776 100644
--- a/release/scripts/modules/bl_ui_utils/bug_report_url.py
+++ b/release/scripts/modules/bl_ui_utils/bug_report_url.py
@@ -19,9 +19,9 @@
# <pep8-80 compliant>
-def url_prefill_from_blender(addon_info=None):
+def url_prefill_from_blender(*, addon_info=None):
import bpy
- import bgl
+ import gpu
import struct
import platform
import urllib.parse
@@ -38,9 +38,9 @@ def url_prefill_from_blender(addon_info=None):
)
fh.write(
"Graphics card: %s %s %s\n" % (
- bgl.glGetString(bgl.GL_RENDERER),
- bgl.glGetString(bgl.GL_VENDOR),
- bgl.glGetString(bgl.GL_VERSION),
+ gpu.platform.renderer_get(),
+ gpu.platform.vendor_get(),
+ gpu.platform.version_get(),
)
)
fh.write(
diff --git a/release/scripts/modules/bpy/path.py b/release/scripts/modules/bpy/path.py
index fad52eae84a..30f93d051e5 100644
--- a/release/scripts/modules/bpy/path.py
+++ b/release/scripts/modules/bpy/path.py
@@ -56,7 +56,7 @@ def _getattr_bytes(var, attr):
return var.path_resolve(attr, False).as_bytes()
-def abspath(path, start=None, library=None):
+def abspath(path, *, start=None, library=None):
"""
Returns the absolute path relative to the current blend file
using the "//" prefix.
@@ -92,7 +92,7 @@ def abspath(path, start=None, library=None):
return path
-def relpath(path, start=None):
+def relpath(path, *, start=None):
"""
Returns the path relative to the current blend file using the "//" prefix.
@@ -134,7 +134,7 @@ def is_subdir(path, directory):
return False
-def clean_name(name, replace="_"):
+def clean_name(name, *, replace="_"):
"""
Returns a name with characters replaced that
may cause problems under various circumstances,
@@ -198,6 +198,7 @@ def _clean_utf8(name):
_display_name_literals = {
":": "_colon_",
"+": "_plus_",
+ "/": "_slash_",
}
@@ -310,7 +311,7 @@ def resolve_ncase(path):
return ncase_path if found else path
-def ensure_ext(filepath, ext, case_sensitive=False):
+def ensure_ext(filepath, ext, *, case_sensitive=False):
"""
Return the path with the extension added if it is not already set.
@@ -331,7 +332,7 @@ def ensure_ext(filepath, ext, case_sensitive=False):
return filepath + ext
-def module_names(path, recursive=False):
+def module_names(path, *, recursive=False):
"""
Return a list of modules which can be imported from *path*.
@@ -360,7 +361,7 @@ def module_names(path, recursive=False):
if isfile(fullpath):
modules.append((filename, fullpath))
if recursive:
- for mod_name, mod_path in module_names(directory, True):
+ for mod_name, mod_path in module_names(directory, recursive=True):
modules.append(("%s.%s" % (filename, mod_name),
mod_path,
))
@@ -370,7 +371,7 @@ def module_names(path, recursive=False):
def basename(path):
"""
- Equivalent to os.path.basename, but skips a "//" prefix.
+ Equivalent to ``os.path.basename``, but skips a "//" prefix.
Use for Windows compatibility.
"""
diff --git a/release/scripts/modules/bpy/utils/__init__.py b/release/scripts/modules/bpy/utils/__init__.py
index 2b19d23a367..e6c7bca3e3c 100644
--- a/release/scripts/modules/bpy/utils/__init__.py
+++ b/release/scripts/modules/bpy/utils/__init__.py
@@ -81,7 +81,7 @@ _script_module_dirs = "startup", "modules"
_is_factory_startup = _bpy.app.factory_startup
-def execfile(filepath, mod=None):
+def execfile(filepath, *, mod=None):
"""
Execute a file path as a Python script.
@@ -193,7 +193,7 @@ _global_loaded_modules = [] # store loaded module names for reloading.
import bpy_types as _bpy_types # keep for comparisons, never ever reload this.
-def load_scripts(reload_scripts=False, refresh_scripts=False):
+def load_scripts(*, reload_scripts=False, refresh_scripts=False):
"""
Load scripts and run each modules register function.
@@ -357,7 +357,7 @@ def script_path_pref():
return _os.path.normpath(path) if path else None
-def script_paths(subdir=None, user_pref=True, check_all=False, use_user=True):
+def script_paths(*, subdir=None, user_pref=True, check_all=False, use_user=True):
"""
Returns a list of valid script paths.
@@ -446,16 +446,16 @@ def refresh_script_paths():
_sys_path_ensure_append(path)
-def app_template_paths(subdir=None):
+def app_template_paths(*, path=None):
"""
Returns valid application template paths.
- :arg subdir: Optional subdir.
- :type subdir: string
+ :arg path: Optional subdir.
+ :type path: string
:return: app template paths.
:rtype: generator
"""
- subdir_args = (subdir,) if subdir is not None else ()
+ subdir_args = (path,) if path is not None else ()
# Note: keep in sync with: Blender's 'BKE_appdir_app_template_any'.
# Uses 'BLENDER_USER_SCRIPTS', 'BLENDER_SYSTEM_SCRIPTS'
# ... in this case 'system' accounts for 'local' too.
@@ -463,9 +463,9 @@ def app_template_paths(subdir=None):
(_user_resource, "bl_app_templates_user"),
(system_resource, "bl_app_templates_system"),
):
- path = resource_fn('SCRIPTS', _os.path.join("startup", module_name, *subdir_args))
- if path and _os.path.isdir(path):
- yield path
+ path_test = resource_fn('SCRIPTS', path=_os.path.join("startup", module_name, *subdir_args))
+ if path_test and _os.path.isdir(path_test):
+ yield path_test
def preset_paths(subdir):
@@ -478,7 +478,7 @@ def preset_paths(subdir):
:rtype: list
"""
dirs = []
- for path in script_paths("presets", check_all=True):
+ for path in script_paths(subdir="presets", check_all=True):
directory = _os.path.join(path, subdir)
if not directory.startswith(path):
raise Exception("invalid subdir given %r" % subdir)
@@ -532,7 +532,7 @@ def is_path_builtin(path):
return False
-def smpte_from_seconds(time, fps=None, fps_base=None):
+def smpte_from_seconds(time, *, fps=None, fps_base=None):
"""
Returns an SMPTE formatted string from the *time*:
``HH:MM:SS:FF``.
@@ -552,7 +552,7 @@ def smpte_from_seconds(time, fps=None, fps_base=None):
)
-def smpte_from_frame(frame, fps=None, fps_base=None):
+def smpte_from_frame(frame, *, fps=None, fps_base=None):
"""
Returns an SMPTE formatted string from the *frame*:
``HH:MM:SS:FF``.
@@ -585,7 +585,7 @@ def smpte_from_frame(frame, fps=None, fps_base=None):
))
-def time_from_frame(frame, fps=None, fps_base=None):
+def time_from_frame(frame, *, fps=None, fps_base=None):
"""
Returns the time from a frame number .
@@ -610,7 +610,7 @@ def time_from_frame(frame, fps=None, fps_base=None):
return timedelta(0, frame / fps)
-def time_to_frame(time, fps=None, fps_base=None):
+def time_to_frame(time, *, fps=None, fps_base=None):
"""
Returns a float frame number from a time given in seconds or
as a datetime.timedelta object.
@@ -639,7 +639,7 @@ def time_to_frame(time, fps=None, fps_base=None):
return time * fps
-def preset_find(name, preset_path, display_name=False, ext=".py"):
+def preset_find(name, preset_path, *, display_name=False, ext=".py"):
if not name:
return None
@@ -676,7 +676,7 @@ def keyconfig_init():
keyconfig_set(filepath)
-def keyconfig_set(filepath, report=None):
+def keyconfig_set(filepath, *, report=None):
from os.path import basename, splitext
if _bpy.app.debug_python:
@@ -712,14 +712,14 @@ def keyconfig_set(filepath, report=None):
return True
-def user_resource(resource_type, path="", create=False):
+def user_resource(resource_type, *, path="", create=False):
"""
Return a user resource path (normally from the users home directory).
:arg type: Resource type in ['DATAFILES', 'CONFIG', 'SCRIPTS', 'AUTOSAVE'].
:type type: string
- :arg subdir: Optional subdirectory.
- :type subdir: string
+ :arg path: Optional subdirectory.
+ :type path: string
:arg create: Treat the path as a directory and create
it if its not existing.
:type create: boolean
@@ -727,7 +727,7 @@ def user_resource(resource_type, path="", create=False):
:rtype: string
"""
- target_path = _user_resource(resource_type, path)
+ target_path = _user_resource(resource_type, path=path)
if create:
# should always be true.
diff --git a/release/scripts/modules/bpy_extras/anim_utils.py b/release/scripts/modules/bpy_extras/anim_utils.py
index 269592b3bb1..0868b772a2b 100644
--- a/release/scripts/modules/bpy_extras/anim_utils.py
+++ b/release/scripts/modules/bpy_extras/anim_utils.py
@@ -296,7 +296,7 @@ def bake_action_iter(
pbone.keyframe_insert("rotation_axis_angle", index=-1, frame=f, group=name)
else: # euler, XYZ, ZXY etc
if euler_prev is not None:
- euler = pbone.matrix_basis.to_euler(obj.rotation_mode, euler_prev)
+ euler = pbone.matrix_basis.to_euler(pbone.rotation_mode, euler_prev)
pbone.rotation_euler = euler
del euler
euler_prev = pbone.rotation_euler.copy()
diff --git a/release/scripts/modules/bpy_extras/asset_utils.py b/release/scripts/modules/bpy_extras/asset_utils.py
index db982e119d4..1656c21a137 100644
--- a/release/scripts/modules/bpy_extras/asset_utils.py
+++ b/release/scripts/modules/bpy_extras/asset_utils.py
@@ -34,7 +34,7 @@ __all__ = (
class SpaceAssetInfo:
@classmethod
def is_asset_browser(cls, space_data: bpy.types.Space):
- return space_data.type == 'FILE_BROWSER' and space_data.browse_mode == 'ASSETS'
+ return space_data and space_data.type == 'FILE_BROWSER' and space_data.browse_mode == 'ASSETS'
@classmethod
def is_asset_browser_poll(cls, context: Context):
diff --git a/release/scripts/modules/bpy_extras/io_utils.py b/release/scripts/modules/bpy_extras/io_utils.py
index 19697b25f70..9e3c5bb64e0 100644
--- a/release/scripts/modules/bpy_extras/io_utils.py
+++ b/release/scripts/modules/bpy_extras/io_utils.py
@@ -94,13 +94,11 @@ class ExportHelper:
if check_extension is not None:
filepath = self.filepath
if os.path.basename(filepath):
- filepath = bpy.path.ensure_ext(
- filepath,
- self.filename_ext
- if check_extension
- else "",
- )
-
+ if check_extension:
+ filepath = bpy.path.ensure_ext(
+ os.path.splitext(filepath)[0],
+ self.filename_ext,
+ )
if filepath != self.filepath:
self.filepath = filepath
change_ext = True
@@ -449,7 +447,7 @@ def path_reference(
"""
import os
is_relative = filepath.startswith("//")
- filepath_abs = bpy.path.abspath(filepath, base_src, library)
+ filepath_abs = bpy.path.abspath(filepath, start=base_src, library=library)
filepath_abs = os.path.normpath(filepath_abs)
if mode in {'ABSOLUTE', 'RELATIVE', 'STRIP'}:
diff --git a/release/scripts/modules/bpy_extras/view3d_utils.py b/release/scripts/modules/bpy_extras/view3d_utils.py
index 9e4958802f1..2a8b3c2ce9b 100644
--- a/release/scripts/modules/bpy_extras/view3d_utils.py
+++ b/release/scripts/modules/bpy_extras/view3d_utils.py
@@ -64,7 +64,7 @@ def region_2d_to_vector_3d(region, rv3d, coord):
return view_vector
-def region_2d_to_origin_3d(region, rv3d, coord, clamp=None):
+def region_2d_to_origin_3d(region, rv3d, coord, *, clamp=None):
"""
Return the 3d view origin from the region relative 2d coords.
@@ -167,7 +167,7 @@ def region_2d_to_location_3d(region, rv3d, coord, depth_location):
)[0]
-def location_3d_to_region_2d(region, rv3d, coord, default=None):
+def location_3d_to_region_2d(region, rv3d, coord, *, default=None):
"""
Return the *region* relative 2d location of a 3d position.
diff --git a/release/scripts/modules/bpy_types.py b/release/scripts/modules/bpy_types.py
index ee9115b6643..29b53aedf78 100644
--- a/release/scripts/modules/bpy_types.py
+++ b/release/scripts/modules/bpy_types.py
@@ -150,7 +150,11 @@ class Object(bpy_types.ID):
class WindowManager(bpy_types.ID):
__slots__ = ()
- def popup_menu(self, draw_func, title="", icon='NONE'):
+ def popup_menu(
+ self, draw_func, *,
+ title="",
+ icon='NONE',
+ ):
import bpy
popup = self.popmenu_begin__internal(title, icon=icon)
@@ -176,7 +180,11 @@ class WindowManager(bpy_types.ID):
finally:
self.popover_end__internal(popup, keymap=keymap)
- def popup_menu_pie(self, event, draw_func, title="", icon='NONE'):
+ def popup_menu_pie(
+ self, event, draw_func, *,
+ title="",
+ icon='NONE',
+ ):
import bpy
pie = self.piemenu_begin__internal(title, icon=icon, event=event)
@@ -392,7 +400,7 @@ class EditBone(StructRNA, _GenericBone, metaclass=StructMetaPropGroup):
self.tail = self.head + vec
self.roll = other.roll
- def transform(self, matrix, scale=True, roll=True):
+ def transform(self, matrix, *, scale=True, roll=True):
"""
Transform the the bones head, tail, roll and envelope
(when the matrix has a scale component).
@@ -560,9 +568,17 @@ class Text(bpy_types.ID):
self.write(string)
def as_module(self):
- from os.path import splitext
+ import bpy
+ from os.path import splitext, join
from types import ModuleType
- mod = ModuleType(splitext(self.name)[0])
+ name = self.name
+ mod = ModuleType(splitext(name)[0])
+ # This is a fake file-path, set this since some scripts check `__file__`,
+ # error messages may include this as well.
+ # NOTE: the file path may be a blank string if the file hasn't been saved.
+ mod.__dict__.update({
+ "__file__": join(bpy.data.filepath, name),
+ })
# TODO: We could use Text.compiled (C struct member)
# if this is called often it will be much faster.
exec(self.as_string(), mod.__dict__)
@@ -657,16 +673,14 @@ class Gizmo(StructRNA):
use_blend = color[3] < 1.0
if use_blend:
- # TODO: wrap GPU_blend from GPU state.
- from bgl import glEnable, glDisable, GL_BLEND
- glEnable(GL_BLEND)
+ gpu.state.blend_set('ALPHA')
with gpu.matrix.push_pop():
gpu.matrix.multiply_matrix(matrix)
batch.draw()
if use_blend:
- glDisable(GL_BLEND)
+ gpu.state.blend_set('NONE')
@staticmethod
def new_custom_shape(type, verts):
@@ -733,7 +747,7 @@ class Operator(StructRNA, metaclass=RNAMeta):
return delattr(properties, attr)
return super().__delattr__(attr)
- def as_keywords(self, ignore=()):
+ def as_keywords(self, *, ignore=()):
"""Return a copy of the properties as a dictionary"""
ignore = ignore + ("rna_type",)
return {attr: getattr(self, attr)
@@ -923,7 +937,7 @@ class Menu(StructRNA, _GenericUI, metaclass=RNAMeta):
# Perform a "natural sort", so 20 comes after 3 (for example).
files.sort(
key=lambda file_path:
- tuple(int(t) if t.isdigit() else t for t in re.split("(\d+)", file_path[0].lower())),
+ tuple(int(t) if t.isdigit() else t for t in re.split(r"(\d+)", file_path[0].lower())),
)
col = layout.column(align=True)
diff --git a/release/scripts/modules/console/complete_calltip.py b/release/scripts/modules/console/complete_calltip.py
index 4271003ae13..706af67905b 100644
--- a/release/scripts/modules/console/complete_calltip.py
+++ b/release/scripts/modules/console/complete_calltip.py
@@ -86,7 +86,7 @@ def get_doc(obj):
return result and RE_EMPTY_LINE.sub('', result.rstrip()) or ''
-def get_argspec(func, strip_self=True, doc=None, source=None):
+def get_argspec(func, *, strip_self=True, doc=None, source=None):
"""Get argument specifications.
:param strip_self: strip `self` from argspec
diff --git a/release/scripts/modules/console/complete_import.py b/release/scripts/modules/console/complete_import.py
index 6f51f6b691b..4bdd4eb188a 100644
--- a/release/scripts/modules/console/complete_import.py
+++ b/release/scripts/modules/console/complete_import.py
@@ -143,7 +143,7 @@ def complete(line):
"""
import inspect
- def try_import(mod, only_modules=False):
+ def try_import(mod, *, only_modules=False):
def is_importable(module, attr):
if only_modules:
@@ -184,7 +184,7 @@ def complete(line):
mod = words[1].split('.')
if len(mod) < 2:
return filter_prefix(get_root_modules(), words[-1])
- completion_list = try_import('.'.join(mod[:-1]), True)
+ completion_list = try_import('.'.join(mod[:-1]), only_modules=True)
completion_list = ['.'.join(mod[:-1] + [el]) for el in completion_list]
return filter_prefix(completion_list, words[-1])
if len(words) >= 3 and words[0] == 'from':
diff --git a/release/scripts/modules/console/complete_namespace.py b/release/scripts/modules/console/complete_namespace.py
index 3d8ba6b04a2..fa6323dcc66 100644
--- a/release/scripts/modules/console/complete_namespace.py
+++ b/release/scripts/modules/console/complete_namespace.py
@@ -62,7 +62,7 @@ def complete_names(word, namespace):
return sorted(set(completer.matches))
-def complete_indices(word, namespace, obj=None, base=None):
+def complete_indices(word, namespace, *, obj=None, base=None):
"""Complete a list or dictionary with its indices:
* integer numbers for list
@@ -117,7 +117,7 @@ def complete_indices(word, namespace, obj=None, base=None):
return matches
-def complete(word, namespace, private=True):
+def complete(word, namespace, *, private=True):
"""Complete word within a namespace with the standard rlcompleter
module. Also supports index or key access [].
@@ -191,7 +191,7 @@ def complete(word, namespace, private=True):
# an extra char '[', '(' or '.' will be added
if hasattr(obj, '__getitem__') and not is_struct_seq(obj):
# list or dictionary
- matches = complete_indices(word, namespace, obj)
+ matches = complete_indices(word, namespace, obj=obj)
elif hasattr(obj, '__call__'):
# callables
matches = [word + '(']
diff --git a/release/scripts/modules/console/intellisense.py b/release/scripts/modules/console/intellisense.py
index 249ddc29e4c..7e293ee0082 100644
--- a/release/scripts/modules/console/intellisense.py
+++ b/release/scripts/modules/console/intellisense.py
@@ -87,7 +87,7 @@ def complete(line, cursor, namespace, private):
matches.sort()
else:
from . import complete_namespace
- matches = complete_namespace.complete(word, namespace, private)
+ matches = complete_namespace.complete(word, namespace, private=private)
else:
# for now we don't have completers for strings
# TODO: add file auto completer for strings
@@ -96,7 +96,7 @@ def complete(line, cursor, namespace, private):
return matches, word
-def expand(line, cursor, namespace, private=True):
+def expand(line, cursor, namespace, *, private=True):
"""This method is invoked when the user asks autocompletion,
e.g. when Ctrl+Space is clicked.
@@ -150,5 +150,5 @@ def expand(line, cursor, namespace, private=True):
line = line[:cursor] + prefix + line[cursor:]
cursor += len(prefix.encode('utf-8'))
if no_calltip and prefix.endswith('('):
- return expand(line, cursor, namespace, private)
+ return expand(line, cursor, namespace, private=private)
return line, cursor, scrollback
diff --git a/release/scripts/modules/gpu_extras/batch.py b/release/scripts/modules/gpu_extras/batch.py
index 64d731e318a..e47afb24539 100644
--- a/release/scripts/modules/gpu_extras/batch.py
+++ b/release/scripts/modules/gpu_extras/batch.py
@@ -21,7 +21,7 @@ __all__ = (
)
-def batch_for_shader(shader, type, content, indices=None):
+def batch_for_shader(shader, type, content, *, indices=None):
"""
Return a batch already configured and compatible with the shader.
diff --git a/release/scripts/modules/gpu_extras/presets.py b/release/scripts/modules/gpu_extras/presets.py
index 81d515904a1..6d12533e950 100644
--- a/release/scripts/modules/gpu_extras/presets.py
+++ b/release/scripts/modules/gpu_extras/presets.py
@@ -16,7 +16,7 @@
#
# ***** END GPL LICENSE BLOCK *****
-def draw_circle_2d(position, color, radius, segments=32):
+def draw_circle_2d(position, color, radius, *, segments=32):
"""
Draw a circle.
@@ -57,12 +57,12 @@ def draw_circle_2d(position, color, radius, segments=32):
batch.draw()
-def draw_texture_2d(texture_id, position, width, height):
+def draw_texture_2d(texture, position, width, height):
"""
Draw a 2d texture.
- :arg texture_id: OpenGL id of the texture (e.g. :class:`bpy.types.Image.bindcode`).
- :type texture_id: int
+ :arg texture: GPUTexture to draw (e.g. gpu.texture.from_image(image) for :class:`bpy.types.Image`).
+ :type texture: :class:`gpu.types.GPUTexture`
:arg position: Position of the lower left corner.
:type position: 2D Vector
:arg width: Width of the image when drawn (not necessarily
@@ -72,7 +72,6 @@ def draw_texture_2d(texture_id, position, width, height):
:type height: float
"""
import gpu
- import bgl
from . batch import batch_for_shader
coords = ((0, 0), (1, 0), (1, 1), (0, 1))
@@ -83,14 +82,20 @@ def draw_texture_2d(texture_id, position, width, height):
{"pos": coords, "texCoord": coords},
)
- bgl.glActiveTexture(bgl.GL_TEXTURE0)
- bgl.glBindTexture(bgl.GL_TEXTURE_2D, texture_id)
-
with gpu.matrix.push_pop():
gpu.matrix.translate(position)
gpu.matrix.scale((width, height))
shader = gpu.shader.from_builtin('2D_IMAGE')
shader.bind()
- shader.uniform_int("image", 0)
+
+ if isinstance(texture, int):
+ # Call the legacy bgl to not break the existing API
+ import bgl
+ bgl.glActiveTexture(bgl.GL_TEXTURE0)
+ bgl.glBindTexture(bgl.GL_TEXTURE_2D, texture)
+ shader.uniform_int("image", 0)
+ else:
+ shader.uniform_sampler("image", texture)
+
batch.draw(shader)
diff --git a/release/scripts/modules/keyingsets_utils.py b/release/scripts/modules/keyingsets_utils.py
index 190f0282339..b7a15bbbc19 100644
--- a/release/scripts/modules/keyingsets_utils.py
+++ b/release/scripts/modules/keyingsets_utils.py
@@ -219,6 +219,40 @@ def RKS_GEN_scaling(_ksi, _context, ks, data):
else:
ks.paths.add(id_block, path)
+
+# Custom Properties
+def RKS_GEN_custom_props(_ksi, _context, ks, data):
+ # get id-block and path info
+ id_block, base_path, grouping = get_transform_generators_base_info(data)
+
+ # Only some RNA types can be animated.
+ prop_type_compat = {bpy.types.BoolProperty,
+ bpy.types.IntProperty,
+ bpy.types.FloatProperty}
+
+ # When working with a pose, 'id_block' is the armature object (which should
+ # get the animation data), whereas 'data' is the bone being keyed.
+ for cprop_name in data.keys():
+ # ignore special "_RNA_UI" used for UI editing
+ if cprop_name == "_RNA_UI":
+ continue
+
+ prop_path = '["%s"]' % bpy.utils.escape_identifier(cprop_name)
+ try:
+ rna_property = data.path_resolve(prop_path, False)
+ except ValueError as ex:
+ # This happens when a custom property is set to None. In that case it cannot
+ # be converted to an FCurve-compatible value, so we can't keyframe it anyway.
+ continue
+ if rna_property.rna_type not in prop_type_compat:
+ continue
+
+ path = "%s%s" % (base_path, prop_path)
+ if grouping:
+ ks.paths.add(id_block, path, group_method='NAMED', group_name=grouping)
+ else:
+ ks.paths.add(id_block, path)
+
# ------
diff --git a/release/scripts/modules/nodeitems_utils.py b/release/scripts/modules/nodeitems_utils.py
index d1e1cc5e346..a50997fab5f 100644
--- a/release/scripts/modules/nodeitems_utils.py
+++ b/release/scripts/modules/nodeitems_utils.py
@@ -25,7 +25,7 @@ class NodeCategory:
def poll(cls, _context):
return True
- def __init__(self, identifier, name, description="", items=None):
+ def __init__(self, identifier, name, *, description="", items=None):
self.identifier = identifier
self.name = name
self.description = description
@@ -43,7 +43,7 @@ class NodeCategory:
class NodeItem:
- def __init__(self, nodetype, label=None, settings=None, poll=None):
+ def __init__(self, nodetype, *, label=None, settings=None, poll=None):
if settings is None:
settings = {}
@@ -92,7 +92,7 @@ class NodeItem:
class NodeItemCustom:
- def __init__(self, poll=None, draw=None):
+ def __init__(self, *, poll=None, draw=None):
self.poll = poll
self.draw = draw
diff --git a/release/scripts/modules/rna_keymap_ui.py b/release/scripts/modules/rna_keymap_ui.py
index 6076bf00063..365d6c0087f 100644
--- a/release/scripts/modules/rna_keymap_ui.py
+++ b/release/scripts/modules/rna_keymap_ui.py
@@ -421,7 +421,7 @@ def draw_keymaps(context, layout):
rowsubsub.prop(spref, "filter_text", text="", icon='VIEWZOOM')
if not filter_text:
- # When the keyconfig defines it's own preferences.
+ # When the keyconfig defines its own preferences.
kc_prefs = kc_active.preferences
if kc_prefs is not None:
box = col.box()
diff --git a/release/scripts/modules/rna_manual_reference.py b/release/scripts/modules/rna_manual_reference.py
index 2ed1e22a94f..552f7eeb4f3 100644
--- a/release/scripts/modules/rna_manual_reference.py
+++ b/release/scripts/modules/rna_manual_reference.py
@@ -41,10 +41,12 @@ url_manual_mapping = (
("bpy.types.fluiddomainsettings.sndparticle_potential_max_wavecrest*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-potential-max-wavecrest"),
("bpy.types.fluiddomainsettings.sndparticle_potential_min_wavecrest*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-potential-min-wavecrest"),
("bpy.types.movietrackingsettings.refine_intrinsics_principal_point*", "movie_clip/tracking/clip/toolbar/solve.html#bpy-types-movietrackingsettings-refine-intrinsics-principal-point"),
+ ("bpy.types.cyclesrenderlayersettings.denoising_optix_input_passes*", "render/layers/denoising.html#bpy-types-cyclesrenderlayersettings-denoising-optix-input-passes"),
("bpy.types.fluiddomainsettings.sndparticle_potential_max_energy*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-potential-max-energy"),
("bpy.types.fluiddomainsettings.sndparticle_potential_min_energy*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-potential-min-energy"),
("bpy.types.movietrackingsettings.refine_intrinsics_focal_length*", "movie_clip/tracking/clip/toolbar/solve.html#bpy-types-movietrackingsettings-refine-intrinsics-focal-length"),
("bpy.types.rigidbodyconstraint.rigidbodyconstraint.use_breaking*", "physics/rigid_body/constraints/introduction.html#bpy-types-rigidbodyconstraint-rigidbodyconstraint-use-breaking"),
+ ("bpy.types.cyclesrendersettings.preview_denoising_input_passes*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-preview-denoising-input-passes"),
("bpy.types.fluiddomainsettings.sndparticle_sampling_trappedair*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-sampling-trappedair"),
("bpy.types.fluiddomainsettings.sndparticle_sampling_wavecrest*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-sampling-wavecrest"),
("bpy.types.rigidbodyconstraint.use_override_solver_iterations*", "physics/rigid_body/constraints/introduction.html#bpy-types-rigidbodyconstraint-use-override-solver-iterations"),
@@ -56,6 +58,7 @@ url_manual_mapping = (
("bpy.types.fluiddomainsettings.sndparticle_combined_export*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-combined-export"),
("bpy.types.fluiddomainsettings.use_collision_border_bottom*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-collision-border-bottom"),
("bpy.types.fluiddomainsettings.vector_scale_with_magnitude*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-vector-scale-with-magnitude"),
+ ("bpy.types.spacespreadsheet.display_context_path_collapsed*", "editors/spreadsheet.html#bpy-types-spacespreadsheet-display-context-path-collapsed"),
("bpy.types.fluiddomainsettings.use_collision_border_front*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-collision-border-front"),
("bpy.types.fluiddomainsettings.use_collision_border_right*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-collision-border-right"),
("bpy.types.cyclesobjectsettings.use_adaptive_subdivision*", "render/cycles/object_settings/adaptive_subdiv.html#bpy-types-cyclesobjectsettings-use-adaptive-subdivision"),
@@ -64,16 +67,20 @@ url_manual_mapping = (
("bpy.types.fluiddomainsettings.use_collision_border_left*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-collision-border-left"),
("bpy.types.rendersettings_simplify_gpencil_view_modifier*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-gpencil-view-modifier"),
("bpy.types.brushgpencilsettings.use_settings_stabilizer*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-use-settings-stabilizer"),
+ ("bpy.types.colormanagedsequencercolorspacesettings.name*", "render/color_management.html#bpy-types-colormanagedsequencercolorspacesettings-name"),
("bpy.types.fluiddomainsettings.use_collision_border_top*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-collision-border-top"),
("bpy.types.gpencilsculptsettings.use_multiframe_falloff*", "grease_pencil/multiframe.html#bpy-types-gpencilsculptsettings-use-multiframe-falloff"),
("bpy.types.movietrackingsettings.use_keyframe_selection*", "movie_clip/tracking/clip/toolbar/solve.html#bpy-types-movietrackingsettings-use-keyframe-selection"),
("bpy.types.rendersettings.simplify_gpencil_antialiasing*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-gpencil-antialiasing"),
+ ("bpy.types.spaceoutliner.use_filter_lib_override_system*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-lib-override-system"),
("bpy.types.toolsettings.use_transform_pivot_point_align*", "scene_layout/object/tools/tool_settings.html#bpy-types-toolsettings-use-transform-pivot-point-align"),
("bpy.types.brush.show_multiplane_scrape_planes_preview*", "sculpt_paint/sculpting/tools/multiplane_scrape.html#bpy-types-brush-show-multiplane-scrape-planes-preview"),
("bpy.types.cyclesrendersettings.offscreen_dicing_scale*", "render/cycles/render_settings/subdivision.html#bpy-types-cyclesrendersettings-offscreen-dicing-scale"),
("bpy.types.fluiddomainsettings.sndparticle_bubble_drag*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-bubble-drag"),
("bpy.types.linestylegeometrymodifier_backbonestretcher*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/backbone_stretcher.html#bpy-types-linestylegeometrymodifier-backbonestretcher"),
("bpy.types.linestylegeometrymodifier_sinusdisplacement*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/sinus_displacement.html#bpy-types-linestylegeometrymodifier-sinusdisplacement"),
+ ("bpy.types.colormanageddisplaysettings.display_device*", "render/color_management.html#bpy-types-colormanageddisplaysettings-display-device"),
+ ("bpy.types.colormanagedviewsettings.use_curve_mapping*", "render/color_management.html#bpy-types-colormanagedviewsettings-use-curve-mapping"),
("bpy.types.fluiddomainsettings.color_ramp_field_scale*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-color-ramp-field-scale"),
("bpy.types.fluiddomainsettings.use_adaptive_timesteps*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-adaptive-timesteps"),
("bpy.types.fluiddomainsettings.use_dissolve_smoke_log*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-dissolve-smoke-log"),
@@ -107,6 +114,7 @@ url_manual_mapping = (
("bpy.types.fluiddomainsettings.use_bubble_particles*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-use-bubble-particles"),
("bpy.types.linestylegeometrymodifier_simplification*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/simplification.html#bpy-types-linestylegeometrymodifier-simplification"),
("bpy.types.materialgpencilstyle.use_overlap_strokes*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-use-overlap-strokes"),
+ ("bpy.types.spacespreadsheet.geometry_component_type*", "editors/spreadsheet.html#bpy-types-spacespreadsheet-geometry-component-type"),
("bpy.types.toolsettings.use_gpencil_weight_data_add*", "grease_pencil/modes/draw/introduction.html#bpy-types-toolsettings-use-gpencil-weight-data-add"),
("bpy.types.view3doverlay.texture_paint_mode_opacity*", "editors/3dview/display/overlays.html#bpy-types-view3doverlay-texture-paint-mode-opacity"),
("bpy.types.bakesettings.use_pass_ambient_occlusion*", "render/cycles/baking.html#bpy-types-bakesettings-use-pass-ambient-occlusion"),
@@ -114,6 +122,8 @@ url_manual_mapping = (
("bpy.types.brush.use_cloth_pin_simulation_boundary*", "sculpt_paint/sculpting/tools/cloth.html#bpy-types-brush-use-cloth-pin-simulation-boundary"),
("bpy.types.brushgpencilsettings.show_fill_boundary*", "grease_pencil/modes/draw/tools/fill.html#bpy-types-brushgpencilsettings-show-fill-boundary"),
("bpy.types.brushgpencilsettings.use_default_eraser*", "grease_pencil/modes/draw/tools/erase.html#bpy-types-brushgpencilsettings-use-default-eraser"),
+ ("bpy.types.colormanagedsequencercolorspacesettings*", "render/color_management.html#bpy-types-colormanagedsequencercolorspacesettings"),
+ ("bpy.types.colormanagedviewsettings.view_transform*", "render/color_management.html#bpy-types-colormanagedviewsettings-view-transform"),
("bpy.types.cyclesrendersettings.camera_cull_margin*", "render/cycles/render_settings/simplify.html#bpy-types-cyclesrendersettings-camera-cull-margin"),
("bpy.types.fluiddomainsettings.export_manta_script*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-export-manta-script"),
("bpy.types.fluiddomainsettings.fractions_threshold*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-fractions-threshold"),
@@ -139,6 +149,7 @@ url_manual_mapping = (
("bpy.ops.gpencil.vertex_color_brightness_contrast*", "grease_pencil/modes/vertex_paint/editing.html#bpy-ops-gpencil-vertex-color-brightness-contrast"),
("bpy.ops.view3d.edit_mesh_extrude_individual_move*", "modeling/meshes/editing/face/extrude_faces.html#bpy-ops-view3d-edit-mesh-extrude-individual-move"),
("bpy.ops.view3d.edit_mesh_extrude_manifold_normal*", "modeling/meshes/tools/extrude_manifold.html#bpy-ops-view3d-edit-mesh-extrude-manifold-normal"),
+ ("bpy.types.cyclesrendersettings.ao_bounces_render*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-ao-bounces-render"),
("bpy.types.cyclesrendersettings.use_distance_cull*", "render/cycles/render_settings/simplify.html#bpy-types-cyclesrendersettings-use-distance-cull"),
("bpy.types.fluiddomainsettings.cache_frame_offset*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-cache-frame-offset"),
("bpy.types.fluiddomainsettings.delete_in_obstacle*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-delete-in-obstacle"),
@@ -160,6 +171,7 @@ url_manual_mapping = (
("bpy.types.rigidbodyconstraint.breaking_threshold*", "physics/rigid_body/constraints/introduction.html#bpy-types-rigidbodyconstraint-breaking-threshold"),
("bpy.types.spacedopesheeteditor.show_pose_markers*", "animation/markers.html#bpy-types-spacedopesheeteditor-show-pose-markers"),
("bpy.types.spaceoutliner.use_filter_object_camera*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-object-camera"),
+ ("bpy.types.spaceoutliner.use_filter_object_others*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-object-others"),
("bpy.types.toolsettings.proportional_edit_falloff*", "editors/3dview/controls/proportional_editing.html#bpy-types-toolsettings-proportional-edit-falloff"),
("bpy.types.toolsettings.use_edge_path_live_unwrap*", "modeling/meshes/tools/tool_settings.html#bpy-types-toolsettings-use-edge-path-live-unwrap"),
("bpy.types.toolsettings.use_gpencil_draw_additive*", "grease_pencil/modes/draw/introduction.html#bpy-types-toolsettings-use-gpencil-draw-additive"),
@@ -188,11 +200,15 @@ url_manual_mapping = (
("bpy.types.materialgpencilstyle.use_fill_holdout*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-use-fill-holdout"),
("bpy.types.particlesettings.use_parent_particles*", "physics/particles/emitter/render.html#bpy-types-particlesettings-use-parent-particles"),
("bpy.types.rigidbodyconstraint.solver_iterations*", "physics/rigid_body/constraints/introduction.html#bpy-types-rigidbodyconstraint-solver-iterations"),
+ ("bpy.types.spaceoutliner.use_filter_lib_override*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-lib-override"),
("bpy.types.spaceoutliner.use_filter_object_empty*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-object-empty"),
("bpy.types.spaceoutliner.use_filter_object_light*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-object-light"),
+ ("bpy.types.spacesequenceeditor.proxy_render_size*", "video_editing/preview/sidebar.html#bpy-types-spacesequenceeditor-proxy-render-size"),
("bpy.types.spacesequenceeditor.show_strip_offset*", "video_editing/sequencer/navigating.html#bpy-types-spacesequenceeditor-show-strip-offset"),
("bpy.types.spacesequenceeditor.show_strip_source*", "video_editing/sequencer/navigating.html#bpy-types-spacesequenceeditor-show-strip-source"),
("bpy.types.toolsettings.gpencil_stroke_placement*", "grease_pencil/modes/draw/stroke_placement.html#bpy-types-toolsettings-gpencil-stroke-placement"),
+ ("bpy.types.toolsettings.use_keyframe_cycle_aware*", "editors/timeline.html#bpy-types-toolsettings-use-keyframe-cycle-aware"),
+ ("bpy.types.toolsettings.use_keyframe_insert_auto*", "editors/timeline.html#bpy-types-toolsettings-use-keyframe-insert-auto"),
("bpy.types.viewlayer.use_pass_cryptomatte_object*", "render/layers/passes.html#bpy-types-viewlayer-use-pass-cryptomatte-object"),
("bpy.ops.armature.rigify_apply_selection_colors*", "addons/rigging/rigify/metarigs.html#bpy-ops-armature-rigify-apply-selection-colors"),
("bpy.types.brushgpencilsettings.fill_layer_mode*", "grease_pencil/modes/draw/tools/fill.html#bpy-types-brushgpencilsettings-fill-layer-mode"),
@@ -206,6 +222,7 @@ url_manual_mapping = (
("bpy.types.rendersettings.resolution_percentage*", "render/output/properties/dimensions.html#bpy-types-rendersettings-resolution-percentage"),
("bpy.types.rendersettings_simplify_gpencil_tint*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-gpencil-tint"),
("bpy.types.spaceoutliner.use_filter_object_mesh*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-object-mesh"),
+ ("bpy.types.spacesequenceeditor.show_overexposed*", "video_editing/preview/sidebar.html#bpy-types-spacesequenceeditor-show-overexposed"),
("bpy.types.toolsettings.use_gpencil_draw_onback*", "grease_pencil/modes/draw/introduction.html#bpy-types-toolsettings-use-gpencil-draw-onback"),
("bpy.types.toolsettings.use_snap_align_rotation*", "editors/3dview/controls/snapping.html#bpy-types-toolsettings-use-snap-align-rotation"),
("bpy.types.viewlayer.use_pass_cryptomatte_asset*", "render/layers/passes.html#bpy-types-viewlayer-use-pass-cryptomatte-asset"),
@@ -236,11 +253,14 @@ url_manual_mapping = (
("bpy.types.materialgpencilstyle.alignment_mode*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-alignment-mode"),
("bpy.types.particlesettings.use_modifier_stack*", "physics/particles/emitter/emission.html#bpy-types-particlesettings-use-modifier-stack"),
("bpy.types.rendersettings.simplify_subdivision*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-subdivision"),
+ ("bpy.types.spacegrapheditor.show_extrapolation*", "editors/graph_editor/introduction.html#bpy-types-spacegrapheditor-show-extrapolation"),
("bpy.types.spaceoutliner.use_filter_collection*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-collection"),
+ ("bpy.types.spacesequenceeditor.display_channel*", "video_editing/preview/sidebar.html#bpy-types-spacesequenceeditor-display-channel"),
("bpy.types.spacesequenceeditor.show_annotation*", "video_editing/preview/introduction.html#bpy-types-spacesequenceeditor-show-annotation"),
("bpy.types.spacesequenceeditor.show_region_hud*", "video_editing/sequencer/navigating.html#bpy-types-spacesequenceeditor-show-region-hud"),
("bpy.types.spacesequenceeditor.show_safe_areas*", "video_editing/preview/introduction.html#bpy-types-spacesequenceeditor-show-safe-areas"),
("bpy.types.spacesequenceeditor.show_strip_name*", "video_editing/sequencer/navigating.html#bpy-types-spacesequenceeditor-show-strip-name"),
+ ("bpy.types.spacespreadsheet.show_only_selected*", "editors/spreadsheet.html#bpy-types-spacespreadsheet-show-only-selected"),
("bpy.types.toolsettings.use_snap_grid_absolute*", "editors/3dview/controls/snapping.html#bpy-types-toolsettings-use-snap-grid-absolute"),
("bpy.types.view3doverlay.show_face_orientation*", "editors/3dview/display/overlays.html#bpy-types-view3doverlay-show-face-orientation"),
("bpy.ops.object.blenderkit_material_thumbnail*", "addons/3d_view/blenderkit.html#bpy-ops-object-blenderkit-material-thumbnail"),
@@ -275,11 +295,13 @@ url_manual_mapping = (
("bpy.types.nodesocketinterface*.default_value*", "interface/controls/nodes/groups.html#bpy-types-nodesocketinterface-default-value"),
("bpy.types.rendersettings.use_persistent_data*", "render/cycles/render_settings/performance.html#bpy-types-rendersettings-use-persistent-data"),
("bpy.types.spaceoutliner.show_restrict_column*", "editors/outliner/interface.html#bpy-types-spaceoutliner-show-restrict-column"),
+ ("bpy.types.spacespreadsheet.object_eval_state*", "editors/spreadsheet.html#bpy-types-spacespreadsheet-object-eval-state"),
("bpy.types.toolsettings.transform_pivot_point*", "editors/3dview/controls/pivot_point/index.html#bpy-types-toolsettings-transform-pivot-point"),
("bpy.types.toolsettings.use_proportional_edit*", "editors/3dview/controls/proportional_editing.html#bpy-types-toolsettings-use-proportional-edit"),
("bpy.types.volumedisplay.interpolation_method*", "modeling/volumes/properties.html#bpy-types-volumedisplay-interpolation-method"),
("bpy.types.clothsettings.use_pressure_volume*", "physics/cloth/settings/physical_properties.html#bpy-types-clothsettings-use-pressure-volume"),
("bpy.types.clothsettings.vertex_group_intern*", "physics/cloth/settings/physical_properties.html#bpy-types-clothsettings-vertex-group-intern"),
+ ("bpy.types.colormanagedviewsettings.exposure*", "render/color_management.html#bpy-types-colormanagedviewsettings-exposure"),
("bpy.types.cyclesrendersettings.*dicing_rate*", "render/cycles/render_settings/subdivision.html#bpy-types-cyclesrendersettings-dicing-rate"),
("bpy.types.fluiddomainsettings.cfl_condition*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-cfl-condition"),
("bpy.types.fluiddomainsettings.show_velocity*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-show-velocity"),
@@ -294,6 +316,7 @@ url_manual_mapping = (
("bpy.types.geometrynodealignrotationtovector*", "modeling/geometry_nodes/point/align_rotation_to_vector.html#bpy-types-geometrynodealignrotationtovector"),
("bpy.types.greasepencil.curve_edit_threshold*", "grease_pencil/modes/edit/curve_editing.html#bpy-types-greasepencil-curve-edit-threshold"),
("bpy.types.materialgpencilstyle.stroke_style*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-stroke-style"),
+ ("bpy.types.objectlineart.use_crease_override*", "scene_layout/object/properties/line_art.html#bpy-types-objectlineart-use-crease-override"),
("bpy.types.rendersettings.preview_pixel_size*", "render/cycles/render_settings/performance.html#bpy-types-rendersettings-preview-pixel-size"),
("bpy.types.rendersettings.use_crop_to_border*", "render/output/properties/dimensions.html#bpy-types-rendersettings-use-crop-to-border"),
("bpy.types.rendersettings.use_file_extension*", "render/output/properties/output.html#bpy-types-rendersettings-use-file-extension"),
@@ -302,6 +325,7 @@ url_manual_mapping = (
("bpy.types.spaceoutliner.use_filter_children*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-children"),
("bpy.types.spaceoutliner.use_filter_complete*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-complete"),
("bpy.types.spacesequenceeditor.show_metadata*", "video_editing/preview/introduction.html#bpy-types-spacesequenceeditor-show-metadata"),
+ ("bpy.types.spacespreadsheet.attribute_domain*", "editors/spreadsheet.html#bpy-types-spacespreadsheet-attribute-domain"),
("bpy.types.spaceview3d.transform_orientation*", "editors/3dview/controls/orientation.html#bpy-types-spaceview3d-transform-orientation"),
("bpy.types.spaceview3d.use_local_collections*", "editors/3dview/sidebar.html#bpy-types-spaceview3d-use-local-collections"),
("bpy.types.toolsettings.use_snap_peel_object*", "editors/3dview/controls/snapping.html#bpy-types-toolsettings-use-snap-peel-object"),
@@ -315,6 +339,7 @@ url_manual_mapping = (
("bpy.types.brushgpencilsettings.fill_factor*", "grease_pencil/modes/draw/tools/fill.html#bpy-types-brushgpencilsettings-fill-factor"),
("bpy.types.curve.bevel_factor_mapping_start*", "modeling/curves/properties/geometry.html#bpy-types-curve-bevel-factor-mapping-start"),
("bpy.types.cyclesobjectsettings.dicing_rate*", "render/cycles/object_settings/adaptive_subdiv.html#bpy-types-cyclesobjectsettings-dicing-rate"),
+ ("bpy.types.cyclesrendersettings.use_fast_gi*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-use-fast-gi"),
("bpy.types.fluiddomainsettings.adapt_margin*", "physics/fluid/type/domain/gas/adaptive_domain.html#bpy-types-fluiddomainsettings-adapt-margin"),
("bpy.types.fluiddomainsettings.burning_rate*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-burning-rate"),
("bpy.types.fluiddomainsettings.guide_parent*", "physics/fluid/type/domain/guides.html#bpy-types-fluiddomainsettings-guide-parent"),
@@ -335,6 +360,7 @@ url_manual_mapping = (
("bpy.types.posebone.use_ik_rotation_control*", "animation/armatures/posing/bone_constraints/inverse_kinematics/introduction.html#bpy-types-posebone-use-ik-rotation-control"),
("bpy.types.rendersettings.use_bake_multires*", "render/cycles/baking.html#bpy-types-rendersettings-use-bake-multires"),
("bpy.types.scenegpencil.antialias_threshold*", "render/cycles/render_settings/grease_pencil.html#bpy-types-scenegpencil-antialias-threshold"),
+ ("bpy.types.spacesequenceeditor.overlay_type*", "video_editing/preview/sidebar.html#bpy-types-spacesequenceeditor-overlay-type"),
("bpy.types.spacesequenceeditor.show_fcurves*", "video_editing/sequencer/navigating.html#bpy-types-spacesequenceeditor-show-fcurves"),
("bpy.types.spaceuveditor.sticky_select_mode*", "editors/uv/selecting.html#bpy-types-spaceuveditor-sticky-select-mode"),
("bpy.types.spaceview3d.show_object_viewport*", "editors/3dview/display/visibility.html#bpy-types-spaceview3d-show-object-viewport"),
@@ -353,6 +379,7 @@ url_manual_mapping = (
("bpy.types.brush.disconnected_distance_max*", "sculpt_paint/sculpting/tools/pose.html#bpy-types-brush-disconnected-distance-max"),
("bpy.types.brush.surface_smooth_iterations*", "sculpt_paint/sculpting/tools/smooth.html#bpy-types-brush-surface-smooth-iterations"),
("bpy.types.brushgpencilsettings.pen_jitter*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-pen-jitter"),
+ ("bpy.types.cyclesrendersettings.ao_bounces*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-ao-bounces"),
("bpy.types.fluiddomainsettings.domain_type*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-domain-type"),
("bpy.types.fluiddomainsettings.flame_smoke*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-flame-smoke"),
("bpy.types.fluiddomainsettings.fluid_group*", "physics/fluid/type/domain/collections.html#bpy-types-fluiddomainsettings-fluid-group"),
@@ -373,6 +400,7 @@ url_manual_mapping = (
("bpy.types.materialgpencilstyle.fill_style*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-fill-style"),
("bpy.types.materialgpencilstyle.mix_factor*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-mix-factor"),
("bpy.types.materialgpencilstyle.pass_index*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-pass-index"),
+ ("bpy.types.nodesocketinterface.description*", "interface/controls/nodes/groups.html#bpy-types-nodesocketinterface-description"),
("bpy.types.rendersettings.dither_intensity*", "render/output/properties/post_processing.html#bpy-types-rendersettings-dither-intensity"),
("bpy.types.rendersettings.simplify_volumes*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-volumes"),
("bpy.types.rendersettings.use_render_cache*", "render/output/properties/output.html#bpy-types-rendersettings-use-render-cache"),
@@ -382,6 +410,7 @@ url_manual_mapping = (
("bpy.types.sceneeevee.use_taa_reprojection*", "render/eevee/render_settings/sampling.html#bpy-types-sceneeevee-use-taa-reprojection"),
("bpy.types.sequenceeditor.use_overlay_lock*", "video_editing/preview/sidebar.html#bpy-types-sequenceeditor-use-overlay-lock"),
("bpy.types.spaceoutliner.use_filter_object*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-object"),
+ ("bpy.types.spacesequenceeditor.use_proxies*", "video_editing/preview/sidebar.html#bpy-types-spacesequenceeditor-use-proxies"),
("bpy.types.spaceuveditor.show_pixel_coords*", "editors/uv/sidebar.html#bpy-types-spaceuveditor-show-pixel-coords"),
("bpy.types.spaceview3d.show_reconstruction*", "editors/3dview/display/overlays.html#bpy-types-spaceview3d-show-reconstruction"),
("bpy.types.toolsettings.gpencil_selectmode*", "grease_pencil/selecting.html#bpy-types-toolsettings-gpencil-selectmode"),
@@ -392,6 +421,7 @@ url_manual_mapping = (
("bpy.types.view3doverlay.wireframe_opacity*", "editors/3dview/display/overlays.html#bpy-types-view3doverlay-wireframe-opacity"),
("bpy.ops.gpencil.active_frames_delete_all*", "grease_pencil/animation/tools.html#bpy-ops-gpencil-active-frames-delete-all"),
("bpy.ops.gpencil.stroke_merge_by_distance*", "grease_pencil/modes/edit/grease_pencil_menu.html#bpy-ops-gpencil-stroke-merge-by-distance"),
+ ("bpy.ops.node.collapse_hide_unused_toggle*", "interface/controls/nodes/editing.html#bpy-ops-node-collapse-hide-unused-toggle"),
("bpy.ops.object.anim_transforms_to_deltas*", "scene_layout/object/editing/apply.html#bpy-ops-object-anim-transforms-to-deltas"),
("bpy.ops.object.convert_proxy_to_override*", "files/linked_libraries/library_overrides.html#bpy-ops-object-convert-proxy-to-override"),
("bpy.ops.object.modifier_copy_to_selected*", "modeling/modifiers/introduction.html#bpy-ops-object-modifier-copy-to-selected"),
@@ -403,6 +433,7 @@ url_manual_mapping = (
("bpy.types.brushgpencilsettings.show_fill*", "grease_pencil/modes/draw/tools/fill.html#bpy-types-brushgpencilsettings-show-fill"),
("bpy.types.brushgpencilsettings.uv_random*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-uv-random"),
("bpy.types.clothsettings.internal_tension*", "physics/cloth/settings/physical_properties.html#bpy-types-clothsettings-internal-tension"),
+ ("bpy.types.colormanagedviewsettings.gamma*", "render/color_management.html#bpy-types-colormanagedviewsettings-gamma"),
("bpy.types.compositornodeplanetrackdeform*", "compositing/types/distort/plane_track_deform.html#bpy-types-compositornodeplanetrackdeform"),
("bpy.types.curve.bevel_factor_mapping_end*", "modeling/curves/properties/geometry.html#bpy-types-curve-bevel-factor-mapping-end"),
("bpy.types.fluiddomainsettings.cache_type*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-cache-type"),
@@ -431,6 +462,7 @@ url_manual_mapping = (
("bpy.types.nodesocketinterface*.max_value*", "interface/controls/nodes/groups.html#bpy-types-nodesocketinterface-max-value"),
("bpy.types.nodesocketinterface*.min_value*", "interface/controls/nodes/groups.html#bpy-types-nodesocketinterface-min-value"),
("bpy.types.nodesocketinterface.hide_value*", "interface/controls/nodes/groups.html#bpy-types-nodesocketinterface-hide-value"),
+ ("bpy.types.objectlineart.crease_threshold*", "scene_layout/object/properties/line_art.html#bpy-types-objectlineart-crease-threshold"),
("bpy.types.rendersettings.use_compositing*", "render/output/properties/post_processing.html#bpy-types-rendersettings-use-compositing"),
("bpy.types.rendersettings.use_placeholder*", "render/output/properties/output.html#bpy-types-rendersettings-use-placeholder"),
("bpy.types.shadernodesubsurfacescattering*", "render/shader_nodes/shader/sss.html#bpy-types-shadernodesubsurfacescattering"),
@@ -456,6 +488,7 @@ url_manual_mapping = (
("bpy.types.brush.multiplane_scrape_angle*", "sculpt_paint/sculpting/tools/multiplane_scrape.html#bpy-types-brush-multiplane-scrape-angle"),
("bpy.types.clothsettings.internal_spring*", "physics/cloth/settings/physical_properties.html#bpy-types-clothsettings-internal-spring"),
("bpy.types.clothsettings.pressure_factor*", "physics/cloth/settings/physical_properties.html#bpy-types-clothsettings-pressure-factor"),
+ ("bpy.types.colormanagedviewsettings.look*", "render/color_management.html#bpy-types-colormanagedviewsettings-look"),
("bpy.types.compositornodecolorcorrection*", "compositing/types/color/color_correction.html#bpy-types-compositornodecolorcorrection"),
("bpy.types.compositornodemoviedistortion*", "compositing/types/distort/movie_distortion.html#bpy-types-compositornodemoviedistortion"),
("bpy.types.fluiddomainsettings.use_guide*", "physics/fluid/type/domain/guides.html#bpy-types-fluiddomainsettings-use-guide"),
@@ -468,6 +501,7 @@ url_manual_mapping = (
("bpy.types.fluidflowsettings.temperature*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-temperature"),
("bpy.types.fluidflowsettings.use_texture*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-use-texture"),
("bpy.types.fmodifierenvelopecontrolpoint*", "editors/graph_editor/fcurves/sidebar/modifiers.html#bpy-types-fmodifierenvelopecontrolpoint"),
+ ("bpy.types.geometrynodeattributemaprange*", "modeling/geometry_nodes/attribute/attribute_map_range.html#bpy-types-geometrynodeattributemaprange"),
("bpy.types.layercollection.hide_viewport*", "editors/outliner/interface.html#bpy-types-layercollection-hide-viewport"),
("bpy.types.layercollection.indirect_only*", "editors/outliner/interface.html#bpy-types-layercollection-indirect-only"),
("bpy.types.material.use_sss_translucency*", "render/eevee/materials/settings.html#bpy-types-material-use-sss-translucency"),
@@ -493,6 +527,7 @@ url_manual_mapping = (
("bpy.types.view3doverlay.show_wireframes*", "editors/3dview/display/overlays.html#bpy-types-view3doverlay-show-wireframes"),
("bpy.types.view3dshading.background_type*", "editors/3dview/display/shading.html#bpy-types-view3dshading-background-type"),
("bpy.types.workspace.use_filter_by_owner*", "interface/window_system/workspaces.html#bpy-types-workspace-use-filter-by-owner"),
+ ("bpy.ops.gpencil.image_to_grease_pencil*", "editors/image/editing.html#bpy-ops-gpencil-image-to-grease-pencil"),
("bpy.ops.mesh.vertices_smooth_laplacian*", "modeling/meshes/editing/vertex/laplacian_smooth.html#bpy-ops-mesh-vertices-smooth-laplacian"),
("bpy.ops.object.multires_rebuild_subdiv*", "modeling/modifiers/generate/multiresolution.html#bpy-ops-object-multires-rebuild-subdiv"),
("bpy.ops.sequencer.select_side_of_frame*", "video_editing/sequencer/selecting.html#bpy-ops-sequencer-select-side-of-frame"),
@@ -520,6 +555,7 @@ url_manual_mapping = (
("bpy.types.rendersettings.use_overwrite*", "render/output/properties/output.html#bpy-types-rendersettings-use-overwrite"),
("bpy.types.rendersettings.use_sequencer*", "render/output/properties/post_processing.html#bpy-types-rendersettings-use-sequencer"),
("bpy.types.sceneeevee.volumetric_shadow*", "render/eevee/render_settings/volumetrics.html#bpy-types-sceneeevee-volumetric-shadow"),
+ ("bpy.types.sequenceeditor.overlay_frame*", "video_editing/preview/sidebar.html#bpy-types-sequenceeditor-overlay-frame"),
("bpy.types.shadernodebsdfhairprincipled*", "render/shader_nodes/shader/hair_principled.html#bpy-types-shadernodebsdfhairprincipled"),
("bpy.types.shadernodevectordisplacement*", "render/shader_nodes/vector/vector_displacement.html#bpy-types-shadernodevectordisplacement"),
("bpy.types.spacegrapheditor.show_cursor*", "editors/graph_editor/introduction.html#bpy-types-spacegrapheditor-show-cursor"),
@@ -532,16 +568,19 @@ url_manual_mapping = (
("bpy.types.toolsettings.use_snap_rotate*", "editors/3dview/controls/snapping.html#bpy-types-toolsettings-use-snap-rotate"),
("bpy.types.view3doverlay.display_handle*", "editors/3dview/display/overlays.html#bpy-types-view3doverlay-display-handle"),
("bpy.types.volumedisplay.wireframe_type*", "modeling/volumes/properties.html#bpy-types-volumedisplay-wireframe-type"),
+ ("bpy.ops.anim.channels_editable_toggle*", "editors/graph_editor/channels.html#bpy-ops-anim-channels-editable-toggle"),
("bpy.ops.curve.normals_make_consistent*", "modeling/curves/editing/control_points.html#bpy-ops-curve-normals-make-consistent"),
("bpy.ops.gpencil.frame_clean_duplicate*", "grease_pencil/modes/edit/grease_pencil_menu.html#bpy-ops-gpencil-frame-clean-duplicate"),
("bpy.ops.gpencil.stroke_simplify_fixed*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-stroke-simplify-fixed"),
("bpy.ops.mesh.faces_select_linked_flat*", "modeling/meshes/selecting/linked.html#bpy-ops-mesh-faces-select-linked-flat"),
("bpy.ops.mesh.primitive_ico_sphere_add*", "modeling/meshes/primitives.html#bpy-ops-mesh-primitive-ico-sphere-add"),
("bpy.ops.object.gpencil_modifier_apply*", "grease_pencil/modifiers/introduction.html#bpy-ops-object-gpencil-modifier-apply"),
+ ("bpy.ops.object.material_slot_deselect*", "render/materials/assignment.html#bpy-ops-object-material-slot-deselect"),
("bpy.ops.object.multires_external_save*", "modeling/modifiers/generate/multiresolution.html#bpy-ops-object-multires-external-save"),
("bpy.ops.object.vertex_group_normalize*", "sculpt_paint/weight_paint/editing.html#bpy-ops-object-vertex-group-normalize"),
("bpy.ops.object.visual_transform_apply*", "scene_layout/object/editing/apply.html#bpy-ops-object-visual-transform-apply"),
("bpy.ops.outliner.collection_duplicate*", "editors/outliner/editing.html#bpy-ops-outliner-collection-duplicate"),
+ ("bpy.ops.pose.select_constraint_target*", "animation/armatures/posing/selecting.html#bpy-ops-pose-select-constraint-target"),
("bpy.ops.sequencer.change_effect_input*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-change-effect-input"),
("bpy.ops.sequencer.strip_transform_fit*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-strip-transform-fit"),
("bpy.types.armature.rigify_colors_lock*", "addons/rigging/rigify/metarigs.html#bpy-types-armature-rigify-colors-lock"),
@@ -551,6 +590,7 @@ url_manual_mapping = (
("bpy.types.brush.texture_overlay_alpha*", "sculpt_paint/brush/cursor.html#bpy-types-brush-texture-overlay-alpha"),
("bpy.types.brushgpencilsettings.random*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-random"),
("bpy.types.clothsettings.target_volume*", "physics/cloth/settings/physical_properties.html#bpy-types-clothsettings-target-volume"),
+ ("bpy.types.colormanageddisplaysettings*", "render/color_management.html#bpy-types-colormanageddisplaysettings"),
("bpy.types.compositornodebilateralblur*", "compositing/types/filter/bilateral_blur.html#bpy-types-compositornodebilateralblur"),
("bpy.types.compositornodedistancematte*", "compositing/types/matte/distance_key.html#bpy-types-compositornodedistancematte"),
("bpy.types.compositornodesetalpha.mode*", "compositing/types/converter/set_alpha.html#bpy-types-compositornodesetalpha-mode"),
@@ -579,6 +619,7 @@ url_manual_mapping = (
("bpy.types.sculpt.detail_refine_method*", "sculpt_paint/sculpting/tool_settings/dyntopo.html#bpy-types-sculpt-detail-refine-method"),
("bpy.types.sculpt.symmetrize_direction*", "sculpt_paint/sculpting/tool_settings/symmetry.html#bpy-types-sculpt-symmetrize-direction"),
("bpy.types.sequenceeditor.show_overlay*", "video_editing/preview/sidebar.html#bpy-types-sequenceeditor-show-overlay"),
+ ("bpy.types.sequenceeditor.use_prefetch*", "video_editing/preview/sidebar.html#bpy-types-sequenceeditor-use-prefetch"),
("bpy.types.soundsequence.show_waveform*", "video_editing/sequencer/sidebar/strip.html#bpy-types-soundsequence-show-waveform"),
("bpy.types.spaceoutliner.filter_invert*", "editors/outliner/interface.html#bpy-types-spaceoutliner-filter-invert"),
("bpy.types.spacetexteditor.show_margin*", "editors/text_editor.html#bpy-types-spacetexteditor-show-margin"),
@@ -587,6 +628,9 @@ url_manual_mapping = (
("bpy.types.toolsettings.use_snap_scale*", "editors/3dview/controls/snapping.html#bpy-types-toolsettings-use-snap-scale"),
("bpy.types.toolsettings.uv_select_mode*", "editors/uv/selecting.html#bpy-types-toolsettings-uv-select-mode"),
("bpy.types.viewlayer.material_override*", "render/layers/introduction.html#bpy-types-viewlayer-material-override"),
+ ("bpy.ops.anim.channels_disable_toggle*", "editors/graph_editor/channels.html#bpy-ops-anim-channels-disable-toggle"),
+ ("bpy.ops.anim.channels_fcurves_enable*", "editors/graph_editor/channels.html#bpy-ops-anim-channels-fcurves-enable"),
+ ("bpy.ops.anim.channels_setting_toggle*", "editors/graph_editor/channels.html#bpy-ops-anim-channels-setting-toggle"),
("bpy.ops.clip.set_viewport_background*", "movie_clip/tracking/clip/editing/clip.html#bpy-ops-clip-set-viewport-background"),
("bpy.ops.gpencil.interpolate_sequence*", "grease_pencil/animation/tools.html#bpy-ops-gpencil-interpolate-sequence"),
("bpy.ops.mesh.normals_make_consistent*", "modeling/meshes/editing/mesh/normals.html#bpy-ops-mesh-normals-make-consistent"),
@@ -609,12 +653,14 @@ url_manual_mapping = (
("bpy.types.brush.topology_rake_factor*", "sculpt_paint/sculpting/tool_settings/dyntopo.html#bpy-types-brush-topology-rake-factor"),
("bpy.types.brush.use_pose_ik_anchored*", "sculpt_paint/sculpting/tools/pose.html#bpy-types-brush-use-pose-ik-anchored"),
("bpy.types.clothsettings.use_pressure*", "physics/cloth/settings/physical_properties.html#bpy-types-clothsettings-use-pressure"),
+ ("bpy.types.compositornodeantialiasing*", "compositing/types/filter/anti_aliasing.html#bpy-types-compositornodeantialiasing"),
("bpy.types.compositornodechannelmatte*", "compositing/types/matte/channel_key.html#bpy-types-compositornodechannelmatte"),
("bpy.types.compositornodecolorbalance*", "compositing/types/color/color_balance.html#bpy-types-compositornodecolorbalance"),
("bpy.types.compositornodekeyingscreen*", "compositing/types/matte/keying_screen.html#bpy-types-compositornodekeyingscreen"),
("bpy.types.dynamicpaintcanvassettings*", "physics/dynamic_paint/canvas.html#bpy-types-dynamicpaintcanvassettings"),
("bpy.types.fluidflowsettings.use_flow*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-use-flow"),
("bpy.types.fmodifierfunctiongenerator*", "editors/graph_editor/fcurves/sidebar/modifiers.html#bpy-types-fmodifierfunctiongenerator"),
+ ("bpy.types.geometrynodeattributeclamp*", "modeling/geometry_nodes/attribute/attribute_clamp.html#bpy-types-geometrynodeattributeclamp"),
("bpy.types.geometrynodecollectioninfo*", "modeling/geometry_nodes/input/collection_info.html#bpy-types-geometrynodecollectioninfo"),
("bpy.types.geometrynodepointstovolume*", "modeling/geometry_nodes/volume/points_to_volume.html#bpy-types-geometrynodepointstovolume"),
("bpy.types.geometrynodepointtranslate*", "modeling/geometry_nodes/point/point_translate.html#bpy-types-geometrynodepointtranslate"),
@@ -630,10 +676,13 @@ url_manual_mapping = (
("bpy.types.shadernodeambientocclusion*", "render/shader_nodes/input/ao.html#bpy-types-shadernodeambientocclusion"),
("bpy.types.shadernodevolumeabsorption*", "render/shader_nodes/shader/volume_absorption.html#bpy-types-shadernodevolumeabsorption"),
("bpy.types.shadernodevolumeprincipled*", "render/shader_nodes/shader/volume_principled.html#bpy-types-shadernodevolumeprincipled"),
+ ("bpy.types.spaceoutliner.display_mode*", "editors/outliner/interface.html#bpy-types-spaceoutliner-display-mode"),
("bpy.types.spaceoutliner.filter_state*", "editors/outliner/interface.html#bpy-types-spaceoutliner-filter-state"),
+ ("bpy.types.toolsettings.keyframe_type*", "editors/timeline.html#bpy-types-toolsettings-keyframe-type"),
("bpy.types.toolsettings.snap_elements*", "editors/3dview/controls/snapping.html#bpy-types-toolsettings-snap-elements"),
("bpy.types.toolsettings.use_snap_self*", "editors/3dview/controls/snapping.html#bpy-types-toolsettings-use-snap-self"),
("bpy.types.viewlayer.active_aov_index*", "render/layers/passes.html#bpy-types-viewlayer-active-aov-index"),
+ ("bpy.ops.anim.channels_enable_toggle*", "editors/graph_editor/channels.html#bpy-ops-anim-channels-enable-toggle"),
("bpy.ops.gpencil.bake_mesh_animation*", "grease_pencil/animation/tools.html#bpy-ops-gpencil-bake-mesh-animation"),
("bpy.ops.gpencil.select_vertex_color*", "grease_pencil/selecting.html#bpy-ops-gpencil-select-vertex-color"),
("bpy.ops.gpencil.set_active_material*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-set-active-material"),
@@ -646,6 +695,8 @@ url_manual_mapping = (
("bpy.ops.mesh.shape_propagate_to_all*", "modeling/meshes/editing/vertex/propagate_shapes.html#bpy-ops-mesh-shape-propagate-to-all"),
("bpy.ops.mesh.vert_connect_nonplanar*", "modeling/meshes/editing/mesh/cleanup.html#bpy-ops-mesh-vert-connect-nonplanar"),
("bpy.ops.object.duplicates_make_real*", "scene_layout/object/editing/apply.html#bpy-ops-object-duplicates-make-real"),
+ ("bpy.ops.object.material_slot_assign*", "render/materials/assignment.html#bpy-ops-object-material-slot-assign"),
+ ("bpy.ops.object.material_slot_select*", "render/materials/assignment.html#bpy-ops-object-material-slot-select"),
("bpy.ops.object.multires_unsubdivide*", "modeling/modifiers/generate/multiresolution.html#bpy-ops-object-multires-unsubdivide"),
("bpy.ops.object.transforms_to_deltas*", "scene_layout/object/editing/apply.html#bpy-ops-object-transforms-to-deltas"),
("bpy.ops.outliner.collection_disable*", "editors/outliner/editing.html#bpy-ops-outliner-collection-disable"),
@@ -662,6 +713,7 @@ url_manual_mapping = (
("bpy.types.brush.use_cloth_collision*", "sculpt_paint/sculpting/tools/cloth.html#bpy-types-brush-use-cloth-collision"),
("bpy.types.brush.use_grab_silhouette*", "sculpt_paint/sculpting/tools/grab.html#bpy-types-brush-use-grab-silhouette"),
("bpy.types.brush.use_primary_overlay*", "sculpt_paint/brush/cursor.html#bpy-types-brush-use-primary-overlay"),
+ ("bpy.types.brushtextureslot.map_mode*", "sculpt_paint/brush/texture.html#bpy-types-brushtextureslot-map-mode"),
("bpy.types.camera.passepartout_alpha*", "render/cameras.html#bpy-types-camera-passepartout-alpha"),
("bpy.types.compositornodechromamatte*", "compositing/types/matte/chroma_key.html#bpy-types-compositornodechromamatte"),
("bpy.types.compositornodedilateerode*", "compositing/types/filter/dilate_erode.html#bpy-types-compositornodedilateerode"),
@@ -673,6 +725,7 @@ url_manual_mapping = (
("bpy.types.fluidflowsettings.density*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-density"),
("bpy.types.geometrynodeattributefill*", "modeling/geometry_nodes/attribute/attribute_fill.html#bpy-types-geometrynodeattributefill"),
("bpy.types.geometrynodeattributemath*", "modeling/geometry_nodes/attribute/attribute_math.html#bpy-types-geometrynodeattributemath"),
+ ("bpy.types.geometrynodemeshicosphere*", "modeling/geometry_nodes/mesh_primitives/icosphere.html#bpy-types-geometrynodemeshicosphere"),
("bpy.types.geometrynodepointinstance*", "modeling/geometry_nodes/point/point_instance.html#bpy-types-geometrynodepointinstance"),
("bpy.types.geometrynodepointseparate*", "modeling/geometry_nodes/point/point_separate.html#bpy-types-geometrynodepointseparate"),
("bpy.types.light.use_custom_distance*", "render/eevee/lighting.html#bpy-types-light-use-custom-distance"),
@@ -683,6 +736,7 @@ url_manual_mapping = (
("bpy.types.regionview3d.use_box_clip*", "editors/3dview/navigate/views.html#bpy-types-regionview3d-use-box-clip"),
("bpy.types.rendersettings.use_border*", "render/output/properties/dimensions.html#bpy-types-rendersettings-use-border"),
("bpy.types.rigidbodyconstraint.limit*", "physics/rigid_body/constraints/introduction.html#bpy-types-rigidbodyconstraint-limit"),
+ ("bpy.types.rigidbodyobject.kinematic*", "physics/rigid_body/properties/settings.html#bpy-types-rigidbodyobject-kinematic"),
("bpy.types.scene.audio_doppler_speed*", "scene_layout/scene/properties.html#bpy-types-scene-audio-doppler-speed"),
("bpy.types.sceneeevee.bokeh_max_size*", "render/eevee/render_settings/depth_of_field.html#bpy-types-sceneeevee-bokeh-max-size"),
("bpy.types.sculpt.detail_type_method*", "sculpt_paint/sculpting/tool_settings/dyntopo.html#bpy-types-sculpt-detail-type-method"),
@@ -707,7 +761,7 @@ url_manual_mapping = (
("bpy.ops.mesh.select_interior_faces*", "modeling/meshes/selecting/all_by_trait.html#bpy-ops-mesh-select-interior-faces"),
("bpy.ops.mesh.select_similar_region*", "modeling/meshes/selecting/similar.html#bpy-ops-mesh-select-similar-region"),
("bpy.ops.mesh.tris_convert_to_quads*", "modeling/meshes/editing/face/triangles_quads.html#bpy-ops-mesh-tris-convert-to-quads"),
- ("bpy.ops.node.read_fullsamplelayers*", "interface/controls/nodes/editing.html#bpy-ops-node-read-fullsamplelayers"),
+ ("bpy.ops.node.active_preview_toggle*", "modeling/geometry_nodes/introduction.html#bpy-ops-node-active-preview-toggle"),
("bpy.ops.object.datalayout_transfer*", "scene_layout/object/editing/link_transfer/transfer_mesh_data_layout.html#bpy-ops-object-datalayout-transfer"),
("bpy.ops.object.multires_base_apply*", "modeling/modifiers/generate/multiresolution.html#bpy-ops-object-multires-base-apply"),
("bpy.ops.object.randomize_transform*", "scene_layout/object/editing/transform/randomize.html#bpy-ops-object-randomize-transform"),
@@ -717,6 +771,7 @@ url_manual_mapping = (
("bpy.ops.object.vertex_group_remove*", "modeling/meshes/properties/vertex_groups/vertex_groups.html#bpy-ops-object-vertex-group-remove"),
("bpy.ops.object.vertex_group_smooth*", "sculpt_paint/weight_paint/editing.html#bpy-ops-object-vertex-group-smooth"),
("bpy.ops.outliner.collection_enable*", "editors/outliner/editing.html#bpy-ops-outliner-collection-enable"),
+ ("bpy.ops.palette.extract_from_image*", "editors/image/editing.html#bpy-ops-palette-extract-from-image"),
("bpy.ops.pose.user_transforms_clear*", "animation/armatures/posing/editing/clear.html#bpy-ops-pose-user-transforms-clear"),
("bpy.ops.poselib.browse_interactive*", "animation/armatures/posing/editing/pose_library.html#bpy-ops-poselib-browse-interactive"),
("bpy.ops.sculpt.set_persistent_base*", "sculpt_paint/sculpting/tools/layer.html#bpy-ops-sculpt-set-persistent-base"),
@@ -730,6 +785,8 @@ url_manual_mapping = (
("bpy.types.brush.use_connected_only*", "sculpt_paint/sculpting/tools/pose.html#bpy-types-brush-use-connected-only"),
("bpy.types.brush.use_cursor_overlay*", "sculpt_paint/brush/cursor.html#bpy-types-brush-use-cursor-overlay"),
("bpy.types.camera.show_passepartout*", "render/cameras.html#bpy-types-camera-show-passepartout"),
+ ("bpy.types.collection.lineart_usage*", "scene_layout/collections/properties.html#bpy-types-collection-lineart-usage"),
+ ("bpy.types.colormanagedviewsettings*", "render/color_management.html#bpy-types-colormanagedviewsettings"),
("bpy.types.compositornodebokehimage*", "compositing/types/input/bokeh_image.html#bpy-types-compositornodebokehimage"),
("bpy.types.compositornodecolormatte*", "compositing/types/matte/color_key.html#bpy-types-compositornodecolormatte"),
("bpy.types.compositornodecolorspill*", "compositing/types/matte/color_spill.html#bpy-types-compositornodecolorspill"),
@@ -745,6 +802,8 @@ url_manual_mapping = (
("bpy.types.functionnodefloatcompare*", "modeling/geometry_nodes/utilities/float_compare.html#bpy-types-functionnodefloatcompare"),
("bpy.types.geometrynodeattributemix*", "modeling/geometry_nodes/attribute/attribute_mix.html#bpy-types-geometrynodeattributemix"),
("bpy.types.geometrynodejoingeometry*", "modeling/geometry_nodes/geometry/join_geometry.html#bpy-types-geometrynodejoingeometry"),
+ ("bpy.types.geometrynodemeshcylinder*", "modeling/geometry_nodes/mesh_primitives/cylinder.html#bpy-types-geometrynodemeshcylinder"),
+ ("bpy.types.geometrynodemeshuvsphere*", "modeling/geometry_nodes/mesh_primitives/uv_sphere.html#bpy-types-geometrynodemeshuvsphere"),
("bpy.types.geometrynodevolumetomesh*", "modeling/geometry_nodes/volume/volume_to_mesh.html#bpy-types-geometrynodevolumetomesh"),
("bpy.types.image.use_half_precision*", "editors/image/image_settings.html#bpy-types-image-use-half-precision"),
("bpy.types.imagepaint.interpolation*", "sculpt_paint/texture_paint/tool_settings/texture_slots.html#bpy-types-imagepaint-interpolation"),
@@ -759,6 +818,7 @@ url_manual_mapping = (
("bpy.types.shadernodebsdfrefraction*", "render/shader_nodes/shader/refraction.html#bpy-types-shadernodebsdfrefraction"),
("bpy.types.shadernodeoutputmaterial*", "render/shader_nodes/output/material.html#bpy-types-shadernodeoutputmaterial"),
("bpy.types.shadernodetexenvironment*", "render/shader_nodes/textures/environment.html#bpy-types-shadernodetexenvironment"),
+ ("bpy.types.spacesequenceeditor.show*", "video_editing/preview/introduction.html#bpy-types-spacesequenceeditor-show"),
("bpy.types.spaceuveditor.uv_opacity*", "editors/uv/overlays.html#bpy-types-spaceuveditor-uv-opacity"),
("bpy.types.subdividegpencilmodifier*", "grease_pencil/modifiers/generate/subdivide.html#bpy-types-subdividegpencilmodifier"),
("bpy.types.thicknessgpencilmodifier*", "grease_pencil/modifiers/deform/thickness.html#bpy-types-thicknessgpencilmodifier"),
@@ -767,6 +827,7 @@ url_manual_mapping = (
("bpy.types.vertexweighteditmodifier*", "modeling/modifiers/modify/weight_edit.html#bpy-types-vertexweighteditmodifier"),
("bpy.types.volumedisplay.slice_axis*", "modeling/volumes/properties.html#bpy-types-volumedisplay-slice-axis"),
("bpy.ops.anim.channels_clean_empty*", "editors/nla/editing.html#bpy-ops-anim-channels-clean-empty"),
+ ("bpy.ops.armature.select_hierarchy*", "animation/armatures/bones/selecting.html#bpy-ops-armature-select-hierarchy"),
("bpy.ops.clip.apply_solution_scale*", "movie_clip/tracking/clip/editing/reconstruction.html#bpy-ops-clip-apply-solution-scale"),
("bpy.ops.clip.set_center_principal*", "movie_clip/tracking/clip/editing/clip.html#bpy-ops-clip-set-center-principal"),
("bpy.ops.clip.setup_tracking_scene*", "movie_clip/tracking/clip/editing/clip.html#bpy-ops-clip-setup-tracking-scene"),
@@ -829,6 +890,7 @@ url_manual_mapping = (
("bpy.types.multiplygpencilmodifier*", "grease_pencil/modifiers/generate/multiple_strokes.html#bpy-types-multiplygpencilmodifier"),
("bpy.types.rendersettings.filepath*", "render/output/properties/output.html#bpy-types-rendersettings-filepath"),
("bpy.types.rendersettings.fps_base*", "render/output/properties/dimensions.html#bpy-types-rendersettings-fps-base"),
+ ("bpy.types.rigidbodyobject.enabled*", "physics/rigid_body/properties/settings.html#bpy-types-rigidbodyobject-enabled"),
("bpy.types.sceneeevee.use_overscan*", "render/eevee/render_settings/film.html#bpy-types-sceneeevee-use-overscan"),
("bpy.types.sequencetransform.scale*", "video_editing/sequencer/sidebar/strip.html#bpy-types-sequencetransform-scale"),
("bpy.types.shadernodeeeveespecular*", "render/shader_nodes/shader/specular_bsdf.html#bpy-types-shadernodeeeveespecular"),
@@ -842,11 +904,13 @@ url_manual_mapping = (
("bpy.types.viewlayer.use_freestyle*", "render/freestyle/view_layer.html#bpy-types-viewlayer-use-freestyle"),
("bpy.types.volumedisplay.use_slice*", "modeling/volumes/properties.html#bpy-types-volumedisplay-use-slice"),
("bpy.ops.armature.armature_layers*", "animation/armatures/bones/editing/change_layers.html#bpy-ops-armature-armature-layers"),
+ ("bpy.ops.armature.select_linked()*", "animation/armatures/bones/selecting.html#bpy-ops-armature-select-linked"),
("bpy.ops.clip.stabilize_2d_select*", "movie_clip/tracking/clip/selecting.html#bpy-ops-clip-stabilize-2d-select"),
("bpy.ops.gpencil.frame_clean_fill*", "grease_pencil/modes/edit/grease_pencil_menu.html#bpy-ops-gpencil-frame-clean-fill"),
("bpy.ops.gpencil.stroke_subdivide*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-stroke-subdivide"),
("bpy.ops.gpencil.vertex_color_hsv*", "grease_pencil/modes/vertex_paint/editing.html#bpy-ops-gpencil-vertex-color-hsv"),
("bpy.ops.gpencil.vertex_color_set*", "grease_pencil/modes/vertex_paint/editing.html#bpy-ops-gpencil-vertex-color-set"),
+ ("bpy.ops.graph.extrapolation_type*", "editors/graph_editor/channels.html#bpy-ops-graph-extrapolation-type"),
("bpy.ops.graph.interpolation_type*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-interpolation-type"),
("bpy.ops.mesh.dissolve_degenerate*", "modeling/meshes/editing/mesh/cleanup.html#bpy-ops-mesh-dissolve-degenerate"),
("bpy.ops.mesh.face_split_by_edges*", "modeling/meshes/editing/face/weld_edges_faces.html#bpy-ops-mesh-face-split-by-edges"),
@@ -864,6 +928,7 @@ url_manual_mapping = (
("bpy.ops.outliner.collection_hide*", "editors/outliner/editing.html#bpy-ops-outliner-collection-hide"),
("bpy.ops.outliner.collection_show*", "editors/outliner/editing.html#bpy-ops-outliner-collection-show"),
("bpy.ops.paint.mask_lasso_gesture*", "sculpt_paint/sculpting/editing/mask.html#bpy-ops-paint-mask-lasso-gesture"),
+ ("bpy.ops.rigidbody.mass_calculate*", "physics/rigid_body/editing.html#bpy-ops-rigidbody-mass-calculate"),
("bpy.ops.screen.spacedata_cleanup*", "advanced/operators.html#bpy-ops-screen-spacedata-cleanup"),
("bpy.ops.sculpt.detail_flood_fill*", "sculpt_paint/sculpting/tool_settings/dyntopo.html#bpy-ops-sculpt-detail-flood-fill"),
("bpy.ops.sequencer.duplicate_move*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-duplicate-move"),
@@ -901,11 +966,13 @@ url_manual_mapping = (
("bpy.types.cyclesmaterialsettings*", "render/cycles/material_settings.html#bpy-types-cyclesmaterialsettings"),
("bpy.types.dopesheet.show_summary*", "editors/dope_sheet/introduction.html#bpy-types-dopesheet-show-summary"),
("bpy.types.geometrynodeisviewport*", "modeling/geometry_nodes/input/is_viewport.html#bpy-types-geometrynodeisviewport"),
+ ("bpy.types.geometrynodemeshcircle*", "modeling/geometry_nodes/mesh_primitives/circle.html#bpy-types-geometrynodemeshcircle"),
("bpy.types.geometrynodeobjectinfo*", "modeling/geometry_nodes/input/object_info.html#bpy-types-geometrynodeobjectinfo"),
("bpy.types.geometrynodepointscale*", "modeling/geometry_nodes/point/point_scale.html#bpy-types-geometrynodepointscale"),
("bpy.types.imagepaint.use_occlude*", "sculpt_paint/texture_paint/tool_settings/options.html#bpy-types-imagepaint-use-occlude"),
("bpy.types.imagesequence.use_flip*", "video_editing/sequencer/sidebar/strip.html#bpy-types-imagesequence-use-flip"),
("bpy.types.latticegpencilmodifier*", "grease_pencil/modifiers/deform/lattice.html#bpy-types-latticegpencilmodifier"),
+ ("bpy.types.lineartgpencilmodifier*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier"),
("bpy.types.mesh.auto_smooth_angle*", "modeling/meshes/structure.html#bpy-types-mesh-auto-smooth-angle"),
("bpy.types.objectsolverconstraint*", "animation/constraints/motion_tracking/object_solver.html#bpy-types-objectsolverconstraint"),
("bpy.types.opacitygpencilmodifier*", "grease_pencil/modifiers/color/opacity.html#bpy-types-opacitygpencilmodifier"),
@@ -914,7 +981,6 @@ url_manual_mapping = (
("bpy.types.sceneeevee.motion_blur*", "render/eevee/render_settings/motion_blur.html#bpy-types-sceneeevee-motion-blur"),
("bpy.types.sceneeevee.taa_samples*", "render/eevee/render_settings/sampling.html#bpy-types-sceneeevee-taa-samples"),
("bpy.types.sculpt.radial_symmetry*", "sculpt_paint/sculpting/tool_settings/symmetry.html#bpy-types-sculpt-radial-symmetry"),
- ("bpy.types.sequenceeditor.overlay*", "video_editing/preview/sidebar.html#bpy-types-sequenceeditor-overlay"),
("bpy.types.shadernodedisplacement*", "render/shader_nodes/vector/displacement.html#bpy-types-shadernodedisplacement"),
("bpy.types.shadernodelightfalloff*", "render/shader_nodes/color/light_falloff.html#bpy-types-shadernodelightfalloff"),
("bpy.types.shadernodeparticleinfo*", "render/shader_nodes/input/particle_info.html#bpy-types-shadernodeparticleinfo"),
@@ -926,6 +992,7 @@ url_manual_mapping = (
("bpy.types.volumerender.step_size*", "modeling/volumes/properties.html#bpy-types-volumerender-step-size"),
("bpy.types.weightednormalmodifier*", "modeling/modifiers/modify/weighted_normal.html#bpy-types-weightednormalmodifier"),
("bpy.ops.armature.autoside_names*", "animation/armatures/bones/editing/naming.html#bpy-ops-armature-autoside-names"),
+ ("bpy.ops.armature.select_similar*", "animation/armatures/bones/selecting.html#bpy-ops-armature-select-similar"),
("bpy.ops.clip.create_plane_track*", "movie_clip/tracking/clip/editing/track.html#bpy-ops-clip-create-plane-track"),
("bpy.ops.curve.spline_weight_set*", "modeling/curves/editing/other.html#bpy-ops-curve-spline-weight-set"),
("bpy.ops.gpencil.blank_frame_add*", "grease_pencil/animation/tools.html#bpy-ops-gpencil-blank-frame-add"),
@@ -936,6 +1003,7 @@ url_manual_mapping = (
("bpy.ops.gpencil.stroke_separate*", "grease_pencil/modes/edit/grease_pencil_menu.html#bpy-ops-gpencil-stroke-separate"),
("bpy.ops.gpencil.stroke_simplify*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-stroke-simplify"),
("bpy.ops.graph.snap_cursor_value*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-snap-cursor-value"),
+ ("bpy.ops.image.save_all_modified*", "editors/image/editing.html#bpy-ops-image-save-all-modified"),
("bpy.ops.mesh.extrude_edges_move*", "modeling/meshes/editing/edge/extrude_edges.html#bpy-ops-mesh-extrude-edges-move"),
("bpy.ops.mesh.extrude_faces_move*", "modeling/meshes/editing/face/extrude_individual_faces.html#bpy-ops-mesh-extrude-faces-move"),
("bpy.ops.mesh.faces_shade_smooth*", "modeling/meshes/editing/face/shading.html#bpy-ops-mesh-faces-shade-smooth"),
@@ -944,6 +1012,7 @@ url_manual_mapping = (
("bpy.ops.mesh.primitive_cube_add*", "modeling/meshes/primitives.html#bpy-ops-mesh-primitive-cube-add"),
("bpy.ops.mesh.primitive_grid_add*", "modeling/meshes/primitives.html#bpy-ops-mesh-primitive-grid-add"),
("bpy.ops.mesh.subdivide_edgering*", "modeling/meshes/editing/edge/subdivide_edge_ring.html#bpy-ops-mesh-subdivide-edgering"),
+ ("bpy.ops.node.hide_socket_toggle*", "interface/controls/nodes/editing.html#bpy-ops-node-hide-socket-toggle"),
("bpy.ops.node.tree_socket_remove*", "interface/controls/nodes/groups.html#bpy-ops-node-tree-socket-remove"),
("bpy.ops.object.constraints_copy*", "animation/constraints/interface/adding_removing.html#bpy-ops-object-constraints-copy"),
("bpy.ops.object.gpencil_modifier*", "grease_pencil/modifiers/index.html#bpy-ops-object-gpencil-modifier"),
@@ -954,6 +1023,8 @@ url_manual_mapping = (
("bpy.ops.object.vertex_group_add*", "modeling/meshes/properties/vertex_groups/vertex_groups.html#bpy-ops-object-vertex-group-add"),
("bpy.ops.object.vertex_group_fix*", "sculpt_paint/weight_paint/editing.html#bpy-ops-object-vertex-group-fix"),
("bpy.ops.outliner.collection_new*", "editors/outliner/editing.html#bpy-ops-outliner-collection-new"),
+ ("bpy.ops.outliner.show_hierarchy*", "editors/outliner/editing.html#bpy-ops-outliner-show-hierarchy"),
+ ("bpy.ops.outliner.show_one_level*", "editors/outliner/editing.html#bpy-ops-outliner-show-one-level"),
("bpy.ops.paint.brush_colors_flip*", "sculpt_paint/texture_paint/tool_settings/brush_settings.html#bpy-ops-paint-brush-colors-flip"),
("bpy.ops.paint.weight_from_bones*", "sculpt_paint/weight_paint/editing.html#bpy-ops-paint-weight-from-bones"),
("bpy.ops.poselib.action_sanitize*", "animation/armatures/properties/pose_library.html#bpy-ops-poselib-action-sanitize"),
@@ -968,6 +1039,7 @@ url_manual_mapping = (
("bpy.ops.uv.shortest_path_select*", "editors/uv/selecting.html#bpy-ops-uv-shortest-path-select"),
("bpy.ops.wm.operator_cheat_sheet*", "advanced/operators.html#bpy-ops-wm-operator-cheat-sheet"),
("bpy.ops.wm.previews_batch_clear*", "files/blend/previews.html#bpy-ops-wm-previews-batch-clear"),
+ ("bpy.ops.wm.recover_last_session*", "files/blend/open_save.html#bpy-ops-wm-recover-last-session"),
("bpy.types.armature.use_mirror_x*", "animation/armatures/bones/tools/tool_settings.html#bpy-types-armature-use-mirror-x"),
("bpy.types.bakesettings.normal_b*", "render/cycles/baking.html#bpy-types-bakesettings-normal-b"),
("bpy.types.bakesettings.normal_g*", "render/cycles/baking.html#bpy-types-bakesettings-normal-g"),
@@ -976,7 +1048,7 @@ url_manual_mapping = (
("bpy.types.brush.boundary_offset*", "sculpt_paint/sculpting/tools/boundary.html#bpy-types-brush-boundary-offset"),
("bpy.types.brush.cloth_sim_limit*", "sculpt_paint/sculpting/tools/cloth.html#bpy-types-brush-cloth-sim-limit"),
("bpy.types.brush.use_custom_icon*", "sculpt_paint/brush/brush.html#bpy-types-brush-use-custom-icon"),
- ("bpy.types.brushtextureslot.mask*", "sculpt_paint/brush/texture.html#bpy-types-brushtextureslot-mask"),
+ ("bpy.types.brushtextureslot.mask*", "sculpt_paint/brush/texture_mask.html#bpy-types-brushtextureslot-mask"),
("bpy.types.camerabackgroundimage*", "render/cameras.html#bpy-types-camerabackgroundimage"),
("bpy.types.compositornodeboxmask*", "compositing/types/matte/box_mask.html#bpy-types-compositornodeboxmask"),
("bpy.types.compositornodedefocus*", "compositing/types/filter/defocus.html#bpy-types-compositornodedefocus"),
@@ -1003,13 +1075,14 @@ url_manual_mapping = (
("bpy.types.material.blend_method*", "render/eevee/materials/settings.html#bpy-types-material-blend-method"),
("bpy.types.mirrorgpencilmodifier*", "grease_pencil/modifiers/generate/mirror.html#bpy-types-mirrorgpencilmodifier"),
("bpy.types.movietrackingcamera.k*", "movie_clip/tracking/clip/sidebar/track/camera.html#bpy-types-movietrackingcamera-k"),
+ ("bpy.types.node.use_custom_color*", "interface/controls/nodes/sidebar.html#bpy-types-node-use-custom-color"),
("bpy.types.offsetgpencilmodifier*", "grease_pencil/modifiers/deform/offset.html#bpy-types-offsetgpencilmodifier"),
- ("bpy.types.particlefluidsettings*", "physics/particles/emitter/physics/fluid.html#bpy-types-particlefluidsettings"),
("bpy.types.posebone.custom_shape*", "animation/armatures/bones/properties/display.html#bpy-types-posebone-custom-shape"),
("bpy.types.rendersettings.tile_x*", "render/cycles/render_settings/performance.html#bpy-types-rendersettings-tile-x"),
("bpy.types.rendersettings.tile_y*", "render/cycles/render_settings/performance.html#bpy-types-rendersettings-tile-y"),
("bpy.types.rigifyselectioncolors*", "addons/rigging/rigify/metarigs.html#bpy-types-rigifyselectioncolors"),
("bpy.types.sceneeevee.volumetric*", "render/eevee/render_settings/volumetrics.html#bpy-types-sceneeevee-volumetric"),
+ ("bpy.types.screen.show_statusbar*", "interface/window_system/topbar.html#bpy-types-screen-show-statusbar"),
("bpy.types.sculpt.detail_percent*", "sculpt_paint/sculpting/tool_settings/dyntopo.html#bpy-types-sculpt-detail-percent"),
("bpy.types.sculpt.gravity_object*", "sculpt_paint/sculpting/tool_settings/options.html#bpy-types-sculpt-gravity-object"),
("bpy.types.shadernodebsdfdiffuse*", "render/shader_nodes/shader/diffuse.html#bpy-types-shadernodebsdfdiffuse"),
@@ -1017,7 +1090,6 @@ url_manual_mapping = (
("bpy.types.shadernodeoutputlight*", "render/shader_nodes/output/light.html#bpy-types-shadernodeoutputlight"),
("bpy.types.shadernodeoutputworld*", "render/shader_nodes/output/world.html#bpy-types-shadernodeoutputworld"),
("bpy.types.shadernodetexgradient*", "render/shader_nodes/textures/gradient.html#bpy-types-shadernodetexgradient"),
- ("bpy.types.shadernodetexmusgrave*", "render/shader_nodes/textures/musgrave.html#bpy-types-shadernodetexmusgrave"),
("bpy.types.shadernodevectorcurve*", "render/shader_nodes/vector/curves.html#bpy-types-shadernodevectorcurve"),
("bpy.types.shadernodevertexcolor*", "render/shader_nodes/input/vertex_color.html#bpy-types-shadernodevertexcolor"),
("bpy.types.smoothgpencilmodifier*", "grease_pencil/modifiers/deform/smooth.html#bpy-types-smoothgpencilmodifier"),
@@ -1028,6 +1100,8 @@ url_manual_mapping = (
("bpy.types.volumedisplay.density*", "modeling/volumes/properties.html#bpy-types-volumedisplay-density"),
("bpy.types.volumerender.clipping*", "modeling/volumes/properties.html#bpy-types-volumerender-clipping"),
("bpy.types.workspace.object_mode*", "interface/window_system/workspaces.html#bpy-types-workspace-object-mode"),
+ ("bpy.ops.anim.channels_collapse*", "editors/graph_editor/channels.html#bpy-ops-anim-channels-collapse"),
+ ("bpy.ops.armature.select_mirror*", "animation/armatures/bones/selecting.html#bpy-ops-armature-select-mirror"),
("bpy.ops.curve.switch_direction*", "modeling/curves/editing/segments.html#bpy-ops-curve-switch-direction"),
("bpy.ops.gpencil.duplicate_move*", "grease_pencil/modes/edit/grease_pencil_menu.html#bpy-ops-gpencil-duplicate-move"),
("bpy.ops.gpencil.stroke_arrange*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-stroke-arrange"),
@@ -1044,6 +1118,7 @@ url_manual_mapping = (
("bpy.ops.paint.mask_box_gesture*", "sculpt_paint/sculpting/editing/mask.html#bpy-ops-paint-mask-box-gesture"),
("bpy.ops.screen.region_quadview*", "editors/3dview/navigate/views.html#bpy-ops-screen-region-quadview"),
("bpy.ops.sequencer.offset_clear*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-offset-clear"),
+ ("bpy.ops.spreadsheet.toggle_pin*", "editors/spreadsheet.html#bpy-ops-spreadsheet-toggle-pin"),
("bpy.ops.uv.follow_active_quads*", "modeling/meshes/editing/uv.html#bpy-ops-uv-follow-active-quads"),
("bpy.types.arraygpencilmodifier*", "grease_pencil/modifiers/generate/array.html#bpy-types-arraygpencilmodifier"),
("bpy.types.brush.use_persistent*", "sculpt_paint/sculpting/tools/layer.html#bpy-types-brush-use-persistent"),
@@ -1061,20 +1136,29 @@ url_manual_mapping = (
("bpy.types.compositornoderotate*", "compositing/types/distort/rotate.html#bpy-types-compositornoderotate"),
("bpy.types.compositornodeviewer*", "compositing/types/output/viewer.html#bpy-types-compositornodeviewer"),
("bpy.types.constraint.influence*", "animation/constraints/interface/common.html#bpy-types-constraint-influence"),
+ ("bpy.types.curve.use_path_clamp*", "modeling/curves/properties/path_animation.html#bpy-types-curve-use-path-clamp"),
("bpy.types.datatransfermodifier*", "modeling/modifiers/modify/data_transfer.html#bpy-types-datatransfermodifier"),
("bpy.types.dynamicpaintmodifier*", "physics/dynamic_paint/index.html#bpy-types-dynamicpaintmodifier"),
("bpy.types.ffmpegsettings.audio*", "render/output/properties/output.html#bpy-types-ffmpegsettings-audio"),
("bpy.types.followpathconstraint*", "animation/constraints/relationship/follow_path.html#bpy-types-followpathconstraint"),
("bpy.types.gaussianblursequence*", "video_editing/sequencer/strips/effects/blur.html#bpy-types-gaussianblursequence"),
+ ("bpy.types.geometrynodeboundbox*", "modeling/geometry_nodes/geometry/bounding_box.html#bpy-types-geometrynodeboundbox"),
+ ("bpy.types.geometrynodemeshcone*", "modeling/geometry_nodes/mesh_primitives/cone.html#bpy-types-geometrynodemeshcone"),
+ ("bpy.types.geometrynodemeshcube*", "modeling/geometry_nodes/mesh_primitives/cube.html#bpy-types-geometrynodemeshcube"),
+ ("bpy.types.geometrynodemeshgrid*", "modeling/geometry_nodes/mesh_primitives/grid.html#bpy-types-geometrynodemeshgrid"),
+ ("bpy.types.geometrynodemeshline*", "modeling/geometry_nodes/mesh_primitives/line.html#bpy-types-geometrynodemeshline"),
("bpy.types.gpencillayer.opacity*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-opacity"),
("bpy.types.image.display_aspect*", "editors/image/sidebar.html#bpy-types-image-display-aspect"),
+ ("bpy.types.keyingsetsall.active*", "editors/timeline.html#bpy-types-keyingsetsall-active"),
("bpy.types.limitscaleconstraint*", "animation/constraints/transform/limit_scale.html#bpy-types-limitscaleconstraint"),
("bpy.types.materialgpencilstyle*", "grease_pencil/materials/index.html#bpy-types-materialgpencilstyle"),
("bpy.types.mesh.use_auto_smooth*", "modeling/meshes/structure.html#bpy-types-mesh-use-auto-smooth"),
("bpy.types.meshtovolumemodifier*", "modeling/modifiers/generate/mesh_to_volume.html#bpy-types-meshtovolumemodifier"),
+ ("bpy.types.noisegpencilmodifier*", "grease_pencil/modifiers/deform/noise.html#bpy-types-noisegpencilmodifier"),
("bpy.types.object.hide_viewport*", "scene_layout/object/properties/visibility.html#bpy-types-object-hide-viewport"),
("bpy.types.posebone.rigify_type*", "addons/rigging/rigify/rig_types/index.html#bpy-types-posebone-rigify-type"),
("bpy.types.preferencesfilepaths*", "editors/preferences/file_paths.html#bpy-types-preferencesfilepaths"),
+ ("bpy.types.rigidbodyobject.mass*", "physics/rigid_body/properties/settings.html#bpy-types-rigidbodyobject-mass"),
("bpy.types.scene.background_set*", "scene_layout/scene/properties.html#bpy-types-scene-background-set"),
("bpy.types.sequence.frame_start*", "video_editing/sequencer/sidebar/strip.html#bpy-types-sequence-frame-start"),
("bpy.types.shadernodebackground*", "render/shader_nodes/shader/background.html#bpy-types-shadernodebackground"),
@@ -1098,14 +1182,17 @@ url_manual_mapping = (
("bpy.types.volume.sequence_mode*", "modeling/volumes/properties.html#bpy-types-volume-sequence-mode"),
("bpy.types.volumetomeshmodifier*", "modeling/modifiers/generate/volume_to_mesh.html#bpy-types-volumetomeshmodifier"),
("bpy.types.whitebalancemodifier*", "video_editing/sequencer/sidebar/modifiers.html#bpy-types-whitebalancemodifier"),
+ ("bpy.ops.anim.channels_ungroup*", "editors/graph_editor/channels.html#bpy-ops-anim-channels-ungroup"),
("bpy.ops.clip.clear_track_path*", "movie_clip/tracking/clip/editing/track.html#bpy-ops-clip-clear-track-path"),
("bpy.ops.clip.set_scene_frames*", "movie_clip/tracking/clip/editing/clip.html#bpy-ops-clip-set-scene-frames"),
("bpy.ops.curve.handle_type_set*", "modeling/curves/editing/control_points.html#bpy-ops-curve-handle-type-set"),
("bpy.ops.curve.spline_type_set*", "modeling/curves/editing/curve.html#bpy-ops-curve-spline-type-set"),
+ ("bpy.ops.file.unpack_libraries*", "files/blend/packed_data.html#bpy-ops-file-unpack-libraries"),
("bpy.ops.gpencil.move_to_layer*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-move-to-layer"),
("bpy.ops.gpencil.stroke_sample*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-stroke-sample"),
("bpy.ops.gpencil.stroke_smooth*", "grease_pencil/modes/edit/point_menu.html#bpy-ops-gpencil-stroke-smooth"),
("bpy.ops.graph.keyframe_insert*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-keyframe-insert"),
+ ("bpy.ops.image.read_viewlayers*", "editors/image/editing.html#bpy-ops-image-read-viewlayers"),
("bpy.ops.mesh.blend_from_shape*", "modeling/meshes/editing/vertex/blend_shape.html#bpy-ops-mesh-blend-from-shape"),
("bpy.ops.mesh.dissolve_limited*", "modeling/meshes/editing/mesh/delete.html#bpy-ops-mesh-dissolve-limited"),
("bpy.ops.mesh.face_make_planar*", "modeling/meshes/editing/mesh/cleanup.html#bpy-ops-mesh-face-make-planar"),
@@ -1113,6 +1200,7 @@ url_manual_mapping = (
("bpy.ops.mesh.faces_shade_flat*", "modeling/meshes/editing/face/shading.html#bpy-ops-mesh-faces-shade-flat"),
("bpy.ops.mesh.paint_mask_slice*", "sculpt_paint/sculpting/editing/mask.html#bpy-ops-mesh-paint-mask-slice"),
("bpy.ops.mesh.select_ungrouped*", "modeling/meshes/selecting/all_by_trait.html#bpy-ops-mesh-select-ungrouped"),
+ ("bpy.ops.node.delete_reconnect*", "interface/controls/nodes/editing.html#bpy-ops-node-delete-reconnect"),
("bpy.ops.node.tree_path_parent*", "interface/controls/nodes/groups.html#bpy-ops-node-tree-path-parent"),
("bpy.ops.node.tree_socket_move*", "interface/controls/nodes/groups.html#bpy-ops-node-tree-socket-move"),
("bpy.ops.object.duplicate_move*", "scene_layout/object/editing/duplicate.html#bpy-ops-object-duplicate-move"),
@@ -1123,6 +1211,7 @@ url_manual_mapping = (
("bpy.ops.outliner.id_operation*", "editors/outliner/editing.html#bpy-ops-outliner-id-operation"),
("bpy.ops.paint.mask_flood_fill*", "sculpt_paint/sculpting/editing/mask.html#bpy-ops-paint-mask-flood-fill"),
("bpy.ops.pose.quaternions_flip*", "animation/armatures/posing/editing/flip_quats.html#bpy-ops-pose-quaternions-flip"),
+ ("bpy.ops.pose.select_hierarchy*", "animation/armatures/posing/selecting.html#bpy-ops-pose-select-hierarchy"),
("bpy.ops.pose.transforms_clear*", "animation/armatures/posing/editing/clear.html#bpy-ops-pose-transforms-clear"),
("bpy.ops.preferences.copy_prev*", "editors/preferences/introduction.html#bpy-ops-preferences-copy-prev"),
("bpy.ops.preferences.keyconfig*", "editors/preferences/keymap.html#bpy-ops-preferences-keyconfig"),
@@ -1167,12 +1256,13 @@ url_manual_mapping = (
("bpy.types.kinematicconstraint*", "animation/constraints/tracking/ik_solver.html#bpy-types-kinematicconstraint"),
("bpy.types.mesh.use_paint_mask*", "sculpt_paint/brush/introduction.html#bpy-types-mesh-use-paint-mask"),
("bpy.types.movietrackingcamera*", "movie_clip/tracking/clip/sidebar/track/camera.html#bpy-types-movietrackingcamera"),
- ("bpy.types.noisepencilmodifier*", "grease_pencil/modifiers/deform/noise.html#bpy-types-noisepencilmodifier"),
("bpy.types.object.display_type*", "scene_layout/object/properties/display.html#bpy-types-object-display-type"),
+ ("bpy.types.objectlineart.usage*", "scene_layout/object/properties/line_art.html#bpy-types-objectlineart-usage"),
("bpy.types.particledupliweight*", "physics/particles/emitter/vertex_groups.html#bpy-types-particledupliweight"),
("bpy.types.poseboneconstraints*", "animation/armatures/posing/bone_constraints/index.html#bpy-types-poseboneconstraints"),
("bpy.types.rigidbodyconstraint*", "physics/rigid_body/constraints/index.html#bpy-types-rigidbodyconstraint"),
("bpy.types.rigifyarmaturelayer*", "addons/rigging/rigify/metarigs.html#bpy-types-rigifyarmaturelayer"),
+ ("bpy.types.scene.show_subframe*", "editors/timeline.html#bpy-types-scene-show-subframe"),
("bpy.types.shadernodeaddshader*", "render/shader_nodes/shader/add.html#bpy-types-shadernodeaddshader"),
("bpy.types.shadernodeattribute*", "render/shader_nodes/input/attribute.html#bpy-types-shadernodeattribute"),
("bpy.types.shadernodeblackbody*", "render/shader_nodes/converter/blackbody.html#bpy-types-shadernodeblackbody"),
@@ -1195,15 +1285,20 @@ url_manual_mapping = (
("bpy.types.viewlayer.use_solid*", "render/layers/introduction.html#bpy-types-viewlayer-use-solid"),
("bpy.types.volume.frame_offset*", "modeling/volumes/properties.html#bpy-types-volume-frame-offset"),
("bpy.types.windowmanager.addon*", "editors/preferences/addons.html#bpy-types-windowmanager-addon"),
+ ("bpy.ops.anim.channels_delete*", "editors/graph_editor/channels.html#bpy-ops-anim-channels-delete"),
+ ("bpy.ops.anim.channels_expand*", "editors/graph_editor/channels.html#bpy-ops-anim-channels-expand"),
("bpy.ops.anim.keyframe_delete*", "animation/keyframes/editing.html#bpy-ops-anim-keyframe-delete"),
("bpy.ops.anim.keyframe_insert*", "animation/keyframes/editing.html#bpy-ops-anim-keyframe-insert"),
("bpy.ops.armature.bone_layers*", "animation/armatures/bones/editing/change_layers.html#bpy-ops-armature-bone-layers"),
+ ("bpy.ops.armature.select_less*", "animation/armatures/bones/selecting.html#bpy-ops-armature-select-less"),
+ ("bpy.ops.armature.select_more*", "animation/armatures/bones/selecting.html#bpy-ops-armature-select-more"),
("bpy.ops.clip.add_marker_move*", "movie_clip/tracking/clip/editing/track.html#bpy-ops-clip-add-marker-move"),
("bpy.ops.clip.bundles_to_mesh*", "movie_clip/tracking/clip/editing/reconstruction.html#bpy-ops-clip-bundles-to-mesh"),
("bpy.ops.clip.detect_features*", "movie_clip/tracking/clip/editing/track.html#bpy-ops-clip-detect-features"),
("bpy.ops.console.autocomplete*", "editors/python_console.html#bpy-ops-console-autocomplete"),
("bpy.ops.curve.dissolve_verts*", "modeling/curves/editing/curve.html#bpy-ops-curve-dissolve-verts"),
("bpy.ops.curve.duplicate_move*", "modeling/curves/editing/curve.html#bpy-ops-curve-duplicate-move"),
+ ("bpy.ops.file.autopack_toggle*", "files/blend/packed_data.html#bpy-ops-file-autopack-toggle"),
("bpy.ops.fluid.bake_particles*", "physics/fluid/type/domain/liquid/particles.html#bpy-ops-fluid-bake-particles"),
("bpy.ops.fluid.free_particles*", "physics/fluid/type/domain/liquid/particles.html#bpy-ops-fluid-free-particles"),
("bpy.ops.gpencil.extrude_move*", "grease_pencil/modes/edit/point_menu.html#bpy-ops-gpencil-extrude-move"),
@@ -1214,6 +1309,8 @@ url_manual_mapping = (
("bpy.ops.mesh.delete_edgeloop*", "modeling/meshes/editing/mesh/delete.html#bpy-ops-mesh-delete-edgeloop"),
("bpy.ops.mesh.vertices_smooth*", "modeling/meshes/editing/vertex/smooth_vertices.html#bpy-ops-mesh-vertices-smooth"),
("bpy.ops.nla.make_single_user*", "editors/nla/editing.html#bpy-ops-nla-make-single-user"),
+ ("bpy.ops.node.clipboard_paste*", "interface/controls/nodes/editing.html#bpy-ops-node-clipboard-paste"),
+ ("bpy.ops.node.node_copy_color*", "interface/controls/nodes/sidebar.html#bpy-ops-node-node-copy-color"),
("bpy.ops.node.read_viewlayers*", "interface/controls/nodes/editing.html#bpy-ops-node-read-viewlayers"),
("bpy.ops.node.tree_socket_add*", "interface/controls/nodes/groups.html#bpy-ops-node-tree-socket-add"),
("bpy.ops.object.data_transfer*", "scene_layout/object/editing/link_transfer/transfer_mesh_data.html#bpy-ops-object-data-transfer"),
@@ -1221,6 +1318,8 @@ url_manual_mapping = (
("bpy.ops.object.select_linked*", "scene_layout/object/selecting.html#bpy-ops-object-select-linked"),
("bpy.ops.object.select_mirror*", "scene_layout/object/selecting.html#bpy-ops-object-select-mirror"),
("bpy.ops.object.select_random*", "scene_layout/object/selecting.html#bpy-ops-object-select-random"),
+ ("bpy.ops.object.transfer_mode*", "sculpt_paint/sculpting/editing/sculpt.html#bpy-ops-object-transfer-mode"),
+ ("bpy.ops.outliner.show_active*", "editors/outliner/editing.html#bpy-ops-outliner-show-active"),
("bpy.ops.paint.add_simple_uvs*", "sculpt_paint/texture_paint/tool_settings/texture_slots.html#bpy-ops-paint-add-simple-uvs"),
("bpy.ops.pose.rigify_generate*", "addons/rigging/rigify/basics.html#bpy-ops-pose-rigify-generate"),
("bpy.ops.preferences.autoexec*", "editors/preferences/save_load.html#bpy-ops-preferences-autoexec"),
@@ -1237,6 +1336,7 @@ url_manual_mapping = (
("bpy.ops.uv.project_from_view*", "modeling/meshes/editing/uv.html#bpy-ops-uv-project-from-view"),
("bpy.ops.wm.blenderkit_logout*", "addons/3d_view/blenderkit.html#bpy-ops-wm-blenderkit-logout"),
("bpy.ops.wm.memory_statistics*", "advanced/operators.html#bpy-ops-wm-memory-statistics"),
+ ("bpy.ops.wm.recover_auto_save*", "files/blend/open_save.html#bpy-ops-wm-recover-auto-save"),
("bpy.types.adjustmentsequence*", "video_editing/sequencer/strips/adjustment.html#bpy-types-adjustmentsequence"),
("bpy.types.alphaundersequence*", "video_editing/sequencer/strips/effects/alpha_over_under_overdrop.html#bpy-types-alphaundersequence"),
("bpy.types.armature.show_axes*", "animation/armatures/properties/display.html#bpy-types-armature-show-axes"),
@@ -1292,8 +1392,11 @@ url_manual_mapping = (
("bpy.types.volume.frame_start*", "modeling/volumes/properties.html#bpy-types-volume-frame-start"),
("bpy.types.volume.is_sequence*", "modeling/volumes/properties.html#bpy-types-volume-is-sequence"),
("bpy.types.volumerender.space*", "modeling/volumes/properties.html#bpy-types-volumerender-space"),
+ ("bpy.ops.anim.channels_group*", "editors/graph_editor/channels.html#bpy-ops-anim-channels-group"),
("bpy.ops.anim.keyframe_clear*", "animation/keyframes/editing.html#bpy-ops-anim-keyframe-clear"),
("bpy.ops.armature.flip_names*", "animation/armatures/bones/editing/naming.html#bpy-ops-armature-flip-names"),
+ ("bpy.ops.armature.select_all*", "animation/armatures/bones/selecting.html#bpy-ops-armature-select-all"),
+ ("bpy.ops.clip.average_tracks*", "movie_clip/tracking/clip/editing/track.html#bpy-ops-clip-average-tracks"),
("bpy.ops.clip.refine_markers*", "movie_clip/tracking/clip/editing/track.html#bpy-ops-clip-refine-markers"),
("bpy.ops.clip.select_grouped*", "movie_clip/tracking/clip/selecting.html#bpy-ops-clip-select-grouped"),
("bpy.ops.clip.track_to_empty*", "movie_clip/tracking/clip/editing/reconstruction.html#bpy-ops-clip-track-to-empty"),
@@ -1301,11 +1404,13 @@ url_manual_mapping = (
("bpy.ops.curve.primitive*add*", "modeling/curves/primitives.html#bpy-ops-curve-primitive-add"),
("bpy.ops.curve.smooth_radius*", "modeling/curves/editing/control_points.html#bpy-ops-curve-smooth-radius"),
("bpy.ops.curve.smooth_weight*", "modeling/curves/editing/control_points.html#bpy-ops-curve-smooth-weight"),
+ ("bpy.ops.file.pack_libraries*", "files/blend/packed_data.html#bpy-ops-file-pack-libraries"),
("bpy.ops.font.change_spacing*", "modeling/texts/editing.html#bpy-ops-font-change-spacing"),
("bpy.ops.gpencil.stroke_flip*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-stroke-flip"),
("bpy.ops.gpencil.stroke_join*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-stroke-join"),
("bpy.ops.gpencil.stroke_trim*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-stroke-trim"),
("bpy.ops.gpencil.trace_image*", "grease_pencil/modes/object/trace_image.html#bpy-ops-gpencil-trace-image"),
+ ("bpy.ops.image.external_edit*", "editors/image/editing.html#bpy-ops-image-external-edit"),
("bpy.ops.mesh.colors_reverse*", "modeling/meshes/editing/face/face_data.html#bpy-ops-mesh-colors-reverse"),
("bpy.ops.mesh.dissolve_edges*", "modeling/meshes/editing/mesh/delete.html#bpy-ops-mesh-dissolve-edges"),
("bpy.ops.mesh.dissolve_faces*", "modeling/meshes/editing/mesh/delete.html#bpy-ops-mesh-dissolve-faces"),
@@ -1320,11 +1425,16 @@ url_manual_mapping = (
("bpy.ops.mesh.smooth_normals*", "modeling/meshes/editing/mesh/normals.html#bpy-ops-mesh-smooth-normals"),
("bpy.ops.nla.action_pushdown*", "editors/nla/tracks.html#bpy-ops-nla-action-pushdown"),
("bpy.ops.nla.tweakmode_enter*", "editors/nla/editing.html#bpy-ops-nla-tweakmode-enter"),
+ ("bpy.ops.node.clipboard_copy*", "interface/controls/nodes/editing.html#bpy-ops-node-clipboard-copy"),
+ ("bpy.ops.node.duplicate_move*", "interface/controls/nodes/editing.html#bpy-ops-node-duplicate-move"),
+ ("bpy.ops.node.options_toggle*", "interface/controls/nodes/editing.html#bpy-ops-node-options-toggle"),
+ ("bpy.ops.node.preview_toggle*", "interface/controls/nodes/editing.html#bpy-ops-node-preview-toggle"),
("bpy.ops.object.origin_clear*", "scene_layout/object/editing/clear.html#bpy-ops-object-origin-clear"),
("bpy.ops.object.parent_clear*", "scene_layout/object/editing/parent.html#bpy-ops-object-parent-clear"),
("bpy.ops.object.shade_smooth*", "scene_layout/object/editing/shading.html#bpy-ops-object-shade-smooth"),
("bpy.ops.object.voxel_remesh*", "modeling/meshes/retopology.html#bpy-ops-object-voxel-remesh"),
("bpy.ops.pose.armature_apply*", "animation/armatures/posing/editing/apply.html#bpy-ops-pose-armature-apply"),
+ ("bpy.ops.pose.select_grouped*", "animation/armatures/posing/selecting.html#bpy-ops-pose-select-grouped"),
("bpy.ops.poselib.pose_remove*", "animation/armatures/posing/editing/pose_library.html#bpy-ops-poselib-pose-remove"),
("bpy.ops.poselib.pose_rename*", "animation/armatures/posing/editing/pose_library.html#bpy-ops-poselib-pose-rename"),
("bpy.ops.preferences.keyitem*", "editors/preferences/keymap.html#bpy-ops-preferences-keyitem"),
@@ -1339,6 +1449,7 @@ url_manual_mapping = (
("bpy.ops.uv.minimize_stretch*", "modeling/meshes/uv/editing.html#bpy-ops-uv-minimize-stretch"),
("bpy.ops.uv.select_edge_ring*", "editors/uv/selecting.html#bpy-ops-uv-select-edge-ring"),
("bpy.ops.wm.blenderkit_login*", "addons/3d_view/blenderkit.html#bpy-ops-wm-blenderkit-login"),
+ ("bpy.ops.wm.save_as_mainfile*", "files/blend/open_save.html#bpy-ops-wm-save-as-mainfile"),
("bpy.types.alphaoversequence*", "video_editing/sequencer/strips/effects/alpha_over_under_overdrop.html#bpy-types-alphaoversequence"),
("bpy.types.armatureeditbones*", "animation/armatures/bones/editing/index.html#bpy-types-armatureeditbones"),
("bpy.types.brush.pose_offset*", "sculpt_paint/sculpting/tools/pose.html#bpy-types-brush-pose-offset"),
@@ -1360,6 +1471,7 @@ url_manual_mapping = (
("bpy.types.gpencillayer.hide*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-hide"),
("bpy.types.gpencillayer.lock*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-lock"),
("bpy.types.imagepaint.dither*", "sculpt_paint/texture_paint/tool_settings/options.html#bpy-types-imagepaint-dither"),
+ ("bpy.types.materialslot.link*", "render/materials/assignment.html#bpy-types-materialslot-link"),
("bpy.types.mesh.texture_mesh*", "modeling/meshes/uv/uv_texture_spaces.html#bpy-types-mesh-texture-mesh"),
("bpy.types.mesh.use_mirror_x*", "modeling/meshes/tools/tool_settings.html#bpy-types-mesh-use-mirror-x"),
("bpy.types.mesh.use_mirror_y*", "modeling/meshes/tools/tool_settings.html#bpy-types-mesh-use-mirror-y"),
@@ -1374,6 +1486,7 @@ url_manual_mapping = (
("bpy.types.scene.active_clip*", "scene_layout/scene/properties.html#bpy-types-scene-active-clip"),
("bpy.types.scene.frame_start*", "render/output/properties/dimensions.html#bpy-types-scene-frame-start"),
("bpy.types.sceneeevee.shadow*", "render/eevee/render_settings/shadows.html#bpy-types-sceneeevee-shadow"),
+ ("bpy.types.screen.use_follow*", "editors/timeline.html#bpy-types-screen-use-follow"),
("bpy.types.sequencetransform*", "video_editing/sequencer/sidebar/strip.html#bpy-types-sequencetransform"),
("bpy.types.shadernodecombine*", "render/shader_nodes/converter/combine_separate.html#bpy-types-shadernodecombine"),
("bpy.types.shadernodefresnel*", "render/shader_nodes/input/fresnel.html#bpy-types-shadernodefresnel"),
@@ -1393,7 +1506,7 @@ url_manual_mapping = (
("bpy.types.viewlayer.use_sky*", "render/layers/introduction.html#bpy-types-viewlayer-use-sky"),
("bpy.types.wireframemodifier*", "modeling/modifiers/generate/wireframe.html#bpy-types-wireframemodifier"),
("bpy.types.worldmistsettings*", "render/cycles/world_settings.html#bpy-types-worldmistsettings"),
- ("bpy.ops.anim.channels_move*", "editors/nla/editing.html#bpy-ops-anim-channels-move"),
+ ("bpy.ops.anim.channels_move*", "editors/graph_editor/channels.html#bpy-ops-anim-channels-move"),
("bpy.ops.buttons.toggle_pin*", "editors/properties_editor.html#bpy-ops-buttons-toggle-pin"),
("bpy.ops.clip.filter_tracks*", "movie_clip/tracking/clip/editing/track.html#bpy-ops-clip-filter-tracks"),
("bpy.ops.clip.select_circle*", "movie_clip/tracking/clip/selecting.html#bpy-ops-clip-select-circle"),
@@ -1423,6 +1536,8 @@ url_manual_mapping = (
("bpy.ops.object.select_less*", "scene_layout/object/selecting.html#bpy-ops-object-select-less"),
("bpy.ops.object.select_more*", "scene_layout/object/selecting.html#bpy-ops-object-select-more"),
("bpy.ops.object.track_clear*", "animation/constraints/interface/adding_removing.html#bpy-ops-object-track-clear"),
+ ("bpy.ops.pose.select_linked*", "animation/armatures/posing/selecting.html#bpy-ops-pose-select-linked"),
+ ("bpy.ops.pose.select_mirror*", "animation/armatures/posing/selecting.html#bpy-ops-pose-select-mirror"),
("bpy.ops.poselib.apply_pose*", "animation/armatures/properties/pose_library.html#bpy-ops-poselib-apply-pose"),
("bpy.ops.screen.repeat_last*", "interface/undo_redo.html#bpy-ops-screen-repeat-last"),
("bpy.ops.sculpt.mask_expand*", "sculpt_paint/sculpting/editing/mask.html#bpy-ops-sculpt-mask-expand"),
@@ -1433,10 +1548,13 @@ url_manual_mapping = (
("bpy.ops.wm.search_operator*", "interface/controls/templates/operator_search.html#bpy-ops-wm-search-operator"),
("bpy.types.actionconstraint*", "animation/constraints/relationship/action.html#bpy-types-actionconstraint"),
("bpy.types.addonpreferences*", "editors/preferences/addons.html#bpy-types-addonpreferences"),
+ ("bpy.types.arealight.spread*", "render/lights/light_object.html#bpy-types-arealight-spread"),
("bpy.types.armaturemodifier*", "modeling/modifiers/deform/armature.html#bpy-types-armaturemodifier"),
("bpy.types.brush.cloth_mass*", "sculpt_paint/sculpting/tools/cloth.html#bpy-types-brush-cloth-mass"),
+ ("bpy.types.brushtextureslot*", "sculpt_paint/brush/texture.html#bpy-types-brushtextureslot"),
("bpy.types.colormixsequence*", "video_editing/sequencer/strips/effects/color_mix.html#bpy-types-colormixsequence"),
("bpy.types.curve.dimensions*", "modeling/curves/properties/shape.html#bpy-types-curve-dimensions"),
+ ("bpy.types.curve.taper_mode*", "modeling/curves/properties/geometry.html#bpy-types-curve-taper-mode"),
("bpy.types.curve.twist_mode*", "modeling/curves/properties/shape.html#bpy-types-curve-twist-mode"),
("bpy.types.curve.use_radius*", "modeling/curves/properties/shape.html#bpy-types-curve-use-radius"),
("bpy.types.decimatemodifier*", "modeling/modifiers/generate/decimate.html#bpy-types-decimatemodifier"),
@@ -1473,8 +1591,10 @@ url_manual_mapping = (
("bpy.types.solidifymodifier*", "modeling/modifiers/generate/solidify.html#bpy-types-solidifymodifier"),
("bpy.types.spacegrapheditor*", "editors/graph_editor/index.html#bpy-types-spacegrapheditor"),
("bpy.types.spacepreferences*", "editors/preferences/index.html#bpy-types-spacepreferences"),
+ ("bpy.types.spacespreadsheet*", "editors/spreadsheet.html#bpy-types-spacespreadsheet"),
("bpy.types.spaceview3d.lock*", "editors/3dview/sidebar.html#bpy-types-spaceview3d-lock"),
("bpy.types.spaceview3d.show*", "editors/3dview/display/index.html#bpy-types-spaceview3d-show"),
+ ("bpy.types.sphfluidsettings*", "physics/particles/emitter/physics/fluid.html#bpy-types-sphfluidsettings"),
("bpy.types.subtractsequence*", "video_editing/sequencer/strips/effects/subtract.html#bpy-types-subtractsequence"),
("bpy.types.text.indentation*", "editors/text_editor.html#bpy-types-text-indentation"),
("bpy.types.texture.contrast*", "render/materials/legacy_textures/colors.html#bpy-types-texture-contrast"),
@@ -1542,6 +1662,7 @@ url_manual_mapping = (
("bpy.types.rigidbodyobject*", "physics/rigid_body/index.html#bpy-types-rigidbodyobject"),
("bpy.types.scene.frame_end*", "render/output/properties/dimensions.html#bpy-types-scene-frame-end"),
("bpy.types.sceneeevee.gtao*", "render/eevee/render_settings/ambient_occlusion.html#bpy-types-sceneeevee-gtao"),
+ ("bpy.types.screen.use_play*", "editors/timeline.html#bpy-types-screen-use-play"),
("bpy.types.shadernodebevel*", "render/shader_nodes/input/bevel.html#bpy-types-shadernodebevel"),
("bpy.types.shadernodeclamp*", "render/shader_nodes/converter/clamp.html#bpy-types-shadernodeclamp"),
("bpy.types.shadernodegamma*", "render/shader_nodes/color/gamma.html#bpy-types-shadernodegamma"),
@@ -1568,11 +1689,15 @@ url_manual_mapping = (
("bpy.ops.mesh.edge_rotate*", "modeling/meshes/editing/edge/rotate_edge.html#bpy-ops-mesh-edge-rotate"),
("bpy.ops.mesh.unsubdivide*", "modeling/meshes/editing/edge/unsubdivide.html#bpy-ops-mesh-unsubdivide"),
("bpy.ops.mesh.uvs_reverse*", "modeling/meshes/uv/editing.html#bpy-ops-mesh-uvs-reverse"),
+ ("bpy.ops.node.hide_toggle*", "interface/controls/nodes/editing.html#bpy-ops-node-hide-toggle"),
+ ("bpy.ops.node.mute_toggle*", "interface/controls/nodes/editing.html#bpy-ops-node-mute-toggle"),
("bpy.ops.object.hide_view*", "scene_layout/object/editing/show_hide.html#bpy-ops-object-hide-view"),
("bpy.ops.object.track_set*", "animation/constraints/interface/adding_removing.html#bpy-ops-object-track-set"),
("bpy.ops.pose.scale_clear*", "animation/armatures/posing/editing/clear.html#bpy-ops-pose-scale-clear"),
("bpy.ops.poselib.pose_add*", "animation/armatures/posing/editing/pose_library.html#bpy-ops-poselib-pose-add"),
("bpy.ops.scene.view_layer*", "render/layers/introduction.html#bpy-ops-scene-view-layer"),
+ ("bpy.ops.screen.redo_last*", "interface/undo_redo.html#bpy-ops-screen-redo-last"),
+ ("bpy.ops.sculpt.mask_init*", "sculpt_paint/sculpting/editing/mask.html#bpy-ops-sculpt-mask-init"),
("bpy.ops.sequencer.delete*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-delete"),
("bpy.ops.sequencer.reload*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-reload"),
("bpy.ops.sequencer.unlock*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-unlock"),
@@ -1585,7 +1710,9 @@ url_manual_mapping = (
("bpy.ops.uv.snap_selected*", "modeling/meshes/uv/editing.html#bpy-ops-uv-snap-selected"),
("bpy.ops.view3d.localview*", "editors/3dview/navigate/local_view.html#bpy-ops-view3d-localview"),
("bpy.ops.view3d.view_axis*", "editors/3dview/navigate/viewpoint.html#bpy-ops-view3d-view-axis"),
+ ("bpy.ops.wm.open_mainfile*", "files/blend/open_save.html#bpy-ops-wm-open-mainfile"),
("bpy.ops.wm.owner_disable*", "interface/window_system/workspaces.html#bpy-ops-wm-owner-disable"),
+ ("bpy.ops.wm.save_mainfile*", "files/blend/open_save.html#bpy-ops-wm-save-mainfile"),
("bpy.types.bone.show_wire*", "animation/armatures/bones/properties/display.html#bpy-types-bone-show-wire"),
("bpy.types.brush.hardness*", "sculpt_paint/sculpting/tool_settings/brush_settings.html#bpy-types-brush-hardness"),
("bpy.types.curvesmodifier*", "video_editing/sequencer/sidebar/modifiers.html#bpy-types-curvesmodifier"),
@@ -1623,6 +1750,7 @@ url_manual_mapping = (
("bpy.ops.clip.set_origin*", "movie_clip/tracking/clip/editing/reconstruction.html#bpy-ops-clip-set-origin"),
("bpy.ops.curve.subdivide*", "modeling/curves/editing/segments.html#bpy-ops-curve-subdivide"),
("bpy.ops.ed.undo_history*", "interface/undo_redo.html#bpy-ops-ed-undo-history"),
+ ("bpy.ops.file.unpack_all*", "files/blend/packed_data.html#bpy-ops-file-unpack-all"),
("bpy.ops.fluid.bake_data*", "physics/fluid/type/domain/settings.html#bpy-ops-fluid-bake-data"),
("bpy.ops.fluid.bake_mesh*", "physics/fluid/type/domain/liquid/mesh.html#bpy-ops-fluid-bake-mesh"),
("bpy.ops.fluid.free_data*", "physics/fluid/type/domain/settings.html#bpy-ops-fluid-free-data"),
@@ -1640,11 +1768,13 @@ url_manual_mapping = (
("bpy.ops.nla.clear_scale*", "editors/nla/editing.html#bpy-ops-nla-clear-scale"),
("bpy.ops.nla.mute_toggle*", "editors/nla/editing.html#bpy-ops-nla-mute-toggle"),
("bpy.ops.node.group_make*", "interface/controls/nodes/groups.html#bpy-ops-node-group-make"),
+ ("bpy.ops.node.links_mute*", "interface/controls/nodes/editing.html#bpy-ops-node-links-mute"),
("bpy.ops.object.armature*", "animation/armatures/index.html#bpy-ops-object-armature"),
("bpy.ops.object.face_map*", "modeling/meshes/properties/object_data.html#bpy-ops-object-face-map"),
("bpy.ops.object.join_uvs*", "scene_layout/object/editing/link_transfer/copy_uvmaps.html#bpy-ops-object-join-uvs"),
("bpy.ops.outliner.delete*", "editors/outliner/editing.html#bpy-ops-outliner-delete"),
("bpy.ops.pose.relax_rest*", "animation/armatures/posing/editing/in_betweens.html#bpy-ops-pose-relax-rest"),
+ ("bpy.ops.pose.select_all*", "animation/armatures/posing/selecting.html#bpy-ops-pose-select-all"),
("bpy.ops.rigidbody.world*", "physics/rigid_body/world.html#bpy-ops-rigidbody-world"),
("bpy.ops.sculpt.optimize*", "sculpt_paint/sculpting/editing/sculpt.html#bpy-ops-sculpt-optimize"),
("bpy.ops.sequencer.split*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-split"),
@@ -1680,6 +1810,7 @@ url_manual_mapping = (
("bpy.types.nlastrip.name*", "editors/nla/sidebar.html#bpy-types-nlastrip-name"),
("bpy.types.nodesmodifier*", "modeling/modifiers/generate/geometry_nodes.html#bpy-types-nodesmodifier"),
("bpy.types.object.parent*", "scene_layout/object/editing/parent.html#bpy-types-object-parent"),
+ ("bpy.types.objectlineart*", "scene_layout/object/properties/line_art.html#bpy-types-objectlineart"),
("bpy.types.oceanmodifier*", "modeling/modifiers/physics/ocean.html#bpy-types-oceanmodifier"),
("bpy.types.particlebrush*", "physics/particles/mode.html#bpy-types-particlebrush"),
("bpy.types.scene.gravity*", "physics/forces/gravity.html#bpy-types-scene-gravity"),
@@ -1710,12 +1841,15 @@ url_manual_mapping = (
("bpy.ops.gpencil.reveal*", "grease_pencil/properties/layers.html#bpy-ops-gpencil-reveal"),
("bpy.ops.gpencil.select*", "grease_pencil/selecting.html#bpy-ops-gpencil-select"),
("bpy.ops.graph.decimate*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-decimate"),
+ ("bpy.ops.material.paste*", "render/materials/assignment.html#bpy-ops-material-paste"),
("bpy.ops.mesh.fill_grid*", "modeling/meshes/editing/face/grid_fill.html#bpy-ops-mesh-fill-grid"),
("bpy.ops.mesh.intersect*", "modeling/meshes/editing/face/intersect_knife.html#bpy-ops-mesh-intersect"),
("bpy.ops.mesh.mark_seam*", "modeling/meshes/editing/edge/edge_data.html#bpy-ops-mesh-mark-seam"),
("bpy.ops.mesh.polybuild*", "modeling/meshes/tools/poly_build.html#bpy-ops-mesh-polybuild"),
("bpy.ops.mesh.subdivide*", "modeling/meshes/editing/edge/subdivide.html#bpy-ops-mesh-subdivide"),
("bpy.ops.mesh.wireframe*", "modeling/meshes/editing/face/wireframe.html#bpy-ops-mesh-wireframe"),
+ ("bpy.ops.node.link_make*", "interface/controls/nodes/editing.html#bpy-ops-node-link-make"),
+ ("bpy.ops.node.links_cut*", "interface/controls/nodes/editing.html#bpy-ops-node-links-cut"),
("bpy.ops.object.convert*", "scene_layout/object/editing/convert.html#bpy-ops-object-convert"),
("bpy.ops.object.gpencil*", "grease_pencil/index.html#bpy-ops-object-gpencil"),
("bpy.ops.object.speaker*", "render/output/audio/speaker.html#bpy-ops-object-speaker"),
@@ -1737,16 +1871,17 @@ url_manual_mapping = (
("bpy.types.blendtexture*", "render/materials/legacy_textures/types/blend.html#bpy-types-blendtexture"),
("bpy.types.brush.height*", "sculpt_paint/sculpting/tools/layer.html#bpy-types-brush-height"),
("bpy.types.castmodifier*", "modeling/modifiers/deform/cast.html#bpy-types-castmodifier"),
- ("bpy.types.colormanaged*", "render/color_management.html#bpy-types-colormanaged"),
("bpy.types.curve.offset*", "modeling/curves/properties/geometry.html#bpy-types-curve-offset"),
("bpy.types.geometrynode*", "modeling/geometry_nodes/index.html#bpy-types-geometrynode"),
("bpy.types.glowsequence*", "video_editing/sequencer/strips/effects/glow.html#bpy-types-glowsequence"),
("bpy.types.gpencillayer*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer"),
("bpy.types.hookmodifier*", "modeling/modifiers/deform/hooks.html#bpy-types-hookmodifier"),
+ ("bpy.types.imagetexture*", "render/materials/legacy_textures/types/image_movie.html#bpy-types-imagetexture"),
("bpy.types.latticepoint*", "animation/lattice.html#bpy-types-latticepoint"),
("bpy.types.magictexture*", "render/materials/legacy_textures/types/magic.html#bpy-types-magictexture"),
("bpy.types.maskmodifier*", "modeling/modifiers/generate/mask.html#bpy-types-maskmodifier"),
("bpy.types.masksequence*", "video_editing/sequencer/strips/clip_mask.html#bpy-types-masksequence"),
+ ("bpy.types.materialslot*", "render/materials/assignment.html#bpy-types-materialslot"),
("bpy.types.metasequence*", "video_editing/sequencer/meta.html#bpy-types-metasequence"),
("bpy.types.object.color*", "scene_layout/object/properties/display.html#bpy-types-object-color"),
("bpy.types.object.delta*", "scene_layout/object/properties/transforms.html#bpy-types-object-delta"),
@@ -1772,8 +1907,12 @@ url_manual_mapping = (
("bpy.types.wipesequence*", "video_editing/sequencer/strips/transitions/wipe.html#bpy-types-wipesequence"),
("bpy.ops.clip.prefetch*", "movie_clip/tracking/clip/editing/clip.html#bpy-ops-clip-prefetch"),
("bpy.ops.clip.set_axis*", "movie_clip/tracking/clip/editing/reconstruction.html#bpy-ops-clip-set-axis"),
+ ("bpy.ops.file.pack_all*", "files/blend/packed_data.html#bpy-ops-file-pack-all"),
("bpy.ops.gpencil.paste*", "grease_pencil/modes/edit/grease_pencil_menu.html#bpy-ops-gpencil-paste"),
("bpy.ops.image.project*", "sculpt_paint/texture_paint/tool_settings/options.html#bpy-ops-image-project"),
+ ("bpy.ops.image.replace*", "editors/image/editing.html#bpy-ops-image-replace"),
+ ("bpy.ops.image.save_as*", "editors/image/editing.html#bpy-ops-image-save-as"),
+ ("bpy.ops.material.copy*", "render/materials/assignment.html#bpy-ops-material-copy"),
("bpy.ops.mesh.decimate*", "modeling/meshes/editing/mesh/cleanup.html#bpy-ops-mesh-decimate"),
("bpy.ops.mesh.dissolve*", "modeling/meshes/editing/mesh/delete.html#bpy-ops-mesh-dissolve"),
("bpy.ops.mesh.rip_move*", "modeling/meshes/editing/vertex/rip_vertices.html#bpy-ops-mesh-rip-move"),
@@ -1786,6 +1925,7 @@ url_manual_mapping = (
("bpy.ops.render.opengl*", "editors/3dview/viewport_render.html#bpy-ops-render-opengl"),
("bpy.ops.screen.header*", "interface/window_system/regions.html#bpy-ops-screen-header"),
("bpy.ops.script.reload*", "advanced/operators.html#bpy-ops-script-reload"),
+ ("bpy.ops.sculpt.expand*", "sculpt_paint/sculpting/editing/mask.html#bpy-ops-sculpt-expand"),
("bpy.ops.view3d.select*", "editors/3dview/selecting.html#bpy-ops-view3d-select"),
("bpy.ops.wm.debug_menu*", "advanced/operators.html#bpy-ops-wm-debug-menu"),
("bpy.ops.wm.properties*", "files/data_blocks.html#bpy-ops-wm-properties"),
@@ -1820,9 +1960,15 @@ url_manual_mapping = (
("bpy.ops.gpencil.copy*", "grease_pencil/modes/edit/grease_pencil_menu.html#bpy-ops-gpencil-copy"),
("bpy.ops.graph.delete*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-delete"),
("bpy.ops.graph.mirror*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-mirror"),
+ ("bpy.ops.graph.reveal*", "editors/graph_editor/channels.html#bpy-ops-graph-reveal"),
("bpy.ops.graph.sample*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-sample"),
("bpy.ops.graph.smooth*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-smooth"),
("bpy.ops.graph.unbake*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-unbake"),
+ ("bpy.ops.image.invert*", "editors/image/editing.html#bpy-ops-image-invert"),
+ ("bpy.ops.image.reload*", "editors/image/editing.html#bpy-ops-image-reload"),
+ ("bpy.ops.image.resize*", "editors/image/editing.html#bpy-ops-image-resize"),
+ ("bpy.ops.image.unpack*", "editors/image/editing.html#bpy-ops-image-unpack"),
+ ("bpy.ops.material.new*", "render/materials/assignment.html#bpy-ops-material-new"),
("bpy.ops.object.align*", "scene_layout/object/editing/transform/align_objects.html#bpy-ops-object-align"),
("bpy.ops.object.empty*", "modeling/empties.html#bpy-ops-object-empty"),
("bpy.ops.object.quick*", "physics/introduction.html#bpy-ops-object-quick"),
@@ -1836,6 +1982,8 @@ url_manual_mapping = (
("bpy.types.constraint*", "animation/constraints/index.html#bpy-types-constraint"),
("bpy.types.imagepaint*", "sculpt_paint/texture_paint/index.html#bpy-types-imagepaint"),
("bpy.types.lightprobe*", "render/eevee/light_probes/index.html#bpy-types-lightprobe"),
+ ("bpy.types.node.color*", "interface/controls/nodes/sidebar.html#bpy-types-node-color"),
+ ("bpy.types.node.label*", "interface/controls/nodes/sidebar.html#bpy-types-node-label"),
("bpy.types.nodesocket*", "interface/controls/nodes/parts.html#bpy-types-nodesocket"),
("bpy.types.paint.tile*", "sculpt_paint/texture_paint/tool_settings/tiling.html#bpy-types-paint-tile"),
("bpy.types.pointcache*", "physics/baking.html#bpy-types-pointcache"),
@@ -1850,11 +1998,13 @@ url_manual_mapping = (
("bpy.ops.mesh.bisect*", "modeling/meshes/editing/mesh/bisect.html#bpy-ops-mesh-bisect"),
("bpy.ops.mesh.delete*", "modeling/meshes/editing/mesh/delete.html#bpy-ops-mesh-delete"),
("bpy.ops.nla.move_up*", "editors/nla/editing.html#bpy-ops-nla-move-up"),
+ ("bpy.ops.node.delete*", "interface/controls/nodes/editing.html#bpy-ops-node-delete"),
("bpy.ops.object.bake*", "render/cycles/baking.html#bpy-ops-object-bake"),
("bpy.ops.object.hook*", "modeling/meshes/editing/vertex/hooks.html#bpy-ops-object-hook"),
("bpy.ops.object.join*", "scene_layout/object/editing/join.html#bpy-ops-object-join"),
("bpy.ops.object.text*", "modeling/texts/index.html#bpy-ops-object-text"),
("bpy.ops.preferences*", "editors/preferences/index.html#bpy-ops-preferences"),
+ ("bpy.ops.spreadsheet*", "editors/spreadsheet.html#bpy-ops-spreadsheet"),
("bpy.ops.uv.rip_move*", "modeling/meshes/uv/tools/rip.html#bpy-ops-uv-rip-move"),
("bpy.ops.view3d.snap*", "scene_layout/object/editing/snap.html#bpy-ops-view3d-snap"),
("bpy.ops.view3d.zoom*", "editors/3dview/navigate/navigation.html#bpy-ops-view3d-zoom"),
@@ -1868,6 +2018,7 @@ url_manual_mapping = (
("bpy.types.fmodifier*", "editors/graph_editor/fcurves/sidebar/modifiers.html#bpy-types-fmodifier"),
("bpy.types.freestyle*", "render/freestyle/index.html#bpy-types-freestyle"),
("bpy.types.movieclip*", "movie_clip/index.html#bpy-types-movieclip"),
+ ("bpy.types.node.name*", "interface/controls/nodes/sidebar.html#bpy-types-node-name"),
("bpy.types.nodeframe*", "interface/controls/nodes/frame.html#bpy-types-nodeframe"),
("bpy.types.nodegroup*", "interface/controls/nodes/groups.html#bpy-types-nodegroup"),
("bpy.types.spotlight*", "render/lights/light_object.html#bpy-types-spotlight"),
@@ -1883,7 +2034,12 @@ url_manual_mapping = (
("bpy.ops.curve.spin*", "modeling/surfaces/editing/surface.html#bpy-ops-curve-spin"),
("bpy.ops.graph.bake*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-bake"),
("bpy.ops.graph.copy*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-copy"),
+ ("bpy.ops.graph.hide*", "editors/graph_editor/channels.html#bpy-ops-graph-hide"),
("bpy.ops.graph.snap*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-snap"),
+ ("bpy.ops.image.flip*", "editors/image/editing.html#bpy-ops-image-flip"),
+ ("bpy.ops.image.open*", "editors/image/editing.html#bpy-ops-image-open"),
+ ("bpy.ops.image.pack*", "editors/image/editing.html#bpy-ops-image-pack"),
+ ("bpy.ops.image.save*", "editors/image/editing.html#bpy-ops-image-save"),
("bpy.ops.image.tile*", "modeling/meshes/uv/workflows/udims.html#bpy-ops-image-tile"),
("bpy.ops.mesh.bevel*", "modeling/meshes/editing/edge/bevel.html#bpy-ops-mesh-bevel"),
("bpy.ops.mesh.inset*", "modeling/meshes/editing/face/inset_faces.html#bpy-ops-mesh-inset"),
@@ -1915,6 +2071,7 @@ url_manual_mapping = (
("bpy.types.spacenla*", "editors/nla/index.html#bpy-types-spacenla"),
("bpy.types.sunlight*", "render/lights/light_object.html#bpy-types-sunlight"),
("bpy.ops.clip.open*", "movie_clip/tracking/clip/editing/clip.html#bpy-ops-clip-open"),
+ ("bpy.ops.image.new*", "editors/image/editing.html#bpy-ops-image-new"),
("bpy.ops.mesh.fill*", "modeling/meshes/editing/face/fill.html#bpy-ops-mesh-fill"),
("bpy.ops.mesh.poke*", "modeling/meshes/editing/face/poke_faces.html#bpy-ops-mesh-poke"),
("bpy.ops.mesh.spin*", "modeling/meshes/tools/spin.html#bpy-ops-mesh-spin"),
@@ -1937,6 +2094,7 @@ url_manual_mapping = (
("bpy.types.textbox*", "modeling/texts/properties.html#bpy-types-textbox"),
("bpy.types.texture*", "render/materials/legacy_textures/index.html#bpy-types-texture"),
("bpy.ops.armature*", "animation/armatures/index.html#bpy-ops-armature"),
+ ("bpy.ops.material*", "render/materials/index.html#bpy-ops-material"),
("bpy.ops.nla.bake*", "animation/actions.html#bpy-ops-nla-bake"),
("bpy.ops.nla.snap*", "editors/nla/editing.html#bpy-ops-nla-snap"),
("bpy.ops.nla.swap*", "editors/nla/editing.html#bpy-ops-nla-swap"),
@@ -1954,6 +2112,7 @@ url_manual_mapping = (
("bpy.types.object*", "scene_layout/object/index.html#bpy-types-object"),
("bpy.types.region*", "interface/window_system/regions.html#bpy-types-region"),
("bpy.types.render*", "render/index.html#bpy-types-render"),
+ ("bpy.types.screen*", "interface/index.html#bpy-types-screen"),
("bpy.types.sculpt*", "sculpt_paint/sculpting/index.html#bpy-types-sculpt"),
("bpy.types.shader*", "render/shader_nodes/shader/index.html#bpy-types-shader"),
("bpy.types.spline*", "modeling/curves/properties/active_spline.html#bpy-types-spline"),
@@ -2005,6 +2164,7 @@ url_manual_mapping = (
("bpy.types.mask*", "movie_clip/masking/index.html#bpy-types-mask"),
("bpy.types.menu*", "interface/controls/buttons/menus.html#bpy-types-menu"),
("bpy.types.mesh*", "modeling/meshes/index.html#bpy-types-mesh"),
+ ("bpy.types.node*", "interface/controls/nodes/index.html#bpy-types-node"),
("bpy.types.pose*", "animation/armatures/posing/index.html#bpy-types-pose"),
("bpy.types.text*", "editors/text_editor.html#bpy-types-text"),
("bpy.ops.brush*", "sculpt_paint/brush/brush.html#bpy-ops-brush"),
diff --git a/release/scripts/modules/rna_prop_ui.py b/release/scripts/modules/rna_prop_ui.py
index e3158118146..bafa2b28bbf 100644
--- a/release/scripts/modules/rna_prop_ui.py
+++ b/release/scripts/modules/rna_prop_ui.py
@@ -30,7 +30,7 @@ ARRAY_TYPES = (list, tuple, IDPropertyArray, Vector)
MAX_DISPLAY_ROWS = 4
-def rna_idprop_ui_get(item, create=True):
+def rna_idprop_ui_get(item, *, create=True):
try:
return item['_RNA_UI']
except:
@@ -59,9 +59,9 @@ def rna_idprop_ui_prop_update(item, prop):
prop_rna.update()
-def rna_idprop_ui_prop_get(item, prop, create=True):
+def rna_idprop_ui_prop_get(item, prop, *, create=True):
- rna_ui = rna_idprop_ui_get(item, create)
+ rna_ui = rna_idprop_ui_get(item, create=create)
if rna_ui is None:
return None
@@ -73,8 +73,8 @@ def rna_idprop_ui_prop_get(item, prop, create=True):
return rna_ui[prop]
-def rna_idprop_ui_prop_clear(item, prop, remove=True):
- rna_ui = rna_idprop_ui_get(item, False)
+def rna_idprop_ui_prop_clear(item, prop, *, remove=True):
+ rna_ui = rna_idprop_ui_get(item, create=False)
if rna_ui is None:
return
@@ -143,7 +143,7 @@ def rna_idprop_ui_prop_default_set(item, prop, value):
pass
if defvalue:
- rna_ui = rna_idprop_ui_prop_get(item, prop, True)
+ rna_ui = rna_idprop_ui_prop_get(item, prop, create=True)
rna_ui["default"] = defvalue
else:
rna_ui = rna_idprop_ui_prop_get(item, prop)
@@ -181,7 +181,7 @@ def rna_idprop_ui_create(
rna_idprop_ui_prop_update(item, prop)
# Clear the UI settings
- rna_ui_group = rna_idprop_ui_get(item, True)
+ rna_ui_group = rna_idprop_ui_get(item, create=True)
rna_ui_group[prop] = {}
rna_ui = rna_ui_group[prop]
@@ -210,7 +210,7 @@ def rna_idprop_ui_create(
return rna_ui
-def draw(layout, context, context_member, property_type, use_edit=True):
+def draw(layout, context, context_member, property_type, *, use_edit=True):
def assign_props(prop, val, key):
prop.data_path = context_member
@@ -235,7 +235,7 @@ def draw(layout, context, context_member, property_type, use_edit=True):
assert(isinstance(rna_item, property_type))
- items = rna_item.items()
+ items = list(rna_item.items())
items.sort()
# TODO: Allow/support adding new custom props to overrides.
diff --git a/release/scripts/modules/rna_xml.py b/release/scripts/modules/rna_xml.py
index 58abb5c90db..a36b262c883 100644
--- a/release/scripts/modules/rna_xml.py
+++ b/release/scripts/modules/rna_xml.py
@@ -245,9 +245,10 @@ def rna2xml(
fw("%s</%s>\n" % (root_ident, root_node))
-def xml2rna(root_xml,
- root_rna=None, # must be set
- ):
+def xml2rna(
+ root_xml, *,
+ root_rna=None, # must be set
+):
def rna2xml_node(xml_node, value):
# print("evaluating:", xml_node.nodeName)
@@ -394,7 +395,7 @@ def xml_file_run(context, filepath, rna_map):
xml2rna(xml_node, root_rna=value)
-def xml_file_write(context, filepath, rna_map, skip_typemap=None):
+def xml_file_write(context, filepath, rna_map, *, skip_typemap=None):
file = open(filepath, "w", encoding="utf-8")
fw = file.write
diff --git a/release/scripts/modules/sys_info.py b/release/scripts/modules/sys_info.py
index cb80529f0f3..192fc1a201f 100644
--- a/release/scripts/modules/sys_info.py
+++ b/release/scripts/modules/sys_info.py
@@ -23,11 +23,12 @@
def write_sysinfo(filepath):
import sys
+ import platform
import subprocess
import bpy
- import bgl
+ import gpu
# pretty repr
def prepr(v):
@@ -63,7 +64,7 @@ def write_sysinfo(filepath):
))
output.write("build date: %s, %s\n" % (prepr(bpy.app.build_date), prepr(bpy.app.build_time)))
- output.write("platform: %s\n" % prepr(bpy.app.build_platform))
+ output.write("platform: %s\n" % prepr(platform.platform()))
output.write("binary path: %s\n" % prepr(bpy.app.binary_path))
output.write("build cflags: %s\n" % prepr(bpy.app.build_cflags))
output.write("build cxxflags: %s\n" % prepr(bpy.app.build_cxxflags))
@@ -189,46 +190,29 @@ def write_sysinfo(filepath):
if bpy.app.background:
output.write("\nOpenGL: missing, background mode\n")
else:
- output.write(title("OpenGL"))
- version = bgl.glGetString(bgl.GL_RENDERER)
- output.write("renderer:\t%r\n" % version)
- output.write("vendor:\t\t%r\n" % (bgl.glGetString(bgl.GL_VENDOR)))
- output.write("version:\t%r\n" % (bgl.glGetString(bgl.GL_VERSION)))
+ output.write(title("GPU"))
+ output.write("renderer:\t%r\n" % gpu.platform.renderer_get())
+ output.write("vendor:\t\t%r\n" % gpu.platform.vendor_get())
+ output.write("version:\t%r\n" % gpu.platform.version_get())
output.write("extensions:\n")
- limit = bgl.Buffer(bgl.GL_INT, 1)
- bgl.glGetIntegerv(bgl.GL_NUM_EXTENSIONS, limit)
-
- glext = []
- for i in range(limit[0]):
- glext.append(bgl.glGetStringi(bgl.GL_EXTENSIONS, i))
-
- glext = sorted(glext)
+ glext = sorted(gpu.capabilities.extensions_get())
for l in glext:
output.write("\t%s\n" % l)
- output.write(title("Implementation Dependent OpenGL Limits"))
- bgl.glGetIntegerv(bgl.GL_MAX_ELEMENTS_VERTICES, limit)
- output.write("Maximum DrawElements Vertices:\t%d\n" % limit[0])
- bgl.glGetIntegerv(bgl.GL_MAX_ELEMENTS_INDICES, limit)
- output.write("Maximum DrawElements Indices:\t%d\n" % limit[0])
+ output.write(title("Implementation Dependent GPU Limits"))
+ output.write("Maximum Batch Vertices:\t%d\n" % gpu.capabilities.max_batch_vertices_get())
+ output.write("Maximum Batch Indices:\t%d\n" % gpu.capabilities.max_batch_indices_get())
output.write("\nGLSL:\n")
- bgl.glGetIntegerv(bgl.GL_MAX_VARYING_FLOATS, limit)
- output.write("Maximum Varying Floats:\t%d\n" % limit[0])
- bgl.glGetIntegerv(bgl.GL_MAX_VERTEX_ATTRIBS, limit)
- output.write("Maximum Vertex Attributes:\t%d\n" % limit[0])
- bgl.glGetIntegerv(bgl.GL_MAX_VERTEX_UNIFORM_COMPONENTS, limit)
- output.write("Maximum Vertex Uniform Components:\t%d\n" % limit[0])
- bgl.glGetIntegerv(bgl.GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, limit)
- output.write("Maximum Fragment Uniform Components:\t%d\n" % limit[0])
- bgl.glGetIntegerv(bgl.GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, limit)
- output.write("Maximum Vertex Image Units:\t%d\n" % limit[0])
- bgl.glGetIntegerv(bgl.GL_MAX_TEXTURE_IMAGE_UNITS, limit)
- output.write("Maximum Fragment Image Units:\t%d\n" % limit[0])
- bgl.glGetIntegerv(bgl.GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, limit)
- output.write("Maximum Pipeline Image Units:\t%d\n" % limit[0])
+ output.write("Maximum Varying Floats:\t%d\n" % gpu.capabilities.max_varying_floats_get())
+ output.write("Maximum Vertex Attributes:\t%d\n" % gpu.capabilities.max_vertex_attribs_get())
+ output.write("Maximum Vertex Uniform Components:\t%d\n" % gpu.capabilities.max_uniforms_vert_get())
+ output.write("Maximum Fragment Uniform Components:\t%d\n" % gpu.capabilities.max_uniforms_frag_get())
+ output.write("Maximum Vertex Image Units:\t%d\n" % gpu.capabilities.max_textures_vert_get())
+ output.write("Maximum Fragment Image Units:\t%d\n" % gpu.capabilities.max_textures_frag_get())
+ output.write("Maximum Pipeline Image Units:\t%d\n" % gpu.capabilities.max_textures_get())
if bpy.app.build_options.cycles:
import cycles
diff --git a/release/scripts/presets/camera/1_colon_2.3_inch.py b/release/scripts/presets/camera/1_colon_2.3_inch.py
deleted file mode 100644
index 72548384401..00000000000
--- a/release/scripts/presets/camera/1_colon_2.3_inch.py
+++ /dev/null
@@ -1,4 +0,0 @@
-import bpy
-bpy.context.camera.sensor_width = 6.16
-bpy.context.camera.sensor_height = 4.62
-bpy.context.camera.sensor_fit = 'HORIZONTAL'
diff --git a/release/scripts/presets/camera/1_inch.py b/release/scripts/presets/camera/1_inch.py
new file mode 100644
index 00000000000..72b039fb978
--- /dev/null
+++ b/release/scripts/presets/camera/1_inch.py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 13.2
+bpy.context.camera.sensor_height = 8.80
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/camera/1_slash_1.8_inch.py b/release/scripts/presets/camera/1_slash_1.8_inch.py
new file mode 100644
index 00000000000..38e09182de6
--- /dev/null
+++ b/release/scripts/presets/camera/1_slash_1.8_inch.py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 7.18
+bpy.context.camera.sensor_height = 5.32
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/camera/1_slash_2.3_inch.py b/release/scripts/presets/camera/1_slash_2.3_inch.py
new file mode 100644
index 00000000000..4d55738f4ed
--- /dev/null
+++ b/release/scripts/presets/camera/1_slash_2.3_inch.py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 6.17
+bpy.context.camera.sensor_height = 4.55
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/camera/1_colon_2.5_inch.py b/release/scripts/presets/camera/1_slash_2.5_inch.py
index 90f60e7d7f0..cbdb6f3cbe0 100644
--- a/release/scripts/presets/camera/1_colon_2.5_inch.py
+++ b/release/scripts/presets/camera/1_slash_2.5_inch.py
@@ -1,4 +1,4 @@
import bpy
bpy.context.camera.sensor_width = 5.76
bpy.context.camera.sensor_height = 4.29
-bpy.context.camera.sensor_fit = 'HORIZONTAL'
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/camera/1_slash_2.7_inch.py b/release/scripts/presets/camera/1_slash_2.7_inch.py
new file mode 100644
index 00000000000..5ccfa4ab555
--- /dev/null
+++ b/release/scripts/presets/camera/1_slash_2.7_inch.py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 5.37
+bpy.context.camera.sensor_height = 4.04
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/camera/iPhone_4.py b/release/scripts/presets/camera/1_slash_3.2_inch.py
index 1e43cd11494..1963f7ec048 100644
--- a/release/scripts/presets/camera/iPhone_4.py
+++ b/release/scripts/presets/camera/1_slash_3.2_inch.py
@@ -1,5 +1,4 @@
import bpy
bpy.context.camera.sensor_width = 4.54
bpy.context.camera.sensor_height = 3.42
-bpy.context.camera.lens = 3.85
-bpy.context.camera.sensor_fit = 'HORIZONTAL'
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/camera/2_colon_3_inch.py b/release/scripts/presets/camera/2_colon_3_inch.py
deleted file mode 100644
index 46436970efc..00000000000
--- a/release/scripts/presets/camera/2_colon_3_inch.py
+++ /dev/null
@@ -1,4 +0,0 @@
-import bpy
-bpy.context.camera.sensor_width = 9.6
-bpy.context.camera.sensor_height = 5.4
-bpy.context.camera.sensor_fit = 'HORIZONTAL'
diff --git a/release/scripts/presets/camera/2_slash_3_inch.py b/release/scripts/presets/camera/2_slash_3_inch.py
new file mode 100644
index 00000000000..25b46016800
--- /dev/null
+++ b/release/scripts/presets/camera/2_slash_3_inch.py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 8.8
+bpy.context.camera.sensor_height = 6.6
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/camera/4_colon_3_inch.py b/release/scripts/presets/camera/4_colon_3_inch.py
deleted file mode 100644
index 88346c01ef8..00000000000
--- a/release/scripts/presets/camera/4_colon_3_inch.py
+++ /dev/null
@@ -1,4 +0,0 @@
-import bpy
-bpy.context.camera.sensor_width = 17.31
-bpy.context.camera.sensor_height = 12.98
-bpy.context.camera.sensor_fit = 'HORIZONTAL'
diff --git a/release/scripts/presets/camera/APS-C.py b/release/scripts/presets/camera/APS-C.py
new file mode 100644
index 00000000000..84e40825248
--- /dev/null
+++ b/release/scripts/presets/camera/APS-C.py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 23.6
+bpy.context.camera.sensor_height = 15.6
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/camera/APS-C_(Canon).py b/release/scripts/presets/camera/APS-C_(Canon).py
new file mode 100644
index 00000000000..55f20ce0eac
--- /dev/null
+++ b/release/scripts/presets/camera/APS-C_(Canon).py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 22.30
+bpy.context.camera.sensor_height = 14.90
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/camera/Canon_APS-H.py b/release/scripts/presets/camera/APS-H_(Canon).py
index d3b61d1aa46..d63f733280b 100644
--- a/release/scripts/presets/camera/Canon_APS-H.py
+++ b/release/scripts/presets/camera/APS-H_(Canon).py
@@ -1,4 +1,4 @@
import bpy
bpy.context.camera.sensor_width = 27.90
bpy.context.camera.sensor_height = 18.60
-bpy.context.camera.sensor_fit = 'HORIZONTAL'
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/camera/Analog_16mm.py b/release/scripts/presets/camera/Analog_16mm.py
new file mode 100644
index 00000000000..aa98eaf2408
--- /dev/null
+++ b/release/scripts/presets/camera/Analog_16mm.py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 10.26
+bpy.context.camera.sensor_height = 7.49
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/camera/Analog_35mm.py b/release/scripts/presets/camera/Analog_35mm.py
new file mode 100644
index 00000000000..a0dee1f0166
--- /dev/null
+++ b/release/scripts/presets/camera/Analog_35mm.py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 22
+bpy.context.camera.sensor_height = 16
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/camera/Analog_65mm.py b/release/scripts/presets/camera/Analog_65mm.py
new file mode 100644
index 00000000000..8de91ac0ee3
--- /dev/null
+++ b/release/scripts/presets/camera/Analog_65mm.py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 52.45
+bpy.context.camera.sensor_height = 23.01
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/camera/Analog_IMAX.py b/release/scripts/presets/camera/Analog_IMAX.py
new file mode 100644
index 00000000000..5a445f3de8c
--- /dev/null
+++ b/release/scripts/presets/camera/Analog_IMAX.py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 71.41
+bpy.context.camera.sensor_height = 52.63
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/camera/Analog_Super_16.py b/release/scripts/presets/camera/Analog_Super_16.py
new file mode 100644
index 00000000000..a340a31dc25
--- /dev/null
+++ b/release/scripts/presets/camera/Analog_Super_16.py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 12.35
+bpy.context.camera.sensor_height = 7.42
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/camera/Super_35_Film.py b/release/scripts/presets/camera/Analog_Super_35.py
index b22ff545c68..3c8f1837253 100644
--- a/release/scripts/presets/camera/Super_35_Film.py
+++ b/release/scripts/presets/camera/Analog_Super_35.py
@@ -1,4 +1,4 @@
import bpy
bpy.context.camera.sensor_width = 24.89
bpy.context.camera.sensor_height = 18.66
-bpy.context.camera.sensor_fit = 'HORIZONTAL'
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/camera/Arri_Alexa.py b/release/scripts/presets/camera/Arri_Alexa.py
deleted file mode 100644
index 6a6cdfee12b..00000000000
--- a/release/scripts/presets/camera/Arri_Alexa.py
+++ /dev/null
@@ -1,4 +0,0 @@
-import bpy
-bpy.context.camera.sensor_width = 23.760
-bpy.context.camera.sensor_height = 13.365
-bpy.context.camera.sensor_fit = 'HORIZONTAL'
diff --git a/release/scripts/presets/camera/Arri_Alexa_65.py b/release/scripts/presets/camera/Arri_Alexa_65.py
new file mode 100644
index 00000000000..b1467709949
--- /dev/null
+++ b/release/scripts/presets/camera/Arri_Alexa_65.py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 54.12
+bpy.context.camera.sensor_height = 25.58
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/camera/Arri_Alexa_LF.py b/release/scripts/presets/camera/Arri_Alexa_LF.py
new file mode 100644
index 00000000000..1cde94fce8d
--- /dev/null
+++ b/release/scripts/presets/camera/Arri_Alexa_LF.py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 36.70
+bpy.context.camera.sensor_height = 25.54
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/camera/Arri_Alexa_Mini_&_SXT.py b/release/scripts/presets/camera/Arri_Alexa_Mini_&_SXT.py
new file mode 100644
index 00000000000..0f61d35a0f9
--- /dev/null
+++ b/release/scripts/presets/camera/Arri_Alexa_Mini_&_SXT.py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 29.90
+bpy.context.camera.sensor_height = 15.77
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/camera/Blackmagic_Cinema_Camera.py b/release/scripts/presets/camera/Blackmagic_Cinema_Camera.py
deleted file mode 100644
index 6fde30756da..00000000000
--- a/release/scripts/presets/camera/Blackmagic_Cinema_Camera.py
+++ /dev/null
@@ -1,4 +0,0 @@
-import bpy
-bpy.context.camera.sensor_width = 15.81
-bpy.context.camera.sensor_height = 8.88
-bpy.context.camera.sensor_fit = 'HORIZONTAL'
diff --git a/release/scripts/presets/camera/Blackmagic_Pocket_Cinema_Camera.py b/release/scripts/presets/camera/Blackmagic_Pocket_&_Studio.py
index bb2b172919e..260bfbaf94f 100644
--- a/release/scripts/presets/camera/Blackmagic_Pocket_Cinema_Camera.py
+++ b/release/scripts/presets/camera/Blackmagic_Pocket_&_Studio.py
@@ -1,4 +1,4 @@
import bpy
bpy.context.camera.sensor_width = 12.48
bpy.context.camera.sensor_height = 7.02
-bpy.context.camera.sensor_fit = 'HORIZONTAL'
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/camera/Blackmagic_Pocket_4K.py b/release/scripts/presets/camera/Blackmagic_Pocket_4K.py
new file mode 100644
index 00000000000..dc057397828
--- /dev/null
+++ b/release/scripts/presets/camera/Blackmagic_Pocket_4K.py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 18.96
+bpy.context.camera.sensor_height = 10.00
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/camera/Blackmagic_Pocket_6k.py b/release/scripts/presets/camera/Blackmagic_Pocket_6k.py
new file mode 100644
index 00000000000..a483f3d5f98
--- /dev/null
+++ b/release/scripts/presets/camera/Blackmagic_Pocket_6k.py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 23.10
+bpy.context.camera.sensor_height = 12.99
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/camera/Blackmagic_Production_Camera_4K.py b/release/scripts/presets/camera/Blackmagic_Production_Camera_4K.py
deleted file mode 100644
index dbc12c5aa68..00000000000
--- a/release/scripts/presets/camera/Blackmagic_Production_Camera_4K.py
+++ /dev/null
@@ -1,4 +0,0 @@
-import bpy
-bpy.context.camera.sensor_width = 21.12
-bpy.context.camera.sensor_height = 11.88
-bpy.context.camera.sensor_fit = 'HORIZONTAL'
diff --git a/release/scripts/presets/camera/Blackmagic_URSA_4.6K.py b/release/scripts/presets/camera/Blackmagic_URSA_4.6K.py
new file mode 100644
index 00000000000..c71e42d72d3
--- /dev/null
+++ b/release/scripts/presets/camera/Blackmagic_URSA_4.6K.py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 25.34
+bpy.context.camera.sensor_height = 14.25
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/camera/Blender.py b/release/scripts/presets/camera/Blender.py
deleted file mode 100644
index ca4906fbb39..00000000000
--- a/release/scripts/presets/camera/Blender.py
+++ /dev/null
@@ -1,4 +0,0 @@
-import bpy
-bpy.context.camera.sensor_width = 32
-bpy.context.camera.sensor_height = 18
-bpy.context.camera.sensor_fit = 'AUTO'
diff --git a/release/scripts/presets/camera/Canon_1100D.py b/release/scripts/presets/camera/Canon_1100D.py
deleted file mode 100644
index e665e9e95d5..00000000000
--- a/release/scripts/presets/camera/Canon_1100D.py
+++ /dev/null
@@ -1,4 +0,0 @@
-import bpy
-bpy.context.camera.sensor_width = 22.2
-bpy.context.camera.sensor_height = 14.7
-bpy.context.camera.sensor_fit = 'HORIZONTAL'
diff --git a/release/scripts/presets/camera/Canon_APS-C.py b/release/scripts/presets/camera/Canon_APS-C.py
deleted file mode 100644
index 95108b2187f..00000000000
--- a/release/scripts/presets/camera/Canon_APS-C.py
+++ /dev/null
@@ -1,4 +0,0 @@
-import bpy
-bpy.context.camera.sensor_width = 22.3
-bpy.context.camera.sensor_height = 14.9
-bpy.context.camera.sensor_fit = 'HORIZONTAL'
diff --git a/release/scripts/presets/camera/Canon_C300.py b/release/scripts/presets/camera/Canon_C300.py
deleted file mode 100644
index e22af779854..00000000000
--- a/release/scripts/presets/camera/Canon_C300.py
+++ /dev/null
@@ -1,4 +0,0 @@
-import bpy
-bpy.context.camera.sensor_width = 24.4
-bpy.context.camera.sensor_height = 13.5
-bpy.context.camera.sensor_fit = 'HORIZONTAL'
diff --git a/release/scripts/presets/camera/Foveon_(Sigma).py b/release/scripts/presets/camera/Foveon_(Sigma).py
new file mode 100644
index 00000000000..e6a1a0ed344
--- /dev/null
+++ b/release/scripts/presets/camera/Foveon_(Sigma).py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 20.70
+bpy.context.camera.sensor_height = 13.80
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/camera/Full_Frame_35mm_Camera.py b/release/scripts/presets/camera/Fullframe.py
index c8017331b28..95fb4afc10b 100644
--- a/release/scripts/presets/camera/Full_Frame_35mm_Camera.py
+++ b/release/scripts/presets/camera/Fullframe.py
@@ -1,4 +1,4 @@
import bpy
bpy.context.camera.sensor_width = 36
bpy.context.camera.sensor_height = 24
-bpy.context.camera.sensor_fit = 'HORIZONTAL'
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/camera/GoPro_Hero3_Black.py b/release/scripts/presets/camera/GoPro_Hero3_Black.py
deleted file mode 100644
index e294f802a02..00000000000
--- a/release/scripts/presets/camera/GoPro_Hero3_Black.py
+++ /dev/null
@@ -1,6 +0,0 @@
-import bpy
-bpy.context.camera.sensor_width = 6.16
-bpy.context.camera.sensor_height = 4.62
-bpy.context.camera.lens = 2.77
-
-bpy.context.camera.sensor_fit = 'AUTO'
diff --git a/release/scripts/presets/camera/GoPro_Hero3_Silver.py b/release/scripts/presets/camera/GoPro_Hero3_Silver.py
deleted file mode 100644
index 247bd7c4aaf..00000000000
--- a/release/scripts/presets/camera/GoPro_Hero3_Silver.py
+++ /dev/null
@@ -1,6 +0,0 @@
-import bpy
-bpy.context.camera.sensor_width = 5.371
-bpy.context.camera.sensor_height = 4.035
-bpy.context.camera.lens = 2.77
-
-bpy.context.camera.sensor_fit = 'AUTO'
diff --git a/release/scripts/presets/camera/MFT.py b/release/scripts/presets/camera/MFT.py
new file mode 100644
index 00000000000..bc0dd49baa8
--- /dev/null
+++ b/release/scripts/presets/camera/MFT.py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 17.3
+bpy.context.camera.sensor_height = 13.0
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/camera/Medium-format_(Hasselblad).py b/release/scripts/presets/camera/Medium-format_(Hasselblad).py
new file mode 100644
index 00000000000..e9b16024b79
--- /dev/null
+++ b/release/scripts/presets/camera/Medium-format_(Hasselblad).py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 44
+bpy.context.camera.sensor_height = 33
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/camera/Nexus_5.py b/release/scripts/presets/camera/Nexus_5.py
deleted file mode 100644
index 36e741cbba5..00000000000
--- a/release/scripts/presets/camera/Nexus_5.py
+++ /dev/null
@@ -1,5 +0,0 @@
-import bpy
-bpy.context.camera.sensor_width = 4.5
-bpy.context.camera.sensor_height = 3.37
-bpy.context.camera.lens = 3.91
-bpy.context.camera.sensor_fit = 'HORIZONTAL'
diff --git a/release/scripts/presets/camera/Nikon_D3100.py b/release/scripts/presets/camera/Nikon_D3100.py
deleted file mode 100644
index b4ceb3aa721..00000000000
--- a/release/scripts/presets/camera/Nikon_D3100.py
+++ /dev/null
@@ -1,4 +0,0 @@
-import bpy
-bpy.context.camera.sensor_width = 23.1
-bpy.context.camera.sensor_height = 15.4
-bpy.context.camera.sensor_fit = 'HORIZONTAL'
diff --git a/release/scripts/presets/camera/Nikon_DX.py b/release/scripts/presets/camera/Nikon_DX.py
deleted file mode 100644
index dbe9e7fcc18..00000000000
--- a/release/scripts/presets/camera/Nikon_DX.py
+++ /dev/null
@@ -1,4 +0,0 @@
-import bpy
-bpy.context.camera.sensor_width = 23.6
-bpy.context.camera.sensor_height = 15.8
-bpy.context.camera.sensor_fit = 'HORIZONTAL'
diff --git a/release/scripts/presets/camera/Panasonic_AG-HVX200.py b/release/scripts/presets/camera/Panasonic_AG-HVX200.py
deleted file mode 100644
index 71ad3c3a161..00000000000
--- a/release/scripts/presets/camera/Panasonic_AG-HVX200.py
+++ /dev/null
@@ -1,4 +0,0 @@
-import bpy
-bpy.context.camera.sensor_width = 4.68
-bpy.context.camera.sensor_height = 2.633
-bpy.context.camera.sensor_fit = 'HORIZONTAL'
diff --git a/release/scripts/presets/camera/Panasonic_LX2.py b/release/scripts/presets/camera/Panasonic_LX2.py
deleted file mode 100644
index d66e02e32d4..00000000000
--- a/release/scripts/presets/camera/Panasonic_LX2.py
+++ /dev/null
@@ -1,4 +0,0 @@
-import bpy
-bpy.context.camera.sensor_width = 8.5
-bpy.context.camera.sensor_height = 4.78
-bpy.context.camera.sensor_fit = 'HORIZONTAL'
diff --git a/release/scripts/presets/camera/RED_Dragon_5K.py b/release/scripts/presets/camera/RED_Dragon_5K.py
new file mode 100644
index 00000000000..fa95a98f8c4
--- /dev/null
+++ b/release/scripts/presets/camera/RED_Dragon_5K.py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 25.60
+bpy.context.camera.sensor_height = 13.5
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/camera/RED_Dragon_6K.py b/release/scripts/presets/camera/RED_Dragon_6K.py
new file mode 100644
index 00000000000..80f7ad1bbb8
--- /dev/null
+++ b/release/scripts/presets/camera/RED_Dragon_6K.py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 30.70
+bpy.context.camera.sensor_height = 15.80
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/camera/RED_Helium_8K.py b/release/scripts/presets/camera/RED_Helium_8K.py
new file mode 100644
index 00000000000..0f61d35a0f9
--- /dev/null
+++ b/release/scripts/presets/camera/RED_Helium_8K.py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 29.90
+bpy.context.camera.sensor_height = 15.77
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/camera/RED_Monstro_8K.py b/release/scripts/presets/camera/RED_Monstro_8K.py
new file mode 100644
index 00000000000..86c382624ab
--- /dev/null
+++ b/release/scripts/presets/camera/RED_Monstro_8K.py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 40.96
+bpy.context.camera.sensor_height = 21.60
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/camera/Red_Epic.py b/release/scripts/presets/camera/Red_Epic.py
deleted file mode 100644
index 5d71a69a33d..00000000000
--- a/release/scripts/presets/camera/Red_Epic.py
+++ /dev/null
@@ -1,4 +0,0 @@
-import bpy
-bpy.context.camera.sensor_width = 30.0
-bpy.context.camera.sensor_height = 15.0
-bpy.context.camera.sensor_fit = 'HORIZONTAL'
diff --git a/release/scripts/presets/camera/Red_One_2K.py b/release/scripts/presets/camera/Red_One_2K.py
deleted file mode 100644
index 894aedcf9ea..00000000000
--- a/release/scripts/presets/camera/Red_One_2K.py
+++ /dev/null
@@ -1,4 +0,0 @@
-import bpy
-bpy.context.camera.sensor_width = 11.1
-bpy.context.camera.sensor_height = 6.24
-bpy.context.camera.sensor_fit = 'HORIZONTAL'
diff --git a/release/scripts/presets/camera/Red_One_3K.py b/release/scripts/presets/camera/Red_One_3K.py
deleted file mode 100644
index 9ac84c1485a..00000000000
--- a/release/scripts/presets/camera/Red_One_3K.py
+++ /dev/null
@@ -1,4 +0,0 @@
-import bpy
-bpy.context.camera.sensor_width = 16.65
-bpy.context.camera.sensor_height = 9.36
-bpy.context.camera.sensor_fit = 'HORIZONTAL'
diff --git a/release/scripts/presets/camera/Red_One_4K.py b/release/scripts/presets/camera/Red_One_4K.py
deleted file mode 100644
index 067322cd07e..00000000000
--- a/release/scripts/presets/camera/Red_One_4K.py
+++ /dev/null
@@ -1,4 +0,0 @@
-import bpy
-bpy.context.camera.sensor_width = 22.2
-bpy.context.camera.sensor_height = 12.6
-bpy.context.camera.sensor_fit = 'HORIZONTAL'
diff --git a/release/scripts/presets/camera/Samsung_Galaxy_S3.py b/release/scripts/presets/camera/Samsung_Galaxy_S3.py
deleted file mode 100644
index 23eaea7cd27..00000000000
--- a/release/scripts/presets/camera/Samsung_Galaxy_S3.py
+++ /dev/null
@@ -1,5 +0,0 @@
-import bpy
-bpy.context.camera.sensor_width = 4.8
-bpy.context.camera.sensor_height = 3.6
-bpy.context.camera.lens = 3.70
-bpy.context.camera.sensor_fit = 'HORIZONTAL'
diff --git a/release/scripts/presets/camera/Samsung_Galaxy_S4.py b/release/scripts/presets/camera/Samsung_Galaxy_S4.py
deleted file mode 100644
index cc929d26dac..00000000000
--- a/release/scripts/presets/camera/Samsung_Galaxy_S4.py
+++ /dev/null
@@ -1,5 +0,0 @@
-import bpy
-bpy.context.camera.sensor_width = 4.8
-bpy.context.camera.sensor_height = 3.6
-bpy.context.camera.lens = 4.20
-bpy.context.camera.sensor_fit = 'HORIZONTAL'
diff --git a/release/scripts/presets/camera/Sony_A55.py b/release/scripts/presets/camera/Sony_A55.py
deleted file mode 100644
index 0468deb6d4c..00000000000
--- a/release/scripts/presets/camera/Sony_A55.py
+++ /dev/null
@@ -1,4 +0,0 @@
-import bpy
-bpy.context.camera.sensor_width = 23.4
-bpy.context.camera.sensor_height = 15.6
-bpy.context.camera.sensor_fit = 'HORIZONTAL'
diff --git a/release/scripts/presets/camera/Sony_EX1.py b/release/scripts/presets/camera/Sony_EX1.py
deleted file mode 100644
index 3c6b235f21e..00000000000
--- a/release/scripts/presets/camera/Sony_EX1.py
+++ /dev/null
@@ -1,4 +0,0 @@
-import bpy
-bpy.context.camera.sensor_width = 6.97
-bpy.context.camera.sensor_height = 3.92
-bpy.context.camera.sensor_fit = 'HORIZONTAL'
diff --git a/release/scripts/presets/camera/Sony_F65.py b/release/scripts/presets/camera/Sony_F65.py
deleted file mode 100644
index e62b3511836..00000000000
--- a/release/scripts/presets/camera/Sony_F65.py
+++ /dev/null
@@ -1,4 +0,0 @@
-import bpy
-bpy.context.camera.sensor_width = 24.33
-bpy.context.camera.sensor_height = 12.83
-bpy.context.camera.sensor_fit = 'HORIZONTAL'
diff --git a/release/scripts/presets/camera/Super_16_Film.py b/release/scripts/presets/camera/Super_16_Film.py
deleted file mode 100644
index 4ca397a7e27..00000000000
--- a/release/scripts/presets/camera/Super_16_Film.py
+++ /dev/null
@@ -1,4 +0,0 @@
-import bpy
-bpy.context.camera.sensor_width = 12.52
-bpy.context.camera.sensor_height = 7.41
-bpy.context.camera.sensor_fit = 'HORIZONTAL'
diff --git a/release/scripts/presets/camera/iPhone_5.py b/release/scripts/presets/camera/iPhone_5.py
deleted file mode 100644
index a6b6bbc2ec5..00000000000
--- a/release/scripts/presets/camera/iPhone_5.py
+++ /dev/null
@@ -1,5 +0,0 @@
-import bpy
-bpy.context.camera.sensor_width = 4.54
-bpy.context.camera.sensor_height = 3.42
-bpy.context.camera.lens = 4.10
-bpy.context.camera.sensor_fit = 'HORIZONTAL'
diff --git a/release/scripts/presets/cycles/integrator/Default.py b/release/scripts/presets/cycles/integrator/Default.py
new file mode 100644
index 00000000000..726d47e20d7
--- /dev/null
+++ b/release/scripts/presets/cycles/integrator/Default.py
@@ -0,0 +1,14 @@
+import bpy
+cycles = bpy.context.scene.cycles
+
+cycles.max_bounces = 12
+cycles.caustics_reflective = True
+cycles.caustics_refractive = True
+cycles.diffuse_bounces = 4
+cycles.glossy_bounces = 4
+cycles.transmission_bounces = 12
+cycles.volume_bounces = 0
+cycles.transparent_max_bounces = 8
+cycles.use_fast_gi = False
+cycles.ao_bounces = 1
+cycles.ao_bounces_render = 1
diff --git a/release/scripts/presets/cycles/integrator/Direct_Light.py b/release/scripts/presets/cycles/integrator/Direct_Light.py
index 701aa10d556..28f1b218291 100644
--- a/release/scripts/presets/cycles/integrator/Direct_Light.py
+++ b/release/scripts/presets/cycles/integrator/Direct_Light.py
@@ -9,3 +9,6 @@ cycles.glossy_bounces = 1
cycles.transmission_bounces = 2
cycles.volume_bounces = 0
cycles.transparent_max_bounces = 8
+cycles.use_fast_gi = False
+cycles.ao_bounces = 1
+cycles.ao_bounces_render = 1
diff --git a/release/scripts/presets/cycles/integrator/Fast_Global_Illumination.py b/release/scripts/presets/cycles/integrator/Fast_Global_Illumination.py
new file mode 100644
index 00000000000..9f72aa80ee1
--- /dev/null
+++ b/release/scripts/presets/cycles/integrator/Fast_Global_Illumination.py
@@ -0,0 +1,14 @@
+import bpy
+cycles = bpy.context.scene.cycles
+
+cycles.max_bounces = 8
+cycles.caustics_reflective = False
+cycles.caustics_refractive = False
+cycles.diffuse_bounces = 1
+cycles.glossy_bounces = 4
+cycles.transmission_bounces = 8
+cycles.volume_bounces = 2
+cycles.transparent_max_bounces = 8
+cycles.use_fast_gi = True
+cycles.ao_bounces = 2
+cycles.ao_bounces_render = 2
diff --git a/release/scripts/presets/cycles/integrator/Full_Global_Illumination.py b/release/scripts/presets/cycles/integrator/Full_Global_Illumination.py
index a03c6c8bd64..632f5004967 100644
--- a/release/scripts/presets/cycles/integrator/Full_Global_Illumination.py
+++ b/release/scripts/presets/cycles/integrator/Full_Global_Illumination.py
@@ -1,11 +1,14 @@
import bpy
cycles = bpy.context.scene.cycles
-cycles.max_bounces = 128
+cycles.max_bounces = 32
cycles.caustics_reflective = True
cycles.caustics_refractive = True
-cycles.diffuse_bounces = 128
-cycles.glossy_bounces = 128
-cycles.transmission_bounces = 128
-cycles.volume_bounces = 128
-cycles.transparent_max_bounces = 128
+cycles.diffuse_bounces = 32
+cycles.glossy_bounces = 32
+cycles.transmission_bounces = 32
+cycles.volume_bounces = 32
+cycles.transparent_max_bounces = 32
+cycles.use_fast_gi = False
+cycles.ao_bounces = 1
+cycles.ao_bounces_render = 1
diff --git a/release/scripts/presets/cycles/integrator/Limited_Global_Illumination.py b/release/scripts/presets/cycles/integrator/Limited_Global_Illumination.py
index d37bf46c705..e48ffab238e 100644
--- a/release/scripts/presets/cycles/integrator/Limited_Global_Illumination.py
+++ b/release/scripts/presets/cycles/integrator/Limited_Global_Illumination.py
@@ -7,5 +7,8 @@ cycles.caustics_refractive = False
cycles.diffuse_bounces = 1
cycles.glossy_bounces = 4
cycles.transmission_bounces = 8
-cycles.volume_bounces = 2
+cycles.volume_bounces = 0
cycles.transparent_max_bounces = 8
+cycles.use_fast_gi = False
+cycles.ao_bounces = 1
+cycles.ao_bounces_render = 1
diff --git a/release/scripts/presets/keyconfig/Blender.py b/release/scripts/presets/keyconfig/Blender.py
index eb66c961472..222ee43432f 100644
--- a/release/scripts/presets/keyconfig/Blender.py
+++ b/release/scripts/presets/keyconfig/Blender.py
@@ -103,8 +103,8 @@ class Prefs(bpy.types.KeyConfigPreferences):
v3d_tilde_action: EnumProperty(
name="Tilde Action",
items=(
- ('VIEW', "Navigate",
- "View operations (useful for keyboards without a numpad)",
+ ('OBJECT_SWITCH', "Object Switch",
+ "Switch the active object under the cursor (when not in object mode)",
0),
('GIZMO', "Gizmos",
"Control transform gizmos",
@@ -113,7 +113,7 @@ class Prefs(bpy.types.KeyConfigPreferences):
description=(
"Action when 'Tilde' is pressed"
),
- default='VIEW',
+ default='OBJECT_SWITCH',
update=update_fn,
)
diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
index 2fb0e9a0bea..a82304e7614 100644
--- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py
+++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
@@ -278,15 +278,11 @@ def _template_items_uv_select_mode(params):
else:
return [
*_template_items_editmode_mesh_select_mode(params),
+ # Hack to prevent fall-through, when sync select isn't enabled (and the island button isn't visible).
("mesh.select_mode", {"type": 'FOUR', "value": 'PRESS'}, None),
- ("wm.context_set_enum", {"type": 'ONE', "value": 'PRESS'},
- {"properties": [("data_path", 'tool_settings.uv_select_mode'), ("value", 'VERTEX')]}),
- ("wm.context_set_enum", {"type": 'TWO', "value": 'PRESS'},
- {"properties": [("data_path", 'tool_settings.uv_select_mode'), ("value", 'EDGE')]}),
- ("wm.context_set_enum", {"type": 'THREE', "value": 'PRESS'},
- {"properties": [("data_path", 'tool_settings.uv_select_mode'), ("value", 'FACE')]}),
- ("wm.context_set_enum", {"type": 'FOUR', "value": 'PRESS'},
- {"properties": [("data_path", 'tool_settings.uv_select_mode'), ("value", 'ISLAND')]}),
+ *(("wm.context_set_enum", {"type": NUMBERS_1[i], "value": 'PRESS'},
+ {"properties": [("data_path", 'tool_settings.uv_select_mode'), ("value", ty)]})
+ for i, ty in enumerate(('VERTEX', 'EDGE', 'FACE', 'ISLAND')))
]
@@ -1082,12 +1078,7 @@ def km_view3d(params):
{"properties": [("use_all_regions", True), ("center", False)]}),
("view3d.view_all", {"type": 'C', "value": 'PRESS', "shift": True},
{"properties": [("center", True)]}),
- op_menu_pie(
- "VIEW3D_MT_view_pie" if params.v3d_tilde_action == 'VIEW' else "VIEW3D_MT_transform_gizmo_pie",
- {"type": 'ACCENT_GRAVE', "value": params.pie_value},
- ),
- *(() if not params.use_pie_click_drag else
- (("view3d.navigate", {"type": 'ACCENT_GRAVE', "value": 'CLICK'}, None),)),
+ op_menu_pie("VIEW3D_MT_view_pie", {"type": 'D', "value": 'CLICK_DRAG'}),
("view3d.navigate", {"type": 'ACCENT_GRAVE', "value": 'PRESS', "shift": True}, None),
# Numpad views.
("view3d.view_camera", {"type": 'NUMPAD_0', "value": 'PRESS'}, None),
@@ -1331,6 +1322,32 @@ def km_view3d(params):
op_tool_cycle("builtin.select_box", {"type": 'W', "value": 'PRESS'}),
])
+ # Tilda key.
+ if params.use_pie_click_drag:
+ items.extend([
+ ("object.transfer_mode",
+ {"type": 'ACCENT_GRAVE', "value": 'CLICK' if params.use_pie_click_drag else 'PRESS'},
+ None),
+ op_menu_pie(
+ "VIEW3D_MT_transform_gizmo_pie",
+ {"type": 'ACCENT_GRAVE', "value": 'CLICK_DRAG'},
+ )
+ ])
+ else:
+ if params.v3d_tilde_action == 'OBJECT_SWITCH':
+ items.append(
+ ("object.transfer_mode",
+ {"type": 'ACCENT_GRAVE', "value": 'PRESS'},
+ {"properties": [("use_eyedropper", False)]})
+ )
+ else:
+ items.append(
+ op_menu_pie(
+ "VIEW3D_MT_transform_gizmo_pie",
+ {"type": 'ACCENT_GRAVE', "value": 'PRESS'},
+ )
+ )
+
return keymap
@@ -1694,6 +1711,7 @@ def km_image(params):
("image.view_all", {"type": 'HOME', "value": 'PRESS', "shift": True},
{"properties": [("fit_view", True)]}),
("image.view_selected", {"type": 'NUMPAD_PERIOD', "value": 'PRESS'}, None),
+ ("image.view_cursor_center", {"type": 'C', "value": 'PRESS', "shift": True}, None),
("image.view_pan", {"type": 'MIDDLEMOUSE', "value": 'PRESS'}, None),
("image.view_pan", {"type": 'MIDDLEMOUSE', "value": 'PRESS', "shift": True}, None),
("image.view_pan", {"type": 'TRACKPADPAN', "value": 'ANY'}, None),
@@ -2008,8 +2026,7 @@ def km_file_browser_main(params):
)
items.extend([
- ("file.execute", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'},
- {"properties": [("need_active", True)]}),
+ ("file.mouse_execute", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'}, None),
# Both .execute and .select are needed here. The former only works if
# there's a file operator (i.e. not in regular editor mode) but is
# needed to load files. The latter makes selection work if there's no
@@ -2527,6 +2544,7 @@ def km_sequencercommon(params):
{"properties": [("data_path", 'scene.sequence_editor.show_overlay')]}),
("wm.context_toggle_enum", {"type": 'TAB', "value": 'PRESS', "ctrl": True},
{"properties": [("data_path", 'space_data.view_type'), ("value_1", 'SEQUENCER'), ("value_2", 'PREVIEW')]}),
+ ("sequencer.refresh_all", {"type": 'R', "value": 'PRESS', "ctrl": True}, None),
])
if params.select_mouse == 'LEFTMOUSE' and not params.legacy:
@@ -2567,7 +2585,6 @@ def km_sequencer(params):
("sequencer.reload", {"type": 'R', "value": 'PRESS', "alt": True}, None),
("sequencer.reload", {"type": 'R', "value": 'PRESS', "shift": True, "alt": True},
{"properties": [("adjust_length", True)]}),
- ("sequencer.refresh_all", {"type": 'R', "value": 'PRESS', "ctrl": True}, None),
("sequencer.offset_clear", {"type": 'O', "value": 'PRESS', "alt": True}, None),
("sequencer.duplicate_move", {"type": 'D', "value": 'PRESS', "shift": True}, None),
("sequencer.delete", {"type": 'X', "value": 'PRESS'}, None),
@@ -2575,7 +2592,7 @@ def km_sequencer(params):
("sequencer.copy", {"type": 'C', "value": 'PRESS', "ctrl": True}, None),
("sequencer.paste", {"type": 'V', "value": 'PRESS', "ctrl": True}, None),
("sequencer.paste", {"type": 'V', "value": 'PRESS', "ctrl": True, "shift": True},
- {"properties": [("keep_offset", True)]}),
+ {"properties": [("keep_offset", True)]}),
("sequencer.images_separate", {"type": 'Y', "value": 'PRESS'}, None),
("sequencer.meta_toggle", {"type": 'TAB', "value": 'PRESS'}, None),
("sequencer.meta_make", {"type": 'G', "value": 'PRESS', "ctrl": True}, None),
@@ -3381,6 +3398,11 @@ def km_grease_pencil_stroke_paint_mode(params):
# Brush size
("wm.radial_control", {"type": 'F', "value": 'PRESS'},
{"properties": [("data_path_primary", 'tool_settings.gpencil_paint.brush.size')]}),
+ # Increase/Decrease brush size
+ ("brush.scale_size", {"type": 'LEFT_BRACKET', "value": 'PRESS', "repeat": True},
+ {"properties": [("scalar", 0.9)]}),
+ ("brush.scale_size", {"type": 'RIGHT_BRACKET', "value": 'PRESS', "repeat": True},
+ {"properties": [("scalar", 1.0 / 0.9)]}),
# Draw delete menu
op_menu("GPENCIL_MT_gpencil_draw_delete", {"type": 'X', "value": 'PRESS'}),
# Animation menu
@@ -3548,6 +3570,11 @@ def km_grease_pencil_stroke_sculpt_mode(params):
# Brush size
("wm.radial_control", {"type": 'F', "value": 'PRESS'},
{"properties": [("data_path_primary", 'tool_settings.gpencil_sculpt_paint.brush.size')]}),
+ # Increase/Decrease brush size
+ ("brush.scale_size", {"type": 'LEFT_BRACKET', "value": 'PRESS', "repeat": True},
+ {"properties": [("scalar", 0.9)]}),
+ ("brush.scale_size", {"type": 'RIGHT_BRACKET', "value": 'PRESS', "repeat": True},
+ {"properties": [("scalar", 1.0 / 0.9)]}),
# Copy
("gpencil.copy", {"type": 'C', "value": 'PRESS', "ctrl": True}, None),
# Display
@@ -3762,6 +3789,11 @@ def km_grease_pencil_stroke_weight_mode(params):
# Brush size
("wm.radial_control", {"type": 'F', "value": 'PRESS'},
{"properties": [("data_path_primary", 'tool_settings.gpencil_weight_paint.brush.size')]}),
+ # Increase/Decrease brush size
+ ("brush.scale_size", {"type": 'LEFT_BRACKET', "value": 'PRESS', "repeat": True},
+ {"properties": [("scalar", 0.9)]}),
+ ("brush.scale_size", {"type": 'RIGHT_BRACKET', "value": 'PRESS', "repeat": True},
+ {"properties": [("scalar", 1.0 / 0.9)]}),
# Display
*_grease_pencil_display(),
# Keyframe menu
@@ -3819,6 +3851,11 @@ def km_grease_pencil_stroke_vertex_mode(params):
# Brush size
("wm.radial_control", {"type": 'F', "value": 'PRESS'},
{"properties": [("data_path_primary", 'tool_settings.gpencil_vertex_paint.brush.size')]}),
+ # Increase/Decrease brush size
+ ("brush.scale_size", {"type": 'LEFT_BRACKET', "value": 'PRESS', "repeat": True},
+ {"properties": [("scalar", 0.9)]}),
+ ("brush.scale_size", {"type": 'RIGHT_BRACKET', "value": 'PRESS', "repeat": True},
+ {"properties": [("scalar", 1.0 / 0.9)]}),
# Display
*_grease_pencil_display(),
# Tools
@@ -4053,7 +4090,7 @@ def km_pose(params):
("pose.bone_layers", {"type": 'M', "value": 'PRESS'}, None),
("transform.bbone_resize", {"type": 'S', "value": 'PRESS', "ctrl": True, "alt": True}, None),
("anim.keyframe_insert_menu", {"type": 'I', "value": 'PRESS'}, None),
- ("anim.keyframe_delete_v3d", {"type": 'I', "value": 'PRESS', "alt": True}, None),
+ ("anim.keyframe_delete", {"type": 'I', "value": 'PRESS', "alt": True}, None),
("anim.keying_set_active_set", {"type": 'I', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, None),
("poselib.browse_interactive", {"type": 'L', "value": 'PRESS', "alt": True}, None),
("poselib.pose_add", {"type": 'L', "value": 'PRESS', "shift": True}, None),
@@ -4125,7 +4162,7 @@ def km_object_mode(params):
("wm.context_toggle", {"type": 'PERIOD', "value": 'PRESS', "ctrl": True},
{"properties": [("data_path", 'tool_settings.use_transform_data_origin')]}),
("anim.keyframe_insert_menu", {"type": 'I', "value": 'PRESS'}, None),
- ("anim.keyframe_delete_v3d", {"type": 'I', "value": 'PRESS', "alt": True}, None),
+ ("anim.keyframe_delete", {"type": 'I', "value": 'PRESS', "alt": True}, None),
("anim.keying_set_active_set", {"type": 'I', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, None),
("collection.create", {"type": 'G', "value": 'PRESS', "ctrl": True}, None),
("collection.objects_remove", {"type": 'G', "value": 'PRESS', "ctrl": True, "alt": True}, None),
@@ -4468,8 +4505,6 @@ def km_sculpt(params):
)
items.extend([
- # Switch Object (release to avoid conflict with grease pencil drawing).
- ("object.switch_object", {"type": 'D', "value": 'RELEASE'}, None),
# Brush strokes
("sculpt.brush_stroke", {"type": 'LEFTMOUSE', "value": 'PRESS'},
{"properties": [("mode", 'NORMAL')]}),
@@ -4593,8 +4628,6 @@ def km_mesh(params):
)
items.extend([
- # Switch Object (release to avoid conflict with grease pencil drawing).
- ("object.switch_object", {"type": 'D', "value": 'RELEASE'}, None),
# Tools.
("mesh.loopcut_slide", {"type": 'R', "value": 'PRESS', "ctrl": True},
{"properties": [("TRANSFORM_OT_edge_slide", [("release_confirm", False)],)]}),
@@ -5011,32 +5044,30 @@ def km_object_non_modal(params):
{"properties": [("mode", 'VERTEX_PAINT'), ("toggle", True)]}),
("object.mode_set", {"type": 'TAB', "value": 'PRESS', "ctrl": True},
{"properties": [("mode", 'WEIGHT_PAINT'), ("toggle", True)]}),
- ])
- elif params.use_pie_click_drag:
- items.extend([
- ("object.mode_set", {"type": 'TAB', "value": 'CLICK'},
- {"properties": [("mode", 'EDIT'), ("toggle", True)]}),
- op_menu_pie("VIEW3D_MT_object_mode_pie", {"type": 'TAB', "value": 'CLICK_DRAG'}),
- ("view3d.object_mode_pie_or_toggle", {"type": 'TAB', "value": 'PRESS', "ctrl": True}, None),
- ])
- elif not params.use_v3d_tab_menu:
- items.extend([
- ("object.mode_set", {"type": 'TAB', "value": 'PRESS'},
- {"properties": [("mode", 'EDIT'), ("toggle", True)]}),
- ("view3d.object_mode_pie_or_toggle", {"type": 'TAB', "value": 'PRESS', "ctrl": True}, None),
- ])
- else:
- # Swap Tab/Ctrl-Tab
- items.extend([
- ("object.mode_set", {"type": 'TAB', "value": 'PRESS', "ctrl": True},
- {"properties": [("mode", 'EDIT'), ("toggle", True)]}),
- op_menu_pie("VIEW3D_MT_object_mode_pie", {"type": 'TAB', "value": 'PRESS'}),
- ])
- if params.legacy:
- items.extend([
("object.origin_set", {"type": 'C', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, None),
])
+ else:
+ if params.use_pie_click_drag:
+ items.extend([
+ ("object.mode_set", {"type": 'TAB', "value": 'CLICK'},
+ {"properties": [("mode", 'EDIT'), ("toggle", True)]}),
+ op_menu_pie("VIEW3D_MT_object_mode_pie", {"type": 'TAB', "value": 'CLICK_DRAG'}),
+ ("view3d.object_mode_pie_or_toggle", {"type": 'TAB', "value": 'PRESS', "ctrl": True}, None),
+ ])
+ elif params.use_v3d_tab_menu:
+ # Swap Tab/Ctrl-Tab
+ items.extend([
+ ("object.mode_set", {"type": 'TAB', "value": 'PRESS', "ctrl": True},
+ {"properties": [("mode", 'EDIT'), ("toggle", True)]}),
+ op_menu_pie("VIEW3D_MT_object_mode_pie", {"type": 'TAB', "value": 'PRESS'}),
+ ])
+ else:
+ items.extend([
+ ("object.mode_set", {"type": 'TAB', "value": 'PRESS'},
+ {"properties": [("mode", 'EDIT'), ("toggle", True)]}),
+ ("view3d.object_mode_pie_or_toggle", {"type": 'TAB', "value": 'PRESS', "ctrl": True}, None),
+ ])
return keymap
diff --git a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py
index 91f153a0f42..e56783fcc21 100644
--- a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py
+++ b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py
@@ -1263,8 +1263,7 @@ def km_file_browser_main(params):
)
items.extend([
- ("file.execute", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'},
- {"properties": [("need_active", True)]}),
+ ("file.mouse_execute", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'}, None),
("file.refresh", {"type": 'R', "value": 'PRESS', "ctrl": True}, None),
("file.select", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'}, None),
("file.select", {"type": 'LEFTMOUSE', "value": 'CLICK'},
@@ -3001,7 +3000,7 @@ def km_pose(params):
("anim.keyframe_insert_by_name", {"type": 'R', "value": 'PRESS', "shift": True},
{"properties": [("type", 'Scaling')]}),
- ("anim.keyframe_delete_v3d", {"type": 'S', "value": 'PRESS', "alt": True}, None),
+ ("anim.keyframe_delete", {"type": 'S', "value": 'PRESS', "alt": True}, None),
("anim.keying_set_active_set", {"type": 'S', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, None),
*_template_items_context_menu("VIEW3D_MT_pose_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}),
# Tools
@@ -3072,7 +3071,7 @@ def km_object_mode(params):
{"properties": [("type", 'Rotation')]}),
("anim.keyframe_insert_by_name", {"type": 'R', "value": 'PRESS', "shift": True},
{"properties": [("type", 'Scaling')]}),
- ("anim.keyframe_delete_v3d", {"type": 'S', "value": 'PRESS', "alt": True}, None),
+ ("anim.keyframe_delete", {"type": 'S', "value": 'PRESS', "alt": True}, None),
("anim.keying_set_active_set", {"type": 'S', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, None),
*_template_items_context_menu("VIEW3D_MT_object_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}),
("object.move_to_collection", {"type": 'G', "value": 'PRESS', "ctrl": True}, None),
@@ -3334,8 +3333,6 @@ def km_weight_paint(params):
*_template_paint_radial_control("weight_paint"),
("wm.context_toggle", {"type": 'M', "value": 'PRESS'},
{"properties": [("data_path", 'weight_paint_object.data.use_paint_mask')]}),
- ("wm.context_toggle", {"type": 'V', "value": 'PRESS'},
- {"properties": [("data_path", 'weight_paint_object.data.use_paint_mask_vertex')]}),
("wm.context_toggle", {"type": 'S', "value": 'PRESS', "shift": True},
{"properties": [("data_path", 'tool_settings.weight_paint.brush.use_smooth_stroke')]}),
*_template_items_context_panel("VIEW3D_PT_paint_weight_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}),
diff --git a/release/scripts/presets/tracking_camera/1__colon__2.3_inch.py b/release/scripts/presets/tracking_camera/1__colon__2.3_inch.py
deleted file mode 100644
index 9fcd40fbb65..00000000000
--- a/release/scripts/presets/tracking_camera/1__colon__2.3_inch.py
+++ /dev/null
@@ -1,9 +0,0 @@
-import bpy
-camera = bpy.context.edit_movieclip.tracking.camera
-
-camera.sensor_width = 6.16
-camera.units = 'MILLIMETERS'
-camera.pixel_aspect = 1
-camera.k1 = 0.0
-camera.k2 = 0.0
-camera.k3 = 0.0
diff --git a/release/scripts/presets/tracking_camera/1__colon__2.5_inch.py b/release/scripts/presets/tracking_camera/1__colon__2.5_inch.py
deleted file mode 100644
index 2f064e59838..00000000000
--- a/release/scripts/presets/tracking_camera/1__colon__2.5_inch.py
+++ /dev/null
@@ -1,9 +0,0 @@
-import bpy
-camera = bpy.context.edit_movieclip.tracking.camera
-
-camera.sensor_width = 5.76
-camera.units = 'MILLIMETERS'
-camera.pixel_aspect = 1
-camera.k1 = 0.0
-camera.k2 = 0.0
-camera.k3 = 0.0
diff --git a/release/scripts/presets/tracking_camera/1_inch.py b/release/scripts/presets/tracking_camera/1_inch.py
new file mode 100644
index 00000000000..72b039fb978
--- /dev/null
+++ b/release/scripts/presets/tracking_camera/1_inch.py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 13.2
+bpy.context.camera.sensor_height = 8.80
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/tracking_camera/1_slash_1.8_inch.py b/release/scripts/presets/tracking_camera/1_slash_1.8_inch.py
new file mode 100644
index 00000000000..38e09182de6
--- /dev/null
+++ b/release/scripts/presets/tracking_camera/1_slash_1.8_inch.py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 7.18
+bpy.context.camera.sensor_height = 5.32
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/tracking_camera/1_slash_2.3_inch.py b/release/scripts/presets/tracking_camera/1_slash_2.3_inch.py
new file mode 100644
index 00000000000..4d55738f4ed
--- /dev/null
+++ b/release/scripts/presets/tracking_camera/1_slash_2.3_inch.py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 6.17
+bpy.context.camera.sensor_height = 4.55
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/camera/GoPro_Hero3_White.py b/release/scripts/presets/tracking_camera/1_slash_2.5_inch.py
index 948f838f5d6..cbdb6f3cbe0 100644
--- a/release/scripts/presets/camera/GoPro_Hero3_White.py
+++ b/release/scripts/presets/tracking_camera/1_slash_2.5_inch.py
@@ -1,6 +1,4 @@
import bpy
bpy.context.camera.sensor_width = 5.76
bpy.context.camera.sensor_height = 4.29
-bpy.context.camera.lens = 2.77
-
-bpy.context.camera.sensor_fit = 'AUTO'
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/tracking_camera/1_slash_2.7_inch.py b/release/scripts/presets/tracking_camera/1_slash_2.7_inch.py
new file mode 100644
index 00000000000..5ccfa4ab555
--- /dev/null
+++ b/release/scripts/presets/tracking_camera/1_slash_2.7_inch.py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 5.37
+bpy.context.camera.sensor_height = 4.04
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/camera/iPhone_4S.py b/release/scripts/presets/tracking_camera/1_slash_3.2_inch.py
index 1139b7395b5..1963f7ec048 100644
--- a/release/scripts/presets/camera/iPhone_4S.py
+++ b/release/scripts/presets/tracking_camera/1_slash_3.2_inch.py
@@ -1,5 +1,4 @@
import bpy
bpy.context.camera.sensor_width = 4.54
bpy.context.camera.sensor_height = 3.42
-bpy.context.camera.lens = 4.28
-bpy.context.camera.sensor_fit = 'HORIZONTAL'
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/tracking_camera/2__colon__3_inch.py b/release/scripts/presets/tracking_camera/2__colon__3_inch.py
deleted file mode 100644
index 8936e627d77..00000000000
--- a/release/scripts/presets/tracking_camera/2__colon__3_inch.py
+++ /dev/null
@@ -1,9 +0,0 @@
-import bpy
-camera = bpy.context.edit_movieclip.tracking.camera
-
-camera.sensor_width = 9.6
-camera.units = 'MILLIMETERS'
-camera.pixel_aspect = 1
-camera.k1 = 0.0
-camera.k2 = 0.0
-camera.k3 = 0.0
diff --git a/release/scripts/presets/tracking_camera/2_slash_3_inch.py b/release/scripts/presets/tracking_camera/2_slash_3_inch.py
new file mode 100644
index 00000000000..25b46016800
--- /dev/null
+++ b/release/scripts/presets/tracking_camera/2_slash_3_inch.py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 8.8
+bpy.context.camera.sensor_height = 6.6
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/tracking_camera/4__colon__3_inch.py b/release/scripts/presets/tracking_camera/4__colon__3_inch.py
deleted file mode 100644
index 2317715e1b4..00000000000
--- a/release/scripts/presets/tracking_camera/4__colon__3_inch.py
+++ /dev/null
@@ -1,9 +0,0 @@
-import bpy
-camera = bpy.context.edit_movieclip.tracking.camera
-
-camera.sensor_width = 17.31
-camera.units = 'MILLIMETERS'
-camera.pixel_aspect = 1
-camera.k1 = 0.0
-camera.k2 = 0.0
-camera.k3 = 0.0
diff --git a/release/scripts/presets/tracking_camera/APS-C.py b/release/scripts/presets/tracking_camera/APS-C.py
new file mode 100644
index 00000000000..84e40825248
--- /dev/null
+++ b/release/scripts/presets/tracking_camera/APS-C.py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 23.6
+bpy.context.camera.sensor_height = 15.6
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/tracking_camera/APS-C_(Canon).py b/release/scripts/presets/tracking_camera/APS-C_(Canon).py
new file mode 100644
index 00000000000..55f20ce0eac
--- /dev/null
+++ b/release/scripts/presets/tracking_camera/APS-C_(Canon).py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 22.30
+bpy.context.camera.sensor_height = 14.90
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/tracking_camera/APS-H_(Canon).py b/release/scripts/presets/tracking_camera/APS-H_(Canon).py
new file mode 100644
index 00000000000..d63f733280b
--- /dev/null
+++ b/release/scripts/presets/tracking_camera/APS-H_(Canon).py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 27.90
+bpy.context.camera.sensor_height = 18.60
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/tracking_camera/Analog_16mm.py b/release/scripts/presets/tracking_camera/Analog_16mm.py
new file mode 100644
index 00000000000..aa98eaf2408
--- /dev/null
+++ b/release/scripts/presets/tracking_camera/Analog_16mm.py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 10.26
+bpy.context.camera.sensor_height = 7.49
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/tracking_camera/Analog_35mm.py b/release/scripts/presets/tracking_camera/Analog_35mm.py
new file mode 100644
index 00000000000..a0dee1f0166
--- /dev/null
+++ b/release/scripts/presets/tracking_camera/Analog_35mm.py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 22
+bpy.context.camera.sensor_height = 16
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/tracking_camera/Analog_65mm.py b/release/scripts/presets/tracking_camera/Analog_65mm.py
new file mode 100644
index 00000000000..8de91ac0ee3
--- /dev/null
+++ b/release/scripts/presets/tracking_camera/Analog_65mm.py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 52.45
+bpy.context.camera.sensor_height = 23.01
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/tracking_camera/Analog_IMAX.py b/release/scripts/presets/tracking_camera/Analog_IMAX.py
new file mode 100644
index 00000000000..5a445f3de8c
--- /dev/null
+++ b/release/scripts/presets/tracking_camera/Analog_IMAX.py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 71.41
+bpy.context.camera.sensor_height = 52.63
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/tracking_camera/Analog_Super_16.py b/release/scripts/presets/tracking_camera/Analog_Super_16.py
new file mode 100644
index 00000000000..a340a31dc25
--- /dev/null
+++ b/release/scripts/presets/tracking_camera/Analog_Super_16.py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 12.35
+bpy.context.camera.sensor_height = 7.42
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/tracking_camera/Analog_Super_35.py b/release/scripts/presets/tracking_camera/Analog_Super_35.py
new file mode 100644
index 00000000000..3c8f1837253
--- /dev/null
+++ b/release/scripts/presets/tracking_camera/Analog_Super_35.py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 24.89
+bpy.context.camera.sensor_height = 18.66
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/tracking_camera/Arri_Alexa.py b/release/scripts/presets/tracking_camera/Arri_Alexa.py
deleted file mode 100644
index ded361ec965..00000000000
--- a/release/scripts/presets/tracking_camera/Arri_Alexa.py
+++ /dev/null
@@ -1,9 +0,0 @@
-import bpy
-camera = bpy.context.edit_movieclip.tracking.camera
-
-camera.sensor_width = 23.76
-camera.units = 'MILLIMETERS'
-camera.pixel_aspect = 1
-camera.k1 = 0.0
-camera.k2 = 0.0
-camera.k3 = 0.0
diff --git a/release/scripts/presets/tracking_camera/Arri_Alexa_65.py b/release/scripts/presets/tracking_camera/Arri_Alexa_65.py
new file mode 100644
index 00000000000..b1467709949
--- /dev/null
+++ b/release/scripts/presets/tracking_camera/Arri_Alexa_65.py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 54.12
+bpy.context.camera.sensor_height = 25.58
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/tracking_camera/Arri_Alexa_LF.py b/release/scripts/presets/tracking_camera/Arri_Alexa_LF.py
new file mode 100644
index 00000000000..1cde94fce8d
--- /dev/null
+++ b/release/scripts/presets/tracking_camera/Arri_Alexa_LF.py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 36.70
+bpy.context.camera.sensor_height = 25.54
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/tracking_camera/Arri_Alexa_Mini_&_SXT.py b/release/scripts/presets/tracking_camera/Arri_Alexa_Mini_&_SXT.py
new file mode 100644
index 00000000000..0f61d35a0f9
--- /dev/null
+++ b/release/scripts/presets/tracking_camera/Arri_Alexa_Mini_&_SXT.py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 29.90
+bpy.context.camera.sensor_height = 15.77
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/tracking_camera/Blackmagic_Cinema_Camera.py b/release/scripts/presets/tracking_camera/Blackmagic_Cinema_Camera.py
deleted file mode 100644
index f84d0a19d22..00000000000
--- a/release/scripts/presets/tracking_camera/Blackmagic_Cinema_Camera.py
+++ /dev/null
@@ -1,9 +0,0 @@
-import bpy
-camera = bpy.context.edit_movieclip.tracking.camera
-
-camera.sensor_width = 15.81
-camera.units = 'MILLIMETERS'
-camera.pixel_aspect = 1
-camera.k1 = 0.0
-camera.k2 = 0.0
-camera.k3 = 0.0
diff --git a/release/scripts/presets/tracking_camera/Blackmagic_Pocket_&_Studio.py b/release/scripts/presets/tracking_camera/Blackmagic_Pocket_&_Studio.py
new file mode 100644
index 00000000000..260bfbaf94f
--- /dev/null
+++ b/release/scripts/presets/tracking_camera/Blackmagic_Pocket_&_Studio.py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 12.48
+bpy.context.camera.sensor_height = 7.02
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/tracking_camera/Blackmagic_Pocket_4K.py b/release/scripts/presets/tracking_camera/Blackmagic_Pocket_4K.py
new file mode 100644
index 00000000000..dc057397828
--- /dev/null
+++ b/release/scripts/presets/tracking_camera/Blackmagic_Pocket_4K.py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 18.96
+bpy.context.camera.sensor_height = 10.00
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/tracking_camera/Blackmagic_Pocket_6k.py b/release/scripts/presets/tracking_camera/Blackmagic_Pocket_6k.py
new file mode 100644
index 00000000000..a483f3d5f98
--- /dev/null
+++ b/release/scripts/presets/tracking_camera/Blackmagic_Pocket_6k.py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 23.10
+bpy.context.camera.sensor_height = 12.99
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/tracking_camera/Blackmagic_Pocket_Cinema_Camera.py b/release/scripts/presets/tracking_camera/Blackmagic_Pocket_Cinema_Camera.py
deleted file mode 100644
index a9c81f47c21..00000000000
--- a/release/scripts/presets/tracking_camera/Blackmagic_Pocket_Cinema_Camera.py
+++ /dev/null
@@ -1,9 +0,0 @@
-import bpy
-camera = bpy.context.edit_movieclip.tracking.camera
-
-camera.sensor_width = 12.48
-camera.units = 'MILLIMETERS'
-camera.pixel_aspect = 1
-camera.k1 = 0.0
-camera.k2 = 0.0
-camera.k3 = 0.0
diff --git a/release/scripts/presets/tracking_camera/Blackmagic_Production_Camera_4K.py b/release/scripts/presets/tracking_camera/Blackmagic_Production_Camera_4K.py
deleted file mode 100644
index d644d2a26c9..00000000000
--- a/release/scripts/presets/tracking_camera/Blackmagic_Production_Camera_4K.py
+++ /dev/null
@@ -1,9 +0,0 @@
-import bpy
-camera = bpy.context.edit_movieclip.tracking.camera
-
-camera.sensor_width = 21.12
-camera.units = 'MILLIMETERS'
-camera.pixel_aspect = 1
-camera.k1 = 0.0
-camera.k2 = 0.0
-camera.k3 = 0.0
diff --git a/release/scripts/presets/tracking_camera/Blackmagic_URSA_4.6K.py b/release/scripts/presets/tracking_camera/Blackmagic_URSA_4.6K.py
new file mode 100644
index 00000000000..c71e42d72d3
--- /dev/null
+++ b/release/scripts/presets/tracking_camera/Blackmagic_URSA_4.6K.py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 25.34
+bpy.context.camera.sensor_height = 14.25
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/tracking_camera/Blender.py b/release/scripts/presets/tracking_camera/Blender.py
deleted file mode 100644
index 507cedac4fc..00000000000
--- a/release/scripts/presets/tracking_camera/Blender.py
+++ /dev/null
@@ -1,10 +0,0 @@
-import bpy
-camera = bpy.context.edit_movieclip.tracking.camera
-
-camera.sensor_width = 32.0
-camera.units = 'MILLIMETERS'
-camera.focal_length = 35.0
-camera.pixel_aspect = 1
-camera.k1 = 0.0
-camera.k2 = 0.0
-camera.k3 = 0.0
diff --git a/release/scripts/presets/tracking_camera/Canon_1100D.py b/release/scripts/presets/tracking_camera/Canon_1100D.py
deleted file mode 100644
index 96d6d456337..00000000000
--- a/release/scripts/presets/tracking_camera/Canon_1100D.py
+++ /dev/null
@@ -1,9 +0,0 @@
-import bpy
-camera = bpy.context.edit_movieclip.tracking.camera
-
-camera.sensor_width = 22.2
-camera.units = 'MILLIMETERS'
-camera.pixel_aspect = 1
-camera.k1 = 0.0
-camera.k2 = 0.0
-camera.k3 = 0.0
diff --git a/release/scripts/presets/tracking_camera/Canon_APS-C.py b/release/scripts/presets/tracking_camera/Canon_APS-C.py
deleted file mode 100644
index cc4da545272..00000000000
--- a/release/scripts/presets/tracking_camera/Canon_APS-C.py
+++ /dev/null
@@ -1,9 +0,0 @@
-import bpy
-camera = bpy.context.edit_movieclip.tracking.camera
-
-camera.sensor_width = 22.3
-camera.units = 'MILLIMETERS'
-camera.pixel_aspect = 1
-camera.k1 = 0.0
-camera.k2 = 0.0
-camera.k3 = 0.0
diff --git a/release/scripts/presets/tracking_camera/Canon_APS-H.py b/release/scripts/presets/tracking_camera/Canon_APS-H.py
deleted file mode 100644
index 853edd5dcba..00000000000
--- a/release/scripts/presets/tracking_camera/Canon_APS-H.py
+++ /dev/null
@@ -1,9 +0,0 @@
-import bpy
-camera = bpy.context.edit_movieclip.tracking.camera
-
-camera.sensor_width = 27.90
-camera.units = 'MILLIMETERS'
-camera.pixel_aspect = 1
-camera.k1 = 0.0
-camera.k2 = 0.0
-camera.k3 = 0.0
diff --git a/release/scripts/presets/tracking_camera/Canon_C300.py b/release/scripts/presets/tracking_camera/Canon_C300.py
deleted file mode 100644
index 809f8f432f8..00000000000
--- a/release/scripts/presets/tracking_camera/Canon_C300.py
+++ /dev/null
@@ -1,9 +0,0 @@
-import bpy
-camera = bpy.context.edit_movieclip.tracking.camera
-
-camera.sensor_width = 24.4
-camera.units = 'MILLIMETERS'
-camera.pixel_aspect = 1
-camera.k1 = 0.0
-camera.k2 = 0.0
-camera.k3 = 0.0
diff --git a/release/scripts/presets/tracking_camera/Foveon_(Sigma).py b/release/scripts/presets/tracking_camera/Foveon_(Sigma).py
new file mode 100644
index 00000000000..e6a1a0ed344
--- /dev/null
+++ b/release/scripts/presets/tracking_camera/Foveon_(Sigma).py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 20.70
+bpy.context.camera.sensor_height = 13.80
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/tracking_camera/Full_Frame_35mm_Camera.py b/release/scripts/presets/tracking_camera/Full_Frame_35mm_Camera.py
deleted file mode 100644
index 0f3da0b4d72..00000000000
--- a/release/scripts/presets/tracking_camera/Full_Frame_35mm_Camera.py
+++ /dev/null
@@ -1,9 +0,0 @@
-import bpy
-camera = bpy.context.edit_movieclip.tracking.camera
-
-camera.sensor_width = 36
-camera.units = 'MILLIMETERS'
-camera.pixel_aspect = 1
-camera.k1 = 0.0
-camera.k2 = 0.0
-camera.k3 = 0.0
diff --git a/release/scripts/presets/tracking_camera/Fullframe.py b/release/scripts/presets/tracking_camera/Fullframe.py
new file mode 100644
index 00000000000..95fb4afc10b
--- /dev/null
+++ b/release/scripts/presets/tracking_camera/Fullframe.py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 36
+bpy.context.camera.sensor_height = 24
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/tracking_camera/GoPro_Hero3_Black.py b/release/scripts/presets/tracking_camera/GoPro_Hero3_Black.py
deleted file mode 100644
index 29851352284..00000000000
--- a/release/scripts/presets/tracking_camera/GoPro_Hero3_Black.py
+++ /dev/null
@@ -1,10 +0,0 @@
-import bpy
-camera = bpy.context.edit_movieclip.tracking.camera
-
-camera.sensor_width = 6.16
-camera.units = 'MILLIMETERS'
-camera.focal_length = 2.77
-camera.pixel_aspect = 1
-camera.k1 = 0.0
-camera.k2 = 0.0
-camera.k3 = 0.0
diff --git a/release/scripts/presets/tracking_camera/GoPro_Hero3_Silver.py b/release/scripts/presets/tracking_camera/GoPro_Hero3_Silver.py
deleted file mode 100644
index 9e08cf283a7..00000000000
--- a/release/scripts/presets/tracking_camera/GoPro_Hero3_Silver.py
+++ /dev/null
@@ -1,10 +0,0 @@
-import bpy
-camera = bpy.context.edit_movieclip.tracking.camera
-
-camera.sensor_width = 5.371
-camera.units = 'MILLIMETERS'
-camera.focal_length = 2.77
-camera.pixel_aspect = 1
-camera.k1 = 0.0
-camera.k2 = 0.0
-camera.k3 = 0.0
diff --git a/release/scripts/presets/tracking_camera/GoPro_Hero3_White.py b/release/scripts/presets/tracking_camera/GoPro_Hero3_White.py
deleted file mode 100644
index 6b1f9d97e81..00000000000
--- a/release/scripts/presets/tracking_camera/GoPro_Hero3_White.py
+++ /dev/null
@@ -1,10 +0,0 @@
-import bpy
-camera = bpy.context.edit_movieclip.tracking.camera
-
-camera.sensor_width = 5.76
-camera.units = 'MILLIMETERS'
-camera.focal_length = 2.77
-camera.pixel_aspect = 1
-camera.k1 = 0.0
-camera.k2 = 0.0
-camera.k3 = 0.0
diff --git a/release/scripts/presets/tracking_camera/MFT.py b/release/scripts/presets/tracking_camera/MFT.py
new file mode 100644
index 00000000000..bc0dd49baa8
--- /dev/null
+++ b/release/scripts/presets/tracking_camera/MFT.py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 17.3
+bpy.context.camera.sensor_height = 13.0
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/tracking_camera/Medium-format_(Hasselblad).py b/release/scripts/presets/tracking_camera/Medium-format_(Hasselblad).py
new file mode 100644
index 00000000000..e9b16024b79
--- /dev/null
+++ b/release/scripts/presets/tracking_camera/Medium-format_(Hasselblad).py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 44
+bpy.context.camera.sensor_height = 33
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/tracking_camera/Nexus_5.py b/release/scripts/presets/tracking_camera/Nexus_5.py
deleted file mode 100644
index 172c8e93bfd..00000000000
--- a/release/scripts/presets/tracking_camera/Nexus_5.py
+++ /dev/null
@@ -1,10 +0,0 @@
-import bpy
-camera = bpy.context.edit_movieclip.tracking.camera
-
-camera.sensor_width = 4.5
-camera.units = 'MILLIMETERS'
-camera.focal_length = 3.91
-camera.pixel_aspect = 1
-camera.k1 = 0.0
-camera.k2 = 0.0
-camera.k3 = 0.0
diff --git a/release/scripts/presets/tracking_camera/Nikon_D3100.py b/release/scripts/presets/tracking_camera/Nikon_D3100.py
deleted file mode 100644
index 44646f8b112..00000000000
--- a/release/scripts/presets/tracking_camera/Nikon_D3100.py
+++ /dev/null
@@ -1,9 +0,0 @@
-import bpy
-camera = bpy.context.edit_movieclip.tracking.camera
-
-camera.sensor_width = 23.1
-camera.units = 'MILLIMETERS'
-camera.pixel_aspect = 1
-camera.k1 = 0.0
-camera.k2 = 0.0
-camera.k3 = 0.0
diff --git a/release/scripts/presets/tracking_camera/Nikon_DX.py b/release/scripts/presets/tracking_camera/Nikon_DX.py
deleted file mode 100644
index 8d9e3505e3f..00000000000
--- a/release/scripts/presets/tracking_camera/Nikon_DX.py
+++ /dev/null
@@ -1,9 +0,0 @@
-import bpy
-camera = bpy.context.edit_movieclip.tracking.camera
-
-camera.sensor_width = 23.6
-camera.units = 'MILLIMETERS'
-camera.pixel_aspect = 1
-camera.k1 = 0.0
-camera.k2 = 0.0
-camera.k3 = 0.0
diff --git a/release/scripts/presets/tracking_camera/Panasonic_AG-HVX200.py b/release/scripts/presets/tracking_camera/Panasonic_AG-HVX200.py
deleted file mode 100644
index 49cc71fa5da..00000000000
--- a/release/scripts/presets/tracking_camera/Panasonic_AG-HVX200.py
+++ /dev/null
@@ -1,9 +0,0 @@
-import bpy
-camera = bpy.context.edit_movieclip.tracking.camera
-
-camera.sensor_width = 4.68
-camera.units = 'MILLIMETERS'
-camera.pixel_aspect = 1.5
-camera.k1 = 0.0
-camera.k2 = 0.0
-camera.k3 = 0.0
diff --git a/release/scripts/presets/tracking_camera/Panasonic_LX2.py b/release/scripts/presets/tracking_camera/Panasonic_LX2.py
deleted file mode 100644
index f9ffcb8ec03..00000000000
--- a/release/scripts/presets/tracking_camera/Panasonic_LX2.py
+++ /dev/null
@@ -1,9 +0,0 @@
-import bpy
-camera = bpy.context.edit_movieclip.tracking.camera
-
-camera.sensor_width = 8.5
-camera.units = 'MILLIMETERS'
-camera.pixel_aspect = 1
-camera.k1 = 0.0
-camera.k2 = 0.0
-camera.k3 = 0.0
diff --git a/release/scripts/presets/tracking_camera/RED_Dragon_5K.py b/release/scripts/presets/tracking_camera/RED_Dragon_5K.py
new file mode 100644
index 00000000000..fa95a98f8c4
--- /dev/null
+++ b/release/scripts/presets/tracking_camera/RED_Dragon_5K.py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 25.60
+bpy.context.camera.sensor_height = 13.5
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/tracking_camera/RED_Dragon_6K.py b/release/scripts/presets/tracking_camera/RED_Dragon_6K.py
new file mode 100644
index 00000000000..80f7ad1bbb8
--- /dev/null
+++ b/release/scripts/presets/tracking_camera/RED_Dragon_6K.py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 30.70
+bpy.context.camera.sensor_height = 15.80
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/tracking_camera/RED_Helium_8K.py b/release/scripts/presets/tracking_camera/RED_Helium_8K.py
new file mode 100644
index 00000000000..0f61d35a0f9
--- /dev/null
+++ b/release/scripts/presets/tracking_camera/RED_Helium_8K.py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 29.90
+bpy.context.camera.sensor_height = 15.77
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/tracking_camera/RED_Monstro_8K.py b/release/scripts/presets/tracking_camera/RED_Monstro_8K.py
new file mode 100644
index 00000000000..86c382624ab
--- /dev/null
+++ b/release/scripts/presets/tracking_camera/RED_Monstro_8K.py
@@ -0,0 +1,4 @@
+import bpy
+bpy.context.camera.sensor_width = 40.96
+bpy.context.camera.sensor_height = 21.60
+bpy.context.camera.sensor_fit = 'HORIZONTAL' \ No newline at end of file
diff --git a/release/scripts/presets/tracking_camera/Red_Epic.py b/release/scripts/presets/tracking_camera/Red_Epic.py
deleted file mode 100644
index c0790e6baed..00000000000
--- a/release/scripts/presets/tracking_camera/Red_Epic.py
+++ /dev/null
@@ -1,9 +0,0 @@
-import bpy
-camera = bpy.context.edit_movieclip.tracking.camera
-
-camera.sensor_width = 30.0
-camera.units = 'MILLIMETERS'
-camera.pixel_aspect = 1
-camera.k1 = 0.0
-camera.k2 = 0.0
-camera.k3 = 0.0
diff --git a/release/scripts/presets/tracking_camera/Red_One_2K.py b/release/scripts/presets/tracking_camera/Red_One_2K.py
deleted file mode 100644
index fa9585b5e08..00000000000
--- a/release/scripts/presets/tracking_camera/Red_One_2K.py
+++ /dev/null
@@ -1,9 +0,0 @@
-import bpy
-camera = bpy.context.edit_movieclip.tracking.camera
-
-camera.sensor_width = 11.1
-camera.units = 'MILLIMETERS'
-camera.pixel_aspect = 1
-camera.k1 = 0.0
-camera.k2 = 0.0
-camera.k3 = 0.0
diff --git a/release/scripts/presets/tracking_camera/Red_One_3K.py b/release/scripts/presets/tracking_camera/Red_One_3K.py
deleted file mode 100644
index 5a1b7472109..00000000000
--- a/release/scripts/presets/tracking_camera/Red_One_3K.py
+++ /dev/null
@@ -1,9 +0,0 @@
-import bpy
-camera = bpy.context.edit_movieclip.tracking.camera
-
-camera.sensor_width = 16.65
-camera.units = 'MILLIMETERS'
-camera.pixel_aspect = 1
-camera.k1 = 0.0
-camera.k2 = 0.0
-camera.k3 = 0.0
diff --git a/release/scripts/presets/tracking_camera/Red_One_4K.py b/release/scripts/presets/tracking_camera/Red_One_4K.py
deleted file mode 100644
index 96d6d456337..00000000000
--- a/release/scripts/presets/tracking_camera/Red_One_4K.py
+++ /dev/null
@@ -1,9 +0,0 @@
-import bpy
-camera = bpy.context.edit_movieclip.tracking.camera
-
-camera.sensor_width = 22.2
-camera.units = 'MILLIMETERS'
-camera.pixel_aspect = 1
-camera.k1 = 0.0
-camera.k2 = 0.0
-camera.k3 = 0.0
diff --git a/release/scripts/presets/tracking_camera/Samsung_Galaxy_S3.py b/release/scripts/presets/tracking_camera/Samsung_Galaxy_S3.py
deleted file mode 100644
index d10994e45f5..00000000000
--- a/release/scripts/presets/tracking_camera/Samsung_Galaxy_S3.py
+++ /dev/null
@@ -1,10 +0,0 @@
-import bpy
-camera = bpy.context.edit_movieclip.tracking.camera
-
-camera.sensor_width = 4.8
-camera.units = 'MILLIMETERS'
-camera.focal_length = 3.70
-camera.pixel_aspect = 1
-camera.k1 = 0.0
-camera.k2 = 0.0
-camera.k3 = 0.0
diff --git a/release/scripts/presets/tracking_camera/Samsung_Galaxy_S4.py b/release/scripts/presets/tracking_camera/Samsung_Galaxy_S4.py
deleted file mode 100644
index c5fef80b3de..00000000000
--- a/release/scripts/presets/tracking_camera/Samsung_Galaxy_S4.py
+++ /dev/null
@@ -1,10 +0,0 @@
-import bpy
-camera = bpy.context.edit_movieclip.tracking.camera
-
-camera.sensor_width = 4.8
-camera.units = 'MILLIMETERS'
-camera.focal_length = 4.2
-camera.pixel_aspect = 1
-camera.k1 = 0.0
-camera.k2 = 0.0
-camera.k3 = 0.0
diff --git a/release/scripts/presets/tracking_camera/Sony_A55.py b/release/scripts/presets/tracking_camera/Sony_A55.py
deleted file mode 100644
index 26920d06f94..00000000000
--- a/release/scripts/presets/tracking_camera/Sony_A55.py
+++ /dev/null
@@ -1,9 +0,0 @@
-import bpy
-camera = bpy.context.edit_movieclip.tracking.camera
-
-camera.sensor_width = 23.4
-camera.units = 'MILLIMETERS'
-camera.pixel_aspect = 1
-camera.k1 = 0.0
-camera.k2 = 0.0
-camera.k3 = 0.0
diff --git a/release/scripts/presets/tracking_camera/Sony_EX1.py b/release/scripts/presets/tracking_camera/Sony_EX1.py
deleted file mode 100644
index 2b99c91d221..00000000000
--- a/release/scripts/presets/tracking_camera/Sony_EX1.py
+++ /dev/null
@@ -1,9 +0,0 @@
-import bpy
-camera = bpy.context.edit_movieclip.tracking.camera
-
-camera.sensor_width = 6.97
-camera.units = 'MILLIMETERS'
-camera.pixel_aspect = 1
-camera.k1 = 0.0
-camera.k2 = 0.0
-camera.k3 = 0.0
diff --git a/release/scripts/presets/tracking_camera/Sony_F65.py b/release/scripts/presets/tracking_camera/Sony_F65.py
deleted file mode 100644
index 7da93fd5d47..00000000000
--- a/release/scripts/presets/tracking_camera/Sony_F65.py
+++ /dev/null
@@ -1,9 +0,0 @@
-import bpy
-camera = bpy.context.edit_movieclip.tracking.camera
-
-camera.sensor_width = 24.33
-camera.units = 'MILLIMETERS'
-camera.pixel_aspect = 1
-camera.k1 = 0.0
-camera.k2 = 0.0
-camera.k3 = 0.0
diff --git a/release/scripts/presets/tracking_camera/Super_16.py b/release/scripts/presets/tracking_camera/Super_16.py
deleted file mode 100644
index e94da9a99ba..00000000000
--- a/release/scripts/presets/tracking_camera/Super_16.py
+++ /dev/null
@@ -1,9 +0,0 @@
-import bpy
-camera = bpy.context.edit_movieclip.tracking.camera
-
-camera.sensor_width = 12.52
-camera.units = 'MILLIMETERS'
-camera.pixel_aspect = 1
-camera.k1 = 0.0
-camera.k2 = 0.0
-camera.k3 = 0.0
diff --git a/release/scripts/presets/tracking_camera/Super_35.py b/release/scripts/presets/tracking_camera/Super_35.py
deleted file mode 100644
index e07edc3a22c..00000000000
--- a/release/scripts/presets/tracking_camera/Super_35.py
+++ /dev/null
@@ -1,9 +0,0 @@
-import bpy
-camera = bpy.context.edit_movieclip.tracking.camera
-
-camera.sensor_width = 24.89
-camera.units = 'MILLIMETERS'
-camera.pixel_aspect = 1
-camera.k1 = 0.0
-camera.k2 = 0.0
-camera.k3 = 0.0
diff --git a/release/scripts/presets/tracking_camera/iPhone_4.py b/release/scripts/presets/tracking_camera/iPhone_4.py
deleted file mode 100644
index 220e5e08147..00000000000
--- a/release/scripts/presets/tracking_camera/iPhone_4.py
+++ /dev/null
@@ -1,10 +0,0 @@
-import bpy
-camera = bpy.context.edit_movieclip.tracking.camera
-
-camera.sensor_width = 4.54
-camera.units = 'MILLIMETERS'
-camera.focal_length = 3.85
-camera.pixel_aspect = 1
-camera.k1 = 0.0
-camera.k2 = 0.0
-camera.k3 = 0.0
diff --git a/release/scripts/presets/tracking_camera/iPhone_4S.py b/release/scripts/presets/tracking_camera/iPhone_4S.py
deleted file mode 100644
index 686cffc8f99..00000000000
--- a/release/scripts/presets/tracking_camera/iPhone_4S.py
+++ /dev/null
@@ -1,10 +0,0 @@
-import bpy
-camera = bpy.context.edit_movieclip.tracking.camera
-
-camera.sensor_width = 4.54
-camera.units = 'MILLIMETERS'
-camera.focal_length = 4.28
-camera.pixel_aspect = 1
-camera.k1 = 0.0
-camera.k2 = 0.0
-camera.k3 = 0.0
diff --git a/release/scripts/presets/tracking_camera/iPhone_5.py b/release/scripts/presets/tracking_camera/iPhone_5.py
deleted file mode 100644
index d8e05da8425..00000000000
--- a/release/scripts/presets/tracking_camera/iPhone_5.py
+++ /dev/null
@@ -1,10 +0,0 @@
-import bpy
-camera = bpy.context.edit_movieclip.tracking.camera
-
-camera.sensor_width = 4.54
-camera.units = 'MILLIMETERS'
-camera.focal_length = 4.10
-camera.pixel_aspect = 1
-camera.k1 = 0.0
-camera.k2 = 0.0
-camera.k3 = 0.0
diff --git a/release/scripts/startup/bl_operators/anim.py b/release/scripts/startup/bl_operators/anim.py
index ab56b24d186..81a28964389 100644
--- a/release/scripts/startup/bl_operators/anim.py
+++ b/release/scripts/startup/bl_operators/anim.py
@@ -355,7 +355,8 @@ class UpdateAnimatedTransformConstraint(Operator):
use_convert_to_radians: BoolProperty(
name="Convert to Radians",
- description="Convert fcurves/drivers affecting rotations to radians (Warning: use this only once!)",
+ description="Convert f-curves/drivers affecting rotations to radians.\n"
+ "Warning: Use this only once",
default=True,
)
diff --git a/release/scripts/startup/bl_operators/clip.py b/release/scripts/startup/bl_operators/clip.py
index 73391f94c85..45d1ea98a8a 100644
--- a/release/scripts/startup/bl_operators/clip.py
+++ b/release/scripts/startup/bl_operators/clip.py
@@ -18,7 +18,6 @@
# <pep8 compliant>
import bpy
-import os
from bpy.types import Operator
from bpy.props import FloatProperty
from mathutils import (
@@ -207,8 +206,8 @@ class CLIP_OT_filter_tracks(Operator):
@classmethod
def poll(cls, context):
- space = context.space_data
- return (space.type == 'CLIP_EDITOR') and space.clip
+ sc = context.space_data
+ return sc and (sc.type == 'CLIP_EDITOR') and sc.clip
def execute(self, context):
num_tracks = self._filter_values(context, self.track_threshold)
@@ -222,8 +221,8 @@ class CLIP_OT_set_active_clip(Operator):
@classmethod
def poll(cls, context):
- space = context.space_data
- return space.type == 'CLIP_EDITOR' and space.clip
+ sc = context.space_data
+ return sc and (sc.type == 'CLIP_EDITOR') and sc.clip
def execute(self, context):
clip = context.space_data.clip
@@ -269,8 +268,8 @@ class CLIP_OT_track_to_empty(Operator):
@classmethod
def poll(cls, context):
- space = context.space_data
- return space.type == 'CLIP_EDITOR' and space.clip
+ sc = context.space_data
+ return sc and (sc.type == 'CLIP_EDITOR') and sc.clip
def execute(self, context):
sc = context.space_data
@@ -294,7 +293,7 @@ class CLIP_OT_bundles_to_mesh(Operator):
@classmethod
def poll(cls, context):
sc = context.space_data
- return (sc.type == 'CLIP_EDITOR') and sc.clip
+ return sc and (sc.type == 'CLIP_EDITOR') and sc.clip
def execute(self, context):
from bpy_extras.io_utils import unpack_list
@@ -342,12 +341,8 @@ class CLIP_OT_delete_proxy(Operator):
@classmethod
def poll(cls, context):
- if context.space_data.type != 'CLIP_EDITOR':
- return False
-
sc = context.space_data
-
- return sc.clip
+ return sc and (sc.type == 'CLIP_EDITOR') and sc.clip
def invoke(self, context, event):
wm = context.window_manager
@@ -356,6 +351,7 @@ class CLIP_OT_delete_proxy(Operator):
@staticmethod
def _rmproxy(abspath):
+ import os
import shutil
if not os.path.exists(abspath):
@@ -367,6 +363,7 @@ class CLIP_OT_delete_proxy(Operator):
os.remove(abspath)
def execute(self, context):
+ import os
sc = context.space_data
clip = sc.clip
if clip.use_proxy_custom_directory:
@@ -423,12 +420,8 @@ class CLIP_OT_set_viewport_background(Operator):
@classmethod
def poll(cls, context):
- if context.space_data.type != 'CLIP_EDITOR':
- return False
-
sc = context.space_data
-
- return sc.clip
+ return sc and (sc.type == 'CLIP_EDITOR') and sc.clip
def execute(self, context):
sc = context.space_data
@@ -562,13 +555,11 @@ class CLIP_OT_setup_tracking_scene(Operator):
@classmethod
def poll(cls, context):
sc = context.space_data
-
- if sc.type != 'CLIP_EDITOR':
- return False
-
- clip = sc.clip
-
- return clip and clip.tracking.reconstruction.is_valid
+ if sc and sc.type == 'CLIP_EDITOR':
+ clip = sc.clip
+ if clip and clip.tracking.reconstruction.is_valid:
+ return True
+ return False
@staticmethod
def _setupScene(context):
@@ -1017,13 +1008,11 @@ class CLIP_OT_track_settings_as_default(Operator):
@classmethod
def poll(cls, context):
sc = context.space_data
-
- if sc.type != 'CLIP_EDITOR':
- return False
-
- clip = sc.clip
-
- return clip and clip.tracking.tracks.active
+ if sc and sc.type == 'CLIP_EDITOR':
+ clip = sc.clip
+ if clip and clip.tracking.tracks.active:
+ return True
+ return False
def execute(self, context):
sc = context.space_data
@@ -1067,11 +1056,12 @@ class CLIP_OT_track_settings_to_track(Operator):
@classmethod
def poll(cls, context):
- space = context.space_data
- if space.type != 'CLIP_EDITOR':
- return False
- clip = space.clip
- return clip and clip.tracking.tracks.active
+ sc = context.space_data
+ if sc and sc.type == 'CLIP_EDITOR':
+ clip = sc.clip
+ if clip and clip.tracking.tracks.active:
+ return True
+ return False
def execute(self, context):
space = context.space_data
diff --git a/release/scripts/startup/bl_operators/constraint.py b/release/scripts/startup/bl_operators/constraint.py
index 49fc6a04112..f18f3bb3a49 100644
--- a/release/scripts/startup/bl_operators/constraint.py
+++ b/release/scripts/startup/bl_operators/constraint.py
@@ -33,6 +33,11 @@ class CONSTRAINT_OT_add_target(Operator):
bl_label = "Add Target"
bl_options = {'UNDO', 'INTERNAL'}
+ @classmethod
+ def poll(cls, context):
+ constraint = getattr(context, "constraint", None)
+ return constraint
+
def execute(self, context):
context.constraint.targets.new()
return {'FINISHED'}
@@ -46,6 +51,11 @@ class CONSTRAINT_OT_remove_target(Operator):
index: IntProperty()
+ @classmethod
+ def poll(cls, context):
+ constraint = getattr(context, "constraint", None)
+ return constraint
+
def execute(self, context):
tgts = context.constraint.targets
tgts.remove(tgts[self.index])
@@ -58,6 +68,11 @@ class CONSTRAINT_OT_normalize_target_weights(Operator):
bl_label = "Normalize Weights"
bl_options = {'UNDO', 'INTERNAL'}
+ @classmethod
+ def poll(cls, context):
+ constraint = getattr(context, "constraint", None)
+ return constraint
+
def execute(self, context):
tgts = context.constraint.targets
total = sum(t.weight for t in tgts)
diff --git a/release/scripts/startup/bl_operators/geometry_nodes.py b/release/scripts/startup/bl_operators/geometry_nodes.py
index 0c7a2a01b7a..71ef89a066b 100644
--- a/release/scripts/startup/bl_operators/geometry_nodes.py
+++ b/release/scripts/startup/bl_operators/geometry_nodes.py
@@ -42,8 +42,8 @@ def geometry_node_group_empty_new():
def geometry_modifier_poll(context):
ob = context.object
- # Test object support for geometry node modifier (No volume, curve, or hair object support yet)
- if not ob or ob.type not in {'MESH', 'POINTCLOUD'}:
+ # Test object support for geometry node modifier (No curve, or hair object support yet)
+ if not ob or ob.type not in {'MESH', 'POINTCLOUD', 'VOLUME'}:
return False
return True
diff --git a/release/scripts/startup/bl_operators/mesh.py b/release/scripts/startup/bl_operators/mesh.py
index 5fca3e194d7..ddbfe7845b1 100644
--- a/release/scripts/startup/bl_operators/mesh.py
+++ b/release/scripts/startup/bl_operators/mesh.py
@@ -219,7 +219,7 @@ class MeshSelectNext(Operator):
if find_adjacent.select_next(bm, self.report):
bm.select_flush_mode()
- bmesh.update_edit_mesh(me, False)
+ bmesh.update_edit_mesh(me, loop_triangles=False)
return {'FINISHED'}
@@ -244,7 +244,7 @@ class MeshSelectPrev(Operator):
if find_adjacent.select_prev(bm, self.report):
bm.select_flush_mode()
- bmesh.update_edit_mesh(me, False)
+ bmesh.update_edit_mesh(me, loop_triangles=False)
return {'FINISHED'}
diff --git a/release/scripts/startup/bl_operators/node.py b/release/scripts/startup/bl_operators/node.py
index 52484f8ffc8..17e17273432 100644
--- a/release/scripts/startup/bl_operators/node.py
+++ b/release/scripts/startup/bl_operators/node.py
@@ -20,7 +20,6 @@
from __future__ import annotations
import bpy
-import nodeitems_utils
from bpy.types import (
Operator,
PropertyGroup,
@@ -121,7 +120,7 @@ class NodeAddOperator:
def poll(cls, context):
space = context.space_data
# needs active node editor and a tree to add nodes to
- return ((space.type == 'NODE_EDITOR') and
+ return (space and (space.type == 'NODE_EDITOR') and
space.edit_tree and not space.edit_tree.library)
# Default execute simply adds a node
@@ -195,6 +194,8 @@ class NODE_OT_add_search(NodeAddOperator, Operator):
# Create an enum list from node items
def node_enum_items(self, context):
+ import nodeitems_utils
+
enum_items = NODE_OT_add_search._enum_item_hack
enum_items.clear()
@@ -210,6 +211,8 @@ class NODE_OT_add_search(NodeAddOperator, Operator):
# Look up the item based on index
def find_node_item(self, context):
+ import nodeitems_utils
+
node_item = int(self.node_item)
for index, item in enumerate(nodeitems_utils.node_items_iter(context)):
if index == node_item:
@@ -262,7 +265,7 @@ class NODE_OT_collapse_hide_unused_toggle(Operator):
def poll(cls, context):
space = context.space_data
# needs active node editor and a tree
- return ((space.type == 'NODE_EDITOR') and
+ return (space and (space.type == 'NODE_EDITOR') and
(space.edit_tree and not space.edit_tree.library))
def execute(self, context):
@@ -293,7 +296,7 @@ class NODE_OT_tree_path_parent(Operator):
def poll(cls, context):
space = context.space_data
# needs active node editor and a tree
- return (space.type == 'NODE_EDITOR' and len(space.path) > 1)
+ return (space and (space.type == 'NODE_EDITOR') and len(space.path) > 1)
def execute(self, context):
space = context.space_data
@@ -302,7 +305,6 @@ class NODE_OT_tree_path_parent(Operator):
return {'FINISHED'}
-
class NODE_OT_expose_input_socket(Operator):
'''Expose socket'''
bl_idname = "node.expose_input_socket"
@@ -324,6 +326,65 @@ class NODE_OT_expose_input_socket(Operator):
return {'FINISHED'}
+class NODE_OT_active_preview_toggle(Operator):
+ '''Toggle active preview state of node'''
+ bl_idname = "node.active_preview_toggle"
+ bl_label = "Toggle Active Preview"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ @classmethod
+ def poll(cls, context):
+ space = context.space_data
+ if space is None:
+ return False
+ if space.type != 'NODE_EDITOR':
+ return False
+ if space.edit_tree is None:
+ return False
+ if space.edit_tree.nodes.active is None:
+ return False
+ return True
+
+ def execute(self, context):
+ node_editor = context.space_data
+ ntree = node_editor.edit_tree
+ active_node = ntree.nodes.active
+
+ if active_node.active_preview:
+ self.disable_preview(context, ntree, active_node)
+ else:
+ self.enable_preview(context, node_editor, ntree, active_node)
+
+ return {'FINISHED'}
+
+ def enable_preview(self, context, node_editor, ntree, active_node):
+ spreadsheets = self.find_unpinned_spreadsheets(context)
+
+ for spreadsheet in spreadsheets:
+ spreadsheet.set_geometry_node_context(node_editor, active_node)
+
+ for node in ntree.nodes:
+ node.active_preview = False
+ active_node.active_preview = True
+
+ def disable_preview(self, context, ntree, active_node):
+ spreadsheets = self.find_unpinned_spreadsheets(context)
+ for spreadsheet in spreadsheets:
+ spreadsheet.context_path.clear()
+
+ active_node.active_preview = False
+
+ def find_unpinned_spreadsheets(self, context):
+ spreadsheets = []
+ for window in context.window_manager.windows:
+ for area in window.screen.areas:
+ space = area.spaces.active
+ if space.type == 'SPREADSHEET' and not space.is_pinned:
+ spreadsheets.append(space)
+ return spreadsheets
+
+
+
classes = (
NodeSetting,
@@ -333,4 +394,5 @@ classes = (
NODE_OT_collapse_hide_unused_toggle,
NODE_OT_tree_path_parent,
NODE_OT_expose_input_socket,
+ NODE_OT_active_preview_toggle,
)
diff --git a/release/scripts/startup/bl_operators/object.py b/release/scripts/startup/bl_operators/object.py
index 5a388047ddd..d61bed71cab 100644
--- a/release/scripts/startup/bl_operators/object.py
+++ b/release/scripts/startup/bl_operators/object.py
@@ -133,7 +133,7 @@ class SelectCamera(Operator):
scene = context.scene
view_layer = context.view_layer
view = context.space_data
- if view.type == 'VIEW_3D' and view.use_local_camera:
+ if view and view.type == 'VIEW_3D' and view.use_local_camera:
camera = view.camera
else:
camera = scene.camera
diff --git a/release/scripts/startup/bl_operators/presets.py b/release/scripts/startup/bl_operators/presets.py
index cedbe542287..3189f3b3376 100644
--- a/release/scripts/startup/bl_operators/presets.py
+++ b/release/scripts/startup/bl_operators/presets.py
@@ -114,9 +114,7 @@ class AddPresetBase:
filename = self.as_filename(name)
target_path = os.path.join("presets", self.preset_subdir)
- target_path = bpy.utils.user_resource('SCRIPTS',
- target_path,
- create=True)
+ target_path = bpy.utils.user_resource('SCRIPTS', path=target_path, create=True)
if not target_path:
self.report({'WARNING'}, "Failed to create presets path")
diff --git a/release/scripts/startup/bl_operators/screen_play_rendered_anim.py b/release/scripts/startup/bl_operators/screen_play_rendered_anim.py
index 6c29c07c62e..6d60c58cc3a 100644
--- a/release/scripts/startup/bl_operators/screen_play_rendered_anim.py
+++ b/release/scripts/startup/bl_operators/screen_play_rendered_anim.py
@@ -22,7 +22,6 @@
import bpy
from bpy.types import Operator
-import os
from bpy.app.translations import pgettext_tip as tip_
@@ -62,6 +61,7 @@ class PlayRenderedAnim(Operator):
bl_options = {'REGISTER'}
def execute(self, context):
+ import os
import subprocess
from shlex import quote
@@ -130,6 +130,7 @@ class PlayRenderedAnim(Operator):
"-s", str(frame_start),
"-e", str(frame_end),
"-j", str(scene.frame_step),
+ "-c", str(prefs.system.memory_cache_limit),
file,
]
cmd.extend(opts)
diff --git a/release/scripts/startup/bl_operators/spreadsheet.py b/release/scripts/startup/bl_operators/spreadsheet.py
index 91fca883bb5..5cc83d4eddd 100644
--- a/release/scripts/startup/bl_operators/spreadsheet.py
+++ b/release/scripts/startup/bl_operators/spreadsheet.py
@@ -34,13 +34,45 @@ class SPREADSHEET_OT_toggle_pin(Operator):
def execute(self, context):
space = context.space_data
- if space.pinned_id:
- space.pinned_id = None
+ if space.is_pinned:
+ self.unpin(context)
else:
- space.pinned_id = context.active_object
-
+ self.pin(context)
return {'FINISHED'}
+ def pin(self, context):
+ space = context.space_data
+ space.is_pinned = True
+
+ def unpin(self, context):
+ space = context.space_data
+ space.is_pinned = False
+
+ space.context_path.clear()
+
+ # Try to find a node with an active preview in any open editor.
+ if space.object_eval_state == 'EVALUATED':
+ node_editors = self.find_geometry_node_editors(context)
+ for node_editor in node_editors:
+ ntree = node_editor.edit_tree
+ for node in ntree.nodes:
+ if node.active_preview:
+ space.set_geometry_node_context(node_editor, node)
+ return
+
+ def find_geometry_node_editors(self, context):
+ editors = []
+ for window in context.window_manager.windows:
+ for area in window.screen.areas:
+ space = area.spaces.active
+ if space.type != 'NODE_EDITOR':
+ continue
+ if space.edit_tree is None:
+ continue
+ if space.edit_tree.type == 'GEOMETRY':
+ editors.append(space)
+ return editors
+
classes = (
SPREADSHEET_OT_toggle_pin,
diff --git a/release/scripts/startup/bl_operators/userpref.py b/release/scripts/startup/bl_operators/userpref.py
index 7547184dc04..623bf583a74 100644
--- a/release/scripts/startup/bl_operators/userpref.py
+++ b/release/scripts/startup/bl_operators/userpref.py
@@ -90,7 +90,7 @@ class PREFERENCES_OT_copy_prev(Operator):
@classmethod
def _old_version_path(cls, version):
- return bpy.utils.resource_path('USER', version[0], version[1])
+ return bpy.utils.resource_path('USER', major=version[0], minor=version[1])
@classmethod
def previous_version(cls):
@@ -99,6 +99,15 @@ class PREFERENCES_OT_copy_prev(Operator):
version = bpy.app.version
version_new = ((version[0] * 100) + version[1])
version_old = ((version[0] * 100) + version[1]) - 1
+
+ # Special case, remove when the version is > 3.0.
+ if version_new == 300:
+ version_new = 294
+ version_old = 293
+ else:
+ print("TODO: remove exception!")
+ # End special case.
+
# Ensure we only try to copy files from a point release.
# The check below ensures the second numbers match.
while (version_new % 100) // 10 == (version_old % 100) // 10:
@@ -144,7 +153,7 @@ class PREFERENCES_OT_copy_prev(Operator):
def execute(self, _context):
import shutil
- shutil.copytree(self._old_path(), self._new_path(), dirs_exist_ok=True)
+ shutil.copytree(self._old_path(), self._new_path(), dirs_exist_ok=True, symlinks=True)
# reload preferences and recent-files.txt
bpy.ops.wm.read_userpref()
@@ -217,7 +226,11 @@ class PREFERENCES_OT_keyconfig_import(Operator):
config_name = basename(self.filepath)
- path = bpy.utils.user_resource('SCRIPTS', os.path.join("presets", "keyconfig"), create=True)
+ path = bpy.utils.user_resource(
+ 'SCRIPTS',
+ path=os.path.join("presets", "keyconfig"),
+ create=True,
+ )
path = os.path.join(path, config_name)
try:
@@ -520,7 +533,11 @@ class PREFERENCES_OT_theme_install(Operator):
xmlfile = self.filepath
- path_themes = bpy.utils.user_resource('SCRIPTS', "presets/interface_theme", create=True)
+ path_themes = bpy.utils.user_resource(
+ 'SCRIPTS',
+ path=os.path.join("presets", "interface_theme"),
+ create=True,
+ )
if not path_themes:
self.report({'ERROR'}, "Failed to get themes path")
@@ -581,7 +598,7 @@ class PREFERENCES_OT_addon_install(Operator):
name="Target Path",
items=(
('DEFAULT', "Default", ""),
- ('PREFS', "User Prefs", ""),
+ ('PREFS', "Preferences", ""),
),
)
@@ -613,8 +630,8 @@ class PREFERENCES_OT_addon_install(Operator):
pyfile = self.filepath
if self.target == 'DEFAULT':
- # don't use bpy.utils.script_paths("addons") because we may not be able to write to it.
- path_addons = bpy.utils.user_resource('SCRIPTS', "addons", create=True)
+ # Don't use `bpy.utils.script_paths(path="addons")` because we may not be able to write to it.
+ path_addons = bpy.utils.user_resource('SCRIPTS', path="addons", create=True)
else:
path_addons = context.preferences.filepaths.script_directory
if path_addons:
@@ -873,7 +890,8 @@ class PREFERENCES_OT_app_template_install(Operator):
filepath = self.filepath
path_app_templates = bpy.utils.user_resource(
- 'SCRIPTS', os.path.join("startup", "bl_app_templates_user"),
+ 'SCRIPTS',
+ path=os.path.join("startup", "bl_app_templates_user"),
create=True,
)
@@ -979,7 +997,7 @@ class PREFERENCES_OT_studiolight_install(Operator):
prefs = context.preferences
path_studiolights = os.path.join("studiolights", self.type.lower())
- path_studiolights = bpy.utils.user_resource('DATAFILES', path_studiolights, create=True)
+ path_studiolights = bpy.utils.user_resource('DATAFILES', path=path_studiolights, create=True)
if not path_studiolights:
self.report({'ERROR'}, "Failed to create Studio Light path")
return {'CANCELLED'}
@@ -1025,7 +1043,11 @@ class PREFERENCES_OT_studiolight_new(Operator):
wm = context.window_manager
filename = bpy.path.ensure_ext(self.filename, ".sl")
- path_studiolights = bpy.utils.user_resource('DATAFILES', os.path.join("studiolights", "studio"), create=True)
+ path_studiolights = bpy.utils.user_resource(
+ 'DATAFILES',
+ path=os.path.join("studiolights", "studio"),
+ create=True,
+ )
if not path_studiolights:
self.report({'ERROR'}, "Failed to get Studio Light path")
return {'CANCELLED'}
diff --git a/release/scripts/startup/bl_operators/uvcalc_follow_active.py b/release/scripts/startup/bl_operators/uvcalc_follow_active.py
index 1b801f77e07..90131109e24 100644
--- a/release/scripts/startup/bl_operators/uvcalc_follow_active.py
+++ b/release/scripts/startup/bl_operators/uvcalc_follow_active.py
@@ -223,7 +223,7 @@ def extend(obj, EXTEND_MODE):
for f_triple in walk_face(f_act):
apply_uv(*f_triple)
- bmesh.update_edit_mesh(me, False)
+ bmesh.update_edit_mesh(me, loop_triangles=False)
return STATUS_OK
diff --git a/release/scripts/startup/bl_operators/uvcalc_lightmap.py b/release/scripts/startup/bl_operators/uvcalc_lightmap.py
index 29c17711c2a..6ba8750e9df 100644
--- a/release/scripts/startup/bl_operators/uvcalc_lightmap.py
+++ b/release/scripts/startup/bl_operators/uvcalc_lightmap.py
@@ -628,7 +628,7 @@ class LightMapPack(Operator):
name="New Image",
description=(
"Assign new images for every mesh (only one if "
- "shared tex space enabled)"
+ "Share Texture Space is enabled)"
),
default=False,
)
diff --git a/release/scripts/startup/bl_operators/view3d.py b/release/scripts/startup/bl_operators/view3d.py
index ff5bcdb034f..0fa82e36d20 100644
--- a/release/scripts/startup/bl_operators/view3d.py
+++ b/release/scripts/startup/bl_operators/view3d.py
@@ -208,7 +208,8 @@ class VIEW3D_OT_transform_gizmo_set(Operator):
@classmethod
def poll(cls, context):
- return context.area.type == 'VIEW_3D'
+ area = context.area
+ return area and (area.type == 'VIEW_3D')
def execute(self, context):
space_data = context.space_data
diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py
index 2f97942faa4..2cc7b828c11 100644
--- a/release/scripts/startup/bl_operators/wm.py
+++ b/release/scripts/startup/bl_operators/wm.py
@@ -95,6 +95,76 @@ def context_path_validate(context, data_path):
return value
+def context_path_to_rna_property(context, data_path):
+ from bl_rna_utils.data_path import property_definition_from_data_path
+ rna_prop = property_definition_from_data_path(context, "." + data_path)
+ if rna_prop is not None:
+ return rna_prop
+ return None
+
+
+def context_path_decompose(data_path):
+ # Decompose a data_path into 3 components:
+ # base_path, prop_attr, prop_item, where:
+ # `"foo.bar["baz"].fiz().bob.buz[10][2]"`, returns...
+ # `("foo.bar["baz"].fiz().bob", "buz", "[10][2]")`
+ #
+ # This is useful as we often want the base and the property, ignoring any item access.
+ # Note that item access includes function calls since these aren't properties.
+ #
+ # Note that the `.` is removed from the start of the first and second values,
+ # this is done because `.attr` isn't convenient to use as an argument,
+ # also the convention is not to include this within the data paths or the operator logic for `bpy.ops.wm.*`.
+ from bl_rna_utils.data_path import decompose_data_path
+ path_split = decompose_data_path("." + data_path)
+
+ # Find the last property that isn't a function call.
+ value_prev = ""
+ i = len(path_split)
+ while (i := i - 1) >= 0:
+ value = path_split[i]
+ if value.startswith("."):
+ if not value_prev.startswith("("):
+ break
+ value_prev = value
+
+ if i != -1:
+ base_path = "".join(path_split[:i])
+ prop_attr = path_split[i]
+ prop_item = "".join(path_split[i + 1:])
+
+ if base_path:
+ assert(base_path.startswith("."))
+ base_path= base_path[1:]
+ if prop_attr:
+ assert(prop_attr.startswith("."))
+ prop_attr = prop_attr[1:]
+ else:
+ # If there are no properties, everything is an item.
+ # Note that should not happen in practice with values which are added onto `context`,
+ # include since it's correct to account for this case and not doing so will create a confusing exception.
+ base_path = ""
+ prop_attr = ""
+ prop_item = "".join(path_split)
+
+ return (base_path, prop_attr, prop_item)
+
+
+def description_from_data_path(base, data_path, *, prefix, value=Ellipsis):
+ if context_path_validate(base, data_path) is Ellipsis:
+ return None
+
+ if (
+ (rna_prop := context_path_to_rna_property(base, data_path)) and
+ (description := rna_prop.description)
+ ):
+ description = "%s: %s" % (prefix, description)
+ if value != Ellipsis:
+ description = "%s\n%s: %s" % (description, iface_("Value"), str(value))
+ return description
+ return None
+
+
def operator_value_is_undo(value):
if value in {None, Ellipsis}:
return False
@@ -120,12 +190,9 @@ def operator_value_is_undo(value):
def operator_path_is_undo(context, data_path):
- # note that if we have data paths that use strings this could fail
- # luckily we don't do this!
- #
- # When we can't find the data owner assume no undo is needed.
- data_path_head = data_path.rpartition(".")[0]
+ data_path_head, _, _ = context_path_decompose(data_path)
+ # When we can't find the data owner assume no undo is needed.
if not data_path_head:
return False
@@ -168,6 +235,10 @@ class WM_OT_context_set_boolean(Operator):
default=True,
)
+ @classmethod
+ def description(cls, context, props):
+ return description_from_data_path(context, props.data_path, prefix=iface_("Assign"), value=props.value)
+
execute = execute_context_assign
@@ -185,6 +256,10 @@ class WM_OT_context_set_int(Operator): # same as enum
)
relative: rna_relative_prop
+ @classmethod
+ def description(cls, context, props):
+ return description_from_data_path(context, props.data_path, prefix="Assign", value=props.value)
+
execute = execute_context_assign
@@ -201,6 +276,10 @@ class WM_OT_context_scale_float(Operator):
default=1.0,
)
+ @classmethod
+ def description(cls, context, props):
+ return description_from_data_path(context, props.data_path, prefix=iface_("Scale"), value=props.value)
+
def execute(self, context):
data_path = self.data_path
if context_path_validate(context, data_path) is Ellipsis:
@@ -235,6 +314,10 @@ class WM_OT_context_scale_int(Operator):
options={'SKIP_SAVE'},
)
+ @classmethod
+ def description(cls, context, props):
+ return description_from_data_path(context, props.data_path, prefix=iface_("Scale"), value=props.value)
+
def execute(self, context):
data_path = self.data_path
if context_path_validate(context, data_path) is Ellipsis:
@@ -274,6 +357,10 @@ class WM_OT_context_set_float(Operator): # same as enum
)
relative: rna_relative_prop
+ @classmethod
+ def description(cls, context, props):
+ return description_from_data_path(context, props.data_path, prefix="Assign", value=props.value)
+
execute = execute_context_assign
@@ -290,6 +377,10 @@ class WM_OT_context_set_string(Operator): # same as enum
maxlen=1024,
)
+ @classmethod
+ def description(cls, context, props):
+ return description_from_data_path(context, props.data_path, prefix=iface_("Assign"), value=props.value)
+
execute = execute_context_assign
@@ -306,6 +397,10 @@ class WM_OT_context_set_enum(Operator):
maxlen=1024,
)
+ @classmethod
+ def description(cls, context, props):
+ return description_from_data_path(context, props.data_path, prefix=iface_("Assign"), value=props.value)
+
execute = execute_context_assign
@@ -322,6 +417,10 @@ class WM_OT_context_set_value(Operator):
maxlen=1024,
)
+ @classmethod
+ def description(cls, context, props):
+ return description_from_data_path(context, props.data_path, prefix=iface_("Assign"), value=props.value)
+
def execute(self, context):
data_path = self.data_path
if context_path_validate(context, data_path) is Ellipsis:
@@ -339,6 +438,13 @@ class WM_OT_context_toggle(Operator):
data_path: rna_path_prop
module: rna_module_prop
+ @classmethod
+ def description(cls, context, props):
+ # Currently unsupported, it might be possible to extract this.
+ if props.module:
+ return None
+ return description_from_data_path(context, props.data_path, prefix=iface_("Toggle"))
+
def execute(self, context):
data_path = self.data_path
@@ -375,6 +481,11 @@ class WM_OT_context_toggle_enum(Operator):
maxlen=1024,
)
+ @classmethod
+ def description(cls, context, props):
+ value = "(%r, %r)" % (props.value_1, props.value_2)
+ return description_from_data_path(context, props.data_path, prefix=iface_("Toggle"), value=value)
+
def execute(self, context):
data_path = self.data_path
@@ -406,6 +517,10 @@ class WM_OT_context_cycle_int(Operator):
reverse: rna_reverse_prop
wrap: rna_wrap_prop
+ @classmethod
+ def description(cls, context, props):
+ return description_from_data_path(context, props.data_path, prefix=iface_("Cycle"))
+
def execute(self, context):
data_path = self.data_path
value = context_path_validate(context, data_path)
@@ -442,6 +557,10 @@ class WM_OT_context_cycle_enum(Operator):
reverse: rna_reverse_prop
wrap: rna_wrap_prop
+ @classmethod
+ def description(cls, context, props):
+ return description_from_data_path(context, props.data_path, prefix=iface_("Cycle"))
+
def execute(self, context):
data_path = self.data_path
value = context_path_validate(context, data_path)
@@ -450,22 +569,11 @@ class WM_OT_context_cycle_enum(Operator):
orig_value = value
- # Have to get rna enum values
- rna_struct_str, rna_prop_str = data_path.rsplit('.', 1)
- i = rna_prop_str.find('[')
-
- # just in case we get "context.foo.bar[0]"
- if i != -1:
- rna_prop_str = rna_prop_str[0:i]
-
- rna_struct = eval("context.%s.rna_type" % rna_struct_str)
-
- rna_prop = rna_struct.properties[rna_prop_str]
-
+ rna_prop = context_path_to_rna_property(context, data_path)
if type(rna_prop) != bpy.types.EnumProperty:
raise Exception("expected an enum property")
- enums = rna_struct.properties[rna_prop_str].enum_items.keys()
+ enums = rna_prop.enum_items.keys()
orig_index = enums.index(orig_value)
# Have the info we need, advance to the next item.
@@ -498,6 +606,10 @@ class WM_OT_context_cycle_array(Operator):
data_path: rna_path_prop
reverse: rna_reverse_prop
+ @classmethod
+ def description(cls, context, props):
+ return description_from_data_path(context, props.data_path, prefix=iface_("Cycle"))
+
def execute(self, context):
data_path = self.data_path
value = context_path_validate(context, data_path)
@@ -523,6 +635,10 @@ class WM_OT_context_menu_enum(Operator):
data_path: rna_path_prop
+ @classmethod
+ def description(cls, context, props):
+ return description_from_data_path(context, props.data_path, prefix=iface_("Menu"))
+
def execute(self, context):
data_path = self.data_path
value = context_path_validate(context, data_path)
@@ -530,15 +646,15 @@ class WM_OT_context_menu_enum(Operator):
if value is Ellipsis:
return {'PASS_THROUGH'}
- base_path, prop_string = data_path.rsplit(".", 1)
+ base_path, prop_attr, _ = context_path_decompose(data_path)
value_base = context_path_validate(context, base_path)
- prop = value_base.bl_rna.properties[prop_string]
+ rna_prop = context_path_to_rna_property(context, data_path)
def draw_cb(self, context):
layout = self.layout
- layout.prop(value_base, prop_string, expand=True)
+ layout.prop(value_base, prop_attr, expand=True)
- context.window_manager.popup_menu(draw_func=draw_cb, title=prop.name, icon=prop.icon)
+ context.window_manager.popup_menu(draw_func=draw_cb, title=rna_prop.name, icon=rna_prop.icon)
return {'FINISHED'}
@@ -550,6 +666,10 @@ class WM_OT_context_pie_enum(Operator):
data_path: rna_path_prop
+ @classmethod
+ def description(cls, context, props):
+ return description_from_data_path(context, props.data_path, prefix=iface_("Pie Menu"))
+
def invoke(self, context, event):
wm = context.window_manager
data_path = self.data_path
@@ -558,15 +678,15 @@ class WM_OT_context_pie_enum(Operator):
if value is Ellipsis:
return {'PASS_THROUGH'}
- base_path, prop_string = data_path.rsplit(".", 1)
+ base_path, prop_attr, _ = context_path_decompose(data_path)
value_base = context_path_validate(context, base_path)
- prop = value_base.bl_rna.properties[prop_string]
+ rna_prop = context_path_to_rna_property(context, data_path)
def draw_cb(self, context):
layout = self.layout
- layout.prop(value_base, prop_string, expand=True)
+ layout.prop(value_base, prop_attr, expand=True)
- wm.popup_menu_pie(draw_func=draw_cb, title=prop.name, icon=prop.icon, event=event)
+ wm.popup_menu_pie(draw_func=draw_cb, title=rna_prop.name, icon=rna_prop.icon, event=event)
return {'FINISHED'}
@@ -587,11 +707,15 @@ class WM_OT_operator_pie_enum(Operator):
maxlen=1024,
)
+ @classmethod
+ def description(cls, context, props):
+ return description_from_data_path(context, props.data_path, prefix=iface_("Pie Menu"))
+
def invoke(self, context, event):
wm = context.window_manager
data_path = self.data_path
- prop_string = self.prop_string
+ prop_attr = self.prop_string
# same as eval("bpy.ops." + data_path)
op_mod_str, ob_id_str = data_path.split(".", 1)
@@ -607,7 +731,7 @@ class WM_OT_operator_pie_enum(Operator):
def draw_cb(self, context):
layout = self.layout
pie = layout.menu_pie()
- pie.operator_enum(data_path, prop_string)
+ pie.operator_enum(data_path, prop_attr)
wm.popup_menu_pie(draw_func=draw_cb, title=op_rna.name, event=event)
@@ -631,17 +755,17 @@ class WM_OT_context_set_id(Operator):
value = self.value
data_path = self.data_path
- # match the pointer type from the target property to bpy.data.*
+ # Match the pointer type from the target property to `bpy.data.*`
# so we lookup the correct list.
- data_path_base, data_path_prop = data_path.rsplit(".", 1)
- data_prop_rna = eval("context.%s" % data_path_base).rna_type.properties[data_path_prop]
- data_prop_rna_type = data_prop_rna.fixed_type
+
+ rna_prop = context_path_to_rna_property(context, data_path)
+ rna_prop_fixed_type = rna_prop.fixed_type
id_iter = None
for prop in bpy.data.rna_type.properties:
if prop.rna_type.identifier == "CollectionProperty":
- if prop.fixed_type == data_prop_rna_type:
+ if prop.fixed_type == rna_prop_fixed_type:
id_iter = prop.identifier
break
@@ -976,7 +1100,7 @@ class WM_OT_path_open(Operator):
return {'FINISHED'}
-def _wm_doc_get_id(doc_id, do_url=True, url_prefix="", report=None):
+def _wm_doc_get_id(doc_id, *, do_url=True, url_prefix="", report=None):
def operator_exists_pair(a, b):
# Not fast, this is only for docs.
@@ -1066,7 +1190,7 @@ class WM_OT_doc_view_manual(Operator):
doc_id: doc_id
@staticmethod
- def _find_reference(rna_id, url_mapping, verbose=True):
+ def _find_reference(rna_id, url_mapping, *, verbose=True):
if verbose:
print("online manual check for: '%s'... " % rna_id)
from fnmatch import fnmatchcase
@@ -1402,7 +1526,7 @@ class WM_OT_properties_edit(Operator):
self.default = ""
# setup defaults
- prop_ui = rna_idprop_ui_prop_get(item, prop, False) # don't create
+ prop_ui = rna_idprop_ui_prop_get(item, prop, create=False)
if prop_ui:
self.min = prop_ui.get("min", -1000000000)
self.max = prop_ui.get("max", 1000000000)
@@ -1786,7 +1910,7 @@ class WM_OT_toolbar(Operator):
return context.space_data is not None
@staticmethod
- def keymap_from_toolbar(context, space_type, use_fallback_keys=True, use_reset=True):
+ def keymap_from_toolbar(context, space_type, *, use_fallback_keys=True, use_reset=True):
from bl_ui.space_toolsystem_common import ToolSelectPanelHelper
from bl_keymap_utils import keymap_from_toolbar
@@ -2087,7 +2211,7 @@ class WM_OT_batch_rename(Operator):
actions: CollectionProperty(type=BatchRenameAction)
@staticmethod
- def _data_from_context(context, data_type, only_selected, check_context=False):
+ def _data_from_context(context, data_type, only_selected, *, check_context=False):
mode = context.mode
scene = context.scene
diff --git a/release/scripts/startup/bl_ui/properties_collection.py b/release/scripts/startup/bl_ui/properties_collection.py
index 186314f9591..c5c35121135 100644
--- a/release/scripts/startup/bl_ui/properties_collection.py
+++ b/release/scripts/startup/bl_ui/properties_collection.py
@@ -77,6 +77,7 @@ class COLLECTION_PT_instancing(CollectionButtonsPanel, Panel):
class COLLECTION_PT_lineart_collection(CollectionButtonsPanel, Panel):
bl_label = "Line Art"
+ bl_order = 10
def draw(self, context):
layout = self.layout
diff --git a/release/scripts/startup/bl_ui/properties_constraint.py b/release/scripts/startup/bl_ui/properties_constraint.py
index e835e577953..a88def34767 100644
--- a/release/scripts/startup/bl_ui/properties_constraint.py
+++ b/release/scripts/startup/bl_ui/properties_constraint.py
@@ -245,6 +245,7 @@ class ConstraintButtonsPanel:
sub.prop(con, "max_z", text="Max")
row.label(icon='BLANK1')
+ layout.prop(con, "euler_order", text="Order")
layout.prop(con, "use_transform_limit")
self.space_template(layout, con, target=False, owner=True)
diff --git a/release/scripts/startup/bl_ui/properties_data_armature.py b/release/scripts/startup/bl_ui/properties_data_armature.py
index 4cdcab45926..87572fcd438 100644
--- a/release/scripts/startup/bl_ui/properties_data_armature.py
+++ b/release/scripts/startup/bl_ui/properties_data_armature.py
@@ -86,12 +86,19 @@ class DATA_PT_display(ArmatureButtonsPanel, Panel):
col = layout.column(heading="Show")
col.prop(arm, "show_names", text="Names")
- col.prop(arm, "show_axes", text="Axes")
col.prop(arm, "show_bone_custom_shapes", text="Shapes")
col.prop(arm, "show_group_colors", text="Group Colors")
+
if ob:
col.prop(ob, "show_in_front", text="In Front")
+ col = layout.column(align=False, heading="Axes")
+ row = col.row(align=True)
+ row.prop(arm, "show_axes", text="")
+ sub = row.row(align=True)
+ sub.active = arm.show_axes
+ sub.prop(arm, "axes_position", text="Position")
+
class DATA_MT_bone_group_context_menu(Menu):
bl_label = "Bone Group Specials"
diff --git a/release/scripts/startup/bl_ui/properties_data_bone.py b/release/scripts/startup/bl_ui/properties_data_bone.py
index f3e116ca321..6452ad8465b 100644
--- a/release/scripts/startup/bl_ui/properties_data_bone.py
+++ b/release/scripts/startup/bl_ui/properties_data_bone.py
@@ -292,10 +292,15 @@ class BONE_PT_display_custom_shape(BoneButtonsPanel, Panel):
sub = col.column()
sub.active = bool(pchan and pchan.custom_shape)
sub.separator()
- sub.prop(pchan, "custom_shape_scale", text="Scale")
+
+ sub.prop(pchan, "custom_shape_scale_xyz", text="Scale")
+ sub.prop(pchan, "custom_shape_translation", text="Translation")
+ sub.prop(pchan, "custom_shape_rotation_euler", text="Rotation")
+
sub.prop_search(pchan, "custom_shape_transform",
ob.pose, "bones", text="Override Transform")
sub.prop(pchan, "use_custom_shape_bone_size")
+
sub.separator()
sub.prop(bone, "show_wire", text="Wireframe")
diff --git a/release/scripts/startup/bl_ui/properties_data_curve.py b/release/scripts/startup/bl_ui/properties_data_curve.py
index 4bd2d66e257..85f672cd50f 100644
--- a/release/scripts/startup/bl_ui/properties_data_curve.py
+++ b/release/scripts/startup/bl_ui/properties_data_curve.py
@@ -267,6 +267,7 @@ class DATA_PT_pathanim(CurveButtonsPanelCurve, Panel):
# these are for paths only
col.separator()
+ col.prop(curve, "use_path_clamp")
col.prop(curve, "use_path_follow")
diff --git a/release/scripts/startup/bl_ui/properties_data_gpencil.py b/release/scripts/startup/bl_ui/properties_data_gpencil.py
index 69720a6c54b..e71ea2f31a4 100644
--- a/release/scripts/startup/bl_ui/properties_data_gpencil.py
+++ b/release/scripts/startup/bl_ui/properties_data_gpencil.py
@@ -113,7 +113,8 @@ class GPENCIL_MT_layer_context_menu(Menu):
layout.operator("gpencil.layer_merge", icon='SORT_ASC', text="Merge Down")
layout.separator()
- layout.menu("VIEW3D_MT_gpencil_copy_layer")
+ layout.operator("gpencil.layer_duplicate_object", text="Copy Layer to Selected").only_active=True
+ layout.operator("gpencil.layer_duplicate_object", text="Copy All Layers to Selected").only_active=False
class DATA_PT_gpencil_layers(DataButtonsPanel, Panel):
diff --git a/release/scripts/startup/bl_ui/properties_data_pointcloud.py b/release/scripts/startup/bl_ui/properties_data_pointcloud.py
index f0584c81c0c..3b7e7a42fd4 100644
--- a/release/scripts/startup/bl_ui/properties_data_pointcloud.py
+++ b/release/scripts/startup/bl_ui/properties_data_pointcloud.py
@@ -71,10 +71,10 @@ class POINTCLOUD_MT_add_attribute(Menu):
layout = self.layout
pointcloud = context.pointcloud
- self.add_standard_attribute(layout, pointcloud, 'Radius', 'FLOAT', 'POINT')
- self.add_standard_attribute(layout, pointcloud, 'Color', 'FLOAT_COLOR', 'POINT')
- self.add_standard_attribute(layout, pointcloud, 'Particle ID', 'INT', 'POINT')
- self.add_standard_attribute(layout, pointcloud, 'Velocity', 'FLOAT_VECTOR', 'POINT')
+ self.add_standard_attribute(layout, pointcloud, 'radius', 'FLOAT', 'POINT')
+ self.add_standard_attribute(layout, pointcloud, 'color', 'FLOAT_COLOR', 'POINT')
+ self.add_standard_attribute(layout, pointcloud, 'id', 'INT', 'POINT')
+ self.add_standard_attribute(layout, pointcloud, 'velocity', 'FLOAT_VECTOR', 'POINT')
layout.separator()
diff --git a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py
index c23cc838e51..0da0716e850 100644
--- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py
+++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py
@@ -147,8 +147,7 @@ class GreasePencilDisplayPanel:
if self.is_popover:
row = layout.row(align=True)
- row.prop(settings, "show_brush", text="")
- row.label(text="Display Cursor")
+ row.prop(settings, "show_brush", text="Display Cursor")
col = layout.column(align=True)
col.active = settings.show_brush
@@ -180,21 +179,6 @@ class GreasePencilBrushFalloff:
bl_label = "Falloff"
bl_options = {'DEFAULT_CLOSED'}
- @classmethod
- def poll(cls, context):
- ts = context.tool_settings
- settings = None
- if context.mode == 'PAINT_GPENCIL':
- settings = ts.gpencil_paint
- if context.mode == 'SCULPT_GPENCIL':
- settings = ts.gpencil_sculpt_paint
- elif context.mode == 'WEIGHT_GPENCIL':
- settings = ts.gpencil_weight_paint
- elif context.mode == 'VERTEX_GPENCIL':
- settings = ts.gpencil_vertex_paint
-
- return (settings and settings.brush and settings.brush.curve)
-
def draw(self, context):
layout = self.layout
ts = context.tool_settings
@@ -408,7 +392,7 @@ class AnnotationDataPanel:
bl_options = {'DEFAULT_CLOSED'}
def draw_header(self, context):
- if context.space_data.type not in {'VIEW_3D', 'TOPBAR'}:
+ if context.space_data.type not in {'VIEW_3D', 'TOPBAR', 'SEQUENCE_EDITOR'}:
self.layout.prop(context.space_data, "show_annotation", text="")
def draw(self, context):
@@ -822,6 +806,12 @@ class GreasePencilLayerMasksPanel:
col2.menu("GPENCIL_MT_layer_mask_menu", icon='ADD', text="")
col2.operator("gpencil.layer_mask_remove", icon='REMOVE', text="")
+ col2.separator()
+
+ sub = col2.column(align=True)
+ sub.operator("gpencil.layer_mask_move", icon='TRIA_UP', text="").type = 'UP'
+ sub.operator("gpencil.layer_mask_move", icon='TRIA_DOWN', text="").type = 'DOWN'
+
class GreasePencilLayerRelationsPanel:
@@ -852,6 +842,10 @@ class GreasePencilLayerRelationsPanel:
col = layout.row(align=True)
col.prop_search(gpl, "viewlayer_render", scene, "view_layers", text="View Layer")
+ col = layout.row(align=True)
+ # Only enable this property when a view layer is selected.
+ col.enabled = bool(gpl.viewlayer_render)
+ col.prop(gpl, "use_viewlayer_masks")
class GreasePencilLayerDisplayPanel:
@@ -882,37 +876,43 @@ class GreasePencilFlipTintColors(Operator):
bl_idname = "gpencil.tint_flip"
bl_description = "Switch tint colors"
- def execute(self, context):
- try:
- ts = context.tool_settings
- settings = None
- if context.mode == 'PAINT_GPENCIL':
- settings = ts.gpencil_paint
- if context.mode == 'SCULPT_GPENCIL':
- settings = ts.gpencil_sculpt_paint
- elif context.mode == 'WEIGHT_GPENCIL':
- settings = ts.gpencil_weight_paint
- elif context.mode == 'VERTEX_GPENCIL':
- settings = ts.gpencil_vertex_paint
-
- brush = settings.brush
- if brush is not None:
- color = brush.color
- secondary_color = brush.secondary_color
+ @classmethod
+ def poll(cls, context):
+ ts = context.tool_settings
+ settings = None
+ if context.mode == 'PAINT_GPENCIL':
+ settings = ts.gpencil_paint
+ if context.mode == 'SCULPT_GPENCIL':
+ settings = ts.gpencil_sculpt_paint
+ elif context.mode == 'WEIGHT_GPENCIL':
+ settings = ts.gpencil_weight_paint
+ elif context.mode == 'VERTEX_GPENCIL':
+ settings = ts.gpencil_vertex_paint
- orig_prim = color.hsv
- orig_sec = secondary_color.hsv
+ return settings and settings.brush
- color.hsv = orig_sec
- secondary_color.hsv = orig_prim
+ def execute(self, context):
+ ts = context.tool_settings
+ settings = None
+ if context.mode == 'PAINT_GPENCIL':
+ settings = ts.gpencil_paint
+ if context.mode == 'SCULPT_GPENCIL':
+ settings = ts.gpencil_sculpt_paint
+ elif context.mode == 'WEIGHT_GPENCIL':
+ settings = ts.gpencil_weight_paint
+ elif context.mode == 'VERTEX_GPENCIL':
+ settings = ts.gpencil_vertex_paint
- return {'FINISHED'}
+ brush = settings.brush
+ color = brush.color
+ secondary_color = brush.secondary_color
- except Exception as e:
- utils_core.error_handlers(self, "gpencil.tint_flip", e,
- "Flip Colors could not be completed")
+ orig_prim = color.hsv
+ orig_sec = secondary_color.hsv
- return {'CANCELLED'}
+ color.hsv = orig_sec
+ secondary_color.hsv = orig_prim
+ return {'FINISHED'}
classes = (
diff --git a/release/scripts/startup/bl_ui/properties_material.py b/release/scripts/startup/bl_ui/properties_material.py
index d85078d4ec2..217ec248764 100644
--- a/release/scripts/startup/bl_ui/properties_material.py
+++ b/release/scripts/startup/bl_ui/properties_material.py
@@ -277,6 +277,7 @@ class MATERIAL_PT_viewport(MaterialButtonsPanel, Panel):
class MATERIAL_PT_lineart(MaterialButtonsPanel, Panel):
bl_label = "Line Art"
bl_options = {'DEFAULT_CLOSED'}
+ bl_order = 10
@classmethod
def poll(cls, context):
@@ -285,19 +286,17 @@ class MATERIAL_PT_lineart(MaterialButtonsPanel, Panel):
def draw(self, context):
layout = self.layout
+ layout.use_property_split = True
mat = context.material
lineart = mat.lineart
layout.prop(lineart, "use_transparency")
- if lineart.use_transparency:
-
- layout.label(text="Transparency Masks:")
-
- row = layout.row(align=True)
- for i in range(8):
- row.prop(lineart, "use_transparency_mask", text=str(i), index=i, toggle=True)
+ row = layout.row(align=True, heading="Masks")
+ row.active = lineart.use_transparency
+ for i in range(8):
+ row.prop(lineart, "use_transparency_mask", text=str(i), index=i, toggle=True)
classes = (
diff --git a/release/scripts/startup/bl_ui/properties_material_gpencil.py b/release/scripts/startup/bl_ui/properties_material_gpencil.py
index 6a5c000116f..9d099ff2231 100644
--- a/release/scripts/startup/bl_ui/properties_material_gpencil.py
+++ b/release/scripts/startup/bl_ui/properties_material_gpencil.py
@@ -46,13 +46,19 @@ class GPENCIL_MT_material_context_menu(Menu):
layout.separator()
- layout.operator("object.material_slot_remove_unused")
- layout.operator("gpencil.stroke_merge_material", text="Merge Similar")
-
- layout.separator()
layout.operator("gpencil.material_to_vertex_color", text="Convert Materials to Vertex Color")
layout.operator("gpencil.extract_palette_vertex", text="Extract Palette from Vertex Color")
+ layout.separator()
+
+ layout.operator("gpencil.materials_copy_to_object", text="Copy Material to Selected").only_active = True
+ layout.operator("gpencil.materials_copy_to_object", text="Copy All Materials to Selected").only_active = False
+
+ layout.separator()
+
+ layout.operator("gpencil.stroke_merge_material", text="Merge Similar")
+ layout.operator("object.material_slot_remove_unused")
+
class GPENCIL_UL_matslots(UIList):
def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index):
diff --git a/release/scripts/startup/bl_ui/properties_object.py b/release/scripts/startup/bl_ui/properties_object.py
index b74100aa570..4ea1ec26738 100644
--- a/release/scripts/startup/bl_ui/properties_object.py
+++ b/release/scripts/startup/bl_ui/properties_object.py
@@ -311,6 +311,7 @@ class OBJECT_PT_instancing_size(ObjectButtonsPanel, Panel):
class OBJECT_PT_lineart(ObjectButtonsPanel, Panel):
bl_label = "Line Art"
bl_options = {'DEFAULT_CLOSED'}
+ bl_order = 10
@classmethod
def poll(cls, context):
@@ -328,7 +329,9 @@ class OBJECT_PT_lineart(ObjectButtonsPanel, Panel):
row = layout.row(heading="Override Crease")
row.prop(lineart, "use_crease_override", text="")
- row.prop(lineart, "crease_threshold", slider=True, text="")
+ subrow = row.row()
+ subrow.active = lineart.use_crease_override
+ subrow.prop(lineart, "crease_threshold", slider=True, text="")
class OBJECT_PT_motion_paths(MotionPathButtonsPanel, Panel):
diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py
index 6989139d447..4bfd2fd32b0 100644
--- a/release/scripts/startup/bl_ui/properties_paint_common.py
+++ b/release/scripts/startup/bl_ui/properties_paint_common.py
@@ -356,7 +356,7 @@ class StrokePanel(BrushPanel):
col.operator("paintcurve.draw")
col.separator()
- if brush.use_space:
+ if brush.use_space or brush.use_line or brush.use_curve:
col.separator()
row = col.row(align=True)
col.prop(brush, "dash_ratio", text="Dash Ratio")
@@ -1225,7 +1225,7 @@ def brush_basic_gpencil_paint_settings(layout, context, brush, *, compact=False)
row = layout.row(align=True)
row.prop(gp_settings, "fill_factor")
row = layout.row(align=True)
- row.prop(gp_settings, "fill_leak", text="Leak Size")
+ row.prop(gp_settings, "dilate")
row = layout.row(align=True)
row.prop(brush, "size", text="Thickness")
layout.use_property_split = use_property_split_prev
@@ -1235,7 +1235,7 @@ def brush_basic_gpencil_paint_settings(layout, context, brush, *, compact=False)
row.prop(brush, "size", text="Radius")
row.prop(gp_settings, "use_pressure", text="", icon='STYLUS_PRESSURE')
- if gp_settings.use_pressure and context.area.type == 'PROPERTIES':
+ if gp_settings.use_pressure and not compact:
col = layout.column()
col.template_curve_mapping(gp_settings, "curve_sensitivity", brush=True,
use_negative_slope=True)
@@ -1244,7 +1244,7 @@ def brush_basic_gpencil_paint_settings(layout, context, brush, *, compact=False)
row.prop(gp_settings, "pen_strength", slider=True)
row.prop(gp_settings, "use_strength_pressure", text="", icon='STYLUS_PRESSURE')
- if gp_settings.use_strength_pressure and context.area.type == 'PROPERTIES':
+ if gp_settings.use_strength_pressure and not compact:
col = layout.column()
col.template_curve_mapping(gp_settings, "curve_strength", brush=True,
use_negative_slope=True)
diff --git a/release/scripts/startup/bl_ui/properties_particle.py b/release/scripts/startup/bl_ui/properties_particle.py
index a9f040db9b5..ae15fda2c69 100644
--- a/release/scripts/startup/bl_ui/properties_particle.py
+++ b/release/scripts/startup/bl_ui/properties_particle.py
@@ -1803,9 +1803,10 @@ class PARTICLE_PT_force_fields_type1(ParticleButtonsPanel, Panel):
part = particle_get_settings(context)
- col = layout.column()
- col.prop(part.force_field_1, "type", text="Type 1")
- basic_force_field_settings_ui(self, part.force_field_1)
+ if part.force_field_1:
+ col = layout.column()
+ col.prop(part.force_field_1, "type", text="Type 1")
+ basic_force_field_settings_ui(self, part.force_field_1)
class PARTICLE_PT_force_fields_type2(ParticleButtonsPanel, Panel):
@@ -1819,9 +1820,10 @@ class PARTICLE_PT_force_fields_type2(ParticleButtonsPanel, Panel):
part = particle_get_settings(context)
- col = layout.column()
- col.prop(part.force_field_2, "type", text="Type 2")
- basic_force_field_settings_ui(self, part.force_field_2)
+ if part.force_field_2:
+ col = layout.column()
+ col.prop(part.force_field_2, "type", text="Type 2")
+ basic_force_field_settings_ui(self, part.force_field_2)
class PARTICLE_PT_force_fields_type1_falloff(ParticleButtonsPanel, Panel):
@@ -1836,7 +1838,8 @@ class PARTICLE_PT_force_fields_type1_falloff(ParticleButtonsPanel, Panel):
part = particle_get_settings(context)
- basic_force_field_falloff_ui(self, part.force_field_1)
+ if part.force_field_1:
+ basic_force_field_falloff_ui(self, part.force_field_1)
class PARTICLE_PT_force_fields_type2_falloff(ParticleButtonsPanel, Panel):
@@ -1851,7 +1854,8 @@ class PARTICLE_PT_force_fields_type2_falloff(ParticleButtonsPanel, Panel):
part = particle_get_settings(context)
- basic_force_field_falloff_ui(self, part.force_field_2)
+ if part.force_field_2:
+ basic_force_field_falloff_ui(self, part.force_field_2)
class PARTICLE_PT_vertexgroups(ParticleButtonsPanel, Panel):
diff --git a/release/scripts/startup/bl_ui/properties_physics_common.py b/release/scripts/startup/bl_ui/properties_physics_common.py
index 82f43790d72..f13a808e324 100644
--- a/release/scripts/startup/bl_ui/properties_physics_common.py
+++ b/release/scripts/startup/bl_ui/properties_physics_common.py
@@ -38,13 +38,12 @@ class PhysicButtonsPanel:
def physics_add(layout, md, name, type, typeicon, toggles):
row = layout.row(align=True)
if md:
- row.context_pointer_set("modifier", md)
row.operator(
"object.modifier_remove",
text=name,
text_ctxt=i18n_contexts.default,
icon='X',
- )
+ ).modifier = md.name
if toggles:
row.prop(md, "show_viewport", text="")
row.prop(md, "show_render", text="")
@@ -74,17 +73,13 @@ class PHYSICS_PT_add(PhysicButtonsPanel, Panel):
def draw(self, context):
layout = self.layout
- row = layout.row(align=True)
- row.alignment = 'LEFT'
- row.label(text="Enable physics for:")
-
flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
obj = context.object
col = flow.column()
- if obj.field.type == 'NONE':
+ if not obj.field or obj.field.type == 'NONE':
col.operator("object.forcefield_toggle", text="Force Field", icon='FORCE_FORCE')
else:
col.operator("object.forcefield_toggle", text="Force Field", icon='X')
diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py
index b236b2ee7cf..1ad88744b16 100644
--- a/release/scripts/startup/bl_ui/space_filebrowser.py
+++ b/release/scripts/startup/bl_ui/space_filebrowser.py
@@ -44,6 +44,10 @@ class FILEBROWSER_HT_header(Header):
layout.separator_spacer()
+ layout.prop(params, "import_type", text="")
+
+ layout.separator_spacer()
+
# Uses prop_with_popover() as popover() only adds the triangle icon in headers.
layout.prop_with_popover(
params,
diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py
index e7589709130..11eaf55a98b 100644
--- a/release/scripts/startup/bl_ui/space_image.py
+++ b/release/scripts/startup/bl_ui/space_image.py
@@ -246,11 +246,12 @@ class IMAGE_MT_image(Menu):
layout.separator()
layout.operator("image.pack", text="Pack")
- if ima:
+ if ima and context.area.ui_type == 'IMAGE_EDITOR':
layout.separator()
layout.operator("palette.extract_from_image", text="Extract Palette")
layout.operator("gpencil.image_to_grease_pencil", text="Generate Grease Pencil")
+
class IMAGE_MT_image_flip(Menu):
bl_label = "Flip"
diff --git a/release/scripts/startup/bl_ui/space_info.py b/release/scripts/startup/bl_ui/space_info.py
index cd65980fc0d..3a97b104271 100644
--- a/release/scripts/startup/bl_ui/space_info.py
+++ b/release/scripts/startup/bl_ui/space_info.py
@@ -92,16 +92,15 @@ class INFO_MT_area(Menu):
layout.separator()
- layout.operator("screen.area_dupli", icon='WINDOW')
-
- layout.separator()
-
layout.operator("screen.screen_full_area")
layout.operator(
"screen.screen_full_area",
- text="Toggle Fullscreen Area",
- icon='FULLSCREEN_ENTER',
- ).use_hide_panels = True
+ text="Toggle Fullscreen Area").use_hide_panels = True
+ layout.operator("screen.area_dupli")
+
+ layout.separator()
+
+ layout.operator("screen.area_close")
class INFO_MT_context_menu(Menu):
diff --git a/release/scripts/startup/bl_ui/space_node.py b/release/scripts/startup/bl_ui/space_node.py
index 11685b3e7e0..c668d3908b3 100644
--- a/release/scripts/startup/bl_ui/space_node.py
+++ b/release/scripts/startup/bl_ui/space_node.py
@@ -18,7 +18,6 @@
# <pep8 compliant>
import bpy
-import nodeitems_utils
from bpy.types import Header, Menu, Panel
from bpy.app.translations import pgettext_iface as iface_
from bpy.app.translations import contexts as i18n_contexts
@@ -225,6 +224,8 @@ class NODE_MT_add(bpy.types.Menu):
bl_translation_context = i18n_contexts.operator_default
def draw(self, context):
+ import nodeitems_utils
+
layout = self.layout
layout.operator_context = 'INVOKE_DEFAULT'
@@ -644,8 +645,12 @@ class NODE_PT_quality(bpy.types.Panel):
snode = context.space_data
tree = snode.node_tree
+ prefs = bpy.context.preferences
col = layout.column()
+ if prefs.experimental.use_full_frame_compositor:
+ col.prop(tree, "execution_mode")
+
col.prop(tree, "render_quality", text="Render")
col.prop(tree, "edit_quality", text="Edit")
col.prop(tree, "chunk_size")
diff --git a/release/scripts/startup/bl_ui/space_outliner.py b/release/scripts/startup/bl_ui/space_outliner.py
index 5c5a78f3942..6eafa570f4c 100644
--- a/release/scripts/startup/bl_ui/space_outliner.py
+++ b/release/scripts/startup/bl_ui/space_outliner.py
@@ -51,13 +51,13 @@ class OUTLINER_HT_header(Header):
row.prop(space, "use_sync_select", icon='UV_SYNC_SELECT', text="")
row = layout.row(align=True)
- if display_mode in {'SCENES', 'VIEW_LAYER'}:
+ if display_mode in {'SCENES', 'VIEW_LAYER', 'LIBRARY_OVERRIDES'}:
row.popover(
panel="OUTLINER_PT_filter",
text="",
icon='FILTER',
)
- elif display_mode in {'LIBRARIES', 'ORPHAN_DATA'}:
+ if display_mode in {'LIBRARIES', 'LIBRARY_OVERRIDES', 'ORPHAN_DATA'}:
row.prop(space, "use_filter_id_type", text="", icon='FILTER')
sub = row.row(align=True)
sub.active = space.use_filter_id_type
@@ -162,13 +162,13 @@ class OUTLINER_MT_collection_view_layer(Menu):
layout.operator("outliner.collection_exclude_set")
layout.operator("outliner.collection_exclude_clear")
+ layout.operator("outliner.collection_holdout_set")
+ layout.operator("outliner.collection_holdout_clear")
+
if context.engine == 'CYCLES':
layout.operator("outliner.collection_indirect_only_set")
layout.operator("outliner.collection_indirect_only_clear")
- layout.operator("outliner.collection_holdout_set")
- layout.operator("outliner.collection_holdout_clear")
-
class OUTLINER_MT_collection_visibility(Menu):
bl_label = "Visibility"
@@ -341,19 +341,26 @@ class OUTLINER_PT_filter(Panel):
col = layout.column(align=True)
col.prop(space, "use_sort_alpha")
- row = layout.row(align=True)
- row.prop(space, "use_sync_select", text="Sync Selection")
+ if display_mode not in {'LIBRARY_OVERRIDES'}:
+ row = layout.row(align=True)
+ row.prop(space, "use_sync_select", text="Sync Selection")
- row = layout.row(align=True)
- row.prop(space, "show_mode_column", text="Show Mode Column")
- layout.separator()
+ row = layout.row(align=True)
+ row.prop(space, "show_mode_column", text="Show Mode Column")
+ layout.separator()
col = layout.column(align=True)
col.label(text="Search")
col.prop(space, "use_filter_complete", text="Exact Match")
col.prop(space, "use_filter_case_sensitive", text="Case Sensitive")
- if display_mode != 'VIEW_LAYER':
+ if display_mode in {'LIBRARY_OVERRIDES'} and bpy.data.libraries:
+ col.separator()
+ row = col.row()
+ row.label(icon='LIBRARY_DATA_OVERRIDE')
+ row.prop(space, "use_filter_lib_override_system", text="System Overrides")
+
+ if display_mode not in {'VIEW_LAYER'}:
return
layout.separator()
@@ -405,10 +412,6 @@ class OUTLINER_PT_filter(Panel):
row = sub.row()
row.label(icon='EMPTY_DATA')
row.prop(space, "use_filter_object_empty", text="Empties")
- row = sub.row()
- if bpy.data.libraries:
- row.label(icon='LIBRARY_DATA_OVERRIDE')
- row.prop(space, "use_filter_lib_override", text="Library Overrides")
if (
bpy.data.curves or
@@ -425,6 +428,16 @@ class OUTLINER_PT_filter(Panel):
row.label(icon='BLANK1')
row.prop(space, "use_filter_object_others", text="Others")
+ if bpy.data.libraries:
+ col.separator()
+ row = col.row()
+ row.label(icon='LIBRARY_DATA_OVERRIDE')
+ row.prop(space, "use_filter_lib_override", text="Library Overrides")
+ row = col.row()
+ row.label(icon='LIBRARY_DATA_OVERRIDE')
+ row.prop(space, "use_filter_lib_override_system", text="System Overrides")
+
+
classes = (
OUTLINER_HT_header,
diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py
index f6a03b4769c..07d9b0ee1f8 100644
--- a/release/scripts/startup/bl_ui/space_sequencer.py
+++ b/release/scripts/startup/bl_ui/space_sequencer.py
@@ -1381,7 +1381,6 @@ class SEQUENCER_PT_source(SequencerButtonsPanel, Panel):
col = layout.column()
col.prop(strip, "filepath", text="")
col.prop(strip.colorspace_settings, "name", text="Color Space")
- col.prop(strip, "mpeg_preseek")
col.prop(strip, "stream_index")
col.prop(strip, "use_deinterlace")
@@ -1398,8 +1397,8 @@ class SEQUENCER_PT_source(SequencerButtonsPanel, Panel):
box.template_image_stereo_3d(strip.stereo_3d_format)
# Resolution.
- col = layout.column(align=True)
- col = col.box()
+ col = layout.box()
+ col = col.column(align=True)
split = col.split(factor=0.5, align=False)
split.alignment = 'RIGHT'
split.label(text="Resolution")
@@ -1409,6 +1408,14 @@ class SEQUENCER_PT_source(SequencerButtonsPanel, Panel):
split.label(text="%dx%d" % size, translate=False)
else:
split.label(text="None")
+ #FPS
+ if elem.orig_fps:
+ split = col.split(factor=0.5, align=False)
+ split.alignment = 'RIGHT'
+ split.label(text="FPS")
+ split.alignment = 'LEFT'
+ split.label(text="%.2f" % elem.orig_fps, translate=False)
+
class SEQUENCER_PT_scene(SequencerButtonsPanel, Panel):
@@ -1453,7 +1460,7 @@ class SEQUENCER_PT_scene(SequencerButtonsPanel, Panel):
if strip.scene_input == 'CAMERA':
layout = layout.column(heading="Show")
- layout.prop(strip, "use_grease_pencil", text="Grease Pencil")
+ layout.prop(strip, "use_annotations", text="Annotations")
if scene:
# Warning, this is not a good convention to follow.
# Expose here because setting the alpha from the 'Render' menu is very inconvenient.
@@ -1938,7 +1945,7 @@ class SEQUENCER_PT_strip_proxy(SequencerButtonsPanel, Panel):
layout.prop(proxy, "use_overwrite")
col = layout.column()
- col.prop(proxy, "quality", text="Build JPEG Quality")
+ col.prop(proxy, "quality", text="Quality")
if strip.type == 'MOVIE':
col = layout.column()
diff --git a/release/scripts/startup/bl_ui/space_spreadsheet.py b/release/scripts/startup/bl_ui/space_spreadsheet.py
index 188eddbcce3..13e435a7350 100644
--- a/release/scripts/startup/bl_ui/space_spreadsheet.py
+++ b/release/scripts/startup/bl_ui/space_spreadsheet.py
@@ -28,8 +28,17 @@ class SPREADSHEET_HT_header(bpy.types.Header):
layout.template_header()
- pinned_id = space.pinned_id
- used_id = pinned_id if pinned_id else context.active_object
+ if len(space.context_path) == 0:
+ self.draw_without_context_path(layout)
+ return
+ root_context = space.context_path[0]
+ if root_context.type != 'OBJECT':
+ self.draw_without_context_path(layout)
+ return
+ obj = root_context.object
+ if obj is None:
+ self.draw_without_context_path(layout)
+ return
layout.prop(space, "object_eval_state", text="")
if space.object_eval_state != 'ORIGINAL':
@@ -37,16 +46,61 @@ class SPREADSHEET_HT_header(bpy.types.Header):
if space.geometry_component_type != 'INSTANCES':
layout.prop(space, "attribute_domain", text="")
- if used_id:
- layout.label(text=used_id.name, icon='OBJECT_DATA')
+ context_path = space.context_path
+ if space.object_eval_state == 'ORIGINAL':
+ # Only show first context.
+ context_path = context_path[:1]
+ if space.display_context_path_collapsed:
+ self.draw_collapsed_context_path(context, layout, context_path)
+ else:
+ self.draw_full_context_path(context, layout, context_path)
- layout.operator("spreadsheet.toggle_pin", text="", icon='PINNED' if pinned_id else 'UNPINNED', emboss=False)
+ pin_icon = 'PINNED' if space.is_pinned else 'UNPINNED'
+ layout.operator("spreadsheet.toggle_pin", text="", icon=pin_icon, emboss=False)
layout.separator_spacer()
- if isinstance(used_id, bpy.types.Object) and used_id.mode == 'EDIT':
+ if isinstance(obj, bpy.types.Object) and obj.mode == 'EDIT':
layout.prop(space, "show_only_selected", text="Selected Only")
+ def draw_without_context_path(self, layout):
+ layout.label(text="No active context")
+
+ def draw_full_context_path(self, context, layout, context_path):
+ space = context.space_data
+ row = layout.row()
+ for ctx in context_path[:-1]:
+ subrow = row.row(align=True)
+ self.draw_spreadsheet_context(subrow, ctx)
+ self.draw_spreadsheet_context_path_icon(subrow, space)
+
+ self.draw_spreadsheet_context(row, context_path[-1])
+
+ def draw_collapsed_context_path(self, context, layout, context_path):
+ space = context.space_data
+ row = layout.row(align=True)
+ self.draw_spreadsheet_context(row, context_path[0])
+ if len(context_path) == 1:
+ return
+ self.draw_spreadsheet_context_path_icon(row, space)
+ if len(context_path) > 2:
+ self.draw_spreadsheet_context_path_icon(row, space, icon='DOT')
+ self.draw_spreadsheet_context_path_icon(row, space)
+ self.draw_spreadsheet_context(row, context_path[-1])
+
+ def draw_spreadsheet_context(self, layout, ctx):
+ if ctx.type == 'OBJECT':
+ if ctx.object is None:
+ layout.label(text="<no object>", icon='OBJECT_DATA')
+ else:
+ layout.label(text=ctx.object.name, icon='OBJECT_DATA')
+ elif ctx.type == 'MODIFIER':
+ layout.label(text=ctx.modifier_name, icon='MODIFIER')
+ elif ctx.type == 'NODE':
+ layout.label(text=ctx.node_name, icon='NODE')
+
+ def draw_spreadsheet_context_path_icon(self, layout, space, icon='RIGHTARROW_THIN'):
+ layout.prop(space, "display_context_path_collapsed", icon_only=True, emboss=False, icon=icon)
classes = (
SPREADSHEET_HT_header,
diff --git a/release/scripts/startup/bl_ui/space_text.py b/release/scripts/startup/bl_ui/space_text.py
index c937882bd6e..93ab12e8462 100644
--- a/release/scripts/startup/bl_ui/space_text.py
+++ b/release/scripts/startup/bl_ui/space_text.py
@@ -284,7 +284,7 @@ class TEXT_MT_templates_py(Menu):
def draw(self, _context):
self.path_menu(
- bpy.utils.script_paths("templates_py"),
+ bpy.utils.script_paths(subdir="templates_py"),
"text.open",
props_default={"internal": True},
filter_ext=lambda ext: (ext.lower() == ".py")
@@ -296,7 +296,7 @@ class TEXT_MT_templates_osl(Menu):
def draw(self, _context):
self.path_menu(
- bpy.utils.script_paths("templates_osl"),
+ bpy.utils.script_paths(subdir="templates_osl"),
"text.open",
props_default={"internal": True},
filter_ext=lambda ext: (ext.lower() == ".osl")
diff --git a/release/scripts/startup/bl_ui/space_time.py b/release/scripts/startup/bl_ui/space_time.py
index 774e2938deb..30967e9746d 100644
--- a/release/scripts/startup/bl_ui/space_time.py
+++ b/release/scripts/startup/bl_ui/space_time.py
@@ -300,6 +300,8 @@ class TIME_PT_keyframing_settings(TimelinePanelButtons, Panel):
col.label(text="New Keyframe Type")
col.prop(tool_settings, "keyframe_type", text="")
+ layout.prop(tool_settings, "use_keyframe_cycle_aware")
+
class TIME_PT_auto_keyframing(TimelinePanelButtons, Panel):
bl_label = "Auto Keyframing"
@@ -327,8 +329,6 @@ class TIME_PT_auto_keyframing(TimelinePanelButtons, Panel):
if not prefs.edit.use_keyframe_insert_available:
col.prop(tool_settings, "use_record_with_nla", text="Layered Recording")
- col.prop(tool_settings, "use_keyframe_cycle_aware")
-
###################################
diff --git a/release/scripts/startup/bl_ui/space_toolsystem_common.py b/release/scripts/startup/bl_ui/space_toolsystem_common.py
index 12ec863327c..cde430c1e6f 100644
--- a/release/scripts/startup/bl_ui/space_toolsystem_common.py
+++ b/release/scripts/startup/bl_ui/space_toolsystem_common.py
@@ -218,7 +218,7 @@ class ToolSelectPanelHelper:
assert(type(icon_name) is str)
icon_value = _icon_cache.get(icon_name)
if icon_value is None:
- dirname = bpy.utils.system_resource('DATAFILES', "icons")
+ dirname = bpy.utils.system_resource('DATAFILES', path="icons")
filename = os.path.join(dirname, icon_name + ".dat")
try:
icon_value = bpy.app.icons.new_triangles_from_file(filename)
diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
index 1e52142c85c..c55f637f8b2 100644
--- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
@@ -164,7 +164,7 @@ class _defs_annotate:
gpl = context.active_annotation_layer
if gpl is not None:
layout.label(text="Annotation:")
- if context.space_data.type == 'VIEW_3D':
+ if context.space_data.type in {'VIEW_3D', 'SEQUENCE_EDITOR'}:
if region_type == 'TOOL_HEADER':
sub = layout.split(align=True, factor=0.5)
sub.ui_units_x = 6.5
@@ -206,14 +206,22 @@ class _defs_annotate:
col = layout.row().column(align=True)
col.prop(props, "arrowstyle_start", text="Style Start")
col.prop(props, "arrowstyle_end", text="End")
- elif tool.idname == "builtin.annotate" and region_type != 'TOOL_HEADER':
- layout.separator()
+ elif tool.idname == "builtin.annotate":
props = tool.operator_properties("gpencil.annotate")
- layout.prop(props, "use_stabilizer", text="Stabilize Stroke")
- col = layout.column(align=False)
- col.active = props.use_stabilizer
- col.prop(props, "stabilizer_radius", text="Radius", slider=True)
- col.prop(props, "stabilizer_factor", text="Factor", slider=True)
+ if region_type == 'TOOL_HEADER':
+ row = layout.row()
+ row.prop(props, "use_stabilizer", text="Stabilize Stroke")
+ subrow = layout.row(align=False)
+ subrow.active = props.use_stabilizer
+ subrow.prop(props, "stabilizer_radius", text="Radius", slider=True)
+ subrow.prop(props, "stabilizer_factor", text="Factor", slider=True)
+ else:
+ layout.separator()
+ layout.prop(props, "use_stabilizer", text="Stabilize Stroke")
+ col = layout.column(align=False)
+ col.active = props.use_stabilizer
+ col.prop(props, "stabilizer_radius", text="Radius", slider=True)
+ col.prop(props, "stabilizer_factor", text="Factor", slider=True)
@ToolDef.from_fn.with_args(draw_settings=draw_settings_common)
def scribble(*, draw_settings):
@@ -426,7 +434,7 @@ class _defs_view3d_select:
from gpu_extras.presets import draw_circle_2d
props = tool.operator_properties("view3d.select_circle")
radius = props.radius
- draw_circle_2d(xy, (1.0,) * 4, radius, 32)
+ draw_circle_2d(xy, (1.0,) * 4, radius, segments=32)
return dict(
idname="builtin.select_circle",
@@ -1805,7 +1813,7 @@ class _defs_image_uv_select:
from gpu_extras.presets import draw_circle_2d
props = tool.operator_properties("uv.select_circle")
radius = props.radius
- draw_circle_2d(xy, (1.0,) * 4, radius, 32)
+ draw_circle_2d(xy, (1.0,) * 4, radius, segments=32)
return dict(
idname="builtin.select_circle",
@@ -1850,7 +1858,7 @@ class _defs_image_uv_sculpt:
if brush is None:
return
radius = brush.size
- draw_circle_2d(xy, (1.0,) * 4, radius, 32)
+ draw_circle_2d(xy, (1.0,) * 4, radius, segments=32)
return generate_from_enum_ex(
context,
@@ -2142,7 +2150,7 @@ class _defs_gpencil_edit:
from gpu_extras.presets import draw_circle_2d
props = tool.operator_properties("gpencil.select_circle")
radius = props.radius
- draw_circle_2d(xy, (1.0,) * 4, radius, 32)
+ draw_circle_2d(xy, (1.0,) * 4, radius, segments=32)
return dict(
idname="builtin.select_circle",
@@ -2364,7 +2372,7 @@ class _defs_node_select:
from gpu_extras.presets import draw_circle_2d
props = tool.operator_properties("node.select_circle")
radius = props.radius
- draw_circle_2d(xy, (1.0,) * 4, radius, 32)
+ draw_circle_2d(xy, (1.0,) * 4, radius, segments=32)
return dict(
idname="builtin.select_circle",
diff --git a/release/scripts/startup/bl_ui/space_topbar.py b/release/scripts/startup/bl_ui/space_topbar.py
index adab0b0c88a..5e68896a2a7 100644
--- a/release/scripts/startup/bl_ui/space_topbar.py
+++ b/release/scripts/startup/bl_ui/space_topbar.py
@@ -504,8 +504,6 @@ class TOPBAR_MT_file_external_data(Menu):
icon = 'CHECKBOX_HLT' if bpy.data.use_autopack else 'CHECKBOX_DEHLT'
layout.operator("file.autopack_toggle", icon=icon)
- layout.separator()
-
pack_all = layout.row()
pack_all.operator("file.pack_all")
pack_all.active = not bpy.data.use_autopack
@@ -516,8 +514,16 @@ class TOPBAR_MT_file_external_data(Menu):
layout.separator()
+ layout.operator("file.pack_libraries")
+ layout.operator("file.unpack_libraries")
+
+ layout.separator()
+
layout.operator("file.make_paths_relative")
layout.operator("file.make_paths_absolute")
+
+ layout.separator()
+
layout.operator("file.report_missing_files")
layout.operator("file.find_missing_files")
diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py
index 96695ff1be5..26ad22d3ac2 100644
--- a/release/scripts/startup/bl_ui/space_userpref.py
+++ b/release/scripts/startup/bl_ui/space_userpref.py
@@ -570,7 +570,7 @@ class USERPREF_PT_system_sound(SystemPanel, CenterAlignMixIn, Panel):
layout.prop(system, "audio_device", expand=False)
sub = layout.grid_flow(row_major=False, columns=0, even_columns=False, even_rows=False, align=False)
- sub.active = system.audio_device not in {'NONE', 'Null'}
+ sub.active = system.audio_device not in {'NONE', 'None'}
sub.prop(system, "audio_channels", text="Channels")
sub.prop(system, "audio_mixing_buffer", text="Mixing Buffer")
sub.prop(system, "audio_sample_rate", text="Sample Rate")
@@ -1815,7 +1815,7 @@ class USERPREF_PT_addons(AddOnPanel, Panel):
addon_user_dirs = tuple(
p for p in (
os.path.join(prefs.filepaths.script_directory, "addons"),
- bpy.utils.user_resource('SCRIPTS', "addons"),
+ bpy.utils.user_resource('SCRIPTS', path="addons"),
)
if p
)
@@ -2241,9 +2241,9 @@ class USERPREF_PT_experimental_new_features(ExperimentalPanel, Panel):
self._draw_items(
context, (
({"property": "use_sculpt_vertex_colors"}, "T71947"),
- ({"property": "use_switch_object_operator"}, "T80402"),
({"property": "use_sculpt_tools_tilt"}, "T82877"),
({"property": "use_asset_browser"}, ("project/profile/124/", "Milestone 1")),
+ ({"property": "use_override_templates"}, ("T73318", "Milestone 4")),
),
)
@@ -2256,6 +2256,7 @@ class USERPREF_PT_experimental_prototypes(ExperimentalPanel, Panel):
context, (
({"property": "use_new_hair_type"}, "T68981"),
({"property": "use_new_point_cloud_type"}, "T75717"),
+ ({"property": "use_full_frame_compositor"}, "T88150"),
),
)
diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py
index e68f006ccc1..fd56e86ea39 100644
--- a/release/scripts/startup/bl_ui/space_view3d.py
+++ b/release/scripts/startup/bl_ui/space_view3d.py
@@ -161,9 +161,9 @@ class VIEW3D_HT_tool_header(Header):
elif mode_string in {'EDIT_MESH', 'PAINT_WEIGHT', 'SCULPT', 'PAINT_VERTEX', 'PAINT_TEXTURE'}:
# Mesh Modes, Use Mesh Symmetry
row, sub = row_for_mirror()
- sub.prop(context.object.data, "use_mirror_x", text="X", toggle=True)
- sub.prop(context.object.data, "use_mirror_y", text="Y", toggle=True)
- sub.prop(context.object.data, "use_mirror_z", text="Z", toggle=True)
+ sub.prop(context.object, "use_mesh_mirror_x", text="X", toggle=True)
+ sub.prop(context.object, "use_mesh_mirror_y", text="Y", toggle=True)
+ sub.prop(context.object, "use_mesh_mirror_z", text="Z", toggle=True)
if mode_string == 'EDIT_MESH':
tool_settings = context.tool_settings
layout.prop(tool_settings, "use_mesh_automerge", text="")
@@ -938,7 +938,8 @@ class VIEW3D_MT_transform_base:
layout.operator("transform.bend", text="Bend")
layout.operator("transform.push_pull", text="Push/Pull")
- if context.mode != 'OBJECT':
+ if context.mode in {'EDIT_MESH', 'EDIT_ARMATURE', 'EDIT_SURFACE', 'EDIT_CURVE',
+ 'EDIT_LATTICE', 'EDIT_METABALL'}:
layout.operator("transform.vertex_warp", text="Warp")
layout.operator_context = 'EXEC_REGION_WIN'
layout.operator("transform.vertex_random", text="Randomize").offset = 0.1
@@ -1366,7 +1367,7 @@ class VIEW3D_MT_select_object(Menu):
layout.operator_menu_enum("object.select_by_type", "type", text="Select All by Type")
layout.operator("object.select_camera", text="Select Active Camera")
- layout.operator("object.select_mirror", text="Mirror Selection")
+ layout.operator("object.select_mirror")
layout.operator("object.select_random", text="Select Random")
layout.separator()
@@ -1424,7 +1425,7 @@ class VIEW3D_MT_select_pose(Menu):
layout.separator()
- layout.operator("pose.select_mirror", text="Flip Active")
+ layout.operator("pose.select_mirror")
layout.separator()
@@ -1597,7 +1598,7 @@ class VIEW3D_MT_select_edit_mesh(Menu):
layout.separator()
layout.operator("mesh.select_axis", text="Side of Active")
- layout.operator("mesh.select_mirror", text="Mirror Selection")
+ layout.operator("mesh.select_mirror")
class VIEW3D_MT_select_edit_curve(Menu):
@@ -1784,7 +1785,7 @@ class VIEW3D_MT_select_edit_armature(Menu):
layout.separator()
- layout.operator("armature.select_mirror", text="Mirror").extend = False
+ layout.operator("armature.select_mirror")
layout.separator()
@@ -2313,6 +2314,7 @@ class VIEW3D_MT_object_animation(Menu):
layout.operator("nla.bake", text="Bake Action...")
layout.operator("gpencil.bake_mesh_animation", text="Bake Mesh to Grease Pencil...")
+ layout.operator("gpencil.bake_grease_pencil_animation", text="Bake Object Transform to Grease Pencil...")
class VIEW3D_MT_object_rigid_body(Menu):
@@ -3038,6 +3040,11 @@ class VIEW3D_MT_sculpt(Menu):
layout.operator("sculpt.optimize")
+ layout.separator()
+
+ props = layout.operator("object.transfer_mode", text="Transfer Sculpt Mode")
+ props.use_eyedropper = True
+
class VIEW3D_MT_mask(Menu):
bl_label = "Mask"
@@ -3090,19 +3097,15 @@ class VIEW3D_MT_mask(Menu):
layout.separator()
- props = layout.operator("sculpt.mask_expand", text="Expand Mask by Topology")
- props.use_normals = False
- props.keep_previous_mask = False
+ props = layout.operator("sculpt.expand", text="Expand Mask by Topology")
+ props.target = 'MASK'
+ props.falloff_type = 'GEODESIC'
props.invert = True
- props.smooth_iterations = 2
- props.create_face_set = False
- props = layout.operator("sculpt.mask_expand", text="Expand Mask by Curvature")
- props.use_normals = True
- props.keep_previous_mask = True
+ props = layout.operator("sculpt.expand", text="Expand Mask by Normals")
+ props.target = 'MASK'
+ props.falloff_type = 'NORMALS'
props.invert = False
- props.smooth_iterations = 0
- props.create_face_set = False
layout.separator()
@@ -3156,6 +3159,20 @@ class VIEW3D_MT_face_sets(Menu):
layout.separator()
+ props = layout.operator("sculpt.expand", text="Expand Face Set by Topology")
+ props.target = 'FACE_SETS'
+ props.falloff_type = 'GEODESIC'
+ props.invert = False
+ props.use_modify_active = False
+
+ props = layout.operator("sculpt.expand", text="Expand Active Face Set")
+ props.target = 'FACE_SETS'
+ props.falloff_type = 'BOUNDARY_FACE_SET'
+ props.invert = False
+ props.use_modify_active = True
+
+ layout.separator()
+
op = layout.operator("mesh.face_set_extract", text='Extract Face Set')
layout.separator()
@@ -4940,28 +4957,6 @@ class VIEW3D_MT_assign_material(Menu):
icon='LAYER_ACTIVE' if mat == mat_active else 'BLANK1').material = mat.name
-class VIEW3D_MT_gpencil_copy_layer(Menu):
- bl_label = "Copy Layer to Object"
-
- def draw(self, context):
- layout = self.layout
- view_layer = context.view_layer
- obact = context.active_object
- gpl = context.active_gpencil_layer
-
- done = False
- if gpl is not None:
- for ob in view_layer.objects:
- if ob.type == 'GPENCIL' and ob != obact:
- layout.operator("gpencil.layer_duplicate_object", text=ob.name).object = ob.name
- done = True
-
- if done is False:
- layout.label(text="No destination object", icon='ERROR')
- else:
- layout.label(text="No layer to copy", icon='ERROR')
-
-
class VIEW3D_MT_edit_gpencil(Menu):
bl_label = "Grease Pencil"
@@ -5041,6 +5036,10 @@ class VIEW3D_MT_edit_gpencil_stroke(Menu):
layout.prop(settings, "use_scale_thickness", text="Scale Thickness")
layout.separator()
+ layout.operator("gpencil.stroke_normalize", text="Normalize Thickness").mode = 'THICKNESS'
+ layout.operator("gpencil.stroke_normalize", text="Normalize Opacity").mode = 'OPACITY'
+
+ layout.separator()
layout.operator("gpencil.reset_transform_fill", text="Reset Fill Transform")
@@ -6175,10 +6174,13 @@ class VIEW3D_PT_overlay_geometry(Panel):
sub.prop(overlay, "wireframe_opacity", text="Opacity")
row = col.row(align=True)
- if context.mode not in {
- 'EDIT_ARMATURE', 'POSE', 'OBJECT',
- 'PAINT_GPENCIL', 'VERTEX_GPENCIL', 'WEIGHT_GPENCIL', 'SCULPT_GPENCIL', 'EDIT_GPENCIL',
- }:
+
+ # These properties should be always available in the UI for all modes
+ # other than Object.
+ # Even when the Fade Inactive Geometry overlay is not affecting the
+ # current active object depending on its mode, it will always affect
+ # the rest of the scene.
+ if context.mode != 'OBJECT':
row.prop(overlay, "show_fade_inactive", text="")
sub = row.row()
sub.active = overlay.show_fade_inactive
@@ -6997,7 +6999,7 @@ class VIEW3D_PT_context_properties(Panel):
if member:
# Draw with no edit button
- rna_prop_ui.draw(self.layout, context, member, object, False)
+ rna_prop_ui.draw(self.layout, context, member, object, use_edit=False)
# Grease Pencil Object - Multiframe falloff tools
@@ -7629,7 +7631,6 @@ classes = (
VIEW3D_MT_weight_gpencil,
VIEW3D_MT_gpencil_animation,
VIEW3D_MT_gpencil_simplify,
- VIEW3D_MT_gpencil_copy_layer,
VIEW3D_MT_gpencil_autoweights,
VIEW3D_MT_gpencil_edit_context_menu,
VIEW3D_MT_edit_curve,
diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
index 72baa5331bf..89bb2005f53 100644
--- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
@@ -123,13 +123,15 @@ class View3DPanel:
# **************** standard tool clusters ******************
# Used by vertex & weight paint
-def draw_vpaint_symmetry(layout, vpaint, mesh):
+def draw_vpaint_symmetry(layout, vpaint, obj):
col = layout.column()
row = col.row(heading="Mirror", align=True)
- row.prop(mesh, "use_mirror_x", text="X", toggle=True)
- row.prop(mesh, "use_mirror_y", text="Y", toggle=True)
- row.prop(mesh, "use_mirror_z", text="Z", toggle=True)
+ row.prop(obj, "use_mesh_mirror_x", text="X", toggle=True)
+ row.prop(obj, "use_mesh_mirror_y", text="Y", toggle=True)
+ row.prop(obj, "use_mesh_mirror_z", text="Z", toggle=True)
+ col = layout.column()
+ col.active = not obj.data.use_mirror_vertex_groups
col.prop(vpaint, "radial_symmetry", text="Radial")
@@ -622,9 +624,15 @@ class VIEW3D_PT_tools_brush_texture(Panel, View3DPaintPanel):
@classmethod
def poll(cls, context):
- settings = cls.paint_settings(context)
- return (settings and settings.brush and
- (context.sculpt_object or context.image_paint_object or context.vertex_paint_object))
+ if (
+ (settings := cls.paint_settings(context)) and
+ (brush := settings.brush)
+ ):
+ if context.sculpt_object or context.vertex_paint_object:
+ return True
+ elif context.image_paint_object:
+ return (brush.image_tool == 'DRAW')
+ return False
def draw(self, context):
layout = self.layout
@@ -971,12 +979,12 @@ class VIEW3D_PT_tools_weightpaint_symmetry(Panel, View3DPaintPanel):
wpaint = tool_settings.weight_paint
mesh = context.object.data
- draw_vpaint_symmetry(layout, wpaint, mesh)
+ layout.prop(mesh, 'use_mirror_vertex_groups')
- col = layout.column(align=True)
- col.prop(mesh, 'use_mirror_vertex_group_x', text="Vertex Group X")
- row = col.row()
- row.active = mesh.use_mirror_vertex_group_x
+ draw_vpaint_symmetry(layout, wpaint, context.object)
+
+ row = layout.row()
+ row.active = mesh.use_mirror_vertex_groups
row.prop(mesh, "use_mirror_topology")
@@ -1051,7 +1059,7 @@ class VIEW3D_PT_tools_vertexpaint_symmetry(Panel, View3DPaintPanel):
tool_settings = context.tool_settings
vpaint = tool_settings.vertex_paint
- draw_vpaint_symmetry(layout, vpaint, context.object.data)
+ draw_vpaint_symmetry(layout, vpaint, context.object)
class VIEW3D_PT_tools_vertexpaint_symmetry_for_topbar(Panel):
@@ -1314,6 +1322,14 @@ class VIEW3D_PT_tools_particlemode_options_display(View3DPanel, Panel):
# Grease Pencil drawing brushes
+def tool_use_brush(context):
+ from bl_ui.space_toolsystem_common import ToolSelectPanelHelper
+ tool = ToolSelectPanelHelper.tool_active_from_context(context)
+ if tool and tool.has_datablock is False:
+ return False
+
+ return True
+
class GreasePencilPaintPanel:
bl_context = ".greasepencil_paint"
@@ -1325,6 +1341,10 @@ class GreasePencilPaintPanel:
if context.gpencil_data is None:
return False
+ # Hide for tools not using bruhses
+ if tool_use_brush(context) is False:
+ return False
+
gpd = context.gpencil_data
return bool(gpd.is_stroke_paint_mode)
else:
@@ -1448,7 +1468,12 @@ class VIEW3D_PT_tools_grease_pencil_brush_advanced(View3DPanel, Panel):
elif brush.gpencil_tool == 'FILL':
row = col.row(align=True)
row.prop(gp_settings, "fill_draw_mode", text="Boundary")
- row.prop(gp_settings, "show_fill_boundary", text="", icon='GRID')
+ row.prop(
+ gp_settings,
+ "show_fill_boundary",
+ icon='HIDE_OFF' if gp_settings.show_fill_boundary else 'HIDE_ON',
+ text="",
+ )
col.separator()
row = col.row(align=True)
@@ -1457,7 +1482,15 @@ class VIEW3D_PT_tools_grease_pencil_brush_advanced(View3DPanel, Panel):
col.separator()
row = col.row(align=True)
row.prop(gp_settings, "extend_stroke_factor")
- row.prop(gp_settings, "show_fill_extend", text="", icon='GRID')
+ row.prop(
+ gp_settings,
+ "show_fill_extend",
+ icon='HIDE_OFF' if gp_settings.show_fill_extend else 'HIDE_ON',
+ text="",
+ )
+
+ col.separator()
+ col.prop(gp_settings, "fill_leak", text="Leak Size")
col.separator()
col.prop(gp_settings, "fill_simplify_level", text="Simplify")
@@ -1697,9 +1730,14 @@ class VIEW3D_PT_tools_grease_pencil_brush_paint_falloff(GreasePencilBrushFalloff
if brush is None:
return False
- tool = brush.gpencil_tool
+ from bl_ui.space_toolsystem_common import ToolSelectPanelHelper
+ tool = ToolSelectPanelHelper.tool_active_from_context(context)
+ if tool and tool.idname != 'builtin_brush.Tint':
+ return False
+
+ gptool = brush.gpencil_tool
- return (settings and settings.brush and settings.brush.curve and tool == 'TINT')
+ return (settings and settings.brush and settings.brush.curve and gptool == 'TINT')
# Grease Pencil stroke sculpting tools
@@ -2018,6 +2056,11 @@ class VIEW3D_PT_tools_grease_pencil_brush_mixcolor(View3DPanel, Panel):
if context.region.type == 'TOOL_HEADER':
return False
+ from bl_ui.space_toolsystem_common import ToolSelectPanelHelper
+ tool = ToolSelectPanelHelper.tool_active_from_context(context)
+ if tool and tool.idname in('builtin.cutter', 'builtin.eyedropper', 'builtin.interpolate'):
+ return False
+
if brush.gpencil_tool == 'TINT':
return True
@@ -2074,6 +2117,11 @@ class VIEW3D_PT_tools_grease_pencil_brush_mix_palette(View3DPanel, Panel):
if ob is None or brush is None:
return False
+ from bl_ui.space_toolsystem_common import ToolSelectPanelHelper
+ tool = ToolSelectPanelHelper.tool_active_from_context(context)
+ if tool and tool.idname in('builtin.cutter', 'builtin.eyedropper', 'builtin.interpolate'):
+ return False
+
if brush.gpencil_tool == 'TINT':
return True
diff --git a/release/scripts/startup/keyingsets_builtins.py b/release/scripts/startup/keyingsets_builtins.py
index 012febc7cc7..83151a3480c 100644
--- a/release/scripts/startup/keyingsets_builtins.py
+++ b/release/scripts/startup/keyingsets_builtins.py
@@ -44,6 +44,7 @@ ANIM_KS_LOCATION_ID = "Location"
ANIM_KS_ROTATION_ID = "Rotation"
ANIM_KS_SCALING_ID = "Scaling"
ANIM_KS_LOC_ROT_SCALE_ID = "LocRotScale"
+ANIM_KS_LOC_ROT_SCALE_CPROP_ID = "LocRotScaleCProp"
ANIM_KS_AVAILABLE_ID = "Available"
ANIM_KS_WHOLE_CHARACTER_ID = "WholeCharacter"
ANIM_KS_WHOLE_CHARACTER_SELECTED_ID = "WholeCharacterSelected"
@@ -159,6 +160,22 @@ class BUILTIN_KSI_LocRotScale(KeyingSetInfo):
keyingsets_utils.RKS_GEN_scaling(self, context, ks, data)
+# LocRotScaleCProp
+class BUILTIN_KSI_LocRotScaleCProp(KeyingSetInfo):
+ """Key location/rotation/scale as well as custom properties"""
+ bl_idname = ANIM_KS_LOC_ROT_SCALE_CPROP_ID
+ bl_label = "Location, Rotation, Scale & Custom Properties"
+
+ poll = keyingsets_utils.RKS_POLL_selected_items
+ iterator = keyingsets_utils.RKS_ITER_selected_item
+
+ def generate(self, context, ks, data):
+ keyingsets_utils.RKS_GEN_location(self, context, ks, data)
+ keyingsets_utils.RKS_GEN_rotation(self, context, ks, data)
+ keyingsets_utils.RKS_GEN_scaling(self, context, ks, data)
+ keyingsets_utils.RKS_GEN_custom_props(self, context, ks, data)
+
+
# RotScale
class BUILTIN_KSI_RotScale(KeyingSetInfo):
"""Insert a keyframe on each of the rotation and scale channels"""
@@ -350,7 +367,7 @@ class BUILTIN_KSI_Available(KeyingSetInfo):
bl_label = "Available"
# poll - selected objects or selected object with animation data
- def poll(ksi, context):
+ def poll(self, context):
ob = context.active_object
if ob:
# TODO: this fails if one animation-less object is active, but many others are selected
@@ -366,14 +383,7 @@ class BUILTIN_KSI_Available(KeyingSetInfo):
###############################
-
-# All properties that are likely to get animated in a character rig
-class BUILTIN_KSI_WholeCharacter(KeyingSetInfo):
- """Insert a keyframe for all properties that are likely to get animated in a character rig """ \
- """(useful when blocking out a shot)"""
- bl_idname = ANIM_KS_WHOLE_CHARACTER_ID
- bl_label = "Whole Character"
-
+class WholeCharacterMixin:
# these prefixes should be avoided, as they are not really bones
# that animators should be touching (or need to touch)
badBonePrefixes = (
@@ -387,38 +397,37 @@ class BUILTIN_KSI_WholeCharacter(KeyingSetInfo):
)
# poll - pose-mode on active object only
- def poll(ksi, context):
+ def poll(self, context):
return ((context.active_object) and (context.active_object.pose) and
(context.active_object.mode == 'POSE'))
# iterator - all bones regardless of selection
- def iterator(ksi, context, ks):
+ def iterator(self, context, ks):
for bone in context.active_object.pose.bones:
- if not bone.name.startswith(BUILTIN_KSI_WholeCharacter.badBonePrefixes):
- ksi.generate(context, ks, bone)
+ if not bone.name.startswith(self.badBonePrefixes):
+ self.generate(context, ks, bone)
# generator - all unlocked bone transforms + custom properties
- def generate(ksi, context, ks, bone):
+ def generate(self, context, ks, bone):
# loc, rot, scale - only include unlocked ones
if not bone.bone.use_connect:
- ksi.doLoc(ks, bone)
+ self.doLoc(ks, bone)
if bone.rotation_mode in {'QUATERNION', 'AXIS_ANGLE'}:
- ksi.doRot4d(ks, bone)
+ self.doRot4d(ks, bone)
else:
- ksi.doRot3d(ks, bone)
- ksi.doScale(ks, bone)
+ self.doRot3d(ks, bone)
+ self.doScale(ks, bone)
# bbone properties?
- ksi.doBBone(context, ks, bone)
+ self.doBBone(context, ks, bone)
# custom props?
- ksi.doCustomProps(ks, bone)
-
+ self.doCustomProps(ks, bone)
# ----------------
# helper to add some bone's property to the Keying Set
- def addProp(ksi, ks, bone, prop, index=-1, use_groups=True):
+ def addProp(self, ks, bone, prop, index=-1, use_groups=True):
# add the property name to the base path
id_path = bone.path_from_id()
id_block = bone.id_data
@@ -439,16 +448,16 @@ class BUILTIN_KSI_WholeCharacter(KeyingSetInfo):
# ----------------
# location properties
- def doLoc(ksi, ks, bone):
+ def doLoc(self, ks, bone):
if bone.lock_location == (False, False, False):
- ksi.addProp(ks, bone, "location")
+ self.addProp(ks, bone, "location")
else:
for i in range(3):
if not bone.lock_location[i]:
- ksi.addProp(ks, bone, "location", i)
+ self.addProp(ks, bone, "location", i)
# rotation properties
- def doRot4d(ksi, ks, bone):
+ def doRot4d(self, ks, bone):
# rotation mode affects the property used
if bone.rotation_mode == 'QUATERNION':
prop = "rotation_quaternion"
@@ -459,40 +468,40 @@ class BUILTIN_KSI_WholeCharacter(KeyingSetInfo):
if bone.lock_rotations_4d:
# can check individually
if (bone.lock_rotation == (False, False, False)) and (bone.lock_rotation_w is False):
- ksi.addProp(ks, bone, prop)
+ self.addProp(ks, bone, prop)
else:
if bone.lock_rotation_w is False:
- ksi.addProp(ks, bone, prop, 0) # w = 0
+ self.addProp(ks, bone, prop, 0) # w = 0
for i in range(3):
if not bone.lock_rotation[i]:
- ksi.addProp(ks, bone, prop, i + 1) # i + 1, since here x/y/z = 1,2,3, and w=0
+ self.addProp(ks, bone, prop, i + 1) # i + 1, since here x/y/z = 1,2,3, and w=0
elif True not in bone.lock_rotation:
# if axis-angle rotations get locked as eulers, then it's too messy to allow anything
# other than all open unless we keyframe the whole lot
- ksi.addProp(ks, bone, prop)
+ self.addProp(ks, bone, prop)
- def doRot3d(ksi, ks, bone):
+ def doRot3d(self, ks, bone):
if bone.lock_rotation == (False, False, False):
- ksi.addProp(ks, bone, "rotation_euler")
+ self.addProp(ks, bone, "rotation_euler")
else:
for i in range(3):
if not bone.lock_rotation[i]:
- ksi.addProp(ks, bone, "rotation_euler", i)
+ self.addProp(ks, bone, "rotation_euler", i)
# scale properties
- def doScale(ksi, ks, bone):
+ def doScale(self, ks, bone):
if bone.lock_scale == (0, 0, 0):
- ksi.addProp(ks, bone, "scale")
+ self.addProp(ks, bone, "scale")
else:
for i in range(3):
if not bone.lock_scale[i]:
- ksi.addProp(ks, bone, "scale", i)
+ self.addProp(ks, bone, "scale", i)
# ----------------
# bendy bone properties
- def doBBone(ksi, context, ks, pchan):
+ def doBBone(self, context, ks, pchan):
bone = pchan.bone
# This check is crude, but is the best we can do for now
@@ -500,12 +509,12 @@ class BUILTIN_KSI_WholeCharacter(KeyingSetInfo):
# (and the bone is a control bone). This may lead to some
# false positives...
if bone.bbone_segments > 1:
- keyingsets_utils.RKS_GEN_bendy_bones(ksi, context, ks, pchan)
+ keyingsets_utils.RKS_GEN_bendy_bones(self, context, ks, pchan)
# ----------------
# custom properties
- def doCustomProps(ksi, ks, bone):
+ def doCustomProps(self, ks, bone):
prop_type_compat = {bpy.types.BoolProperty,
bpy.types.IntProperty,
@@ -528,39 +537,34 @@ class BUILTIN_KSI_WholeCharacter(KeyingSetInfo):
# be converted to an FCurve-compatible value, so we can't keyframe it anyway.
continue
if rna_property.rna_type in prop_type_compat:
- ksi.addProp(ks, bone, prop_path)
+ self.addProp(ks, bone, prop_path)
elif prop_rna.is_animatable:
- ksi.addProp(ks, bone, prop)
+ self.addProp(ks, bone, prop)
-# All properties that are likely to get animated in a character rig, only selected bones.
+
+class BUILTIN_KSI_WholeCharacter(WholeCharacterMixin, KeyingSetInfo):
+ """Insert a keyframe for all properties that are likely to get animated in a character rig """ \
+ """(useful when blocking out a shot)"""
+ bl_idname = ANIM_KS_WHOLE_CHARACTER_ID
+ bl_label = "Whole Character"
-class BUILTIN_KSI_WholeCharacterSelected(KeyingSetInfo):
+class BUILTIN_KSI_WholeCharacterSelected(WholeCharacterMixin, KeyingSetInfo):
"""Insert a keyframe for all properties that are likely to get animated in a character rig """ \
"""(only selected bones)"""
bl_idname = ANIM_KS_WHOLE_CHARACTER_SELECTED_ID
bl_label = "Whole Character (Selected Bones Only)"
# iterator - all bones regardless of selection
- def iterator(ksi, context, ks):
+ def iterator(self, context, ks):
# Use either the selected bones, or all of them if none are selected.
bones = context.selected_pose_bones_from_active_object or context.active_object.pose.bones
for bone in bones:
- if bone.name.startswith(BUILTIN_KSI_WholeCharacter.badBonePrefixes):
+ if bone.name.startswith(self.badBonePrefixes):
continue
- ksi.generate(context, ks, bone)
-
- # Poor man's subclassing. Blender breaks when we actually subclass BUILTIN_KSI_WholeCharacter.
- poll = BUILTIN_KSI_WholeCharacter.poll
- generate = BUILTIN_KSI_WholeCharacter.generate
- addProp = BUILTIN_KSI_WholeCharacter.addProp
- doLoc = BUILTIN_KSI_WholeCharacter.doLoc
- doRot4d = BUILTIN_KSI_WholeCharacter.doRot4d
- doRot3d = BUILTIN_KSI_WholeCharacter.doRot3d
- doScale = BUILTIN_KSI_WholeCharacter.doScale
- doBBone = BUILTIN_KSI_WholeCharacter.doBBone
- doCustomProps = BUILTIN_KSI_WholeCharacter.doCustomProps
+ self.generate(context, ks, bone)
+
###############################
@@ -578,7 +582,7 @@ class BUILTIN_KSI_DeltaLocation(KeyingSetInfo):
iterator = keyingsets_utils.RKS_ITER_selected_objects
# generator - delta location channels only
- def generate(ksi, context, ks, data):
+ def generate(self, context, ks, data):
# get id-block and path info
id_block, base_path, grouping = keyingsets_utils.get_transform_generators_base_info(data)
@@ -604,7 +608,7 @@ class BUILTIN_KSI_DeltaRotation(KeyingSetInfo):
iterator = keyingsets_utils.RKS_ITER_selected_objects
# generator - delta location channels only
- def generate(ksi, context, ks, data):
+ def generate(self, context, ks, data):
# get id-block and path info
id_block, base_path, grouping = keyingsets_utils.get_transform_generators_base_info(data)
@@ -638,7 +642,7 @@ class BUILTIN_KSI_DeltaScale(KeyingSetInfo):
iterator = keyingsets_utils.RKS_ITER_selected_objects
# generator - delta location channels only
- def generate(ksi, context, ks, data):
+ def generate(self, context, ks, data):
# get id-block and path info
id_block, base_path, grouping = keyingsets_utils.get_transform_generators_base_info(data)
@@ -664,6 +668,7 @@ classes = (
BUILTIN_KSI_Scaling,
BUILTIN_KSI_LocRot,
BUILTIN_KSI_LocRotScale,
+ BUILTIN_KSI_LocRotScaleCProp,
BUILTIN_KSI_LocScale,
BUILTIN_KSI_RotScale,
BUILTIN_KSI_DeltaLocation,
diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py
index febb31af188..5927123cdd8 100644
--- a/release/scripts/startup/nodeitems_builtins.py
+++ b/release/scripts/startup/nodeitems_builtins.py
@@ -34,7 +34,7 @@ class SortedNodeCategory(NodeCategory):
if isinstance(items, list):
items = sorted(items, key=lambda item: item.label.lower())
- super().__init__(identifier, name, description, items)
+ super().__init__(identifier, name, description=description, items=items)
class CompositorNodeCategory(SortedNodeCategory):
@@ -119,8 +119,8 @@ def node_group_items(context):
if group.name.startswith('.'):
continue
yield NodeItem(node_tree_group_type[group.bl_idname],
- group.name,
- {"node_tree": "bpy.data.node_groups[%r]" % group.name})
+ label=group.name,
+ settings={"node_tree": "bpy.data.node_groups[%r]" % group.name})
# only show input/output nodes inside node groups
@@ -368,6 +368,7 @@ compositor_node_categories = [
NodeItem("CompositorNodePixelate"),
NodeItem("CompositorNodeSunBeams"),
NodeItem("CompositorNodeDenoise"),
+ NodeItem("CompositorNodeAntiAliasing"),
]),
CompositorNodeCategory("CMP_OP_VECTOR", "Vector", items=[
NodeItem("CompositorNodeNormal"),
@@ -471,37 +472,44 @@ texture_node_categories = [
]),
]
-
-def not_implemented_node(idname):
- NodeType = getattr(bpy.types, idname)
- name = NodeType.bl_rna.name
- label = "%s (mockup)" % name
- return NodeItem(idname, label=label)
-
-
geometry_node_categories = [
# Geometry Nodes
GeometryNodeCategory("GEO_ATTRIBUTE", "Attribute", items=[
NodeItem("GeometryNodeAttributeRandomize"),
NodeItem("GeometryNodeAttributeMath"),
+ NodeItem("GeometryNodeAttributeClamp"),
NodeItem("GeometryNodeAttributeCompare"),
NodeItem("GeometryNodeAttributeConvert"),
+ NodeItem("GeometryNodeAttributeCurveMap"),
NodeItem("GeometryNodeAttributeFill"),
NodeItem("GeometryNodeAttributeMix"),
NodeItem("GeometryNodeAttributeProximity"),
NodeItem("GeometryNodeAttributeColorRamp"),
NodeItem("GeometryNodeAttributeVectorMath"),
+ NodeItem("GeometryNodeAttributeVectorRotate"),
NodeItem("GeometryNodeAttributeSampleTexture"),
NodeItem("GeometryNodeAttributeCombineXYZ"),
NodeItem("GeometryNodeAttributeSeparateXYZ"),
NodeItem("GeometryNodeAttributeRemove"),
+ NodeItem("GeometryNodeAttributeMapRange"),
+ NodeItem("GeometryNodeAttributeTransfer"),
]),
GeometryNodeCategory("GEO_COLOR", "Color", items=[
+ NodeItem("ShaderNodeRGBCurve"),
NodeItem("ShaderNodeValToRGB"),
NodeItem("ShaderNodeSeparateRGB"),
NodeItem("ShaderNodeCombineRGB"),
]),
+ GeometryNodeCategory("GEO_CURVE", "Curve", items=[
+ NodeItem("GeometryNodeCurveToMesh"),
+ NodeItem("GeometryNodeCurveResample"),
+ NodeItem("GeometryNodeMeshToCurve"),
+ NodeItem("GeometryNodeCurveLength"),
+ ]),
GeometryNodeCategory("GEO_GEOMETRY", "Geometry", items=[
+ NodeItem("GeometryNodeBoundBox"),
+ NodeItem("GeometryNodeConvexHull"),
+ NodeItem("GeometryNodeDeleteGeometry"),
NodeItem("GeometryNodeTransform"),
NodeItem("GeometryNodeJoinGeometry"),
]),
@@ -512,15 +520,30 @@ geometry_node_categories = [
NodeItem("ShaderNodeValue"),
NodeItem("FunctionNodeInputString"),
NodeItem("FunctionNodeInputVector"),
+ NodeItem("GeometryNodeInputMaterial"),
NodeItem("GeometryNodeIsViewport"),
]),
+ GeometryNodeCategory("GEO_MATERIAL", "Material", items=[
+ NodeItem("GeometryNodeMaterialAssign"),
+ NodeItem("GeometryNodeSelectByMaterial"),
+ NodeItem("GeometryNodeMaterialReplace"),
+ ]),
GeometryNodeCategory("GEO_MESH", "Mesh", items=[
NodeItem("GeometryNodeBoolean"),
NodeItem("GeometryNodeTriangulate"),
NodeItem("GeometryNodeEdgeSplit"),
NodeItem("GeometryNodeSubdivisionSurface"),
NodeItem("GeometryNodeSubdivide"),
-
+ ]),
+ GeometryNodeCategory("GEO_PRIMITIVES", "Mesh Primitives", items=[
+ NodeItem("GeometryNodeMeshCircle"),
+ NodeItem("GeometryNodeMeshCone"),
+ NodeItem("GeometryNodeMeshCube"),
+ NodeItem("GeometryNodeMeshCylinder"),
+ NodeItem("GeometryNodeMeshGrid"),
+ NodeItem("GeometryNodeMeshIcoSphere"),
+ NodeItem("GeometryNodeMeshLine"),
+ NodeItem("GeometryNodeMeshUVSphere"),
]),
GeometryNodeCategory("GEO_POINT", "Point", items=[
NodeItem("GeometryNodePointDistribute"),
@@ -531,33 +554,25 @@ geometry_node_categories = [
NodeItem("GeometryNodeRotatePoints"),
NodeItem("GeometryNodeAlignRotationToVector"),
]),
- GeometryNodeCategory("GEO_VOLUME", "Volume", items=[
- NodeItem("GeometryNodePointsToVolume"),
- NodeItem("GeometryNodeVolumeToMesh"),
- ]),
- GeometryNodeCategory("GEO_PRIMITIVES", "Mesh Primitives", items=[
- NodeItem("GeometryNodeMeshCube"),
- NodeItem("GeometryNodeMeshCircle"),
- NodeItem("GeometryNodeMeshUVSphere"),
- NodeItem("GeometryNodeMeshIcoSphere"),
- NodeItem("GeometryNodeMeshCylinder"),
- NodeItem("GeometryNodeMeshCone"),
- NodeItem("GeometryNodeMeshLine"),
- NodeItem("GeometryNodeMeshPlane"),
- ]),
GeometryNodeCategory("GEO_UTILITIES", "Utilities", items=[
NodeItem("ShaderNodeMapRange"),
NodeItem("ShaderNodeClamp"),
NodeItem("ShaderNodeMath"),
NodeItem("FunctionNodeBooleanMath"),
NodeItem("FunctionNodeFloatCompare"),
+ NodeItem("GeometryNodeSwitch"),
]),
GeometryNodeCategory("GEO_VECTOR", "Vector", items=[
+ NodeItem("ShaderNodeVectorCurve"),
NodeItem("ShaderNodeSeparateXYZ"),
NodeItem("ShaderNodeCombineXYZ"),
NodeItem("ShaderNodeVectorMath"),
NodeItem("ShaderNodeVectorRotate"),
]),
+ GeometryNodeCategory("GEO_VOLUME", "Volume", items=[
+ NodeItem("GeometryNodePointsToVolume"),
+ NodeItem("GeometryNodeVolumeToMesh"),
+ ]),
GeometryNodeCategory("GEO_GROUP", "Group", items=node_group_items),
GeometryNodeCategory("GEO_LAYOUT", "Layout", items=[
NodeItem("NodeFrame"),
diff --git a/release/scripts/templates_osl/basic_shader.osl b/release/scripts/templates_osl/basic_shader.osl
new file mode 100644
index 00000000000..3c5240a1bbd
--- /dev/null
+++ b/release/scripts/templates_osl/basic_shader.osl
@@ -0,0 +1,10 @@
+shader basic_shader(
+ float in_float = 1.0,
+ color in_color = color(1.0, 1.0, 1.0),
+ output float out_float = 0.0,
+ output color out_color = color(0.0, 0.0, 0.0)
+ )
+{
+ out_float = in_float * 2.0;
+ out_color = in_color * 2.0;
+}
diff --git a/release/scripts/templates_py/bmesh_simple_editmode.py b/release/scripts/templates_py/bmesh_simple_editmode.py
index d79ba02c2cb..1e7ce8b4495 100644
--- a/release/scripts/templates_py/bmesh_simple_editmode.py
+++ b/release/scripts/templates_py/bmesh_simple_editmode.py
@@ -20,4 +20,4 @@ for v in bm.verts:
# Show the updates in the viewport
# and recalculate n-gon tessellation.
-bmesh.update_edit_mesh(me, True)
+bmesh.update_edit_mesh(me, loop_triangles=True)
diff --git a/release/scripts/templates_py/gizmo_custom_geometry.py b/release/scripts/templates_py/gizmo_custom_geometry.py
index 4105a99c633..701b1ac48ba 100644
--- a/release/scripts/templates_py/gizmo_custom_geometry.py
+++ b/release/scripts/templates_py/gizmo_custom_geometry.py
@@ -1,4 +1,4 @@
-# Example of a custom widget that defines it's own geometry.
+# Example of a custom widget that defines its own geometry.
#
# Usage: Select a light in the 3D view and drag the arrow at it's rear
# to change it's energy value.
diff --git a/release/scripts/templates_py/image_processing.py b/release/scripts/templates_py/image_processing.py
new file mode 100644
index 00000000000..2392faf440c
--- /dev/null
+++ b/release/scripts/templates_py/image_processing.py
@@ -0,0 +1,35 @@
+# This sample shows the an efficient way of doing image processing
+# over Blender's images using Python.
+
+import bpy
+import numpy as np
+
+
+input_image_name = "Image"
+output_image_name = "NewImage"
+
+# Retrieve input image.
+input_image = bpy.data.images[input_image_name]
+w, h = input_image.size
+
+# Allocate a numpy array to manipulate pixel data.
+pixel_data = np.zeros((w, h, 4), 'f')
+
+# Fast copy of pixel data from bpy.data to numpy array.
+input_image.pixels.foreach_get(pixel_data.ravel())
+
+# Do whatever image processing you want using numpy here:
+# Example 1: Inverse red green and blue channels.
+pixel_data[:,:,:3] = 1.0 - pixel_data[:,:,:3]
+# Example 2: Change gamma on the red channel.
+pixel_data[:,:,0] = np.power(pixel_data[:,:,0], 1.5)
+
+# Create output image.
+if output_image_name in bpy.data.images:
+ output_image = bpy.data.images[output_image_name]
+else:
+ output_image = bpy.data.images.new(output_image_name, width=w, height=h)
+
+# Copy of pixel data from numpy array back to the output image.
+output_image.pixels.foreach_set(pixel_data.ravel())
+output_image.update()
diff --git a/release/scripts/templates_py/operator_modal_draw.py b/release/scripts/templates_py/operator_modal_draw.py
index 1e185ecfe2b..16c6f6dbe22 100644
--- a/release/scripts/templates_py/operator_modal_draw.py
+++ b/release/scripts/templates_py/operator_modal_draw.py
@@ -1,5 +1,4 @@
import bpy
-import bgl
import blf
import gpu
from gpu_extras.batch import batch_for_shader
@@ -17,16 +16,16 @@ def draw_callback_px(self, context):
# 50% alpha, 2 pixel width line
shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR')
- bgl.glEnable(bgl.GL_BLEND)
- bgl.glLineWidth(2)
+ gpu.state.blend_set('ALPHA')
+ gpu.state.line_width_set(2.0)
batch = batch_for_shader(shader, 'LINE_STRIP', {"pos": self.mouse_path})
shader.bind()
shader.uniform_float("color", (0.0, 0.0, 0.0, 0.5))
batch.draw(shader)
# restore opengl defaults
- bgl.glLineWidth(1)
- bgl.glDisable(bgl.GL_BLEND)
+ gpu.state.line_width_set(1.0)
+ gpu.state.blend_set('NONE')
class ModalDrawOperator(bpy.types.Operator):
diff --git a/release/steam/README.md b/release/steam/README.md
deleted file mode 100644
index 05eda799c3f..00000000000
--- a/release/steam/README.md
+++ /dev/null
@@ -1,70 +0,0 @@
-Creating Steam builds for Blender
-=================================
-
-This script automates creation of the Steam files: download of the archives,
-extraction of the archives, preparation of the build scripts (VDF files), actual
-building of the Steam game files.
-
-Requirements
-============
-
-* MacOS machine - Tested on Catalina 10.15.6. Extracting contents from the DMG
- archive did not work Windows nor on Linux using 7-zip. All DMG archives tested
- failed to be extracted. As such only MacOS is known to work.
-* Steam SDK downloaded from SteamWorks - The `steamcmd` is used to generate the
- Steam game files. The path to the `steamcmd` is what is actually needed.
-* SteamWorks credentials - Needed to log in using `steamcmd`.
-* Login to SteamWorks with the `steamcmd` from the command-line at least once -
- Needded to ensure the user is properly logged in. On a new machine the user
- will have to go through two-factor authentication.
-* App ID and Depot IDs - Needed to create the VDF files.
-* Python 3.x - 3.7 was tested.
-* Base URL - for downloading the archives.
-
-Usage
-=====
-
-```bash
-$ export STEAMUSER=SteamUserName
-$ export STEAMPW=SteamUserPW
-$ export BASEURL=https://download.blender.org/release/Blender2.83/
-$ export VERSION=2.83.3
-$ export APPID=appidnr
-$ export WINID=winidnr
-$ export LINID=linuxidnr
-$ export MACOSID=macosidnr
-
-# log in to SteamWorks from command-line at least once
-
-$ ../sdk/tools/ContentBuilder/builder_osx/steamcmd +login $STEAMUSER $STEAMPW
-
-# once that has been done we can now actually start our tool
-
-$ python3.7 create_steam_builds.py --baseurl $BASEURL --version $VERSION --appid $APPID --winid $WINID --linuxid $LINID --macosid $MACOSID --steamuser $STEAMUSER --steampw $STEAMPW --steamcmd ../sdk/tools/ContentBuilder/builder_osx/steamcmd
-```
-
-All arguments in the above example are required.
-
-At the start the tool will login using `steamcmd`. This is necessary to let the
-Steam SDK update itself if necessary.
-
-There are a few optional arguments:
-
-* `--dryrun`: If set building the game files will not actually happen. A set of
- log files and a preview manifest per depot will be created in the output folder.
- This can be used to double-check everything works as expected.
-* `--skipdl`: If set will skip downloading of the archives. The tool expects the
- archives to already exist in the correct content location.
-* `--skipextract`: If set will skip extraction of all archives. The tool expects
- the archives to already have been correctly extracted in the content location.
-
-Run the tool with `-h` for detailed information on each argument.
-
-The content and output folders are generated through appending the version
-without dots to the words `content` and `output` respectively, e.g. `content2833`
-and `output2833`. These folders are created next to the tool.
-
-From all `.template` files the Steam build scripts will be generated also in the
-same directory as the tool. The files will have the extension `.vdf`.
-
-In case of errors the tool will have a non-zero return code. \ No newline at end of file
diff --git a/release/steam/blender_app_build.vdf.template b/release/steam/blender_app_build.vdf.template
deleted file mode 100644
index 9e2d0625d72..00000000000
--- a/release/steam/blender_app_build.vdf.template
+++ /dev/null
@@ -1,17 +0,0 @@
-"appbuild"
-{
- "appid" "[APPID]"
- "desc" "Blender [VERSION]" // description for this build
- "buildoutput" "./[OUTPUT]" // build output folder for .log, .csm & .csd files, relative to location of this file
- "contentroot" "./[CONTENT]" // root content folder, relative to location of this file
- "setlive" "" // branch to set live after successful build, non if empty
- "preview" "[DRYRUN]" // 1 to enable preview builds, 0 to commit build to steampipe
- "local" "" // set to flie path of local content server
-
- "depots"
- {
- "[WINID]" "depot_build_win.vdf"
- "[LINUXID]" "depot_build_linux.vdf"
- "[MACOSID]" "depot_build_macos.vdf"
- }
-}
diff --git a/release/steam/create_steam_builds.py b/release/steam/create_steam_builds.py
deleted file mode 100644
index 2ecd0c347f7..00000000000
--- a/release/steam/create_steam_builds.py
+++ /dev/null
@@ -1,397 +0,0 @@
-#!/usr/bin/env python3
-
-import argparse
-import pathlib
-import requests
-import shutil
-import subprocess
-from typing import Callable, Iterator, List, Tuple
-
-# supported archive and platform endings, used to create actual archive names
-archive_endings = ["windows64.zip", "linux64.tar.xz", "macOS.dmg"]
-
-
-def add_optional_argument(option: str, help: str) -> None:
- global parser
- """Add an optional argument
-
- Args:
- option (str): Option to add
- help (str): Help description for the argument
- """
- parser.add_argument(option, help=help, action='store_const', const=1)
-
-
-def blender_archives(version: str) -> Iterator[str]:
- """Generator for Blender archives for version.
-
- Yields for items in archive_endings an archive name in the form of
- blender-{version}-{ending}.
-
- Args:
- version (str): Version string of the form 2.83.2
-
-
- Yields:
- Iterator[str]: Name in the form of blender-{version}-{ending}
- """
- global archive_endings
-
- for ending in archive_endings:
- yield f"blender-{version}-{ending}"
-
-
-def get_archive_type(archive_type: str, version: str) -> str:
- """Return the archive of given type and version.
-
- Args:
- archive_type (str): extension for archive type to check for
- version (str): Version string in the form 2.83.2
-
- Raises:
- Exception: Execption when archive type isn't found
-
- Returns:
- str: archive name for given type
- """
-
- for archive in blender_archives(version):
- if archive.endswith(archive_type):
- return archive
- raise Exception("Unknown archive type")
-
-
-def execute_command(cmd: List[str], name: str, errcode: int, cwd=".", capture_output=True) -> str:
- """Execute the given command.
-
- Returns the process stdout upon success if any.
-
- On error print message the command with name that has failed. Print stdout
- and stderr of the process if any, and then exit with given error code.
-
- Args:
- cmd (List[str]): Command in list format, each argument as their own item
- name (str): Name of command to use when printing to command-line
- errcode (int): Error code to use in case of exit()
- cwd (str, optional): Folder to use as current work directory for command
- execution. Defaults to ".".
- capture_output (bool, optional): Whether to capture command output or not.
- Defaults to True.
-
- Returns:
- str: stdout if any, or empty string
- """
- cmd_process = subprocess.run(
- cmd, capture_output=capture_output, encoding="UTF-8", cwd=cwd)
- if cmd_process.returncode == 0:
- if cmd_process.stdout:
- return cmd_process.stdout
- else:
- return ""
- else:
- print(f"ERROR: {name} failed.")
- if cmd_process.stdout:
- print(cmd_process.stdout)
- if cmd_process.stderr:
- print(cmd_process.stderr)
- exit(errcode)
- return ""
-
-
-def download_archives(base_url: str, archives: Callable[[str], Iterator[str]], version: str, dst_dir: pathlib.Path):
- """Download archives from the given base_url.
-
- Archives is a generator for Blender archive names based on version.
-
- Archive names are appended to the base_url to load from, and appended to
- dst_dir to save to.
-
- Args:
- base_url (str): Base URL to load archives from
- archives (Callable[[str], Iterator[str]]): Generator for Blender archive
- names based on version
- version (str): Version string in the form of 2.83.2
- dst_dir (pathlib.Path): Download destination
- """
-
- if base_url[-1] != '/':
- base_url = base_url + '/'
-
- for archive in archives(version):
- download_url = f"{base_url}{archive}"
- target_file = dst_dir.joinpath(archive)
- download_file(download_url, target_file)
-
-
-def download_file(from_url: str, to_file: pathlib.Path) -> None:
- """Download from_url as to_file.
-
- Actual downloading will be skipped if --skipdl is given on the command-line.
-
- Args:
- from_url (str): Full URL to resource to download
- to_file (pathlib.Path): Full path to save downloaded resource as
- """
- global args
-
- if not args.skipdl or not to_file.exists():
- print(f"Downloading {from_url}")
- with open(to_file, "wb") as download_zip:
- response = requests.get(from_url)
- if response.status_code != requests.codes.ok:
- print(f"ERROR: failed to download {from_url} (status code: {response.status_code})")
- exit(1313)
- download_zip.write(response.content)
- else:
- print(f"Downloading {from_url} skipped")
- print(" ... OK")
-
-
-def copy_contents_from_dmg_to_path(dmg_file: pathlib.Path, dst: pathlib.Path) -> None:
- """Copy the contents of the given DMG file to the destination folder.
-
- Args:
- dmg_file (pathlib.Path): Full path to DMG archive to extract from
- dst (pathlib.Path): Full path to destination to extract to
- """
- hdiutil_attach = ["hdiutil",
- "attach",
- "-readonly",
- f"{dmg_file}"
- ]
- attached = execute_command(hdiutil_attach, "hdiutil attach", 1)
-
- # Last line of output is what we want, it is of the form
- # /dev/somedisk Apple_HFS /Volumes/Blender
- # We want to retain the mount point, and the folder the mount is
- # created on. The mounted disk we need for detaching, the folder we
- # need to be able to copy the contents to where we can use them
- attachment_items = attached.splitlines()[-1].split()
- mounted_disk = attachment_items[0]
- source_location = pathlib.Path(attachment_items[2], "Blender.app")
-
- print(f"{source_location} -> {dst}")
-
- shutil.copytree(source_location, dst)
-
- hdiutil_detach = ["hdiutil",
- "detach",
- f"{mounted_disk}"
- ]
- execute_command(hdiutil_detach, "hdiutil detach", 2)
-
-
-def create_build_script(template_name: str, vars: List[Tuple[str, str]]) -> pathlib.Path:
- """
- Create the Steam build script
-
- Use the given template and template variable tuple list.
-
- Returns pathlib.Path to the created script.
-
- Args:
- template_name (str): [description]
- vars (List[Tuple[str, str]]): [description]
-
- Returns:
- pathlib.Path: Full path to the generated script
- """
- build_script = pathlib.Path(".", template_name).read_text()
- for var in vars:
- build_script = build_script.replace(var[0], var[1])
- build_script_file = template_name.replace(".template", "")
- build_script_path = pathlib.Path(".", build_script_file)
- build_script_path.write_text(build_script)
- return build_script_path
-
-
-def clean_up() -> None:
- """Remove intermediate files depending on given command-line arguments
- """
- global content_location, args
-
- if not args.leavearch and not args.leaveextracted:
- shutil.rmtree(content_location)
-
- if args.leavearch and not args.leaveextracted:
- shutil.rmtree(content_location.joinpath(zip_extract_folder))
- shutil.rmtree(content_location.joinpath(tarxz_extract_folder))
- shutil.rmtree(content_location.joinpath(dmg_extract_folder))
-
- if args.leaveextracted and not args.leavearch:
- import os
- os.remove(content_location.joinpath(zipped_blender))
- os.remove(content_location.joinpath(tarxz_blender))
- os.remove(content_location.joinpath(dmg_blender))
-
-
-def extract_archive(archive: str, extract_folder_name: str,
- cmd: List[str], errcode: int) -> None:
- """Extract all files from archive to given folder name.
-
- Will not extract if
- target folder already exists, or if --skipextract was given on the
- command-line.
-
- Args:
- archive (str): Archive name to extract
- extract_folder_name (str): Folder name to extract to
- cmd (List[str]): Command with arguments to use
- errcode (int): Error code to use for exit()
- """
- global args, content_location
-
- extract_location = content_location.joinpath(extract_folder_name)
-
- pre_extract = set(content_location.glob("*"))
-
- if not args.skipextract or not extract_location.exists():
- print(f"Extracting files from {archive}...")
- cmd.append(content_location.joinpath(archive))
- execute_command(cmd, cmd[0], errcode, cwd=content_location)
- # in case we use a non-release archive the naming will be incorrect.
- # simply rename to expected target name
- post_extract = set(content_location.glob("*"))
- diff_extract = post_extract - pre_extract
- if not extract_location in diff_extract:
- folder_to_rename = list(diff_extract)[0]
- folder_to_rename.rename(extract_location)
- print(" OK")
- else:
- print(f"Skipping extraction {archive}!")
-
-# ==============================================================================
-
-
-parser = argparse.ArgumentParser()
-
-parser.add_argument("--baseurl", required=True,
- help="The base URL for files to download, "
- "i.e. https://download.blender.org/release/Blender2.83/")
-
-parser.add_argument("--version", required=True,
- help="The Blender version to release, in the form 2.83.3")
-
-parser.add_argument("--appid", required=True,
- help="The Blender App ID on Steam")
-parser.add_argument("--winid", required=True,
- help="The Windows depot ID")
-parser.add_argument("--linuxid", required=True,
- help="The Linux depot ID")
-parser.add_argument("--macosid", required=True,
- help="The MacOS depot ID")
-
-parser.add_argument("--steamcmd", required=True,
- help="Path to the steamcmd")
-parser.add_argument("--steamuser", required=True,
- help="The login for the Steam builder user")
-parser.add_argument("--steampw", required=True,
- help="Login password for the Steam builder user")
-
-add_optional_argument("--dryrun",
- "If set the Steam files will not be uploaded")
-add_optional_argument("--leavearch",
- help="If set don't clean up the downloaded archives")
-add_optional_argument("--leaveextracted",
- help="If set don't clean up the extraction folders")
-add_optional_argument("--skipdl",
- help="If set downloading the archives is skipped if it already exists locally.")
-add_optional_argument("--skipextract",
- help="If set skips extracting of archives. The tool assumes the archives"
- "have already been extracted to their correct locations")
-
-args = parser.parse_args()
-
-VERSIONNODOTS = args.version.replace('.', '')
-OUTPUT = f"output{VERSIONNODOTS}"
-CONTENT = f"content{VERSIONNODOTS}"
-
-# ===== set up main locations
-
-content_location = pathlib.Path(".", CONTENT).absolute()
-output_location = pathlib.Path(".", OUTPUT).absolute()
-
-content_location.mkdir(parents=True, exist_ok=True)
-output_location.mkdir(parents=True, exist_ok=True)
-
-# ===== login
-
-# Logging into Steam once to ensure the SDK updates itself properly. If we don't
-# do that the combined +login and +run_app_build_http at the end of the tool
-# will fail.
-steam_login = [args.steamcmd,
- "+login",
- args.steamuser,
- args.steampw,
- "+quit"
- ]
-print("Logging in to Steam...")
-execute_command(steam_login, "Login to Steam", 10)
-print(" OK")
-
-# ===== prepare Steam build scripts
-
-template_vars = [
- ("[APPID]", args.appid),
- ("[OUTPUT]", OUTPUT),
- ("[CONTENT]", CONTENT),
- ("[VERSION]", args.version),
- ("[WINID]", args.winid),
- ("[LINUXID]", args.linuxid),
- ("[MACOSID]", args.macosid),
- ("[DRYRUN]", f"{args.dryrun}" if args.dryrun else "0")
-]
-
-blender_app_build = create_build_script(
- "blender_app_build.vdf.template", template_vars)
-create_build_script("depot_build_win.vdf.template", template_vars)
-create_build_script("depot_build_linux.vdf.template", template_vars)
-create_build_script("depot_build_macos.vdf.template", template_vars)
-
-# ===== download archives
-
-download_archives(args.baseurl, blender_archives,
- args.version, content_location)
-
-# ===== set up file and folder names
-
-zipped_blender = get_archive_type("zip", args.version)
-zip_extract_folder = zipped_blender.replace(".zip", "")
-tarxz_blender = get_archive_type("tar.xz", args.version)
-tarxz_extract_folder = tarxz_blender.replace(".tar.xz", "")
-dmg_blender = get_archive_type("dmg", args.version)
-dmg_extract_folder = dmg_blender.replace(".dmg", "")
-
-# ===== extract
-
-unzip_cmd = ["unzip", "-q"]
-extract_archive(zipped_blender, zip_extract_folder, unzip_cmd, 3)
-
-untarxz_cmd = ["tar", "-xf"]
-extract_archive(tarxz_blender, tarxz_extract_folder, untarxz_cmd, 4)
-
-if not args.skipextract or not content_location.joinpath(dmg_extract_folder).exists():
- print("Extracting files from Blender MacOS archive...")
- blender_dmg = content_location.joinpath(dmg_blender)
- target_location = content_location.joinpath(
- dmg_extract_folder, "Blender.app")
- copy_contents_from_dmg_to_path(blender_dmg, target_location)
- print(" OK")
-else:
- print("Skipping extraction of .dmg!")
-
-# ===== building
-
-print("Build Steam game files...")
-steam_build = [args.steamcmd,
- "+login",
- args.steamuser,
- args.steampw,
- "+run_app_build_http",
- blender_app_build.absolute(),
- "+quit"
- ]
-execute_command(steam_build, "Build with steamcmd", 13)
-print(" OK")
-
-clean_up()
diff --git a/release/steam/depot_build_linux.vdf.template b/release/steam/depot_build_linux.vdf.template
deleted file mode 100644
index 0f69008548e..00000000000
--- a/release/steam/depot_build_linux.vdf.template
+++ /dev/null
@@ -1,31 +0,0 @@
-"DepotBuildConfig"
-{
- // Set your assigned depot ID here
- "DepotID" "[LINUXID]"
-
- // Set a root for all content.
- // All relative paths specified below (LocalPath in FileMapping entries, and FileExclusion paths)
- // will be resolved relative to this root.
- // If you don't define ContentRoot, then it will be assumed to be
- // the location of this script file, which probably isn't what you want
- "ContentRoot" "./blender-[VERSION]-linux64/"
-
- // include all files recursivley
- "FileMapping"
- {
- // This can be a full path, or a path relative to ContentRoot
- "LocalPath" "*"
-
- // This is a path relative to the install folder of your game
- "DepotPath" "."
-
- // If LocalPath contains wildcards, setting this means that all
- // matching files within subdirectories of LocalPath will also
- // be included.
- "recursive" "1"
- }
-
- // but exclude all symbol files
- // This can be a full path, or a path relative to ContentRoot
- "FileExclusion" "*.pdb"
-}
diff --git a/release/steam/depot_build_macos.vdf.template b/release/steam/depot_build_macos.vdf.template
deleted file mode 100644
index 33dde860462..00000000000
--- a/release/steam/depot_build_macos.vdf.template
+++ /dev/null
@@ -1,30 +0,0 @@
-"DepotBuildConfig"
-{
- // Set your assigned depot ID here
- "DepotID" "[MACOSID]"
-
- // Set a root for all content.
- // All relative paths specified below (LocalPath in FileMapping entries, and FileExclusion paths)
- // will be resolved relative to this root.
- // If you don't define ContentRoot, then it will be assumed to be
- // the location of this script file, which probably isn't what you want
- "ContentRoot" "./blender-[VERSION]-macOS/"
- // include all files recursivley
- "FileMapping"
- {
- // This can be a full path, or a path relative to ContentRoot
- "LocalPath" "*"
-
- // This is a path relative to the install folder of your game
- "DepotPath" "."
-
- // If LocalPath contains wildcards, setting this means that all
- // matching files within subdirectories of LocalPath will also
- // be included.
- "recursive" "1"
- }
-
- // but exclude all symbol files
- // This can be a full path, or a path relative to ContentRoot
- "FileExclusion" "*.pdb"
-}
diff --git a/release/steam/depot_build_win.vdf.template b/release/steam/depot_build_win.vdf.template
deleted file mode 100644
index 2c18a0f15dd..00000000000
--- a/release/steam/depot_build_win.vdf.template
+++ /dev/null
@@ -1,31 +0,0 @@
-"DepotBuildConfig"
-{
- // Set your assigned depot ID here
- "DepotID" "[WINID]"
-
- // Set a root for all content.
- // All relative paths specified below (LocalPath in FileMapping entries, and FileExclusion paths)
- // will be resolved relative to this root.
- // If you don't define ContentRoot, then it will be assumed to be
- // the location of this script file, which probably isn't what you want
- "ContentRoot" "./blender-[VERSION]-windows64/"
-
- // include all files recursivley
- "FileMapping"
- {
- // This can be a full path, or a path relative to ContentRoot
- "LocalPath" "*"
-
- // This is a path relative to the install folder of your game
- "DepotPath" "."
-
- // If LocalPath contains wildcards, setting this means that all
- // matching files within subdirectories of LocalPath will also
- // be included.
- "recursive" "1"
- }
-
- // but exclude all symbol files
- // This can be a full path, or a path relative to ContentRoot
- "FileExclusion" "*.pdb"
-}
diff --git a/release/windows/manifest/blender.exe.manifest.in b/release/windows/manifest/blender.exe.manifest.in
index e73ddf3267b..b516efe24cb 100644
--- a/release/windows/manifest/blender.exe.manifest.in
+++ b/release/windows/manifest/blender.exe.manifest.in
@@ -13,12 +13,6 @@
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
<!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
- <!-- Windows 8 -->
- <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
- <!-- Windows 7 -->
- <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
- <!-- Windows Vista -->
- <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
</application>
</compatibility>
<dependency>
diff --git a/release/windows/msix/README.md b/release/windows/msix/README.md
index 3f661a44066..96f753f0e78 100644
--- a/release/windows/msix/README.md
+++ b/release/windows/msix/README.md
@@ -1,82 +1,4 @@
-create_msix_package
-===================
+Buildbot Configuration
+======================
-This tool is used to create MSIX packages from a given ZiP archive. The MSIX
-package is distributed mainly through the Microsoft Store. It can also be
-installed when downloaded from blender.org. For that to work the MSIX package
-needs to be signed.
-
-Requirements
-============
-
-* MakeAppX.exe - this tool is distributed with the Windows 10 SDK and is used to build the .appx package.
-* MakePri.exe - this tool is distributed with the Windows 10 SDK and is used to generate a resources file.
-* SignTool.exe - this tool is distributed with the Windows 10 SDK and is used to sign the .appx package.
-* Python 3 (3.7 or later tested) - to run the create_msix_package.py script
-* requests module - can be installed with `pip install requests`
-* PFX file (optional, but strongly recommended) - for signing the resulting MSIX
- package. **NOTE:** If the MSIX package is not signed when uploaded to the Microsoft
- store the validation and certification process can take up to three full
- business day.
-
-Usage
-=====
-
-On the command-line:
-```batch
-set VERSION=2.83.4.0
-set URL=https://download.blender.org/release/Blender2.83/blender-2.83.4-windows64.zip
-set PUBID=CN=PUBIDHERE
-set PFX=X:\path\to\cert.pfx
-set PFXPW=pwhere
-
-python create_msix_package.py --version %VERSION% --url %URL% --publisher %PUBID% --pfx %PFX% --password %PFXPW%
-```
-
-Result will be a MSIX package with the name `blender-2.83.4-windows64.msix`.
-With the above usage it will be signed. If the signing options are left out the
-package will not be signed.
-
-Optional arguments
-==================
-
-In support of testing and developing the manifest and scripts there are a few
-optional arguments:
-
-* `--skipdl` : If a `blender.zip` is available already next to the tool use this
- to skip actual downloading of the archive designated by `--url`. The latter
- option is still required
-* `--overwrite` : When script fails the final clean-up may be incomplete leaving
- the `Content` folder with its structure. Specify this argument to automatically
- clean up this folder before starting to seed the `Content` folder
-* `--leavezip` : When specified leave the `blender.zip` file while cleaning up
- all other intermediate files, including the `Content` folder. This is useful
- to not have to re-download the same archive from `--url` on each usage
-
-
-What it does
-============
-
-The tool creates in the directory it lives a subfolder called `Content`. This is
-where all necessary files are placed.
-
-The `Assets` folder is copied to the `Content` folder.
-
-From the application manifest template a version with necessary parts replaced as
-their actual values as specified on the command-line is realized. This manifest controls the packaging of Blender into the MSIX format.
-
-Next the tool downloads the designated ZIP archive locally as blender.zip. From
-this archive the files are extracted into the `Content\Blender` folder, but skip
-the leading part of paths in the ZIP. We want to write the files to the
-content_blender_folder where blender.exe ends up as
-`Content\Blender\blender.exe`, and not
-`Content\Blender\blender-2.83.4-windows64\blender.exe`
-
-Once the extraction is completed the MakeAppX tool is executed with the `Content`
-folder as input. The result will be the MSIX package with the name in the form
-`blender-X.YY.Z-windows64.msix`.
-
-If the PFX file and its password are given on the command-line this MSIX package
-will be signed.
-
-All intermediate files and directories will be removed.
+Files used by Buildbot's `package-code-store-windows` step.
diff --git a/release/windows/msix/create_msix_package.py b/release/windows/msix/create_msix_package.py
deleted file mode 100644
index 3e41484eef5..00000000000
--- a/release/windows/msix/create_msix_package.py
+++ /dev/null
@@ -1,197 +0,0 @@
-#!/usr/bin/env python3
-
-import argparse
-import os
-import pathlib
-import requests
-import shutil
-import subprocess
-import zipfile
-
-parser = argparse.ArgumentParser()
-parser.add_argument(
- "--version",
- required=True,
- help="Version string in the form of 2.83.3.0",
-)
-parser.add_argument(
- "--url",
- required=True,
- help="Location of the release ZIP archive to download",
-)
-parser.add_argument(
- "--publisher",
- required=True,
- help="A string in the form of 'CN=PUBLISHER'",
-)
-parser.add_argument(
- "--pfx",
- required=False,
- help="Absolute path to the PFX file used for signing the resulting MSIX package",
-)
-parser.add_argument(
- "--password",
- required=False,
- default="blender",
- help="Password for the PFX file",
-)
-parser.add_argument(
- "--lts",
- required=False,
- help="If set this MSIX is for an LTS release",
- action='store_const',
- const=1,
-)
-parser.add_argument(
- "--skipdl",
- required=False,
- help="If set skip downloading of the specified URL as blender.zip. The tool assumes blender.zip exists",
- action='store_const',
- const=1,
-)
-parser.add_argument(
- "--leavezip",
- required=False,
- help="If set don't clean up the downloaded blender.zip",
- action='store_const',
- const=1,
-)
-parser.add_argument(
- "--overwrite",
- required=False,
- help="If set remove Content folder if it already exists",
- action='store_const',
- const=1,
-)
-args = parser.parse_args()
-
-
-def execute_command(cmd: list, name: str, errcode: int):
- """
- Execute given command in cmd. Output is captured. If an error
- occurs name is used to print ERROR message, along with stderr and
- stdout of the process if either was captured.
- """
- cmd_process = subprocess.run(cmd, capture_output=True, encoding="UTF-8")
- if cmd_process.returncode != 0:
- print(f"ERROR: {name} failed.")
- if cmd_process.stdout:
- print(cmd_process.stdout)
- if cmd_process.stderr:
- print(cmd_process.stderr)
- exit(errcode)
-
-
-LTSORNOT = ""
-PACKAGETYPE = ""
-if args.lts:
- versionparts = args.version.split(".")
- LTSORNOT = f" {versionparts[0]}.{versionparts[1]} LTS"
- PACKAGETYPE = f"{versionparts[0]}.{versionparts[1]}LTS"
-
-blender_package_msix = pathlib.Path(".", f"blender-{args.version}-windows64.msix").absolute()
-content_folder = pathlib.Path(".", "Content")
-content_blender_folder = pathlib.Path(content_folder, "Blender").absolute()
-content_assets_folder = pathlib.Path(content_folder, "Assets")
-assets_original_folder = pathlib.Path(".", "Assets")
-
-pri_config_file = pathlib.Path(".", "priconfig.xml")
-pri_resources_file = pathlib.Path(content_folder, "resources.pri")
-
-local_blender_zip = pathlib.Path(".", "blender.zip")
-
-if args.pfx:
- pfx_path = pathlib.Path(args.pfx)
- if not pfx_path.exists():
- print("ERROR: PFX file not found. Please ensure you give the correct path to the PFX file on the command-line.")
- exit(1)
- print(f"Creating MSIX package with signing using PFX file at {pfx_path}")
-else:
- pfx_path = None
- print("Creating MSIX package without signing.")
-
-pri_command = ["makepri",
- "new",
- "/pr", f"{content_folder.absolute()}",
- "/cf", f"{pri_config_file.absolute()}",
- "/of", f"{pri_resources_file.absolute()}"
- ]
-
-msix_command = ["makeappx",
- "pack",
- "/h", "SHA256",
- "/d", f"{content_folder.absolute()}",
- "/p", f"{blender_package_msix}"
- ]
-if pfx_path:
- sign_command = ["signtool",
- "sign",
- "/fd", "sha256",
- "/a", "/f", f"{pfx_path.absolute()}",
- "/p", f"{args.password}",
- f"{blender_package_msix}"
- ]
-
-if args.overwrite:
- if content_folder.joinpath("Assets").exists():
- shutil.rmtree(content_folder)
-content_folder.mkdir(exist_ok=True)
-shutil.copytree(assets_original_folder, content_assets_folder)
-
-manifest_text = pathlib.Path("AppxManifest.xml.template").read_text()
-manifest_text = manifest_text.replace("[VERSION]", args.version)
-manifest_text = manifest_text.replace("[PUBLISHER]", args.publisher)
-manifest_text = manifest_text.replace("[LTSORNOT]", LTSORNOT)
-manifest_text = manifest_text.replace("[PACKAGETYPE]", PACKAGETYPE)
-pathlib.Path(content_folder, "AppxManifest.xml").write_text(manifest_text)
-
-if not args.skipdl:
- print(f"Downloading blender archive {args.url} to {local_blender_zip}...")
-
- with open(local_blender_zip, "wb") as download_zip:
- response = requests.get(args.url)
- download_zip.write(response.content)
-
- print("... download complete.")
-else:
- print("Skipping download")
-
-print(f"Extracting files from ZIP to {content_blender_folder}...")
-
-# Extract the files from the ZIP archive, but skip the leading part of paths
-# in the ZIP. We want to write the files to the content_blender_folder where
-# blender.exe ends up as ./Content/Blender/blender.exe, and not
-# ./Content/Blender/blender-2.83.3-windows64/blender.exe
-with zipfile.ZipFile(local_blender_zip, "r") as blender_zip:
- for entry in blender_zip.infolist():
- if entry.is_dir():
- continue
- entry_location = pathlib.Path(entry.filename)
- target_location = content_blender_folder.joinpath(*entry_location.parts[1:])
- pathlib.Path(target_location.parent).mkdir(parents=True, exist_ok=True)
- extracted_entry = blender_zip.read(entry)
- target_location.write_bytes(extracted_entry)
-
-print("... extraction complete.")
-
-
-print(f"Generating Package Resource Index (PRI) file using command: {' '.join(pri_command)}")
-execute_command(pri_command, "MakePri", 4)
-
-print(f"Creating MSIX package using command: {' '.join(msix_command)}")
-
-# Remove MSIX file if it already exists. Otherwise the MakeAppX tool
-# will hang.
-if blender_package_msix.exists():
- os.remove(blender_package_msix)
-execute_command(msix_command, "MakeAppX", 2)
-
-if args.pfx:
- print(f"Signing MSIX package using command: {' '.join(sign_command)}")
- execute_command(sign_command, "SignTool", 3)
-
-if not args.leavezip:
- os.remove(local_blender_zip)
-shutil.rmtree(content_folder)
-
-print("Done.")
diff --git a/source/blender/blendthumb/CMakeLists.txt b/source/blender/blendthumb/CMakeLists.txt
index cb121cb9c8d..b42ca284ecb 100644
--- a/source/blender/blendthumb/CMakeLists.txt
+++ b/source/blender/blendthumb/CMakeLists.txt
@@ -31,6 +31,7 @@ set(SRC
string(APPEND CMAKE_SHARED_LINKER_FLAGS_DEBUG " /nodefaultlib:MSVCRT.lib")
add_library(BlendThumb SHARED ${SRC})
+setup_platform_linker_flags(BlendThumb)
target_link_libraries(BlendThumb ${ZLIB_LIBRARIES})
install(
diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c
index f83ee409187..b7c226ada1d 100644
--- a/source/blender/blenfont/intern/blf_font.c
+++ b/source/blender/blenfont/intern/blf_font.c
@@ -41,6 +41,7 @@
#include "BLI_listbase.h"
#include "BLI_math.h"
+#include "BLI_math_color_blend.h"
#include "BLI_rect.h"
#include "BLI_string.h"
#include "BLI_string_utf8.h"
@@ -640,18 +641,12 @@ static void blf_font_draw_buffer_ex(FontBLF *font,
(size_t)buf_info->ch);
float *fbuf = buf_info->fbuf + buf_ofs;
- if (a >= 1.0f) {
- fbuf[0] = b_col_float[0];
- fbuf[1] = b_col_float[1];
- fbuf[2] = b_col_float[2];
- fbuf[3] = 1.0f;
- }
- else {
- fbuf[0] = (b_col_float[0] * a) + (fbuf[0] * (1.0f - a));
- fbuf[1] = (b_col_float[1] * a) + (fbuf[1] * (1.0f - a));
- fbuf[2] = (b_col_float[2] * a) + (fbuf[2] * (1.0f - a));
- fbuf[3] = MIN2(fbuf[3] + a, 1.0f); /* clamp to 1.0 */
- }
+ float font_pixel[4];
+ font_pixel[0] = b_col_float[0] * a;
+ font_pixel[1] = b_col_float[1] * a;
+ font_pixel[2] = b_col_float[2] * a;
+ font_pixel[3] = a;
+ blend_color_mix_float(fbuf, fbuf, font_pixel);
}
}
@@ -677,19 +672,12 @@ static void blf_font_draw_buffer_ex(FontBLF *font,
(size_t)buf_info->ch);
unsigned char *cbuf = buf_info->cbuf + buf_ofs;
- if (a >= 1.0f) {
- cbuf[0] = b_col_char[0];
- cbuf[1] = b_col_char[1];
- cbuf[2] = b_col_char[2];
- cbuf[3] = 255;
- }
- else {
- cbuf[0] = (unsigned char)((b_col_char[0] * a) + (cbuf[0] * (1.0f - a)));
- cbuf[1] = (unsigned char)((b_col_char[1] * a) + (cbuf[1] * (1.0f - a)));
- cbuf[2] = (unsigned char)((b_col_char[2] * a) + (cbuf[2] * (1.0f - a)));
- /* clamp to 255 */
- cbuf[3] = (unsigned char)MIN2((int)cbuf[3] + (int)(a * 255), 255);
- }
+ uchar font_pixel[4];
+ font_pixel[0] = b_col_char[0];
+ font_pixel[1] = b_col_char[1];
+ font_pixel[2] = b_col_char[2];
+ font_pixel[3] = unit_float_to_uchar_clamp(a);
+ blend_color_mix_byte(cbuf, cbuf, font_pixel);
}
}
diff --git a/source/blender/blenfont/intern/blf_font_win32_compat.c b/source/blender/blenfont/intern/blf_font_win32_compat.c
index 7d130204c07..e573d3bd224 100644
--- a/source/blender/blenfont/intern/blf_font_win32_compat.c
+++ b/source/blender/blenfont/intern/blf_font_win32_compat.c
@@ -66,7 +66,7 @@ static unsigned long ft_ansi_stream_io(FT_Stream stream,
file = STREAM_FILE(stream);
if (stream->pos != offset) {
- fseek(file, offset, SEEK_SET);
+ BLI_fseek(file, offset, SEEK_SET);
}
return fread(buffer, 1, count, file);
@@ -93,7 +93,7 @@ static FT_Error FT_Stream_Open__win32_compat(FT_Stream stream, const char *filep
return FT_THROW(Cannot_Open_Resource);
}
- fseek(file, 0, SEEK_END);
+ BLI_fseek(file, 0LL, SEEK_END);
stream->size = ftell(file);
if (!stream->size) {
fprintf(stderr,
@@ -104,7 +104,7 @@ static FT_Error FT_Stream_Open__win32_compat(FT_Stream stream, const char *filep
return FT_THROW(Cannot_Open_Stream);
}
- fseek(file, 0, SEEK_SET);
+ BLI_fseek(file, 0LL, SEEK_SET);
stream->descriptor.pointer = file;
stream->read = ft_ansi_stream_io;
diff --git a/source/blender/blenfont/intern/blf_glyph.c b/source/blender/blenfont/intern/blf_glyph.c
index f3c5c057dec..2ec0ca22865 100644
--- a/source/blender/blenfont/intern/blf_glyph.c
+++ b/source/blender/blenfont/intern/blf_glyph.c
@@ -506,7 +506,7 @@ void blf_glyph_render(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, float x, fl
if (gc->texture) {
GPU_texture_free(gc->texture);
}
- gc->texture = GPU_texture_create_1d_array(__func__, w, h, 1, GPU_R8, NULL);
+ gc->texture = GPU_texture_create_2d(__func__, w, h, 1, GPU_R8, NULL);
gc->bitmap_len_landed = 0;
}
diff --git a/source/blender/blenkernel/BKE_action.h b/source/blender/blenkernel/BKE_action.h
index 717cfa607ad..3d81fcba37d 100644
--- a/source/blender/blenkernel/BKE_action.h
+++ b/source/blender/blenkernel/BKE_action.h
@@ -140,7 +140,7 @@ void BKE_pose_channel_free_bbone_cache(struct bPoseChannel_Runtime *runtime);
void BKE_pose_channels_free(struct bPose *pose);
void BKE_pose_channels_free_ex(struct bPose *pose, bool do_id_user);
-void BKE_pose_channels_hash_make(struct bPose *pose);
+void BKE_pose_channels_hash_ensure(struct bPose *pose);
void BKE_pose_channels_hash_free(struct bPose *pose);
void BKE_pose_channels_remove(struct Object *ob,
@@ -161,7 +161,7 @@ void BKE_pose_channel_session_uuid_generate(struct bPoseChannel *pchan);
struct bPoseChannel *BKE_pose_channel_find_name(const struct bPose *pose, const char *name);
struct bPoseChannel *BKE_pose_channel_active(struct Object *ob);
struct bPoseChannel *BKE_pose_channel_active_or_first_selected(struct Object *ob);
-struct bPoseChannel *BKE_pose_channel_verify(struct bPose *pose, const char *name);
+struct bPoseChannel *BKE_pose_channel_ensure(struct bPose *pose, const char *name);
struct bPoseChannel *BKE_pose_channel_get_mirrored(const struct bPose *pose, const char *name);
void BKE_pose_check_uuids_unique_and_report(const struct bPose *pose);
@@ -188,10 +188,6 @@ void BKE_pose_itasc_init(struct bItasc *itasc);
/* Checks if a bone is part of an IK chain or not */
bool BKE_pose_channel_in_IK_chain(struct Object *ob, struct bPoseChannel *pchan);
-/* clears BONE_UNKEYED flags for frame changing */
-// XXX to be deprecated for a more general solution in animsys...
-void framechange_poses_clear_unkeyed(struct Main *bmain);
-
/* Bone Groups API --------------------- */
/* Adds a new bone-group */
@@ -227,6 +223,9 @@ void BKE_pose_blend_read_data(struct BlendDataReader *reader, struct bPose *pose
void BKE_pose_blend_read_lib(struct BlendLibReader *reader, struct Object *ob, struct bPose *pose);
void BKE_pose_blend_read_expand(struct BlendExpander *expander, struct bPose *pose);
+/* action_mirror.c */
+void BKE_action_flip_with_pose(struct bAction *act, struct Object *ob_arm);
+
#ifdef __cplusplus
};
#endif
diff --git a/source/blender/blenkernel/BKE_anim_path.h b/source/blender/blenkernel/BKE_anim_path.h
index ae2d13530ed..9db63080fd9 100644
--- a/source/blender/blenkernel/BKE_anim_path.h
+++ b/source/blender/blenkernel/BKE_anim_path.h
@@ -26,22 +26,28 @@
extern "C" {
#endif
-struct ListBase;
+struct CurveCache;
struct Object;
-struct Path;
/* ---------------------------------------------------- */
/* Curve Paths */
-void free_path(struct Path *path);
-void calc_curvepath(struct Object *ob, struct ListBase *nurbs);
-bool where_on_path(const struct Object *ob,
- float ctime,
- float r_vec[4],
- float r_dir[3],
- float r_quat[4],
- float *r_radius,
- float *r_weight);
+int BKE_anim_path_get_array_size(const struct CurveCache *curve_cache);
+float BKE_anim_path_get_length(const struct CurveCache *curve_cache);
+
+/* This function populates the 'ob->runtime.curve_cache->anim_path_accum_length' data.
+ * You should never have to call this manually as it should already have been called by
+ * 'BKE_displist_make_curveTypes'. Do not call this manually unless you know what you are doing.
+ */
+void BKE_anim_path_calc_data(struct Object *ob);
+
+bool BKE_where_on_path(const struct Object *ob,
+ float ctime,
+ float r_vec[4],
+ float r_dir[3],
+ float r_quat[4],
+ float *r_radius,
+ float *r_weight);
#ifdef __cplusplus
}
diff --git a/source/blender/blenkernel/BKE_armature.h b/source/blender/blenkernel/BKE_armature.h
index a0ba1415b41..3002a9cc10d 100644
--- a/source/blender/blenkernel/BKE_armature.h
+++ b/source/blender/blenkernel/BKE_armature.h
@@ -178,6 +178,7 @@ void BKE_armature_where_is_bone(struct Bone *bone,
void BKE_pose_clear_pointers(struct bPose *pose);
void BKE_pose_remap_bone_pointers(struct bArmature *armature, struct bPose *pose);
void BKE_pchan_rebuild_bbone_handles(struct bPose *pose, struct bPoseChannel *pchan);
+void BKE_pose_channels_clear_with_null_bone(struct bPose *pose, const bool do_id_user);
void BKE_pose_rebuild(struct Main *bmain,
struct Object *ob,
struct bArmature *arm,
@@ -342,6 +343,8 @@ void BKE_pchan_bbone_deform_segment_index(const struct bPoseChannel *pchan,
#define PBONE_SELECTABLE(arm, bone) \
(PBONE_VISIBLE(arm, bone) && !((bone)->flag & BONE_UNSELECTABLE))
+#define PBONE_SELECTED(arm, bone) (((bone)->flag & BONE_SELECTED) & PBONE_VISIBLE(arm, bone))
+
/* context.selected_pose_bones */
#define FOREACH_PCHAN_SELECTED_IN_OBJECT_BEGIN(_ob, _pchan) \
for (bPoseChannel *_pchan = (_ob)->pose->chanbase.first; _pchan; _pchan = _pchan->next) { \
diff --git a/source/blender/blenkernel/BKE_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh
index 120b4e08b9c..c3f7dbd4bd9 100644
--- a/source/blender/blenkernel/BKE_attribute_access.hh
+++ b/source/blender/blenkernel/BKE_attribute_access.hh
@@ -20,299 +20,342 @@
#include "FN_cpp_type.hh"
#include "FN_generic_span.hh"
+#include "FN_generic_virtual_array.hh"
#include "BKE_attribute.h"
#include "BLI_color.hh"
#include "BLI_float2.hh"
#include "BLI_float3.hh"
-
-namespace blender::bke {
-
-using fn::CPPType;
-
-const CPPType *custom_data_type_to_cpp_type(const CustomDataType type);
-CustomDataType cpp_type_to_custom_data_type(const CPPType &type);
-CustomDataType attribute_data_type_highest_complexity(Span<CustomDataType> data_types);
-AttributeDomain attribute_domain_highest_priority(Span<AttributeDomain> domains);
+#include "BLI_function_ref.hh"
/**
- * This class offers an indirection for reading an attribute.
- * This is useful for the following reasons:
- * - Blender does not store all attributes the same way.
- * The simplest case are custom data layers with primitive types.
- * A bit more complex are mesh attributes like the position of vertices,
- * which are embedded into the MVert struct.
- * Even more complex to access are vertex weights.
- * - Sometimes attributes are stored on one domain, but we want to access
- * the attribute on a different domain. Therefore, we have to interpolate
- * between the domains.
+ * Contains information about an attribute in a geometry component.
+ * More information can be added in the future. E.g. whether the attribute is builtin and how it is
+ * stored (uv map, vertex group, ...).
*/
-class ReadAttribute {
- protected:
- const AttributeDomain domain_;
- const CPPType &cpp_type_;
- const CustomDataType custom_data_type_;
- const int64_t size_;
-
- /* Protects the span below, so that no two threads initialize it at the same time. */
- mutable std::mutex span_mutex_;
- /* When it is not null, it points to the attribute array or a temporary array that contains all
- * the attribute values. */
- mutable void *array_buffer_ = nullptr;
- /* Is true when the buffer above is owned by the attribute accessor. */
- mutable bool array_is_temporary_ = false;
+struct AttributeMetaData {
+ AttributeDomain domain;
+ CustomDataType data_type;
- public:
- ReadAttribute(AttributeDomain domain, const CPPType &cpp_type, const int64_t size)
- : domain_(domain),
- cpp_type_(cpp_type),
- custom_data_type_(cpp_type_to_custom_data_type(cpp_type)),
- size_(size)
+ constexpr friend bool operator==(AttributeMetaData a, AttributeMetaData b)
{
+ return (a.domain == b.domain) && (a.data_type == b.data_type);
}
+};
- virtual ~ReadAttribute();
-
- AttributeDomain domain() const
+/**
+ * Base class for the attribute initializer types described below.
+ */
+struct AttributeInit {
+ enum class Type {
+ Default,
+ VArray,
+ MoveArray,
+ };
+ Type type;
+ AttributeInit(const Type type) : type(type)
{
- return domain_;
}
+};
- const CPPType &cpp_type() const
+/**
+ * Create an attribute using the default value for the data type.
+ * The default values may depend on the attribute provider implementation.
+ */
+struct AttributeInitDefault : public AttributeInit {
+ AttributeInitDefault() : AttributeInit(Type::Default)
{
- return cpp_type_;
}
+};
- CustomDataType custom_data_type() const
- {
- return custom_data_type_;
- }
+/**
+ * Create an attribute by copying data from an existing virtual array. The virtual array
+ * must have the same type as the newly created attribute.
+ *
+ * Note that this can be used to fill the new attribute with the default
+ */
+struct AttributeInitVArray : public AttributeInit {
+ const blender::fn::GVArray *varray;
- int64_t size() const
+ AttributeInitVArray(const blender::fn::GVArray *varray)
+ : AttributeInit(Type::VArray), varray(varray)
{
- return size_;
}
+};
+
+/**
+ * Create an attribute with a by passing ownership of a pre-allocated contiguous array of data.
+ * Sometimes data is created before a geometry component is available. In that case, it's
+ * preferable to move data directly to the created attribute to avoid a new allocation and a copy.
+ *
+ * Note that this will only have a benefit for attributes that are stored directly as contiguous
+ * arrays, so not for some built-in attributes.
+ *
+ * The array must be allocated with MEM_*, since `attribute_try_create` will free the array if it
+ * can't be used directly, and that is generally how Blender expects custom data to be allocated.
+ */
+struct AttributeInitMove : public AttributeInit {
+ void *data = nullptr;
- void get(const int64_t index, void *r_value) const
+ AttributeInitMove(void *data) : AttributeInit(Type::MoveArray), data(data)
{
- BLI_assert(index < size_);
- this->get_internal(index, r_value);
}
+};
+
+/* Returns false when the iteration should be stopped. */
+using AttributeForeachCallback = blender::FunctionRef<bool(blender::StringRefNull attribute_name,
+ const AttributeMetaData &meta_data)>;
- /* Get a span that contains all attribute values. */
- fn::GSpan get_span() const;
+namespace blender::bke {
- template<typename T> Span<T> get_span() const
+using fn::CPPType;
+using fn::GVArray;
+using fn::GVArrayPtr;
+using fn::GVMutableArray;
+using fn::GVMutableArrayPtr;
+
+const CPPType *custom_data_type_to_cpp_type(const CustomDataType type);
+CustomDataType cpp_type_to_custom_data_type(const CPPType &type);
+CustomDataType attribute_data_type_highest_complexity(Span<CustomDataType> data_types);
+AttributeDomain attribute_domain_highest_priority(Span<AttributeDomain> domains);
+
+/**
+ * Used when looking up a "plain attribute" based on a name for reading from it.
+ */
+struct ReadAttributeLookup {
+ /* The virtual array that is used to read from this attribute. */
+ GVArrayPtr varray;
+ /* Domain the attribute lives on in the geometry. */
+ AttributeDomain domain;
+
+ /* Convenience function to check if the attribute has been found. */
+ operator bool() const
{
- return this->get_span().typed<T>();
+ return this->varray.get() != nullptr;
}
+};
- protected:
- /* r_value is expected to be uninitialized. */
- virtual void get_internal(const int64_t index, void *r_value) const = 0;
-
- virtual void initialize_span() const;
+/**
+ * Used when looking up a "plain attribute" based on a name for reading from it and writing to it.
+ */
+struct WriteAttributeLookup {
+ /* The virtual array that is used to read from and write to the attribute. */
+ GVMutableArrayPtr varray;
+ /* Domain the attributes lives on in the geometry. */
+ AttributeDomain domain;
+
+ /* Convenience function to check if the attribute has been found. */
+ operator bool() const
+ {
+ return this->varray.get() != nullptr;
+ }
};
/**
- * This exists for similar reasons as the ReadAttribute class, except that
- * it does not deal with interpolation between domains.
+ * An output attribute allows writing to an attribute (and optionally reading as well). It adds
+ * some convenience features on top of `GVMutableArray` that are very commonly used.
+ *
+ * Supported convenience features:
+ * - Implicit type conversion when writing to builtin attributes.
+ * - Supports simple access to a span containing the attribute values (that avoids the use of
+ * VMutableArray_Span in many cases).
+ * - An output attribute can live side by side with an existing attribute with a different domain
+ * or data type. The old attribute will only be overwritten when the #save function is called.
*/
-class WriteAttribute {
- protected:
- const AttributeDomain domain_;
- const CPPType &cpp_type_;
- const CustomDataType custom_data_type_;
- const int64_t size_;
-
- /* When not null, this points either to the attribute array or to a temporary array. */
- void *array_buffer_ = nullptr;
- /* True, when the buffer points to a temporary array. */
- bool array_is_temporary_ = false;
- /* This helps to protect against forgetting to apply changes done to the array. */
- bool array_should_be_applied_ = false;
+class OutputAttribute {
+ public:
+ using SaveFn = std::function<void(OutputAttribute &)>;
+
+ private:
+ GVMutableArrayPtr varray_;
+ AttributeDomain domain_;
+ SaveFn save_;
+ std::optional<fn::GVMutableArray_GSpan> optional_span_varray_;
+ bool ignore_old_values_ = false;
+ bool save_has_been_called_ = false;
public:
- WriteAttribute(AttributeDomain domain, const CPPType &cpp_type, const int64_t size)
- : domain_(domain),
- cpp_type_(cpp_type),
- custom_data_type_(cpp_type_to_custom_data_type(cpp_type)),
- size_(size)
+ OutputAttribute() = default;
+
+ OutputAttribute(GVMutableArrayPtr varray,
+ AttributeDomain domain,
+ SaveFn save,
+ const bool ignore_old_values)
+ : varray_(std::move(varray)),
+ domain_(domain),
+ save_(std::move(save)),
+ ignore_old_values_(ignore_old_values)
{
}
- virtual ~WriteAttribute();
+ OutputAttribute(OutputAttribute &&other) = default;
- AttributeDomain domain() const
+ ~OutputAttribute();
+
+ operator bool() const
{
- return domain_;
+ return varray_.get() != nullptr;
}
- const CPPType &cpp_type() const
+ GVMutableArray &operator*()
{
- return cpp_type_;
+ return *varray_;
}
- CustomDataType custom_data_type() const
+ GVMutableArray *operator->()
{
- return custom_data_type_;
+ return varray_.get();
}
- int64_t size() const
+ GVMutableArray &varray()
{
- return size_;
+ return *varray_;
}
- void get(const int64_t index, void *r_value) const
+ AttributeDomain domain() const
{
- BLI_assert(index < size_);
- this->get_internal(index, r_value);
+ return domain_;
}
- void set(const int64_t index, const void *value)
+ const CPPType &cpp_type() const
{
- BLI_assert(index < size_);
- this->set_internal(index, value);
+ return varray_->type();
}
- /* Get a span that new attribute values can be written into. When all values have been changed,
- * #apply_span has to be called. */
- fn::GMutableSpan get_span();
- /* The span returned by this method might not contain the current attribute values. */
- fn::GMutableSpan get_span_for_write_only();
- /* Write the changes to the span into the actual attribute, if they aren't already. */
- void apply_span();
-
- template<typename T> MutableSpan<T> get_span()
+ CustomDataType custom_data_type() const
{
- return this->get_span().typed<T>();
+ return cpp_type_to_custom_data_type(this->cpp_type());
}
- template<typename T> MutableSpan<T> get_span_for_write_only()
+ fn::GMutableSpan as_span()
{
- return this->get_span_for_write_only().typed<T>();
+ if (!optional_span_varray_.has_value()) {
+ const bool materialize_old_values = !ignore_old_values_;
+ optional_span_varray_.emplace(*varray_, materialize_old_values);
+ }
+ fn::GVMutableArray_GSpan &span_varray = *optional_span_varray_;
+ return span_varray;
}
- protected:
- virtual void get_internal(const int64_t index, void *r_value) const = 0;
- virtual void set_internal(const int64_t index, const void *value) = 0;
+ template<typename T> MutableSpan<T> as_span()
+ {
+ return this->as_span().typed<T>();
+ }
- virtual void initialize_span(const bool write_only);
- virtual void apply_span_if_necessary();
+ void save();
};
-using ReadAttributePtr = std::unique_ptr<ReadAttribute>;
-using WriteAttributePtr = std::unique_ptr<WriteAttribute>;
-
-/* This provides type safe access to an attribute.
- * The underlying ReadAttribute is owned optionally. */
-template<typename T> class TypedReadAttribute {
+/**
+ * Same as OutputAttribute, but should be used when the data type is known at compile time.
+ */
+template<typename T> class OutputAttribute_Typed {
private:
- std::unique_ptr<const ReadAttribute> owned_attribute_;
- const ReadAttribute *attribute_;
+ OutputAttribute attribute_;
+ std::optional<fn::GVMutableArray_Typed<T>> optional_varray_;
+ VMutableArray<T> *varray_ = nullptr;
public:
- TypedReadAttribute(ReadAttributePtr attribute) : TypedReadAttribute(*attribute)
+ OutputAttribute_Typed(OutputAttribute attribute) : attribute_(std::move(attribute))
{
- owned_attribute_ = std::move(attribute);
- BLI_assert(owned_attribute_);
+ if (attribute_) {
+ optional_varray_.emplace(attribute_.varray());
+ varray_ = &**optional_varray_;
+ }
}
- TypedReadAttribute(const ReadAttribute &attribute) : attribute_(&attribute)
+ operator bool() const
{
- BLI_assert(attribute_->cpp_type().is<T>());
+ return varray_ != nullptr;
}
- int64_t size() const
+ VMutableArray<T> &operator*()
{
- return attribute_->size();
+ return *varray_;
}
- T operator[](const int64_t index) const
+ VMutableArray<T> *operator->()
{
- BLI_assert(index < attribute_->size());
- T value;
- value.~T();
- attribute_->get(index, &value);
- return value;
+ return varray_;
}
- /* Get a span to that contains all attribute values for faster and more convenient access. */
- Span<T> get_span() const
+ VMutableArray<T> &varray()
{
- return attribute_->get_span().template typed<T>();
+ return *varray_;
}
-};
-
-/* This provides type safe access to an attribute.
- * The underlying WriteAttribute is owned optionally. */
-template<typename T> class TypedWriteAttribute {
- private:
- std::unique_ptr<WriteAttribute> owned_attribute_;
- WriteAttribute *attribute_;
- public:
- TypedWriteAttribute(WriteAttributePtr attribute) : TypedWriteAttribute(*attribute)
+ AttributeDomain domain() const
{
- owned_attribute_ = std::move(attribute);
- BLI_assert(owned_attribute_);
+ return attribute_.domain();
}
- TypedWriteAttribute(WriteAttribute &attribute) : attribute_(&attribute)
+ const CPPType &cpp_type() const
{
- BLI_assert(attribute_->cpp_type().is<T>());
+ return CPPType::get<T>();
}
- int64_t size() const
+ CustomDataType custom_data_type() const
{
- return attribute_->size();
+ return cpp_type_to_custom_data_type(this->cpp_type());
}
- T operator[](const int64_t index) const
+ MutableSpan<T> as_span()
{
- BLI_assert(index < attribute_->size());
- T value;
- value.~T();
- attribute_->get(index, &value);
- return value;
+ return attribute_.as_span<T>();
}
- void set(const int64_t index, const T &value)
+ void save()
{
- attribute_->set(index, &value);
+ attribute_.save();
}
+};
- /* Get a span that new values can be written into. Once all values have been updated #apply_span
- * has to be called. */
- MutableSpan<T> get_span()
- {
- return attribute_->get_span().typed<T>();
- }
- /* The span returned by this method might not contain the current attribute values. */
- MutableSpan<T> get_span_for_write_only()
- {
- return attribute_->get_span_for_write_only().typed<T>();
- }
+/**
+ * A basic container around DNA CustomData so that its users
+ * don't have to implement special copy and move constructors.
+ */
+class CustomDataAttributes {
+ /**
+ * #CustomData needs a size to be freed, and unfortunately it isn't stored in the struct
+ * itself, so keep track of the size here so this class can implement its own destructor.
+ * If the implementation of the attribute storage changes, this could be removed.
+ */
+ int size_;
+
+ public:
+ CustomData data;
+
+ CustomDataAttributes();
+ ~CustomDataAttributes();
+ CustomDataAttributes(const CustomDataAttributes &other);
+ CustomDataAttributes(CustomDataAttributes &&other);
+ CustomDataAttributes &operator=(const CustomDataAttributes &other);
+
+ void reallocate(const int size);
- /* Write back all changes to the actual attribute, if necessary. */
- void apply_span()
+ std::optional<blender::fn::GSpan> get_for_read(const blender::StringRef name) const;
+
+ blender::fn::GVArrayPtr get_for_read(const StringRef name,
+ const CustomDataType data_type,
+ const void *default_value) const;
+
+ template<typename T>
+ blender::fn::GVArray_Typed<T> get_for_read(const blender::StringRef name,
+ const T &default_value) const
{
- attribute_->apply_span();
+ const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>();
+ const CustomDataType type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
+ GVArrayPtr varray = this->get_for_read(name, type, &default_value);
+ return blender::fn::GVArray_Typed<T>(std::move(varray));
}
-};
-using BooleanReadAttribute = TypedReadAttribute<bool>;
-using FloatReadAttribute = TypedReadAttribute<float>;
-using Float2ReadAttribute = TypedReadAttribute<float2>;
-using Float3ReadAttribute = TypedReadAttribute<float3>;
-using Int32ReadAttribute = TypedReadAttribute<int>;
-using Color4fReadAttribute = TypedReadAttribute<Color4f>;
-using BooleanWriteAttribute = TypedWriteAttribute<bool>;
-using FloatWriteAttribute = TypedWriteAttribute<float>;
-using Float2WriteAttribute = TypedWriteAttribute<float2>;
-using Float3WriteAttribute = TypedWriteAttribute<float3>;
-using Int32WriteAttribute = TypedWriteAttribute<int>;
-using Color4fWriteAttribute = TypedWriteAttribute<Color4f>;
+ std::optional<blender::fn::GMutableSpan> get_for_write(const blender::StringRef name);
+ bool create(const blender::StringRef name, const CustomDataType data_type);
+ bool create_by_move(const blender::StringRef name, const CustomDataType data_type, void *buffer);
+ bool remove(const blender::StringRef name);
+
+ bool foreach_attribute(const AttributeForeachCallback callback,
+ const AttributeDomain domain) const;
+};
} // namespace blender::bke
diff --git a/source/blender/blenkernel/BKE_attribute_math.hh b/source/blender/blenkernel/BKE_attribute_math.hh
index 16fc0db60fb..ba683362e69 100644
--- a/source/blender/blenkernel/BKE_attribute_math.hh
+++ b/source/blender/blenkernel/BKE_attribute_math.hh
@@ -14,6 +14,8 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#pragma once
+
#include "BLI_array.hh"
#include "BLI_color.hh"
#include "BLI_float2.hh"
@@ -21,13 +23,17 @@
#include "DNA_customdata_types.h"
+#include "FN_cpp_type.hh"
+
namespace blender::attribute_math {
+using fn::CPPType;
+
/**
* Utility function that simplifies calling a templated function based on a custom data type.
*/
template<typename Func>
-void convert_to_static_type(const CustomDataType data_type, const Func &func)
+inline void convert_to_static_type(const CustomDataType data_type, const Func &func)
{
switch (data_type) {
case CD_PROP_FLOAT:
@@ -46,7 +52,7 @@ void convert_to_static_type(const CustomDataType data_type, const Func &func)
func(bool());
break;
case CD_PROP_COLOR:
- func(Color4f());
+ func(ColorGeometry4f());
break;
default:
BLI_assert_unreachable();
@@ -54,6 +60,32 @@ void convert_to_static_type(const CustomDataType data_type, const Func &func)
}
}
+template<typename Func>
+inline void convert_to_static_type(const fn::CPPType &cpp_type, const Func &func)
+{
+ if (cpp_type.is<float>()) {
+ func(float());
+ }
+ else if (cpp_type.is<float2>()) {
+ func(float2());
+ }
+ else if (cpp_type.is<float3>()) {
+ func(float3());
+ }
+ else if (cpp_type.is<int>()) {
+ func(int());
+ }
+ else if (cpp_type.is<bool>()) {
+ func(bool());
+ }
+ else if (cpp_type.is<ColorGeometry4f>()) {
+ func(ColorGeometry4f());
+ }
+ else {
+ BLI_assert_unreachable();
+ }
+}
+
/* -------------------------------------------------------------------- */
/** \name Mix three values of the same type.
*
@@ -91,9 +123,12 @@ inline float3 mix3(const float3 &weights, const float3 &v0, const float3 &v1, co
}
template<>
-inline Color4f mix3(const float3 &weights, const Color4f &v0, const Color4f &v1, const Color4f &v2)
+inline ColorGeometry4f mix3(const float3 &weights,
+ const ColorGeometry4f &v0,
+ const ColorGeometry4f &v1,
+ const ColorGeometry4f &v2)
{
- Color4f result;
+ ColorGeometry4f result;
interp_v4_v4v4v4(result, v0, v1, v2, weights);
return result;
}
@@ -101,6 +136,49 @@ inline Color4f mix3(const float3 &weights, const Color4f &v0, const Color4f &v1,
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Mix two values of the same type.
+ *
+ * This is just basic linear interpolation.
+ * \{ */
+
+template<typename T> T mix2(const float factor, const T &a, const T &b);
+
+template<> inline bool mix2(const float factor, const bool &a, const bool &b)
+{
+ return ((1.0f - factor) * a + factor * b) >= 0.5f;
+}
+
+template<> inline int mix2(const float factor, const int &a, const int &b)
+{
+ return static_cast<int>((1.0f - factor) * a + factor * b);
+}
+
+template<> inline float mix2(const float factor, const float &a, const float &b)
+{
+ return (1.0f - factor) * a + factor * b;
+}
+
+template<> inline float2 mix2(const float factor, const float2 &a, const float2 &b)
+{
+ return float2::interpolate(a, b, factor);
+}
+
+template<> inline float3 mix2(const float factor, const float3 &a, const float3 &b)
+{
+ return float3::interpolate(a, b, factor);
+}
+
+template<>
+inline ColorGeometry4f mix2(const float factor, const ColorGeometry4f &a, const ColorGeometry4f &b)
+{
+ ColorGeometry4f result;
+ interp_v4_v4v4(result, a, b, factor);
+ return result;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Mix a dynamic amount of values with weights for many elements.
*
* This section provides an abstraction for "mixers". The abstraction encapsulates details about
@@ -153,8 +231,10 @@ template<typename T> class SimpleMixer {
}
};
-/** This mixer accumulates values in a type that is different from the one that is mixed. Some
- * types cannot encode the floating point weights in their values (e.g. int and bool). */
+/**
+ * This mixer accumulates values in a type that is different from the one that is mixed.
+ * Some types cannot encode the floating point weights in their values (e.g. int and bool).
+ */
template<typename T, typename AccumulationT, T (*ConvertToT)(const AccumulationT &value)>
class SimpleMixerWithAccumulationType {
private:
@@ -198,15 +278,16 @@ class SimpleMixerWithAccumulationType {
}
};
-class Color4fMixer {
+class ColorGeometryMixer {
private:
- MutableSpan<Color4f> buffer_;
- Color4f default_color_;
+ MutableSpan<ColorGeometry4f> buffer_;
+ ColorGeometry4f default_color_;
Array<float> total_weights_;
public:
- Color4fMixer(MutableSpan<Color4f> buffer, Color4f default_color = {0, 0, 0, 1});
- void mix_in(const int64_t index, const Color4f &color, const float weight = 1.0f);
+ ColorGeometryMixer(MutableSpan<ColorGeometry4f> buffer,
+ ColorGeometry4f default_color = ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f));
+ void mix_in(const int64_t index, const ColorGeometry4f &color, const float weight = 1.0f);
void finalize();
};
@@ -223,10 +304,10 @@ template<> struct DefaultMixerStruct<float2> {
template<> struct DefaultMixerStruct<float3> {
using type = SimpleMixer<float3>;
};
-template<> struct DefaultMixerStruct<Color4f> {
- /* Use a special mixer for colors. Color4f can't be added/multiplied, because this is not
+template<> struct DefaultMixerStruct<ColorGeometry4f> {
+ /* Use a special mixer for colors. ColorGeometry4f can't be added/multiplied, because this is not
* something one should usually do with colors. */
- using type = Color4fMixer;
+ using type = ColorGeometryMixer;
};
template<> struct DefaultMixerStruct<int> {
static int double_to_int(const double &value)
diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h
index c9585430ae2..6ad910ff8ab 100644
--- a/source/blender/blenkernel/BKE_blender_version.h
+++ b/source/blender/blenkernel/BKE_blender_version.h
@@ -31,7 +31,7 @@ extern "C" {
*/
/* Blender major and minor version. */
-#define BLENDER_VERSION 293
+#define BLENDER_VERSION 300
/* Blender patch version for bugfix releases. */
#define BLENDER_VERSION_PATCH 0
/** Blender release cycle stage: alpha/beta/rc/release. */
@@ -39,7 +39,7 @@ extern "C" {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
-#define BLENDER_FILE_SUBVERSION 14
+#define BLENDER_FILE_SUBVERSION 3
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and show a warning if the file
diff --git a/source/blender/blenkernel/BKE_callbacks.h b/source/blender/blenkernel/BKE_callbacks.h
index fadba5644de..ef2a0ed34a0 100644
--- a/source/blender/blenkernel/BKE_callbacks.h
+++ b/source/blender/blenkernel/BKE_callbacks.h
@@ -30,11 +30,60 @@ struct Main;
struct PointerRNA;
/**
- * Common suffix uses:
- * - ``_PRE/_POST``:
- * For handling discrete non-interactive events.
- * - ``_INIT/_COMPLETE/_CANCEL``:
- * For handling jobs (which may in turn cause other handlers to be called).
+ Callbacks for One Off Actions
+ * =============================
+ *
+ * - `{ACTION}` use in cases where only a single callback is required,
+ * `VERSION_UPDATE` and `RENDER_STATS` for example.
+ *
+ * \note avoid single callbacks if there is a chance `PRE/POST` are useful to differentiate
+ * since renaming callbacks may break Python scripts.
+ *
+ * Callbacks for Common Actions
+ * ============================
+ *
+ * - `{ACTION}_PRE` run before the action.
+ * - `{ACTION}_POST` run after the action.
+ *
+ * Optional Additional Callbacks
+ * -----------------------------
+ *
+ * - `{ACTION}_INIT` when the handler may manipulate the context used to run the action.
+ *
+ * Examples where `INIT` functions may be useful are:
+ *
+ * - When rendering, an `INIT` function may change the camera or render settings,
+ * things which a `PRE` function can't support as this information has already been used.
+ * - When saving an `INIT` function could temporarily change the preferences.
+ *
+ * - `{ACTION}_POST_FAIL` should be included if the action may fail.
+ *
+ * Use this so a call to the `PRE` callback always has a matching call to `POST` or `POST_FAIL`.
+ *
+ * \note in most cases only `PRE/POST` are required.
+ *
+ * Callbacks for Background/Modal Tasks
+ * ====================================
+ *
+ * - `{ACTION}_INIT`
+ * - `{ACTION}_COMPLETE` when a background job has finished.
+ * - `{ACTION}_CANCEL` When a background job is canceled partway through.
+ *
+ * While cancellation may be caused by any number of reasons, common causes may include:
+ *
+ * - Explicit user cancellation.
+ * - Exiting Blender.
+ * - Failure to acquire resources (such as disk-full, out of memory ... etc).
+ *
+ * \note `PRE/POST` handlers may be used along side modal task handlers
+ * as is the case for rendering, where rendering an animation uses modal task handlers,
+ * rendering a single frame has `PRE/POST` handlers.
+ *
+ * Python Access
+ * =============
+ *
+ * All callbacks here must be exposed via the Python module `bpy.app.handlers`,
+ * see `bpy_app_handlers.c`.
*/
typedef enum {
BKE_CB_EVT_FRAME_CHANGE_PRE,
@@ -59,6 +108,7 @@ typedef enum {
BKE_CB_EVT_VERSION_UPDATE,
BKE_CB_EVT_LOAD_FACTORY_USERDEF_POST,
BKE_CB_EVT_LOAD_FACTORY_STARTUP_POST,
+ BKE_CB_EVT_XR_SESSION_START_PRE,
BKE_CB_EVT_TOT,
} eCbEvent;
diff --git a/source/blender/blenkernel/BKE_collection.h b/source/blender/blenkernel/BKE_collection.h
index d0fca5e3796..7963d54126e 100644
--- a/source/blender/blenkernel/BKE_collection.h
+++ b/source/blender/blenkernel/BKE_collection.h
@@ -88,7 +88,7 @@ struct Collection *BKE_collection_object_find(struct Main *bmain,
struct Scene *scene,
struct Collection *collection,
struct Object *ob);
-bool BKE_collection_is_empty(struct Collection *collection);
+bool BKE_collection_is_empty(const struct Collection *collection);
bool BKE_collection_object_add(struct Main *bmain,
struct Collection *collection,
@@ -112,7 +112,9 @@ bool BKE_scene_collections_object_remove(struct Main *bmain,
struct Object *object,
const bool free_us);
void BKE_collections_object_remove_nulls(struct Main *bmain);
-void BKE_collections_child_remove_nulls(struct Main *bmain, struct Collection *old_collection);
+void BKE_collections_child_remove_nulls(struct Main *bmain,
+ struct Collection *parent_collection,
+ struct Collection *child_collection);
/* Dependencies. */
@@ -227,6 +229,8 @@ void BKE_scene_objects_iterator_begin(struct BLI_Iterator *iter, void *data_in);
void BKE_scene_objects_iterator_next(struct BLI_Iterator *iter);
void BKE_scene_objects_iterator_end(struct BLI_Iterator *iter);
+struct GSet *BKE_scene_objects_as_gset(struct Scene *scene, struct GSet *objects_gset);
+
#define FOREACH_SCENE_COLLECTION_BEGIN(scene, _instance) \
ITER_BEGIN (BKE_scene_collections_iterator_begin, \
BKE_scene_collections_iterator_next, \
diff --git a/source/blender/blenkernel/BKE_context.h b/source/blender/blenkernel/BKE_context.h
index 3d30188e517..50aa6027840 100644
--- a/source/blender/blenkernel/BKE_context.h
+++ b/source/blender/blenkernel/BKE_context.h
@@ -206,8 +206,25 @@ void CTX_wm_area_set(bContext *C, struct ScrArea *area);
void CTX_wm_region_set(bContext *C, struct ARegion *region);
void CTX_wm_menu_set(bContext *C, struct ARegion *menu);
void CTX_wm_gizmo_group_set(bContext *C, struct wmGizmoGroup *gzgroup);
-const char *CTX_wm_operator_poll_msg_get(struct bContext *C);
+
+/**
+ * Values to create the message that describes the reason poll failed.
+ *
+ * \note This must be called in the same context as the poll function that created it.
+ */
+struct bContextPollMsgDyn_Params {
+ /** The result is allocated . */
+ char *(*get_fn)(bContext *C, void *user_data);
+ /** Optionally free the user-data. */
+ void (*free_fn)(bContext *C, void *user_data);
+ void *user_data;
+};
+
+const char *CTX_wm_operator_poll_msg_get(struct bContext *C, bool *r_free);
void CTX_wm_operator_poll_msg_set(struct bContext *C, const char *msg);
+void CTX_wm_operator_poll_msg_set_dynamic(bContext *C,
+ const struct bContextPollMsgDyn_Params *params);
+void CTX_wm_operator_poll_msg_clear(struct bContext *C);
/* Data Context
*
diff --git a/source/blender/blenkernel/BKE_curve.h b/source/blender/blenkernel/BKE_curve.h
index 660e7c08062..eb9c68f80ec 100644
--- a/source/blender/blenkernel/BKE_curve.h
+++ b/source/blender/blenkernel/BKE_curve.h
@@ -40,7 +40,6 @@ struct MDeformVert;
struct Main;
struct Nurb;
struct Object;
-struct Path;
struct TextBox;
struct rctf;
@@ -50,7 +49,13 @@ typedef struct CurveCache {
ListBase disp;
ListBase bev;
ListBase deformed_nurbs;
- struct Path *path;
+ /* This array contains the accumulative length of the curve segments.
+ * So you can see this as a "total distance traveled" along the curve.
+ * The first entry is the length between point 0 and 1 while the last is the
+ * total length of the curve.
+ *
+ * Used by #BKE_where_on_path. */
+ const float *anim_path_accum_length;
} CurveCache;
/* Definitions needed for shape keys */
@@ -69,16 +74,16 @@ typedef struct CVKeyIndex {
#define SEGMENTSU(nu) (((nu)->flagu & CU_NURB_CYCLIC) ? (nu)->pntsu : (nu)->pntsu - 1)
#define SEGMENTSV(nu) (((nu)->flagv & CU_NURB_CYCLIC) ? (nu)->pntsv : (nu)->pntsv - 1)
-#define CU_DO_TILT(cu, nu) ((((nu)->flag & CU_2D) && ((cu)->flag & CU_3D) == 0) ? 0 : 1)
#define CU_DO_RADIUS(cu, nu) \
- ((CU_DO_TILT(cu, nu) || ((cu)->flag & CU_PATH_RADIUS) || (cu)->bevobj || (cu)->ext1 != 0.0f || \
+ ((((cu)->flag & (CU_PATH_RADIUS | CU_3D)) || (cu)->bevobj || (cu)->ext1 != 0.0f || \
(cu)->ext2 != 0.0f) ? \
1 : \
0)
+#define CU_IS_2D(cu) (((cu)->flag & CU_3D) == 0)
+
/* not 3d and not unfilled */
-#define CU_DO_2DFILL(cu) \
- ((((cu)->flag & CU_3D) == 0) && (((cu)->flag & (CU_FRONT | CU_BACK)) != 0))
+#define CU_DO_2DFILL(cu) (CU_IS_2D(cu) && (((cu)->flag & (CU_FRONT | CU_BACK)) != 0))
/* ** Curve ** */
void BKE_curve_editfont_free(struct Curve *cu);
@@ -86,7 +91,7 @@ void BKE_curve_init(struct Curve *cu, const short curve_type);
struct Curve *BKE_curve_add(struct Main *bmain, const char *name, int type);
short BKE_curve_type_get(const struct Curve *cu);
void BKE_curve_type_test(struct Object *ob);
-void BKE_curve_curve_dimension_update(struct Curve *cu);
+void BKE_curve_dimension_update(struct Curve *cu);
struct BoundBox *BKE_curve_boundbox_get(struct Object *ob);
@@ -108,7 +113,7 @@ void BKE_curve_transform(struct Curve *cu,
const bool do_props);
void BKE_curve_translate(struct Curve *cu, const float offset[3], const bool do_keys);
void BKE_curve_material_index_remove(struct Curve *cu, int index);
-bool BKE_curve_material_index_used(struct Curve *cu, int index);
+bool BKE_curve_material_index_used(const struct Curve *cu, int index);
void BKE_curve_material_index_clear(struct Curve *cu);
bool BKE_curve_material_index_validate(struct Curve *cu);
void BKE_curve_material_remap(struct Curve *cu, const unsigned int *remap, unsigned int remap_len);
@@ -125,8 +130,10 @@ void BKE_curve_nurb_vert_active_set(struct Curve *cu, const struct Nurb *nu, con
bool BKE_curve_nurb_vert_active_get(struct Curve *cu, struct Nurb **r_nu, void **r_vert);
void BKE_curve_nurb_vert_active_validate(struct Curve *cu);
-float (*BKE_curve_nurbs_vert_coords_alloc(struct ListBase *lb, int *r_vert_len))[3];
-void BKE_curve_nurbs_vert_coords_get(struct ListBase *lb, float (*vert_coords)[3], int vert_len);
+float (*BKE_curve_nurbs_vert_coords_alloc(const struct ListBase *lb, int *r_vert_len))[3];
+void BKE_curve_nurbs_vert_coords_get(const struct ListBase *lb,
+ float (*vert_coords)[3],
+ int vert_len);
void BKE_curve_nurbs_vert_coords_apply_with_mat4(struct ListBase *lb,
const float (*vert_coords)[3],
@@ -137,7 +144,7 @@ void BKE_curve_nurbs_vert_coords_apply(struct ListBase *lb,
const float (*vert_coords)[3],
const bool constrain_2d);
-float (*BKE_curve_nurbs_key_vert_coords_alloc(struct ListBase *lb,
+float (*BKE_curve_nurbs_key_vert_coords_alloc(const struct ListBase *lb,
float *key,
int *r_vert_len))[3];
void BKE_curve_nurbs_key_vert_tilts_apply(struct ListBase *lb, const float *key);
@@ -149,7 +156,7 @@ struct ListBase *BKE_curve_editNurbs_get(struct Curve *cu);
void BKE_curve_bevelList_free(struct ListBase *bev);
void BKE_curve_bevelList_make(struct Object *ob, struct ListBase *nurbs, bool for_render);
-void BKE_curve_bevel_make(struct Object *ob, struct ListBase *disp);
+ListBase BKE_curve_bevel_make(const struct Curve *ob);
void BKE_curve_forward_diff_bezier(
float q0, float q1, float q2, float q3, float *p, int it, int stride);
@@ -166,8 +173,8 @@ void BKE_curve_correct_bezpart(const float v1[2], float v2[2], float v3[2], cons
bool BKE_nurbList_index_get_co(struct ListBase *editnurb, const int index, float r_co[3]);
-int BKE_nurbList_verts_count(struct ListBase *nurb);
-int BKE_nurbList_verts_count_without_handles(struct ListBase *nurb);
+int BKE_nurbList_verts_count(const struct ListBase *nurb);
+int BKE_nurbList_verts_count_without_handles(const struct ListBase *nurb);
void BKE_nurbList_free(struct ListBase *lb);
void BKE_nurbList_duplicate(struct ListBase *lb1, const struct ListBase *lb2);
@@ -184,8 +191,8 @@ void BKE_nurb_free(struct Nurb *nu);
struct Nurb *BKE_nurb_duplicate(const struct Nurb *nu);
struct Nurb *BKE_nurb_copy(struct Nurb *src, int pntsu, int pntsv);
-void BKE_nurb_test_2d(struct Nurb *nu);
-void BKE_nurb_minmax(struct Nurb *nu, bool use_radius, float min[3], float max[3]);
+void BKE_nurb_project_2d(struct Nurb *nu);
+void BKE_nurb_minmax(const struct Nurb *nu, bool use_radius, float min[3], float max[3]);
float BKE_nurb_calc_length(const struct Nurb *nu, int resolution);
void BKE_nurb_makeFaces(
diff --git a/source/blender/blenkernel/BKE_displist.h b/source/blender/blenkernel/BKE_displist.h
index 05e60d38487..a2d9bbcd011 100644
--- a/source/blender/blenkernel/BKE_displist.h
+++ b/source/blender/blenkernel/BKE_displist.h
@@ -65,47 +65,36 @@ struct Mesh;
struct Object;
struct Scene;
-/* used for curves, nurbs, mball, importing */
+/* Used for curves, nurbs, meta-balls. */
typedef struct DispList {
struct DispList *next, *prev;
short type, flag;
int parts, nr;
- short col, rt; /* rt used by initrenderNurbs */
+ short col, rt; /* Currently only used for smooth flag. */
float *verts, *nors;
int *index;
int charidx;
int totindex; /* indexed array drawing surfaces */
-
- unsigned int *bevel_split; /* BLI_bitmap */
} DispList;
-void BKE_displist_copy(struct ListBase *lbn, struct ListBase *lb);
-void BKE_displist_elem_free(DispList *dl);
-DispList *BKE_displist_find_or_create(struct ListBase *lb, int type);
+void BKE_displist_copy(struct ListBase *lbn, const struct ListBase *lb);
DispList *BKE_displist_find(struct ListBase *lb, int type);
void BKE_displist_normals_add(struct ListBase *lb);
-void BKE_displist_count(struct ListBase *lb, int *totvert, int *totface, int *tottri);
+void BKE_displist_count(const struct ListBase *lb, int *totvert, int *totface, int *tottri);
void BKE_displist_free(struct ListBase *lb);
-bool BKE_displist_has_faces(struct ListBase *lb);
-
-void BKE_displist_make_surf(struct Depsgraph *depsgraph,
- struct Scene *scene,
- struct Object *ob,
- struct ListBase *dispbase,
- struct Mesh **r_final,
- const bool for_render,
- const bool for_orco);
+bool BKE_displist_has_faces(const struct ListBase *lb);
+
void BKE_displist_make_curveTypes(struct Depsgraph *depsgraph,
- struct Scene *scene,
+ const struct Scene *scene,
struct Object *ob,
const bool for_render,
const bool for_orco);
void BKE_displist_make_curveTypes_forRender(struct Depsgraph *depsgraph,
- struct Scene *scene,
+ const struct Scene *scene,
struct Object *ob,
struct ListBase *dispbase,
- struct Mesh **r_final,
- const bool for_orco);
+ const bool for_orco,
+ struct Mesh **r_final);
void BKE_displist_make_mball(struct Depsgraph *depsgraph, struct Scene *scene, struct Object *ob);
void BKE_displist_make_mball_forRender(struct Depsgraph *depsgraph,
struct Scene *scene,
@@ -113,22 +102,26 @@ void BKE_displist_make_mball_forRender(struct Depsgraph *depsgraph,
struct ListBase *dispbase);
bool BKE_curve_calc_modifiers_pre(struct Depsgraph *depsgraph,
- struct Scene *scene,
+ const struct Scene *scene,
struct Object *ob,
struct ListBase *source_nurb,
struct ListBase *target_nurb,
const bool for_render);
+bool BKE_displist_surfindex_get(
+ const struct DispList *dl, int a, int *b, int *p1, int *p2, int *p3, int *p4);
-bool BKE_displist_surfindex_get(DispList *dl, int a, int *b, int *p1, int *p2, int *p3, int *p4);
void BKE_displist_fill(const struct ListBase *dispbase,
struct ListBase *to,
const float normal_proj[3],
const bool flip_normal);
-float BKE_displist_calc_taper(
- struct Depsgraph *depsgraph, struct Scene *scene, struct Object *taperobj, int cur, int tot);
+float BKE_displist_calc_taper(struct Depsgraph *depsgraph,
+ const struct Scene *scene,
+ struct Object *taperobj,
+ int cur,
+ int tot);
-void BKE_displist_minmax(struct ListBase *dispbase, float min[3], float max[3]);
+void BKE_displist_minmax(const struct ListBase *dispbase, float min[3], float max[3]);
#ifdef __cplusplus
}
diff --git a/source/blender/blenkernel/BKE_editmesh.h b/source/blender/blenkernel/BKE_editmesh.h
index 2fb713a4299..3a1eedfd807 100644
--- a/source/blender/blenkernel/BKE_editmesh.h
+++ b/source/blender/blenkernel/BKE_editmesh.h
@@ -33,6 +33,7 @@ extern "C" {
struct BMLoop;
struct BMesh;
+struct BMPartialUpdate;
struct BoundBox;
struct Depsgraph;
struct Mesh;
@@ -85,6 +86,8 @@ typedef struct BMEditMesh {
/* editmesh.c */
void BKE_editmesh_looptri_calc(BMEditMesh *em);
+void BKE_editmesh_looptri_calc_with_partial(BMEditMesh *em, struct BMPartialUpdate *bmpinfo);
+
BMEditMesh *BKE_editmesh_create(BMesh *bm, const bool do_tessellate);
BMEditMesh *BKE_editmesh_copy(BMEditMesh *em);
BMEditMesh *BKE_editmesh_from_object(struct Object *ob);
diff --git a/source/blender/blenkernel/BKE_fcurve.h b/source/blender/blenkernel/BKE_fcurve.h
index 4569e68ea4a..589d1839dd4 100644
--- a/source/blender/blenkernel/BKE_fcurve.h
+++ b/source/blender/blenkernel/BKE_fcurve.h
@@ -232,6 +232,19 @@ int BKE_fcurve_bezt_binarysearch_index(struct BezTriple array[],
int arraylen,
bool *r_replace);
+/* fcurve_cache.c */
+/* Cached f-curve look-ups, use when this needs to be done many times. */
+struct FCurvePathCache;
+struct FCurvePathCache *BKE_fcurve_pathcache_create(ListBase *list);
+void BKE_fcurve_pathcache_destroy(struct FCurvePathCache *fcache);
+struct FCurve *BKE_fcurve_pathcache_find(struct FCurvePathCache *fcache,
+ const char rna_path[],
+ const int array_index);
+int BKE_fcurve_pathcache_find_array(struct FCurvePathCache *fcache,
+ const char *rna_path,
+ struct FCurve **fcurve_result,
+ int fcurve_result_len);
+
/* get the time extents for F-Curve */
bool BKE_fcurve_calc_range(
struct FCurve *fcu, float *min, float *max, const bool do_sel_only, const bool do_min_length);
@@ -245,6 +258,14 @@ bool BKE_fcurve_calc_bounds(struct FCurve *fcu,
const bool do_sel_only,
const bool include_handles);
+float *BKE_fcurves_calc_keyed_frames_ex(struct FCurve **fcurve_array,
+ const int fcurve_array_len,
+ const float interval,
+ int *r_frames_len);
+float *BKE_fcurves_calc_keyed_frames(struct FCurve **fcurve_array,
+ const int fcurve_array_len,
+ int *r_frames_len);
+
void BKE_fcurve_active_keyframe_set(struct FCurve *fcu, const struct BezTriple *active_bezt);
int BKE_fcurve_active_keyframe_index(const struct FCurve *fcu);
diff --git a/source/blender/blenkernel/BKE_geometry_set.h b/source/blender/blenkernel/BKE_geometry_set.h
index 08b4a25d946..5f6a9ec7b91 100644
--- a/source/blender/blenkernel/BKE_geometry_set.h
+++ b/source/blender/blenkernel/BKE_geometry_set.h
@@ -36,30 +36,13 @@ typedef enum GeometryComponentType {
GEO_COMPONENT_TYPE_POINT_CLOUD = 1,
GEO_COMPONENT_TYPE_INSTANCES = 2,
GEO_COMPONENT_TYPE_VOLUME = 3,
+ GEO_COMPONENT_TYPE_CURVE = 4,
} GeometryComponentType;
void BKE_geometry_set_free(struct GeometrySet *geometry_set);
bool BKE_geometry_set_has_instances(const struct GeometrySet *geometry_set);
-typedef enum InstancedDataType {
- INSTANCE_DATA_TYPE_OBJECT = 0,
- INSTANCE_DATA_TYPE_COLLECTION = 1,
-} InstancedDataType;
-
-typedef struct InstancedData {
- InstancedDataType type;
- union {
- struct Object *object;
- struct Collection *collection;
- } data;
-} InstancedData;
-
-int BKE_geometry_set_instances(const struct GeometrySet *geometry_set,
- float (**r_transforms)[4][4],
- const int **r_almost_unique_ids,
- struct InstancedData **r_instanced_data);
-
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh
index 8cc37a3e711..b2342a5fd96 100644
--- a/source/blender/blenkernel/BKE_geometry_set.hh
+++ b/source/blender/blenkernel/BKE_geometry_set.hh
@@ -25,11 +25,11 @@
#include "BLI_float3.hh"
#include "BLI_float4x4.hh"
-#include "BLI_function_ref.hh"
#include "BLI_hash.hh"
#include "BLI_map.hh"
#include "BLI_set.hh"
#include "BLI_user_counter.hh"
+#include "BLI_vector_set.hh"
#include "BKE_attribute_access.hh"
#include "BKE_geometry_set.h"
@@ -39,6 +39,8 @@ struct Mesh;
struct Object;
struct PointCloud;
struct Volume;
+struct Curve;
+struct CurveEval;
enum class GeometryOwnershipType {
/* The geometry is owned. This implies that it can be changed. */
@@ -56,74 +58,6 @@ class ComponentAttributeProviders;
class GeometryComponent;
/**
- * An #OutputAttributePtr wraps a #WriteAttributePtr that might not be stored in its final
- * destination yet. Therefore, once the attribute has been filled with data, the #save method has
- * to be called, to store the attribute where it belongs (possibly by replacing an existing
- * attribute with the same name).
- *
- * This is useful for example in the Attribute Color Ramp node, when the same attribute name is
- * used as input and output. Typically the input is a float attribute, and the output is a color.
- * Those two attributes cannot exist at the same time, due to a name collision. To handle this
- * situation well, first the output colors have to be computed before the input floats are deleted.
- * Therefore, the outputs have to be written to a temporary buffer that replaces the existing
- * attribute once all computations are done.
- */
-class OutputAttributePtr {
- private:
- blender::bke::WriteAttributePtr attribute_;
-
- public:
- OutputAttributePtr() = default;
- OutputAttributePtr(blender::bke::WriteAttributePtr attribute);
- OutputAttributePtr(GeometryComponent &component,
- AttributeDomain domain,
- std::string name,
- CustomDataType data_type);
-
- ~OutputAttributePtr();
-
- /* Returns false, when this wrapper is empty. */
- operator bool() const
- {
- return static_cast<bool>(attribute_);
- }
-
- /* Get a reference to the underlying #WriteAttribute. */
- blender::bke::WriteAttribute &get()
- {
- BLI_assert(attribute_);
- return *attribute_;
- }
-
- blender::bke::WriteAttribute &operator*()
- {
- return *attribute_;
- }
-
- blender::bke::WriteAttribute *operator->()
- {
- return attribute_.get();
- }
-
- void save();
- void apply_span_and_save();
-};
-
-/**
- * Contains information about an attribute in a geometry component.
- * More information can be added in the future. E.g. whether the attribute is builtin and how it is
- * stored (uv map, vertex group, ...).
- */
-struct AttributeMetaData {
- AttributeDomain domain;
- CustomDataType data_type;
-};
-
-/* Returns false when the iteration should be stopped. */
-using AttributeForeachCallback = blender::FunctionRef<bool(blender::StringRefNull attribute_name,
- const AttributeMetaData &meta_data)>;
-
-/**
* This is the base class for specialized geometry component types.
*/
class GeometryComponent {
@@ -135,12 +69,18 @@ class GeometryComponent {
public:
GeometryComponent(GeometryComponentType type);
- virtual ~GeometryComponent();
+ virtual ~GeometryComponent() = default;
static GeometryComponent *create(GeometryComponentType component_type);
/* The returned component should be of the same type as the type this is called on. */
virtual GeometryComponent *copy() const = 0;
+ /* Direct data is everything except for instances of objects/collections.
+ * If this returns true, the geometry set can be cached and is still valid after e.g. modifier
+ * evaluation ends. Instances can only be valid as long as the data they instance is valid. */
+ virtual bool owns_direct_data() const = 0;
+ virtual void ensure_owns_direct_data() = 0;
+
void user_add() const;
void user_remove() const;
bool is_mutable() const;
@@ -150,26 +90,34 @@ class GeometryComponent {
/* Return true when any attribute with this name exists, including built in attributes. */
bool attribute_exists(const blender::StringRef attribute_name) const;
+ /* Return the data type and domain of an attribute with the given name if it exists. */
+ std::optional<AttributeMetaData> attribute_get_meta_data(
+ const blender::StringRef attribute_name) const;
+
/* Returns true when the geometry component supports this attribute domain. */
bool attribute_domain_supported(const AttributeDomain domain) const;
/* Can only be used with supported domain types. */
virtual int attribute_domain_size(const AttributeDomain domain) const;
+ bool attribute_is_builtin(const blender::StringRef attribute_name) const;
+
/* Get read-only access to the highest priority attribute with the given name.
* Returns null if the attribute does not exist. */
- blender::bke::ReadAttributePtr attribute_try_get_for_read(
+ blender::bke::ReadAttributeLookup attribute_try_get_for_read(
const blender::StringRef attribute_name) const;
/* Get read and write access to the highest priority attribute with the given name.
* Returns null if the attribute does not exist. */
- blender::bke::WriteAttributePtr attribute_try_get_for_write(
+ blender::bke::WriteAttributeLookup attribute_try_get_for_write(
const blender::StringRef attribute_name);
/* Get a read-only attribute for the domain based on the given attribute. This can be used to
* interpolate from one domain to another.
* Returns null if the interpolation is not implemented. */
- virtual blender::bke::ReadAttributePtr attribute_try_adapt_domain(
- blender::bke::ReadAttributePtr attribute, const AttributeDomain new_domain) const;
+ virtual std::unique_ptr<blender::fn::GVArray> attribute_try_adapt_domain(
+ std::unique_ptr<blender::fn::GVArray> varray,
+ const AttributeDomain from_domain,
+ const AttributeDomain to_domain) const;
/* Returns true when the attribute has been deleted. */
bool attribute_try_delete(const blender::StringRef attribute_name);
@@ -177,82 +125,104 @@ class GeometryComponent {
/* Returns true when the attribute has been created. */
bool attribute_try_create(const blender::StringRef attribute_name,
const AttributeDomain domain,
- const CustomDataType data_type);
+ const CustomDataType data_type,
+ const AttributeInit &initializer);
+
+ /* Try to create the builtin attribute with the given name. No data type or domain has to be
+ * provided, because those are fixed for builtin attributes. */
+ bool attribute_try_create_builtin(const blender::StringRef attribute_name,
+ const AttributeInit &initializer);
blender::Set<std::string> attribute_names() const;
- void attribute_foreach(const AttributeForeachCallback callback) const;
+ bool attribute_foreach(const AttributeForeachCallback callback) const;
virtual bool is_empty() const;
- /* Get a read-only attribute for the given domain and data type.
- * Returns null when it does not exist. */
- blender::bke::ReadAttributePtr attribute_try_get_for_read(
+ /* Get a virtual array to read the data of an attribute on the given domain and data type.
+ * Returns null when the attribute does not exist or cannot be converted to the requested domain
+ * and data type. */
+ std::unique_ptr<blender::fn::GVArray> attribute_try_get_for_read(
const blender::StringRef attribute_name,
const AttributeDomain domain,
const CustomDataType data_type) const;
- /* Get a read-only attribute interpolated to the input domain, leaving the data type unchanged.
- * Returns null when the attribute does not exist. */
- blender::bke::ReadAttributePtr attribute_try_get_for_read(
+ /* Get a virtual array to read the data of an attribute on the given domain. The data type is
+ * left unchanged. Returns null when the attribute does not exist or cannot be adapted to the
+ * requested domain. */
+ std::unique_ptr<blender::fn::GVArray> attribute_try_get_for_read(
const blender::StringRef attribute_name, const AttributeDomain domain) const;
- /* Get a read-only attribute for the given domain and data type.
- * Returns a constant attribute based on the default value if the attribute does not exist.
- * Never returns null. */
- blender::bke::ReadAttributePtr attribute_get_for_read(const blender::StringRef attribute_name,
- const AttributeDomain domain,
- const CustomDataType data_type,
- const void *default_value) const;
+ /* Get a virtual array to read data of an attribute with the given data type. The domain is
+ * left unchanged. Returns null when the attribute does not exist or cannot be converted to the
+ * requested data type. */
+ blender::bke::ReadAttributeLookup attribute_try_get_for_read(
+ const blender::StringRef attribute_name, const CustomDataType data_type) const;
- /* Get a typed read-only attribute for the given domain and type. */
- template<typename T>
- blender::bke::TypedReadAttribute<T> attribute_get_for_read(
+ /* Get a virtual array to read the data of an attribute. If that is not possible, the returned
+ * virtual array will contain a default value. This never returns null. */
+ std::unique_ptr<blender::fn::GVArray> attribute_get_for_read(
const blender::StringRef attribute_name,
const AttributeDomain domain,
- const T &default_value) const
+ const CustomDataType data_type,
+ const void *default_value = nullptr) const;
+
+ /* Should be used instead of the method above when the requested data type is known at compile
+ * time for better type safety. */
+ template<typename T>
+ blender::fn::GVArray_Typed<T> attribute_get_for_read(const blender::StringRef attribute_name,
+ const AttributeDomain domain,
+ const T &default_value) const
{
const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>();
const CustomDataType type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
- return this->attribute_get_for_read(attribute_name, domain, type, &default_value);
+ std::unique_ptr varray = this->attribute_get_for_read(
+ attribute_name, domain, type, &default_value);
+ return blender::fn::GVArray_Typed<T>(std::move(varray));
}
- /* Get a read-only dummy attribute that always returns the same value. */
- blender::bke::ReadAttributePtr attribute_get_constant_for_read(const AttributeDomain domain,
- const CustomDataType data_type,
- const void *value) const;
+ /**
+ * Returns an "output attribute", which is essentially a mutable virtual array with some commonly
+ * used convince features. The returned output attribute might be empty if requested attribute
+ * cannot exist on the geometry.
+ *
+ * The included convenience features are:
+ * - Implicit type conversion when writing to builtin attributes.
+ * - If the attribute name exists already, but has a different type/domain, a temporary attribute
+ * is created that will overwrite the existing attribute in the end.
+ */
+ blender::bke::OutputAttribute attribute_try_get_for_output(
+ const blender::StringRef attribute_name,
+ const AttributeDomain domain,
+ const CustomDataType data_type,
+ const void *default_value = nullptr);
- /* Create a read-only dummy attribute that always returns the same value.
- * The given value is converted to the correct type if necessary. */
- blender::bke::ReadAttributePtr attribute_get_constant_for_read_converted(
+ /* Same as attribute_try_get_for_output, but should be used when the original values in the
+ * attributes are not read, i.e. the attribute is used only for output. Since values are not read
+ * from this attribute, no default value is necessary. */
+ blender::bke::OutputAttribute attribute_try_get_for_output_only(
+ const blender::StringRef attribute_name,
const AttributeDomain domain,
- const CustomDataType in_data_type,
- const CustomDataType out_data_type,
- const void *value) const;
+ const CustomDataType data_type);
- /* Get a read-only dummy attribute that always returns the same value. */
+ /* Statically typed method corresponding to the equally named generic one. */
template<typename T>
- blender::bke::TypedReadAttribute<T> attribute_get_constant_for_read(const AttributeDomain domain,
- const T &value) const
+ blender::bke::OutputAttribute_Typed<T> attribute_try_get_for_output(
+ const blender::StringRef attribute_name, const AttributeDomain domain, const T default_value)
{
const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>();
- const CustomDataType type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
- return this->attribute_get_constant_for_read(domain, type, &value);
+ const CustomDataType data_type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
+ return this->attribute_try_get_for_output(attribute_name, domain, data_type, &default_value);
}
- /**
- * If an attribute with the given params exist, it is returned.
- * If no attribute with the given name exists, create it and
- * fill it with the default value if it is provided.
- * If an attribute with the given name but different domain or type exists, a temporary attribute
- * is created that has to be saved after the output has been computed. This avoids deleting
- * another attribute, before a computation is finished.
- *
- * This might return no attribute when the attribute cannot exist on the component.
- */
- OutputAttributePtr attribute_try_get_for_output(const blender::StringRef attribute_name,
- const AttributeDomain domain,
- const CustomDataType data_type,
- const void *default_value = nullptr);
+ /* Statically typed method corresponding to the equally named generic one. */
+ template<typename T>
+ blender::bke::OutputAttribute_Typed<T> attribute_try_get_for_output_only(
+ const blender::StringRef attribute_name, const AttributeDomain domain)
+ {
+ const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get<T>();
+ const CustomDataType data_type = blender::bke::cpp_type_to_custom_data_type(cpp_type);
+ return this->attribute_try_get_for_output_only(attribute_name, domain, data_type);
+ }
private:
virtual const blender::bke::ComponentAttributeProviders *get_attribute_providers() const;
@@ -313,28 +283,43 @@ struct GeometrySet {
friend bool operator==(const GeometrySet &a, const GeometrySet &b);
uint64_t hash() const;
+ void clear();
+
+ void ensure_owns_direct_data();
+
/* Utility methods for creation. */
static GeometrySet create_with_mesh(
Mesh *mesh, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
static GeometrySet create_with_pointcloud(
PointCloud *pointcloud, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
+ static GeometrySet create_with_curve(
+ CurveEval *curve, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
/* Utility methods for access. */
bool has_mesh() const;
bool has_pointcloud() const;
bool has_instances() const;
bool has_volume() const;
+ bool has_curve() const;
+
const Mesh *get_mesh_for_read() const;
const PointCloud *get_pointcloud_for_read() const;
const Volume *get_volume_for_read() const;
+ const CurveEval *get_curve_for_read() const;
+
Mesh *get_mesh_for_write();
PointCloud *get_pointcloud_for_write();
Volume *get_volume_for_write();
+ CurveEval *get_curve_for_write();
/* Utility methods for replacement. */
void replace_mesh(Mesh *mesh, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
void replace_pointcloud(PointCloud *pointcloud,
GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
+ void replace_volume(Volume *volume,
+ GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
+ void replace_curve(CurveEval *curve,
+ GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
};
/** A geometry component that can store a mesh. */
@@ -367,11 +352,16 @@ class MeshComponent : public GeometryComponent {
Mesh *get_for_write();
int attribute_domain_size(const AttributeDomain domain) const final;
- blender::bke::ReadAttributePtr attribute_try_adapt_domain(
- blender::bke::ReadAttributePtr attribute, const AttributeDomain new_domain) const final;
+ std::unique_ptr<blender::fn::GVArray> attribute_try_adapt_domain(
+ std::unique_ptr<blender::fn::GVArray> varray,
+ const AttributeDomain from_domain,
+ const AttributeDomain to_domain) const final;
bool is_empty() const final;
+ bool owns_direct_data() const override;
+ void ensure_owns_direct_data() override;
+
static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_MESH;
private:
@@ -402,18 +392,137 @@ class PointCloudComponent : public GeometryComponent {
bool is_empty() const final;
+ bool owns_direct_data() const override;
+ void ensure_owns_direct_data() override;
+
static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_POINT_CLOUD;
private:
const blender::bke::ComponentAttributeProviders *get_attribute_providers() const final;
};
+/** A geometry component that stores curve data, in other words, a group of splines. */
+class CurveComponent : public GeometryComponent {
+ private:
+ CurveEval *curve_ = nullptr;
+ GeometryOwnershipType ownership_ = GeometryOwnershipType::Owned;
+
+ /**
+ * Curve data necessary to hold the draw cache for rendering, consistent over multiple redraws.
+ * This is necessary because Blender assumes that objects evaluate to an object data type, and
+ * we use #CurveEval rather than #Curve here. It also allows us to mostly reuse the same
+ * batch cache implementation.
+ */
+ mutable Curve *curve_for_render_ = nullptr;
+ mutable std::mutex curve_for_render_mutex_;
+
+ public:
+ CurveComponent();
+ ~CurveComponent();
+ GeometryComponent *copy() const override;
+
+ void clear();
+ bool has_curve() const;
+ void replace(CurveEval *curve, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
+ CurveEval *release();
+
+ const CurveEval *get_for_read() const;
+ CurveEval *get_for_write();
+
+ int attribute_domain_size(const AttributeDomain domain) const final;
+ std::unique_ptr<blender::fn::GVArray> attribute_try_adapt_domain(
+ std::unique_ptr<blender::fn::GVArray> varray,
+ const AttributeDomain from_domain,
+ const AttributeDomain to_domain) const final;
+
+ bool is_empty() const final;
+
+ bool owns_direct_data() const override;
+ void ensure_owns_direct_data() override;
+
+ const Curve *get_curve_for_render() const;
+
+ static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_CURVE;
+
+ private:
+ const blender::bke::ComponentAttributeProviders *get_attribute_providers() const final;
+};
+
+class InstanceReference {
+ public:
+ enum class Type {
+ /**
+ * An empty instance. This allows an `InstanceReference` to be default constructed without
+ * being in an invalid state. There might also be other use cases that we haven't explored much
+ * yet (such as changing the instance later on, and "disabling" some instances).
+ */
+ None,
+ Object,
+ Collection,
+ };
+
+ private:
+ Type type_ = Type::None;
+ /** Depending on the type this is either null, an Object or Collection pointer. */
+ void *data_ = nullptr;
+
+ public:
+ InstanceReference() = default;
+
+ InstanceReference(Object &object) : type_(Type::Object), data_(&object)
+ {
+ }
+
+ InstanceReference(Collection &collection) : type_(Type::Collection), data_(&collection)
+ {
+ }
+
+ Type type() const
+ {
+ return type_;
+ }
+
+ Object &object() const
+ {
+ BLI_assert(type_ == Type::Object);
+ return *(Object *)data_;
+ }
+
+ Collection &collection() const
+ {
+ BLI_assert(type_ == Type::Collection);
+ return *(Collection *)data_;
+ }
+
+ uint64_t hash() const
+ {
+ return blender::get_default_hash(data_);
+ }
+
+ friend bool operator==(const InstanceReference &a, const InstanceReference &b)
+ {
+ return a.data_ == b.data_;
+ }
+};
+
/** A geometry component that stores instances. */
class InstancesComponent : public GeometryComponent {
private:
- blender::Vector<blender::float4x4> transforms_;
- blender::Vector<int> ids_;
- blender::Vector<InstancedData> instanced_data_;
+ /**
+ * Indexed set containing information about the data that is instanced.
+ * Actual instances store an index ("handle") into this set.
+ */
+ blender::VectorSet<InstanceReference> references_;
+
+ /** Index into `references_`. Determines what data is instanced. */
+ blender::Vector<int> instance_reference_handles_;
+ /** Transformation of the instances. */
+ blender::Vector<blender::float4x4> instance_transforms_;
+ /**
+ * IDs of the instances. They are used for consistency over multiple frames for things like
+ * motion blur.
+ */
+ blender::Vector<int> instance_ids_;
/* These almost unique ids are generated based on `ids_`, which might not contain unique ids at
* all. They are *almost* unique, because under certain very unlikely circumstances, they are not
@@ -428,20 +537,31 @@ class InstancesComponent : public GeometryComponent {
GeometryComponent *copy() const override;
void clear();
- void add_instance(Object *object, blender::float4x4 transform, const int id = -1);
- void add_instance(Collection *collection, blender::float4x4 transform, const int id = -1);
- void add_instance(InstancedData data, blender::float4x4 transform, const int id = -1);
-
- blender::Span<InstancedData> instanced_data() const;
- blender::Span<blender::float4x4> transforms() const;
- blender::Span<int> ids() const;
- blender::MutableSpan<blender::float4x4> transforms();
+
+ void reserve(int min_capacity);
+ void resize(int capacity);
+
+ int add_reference(InstanceReference reference);
+ void add_instance(int instance_handle, const blender::float4x4 &transform, const int id = -1);
+
+ blender::Span<InstanceReference> references() const;
+
+ blender::Span<int> instance_reference_handles() const;
+ blender::MutableSpan<int> instance_reference_handles();
+ blender::MutableSpan<blender::float4x4> instance_transforms();
+ blender::Span<blender::float4x4> instance_transforms() const;
+ blender::MutableSpan<int> instance_ids();
+ blender::Span<int> instance_ids() const;
+
int instances_amount() const;
blender::Span<int> almost_unique_ids() const;
bool is_empty() const final;
+ bool owns_direct_data() const override;
+ void ensure_owns_direct_data() override;
+
static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_INSTANCES;
};
@@ -464,5 +584,8 @@ class VolumeComponent : public GeometryComponent {
const Volume *get_for_read() const;
Volume *get_for_write();
+ bool owns_direct_data() const override;
+ void ensure_owns_direct_data() override;
+
static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_VOLUME;
};
diff --git a/source/blender/blenkernel/BKE_geometry_set_instances.hh b/source/blender/blenkernel/BKE_geometry_set_instances.hh
index 16c28e32e3c..25876296a47 100644
--- a/source/blender/blenkernel/BKE_geometry_set_instances.hh
+++ b/source/blender/blenkernel/BKE_geometry_set_instances.hh
@@ -39,7 +39,12 @@ struct GeometryInstanceGroup {
Vector<float4x4> transforms;
};
-Vector<GeometryInstanceGroup> geometry_set_gather_instances(const GeometrySet &geometry_set);
+void geometry_set_instances_attribute_foreach(const GeometrySet &geometry_set,
+ const AttributeForeachCallback callback,
+ const int limit);
+
+void geometry_set_gather_instances(const GeometrySet &geometry_set,
+ Vector<GeometryInstanceGroup> &r_instance_groups);
GeometrySet geometry_set_realize_mesh_for_modifier(const GeometrySet &geometry_set);
GeometrySet geometry_set_realize_instances(const GeometrySet &geometry_set);
@@ -54,9 +59,9 @@ struct AttributeKind {
* will contain the highest complexity data type and the highest priority domain among every
* attribute with the given name on all of the input components.
*/
-void gather_attribute_info(Map<std::string, AttributeKind> &attributes,
- Span<GeometryComponentType> component_types,
- Span<bke::GeometryInstanceGroup> set_groups,
- const Set<std::string> &ignored_attributes);
+void geometry_set_gather_instances_attribute_info(Span<GeometryInstanceGroup> set_groups,
+ Span<GeometryComponentType> component_types,
+ const Set<std::string> &ignored_attributes,
+ Map<std::string, AttributeKind> &r_attributes);
} // namespace blender::bke
diff --git a/source/blender/blenkernel/BKE_global.h b/source/blender/blenkernel/BKE_global.h
index 9e237679795..74f2bf7c6ad 100644
--- a/source/blender/blenkernel/BKE_global.h
+++ b/source/blender/blenkernel/BKE_global.h
@@ -199,20 +199,6 @@ enum {
*/
#define G_FILE_FLAG_ALL_RUNTIME (G_FILE_NO_UI | G_FILE_RECOVER_READ | G_FILE_RECOVER_WRITE)
-/** ENDIAN_ORDER: indicates what endianness the platform where the file was written had. */
-#if !defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__)
-# error Either __BIG_ENDIAN__ or __LITTLE_ENDIAN__ must be defined.
-#endif
-
-#define L_ENDIAN 1
-#define B_ENDIAN 0
-
-#ifdef __BIG_ENDIAN__
-# define ENDIAN_ORDER B_ENDIAN
-#else
-# define ENDIAN_ORDER L_ENDIAN
-#endif
-
/** #Global.moving, signals drawing in (3d) window to denote transform */
enum {
G_TRANSFORM_OBJ = (1 << 0),
diff --git a/source/blender/blenkernel/BKE_gpencil.h b/source/blender/blenkernel/BKE_gpencil.h
index 4b4886e8bf3..bb145580928 100644
--- a/source/blender/blenkernel/BKE_gpencil.h
+++ b/source/blender/blenkernel/BKE_gpencil.h
@@ -108,7 +108,10 @@ void BKE_gpencil_stroke_select_index_reset(struct bGPDstroke *gps);
struct bGPDframe *BKE_gpencil_frame_addnew(struct bGPDlayer *gpl, int cframe);
struct bGPDframe *BKE_gpencil_frame_addcopy(struct bGPDlayer *gpl, int cframe);
-struct bGPDlayer *BKE_gpencil_layer_addnew(struct bGPdata *gpd, const char *name, bool setactive);
+struct bGPDlayer *BKE_gpencil_layer_addnew(struct bGPdata *gpd,
+ const char *name,
+ const bool setactive,
+ const bool add_to_header);
struct bGPdata *BKE_gpencil_data_addnew(struct Main *bmain, const char name[]);
struct bGPDframe *BKE_gpencil_frame_duplicate(const struct bGPDframe *gpf_src,
diff --git a/source/blender/blenkernel/BKE_gpencil_geom.h b/source/blender/blenkernel/BKE_gpencil_geom.h
index a9bd0a524c4..8fc3ce133a0 100644
--- a/source/blender/blenkernel/BKE_gpencil_geom.h
+++ b/source/blender/blenkernel/BKE_gpencil_geom.h
@@ -111,7 +111,10 @@ void BKE_gpencil_dissolve_points(struct bGPdata *gpd,
struct bGPDstroke *gps,
const short tag);
-bool BKE_gpencil_stroke_stretch(struct bGPDstroke *gps, const float dist, const float tip_length);
+bool BKE_gpencil_stroke_stretch(struct bGPDstroke *gps,
+ const float dist,
+ const float overshoot_fac,
+ const short mode);
bool BKE_gpencil_stroke_trim_points(struct bGPDstroke *gps,
const int index_from,
const int index_to);
@@ -135,7 +138,7 @@ bool BKE_gpencil_stroke_split(struct bGPdata *gpd,
struct bGPDstroke *gps,
const int before_index,
struct bGPDstroke **remaining_gps);
-bool BKE_gpencil_stroke_shrink(struct bGPDstroke *gps, const float dist);
+bool BKE_gpencil_stroke_shrink(struct bGPDstroke *gps, const float dist, const short mode);
float BKE_gpencil_stroke_length(const struct bGPDstroke *gps, bool use_3d);
float BKE_gpencil_stroke_segment_length(const struct bGPDstroke *gps,
diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h
index c51a5f7e5e1..d298e5dcf6d 100644
--- a/source/blender/blenkernel/BKE_image.h
+++ b/source/blender/blenkernel/BKE_image.h
@@ -325,6 +325,7 @@ int BKE_image_get_tile_from_pos(struct Image *ima,
const float uv[2],
float r_uv[2],
float r_ofs[2]);
+int BKE_image_find_nearest_tile(const struct Image *image, const float co[2]);
void BKE_image_get_size(struct Image *image, struct ImageUser *iuser, int *r_width, int *r_height);
void BKE_image_get_size_fl(struct Image *image, struct ImageUser *iuser, float r_size[2]);
diff --git a/source/blender/blenkernel/BKE_lib_id.h b/source/blender/blenkernel/BKE_lib_id.h
index 5a8d36b94ec..e16507bf3cc 100644
--- a/source/blender/blenkernel/BKE_lib_id.h
+++ b/source/blender/blenkernel/BKE_lib_id.h
@@ -104,6 +104,10 @@ enum {
* specific code in some copy cases (mostly for node trees). */
LIB_ID_CREATE_LOCAL = 1 << 9,
+ /** Create for the depsgraph, when set #LIB_TAG_COPIED_ON_WRITE must be set.
+ * Internally this is used to share some pointers instead of duplicating them. */
+ LIB_ID_COPY_SET_COPIED_ON_WRITE = 1 << 10,
+
/* *** Specific options to some ID types or usages. *** */
/* *** May be ignored by unrelated ID copying functions. *** */
/** Object only, needed by make_local code. */
@@ -116,6 +120,8 @@ enum {
LIB_ID_COPY_NO_ANIMDATA = 1 << 19,
/** Mesh: Reference CD data layers instead of doing real copy - USE WITH CAUTION! */
LIB_ID_COPY_CD_REFERENCE = 1 << 20,
+ /** Do not copy id->override_library, used by ID datablock override routines. */
+ LIB_ID_COPY_NO_LIB_OVERRIDE = 1 << 21,
/* *** XXX Hackish/not-so-nice specific behaviors needed for some corner cases. *** */
/* *** Ideally we should not have those, but we need them for now... *** */
@@ -136,7 +142,8 @@ enum {
LIB_ID_CREATE_LOCALIZE = LIB_ID_CREATE_NO_MAIN | LIB_ID_CREATE_NO_USER_REFCOUNT |
LIB_ID_CREATE_NO_DEG_TAG,
/** Generate a local copy, outside of bmain, to work on (used by COW e.g.). */
- LIB_ID_COPY_LOCALIZE = LIB_ID_CREATE_LOCALIZE | LIB_ID_COPY_NO_PREVIEW | LIB_ID_COPY_CACHES,
+ LIB_ID_COPY_LOCALIZE = LIB_ID_CREATE_LOCALIZE | LIB_ID_COPY_NO_PREVIEW | LIB_ID_COPY_CACHES |
+ LIB_ID_COPY_NO_LIB_OVERRIDE,
};
void BKE_libblock_copy_ex(struct Main *bmain,
@@ -251,8 +258,10 @@ void BKE_lib_id_swap_full(struct Main *bmain, struct ID *id_a, struct ID *id_b);
void id_sort_by_name(struct ListBase *lb, struct ID *id, struct ID *id_sorting_hint);
void BKE_lib_id_expand_local(struct Main *bmain, struct ID *id);
-bool BKE_id_new_name_validate(struct ListBase *lb, struct ID *id, const char *name)
- ATTR_NONNULL(1, 2);
+bool BKE_id_new_name_validate(struct ListBase *lb,
+ struct ID *id,
+ const char *name,
+ const bool do_linked_data) ATTR_NONNULL(1, 2);
void BKE_lib_id_clear_library_data(struct Main *bmain, struct ID *id);
/* Affect whole Main database. */
@@ -266,7 +275,7 @@ void BKE_main_id_tag_all(struct Main *mainvar, const int tag, const bool value);
void BKE_main_id_flag_listbase(struct ListBase *lb, const int flag, const bool value);
void BKE_main_id_flag_all(struct Main *bmain, const int flag, const bool value);
-void BKE_main_id_clear_newpoins(struct Main *bmain);
+void BKE_main_id_newptr_and_tag_clear(struct Main *bmain);
void BKE_main_id_refcount_recompute(struct Main *bmain, const bool do_linked_only);
diff --git a/source/blender/blenkernel/BKE_lib_override.h b/source/blender/blenkernel/BKE_lib_override.h
index f69580d38be..4dc99e64cf2 100644
--- a/source/blender/blenkernel/BKE_lib_override.h
+++ b/source/blender/blenkernel/BKE_lib_override.h
@@ -47,6 +47,7 @@ struct ID;
struct IDOverrideLibrary;
struct IDOverrideLibraryProperty;
struct IDOverrideLibraryPropertyOperation;
+struct Library;
struct Main;
struct Object;
struct PointerRNA;
@@ -68,12 +69,16 @@ bool BKE_lib_override_library_is_user_edited(struct ID *id);
struct ID *BKE_lib_override_library_create_from_id(struct Main *bmain,
struct ID *reference_id,
const bool do_tagged_remap);
-bool BKE_lib_override_library_create_from_tag(struct Main *bmain);
+bool BKE_lib_override_library_create_from_tag(struct Main *bmain,
+ const struct Library *reference_library,
+ const bool do_no_main);
bool BKE_lib_override_library_create(struct Main *bmain,
struct Scene *scene,
struct ViewLayer *view_layer,
struct ID *id_root,
- struct ID *id_reference);
+ struct ID *id_reference,
+ struct ID **r_id_root_override);
+bool BKE_lib_override_library_template_create(struct ID *id);
bool BKE_lib_override_library_proxy_convert(struct Main *bmain,
struct Scene *scene,
struct ViewLayer *view_layer,
@@ -83,10 +88,13 @@ bool BKE_lib_override_library_resync(struct Main *bmain,
struct ViewLayer *view_layer,
struct ID *id_root,
struct Collection *override_resync_residual_storage,
- const bool do_hierarchy_enforce);
+ const bool do_hierarchy_enforce,
+ const bool do_post_process,
+ struct ReportList *reports);
void BKE_lib_override_library_main_resync(struct Main *bmain,
struct Scene *scene,
- struct ViewLayer *view_layer);
+ struct ViewLayer *view_layer,
+ struct ReportList *reports);
void BKE_lib_override_library_delete(struct Main *bmain, struct ID *id_root);
diff --git a/source/blender/blenkernel/BKE_lib_query.h b/source/blender/blenkernel/BKE_lib_query.h
index 4e781aea9d3..9c49514e7b8 100644
--- a/source/blender/blenkernel/BKE_lib_query.h
+++ b/source/blender/blenkernel/BKE_lib_query.h
@@ -70,12 +70,15 @@ enum {
/** That ID is used as library override's reference by its owner. */
IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE = (1 << 5),
+ /** That ID pointer is not overridable. */
+ IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE = (1 << 6),
+
/**
* Indicates that this is an internal runtime ID pointer, like e.g. `ID.newid` or `ID.original`.
* \note Those should be ignored in most cases, and won't be processed/generated anyway unless
* `IDWALK_DO_INTERNAL_RUNTIME_POINTERS` option is enabled.
*/
- IDWALK_CB_INTERNAL = (1 << 6),
+ IDWALK_CB_INTERNAL = (1 << 7),
/**
* This ID usage is fully refcounted.
diff --git a/source/blender/blenkernel/BKE_lib_remap.h b/source/blender/blenkernel/BKE_lib_remap.h
index 705d2b030e5..e806dedc14c 100644
--- a/source/blender/blenkernel/BKE_lib_remap.h
+++ b/source/blender/blenkernel/BKE_lib_remap.h
@@ -85,6 +85,10 @@ enum {
* freed ones).
*/
ID_REMAP_FORCE_INTERNAL_RUNTIME_POINTERS = 1 << 7,
+ /** Force handling user count even for IDs that are outside of Main (used in some cases when
+ * dealing with IDs temporarily out of Main, but which will be put in it ultimately).
+ */
+ ID_REMAP_FORCE_USER_REFCOUNT = 1 << 8,
};
/* Note: Requiring new_id to be non-null, this *may* not be the case ultimately,
diff --git a/source/blender/blenkernel/BKE_material.h b/source/blender/blenkernel/BKE_material.h
index 14ea50f808a..69e2d52e1dd 100644
--- a/source/blender/blenkernel/BKE_material.h
+++ b/source/blender/blenkernel/BKE_material.h
@@ -51,6 +51,9 @@ void BKE_object_material_remap(struct Object *ob, const unsigned int *remap);
void BKE_object_material_remap_calc(struct Object *ob_dst,
struct Object *ob_src,
short *remap_src_to_dst);
+void BKE_object_material_from_eval_data(struct Main *bmain,
+ struct Object *ob_orig,
+ struct ID *data_eval);
struct Material *BKE_material_add(struct Main *bmain, const char *name);
struct Material *BKE_gpencil_material_add(struct Main *bmain, const char *name);
void BKE_gpencil_material_attr_init(struct Material *ma);
@@ -105,6 +108,13 @@ struct Material *BKE_id_material_pop(struct Main *bmain,
/* index is an int because of RNA. */
int index);
void BKE_id_material_clear(struct Main *bmain, struct ID *id);
+
+/* eval api */
+struct Material *BKE_object_material_get_eval(struct Object *ob, short act);
+int BKE_object_material_count_eval(struct Object *ob);
+void BKE_id_material_eval_assign(struct ID *id, int slot, struct Material *material);
+void BKE_id_material_eval_ensure_default_slot(struct ID *id);
+
/* rendering */
void ramp_blend(int type, float r_col[3], const float fac, const float col[3]);
diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h
index e39caac7c36..62837c4f2a7 100644
--- a/source/blender/blenkernel/BKE_mesh.h
+++ b/source/blender/blenkernel/BKE_mesh.h
@@ -215,7 +215,8 @@ void BKE_mesh_split_faces(struct Mesh *mesh, bool free_loop_normals);
* ignored otherwise. */
struct Mesh *BKE_mesh_new_from_object(struct Depsgraph *depsgraph,
struct Object *object,
- bool preserve_all_data_layers);
+ const bool preserve_all_data_layers,
+ const bool preserve_origindex);
/* This is a version of BKE_mesh_new_from_object() which stores mesh in the given main database.
* However, that function enforces object type to be a geometry one, and ensures a mesh is always
@@ -577,9 +578,9 @@ void BKE_mesh_polygons_flip(struct MPoly *mpoly,
struct CustomData *ldata,
int totpoly);
-/* merge verts */
-/* Enum for merge_mode of CDDM_merge_verts.
- * Refer to mesh.c for details. */
+/* Merge verts. */
+/* Enum for merge_mode of #BKE_mesh_merge_verts.
+ * Refer to mesh_merge.c for details. */
enum {
MESH_MERGE_VERTS_DUMP_IF_MAPPED,
MESH_MERGE_VERTS_DUMP_IF_EQUAL,
diff --git a/source/blender/blenkernel/BKE_mesh_boolean_convert.hh b/source/blender/blenkernel/BKE_mesh_boolean_convert.hh
new file mode 100644
index 00000000000..59f6e75183e
--- /dev/null
+++ b/source/blender/blenkernel/BKE_mesh_boolean_convert.hh
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2019 Blender Foundation.
+ * All rights reserved.
+ */
+
+#pragma once
+
+/** \file
+ * \ingroup bke
+ */
+
+#include "BLI_array.hh"
+#include "BLI_float4x4.hh"
+#include "BLI_mesh_boolean.hh"
+#include "BLI_span.hh"
+
+struct Mesh;
+
+namespace blender::meshintersect {
+
+Mesh *direct_mesh_boolean(blender::Span<const Mesh *> meshes,
+ blender::Span<const float4x4 *> obmats,
+ const float4x4 &target_transform,
+ blender::Span<blender::Array<short>> material_remaps,
+ const bool use_self,
+ const bool hole_tolerant,
+ const int boolean_mode);
+
+} // namespace blender::meshintersect
diff --git a/source/blender/blenkernel/BKE_mesh_sample.hh b/source/blender/blenkernel/BKE_mesh_sample.hh
new file mode 100644
index 00000000000..f504650e349
--- /dev/null
+++ b/source/blender/blenkernel/BKE_mesh_sample.hh
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+/** \file
+ * \ingroup bke
+ */
+
+#include "FN_generic_virtual_array.hh"
+
+#include "BLI_float3.hh"
+
+#include "BKE_attribute.h"
+
+struct Mesh;
+
+namespace blender::bke::mesh_surface_sample {
+
+using fn::CPPType;
+using fn::GMutableSpan;
+using fn::GSpan;
+using fn::GVArray;
+
+void sample_point_attribute(const Mesh &mesh,
+ Span<int> looptri_indices,
+ Span<float3> bary_coords,
+ const GVArray &data_in,
+ GMutableSpan data_out);
+
+void sample_corner_attribute(const Mesh &mesh,
+ Span<int> looptri_indices,
+ Span<float3> bary_coords,
+ const GVArray &data_in,
+ GMutableSpan data_out);
+
+void sample_face_attribute(const Mesh &mesh,
+ Span<int> looptri_indices,
+ const GVArray &data_in,
+ GMutableSpan data_out);
+
+} // namespace blender::bke::mesh_surface_sample
diff --git a/source/blender/blenkernel/BKE_modifier.h b/source/blender/blenkernel/BKE_modifier.h
index ab2ecbe2507..48b4540e3d9 100644
--- a/source/blender/blenkernel/BKE_modifier.h
+++ b/source/blender/blenkernel/BKE_modifier.h
@@ -258,10 +258,6 @@ typedef struct ModifierTypeInfo {
const struct ModifierEvalContext *ctx,
struct GeometrySet *geometry_set);
- struct Volume *(*modifyVolume)(struct ModifierData *md,
- const struct ModifierEvalContext *ctx,
- struct Volume *volume);
-
/********************* Optional functions *********************/
/**
@@ -450,8 +446,8 @@ bool BKE_modifier_is_preview(struct ModifierData *md);
void BKE_modifiers_foreach_ID_link(struct Object *ob, IDWalkFunc walk, void *userData);
void BKE_modifiers_foreach_tex_link(struct Object *ob, TexWalkFunc walk, void *userData);
-struct ModifierData *BKE_modifiers_findby_type(struct Object *ob, ModifierType type);
-struct ModifierData *BKE_modifiers_findby_name(struct Object *ob, const char *name);
+struct ModifierData *BKE_modifiers_findby_type(const struct Object *ob, ModifierType type);
+struct ModifierData *BKE_modifiers_findby_name(const struct Object *ob, const char *name);
void BKE_modifiers_clear_errors(struct Object *ob);
int BKE_modifiers_get_cage_index(const struct Scene *scene,
struct Object *ob,
diff --git a/source/blender/blenkernel/BKE_nla.h b/source/blender/blenkernel/BKE_nla.h
index 16d48024d07..af238fda659 100644
--- a/source/blender/blenkernel/BKE_nla.h
+++ b/source/blender/blenkernel/BKE_nla.h
@@ -58,7 +58,14 @@ struct NlaTrack *BKE_nlatrack_copy(struct Main *bmain,
struct NlaTrack *nlt,
const bool use_same_actions,
const int flag);
-void BKE_nla_tracks_copy(struct Main *bmain, ListBase *dst, ListBase *src, const int flag);
+void BKE_nla_tracks_copy(struct Main *bmain, ListBase *dst, const ListBase *src, const int flag);
+
+/* Copy NLA tracks from #adt_source to #adt_dest, and update the active track/strip pointers to
+ * point at those copies. */
+void BKE_nla_tracks_copy_from_adt(struct Main *bmain,
+ struct AnimData *adt_dest,
+ const struct AnimData *adt_source,
+ int flag);
struct NlaTrack *BKE_nlatrack_add(struct AnimData *adt,
struct NlaTrack *prev,
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h
index 71f188a2de1..a67d7116874 100644
--- a/source/blender/blenkernel/BKE_node.h
+++ b/source/blender/blenkernel/BKE_node.h
@@ -289,10 +289,22 @@ typedef struct bNodeType {
void (*freefunc_api)(struct PointerRNA *ptr);
void (*copyfunc_api)(struct PointerRNA *ptr, const struct bNode *src_node);
- /* can this node type be added to a node tree */
- bool (*poll)(struct bNodeType *ntype, struct bNodeTree *nodetree);
- /* can this node be added to a node tree */
- bool (*poll_instance)(struct bNode *node, struct bNodeTree *nodetree);
+ /**
+ * Can this node type be added to a node tree?
+ * \param r_disabled_hint: Optional hint to display in the UI when the poll fails.
+ * The callback can set this to a static string without having to
+ * null-check it (or without setting it to null if it's not used).
+ * The caller must pass a valid `const char **` and null-initialize it
+ * when it's not just a dummy, that is, if it actually wants to access
+ * the returned disabled-hint (null-check needed!).
+ */
+ bool (*poll)(struct bNodeType *ntype, struct bNodeTree *nodetree, const char **r_disabled_hint);
+ /** Can this node be added to a node tree?
+ * \param r_disabled_hint: See `poll()`.
+ */
+ bool (*poll_instance)(struct bNode *node,
+ struct bNodeTree *nodetree,
+ const char **r_disabled_hint);
/* optional handling of link insertion */
void (*insert_link)(struct bNodeTree *ntree, struct bNode *node, struct bNodeLink *link);
@@ -313,6 +325,7 @@ typedef struct bNodeType {
/* Execute a geometry node. */
NodeGeometryExecFunction geometry_node_execute;
+ bool geometry_node_execute_supports_laziness;
/* RNA integration */
ExtensionRNA rna_ext;
@@ -398,6 +411,9 @@ typedef struct bNodeTreeType {
void (*node_add_init)(struct bNodeTree *ntree, struct bNode *bnode);
+ /* Check if the socket type is valid for this tree type. */
+ bool (*valid_socket_type)(enum eNodeSocketDatatype socket_type, struct bNodeTreeType *ntreetype);
+
/* RNA integration */
ExtensionRNA rna_ext;
} bNodeTreeType;
@@ -483,14 +499,14 @@ void ntreeBlendReadExpand(struct BlendExpander *expander, struct bNodeTree *ntre
/** \name Node Tree Interface
* \{ */
struct bNodeSocket *ntreeFindSocketInterface(struct bNodeTree *ntree,
- int in_out,
+ eNodeSocketInOut in_out,
const char *identifier);
struct bNodeSocket *ntreeAddSocketInterface(struct bNodeTree *ntree,
- int in_out,
+ eNodeSocketInOut in_out,
const char *idname,
const char *name);
struct bNodeSocket *ntreeInsertSocketInterface(struct bNodeTree *ntree,
- int in_out,
+ eNodeSocketInOut in_out,
const char *idname,
struct bNodeSocket *next_sock,
const char *name);
@@ -556,30 +572,32 @@ const char *nodeStaticSocketInterfaceType(int type, int subtype);
} \
((void)0)
-struct bNodeSocket *nodeFindSocket(const struct bNode *node, int in_out, const char *identifier);
+struct bNodeSocket *nodeFindSocket(const struct bNode *node,
+ eNodeSocketInOut in_out,
+ const char *identifier);
struct bNodeSocket *nodeAddSocket(struct bNodeTree *ntree,
struct bNode *node,
- int in_out,
+ eNodeSocketInOut in_out,
const char *idname,
const char *identifier,
const char *name);
struct bNodeSocket *nodeInsertSocket(struct bNodeTree *ntree,
struct bNode *node,
- int in_out,
+ eNodeSocketInOut in_out,
const char *idname,
struct bNodeSocket *next_sock,
const char *identifier,
const char *name);
struct bNodeSocket *nodeAddStaticSocket(struct bNodeTree *ntree,
struct bNode *node,
- int in_out,
+ eNodeSocketInOut in_out,
int type,
int subtype,
const char *identifier,
const char *name);
struct bNodeSocket *nodeInsertStaticSocket(struct bNodeTree *ntree,
struct bNode *node,
- int in_out,
+ eNodeSocketInOut in_out,
int type,
int subtype,
struct bNodeSocket *next_sock,
@@ -802,7 +820,9 @@ void BKE_node_preview_set_pixel(
void nodeLabel(struct bNodeTree *ntree, struct bNode *node, char *label, int maxlen);
const char *nodeSocketLabel(const struct bNodeSocket *sock);
-int nodeGroupPoll(struct bNodeTree *nodetree, struct bNodeTree *grouptree);
+bool nodeGroupPoll(struct bNodeTree *nodetree,
+ struct bNodeTree *grouptree,
+ const char **r_disabled_hint);
/* Init a new node type struct with default values and callbacks */
void node_type_base(struct bNodeType *ntype, int type, const char *name, short nclass, short flag);
@@ -1065,7 +1085,6 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree,
struct bNodeTreeExec *ntreeShaderBeginExecTree(struct bNodeTree *ntree);
void ntreeShaderEndExecTree(struct bNodeTreeExec *exec);
-bool ntreeShaderExecTree(struct bNodeTree *ntree, int thread);
struct bNode *ntreeShaderOutputNode(struct bNodeTree *ntree, int target);
void ntreeGPUMaterialNodes(struct bNodeTree *localtree,
@@ -1188,6 +1207,7 @@ void ntreeGPUMaterialNodes(struct bNodeTree *localtree,
#define CMP_NODE_TRACKPOS 271
#define CMP_NODE_INPAINT 272
#define CMP_NODE_DESPECKLE 273
+#define CMP_NODE_ANTIALIASING 274
#define CMP_NODE_GLARE 301
#define CMP_NODE_TONEMAP 302
@@ -1285,15 +1305,18 @@ void ntreeCompositOutputFileUniqueLayer(struct ListBase *list,
void ntreeCompositColorBalanceSyncFromLGG(bNodeTree *ntree, bNode *node);
void ntreeCompositColorBalanceSyncFromCDL(bNodeTree *ntree, bNode *node);
-void ntreeCompositCryptomatteSyncFromAdd(bNode *node);
+void ntreeCompositCryptomatteSyncFromAdd(const Scene *scene, bNode *node);
void ntreeCompositCryptomatteSyncFromRemove(bNode *node);
bNodeSocket *ntreeCompositCryptomatteAddSocket(bNodeTree *ntree, bNode *node);
int ntreeCompositCryptomatteRemoveSocket(bNodeTree *ntree, bNode *node);
-void ntreeCompositCryptomatteLayerPrefix(const bNode *node, char *r_prefix, size_t prefix_len);
+void ntreeCompositCryptomatteLayerPrefix(const Scene *scene,
+ const bNode *node,
+ char *r_prefix,
+ size_t prefix_len);
/* Update the runtime layer names with the cryptomatte layer names of the references
* render layer or image. */
-void ntreeCompositCryptomatteUpdateLayerNames(bNode *node);
-struct CryptomatteSession *ntreeCompositCryptomatteSession(bNode *node);
+void ntreeCompositCryptomatteUpdateLayerNames(const Scene *scene, bNode *node);
+struct CryptomatteSession *ntreeCompositCryptomatteSession(const Scene *scene, bNode *node);
/** \} */
@@ -1393,7 +1416,24 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE 1036
#define GEO_NODE_MESH_PRIMITIVE_CONE 1037
#define GEO_NODE_MESH_PRIMITIVE_LINE 1038
-#define GEO_NODE_MESH_PRIMITIVE_PLANE 1039
+#define GEO_NODE_MESH_PRIMITIVE_GRID 1039
+#define GEO_NODE_ATTRIBUTE_MAP_RANGE 1040
+#define GEO_NODE_ATTRIBUTE_CLAMP 1041
+#define GEO_NODE_BOUNDING_BOX 1042
+#define GEO_NODE_SWITCH 1043
+#define GEO_NODE_ATTRIBUTE_TRANSFER 1044
+#define GEO_NODE_CURVE_TO_MESH 1045
+#define GEO_NODE_ATTRIBUTE_CURVE_MAP 1046
+#define GEO_NODE_CURVE_RESAMPLE 1047
+#define GEO_NODE_ATTRIBUTE_VECTOR_ROTATE 1048
+#define GEO_NODE_MATERIAL_ASSIGN 1049
+#define GEO_NODE_INPUT_MATERIAL 1050
+#define GEO_NODE_MATERIAL_REPLACE 1051
+#define GEO_NODE_MESH_TO_CURVE 1052
+#define GEO_NODE_DELETE_GEOMETRY 1053
+#define GEO_NODE_CURVE_LENGTH 1054
+#define GEO_NODE_SELECT_BY_MATERIAL 1055
+#define GEO_NODE_CONVEX_HULL 1056
/** \} */
diff --git a/source/blender/blenkernel/BKE_node_ui_storage.hh b/source/blender/blenkernel/BKE_node_ui_storage.hh
index aa7e5180b03..4ec165aad8c 100644
--- a/source/blender/blenkernel/BKE_node_ui_storage.hh
+++ b/source/blender/blenkernel/BKE_node_ui_storage.hh
@@ -20,7 +20,6 @@
#include "BLI_hash.hh"
#include "BLI_map.hh"
-#include "BLI_multi_value_map.hh"
#include "BLI_session_uuid.h"
#include "BLI_set.hh"
@@ -80,30 +79,37 @@ struct NodeWarning {
};
struct AvailableAttributeInfo {
+ std::string name;
AttributeDomain domain;
CustomDataType data_type;
uint64_t hash() const
{
- uint64_t domain_hash = (uint64_t)domain;
- uint64_t data_type_hash = (uint64_t)data_type;
- return (domain_hash * 33) ^ (data_type_hash * 89);
+ return blender::get_default_hash(name);
}
friend bool operator==(const AvailableAttributeInfo &a, const AvailableAttributeInfo &b)
{
- return a.domain == b.domain && a.data_type == b.data_type;
+ return a.name == b.name;
}
};
struct NodeUIStorage {
blender::Vector<NodeWarning> warnings;
- blender::MultiValueMap<std::string, AvailableAttributeInfo> attribute_hints;
+ blender::Set<AvailableAttributeInfo> attribute_hints;
};
struct NodeTreeUIStorage {
+ std::mutex mutex;
blender::Map<NodeTreeEvaluationContext, blender::Map<std::string, NodeUIStorage>> context_map;
- std::mutex context_map_mutex;
+
+ /**
+ * Attribute search uses this to store the fake info for the string typed into a node, in order
+ * to pass the info to the execute callback that sets node socket values. This is mutable since
+ * we can count on only one attribute search being open at a time, and there is no real data
+ * stored here.
+ */
+ mutable AvailableAttributeInfo dummy_info_for_search;
};
const NodeUIStorage *BKE_node_tree_ui_storage_get_from_context(const bContext *C,
diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h
index 12c40e891c9..f3a5c794de8 100644
--- a/source/blender/blenkernel/BKE_object.h
+++ b/source/blender/blenkernel/BKE_object.h
@@ -34,6 +34,7 @@ struct Base;
struct BoundBox;
struct Curve;
struct Depsgraph;
+struct GeometrySet;
struct GpencilModifierData;
struct HookGpencilModifierData;
struct HookModifierData;
@@ -69,6 +70,10 @@ void BKE_object_free_curve_cache(struct Object *ob);
void BKE_object_free_derived_caches(struct Object *ob);
void BKE_object_free_caches(struct Object *object);
+void BKE_object_preview_geometry_set_add(struct Object *ob,
+ const uint64_t key,
+ struct GeometrySet *geometry_set);
+
void BKE_object_modifier_hook_reset(struct Object *ob, struct HookModifierData *hmd);
void BKE_object_modifier_gpencil_hook_reset(struct Object *ob,
struct HookGpencilModifierData *hmd);
@@ -336,6 +341,12 @@ struct Mesh *BKE_object_get_evaluated_mesh(struct Object *object);
struct Mesh *BKE_object_get_pre_modified_mesh(struct Object *object);
struct Mesh *BKE_object_get_original_mesh(struct Object *object);
+/* Lattice accessors.
+ * These functions return either the regular lattice, or the edit-mode lattice,
+ * whichever is currently in use. */
+struct Lattice *BKE_object_get_lattice(const struct Object *object);
+struct Lattice *BKE_object_get_evaluated_lattice(const struct Object *object);
+
int BKE_object_insert_ptcache(struct Object *ob);
void BKE_object_delete_ptcache(struct Object *ob, int index);
struct KeyBlock *BKE_object_shapekey_insert(struct Main *bmain,
@@ -363,6 +374,7 @@ struct MovieClip *BKE_object_movieclip_get(struct Scene *scene,
void BKE_object_runtime_reset(struct Object *object);
void BKE_object_runtime_reset_on_copy(struct Object *object, const int flag);
+void BKE_object_runtime_free_data(struct Object *object);
void BKE_object_batch_cache_dirty_tag(struct Object *ob);
diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h
index 228b52123f3..73413b61456 100644
--- a/source/blender/blenkernel/BKE_paint.h
+++ b/source/blender/blenkernel/BKE_paint.h
@@ -486,7 +486,11 @@ typedef struct SculptSession {
/* Total number of polys of the base mesh. */
int totfaces;
/* Face sets store its visibility in the sign of the integer, using the absolute value as the
- * Face Set ID. Positive IDs are visible, negative IDs are hidden. */
+ * Face Set ID. Positive IDs are visible, negative IDs are hidden.
+ * The 0 ID is not used by the tools or the visibility system, it is just used when creating new
+ * geometry (the trim tool, for example) to detect which geometry was just added, so it can be
+ * assigned a valid Face Set after creation. Tools are not intended to run with Face Sets IDs set
+ * to 0. */
int *face_sets;
/* BMesh for dynamic topology sculpting */
diff --git a/source/blender/blenkernel/BKE_persistent_data_handle.hh b/source/blender/blenkernel/BKE_persistent_data_handle.hh
deleted file mode 100644
index bbee09c7bf4..00000000000
--- a/source/blender/blenkernel/BKE_persistent_data_handle.hh
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * 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.
- */
-
-#pragma once
-
-/** \file
- * \ingroup bke
- *
- * A PersistentDataHandle is a weak reference to some data in a Blender file. The handle itself is
- * just a number. A PersistentDataHandleMap is used to convert between handles and the actual data.
- */
-
-#include "BLI_map.hh"
-
-#include "DNA_ID.h"
-
-struct Collection;
-struct Object;
-
-namespace blender::bke {
-
-class PersistentDataHandleMap;
-
-class PersistentDataHandle {
- private:
- /* Negative values indicate that the handle is "empty". */
- int32_t handle_;
-
- friend PersistentDataHandleMap;
-
- protected:
- PersistentDataHandle(int handle) : handle_(handle)
- {
- }
-
- public:
- PersistentDataHandle() : handle_(-1)
- {
- }
-
- friend bool operator==(const PersistentDataHandle &a, const PersistentDataHandle &b)
- {
- return a.handle_ == b.handle_;
- }
-
- friend bool operator!=(const PersistentDataHandle &a, const PersistentDataHandle &b)
- {
- return !(a == b);
- }
-
- friend std::ostream &operator<<(std::ostream &stream, const PersistentDataHandle &a)
- {
- stream << a.handle_;
- return stream;
- }
-
- uint64_t hash() const
- {
- return static_cast<uint64_t>(handle_);
- }
-};
-
-class PersistentIDHandle : public PersistentDataHandle {
- friend PersistentDataHandleMap;
- using PersistentDataHandle::PersistentDataHandle;
-};
-
-class PersistentObjectHandle : public PersistentIDHandle {
- friend PersistentDataHandleMap;
- using PersistentIDHandle::PersistentIDHandle;
-};
-
-class PersistentCollectionHandle : public PersistentIDHandle {
- friend PersistentDataHandleMap;
- using PersistentIDHandle::PersistentIDHandle;
-};
-
-class PersistentDataHandleMap {
- private:
- Map<int32_t, ID *> id_by_handle_;
- Map<ID *, int32_t> handle_by_id_;
-
- public:
- void add(int32_t handle, ID &id)
- {
- BLI_assert(handle >= 0);
- handle_by_id_.add(&id, handle);
- id_by_handle_.add(handle, &id);
- }
-
- PersistentIDHandle lookup(ID *id) const
- {
- const int handle = handle_by_id_.lookup_default(id, -1);
- return PersistentIDHandle(handle);
- }
-
- PersistentObjectHandle lookup(Object *object) const
- {
- const int handle = handle_by_id_.lookup_default((ID *)object, -1);
- return PersistentObjectHandle(handle);
- }
-
- PersistentCollectionHandle lookup(Collection *collection) const
- {
- const int handle = handle_by_id_.lookup_default((ID *)collection, -1);
- return PersistentCollectionHandle(handle);
- }
-
- ID *lookup(const PersistentIDHandle &handle) const
- {
- ID *id = id_by_handle_.lookup_default(handle.handle_, nullptr);
- return id;
- }
-
- Object *lookup(const PersistentObjectHandle &handle) const
- {
- ID *id = this->lookup((const PersistentIDHandle &)handle);
- if (id == nullptr) {
- return nullptr;
- }
- if (GS(id->name) != ID_OB) {
- return nullptr;
- }
- return (Object *)id;
- }
-
- Collection *lookup(const PersistentCollectionHandle &handle) const
- {
- ID *id = this->lookup((const PersistentIDHandle &)handle);
- if (id == nullptr) {
- return nullptr;
- }
- if (GS(id->name) != ID_GR) {
- return nullptr;
- }
- return (Collection *)id;
- }
-};
-
-} // namespace blender::bke
diff --git a/source/blender/blenkernel/BKE_scene.h b/source/blender/blenkernel/BKE_scene.h
index b2726885593..9792f819bf9 100644
--- a/source/blender/blenkernel/BKE_scene.h
+++ b/source/blender/blenkernel/BKE_scene.h
@@ -140,6 +140,8 @@ struct TransformOrientationSlot *BKE_scene_orientation_slot_get(struct Scene *sc
void BKE_scene_orientation_slot_set_index(struct TransformOrientationSlot *orient_slot,
int orientation);
int BKE_scene_orientation_slot_get_index(const struct TransformOrientationSlot *orient_slot);
+int BKE_scene_orientation_get_index(struct Scene *scene, int slot_index);
+int BKE_scene_orientation_get_index_from_flag(struct Scene *scene, int flag);
/* ** Scene evaluation ** */
@@ -150,6 +152,7 @@ void BKE_scene_graph_update_tagged(struct Depsgraph *depsgraph, struct Main *bma
void BKE_scene_graph_evaluated_ensure(struct Depsgraph *depsgraph, struct Main *bmain);
void BKE_scene_graph_update_for_newframe(struct Depsgraph *depsgraph);
+void BKE_scene_graph_update_for_newframe_ex(struct Depsgraph *depsgraph, const bool clear_recalc);
void BKE_scene_view_layer_graph_evaluated_ensure(struct Main *bmain,
struct Scene *scene,
diff --git a/source/blender/blenkernel/BKE_softbody.h b/source/blender/blenkernel/BKE_softbody.h
index 4f8b21141b6..58dc90f62dc 100644
--- a/source/blender/blenkernel/BKE_softbody.h
+++ b/source/blender/blenkernel/BKE_softbody.h
@@ -47,7 +47,7 @@ typedef struct BodyPoint {
} BodyPoint;
/* allocates and initializes general main data */
-extern struct SoftBody *sbNew(struct Scene *scene);
+extern struct SoftBody *sbNew(void);
/* frees internal data and soft-body itself */
extern void sbFree(struct Object *ob);
diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh
new file mode 100644
index 00000000000..dfbe82f31fd
--- /dev/null
+++ b/source/blender/blenkernel/BKE_spline.hh
@@ -0,0 +1,551 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+/** \file
+ * \ingroup bke
+ */
+
+#include <mutex>
+
+#include "FN_generic_virtual_array.hh"
+
+#include "BLI_float3.hh"
+#include "BLI_float4x4.hh"
+#include "BLI_vector.hh"
+
+#include "BKE_attribute_access.hh"
+#include "BKE_attribute_math.hh"
+
+struct Curve;
+
+class Spline;
+using SplinePtr = std::unique_ptr<Spline>;
+
+/**
+ * A spline is an abstraction of a single branch-less curve section, its evaluation methods,
+ * and data. The spline data itself is just control points and a set of attributes by the set
+ * of "evaluated" data is often used instead.
+ *
+ * Any derived class of Spline has to manage two things:
+ * 1. Interpolating arbitrary attribute data from the control points to evaluated points.
+ * 2. Evaluating the positions based on the stored control point data.
+ *
+ * Beyond that, everything is the base class's responsibility, with minor exceptions. Further
+ * evaluation happens in a layer on top of the evaluated points generated by the derived types.
+ *
+ * There are a few methods to evaluate a spline:
+ * 1. #evaluated_positions and #interpolate_to_evaluated_points give data for the initial
+ * evaluated points, depending on the resolution.
+ * 2. #lookup_evaluated_factor and #lookup_evaluated_factor are meant for one-off lookups
+ * along the length of a curve.
+ * 3. #sample_uniform_index_factors returns an array that stores uniform-length samples
+ * along the spline which can be used to interpolate data from method 1.
+ *
+ * Commonly used evaluated data is stored in caches on the spline itself so that operations on
+ * splines don't need to worry about taking ownership of evaluated data when they don't need to.
+ */
+class Spline {
+ public:
+ enum class Type {
+ Bezier,
+ NURBS,
+ Poly,
+ };
+
+ enum NormalCalculationMode {
+ ZUp,
+ Minimum,
+ Tangent,
+ };
+ /* Only #Zup is supported at the moment. */
+ NormalCalculationMode normal_mode;
+
+ blender::bke::CustomDataAttributes attributes;
+
+ protected:
+ Type type_;
+ bool is_cyclic_ = false;
+
+ /** Direction of the spline at each evaluated point. */
+ mutable blender::Vector<blender::float3> evaluated_tangents_cache_;
+ mutable std::mutex tangent_cache_mutex_;
+ mutable bool tangent_cache_dirty_ = true;
+
+ /** Normal direction vectors for each evaluated point. */
+ mutable blender::Vector<blender::float3> evaluated_normals_cache_;
+ mutable std::mutex normal_cache_mutex_;
+ mutable bool normal_cache_dirty_ = true;
+
+ /** Accumulated lengths along the evaluated points. */
+ mutable blender::Vector<float> evaluated_lengths_cache_;
+ mutable std::mutex length_cache_mutex_;
+ mutable bool length_cache_dirty_ = true;
+
+ public:
+ virtual ~Spline() = default;
+ Spline(const Type type) : type_(type)
+ {
+ }
+ Spline(Spline &other) : attributes(other.attributes), type_(other.type_)
+ {
+ copy_base_settings(other, *this);
+ }
+
+ virtual SplinePtr copy() const = 0;
+ /** Return a new spline with the same type and settings like "cyclic", but without any data. */
+ virtual SplinePtr copy_settings() const = 0;
+
+ Spline::Type type() const;
+
+ /** Return the number of control points. */
+ virtual int size() const = 0;
+ int segments_size() const;
+ bool is_cyclic() const;
+ void set_cyclic(const bool value);
+
+ virtual void resize(const int size) = 0;
+ virtual blender::MutableSpan<blender::float3> positions() = 0;
+ virtual blender::Span<blender::float3> positions() const = 0;
+ virtual blender::MutableSpan<float> radii() = 0;
+ virtual blender::Span<float> radii() const = 0;
+ virtual blender::MutableSpan<float> tilts() = 0;
+ virtual blender::Span<float> tilts() const = 0;
+
+ virtual void translate(const blender::float3 &translation);
+ virtual void transform(const blender::float4x4 &matrix);
+
+ /**
+ * Mark all caches for re-computation. This must be called after any operation that would
+ * change the generated positions, tangents, normals, mapping, etc. of the evaluated points.
+ */
+ virtual void mark_cache_invalid() = 0;
+ virtual int evaluated_points_size() const = 0;
+ int evaluated_edges_size() const;
+
+ float length() const;
+
+ virtual blender::Span<blender::float3> evaluated_positions() const = 0;
+
+ blender::Span<float> evaluated_lengths() const;
+ blender::Span<blender::float3> evaluated_tangents() const;
+ blender::Span<blender::float3> evaluated_normals() const;
+
+ void bounds_min_max(blender::float3 &min, blender::float3 &max, const bool use_evaluated) const;
+
+ struct LookupResult {
+ /**
+ * The index of the evaluated point before the result location. In other words, the index of
+ * the edge that the result lies on. If the sampled factor/length is the very end of the
+ * spline, this will be the second to last index, if it's the very beginning, this will be 0.
+ */
+ int evaluated_index;
+ /**
+ * The index of the evaluated point after the result location, accounting for wrapping when
+ * the spline is cyclic. If the sampled factor/length is the very end of the spline, this will
+ * be the last index (#evaluated_points_size - 1).
+ */
+ int next_evaluated_index;
+ /**
+ * The portion of the way from the evaluated point at #evaluated_index to the next point.
+ * If the sampled factor/length is the very end of the spline, this will be the 1.0f
+ */
+ float factor;
+ };
+ LookupResult lookup_evaluated_factor(const float factor) const;
+ LookupResult lookup_evaluated_length(const float length) const;
+
+ blender::Array<float> sample_uniform_index_factors(const int samples_size) const;
+ LookupResult lookup_data_from_index_factor(const float index_factor) const;
+
+ void sample_based_on_index_factors(const blender::fn::GVArray &src,
+ blender::Span<float> index_factors,
+ blender::fn::GMutableSpan dst) const;
+ template<typename T>
+ void sample_based_on_index_factors(const blender::VArray<T> &src,
+ blender::Span<float> index_factors,
+ blender::MutableSpan<T> dst) const
+ {
+ this->sample_based_on_index_factors(
+ blender::fn::GVArray_For_VArray(src), index_factors, blender::fn::GMutableSpan(dst));
+ }
+ template<typename T>
+ void sample_based_on_index_factors(blender::Span<T> src,
+ blender::Span<float> index_factors,
+ blender::MutableSpan<T> dst) const
+ {
+ this->sample_based_on_index_factors(blender::VArray_For_Span(src), index_factors, dst);
+ }
+
+ /**
+ * Interpolate a virtual array of data with the size of the number of control points to the
+ * evaluated points. For poly splines, the lifetime of the returned virtual array must not
+ * exceed the lifetime of the input data.
+ */
+ virtual blender::fn::GVArrayPtr interpolate_to_evaluated_points(
+ const blender::fn::GVArray &source_data) const = 0;
+ blender::fn::GVArrayPtr interpolate_to_evaluated_points(blender::fn::GSpan data) const;
+ template<typename T>
+ blender::fn::GVArray_Typed<T> interpolate_to_evaluated_points(blender::Span<T> data) const
+ {
+ return blender::fn::GVArray_Typed<T>(
+ this->interpolate_to_evaluated_points(blender::fn::GSpan(data)));
+ }
+
+ protected:
+ virtual void correct_end_tangents() const = 0;
+ /** Copy settings stored in the base spline class. */
+ static void copy_base_settings(const Spline &src, Spline &dst)
+ {
+ dst.normal_mode = src.normal_mode;
+ dst.is_cyclic_ = src.is_cyclic_;
+ }
+};
+
+/**
+ * A Bézier spline is made up of a many curve segments, possibly achieving continuity of curvature
+ * by constraining the alignment of curve handles. Evaluation stores the positions and a map of
+ * factors and indices in a list of floats, which is then used to interpolate any other data.
+ */
+class BezierSpline final : public Spline {
+ public:
+ enum class HandleType {
+ /** The handle can be moved anywhere, and doesn't influence the point's other handle. */
+ Free,
+ /** The location is automatically calculated to be smooth. */
+ Auto,
+ /** The location is calculated to point to the next/previous control point. */
+ Vector,
+ /** The location is constrained to point in the opposite direction as the other handle. */
+ Align,
+ };
+
+ private:
+ blender::Vector<blender::float3> positions_;
+ blender::Vector<float> radii_;
+ blender::Vector<float> tilts_;
+ int resolution_;
+
+ blender::Vector<HandleType> handle_types_left_;
+ blender::Vector<HandleType> handle_types_right_;
+
+ /* These are mutable to allow lazy recalculation of #Auto and #Vector handle positions. */
+ mutable blender::Vector<blender::float3> handle_positions_left_;
+ mutable blender::Vector<blender::float3> handle_positions_right_;
+
+ mutable std::mutex auto_handle_mutex_;
+ mutable bool auto_handles_dirty_ = true;
+
+ /** Start index in evaluated points array for every control point. */
+ mutable blender::Vector<int> offset_cache_;
+ mutable std::mutex offset_cache_mutex_;
+ mutable bool offset_cache_dirty_ = true;
+
+ /** Cache of evaluated positions. */
+ mutable blender::Vector<blender::float3> evaluated_position_cache_;
+ mutable std::mutex position_cache_mutex_;
+ mutable bool position_cache_dirty_ = true;
+
+ /** Cache of "index factors" based calculated from the evaluated positions. */
+ mutable blender::Vector<float> evaluated_mapping_cache_;
+ mutable std::mutex mapping_cache_mutex_;
+ mutable bool mapping_cache_dirty_ = true;
+
+ public:
+ virtual SplinePtr copy() const final;
+ SplinePtr copy_settings() const final;
+ BezierSpline() : Spline(Type::Bezier)
+ {
+ }
+ BezierSpline(const BezierSpline &other)
+ : Spline((Spline &)other),
+ positions_(other.positions_),
+ radii_(other.radii_),
+ tilts_(other.tilts_),
+ resolution_(other.resolution_),
+ handle_types_left_(other.handle_types_left_),
+ handle_types_right_(other.handle_types_right_),
+ handle_positions_left_(other.handle_positions_left_),
+ handle_positions_right_(other.handle_positions_right_)
+ {
+ }
+
+ int size() const final;
+ int resolution() const;
+ void set_resolution(const int value);
+
+ void add_point(const blender::float3 position,
+ const HandleType handle_type_left,
+ const blender::float3 handle_position_left,
+ const HandleType handle_type_right,
+ const blender::float3 handle_position_right,
+ const float radius,
+ const float tilt);
+
+ void resize(const int size) final;
+ blender::MutableSpan<blender::float3> positions() final;
+ blender::Span<blender::float3> positions() const final;
+ blender::MutableSpan<float> radii() final;
+ blender::Span<float> radii() const final;
+ blender::MutableSpan<float> tilts() final;
+ blender::Span<float> tilts() const final;
+ blender::Span<HandleType> handle_types_left() const;
+ blender::MutableSpan<HandleType> handle_types_left();
+ blender::Span<blender::float3> handle_positions_left() const;
+ blender::MutableSpan<blender::float3> handle_positions_left();
+ blender::Span<HandleType> handle_types_right() const;
+ blender::MutableSpan<HandleType> handle_types_right();
+ blender::Span<blender::float3> handle_positions_right() const;
+ blender::MutableSpan<blender::float3> handle_positions_right();
+
+ void translate(const blender::float3 &translation) override;
+ void transform(const blender::float4x4 &matrix) override;
+
+ bool point_is_sharp(const int index) const;
+
+ void mark_cache_invalid() final;
+ int evaluated_points_size() const final;
+
+ blender::Span<int> control_point_offsets() const;
+ blender::Span<float> evaluated_mappings() const;
+ blender::Span<blender::float3> evaluated_positions() const final;
+ struct InterpolationData {
+ int control_point_index;
+ int next_control_point_index;
+ /**
+ * Linear interpolation weight between the two indices, from 0 to 1.
+ * Higher means closer to next control point.
+ */
+ float factor;
+ };
+ InterpolationData interpolation_data_from_index_factor(const float index_factor) const;
+
+ virtual blender::fn::GVArrayPtr interpolate_to_evaluated_points(
+ const blender::fn::GVArray &source_data) const override;
+
+ private:
+ void ensure_auto_handles() const;
+ void correct_end_tangents() const final;
+ bool segment_is_vector(const int start_index) const;
+ void evaluate_bezier_segment(const int index,
+ const int next_index,
+ blender::MutableSpan<blender::float3> positions) const;
+};
+
+/**
+ * Data for Non-Uniform Rational B-Splines. The mapping from control points to evaluated points is
+ * influenced by a vector of knots, weights for each point, and the order of the spline. Every
+ * mapping of data to evaluated points is handled the same way, but the positions are cached in
+ * the spline.
+ */
+class NURBSpline final : public Spline {
+ public:
+ enum class KnotsMode {
+ Normal,
+ EndPoint,
+ Bezier,
+ };
+
+ /** Method used to recalculate the knots vector when points are added or removed. */
+ KnotsMode knots_mode;
+
+ struct BasisCache {
+ /** The influence at each control point `i + #start_index`. */
+ blender::Vector<float> weights;
+ /**
+ * An offset for the start of #weights: the first control point index with a non-zero weight.
+ */
+ int start_index;
+ };
+
+ private:
+ blender::Vector<blender::float3> positions_;
+ blender::Vector<float> radii_;
+ blender::Vector<float> tilts_;
+ blender::Vector<float> weights_;
+ int resolution_;
+ /**
+ * Defines the number of nearby control points that influence a given evaluated point. Higher
+ * orders give smoother results. The number of control points must be greater than or equal to
+ * this value.
+ */
+ uint8_t order_;
+
+ /**
+ * Determines where and how the control points affect the evaluated points. The length should
+ * always be the value returned by #knots_size(), and each value should be greater than or equal
+ * to the previous. Only invalidated when a point is added or removed.
+ */
+ mutable blender::Vector<float> knots_;
+ mutable std::mutex knots_mutex_;
+ mutable bool knots_dirty_ = true;
+
+ /** Cache of control point influences on each evaluated point. */
+ mutable blender::Vector<BasisCache> basis_cache_;
+ mutable std::mutex basis_cache_mutex_;
+ mutable bool basis_cache_dirty_ = true;
+
+ /**
+ * Cache of position data calculated from the basis cache. Though it is interpolated
+ * in the same way as any other attribute, it is stored to save unnecessary recalculation.
+ */
+ mutable blender::Vector<blender::float3> evaluated_position_cache_;
+ mutable std::mutex position_cache_mutex_;
+ mutable bool position_cache_dirty_ = true;
+
+ public:
+ SplinePtr copy() const final;
+ SplinePtr copy_settings() const final;
+ NURBSpline() : Spline(Type::NURBS)
+ {
+ }
+ NURBSpline(const NURBSpline &other)
+ : Spline((Spline &)other),
+ knots_mode(other.knots_mode),
+ positions_(other.positions_),
+ radii_(other.radii_),
+ tilts_(other.tilts_),
+ weights_(other.weights_),
+ resolution_(other.resolution_),
+ order_(other.order_)
+ {
+ }
+
+ int size() const final;
+ int resolution() const;
+ void set_resolution(const int value);
+ uint8_t order() const;
+ void set_order(const uint8_t value);
+
+ void add_point(const blender::float3 position,
+ const float radius,
+ const float tilt,
+ const float weight);
+
+ bool check_valid_size_and_order() const;
+ int knots_size() const;
+
+ void resize(const int size) final;
+ blender::MutableSpan<blender::float3> positions() final;
+ blender::Span<blender::float3> positions() const final;
+ blender::MutableSpan<float> radii() final;
+ blender::Span<float> radii() const final;
+ blender::MutableSpan<float> tilts() final;
+ blender::Span<float> tilts() const final;
+ blender::Span<float> knots() const;
+
+ blender::MutableSpan<float> weights();
+ blender::Span<float> weights() const;
+
+ void mark_cache_invalid() final;
+ int evaluated_points_size() const final;
+
+ blender::Span<blender::float3> evaluated_positions() const final;
+
+ blender::fn::GVArrayPtr interpolate_to_evaluated_points(
+ const blender::fn::GVArray &source_data) const final;
+
+ protected:
+ void correct_end_tangents() const final;
+ void calculate_knots() const;
+ void calculate_basis_cache() const;
+};
+
+/**
+ * A Poly spline is like a bezier spline with a resolution of one. The main reason to distinguish
+ * the two is for reduced complexity and increased performance, since interpolating data to control
+ * points does not change it.
+ */
+class PolySpline final : public Spline {
+ blender::Vector<blender::float3> positions_;
+ blender::Vector<float> radii_;
+ blender::Vector<float> tilts_;
+
+ public:
+ SplinePtr copy() const final;
+ SplinePtr copy_settings() const final;
+ PolySpline() : Spline(Type::Poly)
+ {
+ }
+ PolySpline(const PolySpline &other)
+ : Spline((Spline &)other),
+ positions_(other.positions_),
+ radii_(other.radii_),
+ tilts_(other.tilts_)
+ {
+ }
+
+ int size() const final;
+
+ void add_point(const blender::float3 position, const float radius, const float tilt);
+
+ void resize(const int size) final;
+ blender::MutableSpan<blender::float3> positions() final;
+ blender::Span<blender::float3> positions() const final;
+ blender::MutableSpan<float> radii() final;
+ blender::Span<float> radii() const final;
+ blender::MutableSpan<float> tilts() final;
+ blender::Span<float> tilts() const final;
+
+ void mark_cache_invalid() final;
+ int evaluated_points_size() const final;
+
+ blender::Span<blender::float3> evaluated_positions() const final;
+
+ blender::fn::GVArrayPtr interpolate_to_evaluated_points(
+ const blender::fn::GVArray &source_data) const final;
+
+ protected:
+ void correct_end_tangents() const final;
+};
+
+/**
+ * A #CurveEval corresponds to the #Curve object data. The name is different for clarity, since
+ * more of the data is stored in the splines, but also just to be different than the name in DNA.
+ */
+struct CurveEval {
+ private:
+ blender::Vector<SplinePtr> splines_;
+
+ public:
+ blender::bke::CustomDataAttributes attributes;
+
+ CurveEval() = default;
+ CurveEval(const CurveEval &other) : attributes(other.attributes)
+ {
+ for (const SplinePtr &spline : other.splines()) {
+ this->add_spline(spline->copy());
+ }
+ }
+
+ blender::Span<SplinePtr> splines() const;
+ blender::MutableSpan<SplinePtr> splines();
+
+ void add_spline(SplinePtr spline);
+ void remove_splines(blender::IndexMask mask);
+
+ void translate(const blender::float3 &translation);
+ void transform(const blender::float4x4 &matrix);
+ void bounds_min_max(blender::float3 &min, blender::float3 &max, const bool use_evaluated) const;
+
+ blender::Array<int> control_point_offsets() const;
+ blender::Array<int> evaluated_point_offsets() const;
+
+ void assert_valid_point_attributes() const;
+};
+
+std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &curve);
diff --git a/source/blender/blenkernel/BKE_unit.h b/source/blender/blenkernel/BKE_unit.h
index c4af9ab40e2..b28215a72d1 100644
--- a/source/blender/blenkernel/BKE_unit.h
+++ b/source/blender/blenkernel/BKE_unit.h
@@ -82,12 +82,13 @@ enum {
B_UNIT_MASS = 4,
B_UNIT_ROTATION = 5,
B_UNIT_TIME = 6,
- B_UNIT_VELOCITY = 7,
- B_UNIT_ACCELERATION = 8,
- B_UNIT_CAMERA = 9,
- B_UNIT_POWER = 10,
- B_UNIT_TEMPERATURE = 11,
- B_UNIT_TYPE_TOT = 12,
+ B_UNIT_TIME_ABSOLUTE = 7,
+ B_UNIT_VELOCITY = 8,
+ B_UNIT_ACCELERATION = 9,
+ B_UNIT_CAMERA = 10,
+ B_UNIT_POWER = 11,
+ B_UNIT_TEMPERATURE = 12,
+ B_UNIT_TYPE_TOT = 13,
};
#ifdef __cplusplus
diff --git a/source/blender/blenkernel/BKE_volume.h b/source/blender/blenkernel/BKE_volume.h
index 53626dbeb1b..cf755827a6c 100644
--- a/source/blender/blenkernel/BKE_volume.h
+++ b/source/blender/blenkernel/BKE_volume.h
@@ -78,16 +78,17 @@ extern void (*BKE_volume_batch_cache_free_cb)(struct Volume *volume);
typedef struct VolumeGrid VolumeGrid;
-bool BKE_volume_load(struct Volume *volume, struct Main *bmain);
+bool BKE_volume_load(const struct Volume *volume, const struct Main *bmain);
void BKE_volume_unload(struct Volume *volume);
bool BKE_volume_is_loaded(const struct Volume *volume);
int BKE_volume_num_grids(const struct Volume *volume);
const char *BKE_volume_grids_error_msg(const struct Volume *volume);
const char *BKE_volume_grids_frame_filepath(const struct Volume *volume);
-VolumeGrid *BKE_volume_grid_get(const struct Volume *volume, int grid_index);
-VolumeGrid *BKE_volume_grid_active_get(const struct Volume *volume);
-VolumeGrid *BKE_volume_grid_find(const struct Volume *volume, const char *name);
+const VolumeGrid *BKE_volume_grid_get_for_read(const struct Volume *volume, int grid_index);
+VolumeGrid *BKE_volume_grid_get_for_write(struct Volume *volume, int grid_index);
+const VolumeGrid *BKE_volume_grid_active_get_for_read(const struct Volume *volume);
+const VolumeGrid *BKE_volume_grid_find_for_read(const struct Volume *volume, const char *name);
/* Grid
*
@@ -109,8 +110,8 @@ typedef enum VolumeGridType {
VOLUME_GRID_POINTS,
} VolumeGridType;
-bool BKE_volume_grid_load(const struct Volume *volume, struct VolumeGrid *grid);
-void BKE_volume_grid_unload(const struct Volume *volume, struct VolumeGrid *grid);
+bool BKE_volume_grid_load(const struct Volume *volume, const struct VolumeGrid *grid);
+void BKE_volume_grid_unload(const struct Volume *volume, const struct VolumeGrid *grid);
bool BKE_volume_grid_is_loaded(const struct VolumeGrid *grid);
/* Metadata */
@@ -119,9 +120,6 @@ VolumeGridType BKE_volume_grid_type(const struct VolumeGrid *grid);
int BKE_volume_grid_channels(const struct VolumeGrid *grid);
void BKE_volume_grid_transform_matrix(const struct VolumeGrid *grid, float mat[4][4]);
-/* Bounds */
-bool BKE_volume_grid_bounds(const struct VolumeGrid *grid, float min[3], float max[3]);
-
/* Volume Editing
*
* These are intended for modifiers to use on evaluated datablocks.
@@ -145,8 +143,8 @@ int BKE_volume_simplify_level(const struct Depsgraph *depsgraph);
float BKE_volume_simplify_factor(const struct Depsgraph *depsgraph);
/* File Save */
-bool BKE_volume_save(struct Volume *volume,
- struct Main *bmain,
+bool BKE_volume_save(const struct Volume *volume,
+ const struct Main *bmain,
struct ReportList *reports,
const char *filepath);
@@ -159,13 +157,26 @@ bool BKE_volume_save(struct Volume *volume,
* Access to OpenVDB grid for C++. These will automatically load grids from
* file or copy shared grids to make them writeable. */
-#if defined(__cplusplus) && defined(WITH_OPENVDB)
-# include <openvdb/openvdb.h>
-# include <openvdb/points/PointDataGrid.h>
+#ifdef __cplusplus
+# include "BLI_float3.hh"
+# include "BLI_float4x4.hh"
+
+bool BKE_volume_min_max(const Volume *volume, blender::float3 &r_min, blender::float3 &r_max);
+
+# ifdef WITH_OPENVDB
+# include <openvdb/openvdb.h>
+# include <openvdb/points/PointDataGrid.h>
+
+bool BKE_volume_grid_bounds(openvdb::GridBase::ConstPtr grid,
+ blender::float3 &r_min,
+ blender::float3 &r_max);
+
+openvdb::GridBase::ConstPtr BKE_volume_grid_shallow_transform(openvdb::GridBase::ConstPtr grid,
+ const blender::float4x4 &transform);
openvdb::GridBase::ConstPtr BKE_volume_grid_openvdb_for_metadata(const struct VolumeGrid *grid);
openvdb::GridBase::ConstPtr BKE_volume_grid_openvdb_for_read(const struct Volume *volume,
- struct VolumeGrid *grid);
+ const struct VolumeGrid *grid);
openvdb::GridBase::Ptr BKE_volume_grid_openvdb_for_write(const struct Volume *volume,
struct VolumeGrid *grid,
const bool clear);
@@ -212,4 +223,5 @@ openvdb::GridBase::Ptr BKE_volume_grid_create_with_changed_resolution(
const openvdb::GridBase &old_grid,
const float resolution_factor);
+# endif
#endif
diff --git a/source/blender/blenkernel/BKE_volume_render.h b/source/blender/blenkernel/BKE_volume_render.h
index d7553ccb10b..c959605721e 100644
--- a/source/blender/blenkernel/BKE_volume_render.h
+++ b/source/blender/blenkernel/BKE_volume_render.h
@@ -43,17 +43,17 @@ typedef struct DenseFloatVolumeGrid {
} DenseFloatVolumeGrid;
bool BKE_volume_grid_dense_floats(const struct Volume *volume,
- struct VolumeGrid *volume_grid,
+ const struct VolumeGrid *volume_grid,
DenseFloatVolumeGrid *r_dense_grid);
void BKE_volume_dense_float_grid_clear(DenseFloatVolumeGrid *dense_grid);
/* Wireframe */
typedef void (*BKE_volume_wireframe_cb)(
- void *userdata, float (*verts)[3], int (*edges)[2], int totvert, int totedge);
+ void *userdata, const float (*verts)[3], const int (*edges)[2], int totvert, int totedge);
void BKE_volume_grid_wireframe(const struct Volume *volume,
- struct VolumeGrid *volume_grid,
+ const struct VolumeGrid *volume_grid,
BKE_volume_wireframe_cb cb,
void *cb_userdata);
@@ -63,7 +63,7 @@ typedef void (*BKE_volume_selection_surface_cb)(
void *userdata, float (*verts)[3], int (*tris)[3], int totvert, int tottris);
void BKE_volume_grid_selection_surface(const struct Volume *volume,
- struct VolumeGrid *volume_grid,
+ const struct VolumeGrid *volume_grid,
BKE_volume_selection_surface_cb cb,
void *cb_userdata);
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index 310ac6c4903..021d7e15814 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -69,6 +69,7 @@ set(SRC
intern/CCGSubSurf_util.c
intern/DerivedMesh.cc
intern/action.c
+ intern/action_mirror.c
intern/addon.c
intern/anim_data.c
intern/anim_path.c
@@ -111,12 +112,13 @@ set(SRC
intern/curve_convert.c
intern/curve_decimate.c
intern/curve_deform.c
+ intern/curve_eval.cc
intern/curveprofile.c
intern/customdata.c
intern/customdata_file.c
intern/data_transfer.c
intern/deform.c
- intern/displist.c
+ intern/displist.cc
intern/displist_tangent.c
intern/dynamicpaint.c
intern/editlattice.c
@@ -126,11 +128,13 @@ set(SRC
intern/editmesh_tangent.c
intern/effect.c
intern/fcurve.c
+ intern/fcurve_cache.c
intern/fcurve_driver.c
intern/fluid.c
intern/fmodifier.c
intern/font.c
intern/freestyle.c
+ intern/geometry_component_curve.cc
intern/geometry_component_instances.cc
intern/geometry_component_mesh.cc
intern/geometry_component_pointcloud.cc
@@ -188,6 +192,7 @@ set(SRC
intern/mesh_remap.c
intern/mesh_remesh_voxel.c
intern/mesh_runtime.c
+ intern/mesh_sample.cc
intern/mesh_tangent.c
intern/mesh_validate.c
intern/mesh_validate.cc
@@ -210,7 +215,7 @@ set(SRC
intern/node_ui_storage.cc
intern/object.c
intern/object_deform.c
- intern/object_dupli.c
+ intern/object_dupli.cc
intern/object_facemap.c
intern/object_update.c
intern/ocean.c
@@ -238,6 +243,10 @@ set(SRC
intern/softbody.c
intern/sound.c
intern/speaker.c
+ intern/spline_base.cc
+ intern/spline_bezier.cc
+ intern/spline_nurbs.cc
+ intern/spline_poly.cc
intern/studiolight.c
intern/subdiv.c
intern/subdiv_ccg.c
@@ -319,6 +328,7 @@ set(SRC
BKE_customdata_file.h
BKE_data_transfer.h
BKE_deform.h
+ BKE_spline.hh
BKE_displist.h
BKE_displist_tangent.h
BKE_duplilist.h
@@ -369,7 +379,7 @@ set(SRC
BKE_mball.h
BKE_mball_tessellate.h
BKE_mesh.h
- BKE_mesh_boolean_convert.h
+ BKE_mesh_boolean_convert.hh
BKE_mesh_fair.h
BKE_mesh_iterators.h
BKE_mesh_mapping.h
@@ -377,6 +387,7 @@ set(SRC
BKE_mesh_remap.h
BKE_mesh_remesh_voxel.h
BKE_mesh_runtime.h
+ BKE_mesh_sample.hh
BKE_mesh_tangent.h
BKE_mesh_types.h
BKE_mesh_wrapper.h
@@ -385,6 +396,7 @@ set(SRC
BKE_multires.h
BKE_nla.h
BKE_node.h
+ BKE_node_ui_storage.hh
BKE_object.h
BKE_object_deform.h
BKE_object_facemap.h
@@ -394,7 +406,6 @@ set(SRC
BKE_paint.h
BKE_particle.h
BKE_pbvh.h
- BKE_persistent_data_handle.hh
BKE_pointcache.h
BKE_pointcloud.h
BKE_preferences.h
@@ -434,10 +445,10 @@ set(SRC
nla_private.h
particle_private.h
tracking_private.h
- intern/attribute_access_intern.hh
intern/CCGSubSurf.h
intern/CCGSubSurf_inline.h
intern/CCGSubSurf_intern.h
+ intern/attribute_access_intern.hh
intern/data_transfer_intern.h
intern/lib_intern.h
intern/multires_inline.h
@@ -580,10 +591,6 @@ if(WITH_CODEC_FFMPEG)
${FFMPEG_LIBRARIES}
)
add_definitions(-DWITH_FFMPEG)
-
- remove_strict_c_flags_file(
- intern/writeffmpeg.c
- )
endif()
if(WITH_PYTHON)
@@ -742,7 +749,7 @@ if(WITH_GMP)
list(APPEND INC_SYS
${GMP_INCLUDE_DIRS}
)
- endif()
+endif()
# # Warnings as errors, this is too strict!
# if(MSVC)
@@ -762,6 +769,7 @@ if(WITH_GTESTS)
intern/fcurve_test.cc
intern/lattice_deform_test.cc
intern/layer_test.cc
+ intern/lib_id_test.cc
intern/tracking_test.cc
)
set(TEST_INC
diff --git a/source/blender/blenkernel/intern/DerivedMesh.cc b/source/blender/blenkernel/intern/DerivedMesh.cc
index 9f51ef5292f..d4dd7e248d5 100644
--- a/source/blender/blenkernel/intern/DerivedMesh.cc
+++ b/source/blender/blenkernel/intern/DerivedMesh.cc
@@ -712,11 +712,13 @@ static float (*get_orco_coords(Object *ob, BMEditMesh *em, int layer, int *free)
if (!em) {
ClothModifierData *clmd = (ClothModifierData *)BKE_modifiers_findby_type(
ob, eModifierType_Cloth);
- KeyBlock *kb = BKE_keyblock_from_key(BKE_key_from_object(ob),
- clmd->sim_parms->shapekey_rest);
+ if (clmd) {
+ KeyBlock *kb = BKE_keyblock_from_key(BKE_key_from_object(ob),
+ clmd->sim_parms->shapekey_rest);
- if (kb && kb->data) {
- return (float(*)[3])kb->data;
+ if (kb && kb->data) {
+ return (float(*)[3])kb->data;
+ }
}
}
@@ -1603,6 +1605,12 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph,
/* This geometry set contains the non-mesh data that might be generated by modifiers. */
GeometrySet geometry_set_final;
+ /* Add the initial mesh component, with a copy of the vertex group names from the object,
+ * since they need to be stored in the geometry set for evaluation. */
+ MeshComponent &initial_mesh_component =
+ geometry_set_final.get_component_for_write<MeshComponent>();
+ initial_mesh_component.copy_vertex_group_names_from_object(*ob);
+
/* Deformed vertex locations array. Deform only modifier need this type of
* float array rather than MVert*. Tracked along with mesh_final as an
* optimization to avoid copying coordinates back and forth if there are
@@ -1853,9 +1861,11 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph,
BKE_id_free(nullptr, mesh_orco);
}
- /* Ensure normals calculation below is correct. */
- BLI_assert((mesh_input->flag & ME_AUTOSMOOTH) == (mesh_final->flag & ME_AUTOSMOOTH));
- BLI_assert(mesh_input->smoothresh == mesh_final->smoothresh);
+ /* Ensure normals calculation below is correct (normal settings have transferred properly).
+ * However, nodes modifiers might create meshes from scratch or transfer meshes from other
+ * objects with different settings, and in general it doesn't make sense to guarantee that
+ * the settings are the same as the original mesh. If necessary, this could become a modifier
+ * type flag. */
BLI_assert(mesh_input->smoothresh == mesh_cage->smoothresh);
/* Compute normals. */
diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c
index f9c2a4e53ad..a7e36b09516 100644
--- a/source/blender/blenkernel/intern/action.c
+++ b/source/blender/blenkernel/intern/action.c
@@ -635,7 +635,7 @@ bPoseChannel *BKE_pose_channel_find_name(const bPose *pose, const char *name)
* \note Use with care, not on Armature poses but for temporal ones.
* \note (currently used for action constraints and in rebuild_pose).
*/
-bPoseChannel *BKE_pose_channel_verify(bPose *pose, const char *name)
+bPoseChannel *BKE_pose_channel_ensure(bPose *pose, const char *name)
{
bPoseChannel *chan;
@@ -656,7 +656,9 @@ bPoseChannel *BKE_pose_channel_verify(bPose *pose, const char *name)
BLI_strncpy(chan->name, name, sizeof(chan->name));
- chan->custom_scale = 1.0f;
+ copy_v3_fl(chan->custom_scale_xyz, 1.0f);
+ zero_v3(chan->custom_translation);
+ zero_v3(chan->custom_rotation_euler);
/* init vars to prevent math errors */
unit_qt(chan->quat);
@@ -815,7 +817,7 @@ void BKE_pose_copy_data_ex(bPose **dst,
*/
if (outPose->chanbase.first != outPose->chanbase.last) {
outPose->chanhash = NULL;
- BKE_pose_channels_hash_make(outPose);
+ BKE_pose_channels_hash_ensure(outPose);
}
outPose->iksolver = src->iksolver;
@@ -945,7 +947,7 @@ bool BKE_pose_channel_in_IK_chain(Object *ob, bPoseChannel *pchan)
* Removes the hash for quick lookup of channels, must
* be done when adding/removing channels.
*/
-void BKE_pose_channels_hash_make(bPose *pose)
+void BKE_pose_channels_hash_ensure(bPose *pose)
{
if (!pose->chanhash) {
bPoseChannel *pchan;
@@ -1191,7 +1193,7 @@ void BKE_pose_free(bPose *pose)
* and ID-Props, used when duplicating bones in editmode.
* (unlike copy_pose_channel_data which only does posing-related stuff).
*
- * \note use when copying bones in editmode (on returned value from #BKE_pose_channel_verify)
+ * \note use when copying bones in editmode (on returned value from #BKE_pose_channel_ensure)
*/
void BKE_pose_channel_copy_data(bPoseChannel *pchan, const bPoseChannel *pchan_from)
{
@@ -1235,8 +1237,10 @@ void BKE_pose_channel_copy_data(bPoseChannel *pchan, const bPoseChannel *pchan_f
if (pchan->custom) {
id_us_plus(&pchan->custom->id);
}
+ copy_v3_v3(pchan->custom_scale_xyz, pchan_from->custom_scale_xyz);
+ copy_v3_v3(pchan->custom_translation, pchan_from->custom_translation);
+ copy_v3_v3(pchan->custom_rotation_euler, pchan_from->custom_rotation_euler);
- pchan->custom_scale = pchan_from->custom_scale;
pchan->drawflag = pchan_from->drawflag;
}
@@ -1316,30 +1320,6 @@ void BKE_pose_tag_update_constraint_flags(bPose *pose)
pose->flag |= POSE_CONSTRAINTS_NEED_UPDATE_FLAGS;
}
-/* Clears all BONE_UNKEYED flags for every pose channel in every pose
- * This should only be called on frame changing, when it is acceptable to
- * do this. Otherwise, these flags should not get cleared as poses may get lost.
- */
-void framechange_poses_clear_unkeyed(Main *bmain)
-{
- Object *ob;
- bPose *pose;
- bPoseChannel *pchan;
-
- /* This needs to be done for each object that has a pose */
- /* TODO: proxies may/may not be correctly handled here... (this needs checking) */
- for (ob = bmain->objects.first; ob; ob = ob->id.next) {
- /* we only need to do this on objects with a pose */
- if ((pose = ob->pose)) {
- for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) {
- if (pchan->bone) {
- pchan->bone->flag &= ~BONE_UNKEYED;
- }
- }
- }
- }
-}
-
/* ************************** Bone Groups ************************** */
/* Adds a new bone-group (name may be NULL) */
@@ -1798,7 +1778,7 @@ void what_does_obaction(Object *ob,
* allocation and also will make lookup slower.
*/
if (pose->chanbase.first != pose->chanbase.last) {
- BKE_pose_channels_hash_make(pose);
+ BKE_pose_channels_hash_ensure(pose);
}
if (pose->flag & POSE_CONSTRAINTS_NEED_UPDATE_FLAGS) {
BKE_pose_update_constraint_flags(pose);
diff --git a/source/blender/blenkernel/intern/action_mirror.c b/source/blender/blenkernel/intern/action_mirror.c
new file mode 100644
index 00000000000..c975d2bfb9c
--- /dev/null
+++ b/source/blender/blenkernel/intern/action_mirror.c
@@ -0,0 +1,457 @@
+/*
+ * 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.
+ */
+
+/** \file
+ * \ingroup bke
+ *
+ * Mirror/Symmetry functions applying to actions.
+ */
+
+#include <math.h>
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_anim_types.h"
+#include "DNA_armature_types.h"
+#include "DNA_object_types.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_math.h"
+#include "BLI_string_utils.h"
+#include "BLI_utildefines.h"
+
+#include "BKE_action.h"
+#include "BKE_armature.h"
+#include "BKE_fcurve.h"
+
+#include "DEG_depsgraph.h"
+
+/* -------------------------------------------------------------------- */
+/** \name Flip the Action (Armature/Pose Objects)
+ *
+ * This flips the action using the rest pose (not the evaluated pose).
+ *
+ * Details:
+ *
+ * - Key-frames are modified in-place, creating new key-frames is not yet supported.
+ * That could be useful if a user for example only has 2x rotation channels set.
+ * In practice users typically keyframe all rotation channels or none.
+ *
+ * - F-curve modifiers are disabled for evaluation,
+ * so the values written back to the keyframes don't include modifier offsets.
+ *
+ * - Sub-frame key-frames aren't supported,
+ * this could be added if needed without much trouble.
+ *
+ * - F-curves must have a #FCurve.bezt array (sampled curves aren't supported).
+ * \{ */
+
+/**
+ * This structure is created for each pose channels F-curve,
+ * an action be evaluated and stored in `fcurve_eval`,
+ * with the mirrored values written into `bezt_array`.
+ *
+ * Store F-curve evaluated values, constructed with a sorted array of rounded keyed-frames,
+ * passed to #action_flip_pchan_cache_init.
+ */
+struct FCurve_KeyCache {
+ /**
+ * When NULL, ignore this channel.
+ */
+ FCurve *fcurve;
+ /**
+ * Cached evaluated F-curve values (without modifiers).
+ */
+ float *fcurve_eval;
+ /**
+ * Cached #FCurve.bezt values, NULL when no key-frame exists on this frame.
+ *
+ * \note The case where two keyframes round to the same frame isn't supported.
+ * In this case only the first will be used.
+ */
+ BezTriple **bezt_array;
+};
+
+/**
+ * Assign `fkc` path, using a `path` lookup for a single value.
+ */
+static void action_flip_pchan_cache_fcurve_assign_value(struct FCurve_KeyCache *fkc,
+ int index,
+ const char *path,
+ struct FCurvePathCache *fcache)
+{
+ FCurve *fcu = BKE_fcurve_pathcache_find(fcache, path, index);
+ if (fcu && fcu->bezt) {
+ fkc->fcurve = fcu;
+ }
+}
+
+/**
+ * Assign #FCurve_KeyCache.fcurve path, using a `path` lookup for an array.
+ */
+static void action_flip_pchan_cache_fcurve_assign_array(struct FCurve_KeyCache *fkc,
+ int fkc_len,
+ const char *path,
+ struct FCurvePathCache *fcache)
+{
+ FCurve **fcurves = alloca(sizeof(*fcurves) * fkc_len);
+ if (BKE_fcurve_pathcache_find_array(fcache, path, fcurves, fkc_len)) {
+ for (int i = 0; i < fkc_len; i++) {
+ if (fcurves[i] && fcurves[i]->bezt) {
+ fkc[i].fcurve = fcurves[i];
+ }
+ }
+ }
+}
+
+/**
+ * Fill in pose channel cache for each frame in `keyed_frames`.
+ *
+ * \param keyed_frames: An array of keyed_frames to evaluate,
+ * note that each frame is rounded to the nearest int.
+ * \param keyed_frames_len: The length of the `keyed_frames` array.
+ */
+static void action_flip_pchan_cache_init(struct FCurve_KeyCache *fkc,
+ const float *keyed_frames,
+ int keyed_frames_len)
+{
+ BLI_assert(fkc->fcurve != NULL);
+
+ /* Cache the F-curve values for `keyed_frames`. */
+ const int fcurve_flag = fkc->fcurve->flag;
+ fkc->fcurve->flag |= FCURVE_MOD_OFF;
+ fkc->fcurve_eval = MEM_mallocN(sizeof(float) * keyed_frames_len, __func__);
+ for (int frame_index = 0; frame_index < keyed_frames_len; frame_index++) {
+ const float evaltime = keyed_frames[frame_index];
+ fkc->fcurve_eval[frame_index] = evaluate_fcurve_only_curve(fkc->fcurve, evaltime);
+ }
+ fkc->fcurve->flag = fcurve_flag;
+
+ /* Cache the #BezTriple for `keyed_frames`, or leave as NULL. */
+ fkc->bezt_array = MEM_mallocN(sizeof(*fkc->bezt_array) * keyed_frames_len, __func__);
+ BezTriple *bezt = fkc->fcurve->bezt;
+ BezTriple *bezt_end = fkc->fcurve->bezt + fkc->fcurve->totvert;
+
+ int frame_index = 0;
+ while (frame_index < keyed_frames_len) {
+ const float evaltime = keyed_frames[frame_index];
+ const float bezt_time = roundf(bezt->vec[1][0]);
+ if (bezt_time > evaltime) {
+ fkc->bezt_array[frame_index++] = NULL;
+ }
+ else {
+ if (bezt_time == evaltime) {
+ fkc->bezt_array[frame_index++] = bezt;
+ }
+ bezt++;
+ if (bezt == bezt_end) {
+ break;
+ }
+ }
+ }
+ /* Clear remaining unset keyed_frames (if-any). */
+ while (frame_index < keyed_frames_len) {
+ fkc->bezt_array[frame_index++] = NULL;
+ }
+}
+
+/**
+ */
+static void action_flip_pchan(Object *ob_arm,
+ const bPoseChannel *pchan,
+ struct FCurvePathCache *fcache)
+{
+ /* Begin F-Curve pose channel value extraction. */
+ /* Use a fixed buffer size as it's known this can only be at most:
+ * `pose.bones["{MAXBONENAME}"].rotation_quaternion`. */
+ char path_xform[256];
+ char pchan_name_esc[sizeof(((bActionChannel *)NULL)->name) * 2];
+ BLI_str_escape(pchan_name_esc, pchan->name, sizeof(pchan_name_esc));
+ const int path_xform_prefix_len = SNPRINTF(path_xform, "pose.bones[\"%s\"]", pchan_name_esc);
+ char *path_xform_suffix = path_xform + path_xform_prefix_len;
+ const int path_xform_suffix_len = sizeof(path_xform) - path_xform_prefix_len;
+
+ /* Lookup and assign all available #FCurve channels,
+ * unavailable channels are left NULL. */
+
+ /**
+ * Structure to store transformation F-curves corresponding to a pose bones transformation.
+ * Match struct member names from #bPoseChannel so macros avoid repetition.
+ *
+ * \note There is no need to read values unless they influence the 4x4 transform matrix,
+ * and no need to write values back unless they would be changed by a modified matrix.
+ * So `rotmode` needs to be read, but doesn't need to be written back to.
+ *
+ * Most bendy-bone settings don't need to be included either, flipping their RNA paths is enough.
+ * Although the X/Y settings could make sense to transform, in practice it would only
+ * work well if the rotation happened to swap X/Y alignment, leave this for now.
+ */
+ struct {
+ struct FCurve_KeyCache loc[3], eul[3], quat[4], rotAxis[3], rotAngle, size[3], rotmode;
+ } fkc_pchan = {{{NULL}}};
+
+#define FCURVE_ASSIGN_VALUE(id, path_test_suffix, index) \
+ BLI_strncpy(path_xform_suffix, path_test_suffix, path_xform_suffix_len); \
+ action_flip_pchan_cache_fcurve_assign_value(&fkc_pchan.id, index, path_xform, fcache)
+
+#define FCURVE_ASSIGN_ARRAY(id, path_test_suffix) \
+ BLI_strncpy(path_xform_suffix, path_test_suffix, path_xform_suffix_len); \
+ action_flip_pchan_cache_fcurve_assign_array( \
+ fkc_pchan.id, ARRAY_SIZE(fkc_pchan.id), path_xform, fcache)
+
+ FCURVE_ASSIGN_ARRAY(loc, ".location");
+ FCURVE_ASSIGN_ARRAY(eul, ".rotation_euler");
+ FCURVE_ASSIGN_ARRAY(quat, ".rotation_quaternion");
+ FCURVE_ASSIGN_ARRAY(rotAxis, ".rotation_axis_angle");
+ FCURVE_ASSIGN_VALUE(rotAngle, ".rotation_axis_angle", 3);
+ FCURVE_ASSIGN_ARRAY(size, ".scale");
+ FCURVE_ASSIGN_VALUE(rotmode, ".rotation_mode", 0);
+
+#undef FCURVE_ASSIGN_VALUE
+#undef FCURVE_ASSIGN_ARRAY
+
+ /* Array of F-curves, for convenient access. */
+#define FCURVE_CHANNEL_LEN (sizeof(fkc_pchan) / sizeof(struct FCurve_KeyCache))
+ FCurve *fcurve_array[FCURVE_CHANNEL_LEN];
+ int fcurve_array_len = 0;
+
+ for (int chan = 0; chan < FCURVE_CHANNEL_LEN; chan++) {
+ struct FCurve_KeyCache *fkc = (struct FCurve_KeyCache *)(&fkc_pchan) + chan;
+ if (fkc->fcurve != NULL) {
+ fcurve_array[fcurve_array_len++] = fkc->fcurve;
+ }
+ }
+
+ /* If this pose has no transform channels, there is nothing to do. */
+ if (fcurve_array_len == 0) {
+ return;
+ }
+
+ /* Calculate an array of frames used by any of the key-frames in `fcurve_array`. */
+ int keyed_frames_len;
+ const float *keyed_frames = BKE_fcurves_calc_keyed_frames(
+ fcurve_array, fcurve_array_len, &keyed_frames_len);
+
+ /* Initialize the pose channel curve cache from the F-curve. */
+ for (int chan = 0; chan < FCURVE_CHANNEL_LEN; chan++) {
+ struct FCurve_KeyCache *fkc = (struct FCurve_KeyCache *)(&fkc_pchan) + chan;
+ if (fkc->fcurve == NULL) {
+ continue;
+ }
+ action_flip_pchan_cache_init(fkc, keyed_frames, keyed_frames_len);
+ }
+
+ /* X-axis flipping matrix. */
+ float flip_mtx[4][4];
+ unit_m4(flip_mtx);
+ flip_mtx[0][0] = -1;
+
+ bPoseChannel *pchan_flip = NULL;
+ char pchan_name_flip[MAXBONENAME];
+ BLI_string_flip_side_name(pchan_name_flip, pchan->name, false, sizeof(pchan_name_flip));
+ if (!STREQ(pchan_name_flip, pchan->name)) {
+ pchan_flip = BKE_pose_channel_find_name(ob_arm->pose, pchan_name_flip);
+ }
+
+ float arm_mat_inv[4][4];
+ invert_m4_m4(arm_mat_inv, pchan_flip ? pchan_flip->bone->arm_mat : pchan->bone->arm_mat);
+
+ /* Now flip the transformation & write it back to the F-curves in `fkc_pchan`. */
+
+ for (int frame_index = 0; frame_index < keyed_frames_len; frame_index++) {
+
+ /* Temporary pose channel to write values into,
+ * using the `fkc_pchan` values, falling back to the values in the pose channel. */
+ bPoseChannel pchan_temp = *pchan;
+
+ /* Load the values into the channel. */
+#define READ_VALUE_FLT(id) \
+ if (fkc_pchan.id.fcurve_eval != NULL) { \
+ pchan_temp.id = fkc_pchan.id.fcurve_eval[frame_index]; \
+ } \
+ ((void)0)
+
+#define READ_VALUE_INT(id) \
+ if (fkc_pchan.id.fcurve_eval != NULL) { \
+ pchan_temp.id = floorf(fkc_pchan.id.fcurve_eval[frame_index] + 0.5f); \
+ } \
+ ((void)0)
+
+#define READ_ARRAY_FLT(id) \
+ for (int i = 0; i < ARRAY_SIZE(pchan_temp.id); i++) { \
+ READ_VALUE_FLT(id[i]); \
+ } \
+ ((void)0)
+
+ READ_ARRAY_FLT(loc);
+ READ_ARRAY_FLT(eul);
+ READ_ARRAY_FLT(quat);
+ READ_ARRAY_FLT(rotAxis);
+ READ_VALUE_FLT(rotAngle);
+ READ_ARRAY_FLT(size);
+ READ_VALUE_INT(rotmode);
+
+#undef READ_ARRAY_FLT
+#undef READ_VALUE_FLT
+#undef READ_VALUE_INT
+
+ float chan_mat[4][4];
+ BKE_pchan_to_mat4(&pchan_temp, chan_mat);
+
+ /* Move to the pose-space. */
+ mul_m4_m4m4(chan_mat, pchan->bone->arm_mat, chan_mat);
+
+ /* Flip the matrix. */
+ mul_m4_m4m4(chan_mat, chan_mat, flip_mtx);
+ mul_m4_m4m4(chan_mat, flip_mtx, chan_mat);
+
+ /* Move back to bone-space space, using the flipped bone if it exists. */
+ mul_m4_m4m4(chan_mat, arm_mat_inv, chan_mat);
+
+ BKE_pchan_apply_mat4(&pchan_temp, chan_mat, false);
+
+ /* Write the values back to the F-curves. */
+#define WRITE_VALUE_FLT(id) \
+ if (fkc_pchan.id.fcurve_eval != NULL) { \
+ BezTriple *bezt = fkc_pchan.id.bezt_array[frame_index]; \
+ if (bezt != NULL) { \
+ const float delta = pchan_temp.id - bezt->vec[1][1]; \
+ bezt->vec[0][1] += delta; \
+ bezt->vec[1][1] += delta; \
+ bezt->vec[2][1] += delta; \
+ } \
+ } \
+ ((void)0)
+
+#define WRITE_ARRAY_FLT(id) \
+ for (int i = 0; i < ARRAY_SIZE(pchan_temp.id); i++) { \
+ WRITE_VALUE_FLT(id[i]); \
+ } \
+ ((void)0)
+
+ /* Write the values back the the F-curves. */
+ WRITE_ARRAY_FLT(loc);
+ WRITE_ARRAY_FLT(eul);
+ WRITE_ARRAY_FLT(quat);
+ WRITE_ARRAY_FLT(rotAxis);
+ WRITE_VALUE_FLT(rotAngle);
+ WRITE_ARRAY_FLT(size);
+ /* No need to write back 'rotmode' as it can't be transformed. */
+
+#undef WRITE_ARRAY_FLT
+#undef WRITE_VALUE_FLT
+ }
+
+ /* Recalculate handles. */
+ for (int i = 0; i < fcurve_array_len; i++) {
+ calchandles_fcurve_ex(fcurve_array[i], 0);
+ }
+
+ MEM_freeN((void *)keyed_frames);
+
+ for (int chan = 0; chan < FCURVE_CHANNEL_LEN; chan++) {
+ struct FCurve_KeyCache *fkc = (struct FCurve_KeyCache *)(&fkc_pchan) + chan;
+ if (fkc->fcurve_eval) {
+ MEM_freeN(fkc->fcurve_eval);
+ }
+ if (fkc->bezt_array) {
+ MEM_freeN(fkc->bezt_array);
+ }
+ }
+}
+
+/**
+ * Swap all RNA paths left/right.
+ */
+static void action_flip_pchan_rna_paths(struct bAction *act)
+{
+ const char *path_pose_prefix = "pose.bones[\"";
+ const int path_pose_prefix_len = strlen(path_pose_prefix);
+
+ /* Tag curves that have renamed f-curves. */
+ LISTBASE_FOREACH (bActionGroup *, agrp, &act->groups) {
+ agrp->flag &= ~AGRP_TEMP;
+ }
+
+ LISTBASE_FOREACH (FCurve *, fcu, &act->curves) {
+ if (!STRPREFIX(fcu->rna_path, path_pose_prefix)) {
+ continue;
+ }
+
+ const char *name_esc = fcu->rna_path + path_pose_prefix_len;
+ const char *name_esc_end = BLI_str_escape_find_quote(name_esc);
+
+ /* While unlikely, an RNA path could be malformed. */
+ if (UNLIKELY(name_esc_end == NULL)) {
+ continue;
+ }
+
+ char name[MAXBONENAME];
+ const size_t name_esc_len = (size_t)(name_esc_end - name_esc);
+ const size_t name_len = BLI_str_unescape(name, name_esc, name_esc_len);
+
+ /* While unlikely, data paths could be constructed that have longer names than
+ * are currently supported. */
+ if (UNLIKELY(name_len >= sizeof(name))) {
+ continue;
+ }
+
+ /* When the flipped name differs, perform the rename. */
+ char name_flip[MAXBONENAME];
+ BLI_string_flip_side_name(name_flip, name, false, sizeof(name_flip));
+ if (!STREQ(name_flip, name)) {
+ char name_flip_esc[MAXBONENAME * 2];
+ BLI_str_escape(name_flip_esc, name_flip, sizeof(name_flip_esc));
+ char *path_flip = BLI_sprintfN("pose.bones[\"%s%s", name_flip_esc, name_esc_end);
+ MEM_freeN(fcu->rna_path);
+ fcu->rna_path = path_flip;
+
+ if (fcu->grp != NULL) {
+ fcu->grp->flag |= AGRP_TEMP;
+ }
+ }
+ }
+
+ /* Rename tagged groups. */
+ LISTBASE_FOREACH (bActionGroup *, agrp, &act->groups) {
+ if ((agrp->flag & AGRP_TEMP) == 0) {
+ continue;
+ }
+ agrp->flag &= ~AGRP_TEMP;
+ char name_flip[MAXBONENAME];
+ BLI_string_flip_side_name(name_flip, agrp->name, false, sizeof(name_flip));
+ if (!STREQ(name_flip, agrp->name)) {
+ STRNCPY(agrp->name, name_flip);
+ }
+ }
+}
+
+void BKE_action_flip_with_pose(struct bAction *act, struct Object *ob_arm)
+{
+ struct FCurvePathCache *fcache = BKE_fcurve_pathcache_create(&act->curves);
+ int i;
+ LISTBASE_FOREACH_INDEX (bPoseChannel *, pchan, &ob_arm->pose->chanbase, i) {
+ action_flip_pchan(ob_arm, pchan, fcache);
+ }
+ BKE_fcurve_pathcache_destroy(fcache);
+
+ action_flip_pchan_rna_paths(act);
+
+ DEG_id_tag_update(&act->id, ID_RECALC_COPY_ON_WRITE);
+}
+
+/** \} */
diff --git a/source/blender/blenkernel/intern/anim_data.c b/source/blender/blenkernel/intern/anim_data.c
index 633d6202222..44b760aefc8 100644
--- a/source/blender/blenkernel/intern/anim_data.c
+++ b/source/blender/blenkernel/intern/anim_data.c
@@ -287,8 +287,10 @@ bool BKE_animdata_id_is_animated(const struct ID *id)
!BLI_listbase_is_empty(&adt->overrides);
}
-/** Callback used by lib_query to walk over all ID usages (mimics `foreach_id` callback of
- * `IDTypeInfo` structure). */
+/**
+ * Callback used by lib_query to walk over all ID usages (mimics `foreach_id` callback of
+ * `IDTypeInfo` structure).
+ */
void BKE_animdata_foreach_id(AnimData *adt, LibraryForeachIDData *data)
{
LISTBASE_FOREACH (FCurve *, fcu, &adt->drivers) {
@@ -352,7 +354,7 @@ AnimData *BKE_animdata_copy(Main *bmain, AnimData *adt, const int flag)
}
/* duplicate NLA data */
- BKE_nla_tracks_copy(bmain, &dadt->nla_tracks, &adt->nla_tracks, flag);
+ BKE_nla_tracks_copy_from_adt(bmain, dadt, adt, flag);
/* duplicate drivers (F-Curves) */
BKE_fcurves_copy(&dadt->drivers, &adt->drivers);
@@ -945,7 +947,7 @@ static bool nlastrips_path_rename_fix(ID *owner_id,
owner_id, prefix, oldName, newName, oldKey, newKey, &strip->act->curves, verify_paths);
}
/* Ignore own F-Curves, since those are local. */
- /* Check sub-strips (if metas) */
+ /* Check sub-strips (if meta-strips). */
is_changed |= nlastrips_path_rename_fix(
owner_id, prefix, oldName, newName, oldKey, newKey, &strip->strips, verify_paths);
}
@@ -1175,7 +1177,7 @@ static bool nlastrips_path_remove_fix(const char *prefix, ListBase *strips)
any_removed |= fcurves_path_remove_fix(prefix, &strip->act->curves);
}
- /* check sub-strips (if metas) */
+ /* Check sub-strips (if meta-strips). */
any_removed |= nlastrips_path_remove_fix(prefix, &strip->strips);
}
return any_removed;
@@ -1243,7 +1245,7 @@ static void nlastrips_apply_all_curves_cb(ID *id, ListBase *strips, AllFCurvesCb
fcurves_apply_cb(id, &strip->act->curves, wrapper->func, wrapper->user_data);
}
- /* check sub-strips (if metas) */
+ /* Check sub-strips (if meta-strips). */
nlastrips_apply_all_curves_cb(id, &strip->strips, wrapper);
}
}
diff --git a/source/blender/blenkernel/intern/anim_path.c b/source/blender/blenkernel/intern/anim_path.c
index 628e54971ce..af2b386a30a 100644
--- a/source/blender/blenkernel/intern/anim_path.c
+++ b/source/blender/blenkernel/intern/anim_path.c
@@ -42,157 +42,192 @@ static CLG_LogRef LOG = {"bke.anim"};
/* ******************************************************************** */
/* Curve Paths - for curve deforms and/or curve following */
-/**
- * Free curve path data
- *
- * \note Frees the path itself!
- * \note This is increasingly inaccurate with non-uniform #BevPoint subdivisions T24633.
- */
-void free_path(Path *path)
+static int get_bevlist_seg_array_size(const BevList *bl)
{
- if (path->data) {
- MEM_freeN(path->data);
+ if (bl->poly >= 0) {
+ /* Cyclic curve. */
+ return bl->nr;
}
- MEM_freeN(path);
+
+ return bl->nr - 1;
}
-/**
- * Calculate a curve-deform path for a curve
- * - Only called from displist.c -> #do_makeDispListCurveTypes
- */
-void calc_curvepath(Object *ob, ListBase *nurbs)
+int BKE_anim_path_get_array_size(const CurveCache *curve_cache)
+{
+ BLI_assert(curve_cache != NULL);
+
+ BevList *bl = curve_cache->bev.first;
+
+ BLI_assert(bl != NULL && bl->nr > 1);
+
+ return get_bevlist_seg_array_size(bl);
+}
+
+float BKE_anim_path_get_length(const CurveCache *curve_cache)
{
- BevList *bl;
- BevPoint *bevp, *bevpn, *bevpfirst, *bevplast;
- PathPoint *pp;
- Nurb *nu;
- Path *path;
- float *fp, *dist, *maxdist, xyz[3];
- float fac, d = 0, fac1, fac2;
- int a, tot, cycl = 0;
-
- /* in a path vertices are with equal differences: path->len = number of verts */
- /* NOW WITH BEVELCURVE!!! */
+ const int seg_size = BKE_anim_path_get_array_size(curve_cache);
+ return curve_cache->anim_path_accum_length[seg_size - 1];
+}
+void BKE_anim_path_calc_data(Object *ob)
+{
if (ob == NULL || ob->type != OB_CURVE) {
return;
}
-
- if (ob->runtime.curve_cache->path) {
- free_path(ob->runtime.curve_cache->path);
+ if (ob->runtime.curve_cache == NULL) {
+ CLOG_WARN(&LOG, "No curve cache!");
+ return;
}
- ob->runtime.curve_cache->path = NULL;
-
- /* weak! can only use first curve */
- bl = ob->runtime.curve_cache->bev.first;
+ /* We only use the first curve. */
+ BevList *bl = ob->runtime.curve_cache->bev.first;
if (bl == NULL || !bl->nr) {
+ CLOG_WARN(&LOG, "No bev list data!");
return;
}
- nu = nurbs->first;
+ /* Free old data. */
+ if (ob->runtime.curve_cache->anim_path_accum_length) {
+ MEM_freeN((void *)ob->runtime.curve_cache->anim_path_accum_length);
+ }
- ob->runtime.curve_cache->path = path = MEM_callocN(sizeof(Path), "calc_curvepath");
+ /* We assume that we have at least two points.
+ * If there is less than two points in the curve,
+ * no BevList should have been generated.
+ */
+ BLI_assert(bl->nr > 1);
- /* if POLY: last vertice != first vertice */
- cycl = (bl->poly != -1);
+ const int seg_size = get_bevlist_seg_array_size(bl);
+ float *len_data = (float *)MEM_mallocN(sizeof(float) * seg_size, "calcpathdist");
+ ob->runtime.curve_cache->anim_path_accum_length = len_data;
- tot = cycl ? bl->nr : bl->nr - 1;
+ BevPoint *bp_arr = bl->bevpoints;
+ float prev_len = 0.0f;
+ for (int i = 0; i < bl->nr - 1; i++) {
+ prev_len += len_v3v3(bp_arr[i].vec, bp_arr[i + 1].vec);
+ len_data[i] = prev_len;
+ }
- path->len = tot + 1;
- /* Exception: vector handle paths and polygon paths should be subdivided
- * at least a factor resolution. */
- if (path->len < nu->resolu * SEGMENTSU(nu)) {
- path->len = nu->resolu * SEGMENTSU(nu);
+ if (bl->poly >= 0) {
+ /* Cyclic curve. */
+ len_data[seg_size - 1] = prev_len + len_v3v3(bp_arr[0].vec, bp_arr[bl->nr - 1].vec);
}
+}
+
+static void get_curve_points_from_idx(const int idx,
+ const BevList *bl,
+ const bool is_cyclic,
+ BevPoint const **r_p0,
+ BevPoint const **r_p1,
+ BevPoint const **r_p2,
+ BevPoint const **r_p3)
+{
+ BLI_assert(idx >= 0);
+ BLI_assert(idx < bl->nr - 1 || (is_cyclic && idx < bl->nr));
+ BLI_assert(bl->nr > 1);
- dist = (float *)MEM_mallocN(sizeof(float) * (tot + 1), "calcpathdist");
+ const BevPoint *bp_arr = bl->bevpoints;
- /* all lengths in *dist */
- bevp = bevpfirst = bl->bevpoints;
- fp = dist;
- *fp = 0.0f;
- for (a = 0; a < tot; a++) {
- fp++;
- if (cycl && a == tot - 1) {
- sub_v3_v3v3(xyz, bevpfirst->vec, bevp->vec);
+ /* First segment. */
+ if (idx == 0) {
+ *r_p1 = &bp_arr[0];
+ if (is_cyclic) {
+ *r_p0 = &bp_arr[bl->nr - 1];
}
else {
- sub_v3_v3v3(xyz, (bevp + 1)->vec, bevp->vec);
+ *r_p0 = *r_p1;
}
- *fp = *(fp - 1) + len_v3(xyz);
- bevp++;
- }
+ *r_p2 = &bp_arr[1];
- path->totdist = *fp;
-
- /* the path verts in path->data */
- /* now also with TILT value */
- pp = path->data = (PathPoint *)MEM_callocN(sizeof(PathPoint) * path->len, "pathdata");
-
- bevp = bevpfirst;
- bevpn = bevp + 1;
- bevplast = bevpfirst + (bl->nr - 1);
- if (UNLIKELY(bevpn > bevplast)) {
- bevpn = cycl ? bevpfirst : bevplast;
- }
- fp = dist + 1;
- maxdist = dist + tot;
- fac = 1.0f / ((float)path->len - 1.0f);
- fac = fac * path->totdist;
-
- for (a = 0; a < path->len; a++) {
-
- d = ((float)a) * fac;
-
- /* we're looking for location (distance) 'd' in the array */
- if (LIKELY(tot > 0)) {
- while ((fp < maxdist) && (d >= *fp)) {
- fp++;
- if (bevp < bevplast) {
- bevp++;
- }
- bevpn = bevp + 1;
- if (UNLIKELY(bevpn > bevplast)) {
- bevpn = cycl ? bevpfirst : bevplast;
- }
- }
-
- fac1 = (*(fp)-d) / (*(fp) - *(fp - 1));
- fac2 = 1.0f - fac1;
+ if (bl->nr > 2) {
+ *r_p3 = &bp_arr[2];
}
else {
- fac1 = 1.0f;
- fac2 = 0.0f;
+ *r_p3 = *r_p2;
}
+ return;
+ }
- interp_v3_v3v3(pp->vec, bevp->vec, bevpn->vec, fac2);
- pp->vec[3] = fac1 * bevp->tilt + fac2 * bevpn->tilt;
- pp->radius = fac1 * bevp->radius + fac2 * bevpn->radius;
- pp->weight = fac1 * bevp->weight + fac2 * bevpn->weight;
- interp_qt_qtqt(pp->quat, bevp->quat, bevpn->quat, fac2);
- normalize_qt(pp->quat);
+ /* Last segment (or next to last in a cyclic curve). */
+ if (idx == bl->nr - 2) {
+ /* The case when the bl->nr == 2 falls in to the "first segment" check above.
+ * So here we can assume that bl->nr > 2.
+ */
+ *r_p0 = &bp_arr[idx - 1];
+ *r_p1 = &bp_arr[idx];
+ *r_p2 = &bp_arr[idx + 1];
+
+ if (is_cyclic) {
+ *r_p3 = &bp_arr[0];
+ }
+ else {
+ *r_p3 = *r_p2;
+ }
+ return;
+ }
- pp++;
+ if (idx == bl->nr - 1) {
+ /* Last segment in a cyclic curve. This should only trigger if the curve is cyclic
+ * as it gets an extra segment between the end and the start point. */
+ *r_p0 = &bp_arr[idx - 1];
+ *r_p1 = &bp_arr[idx];
+ *r_p2 = &bp_arr[0];
+ *r_p3 = &bp_arr[1];
+ return;
}
- MEM_freeN(dist);
+ /* To get here the curve has to have four curve points or more and idx can't
+ * be the first or the last segment.
+ * So we can assume that we can get four points without any special checks.
+ */
+ *r_p0 = &bp_arr[idx - 1];
+ *r_p1 = &bp_arr[idx];
+ *r_p2 = &bp_arr[idx + 1];
+ *r_p3 = &bp_arr[idx + 2];
}
-static int interval_test(const int min, const int max, int p1, const int cycl)
+static bool binary_search_anim_path(const float *accum_len_arr,
+ const int seg_size,
+ const float goal_len,
+ int *r_idx,
+ float *r_frac)
{
- if (cycl) {
- p1 = mod_i(p1 - min, (max - min + 1)) + min;
- }
- else {
- if (p1 < min) {
- p1 = min;
+ float left_len, right_len;
+ int cur_idx = 0, cur_base = 0;
+ int cur_step = seg_size - 1;
+
+ while (true) {
+ cur_idx = cur_base + cur_step / 2;
+ left_len = accum_len_arr[cur_idx];
+ right_len = accum_len_arr[cur_idx + 1];
+
+ if (left_len <= goal_len && right_len > goal_len) {
+ *r_idx = cur_idx + 1;
+ *r_frac = (goal_len - left_len) / (right_len - left_len);
+ return true;
}
- else if (p1 > max) {
- p1 = max;
+ if (cur_idx == 0) {
+ /* We ended up at the first segment. The point must be in here. */
+ *r_idx = 0;
+ *r_frac = goal_len / accum_len_arr[0];
+ return true;
}
+
+ if (UNLIKELY(cur_step == 0)) {
+ /* This should never happen unless there is something horribly wrong. */
+ CLOG_ERROR(&LOG, "Couldn't find any valid point on the animation path!");
+ BLI_assert(!"Couldn't find any valid point on the animation path!");
+ return false;
+ }
+
+ if (left_len < goal_len) {
+ /* Go to the right. */
+ cur_base = cur_idx + 1;
+ cur_step--;
+ } /* Else, go to the left. */
+
+ cur_step /= 2;
}
- return p1;
}
/**
@@ -203,66 +238,70 @@ static int interval_test(const int min, const int max, int p1, const int cycl)
*
* \return success.
*/
-bool where_on_path(const Object *ob,
- float ctime,
- float r_vec[4],
- float r_dir[3],
- float r_quat[4],
- float *r_radius,
- float *r_weight)
+bool BKE_where_on_path(const Object *ob,
+ float ctime,
+ float r_vec[4],
+ float r_dir[3],
+ float r_quat[4],
+ float *r_radius,
+ float *r_weight)
{
- Curve *cu;
- const Nurb *nu;
- const BevList *bl;
- const Path *path;
- const PathPoint *pp, *p0, *p1, *p2, *p3;
- float fac;
- float data[4];
- int cycl = 0, s0, s1, s2, s3;
- const ListBase *nurbs;
-
if (ob == NULL || ob->type != OB_CURVE) {
return false;
}
- cu = ob->data;
- if (ob->runtime.curve_cache == NULL || ob->runtime.curve_cache->path == NULL ||
- ob->runtime.curve_cache->path->data == NULL) {
- CLOG_WARN(&LOG, "no path!");
- return false;
- }
- path = ob->runtime.curve_cache->path;
- pp = path->data;
-
- /* test for cyclic */
- bl = ob->runtime.curve_cache->bev.first;
- if (!bl) {
+ Curve *cu = ob->data;
+ if (ob->runtime.curve_cache == NULL) {
+ CLOG_WARN(&LOG, "No curve cache!");
return false;
}
- if (!bl->nr) {
+ /* We only use the first curve. */
+ BevList *bl = ob->runtime.curve_cache->bev.first;
+ if (bl == NULL || !bl->nr) {
+ CLOG_WARN(&LOG, "No bev list data!");
return false;
}
- if (bl->poly > -1) {
- cycl = 1;
+
+ /* Test for cyclic curve. */
+ const bool is_cyclic = bl->poly >= 0;
+
+ if (is_cyclic) {
+ /* Wrap the time into a 0.0 - 1.0 range. */
+ if (ctime < 0.0f || ctime > 1.0f) {
+ ctime -= floorf(ctime);
+ }
}
- /* values below zero for non-cyclic curves give strange results */
- BLI_assert(cycl || ctime >= 0.0f);
+ /* The curve points for this ctime value. */
+ const BevPoint *p0, *p1, *p2, *p3;
- ctime *= (path->len - 1);
+ float frac;
+ const int seg_size = get_bevlist_seg_array_size(bl);
+ const float *accum_len_arr = ob->runtime.curve_cache->anim_path_accum_length;
+ const float goal_len = ctime * accum_len_arr[seg_size - 1];
- s1 = (int)floor(ctime);
- fac = (float)(s1 + 1) - ctime;
+ /* Are we simply trying to get the start/end point? */
+ if (ctime <= 0.0f || ctime >= 1.0f) {
+ const float clamp_time = clamp_f(ctime, 0.0f, 1.0f);
+ const int idx = clamp_time * (seg_size - 1);
+ get_curve_points_from_idx(idx, bl, is_cyclic, &p0, &p1, &p2, &p3);
- /* path->len is corrected for cyclic */
- s0 = interval_test(0, path->len - 1 - cycl, s1 - 1, cycl);
- s1 = interval_test(0, path->len - 1 - cycl, s1, cycl);
- s2 = interval_test(0, path->len - 1 - cycl, s1 + 1, cycl);
- s3 = interval_test(0, path->len - 1 - cycl, s1 + 2, cycl);
+ if (idx == 0) {
+ frac = goal_len / accum_len_arr[0];
+ }
+ else {
+ frac = (goal_len - accum_len_arr[idx - 1]) / (accum_len_arr[idx] - accum_len_arr[idx - 1]);
+ }
+ }
+ else {
+ /* Do binary search to get the correct segment. */
+ int idx;
+ const bool found_idx = binary_search_anim_path(accum_len_arr, seg_size, goal_len, &idx, &frac);
- p0 = pp + s0;
- p1 = pp + s1;
- p2 = pp + s2;
- p3 = pp + s3;
+ if (UNLIKELY(!found_idx)) {
+ return false;
+ }
+ get_curve_points_from_idx(idx, bl, is_cyclic, &p0, &p1, &p2, &p3);
+ }
/* NOTE: commented out for follow constraint
*
@@ -272,65 +311,68 @@ bool where_on_path(const Object *ob,
*/
// if (cu->flag & CU_FOLLOW) {
- key_curve_tangent_weights(1.0f - fac, data, KEY_BSPLINE);
+ float w[4];
+
+ key_curve_tangent_weights(frac, w, KEY_BSPLINE);
- interp_v3_v3v3v3v3(r_dir, p0->vec, p1->vec, p2->vec, p3->vec, data);
+ interp_v3_v3v3v3v3(r_dir, p0->vec, p1->vec, p2->vec, p3->vec, w);
/* Make compatible with #vec_to_quat. */
negate_v3(r_dir);
//}
- nurbs = BKE_curve_editNurbs_get(cu);
+ const ListBase *nurbs = BKE_curve_editNurbs_get(cu);
if (!nurbs) {
nurbs = &cu->nurb;
}
- nu = nurbs->first;
+ const Nurb *nu = nurbs->first;
/* make sure that first and last frame are included in the vectors here */
- if (nu->type == CU_POLY) {
- key_curve_position_weights(1.0f - fac, data, KEY_LINEAR);
+ if (ELEM(nu->type, CU_POLY, CU_BEZIER, CU_NURBS)) {
+ key_curve_position_weights(frac, w, KEY_LINEAR);
}
- else if (nu->type == CU_BEZIER) {
- key_curve_position_weights(1.0f - fac, data, KEY_LINEAR);
- }
- else if (s0 == s1 || p2 == p3) {
- key_curve_position_weights(1.0f - fac, data, KEY_CARDINAL);
+ else if (p2 == p3) {
+ key_curve_position_weights(frac, w, KEY_CARDINAL);
}
else {
- key_curve_position_weights(1.0f - fac, data, KEY_BSPLINE);
+ key_curve_position_weights(frac, w, KEY_BSPLINE);
}
r_vec[0] = /* X */
- data[0] * p0->vec[0] + data[1] * p1->vec[0] + data[2] * p2->vec[0] + data[3] * p3->vec[0];
+ w[0] * p0->vec[0] + w[1] * p1->vec[0] + w[2] * p2->vec[0] + w[3] * p3->vec[0];
r_vec[1] = /* Y */
- data[0] * p0->vec[1] + data[1] * p1->vec[1] + data[2] * p2->vec[1] + data[3] * p3->vec[1];
+ w[0] * p0->vec[1] + w[1] * p1->vec[1] + w[2] * p2->vec[1] + w[3] * p3->vec[1];
r_vec[2] = /* Z */
- data[0] * p0->vec[2] + data[1] * p1->vec[2] + data[2] * p2->vec[2] + data[3] * p3->vec[2];
- r_vec[3] = /* Tilt, should not be needed since we have quat still used */
- data[0] * p0->vec[3] + data[1] * p1->vec[3] + data[2] * p2->vec[3] + data[3] * p3->vec[3];
+ w[0] * p0->vec[2] + w[1] * p1->vec[2] + w[2] * p2->vec[2] + w[3] * p3->vec[2];
+
+ /* Clamp weights to 0-1 as we don't want to extrapolate other values than position. */
+ clamp_v4(w, 0.0f, 1.0f);
+
+ /* Tilt, should not be needed since we have quat still used. */
+ r_vec[3] = w[0] * p0->tilt + w[1] * p1->tilt + w[2] * p2->tilt + w[3] * p3->tilt;
if (r_quat) {
float totfac, q1[4], q2[4];
- totfac = data[0] + data[3];
+ totfac = w[0] + w[3];
if (totfac > FLT_EPSILON) {
- interp_qt_qtqt(q1, p0->quat, p3->quat, data[3] / totfac);
+ interp_qt_qtqt(q1, p0->quat, p3->quat, w[3] / totfac);
}
else {
copy_qt_qt(q1, p1->quat);
}
- totfac = data[1] + data[2];
+ totfac = w[1] + w[2];
if (totfac > FLT_EPSILON) {
- interp_qt_qtqt(q2, p1->quat, p2->quat, data[2] / totfac);
+ interp_qt_qtqt(q2, p1->quat, p2->quat, w[2] / totfac);
}
else {
copy_qt_qt(q2, p3->quat);
}
- totfac = data[0] + data[1] + data[2] + data[3];
+ totfac = w[0] + w[1] + w[2] + w[3];
if (totfac > FLT_EPSILON) {
- interp_qt_qtqt(r_quat, q1, q2, (data[1] + data[2]) / totfac);
+ interp_qt_qtqt(r_quat, q1, q2, (w[1] + w[2]) / totfac);
}
else {
copy_qt_qt(r_quat, q2);
@@ -338,13 +380,11 @@ bool where_on_path(const Object *ob,
}
if (r_radius) {
- *r_radius = data[0] * p0->radius + data[1] * p1->radius + data[2] * p2->radius +
- data[3] * p3->radius;
+ *r_radius = w[0] * p0->radius + w[1] * p1->radius + w[2] * p2->radius + w[3] * p3->radius;
}
if (r_weight) {
- *r_weight = data[0] * p0->weight + data[1] * p1->weight + data[2] * p2->weight +
- data[3] * p3->weight;
+ *r_weight = w[0] * p0->weight + w[1] * p1->weight + w[2] * p2->weight + w[3] * p3->weight;
}
return true;
diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c
index 9a890fd02be..e347306e0ae 100644
--- a/source/blender/blenkernel/intern/anim_sys.c
+++ b/source/blender/blenkernel/intern/anim_sys.c
@@ -1040,6 +1040,7 @@ static NlaEvalChannelSnapshot *nlaevalchan_snapshot_new(NlaEvalChannel *nec)
nec_snapshot->channel = nec;
nec_snapshot->length = length;
nlavalidmask_init(&nec_snapshot->blend_domain, length);
+ nlavalidmask_init(&nec_snapshot->remap_domain, length);
return nec_snapshot;
}
@@ -1050,6 +1051,7 @@ static void nlaevalchan_snapshot_free(NlaEvalChannelSnapshot *nec_snapshot)
BLI_assert(!nec_snapshot->is_base);
nlavalidmask_free(&nec_snapshot->blend_domain);
+ nlavalidmask_free(&nec_snapshot->remap_domain);
MEM_freeN(nec_snapshot);
}
@@ -1605,8 +1607,9 @@ static bool nla_combine_get_inverted_strip_value(const int mix_mode,
}
}
-/** Accumulate quaternion channels for Combine mode according to influence.
- * \returns blended_value = lower_values @ strip_values^infl
+/**
+ * Accumulate quaternion channels for Combine mode according to influence.
+ * \returns `blended_value = lower_values @ strip_values^infl`
*/
static void nla_combine_quaternion(const float lower_values[4],
const float strip_values[4],
@@ -1649,6 +1652,363 @@ static bool nla_combine_quaternion_get_inverted_strip_values(const float lower_v
}
/* ---------------------- */
+
+/* Assert necs and necs->channel is nonNull. */
+static void nlaevalchan_assert_nonNull(NlaEvalChannelSnapshot *necs)
+{
+ UNUSED_VARS_NDEBUG(necs);
+ BLI_assert(necs != NULL && necs->channel != NULL);
+}
+
+/* Assert that the channels given can be blended or combined together. */
+static void nlaevalchan_assert_blendOrcombine_compatible(NlaEvalChannelSnapshot *lower_necs,
+ NlaEvalChannelSnapshot *upper_necs,
+ NlaEvalChannelSnapshot *blended_necs)
+{
+ UNUSED_VARS_NDEBUG(lower_necs, upper_necs, blended_necs);
+ BLI_assert(!ELEM(NULL, lower_necs, blended_necs));
+ BLI_assert(upper_necs == NULL || lower_necs->length == upper_necs->length);
+ BLI_assert(lower_necs->length == blended_necs->length);
+}
+
+/* Assert that the channels given can be blended or combined together as a quaternion. */
+static void nlaevalchan_assert_blendOrcombine_compatible_quaternion(
+ NlaEvalChannelSnapshot *lower_necs,
+ NlaEvalChannelSnapshot *upper_necs,
+ NlaEvalChannelSnapshot *blended_necs)
+{
+ nlaevalchan_assert_blendOrcombine_compatible(lower_necs, upper_necs, blended_necs);
+ BLI_assert(lower_necs->length == 4);
+}
+
+static void nlaevalchan_copy_values(NlaEvalChannelSnapshot *dst, NlaEvalChannelSnapshot *src)
+{
+ memcpy(dst->values, src->values, src->length * sizeof(float));
+}
+
+/**
+ * Copies lower necs to blended necs if upper necs is NULL or has zero influence.
+ * \return true if copied.
+ */
+static bool nlaevalchan_blendOrcombine_try_copy_lower(NlaEvalChannelSnapshot *lower_necs,
+ NlaEvalChannelSnapshot *upper_necs,
+ const float upper_influence,
+ NlaEvalChannelSnapshot *r_blended_necs)
+{
+ const bool has_influence = !IS_EQF(upper_influence, 0.0f);
+ if (upper_necs != NULL && has_influence) {
+ return false;
+ }
+
+ nlaevalchan_copy_values(r_blended_necs, lower_necs);
+ return true;
+}
+
+/**
+ * Based on blend-mode, blend lower necs with upper necs into blended necs.
+ *
+ * Each upper value's blend domain determines whether to blend or to copy directly from lower.
+ */
+static void nlaevalchan_blend_value(NlaEvalChannelSnapshot *lower_necs,
+ NlaEvalChannelSnapshot *upper_necs,
+ const int upper_blendmode,
+ const float upper_influence,
+ NlaEvalChannelSnapshot *r_blended_necs)
+{
+ nlaevalchan_assert_blendOrcombine_compatible(lower_necs, upper_necs, r_blended_necs);
+ if (nlaevalchan_blendOrcombine_try_copy_lower(
+ lower_necs, upper_necs, upper_influence, r_blended_necs)) {
+ return;
+ }
+
+ const int length = lower_necs->length;
+ for (int j = 0; j < length; j++) {
+ if (!BLI_BITMAP_TEST_BOOL(upper_necs->blend_domain.ptr, j)) {
+ r_blended_necs->values[j] = lower_necs->values[j];
+ continue;
+ }
+
+ r_blended_necs->values[j] = nla_blend_value(
+ upper_blendmode, lower_necs->values[j], upper_necs->values[j], upper_influence);
+ }
+}
+
+/**
+ * Based on mix-mode, provided by one the necs,
+ * combines lower necs with upper necs into blended necs.
+ *
+ * Each upper value's blend domain determines whether to blend or to copy directly from lower.
+ */
+static void nlaevalchan_combine_value(NlaEvalChannelSnapshot *lower_necs,
+ NlaEvalChannelSnapshot *upper_necs,
+ const float upper_influence,
+ NlaEvalChannelSnapshot *r_blended_necs)
+{
+ nlaevalchan_assert_blendOrcombine_compatible(lower_necs, upper_necs, r_blended_necs);
+ if (nlaevalchan_blendOrcombine_try_copy_lower(
+ lower_necs, upper_necs, upper_influence, r_blended_necs)) {
+ return;
+ }
+
+ /* Assumes every base is the same. */
+ float *base_values = lower_necs->channel->base_snapshot.values;
+ const int length = lower_necs->length;
+ const char mix_mode = lower_necs->channel->mix_mode;
+
+ for (int j = 0; j < length; j++) {
+ if (!BLI_BITMAP_TEST_BOOL(upper_necs->blend_domain.ptr, j)) {
+ r_blended_necs->values[j] = lower_necs->values[j];
+ continue;
+ }
+
+ r_blended_necs->values[j] = nla_combine_value(
+ mix_mode, base_values[j], lower_necs->values[j], upper_necs->values[j], upper_influence);
+ }
+}
+
+/**
+ * Quaternion combines lower necs with upper necs into blended necs.
+ *
+ * Each upper value's blend domain determines whether to blend or to copy directly
+ * from lower.
+ */
+static void nlaevalchan_combine_quaternion(NlaEvalChannelSnapshot *lower_necs,
+ NlaEvalChannelSnapshot *upper_necs,
+ const float upper_influence,
+ NlaEvalChannelSnapshot *r_blended_necs)
+{
+ nlaevalchan_assert_blendOrcombine_compatible_quaternion(lower_necs, upper_necs, r_blended_necs);
+ if (nlaevalchan_blendOrcombine_try_copy_lower(
+ lower_necs, upper_necs, upper_influence, r_blended_necs)) {
+ return;
+ }
+
+ /** No need to check per index. We limit to all or nothing combining for quaternions. */
+ if (!BLI_BITMAP_TEST_BOOL(upper_necs->blend_domain.ptr, 0)) {
+ nlaevalchan_copy_values(r_blended_necs, lower_necs);
+ return;
+ }
+
+ nla_combine_quaternion(
+ lower_necs->values, upper_necs->values, upper_influence, r_blended_necs->values);
+}
+
+/**
+ * Based on blend-mode and mix-mode, blend lower necs with upper necs into blended necs.
+ *
+ * Each upper value's blend domain determines whether to blend or to copy directly
+ * from lower.
+ *
+ * \param lower_necs: Never NULL.
+ * \param upper_necs: Can be NULL.
+ * \param upper_blendmode: Enum value in eNlaStrip_Blend_Mode.
+ * \param upper_influence: Value in range [0, 1].
+ * \param upper_necs: Never NULL.
+ *
+ */
+static void nlaevalchan_blendOrcombine(NlaEvalChannelSnapshot *lower_necs,
+ NlaEvalChannelSnapshot *upper_necs,
+ const int upper_blendmode,
+ const float upper_influence,
+ NlaEvalChannelSnapshot *r_blended_necs)
+{
+ nlaevalchan_assert_nonNull(r_blended_necs);
+
+ switch (upper_blendmode) {
+ case NLASTRIP_MODE_COMBINE: {
+ switch (r_blended_necs->channel->mix_mode) {
+ case NEC_MIX_QUATERNION: {
+ nlaevalchan_combine_quaternion(lower_necs, upper_necs, upper_influence, r_blended_necs);
+ return;
+ }
+ case NEC_MIX_ADD:
+ case NEC_MIX_AXIS_ANGLE:
+ case NEC_MIX_MULTIPLY: {
+ nlaevalchan_combine_value(lower_necs, upper_necs, upper_influence, r_blended_necs);
+ return;
+ }
+ default:
+ BLI_assert("Mix mode should've been handled");
+ }
+ return;
+ }
+ case NLASTRIP_MODE_ADD:
+ case NLASTRIP_MODE_SUBTRACT:
+ case NLASTRIP_MODE_MULTIPLY:
+ case NLASTRIP_MODE_REPLACE: {
+ nlaevalchan_blend_value(
+ lower_necs, upper_necs, upper_blendmode, upper_influence, r_blended_necs);
+ return;
+ }
+ default:
+ BLI_assert("Blend mode should've been handled");
+ }
+}
+
+/**
+ * Based on blend-mode, solve for the upper values such that when lower blended with upper then we
+ * get blended values as a result.
+ *
+ * Only processes blended values in the remap domain. Successfully remapped upper values are placed
+ * in the remap domain so caller knows which values are usable.
+ */
+static void nlaevalchan_blend_value_get_inverted_upper_evalchan(
+ NlaEvalChannelSnapshot *lower_necs,
+ NlaEvalChannelSnapshot *blended_necs,
+ const int upper_blendmode,
+ const float upper_influence,
+ NlaEvalChannelSnapshot *r_upper_necs)
+{
+ nlaevalchan_assert_nonNull(r_upper_necs);
+ nlaevalchan_assert_blendOrcombine_compatible(lower_necs, r_upper_necs, blended_necs);
+
+ const int length = lower_necs->length;
+ for (int j = 0; j < length; j++) {
+ if (!BLI_BITMAP_TEST_BOOL(blended_necs->remap_domain.ptr, j)) {
+ BLI_BITMAP_DISABLE(r_upper_necs->remap_domain.ptr, j);
+ continue;
+ }
+
+ const bool success = nla_blend_get_inverted_strip_value(upper_blendmode,
+ lower_necs->values[j],
+ blended_necs->values[j],
+ upper_influence,
+ &r_upper_necs->values[j]);
+ BLI_BITMAP_SET(r_upper_necs->remap_domain.ptr, j, success);
+ }
+}
+
+/**
+ * Based on mix-mode, solve for the upper values such that when lower combined with upper then we
+ * get blended values as a result.
+ *
+ * Only processes blended values in the remap domain. Successfully remapped upper values are placed
+ * in the remap domain so caller knows which values are usable.
+ */
+static void nlaevalchan_combine_value_get_inverted_upper_evalchan(
+ NlaEvalChannelSnapshot *lower_necs,
+ NlaEvalChannelSnapshot *blended_necs,
+ const float upper_influence,
+ NlaEvalChannelSnapshot *r_upper_necs)
+{
+ nlaevalchan_assert_nonNull(r_upper_necs);
+ nlaevalchan_assert_blendOrcombine_compatible(lower_necs, r_upper_necs, blended_necs);
+
+ /* Assumes every channel's base is the same. */
+ float *base_values = lower_necs->channel->base_snapshot.values;
+ const int length = lower_necs->length;
+ const char mix_mode = lower_necs->channel->mix_mode;
+
+ for (int j = 0; j < length; j++) {
+ if (!BLI_BITMAP_TEST_BOOL(blended_necs->remap_domain.ptr, j)) {
+ BLI_BITMAP_DISABLE(r_upper_necs->remap_domain.ptr, j);
+ continue;
+ }
+
+ const bool success = nla_combine_get_inverted_strip_value(mix_mode,
+ base_values[j],
+ lower_necs->values[j],
+ blended_necs->values[j],
+ upper_influence,
+ &r_upper_necs->values[j]);
+
+ BLI_BITMAP_SET(r_upper_necs->remap_domain.ptr, j, success);
+ }
+}
+
+/**
+ * Solve for the upper values such that when lower quaternion combined with upper then we get
+ * blended values as a result.
+ *
+ * All blended values must be in the remap domain. If successfully remapped, then all upper values
+ * are placed in the remap domain so caller knows the result is usable.
+ */
+static void nlaevalchan_combine_quaternion_get_inverted_upper_evalchan(
+ NlaEvalChannelSnapshot *lower_necs,
+ NlaEvalChannelSnapshot *blended_necs,
+ const float upper_influence,
+ NlaEvalChannelSnapshot *r_upper_necs)
+{
+ nlaevalchan_assert_nonNull(r_upper_necs);
+ nlaevalchan_assert_blendOrcombine_compatible_quaternion(lower_necs, r_upper_necs, blended_necs);
+
+ /* Must check each domain index individually in case animator had a non-combine NLA strip with a
+ * subset of quaternion channels and remapping through any of them failed and thus potentially
+ * has undefined values. */
+ for (int j = 0; j < 4; j++) {
+ if (!BLI_BITMAP_TEST_BOOL(blended_necs->remap_domain.ptr, j)) {
+ BLI_bitmap_set_all(r_upper_necs->remap_domain.ptr, false, 4);
+ return;
+ }
+ }
+
+ const bool success = nla_combine_quaternion_get_inverted_strip_values(
+ lower_necs->values, blended_necs->values, upper_influence, r_upper_necs->values);
+
+ BLI_bitmap_set_all(r_upper_necs->remap_domain.ptr, success, 4);
+}
+
+/**
+ * Based on blend-mode and mix mode, solve for the upper values such that when lower blended or
+ * combined with upper then we get blended values as a result.
+ *
+ * Only processes blended values in the remap domain. Successfully remapped upper values are placed
+ * in the remap domain so caller knows which values are usable.
+ *
+ * \param lower_necs: Never NULL.
+ * \param blended_necs: Never NULL.
+ * \param upper_blendmode: Enum value in eNlaStrip_Blend_Mode.
+ * \param upper_influence: Value in range [0, 1].
+ * \param r_upper_necs: Never NULL.
+ */
+static void nlaevalchan_blendOrcombine_get_inverted_upper_evalchan(
+ NlaEvalChannelSnapshot *lower_necs,
+ NlaEvalChannelSnapshot *blended_necs,
+ const int upper_blendmode,
+ const float upper_influence,
+ NlaEvalChannelSnapshot *r_upper_necs)
+{
+ nlaevalchan_assert_nonNull(r_upper_necs);
+
+ if (IS_EQF(upper_influence, 0.0f)) {
+ BLI_bitmap_set_all(r_upper_necs->remap_domain.ptr, false, r_upper_necs->length);
+ return;
+ }
+
+ switch (upper_blendmode) {
+ case NLASTRIP_MODE_COMBINE: {
+ switch (r_upper_necs->channel->mix_mode) {
+ case NEC_MIX_QUATERNION: {
+ nlaevalchan_combine_quaternion_get_inverted_upper_evalchan(
+ lower_necs, blended_necs, upper_influence, r_upper_necs);
+ return;
+ }
+ case NEC_MIX_ADD:
+ case NEC_MIX_AXIS_ANGLE:
+ case NEC_MIX_MULTIPLY: {
+ nlaevalchan_combine_value_get_inverted_upper_evalchan(
+ lower_necs, blended_necs, upper_influence, r_upper_necs);
+ return;
+ }
+ default:
+ BLI_assert("Mix mode should've been handled");
+ }
+ return;
+ }
+ case NLASTRIP_MODE_ADD:
+ case NLASTRIP_MODE_SUBTRACT:
+ case NLASTRIP_MODE_MULTIPLY:
+ case NLASTRIP_MODE_REPLACE: {
+ nlaevalchan_blend_value_get_inverted_upper_evalchan(
+ lower_necs, blended_necs, upper_blendmode, upper_influence, r_upper_necs);
+ return;
+ }
+ default:
+ BLI_assert("Blend mode should've been handled");
+ }
+}
+
+/* ---------------------- */
/* F-Modifier stack joining/separation utilities -
* should we generalize these for BLI_listbase.h interface? */
@@ -2047,12 +2407,12 @@ static void nla_eval_domain_strips(PointerRNA *ptr,
GSet *touched_actions)
{
LISTBASE_FOREACH (NlaStrip *, strip, strips) {
- /* check strip's action */
+ /* Check strip's action. */
if (strip->act) {
nla_eval_domain_action(ptr, channels, strip->act, touched_actions);
}
- /* check sub-strips (if metas) */
+ /* Check sub-strips (if meta-strips). */
nla_eval_domain_strips(ptr, channels, &strip->strips, touched_actions);
}
}
@@ -2094,8 +2454,10 @@ static void animsys_evaluate_nla_domain(PointerRNA *ptr, NlaEvalData *channels,
/* ---------------------- */
-/** Tweaked strip is evaluated differently from other strips. Adjacent strips are ignored
- * and includes a workaround for when user is not editing in place. */
+/**
+ * Tweaked strip is evaluated differently from other strips. Adjacent strips are ignored
+ * and includes a workaround for when user is not editing in place.
+ */
static void animsys_create_tweak_strip(const AnimData *adt,
const bool keyframing_to_strip,
NlaStrip *r_tweak_strip)
@@ -2210,8 +2572,10 @@ static bool is_nlatrack_evaluatable(const AnimData *adt, const NlaTrack *nlt)
return true;
}
-/** Check for special case of non-pushed action being evaluated with no NLA influence (off and no
- * strips evaluated) nor NLA interference (ensure NLA not soloing). */
+/**
+ * Check for special case of non-pushed action being evaluated with no NLA influence (off and no
+ * strips evaluated) nor NLA interference (ensure NLA not soloing).
+ */
static bool is_action_track_evaluated_without_nla(const AnimData *adt,
const bool any_strip_evaluated)
{
@@ -2491,12 +2855,13 @@ void nlasnapshot_ensure_channels(NlaEvalData *eval_data, NlaEvalSnapshot *snapsh
}
}
-/** Blends the \a lower_snapshot with the \a upper_snapshot into \a r_blended_snapshot according
+/**
+ * Blends the \a lower_snapshot with the \a upper_snapshot into \a r_blended_snapshot according
* to the given \a upper_blendmode and \a upper_influence.
*
- * For \a upper_snapshot, blending limited to values in the \a blend_domain. For Replace blendmode,
- * this allows the upper snapshot to have a location XYZ channel where only a subset of values are
- * blended.
+ * For \a upper_snapshot, blending limited to values in the \a blend_domain.
+ * For Replace blend-mode, this allows the upper snapshot to have a location XYZ channel
+ * where only a subset of values are blended.
*/
void nlasnapshot_blend(NlaEvalData *eval_data,
NlaEvalSnapshot *lower_snapshot,
@@ -2507,11 +2872,7 @@ void nlasnapshot_blend(NlaEvalData *eval_data,
{
nlaeval_snapshot_ensure_size(r_blended_snapshot, eval_data->num_channels);
- const bool zero_upper_influence = IS_EQF(upper_influence, 0.0f);
-
LISTBASE_FOREACH (NlaEvalChannel *, nec, &eval_data->channels) {
- const int length = nec->base_snapshot.length;
-
NlaEvalChannelSnapshot *upper_necs = nlaeval_snapshot_get(upper_snapshot, nec->index);
NlaEvalChannelSnapshot *lower_necs = nlaeval_snapshot_get(lower_snapshot, nec->index);
if (upper_necs == NULL && lower_necs == NULL) {
@@ -2524,49 +2885,44 @@ void nlasnapshot_blend(NlaEvalData *eval_data,
}
NlaEvalChannelSnapshot *result_necs = nlaeval_snapshot_ensure_channel(r_blended_snapshot, nec);
+ nlaevalchan_blendOrcombine(
+ lower_necs, upper_necs, upper_blendmode, upper_influence, result_necs);
+ }
+}
+
+/**
+ * Using \a blended_snapshot and \a lower_snapshot, we can solve for the \a r_upper_snapshot.
+ *
+ * Only channels that exist within \a blended_snapshot are inverted.
+ *
+ * For \a r_upper_snapshot, disables \a NlaEvalChannelSnapshot->remap_domain for failed inversions.
+ * Only values within the \a remap_domain are processed.
+ */
+void nlasnapshot_blend_get_inverted_upper_snapshot(NlaEvalData *eval_data,
+ NlaEvalSnapshot *lower_snapshot,
+ NlaEvalSnapshot *blended_snapshot,
+ const short upper_blendmode,
+ const float upper_influence,
+ NlaEvalSnapshot *r_upper_snapshot)
+{
+ nlaeval_snapshot_ensure_size(r_upper_snapshot, eval_data->num_channels);
- /** Always copy \a lower_snapshot to result, irrelevant of whether \a upper_snapshot has a
- * corresponding channel. This only matters when \a lower_snapshot not the same as
- * \a r_blended_snapshot. */
- memcpy(result_necs->values, lower_necs->values, length * sizeof(float));
- if (upper_necs == NULL || zero_upper_influence) {
+ LISTBASE_FOREACH (NlaEvalChannel *, nec, &eval_data->channels) {
+ NlaEvalChannelSnapshot *blended_necs = nlaeval_snapshot_get(blended_snapshot, nec->index);
+ if (blended_necs == NULL) {
+ /** We assume the caller only wants a subset of channels to be inverted, those that exist
+ * within \a blended_snapshot. */
continue;
}
- if (upper_blendmode == NLASTRIP_MODE_COMBINE) {
- const int mix_mode = nec->mix_mode;
- if (mix_mode == NEC_MIX_QUATERNION) {
- if (!BLI_BITMAP_TEST_BOOL(upper_necs->blend_domain.ptr, 0)) {
- continue;
- }
-
- nla_combine_quaternion(
- lower_necs->values, upper_necs->values, upper_influence, result_necs->values);
- }
- else {
- for (int j = 0; j < length; j++) {
- if (!BLI_BITMAP_TEST_BOOL(upper_necs->blend_domain.ptr, j)) {
- continue;
- }
-
- result_necs->values[j] = nla_combine_value(mix_mode,
- nec->base_snapshot.values[j],
- lower_necs->values[j],
- upper_necs->values[j],
- upper_influence);
- }
- }
+ NlaEvalChannelSnapshot *lower_necs = nlaeval_snapshot_get(lower_snapshot, nec->index);
+ if (lower_necs == NULL) {
+ lower_necs = nlaeval_snapshot_find_channel(lower_snapshot->base, nec);
}
- else {
- for (int j = 0; j < length; j++) {
- if (!BLI_BITMAP_TEST_BOOL(upper_necs->blend_domain.ptr, j)) {
- continue;
- }
- result_necs->values[j] = nla_blend_value(
- upper_blendmode, lower_necs->values[j], upper_necs->values[j], upper_influence);
- }
- }
+ NlaEvalChannelSnapshot *result_necs = nlaeval_snapshot_ensure_channel(r_upper_snapshot, nec);
+ nlaevalchan_blendOrcombine_get_inverted_upper_evalchan(
+ lower_necs, blended_necs, upper_blendmode, upper_influence, result_necs);
}
}
@@ -2664,74 +3020,64 @@ bool BKE_animsys_nla_remap_keyframe_values(struct NlaKeyframingContext *context,
return false;
}
- /* Find the evaluation channel for the NLA stack below current strip. */
+ /** Create \a blended_snapshot and fill with input \a values. */
+ NlaEvalData *eval_data = &context->lower_eval_data;
+ NlaEvalSnapshot blended_snapshot;
+ nlaeval_snapshot_init(&blended_snapshot, eval_data, NULL);
+
NlaEvalChannelKey key = {
.ptr = *prop_ptr,
.prop = prop,
};
- /**
- * Remove lower NLA stack effects.
- *
- * Using the tweak strip's blended result and the lower snapshot value, we can solve for the
- * tweak strip value it must evaluate to.
- */
- NlaEvalData *const lower_eval_data = &context->lower_eval_data;
- NlaEvalChannel *const lower_nec = nlaevalchan_verify_key(lower_eval_data, NULL, &key);
- if ((lower_nec->base_snapshot.length != count)) {
+ NlaEvalChannel *nec = nlaevalchan_verify_key(eval_data, NULL, &key);
+ BLI_assert(nec);
+ if (nec->base_snapshot.length != count) {
BLI_assert(!"invalid value count");
+ nlaeval_snapshot_free_data(&blended_snapshot);
return false;
}
- /* Invert the blending operation to compute the desired strip values. */
- NlaEvalChannelSnapshot *const lower_nec_snapshot = nlaeval_snapshot_find_channel(
- &lower_eval_data->eval_snapshot, lower_nec);
+ NlaEvalChannelSnapshot *blended_necs = nlaeval_snapshot_ensure_channel(&blended_snapshot, nec);
+ memcpy(blended_necs->values, values, sizeof(float) * count);
+ BLI_bitmap_set_all(blended_necs->remap_domain.ptr, true, count);
- float *lower_values = lower_nec_snapshot->values;
+ /** Remove lower NLA stack effects. */
+ nlasnapshot_blend_get_inverted_upper_snapshot(eval_data,
+ &context->lower_eval_data.eval_snapshot,
+ &blended_snapshot,
+ blend_mode,
+ influence,
+ &blended_snapshot);
- if (blend_mode == NLASTRIP_MODE_COMBINE) {
- /* Quaternion combine handles all sub-channels as a unit. */
- if (lower_nec->mix_mode == NEC_MIX_QUATERNION) {
- if (r_force_all == NULL) {
- return false;
- }
+ /** Write results into \a values. */
+ bool successful_remap = true;
+ if (blended_necs->channel->mix_mode == NEC_MIX_QUATERNION &&
+ blend_mode == NLASTRIP_MODE_COMBINE) {
+ if (r_force_all != NULL) {
*r_force_all = true;
-
- if (!nla_combine_quaternion_get_inverted_strip_values(
- lower_values, values, influence, values)) {
- return false;
- }
+ index = -1;
}
else {
- float *base_values = lower_nec->base_snapshot.values;
-
- for (int i = 0; i < count; i++) {
- if (ELEM(index, i, -1)) {
- if (!nla_combine_get_inverted_strip_value(lower_nec->mix_mode,
- base_values[i],
- lower_values[i],
- values[i],
- influence,
- &values[i])) {
- return false;
- }
- }
- }
+ successful_remap = false;
}
}
- else {
- for (int i = 0; i < count; i++) {
- if (ELEM(index, i, -1)) {
- if (!nla_blend_get_inverted_strip_value(
- blend_mode, lower_values[i], values[i], influence, &values[i])) {
- return false;
- }
- }
+
+ for (int i = 0; i < count; i++) {
+ if (!ELEM(index, i, -1)) {
+ continue;
+ }
+ if (!BLI_BITMAP_TEST_BOOL(blended_necs->remap_domain.ptr, i)) {
+ successful_remap = false;
}
+
+ values[i] = blended_necs->values[i];
}
- return true;
+ nlaeval_snapshot_free_data(&blended_snapshot);
+
+ return successful_remap;
}
/**
diff --git a/source/blender/blenkernel/intern/appdir.c b/source/blender/blenkernel/intern/appdir.c
index 1075a46e72b..bcfd34ab42f 100644
--- a/source/blender/blenkernel/intern/appdir.c
+++ b/source/blender/blenkernel/intern/appdir.c
@@ -137,7 +137,7 @@ static char *blender_version_decimal(const int version)
{
static char version_str[5];
BLI_assert(version < 1000);
- BLI_snprintf(version_str, sizeof(version_str), "%d.%02d", version / 100, version % 100);
+ BLI_snprintf(version_str, sizeof(version_str), "%d.%d", version / 100, version % 100);
return version_str;
}
diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c
index a1ebec1d756..4ea71922df5 100644
--- a/source/blender/blenkernel/intern/armature.c
+++ b/source/blender/blenkernel/intern/armature.c
@@ -2444,7 +2444,7 @@ static void pose_proxy_sync(Object *ob, Object *from, int layer_protected)
static int rebuild_pose_bone(
bPose *pose, Bone *bone, bPoseChannel *parchan, int counter, Bone **r_last_visited_bone_p)
{
- bPoseChannel *pchan = BKE_pose_channel_verify(pose, bone->name); /* verify checks and/or adds */
+ bPoseChannel *pchan = BKE_pose_channel_ensure(pose, bone->name); /* verify checks and/or adds */
pchan->bone = bone;
pchan->parent = parchan;
@@ -2515,6 +2515,17 @@ void BKE_pchan_rebuild_bbone_handles(bPose *pose, bPoseChannel *pchan)
pchan->bbone_next = pose_channel_find_bone(pose, pchan->bone->bbone_next);
}
+void BKE_pose_channels_clear_with_null_bone(bPose *pose, const bool do_id_user)
+{
+ LISTBASE_FOREACH_MUTABLE (bPoseChannel *, pchan, &pose->chanbase) {
+ if (pchan->bone == NULL) {
+ BKE_pose_channel_free_ex(pchan, do_id_user);
+ BKE_pose_channels_hash_free(pose);
+ BLI_freelinkN(&pose->chanbase, pchan);
+ }
+ }
+}
+
/**
* Only after leave editmode, duplicating, validating older files, library syncing.
*
@@ -2526,7 +2537,7 @@ void BKE_pose_rebuild(Main *bmain, Object *ob, bArmature *arm, const bool do_id_
{
Bone *bone;
bPose *pose;
- bPoseChannel *pchan, *next;
+ bPoseChannel *pchan;
int counter = 0;
/* only done here */
@@ -2549,16 +2560,9 @@ void BKE_pose_rebuild(Main *bmain, Object *ob, bArmature *arm, const bool do_id_
}
/* and a check for garbage */
- for (pchan = pose->chanbase.first; pchan; pchan = next) {
- next = pchan->next;
- if (pchan->bone == NULL) {
- BKE_pose_channel_free_ex(pchan, do_id_user);
- BKE_pose_channels_hash_free(pose);
- BLI_freelinkN(&pose->chanbase, pchan);
- }
- }
+ BKE_pose_channels_clear_with_null_bone(pose, do_id_user);
- BKE_pose_channels_hash_make(pose);
+ BKE_pose_channels_hash_ensure(pose);
for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) {
/* Find the custom B-Bone handles. */
@@ -2877,7 +2881,8 @@ bool BKE_pose_minmax(Object *ob, float r_min[3], float r_max[3], bool use_hidden
NULL;
if (bb_custom) {
float mat[4][4], smat[4][4];
- scale_m4_fl(smat, PCHAN_CUSTOM_DRAW_SIZE(pchan));
+ scale_m4_fl(smat, PCHAN_CUSTOM_BONE_LENGTH(pchan));
+ rescale_m4(smat, pchan->custom_scale_xyz);
mul_m4_series(mat, ob->obmat, pchan_tx->pose_mat, smat);
BKE_boundbox_minmax(bb_custom, mat, r_min, r_max);
}
diff --git a/source/blender/blenkernel/intern/armature_deform.c b/source/blender/blenkernel/intern/armature_deform.c
index 8711a001e32..bca5503c8d2 100644
--- a/source/blender/blenkernel/intern/armature_deform.c
+++ b/source/blender/blenkernel/intern/armature_deform.c
@@ -444,7 +444,9 @@ static void armature_vert_task(void *__restrict userdata,
armature_vert_task_with_dvert(data, i, dvert);
}
-static void armature_vert_task_editmesh(void *__restrict userdata, MempoolIterData *iter)
+static void armature_vert_task_editmesh(void *__restrict userdata,
+ MempoolIterData *iter,
+ const TaskParallelTLS *__restrict UNUSED(tls))
{
const ArmatureUserdata *data = userdata;
BMVert *v = (BMVert *)iter;
@@ -452,7 +454,9 @@ static void armature_vert_task_editmesh(void *__restrict userdata, MempoolIterDa
armature_vert_task_with_dvert(data, BM_elem_index_get(v), dvert);
}
-static void armature_vert_task_editmesh_no_dvert(void *__restrict userdata, MempoolIterData *iter)
+static void armature_vert_task_editmesh_no_dvert(void *__restrict userdata,
+ MempoolIterData *iter,
+ const TaskParallelTLS *__restrict UNUSED(tls))
{
const ArmatureUserdata *data = userdata;
BMVert *v = (BMVert *)iter;
@@ -593,12 +597,16 @@ static void armature_deform_coords_impl(const Object *ob_arm,
* have already been properly set. */
BM_mesh_elem_index_ensure(em_target->bm, BM_VERT);
+ TaskParallelSettings settings;
+ BLI_parallel_mempool_settings_defaults(&settings);
+
if (use_dverts) {
- BLI_task_parallel_mempool(em_target->bm->vpool, &data, armature_vert_task_editmesh, true);
+ BLI_task_parallel_mempool(
+ em_target->bm->vpool, &data, armature_vert_task_editmesh, &settings);
}
else {
BLI_task_parallel_mempool(
- em_target->bm->vpool, &data, armature_vert_task_editmesh_no_dvert, true);
+ em_target->bm->vpool, &data, armature_vert_task_editmesh_no_dvert, &settings);
}
}
else {
diff --git a/source/blender/blenkernel/intern/armature_pose.cc b/source/blender/blenkernel/intern/armature_pose.cc
index bb371b16c42..ca11692372b 100644
--- a/source/blender/blenkernel/intern/armature_pose.cc
+++ b/source/blender/blenkernel/intern/armature_pose.cc
@@ -39,7 +39,7 @@ namespace {
using BoneNameSet = blender::Set<std::string>;
// Forward declarations.
-BoneNameSet pose_apply_find_selected_bones(const bPose *pose);
+BoneNameSet pose_apply_find_selected_bones(const bArmature *armature, const bPose *pose);
void pose_apply_disable_fcurves_for_unselected_bones(bAction *action,
const BoneNameSet &selected_bone_names);
void pose_apply_restore_fcurves(bAction *action);
@@ -54,7 +54,8 @@ void BKE_pose_apply_action(struct Object *ob,
return;
}
- const BoneNameSet selected_bone_names = pose_apply_find_selected_bones(pose);
+ const bArmature *armature = (bArmature *)ob->data;
+ const BoneNameSet selected_bone_names = pose_apply_find_selected_bones(armature, pose);
const bool limit_to_selected_bones = !selected_bone_names.is_empty();
if (limit_to_selected_bones) {
@@ -74,15 +75,14 @@ void BKE_pose_apply_action(struct Object *ob,
}
namespace {
-BoneNameSet pose_apply_find_selected_bones(const bPose *pose)
+BoneNameSet pose_apply_find_selected_bones(const bArmature *armature, const bPose *pose)
{
BoneNameSet selected_bone_names;
bool all_bones_selected = true;
bool no_bones_selected = true;
LISTBASE_FOREACH (bPoseChannel *, pchan, &pose->chanbase) {
- const bool is_selected = (pchan->bone->flag & BONE_SELECTED) != 0 &&
- (pchan->bone->flag & BONE_HIDDEN_P) == 0;
+ const bool is_selected = PBONE_SELECTED(armature, pchan->bone);
all_bones_selected &= is_selected;
no_bones_selected &= !is_selected;
diff --git a/source/blender/blenkernel/intern/armature_update.c b/source/blender/blenkernel/intern/armature_update.c
index 8f74b8ff054..4504f10967c 100644
--- a/source/blender/blenkernel/intern/armature_update.c
+++ b/source/blender/blenkernel/intern/armature_update.c
@@ -63,40 +63,40 @@ typedef struct tSplineIK_Tree {
bPoseChannel *root; /* bone that is the root node of the chain */
- bConstraint *con; /* constraint for this chain */
- bSplineIKConstraint *ikData; /* constraint settings for this chain */
+ bConstraint *con; /* constraint for this chain */
+ bSplineIKConstraint *ik_data; /* constraint settings for this chain */
} tSplineIK_Tree;
/* ----------- */
-/* Tag the bones in the chain formed by the given bone for IK */
+/* Tag the bones in the chain formed by the given bone for IK. */
static void splineik_init_tree_from_pchan(Scene *UNUSED(scene),
Object *UNUSED(ob),
bPoseChannel *pchan_tip)
{
- bPoseChannel *pchan, *pchanRoot = NULL;
- bPoseChannel *pchanChain[255];
+ bPoseChannel *pchan, *pchan_root = NULL;
+ bPoseChannel *pchan_chain[255];
bConstraint *con = NULL;
- bSplineIKConstraint *ikData = NULL;
- float boneLengths[255];
- float totLength = 0.0f;
+ bSplineIKConstraint *ik_data = NULL;
+ float bone_lengths[255];
+ float totlength = 0.0f;
int segcount = 0;
- /* find the SplineIK constraint */
+ /* Find the SplineIK constraint. */
for (con = pchan_tip->constraints.first; con; con = con->next) {
if (con->type == CONSTRAINT_TYPE_SPLINEIK) {
- ikData = con->data;
+ ik_data = con->data;
- /* target can only be curve */
- if ((ikData->tar == NULL) || (ikData->tar->type != OB_CURVE)) {
+ /* Target can only be a curve. */
+ if ((ik_data->tar == NULL) || (ik_data->tar->type != OB_CURVE)) {
continue;
}
- /* skip if disabled */
+ /* Skip if disabled. */
if ((con->enforce == 0.0f) || (con->flag & (CONSTRAINT_DISABLE | CONSTRAINT_OFF))) {
continue;
}
- /* otherwise, constraint is ok... */
+ /* Otherwise, constraint is ok... */
break;
}
}
@@ -104,102 +104,102 @@ static void splineik_init_tree_from_pchan(Scene *UNUSED(scene),
return;
}
- /* find the root bone and the chain of bones from the root to the tip
+ /* Find the root bone and the chain of bones from the root to the tip.
* NOTE: this assumes that the bones are connected, but that may not be true... */
- for (pchan = pchan_tip; pchan && (segcount < ikData->chainlen);
+ for (pchan = pchan_tip; pchan && (segcount < ik_data->chainlen);
pchan = pchan->parent, segcount++) {
- /* store this segment in the chain */
- pchanChain[segcount] = pchan;
+ /* Store this segment in the chain. */
+ pchan_chain[segcount] = pchan;
- /* if performing rebinding, calculate the length of the bone */
- boneLengths[segcount] = pchan->bone->length;
- totLength += boneLengths[segcount];
+ /* If performing rebinding, calculate the length of the bone. */
+ bone_lengths[segcount] = pchan->bone->length;
+ totlength += bone_lengths[segcount];
}
if (segcount == 0) {
return;
}
- pchanRoot = pchanChain[segcount - 1];
+ pchan_root = pchan_chain[segcount - 1];
- /* perform binding step if required */
- if ((ikData->flag & CONSTRAINT_SPLINEIK_BOUND) == 0) {
+ /* Perform binding step if required. */
+ if ((ik_data->flag & CONSTRAINT_SPLINEIK_BOUND) == 0) {
float segmentLen = (1.0f / (float)segcount);
- /* setup new empty array for the points list */
- if (ikData->points) {
- MEM_freeN(ikData->points);
+ /* Setup new empty array for the points list. */
+ if (ik_data->points) {
+ MEM_freeN(ik_data->points);
}
- ikData->numpoints = ikData->chainlen + 1;
- ikData->points = MEM_mallocN(sizeof(float) * ikData->numpoints, "Spline IK Binding");
+ ik_data->numpoints = ik_data->chainlen + 1;
+ ik_data->points = MEM_mallocN(sizeof(float) * ik_data->numpoints, "Spline IK Binding");
- /* bind 'tip' of chain (i.e. first joint = tip of bone with the Spline IK Constraint) */
- ikData->points[0] = 1.0f;
+ /* Bind 'tip' of chain (i.e. first joint = tip of bone with the Spline IK Constraint). */
+ ik_data->points[0] = 1.0f;
- /* perform binding of the joints to parametric positions along the curve based
- * proportion of the total length that each bone occupies
+ /* Perform binding of the joints to parametric positions along the curve based
+ * proportion of the total length that each bone occupies.
*/
for (int i = 0; i < segcount; i++) {
- /* 'head' joints, traveling towards the root of the chain
- * - 2 methods; the one chosen depends on whether we've got usable lengths
+ /* 'head' joints, traveling towards the root of the chain.
+ * - 2 methods; the one chosen depends on whether we've got usable lengths.
*/
- if ((ikData->flag & CONSTRAINT_SPLINEIK_EVENSPLITS) || (totLength == 0.0f)) {
- /* 1) equi-spaced joints */
- ikData->points[i + 1] = ikData->points[i] - segmentLen;
+ if ((ik_data->flag & CONSTRAINT_SPLINEIK_EVENSPLITS) || (totlength == 0.0f)) {
+ /* 1) Equi-spaced joints. */
+ ik_data->points[i + 1] = ik_data->points[i] - segmentLen;
}
else {
- /* 2) to find this point on the curve, we take a step from the previous joint
- * a distance given by the proportion that this bone takes
+ /* 2) To find this point on the curve, we take a step from the previous joint
+ * a distance given by the proportion that this bone takes.
*/
- ikData->points[i + 1] = ikData->points[i] - (boneLengths[i] / totLength);
+ ik_data->points[i + 1] = ik_data->points[i] - (bone_lengths[i] / totlength);
}
}
- /* spline has now been bound */
- ikData->flag |= CONSTRAINT_SPLINEIK_BOUND;
+ /* Spline has now been bound. */
+ ik_data->flag |= CONSTRAINT_SPLINEIK_BOUND;
}
- /* disallow negative values (happens with float precision) */
- CLAMP_MIN(ikData->points[segcount], 0.0f);
+ /* Disallow negative values (happens with float precision). */
+ CLAMP_MIN(ik_data->points[segcount], 0.0f);
- /* make a new Spline-IK chain, and store it in the IK chains */
+ /* Make a new Spline-IK chain, and store it in the IK chains. */
/* TODO: we should check if there is already an IK chain on this,
* since that would take precedence... */
{
- /* make new tree */
+ /* Make a new tree. */
tSplineIK_Tree *tree = MEM_callocN(sizeof(tSplineIK_Tree), "SplineIK Tree");
tree->type = CONSTRAINT_TYPE_SPLINEIK;
tree->chainlen = segcount;
- tree->totlength = totLength;
+ tree->totlength = totlength;
- /* copy over the array of links to bones in the chain (from tip to root) */
+ /* Copy over the array of links to bones in the chain (from tip to root). */
tree->chain = MEM_mallocN(sizeof(bPoseChannel *) * segcount, "SplineIK Chain");
- memcpy(tree->chain, pchanChain, sizeof(bPoseChannel *) * segcount);
+ memcpy(tree->chain, pchan_chain, sizeof(bPoseChannel *) * segcount);
- /* store reference to joint position array */
- tree->points = ikData->points;
+ /* Store reference to joint position array. */
+ tree->points = ik_data->points;
- /* store references to different parts of the chain */
- tree->root = pchanRoot;
+ /* Store references to different parts of the chain. */
+ tree->root = pchan_root;
tree->con = con;
- tree->ikData = ikData;
+ tree->ik_data = ik_data;
- /* AND! link the tree to the root */
- BLI_addtail(&pchanRoot->siktree, tree);
+ /* AND! Link the tree to the root. */
+ BLI_addtail(&pchan_root->siktree, tree);
}
- /* mark root channel having an IK tree */
- pchanRoot->flag |= POSE_IKSPLINE;
+ /* Mark root channel having an IK tree. */
+ pchan_root->flag |= POSE_IKSPLINE;
}
-/* Tag which bones are members of Spline IK chains */
+/* Tag which bones are members of Spline IK chains. */
static void splineik_init_tree(Scene *scene, Object *ob, float UNUSED(ctime))
{
bPoseChannel *pchan;
- /* find the tips of Spline IK chains,
- * which are simply the bones which have been tagged as such */
+ /* Find the tips of Spline IK chains,
+ * which are simply the bones which have been tagged as such. */
for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
if (pchan->constflag & PCHAN_HAS_SPLINEIK) {
splineik_init_tree_from_pchan(scene, ob, pchan);
@@ -213,23 +213,26 @@ typedef struct tSplineIk_EvalState {
float curve_position; /* Current position along the curve. */
float curve_scale; /* Global scale to apply to curve positions. */
float locrot_offset[4][4]; /* Bone rotation and location offset inherited from parent. */
+ float prev_tail_loc[3]; /* Tail location of the previous bone. */
+ float prev_tail_radius; /* Tail curve radius of the previous bone. */
+ int prev_tail_seg_idx; /* Curve segment the previous tail bone belongs to. */
} tSplineIk_EvalState;
/* Prepare data to evaluate spline IK. */
static bool splineik_evaluate_init(tSplineIK_Tree *tree, tSplineIk_EvalState *state)
{
- bSplineIKConstraint *ikData = tree->ikData;
+ bSplineIKConstraint *ik_data = tree->ik_data;
/* Make sure that the constraint targets are ok, to avoid crashes
* in case of a depsgraph bug or dependency cycle.
*/
- if (ikData->tar == NULL) {
+ if (ik_data->tar == NULL) {
return false;
}
- CurveCache *cache = ikData->tar->runtime.curve_cache;
+ CurveCache *cache = ik_data->tar->runtime.curve_cache;
- if (ELEM(NULL, cache, cache->path, cache->path->data)) {
+ if (ELEM(NULL, cache, cache->anim_path_accum_length)) {
return false;
}
@@ -237,97 +240,257 @@ static bool splineik_evaluate_init(tSplineIK_Tree *tree, tSplineIk_EvalState *st
state->curve_position = 0.0f;
state->curve_scale = 1.0f;
unit_m4(state->locrot_offset);
+ zero_v3(state->prev_tail_loc);
+ state->prev_tail_radius = 1.0f;
+ state->prev_tail_seg_idx = 0;
/* Apply corrections for sensitivity to scaling. */
- if ((ikData->yScaleMode != CONSTRAINT_SPLINEIK_YS_FIT_CURVE) && (tree->totlength != 0.0f)) {
- /* get the current length of the curve */
- /* NOTE: this is assumed to be correct even after the curve was resized */
- float splineLen = cache->path->totdist;
+ if ((ik_data->yScaleMode != CONSTRAINT_SPLINEIK_YS_FIT_CURVE) && (tree->totlength != 0.0f)) {
+ /* Get the current length of the curve. */
+ /* NOTE: This is assumed to be correct even after the curve was resized. */
+ const float spline_len = BKE_anim_path_get_length(cache);
- /* calculate the scale factor to multiply all the path values by so that the
- * bone chain retains its current length, such that
+ /* Calculate the scale factor to multiply all the path values by so that the
+ * bone chain retains its current length, such that:
* maxScale * splineLen = totLength
*/
- state->curve_scale = tree->totlength / splineLen;
+ state->curve_scale = tree->totlength / spline_len;
}
return true;
}
+static void apply_curve_transform(
+ bSplineIKConstraint *ik_data, Object *ob, float radius, float r_vec[3], float *r_radius)
+{
+ /* Apply the curve's object-mode transforms to the position
+ * unless the option to allow curve to be positioned elsewhere is activated (i.e. no root).
+ */
+ if ((ik_data->flag & CONSTRAINT_SPLINEIK_NO_ROOT) == 0) {
+ mul_m4_v3(ik_data->tar->obmat, r_vec);
+ }
+
+ /* Convert the position to pose-space. */
+ mul_m4_v3(ob->imat, r_vec);
+
+ /* Set the new radius (it should be the average value). */
+ *r_radius = (radius + *r_radius) / 2;
+}
+
+/* This function positions the tail of the bone so that it preserves the length of it.
+ * The length of the bone can be seen as a sphere radius.
+ */
+static int position_tail_on_spline(bSplineIKConstraint *ik_data,
+ const float head_pos[3],
+ const float sphere_radius,
+ const int prev_seg_idx,
+ float r_tail_pos[3],
+ float *r_new_curve_pos,
+ float *r_radius)
+{
+ /* This is using the tessellated curve data.
+ * So we are working with piece-wise linear curve segments.
+ * The same method is use in #BKE_where_on_path to get curve location data. */
+ const CurveCache *cache = ik_data->tar->runtime.curve_cache;
+ const BevList *bl = cache->bev.first;
+ BevPoint *bp = bl->bevpoints;
+ const float spline_len = BKE_anim_path_get_length(cache);
+ const float *seg_accum_len = cache->anim_path_accum_length;
+
+ int max_seg_idx = BKE_anim_path_get_array_size(cache) - 1;
+
+ /* Convert our initial intersection point guess to a point index.
+ * If the curve was a straight line, then pointEnd would be the correct location.
+ * So make it our first initial guess.
+ */
+ const float guessed_len = *r_new_curve_pos * spline_len;
+
+ BLI_assert(prev_seg_idx >= 0);
+
+ int cur_seg_idx = prev_seg_idx;
+ while (cur_seg_idx < max_seg_idx && guessed_len > seg_accum_len[cur_seg_idx]) {
+ cur_seg_idx++;
+ }
+
+ int bp_idx = cur_seg_idx + 1;
+
+ bp = bp + bp_idx;
+ bool is_cyclic = bl->poly >= 0;
+ BevPoint *prev_bp = bp - 1;
+
+ /* Go to the next tessellated curve point until we cross to outside of the sphere. */
+ while (len_v3v3(head_pos, bp->vec) < sphere_radius) {
+ if (bp_idx > max_seg_idx) {
+ /* We are outside the defined curve. We will now extrapolate the intersection point. */
+ break;
+ }
+ prev_bp = bp;
+ if (is_cyclic && bp_idx == max_seg_idx) {
+ /* Wrap around to the start point.
+ * Don't set the bp_idx to zero here as we use it to get the segment index later.
+ */
+ bp = bl->bevpoints;
+ }
+ else {
+ bp++;
+ }
+ bp_idx++;
+ }
+
+ float isect_1[3], isect_2[3];
+
+ /* Calculate the intersection point. */
+ int ret = isect_line_sphere_v3(prev_bp->vec, bp->vec, head_pos, sphere_radius, isect_1, isect_2);
+
+ if (ret > 0) {
+ /* Because of how `isect_line_sphere_v3` works, we know that `isect_1` contains the
+ * intersection point we want. And it will always intersect as we go from inside to outside
+ * of the sphere.
+ */
+ copy_v3_v3(r_tail_pos, isect_1);
+ }
+ else {
+ /* Couldn't find an intersection point. This means that the floating point
+ * values are too small and thus the intersection check fails.
+ * So assume that the distance is so small that tail_pos == head_pos.
+ */
+ copy_v3_v3(r_tail_pos, head_pos);
+ }
+
+ cur_seg_idx = bp_idx - 2;
+ float prev_seg_len = 0;
+
+ if (cur_seg_idx < 0) {
+ cur_seg_idx = 0;
+ prev_seg_len = 0;
+ }
+ else {
+ prev_seg_len = seg_accum_len[cur_seg_idx];
+ }
+
+ /* Convert the point back into the 0-1 interpolation range. */
+ const float isect_seg_len = len_v3v3(prev_bp->vec, r_tail_pos);
+ const float frac = isect_seg_len / len_v3v3(prev_bp->vec, bp->vec);
+ *r_new_curve_pos = (prev_seg_len + isect_seg_len) / spline_len;
+
+ if (*r_new_curve_pos > 1.0f) {
+ *r_radius = bp->radius;
+ }
+ else {
+ *r_radius = (1.0f - frac) * prev_bp->radius + frac * bp->radius;
+ }
+
+ return cur_seg_idx;
+}
+
/* Evaluate spline IK for a given bone. */
static void splineik_evaluate_bone(
tSplineIK_Tree *tree, Object *ob, bPoseChannel *pchan, int index, tSplineIk_EvalState *state)
{
- bSplineIKConstraint *ikData = tree->ikData;
- float origHead[3], origTail[3], poseHead[3], poseTail[3], basePoseMat[3][3], poseMat[3][3];
- float splineVec[3], scaleFac, radius = 1.0f;
- float tailBlendFac = 0.0f;
+ bSplineIKConstraint *ik_data = tree->ik_data;
+
+ if (pchan->bone->length < FLT_EPSILON) {
+ /* Only move the bone position with zero length bones. */
+ float bone_pos[4], dir[3], rad;
+ BKE_where_on_path(ik_data->tar, state->curve_position, bone_pos, dir, NULL, &rad, NULL);
- mul_v3_m4v3(poseHead, state->locrot_offset, pchan->pose_head);
- mul_v3_m4v3(poseTail, state->locrot_offset, pchan->pose_tail);
+ apply_curve_transform(ik_data, ob, rad, bone_pos, &rad);
- copy_v3_v3(origHead, poseHead);
+ copy_v3_v3(pchan->pose_mat[3], bone_pos);
+ copy_v3_v3(pchan->pose_head, bone_pos);
+ copy_v3_v3(pchan->pose_tail, bone_pos);
+ pchan->flag |= POSE_DONE;
+ return;
+ }
+
+ float orig_head[3], orig_tail[3], pose_head[3], pose_tail[3];
+ float base_pose_mat[3][3], pose_mat[3][3];
+ float spline_vec[3], scale_fac, radius = 1.0f;
+ float tail_blend_fac = 0.0f;
+
+ mul_v3_m4v3(pose_head, state->locrot_offset, pchan->pose_head);
+ mul_v3_m4v3(pose_tail, state->locrot_offset, pchan->pose_tail);
+
+ copy_v3_v3(orig_head, pose_head);
- /* first, adjust the point positions on the curve */
+ /* First, adjust the point positions on the curve. */
float curveLen = tree->points[index] - tree->points[index + 1];
- float pointStart = state->curve_position;
- float poseScale = len_v3v3(poseHead, poseTail) / pchan->bone->length;
- float baseScale = 1.0f;
+ float bone_len = len_v3v3(pose_head, pose_tail);
+ float point_start = state->curve_position;
+ float pose_scale = bone_len / pchan->bone->length;
+ float base_scale = 1.0f;
- if (ikData->yScaleMode == CONSTRAINT_SPLINEIK_YS_ORIGINAL) {
+ if (ik_data->yScaleMode == CONSTRAINT_SPLINEIK_YS_ORIGINAL) {
/* Carry over the bone Y scale to the curve range. */
- baseScale = poseScale;
+ base_scale = pose_scale;
}
- float pointEnd = pointStart + curveLen * baseScale * state->curve_scale;
+ float point_end = point_start + curveLen * base_scale * state->curve_scale;
- state->curve_position = pointEnd;
+ state->curve_position = point_end;
- /* step 1: determine the positions for the endpoints of the bone */
- if (pointStart < 1.0f) {
+ /* Step 1: determine the positions for the endpoints of the bone. */
+ if (point_start < 1.0f) {
float vec[4], dir[3], rad;
+ radius = 0.0f;
- /* determine if the bone should still be affected by SplineIK */
- if (pointEnd >= 1.0f) {
- /* blending factor depends on the amount of the bone still left on the chain */
- tailBlendFac = (1.0f - pointStart) / (pointEnd - pointStart);
+ /* Calculate head position. */
+ if (point_start == 0.0f) {
+ /* Start of the path. We have no previous tail position to copy. */
+ BKE_where_on_path(ik_data->tar, point_start, vec, dir, NULL, &rad, NULL);
}
else {
- tailBlendFac = 1.0f;
+ copy_v3_v3(vec, state->prev_tail_loc);
+ rad = state->prev_tail_radius;
}
- /* tail endpoint */
- if (where_on_path(ikData->tar, pointEnd, vec, dir, NULL, &rad, NULL)) {
- /* apply curve's object-mode transforms to the position
- * unless the option to allow curve to be positioned elsewhere is activated (i.e. no root)
- */
- if ((ikData->flag & CONSTRAINT_SPLINEIK_NO_ROOT) == 0) {
- mul_m4_v3(ikData->tar->obmat, vec);
+ radius = rad;
+ copy_v3_v3(pose_head, vec);
+ apply_curve_transform(ik_data, ob, rad, pose_head, &radius);
+
+ /* Calculate tail position. */
+ if (ik_data->yScaleMode != CONSTRAINT_SPLINEIK_YS_FIT_CURVE) {
+ float sphere_radius;
+
+ if (ik_data->yScaleMode == CONSTRAINT_SPLINEIK_YS_ORIGINAL) {
+ sphere_radius = bone_len;
+ }
+ else {
+ /* Don't take bone scale into account. */
+ sphere_radius = pchan->bone->length;
}
- /* convert the position to pose-space, then store it */
- mul_m4_v3(ob->imat, vec);
- copy_v3_v3(poseTail, vec);
+ /* Calculate the tail position with sphere curve intersection. */
+ state->prev_tail_seg_idx = position_tail_on_spline(
+ ik_data, vec, sphere_radius, state->prev_tail_seg_idx, pose_tail, &point_end, &rad);
- /* set the new radius */
- radius = rad;
- }
+ state->prev_tail_radius = rad;
+ copy_v3_v3(state->prev_tail_loc, pose_tail);
- /* head endpoint */
- if (where_on_path(ikData->tar, pointStart, vec, dir, NULL, &rad, NULL)) {
- /* apply curve's object-mode transforms to the position
- * unless the option to allow curve to be positioned elsewhere is activated (i.e. no root)
- */
- if ((ikData->flag & CONSTRAINT_SPLINEIK_NO_ROOT) == 0) {
- mul_m4_v3(ikData->tar->obmat, vec);
+ apply_curve_transform(ik_data, ob, rad, pose_tail, &radius);
+ state->curve_position = point_end;
+ }
+ else {
+ /* Scale to fit curve end position. */
+ if (BKE_where_on_path(ik_data->tar, point_end, vec, dir, NULL, &rad, NULL)) {
+ state->prev_tail_radius = rad;
+ copy_v3_v3(state->prev_tail_loc, vec);
+ copy_v3_v3(pose_tail, vec);
+ apply_curve_transform(ik_data, ob, rad, pose_tail, &radius);
}
+ }
- /* store the position, and convert it to pose space */
- mul_m4_v3(ob->imat, vec);
- copy_v3_v3(poseHead, vec);
-
- /* set the new radius (it should be the average value) */
- radius = (radius + rad) / 2;
+ /* Determine if the bone should still be affected by SplineIK.
+ * This makes it so that the bone slowly becomes poseable again the further it rolls off the
+ * curve. When the whole bone has rolled off the curve, the IK constraint will not influence it
+ * anymore.
+ */
+ if (point_end >= 1.0f) {
+ /* Blending factor depends on the amount of the bone still left on the chain. */
+ tail_blend_fac = (1.0f - point_start) / (point_end - point_start);
+ }
+ else {
+ tail_blend_fac = 1.0f;
}
}
@@ -335,11 +498,8 @@ static void splineik_evaluate_bone(
* - splineVec: the vector direction that the spline applies on the bone.
* - scaleFac: the factor that the bone length is scaled by to get the desired amount.
*/
- sub_v3_v3v3(splineVec, poseTail, poseHead);
- scaleFac = len_v3(splineVec) / pchan->bone->length;
-
- /* Extrapolate the full length of the bone as it rolls off the end of the curve. */
- scaleFac = (tailBlendFac < 1e-5f) ? baseScale : scaleFac / tailBlendFac;
+ sub_v3_v3v3(spline_vec, pose_tail, pose_head);
+ scale_fac = len_v3(spline_vec) / pchan->bone->length;
/* Step 3: compute the shortest rotation needed
* to map from the bone rotation to the current axis.
@@ -350,83 +510,102 @@ static void splineik_evaluate_bone(
float dmat[3][3], rmat[3][3];
float raxis[3], rangle;
- /* compute the raw rotation matrix from the bone's current matrix by extracting only the
- * orientation-relevant axes, and normalizing them
+ /* Compute the raw rotation matrix from the bone's current matrix by extracting only the
+ * orientation-relevant axes, and normalizing them.
*/
- mul_m3_m4m4(basePoseMat, state->locrot_offset, pchan->pose_mat);
- normalize_m3_m3(rmat, basePoseMat);
+ mul_m3_m4m4(base_pose_mat, state->locrot_offset, pchan->pose_mat);
+ normalize_m3_m3(rmat, base_pose_mat);
/* Also, normalize the orientation imposed by the bone,
* now that we've extracted the scale factor. */
- normalize_v3(splineVec);
+ normalize_v3(spline_vec);
+
+ /* Calculate smallest axis-angle rotation necessary for getting from the
+ * current orientation of the bone, to the spline-imposed direction.
+ */
+ cross_v3_v3v3(raxis, rmat[1], spline_vec);
- /* calculate smallest axis-angle rotation necessary for getting from the
- * current orientation of the bone, to the spline-imposed direction
+ /* Check if the old and new bone direction is parallel to each other.
+ * If they are, then 'raxis' should be near zero and we will have to get the rotation axis in
+ * some other way.
*/
- cross_v3_v3v3(raxis, rmat[1], splineVec);
+ float norm = normalize_v3(raxis);
+
+ if (norm < FLT_EPSILON) {
+ /* Can't use cross product! */
+ int order[3] = {0, 1, 2};
+ float tmp_axis[3];
+ zero_v3(tmp_axis);
+
+ axis_sort_v3(spline_vec, order);
+
+ /* Use the second largest axis as the basis for the rotation axis. */
+ tmp_axis[order[1]] = 1.0f;
+ cross_v3_v3v3(raxis, tmp_axis, spline_vec);
+ }
- rangle = dot_v3v3(rmat[1], splineVec);
+ rangle = dot_v3v3(rmat[1], spline_vec);
CLAMP(rangle, -1.0f, 1.0f);
rangle = acosf(rangle);
- /* multiply the magnitude of the angle by the influence of the constraint to
- * control the influence of the SplineIK effect
+ /* Multiply the magnitude of the angle by the influence of the constraint to
+ * control the influence of the SplineIK effect.
*/
- rangle *= tree->con->enforce * tailBlendFac;
+ rangle *= tree->con->enforce * tail_blend_fac;
- /* construct rotation matrix from the axis-angle rotation found above
- * - this call takes care to make sure that the axis provided is a unit vector first
+ /* Construct rotation matrix from the axis-angle rotation found above.
+ * - This call takes care to make sure that the axis provided is a unit vector first.
*/
axis_angle_to_mat3(dmat, raxis, rangle);
/* Combine these rotations so that the y-axis of the bone is now aligned as the
* spline dictates, while still maintaining roll control from the existing bone animation. */
- mul_m3_m3m3(poseMat, dmat, rmat);
+ mul_m3_m3m3(pose_mat, dmat, rmat);
- /* attempt to reduce shearing, though I doubt this'll really help too much now... */
- normalize_m3(poseMat);
+ /* Attempt to reduce shearing, though I doubt this'll really help too much now... */
+ normalize_m3(pose_mat);
- mul_m3_m3m3(basePoseMat, dmat, basePoseMat);
+ mul_m3_m3m3(base_pose_mat, dmat, base_pose_mat);
- /* apply rotation to the accumulated parent transform */
+ /* Apply rotation to the accumulated parent transform. */
mul_m4_m3m4(state->locrot_offset, dmat, state->locrot_offset);
}
- /* step 4: set the scaling factors for the axes */
+ /* Step 4: Set the scaling factors for the axes. */
/* Always multiply the y-axis by the scaling factor to get the correct length. */
- mul_v3_fl(poseMat[1], scaleFac);
+ mul_v3_fl(pose_mat[1], scale_fac);
/* After that, apply x/z scaling modes. */
- if (ikData->xzScaleMode != CONSTRAINT_SPLINEIK_XZS_NONE) {
+ if (ik_data->xzScaleMode != CONSTRAINT_SPLINEIK_XZS_NONE) {
/* First, apply the original scale if enabled. */
- if (ikData->xzScaleMode == CONSTRAINT_SPLINEIK_XZS_ORIGINAL ||
- (ikData->flag & CONSTRAINT_SPLINEIK_USE_ORIGINAL_SCALE) != 0) {
+ if (ik_data->xzScaleMode == CONSTRAINT_SPLINEIK_XZS_ORIGINAL ||
+ (ik_data->flag & CONSTRAINT_SPLINEIK_USE_ORIGINAL_SCALE) != 0) {
float scale;
- /* x-axis scale */
+ /* X-axis scale. */
scale = len_v3(pchan->pose_mat[0]);
- mul_v3_fl(poseMat[0], scale);
- /* z-axis scale */
+ mul_v3_fl(pose_mat[0], scale);
+ /* Z-axis scale. */
scale = len_v3(pchan->pose_mat[2]);
- mul_v3_fl(poseMat[2], scale);
+ mul_v3_fl(pose_mat[2], scale);
/* Adjust the scale factor used for volume preservation
* to consider the pre-IK scaling as the initial volume. */
- scaleFac /= poseScale;
+ scale_fac /= pose_scale;
}
/* Apply volume preservation. */
- switch (ikData->xzScaleMode) {
+ switch (ik_data->xzScaleMode) {
case CONSTRAINT_SPLINEIK_XZS_INVERSE: {
- /* old 'volume preservation' method using the inverse scale */
+ /* Old 'volume preservation' method using the inverse scale. */
float scale;
- /* calculate volume preservation factor which is
- * basically the inverse of the y-scaling factor
+ /* Calculate volume preservation factor which is
+ * basically the inverse of the y-scaling factor.
*/
- if (fabsf(scaleFac) != 0.0f) {
- scale = 1.0f / fabsf(scaleFac);
+ if (fabsf(scale_fac) != 0.0f) {
+ scale = 1.0f / fabsf(scale_fac);
/* We need to clamp this within sensible values. */
/* NOTE: these should be fine for now, but should get sanitized in future. */
@@ -436,56 +615,56 @@ static void splineik_evaluate_bone(
scale = 1.0f;
}
- /* apply the scaling */
- mul_v3_fl(poseMat[0], scale);
- mul_v3_fl(poseMat[2], scale);
+ /* Apply the scaling. */
+ mul_v3_fl(pose_mat[0], scale);
+ mul_v3_fl(pose_mat[2], scale);
break;
}
case CONSTRAINT_SPLINEIK_XZS_VOLUMETRIC: {
- /* improved volume preservation based on the Stretch To constraint */
+ /* Improved volume preservation based on the Stretch To constraint. */
float final_scale;
- /* as the basis for volume preservation, we use the inverse scale factor... */
- if (fabsf(scaleFac) != 0.0f) {
- /* NOTE: The method here is taken wholesale from the Stretch To constraint */
- float bulge = powf(1.0f / fabsf(scaleFac), ikData->bulge);
+ /* As the basis for volume preservation, we use the inverse scale factor... */
+ if (fabsf(scale_fac) != 0.0f) {
+ /* NOTE: The method here is taken wholesale from the Stretch To constraint. */
+ float bulge = powf(1.0f / fabsf(scale_fac), ik_data->bulge);
if (bulge > 1.0f) {
- if (ikData->flag & CONSTRAINT_SPLINEIK_USE_BULGE_MAX) {
- float bulge_max = max_ff(ikData->bulge_max, 1.0f);
+ if (ik_data->flag & CONSTRAINT_SPLINEIK_USE_BULGE_MAX) {
+ float bulge_max = max_ff(ik_data->bulge_max, 1.0f);
float hard = min_ff(bulge, bulge_max);
float range = bulge_max - 1.0f;
float scale = (range > 0.0f) ? 1.0f / range : 0.0f;
float soft = 1.0f + range * atanf((bulge - 1.0f) * scale) / (float)M_PI_2;
- bulge = interpf(soft, hard, ikData->bulge_smooth);
+ bulge = interpf(soft, hard, ik_data->bulge_smooth);
}
}
if (bulge < 1.0f) {
- if (ikData->flag & CONSTRAINT_SPLINEIK_USE_BULGE_MIN) {
- float bulge_min = CLAMPIS(ikData->bulge_min, 0.0f, 1.0f);
+ if (ik_data->flag & CONSTRAINT_SPLINEIK_USE_BULGE_MIN) {
+ float bulge_min = CLAMPIS(ik_data->bulge_min, 0.0f, 1.0f);
float hard = max_ff(bulge, bulge_min);
float range = 1.0f - bulge_min;
float scale = (range > 0.0f) ? 1.0f / range : 0.0f;
float soft = 1.0f - range * atanf((1.0f - bulge) * scale) / (float)M_PI_2;
- bulge = interpf(soft, hard, ikData->bulge_smooth);
+ bulge = interpf(soft, hard, ik_data->bulge_smooth);
}
}
- /* compute scale factor for xz axes from this value */
+ /* Compute scale factor for xz axes from this value. */
final_scale = sqrtf(bulge);
}
else {
- /* no scaling, so scale factor is simple */
+ /* No scaling, so scale factor is simple. */
final_scale = 1.0f;
}
/* Apply the scaling (assuming normalized scale). */
- mul_v3_fl(poseMat[0], final_scale);
- mul_v3_fl(poseMat[2], final_scale);
+ mul_v3_fl(pose_mat[0], final_scale);
+ mul_v3_fl(pose_mat[2], final_scale);
break;
}
}
@@ -494,49 +673,49 @@ static void splineik_evaluate_bone(
/* Finally, multiply the x and z scaling by the radius of the curve too,
* to allow automatic scales to get tweaked still.
*/
- if ((ikData->flag & CONSTRAINT_SPLINEIK_NO_CURVERAD) == 0) {
- mul_v3_fl(poseMat[0], radius);
- mul_v3_fl(poseMat[2], radius);
+ if ((ik_data->flag & CONSTRAINT_SPLINEIK_NO_CURVERAD) == 0) {
+ mul_v3_fl(pose_mat[0], radius);
+ mul_v3_fl(pose_mat[2], radius);
}
/* Blend the scaling of the matrix according to the influence. */
- sub_m3_m3m3(poseMat, poseMat, basePoseMat);
- madd_m3_m3m3fl(poseMat, basePoseMat, poseMat, tree->con->enforce * tailBlendFac);
+ sub_m3_m3m3(pose_mat, pose_mat, base_pose_mat);
+ madd_m3_m3m3fl(pose_mat, base_pose_mat, pose_mat, tree->con->enforce * tail_blend_fac);
- /* step 5: set the location of the bone in the matrix */
- if (ikData->flag & CONSTRAINT_SPLINEIK_NO_ROOT) {
- /* when the 'no-root' option is affected, the chain can retain
- * the shape but be moved elsewhere
+ /* Step 5: Set the location of the bone in the matrix. */
+ if (ik_data->flag & CONSTRAINT_SPLINEIK_NO_ROOT) {
+ /* When the 'no-root' option is affected, the chain can retain
+ * the shape but be moved elsewhere.
*/
- copy_v3_v3(poseHead, origHead);
+ copy_v3_v3(pose_head, orig_head);
}
else if (tree->con->enforce < 1.0f) {
- /* when the influence is too low
- * - blend the positions for the 'root' bone
- * - stick to the parent for any other
+ /* When the influence is too low:
+ * - Blend the positions for the 'root' bone.
+ * - Stick to the parent for any other.
*/
if (index < tree->chainlen - 1) {
- copy_v3_v3(poseHead, origHead);
+ copy_v3_v3(pose_head, orig_head);
}
else {
- interp_v3_v3v3(poseHead, origHead, poseHead, tree->con->enforce);
+ interp_v3_v3v3(pose_head, orig_head, pose_head, tree->con->enforce);
}
}
- /* finally, store the new transform */
- copy_m4_m3(pchan->pose_mat, poseMat);
- copy_v3_v3(pchan->pose_mat[3], poseHead);
- copy_v3_v3(pchan->pose_head, poseHead);
+ /* Finally, store the new transform. */
+ copy_m4_m3(pchan->pose_mat, pose_mat);
+ copy_v3_v3(pchan->pose_mat[3], pose_head);
+ copy_v3_v3(pchan->pose_head, pose_head);
- mul_v3_mat3_m4v3(origTail, state->locrot_offset, pchan->pose_tail);
+ mul_v3_mat3_m4v3(orig_tail, state->locrot_offset, pchan->pose_tail);
- /* recalculate tail, as it's now outdated after the head gets adjusted above! */
+ /* Recalculate tail, as it's now outdated after the head gets adjusted above! */
BKE_pose_where_is_bone_tail(pchan);
- /* update the offset in the accumulated parent transform */
- sub_v3_v3v3(state->locrot_offset[3], pchan->pose_tail, origTail);
+ /* Update the offset in the accumulated parent transform. */
+ sub_v3_v3v3(state->locrot_offset[3], pchan->pose_tail, orig_tail);
- /* done! */
+ /* Done! */
pchan->flag |= POSE_DONE;
}
@@ -559,8 +738,8 @@ static void splineik_execute_tree(
if (splineik_evaluate_init(tree, &state)) {
/* Walk over each bone in the chain, calculating the effects of spline IK
- * - the chain is traversed in the opposite order to storage order (i.e. parent to children)
- * so that dependencies are correct
+ * - the chain is traversed in the opposite order to storage order
+ * (i.e. parent to children) so that dependencies are correct
*/
for (int i = tree->chainlen - 1; i >= 0; i--) {
bPoseChannel *pchan = tree->chain[i];
diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc
index 05da47aed8e..8bbb3014dac 100644
--- a/source/blender/blenkernel/intern/attribute_access.cc
+++ b/source/blender/blenkernel/intern/attribute_access.cc
@@ -34,7 +34,7 @@
#include "CLG_log.h"
-#include "NOD_node_tree_multi_function.hh"
+#include "NOD_type_conversions.hh"
#include "attribute_access_intern.hh"
@@ -44,194 +44,13 @@ using blender::float3;
using blender::Set;
using blender::StringRef;
using blender::StringRefNull;
-using blender::bke::ReadAttributePtr;
-using blender::bke::WriteAttributePtr;
using blender::fn::GMutableSpan;
+using blender::fn::GSpan;
+using blender::fn::GVArray_For_GSpan;
+using blender::fn::GVArray_For_SingleValue;
namespace blender::bke {
-/* -------------------------------------------------------------------- */
-/** \name Attribute Accessor implementations
- * \{ */
-
-ReadAttribute::~ReadAttribute()
-{
- if (array_is_temporary_ && array_buffer_ != nullptr) {
- cpp_type_.destruct_n(array_buffer_, size_);
- MEM_freeN(array_buffer_);
- }
-}
-
-fn::GSpan ReadAttribute::get_span() const
-{
- if (size_ == 0) {
- return fn::GSpan(cpp_type_);
- }
- if (array_buffer_ == nullptr) {
- std::lock_guard lock{span_mutex_};
- if (array_buffer_ == nullptr) {
- this->initialize_span();
- }
- }
- return fn::GSpan(cpp_type_, array_buffer_, size_);
-}
-
-void ReadAttribute::initialize_span() const
-{
- const int element_size = cpp_type_.size();
- array_buffer_ = MEM_mallocN_aligned(size_ * element_size, cpp_type_.alignment(), __func__);
- array_is_temporary_ = true;
- for (const int i : IndexRange(size_)) {
- this->get_internal(i, POINTER_OFFSET(array_buffer_, i * element_size));
- }
-}
-
-WriteAttribute::~WriteAttribute()
-{
- if (array_should_be_applied_) {
- CLOG_ERROR(&LOG, "Forgot to call apply_span.");
- }
- if (array_is_temporary_ && array_buffer_ != nullptr) {
- cpp_type_.destruct_n(array_buffer_, size_);
- MEM_freeN(array_buffer_);
- }
-}
-
-/**
- * Get a mutable span that can be modified. When all modifications to the attribute are done,
- * #apply_span should be called. */
-fn::GMutableSpan WriteAttribute::get_span()
-{
- if (size_ == 0) {
- return fn::GMutableSpan(cpp_type_);
- }
- if (array_buffer_ == nullptr) {
- this->initialize_span(false);
- }
- array_should_be_applied_ = true;
- return fn::GMutableSpan(cpp_type_, array_buffer_, size_);
-}
-
-fn::GMutableSpan WriteAttribute::get_span_for_write_only()
-{
- if (size_ == 0) {
- return fn::GMutableSpan(cpp_type_);
- }
- if (array_buffer_ == nullptr) {
- this->initialize_span(true);
- }
- array_should_be_applied_ = true;
- return fn::GMutableSpan(cpp_type_, array_buffer_, size_);
-}
-
-void WriteAttribute::initialize_span(const bool write_only)
-{
- const int element_size = cpp_type_.size();
- array_buffer_ = MEM_mallocN_aligned(element_size * size_, cpp_type_.alignment(), __func__);
- array_is_temporary_ = true;
- if (write_only) {
- /* This does nothing for trivial types, but is necessary for general correctness. */
- cpp_type_.construct_default_n(array_buffer_, size_);
- }
- else {
- for (const int i : IndexRange(size_)) {
- this->get(i, POINTER_OFFSET(array_buffer_, i * element_size));
- }
- }
-}
-
-void WriteAttribute::apply_span()
-{
- this->apply_span_if_necessary();
- array_should_be_applied_ = false;
-}
-
-void WriteAttribute::apply_span_if_necessary()
-{
- /* Only works when the span has been initialized beforehand. */
- BLI_assert(array_buffer_ != nullptr);
-
- const int element_size = cpp_type_.size();
- for (const int i : IndexRange(size_)) {
- this->set_internal(i, POINTER_OFFSET(array_buffer_, i * element_size));
- }
-}
-
-/* This is used by the #OutputAttributePtr class. */
-class TemporaryWriteAttribute final : public WriteAttribute {
- public:
- GMutableSpan data;
- GeometryComponent &component;
- std::string final_name;
-
- TemporaryWriteAttribute(AttributeDomain domain,
- GMutableSpan data,
- GeometryComponent &component,
- std::string final_name)
- : WriteAttribute(domain, data.type(), data.size()),
- data(data),
- component(component),
- final_name(std::move(final_name))
- {
- }
-
- ~TemporaryWriteAttribute() override
- {
- if (data.data() != nullptr) {
- cpp_type_.destruct_n(data.data(), data.size());
- MEM_freeN(data.data());
- }
- }
-
- void get_internal(const int64_t index, void *r_value) const override
- {
- data.type().copy_to_uninitialized(data[index], r_value);
- }
-
- void set_internal(const int64_t index, const void *value) override
- {
- data.type().copy_to_initialized(value, data[index]);
- }
-
- void initialize_span(const bool UNUSED(write_only)) override
- {
- array_buffer_ = data.data();
- array_is_temporary_ = false;
- }
-
- void apply_span_if_necessary() override
- {
- /* Do nothing, because the span contains the attribute itself already. */
- }
-};
-
-class ConvertedReadAttribute final : public ReadAttribute {
- private:
- const CPPType &from_type_;
- const CPPType &to_type_;
- ReadAttributePtr base_attribute_;
- const nodes::DataTypeConversions &conversions_;
-
- public:
- ConvertedReadAttribute(ReadAttributePtr base_attribute, const CPPType &to_type)
- : ReadAttribute(base_attribute->domain(), to_type, base_attribute->size()),
- from_type_(base_attribute->cpp_type()),
- to_type_(to_type),
- base_attribute_(std::move(base_attribute)),
- conversions_(nodes::get_implicit_type_conversions())
- {
- }
-
- void get_internal(const int64_t index, void *r_value) const override
- {
- BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer);
- base_attribute_->get(index, buffer);
- conversions_.convert(from_type_, to_type_, buffer, r_value);
- }
-};
-
-/** \} */
-
const blender::fn::CPPType *custom_data_type_to_cpp_type(const CustomDataType type)
{
switch (type) {
@@ -244,7 +63,7 @@ const blender::fn::CPPType *custom_data_type_to_cpp_type(const CustomDataType ty
case CD_PROP_INT32:
return &CPPType::get<int>();
case CD_PROP_COLOR:
- return &CPPType::get<Color4f>();
+ return &CPPType::get<ColorGeometry4f>();
case CD_PROP_BOOL:
return &CPPType::get<bool>();
default:
@@ -267,7 +86,7 @@ CustomDataType cpp_type_to_custom_data_type(const blender::fn::CPPType &type)
if (type.is<int>()) {
return CD_PROP_INT32;
}
- if (type.is<Color4f>()) {
+ if (type.is<ColorGeometry4f>()) {
return CD_PROP_COLOR;
}
if (type.is<bool>()) {
@@ -327,10 +146,8 @@ CustomDataType attribute_data_type_highest_complexity(Span<CustomDataType> data_
static int attribute_domain_priority(const AttributeDomain domain)
{
switch (domain) {
-#if 0
case ATTR_DOMAIN_CURVE:
return 0;
-#endif
case ATTR_DOMAIN_FACE:
return 1;
case ATTR_DOMAIN_EDGE:
@@ -366,7 +183,27 @@ AttributeDomain attribute_domain_highest_priority(Span<AttributeDomain> domains)
return highest_priority_domain;
}
-ReadAttributePtr BuiltinCustomDataLayerProvider::try_get_for_read(
+void OutputAttribute::save()
+{
+ save_has_been_called_ = true;
+ if (optional_span_varray_.has_value()) {
+ optional_span_varray_->save();
+ }
+ if (save_) {
+ save_(*this);
+ }
+}
+
+OutputAttribute::~OutputAttribute()
+{
+ if (!save_has_been_called_) {
+ if (varray_) {
+ std::cout << "Warning: Call `save()` to make sure that changes persist in all cases.\n";
+ }
+ }
+}
+
+GVArrayPtr BuiltinCustomDataLayerProvider::try_get_for_read(
const GeometryComponent &component) const
{
const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
@@ -382,7 +219,7 @@ ReadAttributePtr BuiltinCustomDataLayerProvider::try_get_for_read(
return as_read_attribute_(data, domain_size);
}
-WriteAttributePtr BuiltinCustomDataLayerProvider::try_get_for_write(
+GVMutableArrayPtr BuiltinCustomDataLayerProvider::try_get_for_write(
GeometryComponent &component) const
{
if (writable_ != Writable) {
@@ -428,7 +265,43 @@ bool BuiltinCustomDataLayerProvider::try_delete(GeometryComponent &component) co
return delete_success;
}
-bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component) const
+static bool add_custom_data_layer_from_attribute_init(CustomData &custom_data,
+ const CustomDataType data_type,
+ const int domain_size,
+ const AttributeInit &initializer)
+{
+ switch (initializer.type) {
+ case AttributeInit::Type::Default: {
+ void *data = CustomData_add_layer(&custom_data, data_type, CD_DEFAULT, nullptr, domain_size);
+ return data != nullptr;
+ }
+ case AttributeInit::Type::VArray: {
+ void *data = CustomData_add_layer(&custom_data, data_type, CD_DEFAULT, nullptr, domain_size);
+ if (data == nullptr) {
+ return false;
+ }
+ const GVArray *varray = static_cast<const AttributeInitVArray &>(initializer).varray;
+ varray->materialize_to_uninitialized(IndexRange(varray->size()), data);
+ return true;
+ }
+ case AttributeInit::Type::MoveArray: {
+ void *source_data = static_cast<const AttributeInitMove &>(initializer).data;
+ void *data = CustomData_add_layer(
+ &custom_data, data_type, CD_ASSIGN, source_data, domain_size);
+ if (data == nullptr) {
+ MEM_freeN(source_data);
+ return false;
+ }
+ return true;
+ }
+ }
+
+ BLI_assert_unreachable();
+ return false;
+}
+
+bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component,
+ const AttributeInit &initializer) const
{
if (createable_ != Creatable) {
return false;
@@ -441,10 +314,10 @@ bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component) co
/* Exists already. */
return false;
}
+
const int domain_size = component.attribute_domain_size(domain_);
- const void *data = CustomData_add_layer(
- custom_data, stored_type_, CD_DEFAULT, nullptr, domain_size);
- const bool success = data != nullptr;
+ const bool success = add_custom_data_layer_from_attribute_init(
+ *custom_data, stored_type_, domain_size, initializer);
if (success) {
custom_data_access_.update_custom_data_pointers(component);
}
@@ -461,7 +334,7 @@ bool BuiltinCustomDataLayerProvider::exists(const GeometryComponent &component)
return data != nullptr;
}
-ReadAttributePtr CustomDataAttributeProvider::try_get_for_read(
+ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read(
const GeometryComponent &component, const StringRef attribute_name) const
{
const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
@@ -484,7 +357,7 @@ ReadAttributePtr CustomDataAttributeProvider::try_get_for_read(
case CD_PROP_INT32:
return this->layer_to_read_attribute<int>(layer, domain_size);
case CD_PROP_COLOR:
- return this->layer_to_read_attribute<Color4f>(layer, domain_size);
+ return this->layer_to_read_attribute<ColorGeometry4f>(layer, domain_size);
case CD_PROP_BOOL:
return this->layer_to_read_attribute<bool>(layer, domain_size);
default:
@@ -494,7 +367,7 @@ ReadAttributePtr CustomDataAttributeProvider::try_get_for_read(
return {};
}
-WriteAttributePtr CustomDataAttributeProvider::try_get_for_write(
+WriteAttributeLookup CustomDataAttributeProvider::try_get_for_write(
GeometryComponent &component, const StringRef attribute_name) const
{
CustomData *custom_data = custom_data_access_.get_custom_data(component);
@@ -518,7 +391,7 @@ WriteAttributePtr CustomDataAttributeProvider::try_get_for_write(
case CD_PROP_INT32:
return this->layer_to_write_attribute<int>(layer, domain_size);
case CD_PROP_COLOR:
- return this->layer_to_write_attribute<Color4f>(layer, domain_size);
+ return this->layer_to_write_attribute<ColorGeometry4f>(layer, domain_size);
case CD_PROP_BOOL:
return this->layer_to_write_attribute<bool>(layer, domain_size);
default:
@@ -546,10 +419,52 @@ bool CustomDataAttributeProvider::try_delete(GeometryComponent &component,
return false;
}
+static bool add_named_custom_data_layer_from_attribute_init(const StringRef attribute_name,
+ CustomData &custom_data,
+ const CustomDataType data_type,
+ const int domain_size,
+ const AttributeInit &initializer)
+{
+ char attribute_name_c[MAX_NAME];
+ attribute_name.copy(attribute_name_c);
+
+ switch (initializer.type) {
+ case AttributeInit::Type::Default: {
+ void *data = CustomData_add_layer_named(
+ &custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_name_c);
+ return data != nullptr;
+ }
+ case AttributeInit::Type::VArray: {
+ void *data = CustomData_add_layer_named(
+ &custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_name_c);
+ if (data == nullptr) {
+ return false;
+ }
+ const GVArray *varray = static_cast<const AttributeInitVArray &>(initializer).varray;
+ varray->materialize_to_uninitialized(IndexRange(varray->size()), data);
+ return true;
+ }
+ case AttributeInit::Type::MoveArray: {
+ void *source_data = static_cast<const AttributeInitMove &>(initializer).data;
+ void *data = CustomData_add_layer_named(
+ &custom_data, data_type, CD_ASSIGN, source_data, domain_size, attribute_name_c);
+ if (data == nullptr) {
+ MEM_freeN(source_data);
+ return false;
+ }
+ return true;
+ }
+ }
+
+ BLI_assert_unreachable();
+ return false;
+}
+
bool CustomDataAttributeProvider::try_create(GeometryComponent &component,
const StringRef attribute_name,
const AttributeDomain domain,
- const CustomDataType data_type) const
+ const CustomDataType data_type,
+ const AttributeInit &initializer) const
{
if (domain_ != domain) {
return false;
@@ -567,10 +482,8 @@ bool CustomDataAttributeProvider::try_create(GeometryComponent &component,
}
}
const int domain_size = component.attribute_domain_size(domain_);
- char attribute_name_c[MAX_NAME];
- attribute_name.copy(attribute_name_c);
- CustomData_add_layer_named(
- custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_name_c);
+ add_named_custom_data_layer_from_attribute_init(
+ attribute_name, *custom_data, data_type, domain_size, initializer);
return true;
}
@@ -593,7 +506,7 @@ bool CustomDataAttributeProvider::foreach_attribute(const GeometryComponent &com
return true;
}
-ReadAttributePtr NamedLegacyCustomDataProvider::try_get_for_read(
+ReadAttributeLookup NamedLegacyCustomDataProvider::try_get_for_read(
const GeometryComponent &component, const StringRef attribute_name) const
{
const CustomData *custom_data = custom_data_access_.get_const_custom_data(component);
@@ -604,14 +517,14 @@ ReadAttributePtr NamedLegacyCustomDataProvider::try_get_for_read(
if (layer.type == stored_type_) {
if (layer.name == attribute_name) {
const int domain_size = component.attribute_domain_size(domain_);
- return as_read_attribute_(layer.data, domain_size);
+ return {as_read_attribute_(layer.data, domain_size), domain_};
}
}
}
return {};
}
-WriteAttributePtr NamedLegacyCustomDataProvider::try_get_for_write(
+WriteAttributeLookup NamedLegacyCustomDataProvider::try_get_for_write(
GeometryComponent &component, const StringRef attribute_name) const
{
CustomData *custom_data = custom_data_access_.get_custom_data(component);
@@ -628,7 +541,7 @@ WriteAttributePtr NamedLegacyCustomDataProvider::try_get_for_write(
if (data_old != data_new) {
custom_data_access_.update_custom_data_pointers(component);
}
- return as_write_attribute_(layer.data, domain_size);
+ return {as_write_attribute_(layer.data, domain_size), domain_};
}
}
}
@@ -680,6 +593,142 @@ void NamedLegacyCustomDataProvider::foreach_domain(
callback(domain_);
}
+CustomDataAttributes::CustomDataAttributes()
+{
+ CustomData_reset(&data);
+ size_ = 0;
+}
+
+CustomDataAttributes::~CustomDataAttributes()
+{
+ CustomData_free(&data, size_);
+}
+
+CustomDataAttributes::CustomDataAttributes(const CustomDataAttributes &other)
+{
+ size_ = other.size_;
+ CustomData_copy(&other.data, &data, CD_MASK_ALL, CD_DUPLICATE, size_);
+}
+
+CustomDataAttributes::CustomDataAttributes(CustomDataAttributes &&other)
+{
+ size_ = other.size_;
+ data = other.data;
+ CustomData_reset(&other.data);
+}
+
+CustomDataAttributes &CustomDataAttributes::operator=(const CustomDataAttributes &other)
+{
+ if (this != &other) {
+ CustomData_copy(&other.data, &data, CD_MASK_ALL, CD_DUPLICATE, other.size_);
+ size_ = other.size_;
+ }
+
+ return *this;
+}
+
+std::optional<GSpan> CustomDataAttributes::get_for_read(const StringRef name) const
+{
+ BLI_assert(size_ != 0);
+ for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) {
+ if (layer.name == name) {
+ const CPPType *cpp_type = custom_data_type_to_cpp_type((CustomDataType)layer.type);
+ BLI_assert(cpp_type != nullptr);
+ return GSpan(*cpp_type, layer.data, size_);
+ }
+ }
+ return {};
+}
+
+/**
+ * Return a virtual array for a stored attribute, or a single value virtual array with the default
+ * value if the attribute doesn't exist. If no default value is provided, the default value for the
+ * type will be used.
+ */
+GVArrayPtr CustomDataAttributes::get_for_read(const StringRef name,
+ const CustomDataType data_type,
+ const void *default_value) const
+{
+ const CPPType *type = blender::bke::custom_data_type_to_cpp_type(data_type);
+
+ std::optional<GSpan> attribute = this->get_for_read(name);
+ if (!attribute) {
+ const int domain_size = this->size_;
+ return std::make_unique<GVArray_For_SingleValue>(
+ *type, domain_size, (default_value == nullptr) ? type->default_value() : default_value);
+ }
+
+ if (attribute->type() == *type) {
+ return std::make_unique<GVArray_For_GSpan>(*attribute);
+ }
+ const blender::nodes::DataTypeConversions &conversions =
+ blender::nodes::get_implicit_type_conversions();
+ return conversions.try_convert(std::make_unique<GVArray_For_GSpan>(*attribute), *type);
+}
+
+std::optional<GMutableSpan> CustomDataAttributes::get_for_write(const StringRef name)
+{
+ /* If this assert hits, it most likely means that #reallocate was not called at some point. */
+ BLI_assert(size_ != 0);
+ for (CustomDataLayer &layer : MutableSpan(data.layers, data.totlayer)) {
+ if (layer.name == name) {
+ const CPPType *cpp_type = custom_data_type_to_cpp_type((CustomDataType)layer.type);
+ BLI_assert(cpp_type != nullptr);
+ return GMutableSpan(*cpp_type, layer.data, size_);
+ }
+ }
+ return {};
+}
+
+bool CustomDataAttributes::create(const StringRef name, const CustomDataType data_type)
+{
+ char name_c[MAX_NAME];
+ name.copy(name_c);
+ void *result = CustomData_add_layer_named(&data, data_type, CD_DEFAULT, nullptr, size_, name_c);
+ return result != nullptr;
+}
+
+bool CustomDataAttributes::create_by_move(const blender::StringRef name,
+ const CustomDataType data_type,
+ void *buffer)
+{
+ char name_c[MAX_NAME];
+ name.copy(name_c);
+ void *result = CustomData_add_layer_named(&data, data_type, CD_ASSIGN, buffer, size_, name_c);
+ return result != nullptr;
+}
+
+bool CustomDataAttributes::remove(const blender::StringRef name)
+{
+ bool result = false;
+ for (const int i : IndexRange(data.totlayer)) {
+ const CustomDataLayer &layer = data.layers[i];
+ if (layer.name == name) {
+ CustomData_free_layer(&data, layer.type, size_, i);
+ result = true;
+ }
+ }
+ return result;
+}
+
+void CustomDataAttributes::reallocate(const int size)
+{
+ size_ = size;
+ CustomData_realloc(&data, size);
+}
+
+bool CustomDataAttributes::foreach_attribute(const AttributeForeachCallback callback,
+ const AttributeDomain domain) const
+{
+ for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) {
+ AttributeMetaData meta_data{domain, (CustomDataType)layer.type};
+ if (!callback(layer.name, meta_data)) {
+ return false;
+ }
+ }
+ return true;
+}
+
} // namespace blender::bke
/* -------------------------------------------------------------------- */
@@ -706,7 +755,17 @@ int GeometryComponent::attribute_domain_size(const AttributeDomain UNUSED(domain
return 0;
}
-ReadAttributePtr GeometryComponent::attribute_try_get_for_read(
+bool GeometryComponent::attribute_is_builtin(const blender::StringRef attribute_name) const
+{
+ using namespace blender::bke;
+ const ComponentAttributeProviders *providers = this->get_attribute_providers();
+ if (providers == nullptr) {
+ return false;
+ }
+ return providers->builtin_attribute_providers().contains_as(attribute_name);
+}
+
+blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_for_read(
const StringRef attribute_name) const
{
using namespace blender::bke;
@@ -717,11 +776,11 @@ ReadAttributePtr GeometryComponent::attribute_try_get_for_read(
const BuiltinAttributeProvider *builtin_provider =
providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr);
if (builtin_provider != nullptr) {
- return builtin_provider->try_get_for_read(*this);
+ return {builtin_provider->try_get_for_read(*this), builtin_provider->domain()};
}
for (const DynamicAttributesProvider *dynamic_provider :
providers->dynamic_attribute_providers()) {
- ReadAttributePtr attribute = dynamic_provider->try_get_for_read(*this, attribute_name);
+ ReadAttributeLookup attribute = dynamic_provider->try_get_for_read(*this, attribute_name);
if (attribute) {
return attribute;
}
@@ -729,16 +788,19 @@ ReadAttributePtr GeometryComponent::attribute_try_get_for_read(
return {};
}
-ReadAttributePtr GeometryComponent::attribute_try_adapt_domain(
- ReadAttributePtr attribute, const AttributeDomain new_domain) const
+std::unique_ptr<blender::fn::GVArray> GeometryComponent::attribute_try_adapt_domain(
+ std::unique_ptr<blender::fn::GVArray> varray,
+ const AttributeDomain from_domain,
+ const AttributeDomain to_domain) const
{
- if (attribute && attribute->domain() == new_domain) {
- return attribute;
+ if (from_domain == to_domain) {
+ return varray;
}
return {};
}
-WriteAttributePtr GeometryComponent::attribute_try_get_for_write(const StringRef attribute_name)
+blender::bke::WriteAttributeLookup GeometryComponent::attribute_try_get_for_write(
+ const StringRef attribute_name)
{
using namespace blender::bke;
const ComponentAttributeProviders *providers = this->get_attribute_providers();
@@ -748,11 +810,11 @@ WriteAttributePtr GeometryComponent::attribute_try_get_for_write(const StringRef
const BuiltinAttributeProvider *builtin_provider =
providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr);
if (builtin_provider != nullptr) {
- return builtin_provider->try_get_for_write(*this);
+ return {builtin_provider->try_get_for_write(*this), builtin_provider->domain()};
}
for (const DynamicAttributesProvider *dynamic_provider :
providers->dynamic_attribute_providers()) {
- WriteAttributePtr attribute = dynamic_provider->try_get_for_write(*this, attribute_name);
+ WriteAttributeLookup attribute = dynamic_provider->try_get_for_write(*this, attribute_name);
if (attribute) {
return attribute;
}
@@ -782,7 +844,8 @@ bool GeometryComponent::attribute_try_delete(const StringRef attribute_name)
bool GeometryComponent::attribute_try_create(const StringRef attribute_name,
const AttributeDomain domain,
- const CustomDataType data_type)
+ const CustomDataType data_type,
+ const AttributeInit &initializer)
{
using namespace blender::bke;
if (attribute_name.is_empty()) {
@@ -801,17 +864,36 @@ bool GeometryComponent::attribute_try_create(const StringRef attribute_name,
if (builtin_provider->data_type() != data_type) {
return false;
}
- return builtin_provider->try_create(*this);
+ return builtin_provider->try_create(*this, initializer);
}
for (const DynamicAttributesProvider *dynamic_provider :
providers->dynamic_attribute_providers()) {
- if (dynamic_provider->try_create(*this, attribute_name, domain, data_type)) {
+ if (dynamic_provider->try_create(*this, attribute_name, domain, data_type, initializer)) {
return true;
}
}
return false;
}
+bool GeometryComponent::attribute_try_create_builtin(const blender::StringRef attribute_name,
+ const AttributeInit &initializer)
+{
+ using namespace blender::bke;
+ if (attribute_name.is_empty()) {
+ return false;
+ }
+ const ComponentAttributeProviders *providers = this->get_attribute_providers();
+ if (providers == nullptr) {
+ return false;
+ }
+ const BuiltinAttributeProvider *builtin_provider =
+ providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr);
+ if (builtin_provider == nullptr) {
+ return false;
+ }
+ return builtin_provider->try_create(*this, initializer);
+}
+
Set<std::string> GeometryComponent::attribute_names() const
{
Set<std::string> attributes;
@@ -822,12 +904,16 @@ Set<std::string> GeometryComponent::attribute_names() const
return attributes;
}
-void GeometryComponent::attribute_foreach(const AttributeForeachCallback callback) const
+/**
+ * \return False if the callback explicitly returned false at any point, otherwise true,
+ * meaning the callback made it all the way through.
+ */
+bool GeometryComponent::attribute_foreach(const AttributeForeachCallback callback) const
{
using namespace blender::bke;
const ComponentAttributeProviders *providers = this->get_attribute_providers();
if (providers == nullptr) {
- return;
+ return true;
}
/* Keep track handled attribute names to make sure that we do not return the same name twice. */
@@ -838,7 +924,7 @@ void GeometryComponent::attribute_foreach(const AttributeForeachCallback callbac
if (provider->exists(*this)) {
AttributeMetaData meta_data{provider->domain(), provider->data_type()};
if (!callback(provider->name(), meta_data)) {
- return;
+ return false;
}
handled_attribute_names.add_new(provider->name());
}
@@ -852,271 +938,292 @@ void GeometryComponent::attribute_foreach(const AttributeForeachCallback callbac
return true;
});
if (!continue_loop) {
- return;
+ return false;
}
}
+
+ return true;
}
bool GeometryComponent::attribute_exists(const blender::StringRef attribute_name) const
{
- ReadAttributePtr attribute = this->attribute_try_get_for_read(attribute_name);
+ blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name);
if (attribute) {
return true;
}
return false;
}
-static ReadAttributePtr try_adapt_data_type(ReadAttributePtr attribute,
- const blender::fn::CPPType &to_type)
+std::optional<AttributeMetaData> GeometryComponent::attribute_get_meta_data(
+ const StringRef attribute_name) const
{
- const blender::fn::CPPType &from_type = attribute->cpp_type();
- if (from_type == to_type) {
- return attribute;
- }
+ std::optional<AttributeMetaData> result{std::nullopt};
+ this->attribute_foreach([&](StringRefNull name, const AttributeMetaData &meta_data) {
+ if (attribute_name == name) {
+ result = meta_data;
+ return false;
+ }
+ return true;
+ });
+ return result;
+}
+static std::unique_ptr<blender::fn::GVArray> try_adapt_data_type(
+ std::unique_ptr<blender::fn::GVArray> varray, const blender::fn::CPPType &to_type)
+{
const blender::nodes::DataTypeConversions &conversions =
blender::nodes::get_implicit_type_conversions();
- if (!conversions.is_convertible(from_type, to_type)) {
- return {};
- }
-
- return std::make_unique<blender::bke::ConvertedReadAttribute>(std::move(attribute), to_type);
+ return conversions.try_convert(std::move(varray), to_type);
}
-ReadAttributePtr GeometryComponent::attribute_try_get_for_read(
+std::unique_ptr<blender::fn::GVArray> GeometryComponent::attribute_try_get_for_read(
const StringRef attribute_name,
const AttributeDomain domain,
const CustomDataType data_type) const
{
- ReadAttributePtr attribute = this->attribute_try_get_for_read(attribute_name);
+ blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name);
if (!attribute) {
return {};
}
- if (attribute->domain() != domain) {
- attribute = this->attribute_try_adapt_domain(std::move(attribute), domain);
- if (!attribute) {
+ std::unique_ptr<blender::fn::GVArray> varray = std::move(attribute.varray);
+ if (domain != ATTR_DOMAIN_AUTO && attribute.domain != domain) {
+ varray = this->attribute_try_adapt_domain(std::move(varray), attribute.domain, domain);
+ if (!varray) {
return {};
}
}
const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type);
BLI_assert(cpp_type != nullptr);
- if (attribute->cpp_type() != *cpp_type) {
- attribute = try_adapt_data_type(std::move(attribute), *cpp_type);
- if (!attribute) {
+ if (varray->type() != *cpp_type) {
+ varray = try_adapt_data_type(std::move(varray), *cpp_type);
+ if (!varray) {
return {};
}
}
- return attribute;
+ return varray;
}
-ReadAttributePtr GeometryComponent::attribute_try_get_for_read(const StringRef attribute_name,
- const AttributeDomain domain) const
+std::unique_ptr<blender::bke::GVArray> GeometryComponent::attribute_try_get_for_read(
+ const StringRef attribute_name, const AttributeDomain domain) const
{
if (!this->attribute_domain_supported(domain)) {
return {};
}
- ReadAttributePtr attribute = this->attribute_try_get_for_read(attribute_name);
+ blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name);
if (!attribute) {
return {};
}
- if (attribute->domain() != domain) {
- attribute = this->attribute_try_adapt_domain(std::move(attribute), domain);
- if (!attribute) {
- return {};
- }
+ if (attribute.domain != domain) {
+ return this->attribute_try_adapt_domain(std::move(attribute.varray), attribute.domain, domain);
}
- return attribute;
+ return std::move(attribute.varray);
}
-ReadAttributePtr GeometryComponent::attribute_get_for_read(const StringRef attribute_name,
- const AttributeDomain domain,
- const CustomDataType data_type,
- const void *default_value) const
+blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_for_read(
+ const blender::StringRef attribute_name, const CustomDataType data_type) const
{
- ReadAttributePtr attribute = this->attribute_try_get_for_read(attribute_name, domain, data_type);
- if (attribute) {
- return attribute;
+ blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name);
+ if (!attribute) {
+ return {};
}
- return this->attribute_get_constant_for_read(domain, data_type, default_value);
-}
-
-blender::bke::ReadAttributePtr GeometryComponent::attribute_get_constant_for_read(
- const AttributeDomain domain, const CustomDataType data_type, const void *value) const
-{
- BLI_assert(this->attribute_domain_supported(domain));
- const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type);
- BLI_assert(cpp_type != nullptr);
- if (value == nullptr) {
- value = cpp_type->default_value();
+ const blender::fn::CPPType *type = blender::bke::custom_data_type_to_cpp_type(data_type);
+ BLI_assert(type != nullptr);
+ if (attribute.varray->type() == *type) {
+ return attribute;
}
- const int domain_size = this->attribute_domain_size(domain);
- return std::make_unique<blender::bke::ConstantReadAttribute>(
- domain, domain_size, *cpp_type, value);
+ const blender::nodes::DataTypeConversions &conversions =
+ blender::nodes::get_implicit_type_conversions();
+ return {conversions.try_convert(std::move(attribute.varray), *type), attribute.domain};
}
-blender::bke::ReadAttributePtr GeometryComponent::attribute_get_constant_for_read_converted(
+std::unique_ptr<blender::bke::GVArray> GeometryComponent::attribute_get_for_read(
+ const StringRef attribute_name,
const AttributeDomain domain,
- const CustomDataType in_data_type,
- const CustomDataType out_data_type,
- const void *value) const
+ const CustomDataType data_type,
+ const void *default_value) const
{
- BLI_assert(this->attribute_domain_supported(domain));
- if (value == nullptr || in_data_type == out_data_type) {
- return this->attribute_get_constant_for_read(domain, out_data_type, value);
+ std::unique_ptr<blender::bke::GVArray> varray = this->attribute_try_get_for_read(
+ attribute_name, domain, data_type);
+ if (varray) {
+ return varray;
+ }
+ const blender::fn::CPPType *type = blender::bke::custom_data_type_to_cpp_type(data_type);
+ if (default_value == nullptr) {
+ default_value = type->default_value();
}
-
- const blender::fn::CPPType *in_cpp_type = blender::bke::custom_data_type_to_cpp_type(
- in_data_type);
- const blender::fn::CPPType *out_cpp_type = blender::bke::custom_data_type_to_cpp_type(
- out_data_type);
- BLI_assert(in_cpp_type != nullptr);
- BLI_assert(out_cpp_type != nullptr);
-
- const blender::nodes::DataTypeConversions &conversions =
- blender::nodes::get_implicit_type_conversions();
- BLI_assert(conversions.is_convertible(*in_cpp_type, *out_cpp_type));
-
- void *out_value = alloca(out_cpp_type->size());
- conversions.convert(*in_cpp_type, *out_cpp_type, value, out_value);
-
const int domain_size = this->attribute_domain_size(domain);
- blender::bke::ReadAttributePtr attribute = std::make_unique<blender::bke::ConstantReadAttribute>(
- domain, domain_size, *out_cpp_type, out_value);
-
- out_cpp_type->destruct(out_value);
- return attribute;
+ return std::make_unique<blender::fn::GVArray_For_SingleValue>(*type, domain_size, default_value);
}
-OutputAttributePtr GeometryComponent::attribute_try_get_for_output(const StringRef attribute_name,
- const AttributeDomain domain,
- const CustomDataType data_type,
- const void *default_value)
-{
- const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type);
- BLI_assert(cpp_type != nullptr);
-
- WriteAttributePtr attribute = this->attribute_try_get_for_write(attribute_name);
+class GVMutableAttribute_For_OutputAttribute
+ : public blender::fn::GVMutableArray_For_GMutableSpan {
+ public:
+ GeometryComponent *component;
+ std::string final_name;
- /* If the attribute doesn't exist, make a new one with the correct type. */
- if (!attribute) {
- this->attribute_try_create(attribute_name, domain, data_type);
- attribute = this->attribute_try_get_for_write(attribute_name);
- if (attribute && default_value != nullptr) {
- void *data = attribute->get_span_for_write_only().data();
- cpp_type->fill_initialized(default_value, data, attribute->size());
- attribute->apply_span();
- }
- return OutputAttributePtr(std::move(attribute));
+ GVMutableAttribute_For_OutputAttribute(GMutableSpan data,
+ GeometryComponent &component,
+ std::string final_name)
+ : blender::fn::GVMutableArray_For_GMutableSpan(data),
+ component(&component),
+ final_name(std::move(final_name))
+ {
}
- /* If an existing attribute has a matching domain and type, just use that. */
- if (attribute->domain() == domain && attribute->cpp_type() == *cpp_type) {
- return OutputAttributePtr(std::move(attribute));
+ ~GVMutableAttribute_For_OutputAttribute() override
+ {
+ type_->destruct_n(data_, size_);
+ MEM_freeN(data_);
}
+};
- /* Otherwise create a temporary buffer to use before saving the new attribute. */
- return OutputAttributePtr(*this, domain, attribute_name, data_type);
-}
-
-/* Construct from an attribute that already exists in the geometry component. */
-OutputAttributePtr::OutputAttributePtr(WriteAttributePtr attribute)
- : attribute_(std::move(attribute))
+static void save_output_attribute(blender::bke::OutputAttribute &output_attribute)
{
-}
+ using namespace blender;
+ using namespace blender::fn;
+ using namespace blender::bke;
-/* Construct a temporary attribute that has to replace an existing one later on. */
-OutputAttributePtr::OutputAttributePtr(GeometryComponent &component,
- AttributeDomain domain,
- std::string final_name,
- CustomDataType data_type)
-{
- const blender::fn::CPPType *cpp_type = blender::bke::custom_data_type_to_cpp_type(data_type);
- BLI_assert(cpp_type != nullptr);
+ GVMutableAttribute_For_OutputAttribute &varray =
+ dynamic_cast<GVMutableAttribute_For_OutputAttribute &>(output_attribute.varray());
- const int domain_size = component.attribute_domain_size(domain);
- void *buffer = MEM_malloc_arrayN(domain_size, cpp_type->size(), __func__);
- GMutableSpan new_span{*cpp_type, buffer, domain_size};
+ GeometryComponent &component = *varray.component;
+ const StringRefNull name = varray.final_name;
+ const AttributeDomain domain = output_attribute.domain();
+ const CustomDataType data_type = output_attribute.custom_data_type();
+ const CPPType &cpp_type = output_attribute.cpp_type();
- /* Copy converted values from conflicting attribute, in case the value is read.
- * TODO: An optimization could be to not do this, when the caller says that the attribute will
- * only be written. */
- ReadAttributePtr src_attribute = component.attribute_get_for_read(
- final_name, domain, data_type, nullptr);
- for (const int i : blender::IndexRange(domain_size)) {
- src_attribute->get(i, new_span[i]);
+ component.attribute_try_delete(name);
+ if (!component.attribute_try_create(
+ varray.final_name, domain, data_type, AttributeInitDefault())) {
+ CLOG_WARN(&LOG,
+ "Could not create the '%s' attribute with type '%s'.",
+ name.c_str(),
+ cpp_type.name().c_str());
+ return;
+ }
+ WriteAttributeLookup write_attribute = component.attribute_try_get_for_write(name);
+ BUFFER_FOR_CPP_TYPE_VALUE(varray.type(), buffer);
+ for (const int i : IndexRange(varray.size())) {
+ varray.get(i, buffer);
+ write_attribute.varray->set_by_relocate(i, buffer);
}
-
- attribute_ = std::make_unique<blender::bke::TemporaryWriteAttribute>(
- domain, new_span, component, std::move(final_name));
}
-/* Store the computed attribute. If it was stored from the beginning already, nothing is done. This
- * might delete another attribute with the same name. */
-void OutputAttributePtr::save()
+static blender::bke::OutputAttribute create_output_attribute(
+ GeometryComponent &component,
+ const blender::StringRef attribute_name,
+ const AttributeDomain domain,
+ const CustomDataType data_type,
+ const bool ignore_old_values,
+ const void *default_value)
{
- if (!attribute_) {
- CLOG_WARN(&LOG, "Trying to save an attribute that does not exist anymore.");
- return;
+ using namespace blender;
+ using namespace blender::fn;
+ using namespace blender::bke;
+
+ if (attribute_name.is_empty()) {
+ return {};
}
- blender::bke::TemporaryWriteAttribute *attribute =
- dynamic_cast<blender::bke::TemporaryWriteAttribute *>(attribute_.get());
+ const CPPType *cpp_type = custom_data_type_to_cpp_type(data_type);
+ BLI_assert(cpp_type != nullptr);
+ const nodes::DataTypeConversions &conversions = nodes::get_implicit_type_conversions();
- if (attribute == nullptr) {
- /* The attribute is saved already. */
- attribute_.reset();
- return;
+ if (component.attribute_is_builtin(attribute_name)) {
+ WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_name);
+ if (!attribute) {
+ if (default_value) {
+ const int64_t domain_size = component.attribute_domain_size(domain);
+ const GVArray_For_SingleValueRef default_varray{*cpp_type, domain_size, default_value};
+ component.attribute_try_create_builtin(attribute_name,
+ AttributeInitVArray(&default_varray));
+ }
+ else {
+ component.attribute_try_create_builtin(attribute_name, AttributeInitDefault());
+ }
+ attribute = component.attribute_try_get_for_write(attribute_name);
+ if (!attribute) {
+ /* Builtin attribute does not exist and can't be created. */
+ return {};
+ }
+ }
+ if (attribute.domain != domain) {
+ /* Builtin attribute is on different domain. */
+ return {};
+ }
+ GVMutableArrayPtr varray = std::move(attribute.varray);
+ if (varray->type() == *cpp_type) {
+ /* Builtin attribute matches exactly. */
+ return OutputAttribute(std::move(varray), domain, {}, ignore_old_values);
+ }
+ /* Builtin attribute is on the same domain but has a different data type. */
+ varray = conversions.try_convert(std::move(varray), *cpp_type);
+ return OutputAttribute(std::move(varray), domain, {}, ignore_old_values);
}
- StringRefNull name = attribute->final_name;
- const blender::fn::CPPType &cpp_type = attribute->cpp_type();
+ const int domain_size = component.attribute_domain_size(domain);
- /* Delete an existing attribute with the same name if necessary. */
- attribute->component.attribute_try_delete(name);
+ WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_name);
+ if (!attribute) {
+ if (default_value) {
+ const GVArray_For_SingleValueRef default_varray{*cpp_type, domain_size, default_value};
+ component.attribute_try_create(
+ attribute_name, domain, data_type, AttributeInitVArray(&default_varray));
+ }
+ else {
+ component.attribute_try_create(attribute_name, domain, data_type, AttributeInitDefault());
+ }
- if (!attribute->component.attribute_try_create(
- name, attribute_->domain(), attribute_->custom_data_type())) {
- /* Cannot create the target attribute for some reason. */
- CLOG_WARN(&LOG,
- "Creating the '%s' attribute with type '%s' failed.",
- name.c_str(),
- cpp_type.name().c_str());
- attribute_.reset();
- return;
+ attribute = component.attribute_try_get_for_write(attribute_name);
+ if (!attribute) {
+ /* Can't create the attribute. */
+ return {};
+ }
+ }
+ if (attribute.domain == domain && attribute.varray->type() == *cpp_type) {
+ /* Existing generic attribute matches exactly. */
+ return OutputAttribute(std::move(attribute.varray), domain, {}, ignore_old_values);
}
- WriteAttributePtr new_attribute = attribute->component.attribute_try_get_for_write(name);
-
- GMutableSpan temp_span = attribute->data;
- GMutableSpan new_span = new_attribute->get_span_for_write_only();
- BLI_assert(temp_span.size() == new_span.size());
-
- /* Currently we copy over the attribute. In the future we want to reuse the buffer. */
- cpp_type.move_to_initialized_n(temp_span.data(), new_span.data(), new_span.size());
- new_attribute->apply_span();
+ /* Allocate a new array that lives next to the existing attribute. It will overwrite the existing
+ * attribute after processing is done. */
+ void *data = MEM_mallocN_aligned(
+ cpp_type->size() * domain_size, cpp_type->alignment(), __func__);
+ if (ignore_old_values) {
+ /* This does nothing for trivially constructible types, but is necessary for correctness. */
+ cpp_type->construct_default_n(data, domain);
+ }
+ else {
+ /* Fill the temporary array with values from the existing attribute. */
+ GVArrayPtr old_varray = component.attribute_get_for_read(
+ attribute_name, domain, data_type, default_value);
+ old_varray->materialize_to_uninitialized(IndexRange(domain_size), data);
+ }
+ GVMutableArrayPtr varray = std::make_unique<GVMutableAttribute_For_OutputAttribute>(
+ GMutableSpan{*cpp_type, data, domain_size}, component, attribute_name);
- attribute_.reset();
+ return OutputAttribute(std::move(varray), domain, save_output_attribute, true);
}
-OutputAttributePtr::~OutputAttributePtr()
+blender::bke::OutputAttribute GeometryComponent::attribute_try_get_for_output(
+ const StringRef attribute_name,
+ const AttributeDomain domain,
+ const CustomDataType data_type,
+ const void *default_value)
{
- if (attribute_) {
- CLOG_ERROR(&LOG, "Forgot to call #save or #apply_span_and_save.");
- }
+ return create_output_attribute(*this, attribute_name, domain, data_type, false, default_value);
}
-/* Utility function to call #apply_span and #save in the right order. */
-void OutputAttributePtr::apply_span_and_save()
+blender::bke::OutputAttribute GeometryComponent::attribute_try_get_for_output_only(
+ const blender::StringRef attribute_name,
+ const AttributeDomain domain,
+ const CustomDataType data_type)
{
- BLI_assert(attribute_);
- attribute_->apply_span();
- this->save();
+ return create_output_attribute(*this, attribute_name, domain, data_type, true, nullptr);
}
-
-/** \} */
diff --git a/source/blender/blenkernel/intern/attribute_access_intern.hh b/source/blender/blenkernel/intern/attribute_access_intern.hh
index 806d10e9e89..b3a795faa30 100644
--- a/source/blender/blenkernel/intern/attribute_access_intern.hh
+++ b/source/blender/blenkernel/intern/attribute_access_intern.hh
@@ -24,166 +24,8 @@
namespace blender::bke {
-class ConstantReadAttribute final : public ReadAttribute {
- private:
- void *value_;
-
- public:
- ConstantReadAttribute(AttributeDomain domain,
- const int64_t size,
- const CPPType &type,
- const void *value)
- : ReadAttribute(domain, type, size)
- {
- value_ = MEM_mallocN_aligned(type.size(), type.alignment(), __func__);
- type.copy_to_uninitialized(value, value_);
- }
-
- ~ConstantReadAttribute() override
- {
- this->cpp_type_.destruct(value_);
- MEM_freeN(value_);
- }
-
- void get_internal(const int64_t UNUSED(index), void *r_value) const override
- {
- this->cpp_type_.copy_to_uninitialized(value_, r_value);
- }
-
- void initialize_span() const override
- {
- const int element_size = cpp_type_.size();
- array_buffer_ = MEM_mallocN_aligned(size_ * element_size, cpp_type_.alignment(), __func__);
- array_is_temporary_ = true;
- cpp_type_.fill_uninitialized(value_, array_buffer_, size_);
- }
-};
-
-template<typename T> class ArrayReadAttribute final : public ReadAttribute {
- private:
- Span<T> data_;
-
- public:
- ArrayReadAttribute(AttributeDomain domain, Span<T> data)
- : ReadAttribute(domain, CPPType::get<T>(), data.size()), data_(data)
- {
- }
-
- void get_internal(const int64_t index, void *r_value) const override
- {
- new (r_value) T(data_[index]);
- }
-
- void initialize_span() const override
- {
- /* The data will not be modified, so this const_cast is fine. */
- array_buffer_ = const_cast<T *>(data_.data());
- array_is_temporary_ = false;
- }
-};
-
-template<typename T> class OwnedArrayReadAttribute final : public ReadAttribute {
- private:
- Array<T> data_;
-
- public:
- OwnedArrayReadAttribute(AttributeDomain domain, Array<T> data)
- : ReadAttribute(domain, CPPType::get<T>(), data.size()), data_(std::move(data))
- {
- }
-
- void get_internal(const int64_t index, void *r_value) const override
- {
- new (r_value) T(data_[index]);
- }
-
- void initialize_span() const override
- {
- /* The data will not be modified, so this const_cast is fine. */
- array_buffer_ = const_cast<T *>(data_.data());
- array_is_temporary_ = false;
- }
-};
-
-template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)>
-class DerivedArrayReadAttribute final : public ReadAttribute {
- private:
- Span<StructT> data_;
-
- public:
- DerivedArrayReadAttribute(AttributeDomain domain, Span<StructT> data)
- : ReadAttribute(domain, CPPType::get<ElemT>(), data.size()), data_(data)
- {
- }
-
- void get_internal(const int64_t index, void *r_value) const override
- {
- const StructT &struct_value = data_[index];
- const ElemT value = GetFunc(struct_value);
- new (r_value) ElemT(value);
- }
-};
-
-template<typename T> class ArrayWriteAttribute final : public WriteAttribute {
- private:
- MutableSpan<T> data_;
-
- public:
- ArrayWriteAttribute(AttributeDomain domain, MutableSpan<T> data)
- : WriteAttribute(domain, CPPType::get<T>(), data.size()), data_(data)
- {
- }
-
- void get_internal(const int64_t index, void *r_value) const override
- {
- new (r_value) T(data_[index]);
- }
-
- void set_internal(const int64_t index, const void *value) override
- {
- data_[index] = *reinterpret_cast<const T *>(value);
- }
-
- void initialize_span(const bool UNUSED(write_only)) override
- {
- array_buffer_ = data_.data();
- array_is_temporary_ = false;
- }
-
- void apply_span_if_necessary() override
- {
- /* Do nothing, because the span contains the attribute itself already. */
- }
-};
-
-template<typename StructT,
- typename ElemT,
- ElemT (*GetFunc)(const StructT &),
- void (*SetFunc)(StructT &, const ElemT &)>
-class DerivedArrayWriteAttribute final : public WriteAttribute {
- private:
- MutableSpan<StructT> data_;
-
- public:
- DerivedArrayWriteAttribute(AttributeDomain domain, MutableSpan<StructT> data)
- : WriteAttribute(domain, CPPType::get<ElemT>(), data.size()), data_(data)
- {
- }
-
- void get_internal(const int64_t index, void *r_value) const override
- {
- const StructT &struct_value = data_[index];
- const ElemT value = GetFunc(struct_value);
- new (r_value) ElemT(value);
- }
-
- void set_internal(const int64_t index, const void *value) override
- {
- StructT &struct_value = data_[index];
- const ElemT &typed_value = *reinterpret_cast<const ElemT *>(value);
- SetFunc(struct_value, typed_value);
- }
-};
+using fn::GVArrayPtr;
+using fn::GVMutableArrayPtr;
/**
* Utility to group together multiple functions that are used to access custom data on geometry
@@ -244,10 +86,11 @@ class BuiltinAttributeProvider {
{
}
- virtual ReadAttributePtr try_get_for_read(const GeometryComponent &component) const = 0;
- virtual WriteAttributePtr try_get_for_write(GeometryComponent &component) const = 0;
+ virtual GVArrayPtr try_get_for_read(const GeometryComponent &component) const = 0;
+ virtual GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const = 0;
virtual bool try_delete(GeometryComponent &component) const = 0;
- virtual bool try_create(GeometryComponent &UNUSED(component)) const = 0;
+ virtual bool try_create(GeometryComponent &UNUSED(component),
+ const AttributeInit &UNUSED(initializer)) const = 0;
virtual bool exists(const GeometryComponent &component) const = 0;
StringRefNull name() const
@@ -272,15 +115,16 @@ class BuiltinAttributeProvider {
*/
class DynamicAttributesProvider {
public:
- virtual ReadAttributePtr try_get_for_read(const GeometryComponent &component,
- const StringRef attribute_name) const = 0;
- virtual WriteAttributePtr try_get_for_write(GeometryComponent &component,
- const StringRef attribute_name) const = 0;
+ virtual ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
+ const StringRef attribute_name) const = 0;
+ virtual WriteAttributeLookup try_get_for_write(GeometryComponent &component,
+ const StringRef attribute_name) const = 0;
virtual bool try_delete(GeometryComponent &component, const StringRef attribute_name) const = 0;
virtual bool try_create(GeometryComponent &UNUSED(component),
const StringRef UNUSED(attribute_name),
const AttributeDomain UNUSED(domain),
- const CustomDataType UNUSED(data_type)) const
+ const CustomDataType UNUSED(data_type),
+ const AttributeInit &UNUSED(initializer)) const
{
/* Some providers should not create new attributes. */
return false;
@@ -309,18 +153,19 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider {
{
}
- ReadAttributePtr try_get_for_read(const GeometryComponent &component,
- const StringRef attribute_name) const final;
+ ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
+ const StringRef attribute_name) const final;
- WriteAttributePtr try_get_for_write(GeometryComponent &component,
- const StringRef attribute_name) const final;
+ WriteAttributeLookup try_get_for_write(GeometryComponent &component,
+ const StringRef attribute_name) const final;
bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final;
bool try_create(GeometryComponent &component,
const StringRef attribute_name,
const AttributeDomain domain,
- const CustomDataType data_type) const final;
+ const CustomDataType data_type,
+ const AttributeInit &initializer) const final;
bool foreach_attribute(const GeometryComponent &component,
const AttributeForeachCallback callback) const final;
@@ -332,18 +177,21 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider {
private:
template<typename T>
- ReadAttributePtr layer_to_read_attribute(const CustomDataLayer &layer,
- const int domain_size) const
+ ReadAttributeLookup layer_to_read_attribute(const CustomDataLayer &layer,
+ const int domain_size) const
{
- return std::make_unique<ArrayReadAttribute<T>>(
- domain_, Span(static_cast<const T *>(layer.data), domain_size));
+ return {std::make_unique<fn::GVArray_For_Span<T>>(
+ Span(static_cast<const T *>(layer.data), domain_size)),
+ domain_};
}
template<typename T>
- WriteAttributePtr layer_to_write_attribute(CustomDataLayer &layer, const int domain_size) const
+ WriteAttributeLookup layer_to_write_attribute(CustomDataLayer &layer,
+ const int domain_size) const
{
- return std::make_unique<ArrayWriteAttribute<T>>(
- domain_, MutableSpan(static_cast<T *>(layer.data), domain_size));
+ return {std::make_unique<fn::GVMutableArray_For_MutableSpan<T>>(
+ MutableSpan(static_cast<T *>(layer.data), domain_size)),
+ domain_};
}
bool type_is_supported(CustomDataType data_type) const
@@ -357,8 +205,8 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider {
*/
class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider {
private:
- using AsReadAttribute = ReadAttributePtr (*)(const void *data, const int domain_size);
- using AsWriteAttribute = WriteAttributePtr (*)(void *data, const int domain_size);
+ using AsReadAttribute = GVArrayPtr (*)(const void *data, const int domain_size);
+ using AsWriteAttribute = GVMutableArrayPtr (*)(void *data, const int domain_size);
const AttributeDomain domain_;
const CustomDataType attribute_type_;
const CustomDataType stored_type_;
@@ -382,10 +230,10 @@ class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider {
{
}
- ReadAttributePtr try_get_for_read(const GeometryComponent &component,
- const StringRef attribute_name) const final;
- WriteAttributePtr try_get_for_write(GeometryComponent &component,
- const StringRef attribute_name) const final;
+ ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
+ const StringRef attribute_name) const final;
+ WriteAttributeLookup try_get_for_write(GeometryComponent &component,
+ const StringRef attribute_name) const final;
bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final;
bool foreach_attribute(const GeometryComponent &component,
const AttributeForeachCallback callback) const final;
@@ -398,8 +246,8 @@ class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider {
* the #MVert struct, but is exposed as float3 attribute.
*/
class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider {
- using AsReadAttribute = ReadAttributePtr (*)(const void *data, const int domain_size);
- using AsWriteAttribute = WriteAttributePtr (*)(void *data, const int domain_size);
+ using AsReadAttribute = GVArrayPtr (*)(const void *data, const int domain_size);
+ using AsWriteAttribute = GVMutableArrayPtr (*)(void *data, const int domain_size);
using UpdateOnRead = void (*)(const GeometryComponent &component);
using UpdateOnWrite = void (*)(GeometryComponent &component);
const CustomDataType stored_type_;
@@ -430,10 +278,10 @@ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider {
{
}
- ReadAttributePtr try_get_for_read(const GeometryComponent &component) const final;
- WriteAttributePtr try_get_for_write(GeometryComponent &component) const final;
+ GVArrayPtr try_get_for_read(const GeometryComponent &component) const final;
+ GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const final;
bool try_delete(GeometryComponent &component) const final;
- bool try_create(GeometryComponent &component) const final;
+ bool try_create(GeometryComponent &component, const AttributeInit &initializer) const final;
bool exists(const GeometryComponent &component) const final;
};
diff --git a/source/blender/blenkernel/intern/attribute_math.cc b/source/blender/blenkernel/intern/attribute_math.cc
index 4ff3a6ceff5..5cdf329effb 100644
--- a/source/blender/blenkernel/intern/attribute_math.cc
+++ b/source/blender/blenkernel/intern/attribute_math.cc
@@ -18,18 +18,21 @@
namespace blender::attribute_math {
-Color4fMixer::Color4fMixer(MutableSpan<Color4f> output_buffer, Color4f default_color)
+ColorGeometryMixer::ColorGeometryMixer(MutableSpan<ColorGeometry4f> output_buffer,
+ ColorGeometry4f default_color)
: buffer_(output_buffer),
default_color_(default_color),
total_weights_(output_buffer.size(), 0.0f)
{
- buffer_.fill(Color4f(0, 0, 0, 0));
+ buffer_.fill(ColorGeometry4f(0.0f, 0.0f, 0.0f, 0.0f));
}
-void Color4fMixer::mix_in(const int64_t index, const Color4f &color, const float weight)
+void ColorGeometryMixer::mix_in(const int64_t index,
+ const ColorGeometry4f &color,
+ const float weight)
{
BLI_assert(weight >= 0.0f);
- Color4f &output_color = buffer_[index];
+ ColorGeometry4f &output_color = buffer_[index];
output_color.r += color.r * weight;
output_color.g += color.g * weight;
output_color.b += color.b * weight;
@@ -37,11 +40,11 @@ void Color4fMixer::mix_in(const int64_t index, const Color4f &color, const float
total_weights_[index] += weight;
}
-void Color4fMixer::finalize()
+void ColorGeometryMixer::finalize()
{
for (const int64_t i : buffer_.index_range()) {
const float weight = total_weights_[i];
- Color4f &output_color = buffer_[i];
+ ColorGeometry4f &output_color = buffer_[i];
if (weight > 0.0f) {
const float weight_inv = 1.0f / weight;
output_color.r *= weight_inv;
diff --git a/source/blender/blenkernel/intern/blender.c b/source/blender/blenkernel/intern/blender.c
index e8879cdda8f..e84b485c466 100644
--- a/source/blender/blenkernel/intern/blender.c
+++ b/source/blender/blenkernel/intern/blender.c
@@ -132,7 +132,7 @@ static void blender_version_init(void)
BLI_snprintf(blender_version_string,
ARRAY_SIZE(blender_version_string),
- "%d.%02d.%d%s",
+ "%d.%01d.%d%s",
BLENDER_VERSION / 100,
BLENDER_VERSION % 100,
BLENDER_VERSION_PATCH,
diff --git a/source/blender/blenkernel/intern/blendfile.c b/source/blender/blenkernel/intern/blendfile.c
index efbf19c7381..54fd3f55c31 100644
--- a/source/blender/blenkernel/intern/blendfile.c
+++ b/source/blender/blenkernel/intern/blendfile.c
@@ -399,7 +399,10 @@ static void setup_app_data(bContext *C,
BKE_lib_override_library_main_resync(
bmain,
curscene,
- bfd->cur_view_layer ? bfd->cur_view_layer : BKE_view_layer_default_view(curscene));
+ bfd->cur_view_layer ? bfd->cur_view_layer : BKE_view_layer_default_view(curscene),
+ reports);
+ /* We need to rebuild some of the deleted override rules (for UI feedback purpose). */
+ BKE_lib_override_library_main_operations_create(bmain, true);
}
}
diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c
index 13ba1957a32..fdf9cf21b85 100644
--- a/source/blender/blenkernel/intern/brush.c
+++ b/source/blender/blenkernel/intern/brush.c
@@ -574,8 +574,8 @@ bool BKE_brush_delete(Main *bmain, Brush *brush)
if (brush->id.tag & LIB_TAG_INDIRECT) {
return false;
}
- if (BKE_library_ID_is_indirectly_used(bmain, brush) && ID_REAL_USERS(brush) <= 1 &&
- ID_EXTRA_USERS(brush) == 0) {
+ if (ID_REAL_USERS(brush) <= 1 && ID_EXTRA_USERS(brush) == 0 &&
+ BKE_library_ID_is_indirectly_used(bmain, brush)) {
return false;
}
@@ -989,6 +989,7 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type)
brush->gpencil_settings->draw_smoothfac = 0.1f;
brush->gpencil_settings->draw_smoothlvl = 1;
brush->gpencil_settings->draw_subdivide = 1;
+ brush->gpencil_settings->dilate_pixels = 1;
brush->gpencil_settings->flag |= GP_BRUSH_FILL_SHOW_EXTENDLINES;
@@ -1399,13 +1400,11 @@ void BKE_brush_gpencil_paint_presets(Main *bmain, ToolSettings *ts, const bool r
}
/* Set default Draw brush. */
- if (reset || brush_prev == NULL) {
- BKE_paint_brush_set(paint, deft_draw);
+ if ((reset == false) && (brush_prev != NULL)) {
+ BKE_paint_brush_set(paint, brush_prev);
}
else {
- if (brush_prev != NULL) {
- BKE_paint_brush_set(paint, brush_prev);
- }
+ BKE_paint_brush_set(paint, deft_draw);
}
}
diff --git a/source/blender/blenkernel/intern/bvhutils.c b/source/blender/blenkernel/intern/bvhutils.c
index 790fb128c7c..bc63e423c09 100644
--- a/source/blender/blenkernel/intern/bvhutils.c
+++ b/source/blender/blenkernel/intern/bvhutils.c
@@ -1555,6 +1555,10 @@ BVHTree *BKE_bvhtree_from_mesh_get(struct BVHTreeFromMesh *data,
bvh_cache_type,
bvh_cache_p,
mesh_eval_mutex);
+
+ if (looptri_mask != NULL) {
+ MEM_freeN(looptri_mask);
+ }
}
else {
/* Setup BVHTreeFromMesh */
diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c
index ac23f9012a7..aa51ee0017e 100644
--- a/source/blender/blenkernel/intern/collection.c
+++ b/source/blender/blenkernel/intern/collection.c
@@ -694,8 +694,7 @@ Collection *BKE_collection_duplicate(Main *bmain,
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);
+ BKE_main_id_newptr_and_tag_clear(bmain);
/* In case root duplicated ID is linked, assume we want to get a local copy of it and duplicate
* all expected linked data. */
if (ID_IS_LINKED(collection)) {
@@ -726,8 +725,7 @@ Collection *BKE_collection_duplicate(Main *bmain,
#endif
/* Cleanup. */
- BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false);
- BKE_main_id_clear_newpoins(bmain);
+ BKE_main_id_newptr_and_tag_clear(bmain);
BKE_main_collection_sync(bmain);
}
@@ -1007,7 +1005,7 @@ Collection *BKE_collection_object_find(Main *bmain,
return NULL;
}
-bool BKE_collection_is_empty(Collection *collection)
+bool BKE_collection_is_empty(const Collection *collection)
{
return BLI_listbase_is_empty(&collection->gobject) &&
BLI_listbase_is_empty(&collection->children);
@@ -1150,6 +1148,8 @@ bool BKE_collection_object_add(Main *bmain, Collection *collection, Object *ob)
BKE_main_collection_sync(bmain);
}
+ DEG_id_tag_update(&collection->id, ID_RECALC_GEOMETRY);
+
return true;
}
@@ -1201,6 +1201,8 @@ bool BKE_collection_object_remove(Main *bmain,
BKE_main_collection_sync(bmain);
}
+ DEG_id_tag_update(&collection->id, ID_RECALC_GEOMETRY);
+
return true;
}
@@ -1302,41 +1304,50 @@ static void collection_missing_parents_remove(Collection *collection)
*
* \note caller must ensure #BKE_main_collection_sync_remap() is called afterwards!
*
- * \param collection: may be \a NULL,
+ * \param parent_collection: The collection owning the pointers that were remapped. May be \a NULL,
+ * in which case whole \a bmain database of collections is checked.
+ * \param child_collection: The collection that was remapped to another pointer. May be \a NULL,
* in which case whole \a bmain database of collections is checked.
*/
-void BKE_collections_child_remove_nulls(Main *bmain, Collection *collection)
+void BKE_collections_child_remove_nulls(Main *bmain,
+ Collection *parent_collection,
+ Collection *child_collection)
{
- if (collection == NULL) {
- /* We need to do the checks in two steps when more than one collection may be involved,
- * otherwise we can miss some cases...
- * Also, master collections are not in bmain, so we also need to loop over scenes.
- */
- for (collection = bmain->collections.first; collection != NULL;
- collection = collection->id.next) {
- collection_null_children_remove(collection);
+ if (child_collection == NULL) {
+ if (parent_collection != NULL) {
+ collection_null_children_remove(parent_collection);
}
- for (Scene *scene = bmain->scenes.first; scene != NULL; scene = scene->id.next) {
- collection_null_children_remove(scene->master_collection);
+ else {
+ /* We need to do the checks in two steps when more than one collection may be involved,
+ * otherwise we can miss some cases...
+ * Also, master collections are not in bmain, so we also need to loop over scenes.
+ */
+ for (child_collection = bmain->collections.first; child_collection != NULL;
+ child_collection = child_collection->id.next) {
+ collection_null_children_remove(child_collection);
+ }
+ for (Scene *scene = bmain->scenes.first; scene != NULL; scene = scene->id.next) {
+ collection_null_children_remove(scene->master_collection);
+ }
}
- for (collection = bmain->collections.first; collection != NULL;
- collection = collection->id.next) {
- collection_missing_parents_remove(collection);
+ for (child_collection = bmain->collections.first; child_collection != NULL;
+ child_collection = child_collection->id.next) {
+ collection_missing_parents_remove(child_collection);
}
for (Scene *scene = bmain->scenes.first; scene != NULL; scene = scene->id.next) {
collection_missing_parents_remove(scene->master_collection);
}
}
else {
- for (CollectionParent *parent = collection->parents.first, *parent_next; parent;
+ for (CollectionParent *parent = child_collection->parents.first, *parent_next; parent;
parent = parent_next) {
parent_next = parent->next;
collection_null_children_remove(parent->collection);
- if (!collection_find_child(parent->collection, collection)) {
- BLI_freelinkN(&collection->parents, parent);
+ if (!collection_find_child(parent->collection, child_collection)) {
+ BLI_freelinkN(&child_collection->parents, parent);
}
}
}
@@ -1412,7 +1423,8 @@ static bool collection_instance_find_recursive(Collection *collection,
}
LISTBASE_FOREACH (CollectionChild *, collection_child, &collection->children) {
- if (collection_instance_find_recursive(collection_child->collection, instance_collection)) {
+ if (collection_child->collection != NULL &&
+ collection_instance_find_recursive(collection_child->collection, instance_collection)) {
return true;
}
}
@@ -1631,6 +1643,13 @@ void BKE_collection_parent_relations_rebuild(Collection *collection)
continue;
}
+ /* Can happen when remapping data partially out-of-Main (during advanced ID management
+ * operations like lib-override resync e.g.). */
+ if ((child->collection->id.tag & (LIB_TAG_NO_MAIN | LIB_TAG_COPIED_ON_WRITE)) != 0) {
+ continue;
+ }
+
+ BLI_assert(collection_find_parent(child->collection, collection) == NULL);
CollectionParent *cparent = MEM_callocN(sizeof(CollectionParent), __func__);
cparent->collection = collection;
BLI_addtail(&child->collection->parents, cparent);
@@ -1639,10 +1658,19 @@ void BKE_collection_parent_relations_rebuild(Collection *collection)
static void collection_parents_rebuild_recursive(Collection *collection)
{
+ /* A same collection may be child of several others, no need to process it more than once. */
+ if ((collection->tag & COLLECTION_TAG_RELATION_REBUILD) == 0) {
+ return;
+ }
+
BKE_collection_parent_relations_rebuild(collection);
collection->tag &= ~COLLECTION_TAG_RELATION_REBUILD;
for (CollectionChild *child = collection->children.first; child != NULL; child = child->next) {
+ /* See comment above in `BKE_collection_parent_relations_rebuild`. */
+ if ((collection->id.tag & (LIB_TAG_NO_MAIN | LIB_TAG_COPIED_ON_WRITE)) != 0) {
+ continue;
+ }
collection_parents_rebuild_recursive(child->collection);
}
}
@@ -1666,6 +1694,8 @@ void BKE_main_collections_parent_relations_rebuild(Main *bmain)
/* This function can be called from readfile.c, when this pointer is not guaranteed to be NULL.
*/
if (scene->master_collection != NULL) {
+ BLI_assert(BLI_listbase_is_empty(&scene->master_collection->parents));
+ scene->master_collection->tag |= COLLECTION_TAG_RELATION_REBUILD;
collection_parents_rebuild_recursive(scene->master_collection);
}
}
@@ -2015,12 +2045,10 @@ static void scene_collections_array(Scene *scene,
BLI_assert(collection != NULL);
scene_collection_callback(collection, scene_collections_count, r_collections_array_len);
- if (*r_collections_array_len == 0) {
- return;
- }
+ BLI_assert(*r_collections_array_len > 0);
- Collection **array = MEM_mallocN(sizeof(Collection *) * (*r_collections_array_len),
- "CollectionArray");
+ Collection **array = MEM_malloc_arrayN(
+ *r_collections_array_len, sizeof(Collection *), "CollectionArray");
*r_collections_array = array;
scene_collection_callback(collection, scene_collections_build_array, &array);
}
@@ -2035,8 +2063,9 @@ void BKE_scene_collections_iterator_begin(BLI_Iterator *iter, void *data_in)
CollectionsIteratorData *data = MEM_callocN(sizeof(CollectionsIteratorData), __func__);
data->scene = scene;
+
+ BLI_ITERATOR_INIT(iter);
iter->data = data;
- iter->valid = true;
scene_collections_array(scene, (Collection ***)&data->array, &data->tot);
BLI_assert(data->tot != 0);
@@ -2078,16 +2107,22 @@ typedef struct SceneObjectsIteratorData {
BLI_Iterator scene_collection_iter;
} SceneObjectsIteratorData;
-void BKE_scene_objects_iterator_begin(BLI_Iterator *iter, void *data_in)
+static void scene_objects_iterator_begin(BLI_Iterator *iter, Scene *scene, GSet *visited_objects)
{
- Scene *scene = data_in;
SceneObjectsIteratorData *data = MEM_callocN(sizeof(SceneObjectsIteratorData), __func__);
+
+ BLI_ITERATOR_INIT(iter);
iter->data = data;
- /* lookup list ot make sure each object is object called once */
- data->visited = BLI_gset_ptr_new(__func__);
+ /* Lookup list to make sure that each object is only processed once. */
+ if (visited_objects != NULL) {
+ data->visited = visited_objects;
+ }
+ else {
+ data->visited = BLI_gset_ptr_new(__func__);
+ }
- /* we wrap the scenecollection iterator here to go over the scene collections */
+ /* We wrap the scenecollection iterator here to go over the scene collections. */
BKE_scene_collections_iterator_begin(&data->scene_collection_iter, scene);
Collection *collection = data->scene_collection_iter.current;
@@ -2096,6 +2131,13 @@ void BKE_scene_objects_iterator_begin(BLI_Iterator *iter, void *data_in)
BKE_scene_objects_iterator_next(iter);
}
+void BKE_scene_objects_iterator_begin(BLI_Iterator *iter, void *data_in)
+{
+ Scene *scene = data_in;
+
+ scene_objects_iterator_begin(iter, scene, NULL);
+}
+
/**
* Ensures we only get each object once, even when included in several collections.
*/
@@ -2149,9 +2191,36 @@ void BKE_scene_objects_iterator_end(BLI_Iterator *iter)
SceneObjectsIteratorData *data = iter->data;
if (data) {
BKE_scene_collections_iterator_end(&data->scene_collection_iter);
- BLI_gset_free(data->visited, NULL);
+ if (data->visited != NULL) {
+ BLI_gset_free(data->visited, NULL);
+ }
MEM_freeN(data);
}
}
+/**
+ * Generate a new GSet (or extend given `objects_gset` if not NULL) with all objects referenced by
+ * all collections of given `scene`.
+ *
+ * \note: This will include objects without a base currently (because they would belong to excluded
+ * collections only e.g.).
+ */
+GSet *BKE_scene_objects_as_gset(Scene *scene, GSet *objects_gset)
+{
+ BLI_Iterator iter;
+ scene_objects_iterator_begin(&iter, scene, objects_gset);
+ while (iter.valid) {
+ BKE_scene_objects_iterator_next(&iter);
+ }
+
+ /* `return_gset` is either given `objects_gset` (if non-NULL), or the GSet allocated by the
+ * iterator. Either way, we want to get it back, and prevent `BKE_scene_objects_iterator_end`
+ * from freeing it. */
+ GSet *return_gset = ((SceneObjectsIteratorData *)iter.data)->visited;
+ ((SceneObjectsIteratorData *)iter.data)->visited = NULL;
+ BKE_scene_objects_iterator_end(&iter);
+
+ return return_gset;
+}
+
/** \} */
diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c
index d04a27adec8..826c79c3764 100644
--- a/source/blender/blenkernel/intern/constraint.c
+++ b/source/blender/blenkernel/intern/constraint.c
@@ -1463,30 +1463,20 @@ static void followpath_get_tarmat(struct Depsgraph *UNUSED(depsgraph),
* currently for paths to work it needs to go through the bevlist/displist system (ton)
*/
- if (ct->tar->runtime.curve_cache && ct->tar->runtime.curve_cache->path &&
- ct->tar->runtime.curve_cache->path->data) {
+ if (ct->tar->runtime.curve_cache && ct->tar->runtime.curve_cache->anim_path_accum_length) {
float quat[4];
if ((data->followflag & FOLLOWPATH_STATIC) == 0) {
/* animated position along curve depending on time */
- Nurb *nu = cu->nurb.first;
curvetime = cu->ctime - data->offset;
/* ctime is now a proper var setting of Curve which gets set by Animato like any other var
* that's animated, but this will only work if it actually is animated...
*
* we divide the curvetime calculated in the previous step by the length of the path,
- * to get a time factor, which then gets clamped to lie within 0.0 - 1.0 range. */
+ * to get a time factor. */
curvetime /= cu->pathlen;
- if (nu && nu->flagu & CU_NURB_CYCLIC) {
- /* If the curve is cyclic, enable looping around if the time is
- * outside the bounds 0..1 */
- if ((curvetime < 0.0f) || (curvetime > 1.0f)) {
- curvetime -= floorf(curvetime);
- }
- }
- else {
- /* The curve is not cyclic, so clamp to the begin/end points. */
+ if (cu->flag & CU_PATH_CLAMP) {
CLAMP(curvetime, 0.0f, 1.0f);
}
}
@@ -1495,13 +1485,13 @@ static void followpath_get_tarmat(struct Depsgraph *UNUSED(depsgraph),
curvetime = data->offset_fac;
}
- if (where_on_path(ct->tar,
- curvetime,
- vec,
- dir,
- (data->followflag & FOLLOWPATH_FOLLOW) ? quat : NULL,
- &radius,
- NULL)) { /* quat_pt is quat or NULL*/
+ if (BKE_where_on_path(ct->tar,
+ curvetime,
+ vec,
+ dir,
+ (data->followflag & FOLLOWPATH_FOLLOW) ? quat : NULL,
+ &radius,
+ NULL)) { /* quat_pt is quat or NULL*/
float totmat[4][4];
unit_m4(totmat);
@@ -1645,10 +1635,28 @@ static void rotlimit_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *UN
float eul[3];
float size[3];
+ /* This constraint is based on euler rotation math, which doesn't work well with shear.
+ * The Y axis is chosen as the main one because constraints are most commonly used on bones.
+ * This also allows using the constraint to simply remove shear. */
+ orthogonalize_m4_stable(cob->matrix, 1, false);
+
+ /* Only do the complex processing if some limits are actually enabled. */
+ if (!(data->flag & (LIMIT_XROT | LIMIT_YROT | LIMIT_ZROT))) {
+ return;
+ }
+
+ /* Select the Euler rotation order, defaulting to the owner value. */
+ short rot_order = cob->rotOrder;
+
+ if (data->euler_order != CONSTRAINT_EULER_AUTO) {
+ rot_order = data->euler_order;
+ }
+
+ /* Decompose the matrix using the specified order. */
copy_v3_v3(loc, cob->matrix[3]);
mat4_to_size(size, cob->matrix);
- mat4_to_eulO(eul, cob->rotOrder, cob->matrix);
+ mat4_to_eulO(eul, rot_order, cob->matrix);
/* constraint data uses radians internally */
@@ -1681,7 +1689,7 @@ static void rotlimit_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *UN
}
}
- loc_eulO_size_to_mat4(cob->matrix, loc, eul, size, cob->rotOrder);
+ loc_eulO_size_to_mat4(cob->matrix, loc, eul, size, rot_order);
}
static bConstraintTypeInfo CTI_ROTLIMIT = {
@@ -2850,7 +2858,7 @@ static void actcon_get_tarmat(struct Depsgraph *depsgraph,
* including rotation order, otherwise this fails. */
pchan = cob->pchan;
- tchan = BKE_pose_channel_verify(&pose, pchan->name);
+ tchan = BKE_pose_channel_ensure(&pose, pchan->name);
tchan->rotmode = pchan->rotmode;
/* evaluate action using workob (it will only set the PoseChannel in question) */
@@ -3784,8 +3792,7 @@ static void clampto_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *tar
BKE_object_minmax(ct->tar, curveMin, curveMax, true);
/* get targetmatrix */
- if (data->tar->runtime.curve_cache && data->tar->runtime.curve_cache->path &&
- data->tar->runtime.curve_cache->path->data) {
+ if (data->tar->runtime.curve_cache && data->tar->runtime.curve_cache->anim_path_accum_length) {
float vec[4], dir[3], totmat[4][4];
float curvetime;
short clamp_axis;
@@ -3869,7 +3876,7 @@ static void clampto_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *tar
}
/* 3. position on curve */
- if (where_on_path(ct->tar, curvetime, vec, dir, NULL, NULL, NULL)) {
+ if (BKE_where_on_path(ct->tar, curvetime, vec, dir, NULL, NULL, NULL)) {
unit_m4(totmat);
copy_v3_v3(totmat[3], vec);
diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c
index cbf7a4483c0..81830f5bb61 100644
--- a/source/blender/blenkernel/intern/context.c
+++ b/source/blender/blenkernel/intern/context.c
@@ -80,7 +80,17 @@ struct bContext {
struct ARegion *menu;
struct wmGizmoGroup *gizmo_group;
struct bContextStore *store;
- const char *operator_poll_msg; /* reason for poll failing */
+
+ /* Operator poll. */
+ /**
+ * Store the reason the poll function fails (static string, not allocated).
+ * For more advanced formatting use `operator_poll_msg_dyn_params`.
+ */
+ const char *operator_poll_msg;
+ /**
+ * Store values to dynamically to create the string (called when a tool-tip is shown).
+ */
+ struct bContextPollMsgDyn_Params operator_poll_msg_dyn_params;
} wm;
/* data context */
@@ -113,11 +123,16 @@ bContext *CTX_copy(const bContext *C)
{
bContext *newC = MEM_dupallocN((void *)C);
+ memset(&newC->wm.operator_poll_msg_dyn_params, 0, sizeof(newC->wm.operator_poll_msg_dyn_params));
+
return newC;
}
void CTX_free(bContext *C)
{
+ /* This may contain a dynamically allocated message, free. */
+ CTX_wm_operator_poll_msg_clear(C);
+
MEM_freeN(C);
}
@@ -1003,13 +1018,45 @@ void CTX_wm_gizmo_group_set(bContext *C, struct wmGizmoGroup *gzgroup)
C->wm.gizmo_group = gzgroup;
}
+void CTX_wm_operator_poll_msg_clear(bContext *C)
+{
+ struct bContextPollMsgDyn_Params *params = &C->wm.operator_poll_msg_dyn_params;
+ if (params->free_fn != NULL) {
+ params->free_fn(C, params->user_data);
+ }
+ params->get_fn = NULL;
+ params->free_fn = NULL;
+ params->user_data = NULL;
+
+ C->wm.operator_poll_msg = NULL;
+}
void CTX_wm_operator_poll_msg_set(bContext *C, const char *msg)
{
+ CTX_wm_operator_poll_msg_clear(C);
+
C->wm.operator_poll_msg = msg;
}
-const char *CTX_wm_operator_poll_msg_get(bContext *C)
+void CTX_wm_operator_poll_msg_set_dynamic(bContext *C,
+ const struct bContextPollMsgDyn_Params *params)
{
+ CTX_wm_operator_poll_msg_clear(C);
+
+ C->wm.operator_poll_msg_dyn_params = *params;
+}
+
+const char *CTX_wm_operator_poll_msg_get(bContext *C, bool *r_free)
+{
+ struct bContextPollMsgDyn_Params *params = &C->wm.operator_poll_msg_dyn_params;
+ if (params->get_fn != NULL) {
+ char *msg = params->get_fn(C, params->user_data);
+ if (msg != NULL) {
+ *r_free = true;
+ }
+ return msg;
+ }
+
+ *r_free = false;
return IFACE_(C->wm.operator_poll_msg);
}
diff --git a/source/blender/blenkernel/intern/cryptomatte.cc b/source/blender/blenkernel/intern/cryptomatte.cc
index bc89fa85cea..1ff0ca92306 100644
--- a/source/blender/blenkernel/intern/cryptomatte.cc
+++ b/source/blender/blenkernel/intern/cryptomatte.cc
@@ -54,7 +54,7 @@ struct CryptomatteSession {
/* Layer names in order of creation. */
blender::Vector<std::string> layer_names;
- CryptomatteSession();
+ CryptomatteSession() = default;
CryptomatteSession(const Main *bmain);
CryptomatteSession(StampData *stamp_data);
CryptomatteSession(const Scene *scene);
@@ -67,10 +67,6 @@ struct CryptomatteSession {
#endif
};
-CryptomatteSession::CryptomatteSession()
-{
-}
-
CryptomatteSession::CryptomatteSession(const Main *bmain)
{
if (!BLI_listbase_is_empty(&bmain->objects)) {
@@ -425,6 +421,9 @@ static bool from_manifest(CryptomatteLayer &layer, blender::StringRefNull manife
}
const int quoted_hash_len = quoted_string_len_(ref);
+ if (quoted_hash_len < 2) {
+ return false;
+ }
const int hash_len = quoted_hash_len - 2;
CryptomatteHash hash = CryptomatteHash::from_hex_encoded(ref.substr(1, hash_len));
ref = ref.drop_prefix(quoted_hash_len);
diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c
index f3551c98f07..65cdb8503a4 100644
--- a/source/blender/blenkernel/intern/curve.c
+++ b/source/blender/blenkernel/intern/curve.c
@@ -414,6 +414,7 @@ void BKE_curve_init(Curve *cu, const short curve_type)
cu->tb[0].w = cu->tb[0].h = 0.0;
}
else if (cu->type == OB_SURF) {
+ cu->flag |= CU_3D;
cu->resolv = 4;
}
cu->bevel_profile = NULL;
@@ -462,24 +463,19 @@ short BKE_curve_type_get(const Curve *cu)
return type;
}
-void BKE_curve_curve_dimension_update(Curve *cu)
+void BKE_curve_dimension_update(Curve *cu)
{
ListBase *nurbs = BKE_curve_nurbs_get(cu);
+ bool is_2d = CU_IS_2D(cu);
- if (cu->flag & CU_3D) {
- LISTBASE_FOREACH (Nurb *, nu, nurbs) {
- nu->flag &= ~CU_2D;
+ LISTBASE_FOREACH (Nurb *, nu, nurbs) {
+ if (is_2d) {
+ BKE_nurb_project_2d(nu);
}
- }
- else {
- LISTBASE_FOREACH (Nurb *, nu, nurbs) {
- nu->flag |= CU_2D;
- BKE_nurb_test_2d(nu);
- /* since the handles are moved they need to be auto-located again */
- if (nu->type == CU_BEZIER) {
- BKE_nurb_handles_calc(nu);
- }
+ /* since the handles are moved they need to be auto-located again */
+ if (nu->type == CU_BEZIER) {
+ BKE_nurb_handles_calc(nu);
}
}
}
@@ -489,7 +485,10 @@ void BKE_curve_type_test(Object *ob)
ob->type = BKE_curve_type_get(ob->data);
if (ob->type == OB_CURVE) {
- BKE_curve_curve_dimension_update((Curve *)ob->data);
+ Curve *cu = ob->data;
+ if (CU_IS_2D(cu)) {
+ BKE_curve_dimension_update(cu);
+ }
}
}
@@ -596,11 +595,11 @@ bool BKE_nurbList_index_get_co(ListBase *nurb, const int index, float r_co[3])
return false;
}
-int BKE_nurbList_verts_count(ListBase *nurb)
+int BKE_nurbList_verts_count(const ListBase *nurb)
{
int tot = 0;
- LISTBASE_FOREACH (Nurb *, nu, nurb) {
+ LISTBASE_FOREACH (const Nurb *, nu, nurb) {
if (nu->bezt) {
tot += 3 * nu->pntsu;
}
@@ -612,7 +611,7 @@ int BKE_nurbList_verts_count(ListBase *nurb)
return tot;
}
-int BKE_nurbList_verts_count_without_handles(ListBase *nurb)
+int BKE_nurbList_verts_count_without_handles(const ListBase *nurb)
{
int tot = 0;
@@ -745,16 +744,12 @@ void BKE_nurbList_duplicate(ListBase *lb1, const ListBase *lb2)
}
}
-void BKE_nurb_test_2d(Nurb *nu)
+void BKE_nurb_project_2d(Nurb *nu)
{
BezTriple *bezt;
BPoint *bp;
int a;
- if ((nu->flag & CU_2D) == 0) {
- return;
- }
-
if (nu->type == CU_BEZIER) {
a = nu->pntsu;
bezt = nu->bezt;
@@ -779,7 +774,7 @@ void BKE_nurb_test_2d(Nurb *nu)
* if use_radius is truth, minmax will take points' radius into account,
* which will make boundbox closer to beveled curve.
*/
-void BKE_nurb_minmax(Nurb *nu, bool use_radius, float min[3], float max[3])
+void BKE_nurb_minmax(const Nurb *nu, bool use_radius, float min[3], float max[3])
{
BezTriple *bezt;
BPoint *bp;
@@ -1928,7 +1923,7 @@ static int cu_isectLL(const float v1[3],
return 0;
}
-static bool bevelinside(BevList *bl1, BevList *bl2)
+static bool bevelinside(const BevList *bl1, const BevList *bl2)
{
/* is bl2 INSIDE bl1 ? with left-right method and "lambda's" */
/* returns '1' if correct hole */
@@ -2052,8 +2047,8 @@ static void calc_bevel_sin_cos(
*r_cosa = x3 / t02;
}
-static void tilt_bezpart(BezTriple *prevbezt,
- BezTriple *bezt,
+static void tilt_bezpart(const BezTriple *prevbezt,
+ const BezTriple *bezt,
Nurb *nu,
float *tilt_array,
float *radius_array,
@@ -2061,7 +2056,7 @@ static void tilt_bezpart(BezTriple *prevbezt,
int resolu,
int stride)
{
- BezTriple *pprev, *next, *last;
+ const BezTriple *pprev, *next, *last;
float fac, dfac, t[4];
int a;
@@ -2712,8 +2707,9 @@ void BKE_curve_bevelList_make(Object *ob, ListBase *nurbs, bool for_render)
continue;
}
- /* check if we will calculate tilt data */
- do_tilt = CU_DO_TILT(cu, nu);
+ /* Tilt, as the rotation angle of curve control points, is only calculated for 3D curves,
+ * (since this transformation affects the 3D space). */
+ do_tilt = (cu->flag & CU_3D) != 0;
/* Normal display uses the radius, better just to calculate them. */
do_radius = CU_DO_RADIUS(cu, nu);
@@ -2756,7 +2752,6 @@ void BKE_curve_bevelList_make(Object *ob, ListBase *nurbs, bool for_render)
bevp->tilt = bp->tilt;
bevp->radius = bp->radius;
bevp->weight = bp->weight;
- bevp->split_tag = true;
bp++;
if (seglen != NULL && len != 0) {
*seglen = len_v3v3(bevp->vec, bp->vec);
@@ -2828,7 +2823,6 @@ void BKE_curve_bevelList_make(Object *ob, ListBase *nurbs, bool for_render)
bevp->tilt = prevbezt->tilt;
bevp->radius = prevbezt->radius;
bevp->weight = prevbezt->weight;
- bevp->split_tag = true;
bevp->dupe_tag = false;
bevp++;
bl->nr++;
@@ -2879,21 +2873,6 @@ void BKE_curve_bevelList_make(Object *ob, ListBase *nurbs, bool for_render)
sizeof(BevPoint));
}
- /* indicate with handlecodes double points */
- if (prevbezt->h1 == prevbezt->h2) {
- if (ELEM(prevbezt->h1, 0, HD_VECT)) {
- bevp->split_tag = true;
- }
- }
- else {
- if (ELEM(prevbezt->h1, 0, HD_VECT)) {
- bevp->split_tag = true;
- }
- else if (ELEM(prevbezt->h2, 0, HD_VECT)) {
- bevp->split_tag = true;
- }
- }
-
/* seglen */
if (seglen != NULL) {
*seglen = 0;
@@ -3142,7 +3121,7 @@ void BKE_curve_bevelList_make(Object *ob, ListBase *nurbs, bool for_render)
}
/* turning direction */
- if ((cu->flag & CU_3D) == 0) {
+ if (CU_IS_2D(cu)) {
sd = sortdata;
for (a = 0; a < poly; a++, sd++) {
if (sd->bl->hole == sd->dir) {
@@ -3162,7 +3141,7 @@ void BKE_curve_bevelList_make(Object *ob, ListBase *nurbs, bool for_render)
}
/* STEP 4: 2D-COSINES or 3D ORIENTATION */
- if ((cu->flag & CU_3D) == 0) {
+ if (CU_IS_2D(cu)) {
/* 2D Curves */
LISTBASE_FOREACH (BevList *, bl, bev) {
if (bl->nr < 2) {
@@ -3213,7 +3192,6 @@ static void calchandleNurb_intern(BezTriple *bezt,
float pt[3];
float dvec_a[3], dvec_b[3];
float len, len_a, len_b;
- float len_ratio;
const float eps = 1e-5;
/* assume normal handle until we check */
@@ -3265,8 +3243,6 @@ static void calchandleNurb_intern(BezTriple *bezt,
len_b = 1.0f;
}
- len_ratio = len_a / len_b;
-
if (ELEM(bezt->h1, HD_AUTO, HD_AUTO_ANIM) || ELEM(bezt->h2, HD_AUTO, HD_AUTO_ANIM)) { /* auto */
float tvec[3];
tvec[0] = dvec_b[0] / len_b + dvec_a[0] / len_a;
@@ -3399,7 +3375,7 @@ static void calchandleNurb_intern(BezTriple *bezt,
len_b = 1.0f;
}
- len_ratio = len_a / len_b;
+ const float len_ratio = len_a / len_b;
if (bezt->f1 & handle_sel_flag) { /* order of calculation */
if (ELEM(bezt->h2, HD_ALIGN, HD_ALIGN_DOUBLESIDE)) { /* aligned */
@@ -4681,12 +4657,12 @@ void BKE_nurb_direction_switch(Nurb *nu)
}
}
-void BKE_curve_nurbs_vert_coords_get(ListBase *lb, float (*vert_coords)[3], int vert_len)
+void BKE_curve_nurbs_vert_coords_get(const ListBase *lb, float (*vert_coords)[3], int vert_len)
{
float *co = vert_coords[0];
- LISTBASE_FOREACH (Nurb *, nu, lb) {
+ LISTBASE_FOREACH (const Nurb *, nu, lb) {
if (nu->type == CU_BEZIER) {
- BezTriple *bezt = nu->bezt;
+ const BezTriple *bezt = nu->bezt;
for (int i = 0; i < nu->pntsu; i++, bezt++) {
copy_v3_v3(co, bezt->vec[0]);
co += 3;
@@ -4697,7 +4673,7 @@ void BKE_curve_nurbs_vert_coords_get(ListBase *lb, float (*vert_coords)[3], int
}
}
else {
- BPoint *bp = nu->bp;
+ const BPoint *bp = nu->bp;
for (int i = 0; i < nu->pntsu * nu->pntsv; i++, bp++) {
copy_v3_v3(co, bp->vec);
co += 3;
@@ -4708,7 +4684,7 @@ void BKE_curve_nurbs_vert_coords_get(ListBase *lb, float (*vert_coords)[3], int
UNUSED_VARS_NDEBUG(vert_len);
}
-float (*BKE_curve_nurbs_vert_coords_alloc(ListBase *lb, int *r_vert_len))[3]
+float (*BKE_curve_nurbs_vert_coords_alloc(const ListBase *lb, int *r_vert_len))[3]
{
const int vert_len = BKE_nurbList_verts_count(lb);
float(*vert_coords)[3] = MEM_malloc_arrayN(vert_len, sizeof(*vert_coords), __func__);
@@ -4747,9 +4723,7 @@ void BKE_curve_nurbs_vert_coords_apply_with_mat4(ListBase *lb,
}
if (constrain_2d) {
- if (nu->flag & CU_2D) {
- BKE_nurb_test_2d(nu);
- }
+ BKE_nurb_project_2d(nu);
}
calchandlesNurb_intern(nu, SELECT, true);
@@ -4785,24 +4759,22 @@ void BKE_curve_nurbs_vert_coords_apply(ListBase *lb,
}
if (constrain_2d) {
- if (nu->flag & CU_2D) {
- BKE_nurb_test_2d(nu);
- }
+ BKE_nurb_project_2d(nu);
}
calchandlesNurb_intern(nu, SELECT, true);
}
}
-float (*BKE_curve_nurbs_key_vert_coords_alloc(ListBase *lb, float *key, int *r_vert_len))[3]
+float (*BKE_curve_nurbs_key_vert_coords_alloc(const ListBase *lb, float *key, int *r_vert_len))[3]
{
int vert_len = BKE_nurbList_verts_count(lb);
float(*cos)[3] = MEM_malloc_arrayN(vert_len, sizeof(*cos), __func__);
float *co = cos[0];
- LISTBASE_FOREACH (Nurb *, nu, lb) {
+ LISTBASE_FOREACH (const Nurb *, nu, lb) {
if (nu->type == CU_BEZIER) {
- BezTriple *bezt = nu->bezt;
+ const BezTriple *bezt = nu->bezt;
for (int i = 0; i < nu->pntsu; i++, bezt++) {
copy_v3_v3(co, &key[0]);
@@ -4815,7 +4787,7 @@ float (*BKE_curve_nurbs_key_vert_coords_alloc(ListBase *lb, float *key, int *r_v
}
}
else {
- BPoint *bp = nu->bp;
+ const BPoint *bp = nu->bp;
for (int i = 0; i < nu->pntsu * nu->pntsv; i++, bp++) {
copy_v3_v3(co, key);
@@ -5233,7 +5205,7 @@ bool BKE_curve_minmax(Curve *cu, bool use_radius, float min[3], float max[3])
use_radius = false;
}
/* Do bounding box based on splines. */
- LISTBASE_FOREACH (Nurb *, nu, nurb_lb) {
+ LISTBASE_FOREACH (const Nurb *, nu, nurb_lb) {
BKE_nurb_minmax(nu, use_radius, min, max);
}
const bool result = (BLI_listbase_is_empty(nurb_lb) == false);
@@ -5429,12 +5401,12 @@ void BKE_curve_material_index_remove(Curve *cu, int index)
}
}
-bool BKE_curve_material_index_used(Curve *cu, int index)
+bool BKE_curve_material_index_used(const Curve *cu, int index)
{
const int curvetype = BKE_curve_type_get(cu);
if (curvetype == OB_FONT) {
- struct CharInfo *info = cu->strinfo;
+ const struct CharInfo *info = cu->strinfo;
for (int i = cu->len_char32 - 1; i >= 0; i--, info++) {
if (info->mat_nr == index) {
return true;
@@ -5442,7 +5414,7 @@ bool BKE_curve_material_index_used(Curve *cu, int index)
}
}
else {
- LISTBASE_FOREACH (Nurb *, nu, &cu->nurb) {
+ LISTBASE_FOREACH (const Nurb *, nu, &cu->nurb) {
if (nu->mat_nr == index) {
return true;
}
diff --git a/source/blender/blenkernel/intern/curve_bevel.c b/source/blender/blenkernel/intern/curve_bevel.c
index 911a98cb607..7f2cdfa59d3 100644
--- a/source/blender/blenkernel/intern/curve_bevel.c
+++ b/source/blender/blenkernel/intern/curve_bevel.c
@@ -56,7 +56,9 @@ static CurveBevelFillType curve_bevel_get_fill_type(const Curve *curve)
return (curve->flag & CU_FRONT) ? FRONT : BACK;
}
-static void bevel_quarter_fill(Curve *curve, float *quarter_coords_x, float *quarter_coords_y)
+static void bevel_quarter_fill(const Curve *curve,
+ float *quarter_coords_x,
+ float *quarter_coords_y)
{
if (curve->bevel_mode == CU_BEV_MODE_ROUND) {
float angle = 0.0f;
@@ -83,7 +85,7 @@ static void bevel_quarter_fill(Curve *curve, float *quarter_coords_x, float *qua
}
}
-static void curve_bevel_make_extrude_and_fill(Curve *cu,
+static void curve_bevel_make_extrude_and_fill(const Curve *cu,
ListBase *disp,
const bool use_extrude,
const CurveBevelFillType fill_type)
@@ -193,7 +195,7 @@ static void curve_bevel_make_extrude_and_fill(Curve *cu,
}
}
-static void curve_bevel_make_full_circle(Curve *cu, ListBase *disp)
+static void curve_bevel_make_full_circle(const Curve *cu, ListBase *disp)
{
const int nr = 4 + 2 * cu->bevresol;
@@ -218,7 +220,7 @@ static void curve_bevel_make_full_circle(Curve *cu, ListBase *disp)
}
}
-static void curve_bevel_make_only_extrude(Curve *cu, ListBase *disp)
+static void curve_bevel_make_only_extrude(const Curve *cu, ListBase *disp)
{
DispList *dl = MEM_callocN(sizeof(DispList), __func__);
dl->verts = MEM_malloc_arrayN(2, sizeof(float[3]), __func__);
@@ -235,7 +237,7 @@ static void curve_bevel_make_only_extrude(Curve *cu, ListBase *disp)
fp[5] = cu->ext1;
}
-static void curve_bevel_make_from_object(Curve *cu, ListBase *disp)
+static void curve_bevel_make_from_object(const Curve *cu, ListBase *disp)
{
if (cu->bevobj == NULL) {
return;
@@ -287,15 +289,13 @@ static void curve_bevel_make_from_object(Curve *cu, ListBase *disp)
}
}
-void BKE_curve_bevel_make(Object *ob, ListBase *disp)
+ListBase BKE_curve_bevel_make(const Curve *curve)
{
- Curve *curve = ob->data;
-
- BLI_listbase_clear(disp);
+ ListBase bevel_shape = {NULL, NULL};
if (curve->bevel_mode == CU_BEV_MODE_OBJECT) {
if (curve->bevobj != NULL) {
- curve_bevel_make_from_object(curve, disp);
+ curve_bevel_make_from_object(curve, &bevel_shape);
}
}
else {
@@ -303,18 +303,20 @@ void BKE_curve_bevel_make(Object *ob, ListBase *disp)
const bool use_bevel = curve->ext2 != 0.0f;
/* Pass. */
if (use_extrude && !use_bevel) {
- curve_bevel_make_only_extrude(curve, disp);
+ curve_bevel_make_only_extrude(curve, &bevel_shape);
}
else if (use_extrude || use_bevel) {
CurveBevelFillType fill_type = curve_bevel_get_fill_type(curve);
if (!use_extrude && fill_type == FULL && curve->bevel_mode == CU_BEV_MODE_ROUND) {
- curve_bevel_make_full_circle(curve, disp);
+ curve_bevel_make_full_circle(curve, &bevel_shape);
}
else {
/* The general case for nonzero extrusion or an incomplete loop. */
- curve_bevel_make_extrude_and_fill(curve, disp, use_extrude, fill_type);
+ curve_bevel_make_extrude_and_fill(curve, &bevel_shape, use_extrude, fill_type);
}
}
}
+
+ return bevel_shape;
}
diff --git a/source/blender/blenkernel/intern/curve_convert.c b/source/blender/blenkernel/intern/curve_convert.c
index 988ddb3bbc0..5bcce9c339e 100644
--- a/source/blender/blenkernel/intern/curve_convert.c
+++ b/source/blender/blenkernel/intern/curve_convert.c
@@ -44,7 +44,7 @@ static Curve *curve_from_font_object(Object *object, Depsgraph *depsgraph)
new_curve->type = OB_CURVE;
new_curve->flag &= ~CU_3D;
- BKE_curve_curve_dimension_update(new_curve);
+ BKE_curve_dimension_update(new_curve);
return new_curve;
}
diff --git a/source/blender/blenkernel/intern/curve_deform.c b/source/blender/blenkernel/intern/curve_deform.c
index 63da7c1dd11..10c6d2213ff 100644
--- a/source/blender/blenkernel/intern/curve_deform.c
+++ b/source/blender/blenkernel/intern/curve_deform.c
@@ -68,75 +68,7 @@ static void init_curve_deform(const Object *ob_curve, const Object *ob_target, C
}
/**
- * This makes sure we can extend for non-cyclic.
- *
- * \return Success.
- */
-static bool where_on_path_deform(const Object *ob_curve,
- float ctime,
- float r_vec[4],
- float r_dir[3],
- float r_quat[4],
- float *r_radius)
-{
- BevList *bl;
- float ctime1;
- int cycl = 0;
-
- /* test for cyclic */
- bl = ob_curve->runtime.curve_cache->bev.first;
- if (!bl->nr) {
- return false;
- }
- if (bl->poly > -1) {
- cycl = 1;
- }
-
- if (cycl == 0) {
- ctime1 = CLAMPIS(ctime, 0.0f, 1.0f);
- }
- else {
- ctime1 = ctime;
- }
-
- /* vec needs 4 items */
- if (where_on_path(ob_curve, ctime1, r_vec, r_dir, r_quat, r_radius, NULL)) {
-
- if (cycl == 0) {
- Path *path = ob_curve->runtime.curve_cache->path;
- float dvec[3];
-
- if (ctime < 0.0f) {
- sub_v3_v3v3(dvec, path->data[1].vec, path->data[0].vec);
- mul_v3_fl(dvec, ctime * (float)path->len);
- add_v3_v3(r_vec, dvec);
- if (r_quat) {
- copy_qt_qt(r_quat, path->data[0].quat);
- }
- if (r_radius) {
- *r_radius = path->data[0].radius;
- }
- }
- else if (ctime > 1.0f) {
- sub_v3_v3v3(dvec, path->data[path->len - 1].vec, path->data[path->len - 2].vec);
- mul_v3_fl(dvec, (ctime - 1.0f) * (float)path->len);
- add_v3_v3(r_vec, dvec);
- if (r_quat) {
- copy_qt_qt(r_quat, path->data[path->len - 1].quat);
- }
- if (r_radius) {
- *r_radius = path->data[path->len - 1].radius;
- }
- /* weight - not used but could be added */
- }
- }
- return true;
- }
- return false;
-}
-
-/**
- * For each point, rotate & translate to curve use path, since it has constant distances.
+ * For each point, rotate & translate to curve.
*
* \param co: local coord, result local too.
* \param r_quat: returns quaternion for rotation,
@@ -155,7 +87,7 @@ static bool calc_curve_deform(
return false;
}
- if (ob_curve->runtime.curve_cache->path == NULL) {
+ if (ob_curve->runtime.curve_cache->anim_path_accum_length == NULL) {
return false; /* happens on append, cyclic dependencies and empty curves */
}
@@ -172,8 +104,10 @@ static bool calc_curve_deform(
}
}
else {
- if (LIKELY(ob_curve->runtime.curve_cache->path->totdist > FLT_EPSILON)) {
- fac = -(co[index] - cd->dmax[index]) / (ob_curve->runtime.curve_cache->path->totdist);
+ CurveCache *cc = ob_curve->runtime.curve_cache;
+ float totdist = BKE_anim_path_get_length(cc);
+ if (LIKELY(totdist > FLT_EPSILON)) {
+ fac = -(co[index] - cd->dmax[index]) / totdist;
}
else {
fac = 0.0f;
@@ -192,8 +126,10 @@ static bool calc_curve_deform(
}
}
else {
- if (LIKELY(ob_curve->runtime.curve_cache->path->totdist > FLT_EPSILON)) {
- fac = +(co[index] - cd->dmin[index]) / (ob_curve->runtime.curve_cache->path->totdist);
+ CurveCache *cc = ob_curve->runtime.curve_cache;
+ float totdist = BKE_anim_path_get_length(cc);
+ if (LIKELY(totdist > FLT_EPSILON)) {
+ fac = +(co[index] - cd->dmin[index]) / totdist;
}
else {
fac = 0.0f;
@@ -201,7 +137,7 @@ static bool calc_curve_deform(
}
}
- if (where_on_path_deform(ob_curve, fac, loc, dir, new_quat, &radius)) { /* returns OK */
+ if (BKE_where_on_path(ob_curve, fac, loc, dir, new_quat, &radius, NULL)) { /* returns OK */
float quat[4], cent[3];
if (cd->no_rot_axis) { /* set by caller */
diff --git a/source/blender/blenkernel/intern/curve_eval.cc b/source/blender/blenkernel/intern/curve_eval.cc
new file mode 100644
index 00000000000..9cafe1124b1
--- /dev/null
+++ b/source/blender/blenkernel/intern/curve_eval.cc
@@ -0,0 +1,272 @@
+/*
+ * 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.
+ */
+
+#include "BLI_array.hh"
+#include "BLI_listbase.h"
+#include "BLI_map.hh"
+#include "BLI_span.hh"
+#include "BLI_string_ref.hh"
+
+#include "DNA_curve_types.h"
+
+#include "BKE_curve.h"
+#include "BKE_spline.hh"
+
+using blender::Array;
+using blender::float3;
+using blender::float4x4;
+using blender::Map;
+using blender::Span;
+using blender::StringRefNull;
+
+blender::Span<SplinePtr> CurveEval::splines() const
+{
+ return splines_;
+}
+
+blender::MutableSpan<SplinePtr> CurveEval::splines()
+{
+ return splines_;
+}
+
+/**
+ * \warning Call #reallocate on the spline's attributes after adding all splines.
+ */
+void CurveEval::add_spline(SplinePtr spline)
+{
+ splines_.append(std::move(spline));
+}
+
+void CurveEval::remove_splines(blender::IndexMask mask)
+{
+ for (int i = mask.size() - 1; i >= 0; i--) {
+ splines_.remove_and_reorder(mask.indices()[i]);
+ }
+}
+
+void CurveEval::translate(const float3 &translation)
+{
+ for (SplinePtr &spline : this->splines()) {
+ spline->translate(translation);
+ spline->mark_cache_invalid();
+ }
+}
+
+void CurveEval::transform(const float4x4 &matrix)
+{
+ for (SplinePtr &spline : this->splines()) {
+ spline->transform(matrix);
+ }
+}
+
+void CurveEval::bounds_min_max(float3 &min, float3 &max, const bool use_evaluated) const
+{
+ for (const SplinePtr &spline : this->splines()) {
+ spline->bounds_min_max(min, max, use_evaluated);
+ }
+}
+
+/**
+ * Return the start indices for each of the curve spline's evaluated points, as if they were part
+ * of a flattened array. This can be used to facilitate parallelism by avoiding the need to
+ * accumulate an offset while doing more complex calculations.
+ *
+ * \note The result array is one longer than the spline count; the last element is the total size.
+ */
+blender::Array<int> CurveEval::control_point_offsets() const
+{
+ Array<int> offsets(splines_.size() + 1);
+ int offset = 0;
+ for (const int i : splines_.index_range()) {
+ offsets[i] = offset;
+ offset += splines_[i]->size();
+ }
+ offsets.last() = offset;
+ return offsets;
+}
+
+/**
+ * Exactly like #control_point_offsets, but uses the number of evaluated points instead.
+ */
+blender::Array<int> CurveEval::evaluated_point_offsets() const
+{
+ Array<int> offsets(splines_.size() + 1);
+ int offset = 0;
+ for (const int i : splines_.index_range()) {
+ offsets[i] = offset;
+ offset += splines_[i]->evaluated_points_size();
+ }
+ offsets.last() = offset;
+ return offsets;
+}
+
+static BezierSpline::HandleType handle_type_from_dna_bezt(const eBezTriple_Handle dna_handle_type)
+{
+ switch (dna_handle_type) {
+ case HD_FREE:
+ return BezierSpline::HandleType::Free;
+ case HD_AUTO:
+ return BezierSpline::HandleType::Auto;
+ case HD_VECT:
+ return BezierSpline::HandleType::Vector;
+ case HD_ALIGN:
+ return BezierSpline::HandleType::Align;
+ case HD_AUTO_ANIM:
+ return BezierSpline::HandleType::Auto;
+ case HD_ALIGN_DOUBLESIDE:
+ return BezierSpline::HandleType::Align;
+ }
+ BLI_assert_unreachable();
+ return BezierSpline::HandleType::Auto;
+}
+
+static Spline::NormalCalculationMode normal_mode_from_dna_curve(const int twist_mode)
+{
+ switch (twist_mode) {
+ case CU_TWIST_Z_UP:
+ return Spline::NormalCalculationMode::ZUp;
+ case CU_TWIST_MINIMUM:
+ return Spline::NormalCalculationMode::Minimum;
+ case CU_TWIST_TANGENT:
+ return Spline::NormalCalculationMode::Tangent;
+ }
+ BLI_assert_unreachable();
+ return Spline::NormalCalculationMode::Minimum;
+}
+
+static NURBSpline::KnotsMode knots_mode_from_dna_nurb(const short flag)
+{
+ switch (flag & (CU_NURB_ENDPOINT | CU_NURB_BEZIER)) {
+ case CU_NURB_ENDPOINT:
+ return NURBSpline::KnotsMode::EndPoint;
+ case CU_NURB_BEZIER:
+ return NURBSpline::KnotsMode::Bezier;
+ default:
+ return NURBSpline::KnotsMode::Normal;
+ }
+
+ BLI_assert_unreachable();
+ return NURBSpline::KnotsMode::Normal;
+}
+
+std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &dna_curve)
+{
+ std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>();
+
+ const ListBase *nurbs = BKE_curve_nurbs_get(&const_cast<Curve &>(dna_curve));
+
+ /* TODO: Optimize by reserving the correct points size. */
+ LISTBASE_FOREACH (const Nurb *, nurb, nurbs) {
+ switch (nurb->type) {
+ case CU_BEZIER: {
+ std::unique_ptr<BezierSpline> spline = std::make_unique<BezierSpline>();
+ spline->set_resolution(nurb->resolu);
+ spline->set_cyclic(nurb->flagu & CU_NURB_CYCLIC);
+
+ for (const BezTriple &bezt : Span(nurb->bezt, nurb->pntsu)) {
+ spline->add_point(bezt.vec[1],
+ handle_type_from_dna_bezt((eBezTriple_Handle)bezt.h1),
+ bezt.vec[0],
+ handle_type_from_dna_bezt((eBezTriple_Handle)bezt.h2),
+ bezt.vec[2],
+ bezt.radius,
+ bezt.tilt);
+ }
+ spline->attributes.reallocate(spline->size());
+ curve->add_spline(std::move(spline));
+ break;
+ }
+ case CU_NURBS: {
+ std::unique_ptr<NURBSpline> spline = std::make_unique<NURBSpline>();
+ spline->set_resolution(nurb->resolu);
+ spline->set_cyclic(nurb->flagu & CU_NURB_CYCLIC);
+ spline->set_order(nurb->orderu);
+ spline->knots_mode = knots_mode_from_dna_nurb(nurb->flagu);
+
+ for (const BPoint &bp : Span(nurb->bp, nurb->pntsu)) {
+ spline->add_point(bp.vec, bp.radius, bp.tilt, bp.vec[3]);
+ }
+ spline->attributes.reallocate(spline->size());
+ curve->add_spline(std::move(spline));
+ break;
+ }
+ case CU_POLY: {
+ std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>();
+ spline->set_cyclic(nurb->flagu & CU_NURB_CYCLIC);
+
+ for (const BPoint &bp : Span(nurb->bp, nurb->pntsu)) {
+ spline->add_point(bp.vec, bp.radius, bp.tilt);
+ }
+ spline->attributes.reallocate(spline->size());
+ curve->add_spline(std::move(spline));
+ break;
+ }
+ default: {
+ BLI_assert_unreachable();
+ break;
+ }
+ }
+ }
+
+ /* Though the curve has no attributes, this is necessary to properly set the custom data size. */
+ curve->attributes.reallocate(curve->splines().size());
+
+ /* Note: Normal mode is stored separately in each spline to facilitate combining splines
+ * from multiple curve objects, where the value may be different. */
+ const Spline::NormalCalculationMode normal_mode = normal_mode_from_dna_curve(
+ dna_curve.twist_mode);
+ for (SplinePtr &spline : curve->splines()) {
+ spline->normal_mode = normal_mode;
+ }
+
+ return curve;
+}
+
+/**
+ * Check the invariants that curve control point attributes should always uphold, necessary
+ * because attributes are stored on splines rather than in a flat array on the curve:
+ * - The same set of attributes exists on every spline.
+ * - Attributes with the same name have the same type on every spline.
+ */
+void CurveEval::assert_valid_point_attributes() const
+{
+#ifdef DEBUG
+ if (splines_.size() == 0) {
+ return;
+ }
+ const int layer_len = splines_.first()->attributes.data.totlayer;
+ Map<StringRefNull, AttributeMetaData> map;
+ for (const SplinePtr &spline : splines_) {
+ BLI_assert(spline->attributes.data.totlayer == layer_len);
+ spline->attributes.foreach_attribute(
+ [&](StringRefNull name, const AttributeMetaData &meta_data) {
+ map.add_or_modify(
+ name,
+ [&](AttributeMetaData *map_data) {
+ /* All unique attribute names should be added on the first spline. */
+ BLI_assert(spline == splines_.first());
+ *map_data = meta_data;
+ },
+ [&](AttributeMetaData *map_data) {
+ /* Attributes on different splines should all have the same type. */
+ BLI_assert(meta_data == *map_data);
+ });
+ return true;
+ },
+ ATTR_DOMAIN_POINT);
+ }
+#endif
+} \ No newline at end of file
diff --git a/source/blender/blenkernel/intern/curveprofile.c b/source/blender/blenkernel/intern/curveprofile.c
index 752e0d4dfcf..00cdc7b3031 100644
--- a/source/blender/blenkernel/intern/curveprofile.c
+++ b/source/blender/blenkernel/intern/curveprofile.c
@@ -1015,7 +1015,6 @@ void BKE_curveprofile_create_samples_even_spacing(CurveProfile *profile,
{
const float total_length = BKE_curveprofile_total_length(profile);
const float segment_length = total_length / n_segments;
- float length_travelled = 0.0f;
float distance_to_next_table_point = curveprofile_distance_to_next_table_point(profile, 0);
float distance_to_previous_table_point = 0.0f;
int i_table = 0;
@@ -1029,7 +1028,6 @@ void BKE_curveprofile_create_samples_even_spacing(CurveProfile *profile,
for (int i = 1; i < n_segments; i++) {
/* Travel over all of the points that fit inside this segment. */
while (distance_to_next_table_point < segment_left) {
- length_travelled += distance_to_next_table_point;
segment_left -= distance_to_next_table_point;
i_table++;
distance_to_next_table_point = curveprofile_distance_to_next_table_point(profile, i_table);
@@ -1057,7 +1055,6 @@ void BKE_curveprofile_create_samples_even_spacing(CurveProfile *profile,
/* We sampled in between this table point and the next, so the next travel step is smaller. */
distance_to_next_table_point -= segment_left;
distance_to_previous_table_point += segment_left;
- length_travelled += segment_left;
segment_left = segment_length;
}
}
diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c
index d8d9675b42b..710dfdaf137 100644
--- a/source/blender/blenkernel/intern/customdata.c
+++ b/source/blender/blenkernel/intern/customdata.c
@@ -1226,8 +1226,6 @@ static void layerInterp_mvert_skin(const void **sources,
int count,
void *dest)
{
- MVertSkin *vs_dst = dest;
-
float radius[3];
zero_v3(radius);
@@ -1239,7 +1237,7 @@ static void layerInterp_mvert_skin(const void **sources,
}
/* Delay writing to the destination in case dest is in sources. */
- vs_dst = dest;
+ MVertSkin *vs_dst = dest;
copy_v3_v3(vs_dst->radius, radius);
vs_dst->flag &= ~MVERT_SKIN_ROOT;
}
diff --git a/source/blender/blenkernel/intern/customdata_file.c b/source/blender/blenkernel/intern/customdata_file.c
index 470c2f2d246..314d5f4ff82 100644
--- a/source/blender/blenkernel/intern/customdata_file.c
+++ b/source/blender/blenkernel/intern/customdata_file.c
@@ -24,13 +24,13 @@
#include "MEM_guardedalloc.h"
+#include "BLI_endian_defines.h"
#include "BLI_endian_switch.h"
#include "BLI_fileops.h"
#include "BLI_string.h"
#include "BLI_utildefines.h"
#include "BKE_customdata_file.h"
-#include "BKE_global.h"
/************************* File Format Definitions ***************************/
@@ -167,7 +167,7 @@ static bool cdf_read_header(CDataFile *cdf)
offset += header->structbytes;
header->structbytes = sizeof(CDataFileHeader);
- if (fseek(f, offset, SEEK_SET) != 0) {
+ if (BLI_fseek(f, offset, SEEK_SET) != 0) {
return false;
}
@@ -201,7 +201,7 @@ static bool cdf_read_header(CDataFile *cdf)
mesh->structbytes = sizeof(CDataFileMeshHeader);
}
- if (fseek(f, offset, SEEK_SET) != 0) {
+ if (BLI_fseek(f, offset, SEEK_SET) != 0) {
return false;
}
@@ -233,7 +233,7 @@ static bool cdf_read_header(CDataFile *cdf)
offset += layer->structbytes;
layer->structbytes = sizeof(CDataFileLayer);
- if (fseek(f, offset, SEEK_SET) != 0) {
+ if (BLI_fseek(f, offset, SEEK_SET) != 0) {
return false;
}
}
@@ -321,7 +321,7 @@ bool cdf_read_layer(CDataFile *cdf, CDataFileLayer *blay)
offset += cdf->layer[a].datasize;
}
- return (fseek(cdf->readf, offset, SEEK_SET) == 0);
+ return (BLI_fseek(cdf->readf, offset, SEEK_SET) == 0);
}
bool cdf_read_data(CDataFile *cdf, unsigned int size, void *data)
diff --git a/source/blender/blenkernel/intern/deform.c b/source/blender/blenkernel/intern/deform.c
index 7832f3cd882..e6ef569d4b9 100644
--- a/source/blender/blenkernel/intern/deform.c
+++ b/source/blender/blenkernel/intern/deform.c
@@ -1130,11 +1130,13 @@ static void vgroups_datatransfer_interp(const CustomDataTransferLayerMap *laymap
MDeformWeight *dw_dst = BKE_defvert_find_index(data_dst, idx_dst);
float weight_src = 0.0f, weight_dst = 0.0f;
+ bool has_dw_sources = false;
if (sources) {
for (i = count; i--;) {
for (j = data_src[i]->totweight; j--;) {
if ((dw_src = &data_src[i]->dw[j])->def_nr == idx_src) {
weight_src += dw_src->weight * weights[i];
+ has_dw_sources = true;
break;
}
}
@@ -1152,7 +1154,14 @@ static void vgroups_datatransfer_interp(const CustomDataTransferLayerMap *laymap
CLAMP(weight_src, 0.0f, 1.0f);
- if (!dw_dst) {
+ /* Do not create a destination MDeformWeight data if we had no sources at all. */
+ if (!has_dw_sources) {
+ BLI_assert(weight_src == 0.0f);
+ if (dw_dst) {
+ dw_dst->weight = weight_src;
+ }
+ }
+ else if (!dw_dst) {
BKE_defvert_add_index_notest(data_dst, idx_dst, weight_src);
}
else {
diff --git a/source/blender/blenkernel/intern/displist.c b/source/blender/blenkernel/intern/displist.cc
index d4581991566..70355f3883d 100644
--- a/source/blender/blenkernel/intern/displist.c
+++ b/source/blender/blenkernel/intern/displist.cc
@@ -21,9 +21,9 @@
* \ingroup bke
*/
-#include <math.h>
-#include <stdio.h>
-#include <string.h>
+#include <cmath>
+#include <cstdio>
+#include <cstring>
#include "MEM_guardedalloc.h"
@@ -34,6 +34,7 @@
#include "DNA_vfont_types.h"
#include "BLI_bitmap.h"
+#include "BLI_index_range.hh"
#include "BLI_linklist.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
@@ -60,9 +61,11 @@
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
+using blender::IndexRange;
+
static void boundbox_displist_object(Object *ob);
-void BKE_displist_elem_free(DispList *dl)
+static void displist_elem_free(DispList *dl)
{
if (dl) {
if (dl->verts) {
@@ -74,9 +77,6 @@ void BKE_displist_elem_free(DispList *dl)
if (dl->index) {
MEM_freeN(dl->index);
}
- if (dl->bevel_split) {
- MEM_freeN(dl->bevel_split);
- }
MEM_freeN(dl);
}
}
@@ -85,26 +85,11 @@ void BKE_displist_free(ListBase *lb)
{
DispList *dl;
- while ((dl = BLI_pophead(lb))) {
- BKE_displist_elem_free(dl);
+ while ((dl = (DispList *)BLI_pophead(lb))) {
+ displist_elem_free(dl);
}
}
-DispList *BKE_displist_find_or_create(ListBase *lb, int type)
-{
- LISTBASE_FOREACH (DispList *, dl, lb) {
- if (dl->type == type) {
- return dl;
- }
- }
-
- DispList *dl = MEM_callocN(sizeof(DispList), "find_disp");
- dl->type = type;
- BLI_addtail(lb, dl);
-
- return dl;
-}
-
DispList *BKE_displist_find(ListBase *lb, int type)
{
LISTBASE_FOREACH (DispList *, dl, lb) {
@@ -113,12 +98,12 @@ DispList *BKE_displist_find(ListBase *lb, int type)
}
}
- return NULL;
+ return nullptr;
}
-bool BKE_displist_has_faces(ListBase *lb)
+bool BKE_displist_has_faces(const ListBase *lb)
{
- LISTBASE_FOREACH (DispList *, dl, lb) {
+ LISTBASE_FOREACH (const DispList *, dl, lb) {
if (ELEM(dl->type, DL_INDEX3, DL_INDEX4, DL_SURF)) {
return true;
}
@@ -127,20 +112,16 @@ bool BKE_displist_has_faces(ListBase *lb)
return false;
}
-void BKE_displist_copy(ListBase *lbn, ListBase *lb)
+void BKE_displist_copy(ListBase *lbn, const ListBase *lb)
{
BKE_displist_free(lbn);
LISTBASE_FOREACH (const DispList *, dl, lb) {
- DispList *dln = MEM_dupallocN(dl);
+ DispList *dln = (DispList *)MEM_dupallocN(dl);
BLI_addtail(lbn, dln);
- dln->verts = MEM_dupallocN(dl->verts);
- dln->nors = MEM_dupallocN(dl->nors);
- dln->index = MEM_dupallocN(dl->index);
-
- if (dl->bevel_split) {
- dln->bevel_split = MEM_dupallocN(dl->bevel_split);
- }
+ dln->verts = (float *)MEM_dupallocN(dl->verts);
+ dln->nors = (float *)MEM_dupallocN(dl->nors);
+ dln->index = (int *)MEM_dupallocN(dl->index);
}
}
@@ -153,8 +134,8 @@ void BKE_displist_normals_add(ListBase *lb)
LISTBASE_FOREACH (DispList *, dl, lb) {
if (dl->type == DL_INDEX3) {
- if (dl->nors == NULL) {
- dl->nors = MEM_callocN(sizeof(float[3]), "dlnors");
+ if (dl->nors == nullptr) {
+ dl->nors = (float *)MEM_callocN(sizeof(float[3]), "dlnors");
if (dl->flag & DL_BACK_CURVE) {
dl->nors[2] = -1.0f;
@@ -165,8 +146,8 @@ void BKE_displist_normals_add(ListBase *lb)
}
}
else if (dl->type == DL_SURF) {
- if (dl->nors == NULL) {
- dl->nors = MEM_callocN(sizeof(float[3]) * dl->nr * dl->parts, "dlnors");
+ if (dl->nors == nullptr) {
+ dl->nors = (float *)MEM_callocN(sizeof(float[3]) * dl->nr * dl->parts, "dlnors");
vdata = dl->verts;
ndata = dl->nors;
@@ -215,9 +196,9 @@ void BKE_displist_normals_add(ListBase *lb)
}
}
-void BKE_displist_count(ListBase *lb, int *totvert, int *totface, int *tottri)
+void BKE_displist_count(const ListBase *lb, int *totvert, int *totface, int *tottri)
{
- LISTBASE_FOREACH (DispList *, dl, lb) {
+ LISTBASE_FOREACH (const DispList *, dl, lb) {
int vert_tot = 0;
int face_tot = 0;
int tri_tot = 0;
@@ -258,7 +239,8 @@ void BKE_displist_count(ListBase *lb, int *totvert, int *totface, int *tottri)
}
}
-bool BKE_displist_surfindex_get(DispList *dl, int a, int *b, int *p1, int *p2, int *p3, int *p4)
+bool BKE_displist_surfindex_get(
+ const DispList *dl, int a, int *b, int *p1, int *p2, int *p3, int *p4)
{
if ((dl->flag & DL_CYCL_V) == 0 && a == (dl->parts) - 1) {
return false;
@@ -344,9 +326,9 @@ static void curve_to_displist(const Curve *cu,
* and resolution > 1. */
const bool use_cyclic_sample = is_cyclic && (samples_len != 2);
- DispList *dl = MEM_callocN(sizeof(DispList), __func__);
+ DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), __func__);
/* Add one to the length because of 'BKE_curve_forward_diff_bezier'. */
- dl->verts = MEM_mallocN(sizeof(float[3]) * (samples_len + 1), "dlverts");
+ dl->verts = (float *)MEM_mallocN(sizeof(float[3]) * (samples_len + 1), "dlverts");
BLI_addtail(r_dispbase, dl);
dl->parts = 1;
dl->nr = samples_len;
@@ -399,8 +381,8 @@ static void curve_to_displist(const Curve *cu,
}
else if (nu->type == CU_NURBS) {
const int len = (resolution * SEGMENTSU(nu));
- DispList *dl = MEM_callocN(sizeof(DispList), __func__);
- dl->verts = MEM_mallocN(len * sizeof(float[3]), "dlverts");
+ DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), __func__);
+ dl->verts = (float *)MEM_mallocN(len * sizeof(float[3]), "dlverts");
BLI_addtail(r_dispbase, dl);
dl->parts = 1;
dl->nr = len;
@@ -408,12 +390,12 @@ static void curve_to_displist(const Curve *cu,
dl->charidx = nu->charidx;
dl->type = is_cyclic ? DL_POLY : DL_SEGM;
- BKE_nurb_makeCurve(nu, dl->verts, NULL, NULL, NULL, resolution, sizeof(float[3]));
+ BKE_nurb_makeCurve(nu, dl->verts, nullptr, nullptr, nullptr, resolution, sizeof(float[3]));
}
else if (nu->type == CU_POLY) {
const int len = nu->pntsu;
- DispList *dl = MEM_callocN(sizeof(DispList), __func__);
- dl->verts = MEM_mallocN(len * sizeof(float[3]), "dlverts");
+ DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), __func__);
+ dl->verts = (float *)MEM_mallocN(len * sizeof(float[3]), "dlverts");
BLI_addtail(r_dispbase, dl);
dl->parts = 1;
dl->nr = len;
@@ -441,7 +423,7 @@ void BKE_displist_fill(const ListBase *dispbase,
const float normal_proj[3],
const bool flip_normal)
{
- if (dispbase == NULL) {
+ if (dispbase == nullptr) {
return;
}
if (BLI_listbase_is_empty(dispbase)) {
@@ -477,14 +459,14 @@ void BKE_displist_fill(const ListBase *dispbase,
sf_ctx.poly_nr++;
/* Make verts and edges. */
- ScanFillVert *sf_vert = NULL;
- ScanFillVert *sf_vert_last = NULL;
- ScanFillVert *sf_vert_new = NULL;
+ ScanFillVert *sf_vert = nullptr;
+ ScanFillVert *sf_vert_last = nullptr;
+ ScanFillVert *sf_vert_new = nullptr;
for (int i = 0; i < dl->nr; i++) {
sf_vert_last = sf_vert;
sf_vert = BLI_scanfill_vert_add(&sf_ctx, &dl->verts[3 * i]);
totvert++;
- if (sf_vert_last == NULL) {
+ if (sf_vert_last == nullptr) {
sf_vert_new = sf_vert;
}
else {
@@ -492,7 +474,7 @@ void BKE_displist_fill(const ListBase *dispbase,
}
}
- if (sf_vert != NULL && sf_vert_new != NULL) {
+ if (sf_vert != nullptr && sf_vert_new != nullptr) {
BLI_scanfill_edge_add(&sf_ctx, sf_vert, sf_vert_new);
}
}
@@ -509,7 +491,7 @@ void BKE_displist_fill(const ListBase *dispbase,
const int triangles_len = BLI_scanfill_calc_ex(&sf_ctx, scanfill_flag, normal_proj);
if (totvert != 0 && triangles_len != 0) {
- DispList *dlnew = MEM_callocN(sizeof(DispList), "filldisplist");
+ DispList *dlnew = (DispList *)MEM_callocN(sizeof(DispList), "filldisplist");
dlnew->type = DL_INDEX3;
dlnew->flag = (dl_flag_accum & (DL_BACK_CURVE | DL_FRONT_CURVE));
dlnew->rt = (dl_rt_accum & CU_SMOOTH);
@@ -517,8 +499,8 @@ void BKE_displist_fill(const ListBase *dispbase,
dlnew->nr = totvert;
dlnew->parts = triangles_len;
- dlnew->index = MEM_mallocN(sizeof(int[3]) * triangles_len, "dlindex");
- dlnew->verts = MEM_mallocN(sizeof(float[3]) * totvert, "dlverts");
+ dlnew->index = (int *)MEM_mallocN(sizeof(int[3]) * triangles_len, "dlindex");
+ dlnew->verts = (float *)MEM_mallocN(sizeof(float[3]) * totvert, "dlverts");
/* vert data */
int i;
@@ -557,16 +539,16 @@ void BKE_displist_fill(const ListBase *dispbase,
static void bevels_to_filledpoly(const Curve *cu, ListBase *dispbase)
{
- ListBase front = {NULL, NULL};
- ListBase back = {NULL, NULL};
+ ListBase front = {nullptr, nullptr};
+ ListBase back = {nullptr, nullptr};
LISTBASE_FOREACH (const DispList *, dl, dispbase) {
if (dl->type == DL_SURF) {
if ((dl->flag & DL_CYCL_V) && (dl->flag & DL_CYCL_U) == 0) {
if ((cu->flag & CU_BACK) && (dl->flag & DL_BACK_CURVE)) {
- DispList *dlnew = MEM_callocN(sizeof(DispList), __func__);
+ DispList *dlnew = (DispList *)MEM_callocN(sizeof(DispList), __func__);
BLI_addtail(&front, dlnew);
- dlnew->verts = MEM_mallocN(sizeof(float[3]) * dl->parts, __func__);
+ dlnew->verts = (float *)MEM_mallocN(sizeof(float[3]) * dl->parts, __func__);
dlnew->nr = dl->parts;
dlnew->parts = 1;
dlnew->type = DL_POLY;
@@ -583,9 +565,9 @@ static void bevels_to_filledpoly(const Curve *cu, ListBase *dispbase)
}
}
if ((cu->flag & CU_FRONT) && (dl->flag & DL_FRONT_CURVE)) {
- DispList *dlnew = MEM_callocN(sizeof(DispList), __func__);
+ DispList *dlnew = (DispList *)MEM_callocN(sizeof(DispList), __func__);
BLI_addtail(&back, dlnew);
- dlnew->verts = MEM_mallocN(sizeof(float[3]) * dl->parts, __func__);
+ dlnew->verts = (float *)MEM_mallocN(sizeof(float[3]) * dl->parts, __func__);
dlnew->nr = dl->parts;
dlnew->parts = 1;
dlnew->type = DL_POLY;
@@ -615,7 +597,7 @@ static void bevels_to_filledpoly(const Curve *cu, ListBase *dispbase)
BKE_displist_fill(dispbase, dispbase, z_up, false);
}
-static void curve_to_filledpoly(Curve *cu, ListBase *UNUSED(nurb), ListBase *dispbase)
+static void curve_to_filledpoly(const Curve *cu, ListBase *dispbase)
{
if (!CU_DO_2DFILL(cu)) {
return;
@@ -635,18 +617,21 @@ static void curve_to_filledpoly(Curve *cu, ListBase *UNUSED(nurb), ListBase *dis
* - first point left, last point right
* - based on subdivided points in original curve, not on points in taper curve (still)
*/
-static float displist_calc_taper(Depsgraph *depsgraph, Scene *scene, Object *taperobj, float fac)
+static float displist_calc_taper(Depsgraph *depsgraph,
+ const Scene *scene,
+ Object *taperobj,
+ float fac)
{
- DispList *dl;
-
- if (taperobj == NULL || taperobj->type != OB_CURVE) {
+ if (taperobj == nullptr || taperobj->type != OB_CURVE) {
return 1.0;
}
- dl = taperobj->runtime.curve_cache ? taperobj->runtime.curve_cache->disp.first : NULL;
- if (dl == NULL) {
+ DispList *dl = taperobj->runtime.curve_cache ?
+ (DispList *)taperobj->runtime.curve_cache->disp.first :
+ nullptr;
+ if (dl == nullptr) {
BKE_displist_make_curveTypes(depsgraph, scene, taperobj, false, false);
- dl = taperobj->runtime.curve_cache->disp.first;
+ dl = (DispList *)taperobj->runtime.curve_cache->disp.first;
}
if (dl) {
float minx, dx, *fp;
@@ -678,9 +663,9 @@ static float displist_calc_taper(Depsgraph *depsgraph, Scene *scene, Object *tap
}
float BKE_displist_calc_taper(
- Depsgraph *depsgraph, Scene *scene, Object *taperobj, int cur, int tot)
+ Depsgraph *depsgraph, const Scene *scene, Object *taperobj, int cur, int tot)
{
- float fac = ((float)cur) / (float)(tot - 1);
+ const float fac = ((float)cur) / (float)(tot - 1);
return displist_calc_taper(depsgraph, scene, taperobj, fac);
}
@@ -696,7 +681,8 @@ void BKE_displist_make_mball(Depsgraph *depsgraph, Scene *scene, Object *ob)
BKE_displist_free(&(ob->runtime.curve_cache->disp));
}
else {
- ob->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for MBall");
+ ob->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache),
+ "CurveCache for MBall");
}
BKE_mball_polygonize(depsgraph, scene, ob, &ob->runtime.curve_cache->disp);
@@ -704,7 +690,7 @@ void BKE_displist_make_mball(Depsgraph *depsgraph, Scene *scene, Object *ob)
object_deform_mball(ob, &ob->runtime.curve_cache->disp);
- /* NOP for MBALLs anyway... */
+ /* No-op for MBALLs anyway... */
boundbox_displist_object(ob);
}
}
@@ -720,30 +706,22 @@ void BKE_displist_make_mball_forRender(Depsgraph *depsgraph,
object_deform_mball(ob, dispbase);
}
-static ModifierData *curve_get_tessellate_point(Scene *scene,
- Object *ob,
+static ModifierData *curve_get_tessellate_point(const Scene *scene,
+ const Object *ob,
const bool for_render,
const bool editmode)
{
VirtualModifierData virtualModifierData;
ModifierData *md = BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData);
- ModifierData *pretessellatePoint;
- int required_mode;
-
- if (for_render) {
- required_mode = eModifierMode_Render;
- }
- else {
- required_mode = eModifierMode_Realtime;
- }
+ ModifierMode required_mode = for_render ? eModifierMode_Render : eModifierMode_Realtime;
if (editmode) {
- required_mode |= eModifierMode_Editmode;
+ required_mode = (ModifierMode)((int)required_mode | eModifierMode_Editmode);
}
- pretessellatePoint = NULL;
+ ModifierData *pretessellatePoint = nullptr;
for (; md; md = md->next) {
- const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type);
+ const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
if (!BKE_modifier_is_enabled(scene, md, required_mode)) {
continue;
@@ -757,8 +735,7 @@ static ModifierData *curve_get_tessellate_point(Scene *scene,
/* this modifiers are moving point of tessellation automatically
* (some of them even can't be applied on tessellated curve), set flag
- * for information button in modifier's header
- */
+ * for information button in modifier's header. */
md->mode |= eModifierMode_ApplyOnSpline;
}
else if (md->mode & eModifierMode_ApplyOnSpline) {
@@ -769,48 +746,39 @@ static ModifierData *curve_get_tessellate_point(Scene *scene,
return pretessellatePoint;
}
-/* Return true if any modifier was applied. */
+/**
+ * \return True if any modifier was applied.
+ */
bool BKE_curve_calc_modifiers_pre(Depsgraph *depsgraph,
- Scene *scene,
+ const Scene *scene,
Object *ob,
ListBase *source_nurb,
ListBase *target_nurb,
const bool for_render)
{
- VirtualModifierData virtualModifierData;
- ModifierData *md = BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData);
- ModifierData *pretessellatePoint;
- Curve *cu = ob->data;
- int numElems = 0, numVerts = 0;
- const bool editmode = (!for_render && (cu->editnurb || cu->editfont));
- ModifierApplyFlag apply_flag = 0;
- float(*deformedVerts)[3] = NULL;
- float *keyVerts = NULL;
- int required_mode;
- bool modified = false;
+ const Curve *cu = (const Curve *)ob->data;
BKE_modifiers_clear_errors(ob);
+ const bool editmode = (!for_render && (cu->editnurb || cu->editfont));
+ ModifierMode required_mode = for_render ? eModifierMode_Render : eModifierMode_Realtime;
if (editmode) {
- apply_flag |= MOD_APPLY_USECACHE;
+ required_mode = (ModifierMode)((int)required_mode | eModifierMode_Editmode);
}
- if (for_render) {
- apply_flag |= MOD_APPLY_RENDER;
- required_mode = eModifierMode_Render;
- }
- else {
- required_mode = eModifierMode_Realtime;
- }
-
- const ModifierEvalContext mectx = {depsgraph, ob, apply_flag};
-
- pretessellatePoint = curve_get_tessellate_point(scene, ob, for_render, editmode);
+ ModifierApplyFlag apply_flag = (ModifierApplyFlag)0;
if (editmode) {
- required_mode |= eModifierMode_Editmode;
+ apply_flag = MOD_APPLY_USECACHE;
+ }
+ if (for_render) {
+ apply_flag = MOD_APPLY_RENDER;
}
+ float *keyVerts = nullptr;
+ float(*deformedVerts)[3] = nullptr;
+ int numVerts = 0;
if (!editmode) {
+ int numElems = 0;
keyVerts = BKE_key_evaluate_object(ob, &numElems);
if (keyVerts) {
@@ -824,9 +792,15 @@ bool BKE_curve_calc_modifiers_pre(Depsgraph *depsgraph,
}
}
+ const ModifierEvalContext mectx = {depsgraph, ob, apply_flag};
+ ModifierData *pretessellatePoint = curve_get_tessellate_point(scene, ob, for_render, editmode);
+ bool modified = false;
+
if (pretessellatePoint) {
- for (; md; md = md->next) {
- const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type);
+ VirtualModifierData virtualModifierData;
+ for (ModifierData *md = BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData); md;
+ md = md->next) {
+ const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
if (!BKE_modifier_is_enabled(scene, md, required_mode)) {
continue;
@@ -839,7 +813,7 @@ bool BKE_curve_calc_modifiers_pre(Depsgraph *depsgraph,
deformedVerts = BKE_curve_nurbs_vert_coords_alloc(source_nurb, &numVerts);
}
- mti->deformVerts(md, &mectx, NULL, deformedVerts, numVerts);
+ mti->deformVerts(md, &mectx, nullptr, deformedVerts, numVerts);
modified = true;
if (md == pretessellatePoint) {
@@ -864,18 +838,16 @@ bool BKE_curve_calc_modifiers_pre(Depsgraph *depsgraph,
static float (*displist_vert_coords_alloc(ListBase *dispbase, int *r_vert_len))[3]
{
- float(*allverts)[3], *fp;
-
*r_vert_len = 0;
LISTBASE_FOREACH (DispList *, dl, dispbase) {
*r_vert_len += (dl->type == DL_INDEX3) ? dl->nr : dl->parts * dl->nr;
}
- allverts = MEM_mallocN(sizeof(float[3]) * (*r_vert_len), "displist_vert_coords_alloc allverts");
- fp = (float *)allverts;
+ float(*allverts)[3] = (float(*)[3])MEM_mallocN(sizeof(float[3]) * (*r_vert_len), __func__);
+ float *fp = (float *)allverts;
LISTBASE_FOREACH (DispList *, dl, dispbase) {
- int ofs = 3 * ((dl->type == DL_INDEX3) ? dl->nr : dl->parts * dl->nr);
+ const int ofs = 3 * ((dl->type == DL_INDEX3) ? dl->nr : dl->parts * dl->nr);
memcpy(fp, dl->verts, sizeof(float) * ofs);
fp += ofs;
}
@@ -883,11 +855,9 @@ static float (*displist_vert_coords_alloc(ListBase *dispbase, int *r_vert_len))[
return allverts;
}
-static void displist_vert_coords_apply(ListBase *dispbase, float (*allverts)[3])
+static void displist_vert_coords_apply(ListBase *dispbase, const float (*allverts)[3])
{
- const float *fp;
-
- fp = (float *)allverts;
+ const float *fp = (float *)allverts;
LISTBASE_FOREACH (DispList *, dl, dispbase) {
int ofs = 3 * ((dl->type == DL_INDEX3) ? dl->nr : dl->parts * dl->nr);
memcpy(dl->verts, fp, sizeof(float) * ofs);
@@ -896,70 +866,62 @@ static void displist_vert_coords_apply(ListBase *dispbase, float (*allverts)[3])
}
static void curve_calc_modifiers_post(Depsgraph *depsgraph,
- Scene *scene,
+ const Scene *scene,
Object *ob,
- ListBase *nurb,
ListBase *dispbase,
- Mesh **r_final,
const bool for_render,
- const bool force_mesh_conversion)
+ const bool force_mesh_conversion,
+ Mesh **r_final)
{
- VirtualModifierData virtualModifierData;
- ModifierData *md = BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData);
- ModifierData *pretessellatePoint;
- Curve *cu = ob->data;
- int required_mode = 0, totvert = 0;
+ const Curve *cu = (const Curve *)ob->data;
+
const bool editmode = (!for_render && (cu->editnurb || cu->editfont));
- Mesh *modified = NULL, *mesh_applied;
- float(*vertCos)[3] = NULL;
- int useCache = !for_render;
- ModifierApplyFlag apply_flag = 0;
+ const bool use_cache = !for_render;
- if (for_render) {
- apply_flag |= MOD_APPLY_RENDER;
- required_mode = eModifierMode_Render;
- }
- else {
- required_mode = eModifierMode_Realtime;
+ ModifierApplyFlag apply_flag = for_render ? MOD_APPLY_RENDER : (ModifierApplyFlag)0;
+ ModifierMode required_mode = for_render ? eModifierMode_Render : eModifierMode_Realtime;
+ if (editmode) {
+ required_mode = (ModifierMode)((int)required_mode | eModifierMode_Editmode);
}
const ModifierEvalContext mectx_deform = {
- depsgraph, ob, editmode ? apply_flag | MOD_APPLY_USECACHE : apply_flag};
+ depsgraph, ob, editmode ? (ModifierApplyFlag)(apply_flag | MOD_APPLY_USECACHE) : apply_flag};
const ModifierEvalContext mectx_apply = {
- depsgraph, ob, useCache ? apply_flag | MOD_APPLY_USECACHE : apply_flag};
-
- pretessellatePoint = curve_get_tessellate_point(scene, ob, for_render, editmode);
+ depsgraph,
+ ob,
+ use_cache ? (ModifierApplyFlag)(apply_flag | MOD_APPLY_USECACHE) : apply_flag};
- if (editmode) {
- required_mode |= eModifierMode_Editmode;
- }
+ ModifierData *pretessellatePoint = curve_get_tessellate_point(scene, ob, for_render, editmode);
- if (pretessellatePoint) {
- md = pretessellatePoint->next;
- }
+ VirtualModifierData virtualModifierData;
+ ModifierData *md = pretessellatePoint == nullptr ?
+ BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData) :
+ pretessellatePoint->next;
if (r_final && *r_final) {
- BKE_id_free(NULL, *r_final);
+ BKE_id_free(nullptr, *r_final);
}
+ Mesh *modified = nullptr;
+ float(*vertCos)[3] = nullptr;
for (; md; md = md->next) {
- const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type);
+ const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
if (!BKE_modifier_is_enabled(scene, md, required_mode)) {
continue;
}
/* If we need normals, no choice, have to convert to mesh now. */
- bool need_normal = mti->dependsOnNormals != NULL && mti->dependsOnNormals(md);
+ const bool need_normal = mti->dependsOnNormals != nullptr && mti->dependsOnNormals(md);
/* XXX 2.8 : now that batch cache is stored inside the ob->data
* we need to create a Mesh for each curve that uses modifiers. */
- if (modified == NULL /* && need_normal */) {
- if (vertCos != NULL) {
+ if (modified == nullptr /* && need_normal */) {
+ if (vertCos != nullptr) {
displist_vert_coords_apply(dispbase, vertCos);
}
if (ELEM(ob->type, OB_CURVE, OB_FONT) && (cu->flag & CU_DEFORM_FILL)) {
- curve_to_filledpoly(cu, nurb, dispbase);
+ curve_to_filledpoly(cu, dispbase);
}
modified = BKE_mesh_new_nomain_from_curve_displist(ob, dispbase);
@@ -968,6 +930,7 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
if (mti->type == eModifierTypeType_OnlyDeform ||
(mti->type == eModifierTypeType_DeformOrConstruct && !modified)) {
if (modified) {
+ int totvert = 0;
if (!vertCos) {
vertCos = BKE_mesh_vert_coords_alloc(modified, &totvert);
}
@@ -977,26 +940,26 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
mti->deformVerts(md, &mectx_deform, modified, vertCos, totvert);
}
else {
+ int totvert = 0;
if (!vertCos) {
vertCos = displist_vert_coords_alloc(dispbase, &totvert);
}
- mti->deformVerts(md, &mectx_deform, NULL, vertCos, totvert);
+ mti->deformVerts(md, &mectx_deform, nullptr, vertCos, totvert);
}
}
else {
if (!r_final) {
- /* makeDisplistCurveTypes could be used for beveling, where derived mesh
+ /* makeDisplistCurveTypes could be used for beveling, where mesh
* is totally unnecessary, so we could stop modifiers applying
- * when we found constructive modifier but derived mesh is unwanted result
- */
+ * when we found constructive modifier but mesh is unwanted. */
break;
}
if (modified) {
if (vertCos) {
Mesh *temp_mesh = (Mesh *)BKE_id_copy_ex(
- NULL, &modified->id, NULL, LIB_ID_COPY_LOCALIZE);
- BKE_id_free(NULL, modified);
+ nullptr, &modified->id, nullptr, LIB_ID_COPY_LOCALIZE);
+ BKE_id_free(nullptr, modified);
modified = temp_mesh;
BKE_mesh_vert_coords_apply(modified, vertCos);
@@ -1008,7 +971,7 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
}
if (ELEM(ob->type, OB_CURVE, OB_FONT) && (cu->flag & CU_DEFORM_FILL)) {
- curve_to_filledpoly(cu, nurb, dispbase);
+ curve_to_filledpoly(cu, dispbase);
}
modified = BKE_mesh_new_nomain_from_curve_displist(ob, dispbase);
@@ -1017,19 +980,17 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
if (vertCos) {
/* Vertex coordinates were applied to necessary data, could free it */
MEM_freeN(vertCos);
- vertCos = NULL;
+ vertCos = nullptr;
}
if (need_normal) {
BKE_mesh_ensure_normals(modified);
}
- mesh_applied = mti->modifyMesh(md, &mectx_apply, modified);
+ Mesh *mesh_applied = mti->modifyMesh(md, &mectx_apply, modified);
if (mesh_applied) {
- /* Modifier returned a new derived mesh */
-
- if (modified && modified != mesh_applied) { /* Modifier */
- BKE_id_free(NULL, modified);
+ if (modified && modified != mesh_applied) {
+ BKE_id_free(nullptr, modified);
}
modified = mesh_applied;
}
@@ -1038,8 +999,9 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
if (vertCos) {
if (modified) {
- Mesh *temp_mesh = (Mesh *)BKE_id_copy_ex(NULL, &modified->id, NULL, LIB_ID_COPY_LOCALIZE);
- BKE_id_free(NULL, modified);
+ Mesh *temp_mesh = (Mesh *)BKE_id_copy_ex(
+ nullptr, &modified->id, nullptr, LIB_ID_COPY_LOCALIZE);
+ BKE_id_free(nullptr, modified);
modified = temp_mesh;
BKE_mesh_vert_coords_apply(modified, vertCos);
@@ -1050,7 +1012,7 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
else {
displist_vert_coords_apply(dispbase, vertCos);
MEM_freeN(vertCos);
- vertCos = NULL;
+ vertCos = nullptr;
}
}
@@ -1078,39 +1040,37 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
BKE_mesh_ensure_normals(modified);
/* Special tweaks, needed since neither BKE_mesh_new_nomain_from_template() nor
- * BKE_mesh_new_nomain_from_curve_displist() properly duplicate mat info...
- */
+ * BKE_mesh_new_nomain_from_curve_displist() properly duplicate mat info... */
BLI_strncpy(modified->id.name, cu->id.name, sizeof(modified->id.name));
*((short *)modified->id.name) = ID_ME;
MEM_SAFE_FREE(modified->mat);
/* Set flag which makes it easier to see what's going on in a debugger. */
modified->id.tag |= LIB_TAG_COPIED_ON_WRITE_EVAL_RESULT;
- modified->mat = MEM_dupallocN(cu->mat);
+ modified->mat = (Material **)MEM_dupallocN(cu->mat);
modified->totcol = cu->totcol;
(*r_final) = modified;
}
else {
- (*r_final) = NULL;
+ (*r_final) = nullptr;
}
}
- else if (modified != NULL) {
+ else if (modified != nullptr) {
/* Pretty stupid to generate that whole mesh if it's unused, yet we have to free it. */
- BKE_id_free(NULL, modified);
+ BKE_id_free(nullptr, modified);
}
}
static void displist_surf_indices(DispList *dl)
{
- int a, b, p1, p2, p3, p4;
- int *index;
+ int b, p1, p2, p3, p4;
dl->totindex = 0;
- index = dl->index = MEM_mallocN(sizeof(int[4]) * (dl->parts + 1) * (dl->nr + 1),
- "index array nurbs");
+ int *index = dl->index = (int *)MEM_mallocN(sizeof(int[4]) * (dl->parts + 1) * (dl->nr + 1),
+ "index array nurbs");
- for (a = 0; a < dl->parts; a++) {
+ for (int a = 0; a < dl->parts; a++) {
if (BKE_displist_surfindex_get(dl, a, &b, &p1, &p2, &p3, &p4) == 0) {
break;
@@ -1132,28 +1092,25 @@ static void displist_surf_indices(DispList *dl)
}
}
-void BKE_displist_make_surf(Depsgraph *depsgraph,
- Scene *scene,
- Object *ob,
- ListBase *dispbase,
- Mesh **r_final,
- const bool for_render,
- const bool for_orco)
+static void displist_make_surf(Depsgraph *depsgraph,
+ const Scene *scene,
+ Object *ob,
+ ListBase *dispbase,
+ Mesh **r_final,
+ const bool for_render,
+ const bool for_orco)
{
- ListBase nubase = {NULL, NULL};
- Curve *cu = ob->data;
- DispList *dl;
- float *data;
- int len;
- bool force_mesh_conversion = false;
+ ListBase nubase = {nullptr, nullptr};
+ const Curve *cu = (const Curve *)ob->data;
if (!for_render && cu->editnurb) {
- BKE_nurbList_duplicate(&nubase, BKE_curve_editNurbs_get(cu));
+ BKE_nurbList_duplicate(&nubase, BKE_curve_editNurbs_get(const_cast<Curve *>(cu)));
}
else {
BKE_nurbList_duplicate(&nubase, &cu->nurb);
}
+ bool force_mesh_conversion = false;
if (!for_orco) {
force_mesh_conversion = BKE_curve_calc_modifiers_pre(
depsgraph, scene, ob, &nubase, &nubase, for_render);
@@ -1164,34 +1121,23 @@ void BKE_displist_make_surf(Depsgraph *depsgraph,
continue;
}
- int resolu = nu->resolu, resolv = nu->resolv;
-
- if (for_render) {
- if (cu->resolu_ren) {
- resolu = cu->resolu_ren;
- }
- if (cu->resolv_ren) {
- resolv = cu->resolv_ren;
- }
- }
+ const int resolu = (for_render && cu->resolu_ren) ? cu->resolu_ren : nu->resolu;
+ const int resolv = (for_render && cu->resolv_ren) ? cu->resolv_ren : nu->resolv;
if (nu->pntsv == 1) {
- len = SEGMENTSU(nu) * resolu;
+ const int len = SEGMENTSU(nu) * resolu;
- dl = MEM_callocN(sizeof(DispList), "makeDispListsurf");
- dl->verts = MEM_mallocN(len * sizeof(float[3]), "dlverts");
+ DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), "makeDispListsurf");
+ dl->verts = (float *)MEM_mallocN(len * sizeof(float[3]), "dlverts");
BLI_addtail(dispbase, dl);
dl->parts = 1;
dl->nr = len;
dl->col = nu->mat_nr;
dl->charidx = nu->charidx;
+ dl->rt = nu->flag;
- /* dl->rt will be used as flag for render face and */
- /* CU_2D conflicts with R_NOPUNOFLIP */
- dl->rt = nu->flag & ~CU_2D;
-
- data = dl->verts;
+ float *data = dl->verts;
if (nu->flagu & CU_NURB_CYCLIC) {
dl->type = DL_POLY;
}
@@ -1199,23 +1145,20 @@ void BKE_displist_make_surf(Depsgraph *depsgraph,
dl->type = DL_SEGM;
}
- BKE_nurb_makeCurve(nu, data, NULL, NULL, NULL, resolu, sizeof(float[3]));
+ BKE_nurb_makeCurve(nu, data, nullptr, nullptr, nullptr, resolu, sizeof(float[3]));
}
else {
- len = (nu->pntsu * resolu) * (nu->pntsv * resolv);
+ const int len = (nu->pntsu * resolu) * (nu->pntsv * resolv);
- dl = MEM_callocN(sizeof(DispList), "makeDispListsurf");
- dl->verts = MEM_mallocN(len * sizeof(float[3]), "dlverts");
+ DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), "makeDispListsurf");
+ dl->verts = (float *)MEM_mallocN(len * sizeof(float[3]), "dlverts");
BLI_addtail(dispbase, dl);
dl->col = nu->mat_nr;
dl->charidx = nu->charidx;
+ dl->rt = nu->flag;
- /* dl->rt will be used as flag for render face and */
- /* CU_2D conflicts with R_NOPUNOFLIP */
- dl->rt = nu->flag & ~CU_2D;
-
- data = dl->verts;
+ float *data = dl->verts;
dl->type = DL_SURF;
dl->parts = (nu->pntsu * resolu); /* in reverse, because makeNurbfaces works that way */
@@ -1237,7 +1180,7 @@ void BKE_displist_make_surf(Depsgraph *depsgraph,
if (!for_orco) {
BKE_nurbList_duplicate(&ob->runtime.curve_cache->deformed_nurbs, &nubase);
curve_calc_modifiers_post(
- depsgraph, scene, ob, &nubase, dispbase, r_final, for_render, force_mesh_conversion);
+ depsgraph, scene, ob, dispbase, for_render, force_mesh_conversion, r_final);
}
BKE_nurbList_free(&nubase);
@@ -1262,7 +1205,7 @@ static void rotateBevelPiece(const Curve *cu,
vec[1] = fp[2];
vec[2] = 0.0;
- if (nbevp == NULL) {
+ if (nbevp == nullptr) {
copy_v3_v3(data, bevp->vec);
copy_qt_qt(quat, bevp->quat);
}
@@ -1280,7 +1223,7 @@ static void rotateBevelPiece(const Curve *cu,
else {
float sina, cosa;
- if (nbevp == NULL) {
+ if (nbevp == nullptr) {
copy_v3_v3(data, bevp->vec);
sina = bevp->sina;
cosa = bevp->cosa;
@@ -1304,12 +1247,13 @@ static void rotateBevelPiece(const Curve *cu,
*r_data = data;
}
-static void fillBevelCap(Nurb *nu, DispList *dlb, float *prev_fp, ListBase *dispbase)
+static void fillBevelCap(const Nurb *nu,
+ const DispList *dlb,
+ const float *prev_fp,
+ ListBase *dispbase)
{
- DispList *dl;
-
- dl = MEM_callocN(sizeof(DispList), "makeDispListbev2");
- dl->verts = MEM_mallocN(sizeof(float[3]) * dlb->nr, "dlverts");
+ DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), "makeDispListbev2");
+ dl->verts = (float *)MEM_mallocN(sizeof(float[3]) * dlb->nr, "dlverts");
memcpy(dl->verts, prev_fp, sizeof(float[3]) * dlb->nr);
dl->type = DL_POLY;
@@ -1318,18 +1262,15 @@ static void fillBevelCap(Nurb *nu, DispList *dlb, float *prev_fp, ListBase *disp
dl->nr = dlb->nr;
dl->col = nu->mat_nr;
dl->charidx = nu->charidx;
-
- /* dl->rt will be used as flag for render face and */
- /* CU_2D conflicts with R_NOPUNOFLIP */
- dl->rt = nu->flag & ~CU_2D;
+ dl->rt = nu->flag;
BLI_addtail(dispbase, dl);
}
static void calc_bevfac_segment_mapping(
- BevList *bl, float bevfac, float spline_length, int *r_bev, float *r_blend)
+ const BevList *bl, float bevfac, float spline_length, int *r_bev, float *r_blend)
{
- float normlen, normsum = 0.0f;
+ float normsum = 0.0f;
float *seglen = bl->seglen;
int *segbevcount = bl->segbevcount;
int bevcount = 0, nr = bl->nr;
@@ -1338,7 +1279,7 @@ static void calc_bevfac_segment_mapping(
*r_bev = (int)bev_fl;
while (bevcount < nr - 1) {
- normlen = *seglen / spline_length;
+ float normlen = *seglen / spline_length;
if (normsum + normlen > bevfac) {
bev_fl = bevcount + (bevfac - normsum) / normlen * *segbevcount;
*r_bev = (int)bev_fl;
@@ -1353,7 +1294,7 @@ static void calc_bevfac_segment_mapping(
}
static void calc_bevfac_spline_mapping(
- BevList *bl, float bevfac, float spline_length, int *r_bev, float *r_blend)
+ const BevList *bl, float bevfac, float spline_length, int *r_bev, float *r_blend)
{
const float len_target = bevfac * spline_length;
BevPoint *bevp = bl->bevpoints;
@@ -1375,7 +1316,7 @@ static void calc_bevfac_spline_mapping(
}
static void calc_bevfac_mapping_default(
- BevList *bl, int *r_start, float *r_firstblend, int *r_steps, float *r_lastblend)
+ const BevList *bl, int *r_start, float *r_firstblend, int *r_steps, float *r_lastblend)
{
*r_start = 0;
*r_steps = bl->nr;
@@ -1383,9 +1324,9 @@ static void calc_bevfac_mapping_default(
*r_lastblend = 1.0f;
}
-static void calc_bevfac_mapping(Curve *cu,
- BevList *bl,
- Nurb *nu,
+static void calc_bevfac_mapping(const Curve *cu,
+ const BevList *bl,
+ const Nurb *nu,
int *r_start,
float *r_firstblend,
int *r_steps,
@@ -1463,14 +1404,14 @@ static void calc_bevfac_mapping(Curve *cu,
}
static void do_makeDispListCurveTypes(Depsgraph *depsgraph,
- Scene *scene,
+ const Scene *scene,
Object *ob,
ListBase *dispbase,
const bool for_render,
const bool for_orco,
Mesh **r_final)
{
- Curve *cu = ob->data;
+ const Curve *cu = (const Curve *)ob->data;
/* we do allow duplis... this is only displist on curve level */
if (!ELEM(ob->type, OB_SURF, OB_CURVE, OB_FONT)) {
@@ -1478,259 +1419,246 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph,
}
if (ob->type == OB_SURF) {
- BKE_displist_make_surf(depsgraph, scene, ob, dispbase, r_final, for_render, for_orco);
- }
- else if (ELEM(ob->type, OB_CURVE, OB_FONT)) {
- ListBase dlbev;
- ListBase nubase = {NULL, NULL};
- bool force_mesh_conversion = false;
-
- BKE_curve_bevelList_free(&ob->runtime.curve_cache->bev);
-
- /* We only re-evaluate path if evaluation is not happening for orco.
- * If the calculation happens for orco, we should never free data which
- * was needed before and only not needed for orco calculation.
- */
- if (!for_orco) {
- if (ob->runtime.curve_cache->path) {
- free_path(ob->runtime.curve_cache->path);
- }
- ob->runtime.curve_cache->path = NULL;
- }
+ displist_make_surf(depsgraph, scene, ob, dispbase, r_final, for_render, for_orco);
+ return;
+ }
- if (ob->type == OB_FONT) {
- BKE_vfont_to_curve_nubase(ob, FO_EDIT, &nubase);
- }
- else {
- BKE_nurbList_duplicate(&nubase, BKE_curve_nurbs_get(cu));
- }
+ ListBase nubase = {nullptr, nullptr};
+ bool force_mesh_conversion = false;
+
+ BKE_curve_bevelList_free(&ob->runtime.curve_cache->bev);
- if (!for_orco) {
- force_mesh_conversion = BKE_curve_calc_modifiers_pre(
- depsgraph, scene, ob, &nubase, &nubase, for_render);
+ /* We only re-evaluate path if evaluation is not happening for orco.
+ * If the calculation happens for orco, we should never free data which
+ * was needed before and only not needed for orco calculation. */
+ if (!for_orco) {
+ if (ob->runtime.curve_cache->anim_path_accum_length) {
+ MEM_freeN((void *)ob->runtime.curve_cache->anim_path_accum_length);
}
+ ob->runtime.curve_cache->anim_path_accum_length = nullptr;
+ }
- BKE_curve_bevelList_make(ob, &nubase, for_render);
+ if (ob->type == OB_FONT) {
+ BKE_vfont_to_curve_nubase(ob, FO_EDIT, &nubase);
+ }
+ else {
+ BKE_nurbList_duplicate(&nubase, BKE_curve_nurbs_get(const_cast<Curve *>(cu)));
+ }
- /* If curve has no bevel will return nothing */
- BKE_curve_bevel_make(ob, &dlbev);
+ if (!for_orco) {
+ force_mesh_conversion = BKE_curve_calc_modifiers_pre(
+ depsgraph, scene, ob, &nubase, &nubase, for_render);
+ }
- /* no bevel or extrude, and no width correction? */
- if (!dlbev.first && cu->width == 1.0f) {
- curve_to_displist(cu, &nubase, for_render, dispbase);
- }
- else {
- const float widfac = cu->width - 1.0f;
- BevList *bl = ob->runtime.curve_cache->bev.first;
- Nurb *nu = nubase.first;
+ BKE_curve_bevelList_make(ob, &nubase, for_render);
- for (; bl && nu; bl = bl->next, nu = nu->next) {
- float *data;
+ /* If curve has no bevel will return nothing */
+ ListBase dlbev = BKE_curve_bevel_make(cu);
- if (bl->nr == 0) { /* blank bevel lists can happen */
- continue;
- }
+ /* no bevel or extrude, and no width correction? */
+ if (BLI_listbase_is_empty(&dlbev) && cu->width == 1.0f) {
+ curve_to_displist(cu, &nubase, for_render, dispbase);
+ }
+ else {
+ const float widfac = cu->width - 1.0f;
- /* exception handling; curve without bevel or extrude, with width correction */
- if (BLI_listbase_is_empty(&dlbev)) {
- DispList *dl = MEM_callocN(sizeof(DispList), "makeDispListbev");
- dl->verts = MEM_mallocN(sizeof(float[3]) * bl->nr, "dlverts");
- BLI_addtail(dispbase, dl);
+ BevList *bl = (BevList *)ob->runtime.curve_cache->bev.first;
+ Nurb *nu = (Nurb *)nubase.first;
+ for (; bl && nu; bl = bl->next, nu = nu->next) {
+ float *data;
- if (bl->poly != -1) {
- dl->type = DL_POLY;
- }
- else {
- dl->type = DL_SEGM;
- dl->flag = (DL_FRONT_CURVE | DL_BACK_CURVE);
- }
+ if (bl->nr == 0) { /* blank bevel lists can happen */
+ continue;
+ }
- dl->parts = 1;
- dl->nr = bl->nr;
- dl->col = nu->mat_nr;
- dl->charidx = nu->charidx;
+ /* exception handling; curve without bevel or extrude, with width correction */
+ if (BLI_listbase_is_empty(&dlbev)) {
+ DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), "makeDispListbev");
+ dl->verts = (float *)MEM_mallocN(sizeof(float[3]) * bl->nr, "dlverts");
+ BLI_addtail(dispbase, dl);
- /* dl->rt will be used as flag for render face and */
- /* CU_2D conflicts with R_NOPUNOFLIP */
- dl->rt = nu->flag & ~CU_2D;
-
- int a = dl->nr;
- BevPoint *bevp = bl->bevpoints;
- data = dl->verts;
- while (a--) {
- data[0] = bevp->vec[0] + widfac * bevp->sina;
- data[1] = bevp->vec[1] + widfac * bevp->cosa;
- data[2] = bevp->vec[2];
- bevp++;
- data += 3;
- }
+ if (bl->poly != -1) {
+ dl->type = DL_POLY;
}
else {
- ListBase bottom_capbase = {NULL, NULL};
- ListBase top_capbase = {NULL, NULL};
- float bottom_no[3] = {0.0f};
- float top_no[3] = {0.0f};
- float first_blend = 0.0f, last_blend = 0.0f;
- int start, steps = 0;
-
- if (nu->flagu & CU_NURB_CYCLIC) {
- calc_bevfac_mapping_default(bl, &start, &first_blend, &steps, &last_blend);
- }
- else {
- if (fabsf(cu->bevfac2 - cu->bevfac1) < FLT_EPSILON) {
- continue;
- }
-
- calc_bevfac_mapping(cu, bl, nu, &start, &first_blend, &steps, &last_blend);
- }
+ dl->type = DL_SEGM;
+ dl->flag = (DL_FRONT_CURVE | DL_BACK_CURVE);
+ }
- LISTBASE_FOREACH (DispList *, dlb, &dlbev) {
- /* for each part of the bevel use a separate displblock */
- DispList *dl = MEM_callocN(sizeof(DispList), "makeDispListbev1");
- dl->verts = data = MEM_mallocN(sizeof(float[3]) * dlb->nr * steps, "dlverts");
- BLI_addtail(dispbase, dl);
+ dl->parts = 1;
+ dl->nr = bl->nr;
+ dl->col = nu->mat_nr;
+ dl->charidx = nu->charidx;
+ dl->rt = nu->flag;
- dl->type = DL_SURF;
+ int a = dl->nr;
+ BevPoint *bevp = bl->bevpoints;
+ data = dl->verts;
+ while (a--) {
+ data[0] = bevp->vec[0] + widfac * bevp->sina;
+ data[1] = bevp->vec[1] + widfac * bevp->cosa;
+ data[2] = bevp->vec[2];
+ bevp++;
+ data += 3;
+ }
+ }
+ else {
+ ListBase bottom_capbase = {nullptr, nullptr};
+ ListBase top_capbase = {nullptr, nullptr};
+ float bottom_no[3] = {0.0f};
+ float top_no[3] = {0.0f};
+ float first_blend = 0.0f, last_blend = 0.0f;
+ int start, steps = 0;
+
+ if (nu->flagu & CU_NURB_CYCLIC) {
+ calc_bevfac_mapping_default(bl, &start, &first_blend, &steps, &last_blend);
+ }
+ else {
+ if (fabsf(cu->bevfac2 - cu->bevfac1) < FLT_EPSILON) {
+ continue;
+ }
- dl->flag = dlb->flag & (DL_FRONT_CURVE | DL_BACK_CURVE);
- if (dlb->type == DL_POLY) {
- dl->flag |= DL_CYCL_U;
- }
- if ((bl->poly >= 0) && (steps > 2)) {
- dl->flag |= DL_CYCL_V;
- }
+ calc_bevfac_mapping(cu, bl, nu, &start, &first_blend, &steps, &last_blend);
+ }
- dl->parts = steps;
- dl->nr = dlb->nr;
- dl->col = nu->mat_nr;
- dl->charidx = nu->charidx;
+ LISTBASE_FOREACH (DispList *, dlb, &dlbev) {
+ /* for each part of the bevel use a separate displblock */
+ DispList *dl = (DispList *)MEM_callocN(sizeof(DispList), "makeDispListbev1");
+ dl->verts = data = (float *)MEM_mallocN(sizeof(float[3]) * dlb->nr * steps, "dlverts");
+ BLI_addtail(dispbase, dl);
- /* dl->rt will be used as flag for render face and */
- /* CU_2D conflicts with R_NOPUNOFLIP */
- dl->rt = nu->flag & ~CU_2D;
+ dl->type = DL_SURF;
- dl->bevel_split = BLI_BITMAP_NEW(steps, "bevel_split");
+ dl->flag = dlb->flag & (DL_FRONT_CURVE | DL_BACK_CURVE);
+ if (dlb->type == DL_POLY) {
+ dl->flag |= DL_CYCL_U;
+ }
+ if ((bl->poly >= 0) && (steps > 2)) {
+ dl->flag |= DL_CYCL_V;
+ }
- /* for each point of poly make a bevel piece */
- BevPoint *bevp_first = bl->bevpoints;
- BevPoint *bevp_last = &bl->bevpoints[bl->nr - 1];
- BevPoint *bevp = &bl->bevpoints[start];
- for (int i = start, a = 0; a < steps; i++, bevp++, a++) {
- float radius_factor = 1.0;
- float *cur_data = data;
+ dl->parts = steps;
+ dl->nr = dlb->nr;
+ dl->col = nu->mat_nr;
+ dl->charidx = nu->charidx;
+ dl->rt = nu->flag;
+
+ /* for each point of poly make a bevel piece */
+ BevPoint *bevp_first = bl->bevpoints;
+ BevPoint *bevp_last = &bl->bevpoints[bl->nr - 1];
+ BevPoint *bevp = &bl->bevpoints[start];
+ for (int i = start, a = 0; a < steps; i++, bevp++, a++) {
+ float radius_factor = 1.0;
+ float *cur_data = data;
+
+ if (cu->taperobj == nullptr) {
+ radius_factor = bevp->radius;
+ }
+ else {
+ float taper_factor;
+ if (cu->flag & CU_MAP_TAPER) {
+ float len = (steps - 3) + first_blend + last_blend;
- if (cu->taperobj == NULL) {
- radius_factor = bevp->radius;
- }
- else {
- float taper_factor;
- if (cu->flag & CU_MAP_TAPER) {
- float len = (steps - 3) + first_blend + last_blend;
-
- if (a == 0) {
- taper_factor = 0.0f;
- }
- else if (a == steps - 1) {
- taper_factor = 1.0f;
- }
- else {
- taper_factor = ((float)a - (1.0f - first_blend)) / len;
- }
+ if (a == 0) {
+ taper_factor = 0.0f;
+ }
+ else if (a == steps - 1) {
+ taper_factor = 1.0f;
}
else {
- float len = bl->nr - 1;
- taper_factor = (float)i / len;
-
- if (a == 0) {
- taper_factor += (1.0f - first_blend) / len;
- }
- else if (a == steps - 1) {
- taper_factor -= (1.0f - last_blend) / len;
- }
+ taper_factor = ((float)a - (1.0f - first_blend)) / len;
}
+ }
+ else {
+ float len = bl->nr - 1;
+ taper_factor = (float)i / len;
- radius_factor = displist_calc_taper(depsgraph, scene, cu->taperobj, taper_factor);
-
- if (cu->taper_radius_mode == CU_TAPER_RADIUS_MULTIPLY) {
- radius_factor *= bevp->radius;
+ if (a == 0) {
+ taper_factor += (1.0f - first_blend) / len;
}
- else if (cu->taper_radius_mode == CU_TAPER_RADIUS_ADD) {
- radius_factor += bevp->radius;
+ else if (a == steps - 1) {
+ taper_factor -= (1.0f - last_blend) / len;
}
}
- if (bevp->split_tag) {
- BLI_BITMAP_ENABLE(dl->bevel_split, a);
- }
+ radius_factor = displist_calc_taper(depsgraph, scene, cu->taperobj, taper_factor);
- /* rotate bevel piece and write in data */
- if ((a == 0) && (bevp != bevp_last)) {
- rotateBevelPiece(
- cu, bevp, bevp + 1, dlb, 1.0f - first_blend, widfac, radius_factor, &data);
+ if (cu->taper_radius_mode == CU_TAPER_RADIUS_MULTIPLY) {
+ radius_factor *= bevp->radius;
}
- else if ((a == steps - 1) && (bevp != bevp_first)) {
- rotateBevelPiece(
- cu, bevp, bevp - 1, dlb, 1.0f - last_blend, widfac, radius_factor, &data);
- }
- else {
- rotateBevelPiece(cu, bevp, NULL, dlb, 0.0f, widfac, radius_factor, &data);
+ else if (cu->taper_radius_mode == CU_TAPER_RADIUS_ADD) {
+ radius_factor += bevp->radius;
}
+ }
- if ((cu->flag & CU_FILL_CAPS) && !(nu->flagu & CU_NURB_CYCLIC)) {
- if (a == 1) {
- fillBevelCap(nu, dlb, cur_data - 3 * dlb->nr, &bottom_capbase);
- copy_v3_v3(bottom_no, bevp->dir);
- }
- if (a == steps - 1) {
- fillBevelCap(nu, dlb, cur_data, &top_capbase);
- negate_v3_v3(top_no, bevp->dir);
- }
- }
+ /* rotate bevel piece and write in data */
+ if ((a == 0) && (bevp != bevp_last)) {
+ rotateBevelPiece(
+ cu, bevp, bevp + 1, dlb, 1.0f - first_blend, widfac, radius_factor, &data);
+ }
+ else if ((a == steps - 1) && (bevp != bevp_first)) {
+ rotateBevelPiece(
+ cu, bevp, bevp - 1, dlb, 1.0f - last_blend, widfac, radius_factor, &data);
+ }
+ else {
+ rotateBevelPiece(cu, bevp, nullptr, dlb, 0.0f, widfac, radius_factor, &data);
}
- /* gl array drawing: using indices */
- displist_surf_indices(dl);
+ if ((cu->flag & CU_FILL_CAPS) && !(nu->flagu & CU_NURB_CYCLIC)) {
+ if (a == 1) {
+ fillBevelCap(nu, dlb, cur_data - 3 * dlb->nr, &bottom_capbase);
+ copy_v3_v3(bottom_no, bevp->dir);
+ }
+ if (a == steps - 1) {
+ fillBevelCap(nu, dlb, cur_data, &top_capbase);
+ negate_v3_v3(top_no, bevp->dir);
+ }
+ }
}
- if (bottom_capbase.first) {
- BKE_displist_fill(&bottom_capbase, dispbase, bottom_no, false);
- BKE_displist_fill(&top_capbase, dispbase, top_no, false);
- BKE_displist_free(&bottom_capbase);
- BKE_displist_free(&top_capbase);
- }
+ /* gl array drawing: using indices */
+ displist_surf_indices(dl);
}
- }
- BKE_displist_free(&dlbev);
- }
- if (!(cu->flag & CU_DEFORM_FILL)) {
- curve_to_filledpoly(cu, &nubase, dispbase);
+ if (bottom_capbase.first) {
+ BKE_displist_fill(&bottom_capbase, dispbase, bottom_no, false);
+ BKE_displist_fill(&top_capbase, dispbase, top_no, false);
+ BKE_displist_free(&bottom_capbase);
+ BKE_displist_free(&top_capbase);
+ }
+ }
}
+ BKE_displist_free(&dlbev);
+ }
- if (!for_orco) {
- if ((cu->flag & CU_PATH) ||
- DEG_get_eval_flags_for_id(depsgraph, &ob->id) & DAG_EVAL_NEED_CURVE_PATH) {
- calc_curvepath(ob, &nubase);
- }
+ if (!(cu->flag & CU_DEFORM_FILL)) {
+ curve_to_filledpoly(cu, dispbase);
+ }
- BKE_nurbList_duplicate(&ob->runtime.curve_cache->deformed_nurbs, &nubase);
- curve_calc_modifiers_post(
- depsgraph, scene, ob, &nubase, dispbase, r_final, for_render, force_mesh_conversion);
+ if (!for_orco) {
+ if ((cu->flag & CU_PATH) ||
+ DEG_get_eval_flags_for_id(depsgraph, &ob->id) & DAG_EVAL_NEED_CURVE_PATH) {
+ BKE_anim_path_calc_data(ob);
}
- if (cu->flag & CU_DEFORM_FILL && !ob->runtime.data_eval) {
- curve_to_filledpoly(cu, &nubase, dispbase);
- }
+ BKE_nurbList_duplicate(&ob->runtime.curve_cache->deformed_nurbs, &nubase);
+ curve_calc_modifiers_post(
+ depsgraph, scene, ob, dispbase, for_render, force_mesh_conversion, r_final);
+ }
- BKE_nurbList_free(&nubase);
+ if (cu->flag & CU_DEFORM_FILL && !ob->runtime.data_eval) {
+ curve_to_filledpoly(cu, dispbase);
}
+
+ BKE_nurbList_free(&nubase);
}
-void BKE_displist_make_curveTypes(
- Depsgraph *depsgraph, Scene *scene, Object *ob, const bool for_render, const bool for_orco)
+void BKE_displist_make_curveTypes(Depsgraph *depsgraph,
+ const Scene *scene,
+ Object *ob,
+ const bool for_render,
+ const bool for_orco)
{
- ListBase *dispbase;
-
/* The same check for duplis as in do_makeDispListCurveTypes.
* Happens when curve used for constraint/bevel was converted to mesh.
* check there is still needed for render displist and orco displists. */
@@ -1741,15 +1669,16 @@ void BKE_displist_make_curveTypes(
BKE_object_free_derived_caches(ob);
if (!ob->runtime.curve_cache) {
- ob->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for curve types");
+ ob->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache),
+ "CurveCache for curve types");
}
- dispbase = &(ob->runtime.curve_cache->disp);
+ ListBase *dispbase = &(ob->runtime.curve_cache->disp);
- Mesh *mesh_eval = NULL;
+ Mesh *mesh_eval = nullptr;
do_makeDispListCurveTypes(depsgraph, scene, ob, dispbase, for_render, for_orco, &mesh_eval);
- if (mesh_eval != NULL) {
+ if (mesh_eval != nullptr) {
BKE_object_eval_assign_data(ob, &mesh_eval->id, true);
}
@@ -1757,32 +1686,32 @@ void BKE_displist_make_curveTypes(
}
void BKE_displist_make_curveTypes_forRender(Depsgraph *depsgraph,
- Scene *scene,
+ const Scene *scene,
Object *ob,
ListBase *dispbase,
- Mesh **r_final,
- const bool for_orco)
+ const bool for_orco,
+ Mesh **r_final)
{
- if (ob->runtime.curve_cache == NULL) {
- ob->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for Curve");
+ if (ob->runtime.curve_cache == nullptr) {
+ ob->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache),
+ "CurveCache for Curve");
}
do_makeDispListCurveTypes(depsgraph, scene, ob, dispbase, true, for_orco, r_final);
}
-void BKE_displist_minmax(ListBase *dispbase, float min[3], float max[3])
+void BKE_displist_minmax(const ListBase *dispbase, float min[3], float max[3])
{
- const float *vert;
- int a, tot = 0;
- int doit = 0;
+ bool doit = false;
- LISTBASE_FOREACH (DispList *, dl, dispbase) {
- tot = (dl->type == DL_INDEX3) ? dl->nr : dl->nr * dl->parts;
- vert = dl->verts;
- for (a = 0; a < tot; a++, vert += 3) {
- minmax_v3v3_v3(min, max, vert);
+ LISTBASE_FOREACH (const DispList *, dl, dispbase) {
+ const int tot = (dl->type == DL_INDEX3) ? dl->nr : dl->nr * dl->parts;
+ for (const int i : IndexRange(tot)) {
+ minmax_v3v3_v3(min, max, &dl->verts[i]);
+ }
+ if (tot != 0) {
+ doit = true;
}
- doit |= (tot != 0);
}
if (!doit) {
@@ -1797,12 +1726,11 @@ static void boundbox_displist_object(Object *ob)
{
if (ELEM(ob->type, OB_CURVE, OB_SURF, OB_FONT)) {
/* Curve's BB is already calculated as a part of modifier stack,
- * here we only calculate object BB based on final display list.
- */
+ * here we only calculate object BB based on final display list. */
/* object's BB is calculated from final displist */
- if (ob->runtime.bb == NULL) {
- ob->runtime.bb = MEM_callocN(sizeof(BoundBox), "boundbox");
+ if (ob->runtime.bb == nullptr) {
+ ob->runtime.bb = (BoundBox *)MEM_callocN(sizeof(BoundBox), "boundbox");
}
Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob);
diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c
index 4a25b0e9d98..42af3a391ed 100644
--- a/source/blender/blenkernel/intern/dynamicpaint.c
+++ b/source/blender/blenkernel/intern/dynamicpaint.c
@@ -1702,7 +1702,7 @@ static void dynamicPaint_setInitialColor(const Scene *scene, DynamicPaintSurface
}
for (int i = 0; i < totloop; i++) {
- rgba_uchar_to_float(pPoint[mloop[i].v].color, (const unsigned char *)&col[mloop[i].v].r);
+ rgba_uchar_to_float(pPoint[mloop[i].v].color, (const unsigned char *)&col[i].r);
}
}
else if (surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) {
diff --git a/source/blender/blenkernel/intern/editmesh.c b/source/blender/blenkernel/intern/editmesh.c
index 0fa3bb29ccd..472de1f3c77 100644
--- a/source/blender/blenkernel/intern/editmesh.c
+++ b/source/blender/blenkernel/intern/editmesh.c
@@ -127,9 +127,10 @@ static void editmesh_tessface_calc_intern(BMEditMesh *em)
}
em->looptris = looptris;
+ em->tottri = looptris_tot;
/* after allocating the em->looptris, we're ready to tessellate */
- BM_mesh_calc_tessellation(em->bm, em->looptris, &em->tottri);
+ BM_mesh_calc_tessellation(em->bm, em->looptris);
}
void BKE_editmesh_looptri_calc(BMEditMesh *em)
@@ -148,6 +149,14 @@ void BKE_editmesh_looptri_calc(BMEditMesh *em)
#endif
}
+void BKE_editmesh_looptri_calc_with_partial(BMEditMesh *em, struct BMPartialUpdate *bmpinfo)
+{
+ BLI_assert(em->tottri == poly_to_tri_count(em->bm->totface, em->bm->totloop));
+ BLI_assert(em->looptris != NULL);
+
+ BM_mesh_calc_tessellation_with_partial(em->bm, em->looptris, bmpinfo);
+}
+
void BKE_editmesh_free_derivedmesh(BMEditMesh *em)
{
if (em->mesh_eval_cage) {
diff --git a/source/blender/blenkernel/intern/editmesh_tangent.c b/source/blender/blenkernel/intern/editmesh_tangent.c
index d849f4ab37d..088a2087a96 100644
--- a/source/blender/blenkernel/intern/editmesh_tangent.c
+++ b/source/blender/blenkernel/intern/editmesh_tangent.c
@@ -362,7 +362,7 @@ void BKE_editmesh_loop_tangent_calc(BMEditMesh *em,
/* Calculation */
if (em->tottri != 0) {
TaskPool *task_pool;
- task_pool = BLI_task_pool_create(NULL, TASK_PRIORITY_LOW);
+ task_pool = BLI_task_pool_create(NULL, TASK_PRIORITY_LOW, TASK_ISOLATION_ON);
tangent_mask_curr = 0;
/* Calculate tangent layers */
diff --git a/source/blender/blenkernel/intern/effect.c b/source/blender/blenkernel/intern/effect.c
index 4104b6080c5..e39749225ea 100644
--- a/source/blender/blenkernel/intern/effect.c
+++ b/source/blender/blenkernel/intern/effect.c
@@ -161,13 +161,13 @@ static void precalculate_effector(struct Depsgraph *depsgraph, EffectorCache *ef
if (eff->pd->forcefield == PFIELD_GUIDE && eff->ob->type == OB_CURVE) {
Curve *cu = eff->ob->data;
if (cu->flag & CU_PATH) {
- if (eff->ob->runtime.curve_cache == NULL || eff->ob->runtime.curve_cache->path == NULL ||
- eff->ob->runtime.curve_cache->path->data == NULL) {
+ if (eff->ob->runtime.curve_cache == NULL ||
+ eff->ob->runtime.curve_cache->anim_path_accum_length == NULL) {
BKE_displist_make_curveTypes(depsgraph, eff->scene, eff->ob, false, false);
}
- if (eff->ob->runtime.curve_cache->path && eff->ob->runtime.curve_cache->path->data) {
- where_on_path(
+ if (eff->ob->runtime.curve_cache->anim_path_accum_length) {
+ BKE_where_on_path(
eff->ob, 0.0, eff->guide_loc, eff->guide_dir, NULL, &eff->guide_radius, NULL);
mul_m4_v3(eff->ob->obmat, eff->guide_loc);
mul_mat3_m4_v3(eff->ob->obmat, eff->guide_dir);
diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c
index 8e1fa9732ea..68ed3c239ef 100644
--- a/source/blender/blenkernel/intern/fcurve.c
+++ b/source/blender/blenkernel/intern/fcurve.c
@@ -35,7 +35,9 @@
#include "BLI_blenlib.h"
#include "BLI_easing.h"
+#include "BLI_ghash.h"
#include "BLI_math.h"
+#include "BLI_sort_utils.h"
#include "BKE_anim_data.h"
#include "BKE_animsys.h"
@@ -179,8 +181,10 @@ void BKE_fcurves_copy(ListBase *dst, ListBase *src)
}
}
-/** Callback used by lib_query to walk over all ID usages (mimics `foreach_id` callback of
- * `IDTypeInfo` structure). */
+/**
+ * Callback used by lib_query to walk over all ID usages (mimics `foreach_id` callback of
+ * `IDTypeInfo` structure).
+ */
void BKE_fcurve_foreach_id(FCurve *fcu, LibraryForeachIDData *data)
{
ChannelDriver *driver = fcu->driver;
@@ -290,6 +294,12 @@ FCurve *BKE_fcurve_find(ListBase *list, const char rna_path[], const int array_i
return NULL;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name FCurve Iteration
+ * \{ */
+
/* Quick way to loop over all fcurves of a given 'path'. */
FCurve *BKE_fcurve_iter_step(FCurve *fcu_iter, const char rna_path[])
{
@@ -829,6 +839,56 @@ bool BKE_fcurve_calc_range(
return foundvert;
}
+/**
+ * Return an array of keyed frames, rounded to `interval`.
+ *
+ * \param interval: Set to 1.0 to round to whole keyframes, 0.5 for in-between key-frames, etc.
+ *
+ * \note An interval of zero could be supported (this implies no rounding at all),
+ * however this risks very small differences in float values being treated as separate keyframes.
+ */
+float *BKE_fcurves_calc_keyed_frames_ex(FCurve **fcurve_array,
+ int fcurve_array_len,
+ const float interval,
+ int *r_frames_len)
+{
+ /* Use `1e-3f` as the smallest possible value since these are converted to integers
+ * and we can be sure `MAXFRAME / 1e-3f < INT_MAX` as it's around half the size. */
+ const double interval_db = max_ff(interval, 1e-3f);
+ GSet *frames_unique = BLI_gset_int_new(__func__);
+ for (int fcurve_index = 0; fcurve_index < fcurve_array_len; fcurve_index++) {
+ const FCurve *fcu = fcurve_array[fcurve_index];
+ for (int i = 0; i < fcu->totvert; i++) {
+ const BezTriple *bezt = &fcu->bezt[i];
+ const double value = round((double)bezt->vec[1][0] / interval_db);
+ BLI_assert(value > INT_MIN && value < INT_MAX);
+ BLI_gset_add(frames_unique, POINTER_FROM_INT((int)value));
+ }
+ }
+
+ const size_t frames_len = BLI_gset_len(frames_unique);
+ float *frames = MEM_mallocN(sizeof(*frames) * frames_len, __func__);
+
+ GSetIterator gs_iter;
+ int i = 0;
+ GSET_ITER_INDEX (gs_iter, frames_unique, i) {
+ const int value = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter));
+ frames[i] = (double)value * interval_db;
+ }
+ BLI_gset_free(frames_unique, NULL);
+
+ qsort(frames, frames_len, sizeof(*frames), BLI_sortutil_cmp_float);
+ *r_frames_len = frames_len;
+ return frames;
+}
+
+float *BKE_fcurves_calc_keyed_frames(FCurve **fcurve_array,
+ int fcurve_array_len,
+ int *r_frames_len)
+{
+ return BKE_fcurves_calc_keyed_frames_ex(fcurve_array, fcurve_array_len, 1.0f, r_frames_len);
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -1450,7 +1510,8 @@ bool test_time_fcurve(FCurve *fcu)
/** \name F-Curve Calculations
* \{ */
-/* The length of each handle is not allowed to be more
+/**
+ * The length of each handle is not allowed to be more
* than the horizontal distance between (v1-v4).
* This is to prevent curve loops.
*
@@ -1497,9 +1558,12 @@ void BKE_fcurve_correct_bezpart(const float v1[2], float v2[2], float v3[2], con
}
}
-/** Find roots of cubic equation (c0 x³ + c1 x² + c2 x + c3)
+/**
+ * Find roots of cubic equation (c0 x^3 + c1 x^2 + c2 x + c3)
* \return number of roots in `o`.
- * NOTE: it is up to the caller to allocate enough memory for `o`. */
+ *
+ * \note it is up to the caller to allocate enough memory for `o`.
+ */
static int solve_cubic(double c0, double c1, double c2, double c3, float *o)
{
double a, b, c, p, q, d, t, phi;
diff --git a/source/blender/blenkernel/intern/fcurve_cache.c b/source/blender/blenkernel/intern/fcurve_cache.c
new file mode 100644
index 00000000000..8142b871edd
--- /dev/null
+++ b/source/blender/blenkernel/intern/fcurve_cache.c
@@ -0,0 +1,189 @@
+/*
+ * 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.
+ */
+
+/** \file
+ * \ingroup bke
+ *
+ * Cache F-Curve look-ups.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_anim_types.h"
+
+#include "BLI_ghash.h"
+#include "BLI_listbase.h"
+
+#include "BKE_fcurve.h"
+
+/* -------------------------------------------------------------------- */
+/** \name F-Curve Path Cache
+ *
+ * Cache for finding curves by RNA path & array index.
+ * \{ */
+
+struct FCurvePathCache_Span {
+ /** Index in the #FCurvePathCache.fcurve_array indicating the start of the span. */
+ uint index;
+ /** Number of items in the span in #FCurvePathCache.fcurve_array that share an RNA path. */
+ uint len;
+};
+
+struct FCurvePathCache {
+ /** All curves sorted by (#FCurve.rna_path, #FCurve.array_index) */
+ FCurve **fcurve_array;
+ uint fcurve_array_len;
+ /** Storage for values of `span_from_rna_path`. */
+ struct FCurvePathCache_Span *span_table;
+ /** Map `FCurve.rna_path` to elements in #FCurvePathCache.span_table */
+ GHash *span_from_rna_path;
+};
+
+/**
+ * #qsort callback for an #FCurve array.
+ */
+static int fcurve_cmp_for_cache(const void *fcu_a_p, const void *fcu_b_p)
+{
+ const FCurve *fcu_a = *((const FCurve **)fcu_a_p);
+ const FCurve *fcu_b = *((const FCurve **)fcu_b_p);
+ const int cmp = strcmp(fcu_a->rna_path, fcu_b->rna_path);
+ if (cmp != 0) {
+ return cmp;
+ }
+ if (fcu_a->array_index < fcu_b->array_index) {
+ return -1;
+ }
+ if (fcu_a->array_index > fcu_b->array_index) {
+ return 1;
+ }
+ return 0;
+}
+
+struct FCurvePathCache *BKE_fcurve_pathcache_create(ListBase *list)
+{
+ const uint fcurve_array_len = BLI_listbase_count(list);
+ FCurve **fcurve_array = MEM_mallocN(sizeof(*fcurve_array) * fcurve_array_len, __func__);
+ uint i;
+ LISTBASE_FOREACH_INDEX (FCurve *, fcu, list, i) {
+ fcurve_array[i] = fcu;
+ }
+ qsort(fcurve_array, fcurve_array_len, sizeof(FCurve *), fcurve_cmp_for_cache);
+
+ /* Allow for the case no F-curves share an RNA-path, otherwise this is over-allocated.
+ * Although in practice it's likely to only be 3-4x as large as is needed
+ * (with transform channels for e.g.). */
+ struct FCurvePathCache_Span *span_table = MEM_mallocN(sizeof(*span_table) * fcurve_array_len,
+ __func__);
+
+ /* May over reserve, harmless. */
+ GHash *span_from_rna_path = BLI_ghash_str_new_ex(__func__, fcurve_array_len);
+ uint span_index = 0;
+ i = 0;
+ while (i < fcurve_array_len) {
+ uint i_end;
+ for (i_end = i + 1; i_end < fcurve_array_len; i_end++) {
+ /* As the indices are sorted, we know a decrease means a new RNA path is found. */
+ if (fcurve_array[i]->array_index > fcurve_array[i_end]->array_index) {
+ BLI_assert(!STREQ(fcurve_array[i]->rna_path, fcurve_array[i_end]->rna_path));
+ break;
+ }
+ if (!STREQ(fcurve_array[i]->rna_path, fcurve_array[i_end]->rna_path)) {
+ break;
+ }
+ }
+
+ struct FCurvePathCache_Span *span = &span_table[span_index++];
+ span->index = i;
+ span->len = i_end - i;
+ BLI_ghash_insert(span_from_rna_path, fcurve_array[i]->rna_path, span);
+ i = i_end;
+ }
+
+ struct FCurvePathCache *fcache = MEM_callocN(sizeof(struct FCurvePathCache), __func__);
+ fcache->fcurve_array = fcurve_array;
+ fcache->fcurve_array_len = fcurve_array_len;
+ fcache->span_table = span_table;
+ fcache->span_from_rna_path = span_from_rna_path;
+
+ return fcache;
+}
+
+void BKE_fcurve_pathcache_destroy(struct FCurvePathCache *fcache)
+{
+ MEM_freeN(fcache->fcurve_array);
+ MEM_freeN(fcache->span_table);
+ BLI_ghash_free(fcache->span_from_rna_path, NULL, NULL);
+ MEM_freeN(fcache);
+}
+
+FCurve *BKE_fcurve_pathcache_find(struct FCurvePathCache *fcache,
+ const char *rna_path,
+ const int array_index)
+{
+ const struct FCurvePathCache_Span *span = BLI_ghash_lookup(fcache->span_from_rna_path, rna_path);
+ if (span == NULL) {
+ return NULL;
+ }
+
+ FCurve **fcurve = fcache->fcurve_array + span->index;
+ const uint len = span->len;
+ for (int i = 0; i < len; i++) {
+ if (fcurve[i]->array_index == array_index) {
+ return fcurve[i];
+ }
+ /* As these are sorted, early exit. */
+ if (fcurve[i]->array_index > array_index) {
+ break;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * Fill in an array of F-Curve, leave NULL when not found.
+ *
+ * \return The number of F-Curves found.
+ */
+int BKE_fcurve_pathcache_find_array(struct FCurvePathCache *fcache,
+ const char *rna_path,
+ FCurve **fcurve_result,
+ int fcurve_result_len)
+{
+ memset(fcurve_result, 0x0, sizeof(*fcurve_result) * fcurve_result_len);
+
+ const struct FCurvePathCache_Span *span = BLI_ghash_lookup(fcache->span_from_rna_path, rna_path);
+ if (span == NULL) {
+ return 0;
+ }
+
+ int found = 0;
+ FCurve **fcurve = fcache->fcurve_array + span->index;
+ const uint len = span->len;
+ for (int i = 0; i < len; i++) {
+ /* As these are sorted, early exit. */
+ if ((uint)fcurve[i]->array_index > (uint)fcurve_result_len) {
+ break;
+ }
+ fcurve_result[fcurve[i]->array_index] = fcurve[i];
+ found += 1;
+ }
+ return found;
+}
+
+/** \} */
diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c
index 851d8aae378..493a267c2f0 100644
--- a/source/blender/blenkernel/intern/fluid.c
+++ b/source/blender/blenkernel/intern/fluid.c
@@ -623,7 +623,8 @@ static void clamp_bounds_in_domain(FluidDomainSettings *fds,
static bool is_static_object(Object *ob)
{
/* Check if the object has modifiers that might make the object "dynamic". */
- ModifierData *md = ob->modifiers.first;
+ VirtualModifierData virtualModifierData;
+ ModifierData *md = BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData);
for (; md; md = md->next) {
if (ELEM(md->type,
eModifierType_Cloth,
@@ -631,7 +632,8 @@ static bool is_static_object(Object *ob)
eModifierType_Explode,
eModifierType_Ocean,
eModifierType_ShapeKey,
- eModifierType_Softbody)) {
+ eModifierType_Softbody,
+ eModifierType_Nodes)) {
return false;
}
}
diff --git a/source/blender/blenkernel/intern/font.c b/source/blender/blenkernel/intern/font.c
index febc9e24c9f..92cc3c763b6 100644
--- a/source/blender/blenkernel/intern/font.c
+++ b/source/blender/blenkernel/intern/font.c
@@ -447,7 +447,6 @@ static void build_underline(Curve *cu,
nu2->resolu = cu->resolu;
nu2->bezt = NULL;
nu2->knotsu = nu2->knotsv = NULL;
- nu2->flag = CU_2D;
nu2->charidx = charidx + 1000;
if (mat_nr > 0) {
nu2->mat_nr = mat_nr - 1;
@@ -1276,7 +1275,7 @@ static bool vfont_to_curve(Object *ob,
if (cu->textoncurve && cu->textoncurve->type == OB_CURVE) {
BLI_assert(cu->textoncurve->runtime.curve_cache != NULL);
if (cu->textoncurve->runtime.curve_cache != NULL &&
- cu->textoncurve->runtime.curve_cache->path != NULL) {
+ cu->textoncurve->runtime.curve_cache->anim_path_accum_length != NULL) {
float distfac, imat[4][4], imat3[3][3], cmat[3][3];
float minx, maxx;
float timeofs, sizefac;
@@ -1310,7 +1309,8 @@ static bool vfont_to_curve(Object *ob,
/* length correction */
const float chartrans_size_x = maxx - minx;
if (chartrans_size_x != 0.0f) {
- const float totdist = cu->textoncurve->runtime.curve_cache->path->totdist;
+ const CurveCache *cc = cu->textoncurve->runtime.curve_cache;
+ const float totdist = BKE_anim_path_get_length(cc);
distfac = (sizefac * totdist) / chartrans_size_x;
distfac = (distfac > 1.0f) ? (1.0f / distfac) : 1.0f;
}
@@ -1364,8 +1364,8 @@ static bool vfont_to_curve(Object *ob,
/* calc the right loc AND the right rot separately */
/* vec, tvec need 4 items */
- where_on_path(cu->textoncurve, ctime, vec, tvec, NULL, NULL, NULL);
- where_on_path(cu->textoncurve, ctime + dtime, tvec, rotvec, NULL, NULL, NULL);
+ BKE_where_on_path(cu->textoncurve, ctime, vec, tvec, NULL, NULL, NULL);
+ BKE_where_on_path(cu->textoncurve, ctime + dtime, tvec, rotvec, NULL, NULL, NULL);
mul_v3_fl(vec, sizefac);
diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc
new file mode 100644
index 00000000000..de8dc355557
--- /dev/null
+++ b/source/blender/blenkernel/intern/geometry_component_curve.cc
@@ -0,0 +1,1199 @@
+/*
+ * 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.
+ */
+
+#include "DNA_ID_enums.h"
+#include "DNA_curve_types.h"
+
+#include "BKE_attribute_access.hh"
+#include "BKE_attribute_math.hh"
+#include "BKE_curve.h"
+#include "BKE_geometry_set.hh"
+#include "BKE_lib_id.h"
+#include "BKE_spline.hh"
+
+#include "attribute_access_intern.hh"
+
+using blender::fn::GMutableSpan;
+using blender::fn::GSpan;
+using blender::fn::GVArray_For_GSpan;
+using blender::fn::GVArray_GSpan;
+using blender::fn::GVArrayPtr;
+using blender::fn::GVMutableArray_For_GMutableSpan;
+
+/* -------------------------------------------------------------------- */
+/** \name Geometry Component Implementation
+ * \{ */
+
+CurveComponent::CurveComponent() : GeometryComponent(GEO_COMPONENT_TYPE_CURVE)
+{
+}
+
+CurveComponent::~CurveComponent()
+{
+ this->clear();
+}
+
+GeometryComponent *CurveComponent::copy() const
+{
+ CurveComponent *new_component = new CurveComponent();
+ if (curve_ != nullptr) {
+ new_component->curve_ = new CurveEval(*curve_);
+ new_component->ownership_ = GeometryOwnershipType::Owned;
+ }
+ return new_component;
+}
+
+void CurveComponent::clear()
+{
+ BLI_assert(this->is_mutable());
+ if (curve_ != nullptr) {
+ if (ownership_ == GeometryOwnershipType::Owned) {
+ delete curve_;
+ }
+ if (curve_for_render_ != nullptr) {
+ BKE_id_free(nullptr, curve_for_render_);
+ curve_for_render_ = nullptr;
+ }
+
+ curve_ = nullptr;
+ }
+}
+
+bool CurveComponent::has_curve() const
+{
+ return curve_ != nullptr;
+}
+
+/* Clear the component and replace it with the new curve. */
+void CurveComponent::replace(CurveEval *curve, GeometryOwnershipType ownership)
+{
+ BLI_assert(this->is_mutable());
+ this->clear();
+ curve_ = curve;
+ ownership_ = ownership;
+}
+
+CurveEval *CurveComponent::release()
+{
+ BLI_assert(this->is_mutable());
+ CurveEval *curve = curve_;
+ curve_ = nullptr;
+ return curve;
+}
+
+const CurveEval *CurveComponent::get_for_read() const
+{
+ return curve_;
+}
+
+CurveEval *CurveComponent::get_for_write()
+{
+ BLI_assert(this->is_mutable());
+ if (ownership_ == GeometryOwnershipType::ReadOnly) {
+ curve_ = new CurveEval(*curve_);
+ ownership_ = GeometryOwnershipType::Owned;
+ }
+ return curve_;
+}
+
+bool CurveComponent::is_empty() const
+{
+ return curve_ == nullptr;
+}
+
+bool CurveComponent::owns_direct_data() const
+{
+ return ownership_ == GeometryOwnershipType::Owned;
+}
+
+void CurveComponent::ensure_owns_direct_data()
+{
+ BLI_assert(this->is_mutable());
+ if (ownership_ != GeometryOwnershipType::Owned) {
+ curve_ = new CurveEval(*curve_);
+ ownership_ = GeometryOwnershipType::Owned;
+ }
+}
+
+/**
+ * Create empty curve data used for rendering the spline's wire edges.
+ * \note See comment on #curve_for_render_ for further explanation.
+ */
+const Curve *CurveComponent::get_curve_for_render() const
+{
+ if (curve_ == nullptr) {
+ return nullptr;
+ }
+ if (curve_for_render_ != nullptr) {
+ return curve_for_render_;
+ }
+ std::lock_guard lock{curve_for_render_mutex_};
+ if (curve_for_render_ != nullptr) {
+ return curve_for_render_;
+ }
+
+ curve_for_render_ = (Curve *)BKE_id_new_nomain(ID_CU, nullptr);
+ curve_for_render_->curve_eval = curve_;
+
+ return curve_for_render_;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Attribute Access Helper Functions
+ * \{ */
+
+int CurveComponent::attribute_domain_size(const AttributeDomain domain) const
+{
+ if (curve_ == nullptr) {
+ return 0;
+ }
+ if (domain == ATTR_DOMAIN_POINT) {
+ int total = 0;
+ for (const SplinePtr &spline : curve_->splines()) {
+ total += spline->size();
+ }
+ return total;
+ }
+ if (domain == ATTR_DOMAIN_CURVE) {
+ return curve_->splines().size();
+ }
+ return 0;
+}
+
+namespace blender::bke {
+
+namespace {
+struct PointIndices {
+ int spline_index;
+ int point_index;
+};
+} // namespace
+static PointIndices lookup_point_indices(Span<int> offsets, const int index)
+{
+ const int spline_index = std::upper_bound(offsets.begin(), offsets.end(), index) -
+ offsets.begin() - 1;
+ const int index_in_spline = index - offsets[spline_index];
+ return {spline_index, index_in_spline};
+}
+
+/**
+ * Mix together all of a spline's control point values.
+ *
+ * \note Theoretically this interpolation does not need to compute all values at once.
+ * However, doing that makes the implementation simpler, and this can be optimized in the future if
+ * only some values are required.
+ */
+template<typename T>
+static void adapt_curve_domain_point_to_spline_impl(const CurveEval &curve,
+ const VArray<T> &old_values,
+ MutableSpan<T> r_values)
+{
+ const int splines_len = curve.splines().size();
+ Array<int> offsets = curve.control_point_offsets();
+ BLI_assert(r_values.size() == splines_len);
+ attribute_math::DefaultMixer<T> mixer(r_values);
+
+ for (const int i_spline : IndexRange(splines_len)) {
+ const int spline_offset = offsets[i_spline];
+ const int spline_point_len = offsets[i_spline + 1] - spline_offset;
+ for (const int i_point : IndexRange(spline_point_len)) {
+ const T value = old_values[spline_offset + i_point];
+ mixer.mix_in(i_spline, value);
+ }
+ }
+
+ mixer.finalize();
+}
+
+static GVArrayPtr adapt_curve_domain_point_to_spline(const CurveEval &curve, GVArrayPtr varray)
+{
+ GVArrayPtr new_varray;
+ attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
+ Array<T> values(curve.splines().size());
+ adapt_curve_domain_point_to_spline_impl<T>(curve, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
+ }
+ });
+ return new_varray;
+}
+
+/**
+ * A virtual array implementation for the conversion of spline attributes to control point
+ * attributes. The goal is to avoid copying the spline value for every one of its control points
+ * unless it is necessary (in that case the materialize functions will be called).
+ */
+template<typename T> class VArray_For_SplineToPoint final : public VArray<T> {
+ /* Store existing data materialized if it was not already a span. This is expected
+ * to be worth it because a single spline's value will likely be accessed many times. */
+ VArray_Span<T> original_data_;
+ Array<int> offsets_;
+
+ public:
+ VArray_For_SplineToPoint(const VArray<T> &original_varray, Array<int> offsets)
+ : VArray<T>(offsets.last()), original_data_(original_varray), offsets_(std::move(offsets))
+ {
+ }
+
+ T get_impl(const int64_t index) const final
+ {
+ const PointIndices indices = lookup_point_indices(offsets_, index);
+ return original_data_[indices.spline_index];
+ }
+
+ void materialize_impl(const IndexMask mask, MutableSpan<T> r_span) const final
+ {
+ const int total_size = offsets_.last();
+ if (mask.is_range() && mask.as_range() == IndexRange(total_size)) {
+ for (const int spline_index : original_data_.index_range()) {
+ const int offset = offsets_[spline_index];
+ const int next_offset = offsets_[spline_index + 1];
+ r_span.slice(offset, next_offset - offset).fill(original_data_[spline_index]);
+ }
+ }
+ else {
+ int spline_index = 0;
+ for (const int dst_index : mask) {
+ while (offsets_[spline_index] < dst_index) {
+ spline_index++;
+ }
+ r_span[dst_index] = original_data_[spline_index];
+ }
+ }
+ }
+
+ void materialize_to_uninitialized_impl(const IndexMask mask, MutableSpan<T> r_span) const final
+ {
+ T *dst = r_span.data();
+ const int total_size = offsets_.last();
+ if (mask.is_range() && mask.as_range() == IndexRange(total_size)) {
+ for (const int spline_index : original_data_.index_range()) {
+ const int offset = offsets_[spline_index];
+ const int next_offset = offsets_[spline_index + 1];
+ uninitialized_fill_n(dst + offset, next_offset - offset, original_data_[spline_index]);
+ }
+ }
+ else {
+ int spline_index = 0;
+ for (const int dst_index : mask) {
+ while (offsets_[spline_index] < dst_index) {
+ spline_index++;
+ }
+ new (dst + dst_index) T(original_data_[spline_index]);
+ }
+ }
+ }
+};
+
+static GVArrayPtr adapt_curve_domain_spline_to_point(const CurveEval &curve, GVArrayPtr varray)
+{
+ GVArrayPtr new_varray;
+ attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
+ using T = decltype(dummy);
+
+ Array<int> offsets = curve.control_point_offsets();
+ new_varray = std::make_unique<fn::GVArray_For_EmbeddedVArray<T, VArray_For_SplineToPoint<T>>>(
+ offsets.last(), *varray->typed<T>(), std::move(offsets));
+ });
+ return new_varray;
+}
+
+} // namespace blender::bke
+
+GVArrayPtr CurveComponent::attribute_try_adapt_domain(GVArrayPtr varray,
+ const AttributeDomain from_domain,
+ const AttributeDomain to_domain) const
+{
+ if (!varray) {
+ return {};
+ }
+ if (varray->size() == 0) {
+ return {};
+ }
+ if (from_domain == to_domain) {
+ return varray;
+ }
+
+ if (from_domain == ATTR_DOMAIN_POINT && to_domain == ATTR_DOMAIN_CURVE) {
+ return blender::bke::adapt_curve_domain_point_to_spline(*curve_, std::move(varray));
+ }
+ if (from_domain == ATTR_DOMAIN_CURVE && to_domain == ATTR_DOMAIN_POINT) {
+ return blender::bke::adapt_curve_domain_spline_to_point(*curve_, std::move(varray));
+ }
+
+ return {};
+}
+
+static CurveEval *get_curve_from_component_for_write(GeometryComponent &component)
+{
+ BLI_assert(component.type() == GEO_COMPONENT_TYPE_CURVE);
+ CurveComponent &curve_component = static_cast<CurveComponent &>(component);
+ return curve_component.get_for_write();
+}
+
+static const CurveEval *get_curve_from_component_for_read(const GeometryComponent &component)
+{
+ BLI_assert(component.type() == GEO_COMPONENT_TYPE_CURVE);
+ const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
+ return curve_component.get_for_read();
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Builtin Spline Attributes
+ *
+ * Attributes with a value for every spline, stored contiguously or in every spline separately.
+ * \{ */
+
+namespace blender::bke {
+
+class BuiltinSplineAttributeProvider final : public BuiltinAttributeProvider {
+ using AsReadAttribute = GVArrayPtr (*)(const CurveEval &data);
+ using AsWriteAttribute = GVMutableArrayPtr (*)(CurveEval &data);
+ const AsReadAttribute as_read_attribute_;
+ const AsWriteAttribute as_write_attribute_;
+
+ public:
+ BuiltinSplineAttributeProvider(std::string attribute_name,
+ const CustomDataType attribute_type,
+ const WritableEnum writable,
+ const AsReadAttribute as_read_attribute,
+ const AsWriteAttribute as_write_attribute)
+ : BuiltinAttributeProvider(std::move(attribute_name),
+ ATTR_DOMAIN_CURVE,
+ attribute_type,
+ BuiltinAttributeProvider::NonCreatable,
+ writable,
+ BuiltinAttributeProvider::NonDeletable),
+ as_read_attribute_(as_read_attribute),
+ as_write_attribute_(as_write_attribute)
+ {
+ }
+
+ GVArrayPtr try_get_for_read(const GeometryComponent &component) const final
+ {
+ const CurveEval *curve = get_curve_from_component_for_read(component);
+ if (curve == nullptr) {
+ return {};
+ }
+ return as_read_attribute_(*curve);
+ }
+
+ GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const final
+ {
+ if (writable_ != Writable) {
+ return {};
+ }
+ CurveEval *curve = get_curve_from_component_for_write(component);
+ if (curve == nullptr) {
+ return {};
+ }
+ return as_write_attribute_(*curve);
+ }
+
+ bool try_delete(GeometryComponent &UNUSED(component)) const final
+ {
+ return false;
+ }
+
+ bool try_create(GeometryComponent &UNUSED(component),
+ const AttributeInit &UNUSED(initializer)) const final
+ {
+ return false;
+ }
+
+ bool exists(const GeometryComponent &component) const final
+ {
+ return component.attribute_domain_size(ATTR_DOMAIN_CURVE) != 0;
+ }
+};
+
+static int get_spline_resolution(const SplinePtr &spline)
+{
+ if (const BezierSpline *bezier_spline = dynamic_cast<const BezierSpline *>(spline.get())) {
+ return bezier_spline->resolution();
+ }
+ if (const NURBSpline *nurb_spline = dynamic_cast<const NURBSpline *>(spline.get())) {
+ return nurb_spline->resolution();
+ }
+ return 1;
+}
+
+static void set_spline_resolution(SplinePtr &spline, const int resolution)
+{
+ if (BezierSpline *bezier_spline = dynamic_cast<BezierSpline *>(spline.get())) {
+ bezier_spline->set_resolution(std::max(resolution, 1));
+ }
+ if (NURBSpline *nurb_spline = dynamic_cast<NURBSpline *>(spline.get())) {
+ nurb_spline->set_resolution(std::max(resolution, 1));
+ }
+}
+
+static GVArrayPtr make_resolution_read_attribute(const CurveEval &curve)
+{
+ return std::make_unique<fn::GVArray_For_DerivedSpan<SplinePtr, int, get_spline_resolution>>(
+ curve.splines());
+}
+
+static GVMutableArrayPtr make_resolution_write_attribute(CurveEval &curve)
+{
+ return std::make_unique<fn::GVMutableArray_For_DerivedSpan<SplinePtr,
+ int,
+ get_spline_resolution,
+ set_spline_resolution>>(
+ curve.splines());
+}
+
+static bool get_cyclic_value(const SplinePtr &spline)
+{
+ return spline->is_cyclic();
+}
+
+static void set_cyclic_value(SplinePtr &spline, const bool value)
+{
+ if (spline->is_cyclic() != value) {
+ spline->set_cyclic(value);
+ spline->mark_cache_invalid();
+ }
+}
+
+static GVArrayPtr make_cyclic_read_attribute(const CurveEval &curve)
+{
+ return std::make_unique<fn::GVArray_For_DerivedSpan<SplinePtr, bool, get_cyclic_value>>(
+ curve.splines());
+}
+
+static GVMutableArrayPtr make_cyclic_write_attribute(CurveEval &curve)
+{
+ return std::make_unique<
+ fn::GVMutableArray_For_DerivedSpan<SplinePtr, bool, get_cyclic_value, set_cyclic_value>>(
+ curve.splines());
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Builtin Control Point Attributes
+ *
+ * Attributes with a value for every control point. Most of the complexity here is due to the fact
+ * that we must provide access to the attribute data as if it was a contiguous array when it is
+ * really stored separately on each spline. That will be inherently rather slow, but these virtual
+ * array implementations try to make it workable in common situations.
+ * \{ */
+
+template<typename T>
+static void point_attribute_materialize(Span<Span<T>> data,
+ Span<int> offsets,
+ const IndexMask mask,
+ MutableSpan<T> r_span)
+{
+ const int total_size = offsets.last();
+ if (mask.is_range() && mask.as_range() == IndexRange(total_size)) {
+ for (const int spline_index : data.index_range()) {
+ const int offset = offsets[spline_index];
+ const int next_offset = offsets[spline_index + 1];
+ r_span.slice(offset, next_offset - offset).copy_from(data[spline_index]);
+ }
+ }
+ else {
+ int spline_index = 0;
+ for (const int dst_index : mask) {
+ while (offsets[spline_index] < dst_index) {
+ spline_index++;
+ }
+
+ const int index_in_spline = dst_index - offsets[spline_index];
+ r_span[dst_index] = data[spline_index][index_in_spline];
+ }
+ }
+}
+
+template<typename T>
+static void point_attribute_materialize_to_uninitialized(Span<Span<T>> data,
+ Span<int> offsets,
+ const IndexMask mask,
+ MutableSpan<T> r_span)
+{
+ T *dst = r_span.data();
+ const int total_size = offsets.last();
+ if (mask.is_range() && mask.as_range() == IndexRange(total_size)) {
+ for (const int spline_index : data.index_range()) {
+ const int offset = offsets[spline_index];
+ const int next_offset = offsets[spline_index + 1];
+ uninitialized_copy_n(data[spline_index].data(), next_offset - offset, dst + offset);
+ }
+ }
+ else {
+ int spline_index = 0;
+ for (const int dst_index : mask) {
+ while (offsets[spline_index] < dst_index) {
+ spline_index++;
+ }
+
+ const int index_in_spline = dst_index - offsets[spline_index];
+ new (dst + dst_index) T(data[spline_index][index_in_spline]);
+ }
+ }
+}
+
+/**
+ * Virtual array for any control point data accessed with spans and an offset array.
+ */
+template<typename T> class VArray_For_SplinePoints : public VArray<T> {
+ private:
+ const Array<Span<T>> data_;
+ Array<int> offsets_;
+
+ public:
+ VArray_For_SplinePoints(Array<Span<T>> data, Array<int> offsets)
+ : VArray<T>(offsets.last()), data_(std::move(data)), offsets_(std::move(offsets))
+ {
+ }
+
+ T get_impl(const int64_t index) const final
+ {
+ const PointIndices indices = lookup_point_indices(offsets_, index);
+ return data_[indices.spline_index][indices.point_index];
+ }
+
+ void materialize_impl(const IndexMask mask, MutableSpan<T> r_span) const final
+ {
+ point_attribute_materialize(data_.as_span(), offsets_, mask, r_span);
+ }
+
+ void materialize_to_uninitialized_impl(const IndexMask mask, MutableSpan<T> r_span) const final
+ {
+ point_attribute_materialize_to_uninitialized(data_.as_span(), offsets_, mask, r_span);
+ }
+};
+
+/**
+ * Mutable virtual array for any control point data accessed with spans and an offset array.
+ */
+template<typename T> class VMutableArray_For_SplinePoints final : public VMutableArray<T> {
+ private:
+ Array<MutableSpan<T>> data_;
+ Array<int> offsets_;
+
+ public:
+ VMutableArray_For_SplinePoints(Array<MutableSpan<T>> data, Array<int> offsets)
+ : VMutableArray<T>(offsets.last()), data_(std::move(data)), offsets_(std::move(offsets))
+ {
+ }
+
+ T get_impl(const int64_t index) const final
+ {
+ const PointIndices indices = lookup_point_indices(offsets_, index);
+ return data_[indices.spline_index][indices.point_index];
+ }
+
+ void set_impl(const int64_t index, T value) final
+ {
+ const PointIndices indices = lookup_point_indices(offsets_, index);
+ data_[indices.spline_index][indices.point_index] = value;
+ }
+
+ void set_all_impl(Span<T> src) final
+ {
+ for (const int spline_index : data_.index_range()) {
+ const int offset = offsets_[spline_index];
+ const int next_offsets = offsets_[spline_index + 1];
+ data_[spline_index].copy_from(src.slice(offset, next_offsets - offset));
+ }
+ }
+
+ void materialize_impl(const IndexMask mask, MutableSpan<T> r_span) const final
+ {
+ point_attribute_materialize({(Span<T> *)data_.data(), data_.size()}, offsets_, mask, r_span);
+ }
+
+ void materialize_to_uninitialized_impl(const IndexMask mask, MutableSpan<T> r_span) const final
+ {
+ point_attribute_materialize_to_uninitialized(
+ {(Span<T> *)data_.data(), data_.size()}, offsets_, mask, r_span);
+ }
+};
+
+template<typename T> GVArrayPtr point_data_gvarray(Array<Span<T>> spans, Array<int> offsets)
+{
+ return std::make_unique<fn::GVArray_For_EmbeddedVArray<T, VArray_For_SplinePoints<T>>>(
+ offsets.last(), std::move(spans), std::move(offsets));
+}
+
+template<typename T>
+GVMutableArrayPtr point_data_gvarray(Array<MutableSpan<T>> spans, Array<int> offsets)
+{
+ return std::make_unique<
+ fn::GVMutableArray_For_EmbeddedVMutableArray<T, VMutableArray_For_SplinePoints<T>>>(
+ offsets.last(), std::move(spans), std::move(offsets));
+}
+
+/**
+ * Virtual array implementation specifically for control point positions. This is only needed for
+ * Bezier splines, where adjusting the position also requires adjusting handle positions depending
+ * on handle types. We pay a small price for this when other spline types are mixed with Bezier.
+ *
+ * \note There is no need to check the handle type to avoid changing auto handles, since
+ * retrieving write access to the position data will mark them for recomputation anyway.
+ */
+class VMutableArray_For_SplinePosition final : public VMutableArray<float3> {
+ private:
+ MutableSpan<SplinePtr> splines_;
+ Array<int> offsets_;
+
+ public:
+ VMutableArray_For_SplinePosition(MutableSpan<SplinePtr> splines, Array<int> offsets)
+ : VMutableArray<float3>(offsets.last()), splines_(splines), offsets_(std::move(offsets))
+ {
+ }
+
+ float3 get_impl(const int64_t index) const final
+ {
+ const PointIndices indices = lookup_point_indices(offsets_, index);
+ return splines_[indices.spline_index]->positions()[indices.point_index];
+ }
+
+ void set_impl(const int64_t index, float3 value) final
+ {
+ const PointIndices indices = lookup_point_indices(offsets_, index);
+ Spline &spline = *splines_[indices.spline_index];
+ if (BezierSpline *bezier_spline = dynamic_cast<BezierSpline *>(&spline)) {
+ const float3 delta = value - bezier_spline->positions()[indices.point_index];
+ bezier_spline->handle_positions_left()[indices.point_index] += delta;
+ bezier_spline->handle_positions_right()[indices.point_index] += delta;
+ bezier_spline->positions()[indices.point_index] = value;
+ }
+ else {
+ spline.positions()[indices.point_index] = value;
+ }
+ }
+
+ void set_all_impl(Span<float3> src) final
+ {
+ for (const int spline_index : splines_.index_range()) {
+ Spline &spline = *splines_[spline_index];
+ const int offset = offsets_[spline_index];
+ const int next_offset = offsets_[spline_index + 1];
+ if (BezierSpline *bezier_spline = dynamic_cast<BezierSpline *>(&spline)) {
+ MutableSpan<float3> positions = bezier_spline->positions();
+ MutableSpan<float3> handle_positions_left = bezier_spline->handle_positions_left();
+ MutableSpan<float3> handle_positions_right = bezier_spline->handle_positions_right();
+ for (const int i : IndexRange(next_offset - offset)) {
+ const float3 delta = src[offset + i] - positions[i];
+ handle_positions_left[i] += delta;
+ handle_positions_right[i] += delta;
+ positions[i] = src[offset + i];
+ }
+ }
+ else {
+ spline.positions().copy_from(src.slice(offset, next_offset - offset));
+ }
+ }
+ }
+
+ /** Utility so we can pass positions to the materialize functions above. */
+ Array<Span<float3>> get_position_spans() const
+ {
+ Array<Span<float3>> spans(splines_.size());
+ for (const int i : spans.index_range()) {
+ spans[i] = splines_[i]->positions();
+ }
+ return spans;
+ }
+
+ void materialize_impl(const IndexMask mask, MutableSpan<float3> r_span) const final
+ {
+ Array<Span<float3>> spans = this->get_position_spans();
+ point_attribute_materialize(spans.as_span(), offsets_, mask, r_span);
+ }
+
+ void materialize_to_uninitialized_impl(const IndexMask mask,
+ MutableSpan<float3> r_span) const final
+ {
+ Array<Span<float3>> spans = this->get_position_spans();
+ point_attribute_materialize_to_uninitialized(spans.as_span(), offsets_, mask, r_span);
+ }
+};
+
+/**
+ * Provider for any builtin control point attribute that doesn't need
+ * special handling like access to other arrays in the spline.
+ */
+template<typename T> class BuiltinPointAttributeProvider : public BuiltinAttributeProvider {
+ protected:
+ using GetSpan = Span<T> (*)(const Spline &spline);
+ using GetMutableSpan = MutableSpan<T> (*)(Spline &spline);
+ using UpdateOnWrite = void (*)(Spline &spline);
+ const GetSpan get_span_;
+ const GetMutableSpan get_mutable_span_;
+ const UpdateOnWrite update_on_write_;
+
+ public:
+ BuiltinPointAttributeProvider(std::string attribute_name,
+ const WritableEnum writable,
+ const GetSpan get_span,
+ const GetMutableSpan get_mutable_span,
+ const UpdateOnWrite update_on_write)
+ : BuiltinAttributeProvider(std::move(attribute_name),
+ ATTR_DOMAIN_POINT,
+ bke::cpp_type_to_custom_data_type(CPPType::get<T>()),
+ BuiltinAttributeProvider::NonCreatable,
+ writable,
+ BuiltinAttributeProvider::NonDeletable),
+ get_span_(get_span),
+ get_mutable_span_(get_mutable_span),
+ update_on_write_(update_on_write)
+ {
+ }
+
+ GVArrayPtr try_get_for_read(const GeometryComponent &component) const override
+ {
+ const CurveEval *curve = get_curve_from_component_for_read(component);
+ if (curve == nullptr) {
+ return {};
+ }
+
+ Span<SplinePtr> splines = curve->splines();
+ if (splines.size() == 1) {
+ return std::make_unique<fn::GVArray_For_GSpan>(get_span_(*splines.first()));
+ }
+
+ Array<int> offsets = curve->control_point_offsets();
+ Array<Span<T>> spans(splines.size());
+ for (const int i : splines.index_range()) {
+ spans[i] = get_span_(*splines[i]);
+ }
+
+ return point_data_gvarray(spans, offsets);
+ }
+
+ GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const override
+ {
+ CurveEval *curve = get_curve_from_component_for_write(component);
+ if (curve == nullptr) {
+ return {};
+ }
+
+ MutableSpan<SplinePtr> splines = curve->splines();
+ if (splines.size() == 1) {
+ return std::make_unique<fn::GVMutableArray_For_GMutableSpan>(
+ get_mutable_span_(*splines.first()));
+ }
+
+ Array<int> offsets = curve->control_point_offsets();
+ Array<MutableSpan<T>> spans(splines.size());
+ for (const int i : splines.index_range()) {
+ spans[i] = get_mutable_span_(*splines[i]);
+ if (update_on_write_) {
+ update_on_write_(*splines[i]);
+ }
+ }
+
+ return point_data_gvarray(spans, offsets);
+ }
+
+ bool try_delete(GeometryComponent &UNUSED(component)) const final
+ {
+ return false;
+ }
+
+ bool try_create(GeometryComponent &UNUSED(component),
+ const AttributeInit &UNUSED(initializer)) const final
+ {
+ return false;
+ }
+
+ bool exists(const GeometryComponent &component) const final
+ {
+ return component.attribute_domain_size(ATTR_DOMAIN_POINT) != 0;
+ }
+};
+
+/**
+ * Special attribute provider for the position attribute. Keeping this separate means we don't
+ * need to make #BuiltinPointAttributeProvider overly generic, and the special handling for the
+ * positions is more clear.
+ */
+class PositionAttributeProvider final : public BuiltinPointAttributeProvider<float3> {
+ public:
+ PositionAttributeProvider()
+ : BuiltinPointAttributeProvider(
+ "position",
+ BuiltinAttributeProvider::Writable,
+ [](const Spline &spline) { return spline.positions(); },
+ [](Spline &spline) { return spline.positions(); },
+ [](Spline &spline) { spline.mark_cache_invalid(); })
+ {
+ }
+
+ GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const final
+ {
+ CurveEval *curve = get_curve_from_component_for_write(component);
+ if (curve == nullptr) {
+ return {};
+ }
+
+ bool curve_has_bezier_spline = false;
+ for (SplinePtr &spline : curve->splines()) {
+ if (spline->type() == Spline::Type::Bezier) {
+ curve_has_bezier_spline = true;
+ break;
+ }
+ }
+
+ /* Use the regular position virtual array when there aren't any Bezier splines
+ * to avoid the overhead of checking the spline type for every point. */
+ if (!curve_has_bezier_spline) {
+ return BuiltinPointAttributeProvider<float3>::try_get_for_write(component);
+ }
+
+ /* Changing the positions requires recalculation of cached evaluated data in many cases.
+ * This could set more specific flags in the future to avoid unnecessary recomputation. */
+ for (SplinePtr &spline : curve->splines()) {
+ spline->mark_cache_invalid();
+ }
+
+ Array<int> offsets = curve->control_point_offsets();
+ return std::make_unique<
+ fn::GVMutableArray_For_EmbeddedVMutableArray<float3, VMutableArray_For_SplinePosition>>(
+ offsets.last(), curve->splines(), std::move(offsets));
+ }
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Dynamic Control Point Attributes
+ *
+ * The dynamic control point attribute implementation is very similar to the builtin attribute
+ * implementation-- it uses the same virtual array types. In order to work, this code depends on
+ * the fact that all a curve's splines will have the same attributes and they all have the same
+ * type.
+ * \{ */
+
+class DynamicPointAttributeProvider final : public DynamicAttributesProvider {
+ private:
+ static constexpr uint64_t supported_types_mask = CD_MASK_PROP_FLOAT | CD_MASK_PROP_FLOAT2 |
+ CD_MASK_PROP_FLOAT3 | CD_MASK_PROP_INT32 |
+ CD_MASK_PROP_COLOR | CD_MASK_PROP_BOOL;
+
+ public:
+ ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
+ const StringRef attribute_name) const final
+ {
+ const CurveEval *curve = get_curve_from_component_for_read(component);
+ if (curve == nullptr || curve->splines().size() == 0) {
+ return {};
+ }
+
+ Span<SplinePtr> splines = curve->splines();
+ Vector<GSpan> spans; /* GSpan has no default constructor. */
+ spans.reserve(splines.size());
+ std::optional<GSpan> first_span = splines[0]->attributes.get_for_read(attribute_name);
+ if (!first_span) {
+ return {};
+ }
+ spans.append(*first_span);
+ for (const int i : IndexRange(1, splines.size() - 1)) {
+ std::optional<GSpan> span = splines[i]->attributes.get_for_read(attribute_name);
+ if (!span) {
+ /* All splines should have the same set of data layers. It would be possible to recover
+ * here and return partial data instead, but that would add a lot of complexity for a
+ * situation we don't even expect to encounter. */
+ BLI_assert_unreachable();
+ return {};
+ }
+ if (span->type() != spans.last().type()) {
+ /* Data layer types on separate splines do not match. */
+ BLI_assert_unreachable();
+ return {};
+ }
+ spans.append(*span);
+ }
+
+ /* First check for the simpler situation when we can return a simpler span virtual array. */
+ if (spans.size() == 1) {
+ return {std::make_unique<GVArray_For_GSpan>(spans.first()), ATTR_DOMAIN_POINT};
+ }
+
+ ReadAttributeLookup attribute = {};
+ Array<int> offsets = curve->control_point_offsets();
+ attribute_math::convert_to_static_type(spans[0].type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ Array<Span<T>> data(splines.size());
+ for (const int i : splines.index_range()) {
+ data[i] = spans[i].typed<T>();
+ BLI_assert(data[i].data() != nullptr);
+ }
+ attribute = {point_data_gvarray(data, offsets), ATTR_DOMAIN_POINT};
+ });
+ return attribute;
+ }
+
+ /* This function is almost the same as #try_get_for_read, but without const. */
+ WriteAttributeLookup try_get_for_write(GeometryComponent &component,
+ const StringRef attribute_name) const final
+ {
+ CurveEval *curve = get_curve_from_component_for_write(component);
+ if (curve == nullptr || curve->splines().size() == 0) {
+ return {};
+ }
+
+ MutableSpan<SplinePtr> splines = curve->splines();
+ Vector<GMutableSpan> spans; /* GMutableSpan has no default constructor. */
+ spans.reserve(splines.size());
+ std::optional<GMutableSpan> first_span = splines[0]->attributes.get_for_write(attribute_name);
+ if (!first_span) {
+ return {};
+ }
+ spans.append(*first_span);
+ for (const int i : IndexRange(1, splines.size() - 1)) {
+ std::optional<GMutableSpan> span = splines[i]->attributes.get_for_write(attribute_name);
+ if (!span) {
+ /* All splines should have the same set of data layers. It would be possible to recover
+ * here and return partial data instead, but that would add a lot of complexity for a
+ * situation we don't even expect to encounter. */
+ BLI_assert_unreachable();
+ return {};
+ }
+ if (span->type() != spans.last().type()) {
+ /* Data layer types on separate splines do not match. */
+ BLI_assert_unreachable();
+ return {};
+ }
+ spans.append(*span);
+ }
+
+ /* First check for the simpler situation when we can return a simpler span virtual array. */
+ if (spans.size() == 1) {
+ return {std::make_unique<GVMutableArray_For_GMutableSpan>(spans.first()), ATTR_DOMAIN_POINT};
+ }
+
+ WriteAttributeLookup attribute = {};
+ Array<int> offsets = curve->control_point_offsets();
+ attribute_math::convert_to_static_type(spans[0].type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ Array<MutableSpan<T>> data(splines.size());
+ for (const int i : splines.index_range()) {
+ data[i] = spans[i].typed<T>();
+ BLI_assert(data[i].data() != nullptr);
+ }
+ attribute = {point_data_gvarray(data, offsets), ATTR_DOMAIN_POINT};
+ });
+ return attribute;
+ }
+
+ bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final
+ {
+ CurveEval *curve = get_curve_from_component_for_write(component);
+ if (curve == nullptr) {
+ return false;
+ }
+
+ bool layer_freed = false;
+ for (SplinePtr &spline : curve->splines()) {
+ spline->attributes.remove(attribute_name);
+ }
+ return layer_freed;
+ }
+
+ static GVArrayPtr varray_from_initializer(const AttributeInit &initializer,
+ const CustomDataType data_type,
+ const int total_size)
+ {
+ switch (initializer.type) {
+ case AttributeInit::Type::Default:
+ /* This function shouldn't be called in this case, since there
+ * is no need to copy anything to the new custom data array. */
+ BLI_assert_unreachable();
+ return {};
+ case AttributeInit::Type::VArray:
+ return static_cast<const AttributeInitVArray &>(initializer).varray->shallow_copy();
+ case AttributeInit::Type::MoveArray:
+ return std::make_unique<fn::GVArray_For_GSpan>(
+ GSpan(*bke::custom_data_type_to_cpp_type(data_type),
+ static_cast<const AttributeInitMove &>(initializer).data,
+ total_size));
+ }
+ BLI_assert_unreachable();
+ return {};
+ }
+
+ bool try_create(GeometryComponent &component,
+ const StringRef attribute_name,
+ const AttributeDomain domain,
+ const CustomDataType data_type,
+ const AttributeInit &initializer) const final
+ {
+ BLI_assert(this->type_is_supported(data_type));
+ if (domain != ATTR_DOMAIN_POINT) {
+ return false;
+ }
+ CurveEval *curve = get_curve_from_component_for_write(component);
+ if (curve == nullptr || curve->splines().size() == 0) {
+ return false;
+ }
+
+ MutableSpan<SplinePtr> splines = curve->splines();
+
+ /* First check the one case that allows us to avoid copying the input data. */
+ if (splines.size() == 1 && initializer.type == AttributeInit::Type::MoveArray) {
+ void *source_data = static_cast<const AttributeInitMove &>(initializer).data;
+ if (!splines[0]->attributes.create_by_move(attribute_name, data_type, source_data)) {
+ MEM_freeN(source_data);
+ return false;
+ }
+ return true;
+ }
+
+ /* Otherwise just create a custom data layer on each of the splines. */
+ for (const int i : splines.index_range()) {
+ if (!splines[i]->attributes.create(attribute_name, data_type)) {
+ /* If attribute creation fails on one of the splines, we cannot leave the custom data
+ * layers in the previous splines around, so delete them before returning. However,
+ * this is not an expected case. */
+ BLI_assert_unreachable();
+ return false;
+ }
+ }
+
+ /* With a default initializer type, we can keep the values at their initial values. */
+ if (initializer.type == AttributeInit::Type::Default) {
+ return true;
+ }
+
+ WriteAttributeLookup write_attribute = this->try_get_for_write(component, attribute_name);
+ /* We just created the attribute, it should exist. */
+ BLI_assert(write_attribute);
+
+ const int total_size = curve->control_point_offsets().last();
+ GVArrayPtr source_varray = varray_from_initializer(initializer, data_type, total_size);
+ /* TODO: When we can call a variant of #set_all with a virtual array argument,
+ * this theoretically unnecessary materialize step could be removed. */
+ GVArray_GSpan source_varray_span{*source_varray};
+ write_attribute.varray->set_all(source_varray_span.data());
+
+ if (initializer.type == AttributeInit::Type::MoveArray) {
+ MEM_freeN(static_cast<const AttributeInitMove &>(initializer).data);
+ }
+
+ return true;
+ }
+
+ bool foreach_attribute(const GeometryComponent &component,
+ const AttributeForeachCallback callback) const final
+ {
+ const CurveEval *curve = get_curve_from_component_for_read(component);
+ if (curve == nullptr || curve->splines().size() == 0) {
+ return false;
+ }
+
+ Span<SplinePtr> splines = curve->splines();
+
+ /* In a debug build, check that all corresponding custom data layers have the same type. */
+ curve->assert_valid_point_attributes();
+
+ /* Use the first spline as a representative for all the others. */
+ splines.first()->attributes.foreach_attribute(callback, ATTR_DOMAIN_POINT);
+
+ return true;
+ }
+
+ void foreach_domain(const FunctionRef<void(AttributeDomain)> callback) const final
+ {
+ callback(ATTR_DOMAIN_POINT);
+ }
+
+ bool type_is_supported(CustomDataType data_type) const
+ {
+ return ((1ULL << data_type) & supported_types_mask) != 0;
+ }
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Attribute Provider Declaration
+ * \{ */
+
+/**
+ * In this function all the attribute providers for a curve component are created.
+ * Most data in this function is statically allocated, because it does not change over time.
+ */
+static ComponentAttributeProviders create_attribute_providers_for_curve()
+{
+ static BuiltinSplineAttributeProvider resolution("resolution",
+ CD_PROP_INT32,
+ BuiltinAttributeProvider::Writable,
+ make_resolution_read_attribute,
+ make_resolution_write_attribute);
+
+ static BuiltinSplineAttributeProvider cyclic("cyclic",
+ CD_PROP_BOOL,
+ BuiltinAttributeProvider::Writable,
+ make_cyclic_read_attribute,
+ make_cyclic_write_attribute);
+
+ static CustomDataAccessInfo spline_custom_data_access = {
+ [](GeometryComponent &component) -> CustomData * {
+ CurveEval *curve = get_curve_from_component_for_write(component);
+ return curve ? &curve->attributes.data : nullptr;
+ },
+ [](const GeometryComponent &component) -> const CustomData * {
+ const CurveEval *curve = get_curve_from_component_for_read(component);
+ return curve ? &curve->attributes.data : nullptr;
+ },
+ nullptr};
+
+ static CustomDataAttributeProvider spline_custom_data(ATTR_DOMAIN_CURVE,
+ spline_custom_data_access);
+
+ static PositionAttributeProvider position;
+
+ static BuiltinPointAttributeProvider<float> radius(
+ "radius",
+ BuiltinAttributeProvider::Writable,
+ [](const Spline &spline) { return spline.radii(); },
+ [](Spline &spline) { return spline.radii(); },
+ nullptr);
+
+ static BuiltinPointAttributeProvider<float> tilt(
+ "tilt",
+ BuiltinAttributeProvider::Writable,
+ [](const Spline &spline) { return spline.tilts(); },
+ [](Spline &spline) { return spline.tilts(); },
+ [](Spline &spline) { spline.mark_cache_invalid(); });
+
+ static DynamicPointAttributeProvider point_custom_data;
+
+ return ComponentAttributeProviders({&position, &radius, &tilt, &resolution, &cyclic},
+ {&spline_custom_data, &point_custom_data});
+}
+
+} // namespace blender::bke
+
+const blender::bke::ComponentAttributeProviders *CurveComponent::get_attribute_providers() const
+{
+ static blender::bke::ComponentAttributeProviders providers =
+ blender::bke::create_attribute_providers_for_curve();
+ return &providers;
+}
+
+/** \} */
diff --git a/source/blender/blenkernel/intern/geometry_component_instances.cc b/source/blender/blenkernel/intern/geometry_component_instances.cc
index 68c551645d2..3b1b7456162 100644
--- a/source/blender/blenkernel/intern/geometry_component_instances.cc
+++ b/source/blender/blenkernel/intern/geometry_component_instances.cc
@@ -42,70 +42,116 @@ InstancesComponent::InstancesComponent() : GeometryComponent(GEO_COMPONENT_TYPE_
GeometryComponent *InstancesComponent::copy() const
{
InstancesComponent *new_component = new InstancesComponent();
- new_component->transforms_ = transforms_;
- new_component->instanced_data_ = instanced_data_;
+ new_component->instance_reference_handles_ = instance_reference_handles_;
+ new_component->instance_transforms_ = instance_transforms_;
+ new_component->instance_ids_ = instance_ids_;
+ new_component->references_ = references_;
return new_component;
}
+void InstancesComponent::reserve(int min_capacity)
+{
+ instance_reference_handles_.reserve(min_capacity);
+ instance_transforms_.reserve(min_capacity);
+ instance_ids_.reserve(min_capacity);
+}
+
+/**
+ * Resize the transform, handles, and ID vectors to the specified capacity.
+ *
+ * \note This function should be used carefully, only when it's guaranteed
+ * that the data will be filled.
+ */
+void InstancesComponent::resize(int capacity)
+{
+ instance_reference_handles_.resize(capacity);
+ instance_transforms_.resize(capacity);
+ instance_ids_.resize(capacity);
+}
+
void InstancesComponent::clear()
{
- instanced_data_.clear();
- transforms_.clear();
+ instance_reference_handles_.clear();
+ instance_transforms_.clear();
+ instance_ids_.clear();
+
+ references_.clear();
}
-void InstancesComponent::add_instance(Object *object, float4x4 transform, const int id)
+void InstancesComponent::add_instance(const int instance_handle,
+ const float4x4 &transform,
+ const int id)
{
- InstancedData data;
- data.type = INSTANCE_DATA_TYPE_OBJECT;
- data.data.object = object;
- this->add_instance(data, transform, id);
+ BLI_assert(instance_handle >= 0);
+ BLI_assert(instance_handle < references_.size());
+ instance_reference_handles_.append(instance_handle);
+ instance_transforms_.append(transform);
+ instance_ids_.append(id);
}
-void InstancesComponent::add_instance(Collection *collection, float4x4 transform, const int id)
+blender::Span<int> InstancesComponent::instance_reference_handles() const
{
- InstancedData data;
- data.type = INSTANCE_DATA_TYPE_COLLECTION;
- data.data.collection = collection;
- this->add_instance(data, transform, id);
+ return instance_reference_handles_;
}
-void InstancesComponent::add_instance(InstancedData data, float4x4 transform, const int id)
+blender::MutableSpan<int> InstancesComponent::instance_reference_handles()
{
- instanced_data_.append(data);
- transforms_.append(transform);
- ids_.append(id);
+ return instance_reference_handles_;
}
-Span<InstancedData> InstancesComponent::instanced_data() const
+blender::MutableSpan<blender::float4x4> InstancesComponent::instance_transforms()
+{
+ return instance_transforms_;
+}
+blender::Span<blender::float4x4> InstancesComponent::instance_transforms() const
{
- return instanced_data_;
+ return instance_transforms_;
}
-Span<float4x4> InstancesComponent::transforms() const
+blender::MutableSpan<int> InstancesComponent::instance_ids()
+{
+ return instance_ids_;
+}
+blender::Span<int> InstancesComponent::instance_ids() const
{
- return transforms_;
+ return instance_ids_;
}
-Span<int> InstancesComponent::ids() const
+/**
+ * Returns a handle for the given reference.
+ * If the reference exists already, the handle of the existing reference is returned.
+ * Otherwise a new handle is added.
+ */
+int InstancesComponent::add_reference(InstanceReference reference)
{
- return ids_;
+ return references_.index_of_or_add_as(reference);
}
-MutableSpan<float4x4> InstancesComponent::transforms()
+blender::Span<InstanceReference> InstancesComponent::references() const
{
- return transforms_;
+ return references_;
}
int InstancesComponent::instances_amount() const
{
- const int size = instanced_data_.size();
- BLI_assert(transforms_.size() == size);
- return size;
+ return instance_transforms_.size();
}
bool InstancesComponent::is_empty() const
{
- return transforms_.size() == 0;
+ return this->instance_reference_handles_.size() == 0;
+}
+
+bool InstancesComponent::owns_direct_data() const
+{
+ /* The object and collection instances are not direct data. Instance transforms are direct data
+ * and are always owned. Therefore, instance components always own all their direct data. */
+ return true;
+}
+
+void InstancesComponent::ensure_owns_direct_data()
+{
+ BLI_assert(this->is_mutable());
}
static blender::Array<int> generate_unique_instance_ids(Span<int> original_ids)
@@ -164,8 +210,8 @@ static blender::Array<int> generate_unique_instance_ids(Span<int> original_ids)
blender::Span<int> InstancesComponent::almost_unique_ids() const
{
std::lock_guard lock(almost_unique_ids_mutex_);
- if (almost_unique_ids_.size() != ids_.size()) {
- almost_unique_ids_ = generate_unique_instance_ids(ids_);
+ if (almost_unique_ids_.size() != instance_ids_.size()) {
+ almost_unique_ids_ = generate_unique_instance_ids(instance_ids_);
}
return almost_unique_ids_;
}
diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc
index 4fb7bd6a9bd..42f3a854aec 100644
--- a/source/blender/blenkernel/intern/geometry_component_mesh.cc
+++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc
@@ -32,7 +32,7 @@
/* Can't include BKE_object_deform.h right now, due to an enum forward declaration. */
extern "C" MDeformVert *BKE_object_defgroup_data_create(ID *id);
-using blender::bke::ReadAttributePtr;
+using blender::fn::GVArray;
/* -------------------------------------------------------------------- */
/** \name Geometry Component Implementation
@@ -157,6 +157,20 @@ bool MeshComponent::is_empty() const
return mesh_ == nullptr;
}
+bool MeshComponent::owns_direct_data() const
+{
+ return ownership_ == GeometryOwnershipType::Owned;
+}
+
+void MeshComponent::ensure_owns_direct_data()
+{
+ BLI_assert(this->is_mutable());
+ if (ownership_ != GeometryOwnershipType::Owned) {
+ mesh_ = BKE_mesh_copy_for_eval(mesh_, false);
+ ownership_ = GeometryOwnershipType::Owned;
+ }
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -187,14 +201,14 @@ namespace blender::bke {
template<typename T>
static void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh,
- const TypedReadAttribute<T> &attribute,
+ const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totvert);
attribute_math::DefaultMixer<T> mixer(r_values);
for (const int loop_index : IndexRange(mesh.totloop)) {
- const T value = attribute[loop_index];
+ const T value = old_values[loop_index];
const MLoop &loop = mesh.mloop[loop_index];
const int point_index = loop.v;
mixer.mix_in(point_index, value);
@@ -202,55 +216,49 @@ static void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh,
mixer.finalize();
}
-static ReadAttributePtr adapt_mesh_domain_corner_to_point(const Mesh &mesh,
- ReadAttributePtr attribute)
+static GVArrayPtr adapt_mesh_domain_corner_to_point(const Mesh &mesh, GVArrayPtr varray)
{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
- attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ GVArrayPtr new_varray;
+ attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
/* We compute all interpolated values at once, because for this interpolation, one has to
* iterate over all loops anyway. */
Array<T> values(mesh.totvert);
- adapt_mesh_domain_corner_to_point_impl<T>(mesh, *attribute, values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
- std::move(values));
+ adapt_mesh_domain_corner_to_point_impl<T>(mesh, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
- return new_attribute;
+ return new_varray;
}
template<typename T>
static void adapt_mesh_domain_point_to_corner_impl(const Mesh &mesh,
- const TypedReadAttribute<T> &attribute,
+ const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totloop);
for (const int loop_index : IndexRange(mesh.totloop)) {
const int vertex_index = mesh.mloop[loop_index].v;
- r_values[loop_index] = attribute[vertex_index];
+ r_values[loop_index] = old_values[vertex_index];
}
}
-static ReadAttributePtr adapt_mesh_domain_point_to_corner(const Mesh &mesh,
- ReadAttributePtr attribute)
+static GVArrayPtr adapt_mesh_domain_point_to_corner(const Mesh &mesh, GVArrayPtr varray)
{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
- attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ GVArrayPtr new_varray;
+ attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
using T = decltype(dummy);
/* It is not strictly necessary to compute the value for all corners here. Instead one could
* lazily lookup the mesh topology when a specific index accessed. This can be more efficient
* when an algorithm only accesses very few of the corner values. However, for the algorithms
* we currently have, precomputing the array is fine. Also, it is easier to implement. */
Array<T> values(mesh.totloop);
- adapt_mesh_domain_point_to_corner_impl<T>(mesh, *attribute, values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_CORNER,
- std::move(values));
+ adapt_mesh_domain_point_to_corner_impl<T>(mesh, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
});
- return new_attribute;
+ return new_varray;
}
/**
@@ -260,7 +268,7 @@ static ReadAttributePtr adapt_mesh_domain_point_to_corner(const Mesh &mesh,
*/
template<typename T>
static void adapt_mesh_domain_corner_to_face_impl(const Mesh &mesh,
- Span<T> old_values,
+ const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totpoly);
@@ -277,26 +285,23 @@ static void adapt_mesh_domain_corner_to_face_impl(const Mesh &mesh,
mixer.finalize();
}
-static ReadAttributePtr adapt_mesh_domain_corner_to_face(const Mesh &mesh,
- ReadAttributePtr attribute)
+static GVArrayPtr adapt_mesh_domain_corner_to_face(const Mesh &mesh, GVArrayPtr varray)
{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
- attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ GVArrayPtr new_varray;
+ attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totpoly);
- adapt_mesh_domain_corner_to_face_impl<T>(mesh, attribute->get_span<T>(), values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
- std::move(values));
+ adapt_mesh_domain_corner_to_face_impl<T>(mesh, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
- return new_attribute;
+ return new_varray;
}
template<typename T>
static void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh,
- Span<T> old_values,
+ const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totedge);
@@ -318,26 +323,23 @@ static void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh,
mixer.finalize();
}
-static ReadAttributePtr adapt_mesh_domain_corner_to_edge(const Mesh &mesh,
- ReadAttributePtr attribute)
+static GVArrayPtr adapt_mesh_domain_corner_to_edge(const Mesh &mesh, GVArrayPtr varray)
{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
- attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ GVArrayPtr new_varray;
+ attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totedge);
- adapt_mesh_domain_corner_to_edge_impl<T>(mesh, attribute->get_span<T>(), values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
- std::move(values));
+ adapt_mesh_domain_corner_to_edge_impl<T>(mesh, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
- return new_attribute;
+ return new_varray;
}
template<typename T>
void adapt_mesh_domain_face_to_point_impl(const Mesh &mesh,
- Span<T> old_values,
+ const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totvert);
@@ -356,26 +358,23 @@ void adapt_mesh_domain_face_to_point_impl(const Mesh &mesh,
mixer.finalize();
}
-static ReadAttributePtr adapt_mesh_domain_face_to_point(const Mesh &mesh,
- ReadAttributePtr attribute)
+static GVArrayPtr adapt_mesh_domain_face_to_point(const Mesh &mesh, GVArrayPtr varray)
{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
- attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ GVArrayPtr new_varray;
+ attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totvert);
- adapt_mesh_domain_face_to_point_impl<T>(mesh, attribute->get_span<T>(), values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
- std::move(values));
+ adapt_mesh_domain_face_to_point_impl<T>(mesh, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
- return new_attribute;
+ return new_varray;
}
template<typename T>
void adapt_mesh_domain_face_to_corner_impl(const Mesh &mesh,
- const Span<T> old_values,
+ const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totloop);
@@ -387,26 +386,23 @@ void adapt_mesh_domain_face_to_corner_impl(const Mesh &mesh,
}
}
-static ReadAttributePtr adapt_mesh_domain_face_to_corner(const Mesh &mesh,
- ReadAttributePtr attribute)
+static GVArrayPtr adapt_mesh_domain_face_to_corner(const Mesh &mesh, GVArrayPtr varray)
{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
- attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ GVArrayPtr new_varray;
+ attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totloop);
- adapt_mesh_domain_face_to_corner_impl<T>(mesh, attribute->get_span<T>(), values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
- std::move(values));
+ adapt_mesh_domain_face_to_corner_impl<T>(mesh, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
- return new_attribute;
+ return new_varray;
}
template<typename T>
void adapt_mesh_domain_face_to_edge_impl(const Mesh &mesh,
- const Span<T> old_values,
+ const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totedge);
@@ -423,21 +419,18 @@ void adapt_mesh_domain_face_to_edge_impl(const Mesh &mesh,
mixer.finalize();
}
-static ReadAttributePtr adapt_mesh_domain_face_to_edge(const Mesh &mesh,
- ReadAttributePtr attribute)
+static GVArrayPtr adapt_mesh_domain_face_to_edge(const Mesh &mesh, GVArrayPtr varray)
{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
- attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ GVArrayPtr new_varray;
+ attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totedge);
- adapt_mesh_domain_face_to_edge_impl<T>(mesh, attribute->get_span<T>(), values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
- std::move(values));
+ adapt_mesh_domain_face_to_edge_impl<T>(mesh, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
- return new_attribute;
+ return new_varray;
}
/**
@@ -447,7 +440,7 @@ static ReadAttributePtr adapt_mesh_domain_face_to_edge(const Mesh &mesh,
*/
template<typename T>
static void adapt_mesh_domain_point_to_face_impl(const Mesh &mesh,
- const Span<T> old_values,
+ const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totpoly);
@@ -464,21 +457,18 @@ static void adapt_mesh_domain_point_to_face_impl(const Mesh &mesh,
mixer.finalize();
}
-static ReadAttributePtr adapt_mesh_domain_point_to_face(const Mesh &mesh,
- ReadAttributePtr attribute)
+static GVArrayPtr adapt_mesh_domain_point_to_face(const Mesh &mesh, GVArrayPtr varray)
{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
- attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ GVArrayPtr new_varray;
+ attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totpoly);
- adapt_mesh_domain_point_to_face_impl<T>(mesh, attribute->get_span<T>(), values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
- std::move(values));
+ adapt_mesh_domain_point_to_face_impl<T>(mesh, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
- return new_attribute;
+ return new_varray;
}
/**
@@ -488,7 +478,7 @@ static ReadAttributePtr adapt_mesh_domain_point_to_face(const Mesh &mesh,
*/
template<typename T>
static void adapt_mesh_domain_point_to_edge_impl(const Mesh &mesh,
- const Span<T> old_values,
+ const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totedge);
@@ -503,26 +493,23 @@ static void adapt_mesh_domain_point_to_edge_impl(const Mesh &mesh,
mixer.finalize();
}
-static ReadAttributePtr adapt_mesh_domain_point_to_edge(const Mesh &mesh,
- ReadAttributePtr attribute)
+static GVArrayPtr adapt_mesh_domain_point_to_edge(const Mesh &mesh, GVArrayPtr varray)
{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
- attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ GVArrayPtr new_varray;
+ attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totedge);
- adapt_mesh_domain_point_to_edge_impl<T>(mesh, attribute->get_span<T>(), values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
- std::move(values));
+ adapt_mesh_domain_point_to_edge_impl<T>(mesh, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
- return new_attribute;
+ return new_varray;
}
template<typename T>
void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh,
- const Span<T> old_values,
+ const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totloop);
@@ -533,7 +520,7 @@ void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh,
/* For every corner, mix the values from the adjacent edges on the face. */
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
- const int loop_index_prev = (loop_index - 1) % poly.totloop;
+ const int loop_index_prev = loop_index - 1 + (loop_index == poly.loopstart) * poly.totloop;
const MLoop &loop = mesh.mloop[loop_index];
const MLoop &loop_prev = mesh.mloop[loop_index_prev];
mixer.mix_in(loop_index, old_values[loop.e]);
@@ -544,26 +531,23 @@ void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh,
mixer.finalize();
}
-static ReadAttributePtr adapt_mesh_domain_edge_to_corner(const Mesh &mesh,
- ReadAttributePtr attribute)
+static GVArrayPtr adapt_mesh_domain_edge_to_corner(const Mesh &mesh, GVArrayPtr varray)
{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
- attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ GVArrayPtr new_varray;
+ attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totloop);
- adapt_mesh_domain_edge_to_corner_impl<T>(mesh, attribute->get_span<T>(), values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
- std::move(values));
+ adapt_mesh_domain_edge_to_corner_impl<T>(mesh, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
- return new_attribute;
+ return new_varray;
}
template<typename T>
static void adapt_mesh_domain_edge_to_point_impl(const Mesh &mesh,
- const Span<T> old_values,
+ const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totvert);
@@ -579,21 +563,18 @@ static void adapt_mesh_domain_edge_to_point_impl(const Mesh &mesh,
mixer.finalize();
}
-static ReadAttributePtr adapt_mesh_domain_edge_to_point(const Mesh &mesh,
- ReadAttributePtr attribute)
+static GVArrayPtr adapt_mesh_domain_edge_to_point(const Mesh &mesh, GVArrayPtr varray)
{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
- attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ GVArrayPtr new_varray;
+ attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totvert);
- adapt_mesh_domain_edge_to_point_impl<T>(mesh, attribute->get_span<T>(), values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
- std::move(values));
+ adapt_mesh_domain_edge_to_point_impl<T>(mesh, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
- return new_attribute;
+ return new_varray;
}
/**
@@ -603,7 +584,7 @@ static ReadAttributePtr adapt_mesh_domain_edge_to_point(const Mesh &mesh,
*/
template<typename T>
static void adapt_mesh_domain_edge_to_face_impl(const Mesh &mesh,
- const Span<T> old_values,
+ const VArray<T> &old_values,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totpoly);
@@ -620,87 +601,85 @@ static void adapt_mesh_domain_edge_to_face_impl(const Mesh &mesh,
mixer.finalize();
}
-static ReadAttributePtr adapt_mesh_domain_edge_to_face(const Mesh &mesh,
- ReadAttributePtr attribute)
+static GVArrayPtr adapt_mesh_domain_edge_to_face(const Mesh &mesh, GVArrayPtr varray)
{
- ReadAttributePtr new_attribute;
- const CustomDataType data_type = attribute->custom_data_type();
- attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ GVArrayPtr new_varray;
+ attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
using T = decltype(dummy);
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
Array<T> values(mesh.totpoly);
- adapt_mesh_domain_edge_to_face_impl<T>(mesh, attribute->get_span<T>(), values);
- new_attribute = std::make_unique<OwnedArrayReadAttribute<T>>(ATTR_DOMAIN_POINT,
- std::move(values));
+ adapt_mesh_domain_edge_to_face_impl<T>(mesh, varray->typed<T>(), values);
+ new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
}
});
- return new_attribute;
+ return new_varray;
}
} // namespace blender::bke
-ReadAttributePtr MeshComponent::attribute_try_adapt_domain(ReadAttributePtr attribute,
- const AttributeDomain new_domain) const
+blender::fn::GVArrayPtr MeshComponent::attribute_try_adapt_domain(
+ blender::fn::GVArrayPtr varray,
+ const AttributeDomain from_domain,
+ const AttributeDomain to_domain) const
{
- if (!attribute) {
+ if (!varray) {
return {};
}
- if (attribute->size() == 0) {
+ if (varray->size() == 0) {
return {};
}
- const AttributeDomain old_domain = attribute->domain();
- if (old_domain == new_domain) {
- return attribute;
+ if (from_domain == to_domain) {
+ return varray;
}
- switch (old_domain) {
+ switch (from_domain) {
case ATTR_DOMAIN_CORNER: {
- switch (new_domain) {
+ switch (to_domain) {
case ATTR_DOMAIN_POINT:
- return blender::bke::adapt_mesh_domain_corner_to_point(*mesh_, std::move(attribute));
+ return blender::bke::adapt_mesh_domain_corner_to_point(*mesh_, std::move(varray));
case ATTR_DOMAIN_FACE:
- return blender::bke::adapt_mesh_domain_corner_to_face(*mesh_, std::move(attribute));
+ return blender::bke::adapt_mesh_domain_corner_to_face(*mesh_, std::move(varray));
case ATTR_DOMAIN_EDGE:
- return blender::bke::adapt_mesh_domain_corner_to_edge(*mesh_, std::move(attribute));
+ return blender::bke::adapt_mesh_domain_corner_to_edge(*mesh_, std::move(varray));
default:
break;
}
break;
}
case ATTR_DOMAIN_POINT: {
- switch (new_domain) {
+ switch (to_domain) {
case ATTR_DOMAIN_CORNER:
- return blender::bke::adapt_mesh_domain_point_to_corner(*mesh_, std::move(attribute));
+ return blender::bke::adapt_mesh_domain_point_to_corner(*mesh_, std::move(varray));
case ATTR_DOMAIN_FACE:
- return blender::bke::adapt_mesh_domain_point_to_face(*mesh_, std::move(attribute));
+ return blender::bke::adapt_mesh_domain_point_to_face(*mesh_, std::move(varray));
case ATTR_DOMAIN_EDGE:
- return blender::bke::adapt_mesh_domain_point_to_edge(*mesh_, std::move(attribute));
+ return blender::bke::adapt_mesh_domain_point_to_edge(*mesh_, std::move(varray));
default:
break;
}
break;
}
case ATTR_DOMAIN_FACE: {
- switch (new_domain) {
+ switch (to_domain) {
case ATTR_DOMAIN_POINT:
- return blender::bke::adapt_mesh_domain_face_to_point(*mesh_, std::move(attribute));
+ return blender::bke::adapt_mesh_domain_face_to_point(*mesh_, std::move(varray));
case ATTR_DOMAIN_CORNER:
- return blender::bke::adapt_mesh_domain_face_to_corner(*mesh_, std::move(attribute));
+ return blender::bke::adapt_mesh_domain_face_to_corner(*mesh_, std::move(varray));
case ATTR_DOMAIN_EDGE:
- return blender::bke::adapt_mesh_domain_face_to_edge(*mesh_, std::move(attribute));
+ return blender::bke::adapt_mesh_domain_face_to_edge(*mesh_, std::move(varray));
default:
break;
}
break;
}
case ATTR_DOMAIN_EDGE: {
- switch (new_domain) {
+ switch (to_domain) {
case ATTR_DOMAIN_CORNER:
- return blender::bke::adapt_mesh_domain_edge_to_corner(*mesh_, std::move(attribute));
+ return blender::bke::adapt_mesh_domain_edge_to_corner(*mesh_, std::move(varray));
case ATTR_DOMAIN_POINT:
- return blender::bke::adapt_mesh_domain_edge_to_point(*mesh_, std::move(attribute));
+ return blender::bke::adapt_mesh_domain_edge_to_point(*mesh_, std::move(varray));
case ATTR_DOMAIN_FACE:
- return blender::bke::adapt_mesh_domain_edge_to_face(*mesh_, std::move(attribute));
+ return blender::bke::adapt_mesh_domain_edge_to_face(*mesh_, std::move(varray));
default:
break;
}
@@ -729,28 +708,31 @@ static const Mesh *get_mesh_from_component_for_read(const GeometryComponent &com
namespace blender::bke {
-static float3 get_vertex_position(const MVert &vert)
+template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)>
+static GVArrayPtr make_derived_read_attribute(const void *data, const int domain_size)
{
- return float3(vert.co);
+ return std::make_unique<fn::GVArray_For_DerivedSpan<StructT, ElemT, GetFunc>>(
+ Span<StructT>((const StructT *)data, domain_size));
}
-static void set_vertex_position(MVert &vert, const float3 &position)
+template<typename StructT,
+ typename ElemT,
+ ElemT (*GetFunc)(const StructT &),
+ void (*SetFunc)(StructT &, ElemT)>
+static GVMutableArrayPtr make_derived_write_attribute(void *data, const int domain_size)
{
- copy_v3_v3(vert.co, position);
+ return std::make_unique<fn::GVMutableArray_For_DerivedSpan<StructT, ElemT, GetFunc, SetFunc>>(
+ MutableSpan<StructT>((StructT *)data, domain_size));
}
-static ReadAttributePtr make_vertex_position_read_attribute(const void *data,
- const int domain_size)
+static float3 get_vertex_position(const MVert &vert)
{
- return std::make_unique<DerivedArrayReadAttribute<MVert, float3, get_vertex_position>>(
- ATTR_DOMAIN_POINT, Span<MVert>((const MVert *)data, domain_size));
+ return float3(vert.co);
}
-static WriteAttributePtr make_vertex_position_write_attribute(void *data, const int domain_size)
+static void set_vertex_position(MVert &vert, float3 position)
{
- return std::make_unique<
- DerivedArrayWriteAttribute<MVert, float3, get_vertex_position, set_vertex_position>>(
- ATTR_DOMAIN_POINT, MutableSpan<MVert>((MVert *)data, domain_size));
+ copy_v3_v3(vert.co, position);
}
static void tag_normals_dirty_when_writing_position(GeometryComponent &component)
@@ -766,92 +748,45 @@ static int get_material_index(const MPoly &mpoly)
return static_cast<int>(mpoly.mat_nr);
}
-static void set_material_index(MPoly &mpoly, const int &index)
+static void set_material_index(MPoly &mpoly, int index)
{
mpoly.mat_nr = static_cast<short>(std::clamp(index, 0, SHRT_MAX));
}
-static ReadAttributePtr make_material_index_read_attribute(const void *data, const int domain_size)
-{
- return std::make_unique<DerivedArrayReadAttribute<MPoly, int, get_material_index>>(
- ATTR_DOMAIN_FACE, Span<MPoly>((const MPoly *)data, domain_size));
-}
-
-static WriteAttributePtr make_material_index_write_attribute(void *data, const int domain_size)
-{
- return std::make_unique<
- DerivedArrayWriteAttribute<MPoly, int, get_material_index, set_material_index>>(
- ATTR_DOMAIN_FACE, MutableSpan<MPoly>((MPoly *)data, domain_size));
-}
-
static bool get_shade_smooth(const MPoly &mpoly)
{
return mpoly.flag & ME_SMOOTH;
}
-static void set_shade_smooth(MPoly &mpoly, const bool &value)
+static void set_shade_smooth(MPoly &mpoly, bool value)
{
SET_FLAG_FROM_TEST(mpoly.flag, value, ME_SMOOTH);
}
-static ReadAttributePtr make_shade_smooth_read_attribute(const void *data, const int domain_size)
-{
- return std::make_unique<DerivedArrayReadAttribute<MPoly, bool, get_shade_smooth>>(
- ATTR_DOMAIN_FACE, Span<MPoly>((const MPoly *)data, domain_size));
-}
-
-static WriteAttributePtr make_shade_smooth_write_attribute(void *data, const int domain_size)
-{
- return std::make_unique<
- DerivedArrayWriteAttribute<MPoly, bool, get_shade_smooth, set_shade_smooth>>(
- ATTR_DOMAIN_FACE, MutableSpan<MPoly>((MPoly *)data, domain_size));
-}
-
static float2 get_loop_uv(const MLoopUV &uv)
{
return float2(uv.uv);
}
-static void set_loop_uv(MLoopUV &uv, const float2 &co)
+static void set_loop_uv(MLoopUV &uv, float2 co)
{
copy_v2_v2(uv.uv, co);
}
-static ReadAttributePtr make_uvs_read_attribute(const void *data, const int domain_size)
-{
- return std::make_unique<DerivedArrayReadAttribute<MLoopUV, float2, get_loop_uv>>(
- ATTR_DOMAIN_CORNER, Span((const MLoopUV *)data, domain_size));
-}
-
-static WriteAttributePtr make_uvs_write_attribute(void *data, const int domain_size)
-{
- return std::make_unique<DerivedArrayWriteAttribute<MLoopUV, float2, get_loop_uv, set_loop_uv>>(
- ATTR_DOMAIN_CORNER, MutableSpan((MLoopUV *)data, domain_size));
-}
-
-static Color4f get_loop_color(const MLoopCol &col)
+static ColorGeometry4f get_loop_color(const MLoopCol &col)
{
- Color4f value;
- rgba_uchar_to_float(value, &col.r);
- return value;
+ ColorGeometry4b encoded_color = ColorGeometry4b(col.r, col.g, col.b, col.a);
+ ColorGeometry4f linear_color = encoded_color.decode();
+ return linear_color;
}
-static void set_loop_color(MLoopCol &col, const Color4f &value)
+static void set_loop_color(MLoopCol &col, ColorGeometry4f linear_color)
{
- rgba_float_to_uchar(&col.r, value);
-}
-
-static ReadAttributePtr make_vertex_color_read_attribute(const void *data, const int domain_size)
-{
- return std::make_unique<DerivedArrayReadAttribute<MLoopCol, Color4f, get_loop_color>>(
- ATTR_DOMAIN_CORNER, Span((const MLoopCol *)data, domain_size));
-}
-
-static WriteAttributePtr make_vertex_color_write_attribute(void *data, const int domain_size)
-{
- return std::make_unique<
- DerivedArrayWriteAttribute<MLoopCol, Color4f, get_loop_color, set_loop_color>>(
- ATTR_DOMAIN_CORNER, MutableSpan((MLoopCol *)data, domain_size));
+ ColorGeometry4b encoded_color = linear_color.encode();
+ col.r = encoded_color.r;
+ col.g = encoded_color.g;
+ col.b = encoded_color.b;
+ col.a = encoded_color.a;
}
static float get_crease(const MEdge &edge)
@@ -859,83 +794,62 @@ static float get_crease(const MEdge &edge)
return edge.crease / 255.0f;
}
-static void set_crease(MEdge &edge, const float &value)
+static void set_crease(MEdge &edge, float value)
{
edge.crease = round_fl_to_uchar_clamp(value * 255.0f);
}
-static ReadAttributePtr make_crease_read_attribute(const void *data, const int domain_size)
-{
- return std::make_unique<DerivedArrayReadAttribute<MEdge, float, get_crease>>(
- ATTR_DOMAIN_EDGE, Span((const MEdge *)data, domain_size));
-}
-
-static WriteAttributePtr make_crease_write_attribute(void *data, const int domain_size)
-{
- return std::make_unique<DerivedArrayWriteAttribute<MEdge, float, get_crease, set_crease>>(
- ATTR_DOMAIN_EDGE, MutableSpan((MEdge *)data, domain_size));
-}
-
-class VertexWeightWriteAttribute final : public WriteAttribute {
+class VMutableArray_For_VertexWeights final : public VMutableArray<float> {
private:
MDeformVert *dverts_;
const int dvert_index_;
public:
- VertexWeightWriteAttribute(MDeformVert *dverts, const int totvert, const int dvert_index)
- : WriteAttribute(ATTR_DOMAIN_POINT, CPPType::get<float>(), totvert),
- dverts_(dverts),
- dvert_index_(dvert_index)
+ VMutableArray_For_VertexWeights(MDeformVert *dverts, const int totvert, const int dvert_index)
+ : VMutableArray<float>(totvert), dverts_(dverts), dvert_index_(dvert_index)
{
}
- void get_internal(const int64_t index, void *r_value) const override
+ float get_impl(const int64_t index) const override
{
- get_internal(dverts_, dvert_index_, index, r_value);
+ return get_internal(dverts_, dvert_index_, index);
}
- void set_internal(const int64_t index, const void *value) override
+ void set_impl(const int64_t index, const float value) override
{
MDeformWeight *weight = BKE_defvert_ensure_index(&dverts_[index], dvert_index_);
- weight->weight = *reinterpret_cast<const float *>(value);
+ weight->weight = value;
}
- static void get_internal(const MDeformVert *dverts,
- const int dvert_index,
- const int64_t index,
- void *r_value)
+ static float get_internal(const MDeformVert *dverts, const int dvert_index, const int64_t index)
{
if (dverts == nullptr) {
- *(float *)r_value = 0.0f;
- return;
+ return 0.0f;
}
const MDeformVert &dvert = dverts[index];
for (const MDeformWeight &weight : Span(dvert.dw, dvert.totweight)) {
if (weight.def_nr == dvert_index) {
- *(float *)r_value = weight.weight;
- return;
+ return weight.weight;
}
}
- *(float *)r_value = 0.0f;
+ return 0.0f;
}
};
-class VertexWeightReadAttribute final : public ReadAttribute {
+class VArray_For_VertexWeights final : public VArray<float> {
private:
const MDeformVert *dverts_;
const int dvert_index_;
public:
- VertexWeightReadAttribute(const MDeformVert *dverts, const int totvert, const int dvert_index)
- : ReadAttribute(ATTR_DOMAIN_POINT, CPPType::get<float>(), totvert),
- dverts_(dverts),
- dvert_index_(dvert_index)
+ VArray_For_VertexWeights(const MDeformVert *dverts, const int totvert, const int dvert_index)
+ : VArray<float>(totvert), dverts_(dverts), dvert_index_(dvert_index)
{
}
- void get_internal(const int64_t index, void *r_value) const override
+ float get_impl(const int64_t index) const override
{
- VertexWeightWriteAttribute::get_internal(dverts_, dvert_index_, index, r_value);
+ return VMutableArray_For_VertexWeights::get_internal(dverts_, dvert_index_, index);
}
};
@@ -944,8 +858,8 @@ class VertexWeightReadAttribute final : public ReadAttribute {
*/
class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
public:
- ReadAttributePtr try_get_for_read(const GeometryComponent &component,
- const StringRef attribute_name) const final
+ ReadAttributeLookup try_get_for_read(const GeometryComponent &component,
+ const StringRef attribute_name) const final
{
BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH);
const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
@@ -957,15 +871,17 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
}
if (mesh == nullptr || mesh->dvert == nullptr) {
static const float default_value = 0.0f;
- return std::make_unique<ConstantReadAttribute>(
- ATTR_DOMAIN_POINT, mesh->totvert, CPPType::get<float>(), &default_value);
+ return {std::make_unique<fn::GVArray_For_SingleValueRef>(
+ CPPType::get<float>(), mesh->totvert, &default_value),
+ ATTR_DOMAIN_POINT};
}
- return std::make_unique<VertexWeightReadAttribute>(
- mesh->dvert, mesh->totvert, vertex_group_index);
+ return {std::make_unique<fn::GVArray_For_EmbeddedVArray<float, VArray_For_VertexWeights>>(
+ mesh->totvert, mesh->dvert, mesh->totvert, vertex_group_index),
+ ATTR_DOMAIN_POINT};
}
- WriteAttributePtr try_get_for_write(GeometryComponent &component,
- const StringRef attribute_name) const final
+ WriteAttributeLookup try_get_for_write(GeometryComponent &component,
+ const StringRef attribute_name) const final
{
BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH);
MeshComponent &mesh_component = static_cast<MeshComponent &>(component);
@@ -986,8 +902,11 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
mesh->dvert = (MDeformVert *)CustomData_duplicate_referenced_layer(
&mesh->vdata, CD_MDEFORMVERT, mesh->totvert);
}
- return std::make_unique<blender::bke::VertexWeightWriteAttribute>(
- mesh->dvert, mesh->totvert, vertex_group_index);
+ return {
+ std::make_unique<
+ fn::GVMutableArray_For_EmbeddedVMutableArray<float, VMutableArray_For_VertexWeights>>(
+ mesh->totvert, mesh->dvert, mesh->totvert, vertex_group_index),
+ ATTR_DOMAIN_POINT};
}
bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final
@@ -1049,7 +968,7 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider {
{
}
- ReadAttributePtr try_get_for_read(const GeometryComponent &component) const final
+ GVArrayPtr try_get_for_read(const GeometryComponent &component) const final
{
const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
const Mesh *mesh = mesh_component.get_for_read();
@@ -1062,8 +981,8 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider {
CustomData_has_layer(&mesh->pdata, CD_NORMAL)) {
const void *data = CustomData_get_layer(&mesh->pdata, CD_NORMAL);
- return std::make_unique<ArrayReadAttribute<float3>>(
- ATTR_DOMAIN_FACE, Span<float3>((const float3 *)data, mesh->totpoly));
+ return std::make_unique<fn::GVArray_For_Span<float3>>(
+ Span<float3>((const float3 *)data, mesh->totpoly));
}
Array<float3> normals(mesh->totpoly);
@@ -1072,10 +991,10 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider {
BKE_mesh_calc_poly_normal(poly, &mesh->mloop[poly->loopstart], mesh->mvert, normals[i]);
}
- return std::make_unique<OwnedArrayReadAttribute<float3>>(ATTR_DOMAIN_FACE, std::move(normals));
+ return std::make_unique<fn::GVArray_For_ArrayContainer<Array<float3>>>(std::move(normals));
}
- WriteAttributePtr try_get_for_write(GeometryComponent &UNUSED(component)) const final
+ GVMutableArrayPtr try_get_for_write(GeometryComponent &UNUSED(component)) const final
{
return {};
}
@@ -1085,7 +1004,8 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider {
return false;
}
- bool try_create(GeometryComponent &UNUSED(component)) const final
+ bool try_create(GeometryComponent &UNUSED(component),
+ const AttributeInit &UNUSED(initializer)) const final
{
return false;
}
@@ -1136,69 +1056,75 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
#undef MAKE_CONST_CUSTOM_DATA_GETTER
#undef MAKE_MUTABLE_CUSTOM_DATA_GETTER
- static BuiltinCustomDataLayerProvider position("position",
- ATTR_DOMAIN_POINT,
- CD_PROP_FLOAT3,
- CD_MVERT,
- BuiltinAttributeProvider::NonCreatable,
- BuiltinAttributeProvider::Writable,
- BuiltinAttributeProvider::NonDeletable,
- point_access,
- make_vertex_position_read_attribute,
- make_vertex_position_write_attribute,
- tag_normals_dirty_when_writing_position);
+ static BuiltinCustomDataLayerProvider position(
+ "position",
+ ATTR_DOMAIN_POINT,
+ CD_PROP_FLOAT3,
+ CD_MVERT,
+ BuiltinAttributeProvider::NonCreatable,
+ BuiltinAttributeProvider::Writable,
+ BuiltinAttributeProvider::NonDeletable,
+ point_access,
+ make_derived_read_attribute<MVert, float3, get_vertex_position>,
+ make_derived_write_attribute<MVert, float3, get_vertex_position, set_vertex_position>,
+ tag_normals_dirty_when_writing_position);
static NormalAttributeProvider normal;
- static BuiltinCustomDataLayerProvider material_index("material_index",
- ATTR_DOMAIN_FACE,
- CD_PROP_INT32,
- CD_MPOLY,
- BuiltinAttributeProvider::NonCreatable,
- BuiltinAttributeProvider::Writable,
- BuiltinAttributeProvider::NonDeletable,
- face_access,
- make_material_index_read_attribute,
- make_material_index_write_attribute,
- nullptr);
-
- static BuiltinCustomDataLayerProvider shade_smooth("shade_smooth",
- ATTR_DOMAIN_FACE,
- CD_PROP_BOOL,
- CD_MPOLY,
- BuiltinAttributeProvider::NonCreatable,
- BuiltinAttributeProvider::Writable,
- BuiltinAttributeProvider::NonDeletable,
- face_access,
- make_shade_smooth_read_attribute,
- make_shade_smooth_write_attribute,
- nullptr);
-
- static BuiltinCustomDataLayerProvider crease("crease",
- ATTR_DOMAIN_EDGE,
- CD_PROP_FLOAT,
- CD_MEDGE,
- BuiltinAttributeProvider::NonCreatable,
- BuiltinAttributeProvider::Writable,
- BuiltinAttributeProvider::NonDeletable,
- edge_access,
- make_crease_read_attribute,
- make_crease_write_attribute,
- nullptr);
-
- static NamedLegacyCustomDataProvider uvs(ATTR_DOMAIN_CORNER,
- CD_PROP_FLOAT2,
- CD_MLOOPUV,
- corner_access,
- make_uvs_read_attribute,
- make_uvs_write_attribute);
-
- static NamedLegacyCustomDataProvider vertex_colors(ATTR_DOMAIN_CORNER,
- CD_PROP_COLOR,
- CD_MLOOPCOL,
- corner_access,
- make_vertex_color_read_attribute,
- make_vertex_color_write_attribute);
+ static BuiltinCustomDataLayerProvider material_index(
+ "material_index",
+ ATTR_DOMAIN_FACE,
+ CD_PROP_INT32,
+ CD_MPOLY,
+ BuiltinAttributeProvider::NonCreatable,
+ BuiltinAttributeProvider::Writable,
+ BuiltinAttributeProvider::NonDeletable,
+ face_access,
+ make_derived_read_attribute<MPoly, int, get_material_index>,
+ make_derived_write_attribute<MPoly, int, get_material_index, set_material_index>,
+ nullptr);
+
+ static BuiltinCustomDataLayerProvider shade_smooth(
+ "shade_smooth",
+ ATTR_DOMAIN_FACE,
+ CD_PROP_BOOL,
+ CD_MPOLY,
+ BuiltinAttributeProvider::NonCreatable,
+ BuiltinAttributeProvider::Writable,
+ BuiltinAttributeProvider::NonDeletable,
+ face_access,
+ make_derived_read_attribute<MPoly, bool, get_shade_smooth>,
+ make_derived_write_attribute<MPoly, bool, get_shade_smooth, set_shade_smooth>,
+ nullptr);
+
+ static BuiltinCustomDataLayerProvider crease(
+ "crease",
+ ATTR_DOMAIN_EDGE,
+ CD_PROP_FLOAT,
+ CD_MEDGE,
+ BuiltinAttributeProvider::NonCreatable,
+ BuiltinAttributeProvider::Writable,
+ BuiltinAttributeProvider::NonDeletable,
+ edge_access,
+ make_derived_read_attribute<MEdge, float, get_crease>,
+ make_derived_write_attribute<MEdge, float, get_crease, set_crease>,
+ nullptr);
+
+ static NamedLegacyCustomDataProvider uvs(
+ ATTR_DOMAIN_CORNER,
+ CD_PROP_FLOAT2,
+ CD_MLOOPUV,
+ corner_access,
+ make_derived_read_attribute<MLoopUV, float2, get_loop_uv>,
+ make_derived_write_attribute<MLoopUV, float2, get_loop_uv, set_loop_uv>);
+
+ static NamedLegacyCustomDataProvider vertex_colors(
+ ATTR_DOMAIN_CORNER,
+ CD_PROP_COLOR,
+ CD_MLOOPCOL,
+ corner_access,
+ make_derived_read_attribute<MLoopCol, ColorGeometry4f, get_loop_color>,
+ make_derived_write_attribute<MLoopCol, ColorGeometry4f, get_loop_color, set_loop_color>);
static VertexGroupsAttributeProvider vertex_groups;
static CustomDataAttributeProvider corner_custom_data(ATTR_DOMAIN_CORNER, corner_access);
diff --git a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc
index 32c4ee8a6a6..6c4af7a6d23 100644
--- a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc
+++ b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc
@@ -107,6 +107,20 @@ bool PointCloudComponent::is_empty() const
return pointcloud_ == nullptr;
}
+bool PointCloudComponent::owns_direct_data() const
+{
+ return ownership_ == GeometryOwnershipType::Owned;
+}
+
+void PointCloudComponent::ensure_owns_direct_data()
+{
+ BLI_assert(this->is_mutable());
+ if (ownership_ != GeometryOwnershipType::Owned) {
+ pointcloud_ = BKE_pointcloud_copy_for_eval(pointcloud_, false);
+ ownership_ = GeometryOwnershipType::Owned;
+ }
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -126,16 +140,17 @@ int PointCloudComponent::attribute_domain_size(const AttributeDomain domain) con
namespace blender::bke {
-template<typename T, AttributeDomain Domain>
-static ReadAttributePtr make_array_read_attribute(const void *data, const int domain_size)
+template<typename T>
+static GVArrayPtr make_array_read_attribute(const void *data, const int domain_size)
{
- return std::make_unique<ArrayReadAttribute<T>>(Domain, Span<T>((const T *)data, domain_size));
+ return std::make_unique<fn::GVArray_For_Span<T>>(Span<T>((const T *)data, domain_size));
}
-template<typename T, AttributeDomain Domain>
-static WriteAttributePtr make_array_write_attribute(void *data, const int domain_size)
+template<typename T>
+static GVMutableArrayPtr make_array_write_attribute(void *data, const int domain_size)
{
- return std::make_unique<ArrayWriteAttribute<T>>(Domain, MutableSpan<T>((T *)data, domain_size));
+ return std::make_unique<fn::GVMutableArray_For_MutableSpan<T>>(
+ MutableSpan<T>((T *)data, domain_size));
}
/**
@@ -165,30 +180,28 @@ static ComponentAttributeProviders create_attribute_providers_for_point_cloud()
},
update_custom_data_pointers};
- static BuiltinCustomDataLayerProvider position(
- "position",
- ATTR_DOMAIN_POINT,
- CD_PROP_FLOAT3,
- CD_PROP_FLOAT3,
- BuiltinAttributeProvider::NonCreatable,
- BuiltinAttributeProvider::Writable,
- BuiltinAttributeProvider::NonDeletable,
- point_access,
- make_array_read_attribute<float3, ATTR_DOMAIN_POINT>,
- make_array_write_attribute<float3, ATTR_DOMAIN_POINT>,
- nullptr);
- static BuiltinCustomDataLayerProvider radius(
- "radius",
- ATTR_DOMAIN_POINT,
- CD_PROP_FLOAT,
- CD_PROP_FLOAT,
- BuiltinAttributeProvider::Creatable,
- BuiltinAttributeProvider::Writable,
- BuiltinAttributeProvider::Deletable,
- point_access,
- make_array_read_attribute<float, ATTR_DOMAIN_POINT>,
- make_array_write_attribute<float, ATTR_DOMAIN_POINT>,
- nullptr);
+ static BuiltinCustomDataLayerProvider position("position",
+ ATTR_DOMAIN_POINT,
+ CD_PROP_FLOAT3,
+ CD_PROP_FLOAT3,
+ BuiltinAttributeProvider::NonCreatable,
+ BuiltinAttributeProvider::Writable,
+ BuiltinAttributeProvider::NonDeletable,
+ point_access,
+ make_array_read_attribute<float3>,
+ make_array_write_attribute<float3>,
+ nullptr);
+ static BuiltinCustomDataLayerProvider radius("radius",
+ ATTR_DOMAIN_POINT,
+ CD_PROP_FLOAT,
+ CD_PROP_FLOAT,
+ BuiltinAttributeProvider::Creatable,
+ BuiltinAttributeProvider::Writable,
+ BuiltinAttributeProvider::Deletable,
+ point_access,
+ make_array_read_attribute<float>,
+ make_array_write_attribute<float>,
+ nullptr);
static CustomDataAttributeProvider point_custom_data(ATTR_DOMAIN_POINT, point_access);
return ComponentAttributeProviders({&position, &radius}, {&point_custom_data});
}
diff --git a/source/blender/blenkernel/intern/geometry_component_volume.cc b/source/blender/blenkernel/intern/geometry_component_volume.cc
index fd2327e0bf5..94ed07a63de 100644
--- a/source/blender/blenkernel/intern/geometry_component_volume.cc
+++ b/source/blender/blenkernel/intern/geometry_component_volume.cc
@@ -97,4 +97,18 @@ Volume *VolumeComponent::get_for_write()
return volume_;
}
+bool VolumeComponent::owns_direct_data() const
+{
+ return ownership_ == GeometryOwnershipType::Owned;
+}
+
+void VolumeComponent::ensure_owns_direct_data()
+{
+ BLI_assert(this->is_mutable());
+ if (ownership_ != GeometryOwnershipType::Owned) {
+ volume_ = BKE_volume_copy_for_eval(volume_, false);
+ ownership_ = GeometryOwnershipType::Owned;
+ }
+}
+
/** \} */
diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc
index a09c3f5f3d3..3d85118deee 100644
--- a/source/blender/blenkernel/intern/geometry_set.cc
+++ b/source/blender/blenkernel/intern/geometry_set.cc
@@ -24,6 +24,7 @@
#include "BKE_mesh_wrapper.h"
#include "BKE_modifier.h"
#include "BKE_pointcloud.h"
+#include "BKE_spline.hh"
#include "BKE_volume.h"
#include "DNA_collection_types.h"
@@ -49,10 +50,6 @@ GeometryComponent::GeometryComponent(GeometryComponentType type) : type_(type)
{
}
-GeometryComponent ::~GeometryComponent()
-{
-}
-
GeometryComponent *GeometryComponent::create(GeometryComponentType component_type)
{
switch (component_type) {
@@ -64,6 +61,8 @@ GeometryComponent *GeometryComponent::create(GeometryComponentType component_typ
return new InstancesComponent();
case GEO_COMPONENT_TYPE_VOLUME:
return new VolumeComponent();
+ case GEO_COMPONENT_TYPE_CURVE:
+ return new CurveComponent();
}
BLI_assert_unreachable();
return nullptr;
@@ -182,6 +181,15 @@ void GeometrySet::compute_boundbox_without_instances(float3 *r_min, float3 *r_ma
if (mesh != nullptr) {
BKE_mesh_wrapper_minmax(mesh, *r_min, *r_max);
}
+ const Volume *volume = this->get_volume_for_read();
+ if (volume != nullptr) {
+ BKE_volume_min_max(volume, *r_min, *r_max);
+ }
+ const CurveEval *curve = this->get_curve_for_read();
+ if (curve != nullptr) {
+ /* Using the evaluated positions is somewhat arbitrary, but it is probably expected. */
+ curve->bounds_min_max(*r_min, *r_max, true);
+ }
}
std::ostream &operator<<(std::ostream &stream, const GeometrySet &geometry_set)
@@ -205,6 +213,25 @@ uint64_t GeometrySet::hash() const
return reinterpret_cast<uint64_t>(this);
}
+/* Remove all geometry components from the geometry set. */
+void GeometrySet::clear()
+{
+ components_.clear();
+}
+
+/* Make sure that the geometry can be cached. This does not ensure ownership of object/collection
+ * instances. */
+void GeometrySet::ensure_owns_direct_data()
+{
+ for (GeometryComponentType type : components_.keys()) {
+ const GeometryComponent *component = this->get_component_for_read(type);
+ if (!component->owns_direct_data()) {
+ GeometryComponent &component_for_write = this->get_component_for_write(type);
+ component_for_write.ensure_owns_direct_data();
+ }
+ }
+}
+
/* Returns a read-only mesh or null. */
const Mesh *GeometrySet::get_mesh_for_read() const
{
@@ -233,6 +260,13 @@ const Volume *GeometrySet::get_volume_for_read() const
return (component == nullptr) ? nullptr : component->get_for_read();
}
+/* Returns a read-only curve or null. */
+const CurveEval *GeometrySet::get_curve_for_read() const
+{
+ const CurveComponent *component = this->get_component_for_read<CurveComponent>();
+ return (component == nullptr) ? nullptr : component->get_for_read();
+}
+
/* Returns true when the geometry set has a point cloud component that has a point cloud. */
bool GeometrySet::has_pointcloud() const
{
@@ -254,6 +288,13 @@ bool GeometrySet::has_volume() const
return component != nullptr && component->has_volume();
}
+/* Returns true when the geometry set has a curve component that has a curve. */
+bool GeometrySet::has_curve() const
+{
+ const CurveComponent *component = this->get_component_for_read<CurveComponent>();
+ return component != nullptr && component->has_curve();
+}
+
/* Create a new geometry set that only contains the given mesh. */
GeometrySet GeometrySet::create_with_mesh(Mesh *mesh, GeometryOwnershipType ownership)
{
@@ -273,6 +314,15 @@ GeometrySet GeometrySet::create_with_pointcloud(PointCloud *pointcloud,
return geometry_set;
}
+/* Create a new geometry set that only contains the given curve. */
+GeometrySet GeometrySet::create_with_curve(CurveEval *curve, GeometryOwnershipType ownership)
+{
+ GeometrySet geometry_set;
+ CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>();
+ component.replace(curve, ownership);
+ return geometry_set;
+}
+
/* Clear the existing mesh and replace it with the given one. */
void GeometrySet::replace_mesh(Mesh *mesh, GeometryOwnershipType ownership)
{
@@ -280,6 +330,13 @@ void GeometrySet::replace_mesh(Mesh *mesh, GeometryOwnershipType ownership)
component.replace(mesh, ownership);
}
+/* Clear the existing curve and replace it with the given one. */
+void GeometrySet::replace_curve(CurveEval *curve, GeometryOwnershipType ownership)
+{
+ CurveComponent &component = this->get_component_for_write<CurveComponent>();
+ component.replace(curve, ownership);
+}
+
/* Clear the existing point cloud and replace with the given one. */
void GeometrySet::replace_pointcloud(PointCloud *pointcloud, GeometryOwnershipType ownership)
{
@@ -287,6 +344,13 @@ void GeometrySet::replace_pointcloud(PointCloud *pointcloud, GeometryOwnershipTy
pointcloud_component.replace(pointcloud, ownership);
}
+/* Clear the existing volume and replace with the given one. */
+void GeometrySet::replace_volume(Volume *volume, GeometryOwnershipType ownership)
+{
+ VolumeComponent &volume_component = this->get_component_for_write<VolumeComponent>();
+ volume_component.replace(volume, ownership);
+}
+
/* Returns a mutable mesh or null. No ownership is transferred. */
Mesh *GeometrySet::get_mesh_for_write()
{
@@ -308,6 +372,13 @@ Volume *GeometrySet::get_volume_for_write()
return component.get_for_write();
}
+/* Returns a mutable curve or null. No ownership is transferred. */
+CurveEval *GeometrySet::get_curve_for_write()
+{
+ CurveComponent &component = this->get_component_for_write<CurveComponent>();
+ return component.get_for_write();
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -324,19 +395,4 @@ bool BKE_geometry_set_has_instances(const GeometrySet *geometry_set)
return geometry_set->get_component_for_read<InstancesComponent>() != nullptr;
}
-int BKE_geometry_set_instances(const GeometrySet *geometry_set,
- float (**r_transforms)[4][4],
- const int **r_almost_unique_ids,
- InstancedData **r_instanced_data)
-{
- const InstancesComponent *component = geometry_set->get_component_for_read<InstancesComponent>();
- if (component == nullptr) {
- return 0;
- }
- *r_transforms = (float(*)[4][4])component->transforms().data();
- *r_instanced_data = (InstancedData *)component->instanced_data().data();
- *r_almost_unique_ids = (const int *)component->almost_unique_ids().data();
- return component->instances_amount();
-}
-
/** \} */
diff --git a/source/blender/blenkernel/intern/geometry_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc
index 24a402ac545..847df75c8cb 100644
--- a/source/blender/blenkernel/intern/geometry_set_instances.cc
+++ b/source/blender/blenkernel/intern/geometry_set_instances.cc
@@ -15,10 +15,12 @@
*/
#include "BKE_geometry_set_instances.hh"
+#include "BKE_material.h"
#include "BKE_mesh.h"
#include "BKE_mesh_wrapper.h"
#include "BKE_modifier.h"
#include "BKE_pointcloud.h"
+#include "BKE_spline.hh"
#include "DNA_collection_types.h"
#include "DNA_mesh_types.h"
@@ -36,30 +38,55 @@ static void geometry_set_collect_recursive_collection(const Collection &collecti
const float4x4 &transform,
Vector<GeometryInstanceGroup> &r_sets);
+static void add_final_mesh_as_geometry_component(const Object &object, GeometrySet &geometry_set)
+{
+ Mesh *mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(&const_cast<Object &>(object),
+ false);
+
+ if (mesh != nullptr) {
+ BKE_mesh_wrapper_ensure_mdata(mesh);
+
+ MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
+ mesh_component.replace(mesh, GeometryOwnershipType::ReadOnly);
+ mesh_component.copy_vertex_group_names_from_object(object);
+ }
+}
+
+static void add_curve_data_as_geometry_component(const Object &object, GeometrySet &geometry_set)
+{
+ BLI_assert(object.type == OB_CURVE);
+ if (object.data != nullptr) {
+ std::unique_ptr<CurveEval> curve = curve_eval_from_dna_curve(*(Curve *)object.data);
+ CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>();
+ curve_component.replace(curve.release(), GeometryOwnershipType::Owned);
+ }
+}
+
/**
* \note This doesn't extract instances from the "dupli" system for non-geometry-nodes instances.
*/
static GeometrySet object_get_geometry_set_for_read(const Object &object)
{
- /* Objects evaluated with a nodes modifier will have a geometry set already. */
+ if (object.type == OB_MESH && object.mode == OB_MODE_EDIT) {
+ GeometrySet geometry_set;
+ if (object.runtime.geometry_set_eval != nullptr) {
+ /* `geometry_set_eval` only contains non-mesh components, see `editbmesh_build_data`. */
+ geometry_set = *object.runtime.geometry_set_eval;
+ }
+ add_final_mesh_as_geometry_component(object, geometry_set);
+ return geometry_set;
+ }
if (object.runtime.geometry_set_eval != nullptr) {
return *object.runtime.geometry_set_eval;
}
/* Otherwise, construct a new geometry set with the component based on the object type. */
- GeometrySet new_geometry_set;
-
+ GeometrySet geometry_set;
if (object.type == OB_MESH) {
- Mesh *mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(
- &const_cast<Object &>(object), false);
-
- if (mesh != nullptr) {
- BKE_mesh_wrapper_ensure_mdata(mesh);
-
- MeshComponent &mesh_component = new_geometry_set.get_component_for_write<MeshComponent>();
- mesh_component.replace(mesh, GeometryOwnershipType::ReadOnly);
- mesh_component.copy_vertex_group_names_from_object(object);
- }
+ add_final_mesh_as_geometry_component(object, geometry_set);
+ }
+ else if (object.type == OB_CURVE) {
+ add_curve_data_as_geometry_component(object, geometry_set);
}
/* TODO: Cover the case of point-clouds without modifiers-- they may not be covered by the
@@ -68,7 +95,7 @@ static GeometrySet object_get_geometry_set_for_read(const Object &object)
/* TODO: Add volume support. */
/* Return by value since there is not always an existing geometry set owned elsewhere to use. */
- return new_geometry_set;
+ return geometry_set;
}
static void geometry_set_collect_recursive_collection_instance(
@@ -123,21 +150,28 @@ static void geometry_set_collect_recursive(const GeometrySet &geometry_set,
const InstancesComponent &instances_component =
*geometry_set.get_component_for_read<InstancesComponent>();
- Span<float4x4> transforms = instances_component.transforms();
- Span<InstancedData> instances = instances_component.instanced_data();
- for (const int i : instances.index_range()) {
- const InstancedData &data = instances[i];
+ Span<float4x4> transforms = instances_component.instance_transforms();
+ Span<int> handles = instances_component.instance_reference_handles();
+ Span<InstanceReference> references = instances_component.references();
+ for (const int i : transforms.index_range()) {
+ const InstanceReference &reference = references[handles[i]];
const float4x4 instance_transform = transform * transforms[i];
- if (data.type == INSTANCE_DATA_TYPE_OBJECT) {
- BLI_assert(data.data.object != nullptr);
- const Object &object = *data.data.object;
- geometry_set_collect_recursive_object(object, instance_transform, r_sets);
- }
- else if (data.type == INSTANCE_DATA_TYPE_COLLECTION) {
- BLI_assert(data.data.collection != nullptr);
- const Collection &collection = *data.data.collection;
- geometry_set_collect_recursive_collection_instance(collection, instance_transform, r_sets);
+ switch (reference.type()) {
+ case InstanceReference::Type::Object: {
+ Object &object = reference.object();
+ geometry_set_collect_recursive_object(object, instance_transform, r_sets);
+ break;
+ }
+ case InstanceReference::Type::Collection: {
+ Collection &collection = reference.collection();
+ geometry_set_collect_recursive_collection_instance(
+ collection, instance_transform, r_sets);
+ break;
+ }
+ case InstanceReference::Type::None: {
+ break;
+ }
}
}
}
@@ -153,22 +187,140 @@ static void geometry_set_collect_recursive(const GeometrySet &geometry_set,
*
* \note This doesn't extract instances from the "dupli" system for non-geometry-nodes instances.
*/
-Vector<GeometryInstanceGroup> geometry_set_gather_instances(const GeometrySet &geometry_set)
+void geometry_set_gather_instances(const GeometrySet &geometry_set,
+ Vector<GeometryInstanceGroup> &r_instance_groups)
{
- Vector<GeometryInstanceGroup> result_vector;
-
float4x4 unit_transform;
unit_m4(unit_transform.values);
- geometry_set_collect_recursive(geometry_set, unit_transform, result_vector);
+ geometry_set_collect_recursive(geometry_set, unit_transform, r_instance_groups);
+}
- return result_vector;
+static bool collection_instance_attribute_foreach(const Collection &collection,
+ const AttributeForeachCallback callback,
+ const int limit,
+ int &count);
+
+static bool instances_attribute_foreach_recursive(const GeometrySet &geometry_set,
+ const AttributeForeachCallback callback,
+ const int limit,
+ int &count);
+
+static bool object_instance_attribute_foreach(const Object &object,
+ const AttributeForeachCallback callback,
+ const int limit,
+ int &count)
+{
+ GeometrySet instance_geometry_set = object_get_geometry_set_for_read(object);
+ if (!instances_attribute_foreach_recursive(instance_geometry_set, callback, limit, count)) {
+ return false;
+ }
+
+ if (object.type == OB_EMPTY) {
+ const Collection *collection_instance = object.instance_collection;
+ if (collection_instance != nullptr) {
+ if (!collection_instance_attribute_foreach(*collection_instance, callback, limit, count)) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+static bool collection_instance_attribute_foreach(const Collection &collection,
+ const AttributeForeachCallback callback,
+ const int limit,
+ int &count)
+{
+ LISTBASE_FOREACH (const CollectionObject *, collection_object, &collection.gobject) {
+ BLI_assert(collection_object->ob != nullptr);
+ const Object &object = *collection_object->ob;
+ if (!object_instance_attribute_foreach(object, callback, limit, count)) {
+ return false;
+ }
+ }
+ LISTBASE_FOREACH (const CollectionChild *, collection_child, &collection.children) {
+ BLI_assert(collection_child->collection != nullptr);
+ const Collection &collection = *collection_child->collection;
+ if (!collection_instance_attribute_foreach(collection, callback, limit, count)) {
+ return false;
+ }
+ }
+ return true;
}
-void gather_attribute_info(Map<std::string, AttributeKind> &attributes,
- Span<GeometryComponentType> component_types,
- Span<GeometryInstanceGroup> set_groups,
- const Set<std::string> &ignored_attributes)
+/**
+ * \return True if the recursive iteration should continue, false if the limit is reached or the
+ * callback has returned false indicating it should stop.
+ */
+static bool instances_attribute_foreach_recursive(const GeometrySet &geometry_set,
+ const AttributeForeachCallback callback,
+ const int limit,
+ int &count)
+{
+ for (const GeometryComponent *component : geometry_set.get_components_for_read()) {
+ if (!component->attribute_foreach(callback)) {
+ return false;
+ }
+ }
+
+ /* Now that this this geometry set is visited, increase the count and check with the limit. */
+ if (limit > 0 && count++ > limit) {
+ return false;
+ }
+
+ const InstancesComponent *instances_component =
+ geometry_set.get_component_for_read<InstancesComponent>();
+ if (instances_component == nullptr) {
+ return true;
+ }
+
+ for (const InstanceReference &reference : instances_component->references()) {
+ switch (reference.type()) {
+ case InstanceReference::Type::Object: {
+ const Object &object = reference.object();
+ if (!object_instance_attribute_foreach(object, callback, limit, count)) {
+ return false;
+ }
+ break;
+ }
+ case InstanceReference::Type::Collection: {
+ const Collection &collection = reference.collection();
+ if (!collection_instance_attribute_foreach(collection, callback, limit, count)) {
+ return false;
+ }
+ break;
+ }
+ case InstanceReference::Type::None: {
+ break;
+ }
+ }
+ }
+
+ return true;
+}
+
+/**
+ * Call the callback on all of this geometry set's components, including geometry sets from
+ * instances and recursive instances. This is necessary to access available attributes without
+ * making all of the set's geometry real.
+ *
+ * \param limit: The total number of geometry sets to visit before returning early. This is used
+ * to avoid looking through too many geometry sets recursively, as an explicit tradeoff in favor
+ * of performance at the cost of visiting every unique attribute.
+ */
+void geometry_set_instances_attribute_foreach(const GeometrySet &geometry_set,
+ const AttributeForeachCallback callback,
+ const int limit)
+{
+ int count = 0;
+ instances_attribute_foreach_recursive(geometry_set, callback, limit, count);
+}
+
+void geometry_set_gather_instances_attribute_info(Span<GeometryInstanceGroup> set_groups,
+ Span<GeometryComponentType> component_types,
+ const Set<std::string> &ignored_attributes,
+ Map<std::string, AttributeKind> &r_attributes)
{
for (const GeometryInstanceGroup &set_group : set_groups) {
const GeometrySet &set = set_group.geometry_set;
@@ -192,7 +344,7 @@ void gather_attribute_info(Map<std::string, AttributeKind> &attributes,
{attribute_kind->data_type, meta_data.data_type});
};
- attributes.add_or_modify(name, add_info, modify_info);
+ r_attributes.add_or_modify(name, add_info, modify_info);
return true;
});
}
@@ -210,6 +362,8 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<GeometryInstanceGrou
int64_t cd_dirty_poly = 0;
int64_t cd_dirty_edge = 0;
int64_t cd_dirty_loop = 0;
+ VectorSet<Material *> materials;
+
for (const GeometryInstanceGroup &set_group : set_groups) {
const GeometrySet &set = set_group.geometry_set;
const int tot_transforms = set_group.transforms.size();
@@ -223,6 +377,10 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<GeometryInstanceGrou
cd_dirty_poly |= mesh.runtime.cd_dirty_poly;
cd_dirty_edge |= mesh.runtime.cd_dirty_edge;
cd_dirty_loop |= mesh.runtime.cd_dirty_loop;
+ for (const int slot_index : IndexRange(mesh.totcol)) {
+ Material *material = mesh.mat[slot_index];
+ materials.add(material);
+ }
}
if (convert_points_to_vertices && set.has_pointcloud()) {
const PointCloud &pointcloud = *set.get_pointcloud_for_read();
@@ -245,6 +403,10 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<GeometryInstanceGrou
break;
}
}
+ for (const int i : IndexRange(materials.size())) {
+ Material *material = materials[i];
+ BKE_id_material_eval_assign(&new_mesh->id, i + 1, material);
+ }
new_mesh->runtime.cd_dirty_vert = cd_dirty_vert;
new_mesh->runtime.cd_dirty_poly = cd_dirty_poly;
new_mesh->runtime.cd_dirty_edge = cd_dirty_edge;
@@ -258,6 +420,14 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<GeometryInstanceGrou
const GeometrySet &set = set_group.geometry_set;
if (set.has_mesh()) {
const Mesh &mesh = *set.get_mesh_for_read();
+
+ Array<int> material_index_map(mesh.totcol);
+ for (const int i : IndexRange(mesh.totcol)) {
+ Material *material = mesh.mat[i];
+ const int new_material_index = materials.index_of(material);
+ material_index_map[i] = new_material_index;
+ }
+
for (const float4x4 &transform : set_group.transforms) {
for (const int i : IndexRange(mesh.totvert)) {
const MVert &old_vert = mesh.mvert[i];
@@ -287,6 +457,13 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<GeometryInstanceGrou
MPoly &new_poly = new_mesh->mpoly[poly_offset + i];
new_poly = old_poly;
new_poly.loopstart += loop_offset;
+ if (old_poly.mat_nr >= 0 && old_poly.mat_nr < mesh.totcol) {
+ new_poly.mat_nr = material_index_map[new_poly.mat_nr];
+ }
+ else {
+ /* The material index was invalid before. */
+ new_poly.mat_nr = 0;
+ }
}
vert_offset += mesh.totvert;
@@ -295,6 +472,11 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<GeometryInstanceGrou
poly_offset += mesh.totpoly;
}
}
+
+ const float3 point_normal{0.0f, 0.0f, 1.0f};
+ short point_normal_short[3];
+ normal_float_to_short_v3(point_normal_short, point_normal);
+
if (convert_points_to_vertices && set.has_pointcloud()) {
const PointCloud &pointcloud = *set.get_pointcloud_for_read();
for (const float4x4 &transform : set_group.transforms) {
@@ -303,6 +485,7 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<GeometryInstanceGrou
const float3 old_position = pointcloud.co[i];
const float3 new_position = transform * old_position;
copy_v3_v3(new_vert.co, new_position);
+ memcpy(&new_vert.no, point_normal_short, sizeof(point_normal_short));
}
vert_offset += pointcloud.totpoint;
}
@@ -324,13 +507,15 @@ static void join_attributes(Span<GeometryInstanceGroup> set_groups,
const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(data_type_output);
BLI_assert(cpp_type != nullptr);
- result.attribute_try_create(entry.key, domain_output, data_type_output);
- WriteAttributePtr write_attribute = result.attribute_try_get_for_write(name);
- if (!write_attribute || &write_attribute->cpp_type() != cpp_type ||
- write_attribute->domain() != domain_output) {
+ result.attribute_try_create(
+ entry.key, domain_output, data_type_output, AttributeInitDefault());
+ WriteAttributeLookup write_attribute = result.attribute_try_get_for_write(name);
+ if (!write_attribute || &write_attribute.varray->type() != cpp_type ||
+ write_attribute.domain != domain_output) {
continue;
}
- fn::GMutableSpan dst_span = write_attribute->get_span_for_write_only();
+
+ fn::GVMutableArray_GSpan dst_span{*write_attribute.varray};
int offset = 0;
for (const GeometryInstanceGroup &set_group : set_groups) {
@@ -342,11 +527,11 @@ static void join_attributes(Span<GeometryInstanceGroup> set_groups,
if (domain_size == 0) {
continue; /* Domain size is 0, so no need to increment the offset. */
}
- ReadAttributePtr source_attribute = component.attribute_try_get_for_read(
+ GVArrayPtr source_attribute = component.attribute_try_get_for_read(
name, domain_output, data_type_output);
if (source_attribute) {
- fn::GSpan src_span = source_attribute->get_span();
+ fn::GVArray_GSpan src_span{*source_attribute};
const void *src_buffer = src_span.data();
for (const int UNUSED(i) : set_group.transforms.index_range()) {
void *dst_buffer = dst_span[offset];
@@ -361,8 +546,48 @@ static void join_attributes(Span<GeometryInstanceGroup> set_groups,
}
}
- write_attribute->apply_span();
+ dst_span.save();
+ }
+}
+
+static CurveEval *join_curve_splines(Span<GeometryInstanceGroup> set_groups)
+{
+ Vector<SplinePtr> new_splines;
+ for (const GeometryInstanceGroup &set_group : set_groups) {
+ const GeometrySet &set = set_group.geometry_set;
+ if (!set.has_curve()) {
+ continue;
+ }
+
+ const CurveEval &source_curve = *set.get_curve_for_read();
+ for (const SplinePtr &source_spline : source_curve.splines()) {
+ for (const float4x4 &transform : set_group.transforms) {
+ SplinePtr new_spline = source_spline->copy();
+ new_spline->transform(transform);
+ new_splines.append(std::move(new_spline));
+ }
+ }
+ }
+ if (new_splines.is_empty()) {
+ return nullptr;
+ }
+
+ CurveEval *new_curve = new CurveEval();
+ for (SplinePtr &new_spline : new_splines) {
+ new_curve->add_spline(std::move(new_spline));
+ }
+
+ for (SplinePtr &spline : new_curve->splines()) {
+ /* Spline instances should have no custom attributes, since they always come
+ * from original objects which currently do not support custom attributes.
+ *
+ * This is only true as long as a #GeometrySet cannot be instanced directly. */
+ BLI_assert(spline->attributes.data.totlayer == 0);
+ UNUSED_VARS_NDEBUG(spline);
}
+
+ new_curve->attributes.reallocate(new_curve->splines().size());
+ return new_curve;
}
static void join_instance_groups_mesh(Span<GeometryInstanceGroup> set_groups,
@@ -386,10 +611,11 @@ static void join_instance_groups_mesh(Span<GeometryInstanceGroup> set_groups,
/* Don't copy attributes that are stored directly in the mesh data structs. */
Map<std::string, AttributeKind> attributes;
- gather_attribute_info(attributes,
- component_types,
- set_groups,
- {"position", "material_index", "normal", "shade_smooth", "crease"});
+ geometry_set_gather_instances_attribute_info(
+ set_groups,
+ component_types,
+ {"position", "material_index", "normal", "shade_smooth", "crease"},
+ attributes);
join_attributes(
set_groups, component_types, attributes, static_cast<GeometryComponent &>(dst_component));
}
@@ -413,7 +639,8 @@ static void join_instance_groups_pointcloud(Span<GeometryInstanceGroup> set_grou
PointCloud *pointcloud = BKE_pointcloud_new_nomain(totpoint);
dst_component.replace(pointcloud);
Map<std::string, AttributeKind> attributes;
- gather_attribute_info(attributes, {GEO_COMPONENT_TYPE_POINT_CLOUD}, set_groups, {});
+ geometry_set_gather_instances_attribute_info(
+ set_groups, {GEO_COMPONENT_TYPE_POINT_CLOUD}, {}, attributes);
join_attributes(set_groups,
{GEO_COMPONENT_TYPE_POINT_CLOUD},
attributes,
@@ -425,8 +652,17 @@ static void join_instance_groups_volume(Span<GeometryInstanceGroup> set_groups,
{
/* Not yet supported. Joining volume grids with the same name requires resampling of at least
* one of the grids. The cell size of the resulting volume has to be determined somehow. */
- VolumeComponent &dst_component = result.get_component_for_write<VolumeComponent>();
- UNUSED_VARS(set_groups, dst_component);
+ UNUSED_VARS(set_groups, result);
+}
+
+static void join_instance_groups_curve(Span<GeometryInstanceGroup> set_groups, GeometrySet &result)
+{
+ CurveEval *curve = join_curve_splines(set_groups);
+ if (curve == nullptr) {
+ return;
+ }
+ CurveComponent &dst_component = result.get_component_for_write<CurveComponent>();
+ dst_component.replace(curve);
}
GeometrySet geometry_set_realize_mesh_for_modifier(const GeometrySet &geometry_set)
@@ -436,7 +672,8 @@ GeometrySet geometry_set_realize_mesh_for_modifier(const GeometrySet &geometry_s
}
GeometrySet new_geometry_set = geometry_set;
- Vector<GeometryInstanceGroup> set_groups = geometry_set_gather_instances(geometry_set);
+ Vector<GeometryInstanceGroup> set_groups;
+ geometry_set_gather_instances(geometry_set, set_groups);
join_instance_groups_mesh(set_groups, true, new_geometry_set);
/* Remove all instances, even though some might contain other non-mesh data. We can't really
* keep only non-mesh instances in general. */
@@ -454,10 +691,12 @@ GeometrySet geometry_set_realize_instances(const GeometrySet &geometry_set)
GeometrySet new_geometry_set;
- Vector<GeometryInstanceGroup> set_groups = geometry_set_gather_instances(geometry_set);
+ Vector<GeometryInstanceGroup> set_groups;
+ geometry_set_gather_instances(geometry_set, set_groups);
join_instance_groups_mesh(set_groups, false, new_geometry_set);
join_instance_groups_pointcloud(set_groups, new_geometry_set);
join_instance_groups_volume(set_groups, new_geometry_set);
+ join_instance_groups_curve(set_groups, new_geometry_set);
return new_geometry_set;
}
diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c
index f7b895c7ca7..6d1476485ca 100644
--- a/source/blender/blenkernel/intern/gpencil.c
+++ b/source/blender/blenkernel/intern/gpencil.c
@@ -222,7 +222,7 @@ void BKE_gpencil_blend_read_data(BlendDataReader *reader, bGPdata *gpd)
/* relink palettes (old palettes deprecated, only to convert old files) */
BLO_read_list(reader, &gpd->palettes);
if (gpd->palettes.first != NULL) {
- LISTBASE_FOREACH (Palette *, palette, &gpd->palettes) {
+ LISTBASE_FOREACH (bGPDpalette *, palette, &gpd->palettes) {
BLO_read_list(reader, &palette->colors);
}
}
@@ -653,9 +653,13 @@ bGPDframe *BKE_gpencil_frame_addcopy(bGPDlayer *gpl, int cframe)
* \param gpd: Grease pencil data-block
* \param name: Name of the layer
* \param setactive: Set as active
+ * \param add_to_header: Used to force the layer added at header
* \return Pointer to new layer
*/
-bGPDlayer *BKE_gpencil_layer_addnew(bGPdata *gpd, const char *name, bool setactive)
+bGPDlayer *BKE_gpencil_layer_addnew(bGPdata *gpd,
+ const char *name,
+ const bool setactive,
+ const bool add_to_header)
{
bGPDlayer *gpl = NULL;
bGPDlayer *gpl_active = NULL;
@@ -671,14 +675,18 @@ bGPDlayer *BKE_gpencil_layer_addnew(bGPdata *gpd, const char *name, bool setacti
gpl_active = BKE_gpencil_layer_active_get(gpd);
/* Add to data-block. */
- if (gpl_active == NULL) {
- BLI_addtail(&gpd->layers, gpl);
+ if (add_to_header) {
+ BLI_addhead(&gpd->layers, gpl);
}
else {
- /* if active layer, add after that layer */
- BLI_insertlinkafter(&gpd->layers, gpl_active, gpl);
+ if (gpl_active == NULL) {
+ BLI_addtail(&gpd->layers, gpl);
+ }
+ else {
+ /* if active layer, add after that layer */
+ BLI_insertlinkafter(&gpd->layers, gpl_active, gpl);
+ }
}
-
/* annotation vs GP Object behavior is slightly different */
if (gpd->flag & GP_DATA_ANNOTATIONS) {
/* set default color of new strokes for this layer */
@@ -1293,7 +1301,8 @@ bGPDframe *BKE_gpencil_layer_frame_find(bGPDlayer *gpl, int cframe)
return NULL;
}
-/** Get the appropriate gp-frame from a given layer
+/**
+ * Get the appropriate gp-frame from a given layer
* - this sets the layer's actframe var (if allowed to)
* - extension beyond range (if first gp-frame is after all frame in interest and cannot add)
*
@@ -2615,6 +2624,11 @@ static bool gpencil_is_layer_mask(ViewLayer *view_layer, bGPdata *gpd, bGPDlayer
continue;
}
+ /* Skip if masks are disabled for this view layer. */
+ if (gpl->flag & GP_LAYER_DISABLE_MASKS_IN_VIEWLAYER) {
+ continue;
+ }
+
LISTBASE_FOREACH (bGPDlayer_Mask *, mask, &gpl->mask_layers) {
if (STREQ(gpl_mask->info, mask->name)) {
return true;
@@ -2658,6 +2672,7 @@ void BKE_gpencil_visible_stroke_iter(ViewLayer *view_layer,
bGPDframe *act_gpf = gpl->actframe;
bGPDframe *sta_gpf = act_gpf;
bGPDframe *end_gpf = act_gpf ? act_gpf->next : NULL;
+ float prev_opacity = gpl->opacity;
if (gpl->flag & GP_LAYER_HIDE) {
continue;
@@ -2673,9 +2688,12 @@ void BKE_gpencil_visible_stroke_iter(ViewLayer *view_layer,
* This is used only in final render and never in Viewport. */
if ((view_layer != NULL) && (gpl->viewlayername[0] != '\0') &&
(!STREQ(view_layer->name, gpl->viewlayername))) {
- /* If the layer is used as mask, cannot be filtered or the masking system
- * will crash because needs the mask layer in the draw pipeline. */
- if (!gpencil_is_layer_mask(view_layer, gpd, gpl)) {
+ /* Do not skip masks when rendering the view-layer so that it can still be used to clip
+ * other layers. Instead set their opacity to zero. */
+ if (gpencil_is_layer_mask(view_layer, gpd, gpl)) {
+ gpl->opacity = 0.0f;
+ }
+ else {
continue;
}
}
@@ -2770,6 +2788,7 @@ void BKE_gpencil_visible_stroke_iter(ViewLayer *view_layer,
if (layer_cb) {
layer_cb(gpl, act_gpf, NULL, thunk);
}
+ gpl->opacity = prev_opacity;
continue;
}
@@ -2807,6 +2826,7 @@ void BKE_gpencil_visible_stroke_iter(ViewLayer *view_layer,
/* If layer solo mode and Paint mode, only keyframes with data are displayed. */
if (GPENCIL_PAINT_MODE(gpd) && (gpl->flag & GP_LAYER_SOLO_MODE) &&
(act_gpf->framenum != cfra)) {
+ gpl->opacity = prev_opacity;
continue;
}
@@ -2817,6 +2837,9 @@ void BKE_gpencil_visible_stroke_iter(ViewLayer *view_layer,
stroke_cb(gpl, act_gpf, gps, thunk);
}
}
+
+ /* Restore the opacity in case it was overwritten (used to hide masks in render). */
+ gpl->opacity = prev_opacity;
}
}
diff --git a/source/blender/blenkernel/intern/gpencil_curve.c b/source/blender/blenkernel/intern/gpencil_curve.c
index 88d3e917a7a..906d0fb0792 100644
--- a/source/blender/blenkernel/intern/gpencil_curve.c
+++ b/source/blender/blenkernel/intern/gpencil_curve.c
@@ -515,7 +515,7 @@ void BKE_gpencil_convert_curve(Main *bmain,
if (collection != NULL) {
gpl = BKE_gpencil_layer_named_get(gpd, collection->id.name + 2);
if (gpl == NULL) {
- gpl = BKE_gpencil_layer_addnew(gpd, collection->id.name + 2, true);
+ gpl = BKE_gpencil_layer_addnew(gpd, collection->id.name + 2, true, false);
}
}
}
@@ -523,7 +523,7 @@ void BKE_gpencil_convert_curve(Main *bmain,
if (gpl == NULL) {
gpl = BKE_gpencil_layer_active_get(gpd);
if (gpl == NULL) {
- gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true);
+ gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true, false);
}
}
diff --git a/source/blender/blenkernel/intern/gpencil_geom.c b/source/blender/blenkernel/intern/gpencil_geom.c
index 5d8dd99b3ae..7f839650f33 100644
--- a/source/blender/blenkernel/intern/gpencil_geom.c
+++ b/source/blender/blenkernel/intern/gpencil_geom.c
@@ -530,14 +530,23 @@ bool BKE_gpencil_stroke_sample(bGPdata *gpd, bGPDstroke *gps, const float dist,
/**
* Backbone stretch similar to Freestyle.
- * \param gps: Stroke to sample
- * \param dist: Distance of one segment
- * \param tip_length: Ignore tip jittering, set zero to use default value.
+ * \param gps: Stroke to sample.
+ * \param dist: Distance of one segment.
+ * \param overshoot_fac: How exact is the follow curve algorithm.
+ * \param mode: Affect to Start, End or Both extremes (0->Both, 1->Start, 2->End)
*/
-bool BKE_gpencil_stroke_stretch(bGPDstroke *gps, const float dist, const float tip_length)
+bool BKE_gpencil_stroke_stretch(bGPDstroke *gps,
+ const float dist,
+ const float overshoot_fac,
+ const short mode)
{
+#define BOTH 0
+#define START 1
+#define END 2
+
bGPDspoint *pt = gps->points, *last_pt, *second_last, *next_pt;
- float threshold = (tip_length == 0 ? 0.001f : tip_length);
+ int i;
+ float threshold = (overshoot_fac == 0 ? 0.001f : overshoot_fac);
if (gps->totpoints < 2 || dist < FLT_EPSILON) {
return false;
@@ -547,33 +556,36 @@ bool BKE_gpencil_stroke_stretch(bGPDstroke *gps, const float dist, const float t
second_last = &pt[gps->totpoints - 2];
next_pt = &pt[1];
- float len1 = 0.0f;
- float len2 = 0.0f;
-
- int i = 1;
- while (len1 < threshold && gps->totpoints > i) {
- next_pt = &pt[i];
- len1 = len_v3v3(&next_pt->x, &pt->x);
- i++;
- }
+ if (mode == BOTH || mode == START) {
+ float len1 = 0.0f;
+ i = 1;
+ while (len1 < threshold && gps->totpoints > i) {
+ next_pt = &pt[i];
+ len1 = len_v3v3(&next_pt->x, &pt->x);
+ i++;
+ }
+ float extend1 = (len1 + dist) / len1;
+ float result1[3];
- i = 2;
- while (len2 < threshold && gps->totpoints >= i) {
- second_last = &pt[gps->totpoints - i];
- len2 = len_v3v3(&last_pt->x, &second_last->x);
- i++;
+ interp_v3_v3v3(result1, &next_pt->x, &pt->x, extend1);
+ copy_v3_v3(&pt->x, result1);
}
- float extend1 = (len1 + dist) / len1;
- float extend2 = (len2 + dist) / len2;
-
- float result1[3], result2[3];
+ if (mode == BOTH || mode == END) {
+ float len2 = 0.0f;
+ i = 2;
+ while (len2 < threshold && gps->totpoints >= i) {
+ second_last = &pt[gps->totpoints - i];
+ len2 = len_v3v3(&last_pt->x, &second_last->x);
+ i++;
+ }
- interp_v3_v3v3(result1, &next_pt->x, &pt->x, extend1);
- interp_v3_v3v3(result2, &second_last->x, &last_pt->x, extend2);
+ float extend2 = (len2 + dist) / len2;
+ float result2[3];
+ interp_v3_v3v3(result2, &second_last->x, &last_pt->x, extend2);
- copy_v3_v3(&pt->x, result1);
- copy_v3_v3(&last_pt->x, result2);
+ copy_v3_v3(&last_pt->x, result2);
+ }
return true;
}
@@ -702,48 +714,64 @@ bool BKE_gpencil_stroke_split(bGPdata *gpd,
* Shrink the stroke by length.
* \param gps: Stroke to shrink
* \param dist: delta length
+ * \param mode: 1->Start, 2->End
*/
-bool BKE_gpencil_stroke_shrink(bGPDstroke *gps, const float dist)
+bool BKE_gpencil_stroke_shrink(bGPDstroke *gps, const float dist, const short mode)
{
+#define START 1
+#define END 2
+
bGPDspoint *pt = gps->points, *second_last;
int i;
- if (gps->totpoints < 2 || dist < FLT_EPSILON) {
+ if (gps->totpoints < 2) {
+ if (gps->totpoints == 1) {
+ second_last = &pt[1];
+ if (len_v3v3(&second_last->x, &pt->x) < dist) {
+ BKE_gpencil_stroke_trim_points(gps, 0, 0);
+ return true;
+ }
+ }
+
return false;
}
second_last = &pt[gps->totpoints - 2];
- float len1, this_len1, cut_len1;
- float len2, this_len2, cut_len2;
- int index_start, index_end;
-
- len1 = len2 = this_len1 = this_len2 = cut_len1 = cut_len2 = 0.0f;
-
- i = 1;
- while (len1 < dist && gps->totpoints > i - 1) {
- this_len1 = len_v3v3(&pt[i].x, &pt[i + 1].x);
- len1 += this_len1;
- cut_len1 = len1 - dist;
- i++;
+ float len1, cut_len1;
+ float len2, cut_len2;
+ len1 = len2 = cut_len1 = cut_len2 = 0.0f;
+
+ int index_start = 0;
+ int index_end = 0;
+ if (mode == START) {
+ i = 0;
+ index_end = gps->totpoints - 1;
+ while (len1 < dist && gps->totpoints > i + 1) {
+ len1 += len_v3v3(&pt[i].x, &pt[i + 1].x);
+ cut_len1 = len1 - dist;
+ i++;
+ }
+ index_start = i - 1;
}
- index_start = i - 2;
- i = 2;
- while (len2 < dist && gps->totpoints >= i) {
- second_last = &pt[gps->totpoints - i];
- this_len2 = len_v3v3(&second_last[1].x, &second_last->x);
- len2 += this_len2;
- cut_len2 = len2 - dist;
- i++;
+ if (mode == END) {
+ index_start = 0;
+ i = 2;
+ while (len2 < dist && gps->totpoints >= i) {
+ second_last = &pt[gps->totpoints - i];
+ len2 += len_v3v3(&second_last[1].x, &second_last->x);
+ cut_len2 = len2 - dist;
+ i++;
+ }
+ index_end = gps->totpoints - i + 2;
}
- index_end = gps->totpoints - i + 2;
- if (len1 < dist || len2 < dist || index_end <= index_start) {
+ if (index_end <= index_start) {
index_start = index_end = 0; /* empty stroke */
}
- if ((index_end == index_start + 1) && (cut_len1 + cut_len2 > 1.0f)) {
+ if ((index_end == index_start + 1) && (cut_len1 + cut_len2 < dist)) {
index_start = index_end = 0; /* no length left to cut */
}
@@ -753,22 +781,8 @@ bool BKE_gpencil_stroke_shrink(bGPDstroke *gps, const float dist)
return false;
}
- pt = gps->points;
-
- float cut1 = cut_len1 / this_len1;
- float cut2 = cut_len2 / this_len2;
-
- float result1[3], result2[3];
-
- interp_v3_v3v3(result1, &pt[1].x, &pt[0].x, cut1);
- interp_v3_v3v3(result2, &pt[gps->totpoints - 2].x, &pt[gps->totpoints - 1].x, cut2);
-
- copy_v3_v3(&pt[0].x, result1);
- copy_v3_v3(&pt[gps->totpoints - 1].x, result2);
-
return true;
}
-
/**
* Apply smooth position to stroke point.
* \param gps: Stroke to smooth
@@ -1050,8 +1064,21 @@ void BKE_gpencil_stroke_2d_flat(const bGPDspoint *points,
normalize_v3(locx);
normalize_v3(locy);
+ /* Calculate last point first. */
+ const bGPDspoint *pt_last = &points[totpoints - 1];
+ float tmp[3];
+ sub_v3_v3v3(tmp, &pt_last->x, &pt0->x);
+
+ points2d[totpoints - 1][0] = dot_v3v3(tmp, locx);
+ points2d[totpoints - 1][1] = dot_v3v3(tmp, locy);
+
+ /* Calculate the scalar cross product of the 2d points. */
+ float cross = 0.0f;
+ float *co_curr;
+ float *co_prev = (float *)&points2d[totpoints - 1];
+
/* Get all points in local space */
- for (int i = 0; i < totpoints; i++) {
+ for (int i = 0; i < totpoints - 1; i++) {
const bGPDspoint *pt = &points[i];
float loc[3];
@@ -1060,10 +1087,15 @@ void BKE_gpencil_stroke_2d_flat(const bGPDspoint *points,
points2d[i][0] = dot_v3v3(loc, locx);
points2d[i][1] = dot_v3v3(loc, locy);
+
+ /* Calculate cross product. */
+ co_curr = (float *)&points2d[i][0];
+ cross += (co_curr[0] - co_prev[0]) * (co_curr[1] + co_prev[1]);
+ co_prev = (float *)&points2d[i][0];
}
- /* Concave (-1), Convex (1), or Auto-detect (0)? */
- *r_direction = (int)locy[2];
+ /* Concave (-1), Convex (1) */
+ *r_direction = (cross >= 0.0f) ? 1 : -1;
}
/**
@@ -2421,7 +2453,7 @@ bool BKE_gpencil_convert_mesh(Main *bmain,
/* Create Layer and Frame. */
bGPDlayer *gpl_fill = BKE_gpencil_layer_named_get(gpd, element_name);
if (gpl_fill == NULL) {
- gpl_fill = BKE_gpencil_layer_addnew(gpd, element_name, true);
+ gpl_fill = BKE_gpencil_layer_addnew(gpd, element_name, true, false);
}
bGPDframe *gpf_fill = BKE_gpencil_layer_frame_get(
gpl_fill, CFRA + frame_offset, GP_GETFRAME_ADD_NEW);
@@ -2433,7 +2465,7 @@ bool BKE_gpencil_convert_mesh(Main *bmain,
Material *ma = BKE_object_material_get(ob_mesh, mp->mat_nr + 1);
make_element_name(
ob_mesh->id.name + 2, (ma != NULL) ? ma->id.name + 2 : "Fill", 64, element_name);
- mat_idx = gpencil_material_find_index_by_name(ob_gp, element_name);
+ mat_idx = BKE_gpencil_material_find_index_by_name_prefix(ob_gp, element_name);
if (mat_idx == -1) {
float color[4];
if (ma != NULL) {
@@ -2474,7 +2506,7 @@ bool BKE_gpencil_convert_mesh(Main *bmain,
/* Create Layer and Frame. */
bGPDlayer *gpl_stroke = BKE_gpencil_layer_named_get(gpd, element_name);
if (gpl_stroke == NULL) {
- gpl_stroke = BKE_gpencil_layer_addnew(gpd, element_name, true);
+ gpl_stroke = BKE_gpencil_layer_addnew(gpd, element_name, true, false);
}
bGPDframe *gpf_stroke = BKE_gpencil_layer_frame_get(
gpl_stroke, CFRA + frame_offset, GP_GETFRAME_ADD_NEW);
@@ -3663,7 +3695,7 @@ static int generate_perimeter_cap(const float point[4],
/**
* Calculate the perimeter (outline) of a stroke as list of tPerimeterPoint.
- * \param subdivisions: Number of subdivions for the start and end caps
+ * \param subdivisions: Number of subdivisions for the start and end caps
* \return: list of tPerimeterPoint
*/
static ListBase *gpencil_stroke_perimeter_ex(const bGPdata *gpd,
@@ -3710,7 +3742,7 @@ static ListBase *gpencil_stroke_perimeter_ex(const bGPdata *gpd,
copy_v3_v3(first_next_pt, &first_next->x);
copy_v3_v3(last_prev_pt, &last_prev->x);
- /* edgecase if single point */
+ /* Edge-case if single point. */
if (gps->totpoints == 1) {
first_next_pt[0] += 1.0f;
last_prev_pt[0] -= 1.0f;
@@ -3784,7 +3816,7 @@ static ListBase *gpencil_stroke_perimeter_ex(const bGPdata *gpd,
negate_v2(vec_miter_right);
float angle = dot_v2v2(vec_next, nvec_prev);
- /* add two points if angle is close to beeing straight */
+ /* Add two points if angle is close to being straight. */
if (fabsf(angle) < 0.0001f) {
normalize_v2_length(nvec_prev, radius);
normalize_v2_length(nvec_next, radius);
@@ -3910,7 +3942,7 @@ static ListBase *gpencil_stroke_perimeter_ex(const bGPdata *gpd,
/**
* Calculates the perimeter of a stroke projected from the view and
* returns it as a new stroke.
- * \param subdivisions: Number of subdivions for the start and end caps
+ * \param subdivisions: Number of subdivisions for the start and end caps
* \return: bGPDstroke pointer to stroke perimeter
*/
bGPDstroke *BKE_gpencil_stroke_perimeter_from_view(struct RegionView3D *rv3d,
diff --git a/source/blender/blenkernel/intern/gpencil_modifier.c b/source/blender/blenkernel/intern/gpencil_modifier.c
index 6f1896f055a..16386cac029 100644
--- a/source/blender/blenkernel/intern/gpencil_modifier.c
+++ b/source/blender/blenkernel/intern/gpencil_modifier.c
@@ -616,7 +616,8 @@ static int gpencil_remap_time_get(Depsgraph *depsgraph, Scene *scene, Object *ob
return remap_cfra;
}
-/** Get the current frame re-timed with time modifiers.
+/**
+ * Get the current frame re-timed with time modifiers.
* \param depsgraph: Current depsgraph.
* \param scene: Current scene
* \param ob: Grease pencil object
@@ -746,7 +747,8 @@ void BKE_gpencil_prepare_eval_data(Depsgraph *depsgraph, Scene *scene, Object *o
BKE_gpencil_update_orig_pointers(ob_orig, ob);
}
-/** Calculate gpencil modifiers.
+/**
+ * Calculate gpencil modifiers.
* \param depsgraph: Current depsgraph
* \param scene: Current scene
* \param ob: Grease pencil object
@@ -755,9 +757,9 @@ void BKE_gpencil_modifiers_calc(Depsgraph *depsgraph, Scene *scene, Object *ob)
{
bGPdata *gpd = (bGPdata *)ob->data;
const bool is_edit = GPENCIL_ANY_EDIT_MODE(gpd);
- const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
- const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
const bool is_render = (bool)(DEG_get_mode(depsgraph) == DAG_EVAL_RENDER);
+ const bool is_curve_edit = (bool)(GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd) && !is_render);
+ const bool is_multiedit = (bool)(GPENCIL_MULTIEDIT_SESSIONS_ON(gpd) && !is_render);
const bool do_modifiers = (bool)((!is_multiedit) && (!is_curve_edit) &&
(ob->greasepencil_modifiers.first != NULL) &&
(!GPENCIL_SIMPLIFY_MODIF(scene)));
diff --git a/source/blender/blenkernel/intern/idprop.c b/source/blender/blenkernel/intern/idprop.c
index 6b164e6bc50..58715ac2e05 100644
--- a/source/blender/blenkernel/intern/idprop.c
+++ b/source/blender/blenkernel/intern/idprop.c
@@ -503,7 +503,7 @@ void IDP_SyncGroupValues(IDProperty *dest, const IDProperty *src)
void IDP_SyncGroupTypes(IDProperty *dest, const IDProperty *src, const bool do_arraylen)
{
- LISTBASE_FOREACH_MUTABLE (IDProperty *, prop_dst, &src->data.group) {
+ LISTBASE_FOREACH_MUTABLE (IDProperty *, prop_dst, &dest->data.group) {
const IDProperty *prop_src = IDP_GetPropertyFromGroup((IDProperty *)src, prop_dst->name);
if (prop_src != NULL) {
/* check of we should replace? */
diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c
index 368b1c2e66b..2f7e2b41a73 100644
--- a/source/blender/blenkernel/intern/image.c
+++ b/source/blender/blenkernel/intern/image.c
@@ -735,6 +735,37 @@ int BKE_image_get_tile_from_pos(struct Image *ima,
return tile_number;
}
+/**
+ * Return the tile_number for the closest UDIM tile.
+ */
+int BKE_image_find_nearest_tile(const Image *image, const float co[2])
+{
+ const float co_floor[2] = {floorf(co[0]), floorf(co[1])};
+ /* Distance to the closest UDIM tile. */
+ float dist_best_sq = FLT_MAX;
+ int tile_number_best = -1;
+
+ LISTBASE_FOREACH (const ImageTile *, tile, &image->tiles) {
+ const int tile_index = tile->tile_number - 1001;
+ /* Coordinates of the current tile. */
+ const float tile_index_co[2] = {tile_index % 10, tile_index / 10};
+
+ if (equals_v2v2(co_floor, tile_index_co)) {
+ return tile->tile_number;
+ }
+
+ /* Distance between co[2] and UDIM tile. */
+ const float dist_sq = len_squared_v2v2(tile_index_co, co);
+
+ if (dist_sq < dist_best_sq) {
+ dist_best_sq = dist_sq;
+ tile_number_best = tile->tile_number;
+ }
+ }
+
+ return tile_number_best;
+}
+
static void image_init_color_management(Image *ima)
{
ImBuf *ibuf;
diff --git a/source/blender/blenkernel/intern/image_gen.c b/source/blender/blenkernel/intern/image_gen.c
index ceb13c4955e..1a0cc8c2924 100644
--- a/source/blender/blenkernel/intern/image_gen.c
+++ b/source/blender/blenkernel/intern/image_gen.c
@@ -69,10 +69,11 @@ static void image_buf_fill_color_slice(
}
}
-static void image_buf_fill_color_thread_do(void *data_v, int start_scanline, int num_scanlines)
+static void image_buf_fill_color_thread_do(void *data_v, int scanline)
{
FillColorThreadData *data = (FillColorThreadData *)data_v;
- size_t offset = ((size_t)start_scanline) * data->width * 4;
+ const int num_scanlines = 1;
+ size_t offset = ((size_t)scanline) * data->width * 4;
unsigned char *rect = (data->rect != NULL) ? (data->rect + offset) : NULL;
float *rect_float = (data->rect_float != NULL) ? (data->rect_float + offset) : NULL;
image_buf_fill_color_slice(rect, rect_float, data->width, num_scanlines, data->color);
@@ -197,13 +198,14 @@ typedef struct FillCheckerThreadData {
int width;
} FillCheckerThreadData;
-static void image_buf_fill_checker_thread_do(void *data_v, int start_scanline, int num_scanlines)
+static void image_buf_fill_checker_thread_do(void *data_v, int scanline)
{
FillCheckerThreadData *data = (FillCheckerThreadData *)data_v;
- size_t offset = ((size_t)start_scanline) * data->width * 4;
+ size_t offset = ((size_t)scanline) * data->width * 4;
+ const int num_scanlines = 1;
unsigned char *rect = (data->rect != NULL) ? (data->rect + offset) : NULL;
float *rect_float = (data->rect_float != NULL) ? (data->rect_float + offset) : NULL;
- image_buf_fill_checker_slice(rect, rect_float, data->width, num_scanlines, start_scanline);
+ image_buf_fill_checker_slice(rect, rect_float, data->width, num_scanlines, scanline);
}
void BKE_image_buf_fill_checker(unsigned char *rect, float *rect_float, int width, int height)
@@ -444,16 +446,15 @@ typedef struct FillCheckerColorThreadData {
int width, height;
} FillCheckerColorThreadData;
-static void checker_board_color_prepare_thread_do(void *data_v,
- int start_scanline,
- int num_scanlines)
+static void checker_board_color_prepare_thread_do(void *data_v, int scanline)
{
FillCheckerColorThreadData *data = (FillCheckerColorThreadData *)data_v;
- size_t offset = ((size_t)data->width) * start_scanline * 4;
+ const int num_scanlines = 1;
+ size_t offset = ((size_t)data->width) * scanline * 4;
unsigned char *rect = (data->rect != NULL) ? (data->rect + offset) : NULL;
float *rect_float = (data->rect_float != NULL) ? (data->rect_float + offset) : NULL;
checker_board_color_prepare_slice(
- rect, rect_float, data->width, num_scanlines, start_scanline, data->height);
+ rect, rect_float, data->width, num_scanlines, scanline, data->height);
}
void BKE_image_buf_fill_checker_color(unsigned char *rect,
diff --git a/source/blender/blenkernel/intern/image_gpu.c b/source/blender/blenkernel/intern/image_gpu.c
index 8847b88d6f2..bb7495437bb 100644
--- a/source/blender/blenkernel/intern/image_gpu.c
+++ b/source/blender/blenkernel/intern/image_gpu.c
@@ -408,17 +408,19 @@ static GPUTexture *image_get_gpu_texture(Image *ima,
store_premultiplied,
limit_gl_texture_size);
- GPU_texture_wrap_mode(*tex, true, false);
+ if (*tex) {
+ GPU_texture_wrap_mode(*tex, true, false);
- if (GPU_mipmap_enabled()) {
- GPU_texture_generate_mipmap(*tex);
- if (ima) {
- ima->gpuflag |= IMA_GPU_MIPMAP_COMPLETE;
+ if (GPU_mipmap_enabled()) {
+ GPU_texture_generate_mipmap(*tex);
+ if (ima) {
+ ima->gpuflag |= IMA_GPU_MIPMAP_COMPLETE;
+ }
+ GPU_texture_mipmap_mode(*tex, true, true);
+ }
+ else {
+ GPU_texture_mipmap_mode(*tex, false, true);
}
- GPU_texture_mipmap_mode(*tex, true, true);
- }
- else {
- GPU_texture_mipmap_mode(*tex, false, true);
}
}
@@ -427,7 +429,9 @@ static GPUTexture *image_get_gpu_texture(Image *ima,
BKE_image_release_ibuf(ima, ibuf_intern, NULL);
}
- GPU_texture_orig_size_set(*tex, ibuf_intern->x, ibuf_intern->y);
+ if (*tex) {
+ GPU_texture_orig_size_set(*tex, ibuf_intern->x, ibuf_intern->y);
+ }
return *tex;
}
diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c
index f2893e162cb..073276b7011 100644
--- a/source/blender/blenkernel/intern/key.c
+++ b/source/blender/blenkernel/intern/key.c
@@ -105,6 +105,11 @@ static void shapekey_foreach_id(ID *id, LibraryForeachIDData *data)
BKE_LIB_FOREACHID_PROCESS_ID(data, key->from, IDWALK_CB_LOOPBACK);
}
+static ID *shapekey_owner_get(Main *UNUSED(bmain), ID *id)
+{
+ return ((Key *)id)->from;
+}
+
static void shapekey_blend_write(BlendWriter *writer, ID *id, const void *id_address)
{
Key *key = (Key *)id;
@@ -216,7 +221,9 @@ IDTypeInfo IDType_ID_KE = {
.make_local = NULL,
.foreach_id = shapekey_foreach_id,
.foreach_cache = NULL,
- .owner_get = NULL, /* Could have one actually? */
+ /* A bit weird, due to shapekeys not being strictly speaking embedded data... But they also
+ * share a lot with those (non linkable, only ever used by one owner ID, etc.). */
+ .owner_get = shapekey_owner_get,
.blend_write = shapekey_blend_write,
.blend_read_data = shapekey_blend_read_data,
diff --git a/source/blender/blenkernel/intern/lattice.c b/source/blender/blenkernel/intern/lattice.c
index 7c451051727..1357424d5ff 100644
--- a/source/blender/blenkernel/intern/lattice.c
+++ b/source/blender/blenkernel/intern/lattice.c
@@ -94,6 +94,7 @@ static void lattice_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const i
}
lattice_dst->editlatt = NULL;
+ lattice_dst->batch_cache = NULL;
}
static void lattice_free_data(ID *id)
@@ -540,10 +541,12 @@ void BKE_lattice_vert_coords_apply(Lattice *lt, const float (*vert_coords)[3])
void BKE_lattice_modifiers_calc(struct Depsgraph *depsgraph, Scene *scene, Object *ob)
{
+ BKE_object_free_derived_caches(ob);
+ if (ob->runtime.curve_cache == NULL) {
+ ob->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for lattice");
+ }
+
Lattice *lt = ob->data;
- /* Get vertex coordinates from the original copy;
- * otherwise we get already-modified coordinates. */
- Object *ob_orig = DEG_get_original_object(ob);
VirtualModifierData virtualModifierData;
ModifierData *md = BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData);
float(*vert_coords)[3] = NULL;
@@ -551,13 +554,6 @@ void BKE_lattice_modifiers_calc(struct Depsgraph *depsgraph, Scene *scene, Objec
const bool is_editmode = (lt->editlatt != NULL);
const ModifierEvalContext mectx = {depsgraph, ob, 0};
- if (ob->runtime.curve_cache) {
- BKE_displist_free(&ob->runtime.curve_cache->disp);
- }
- else {
- ob->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for lattice");
- }
-
for (; md; md = md->next) {
const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type);
@@ -577,49 +573,33 @@ void BKE_lattice_modifiers_calc(struct Depsgraph *depsgraph, Scene *scene, Objec
continue;
}
- if (!vert_coords) {
- Lattice *lt_orig = ob_orig->data;
- if (lt_orig->editlatt) {
- lt_orig = lt_orig->editlatt->latt;
- }
- vert_coords = BKE_lattice_vert_coords_alloc(lt_orig, &numVerts);
+ if (vert_coords == NULL) {
+ /* Get either the edit-mode or regular lattice, whichever is in use now. */
+ const Lattice *effective_lattice = BKE_object_get_lattice(ob);
+ vert_coords = BKE_lattice_vert_coords_alloc(effective_lattice, &numVerts);
}
+
mti->deformVerts(md, &mectx, NULL, vert_coords, numVerts);
}
- if (ob->id.tag & LIB_TAG_COPIED_ON_WRITE) {
- if (vert_coords) {
- BKE_lattice_vert_coords_apply(ob->data, vert_coords);
- MEM_freeN(vert_coords);
- }
+ if (vert_coords == NULL) {
+ return;
}
- else {
- /* Displist won't do anything; this is just for posterity's sake until we remove it. */
- if (!vert_coords) {
- Lattice *lt_orig = ob_orig->data;
- if (lt_orig->editlatt) {
- lt_orig = lt_orig->editlatt->latt;
- }
- vert_coords = BKE_lattice_vert_coords_alloc(lt_orig, &numVerts);
- }
-
- DispList *dl = MEM_callocN(sizeof(*dl), "lt_dl");
- dl->type = DL_VERTS;
- dl->parts = 1;
- dl->nr = numVerts;
- dl->verts = (float *)vert_coords;
- BLI_addtail(&ob->runtime.curve_cache->disp, dl);
+ Lattice *lt_eval = BKE_object_get_evaluated_lattice(ob);
+ if (lt_eval == NULL) {
+ BKE_id_copy_ex(NULL, &lt->id, (ID **)&lt_eval, LIB_ID_COPY_LOCALIZE);
+ BKE_object_eval_assign_data(ob, &lt_eval->id, true);
}
+
+ BKE_lattice_vert_coords_apply(lt_eval, vert_coords);
+ MEM_freeN(vert_coords);
}
struct MDeformVert *BKE_lattice_deform_verts_get(const struct Object *oblatt)
{
- Lattice *lt = (Lattice *)oblatt->data;
BLI_assert(oblatt->type == OB_LATTICE);
- if (lt->editlatt) {
- lt = lt->editlatt->latt;
- }
+ Lattice *lt = BKE_object_get_lattice(oblatt);
return lt->dvert;
}
diff --git a/source/blender/blenkernel/intern/lattice_deform.c b/source/blender/blenkernel/intern/lattice_deform.c
index 2651042939f..a8126cb5538 100644
--- a/source/blender/blenkernel/intern/lattice_deform.c
+++ b/source/blender/blenkernel/intern/lattice_deform.c
@@ -47,6 +47,7 @@
#include "BKE_key.h"
#include "BKE_lattice.h"
#include "BKE_modifier.h"
+#include "BKE_object.h"
#include "BKE_deform.h"
@@ -69,7 +70,7 @@ typedef struct LatticeDeformData {
LatticeDeformData *BKE_lattice_deform_data_create(const Object *oblatt, const Object *ob)
{
/* we make an array with all differences */
- Lattice *lt = oblatt->data;
+ Lattice *lt = BKE_object_get_lattice(oblatt);
BPoint *bp;
DispList *dl = oblatt->runtime.curve_cache ?
BKE_displist_find(&oblatt->runtime.curve_cache->disp, DL_VERTS) :
@@ -83,9 +84,6 @@ LatticeDeformData *BKE_lattice_deform_data_create(const Object *oblatt, const Ob
float latmat[4][4];
LatticeDeformData *lattice_deform_data;
- if (lt->editlatt) {
- lt = lt->editlatt->latt;
- }
bp = lt->def;
const int32_t num_points = lt->pntsu * lt->pntsv * lt->pntsw;
@@ -322,7 +320,9 @@ static void lattice_deform_vert_task(void *__restrict userdata,
lattice_deform_vert_with_dvert(data, index, data->dvert ? &data->dvert[index] : NULL);
}
-static void lattice_vert_task_editmesh(void *__restrict userdata, MempoolIterData *iter)
+static void lattice_vert_task_editmesh(void *__restrict userdata,
+ MempoolIterData *iter,
+ const TaskParallelTLS *__restrict UNUSED(tls))
{
const LatticeDeformUserdata *data = userdata;
BMVert *v = (BMVert *)iter;
@@ -330,7 +330,9 @@ static void lattice_vert_task_editmesh(void *__restrict userdata, MempoolIterDat
lattice_deform_vert_with_dvert(data, BM_elem_index_get(v), dvert);
}
-static void lattice_vert_task_editmesh_no_dvert(void *__restrict userdata, MempoolIterData *iter)
+static void lattice_vert_task_editmesh_no_dvert(void *__restrict userdata,
+ MempoolIterData *iter,
+ const TaskParallelTLS *__restrict UNUSED(tls))
{
const LatticeDeformUserdata *data = userdata;
BMVert *v = (BMVert *)iter;
@@ -399,12 +401,16 @@ static void lattice_deform_coords_impl(const Object *ob_lattice,
* have already been properly set. */
BM_mesh_elem_index_ensure(em_target->bm, BM_VERT);
+ TaskParallelSettings settings;
+ BLI_parallel_mempool_settings_defaults(&settings);
+
if (cd_dvert_offset != -1) {
- BLI_task_parallel_mempool(em_target->bm->vpool, &data, lattice_vert_task_editmesh, true);
+ BLI_task_parallel_mempool(
+ em_target->bm->vpool, &data, lattice_vert_task_editmesh, &settings);
}
else {
BLI_task_parallel_mempool(
- em_target->bm->vpool, &data, lattice_vert_task_editmesh_no_dvert, true);
+ em_target->bm->vpool, &data, lattice_vert_task_editmesh_no_dvert, &settings);
}
}
else {
diff --git a/source/blender/blenkernel/intern/lattice_deform_test.cc b/source/blender/blenkernel/intern/lattice_deform_test.cc
index f08d0349598..a7cd5c36ec2 100644
--- a/source/blender/blenkernel/intern/lattice_deform_test.cc
+++ b/source/blender/blenkernel/intern/lattice_deform_test.cc
@@ -51,6 +51,7 @@ static void test_lattice_deform_init(LatticeDeformTestContext *ctx,
ctx->coords[index][2] = (rng->get_float() - 0.5f) * 10;
}
IDType_ID_LT.init_data(&ctx->lattice.id);
+ strcpy(ctx->lattice.id.name, "LTLattice");
IDType_ID_OB.init_data(&ctx->ob_lattice.id);
ctx->ob_lattice.type = OB_LATTICE;
ctx->ob_lattice.data = &ctx->lattice;
diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c
index af921307bfb..f26b85338f0 100644
--- a/source/blender/blenkernel/intern/lib_id.c
+++ b/source/blender/blenkernel/intern/lib_id.c
@@ -164,7 +164,7 @@ static void lib_id_clear_library_data_ex(Main *bmain, ID *id)
id->tag &= ~(LIB_TAG_INDIRECT | LIB_TAG_EXTERN);
id->flag &= ~LIB_INDIRECT_WEAK_LINK;
if (id_in_mainlist) {
- if (BKE_id_new_name_validate(which_libbase(bmain, GS(id->name)), id, NULL)) {
+ if (BKE_id_new_name_validate(which_libbase(bmain, GS(id->name)), id, NULL, false)) {
bmain->is_memfile_undo_written = false;
}
}
@@ -525,7 +525,13 @@ static int id_copy_libmanagement_cb(LibraryIDLinkCallbackData *cb_data)
/* Increase used IDs refcount if needed and required. */
if ((data->flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0 && (cb_flag & IDWALK_CB_USER)) {
- id_us_plus(id);
+ if ((data->flag & LIB_ID_CREATE_NO_MAIN) != 0) {
+ BLI_assert(cb_data->id_self->tag & LIB_TAG_NO_MAIN);
+ id_us_plus_no_lib(id);
+ }
+ else {
+ id_us_plus(id);
+ }
}
return IDWALK_RET_NOP;
@@ -578,7 +584,7 @@ ID *BKE_id_copy_ex(Main *bmain, const ID *id, ID **r_newid, const int flag)
}
}
- /* Early output is source is NULL. */
+ /* Early output if source is NULL. */
if (id == NULL) {
return NULL;
}
@@ -827,7 +833,9 @@ void BKE_libblock_management_main_add(Main *bmain, void *idv)
ListBase *lb = which_libbase(bmain, GS(id->name));
BKE_main_lock(bmain);
BLI_addtail(lb, id);
- BKE_id_new_name_validate(lb, id, NULL);
+ /* We need to allow adding extra datablocks into libraries too, e.g. to support generating new
+ * overrides for recursive resync. */
+ BKE_id_new_name_validate(lb, id, NULL, true);
/* alphabetic insertion: is in new_id */
id->tag &= ~(LIB_TAG_NO_MAIN | LIB_TAG_NO_USER_REFCOUNT);
bmain->is_memfile_undo_written = false;
@@ -983,7 +991,7 @@ void BKE_main_id_repair_duplicate_names_listbase(ListBase *lb)
}
for (i = 0; i < lb_len; i++) {
if (!BLI_gset_add(gset, id_array[i]->name + 2)) {
- BKE_id_new_name_validate(lb, id_array[i], NULL);
+ BKE_id_new_name_validate(lb, id_array[i], NULL, false);
}
}
BLI_gset_free(gset, NULL);
@@ -1086,7 +1094,7 @@ void *BKE_libblock_alloc(Main *bmain, short type, const char *name, const int fl
BKE_main_lock(bmain);
BLI_addtail(lb, id);
- BKE_id_new_name_validate(lb, id, name);
+ BKE_id_new_name_validate(lb, id, name, false);
bmain->is_memfile_undo_written = false;
/* alphabetic insertion: is in new_id */
BKE_main_unlock(bmain);
@@ -1215,14 +1223,6 @@ void BKE_libblock_copy_ex(Main *bmain, const ID *id, ID **r_newid, const int ori
BLI_assert((flag & LIB_ID_CREATE_NO_MAIN) != 0 || bmain != NULL);
BLI_assert((flag & LIB_ID_CREATE_NO_MAIN) != 0 || (flag & LIB_ID_CREATE_NO_ALLOCATE) == 0);
BLI_assert((flag & LIB_ID_CREATE_NO_MAIN) != 0 || (flag & LIB_ID_CREATE_LOCAL) == 0);
- if (!is_private_id_data) {
- /* When we are handling private ID data, we might still want to manage usercounts, even
- * though that ID data-block is actually outside of Main... */
- BLI_assert((flag & LIB_ID_CREATE_NO_MAIN) == 0 ||
- (flag & LIB_ID_CREATE_NO_USER_REFCOUNT) != 0);
- }
- /* Never implicitly copy shapekeys when generating temp data outside of Main database. */
- BLI_assert((flag & LIB_ID_CREATE_NO_MAIN) == 0 || (flag & LIB_ID_COPY_SHAPEKEY) == 0);
/* 'Private ID' data handling. */
if ((bmain != NULL) && is_private_id_data) {
@@ -1245,6 +1245,13 @@ void BKE_libblock_copy_ex(Main *bmain, const ID *id, ID **r_newid, const int ori
}
BLI_assert(new_id != NULL);
+ if ((flag & LIB_ID_COPY_SET_COPIED_ON_WRITE) != 0) {
+ new_id->tag |= LIB_TAG_COPIED_ON_WRITE;
+ }
+ else {
+ new_id->tag &= ~LIB_TAG_COPIED_ON_WRITE;
+ }
+
const size_t id_len = BKE_libblock_get_alloc_info(GS(new_id->name), NULL);
const size_t id_offset = sizeof(ID);
if ((int)id_len - (int)id_offset > 0) { /* signed to allow neg result */ /* XXX ????? */
@@ -1264,9 +1271,7 @@ void BKE_libblock_copy_ex(Main *bmain, const ID *id, ID **r_newid, const int ori
new_id->properties = IDP_CopyProperty_ex(id->properties, copy_data_flag);
}
- /* We may need our own flag to control that at some point, but for now 'no main' one should be
- * good enough. */
- if ((orig_flag & LIB_ID_CREATE_NO_MAIN) == 0) {
+ if ((orig_flag & LIB_ID_COPY_NO_LIB_OVERRIDE) == 0) {
if (ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
/* We do not want to copy existing override rules here, as they would break the proper
* remapping between IDs. Proper overrides rules will be re-generated anyway. */
@@ -1350,12 +1355,12 @@ void id_sort_by_name(ListBase *lb, ID *id, ID *id_sorting_hint)
BLI_remlink(lb, id);
/* Check if we can actually insert id before or after id_sorting_hint, if given. */
- if (!ELEM(id_sorting_hint, NULL, id)) {
+ if (!ELEM(id_sorting_hint, NULL, id) && id_sorting_hint->lib == id->lib) {
BLI_assert(BLI_findindex(lb, id_sorting_hint) >= 0);
ID *id_sorting_hint_next = id_sorting_hint->next;
if (BLI_strcasecmp(id_sorting_hint->name, id->name) < 0 &&
- (id_sorting_hint_next == NULL ||
+ (id_sorting_hint_next == NULL || id_sorting_hint_next->lib != id->lib ||
BLI_strcasecmp(id_sorting_hint_next->name, id->name) > 0)) {
BLI_insertlinkafter(lb, id_sorting_hint, id);
return;
@@ -1363,7 +1368,7 @@ void id_sort_by_name(ListBase *lb, ID *id, ID *id_sorting_hint)
ID *id_sorting_hint_prev = id_sorting_hint->prev;
if (BLI_strcasecmp(id_sorting_hint->name, id->name) > 0 &&
- (id_sorting_hint_prev == NULL ||
+ (id_sorting_hint_prev == NULL || id_sorting_hint_prev->lib != id->lib ||
BLI_strcasecmp(id_sorting_hint_prev->name, id->name) < 0)) {
BLI_insertlinkbefore(lb, id_sorting_hint, id);
return;
@@ -1378,16 +1383,33 @@ void id_sort_by_name(ListBase *lb, ID *id, ID *id_sorting_hint)
/* Note: We start from the end, because in typical 'heavy' case (insertion of lots of IDs at
* once using the same base name), newly inserted items will generally be towards the end
* (higher extension numbers). */
- for (idtest = lb->last, item_array_index = ID_SORT_STEP_SIZE - 1; idtest != NULL;
- idtest = idtest->prev, item_array_index--) {
+ bool is_in_library = false;
+ item_array_index = ID_SORT_STEP_SIZE - 1;
+ for (idtest = lb->last; idtest != NULL; idtest = idtest->prev) {
+ if (is_in_library) {
+ if (idtest->lib != id->lib) {
+ /* We got out of expected library 'range' in the list, so we are done here and can move on
+ * to the next step. */
+ break;
+ }
+ }
+ else if (idtest->lib == id->lib) {
+ /* We are entering the expected library 'range' of IDs in the list. */
+ is_in_library = true;
+ }
+
+ if (!is_in_library) {
+ continue;
+ }
+
item_array[item_array_index] = idtest;
if (item_array_index == 0) {
- if ((idtest->lib == NULL && id->lib != NULL) ||
- BLI_strcasecmp(idtest->name, id->name) <= 0) {
+ if (BLI_strcasecmp(idtest->name, id->name) <= 0) {
break;
}
item_array_index = ID_SORT_STEP_SIZE;
}
+ item_array_index--;
}
/* Step two: we go forward in the selected chunk of items and check all of them, as we know
@@ -1399,7 +1421,7 @@ void id_sort_by_name(ListBase *lb, ID *id, ID *id_sorting_hint)
* So we can increment that index in any case. */
for (item_array_index++; item_array_index < ID_SORT_STEP_SIZE; item_array_index++) {
idtest = item_array[item_array_index];
- if ((idtest->lib != NULL && id->lib == NULL) || BLI_strcasecmp(idtest->name, id->name) > 0) {
+ if (BLI_strcasecmp(idtest->name, id->name) > 0) {
BLI_insertlinkbefore(lb, idtest, id);
break;
}
@@ -1407,12 +1429,18 @@ void id_sort_by_name(ListBase *lb, ID *id, ID *id_sorting_hint)
if (item_array_index == ID_SORT_STEP_SIZE) {
if (idtest == NULL) {
/* If idtest is NULL here, it means that in the first loop, the last comparison was
- * performed exactly on the first item of the list, and that it also failed. In other
- * words, all items in the list are greater than inserted one, so we can put it at the
- * start of the list. */
- /* Note that BLI_insertlinkafter() would have same behavior in that case, but better be
- * explicit here. */
- BLI_addhead(lb, id);
+ * performed exactly on the first item of the list, and that it also failed. And that the
+ * second loop was not walked at all.
+ *
+ * In other words, if `id` is local, all the items in the list are greater than the inserted
+ * one, so we can put it at the start of the list. Or, if `id` is linked, it is the first one
+ * of its library, and we can put it at the very end of the list. */
+ if (ID_IS_LINKED(id)) {
+ BLI_addtail(lb, id);
+ }
+ else {
+ BLI_addhead(lb, id);
+ }
}
else {
BLI_insertlinkafter(lb, idtest, id);
@@ -1531,7 +1559,7 @@ static bool check_for_dupid(ListBase *lb, ID *id, char *name, ID **r_id_sorting_
* and that current one is not. */
bool is_valid = false;
for (id_test = lb->first; id_test; id_test = id_test->next) {
- if (id != id_test && !ID_IS_LINKED(id_test)) {
+ if (id != id_test && id_test->lib == id->lib) {
if (id_test->name[2] == final_name[0] && STREQ(final_name, id_test->name + 2)) {
/* We expect final_name to not be already used, so this is a failure. */
is_valid = false;
@@ -1587,7 +1615,7 @@ static bool check_for_dupid(ListBase *lb, ID *id, char *name, ID **r_id_sorting_
for (id_test = lb->first; id_test; id_test = id_test->next) {
char base_name_test[MAX_ID_NAME - 2];
int number_test;
- if ((id != id_test) && !ID_IS_LINKED(id_test) && (name[0] == id_test->name[2]) &&
+ if ((id != id_test) && (id_test->lib == id->lib) && (name[0] == id_test->name[2]) &&
(ELEM(id_test->name[base_name_len + 2], '.', '\0')) &&
STREQLEN(name, id_test->name + 2, base_name_len) &&
(BLI_split_name_num(base_name_test, &number_test, id_test->name + 2, '.') ==
@@ -1676,16 +1704,21 @@ static bool check_for_dupid(ListBase *lb, ID *id, char *name, ID **r_id_sorting_
*
* Only for local IDs (linked ones already have a unique ID in their library).
*
+ * \param do_linked_data if true, also ensure a unique name in case the given \a id is linked
+ * (otherwise, just ensure that it is properly sorted).
+ *
* \return true if a new name had to be created.
*/
-bool BKE_id_new_name_validate(ListBase *lb, ID *id, const char *tname)
+bool BKE_id_new_name_validate(ListBase *lb, ID *id, const char *tname, const bool do_linked_data)
{
- bool result;
+ bool result = false;
char name[MAX_ID_NAME - 2];
- /* if library, don't rename */
- if (ID_IS_LINKED(id)) {
- return false;
+ /* If library, don't rename (unless explicitly required), but do ensure proper sorting. */
+ if (!do_linked_data && ID_IS_LINKED(id)) {
+ id_sort_by_name(lb, id, NULL);
+
+ return result;
}
/* if no name given, use name of current ID
@@ -1726,7 +1759,7 @@ bool BKE_id_new_name_validate(ListBase *lb, ID *id, const char *tname)
}
/* next to indirect usage in read/writefile also in editobject.c scene.c */
-void BKE_main_id_clear_newpoins(Main *bmain)
+void BKE_main_id_newptr_and_tag_clear(Main *bmain)
{
ID *id;
@@ -2140,7 +2173,7 @@ void BKE_library_make_local(Main *bmain,
TIMEIT_VALUE_PRINT(make_local);
#endif
- BKE_main_id_clear_newpoins(bmain);
+ BKE_main_id_newptr_and_tag_clear(bmain);
BLI_memarena_free(linklist_mem);
#ifdef DEBUG_TIME
@@ -2165,9 +2198,9 @@ void BLI_libblock_ensure_unique_name(Main *bmain, const char *name)
/* search for id */
idtest = BLI_findstring(lb, name + 2, offsetof(ID, name) + 2);
- if (idtest != NULL) {
+ if (idtest != NULL && !ID_IS_LINKED(idtest)) {
/* BKE_id_new_name_validate also takes care of sorting. */
- BKE_id_new_name_validate(lb, idtest, NULL);
+ BKE_id_new_name_validate(lb, idtest, NULL, false);
bmain->is_memfile_undo_written = false;
}
}
@@ -2177,8 +2210,9 @@ void BLI_libblock_ensure_unique_name(Main *bmain, const char *name)
*/
void BKE_libblock_rename(Main *bmain, ID *id, const char *name)
{
+ BLI_assert(!ID_IS_LINKED(id));
ListBase *lb = which_libbase(bmain, GS(id->name));
- if (BKE_id_new_name_validate(lb, id, name)) {
+ if (BKE_id_new_name_validate(lb, id, name, false)) {
bmain->is_memfile_undo_written = false;
}
}
diff --git a/source/blender/blenkernel/intern/lib_id_test.cc b/source/blender/blenkernel/intern/lib_id_test.cc
new file mode 100644
index 00000000000..8e21ae88aa6
--- /dev/null
+++ b/source/blender/blenkernel/intern/lib_id_test.cc
@@ -0,0 +1,173 @@
+/*
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2020 by Blender Foundation.
+ */
+#include "testing/testing.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_listbase.h"
+#include "BLI_string.h"
+
+#include "BKE_idtype.h"
+#include "BKE_lib_id.h"
+#include "BKE_main.h"
+
+#include "DNA_ID.h"
+#include "DNA_mesh_types.h"
+#include "DNA_object_types.h"
+
+namespace blender::bke::tests {
+
+struct LibIDMainSortTestContext {
+ Main *bmain;
+};
+
+static void test_lib_id_main_sort_init(LibIDMainSortTestContext *ctx)
+{
+ BKE_idtype_init();
+ ctx->bmain = BKE_main_new();
+}
+
+static void test_lib_id_main_sort_free(LibIDMainSortTestContext *ctx)
+{
+ BKE_main_free(ctx->bmain);
+}
+
+static void test_lib_id_main_sort_check_order(std::initializer_list<ID *> list)
+{
+ ID *prev_id = nullptr;
+ for (ID *id : list) {
+ EXPECT_EQ(id->prev, prev_id);
+ if (prev_id != nullptr) {
+ EXPECT_EQ(prev_id->next, id);
+ }
+ prev_id = id;
+ }
+ EXPECT_EQ(prev_id->next, nullptr);
+}
+
+TEST(lib_id_main_sort, local_ids_1)
+{
+ LibIDMainSortTestContext ctx = {nullptr};
+ test_lib_id_main_sort_init(&ctx);
+ EXPECT_TRUE(BLI_listbase_is_empty(&ctx.bmain->libraries));
+
+ ID *id_c = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_C"));
+ ID *id_a = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_A"));
+ ID *id_b = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_B"));
+ EXPECT_TRUE(ctx.bmain->objects.first == id_a);
+ EXPECT_TRUE(ctx.bmain->objects.last == id_c);
+ test_lib_id_main_sort_check_order({id_a, id_b, id_c});
+
+ test_lib_id_main_sort_free(&ctx);
+}
+
+TEST(lib_id_main_sort, linked_ids_1)
+{
+ LibIDMainSortTestContext ctx = {nullptr};
+ test_lib_id_main_sort_init(&ctx);
+ EXPECT_TRUE(BLI_listbase_is_empty(&ctx.bmain->libraries));
+
+ Library *lib_a = static_cast<Library *>(BKE_id_new(ctx.bmain, ID_LI, "LI_A"));
+ Library *lib_b = static_cast<Library *>(BKE_id_new(ctx.bmain, ID_LI, "LI_B"));
+ ID *id_c = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_C"));
+ ID *id_a = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_A"));
+ ID *id_b = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_B"));
+
+ id_a->lib = lib_a;
+ id_sort_by_name(&ctx.bmain->objects, id_a, nullptr);
+ id_b->lib = lib_a;
+ id_sort_by_name(&ctx.bmain->objects, id_b, nullptr);
+ EXPECT_TRUE(ctx.bmain->objects.first == id_c);
+ EXPECT_TRUE(ctx.bmain->objects.last == id_b);
+ test_lib_id_main_sort_check_order({id_c, id_a, id_b});
+
+ id_a->lib = lib_b;
+ id_sort_by_name(&ctx.bmain->objects, id_a, nullptr);
+ EXPECT_TRUE(ctx.bmain->objects.first == id_c);
+ EXPECT_TRUE(ctx.bmain->objects.last == id_a);
+ test_lib_id_main_sort_check_order({id_c, id_b, id_a});
+
+ id_b->lib = lib_b;
+ id_sort_by_name(&ctx.bmain->objects, id_b, nullptr);
+ EXPECT_TRUE(ctx.bmain->objects.first == id_c);
+ EXPECT_TRUE(ctx.bmain->objects.last == id_b);
+ test_lib_id_main_sort_check_order({id_c, id_a, id_b});
+
+ test_lib_id_main_sort_free(&ctx);
+}
+
+TEST(lib_id_main_unique_name, local_ids_1)
+{
+ LibIDMainSortTestContext ctx = {nullptr};
+ test_lib_id_main_sort_init(&ctx);
+ EXPECT_TRUE(BLI_listbase_is_empty(&ctx.bmain->libraries));
+
+ ID *id_c = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_C"));
+ ID *id_a = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_A"));
+ ID *id_b = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_B"));
+ test_lib_id_main_sort_check_order({id_a, id_b, id_c});
+
+ BLI_strncpy(id_c->name, id_a->name, sizeof(id_c->name));
+ BKE_id_new_name_validate(&ctx.bmain->objects, id_c, NULL, false);
+ EXPECT_TRUE(strcmp(id_c->name + 2, "OB_A.001") == 0);
+ EXPECT_TRUE(strcmp(id_a->name + 2, "OB_A") == 0);
+ EXPECT_TRUE(ctx.bmain->objects.first == id_a);
+ EXPECT_TRUE(ctx.bmain->objects.last == id_b);
+ test_lib_id_main_sort_check_order({id_a, id_c, id_b});
+
+ test_lib_id_main_sort_free(&ctx);
+}
+
+TEST(lib_id_main_unique_name, linked_ids_1)
+{
+ LibIDMainSortTestContext ctx = {nullptr};
+ test_lib_id_main_sort_init(&ctx);
+ EXPECT_TRUE(BLI_listbase_is_empty(&ctx.bmain->libraries));
+
+ Library *lib_a = static_cast<Library *>(BKE_id_new(ctx.bmain, ID_LI, "LI_A"));
+ Library *lib_b = static_cast<Library *>(BKE_id_new(ctx.bmain, ID_LI, "LI_B"));
+ ID *id_c = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_C"));
+ ID *id_a = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_A"));
+ ID *id_b = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_B"));
+
+ id_a->lib = lib_a;
+ id_sort_by_name(&ctx.bmain->objects, id_a, nullptr);
+ id_b->lib = lib_a;
+ id_sort_by_name(&ctx.bmain->objects, id_b, nullptr);
+ BLI_strncpy(id_b->name, id_a->name, sizeof(id_b->name));
+ BKE_id_new_name_validate(&ctx.bmain->objects, id_b, NULL, true);
+ EXPECT_TRUE(strcmp(id_b->name + 2, "OB_A.001") == 0);
+ EXPECT_TRUE(strcmp(id_a->name + 2, "OB_A") == 0);
+ EXPECT_TRUE(ctx.bmain->objects.first == id_c);
+ EXPECT_TRUE(ctx.bmain->objects.last == id_b);
+ test_lib_id_main_sort_check_order({id_c, id_a, id_b});
+
+ id_b->lib = lib_b;
+ id_sort_by_name(&ctx.bmain->objects, id_b, nullptr);
+ BLI_strncpy(id_b->name, id_a->name, sizeof(id_b->name));
+ BKE_id_new_name_validate(&ctx.bmain->objects, id_b, NULL, true);
+ EXPECT_TRUE(strcmp(id_b->name + 2, "OB_A") == 0);
+ EXPECT_TRUE(strcmp(id_a->name + 2, "OB_A") == 0);
+ EXPECT_TRUE(ctx.bmain->objects.first == id_c);
+ EXPECT_TRUE(ctx.bmain->objects.last == id_b);
+ test_lib_id_main_sort_check_order({id_c, id_a, id_b});
+
+ test_lib_id_main_sort_free(&ctx);
+}
+
+} // namespace blender::bke::tests
diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c
index 2ee4f1597be..6b2ffa3b944 100644
--- a/source/blender/blenkernel/intern/lib_override.c
+++ b/source/blender/blenkernel/intern/lib_override.c
@@ -48,6 +48,7 @@
#include "BKE_lib_query.h"
#include "BKE_lib_remap.h"
#include "BKE_main.h"
+#include "BKE_node.h"
#include "BKE_report.h"
#include "BKE_scene.h"
@@ -80,6 +81,19 @@ static void lib_override_library_property_clear(IDOverrideLibraryProperty *op);
static void lib_override_library_property_operation_clear(
IDOverrideLibraryPropertyOperation *opop);
+/** Get override data for a given ID. Needed because of our beloved shape keys snowflake. */
+BLI_INLINE IDOverrideLibrary *lib_override_get(Main *bmain, ID *id)
+{
+ if (id->flag & LIB_EMBEDDED_DATA_LIB_OVERRIDE) {
+ const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id);
+ if (id_type->owner_get != NULL) {
+ return id_type->owner_get(bmain, id)->override_library;
+ }
+ BLI_assert(!"IDTypeInfo of liboverride-embedded ID with no owner getter");
+ }
+ return id->override_library;
+}
+
/** Initialize empty overriding of \a reference_id by \a local_id. */
IDOverrideLibrary *BKE_lib_override_library_init(ID *local_id, ID *reference_id)
{
@@ -118,7 +132,7 @@ IDOverrideLibrary *BKE_lib_override_library_init(ID *local_id, ID *reference_id)
/** Shalow or deep copy of a whole override from \a src_id to \a dst_id. */
void BKE_lib_override_library_copy(ID *dst_id, const ID *src_id, const bool do_full_copy)
{
- BLI_assert(ID_IS_OVERRIDE_LIBRARY(src_id));
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY(src_id) || ID_IS_OVERRIDE_LIBRARY_TEMPLATE(src_id));
if (dst_id->override_library != NULL) {
if (src_id->override_library == NULL) {
@@ -194,9 +208,17 @@ void BKE_lib_override_library_free(struct IDOverrideLibrary **override, const bo
*override = NULL;
}
-static ID *lib_override_library_create_from(Main *bmain, ID *reference_id)
+static ID *lib_override_library_create_from(Main *bmain,
+ ID *reference_id,
+ const int lib_id_copy_flags)
{
- ID *local_id = BKE_id_copy(bmain, reference_id);
+ /* Note: We do not want to copy possible override data from reference here (whether it is an
+ * override template, or already an override of some other ref data). */
+ ID *local_id = BKE_id_copy_ex(bmain,
+ reference_id,
+ NULL,
+ LIB_ID_COPY_DEFAULT | LIB_ID_COPY_NO_LIB_OVERRIDE |
+ lib_id_copy_flags);
if (local_id == NULL) {
return NULL;
@@ -218,16 +240,25 @@ static ID *lib_override_library_create_from(Main *bmain, ID *reference_id)
return local_id;
}
-/** Check if given ID has some override rules that actually indicate the user edited it.
+/**
+ * Check if given ID has some override rules that actually indicate the user edited it.
*
- * TODO: This could be simplified by storing a flag in IDOverrideLibrary during the diffing
- * process? */
+ * TODO: This could be simplified by storing a flag in #IDOverrideLibrary during the diffing
+ * process?
+ */
bool BKE_lib_override_library_is_user_edited(struct ID *id)
{
if (!ID_IS_OVERRIDE_LIBRARY(id)) {
return false;
}
+ /* A bit weird, but those embedded IDs are handled by their owner ID anyway, so we can just
+ * assume they are never user-edited, actual proper detection will happen from their owner check.
+ */
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
+ return false;
+ }
+
LISTBASE_FOREACH (IDOverrideLibraryProperty *, op, &id->override_library->properties) {
LISTBASE_FOREACH (IDOverrideLibraryPropertyOperation *, opop, &op->operations) {
if ((opop->flag & IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE) != 0) {
@@ -252,7 +283,7 @@ ID *BKE_lib_override_library_create_from_id(Main *bmain,
BLI_assert(reference_id != NULL);
BLI_assert(reference_id->lib != NULL);
- ID *local_id = lib_override_library_create_from(bmain, reference_id);
+ ID *local_id = lib_override_library_create_from(bmain, reference_id, 0);
if (do_tagged_remap) {
Key *reference_key, *local_key = NULL;
@@ -297,9 +328,17 @@ ID *BKE_lib_override_library_create_from_id(Main *bmain,
* main. You can add more local IDs to be remapped to use new overriding ones by setting their
* LIB_TAG_DOIT tag.
*
+ * \param reference_library the library from which the linked data being overridden come from
+ * (i.e. the library of the linked reference ID).
+ *
+ * \param do_no_main Create the new override data outside of Main database. Used for resyncing of
+ * linked overrides.
+ *
* \return \a true on success, \a false otherwise.
*/
-bool BKE_lib_override_library_create_from_tag(Main *bmain)
+bool BKE_lib_override_library_create_from_tag(Main *bmain,
+ const Library *reference_library,
+ const bool do_no_main)
{
ID *reference_id;
bool success = true;
@@ -309,7 +348,7 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain)
/* Get all IDs we want to override. */
FOREACH_MAIN_ID_BEGIN (bmain, reference_id) {
- if ((reference_id->tag & LIB_TAG_DOIT) != 0 && reference_id->lib != NULL &&
+ if ((reference_id->tag & LIB_TAG_DOIT) != 0 && reference_id->lib == reference_library &&
BKE_idtype_idcode_is_linkable(GS(reference_id->name))) {
todo_id_iter = MEM_callocN(sizeof(*todo_id_iter), __func__);
todo_id_iter->data = reference_id;
@@ -321,10 +360,16 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain)
/* Override the IDs. */
for (todo_id_iter = todo_ids.first; todo_id_iter != NULL; todo_id_iter = todo_id_iter->next) {
reference_id = todo_id_iter->data;
+
+ /* If `newid` is already set, assume it has been handled by calling code.
+ * Only current use case: re-using proxy ID when converting to liboverride. */
if (reference_id->newid == NULL) {
- /* If `newid` is already set, assume it has been handled by calling code.
- * Only current use case: re-using proxy ID when converting to liboverride. */
- if ((reference_id->newid = lib_override_library_create_from(bmain, reference_id)) == NULL) {
+ /* Note: `no main` case is used during resync procedure, to support recursive resync.
+ * This requires extra care further down the resync process,
+ * see: #BKE_lib_override_library_resync. */
+ reference_id->newid = lib_override_library_create_from(
+ bmain, reference_id, do_no_main ? LIB_ID_CREATE_NO_MAIN : 0);
+ if (reference_id->newid == NULL) {
success = false;
break;
}
@@ -364,23 +409,43 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain)
/* Still checking the whole Main, that way we can tag other local IDs as needing to be
* remapped to use newly created overriding IDs, if needed. */
- FOREACH_MAIN_ID_BEGIN (bmain, other_id) {
- if ((other_id->tag & LIB_TAG_DOIT) != 0 && other_id->lib == NULL) {
- /* Note that using ID_REMAP_SKIP_INDIRECT_USAGE below is superfluous, as we only remap
- * local IDs usages anyway. */
+ ID *id;
+ FOREACH_MAIN_ID_BEGIN (bmain, id) {
+ /* In case we created new overrides as 'no main', they are not accessible directly in this
+ * loop, but we can get to them through their reference's `newid` pointer. */
+ if (do_no_main && id->lib == reference_id->lib && id->newid != NULL) {
+ other_id = id->newid;
+ /* Otherwise we cannot properly distinguish between IDs that are actually from the
+ * linked library (and should not be remapped), and IDs that are overrides re-generated
+ * from the reference from the linked library, and must therefore be remapped.
+ *
+ * This is reset afterwards at the end of this loop. */
+ other_id->lib = NULL;
+ }
+ else {
+ other_id = id;
+ }
+
+ /* If other ID is a linked one, but not from the same library as our reference, then we
+ * consider we should also remap it, as part of recursive resync. */
+ if ((other_id->tag & LIB_TAG_DOIT) != 0 && other_id->lib != reference_id->lib &&
+ other_id != local_id) {
BKE_libblock_relink_ex(bmain,
other_id,
reference_id,
local_id,
- ID_REMAP_SKIP_INDIRECT_USAGE | ID_REMAP_SKIP_OVERRIDE_LIBRARY);
+ ID_REMAP_SKIP_OVERRIDE_LIBRARY | ID_REMAP_FORCE_USER_REFCOUNT);
if (reference_key != NULL) {
BKE_libblock_relink_ex(bmain,
other_id,
&reference_key->id,
&local_key->id,
- ID_REMAP_SKIP_INDIRECT_USAGE | ID_REMAP_SKIP_OVERRIDE_LIBRARY);
+ ID_REMAP_SKIP_OVERRIDE_LIBRARY | ID_REMAP_FORCE_USER_REFCOUNT);
}
}
+ if (other_id != id) {
+ other_id->lib = reference_id->lib;
+ }
}
FOREACH_MAIN_ID_END;
}
@@ -404,6 +469,8 @@ typedef struct LibOverrideGroupTagData {
ID *id_root;
uint tag;
uint missing_tag;
+ /* Whether we are looping on override data, or their references (linked) one. */
+ bool is_override;
} LibOverrideGroupTagData;
/* Tag all IDs in dependency relationships within an override hierarchy/group.
@@ -416,6 +483,7 @@ static bool lib_override_hierarchy_dependencies_recursive_tag(LibOverrideGroupTa
{
Main *bmain = data->bmain;
ID *id = data->id_root;
+ const bool is_override = data->is_override;
MainIDRelationsEntry *entry = BLI_ghash_lookup(bmain->relations->relations_from_pointers, id);
BLI_assert(entry != NULL);
@@ -430,19 +498,23 @@ static bool lib_override_hierarchy_dependencies_recursive_tag(LibOverrideGroupTa
for (MainIDRelationsEntryItem *to_id_entry = entry->to_ids; to_id_entry != NULL;
to_id_entry = to_id_entry->next) {
- if ((to_id_entry->usage_flag & IDWALK_CB_LOOPBACK) != 0) {
- /* Never consider 'loop back' relationships ('from', 'parents', 'owner' etc. pointers) as
+ if ((to_id_entry->usage_flag & IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE) != 0) {
+ /* Never consider non-overridable relationships ('from', 'parents', 'owner' etc. pointers) as
* actual dependencies. */
continue;
}
/* We only consider IDs from the same library. */
ID *to_id = *to_id_entry->id_pointer.to;
- if (to_id != NULL && to_id->lib == id->lib) {
- LibOverrideGroupTagData sub_data = *data;
- sub_data.id_root = to_id;
- if (lib_override_hierarchy_dependencies_recursive_tag(&sub_data)) {
- id->tag |= data->tag;
- }
+ if (to_id == NULL || to_id->lib != id->lib ||
+ (is_override && !ID_IS_OVERRIDE_LIBRARY(to_id))) {
+ /* IDs from different libraries, or non-override IDs in case we are processing overrides, are
+ * both barriers of dependency. */
+ continue;
+ }
+ LibOverrideGroupTagData sub_data = *data;
+ sub_data.id_root = to_id;
+ if (lib_override_hierarchy_dependencies_recursive_tag(&sub_data)) {
+ id->tag |= data->tag;
}
}
@@ -454,6 +526,7 @@ static void lib_override_linked_group_tag_recursive(LibOverrideGroupTagData *dat
Main *bmain = data->bmain;
ID *id_owner = data->id_root;
BLI_assert(ID_IS_LINKED(id_owner));
+ BLI_assert(!data->is_override);
const uint tag = data->tag;
const uint missing_tag = data->missing_tag;
@@ -471,10 +544,8 @@ static void lib_override_linked_group_tag_recursive(LibOverrideGroupTagData *dat
for (MainIDRelationsEntryItem *to_id_entry = entry->to_ids; to_id_entry != NULL;
to_id_entry = to_id_entry->next) {
- if ((to_id_entry->usage_flag &
- (IDWALK_CB_EMBEDDED | IDWALK_CB_LOOPBACK | IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE)) != 0) {
- /* Never consider 'loop back' relationships ('from', 'parents', 'owner' etc. pointers), nor
- * override references or embedded ID pointers, as actual dependencies. */
+ if ((to_id_entry->usage_flag & IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE) != 0) {
+ /* Never consider non-overridable relationships as actual dependencies. */
continue;
}
@@ -521,6 +592,7 @@ static void lib_override_linked_group_tag(LibOverrideGroupTagData *data)
{
Main *bmain = data->bmain;
ID *id_root = data->id_root;
+ BLI_assert(!data->is_override);
if ((id_root->tag & LIB_TAG_MISSING)) {
id_root->tag |= data->missing_tag;
@@ -547,11 +619,12 @@ static void lib_override_linked_group_tag(LibOverrideGroupTagData *data)
}
}
-static void lib_override_local_group_tag_recursive(LibOverrideGroupTagData *data)
+static void lib_override_overrides_group_tag_recursive(LibOverrideGroupTagData *data)
{
Main *bmain = data->bmain;
ID *id_owner = data->id_root;
BLI_assert(ID_IS_OVERRIDE_LIBRARY(id_owner));
+ BLI_assert(data->is_override);
const uint tag = data->tag;
const uint missing_tag = data->missing_tag;
@@ -569,10 +642,8 @@ static void lib_override_local_group_tag_recursive(LibOverrideGroupTagData *data
for (MainIDRelationsEntryItem *to_id_entry = entry->to_ids; to_id_entry != NULL;
to_id_entry = to_id_entry->next) {
- if ((to_id_entry->usage_flag &
- (IDWALK_CB_EMBEDDED | IDWALK_CB_LOOPBACK | IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE)) != 0) {
- /* Never consider 'loop back' relationships ('from', 'parents', 'owner' etc. pointers), nor
- * override references or embedded ID pointers, as actual dependencies. */
+ if ((to_id_entry->usage_flag & IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE) != 0) {
+ /* Never consider non-overridable relationships as actual dependencies. */
continue;
}
@@ -580,45 +651,37 @@ static void lib_override_local_group_tag_recursive(LibOverrideGroupTagData *data
if (ELEM(to_id, NULL, id_owner)) {
continue;
}
- if (!ID_IS_OVERRIDE_LIBRARY(to_id) || ID_IS_LINKED(to_id)) {
+ if (!ID_IS_OVERRIDE_LIBRARY(to_id) || (to_id->lib != id_owner->lib)) {
continue;
}
- /* Do not tag 'virtual' overrides (shape keys here, as we already rejected embedded case
- * above). */
- if (ID_IS_OVERRIDE_LIBRARY_REAL(to_id)) {
- Library *reference_lib = NULL;
- if (GS(id_owner->name) == ID_KE) {
- reference_lib = ((Key *)id_owner)->from->override_library->reference->lib;
- }
- else {
- reference_lib = id_owner->override_library->reference->lib;
- }
- if (to_id->override_library->reference->lib != reference_lib) {
- /* We do not override data-blocks from other libraries, nor do we process them. */
- continue;
- }
+ Library *reference_lib = lib_override_get(bmain, id_owner)->reference->lib;
+ ID *to_id_reference = lib_override_get(bmain, to_id)->reference;
+ if (to_id_reference->lib != reference_lib) {
+ /* We do not override data-blocks from other libraries, nor do we process them. */
+ continue;
+ }
- if (to_id->override_library->reference->tag & LIB_TAG_MISSING) {
- to_id->tag |= missing_tag;
- }
- else {
- to_id->tag |= tag;
- }
+ if (to_id_reference->tag & LIB_TAG_MISSING) {
+ to_id->tag |= missing_tag;
+ }
+ else {
+ to_id->tag |= tag;
}
/* Recursively process the dependencies. */
LibOverrideGroupTagData sub_data = *data;
sub_data.id_root = to_id;
- lib_override_local_group_tag_recursive(&sub_data);
+ lib_override_overrides_group_tag_recursive(&sub_data);
}
}
/* This will tag all override IDs of an override group defined by the given `id_root`. */
-static void lib_override_local_group_tag(LibOverrideGroupTagData *data)
+static void lib_override_overrides_group_tag(LibOverrideGroupTagData *data)
{
ID *id_root = data->id_root;
- BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root) && !ID_IS_LINKED(id_root));
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root));
+ BLI_assert(data->is_override);
if ((id_root->override_library->reference->tag & LIB_TAG_MISSING)) {
id_root->tag |= data->missing_tag;
@@ -628,14 +691,17 @@ static void lib_override_local_group_tag(LibOverrideGroupTagData *data)
}
/* Tag all local overrides in id_root's group. */
- lib_override_local_group_tag_recursive(data);
+ lib_override_overrides_group_tag_recursive(data);
}
static bool lib_override_library_create_do(Main *bmain, ID *id_root)
{
BKE_main_relations_create(bmain, 0);
- LibOverrideGroupTagData data = {
- .bmain = bmain, .id_root = id_root, .tag = LIB_TAG_DOIT, .missing_tag = LIB_TAG_MISSING};
+ LibOverrideGroupTagData data = {.bmain = bmain,
+ .id_root = id_root,
+ .tag = LIB_TAG_DOIT,
+ .missing_tag = LIB_TAG_MISSING,
+ .is_override = false};
lib_override_linked_group_tag(&data);
BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
@@ -643,19 +709,7 @@ static bool lib_override_library_create_do(Main *bmain, ID *id_root)
BKE_main_relations_free(bmain);
- return BKE_lib_override_library_create_from_tag(bmain);
-}
-
-BLI_INLINE bool lib_override_library_create_post_process_object_is_instantiated(
- ViewLayer *view_layer, Object *object, const bool is_resync)
-{
- /* We cannot rely on check for object being actually instantiated in resync case, because often
- * the overridden collection is 'excluded' from the current view-layer.
- *
- * Fallback to a basic user-count check then, this is weak (since it could lead to some object
- * not being instantiated at all), but it should work fine in most common cases. */
- return ((is_resync && ID_REAL_USERS(object) >= 1) ||
- (!is_resync && BKE_view_layer_base_find(view_layer, object) != NULL));
+ return BKE_lib_override_library_create_from_tag(bmain, id_root->lib, false);
}
static void lib_override_library_create_post_process(Main *bmain,
@@ -666,15 +720,28 @@ static void lib_override_library_create_post_process(Main *bmain,
Collection *residual_storage,
const bool is_resync)
{
+ /* NOTE: We only care about local IDs here, if a linked object is not instantiated in any way we
+ * do not do anything about it. */
+
BKE_main_collection_sync(bmain);
- if (id_root->newid != NULL) {
+ /* We create a set of all objects referenced into the scene by its hierarchy of collections.
+ * NOTE: This is different that the list of bases, since objects in excluded collections etc.
+ * won't have a base, but are still considered as instanced from our point of view. */
+ GSet *all_objects_in_scene = BKE_scene_objects_as_gset(scene, NULL);
+
+ /* Instantiating the root collection or object should never be needed in resync case, since the
+ * old override would be remapped to the new one. */
+ if (!is_resync && id_root != NULL && id_root->newid != NULL && !ID_IS_LINKED(id_root->newid)) {
switch (GS(id_root->name)) {
case ID_GR: {
Object *ob_reference = id_reference != NULL && GS(id_reference->name) == ID_OB ?
(Object *)id_reference :
NULL;
Collection *collection_new = ((Collection *)id_root->newid);
+ if (is_resync && BKE_collection_is_in_scene(collection_new)) {
+ break;
+ }
if (ob_reference != NULL) {
BKE_collection_add_from_object(bmain, scene, ob_reference, collection_new);
}
@@ -688,43 +755,16 @@ static void lib_override_library_create_post_process(Main *bmain,
bmain, scene, ((Collection *)id_root), collection_new);
}
- FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (collection_new, ob_new) {
- if (ob_new != NULL && ob_new->id.override_library != NULL) {
- if (ob_reference != NULL) {
- Base *base = BKE_view_layer_base_find(view_layer, ob_new);
- if (!lib_override_library_create_post_process_object_is_instantiated(
- view_layer, ob_new, is_resync)) {
- BKE_collection_object_add_from(bmain, scene, ob_reference, ob_new);
- base = BKE_view_layer_base_find(view_layer, ob_new);
- DEG_id_tag_update_ex(
- bmain, &ob_new->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS);
- }
+ BLI_assert(BKE_collection_is_in_scene(collection_new));
- if (ob_new == (Object *)ob_reference->id.newid && base != NULL) {
- /* TODO: is setting active needed? */
- BKE_view_layer_base_select_and_set_active(view_layer, base);
- }
- }
- else if (!lib_override_library_create_post_process_object_is_instantiated(
- view_layer, ob_new, is_resync)) {
- BKE_collection_object_add(bmain, collection_new, ob_new);
- DEG_id_tag_update_ex(bmain, &ob_new->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS);
- }
- }
- }
- FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
+ all_objects_in_scene = BKE_scene_objects_as_gset(scene, all_objects_in_scene);
break;
}
case ID_OB: {
Object *ob_new = (Object *)id_root->newid;
- if (!lib_override_library_create_post_process_object_is_instantiated(
- view_layer, ob_new, is_resync)) {
- if (is_resync && residual_storage != NULL) {
- BKE_collection_object_add(bmain, residual_storage, ob_new);
- }
- else {
- BKE_collection_object_add_from(bmain, scene, (Object *)id_root, ob_new);
- }
+ if (BLI_gset_lookup(all_objects_in_scene, ob_new) == NULL) {
+ BKE_collection_object_add_from(bmain, scene, (Object *)id_root, ob_new);
+ all_objects_in_scene = BKE_scene_objects_as_gset(scene, all_objects_in_scene);
}
break;
}
@@ -734,57 +774,71 @@ static void lib_override_library_create_post_process(Main *bmain,
}
/* We need to ensure all new overrides of objects are properly instantiated. */
+ Collection *default_instantiating_collection = residual_storage;
LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
Object *ob_new = (Object *)ob->id.newid;
- if (ob_new != NULL) {
- BLI_assert(ob_new->id.override_library != NULL &&
- ob_new->id.override_library->reference == &ob->id);
-
- Collection *default_instantiating_collection = residual_storage;
- if (!lib_override_library_create_post_process_object_is_instantiated(
- view_layer, ob_new, is_resync)) {
- if (default_instantiating_collection == NULL) {
- switch (GS(id_root->name)) {
- case ID_GR: {
- default_instantiating_collection = BKE_collection_add(
- bmain, (Collection *)id_root, "OVERRIDE_HIDDEN");
- /* Hide the collection from viewport and render. */
- default_instantiating_collection->flag |= COLLECTION_RESTRICT_VIEWPORT |
- COLLECTION_RESTRICT_RENDER;
- break;
+ if (ob_new == NULL || ob_new->id.lib != NULL) {
+ continue;
+ }
+
+ BLI_assert(ob_new->id.override_library != NULL &&
+ ob_new->id.override_library->reference == &ob->id);
+
+ if (BLI_gset_lookup(all_objects_in_scene, ob_new) == NULL) {
+ if (id_root != NULL && default_instantiating_collection == NULL) {
+ ID *id_ref = id_root->newid != NULL ? id_root->newid : id_root;
+ switch (GS(id_ref->name)) {
+ case ID_GR: {
+ /* Adding the object to a specific collection outside of the root overridden one is a
+ * fairly bad idea (it breaks the override hierarchy concept). But there is no other
+ * way to do this currently (we cannot add new collections to overridden root one,
+ * this is not currently supported).
+ * Since that will be fairly annoying and noisy, only do that in case the override
+ * object is not part of any existing collection (i.e. its user count is 0). In
+ * practice this should never happen I think. */
+ if (ID_REAL_USERS(ob_new) != 0) {
+ continue;
}
- case ID_OB: {
- /* Add the other objects to one of the collections instantiating the
- * root object, or scene's master collection if none found. */
- Object *ob_root = (Object *)id_root;
- LISTBASE_FOREACH (Collection *, collection, &bmain->collections) {
- if (BKE_collection_has_object(collection, ob_root) &&
- BKE_view_layer_has_collection(view_layer, collection) &&
- !ID_IS_LINKED(collection) && !ID_IS_OVERRIDE_LIBRARY(collection)) {
- default_instantiating_collection = collection;
- }
- }
- if (default_instantiating_collection == NULL) {
- default_instantiating_collection = scene->master_collection;
+ default_instantiating_collection = BKE_collection_add(
+ bmain, (Collection *)id_root, "OVERRIDE_HIDDEN");
+ /* Hide the collection from viewport and render. */
+ default_instantiating_collection->flag |= COLLECTION_RESTRICT_VIEWPORT |
+ COLLECTION_RESTRICT_RENDER;
+ break;
+ }
+ case ID_OB: {
+ /* Add the other objects to one of the collections instantiating the
+ * root object, or scene's master collection if none found. */
+ Object *ob_ref = (Object *)id_ref;
+ LISTBASE_FOREACH (Collection *, collection, &bmain->collections) {
+ if (BKE_collection_has_object(collection, ob_ref) &&
+ BKE_view_layer_has_collection(view_layer, collection) &&
+ !ID_IS_LINKED(collection) && !ID_IS_OVERRIDE_LIBRARY(collection)) {
+ default_instantiating_collection = collection;
}
- break;
}
- default:
- BLI_assert(0);
+ break;
}
+ default:
+ BLI_assert(0);
}
-
- BKE_collection_object_add(bmain, default_instantiating_collection, ob_new);
- DEG_id_tag_update_ex(bmain, &ob_new->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS);
}
+ if (default_instantiating_collection == NULL) {
+ default_instantiating_collection = scene->master_collection;
+ }
+
+ BKE_collection_object_add(bmain, default_instantiating_collection, ob_new);
+ DEG_id_tag_update_ex(bmain, &ob_new->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS);
}
}
+
+ BLI_gset_free(all_objects_in_scene, NULL);
}
/**
* Advanced 'smart' function to create fully functional overrides.
*
- * \note Currently it only does special things if given \a id_root is an object of collection, more
+ * \note Currently it only does special things if given \a id_root is an object or collection, more
* specific behaviors may be added in the future for other ID types.
*
* \note It will override all IDs tagged with \a LIB_TAG_DOIT, and it does not clear that tag at
@@ -794,22 +848,35 @@ static void lib_override_library_create_post_process(Main *bmain,
* \param id_reference: Some reference ID used to do some post-processing after overrides have been
* created, may be NULL. Typically, the Empty object instantiating the linked collection we
* override, currently.
+ * \param r_id_root_override if not NULL, the override generated for the given \a id_root.
* \return true if override was successfully created.
*/
-bool BKE_lib_override_library_create(
- Main *bmain, Scene *scene, ViewLayer *view_layer, ID *id_root, ID *id_reference)
+bool BKE_lib_override_library_create(Main *bmain,
+ Scene *scene,
+ ViewLayer *view_layer,
+ ID *id_root,
+ ID *id_reference,
+ ID **r_id_root_override)
{
+ if (r_id_root_override != NULL) {
+ *r_id_root_override = NULL;
+ }
+
const bool success = lib_override_library_create_do(bmain, id_root);
if (!success) {
return success;
}
+ if (r_id_root_override != NULL) {
+ *r_id_root_override = id_root->newid;
+ }
+
lib_override_library_create_post_process(
bmain, scene, view_layer, id_root, id_reference, NULL, false);
/* Cleanup. */
- BKE_main_id_clear_newpoins(bmain);
+ BKE_main_id_newptr_and_tag_clear(bmain);
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
/* We need to rebuild some of the deleted override rules (for UI feedback purpose). */
@@ -819,6 +886,22 @@ bool BKE_lib_override_library_create(
}
/**
+ * Create a library override template.
+ */
+bool BKE_lib_override_library_template_create(struct ID *id)
+{
+ if (ID_IS_LINKED(id)) {
+ return false;
+ }
+ if (ID_IS_OVERRIDE_LIBRARY(id)) {
+ return false;
+ }
+
+ BKE_lib_override_library_init(id, NULL);
+ return true;
+}
+
+/**
* Convert a given proxy object into a library override.
*
* \note This is a thin wrapper around \a BKE_lib_override_library_create, only extra work is to
@@ -858,7 +941,7 @@ bool BKE_lib_override_library_proxy_convert(Main *bmain,
DEG_id_tag_update(&ob_proxy->id, ID_RECALC_COPY_ON_WRITE);
- return BKE_lib_override_library_create(bmain, scene, view_layer, id_root, id_reference);
+ return BKE_lib_override_library_create(bmain, scene, view_layer, id_root, id_reference, NULL);
}
/**
@@ -873,20 +956,25 @@ bool BKE_lib_override_library_resync(Main *bmain,
ViewLayer *view_layer,
ID *id_root,
Collection *override_resync_residual_storage,
- const bool do_hierarchy_enforce)
+ const bool do_hierarchy_enforce,
+ const bool do_post_process,
+ ReportList *reports)
{
BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root));
- BLI_assert(!ID_IS_LINKED(id_root));
ID *id_root_reference = id_root->override_library->reference;
BKE_main_relations_create(bmain, 0);
- LibOverrideGroupTagData data = {
- .bmain = bmain, .id_root = id_root, .tag = LIB_TAG_DOIT, .missing_tag = LIB_TAG_MISSING};
- lib_override_local_group_tag(&data);
+ LibOverrideGroupTagData data = {.bmain = bmain,
+ .id_root = id_root,
+ .tag = LIB_TAG_DOIT,
+ .missing_tag = LIB_TAG_MISSING,
+ .is_override = true};
+ lib_override_overrides_group_tag(&data);
BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
data.id_root = id_root_reference;
+ data.is_override = false;
lib_override_linked_group_tag(&data);
BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
@@ -897,12 +985,48 @@ bool BKE_lib_override_library_resync(Main *bmain,
BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__);
ID *id;
FOREACH_MAIN_ID_BEGIN (bmain, id) {
- if (id->tag & LIB_TAG_DOIT && ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
+ /* IDs that get fully removed from linked data remain as local overrides (using place-holder
+ * linked IDs as reference), but they are often not reachable from any current valid local
+ * override hierarchy anymore. This will ensure they get properly deleted at the end of this
+ * function. */
+ if (!ID_IS_LINKED(id) && ID_IS_OVERRIDE_LIBRARY_REAL(id) &&
+ (id->override_library->reference->tag & LIB_TAG_MISSING) != 0 &&
+ /* Unfortunately deleting obdata means deleting their objects too. Since there is no
+ * guarantee that a valid override object using an obsolete override obdata gets properly
+ * updated, we ignore those here for now. In practice this should not be a big issue. */
+ !OB_DATA_SUPPORT_ID(GS(id->name))) {
+ id->tag |= LIB_TAG_MISSING;
+ }
+
+ if (id->tag & LIB_TAG_DOIT && (id->lib == id_root->lib) && ID_IS_OVERRIDE_LIBRARY(id)) {
/* While this should not happen in typical cases (and won't be properly supported here), user
* is free to do all kind of very bad things, including having different local overrides of a
* same linked ID in a same hierarchy. */
- if (!BLI_ghash_haskey(linkedref_to_old_override, id->override_library->reference)) {
- BLI_ghash_insert(linkedref_to_old_override, id->override_library->reference, id);
+ IDOverrideLibrary *id_override_library = lib_override_get(bmain, id);
+ ID *reference_id = id_override_library->reference;
+ if (GS(reference_id->name) != GS(id->name)) {
+ switch (GS(id->name)) {
+ case ID_KE:
+ reference_id = (ID *)BKE_key_from_id(reference_id);
+ break;
+ case ID_GR:
+ BLI_assert(GS(reference_id->name) == ID_SCE);
+ reference_id = (ID *)((Scene *)reference_id)->master_collection;
+ break;
+ case ID_NT:
+ reference_id = (ID *)ntreeFromID(id);
+ break;
+ default:
+ break;
+ }
+ }
+ BLI_assert(GS(reference_id->name) == GS(id->name));
+
+ if (!BLI_ghash_haskey(linkedref_to_old_override, reference_id)) {
+ BLI_ghash_insert(linkedref_to_old_override, reference_id, id);
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
+ continue;
+ }
if ((id->override_library->reference->tag & LIB_TAG_DOIT) == 0) {
/* We have an override, but now it does not seem to be necessary to override that ID
* anymore. Check if there are some actual overrides from the user, otherwise assume
@@ -941,7 +1065,8 @@ bool BKE_lib_override_library_resync(Main *bmain,
/* Note that this call also remaps all pointers of tagged IDs from old override IDs to new
* override IDs (including within the old overrides themselves, since those are tagged too
* above). */
- const bool success = BKE_lib_override_library_create_from_tag(bmain);
+ const bool success = BKE_lib_override_library_create_from_tag(
+ bmain, id_root_reference->lib, true);
if (!success) {
return success;
@@ -950,55 +1075,104 @@ bool BKE_lib_override_library_resync(Main *bmain,
ListBase *lb;
FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) {
FOREACH_MAIN_LISTBASE_ID_BEGIN (lb, id) {
- if (id->tag & LIB_TAG_DOIT && id->newid != NULL && ID_IS_LINKED(id)) {
+ if (id->tag & LIB_TAG_DOIT && id->newid != NULL && id->lib == id_root_reference->lib) {
ID *id_override_new = id->newid;
ID *id_override_old = BLI_ghash_lookup(linkedref_to_old_override, id);
BLI_assert((id_override_new->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) == 0);
+ /* We need to 'move back' newly created override into its proper library (since it was
+ * duplicated from the reference ID with 'no main' option, it should currently be the same
+ * as the reference ID one). */
+ BLI_assert(/*id_override_new->lib == NULL || */ id_override_new->lib == id->lib);
+ BLI_assert(id_override_old == NULL || id_override_old->lib == id_root->lib);
+ id_override_new->lib = id_root->lib;
+ /* Remap step below will tag directly linked ones properly as needed. */
+ if (ID_IS_LINKED(id_override_new)) {
+ id_override_new->tag |= LIB_TAG_INDIRECT;
+ }
+
if (id_override_old != NULL) {
/* Swap the names between old override ID and new one. */
char id_name_buf[MAX_ID_NAME];
memcpy(id_name_buf, id_override_old->name, sizeof(id_name_buf));
memcpy(id_override_old->name, id_override_new->name, sizeof(id_override_old->name));
memcpy(id_override_new->name, id_name_buf, sizeof(id_override_new->name));
- /* Note that this is a very efficient way to keep BMain IDs ordered as expected after
- * swapping their names.
- * However, one has to be very careful with this when iterating over the listbase at the
- * same time. Here it works because we only execute this code when we are in the linked
- * IDs, which are always *after* all local ones, and we only affect local IDs. */
- BLI_listbase_swaplinks(lb, id_override_old, id_override_new);
-
- /* Remap the whole local IDs to use the new override. */
- BKE_libblock_remap(
- bmain, id_override_old, id_override_new, ID_REMAP_SKIP_INDIRECT_USAGE);
-
- /* Copy over overrides rules from old override ID to new one. */
- BLI_duplicatelist(&id_override_new->override_library->properties,
- &id_override_old->override_library->properties);
- for (IDOverrideLibraryProperty *
- op_new = id_override_new->override_library->properties.first,
- *op_old = id_override_old->override_library->properties.first;
- op_new;
- op_new = op_new->next, op_old = op_old->next) {
- lib_override_library_property_copy(op_new, op_old);
+
+ BLI_insertlinkreplace(lb, id_override_old, id_override_new);
+ id_override_old->tag |= LIB_TAG_NO_MAIN;
+ id_override_new->tag &= ~LIB_TAG_NO_MAIN;
+
+ if (ID_IS_OVERRIDE_LIBRARY_REAL(id_override_new)) {
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_override_old));
+
+ /* Copy over overrides rules from old override ID to new one. */
+ BLI_duplicatelist(&id_override_new->override_library->properties,
+ &id_override_old->override_library->properties);
+ IDOverrideLibraryProperty *op_new =
+ id_override_new->override_library->properties.first;
+ IDOverrideLibraryProperty *op_old =
+ id_override_old->override_library->properties.first;
+ for (; op_new; op_new = op_new->next, op_old = op_old->next) {
+ lib_override_library_property_copy(op_new, op_old);
+ }
}
}
+ else {
+ /* Add to proper main list, ensure unique name for local ID, sort, and clear relevant
+ * tags. */
+ BKE_libblock_management_main_add(bmain, id_override_new);
+ }
}
}
FOREACH_MAIN_LISTBASE_ID_END;
}
FOREACH_MAIN_LISTBASE_END;
+ /* We need to remap old to new override usages in a separate loop, after all new overrides have
+ * been added to Main. */
+ FOREACH_MAIN_ID_BEGIN (bmain, id) {
+ if (id->tag & LIB_TAG_DOIT && id->newid != NULL && id->lib == id_root_reference->lib) {
+ ID *id_override_new = id->newid;
+ ID *id_override_old = BLI_ghash_lookup(linkedref_to_old_override, id);
+
+ if (id_override_old != NULL) {
+ /* Remap all IDs to use the new override. */
+ BKE_libblock_remap(bmain, id_override_old, id_override_new, 0);
+ /* Remap no-main override IDs we just created too. */
+ GHashIterator linkedref_to_old_override_iter;
+ GHASH_ITER (linkedref_to_old_override_iter, linkedref_to_old_override) {
+ ID *id_override_old_iter = BLI_ghashIterator_getValue(&linkedref_to_old_override_iter);
+ if (id_override_old_iter->tag & LIB_TAG_NO_MAIN) {
+ BKE_libblock_relink_ex(bmain,
+ id_override_old_iter,
+ id_override_old,
+ id_override_new,
+ ID_REMAP_FORCE_USER_REFCOUNT | ID_REMAP_FORCE_NEVER_NULL_USAGE);
+ }
+ }
+ }
+ }
+ }
+ FOREACH_MAIN_ID_END;
+
+ BKE_main_collection_sync(bmain);
+
/* We need to apply override rules in a separate loop, after all ID pointers have been properly
* remapped, and all new local override IDs have gotten their proper original names, otherwise
* override operations based on those ID names would fail. */
FOREACH_MAIN_ID_BEGIN (bmain, id) {
- if (id->tag & LIB_TAG_DOIT && id->newid != NULL && ID_IS_LINKED(id)) {
+ if (id->tag & LIB_TAG_DOIT && id->newid != NULL && id->lib == id_root_reference->lib) {
ID *id_override_new = id->newid;
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(id_override_new)) {
+ continue;
+ }
ID *id_override_old = BLI_ghash_lookup(linkedref_to_old_override, id);
- if (id_override_old != NULL) {
+ if (id_override_old == NULL) {
+ continue;
+ }
+ if (ID_IS_OVERRIDE_LIBRARY_REAL(id_override_old)) {
/* Apply rules on new override ID using old one as 'source' data. */
/* Note that since we already remapped ID pointers in old override IDs to new ones, we
* can also apply ID pointer override rules safely here. */
@@ -1032,29 +1206,44 @@ bool BKE_lib_override_library_resync(Main *bmain,
RNA_OVERRIDE_APPLY_FLAG_IGNORE_ID_POINTERS :
RNA_OVERRIDE_APPLY_FLAG_NOP);
}
+
+ /* Once overrides have been properly 'transferred' from old to new ID, we can clear ID usages
+ * of the old one.
+ * This is necessary in case said old ID is not in Main anymore. */
+ BKE_libblock_relink_ex(bmain,
+ id_override_old,
+ NULL,
+ NULL,
+ ID_REMAP_FORCE_USER_REFCOUNT | ID_REMAP_FORCE_NEVER_NULL_USAGE);
+ id_override_old->tag |= LIB_TAG_NO_USER_REFCOUNT;
}
}
FOREACH_MAIN_ID_END;
/* Delete old override IDs.
- * Note that we have to use tagged group deletion here, since ID deletion also uses LIB_TAG_DOIT.
- * This improves performances anyway, so everything is fine. */
+ * Note that we have to use tagged group deletion here, since ID deletion also uses
+ * LIB_TAG_DOIT. This improves performances anyway, so everything is fine. */
+ int user_edited_overrides_deletion_count = 0;
FOREACH_MAIN_ID_BEGIN (bmain, id) {
if (id->tag & LIB_TAG_DOIT) {
- /* Note that this works because linked IDs are always after local ones (including overrides),
- * so we will only ever tag an old override ID after we have already checked it in this loop,
- * hence we cannot untag it later. */
- if (id->newid != NULL && ID_IS_LINKED(id)) {
+ /* Note that this works because linked IDs are always after local ones (including
+ * overrides), so we will only ever tag an old override ID after we have already checked it
+ * in this loop, hence we cannot untag it later. */
+ if (id->newid != NULL && id->lib == id_root_reference->lib) {
ID *id_override_old = BLI_ghash_lookup(linkedref_to_old_override, id);
if (id_override_old != NULL) {
id->newid->tag &= ~LIB_TAG_DOIT;
id_override_old->tag |= LIB_TAG_DOIT;
+ if (id_override_old->tag & LIB_TAG_NO_MAIN) {
+ BKE_id_free(bmain, id_override_old);
+ }
}
}
id->tag &= ~LIB_TAG_DOIT;
}
- /* Also deal with old overrides that went missing in new linked data. */
+ /* Also deal with old overrides that went missing in new linked data - only for real local
+ * overrides for now, not those who are linked. */
else if (id->tag & LIB_TAG_MISSING && !ID_IS_LINKED(id)) {
BLI_assert(ID_IS_OVERRIDE_LIBRARY(id));
if (!BKE_lib_override_library_is_user_edited(id)) {
@@ -1063,15 +1252,32 @@ bool BKE_lib_override_library_resync(Main *bmain,
id->tag &= ~LIB_TAG_MISSING;
CLOG_INFO(&LOG, 2, "Old override %s is being deleted", id->name);
}
+#if 0
else {
/* Otherwise, keep them, user needs to decide whether what to do with them. */
BLI_assert((id->tag & LIB_TAG_DOIT) == 0);
id_fake_user_set(id);
+ id->flag |= LIB_LIB_OVERRIDE_RESYNC_LEFTOVER;
CLOG_INFO(&LOG, 2, "Old override %s is being kept around as it was user-edited", id->name);
}
+#else
+ else {
+ /* Delete them nevertheless, with fat warning, user needs to decide whether they want to
+ * save that version of the file (and accept the loss), or not. */
+ id->tag |= LIB_TAG_DOIT;
+ id->tag &= ~LIB_TAG_MISSING;
+ CLOG_WARN(
+ &LOG, "Old override %s is being deleted even though it was user-edited", id->name);
+ user_edited_overrides_deletion_count++;
+ }
+#endif
}
}
FOREACH_MAIN_ID_END;
+
+ /* Cleanup, many pointers in this GHash are already invalid now. */
+ BLI_ghash_free(linkedref_to_old_override, NULL, NULL);
+
BKE_id_multi_tagged_delete(bmain);
/* At this point, `id_root` has very likely been deleted, we need to update it to its new
@@ -1079,64 +1285,161 @@ bool BKE_lib_override_library_resync(Main *bmain,
*/
id_root = id_root_reference->newid;
- /* Essentially ensures that potentially new overrides of new objects will be instantiated. */
- /* Note: Here 'reference' collection and 'newly added' collection are the same, which is fine
- * since we already relinked old root override collection to new resync'ed one above. So this
- * call is not expected to instantiate this new resync'ed collection anywhere, just to ensure
- * that we do not have any stray objects. */
- lib_override_library_create_post_process(bmain,
- scene,
- view_layer,
- id_root_reference,
- id_root,
- override_resync_residual_storage,
- true);
+ if (user_edited_overrides_deletion_count > 0) {
+ BKE_reportf(reports,
+ RPT_WARNING,
+ "During resync of data-block %s, %d obsolete overrides were deleted, that had "
+ "local changes defined by user",
+ id_root->name + 2,
+ user_edited_overrides_deletion_count);
+ }
+
+ if (do_post_process) {
+ /* Essentially ensures that potentially new overrides of new objects will be instantiated. */
+ /* Note: Here 'reference' collection and 'newly added' collection are the same, which is fine
+ * since we already relinked old root override collection to new resync'ed one above. So this
+ * call is not expected to instantiate this new resync'ed collection anywhere, just to ensure
+ * that we do not have any stray objects. */
+ lib_override_library_create_post_process(bmain,
+ scene,
+ view_layer,
+ id_root_reference,
+ id_root,
+ override_resync_residual_storage,
+ true);
+ }
/* Cleanup. */
- BLI_ghash_free(linkedref_to_old_override, NULL, NULL);
-
- BKE_main_id_clear_newpoins(bmain);
+ BKE_main_id_newptr_and_tag_clear(bmain);
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); /* That one should not be needed in fact. */
- /* We need to rebuild some of the deleted override rules (for UI feedback purpose). */
- BKE_lib_override_library_main_operations_create(bmain, true);
-
return success;
}
-/**
- * Detect and handle required resync of overrides data, when relations between reference linked IDs
- * have changed.
+/* Also tag ancestors overrides for resync.
*
- * This is a fairly complex and costly operation, typically it should be called after
- * #BKE_lib_override_library_main_update, which would already detect and tag a lot of cases.
+ * WARNING: Expects `bmain` to have valid relation data.
*
- * This function will first detect the remaining cases requiring a resync (namely, either when an
- * existing linked ID that did not require to be overridden before now would be, or when new IDs
- * are added to the hierarchy).
+ * NOTE: Related to `lib_override_library_main_resync_find_root_recurse` below.
*
- * Then it will handle the resync of necessary IDs (through calls to
- * #BKE_lib_override_library_resync).
+ * TODO: This is a sub-optimal, simple solution. At some point, we should rather find a way to
+ * resync a set of 'sub-roots' overrides, instead of having to 'go back' to the real root and
+ * resync the whole hierarchy.
*/
-void BKE_lib_override_library_main_resync(Main *bmain, Scene *scene, ViewLayer *view_layer)
+static void lib_override_resync_tagging_finalize_recurse(Main *bmain,
+ ID *id,
+ const int library_indirect_level)
{
- /* We use a specific collection to gather/store all 'orphaned' override collections and objects
- * generated by re-sync-process. This avoids putting them in scene's master collection. */
-#define OVERRIDE_RESYNC_RESIDUAL_STORAGE_NAME "OVERRIDE_RESYNC_LEFTOVERS"
- Collection *override_resync_residual_storage = BLI_findstring(
- &bmain->collections, OVERRIDE_RESYNC_RESIDUAL_STORAGE_NAME, offsetof(ID, name) + 2);
- if (override_resync_residual_storage != NULL &&
- override_resync_residual_storage->id.lib != NULL) {
- override_resync_residual_storage = NULL;
+ if (id->lib != NULL && id->lib->temp_index > library_indirect_level) {
+ CLOG_ERROR(
+ &LOG,
+ "While processing indirect level %d, ID %s from lib %s of indirect level %d detected "
+ "as needing resync.",
+ library_indirect_level,
+ id->name,
+ id->lib->filepath,
+ id->lib->temp_index);
}
- if (override_resync_residual_storage == NULL) {
- override_resync_residual_storage = BKE_collection_add(
- bmain, scene->master_collection, OVERRIDE_RESYNC_RESIDUAL_STORAGE_NAME);
- /* Hide the collection from viewport and render. */
- override_resync_residual_storage->flag |= COLLECTION_RESTRICT_VIEWPORT |
- COLLECTION_RESTRICT_RENDER;
+
+ MainIDRelationsEntry *entry = BLI_ghash_lookup(bmain->relations->relations_from_pointers, id);
+ BLI_assert(entry != NULL);
+
+ if (entry->tags & MAINIDRELATIONS_ENTRY_TAGS_PROCESSED) {
+ /* This ID has already been processed. */
+ return;
+ }
+ /* This way we won't process again that ID, should we encounter it again through another
+ * relationship hierarchy. */
+ entry->tags |= MAINIDRELATIONS_ENTRY_TAGS_PROCESSED;
+
+ for (MainIDRelationsEntryItem *entry_item = entry->from_ids; entry_item != NULL;
+ entry_item = entry_item->next) {
+ if (entry_item->usage_flag &
+ (IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE | IDWALK_CB_LOOPBACK)) {
+ continue;
+ }
+ ID *id_from = entry_item->id_pointer.from;
+
+ /* Case where this ID pointer was to a linked ID, that now needs to be overridden. */
+ if (id_from != id && ID_IS_OVERRIDE_LIBRARY_REAL(id_from) && id_from->lib == id->lib) {
+ id_from->tag |= LIB_TAG_LIB_OVERRIDE_NEED_RESYNC;
+ CLOG_INFO(&LOG,
+ 4,
+ "ID %s (%p) now tagged as needing resync because they use %s (%p) that needs to "
+ "be overridden",
+ id_from->name,
+ id_from->lib,
+ id->name,
+ id->lib);
+ lib_override_resync_tagging_finalize_recurse(bmain, id_from, library_indirect_level);
+ }
+ }
+}
+
+/* Ensures parent collection (or objects) in the same override group are also tagged for resync.
+ *
+ * This is needed since otherwise, some (new) ID added in one sub-collection might be used in
+ * another unrelated sub-collection, if 'root' collection is not resynced separated resync of those
+ * sub-collections would be unaware that this is the same ID, and would re-generate several
+ * overrides for it.
+ *
+ * NOTE: Related to `lib_override_resync_tagging_finalize` above.
+ */
+static ID *lib_override_library_main_resync_find_root_recurse(ID *id, int *level)
+{
+ (*level)++;
+ ID *return_id = id;
+
+ switch (GS(id->name)) {
+ case ID_GR: {
+ /* Find the highest valid collection in the parenting hierarchy.
+ * Note that in practice, in any decent common case there is only one well defined root
+ * collection anyway. */
+ int max_level = *level;
+ Collection *collection = (Collection *)id;
+ LISTBASE_FOREACH (CollectionParent *, collection_parent_iter, &collection->parents) {
+ Collection *collection_parent = collection_parent_iter->collection;
+ if (ID_IS_OVERRIDE_LIBRARY_REAL(collection_parent) &&
+ collection_parent->id.lib == id->lib) {
+ int tmp_level = *level;
+ ID *tmp_id = lib_override_library_main_resync_find_root_recurse(&collection_parent->id,
+ &tmp_level);
+ if (tmp_level > max_level) {
+ max_level = tmp_level;
+ return_id = tmp_id;
+ }
+ }
+ }
+ break;
+ }
+ case ID_OB: {
+ Object *object = (Object *)id;
+ if (object->parent != NULL && ID_IS_OVERRIDE_LIBRARY_REAL(object->parent) &&
+ object->parent->id.lib == id->lib) {
+ return_id = lib_override_library_main_resync_find_root_recurse(&object->parent->id, level);
+ }
+ break;
+ }
+ default:
+ break;
}
+ return return_id;
+}
+
+/* Ensure resync of all overrides at one level of indirect usage.
+ *
+ * We need to handle each level independently, since an override at level n may be affected by
+ * other overrides from level n + 1 etc. (i.e. from linked overrides it may use).
+ */
+static void lib_override_library_main_resync_on_library_indirect_level(
+ Main *bmain,
+ Scene *scene,
+ ViewLayer *view_layer,
+ Collection *override_resync_residual_storage,
+ const int library_indirect_level,
+ ReportList *reports)
+{
BKE_main_relations_create(bmain, 0);
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
@@ -1148,7 +1451,7 @@ void BKE_lib_override_library_main_resync(Main *bmain, Scene *scene, ViewLayer *
* those used by current existing overrides. */
ID *id;
FOREACH_MAIN_ID_BEGIN (bmain, id) {
- if (!ID_IS_OVERRIDE_LIBRARY_REAL(id) || ID_IS_LINKED(id)) {
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
continue;
}
if (id->tag & (LIB_TAG_DOIT | LIB_TAG_MISSING)) {
@@ -1159,7 +1462,8 @@ void BKE_lib_override_library_main_resync(Main *bmain, Scene *scene, ViewLayer *
LibOverrideGroupTagData data = {.bmain = bmain,
.id_root = id->override_library->reference,
.tag = LIB_TAG_DOIT,
- .missing_tag = LIB_TAG_MISSING};
+ .missing_tag = LIB_TAG_MISSING,
+ .is_override = false};
lib_override_linked_group_tag(&data);
BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
lib_override_hierarchy_dependencies_recursive_tag(&data);
@@ -1169,13 +1473,15 @@ void BKE_lib_override_library_main_resync(Main *bmain, Scene *scene, ViewLayer *
/* Now check existing overrides, those needing resync will be the one either already tagged as
* such, or the one using linked data that is now tagged as needing override. */
+ BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
FOREACH_MAIN_ID_BEGIN (bmain, id) {
- if (!ID_IS_OVERRIDE_LIBRARY_REAL(id) || ID_IS_LINKED(id)) {
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
continue;
}
if (id->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) {
- CLOG_INFO(&LOG, 4, "ID %s was already tagged as needing resync", id->name);
+ CLOG_INFO(&LOG, 4, "ID %s (%p) was already tagged as needing resync", id->name, id->lib);
+ lib_override_resync_tagging_finalize_recurse(bmain, id, library_indirect_level);
continue;
}
@@ -1184,21 +1490,23 @@ void BKE_lib_override_library_main_resync(Main *bmain, Scene *scene, ViewLayer *
for (MainIDRelationsEntryItem *entry_item = entry->to_ids; entry_item != NULL;
entry_item = entry_item->next) {
- if (entry_item->usage_flag &
- (IDWALK_CB_EMBEDDED | IDWALK_CB_LOOPBACK | IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE)) {
+ if (entry_item->usage_flag & IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE) {
continue;
}
ID *id_to = *entry_item->id_pointer.to;
/* Case where this ID pointer was to a linked ID, that now needs to be overridden. */
- if (ID_IS_LINKED(id_to) && (id_to->tag & LIB_TAG_DOIT) != 0) {
+ if (ID_IS_LINKED(id_to) && (id_to->lib != id->lib) && (id_to->tag & LIB_TAG_DOIT) != 0) {
id->tag |= LIB_TAG_LIB_OVERRIDE_NEED_RESYNC;
CLOG_INFO(&LOG,
3,
- "ID %s now tagged as needing resync because they use linked %s that now needs "
- "to be overridden",
+ "ID %s (%p) now tagged as needing resync because they use linked %s (%p) that "
+ "now needs to be overridden",
id->name,
- id_to->name);
+ id->lib,
+ id_to->name,
+ id_to->lib);
+ lib_override_resync_tagging_finalize_recurse(bmain, id, library_indirect_level);
break;
}
}
@@ -1213,21 +1521,31 @@ void BKE_lib_override_library_main_resync(Main *bmain, Scene *scene, ViewLayer *
* `FOREACH_MAIN_ID_BEGIN/END` here, and need special multi-loop processing. */
bool do_continue = true;
while (do_continue) {
- ListBase *lb;
do_continue = false;
+ ListBase *lb;
FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) {
FOREACH_MAIN_LISTBASE_ID_BEGIN (lb, id) {
- if ((id->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) == 0) {
+ if ((id->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) == 0 ||
+ (ID_IS_LINKED(id) && id->lib->temp_index < library_indirect_level) ||
+ (!ID_IS_LINKED(id) && library_indirect_level != 0)) {
continue;
}
+
+ int level = 0;
+ /* In complex non-supported cases, with several different override hierarchies sharing
+ * relations between each-other, we may end up not actually updating/replacing the given
+ * root id (see e.g. pro/shots/110_rextoria/110_0150_A/110_0150_A.anim.blend of sprites
+ * project repository, r2687).
+ * This can lead to infinite loop here, at least avoid this. */
+ id->tag &= ~LIB_TAG_LIB_OVERRIDE_NEED_RESYNC;
+ id = lib_override_library_main_resync_find_root_recurse(id, &level);
+ id->tag &= ~LIB_TAG_LIB_OVERRIDE_NEED_RESYNC;
BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id));
- if (ID_IS_LINKED(id)) {
- continue;
- }
do_continue = true;
- CLOG_INFO(&LOG, 2, "Resyncing %s...", id->name);
+
+ CLOG_INFO(&LOG, 2, "Resyncing %s (%p)...", id->name, id->lib);
const bool success = BKE_lib_override_library_resync(
- bmain, scene, view_layer, id, override_resync_residual_storage, false);
+ bmain, scene, view_layer, id, override_resync_residual_storage, false, false, reports);
CLOG_INFO(&LOG, 2, "\tSuccess: %d", success);
break;
}
@@ -1238,6 +1556,117 @@ void BKE_lib_override_library_main_resync(Main *bmain, Scene *scene, ViewLayer *
}
FOREACH_MAIN_LISTBASE_END;
}
+}
+
+static int lib_override_sort_libraries_func(LibraryIDLinkCallbackData *cb_data)
+{
+ if (cb_data->cb_flag & IDWALK_CB_LOOPBACK) {
+ return IDWALK_RET_NOP;
+ }
+ ID *id_owner = cb_data->id_owner;
+ ID *id = *cb_data->id_pointer;
+ if (id != NULL && ID_IS_LINKED(id) && id->lib != id_owner->lib) {
+ const int owner_library_indirect_level = id_owner->lib != NULL ? id_owner->lib->temp_index : 0;
+ if (owner_library_indirect_level > 10000) {
+ CLOG_ERROR(
+ &LOG,
+ "Levels of indirect usages of libraries is way too high, skipping further building "
+ "loops (Involves at least '%s' and '%s')",
+ id_owner->lib->filepath,
+ id->lib->filepath);
+ BLI_assert(0);
+ return IDWALK_RET_NOP;
+ }
+
+ if (owner_library_indirect_level >= id->lib->temp_index) {
+ id->lib->temp_index = owner_library_indirect_level + 1;
+ *(bool *)cb_data->user_data = true;
+ }
+ }
+ return IDWALK_RET_NOP;
+}
+
+/** Define the `temp_index` of libraries from their highest level of indirect usage.
+ *
+ * E.g. if lib_a uses lib_b, lib_c and lib_d, and lib_b also uses lib_d, then lib_a has an index of
+ * 1, lib_b and lib_c an index of 2, and lib_d an index of 3. */
+static int lib_override_libraries_index_define(Main *bmain)
+{
+ LISTBASE_FOREACH (Library *, library, &bmain->libraries) {
+ /* index 0 is reserved for local data. */
+ library->temp_index = 1;
+ }
+ bool do_continue = true;
+ while (do_continue) {
+ do_continue = false;
+ ID *id;
+ FOREACH_MAIN_ID_BEGIN (bmain, id) {
+ BKE_library_foreach_ID_link(
+ bmain, id, lib_override_sort_libraries_func, &do_continue, IDWALK_READONLY);
+ }
+ FOREACH_MAIN_ID_END;
+ }
+
+ int library_indirect_level_max = 0;
+ LISTBASE_FOREACH (Library *, library, &bmain->libraries) {
+ if (library->temp_index > library_indirect_level_max) {
+ library_indirect_level_max = library->temp_index;
+ }
+ }
+ return library_indirect_level_max;
+}
+
+/**
+ * Detect and handle required resync of overrides data, when relations between reference linked IDs
+ * have changed.
+ *
+ * This is a fairly complex and costly operation, typically it should be called after
+ * #BKE_lib_override_library_main_update, which would already detect and tag a lot of cases.
+ *
+ * This function will first detect the remaining cases requiring a resync (namely, either when an
+ * existing linked ID that did not require to be overridden before now would be, or when new IDs
+ * are added to the hierarchy).
+ *
+ * Then it will handle the resync of necessary IDs (through calls to
+ * #BKE_lib_override_library_resync).
+ */
+void BKE_lib_override_library_main_resync(Main *bmain,
+ Scene *scene,
+ ViewLayer *view_layer,
+ ReportList *reports)
+{
+ /* We use a specific collection to gather/store all 'orphaned' override collections and objects
+ * generated by re-sync-process. This avoids putting them in scene's master collection. */
+#define OVERRIDE_RESYNC_RESIDUAL_STORAGE_NAME "OVERRIDE_RESYNC_LEFTOVERS"
+ Collection *override_resync_residual_storage = BLI_findstring(
+ &bmain->collections, OVERRIDE_RESYNC_RESIDUAL_STORAGE_NAME, offsetof(ID, name) + 2);
+ if (override_resync_residual_storage != NULL &&
+ override_resync_residual_storage->id.lib != NULL) {
+ override_resync_residual_storage = NULL;
+ }
+ if (override_resync_residual_storage == NULL) {
+ override_resync_residual_storage = BKE_collection_add(
+ bmain, scene->master_collection, OVERRIDE_RESYNC_RESIDUAL_STORAGE_NAME);
+ /* Hide the collection from viewport and render. */
+ override_resync_residual_storage->flag |= COLLECTION_RESTRICT_VIEWPORT |
+ COLLECTION_RESTRICT_RENDER;
+ }
+
+ int library_indirect_level = lib_override_libraries_index_define(bmain);
+ while (library_indirect_level >= 0) {
+ /* Update overrides from each indirect level separately. */
+ lib_override_library_main_resync_on_library_indirect_level(bmain,
+ scene,
+ view_layer,
+ override_resync_residual_storage,
+ library_indirect_level,
+ reports);
+ library_indirect_level--;
+ }
+
+ /* Essentially ensures that potentially new overrides of new objects will be instantiated. */
+ lib_override_library_create_post_process(
+ bmain, scene, view_layer, NULL, NULL, override_resync_residual_storage, true);
if (BKE_collection_is_empty(override_resync_residual_storage)) {
BKE_collection_delete(bmain, override_resync_residual_storage, true);
@@ -1258,9 +1687,12 @@ void BKE_lib_override_library_delete(Main *bmain, ID *id_root)
/* Tag all library overrides in the chains of dependencies from the given root one. */
BKE_main_relations_create(bmain, 0);
- LibOverrideGroupTagData data = {
- .bmain = bmain, .id_root = id_root, .tag = LIB_TAG_DOIT, .missing_tag = LIB_TAG_MISSING};
- lib_override_local_group_tag(&data);
+ LibOverrideGroupTagData data = {.bmain = bmain,
+ .id_root = id_root,
+ .tag = LIB_TAG_DOIT,
+ .missing_tag = LIB_TAG_MISSING,
+ .is_override = true};
+ lib_override_overrides_group_tag(&data);
BKE_main_relations_free(bmain);
@@ -1614,7 +2046,7 @@ bool BKE_lib_override_library_property_operation_operands_validate(
return true;
}
-/** Check against potential \a bmain. */
+/** Check against potential \a bmain. */
void BKE_lib_override_library_validate(Main *UNUSED(bmain), ID *id, ReportList *reports)
{
if (id->override_library == NULL) {
@@ -1648,7 +2080,7 @@ void BKE_lib_override_library_validate(Main *UNUSED(bmain), ID *id, ReportList *
}
}
-/** Check against potential \a bmain. */
+/** Check against potential \a bmain. */
void BKE_lib_override_library_main_validate(Main *bmain, ReportList *reports)
{
ID *id;
@@ -1903,10 +2335,11 @@ bool BKE_lib_override_library_main_operations_create(Main *bmain, const bool for
}
struct LibOverrideOpCreateData create_pool_data = {.bmain = bmain, .changed = false};
- TaskPool *task_pool = BLI_task_pool_create(&create_pool_data, TASK_PRIORITY_HIGH);
+ TaskPool *task_pool = BLI_task_pool_create(
+ &create_pool_data, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON);
FOREACH_MAIN_ID_BEGIN (bmain, id) {
- if (ID_IS_OVERRIDE_LIBRARY_REAL(id) &&
+ if (!ID_IS_LINKED(id) && ID_IS_OVERRIDE_LIBRARY_REAL(id) &&
(force_auto || (id->tag & LIB_TAG_OVERRIDE_LIBRARY_AUTOREFRESH))) {
/* Usual issue with pose, it's quiet rare but sometimes they may not be up to date when this
* function is called. */
@@ -2049,8 +2482,8 @@ static void lib_override_library_id_hierarchy_recursive_reset(Main *bmain, ID *i
for (MainIDRelationsEntryItem *to_id_entry = entry->to_ids; to_id_entry != NULL;
to_id_entry = to_id_entry->next) {
- if ((to_id_entry->usage_flag & IDWALK_CB_LOOPBACK) != 0) {
- /* Never consider 'loop back' relationships ('from', 'parents', 'owner' etc. pointers) as
+ if ((to_id_entry->usage_flag & IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE) != 0) {
+ /* Never consider non-overridable relationships ('from', 'parents', 'owner' etc. pointers) as
* actual dependencies. */
continue;
}
@@ -2409,8 +2842,10 @@ ID *BKE_lib_override_library_operations_store_start(Main *bmain,
return storage_id;
}
-/** Restore given ID modified by \a BKE_lib_override_library_operations_store_start, to its
- * original state. */
+/**
+ * Restore given ID modified by #BKE_lib_override_library_operations_store_start, to its
+ * original state.
+ */
void BKE_lib_override_library_operations_store_end(
OverrideLibraryStorage *UNUSED(override_storage), ID *local)
{
diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c
index e33743eb36b..b748061ef8a 100644
--- a/source/blender/blenkernel/intern/lib_query.c
+++ b/source/blender/blenkernel/intern/lib_query.c
@@ -56,11 +56,19 @@ typedef struct LibraryForeachIDData {
*/
ID *self_id;
+ /** Flags controlling the behavior of the 'foreach id' looping code. */
int flag;
+ /** Generic flags to be passed to all callback calls for current processed data. */
int cb_flag;
+ /** Callback flags that are forbidden for all callback calls for current processed data. */
int cb_flag_clear;
+
+ /* Function to call for every ID pointers of current processed data, and its opaque user data
+ * pointer. */
LibraryIDLinkCallback callback;
void *user_data;
+ /** Store the returned value from the callback, to decide how to continue the processing of ID
+ * pointers for current data. */
int status;
/* To handle recursion. */
@@ -73,13 +81,25 @@ bool BKE_lib_query_foreachid_process(LibraryForeachIDData *data, ID **id_pp, int
if (!(data->status & IDWALK_STOP)) {
const int flag = data->flag;
ID *old_id = *id_pp;
- const int callback_return = data->callback(&(struct LibraryIDLinkCallbackData){
- .user_data = data->user_data,
- .bmain = data->bmain,
- .id_owner = data->owner_id,
- .id_self = data->self_id,
- .id_pointer = id_pp,
- .cb_flag = ((cb_flag | data->cb_flag) & ~data->cb_flag_clear)});
+
+ /* Update the callback flags with the ones defined (or forbidden) in `data` by the generic
+ * caller code. */
+ cb_flag = ((cb_flag | data->cb_flag) & ~data->cb_flag_clear);
+
+ /* Update the callback flags with some extra information regarding overrides: all 'loopback',
+ * 'internal', 'embedded' etc. ID pointers are never overridable. */
+ if (cb_flag & (IDWALK_CB_INTERNAL | IDWALK_CB_EMBEDDED | IDWALK_CB_LOOPBACK |
+ IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE)) {
+ cb_flag |= IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE;
+ }
+
+ const int callback_return = data->callback(
+ &(struct LibraryIDLinkCallbackData){.user_data = data->user_data,
+ .bmain = data->bmain,
+ .id_owner = data->owner_id,
+ .id_self = data->self_id,
+ .id_pointer = id_pp,
+ .cb_flag = cb_flag});
if (flag & IDWALK_READONLY) {
BLI_assert(*(id_pp) == old_id);
}
@@ -132,7 +152,10 @@ void BKE_lib_query_idpropertiesForeachIDLink_callback(IDProperty *id_prop, void
BLI_assert(id_prop->type == IDP_ID);
LibraryForeachIDData *data = (LibraryForeachIDData *)user_data;
- BKE_LIB_FOREACHID_PROCESS_ID(data, id_prop->data.pointer, IDWALK_CB_USER);
+ const int cb_flag = IDWALK_CB_USER | ((id_prop->flag & IDP_FLAG_OVERRIDABLE_LIBRARY) ?
+ 0 :
+ IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE);
+ BKE_LIB_FOREACHID_PROCESS_ID(data, id_prop->data.pointer, cb_flag);
}
bool BKE_library_foreach_ID_embedded(LibraryForeachIDData *data, ID **id_pp)
@@ -221,9 +244,10 @@ static void library_foreach_ID_link(Main *bmain,
* (the node tree), but re-use those generated for the 'owner' ID (the material). */
if (inherit_data == NULL) {
data.cb_flag = ID_IS_LINKED(id) ? IDWALK_CB_INDIRECT_USAGE : 0;
- /* When an ID is not in Main database, it should never refcount IDs it is using.
- * Exceptions: NodeTrees (yeah!) directly used by Materials. */
- data.cb_flag_clear = (id->tag & LIB_TAG_NO_MAIN) ? IDWALK_CB_USER | IDWALK_CB_USER_ONE : 0;
+ /* When an ID is defined as not refcounting its ID usages, it should never do it. */
+ data.cb_flag_clear = (id->tag & LIB_TAG_NO_USER_REFCOUNT) ?
+ IDWALK_CB_USER | IDWALK_CB_USER_ONE :
+ 0;
}
else {
data.cb_flag = inherit_data->cb_flag;
diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c
index 1f597bbb9a6..2641208897e 100644
--- a/source/blender/blenkernel/intern/lib_remap.c
+++ b/source/blender/blenkernel/intern/lib_remap.c
@@ -137,6 +137,7 @@ static int foreach_libblock_remap_callback(LibraryIDLinkCallbackData *cb_data)
(id_remap_data->flag & ID_REMAP_FORCE_NEVER_NULL_USAGE) == 0);
const bool skip_reference = (id_remap_data->flag & ID_REMAP_SKIP_OVERRIDE_LIBRARY) != 0;
const bool skip_never_null = (id_remap_data->flag & ID_REMAP_SKIP_NEVER_NULL_USAGE) != 0;
+ const bool force_user_refcount = (id_remap_data->flag & ID_REMAP_FORCE_USER_REFCOUNT) != 0;
#ifdef DEBUG_PRINT
printf(
@@ -203,16 +204,16 @@ static int foreach_libblock_remap_callback(LibraryIDLinkCallbackData *cb_data)
}
}
if (cb_flag & IDWALK_CB_USER) {
- /* NOTE: We don't user-count IDs which are not in the main database.
+ /* NOTE: by default we don't user-count IDs which are not in the main database.
* This is because in certain conditions we can have data-blocks in
* the main which are referencing data-blocks outside of it.
* For example, BKE_mesh_new_from_object() called on an evaluated
* object will cause such situation.
*/
- if ((old_id->tag & LIB_TAG_NO_MAIN) == 0) {
+ if (force_user_refcount || (old_id->tag & LIB_TAG_NO_MAIN) == 0) {
id_us_min(old_id);
}
- if (new_id != NULL && (new_id->tag & LIB_TAG_NO_MAIN) == 0) {
+ if (new_id != NULL && (force_user_refcount || (new_id->tag & LIB_TAG_NO_MAIN) == 0)) {
/* We do not want to handle LIB_TAG_INDIRECT/LIB_TAG_EXTERN here. */
new_id->us++;
}
@@ -303,6 +304,7 @@ static void libblock_remap_data_postprocess_object_update(Main *bmain,
/* Can be called with both old_collection and new_collection being NULL,
* this means we have to check whole Main database then. */
static void libblock_remap_data_postprocess_collection_update(Main *bmain,
+ Collection *owner_collection,
Collection *UNUSED(old_collection),
Collection *new_collection)
{
@@ -311,7 +313,7 @@ static void libblock_remap_data_postprocess_collection_update(Main *bmain,
* and BKE_main_collection_sync_remap() does not tolerate any of those, so for now always check
* whole existing collections for NULL pointers.
* I'd consider optimizing that whole collection remapping process a TODO for later. */
- BKE_collections_child_remove_nulls(bmain, NULL /*old_collection*/);
+ BKE_collections_child_remove_nulls(bmain, owner_collection, NULL /*old_collection*/);
}
else {
/* Temp safe fix, but a "tad" brute force... We should probably be able to use parents from
@@ -523,7 +525,7 @@ void BKE_libblock_remap_locked(Main *bmain, void *old_idv, void *new_idv, const
break;
case ID_GR:
libblock_remap_data_postprocess_collection_update(
- bmain, (Collection *)old_id, (Collection *)new_id);
+ bmain, NULL, (Collection *)old_id, (Collection *)new_id);
break;
case ID_ME:
case ID_CU:
@@ -628,6 +630,12 @@ void BKE_libblock_relink_ex(
switch (GS(id->name)) {
case ID_SCE:
case ID_GR: {
+ /* Note: here we know which collection we have affected, so at lest for NULL children
+ * detection we can only process that one.
+ * This is also a required fix in case `id` would not be in Main anymore, which can happen
+ * e.g. when called from `id_delete`. */
+ Collection *owner_collection = (GS(id->name) == ID_GR) ? (Collection *)id :
+ ((Scene *)id)->master_collection;
if (old_id) {
switch (GS(old_id->name)) {
case ID_OB:
@@ -636,7 +644,7 @@ void BKE_libblock_relink_ex(
break;
case ID_GR:
libblock_remap_data_postprocess_collection_update(
- bmain, (Collection *)old_id, (Collection *)new_id);
+ bmain, owner_collection, (Collection *)old_id, (Collection *)new_id);
break;
default:
break;
@@ -644,7 +652,7 @@ void BKE_libblock_relink_ex(
}
else {
/* No choice but to check whole objects/collections. */
- libblock_remap_data_postprocess_collection_update(bmain, NULL, NULL);
+ libblock_remap_data_postprocess_collection_update(bmain, owner_collection, NULL, NULL);
libblock_remap_data_postprocess_object_update(bmain, NULL, NULL);
}
break;
diff --git a/source/blender/blenkernel/intern/main.c b/source/blender/blenkernel/intern/main.c
index dc678f248c9..39cc5737ca2 100644
--- a/source/blender/blenkernel/intern/main.c
+++ b/source/blender/blenkernel/intern/main.c
@@ -538,10 +538,12 @@ ListBase *which_libbase(Main *bmain, short type)
* This is useful for generic traversal of all the blocks in a #Main (by traversing all the lists
* in turn), without worrying about block types.
*
+ * \param lb: Array of lists #INDEX_ID_MAX in length.
+ *
* \note The order of each ID type #ListBase in the array is determined by the `INDEX_ID_<IDTYPE>`
* enum definitions in `DNA_ID.h`. See also the #FOREACH_MAIN_ID_BEGIN macro in `BKE_main.h`
*/
-int set_listbasepointers(Main *bmain, ListBase *lb[INDEX_ID_MAX])
+int set_listbasepointers(Main *bmain, ListBase *lb[/*INDEX_ID_MAX*/])
{
/* Libraries may be accessed from pretty much any other ID. */
lb[INDEX_ID_LI] = &(bmain->libraries);
diff --git a/source/blender/blenkernel/intern/mask.c b/source/blender/blenkernel/intern/mask.c
index 3a3ad9ef051..a3f122115d8 100644
--- a/source/blender/blenkernel/intern/mask.c
+++ b/source/blender/blenkernel/intern/mask.c
@@ -1893,10 +1893,17 @@ static void interp_weights_uv_v2_calc(float r_uv[2],
const float pt_a[2],
const float pt_b[2])
{
+ const float segment_len = len_v2v2(pt_a, pt_b);
+ if (segment_len == 0.0f) {
+ r_uv[0] = 1.0f;
+ r_uv[1] = 0.0f;
+ return;
+ }
+
float pt_on_line[2];
r_uv[0] = closest_to_line_v2(pt_on_line, pt, pt_a, pt_b);
- r_uv[1] = (len_v2v2(pt_on_line, pt) / len_v2v2(pt_a, pt_b)) *
+ r_uv[1] = (len_v2v2(pt_on_line, pt) / segment_len) *
/* This line only sets the sign. */
((line_point_side_v2(pt_a, pt_b, pt) < 0.0f) ? -1.0f : 1.0f);
}
diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c
index 37d47a984cc..c5060e16e4d 100644
--- a/source/blender/blenkernel/intern/material.c
+++ b/source/blender/blenkernel/intern/material.c
@@ -77,6 +77,7 @@
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_build.h"
+#include "DEG_depsgraph_query.h"
#include "GPU_material.h"
@@ -700,6 +701,114 @@ Material *BKE_object_material_get(Object *ob, short act)
return ma_p ? *ma_p : NULL;
}
+static ID *get_evaluated_object_data_with_materials(Object *ob)
+{
+ ID *data = ob->data;
+ /* Meshes in edit mode need special handling. */
+ if (ob->type == OB_MESH && ob->mode == OB_MODE_EDIT) {
+ Mesh *mesh = ob->data;
+ if (mesh->edit_mesh && mesh->edit_mesh->mesh_eval_final) {
+ data = &mesh->edit_mesh->mesh_eval_final->id;
+ }
+ }
+ return data;
+}
+
+/**
+ * On evaluated objects the number of materials on an object and its data might go out of sync.
+ * This is because during evaluation materials can be added/removed on the object data.
+ *
+ * For rendering or exporting we generally use the materials on the object data. However, some
+ * material indices might be overwritten by the object.
+ */
+Material *BKE_object_material_get_eval(Object *ob, short act)
+{
+ BLI_assert(DEG_is_evaluated_object(ob));
+ const int slot_index = act - 1;
+
+ if (slot_index < 0) {
+ return NULL;
+ }
+ ID *data = get_evaluated_object_data_with_materials(ob);
+ const short *tot_slots_data_ptr = BKE_id_material_len_p(data);
+ const int tot_slots_data = tot_slots_data_ptr ? *tot_slots_data_ptr : 0;
+ if (slot_index >= tot_slots_data) {
+ return NULL;
+ }
+ const int tot_slots_object = ob->totcol;
+
+ Material ***materials_data_ptr = BKE_id_material_array_p(data);
+ Material **materials_data = materials_data_ptr ? *materials_data_ptr : NULL;
+ Material **materials_object = ob->mat;
+
+ /* Check if slot is overwritten by object. */
+ if (slot_index < tot_slots_object) {
+ if (ob->matbits) {
+ if (ob->matbits[slot_index]) {
+ Material *material = materials_object[slot_index];
+ if (material != NULL) {
+ return material;
+ }
+ }
+ }
+ }
+ /* Otherwise use data from object-data. */
+ if (slot_index < tot_slots_data) {
+ Material *material = materials_data[slot_index];
+ return material;
+ }
+ return NULL;
+}
+
+int BKE_object_material_count_eval(Object *ob)
+{
+ BLI_assert(DEG_is_evaluated_object(ob));
+ ID *id = get_evaluated_object_data_with_materials(ob);
+ const short *len_p = BKE_id_material_len_p(id);
+ return len_p ? *len_p : 0;
+}
+
+void BKE_id_material_eval_assign(ID *id, int slot, Material *material)
+{
+ BLI_assert(slot >= 1);
+ Material ***materials_ptr = BKE_id_material_array_p(id);
+ short *len_ptr = BKE_id_material_len_p(id);
+ if (ELEM(NULL, materials_ptr, len_ptr)) {
+ BLI_assert_unreachable();
+ return;
+ }
+
+ const int slot_index = slot - 1;
+ const int old_length = *len_ptr;
+
+ if (slot_index >= old_length) {
+ /* Need to grow slots array. */
+ const int new_length = slot_index + 1;
+ *materials_ptr = MEM_reallocN(*materials_ptr, sizeof(void *) * new_length);
+ *len_ptr = new_length;
+ for (int i = old_length; i < new_length; i++) {
+ (*materials_ptr)[i] = NULL;
+ }
+ }
+
+ (*materials_ptr)[slot_index] = material;
+}
+
+/**
+ * Add an empty material slot if the id has no material slots. This material slot allows the
+ * material to be overwritten by object-linked materials.
+ */
+void BKE_id_material_eval_ensure_default_slot(ID *id)
+{
+ short *len_ptr = BKE_id_material_len_p(id);
+ if (len_ptr == NULL) {
+ return;
+ }
+ if (*len_ptr == 0) {
+ BKE_id_material_eval_assign(id, 1, NULL);
+ }
+}
+
Material *BKE_gpencil_material(Object *ob, short act)
{
Material *ma = BKE_object_material_get(ob, act);
@@ -860,12 +969,6 @@ void BKE_object_material_assign(Main *bmain, Object *ob, Material *ma, short act
act = 1;
}
- /* prevent crashing when using accidentally */
- BLI_assert(!ID_IS_LINKED(ob));
- if (ID_IS_LINKED(ob)) {
- return;
- }
-
/* test arraylens */
totcolp = BKE_object_material_len_p(ob);
@@ -1028,6 +1131,43 @@ void BKE_object_material_remap_calc(Object *ob_dst, Object *ob_src, short *remap
BLI_ghash_free(gh_mat_map, NULL, NULL);
}
+/**
+ * Copy materials from evaluated geometry to the original geometry of an object.
+ */
+void BKE_object_material_from_eval_data(Main *bmain, Object *ob_orig, ID *data_eval)
+{
+ ID *data_orig = ob_orig->data;
+
+ short *orig_totcol = BKE_id_material_len_p(data_orig);
+ Material ***orig_mat = BKE_id_material_array_p(data_orig);
+
+ short *eval_totcol = BKE_id_material_len_p(data_eval);
+ Material ***eval_mat = BKE_id_material_array_p(data_eval);
+
+ if (ELEM(NULL, orig_totcol, orig_mat, eval_totcol, eval_mat)) {
+ return;
+ }
+
+ /* Remove old materials from original geometry. */
+ for (int i = 0; i < *orig_totcol; i++) {
+ id_us_min(&(*orig_mat)[i]->id);
+ }
+ MEM_SAFE_FREE(*orig_mat);
+
+ /* Create new material slots based on materials on evaluated geometry. */
+ *orig_totcol = *eval_totcol;
+ *orig_mat = MEM_callocN(sizeof(void *) * (*eval_totcol), __func__);
+ for (int i = 0; i < *eval_totcol; i++) {
+ Material *material_eval = (*eval_mat)[i];
+ if (material_eval != NULL) {
+ Material *material_orig = (Material *)DEG_get_original_id(&material_eval->id);
+ (*orig_mat)[i] = material_orig;
+ id_us_plus(&material_orig->id);
+ }
+ }
+ BKE_object_materials_test(bmain, ob_orig, data_orig);
+}
+
/* XXX - this calls many more update calls per object then are needed, could be optimized */
void BKE_object_material_array_assign(Main *bmain,
struct Object *ob,
@@ -1367,16 +1507,16 @@ void BKE_texpaint_slots_refresh_object(Scene *scene, struct Object *ob)
}
struct FindTexPaintNodeData {
- bNode *node;
- short iter_index;
- short index;
+ Image *ima;
+ bNode *r_node;
};
static bool texpaint_slot_node_find_cb(bNode *node, void *userdata)
{
struct FindTexPaintNodeData *find_data = userdata;
- if (find_data->iter_index++ == find_data->index) {
- find_data->node = node;
+ Image *ima = (Image *)node->id;
+ if (find_data->ima == ima) {
+ find_data->r_node = node;
return false;
}
@@ -1385,10 +1525,10 @@ static bool texpaint_slot_node_find_cb(bNode *node, void *userdata)
bNode *BKE_texpaint_slot_material_find_node(Material *ma, short texpaint_slot)
{
- struct FindTexPaintNodeData find_data = {NULL, 0, texpaint_slot};
+ struct FindTexPaintNodeData find_data = {ma->texpaintslot[texpaint_slot].ima, NULL};
ntree_foreach_texnode_recursive(ma->nodetree, texpaint_slot_node_find_cb, &find_data);
- return find_data.node;
+ return find_data.r_node;
}
/* r_col = current value, col = new value, (fac == 0) is no change */
diff --git a/source/blender/blenkernel/intern/mball_tessellate.c b/source/blender/blenkernel/intern/mball_tessellate.c
index 1550401cc9c..bb46c7b16c0 100644
--- a/source/blender/blenkernel/intern/mball_tessellate.c
+++ b/source/blender/blenkernel/intern/mball_tessellate.c
@@ -162,7 +162,7 @@ static void make_box_from_metaelem(Box *r, const MetaElem *ml)
}
/**
- * Partitions part of mainb array [start, end) along axis s. Returns i,
+ * Partitions part of #process.mainb array [start, end) along axis s. Returns i,
* where centroids of elements in the [start, i) segment lie "on the right side" of div,
* and elements in the [i, end) segment lie "on the left"
*/
@@ -1170,8 +1170,9 @@ static void polygonize(PROCESS *process)
/**
* Iterates over ALL objects in the scene and all of its sets, including
- * making all duplis(not only metas). Copies metas to mainb array.
- * Computes bounding boxes for building BVH. */
+ * making all duplis (not only meta-elements). Copies meta-elements to #process.mainb array.
+ * Computes bounding boxes for building BVH.
+ */
static void init_meta(Depsgraph *depsgraph, PROCESS *process, Scene *scene, Object *ob)
{
Scene *sce_iter = scene;
@@ -1435,7 +1436,7 @@ void BKE_mball_polygonize(Depsgraph *depsgraph, Scene *scene, Object *ob, ListBa
if (process.totelem > 0) {
build_bvh_spatial(&process, &process.metaball_bvh, 0, process.totelem, &process.allbb);
- /* Don't polygonize meta-balls with too high resolution (base mball to small)
+ /* Don't polygonize meta-balls with too high resolution (base mball too small)
* note: Eps was 0.0001f but this was giving problems for blood animation for
* the open movie "Sintel", using 0.00001f. */
if (ob->scale[0] > 0.00001f * (process.allbb.max[0] - process.allbb.min[0]) ||
diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c
index ddbc7e7d1ef..0b843c3a97a 100644
--- a/source/blender/blenkernel/intern/mesh.c
+++ b/source/blender/blenkernel/intern/mesh.c
@@ -1521,12 +1521,12 @@ void BKE_mesh_transform(Mesh *me, const float mat[4][4], bool do_keys)
void BKE_mesh_translate(Mesh *me, const float offset[3], const bool do_keys)
{
- MVert *mvert = CustomData_duplicate_referenced_layer(&me->vdata, CD_MVERT, me->totvert);
+ CustomData_duplicate_referenced_layer(&me->vdata, CD_MVERT, me->totvert);
/* If the referenced layer has been re-allocated need to update pointers stored in the mesh. */
BKE_mesh_update_customdata_pointers(me, false);
int i = me->totvert;
- for (mvert = me->mvert; i--; mvert++) {
+ for (MVert *mvert = me->mvert; i--; mvert++) {
add_v3_v3(mvert->co, offset);
}
diff --git a/source/blender/blenkernel/intern/mesh_boolean_convert.cc b/source/blender/blenkernel/intern/mesh_boolean_convert.cc
index 824f791d400..cfb1c192afe 100644
--- a/source/blender/blenkernel/intern/mesh_boolean_convert.cc
+++ b/source/blender/blenkernel/intern/mesh_boolean_convert.cc
@@ -28,9 +28,10 @@
#include "BKE_customdata.h"
#include "BKE_material.h"
#include "BKE_mesh.h"
-#include "BKE_mesh_boolean_convert.h"
+#include "BKE_mesh_boolean_convert.hh"
#include "BLI_alloca.h"
+#include "BLI_array.hh"
#include "BLI_float2.hh"
#include "BLI_float4x4.hh"
#include "BLI_math.h"
@@ -51,8 +52,9 @@ constexpr int estimated_max_facelen = 100; /* Used for initial size of some Vect
* so this is a hack to clean up such matrices.
* Would be better to change the transformation code itself.
*/
-static void clean_obmat(float4x4 &cleaned, const float4x4 &mat)
+static float4x4 clean_obmat(const float4x4 &mat)
{
+ float4x4 cleaned;
const float fuzz = 1e-6f;
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
@@ -69,6 +71,7 @@ static void clean_obmat(float4x4 &cleaned, const float4x4 &mat)
cleaned.values[i][j] = f;
}
}
+ return cleaned;
}
/* `MeshesToIMeshInfo` keeps track of information used when combining a number
@@ -92,10 +95,10 @@ class MeshesToIMeshInfo {
Array<Face *> mesh_to_imesh_face;
/* Transformation matrix to transform a coordinate in the corresponding
* Mesh to the local space of the first Mesh. */
- Array<float4x4> to_obj0;
+ Array<float4x4> to_target_transform;
/* For each input mesh, how to remap the material slot numbers to
* the material slots in the first mesh. */
- Array<const short *> material_remaps;
+ Span<Array<short>> material_remaps;
/* Total number of input mesh vertices. */
int tot_meshes_verts;
/* Total number of input mesh edges. */
@@ -228,7 +231,8 @@ const MEdge *MeshesToIMeshInfo::input_medge_for_orig_index(int orig_index,
return medge;
}
-/** Convert all of the meshes in `meshes` to an `IMesh` and return that.
+/**
+ * Convert all of the meshes in `meshes` to an `IMesh` and return that.
* All of the coordinates are transformed into the local space of the
* first Mesh. To do this transformation, we also need the transformation
* obmats corresponding to the Meshes, so they are in the `obmats` argument.
@@ -241,7 +245,8 @@ const MEdge *MeshesToIMeshInfo::input_medge_for_orig_index(int orig_index,
*/
static IMesh meshes_to_imesh(Span<const Mesh *> meshes,
Span<const float4x4 *> obmats,
- Span<const short *> material_remaps,
+ Span<Array<short>> material_remaps,
+ const float4x4 &target_transform,
IMeshArena &arena,
MeshesToIMeshInfo *r_info)
{
@@ -270,8 +275,8 @@ static IMesh meshes_to_imesh(Span<const Mesh *> meshes,
r_info->mesh_vert_offset = Array<int>(nmeshes);
r_info->mesh_edge_offset = Array<int>(nmeshes);
r_info->mesh_poly_offset = Array<int>(nmeshes);
- r_info->to_obj0 = Array<float4x4>(nmeshes);
- r_info->material_remaps = Array<const short *>(nmeshes);
+ r_info->to_target_transform = Array<float4x4>(nmeshes);
+ r_info->material_remaps = material_remaps;
int v = 0;
int e = 0;
int f = 0;
@@ -283,19 +288,10 @@ static IMesh meshes_to_imesh(Span<const Mesh *> meshes,
Vector<int, estimated_max_facelen> face_edge_orig;
/* To convert the coordinates of objects 1, 2, etc. into the local space
- * of object 0, we multiply each object's `obmat` by the inverse of
- * object 0's `obmat`. Exact Boolean works better if these matrices
- * are 'cleaned' -- see the comment for the `clean_obmat` function, above. */
- float4x4 obj0_mat;
- float4x4 inv_obj0_mat;
- if (obmats[0] == nullptr) {
- unit_m4(obj0_mat.values);
- unit_m4(inv_obj0_mat.values);
- }
- else {
- clean_obmat(obj0_mat, *obmats[0]);
- invert_m4_m4(inv_obj0_mat.values, obj0_mat.values);
- }
+ * of the target. We multiply each object's `obmat` by the inverse of the
+ * target matrix. Exact Boolean works better if these matrices are 'cleaned'
+ * -- see the comment for the `clean_obmat` function, above. */
+ const float4x4 inv_target_mat = clean_obmat(target_transform).inverted();
/* For each input `Mesh`, make `Vert`s and `Face`s for the corresponding
* `MVert`s and `MPoly`s, and keep track of the original indices (using the
@@ -303,45 +299,34 @@ static IMesh meshes_to_imesh(Span<const Mesh *> meshes,
* When making `Face`s, we also put in the original indices for `MEdge`s that
* make up the `MPoly`s using the same scheme. */
for (int mi : meshes.index_range()) {
- float4x4 objn_to_obj0_mat;
const Mesh *me = meshes[mi];
- if (mi == 0) {
- r_info->mesh_vert_offset[mi] = 0;
- r_info->mesh_edge_offset[mi] = 0;
- r_info->mesh_poly_offset[mi] = 0;
- unit_m4(r_info->to_obj0[0].values);
- r_info->material_remaps[0] = nullptr;
- }
- else {
- r_info->mesh_vert_offset[mi] = v;
- r_info->mesh_edge_offset[mi] = e;
- r_info->mesh_poly_offset[mi] = f;
- /* Get matrix that transforms a coordinate in objects[mi]'s local space
- * to object[0]'s local space.*/
- float4x4 objn_mat;
- if (obmats[mi] == nullptr) {
- unit_m4(objn_mat.values);
- }
- else {
- clean_obmat(objn_mat, *obmats[mi]);
- }
- objn_to_obj0_mat = inv_obj0_mat * objn_mat;
- r_info->to_obj0[mi] = objn_to_obj0_mat;
- if (mi < material_remaps.size()) {
- r_info->material_remaps[mi] = material_remaps[mi];
- }
- else {
- r_info->material_remaps[mi] = nullptr;
+ r_info->mesh_vert_offset[mi] = v;
+ r_info->mesh_edge_offset[mi] = e;
+ r_info->mesh_poly_offset[mi] = f;
+ /* Get matrix that transforms a coordinate in objects[mi]'s local space
+ * to the target space space.*/
+ const float4x4 objn_mat = (obmats[mi] == nullptr) ? float4x4::identity() :
+ clean_obmat(*obmats[mi]);
+ r_info->to_target_transform[mi] = inv_target_mat * objn_mat;
+
+ /* Skip the matrix multiplication for each point when there is no transform for a mesh,
+ * for example when the first mesh is already in the target space. (Note the logic directly
+ * above, which uses an identity matrix with a null input transform). */
+ if (obmats[mi] == nullptr) {
+ for (const MVert &vert : Span(me->mvert, me->totvert)) {
+ const float3 co = float3(vert.co);
+ r_info->mesh_to_imesh_vert[v] = arena.add_or_find_vert(mpq3(co.x, co.y, co.z), v);
+ ++v;
}
}
- for (int vi = 0; vi < me->totvert; ++vi) {
- float3 co = me->mvert[vi].co;
- if (mi > 0) {
- co = objn_to_obj0_mat * co;
+ else {
+ for (const MVert &vert : Span(me->mvert, me->totvert)) {
+ const float3 co = r_info->to_target_transform[mi] * float3(vert.co);
+ r_info->mesh_to_imesh_vert[v] = arena.add_or_find_vert(mpq3(co.x, co.y, co.z), v);
+ ++v;
}
- r_info->mesh_to_imesh_vert[v] = arena.add_or_find_vert(mpq3(co.x, co.y, co.z), v);
- ++v;
}
+
for (const MPoly &poly : Span(me->mpoly, me->totpoly)) {
int flen = poly.totloop;
face_vert.clear();
@@ -403,14 +388,14 @@ static void copy_poly_attributes(Mesh *dest_mesh,
const Mesh *orig_me,
int mp_index,
int index_in_orig_me,
- const short *material_remap)
+ Span<short> material_remap)
{
mp->mat_nr = orig_mp->mat_nr;
if (mp->mat_nr >= dest_mesh->totcol) {
mp->mat_nr = 0;
}
else {
- if (material_remap) {
+ if (material_remap.size() > 0) {
short mat_nr = material_remap[orig_mp->mat_nr];
if (mat_nr >= 0 && mat_nr < dest_mesh->totcol) {
mp->mat_nr = mat_nr;
@@ -596,7 +581,7 @@ static void copy_or_interp_loop_attributes(Mesh *dest_mesh,
cos_2d = (float(*)[2])BLI_array_alloca(cos_2d, orig_mp->totloop);
weights = Array<float>(orig_mp->totloop);
src_blocks_ofs = Array<const void *>(orig_mp->totloop);
- get_poly2d_cos(orig_me, orig_mp, cos_2d, mim.to_obj0[orig_me_index], axis_mat);
+ get_poly2d_cos(orig_me, orig_mp, cos_2d, mim.to_target_transform[orig_me_index], axis_mat);
}
CustomData *target_cd = &dest_mesh->ldata;
for (int i = 0; i < mp->totloop; ++i) {
@@ -654,7 +639,8 @@ static void copy_or_interp_loop_attributes(Mesh *dest_mesh,
}
}
-/** Make sure that there are custom data layers in the target mesh
+/**
+ * Make sure that there are custom data layers in the target mesh
* corresponding to all target layers in all of the operands after the first.
* (The target should already have layers for those in the first operand mesh).
* Edges done separately -- will have to be done later, after edges are made.
@@ -689,7 +675,8 @@ static void merge_edge_customdata_layers(Mesh *target, MeshesToIMeshInfo &mim)
}
}
-/** Convert the output IMesh im to a Blender Mesh,
+/**
+ * Convert the output IMesh im to a Blender Mesh,
* using the information in mim to get all the attributes right.
*/
static Mesh *imesh_to_mesh(IMesh *im, MeshesToIMeshInfo &mim)
@@ -743,8 +730,15 @@ static Mesh *imesh_to_mesh(IMesh *im, MeshesToIMeshInfo &mim)
++cur_loop_index;
}
- copy_poly_attributes(
- result, mp, orig_mp, orig_me, fi, index_in_orig_me, mim.material_remaps[orig_me_index]);
+ copy_poly_attributes(result,
+ mp,
+ orig_mp,
+ orig_me,
+ fi,
+ index_in_orig_me,
+ (mim.material_remaps.size() > 0) ?
+ mim.material_remaps[orig_me_index].as_span() :
+ Span<short>());
copy_or_interp_loop_attributes(result, f, mp, orig_mp, orig_me, orig_me_index, mim);
}
@@ -778,29 +772,40 @@ static Mesh *imesh_to_mesh(IMesh *im, MeshesToIMeshInfo &mim)
return result;
}
+#endif // WITH_GMP
+
/**
- * Do Exact Boolean directly, without a round trip through #BMesh.
- * The Mesh operands are in `meshes`, with corresponding transforms in in `obmats`.
+ * Do a mesh boolean operation directly on meshes (without going back and forth to BMesh).
+ * \param meshes: An array of of Mesh pointers.
+ * \param obmats: An array of pointers to the obmat matrices that transform local
+ * coordinates to global ones. It is allowed for the pointers to be null, meaning the
+ * transformation is the identity.
+ * \param material_remaps: An array of pointers to arrays of maps from material slot numbers in the
+ * corresponding mesh to the material slot in the first mesh. It is OK for material_remaps or any
+ * of its constituent arrays to be empty.
*/
-static Mesh *direct_mesh_boolean(Span<const Mesh *> meshes,
- Span<const float4x4 *> obmats,
- Span<const short *> material_remaps,
- const bool use_self,
- const bool hole_tolerant,
- const BoolOpType boolean_mode)
+Mesh *direct_mesh_boolean(Span<const Mesh *> meshes,
+ Span<const float4x4 *> obmats,
+ const float4x4 &target_transform,
+ Span<Array<short>> material_remaps,
+ const bool use_self,
+ const bool hole_tolerant,
+ const int boolean_mode)
{
- const int dbg_level = 0;
+#ifdef WITH_GMP
BLI_assert(meshes.size() == obmats.size());
- const int meshes_len = meshes.size();
- if (meshes_len <= 0) {
+ BLI_assert(material_remaps.size() == 0 || material_remaps.size() == meshes.size());
+ if (meshes.size() <= 0) {
return nullptr;
}
+
+ const int dbg_level = 0;
if (dbg_level > 0) {
- std::cout << "\nDIRECT_MESH_INTERSECT, nmeshes = " << meshes_len << "\n";
+ std::cout << "\nDIRECT_MESH_INTERSECT, nmeshes = " << meshes.size() << "\n";
}
MeshesToIMeshInfo mim;
IMeshArena arena;
- IMesh m_in = meshes_to_imesh(meshes, obmats, material_remaps, arena, &mim);
+ IMesh m_in = meshes_to_imesh(meshes, obmats, material_remaps, target_transform, arena, &mim);
std::function<int(int)> shape_fn = [&mim](int f) {
for (int mi = 0; mi < mim.mesh_poly_offset.size() - 1; ++mi) {
if (f < mim.mesh_poly_offset[mi + 1]) {
@@ -809,61 +814,25 @@ static Mesh *direct_mesh_boolean(Span<const Mesh *> meshes,
}
return static_cast<int>(mim.mesh_poly_offset.size()) - 1;
};
- IMesh m_out = boolean_mesh(
- m_in, boolean_mode, meshes_len, shape_fn, use_self, hole_tolerant, nullptr, &arena);
- if (dbg_level > 1) {
+ IMesh m_out = boolean_mesh(m_in,
+ static_cast<BoolOpType>(boolean_mode),
+ meshes.size(),
+ shape_fn,
+ use_self,
+ hole_tolerant,
+ nullptr,
+ &arena);
+ if (dbg_level > 0) {
std::cout << m_out;
write_obj_mesh(m_out, "m_out");
}
return imesh_to_mesh(&m_out, mim);
-}
-
+#else // WITH_GMP
+ UNUSED_VARS(
+ meshes, obmats, material_remaps, target_transform, use_self, hole_tolerant, boolean_mode);
+ return nullptr;
#endif // WITH_GMP
-} // namespace blender::meshintersect
-
-extern "C" {
-
-#ifdef WITH_GMP
-/* Do a mesh boolean directly on meshes (without going back and forth to BMesh).
- * The \a meshes argument is an array of \a meshes_len of Mesh pointers.
- * The \a obmats argument is an array of \a meshes_len of pointers to the obmat
- * The \a material_remaps is an array of pointers to arrays of maps from material
- * slot numbers in the corresponding mesh to the material slot in the first mesh.
- * It is OK for material_remaps or any of its constituent arrays to be NULL.
- * matrices that transform local coordinates to global ones. It is allowed
- * for the pointers to be nullptr, meaning the transformation is the identity. */
-Mesh *BKE_mesh_boolean(const Mesh **meshes,
- const float (*obmats[])[4][4],
- const short **material_remaps,
- const int meshes_len,
- const bool use_self,
- const bool hole_tolerant,
- const int boolean_mode)
-{
- const blender::float4x4 **transforms = (const blender::float4x4 **)obmats;
- return blender::meshintersect::direct_mesh_boolean(
- blender::Span(meshes, meshes_len),
- blender::Span(transforms, meshes_len),
- blender::Span(material_remaps, material_remaps ? meshes_len : 0),
- use_self,
- hole_tolerant,
- static_cast<blender::meshintersect::BoolOpType>(boolean_mode));
-}
-
-#else
-Mesh *BKE_mesh_boolean(const Mesh **UNUSED(meshes),
- const float (*obmats[])[4][4],
- const short **UNUSED(material_remaps),
- const int UNUSED(meshes_len),
- const bool UNUSED(use_self),
- const bool UNUSED(hole_tolerant),
- const int UNUSED(boolean_mode))
-{
- UNUSED_VARS(obmats);
- return NULL;
}
-#endif
-
-} // extern "C"
+} // namespace blender::meshintersect
diff --git a/source/blender/blenkernel/intern/mesh_convert.c b/source/blender/blenkernel/intern/mesh_convert.c
index 385fd473e63..c698d95ed8d 100644
--- a/source/blender/blenkernel/intern/mesh_convert.c
+++ b/source/blender/blenkernel/intern/mesh_convert.c
@@ -1027,11 +1027,15 @@ static Object *object_for_curve_to_mesh_create(Object *object)
*
* Note that there are extra fields in there like bevel and path, but those are not needed during
* conversion, so they are not copied to save unnecessary allocations. */
- if (object->runtime.curve_cache != NULL) {
+ if (temp_object->runtime.curve_cache == NULL) {
temp_object->runtime.curve_cache = MEM_callocN(sizeof(CurveCache),
"CurveCache for curve types");
+ }
+
+ if (object->runtime.curve_cache != NULL) {
BKE_displist_copy(&temp_object->runtime.curve_cache->disp, &object->runtime.curve_cache->disp);
}
+
/* Constructive modifiers will use mesh to store result. */
if (object->runtime.data_eval != NULL) {
BKE_id_copy_ex(
@@ -1057,16 +1061,25 @@ static Object *object_for_curve_to_mesh_create(Object *object)
return temp_object;
}
+/**
+ * Populate `object->runtime.curve_cache` which is then used to create the mesh.
+ */
static void curve_to_mesh_eval_ensure(Object *object)
{
- if (object->runtime.curve_cache == NULL) {
- object->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for Curve");
- }
Curve *curve = (Curve *)object->data;
Curve remapped_curve = *curve;
Object remapped_object = *object;
+ BKE_object_runtime_reset(&remapped_object);
+
remapped_object.data = &remapped_curve;
+ if (object->runtime.curve_cache == NULL) {
+ object->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for Curve");
+ }
+
+ /* Temporarily share the curve-cache with the temporary object, owned by `object`. */
+ remapped_object.runtime.curve_cache = object->runtime.curve_cache;
+
/* Clear all modifiers for the bevel object.
*
* This is because they can not be reliably evaluated for an original object (at least because
@@ -1078,6 +1091,7 @@ static void curve_to_mesh_eval_ensure(Object *object)
if (remapped_curve.bevobj != NULL) {
bevel_object = *remapped_curve.bevobj;
BLI_listbase_clear(&bevel_object.modifiers);
+ BKE_object_runtime_reset(&bevel_object);
remapped_curve.bevobj = &bevel_object;
}
@@ -1086,6 +1100,7 @@ static void curve_to_mesh_eval_ensure(Object *object)
if (remapped_curve.taperobj != NULL) {
taper_object = *remapped_curve.taperobj;
BLI_listbase_clear(&taper_object.modifiers);
+ BKE_object_runtime_reset(&taper_object);
remapped_curve.taperobj = &taper_object;
}
@@ -1098,7 +1113,7 @@ static void curve_to_mesh_eval_ensure(Object *object)
* Brecht says hold off with that. */
Mesh *mesh_eval = NULL;
BKE_displist_make_curveTypes_forRender(
- NULL, NULL, &remapped_object, &remapped_object.runtime.curve_cache->disp, &mesh_eval, false);
+ NULL, NULL, &remapped_object, &remapped_object.runtime.curve_cache->disp, false, &mesh_eval);
/* Note: this is to be consistent with `BKE_displist_make_curveTypes()`, however that is not a
* real issue currently, code here is broken in more than one way, fix(es) will be done
@@ -1107,8 +1122,12 @@ static void curve_to_mesh_eval_ensure(Object *object)
BKE_object_eval_assign_data(&remapped_object, &mesh_eval->id, true);
}
- BKE_object_free_curve_cache(&bevel_object);
- BKE_object_free_curve_cache(&taper_object);
+ /* Owned by `object` & needed by the caller to create the mesh. */
+ remapped_object.runtime.curve_cache = NULL;
+
+ BKE_object_runtime_free_data(&remapped_object);
+ BKE_object_runtime_free_data(&taper_object);
+ BKE_object_runtime_free_data(&taper_object);
}
static Mesh *mesh_new_from_curve_type_object(Object *object)
@@ -1192,7 +1211,9 @@ static Mesh *mesh_new_from_mesh(Object *object, Mesh *mesh)
return mesh_result;
}
-static Mesh *mesh_new_from_mesh_object_with_layers(Depsgraph *depsgraph, Object *object)
+static Mesh *mesh_new_from_mesh_object_with_layers(Depsgraph *depsgraph,
+ Object *object,
+ const bool preserve_origindex)
{
if (DEG_is_original_id(&object->id)) {
return mesh_new_from_mesh(object, (Mesh *)object->data);
@@ -1209,16 +1230,23 @@ static Mesh *mesh_new_from_mesh_object_with_layers(Depsgraph *depsgraph, Object
Scene *scene = DEG_get_evaluated_scene(depsgraph);
CustomData_MeshMasks mask = CD_MASK_MESH;
+ if (preserve_origindex) {
+ mask.vmask |= CD_MASK_ORIGINDEX;
+ mask.emask |= CD_MASK_ORIGINDEX;
+ mask.lmask |= CD_MASK_ORIGINDEX;
+ mask.pmask |= CD_MASK_ORIGINDEX;
+ }
Mesh *result = mesh_create_eval_final(depsgraph, scene, &object_for_eval, &mask);
return result;
}
static Mesh *mesh_new_from_mesh_object(Depsgraph *depsgraph,
Object *object,
- bool preserve_all_data_layers)
+ const bool preserve_all_data_layers,
+ const bool preserve_origindex)
{
- if (preserve_all_data_layers) {
- return mesh_new_from_mesh_object_with_layers(depsgraph, object);
+ if (preserve_all_data_layers || preserve_origindex) {
+ return mesh_new_from_mesh_object_with_layers(depsgraph, object, preserve_origindex);
}
Mesh *mesh_input = object->data;
/* If we are in edit mode, use evaluated mesh from edit structure, matching to what
@@ -1229,7 +1257,10 @@ static Mesh *mesh_new_from_mesh_object(Depsgraph *depsgraph,
return mesh_new_from_mesh(object, mesh_input);
}
-Mesh *BKE_mesh_new_from_object(Depsgraph *depsgraph, Object *object, bool preserve_all_data_layers)
+Mesh *BKE_mesh_new_from_object(Depsgraph *depsgraph,
+ Object *object,
+ const bool preserve_all_data_layers,
+ const bool preserve_origindex)
{
Mesh *new_mesh = NULL;
switch (object->type) {
@@ -1242,7 +1273,8 @@ Mesh *BKE_mesh_new_from_object(Depsgraph *depsgraph, Object *object, bool preser
new_mesh = mesh_new_from_mball_object(object);
break;
case OB_MESH:
- new_mesh = mesh_new_from_mesh_object(depsgraph, object, preserve_all_data_layers);
+ new_mesh = mesh_new_from_mesh_object(
+ depsgraph, object, preserve_all_data_layers, preserve_origindex);
break;
default:
/* Object does not have geometry data. */
@@ -1307,7 +1339,7 @@ Mesh *BKE_mesh_new_from_object_to_bmain(Main *bmain,
{
BLI_assert(ELEM(object->type, OB_FONT, OB_CURVE, OB_SURF, OB_MBALL, OB_MESH));
- Mesh *mesh = BKE_mesh_new_from_object(depsgraph, object, preserve_all_data_layers);
+ Mesh *mesh = BKE_mesh_new_from_object(depsgraph, object, preserve_all_data_layers, false);
if (mesh == NULL) {
/* Unable to convert the object to a mesh, return an empty one. */
Mesh *mesh_in_bmain = BKE_mesh_add(bmain, ((ID *)object->data)->name + 2);
@@ -1535,7 +1567,7 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src,
* check whether it is still true with Mesh */
Mesh tmp = *mesh_dst;
int totvert, totedge /*, totface */ /* UNUSED */, totloop, totpoly;
- int did_shapekeys = 0;
+ bool did_shapekeys = false;
eCDAllocType alloctype = CD_DUPLICATE;
if (take_ownership /* && dm->type == DM_TYPE_CDDM && dm->needsFree */) {
@@ -1590,7 +1622,7 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src,
}
shapekey_layers_to_keyblocks(mesh_src, mesh_dst, uid);
- did_shapekeys = 1;
+ did_shapekeys = true;
}
/* copy texture space */
@@ -1619,13 +1651,18 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src,
totedge);
}
if (!CustomData_has_layer(&tmp.pdata, CD_MPOLY)) {
- /* TODO(Sybren): assignment to tmp.mxxx is probably not necessary due to the
- * BKE_mesh_update_customdata_pointers() call below. */
- tmp.mloop = (alloctype == CD_ASSIGN) ? mesh_src->mloop : MEM_dupallocN(mesh_src->mloop);
- tmp.mpoly = (alloctype == CD_ASSIGN) ? mesh_src->mpoly : MEM_dupallocN(mesh_src->mpoly);
-
- CustomData_add_layer(&tmp.ldata, CD_MLOOP, CD_ASSIGN, tmp.mloop, tmp.totloop);
- CustomData_add_layer(&tmp.pdata, CD_MPOLY, CD_ASSIGN, tmp.mpoly, tmp.totpoly);
+ CustomData_add_layer(&tmp.ldata,
+ CD_MLOOP,
+ CD_ASSIGN,
+ (alloctype == CD_ASSIGN) ? mesh_src->mloop :
+ MEM_dupallocN(mesh_src->mloop),
+ tmp.totloop);
+ CustomData_add_layer(&tmp.pdata,
+ CD_MPOLY,
+ CD_ASSIGN,
+ (alloctype == CD_ASSIGN) ? mesh_src->mpoly :
+ MEM_dupallocN(mesh_src->mpoly),
+ tmp.totpoly);
}
/* object had got displacement layer, should copy this layer to save sculpted data */
@@ -1634,15 +1671,16 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src,
if (totloop == mesh_dst->totloop) {
MDisps *mdisps = CustomData_get_layer(&mesh_dst->ldata, CD_MDISPS);
CustomData_add_layer(&tmp.ldata, CD_MDISPS, alloctype, mdisps, totloop);
+ if (alloctype == CD_ASSIGN) {
+ /* Assign NULL to prevent double-free. */
+ CustomData_set_layer(&mesh_dst->ldata, CD_MDISPS, NULL);
+ }
}
}
/* yes, must be before _and_ after tessellate */
BKE_mesh_update_customdata_pointers(&tmp, false);
- /* since 2.65 caller must do! */
- // BKE_mesh_tessface_calc(&tmp);
-
CustomData_free(&mesh_dst->vdata, mesh_dst->totvert);
CustomData_free(&mesh_dst->edata, mesh_dst->totedge);
CustomData_free(&mesh_dst->fdata, mesh_dst->totface);
diff --git a/source/blender/blenkernel/intern/mesh_evaluate.c b/source/blender/blenkernel/intern/mesh_evaluate.c
index eee1b763be5..826094420a7 100644
--- a/source/blender/blenkernel/intern/mesh_evaluate.c
+++ b/source/blender/blenkernel/intern/mesh_evaluate.c
@@ -1715,7 +1715,8 @@ void BKE_mesh_normals_loop_split(const MVert *mverts,
loop_split_generator(NULL, &common_data);
}
else {
- TaskPool *task_pool = BLI_task_pool_create(&common_data, TASK_PRIORITY_HIGH);
+ TaskPool *task_pool = BLI_task_pool_create(
+ &common_data, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON);
loop_split_generator(task_pool, &common_data);
@@ -2351,7 +2352,7 @@ float BKE_mesh_calc_poly_uv_area(const MPoly *mpoly, const MLoopUV *uv_array)
}
/* finally calculate the area */
- area = area_poly_v2((const float(*)[2])vertexcos, (unsigned int)mpoly->totloop);
+ area = area_poly_v2(vertexcos, (uint)mpoly->totloop);
return area;
}
@@ -2566,7 +2567,7 @@ bool BKE_mesh_center_median_from_polys(const Mesh *me, float r_cent[3])
const MLoop *mloop = me->mloop;
const MVert *mvert = me->mvert;
zero_v3(r_cent);
- for (mpoly = me->mpoly; i--; mpoly++) {
+ for (; 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);
diff --git a/source/blender/blenkernel/intern/mesh_fair.cc b/source/blender/blenkernel/intern/mesh_fair.cc
index ac6dd96ed90..2a364b183b2 100644
--- a/source/blender/blenkernel/intern/mesh_fair.cc
+++ b/source/blender/blenkernel/intern/mesh_fair.cc
@@ -172,7 +172,7 @@ class FairingContext {
}
/* Early return, nothing to do. */
- if (num_affected_vertices == 0 || num_affected_vertices == totvert_) {
+ if (ELEM(num_affected_vertices, 0, totvert_)) {
return;
}
diff --git a/source/blender/blenkernel/intern/mesh_mirror.c b/source/blender/blenkernel/intern/mesh_mirror.c
index a22b52d68d5..3d30c218fba 100644
--- a/source/blender/blenkernel/intern/mesh_mirror.c
+++ b/source/blender/blenkernel/intern/mesh_mirror.c
@@ -51,7 +51,7 @@ Mesh *BKE_mesh_mirror_bisect_on_mirror_plane_for_modifier(MirrorModifierData *mm
(axis == 1 && mmd->flag & MOD_MIR_BISECT_FLIP_AXIS_Y) ||
(axis == 2 && mmd->flag & MOD_MIR_BISECT_FLIP_AXIS_Z));
- const float bisect_distance = 0.001f;
+ const float bisect_distance = mmd->bisect_threshold;
Mesh *result;
BMesh *bm;
@@ -183,6 +183,19 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd,
if (do_bisect) {
copy_v3_v3(plane_co, itmp[3]);
copy_v3_v3(plane_no, itmp[axis]);
+
+ /* Account for non-uniform scale in `ob`, see: T87592. */
+ float ob_scale[3] = {
+ len_squared_v3(ob->obmat[0]),
+ len_squared_v3(ob->obmat[1]),
+ len_squared_v3(ob->obmat[2]),
+ };
+ /* Scale to avoid precision loss with extreme values. */
+ const float ob_scale_max = max_fff(UNPACK3(ob_scale));
+ if (LIKELY(ob_scale_max != 0.0f)) {
+ mul_v3_fl(ob_scale, 1.0f / ob_scale_max);
+ mul_v3_v3(plane_no, ob_scale);
+ }
}
}
else if (do_bisect) {
diff --git a/source/blender/blenkernel/intern/mesh_remap.c b/source/blender/blenkernel/intern/mesh_remap.c
index f3b29171762..58d2a24f15b 100644
--- a/source/blender/blenkernel/intern/mesh_remap.c
+++ b/source/blender/blenkernel/intern/mesh_remap.c
@@ -2399,8 +2399,7 @@ void BKE_mesh_remap_calc_polys_from_mesh(const int mode,
}
tot_rays *= tot_rays;
- poly_area_2d_inv = area_poly_v2((const float(*)[2])poly_vcos_2d,
- (unsigned int)mp->totloop);
+ poly_area_2d_inv = area_poly_v2(poly_vcos_2d, (uint)mp->totloop);
/* In case we have a null-area degenerated poly... */
poly_area_2d_inv = 1.0f / max_ff(poly_area_2d_inv, 1e-9f);
diff --git a/source/blender/blenkernel/intern/mesh_sample.cc b/source/blender/blenkernel/intern/mesh_sample.cc
new file mode 100644
index 00000000000..91c9951ae89
--- /dev/null
+++ b/source/blender/blenkernel/intern/mesh_sample.cc
@@ -0,0 +1,158 @@
+/*
+ * 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.
+ */
+
+#include "BKE_attribute_math.hh"
+#include "BKE_mesh_runtime.h"
+#include "BKE_mesh_sample.hh"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+namespace blender::bke::mesh_surface_sample {
+
+static Span<MLoopTri> get_mesh_looptris(const Mesh &mesh)
+{
+ /* This only updates a cache and can be considered to be logically const. */
+ const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(const_cast<Mesh *>(&mesh));
+ const int looptris_len = BKE_mesh_runtime_looptri_len(&mesh);
+ return {looptris, looptris_len};
+}
+
+template<typename T>
+BLI_NOINLINE static void sample_point_attribute(const Mesh &mesh,
+ const Span<int> looptri_indices,
+ const Span<float3> bary_coords,
+ const VArray<T> &data_in,
+ const MutableSpan<T> data_out)
+{
+ const Span<MLoopTri> looptris = get_mesh_looptris(mesh);
+
+ for (const int i : bary_coords.index_range()) {
+ const int looptri_index = looptri_indices[i];
+ const MLoopTri &looptri = looptris[looptri_index];
+ const float3 &bary_coord = bary_coords[i];
+
+ const int v0_index = mesh.mloop[looptri.tri[0]].v;
+ const int v1_index = mesh.mloop[looptri.tri[1]].v;
+ const int v2_index = mesh.mloop[looptri.tri[2]].v;
+
+ const T v0 = data_in[v0_index];
+ const T v1 = data_in[v1_index];
+ const T v2 = data_in[v2_index];
+
+ const T interpolated_value = attribute_math::mix3(bary_coord, v0, v1, v2);
+ data_out[i] = interpolated_value;
+ }
+}
+
+void sample_point_attribute(const Mesh &mesh,
+ const Span<int> looptri_indices,
+ const Span<float3> bary_coords,
+ const GVArray &data_in,
+ const GMutableSpan data_out)
+{
+ BLI_assert(data_out.size() == looptri_indices.size());
+ BLI_assert(data_out.size() == bary_coords.size());
+ BLI_assert(data_in.size() == mesh.totvert);
+ BLI_assert(data_in.type() == data_out.type());
+
+ const CPPType &type = data_in.type();
+ attribute_math::convert_to_static_type(type, [&](auto dummy) {
+ using T = decltype(dummy);
+ sample_point_attribute<T>(
+ mesh, looptri_indices, bary_coords, data_in.typed<T>(), data_out.typed<T>());
+ });
+}
+
+template<typename T>
+BLI_NOINLINE static void sample_corner_attribute(const Mesh &mesh,
+ const Span<int> looptri_indices,
+ const Span<float3> bary_coords,
+ const VArray<T> &data_in,
+ const MutableSpan<T> data_out)
+{
+ Span<MLoopTri> looptris = get_mesh_looptris(mesh);
+
+ for (const int i : bary_coords.index_range()) {
+ const int looptri_index = looptri_indices[i];
+ const MLoopTri &looptri = looptris[looptri_index];
+ const float3 &bary_coord = bary_coords[i];
+
+ const int loop_index_0 = looptri.tri[0];
+ const int loop_index_1 = looptri.tri[1];
+ const int loop_index_2 = looptri.tri[2];
+
+ const T v0 = data_in[loop_index_0];
+ const T v1 = data_in[loop_index_1];
+ const T v2 = data_in[loop_index_2];
+
+ const T interpolated_value = attribute_math::mix3(bary_coord, v0, v1, v2);
+ data_out[i] = interpolated_value;
+ }
+}
+
+void sample_corner_attribute(const Mesh &mesh,
+ const Span<int> looptri_indices,
+ const Span<float3> bary_coords,
+ const GVArray &data_in,
+ const GMutableSpan data_out)
+{
+ BLI_assert(data_out.size() == looptri_indices.size());
+ BLI_assert(data_out.size() == bary_coords.size());
+ BLI_assert(data_in.size() == mesh.totloop);
+ BLI_assert(data_in.type() == data_out.type());
+
+ const CPPType &type = data_in.type();
+ attribute_math::convert_to_static_type(type, [&](auto dummy) {
+ using T = decltype(dummy);
+ sample_corner_attribute<T>(
+ mesh, looptri_indices, bary_coords, data_in.typed<T>(), data_out.typed<T>());
+ });
+}
+
+template<typename T>
+void sample_face_attribute(const Mesh &mesh,
+ const Span<int> looptri_indices,
+ const VArray<T> &data_in,
+ const MutableSpan<T> data_out)
+{
+ Span<MLoopTri> looptris = get_mesh_looptris(mesh);
+
+ for (const int i : data_out.index_range()) {
+ const int looptri_index = looptri_indices[i];
+ const MLoopTri &looptri = looptris[looptri_index];
+ const int poly_index = looptri.poly;
+ data_out[i] = data_in[poly_index];
+ }
+}
+
+void sample_face_attribute(const Mesh &mesh,
+ const Span<int> looptri_indices,
+ const GVArray &data_in,
+ const GMutableSpan data_out)
+{
+ BLI_assert(data_out.size() == looptri_indices.size());
+ BLI_assert(data_in.size() == mesh.totpoly);
+ BLI_assert(data_in.type() == data_out.type());
+
+ const CPPType &type = data_in.type();
+ attribute_math::convert_to_static_type(type, [&](auto dummy) {
+ using T = decltype(dummy);
+ sample_face_attribute<T>(mesh, looptri_indices, data_in.typed<T>(), data_out.typed<T>());
+ });
+}
+
+} // namespace blender::bke::mesh_surface_sample
diff --git a/source/blender/blenkernel/intern/mesh_tangent.c b/source/blender/blenkernel/intern/mesh_tangent.c
index 2e22e521a13..6a7ff0851f5 100644
--- a/source/blender/blenkernel/intern/mesh_tangent.c
+++ b/source/blender/blenkernel/intern/mesh_tangent.c
@@ -656,7 +656,7 @@ void BKE_mesh_calc_loop_tangent_ex(const MVert *mvert,
/* Calculation */
if (looptri_len != 0) {
- TaskPool *task_pool = BLI_task_pool_create(NULL, TASK_PRIORITY_LOW);
+ TaskPool *task_pool = BLI_task_pool_create(NULL, TASK_PRIORITY_LOW, TASK_ISOLATION_ON);
tangent_mask_curr = 0;
/* Calculate tangent layers */
diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c
index 34b7c4234ec..e60f0102b9a 100644
--- a/source/blender/blenkernel/intern/modifier.c
+++ b/source/blender/blenkernel/intern/modifier.c
@@ -281,7 +281,7 @@ bool BKE_modifier_is_preview(ModifierData *md)
return false;
}
-ModifierData *BKE_modifiers_findby_type(Object *ob, ModifierType type)
+ModifierData *BKE_modifiers_findby_type(const Object *ob, ModifierType type)
{
LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
if (md->type == type) {
@@ -291,7 +291,7 @@ ModifierData *BKE_modifiers_findby_type(Object *ob, ModifierType type)
return NULL;
}
-ModifierData *BKE_modifiers_findby_name(Object *ob, const char *name)
+ModifierData *BKE_modifiers_findby_name(const Object *ob, const char *name)
{
return BLI_findstring(&(ob->modifiers), name, offsetof(ModifierData, name));
}
@@ -729,7 +729,6 @@ Object *BKE_modifiers_is_deformed_by_armature(Object *ob)
ArmatureGpencilModifierData *agmd = NULL;
GpencilModifierData *gmd = BKE_gpencil_modifiers_get_virtual_modifierlist(
ob, &gpencilvirtualModifierData);
- gmd = ob->greasepencil_modifiers.first;
/* return the first selected armature, this lets us use multiple armatures */
for (; gmd; gmd = gmd->next) {
@@ -749,7 +748,6 @@ Object *BKE_modifiers_is_deformed_by_armature(Object *ob)
VirtualModifierData virtualModifierData;
ArmatureModifierData *amd = NULL;
ModifierData *md = BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData);
- md = ob->modifiers.first;
/* return the first selected armature, this lets us use multiple armatures */
for (; md; md = md->next) {
diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c
index 9c2cd03dbc2..0f2f56f4f2b 100644
--- a/source/blender/blenkernel/intern/movieclip.c
+++ b/source/blender/blenkernel/intern/movieclip.c
@@ -950,7 +950,7 @@ static void movieclip_load_get_size(MovieClip *clip)
int width, height;
MovieClipUser user = {0};
- user.framenr = 1;
+ user.framenr = BKE_movieclip_remap_clip_to_scene_frame(clip, 1);
BKE_movieclip_get_size(clip, &user, &width, &height);
if (width && height) {
diff --git a/source/blender/blenkernel/intern/nla.c b/source/blender/blenkernel/intern/nla.c
index 63b14c30b3c..92e21183acb 100644
--- a/source/blender/blenkernel/intern/nla.c
+++ b/source/blender/blenkernel/intern/nla.c
@@ -254,7 +254,7 @@ NlaTrack *BKE_nlatrack_copy(Main *bmain,
* \param flag: Control ID pointers management, see LIB_ID_CREATE_.../LIB_ID_COPY_...
* flags in BKE_lib_id.h
*/
-void BKE_nla_tracks_copy(Main *bmain, ListBase *dst, ListBase *src, const int flag)
+void BKE_nla_tracks_copy(Main *bmain, ListBase *dst, const ListBase *src, const int flag)
{
NlaTrack *nlt, *nlt_d;
@@ -275,6 +275,54 @@ void BKE_nla_tracks_copy(Main *bmain, ListBase *dst, ListBase *src, const int fl
}
}
+/* Set adt_dest->actstrip to the strip with the same index as adt_source->actstrip. */
+static void update_active_strip(AnimData *adt_dest,
+ NlaTrack *track_dest,
+ const AnimData *adt_source,
+ NlaTrack *track_source)
+{
+ BLI_assert(BLI_listbase_count(&track_source->strips) == BLI_listbase_count(&track_dest->strips));
+
+ NlaStrip *strip_dest = track_dest->strips.first;
+ LISTBASE_FOREACH (NlaStrip *, strip_source, &track_source->strips) {
+ if (strip_source == adt_source->actstrip) {
+ adt_dest->actstrip = strip_dest;
+ }
+
+ strip_dest = strip_dest->next;
+ }
+}
+
+/* Set adt_dest->act_track to the track with the same index as adt_source->act_track. */
+static void update_active_track(AnimData *adt_dest, const AnimData *adt_source)
+{
+ BLI_assert(BLI_listbase_count(&adt_source->nla_tracks) ==
+ BLI_listbase_count(&adt_dest->nla_tracks));
+
+ NlaTrack *track_dest = adt_dest->nla_tracks.first;
+ LISTBASE_FOREACH (NlaTrack *, track_source, &adt_source->nla_tracks) {
+ if (track_source == adt_source->act_track) {
+ adt_dest->act_track = track_dest;
+ /* Assumption: the active strip is on the active track. */
+ update_active_strip(adt_dest, track_dest, adt_source, track_source);
+ }
+
+ track_dest = track_dest->next;
+ }
+}
+
+void BKE_nla_tracks_copy_from_adt(Main *bmain,
+ AnimData *adt_dest,
+ const AnimData *adt_source,
+ const int flag)
+{
+ adt_dest->act_track = NULL;
+ adt_dest->actstrip = NULL;
+
+ BKE_nla_tracks_copy(bmain, &adt_dest->nla_tracks, &adt_source->nla_tracks, flag);
+ update_active_track(adt_dest, adt_source);
+}
+
/* Adding ------------------------------------------- */
/* Add a NLA Track to the given AnimData
@@ -434,8 +482,10 @@ NlaStrip *BKE_nla_add_soundstrip(Main *bmain, Scene *scene, Speaker *speaker)
return strip;
}
-/** Callback used by lib_query to walk over all ID usages (mimics `foreach_id` callback of
- * `IDTypeInfo` structure). */
+/**
+ * Callback used by lib_query to walk over all ID usages (mimics `foreach_id` callback of
+ * `IDTypeInfo` structure).
+ */
void BKE_nla_strip_foreach_id(NlaStrip *strip, LibraryForeachIDData *data)
{
BKE_LIB_FOREACHID_PROCESS(data, strip->act, IDWALK_CB_USER);
@@ -1380,8 +1430,10 @@ static void nlastrip_fix_resize_overlaps(NlaStrip *strip)
}
}
-/** Recalculate the start and end frames for the strip to match the bounds of its action such that
- * the overall NLA animation result is unchanged. */
+/**
+ * Recalculate the start and end frames for the strip to match the bounds of its action such that
+ * the overall NLA animation result is unchanged.
+ */
void BKE_nlastrip_recalculate_bounds_sync_action(NlaStrip *strip)
{
float prev_actstart;
diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc
index d06e4030117..1c82218fc65 100644
--- a/source/blender/blenkernel/intern/node.cc
+++ b/source/blender/blenkernel/intern/node.cc
@@ -303,6 +303,16 @@ static void library_foreach_node_socket(LibraryForeachIDData *data, bNodeSocket
BKE_LIB_FOREACHID_PROCESS(data, default_value->value, IDWALK_CB_USER);
break;
}
+ case SOCK_TEXTURE: {
+ bNodeSocketValueTexture *default_value = (bNodeSocketValueTexture *)sock->default_value;
+ BKE_LIB_FOREACHID_PROCESS(data, default_value->value, IDWALK_CB_USER);
+ break;
+ }
+ case SOCK_MATERIAL: {
+ bNodeSocketValueMaterial *default_value = (bNodeSocketValueMaterial *)sock->default_value;
+ BKE_LIB_FOREACHID_PROCESS(data, default_value->value, IDWALK_CB_USER);
+ break;
+ }
case SOCK_FLOAT:
case SOCK_VECTOR:
case SOCK_RGBA:
@@ -434,6 +444,12 @@ static void write_node_socket_default_value(BlendWriter *writer, bNodeSocket *so
case SOCK_COLLECTION:
BLO_write_struct(writer, bNodeSocketValueCollection, sock->default_value);
break;
+ case SOCK_TEXTURE:
+ BLO_write_struct(writer, bNodeSocketValueTexture, sock->default_value);
+ break;
+ case SOCK_MATERIAL:
+ BLO_write_struct(writer, bNodeSocketValueMaterial, sock->default_value);
+ break;
case __SOCK_MESH:
case SOCK_CUSTOM:
case SOCK_SHADER:
@@ -497,10 +513,16 @@ void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree)
if (node->storage) {
/* could be handlerized at some point, now only 1 exception still */
- if ((ntree->type == NTREE_SHADER) &&
+ if ((ELEM(ntree->type, NTREE_SHADER, NTREE_GEOMETRY)) &&
ELEM(node->type, SH_NODE_CURVE_VEC, SH_NODE_CURVE_RGB)) {
BKE_curvemapping_blend_write(writer, (const CurveMapping *)node->storage);
}
+ else if ((ntree->type == NTREE_GEOMETRY) && (node->type == GEO_NODE_ATTRIBUTE_CURVE_MAP)) {
+ BLO_write_struct_by_name(writer, node->typeinfo->storagename, node->storage);
+ NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)node->storage;
+ BKE_curvemapping_blend_write(writer, (const CurveMapping *)data->curve_vec);
+ BKE_curvemapping_blend_write(writer, (const CurveMapping *)data->curve_rgb);
+ }
else if (ntree->type == NTREE_SHADER && (node->type == SH_NODE_SCRIPT)) {
NodeShaderScript *nss = (NodeShaderScript *)node->storage;
if (nss->bytecode) {
@@ -676,6 +698,18 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree)
BKE_curvemapping_blend_read(reader, (CurveMapping *)node->storage);
break;
}
+ case GEO_NODE_ATTRIBUTE_CURVE_MAP: {
+ NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)node->storage;
+ BLO_read_data_address(reader, &data->curve_vec);
+ if (data->curve_vec) {
+ BKE_curvemapping_blend_read(reader, data->curve_vec);
+ }
+ BLO_read_data_address(reader, &data->curve_rgb);
+ if (data->curve_rgb) {
+ BKE_curvemapping_blend_read(reader, data->curve_rgb);
+ }
+ break;
+ }
case SH_NODE_SCRIPT: {
NodeShaderScript *nss = (NodeShaderScript *)node->storage;
BLO_read_data_address(reader, &nss->bytecode);
@@ -778,6 +812,13 @@ static void lib_link_node_socket(BlendLibReader *reader, Library *lib, bNodeSock
{
IDP_BlendReadLib(reader, sock->prop);
+ /* This can happen for all socket types when a file is saved in an older version of Blender than
+ * it was originally created in (T86298). Some socket types still require a default value. The
+ * default value of those sockets will be created in `ntreeSetTypes`. */
+ if (sock->default_value == nullptr) {
+ return;
+ }
+
switch ((eNodeSocketDatatype)sock->type) {
case SOCK_OBJECT: {
bNodeSocketValueObject *default_value = (bNodeSocketValueObject *)sock->default_value;
@@ -795,6 +836,16 @@ static void lib_link_node_socket(BlendLibReader *reader, Library *lib, bNodeSock
BLO_read_id_address(reader, lib, &default_value->value);
break;
}
+ case SOCK_TEXTURE: {
+ bNodeSocketValueTexture *default_value = (bNodeSocketValueTexture *)sock->default_value;
+ BLO_read_id_address(reader, lib, &default_value->value);
+ break;
+ }
+ case SOCK_MATERIAL: {
+ bNodeSocketValueMaterial *default_value = (bNodeSocketValueMaterial *)sock->default_value;
+ BLO_read_id_address(reader, lib, &default_value->value);
+ break;
+ }
case SOCK_FLOAT:
case SOCK_VECTOR:
case SOCK_RGBA:
@@ -880,6 +931,16 @@ static void expand_node_socket(BlendExpander *expander, bNodeSocket *sock)
BLO_expand(expander, default_value->value);
break;
}
+ case SOCK_TEXTURE: {
+ bNodeSocketValueTexture *default_value = (bNodeSocketValueTexture *)sock->default_value;
+ BLO_expand(expander, default_value->value);
+ break;
+ }
+ case SOCK_MATERIAL: {
+ bNodeSocketValueMaterial *default_value = (bNodeSocketValueMaterial *)sock->default_value;
+ BLO_expand(expander, default_value->value);
+ break;
+ }
case SOCK_FLOAT:
case SOCK_VECTOR:
case SOCK_RGBA:
@@ -1307,8 +1368,8 @@ void nodeUnregisterType(bNodeType *nt)
bool nodeTypeUndefined(bNode *node)
{
return (node->typeinfo == &NodeTypeUndefined) ||
- (node->type == NODE_GROUP && node->id && ID_IS_LINKED(node->id) &&
- (node->id->tag & LIB_TAG_MISSING));
+ ((node->type == NODE_GROUP || node->type == NODE_CUSTOM_GROUP) && node->id &&
+ ID_IS_LINKED(node->id) && (node->id->tag & LIB_TAG_MISSING));
}
GHashIterator *nodeTypeGetIterator(void)
@@ -1364,7 +1425,9 @@ GHashIterator *nodeSocketTypeGetIterator(void)
return BLI_ghashIterator_new(nodesockettypes_hash);
}
-struct bNodeSocket *nodeFindSocket(const bNode *node, int in_out, const char *identifier)
+struct bNodeSocket *nodeFindSocket(const bNode *node,
+ eNodeSocketInOut in_out,
+ const char *identifier)
{
const ListBase *sockets = (in_out == SOCK_IN) ? &node->inputs : &node->outputs;
LISTBASE_FOREACH (bNodeSocket *, sock, sockets) {
@@ -1445,6 +1508,16 @@ static void socket_id_user_increment(bNodeSocket *sock)
id_us_plus((ID *)default_value->value);
break;
}
+ case SOCK_TEXTURE: {
+ bNodeSocketValueTexture *default_value = (bNodeSocketValueTexture *)sock->default_value;
+ id_us_plus((ID *)default_value->value);
+ break;
+ }
+ case SOCK_MATERIAL: {
+ bNodeSocketValueMaterial *default_value = (bNodeSocketValueMaterial *)sock->default_value;
+ id_us_plus((ID *)default_value->value);
+ break;
+ }
case SOCK_FLOAT:
case SOCK_VECTOR:
case SOCK_RGBA:
@@ -1484,6 +1557,20 @@ static void socket_id_user_decrement(bNodeSocket *sock)
}
break;
}
+ case SOCK_TEXTURE: {
+ bNodeSocketValueTexture *default_value = (bNodeSocketValueTexture *)sock->default_value;
+ if (default_value->value != nullptr) {
+ id_us_min(&default_value->value->id);
+ }
+ break;
+ }
+ case SOCK_MATERIAL: {
+ bNodeSocketValueMaterial *default_value = (bNodeSocketValueMaterial *)sock->default_value;
+ if (default_value->value != nullptr) {
+ id_us_min(&default_value->value->id);
+ }
+ break;
+ }
case SOCK_FLOAT:
case SOCK_VECTOR:
case SOCK_RGBA:
@@ -1521,7 +1608,7 @@ void nodeModifySocketType(
bNodeSocket *nodeAddSocket(bNodeTree *ntree,
bNode *node,
- int in_out,
+ eNodeSocketInOut in_out,
const char *idname,
const char *identifier,
const char *name)
@@ -1543,7 +1630,7 @@ bNodeSocket *nodeAddSocket(bNodeTree *ntree,
bNodeSocket *nodeInsertSocket(bNodeTree *ntree,
bNode *node,
- int in_out,
+ eNodeSocketInOut in_out,
const char *idname,
bNodeSocket *next_sock,
const char *identifier,
@@ -1575,6 +1662,8 @@ const char *nodeStaticSocketType(int type, int subtype)
return "NodeSocketFloatAngle";
case PROP_TIME:
return "NodeSocketFloatTime";
+ case PROP_TIME_ABSOLUTE:
+ return "NodeSocketFloatTimeAbsolute";
case PROP_DISTANCE:
return "NodeSocketFloatDistance";
case PROP_NONE:
@@ -1627,6 +1716,10 @@ const char *nodeStaticSocketType(int type, int subtype)
return "NodeSocketGeometry";
case SOCK_COLLECTION:
return "NodeSocketCollection";
+ case SOCK_TEXTURE:
+ return "NodeSocketTexture";
+ case SOCK_MATERIAL:
+ return "NodeSocketMaterial";
}
return nullptr;
}
@@ -1646,6 +1739,8 @@ const char *nodeStaticSocketInterfaceType(int type, int subtype)
return "NodeSocketInterfaceFloatAngle";
case PROP_TIME:
return "NodeSocketInterfaceFloatTime";
+ case PROP_TIME_ABSOLUTE:
+ return "NodeSocketInterfaceFloatTimeAbsolute";
case PROP_DISTANCE:
return "NodeSocketInterfaceFloatDistance";
case PROP_NONE:
@@ -1698,13 +1793,17 @@ const char *nodeStaticSocketInterfaceType(int type, int subtype)
return "NodeSocketInterfaceGeometry";
case SOCK_COLLECTION:
return "NodeSocketInterfaceCollection";
+ case SOCK_TEXTURE:
+ return "NodeSocketInterfaceTexture";
+ case SOCK_MATERIAL:
+ return "NodeSocketInterfaceMaterial";
}
return nullptr;
}
bNodeSocket *nodeAddStaticSocket(bNodeTree *ntree,
bNode *node,
- int in_out,
+ eNodeSocketInOut in_out,
int type,
int subtype,
const char *identifier,
@@ -1724,7 +1823,7 @@ bNodeSocket *nodeAddStaticSocket(bNodeTree *ntree,
bNodeSocket *nodeInsertStaticSocket(bNodeTree *ntree,
bNode *node,
- int in_out,
+ eNodeSocketInOut in_out,
int type,
int subtype,
bNodeSocket *next_sock,
@@ -1998,7 +2097,8 @@ bNode *nodeAddStaticNode(const struct bContext *C, bNodeTree *ntree, int type)
/* do an extra poll here, because some int types are used
* for multiple node types, this helps find the desired type
*/
- if (ntype->type == type && (!ntype->poll || ntype->poll(ntype, ntree))) {
+ const char *disabled_hint;
+ if (ntype->type == type && (!ntype->poll || ntype->poll(ntype, ntree, &disabled_hint))) {
idname = ntype->idname;
break;
}
@@ -2162,6 +2262,17 @@ bNodeTree *ntreeCopyTree_ex_new_pointers(const bNodeTree *ntree,
return new_ntree;
}
+static int node_count_links(const bNodeTree *ntree, const bNodeSocket *socket)
+{
+ int count = 0;
+ LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
+ if (ELEM(socket, link->fromsock, link->tosock)) {
+ count++;
+ }
+ }
+ return count;
+}
+
/* also used via rna api, so we check for proper input output direction */
bNodeLink *nodeAddLink(
bNodeTree *ntree, bNode *fromnode, bNodeSocket *fromsock, bNode *tonode, bNodeSocket *tosock)
@@ -2198,6 +2309,10 @@ bNodeLink *nodeAddLink(
ntree->update |= NTREE_UPDATE_LINKS;
}
+ if (link->tosock->flag & SOCK_MULTI_INPUT) {
+ link->multi_input_socket_index = node_count_links(ntree, link->tosock) - 1;
+ }
+
return link;
}
@@ -3087,10 +3202,12 @@ void ntreeSetOutput(bNodeTree *ntree)
* might be different for editor or for "real" use... */
}
-/** Get address of potential nodetree pointer of given ID.
+/**
+ * Get address of potential node-tree pointer of given ID.
*
* \warning Using this function directly is potentially dangerous, if you don't know or are not
- * sure, please use `ntreeFromID()` instead. */
+ * sure, please use `ntreeFromID()` instead.
+ */
bNodeTree **BKE_ntree_ptr_from_id(ID *id)
{
switch (GS(id->name)) {
@@ -3220,13 +3337,11 @@ void ntreeLocalMerge(Main *bmain, bNodeTree *localtree, bNodeTree *ntree)
/* ************ NODE TREE INTERFACE *************** */
static bNodeSocket *make_socket_interface(bNodeTree *ntree,
- int in_out,
+ eNodeSocketInOut in_out,
const char *idname,
const char *name)
{
bNodeSocketType *stype = nodeSocketTypeFind(idname);
- int own_index = ntree->cur_index++;
-
if (stype == nullptr) {
return nullptr;
}
@@ -3238,7 +3353,7 @@ static bNodeSocket *make_socket_interface(bNodeTree *ntree,
sock->type = SOCK_CUSTOM; /* int type undefined by default */
/* assign new unique index */
- own_index = ntree->cur_index++;
+ const int own_index = ntree->cur_index++;
/* use the own_index as socket identifier */
if (in_out == SOCK_IN) {
BLI_snprintf(sock->identifier, MAX_NAME, "Input_%d", own_index);
@@ -3256,7 +3371,9 @@ static bNodeSocket *make_socket_interface(bNodeTree *ntree,
return sock;
}
-bNodeSocket *ntreeFindSocketInterface(bNodeTree *ntree, int in_out, const char *identifier)
+bNodeSocket *ntreeFindSocketInterface(bNodeTree *ntree,
+ eNodeSocketInOut in_out,
+ const char *identifier)
{
ListBase *sockets = (in_out == SOCK_IN) ? &ntree->inputs : &ntree->outputs;
LISTBASE_FOREACH (bNodeSocket *, iosock, sockets) {
@@ -3268,7 +3385,7 @@ bNodeSocket *ntreeFindSocketInterface(bNodeTree *ntree, int in_out, const char *
}
bNodeSocket *ntreeAddSocketInterface(bNodeTree *ntree,
- int in_out,
+ eNodeSocketInOut in_out,
const char *idname,
const char *name)
{
@@ -3284,8 +3401,11 @@ bNodeSocket *ntreeAddSocketInterface(bNodeTree *ntree,
return iosock;
}
-bNodeSocket *ntreeInsertSocketInterface(
- bNodeTree *ntree, int in_out, const char *idname, bNodeSocket *next_sock, const char *name)
+bNodeSocket *ntreeInsertSocketInterface(bNodeTree *ntree,
+ eNodeSocketInOut in_out,
+ const char *idname,
+ bNodeSocket *next_sock,
+ const char *name)
{
bNodeSocket *iosock = make_socket_interface(ntree, in_out, idname, name);
if (in_out == SOCK_IN) {
@@ -3304,7 +3424,7 @@ struct bNodeSocket *ntreeAddSocketInterfaceFromSocket(bNodeTree *ntree,
bNodeSocket *from_sock)
{
bNodeSocket *iosock = ntreeAddSocketInterface(
- ntree, from_sock->in_out, from_sock->idname, from_sock->name);
+ ntree, static_cast<eNodeSocketInOut>(from_sock->in_out), from_sock->idname, from_sock->name);
if (iosock) {
if (iosock->typeinfo->interface_from_socket) {
iosock->typeinfo->interface_from_socket(ntree, iosock, from_node, from_sock);
@@ -3319,7 +3439,11 @@ struct bNodeSocket *ntreeInsertSocketInterfaceFromSocket(bNodeTree *ntree,
bNodeSocket *from_sock)
{
bNodeSocket *iosock = ntreeInsertSocketInterface(
- ntree, from_sock->in_out, from_sock->idname, next_sock, from_sock->name);
+ ntree,
+ static_cast<eNodeSocketInOut>(from_sock->in_out),
+ from_sock->idname,
+ next_sock,
+ from_sock->name);
if (iosock) {
if (iosock->typeinfo->interface_from_socket) {
iosock->typeinfo->interface_from_socket(ntree, iosock, from_node, from_sock);
@@ -4205,7 +4329,7 @@ void ntreeUpdateAllUsers(Main *main, ID *id)
if (GS(id->name) == ID_NT) {
bNodeTree *ngroup = (bNodeTree *)id;
- if (ngroup->type == NTREE_GEOMETRY) {
+ if (ngroup->type == NTREE_GEOMETRY && (ngroup->update & NTREE_UPDATE_GROUP)) {
LISTBASE_FOREACH (Object *, object, &main->objects) {
LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
if (md->type == eModifierType_Nodes) {
@@ -4387,15 +4511,17 @@ static void node_type_base_defaults(bNodeType *ntype)
}
/* allow this node for any tree type */
-static bool node_poll_default(bNodeType *UNUSED(ntype), bNodeTree *UNUSED(ntree))
+static bool node_poll_default(bNodeType *UNUSED(ntype),
+ bNodeTree *UNUSED(ntree),
+ const char **UNUSED(disabled_hint))
{
return true;
}
/* use the basic poll function */
-static bool node_poll_instance_default(bNode *node, bNodeTree *ntree)
+static bool node_poll_instance_default(bNode *node, bNodeTree *ntree, const char **disabled_hint)
{
- return node->typeinfo->poll(node->typeinfo, ntree);
+ return node->typeinfo->poll(node->typeinfo, ntree, disabled_hint);
}
/* NOLINTNEXTLINE: readability-function-size */
@@ -4614,7 +4740,9 @@ void node_type_internal_links(bNodeType *ntype,
/* callbacks for undefined types */
-static bool node_undefined_poll(bNodeType *UNUSED(ntype), bNodeTree *UNUSED(nodetree))
+static bool node_undefined_poll(bNodeType *UNUSED(ntype),
+ bNodeTree *UNUSED(nodetree),
+ const char **UNUSED(r_disabled_hint))
{
/* this type can not be added deliberately, it's just a placeholder */
return false;
@@ -4693,6 +4821,7 @@ static void registerCompositNodes()
register_node_type_cmp_defocus();
register_node_type_cmp_sunbeams();
register_node_type_cmp_denoise();
+ register_node_type_cmp_antialiasing();
register_node_type_cmp_valtorgb();
register_node_type_cmp_rgbtobw();
@@ -4903,31 +5032,46 @@ static void registerGeometryNodes()
register_node_type_geo_group();
register_node_type_geo_align_rotation_to_vector();
+ register_node_type_geo_attribute_clamp();
register_node_type_geo_attribute_color_ramp();
register_node_type_geo_attribute_combine_xyz();
register_node_type_geo_attribute_compare();
register_node_type_geo_attribute_convert();
+ register_node_type_geo_attribute_curve_map();
register_node_type_geo_attribute_fill();
+ register_node_type_geo_attribute_map_range();
register_node_type_geo_attribute_math();
register_node_type_geo_attribute_mix();
register_node_type_geo_attribute_proximity();
register_node_type_geo_attribute_randomize();
register_node_type_geo_attribute_separate_xyz();
+ register_node_type_geo_attribute_transfer();
register_node_type_geo_attribute_vector_math();
+ register_node_type_geo_attribute_vector_rotate();
register_node_type_geo_attribute_remove();
register_node_type_geo_boolean();
+ register_node_type_geo_bounding_box();
register_node_type_geo_collection_info();
+ register_node_type_geo_convex_hull();
+ register_node_type_geo_curve_length();
+ register_node_type_geo_curve_to_mesh();
+ register_node_type_geo_curve_resample();
+ register_node_type_geo_delete_geometry();
register_node_type_geo_edge_split();
+ register_node_type_geo_input_material();
register_node_type_geo_is_viewport();
register_node_type_geo_join_geometry();
+ register_node_type_geo_material_assign();
+ register_node_type_geo_material_replace();
register_node_type_geo_mesh_primitive_circle();
register_node_type_geo_mesh_primitive_cone();
register_node_type_geo_mesh_primitive_cube();
register_node_type_geo_mesh_primitive_cylinder();
+ register_node_type_geo_mesh_primitive_grid();
register_node_type_geo_mesh_primitive_ico_sphere();
register_node_type_geo_mesh_primitive_line();
- register_node_type_geo_mesh_primitive_plane();
register_node_type_geo_mesh_primitive_uv_sphere();
+ register_node_type_geo_mesh_to_curve();
register_node_type_geo_object_info();
register_node_type_geo_point_distribute();
register_node_type_geo_point_instance();
@@ -4937,8 +5081,10 @@ static void registerGeometryNodes()
register_node_type_geo_point_translate();
register_node_type_geo_points_to_volume();
register_node_type_geo_sample_texture();
+ register_node_type_geo_select_by_material();
register_node_type_geo_subdivide();
register_node_type_geo_subdivision_surface();
+ register_node_type_geo_switch();
register_node_type_geo_transform();
register_node_type_geo_triangulate();
register_node_type_geo_volume_to_mesh();
diff --git a/source/blender/blenkernel/intern/node_ui_storage.cc b/source/blender/blenkernel/intern/node_ui_storage.cc
index f2a152ac00d..e5e9f00c7c3 100644
--- a/source/blender/blenkernel/intern/node_ui_storage.cc
+++ b/source/blender/blenkernel/intern/node_ui_storage.cc
@@ -39,7 +39,7 @@ using blender::Vector;
* bNodeTree struct in DNA. This could change if the node tree had a runtime struct. */
static std::mutex global_ui_storage_mutex;
-static void ui_storage_ensure(bNodeTree &ntree)
+static NodeTreeUIStorage &ui_storage_ensure(bNodeTree &ntree)
{
/* As an optimization, only acquire a lock if the UI storage doesn't exist,
* because it only needs to be allocated once for every node tree. */
@@ -50,6 +50,7 @@ static void ui_storage_ensure(bNodeTree &ntree)
ntree.ui_storage = new NodeTreeUIStorage();
}
}
+ return *ntree.ui_storage;
}
const NodeUIStorage *BKE_node_tree_ui_storage_get_from_context(const bContext *C,
@@ -90,7 +91,7 @@ void BKE_nodetree_ui_storage_free_for_context(bNodeTree &ntree,
{
NodeTreeUIStorage *ui_storage = ntree.ui_storage;
if (ui_storage != nullptr) {
- std::lock_guard<std::mutex> lock(ui_storage->context_map_mutex);
+ std::lock_guard<std::mutex> lock(ui_storage->mutex);
ui_storage->context_map.remove(context);
}
}
@@ -126,20 +127,14 @@ static void node_error_message_log(bNodeTree &ntree,
}
}
-static NodeUIStorage &node_ui_storage_ensure(bNodeTree &ntree,
+static NodeUIStorage &node_ui_storage_ensure(NodeTreeUIStorage &locked_ui_storage,
const NodeTreeEvaluationContext &context,
const bNode &node)
{
- ui_storage_ensure(ntree);
- NodeTreeUIStorage &ui_storage = *ntree.ui_storage;
-
- std::lock_guard<std::mutex> lock(ui_storage.context_map_mutex);
Map<std::string, NodeUIStorage> &node_tree_ui_storage =
- ui_storage.context_map.lookup_or_add_default(context);
-
+ locked_ui_storage.context_map.lookup_or_add_default(context);
NodeUIStorage &node_ui_storage = node_tree_ui_storage.lookup_or_add_default_as(
StringRef(node.name));
-
return node_ui_storage;
}
@@ -149,9 +144,12 @@ void BKE_nodetree_error_message_add(bNodeTree &ntree,
const NodeWarningType type,
std::string message)
{
+ NodeTreeUIStorage &ui_storage = ui_storage_ensure(ntree);
+ std::lock_guard lock{ui_storage.mutex};
+
node_error_message_log(ntree, node, message, type);
- NodeUIStorage &node_ui_storage = node_ui_storage_ensure(ntree, context, node);
+ NodeUIStorage &node_ui_storage = node_ui_storage_ensure(ui_storage, context, node);
node_ui_storage.warnings.append({type, std::move(message)});
}
@@ -162,7 +160,10 @@ void BKE_nodetree_attribute_hint_add(bNodeTree &ntree,
const AttributeDomain domain,
const CustomDataType data_type)
{
- NodeUIStorage &node_ui_storage = node_ui_storage_ensure(ntree, context, node);
- node_ui_storage.attribute_hints.add_as(attribute_name,
- AvailableAttributeInfo{domain, data_type});
+ NodeTreeUIStorage &ui_storage = ui_storage_ensure(ntree);
+ std::lock_guard lock{ui_storage.mutex};
+
+ NodeUIStorage &node_ui_storage = node_ui_storage_ensure(ui_storage, context, node);
+ node_ui_storage.attribute_hints.add_as(
+ AvailableAttributeInfo{attribute_name, domain, data_type});
}
diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c
index b07c4b22c39..b73f6a5b78c 100644
--- a/source/blender/blenkernel/intern/object.c
+++ b/source/blender/blenkernel/intern/object.c
@@ -311,8 +311,8 @@ static void object_free_data(ID *id)
/* Free runtime curves data. */
if (ob->runtime.curve_cache) {
BKE_curve_bevelList_free(&ob->runtime.curve_cache->bev);
- if (ob->runtime.curve_cache->path) {
- free_path(ob->runtime.curve_cache->path);
+ if (ob->runtime.curve_cache->anim_path_accum_length) {
+ MEM_freeN((void *)ob->runtime.curve_cache->anim_path_accum_length);
}
MEM_freeN(ob->runtime.curve_cache);
ob->runtime.curve_cache = NULL;
@@ -1188,8 +1188,8 @@ void BKE_object_free_curve_cache(Object *ob)
if (ob->runtime.curve_cache) {
BKE_displist_free(&ob->runtime.curve_cache->disp);
BKE_curve_bevelList_free(&ob->runtime.curve_cache->bev);
- if (ob->runtime.curve_cache->path) {
- free_path(ob->runtime.curve_cache->path);
+ if (ob->runtime.curve_cache->anim_path_accum_length) {
+ MEM_freeN((void *)ob->runtime.curve_cache->anim_path_accum_length);
}
BKE_nurbList_free(&ob->runtime.curve_cache->deformed_nurbs);
MEM_freeN(ob->runtime.curve_cache);
@@ -1332,12 +1332,9 @@ bool BKE_object_support_modifier_type_check(const Object *ob, int modifier_type)
if (ob->type == OB_HAIR) {
return (mti->modifyHair != NULL) || (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly);
}
- if (ob->type == OB_POINTCLOUD) {
+ if (ELEM(ob->type, OB_POINTCLOUD, OB_VOLUME)) {
return (mti->modifyGeometrySet != NULL);
}
- if (ob->type == OB_VOLUME) {
- return (mti->modifyVolume != NULL);
- }
if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_LATTICE)) {
if (ob->type == OB_LATTICE && (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly) == 0) {
return false;
@@ -1359,8 +1356,9 @@ static bool object_modifier_type_copy_check(ModifierType md_type)
return !ELEM(md_type, eModifierType_Hook, eModifierType_Collision);
}
-/** Find a `psys` matching given `psys_src` in `ob_dst` (i.e. sharing the same ParticleSettings
- * ID), or add one, and return valid `psys` from `ob_dst`.
+/**
+ * Find a `psys` matching given `psys_src` in `ob_dst` (i.e. sharing the same ParticleSettings ID),
+ * or add one, and return valid `psys` from `ob_dst`.
*
* \note Order handling is fairly weak here. This code assumes that it is called **before** the
* modifier using the psys is actually copied, and that this copied modifier will be added at the
@@ -1392,7 +1390,8 @@ static ParticleSystem *object_copy_modifier_particle_system_ensure(Main *bmain,
return psys_dst;
}
-/** Copy a single modifier.
+/**
+ * Copy a single modifier.
*
* \note **Do not** use this function to copy a whole modifier stack (see note below too). Use
* `BKE_object_modifier_stack_copy` instead.
@@ -1400,7 +1399,8 @@ static ParticleSystem *object_copy_modifier_particle_system_ensure(Main *bmain,
* \note Complex modifiers relaying on other data (like e.g. dynamic paint or fluid using particle
* systems) are not always 100% 'correctly' copied here, since we have to use heuristics to decide
* which particle system to use or add in `ob_dst`, and it's placement in the stack, etc. If used
- * more than once, this function should preferably be called in stack order. */
+ * more than once, this function should preferably be called in stack order.
+ */
bool BKE_object_copy_modifier(
Main *bmain, Scene *scene, Object *ob_dst, const Object *ob_src, ModifierData *md_src)
{
@@ -1500,10 +1500,12 @@ bool BKE_object_copy_modifier(
return true;
}
-/** Copy a single GPencil modifier.
+/**
+ * Copy a single GPencil modifier.
*
* \note **Do not** use this function to copy a whole modifier stack. Use
- * `BKE_object_modifier_stack_copy` instead. */
+ * `BKE_object_modifier_stack_copy` instead.
+ */
bool BKE_object_copy_gpencil_modifier(struct Object *ob_dst, GpencilModifierData *gmd_src)
{
BLI_assert(ob_dst->type == OB_GPENCIL);
@@ -1756,6 +1758,10 @@ void BKE_object_free_derived_caches(Object *ob)
BKE_geometry_set_free(ob->runtime.geometry_set_eval);
ob->runtime.geometry_set_eval = NULL;
}
+ if (ob->runtime.geometry_set_previews != NULL) {
+ BLI_ghash_free(ob->runtime.geometry_set_previews, NULL, (GHashValFreeFP)BKE_geometry_set_free);
+ ob->runtime.geometry_set_previews = NULL;
+ }
}
void BKE_object_free_caches(Object *object)
@@ -1806,6 +1812,24 @@ void BKE_object_free_caches(Object *object)
}
}
+/* Can be called from multiple threads. */
+void BKE_object_preview_geometry_set_add(Object *ob,
+ const uint64_t key,
+ struct GeometrySet *geometry_set)
+{
+ static ThreadMutex mutex = BLI_MUTEX_INITIALIZER;
+ BLI_mutex_lock(&mutex);
+ if (ob->runtime.geometry_set_previews == NULL) {
+ ob->runtime.geometry_set_previews = BLI_ghash_int_new(__func__);
+ }
+ BLI_ghash_reinsert(ob->runtime.geometry_set_previews,
+ POINTER_FROM_UINT(key),
+ geometry_set,
+ NULL,
+ (GHashValFreeFP)BKE_geometry_set_free);
+ BLI_mutex_unlock(&mutex);
+}
+
/**
* Actual check for internal data, not context or flags.
*/
@@ -2265,7 +2289,7 @@ Object *BKE_object_add_for_data(
void BKE_object_copy_softbody(Object *ob_dst, const Object *ob_src, const int flag)
{
SoftBody *sb = ob_src->soft;
- bool tagged_no_main = ob_dst->id.tag & LIB_TAG_NO_MAIN;
+ const bool is_orig = (flag & LIB_ID_COPY_SET_COPIED_ON_WRITE) == 0;
ob_dst->softflag = ob_src->softflag;
if (sb == NULL) {
@@ -2306,7 +2330,7 @@ void BKE_object_copy_softbody(Object *ob_dst, const Object *ob_src, const int fl
sbn->scratch = NULL;
- if (!tagged_no_main) {
+ if (is_orig) {
sbn->shared = MEM_dupallocN(sb->shared);
sbn->shared->pointcache = BKE_ptcache_copy_list(
&sbn->shared->ptcaches, &sb->shared->ptcaches, flag);
@@ -2345,7 +2369,7 @@ ParticleSystem *BKE_object_copy_particlesystem(ParticleSystem *psys, const int f
BLI_listbase_clear(&psysn->pathcachebufs);
BLI_listbase_clear(&psysn->childcachebufs);
- if (flag & LIB_ID_CREATE_NO_MAIN) {
+ if (flag & LIB_ID_COPY_SET_COPIED_ON_WRITE) {
/* XXX Disabled, fails when evaluating depsgraph after copying ID with no main for preview
* creation. */
// BLI_assert((psys->flag & PSYS_SHARED_CACHES) == 0);
@@ -2598,8 +2622,8 @@ void BKE_object_transform_copy(Object *ob_tar, const Object *ob_src)
*
* \note This function does not do any remapping to new IDs, caller must do it
* (\a #BKE_libblock_relink_to_newid()).
- * \note Caller MUST free \a newid pointers itself (#BKE_main_id_clear_newpoins()) and call updates
- * of DEG too (#DAG_relations_tag_update()).
+ * \note Caller MUST free \a newid pointers itself (#BKE_main_id_newptr_and_tag_clear()) and call
+ * updates of DEG too (#DAG_relations_tag_update()).
*/
Object *BKE_object_duplicate(Main *bmain,
Object *ob,
@@ -2609,8 +2633,7 @@ Object *BKE_object_duplicate(Main *bmain,
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);
+ BKE_main_id_newptr_and_tag_clear(bmain);
/* In case root duplicated ID is linked, assume we want to get a local copy of it and duplicate
* all expected linked data. */
if (ID_IS_LINKED(ob)) {
@@ -2749,8 +2772,7 @@ Object *BKE_object_duplicate(Main *bmain,
#endif
/* Cleanup. */
- BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false);
- BKE_main_id_clear_newpoins(bmain);
+ BKE_main_id_newptr_and_tag_clear(bmain);
}
if (obn->type == OB_ARMATURE) {
@@ -3244,7 +3266,7 @@ static bool ob_parcurve(Object *ob, Object *par, float r_mat[4][4])
if (par->runtime.curve_cache == NULL) {
return false;
}
- if (par->runtime.curve_cache->path == NULL) {
+ if (par->runtime.curve_cache->anim_path_accum_length == NULL) {
return false;
}
@@ -3260,12 +3282,16 @@ static bool ob_parcurve(Object *ob, Object *par, float r_mat[4][4])
else {
ctime = cu->ctime;
}
- CLAMP(ctime, 0.0f, 1.0f);
+
+ if (cu->flag & CU_PATH_CLAMP) {
+ CLAMP(ctime, 0.0f, 1.0f);
+ }
unit_m4(r_mat);
/* vec: 4 items! */
- if (where_on_path(par, ctime, vec, dir, (cu->flag & CU_FOLLOW) ? quat : NULL, &radius, NULL)) {
+ if (BKE_where_on_path(
+ par, ctime, vec, dir, (cu->flag & CU_FOLLOW) ? quat : NULL, &radius, NULL)) {
if (cu->flag & CU_FOLLOW) {
quat_apply_track(quat, ob->trackflag, ob->upflag);
normalize_qt(quat);
@@ -4108,7 +4134,7 @@ bool BKE_object_minmax_dupli(Depsgraph *depsgraph,
const bool use_hidden)
{
bool ok = false;
- if ((ob->transflag & OB_DUPLI) == 0) {
+ if ((ob->transflag & OB_DUPLI) == 0 && ob->runtime.geometry_set_eval == NULL) {
return ok;
}
@@ -4319,7 +4345,7 @@ void BKE_object_handle_update_ex(Depsgraph *depsgraph,
}
/* Speed optimization for animation lookups. */
if (ob->pose != NULL) {
- BKE_pose_channels_hash_make(ob->pose);
+ BKE_pose_channels_hash_ensure(ob->pose);
if (ob->pose->flag & POSE_CONSTRAINTS_NEED_UPDATE_FLAGS) {
BKE_pose_update_constraint_flags(ob->pose);
}
@@ -4353,8 +4379,6 @@ void BKE_object_handle_update_ex(Depsgraph *depsgraph,
BKE_object_handle_data_update(depsgraph, scene, ob);
}
- ob->id.recalc &= ID_RECALC_ALL;
-
object_handle_update_proxy(depsgraph, scene, ob, do_proxy_update);
}
@@ -4474,6 +4498,37 @@ Mesh *BKE_object_get_original_mesh(Object *object)
return result;
}
+Lattice *BKE_object_get_lattice(const Object *object)
+{
+ ID *data = object->data;
+ if (data == NULL || GS(data->name) != ID_LT) {
+ return NULL;
+ }
+
+ Lattice *lt = (Lattice *)data;
+ if (lt->editlatt) {
+ return lt->editlatt->latt;
+ }
+
+ return lt;
+}
+
+Lattice *BKE_object_get_evaluated_lattice(const Object *object)
+{
+ ID *data_eval = object->runtime.data_eval;
+
+ if (data_eval == NULL || GS(data_eval->name) != ID_LT) {
+ return NULL;
+ }
+
+ Lattice *lt_eval = (Lattice *)data_eval;
+ if (lt_eval->editlatt) {
+ return lt_eval->editlatt->latt;
+ }
+
+ return lt_eval;
+}
+
static int pc_cmp(const void *a, const void *b)
{
const LinkData *ad = a, *bd = b;
@@ -5061,6 +5116,20 @@ void BKE_object_runtime_reset_on_copy(Object *object, const int UNUSED(flag))
}
/**
+ * The function frees memory used by the runtime data, but not the runtime field itself.
+ *
+ * All runtime data is cleared to ensure it's not used again,
+ * in keeping with other `_free_data(..)` functions.
+ */
+void BKE_object_runtime_free_data(Object *object)
+{
+ /* Currently this is all that's needed. */
+ BKE_object_free_derived_caches(object);
+
+ BKE_object_runtime_reset(object);
+}
+
+/**
* Find an associated armature object.
*/
static Object *obrel_armature_find(Object *ob)
@@ -5601,7 +5670,7 @@ Mesh *BKE_object_to_mesh(Depsgraph *depsgraph, Object *object, bool preserve_all
{
BKE_object_to_mesh_clear(object);
- Mesh *mesh = BKE_mesh_new_from_object(depsgraph, object, preserve_all_data_layers);
+ Mesh *mesh = BKE_mesh_new_from_object(depsgraph, object, preserve_all_data_layers, false);
object->runtime.object_as_temp_mesh = mesh;
return mesh;
}
diff --git a/source/blender/blenkernel/intern/object_dupli.c b/source/blender/blenkernel/intern/object_dupli.cc
index 632e7519f05..768fa9373c1 100644
--- a/source/blender/blenkernel/intern/object_dupli.c
+++ b/source/blender/blenkernel/intern/object_dupli.cc
@@ -21,18 +21,22 @@
* \ingroup bke
*/
-#include <limits.h>
-#include <stddef.h>
-#include <stdlib.h>
+#include <climits>
+#include <cstddef>
+#include <cstdlib>
#include "MEM_guardedalloc.h"
#include "BLI_listbase.h"
#include "BLI_string_utf8.h"
-#include "BLI_alloca.h"
+#include "BLI_array.hh"
+#include "BLI_float3.hh"
+#include "BLI_float4x4.hh"
#include "BLI_math.h"
#include "BLI_rand.h"
+#include "BLI_span.hh"
+#include "BLI_vector.hh"
#include "DNA_anim_types.h"
#include "DNA_collection_types.h"
@@ -48,6 +52,7 @@
#include "BKE_editmesh_cache.h"
#include "BKE_font.h"
#include "BKE_geometry_set.h"
+#include "BKE_geometry_set.hh"
#include "BKE_global.h"
#include "BKE_idprop.h"
#include "BKE_lattice.h"
@@ -65,11 +70,17 @@
#include "BLI_hash.h"
#include "BLI_strict_flags.h"
+using blender::Array;
+using blender::float3;
+using blender::float4x4;
+using blender::Span;
+using blender::Vector;
+
/* -------------------------------------------------------------------- */
/** \name Internal Duplicate Context
* \{ */
-typedef struct DupliContext {
+struct DupliContext {
Depsgraph *depsgraph;
/** XXX child objects are selected from this group if set, could be nicer. */
Collection *collection;
@@ -81,6 +92,13 @@ typedef struct DupliContext {
Object *object;
float space_mat[4][4];
+ /**
+ * A stack that contains all the "parent" objects of a particular instance when recursive
+ * instancing is used. This is used to prevent objects from instancing themselves accidentally.
+ * Use a vector instead of a stack because we want to use the #contains method.
+ */
+ Vector<Object *> *instance_stack;
+
int persistent_id[MAX_DUPLI_RECUR];
int level;
@@ -88,12 +106,12 @@ typedef struct DupliContext {
/** Result containers. */
ListBase *duplilist; /* Legacy doubly-linked list. */
-} DupliContext;
+};
-typedef struct DupliGenerator {
+struct DupliGenerator {
short type; /* Dupli Type, see members of #OB_DUPLI. */
void (*make_duplis)(const DupliContext *ctx);
-} DupliGenerator;
+};
static const DupliGenerator *get_dupli_generator(const DupliContext *ctx);
@@ -104,15 +122,17 @@ static void init_context(DupliContext *r_ctx,
Depsgraph *depsgraph,
Scene *scene,
Object *ob,
- const float space_mat[4][4])
+ const float space_mat[4][4],
+ Vector<Object *> &instance_stack)
{
r_ctx->depsgraph = depsgraph;
r_ctx->scene = scene;
r_ctx->view_layer = DEG_get_evaluated_view_layer(depsgraph);
- r_ctx->collection = NULL;
+ r_ctx->collection = nullptr;
r_ctx->object = ob;
r_ctx->obedit = OBEDIT_FROM_OBACT(ob);
+ r_ctx->instance_stack = &instance_stack;
if (space_mat) {
copy_m4_m4(r_ctx->space_mat, space_mat);
}
@@ -123,7 +143,7 @@ static void init_context(DupliContext *r_ctx,
r_ctx->gen = get_dupli_generator(r_ctx);
- r_ctx->duplilist = NULL;
+ r_ctx->duplilist = nullptr;
}
/**
@@ -141,6 +161,7 @@ static void copy_dupli_context(
}
r_ctx->object = ob;
+ r_ctx->instance_stack = ctx->instance_stack;
if (mat) {
mul_m4_m4m4(r_ctx->space_mat, (float(*)[4])ctx->space_mat, mat);
}
@@ -165,11 +186,11 @@ static DupliObject *make_dupli(const DupliContext *ctx,
/* Add a #DupliObject instance to the result container. */
if (ctx->duplilist) {
- dob = MEM_callocN(sizeof(DupliObject), "dupli object");
+ dob = (DupliObject *)MEM_callocN(sizeof(DupliObject), "dupli object");
BLI_addtail(ctx->duplilist, dob);
}
else {
- return NULL;
+ return nullptr;
}
dob->ob = ob;
@@ -226,12 +247,19 @@ static void make_recursive_duplis(const DupliContext *ctx,
const float space_mat[4][4],
int index)
{
+ if (ctx->instance_stack->contains(ob)) {
+ /* Avoid recursive instances. */
+ printf("Warning: '%s' object is trying to instance itself.\n", ob->id.name + 2);
+ return;
+ }
/* Simple preventing of too deep nested collections with #MAX_DUPLI_RECUR. */
if (ctx->level < MAX_DUPLI_RECUR) {
DupliContext rctx;
copy_dupli_context(&rctx, ctx, ob, space_mat, index);
if (rctx.gen) {
+ ctx->instance_stack->append(ob);
rctx.gen->make_duplis(&rctx);
+ ctx->instance_stack->remove_last();
}
}
}
@@ -242,7 +270,7 @@ static void make_recursive_duplis(const DupliContext *ctx,
/** \name Internal Child Duplicates (Used by Other Functions)
* \{ */
-typedef void (*MakeChildDuplisFunc)(const DupliContext *ctx, void *userdata, Object *child);
+using MakeChildDuplisFunc = void (*)(const DupliContext *ctx, void *userdata, Object *child);
static bool is_child(const Object *ob, const Object *parent)
{
@@ -270,7 +298,7 @@ static void make_child_duplis(const DupliContext *ctx,
FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (ctx->collection, ob, mode) {
if ((ob != ctx->obedit) && is_child(ob, parent)) {
DupliContext pctx;
- copy_dupli_context(&pctx, ctx, ctx->object, NULL, _base_id);
+ copy_dupli_context(&pctx, ctx, ctx->object, nullptr, _base_id);
/* Meta-balls have a different dupli handling. */
if (ob->type != OB_MBALL) {
@@ -282,13 +310,13 @@ static void make_child_duplis(const DupliContext *ctx,
FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_END;
}
else {
- int baseid = 0;
+ int baseid;
ViewLayer *view_layer = ctx->view_layer;
- for (Base *base = view_layer->object_bases.first; base; base = base->next, baseid++) {
+ LISTBASE_FOREACH_INDEX (Base *, base, &view_layer->object_bases, baseid) {
Object *ob = base->object;
if ((ob != ctx->obedit) && is_child(ob, parent)) {
DupliContext pctx;
- copy_dupli_context(&pctx, ctx, ctx->object, NULL, baseid);
+ copy_dupli_context(&pctx, ctx, ctx->object, nullptr, baseid);
/* Meta-balls have a different dupli-handling. */
if (ob->type != OB_MBALL) {
@@ -316,30 +344,30 @@ static Mesh *mesh_data_from_duplicator_object(Object *ob,
BMEditMesh *em = BKE_editmesh_from_object(ob);
Mesh *me_eval;
- *r_em = NULL;
- *r_vert_coords = NULL;
- if (r_vert_normals != NULL) {
- *r_vert_normals = NULL;
+ *r_em = nullptr;
+ *r_vert_coords = nullptr;
+ if (r_vert_normals != nullptr) {
+ *r_vert_normals = nullptr;
}
/* We do not need any render-specific handling anymore, depsgraph takes care of that. */
/* NOTE: Do direct access to the evaluated mesh: this function is used
* during meta balls evaluation. But even without those all the objects
* which are needed for correct instancing are already evaluated. */
- if (em != NULL) {
+ if (em != nullptr) {
/* Note that this will only show deformation if #eModifierMode_OnCage is enabled.
* We could change this but it matches 2.7x behavior. */
me_eval = em->mesh_eval_cage;
- if ((me_eval == NULL) || (me_eval->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH)) {
- EditMeshData *emd = me_eval ? me_eval->runtime.edit_data : NULL;
+ if ((me_eval == nullptr) || (me_eval->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH)) {
+ EditMeshData *emd = me_eval ? me_eval->runtime.edit_data : nullptr;
/* Only assign edit-mesh in the case we can't use `me_eval`. */
*r_em = em;
- me_eval = NULL;
+ me_eval = nullptr;
- if ((emd != NULL) && (emd->vertexCos != NULL)) {
+ if ((emd != nullptr) && (emd->vertexCos != nullptr)) {
*r_vert_coords = emd->vertexCos;
- if (r_vert_normals != NULL) {
+ if (r_vert_normals != nullptr) {
BKE_editmesh_cache_ensure_vert_normals(em, emd);
*r_vert_normals = emd->vertexNos;
}
@@ -364,7 +392,7 @@ static void make_duplis_collection(const DupliContext *ctx)
Collection *collection;
float collection_mat[4][4];
- if (ob->instance_collection == NULL) {
+ if (ob->instance_collection == nullptr) {
return;
}
collection = ob->instance_collection;
@@ -404,7 +432,7 @@ static const DupliGenerator gen_dupli_collection = {
* \{ */
/** Values shared between different mesh types. */
-typedef struct VertexDupliData_Params {
+struct VertexDupliData_Params {
/**
* It's important we use this context instead of the `ctx` passed into #make_child_duplis
* since these won't match in the case of recursion.
@@ -412,23 +440,23 @@ typedef struct VertexDupliData_Params {
const DupliContext *ctx;
bool use_rotation;
-} VertexDupliData_Params;
+};
-typedef struct VertexDupliData_Mesh {
+struct VertexDupliData_Mesh {
VertexDupliData_Params params;
int totvert;
const MVert *mvert;
const float (*orco)[3];
-} VertexDupliData_Mesh;
+};
-typedef struct VertexDupliData_EditMesh {
+struct VertexDupliData_EditMesh {
VertexDupliData_Params params;
BMEditMesh *em;
- /* Can be NULL. */
+ /* Can be nullptr. */
const float (*vert_coords)[3];
const float (*vert_normals)[3];
@@ -440,7 +468,7 @@ typedef struct VertexDupliData_EditMesh {
* edit-mesh to be converted into a mesh.
*/
bool has_orco;
-} VertexDupliData_EditMesh;
+};
/**
* \param no: The direction,
@@ -505,7 +533,7 @@ static void make_child_duplis_verts_from_mesh(const DupliContext *ctx,
void *userdata,
Object *inst_ob)
{
- VertexDupliData_Mesh *vdd = userdata;
+ VertexDupliData_Mesh *vdd = (VertexDupliData_Mesh *)userdata;
const bool use_rotation = vdd->params.use_rotation;
const MVert *mvert = vdd->mvert;
@@ -519,7 +547,8 @@ static void make_child_duplis_verts_from_mesh(const DupliContext *ctx,
const MVert *mv = mvert;
for (int i = 0; i < totvert; i++, mv++) {
const float *co = mv->co;
- const float no[3] = {UNPACK3(mv->no)};
+ float no[3];
+ normal_short_to_float_v3(no, mv->no);
DupliObject *dob = vertex_dupli(vdd->params.ctx, inst_ob, child_imat, i, co, no, use_rotation);
if (vdd->orco) {
copy_v3_v3(dob->orco, vdd->orco[i]);
@@ -531,7 +560,7 @@ static void make_child_duplis_verts_from_editmesh(const DupliContext *ctx,
void *userdata,
Object *inst_ob)
{
- VertexDupliData_EditMesh *vdd = userdata;
+ VertexDupliData_EditMesh *vdd = (VertexDupliData_EditMesh *)userdata;
BMEditMesh *em = vdd->em;
const bool use_rotation = vdd->params.use_rotation;
@@ -549,9 +578,9 @@ static void make_child_duplis_verts_from_editmesh(const DupliContext *ctx,
BM_ITER_MESH_INDEX (v, &iter, em->bm, BM_VERTS_OF_MESH, i) {
const float *co, *no;
- if (vert_coords != NULL) {
+ if (vert_coords != nullptr) {
co = vert_coords[i];
- no = vert_normals ? vert_normals[i] : NULL;
+ no = vert_normals ? vert_normals[i] : nullptr;
}
else {
co = v->co;
@@ -571,37 +600,34 @@ static void make_duplis_verts(const DupliContext *ctx)
const bool use_rotation = parent->transflag & OB_DUPLIROT;
/* Gather mesh info. */
- BMEditMesh *em = NULL;
- const float(*vert_coords)[3] = NULL;
- const float(*vert_normals)[3] = NULL;
+ BMEditMesh *em = nullptr;
+ const float(*vert_coords)[3] = nullptr;
+ const float(*vert_normals)[3] = nullptr;
Mesh *me_eval = mesh_data_from_duplicator_object(
- parent, &em, &vert_coords, use_rotation ? &vert_normals : NULL);
- if (em == NULL && me_eval == NULL) {
+ parent, &em, &vert_coords, use_rotation ? &vert_normals : nullptr);
+ if (em == nullptr && me_eval == nullptr) {
return;
}
- VertexDupliData_Params vdd_params = {
- .ctx = ctx,
- .use_rotation = use_rotation,
- };
+ VertexDupliData_Params vdd_params{ctx, use_rotation};
+
+ if (em != nullptr) {
+ VertexDupliData_EditMesh vdd{};
+ vdd.params = vdd_params;
+ vdd.em = em;
+ vdd.vert_coords = vert_coords;
+ vdd.vert_normals = vert_normals;
+ vdd.has_orco = (vert_coords != nullptr);
- if (em != NULL) {
- VertexDupliData_EditMesh vdd = {
- .params = vdd_params,
- .em = em,
- .vert_coords = vert_coords,
- .vert_normals = vert_normals,
- .has_orco = (vert_coords != NULL),
- };
make_child_duplis(ctx, &vdd, make_child_duplis_verts_from_editmesh);
}
else {
- VertexDupliData_Mesh vdd = {
- .params = vdd_params,
- .totvert = me_eval->totvert,
- .mvert = me_eval->mvert,
- .orco = CustomData_get_layer(&me_eval->vdata, CD_ORCO),
- };
+ VertexDupliData_Mesh vdd{};
+ vdd.params = vdd_params;
+ vdd.totvert = me_eval->totvert;
+ vdd.mvert = me_eval->mvert;
+ vdd.orco = (const float(*)[3])CustomData_get_layer(&me_eval->vdata, CD_ORCO);
+
make_child_duplis(ctx, &vdd, make_child_duplis_verts_from_mesh);
}
}
@@ -620,34 +646,31 @@ static const DupliGenerator gen_dupli_verts = {
static Object *find_family_object(
Main *bmain, const char *family, size_t family_len, unsigned int ch, GHash *family_gh)
{
- Object **ob_pt;
- Object *ob;
void *ch_key = POINTER_FROM_UINT(ch);
+ Object **ob_pt;
if ((ob_pt = (Object **)BLI_ghash_lookup_p(family_gh, ch_key))) {
- ob = *ob_pt;
+ return *ob_pt;
}
- else {
- char ch_utf8[7];
- size_t ch_utf8_len;
- ch_utf8_len = BLI_str_utf8_from_unicode(ch, ch_utf8);
- ch_utf8[ch_utf8_len] = '\0';
- ch_utf8_len += 1; /* Compare with null terminator. */
+ char ch_utf8[7];
+ size_t ch_utf8_len;
- for (ob = bmain->objects.first; ob; ob = ob->id.next) {
- if (STREQLEN(ob->id.name + 2 + family_len, ch_utf8, ch_utf8_len)) {
- if (STREQLEN(ob->id.name + 2, family, family_len)) {
- break;
- }
+ ch_utf8_len = BLI_str_utf8_from_unicode(ch, ch_utf8);
+ ch_utf8[ch_utf8_len] = '\0';
+ ch_utf8_len += 1; /* Compare with null terminator. */
+
+ LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
+ if (STREQLEN(ob->id.name + 2 + family_len, ch_utf8, ch_utf8_len)) {
+ if (STREQLEN(ob->id.name + 2, family, family_len)) {
+ /* Inserted value can be nullptr, just to save searches in future. */
+ BLI_ghash_insert(family_gh, ch_key, ob);
+ return ob;
}
}
-
- /* Inserted value can be NULL, just to save searches in future. */
- BLI_ghash_insert(family_gh, ch_key, ob);
}
- return ob;
+ return nullptr;
}
static void make_duplis_font(const DupliContext *ctx)
@@ -656,11 +679,11 @@ static void make_duplis_font(const DupliContext *ctx)
GHash *family_gh;
Object *ob;
Curve *cu;
- struct CharTrans *ct, *chartransdata = NULL;
+ struct CharTrans *ct, *chartransdata = nullptr;
float vec[3], obmat[4][4], pmat[4][4], fsize, xof, yof;
int text_len, a;
size_t family_len;
- const char32_t *text = NULL;
+ const char32_t *text = nullptr;
bool text_free = false;
/* Font dupli-verts not supported inside collections. */
@@ -673,13 +696,13 @@ static void make_duplis_font(const DupliContext *ctx)
/* In `par` the family name is stored, use this to find the other objects. */
BKE_vfont_to_curve_ex(
- par, par->data, FO_DUPLI, NULL, &text, &text_len, &text_free, &chartransdata);
+ par, (Curve *)par->data, FO_DUPLI, nullptr, &text, &text_len, &text_free, &chartransdata);
- if (text == NULL || chartransdata == NULL) {
+ if (text == nullptr || chartransdata == nullptr) {
return;
}
- cu = par->data;
+ cu = (Curve *)par->data;
fsize = cu->fsize;
xof = cu->xof;
yof = cu->yof;
@@ -733,7 +756,7 @@ static void make_duplis_font(const DupliContext *ctx)
MEM_freeN((void *)text);
}
- BLI_ghash_free(family_gh, NULL, NULL);
+ BLI_ghash_free(family_gh, nullptr, nullptr);
MEM_freeN(chartransdata);
}
@@ -754,11 +777,11 @@ static void make_child_duplis_pointcloud(const DupliContext *ctx,
Object *child)
{
const Object *parent = ctx->object;
- const PointCloud *pointcloud = parent->data;
+ const PointCloud *pointcloud = (PointCloud *)parent->data;
const float(*co)[3] = pointcloud->co;
const float *radius = pointcloud->radius;
- const float(*rotation)[4] = NULL; /* TODO: add optional rotation attribute. */
- const float(*orco)[3] = NULL; /* TODO: add optional texture coordinate attribute. */
+ const float(*rotation)[4] = nullptr; /* TODO: add optional rotation attribute. */
+ const float(*orco)[3] = nullptr; /* TODO: add optional texture coordinate attribute. */
/* Relative transform from parent to child space. */
float child_imat[4][4];
@@ -797,7 +820,7 @@ static void make_child_duplis_pointcloud(const DupliContext *ctx,
static void make_duplis_pointcloud(const DupliContext *ctx)
{
- make_child_duplis(ctx, NULL, make_child_duplis_pointcloud);
+ make_child_duplis(ctx, nullptr, make_child_duplis_pointcloud);
}
static const DupliGenerator gen_dupli_verts_pointcloud = {
@@ -813,43 +836,44 @@ static const DupliGenerator gen_dupli_verts_pointcloud = {
static void make_duplis_instances_component(const DupliContext *ctx)
{
- float(*instance_offset_matrices)[4][4];
- InstancedData *instanced_data;
- const int *almost_unique_ids;
- const int amount = BKE_geometry_set_instances(ctx->object->runtime.geometry_set_eval,
- &instance_offset_matrices,
- &almost_unique_ids,
- &instanced_data);
+ const InstancesComponent *component =
+ ctx->object->runtime.geometry_set_eval->get_component_for_read<InstancesComponent>();
+ if (component == nullptr) {
+ return;
+ }
- for (int i = 0; i < amount; i++) {
- InstancedData *data = &instanced_data[i];
+ Span<float4x4> instance_offset_matrices = component->instance_transforms();
+ Span<int> instance_reference_handles = component->instance_reference_handles();
+ Span<int> almost_unique_ids = component->almost_unique_ids();
+ Span<InstanceReference> references = component->references();
+ for (int64_t i : instance_offset_matrices.index_range()) {
+ const InstanceReference &reference = references[instance_reference_handles[i]];
const int id = almost_unique_ids[i];
- if (data->type == INSTANCE_DATA_TYPE_OBJECT) {
- Object *object = data->data.object;
- if (object != NULL) {
+ switch (reference.type()) {
+ case InstanceReference::Type::Object: {
+ Object &object = reference.object();
float matrix[4][4];
- mul_m4_m4m4(matrix, ctx->object->obmat, instance_offset_matrices[i]);
- make_dupli(ctx, object, matrix, id);
+ mul_m4_m4m4(matrix, ctx->object->obmat, instance_offset_matrices[i].values);
+ make_dupli(ctx, &object, matrix, id);
float space_matrix[4][4];
- mul_m4_m4m4(space_matrix, instance_offset_matrices[i], object->imat);
+ mul_m4_m4m4(space_matrix, instance_offset_matrices[i].values, object.imat);
mul_m4_m4_pre(space_matrix, ctx->object->obmat);
- make_recursive_duplis(ctx, object, space_matrix, id);
+ make_recursive_duplis(ctx, &object, space_matrix, id);
+ break;
}
- }
- else if (data->type == INSTANCE_DATA_TYPE_COLLECTION) {
- Collection *collection = data->data.collection;
- if (collection != NULL) {
+ case InstanceReference::Type::Collection: {
+ Collection &collection = reference.collection();
float collection_matrix[4][4];
unit_m4(collection_matrix);
- sub_v3_v3(collection_matrix[3], collection->instance_offset);
- mul_m4_m4_pre(collection_matrix, instance_offset_matrices[i]);
+ sub_v3_v3(collection_matrix[3], collection.instance_offset);
+ mul_m4_m4_pre(collection_matrix, instance_offset_matrices[i].values);
mul_m4_m4_pre(collection_matrix, ctx->object->obmat);
eEvaluationMode mode = DEG_get_mode(ctx->depsgraph);
- FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (collection, object, mode) {
+ FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (&collection, object, mode) {
if (object == ctx->object) {
continue;
}
@@ -861,6 +885,10 @@ static void make_duplis_instances_component(const DupliContext *ctx)
make_recursive_duplis(ctx, object, collection_matrix, id);
}
FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_END;
+ break;
+ }
+ case InstanceReference::Type::None: {
+ break;
}
}
}
@@ -878,7 +906,7 @@ static const DupliGenerator gen_dupli_instances_component = {
* \{ */
/** Values shared between different mesh types. */
-typedef struct FaceDupliData_Params {
+struct FaceDupliData_Params {
/**
* It's important we use this context instead of the `ctx` passed into #make_child_duplis
* since these won't match in the case of recursion.
@@ -886,9 +914,9 @@ typedef struct FaceDupliData_Params {
const DupliContext *ctx;
bool use_scale;
-} FaceDupliData_Params;
+};
-typedef struct FaceDupliData_Mesh {
+struct FaceDupliData_Mesh {
FaceDupliData_Params params;
int totface;
@@ -897,53 +925,50 @@ typedef struct FaceDupliData_Mesh {
const MVert *mvert;
const float (*orco)[3];
const MLoopUV *mloopuv;
-} FaceDupliData_Mesh;
+};
-typedef struct FaceDupliData_EditMesh {
+struct FaceDupliData_EditMesh {
FaceDupliData_Params params;
BMEditMesh *em;
bool has_orco, has_uvs;
int cd_loop_uv_offset;
- /* Can be NULL. */
+ /* Can be nullptr. */
const float (*vert_coords)[3];
-} FaceDupliData_EditMesh;
+};
-static void get_dupliface_transform_from_coords(const float coords[][3],
- const int coords_len,
+static void get_dupliface_transform_from_coords(Span<float3> coords,
const bool use_scale,
const float scale_fac,
float r_mat[4][4])
{
- float loc[3], quat[4], scale, size[3];
-
/* Location. */
- {
- const float w = 1.0f / (float)coords_len;
- zero_v3(loc);
- for (int i = 0; i < coords_len; i++) {
- madd_v3_v3fl(loc, coords[i], w);
- }
+ float3 location(0);
+ for (const float3 &coord : coords) {
+ location += coord;
}
+ location *= 1.0f / (float)coords.size();
+
/* Rotation. */
- {
- float f_no[3];
- cross_poly_v3(f_no, coords, (uint)coords_len);
- normalize_v3(f_no);
- tri_to_quat_ex(quat, coords[0], coords[1], coords[2], f_no);
- }
+ float quat[4];
+
+ float3 f_no;
+ cross_poly_v3(f_no, (const float(*)[3])coords.data(), (uint)coords.size());
+ f_no.normalize();
+ tri_to_quat_ex(quat, coords[0], coords[1], coords[2], f_no);
+
/* Scale. */
+ float scale;
if (use_scale) {
- const float area = area_poly_v3(coords, (uint)coords_len);
+ const float area = area_poly_v3((const float(*)[3])coords.data(), (uint)coords.size());
scale = sqrtf(area) * scale_fac;
}
else {
scale = 1.0f;
}
- size[0] = size[1] = size[2] = scale;
- loc_quat_size_to_mat4(r_mat, loc, quat, size);
+ loc_quat_size_to_mat4(r_mat, location, quat, float3(scale));
}
static DupliObject *face_dupli(const DupliContext *ctx,
@@ -952,14 +977,13 @@ static DupliObject *face_dupli(const DupliContext *ctx,
const int index,
const bool use_scale,
const float scale_fac,
- const float (*coords)[3],
- const int coords_len)
+ Span<float3> coords)
{
float obmat[4][4];
float space_mat[4][4];
/* `obmat` is transform to face. */
- get_dupliface_transform_from_coords(coords, coords_len, use_scale, scale_fac, obmat);
+ get_dupliface_transform_from_coords(coords, use_scale, scale_fac, obmat);
/* Make offset relative to inst_ob using relative child transform. */
mul_mat3_m4_v3(child_imat, obmat[3]);
@@ -987,7 +1011,6 @@ static DupliObject *face_dupli(const DupliContext *ctx,
return dob;
}
-/** Wrap #face_dupli, needed since we can't #alloca in a loop. */
static DupliObject *face_dupli_from_mesh(const DupliContext *ctx,
Object *inst_ob,
const float child_imat[4][4],
@@ -1001,17 +1024,16 @@ static DupliObject *face_dupli_from_mesh(const DupliContext *ctx,
const MVert *mvert)
{
const int coords_len = mpoly->totloop;
- float(*coords)[3] = BLI_array_alloca(coords, (size_t)coords_len);
+ Array<float3, 64> coords(coords_len);
const MLoop *ml = mloopstart;
for (int i = 0; i < coords_len; i++, ml++) {
- copy_v3_v3(coords[i], mvert[ml->v].co);
+ coords[i] = float3(mvert[ml->v].co);
}
- return face_dupli(ctx, inst_ob, child_imat, index, use_scale, scale_fac, coords, coords_len);
+ return face_dupli(ctx, inst_ob, child_imat, index, use_scale, scale_fac, coords);
}
-/** Wrap #face_dupli, needed since we can't #alloca in a loop. */
static DupliObject *face_dupli_from_editmesh(const DupliContext *ctx,
Object *inst_ob,
const float child_imat[4][4],
@@ -1024,12 +1046,12 @@ static DupliObject *face_dupli_from_editmesh(const DupliContext *ctx,
const float (*vert_coords)[3])
{
const int coords_len = f->len;
- float(*coords)[3] = BLI_array_alloca(coords, (size_t)coords_len);
+ Array<float3, 64> coords(coords_len);
BMLoop *l_first, *l_iter;
int i = 0;
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
- if (vert_coords != NULL) {
+ if (vert_coords != nullptr) {
do {
copy_v3_v3(coords[i++], vert_coords[BM_elem_index_get(l_iter->v)]);
} while ((l_iter = l_iter->next) != l_first);
@@ -1040,14 +1062,14 @@ static DupliObject *face_dupli_from_editmesh(const DupliContext *ctx,
} while ((l_iter = l_iter->next) != l_first);
}
- return face_dupli(ctx, inst_ob, child_imat, index, use_scale, scale_fac, coords, coords_len);
+ return face_dupli(ctx, inst_ob, child_imat, index, use_scale, scale_fac, coords);
}
static void make_child_duplis_faces_from_mesh(const DupliContext *ctx,
void *userdata,
Object *inst_ob)
{
- FaceDupliData_Mesh *fdd = userdata;
+ FaceDupliData_Mesh *fdd = (FaceDupliData_Mesh *)userdata;
const MPoly *mpoly = fdd->mpoly, *mp;
const MLoop *mloop = fdd->mloop;
const MVert *mvert = fdd->mvert;
@@ -1087,7 +1109,7 @@ static void make_child_duplis_faces_from_editmesh(const DupliContext *ctx,
void *userdata,
Object *inst_ob)
{
- FaceDupliData_EditMesh *fdd = userdata;
+ FaceDupliData_EditMesh *fdd = (FaceDupliData_EditMesh *)userdata;
BMEditMesh *em = fdd->em;
float child_imat[4][4];
int a;
@@ -1097,7 +1119,7 @@ static void make_child_duplis_faces_from_editmesh(const DupliContext *ctx,
const float(*vert_coords)[3] = fdd->vert_coords;
- BLI_assert((vert_coords == NULL) || (em->bm->elem_index_dirty & BM_VERT) == 0);
+ BLI_assert((vert_coords == nullptr) || (em->bm->elem_index_dirty & BM_VERT) == 0);
invert_m4_m4(inst_ob->imat, inst_ob->obmat);
/* Relative transform from parent to child space. */
@@ -1127,44 +1149,41 @@ static void make_duplis_faces(const DupliContext *ctx)
Object *parent = ctx->object;
/* Gather mesh info. */
- BMEditMesh *em = NULL;
- const float(*vert_coords)[3] = NULL;
- Mesh *me_eval = mesh_data_from_duplicator_object(parent, &em, &vert_coords, NULL);
- if (em == NULL && me_eval == NULL) {
+ BMEditMesh *em = nullptr;
+ const float(*vert_coords)[3] = nullptr;
+ Mesh *me_eval = mesh_data_from_duplicator_object(parent, &em, &vert_coords, nullptr);
+ if (em == nullptr && me_eval == nullptr) {
return;
}
- FaceDupliData_Params fdd_params = {
- .ctx = ctx,
- .use_scale = parent->transflag & OB_DUPLIFACES_SCALE,
- };
+ FaceDupliData_Params fdd_params = {ctx, (parent->transflag & OB_DUPLIFACES_SCALE) != 0};
- if (em != NULL) {
+ if (em != nullptr) {
const int uv_idx = CustomData_get_render_layer(&em->bm->ldata, CD_MLOOPUV);
- FaceDupliData_EditMesh fdd = {
- .params = fdd_params,
- .em = em,
- .vert_coords = vert_coords,
- .has_orco = (vert_coords != NULL),
- .has_uvs = (uv_idx != -1),
- .cd_loop_uv_offset = (uv_idx != -1) ?
- CustomData_get_n_offset(&em->bm->ldata, CD_MLOOPUV, uv_idx) :
- -1,
- };
+ FaceDupliData_EditMesh fdd{};
+ fdd.params = fdd_params;
+ fdd.em = em;
+ fdd.vert_coords = vert_coords;
+ fdd.has_orco = (vert_coords != nullptr);
+ fdd.has_uvs = (uv_idx != -1);
+ fdd.cd_loop_uv_offset = (uv_idx != -1) ?
+ CustomData_get_n_offset(&em->bm->ldata, CD_MLOOPUV, uv_idx) :
+ -1;
make_child_duplis(ctx, &fdd, make_child_duplis_faces_from_editmesh);
}
else {
const int uv_idx = CustomData_get_render_layer(&me_eval->ldata, CD_MLOOPUV);
- FaceDupliData_Mesh fdd = {
- .params = fdd_params,
- .totface = me_eval->totpoly,
- .mpoly = me_eval->mpoly,
- .mloop = me_eval->mloop,
- .mvert = me_eval->mvert,
- .mloopuv = (uv_idx != -1) ? CustomData_get_layer_n(&me_eval->ldata, CD_MLOOPUV, uv_idx) :
- NULL,
- .orco = CustomData_get_layer(&me_eval->vdata, CD_ORCO),
- };
+ FaceDupliData_Mesh fdd{};
+ fdd.params = fdd_params;
+ fdd.totface = me_eval->totpoly;
+ fdd.mpoly = me_eval->mpoly;
+ fdd.mloop = me_eval->mloop;
+ fdd.mvert = me_eval->mvert;
+ fdd.mloopuv = (uv_idx != -1) ? (const MLoopUV *)CustomData_get_layer_n(
+ &me_eval->ldata, CD_MLOOPUV, uv_idx) :
+ nullptr;
+ fdd.orco = (const float(*)[3])CustomData_get_layer(&me_eval->vdata, CD_ORCO);
+
make_child_duplis(ctx, &fdd, make_child_duplis_faces_from_mesh);
}
}
@@ -1187,12 +1206,11 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem
eEvaluationMode mode = DEG_get_mode(ctx->depsgraph);
bool for_render = mode == DAG_EVAL_RENDER;
- Object *ob = NULL, **oblist = NULL;
+ Object *ob = nullptr, **oblist = nullptr;
DupliObject *dob;
- ParticleDupliWeight *dw;
ParticleSettings *part;
ParticleData *pa;
- ChildParticle *cpa = NULL;
+ ChildParticle *cpa = nullptr;
ParticleKey state;
ParticleCacheKey *cache;
float ctime, scale = 1.0f;
@@ -1202,13 +1220,13 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem
int no_draw_flag = PARS_UNEXIST;
- if (psys == NULL) {
+ if (psys == nullptr) {
return;
}
part = psys->part;
- if (part == NULL) {
+ if (part == nullptr) {
return;
}
@@ -1228,7 +1246,7 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem
if ((for_render || part->draw_as == PART_DRAW_REND) &&
ELEM(part->ren_as, PART_DRAW_OB, PART_DRAW_GR)) {
- ParticleSimulationData sim = {NULL};
+ ParticleSimulationData sim = {nullptr};
sim.depsgraph = ctx->depsgraph;
sim.scene = scene;
sim.ob = par;
@@ -1239,12 +1257,12 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem
/* First check for loops (particle system object used as dupli-object). */
if (part->ren_as == PART_DRAW_OB) {
- if (ELEM(part->instance_object, NULL, par)) {
+ if (ELEM(part->instance_object, nullptr, par)) {
return;
}
}
else { /* #PART_DRAW_GR. */
- if (part->instance_collection == NULL) {
+ if (part->instance_collection == nullptr) {
return;
}
@@ -1285,8 +1303,7 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem
if (part->ren_as == PART_DRAW_GR) {
if (use_collection_count) {
psys_find_group_weights(part);
-
- for (dw = part->instance_weights.first; dw; dw = dw->next) {
+ LISTBASE_FOREACH (ParticleDupliWeight *, dw, &part->instance_weights) {
FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (
part->instance_collection, object, mode) {
if (dw->ob == object) {
@@ -1306,11 +1323,12 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem
FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_END;
}
- oblist = MEM_callocN((size_t)totcollection * sizeof(Object *), "dupcollection object list");
+ oblist = (Object **)MEM_callocN((size_t)totcollection * sizeof(Object *),
+ "dupcollection object list");
if (use_collection_count) {
a = 0;
- for (dw = part->instance_weights.first; dw; dw = dw->next) {
+ LISTBASE_FOREACH (ParticleDupliWeight *, dw, &part->instance_weights) {
FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (
part->instance_collection, object, mode) {
if (dw->ob == object) {
@@ -1363,7 +1381,7 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem
#if 0 /* UNUSED */
pa_num = a;
#endif
- size = psys_get_child_size(psys, cpa, ctime, NULL);
+ size = psys_get_child_size(psys, cpa, ctime, nullptr);
}
/* Some hair paths might be non-existent so they can't be used for duplication. */
@@ -1394,11 +1412,11 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem
/* Hair we handle separate and compute transform based on hair keys. */
if (a < totpart) {
cache = psys->pathcache[a];
- psys_get_dupli_path_transform(&sim, pa, NULL, cache, pamat, &scale);
+ psys_get_dupli_path_transform(&sim, pa, nullptr, cache, pamat, &scale);
}
else {
cache = psys->childcache[a - totpart];
- psys_get_dupli_path_transform(&sim, NULL, cpa, cache, pamat, &scale);
+ psys_get_dupli_path_transform(&sim, nullptr, cpa, cache, pamat, &scale);
}
copy_v3_v3(pamat[3], cache->co);
@@ -1506,20 +1524,18 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem
if (psys->lattice_deform_data) {
BKE_lattice_deform_data_destroy(psys->lattice_deform_data);
- psys->lattice_deform_data = NULL;
+ psys->lattice_deform_data = nullptr;
}
}
static void make_duplis_particles(const DupliContext *ctx)
{
- ParticleSystem *psys;
- int psysid;
-
/* Particle system take up one level in id, the particles another. */
- for (psys = ctx->object->particlesystem.first, psysid = 0; psys; psys = psys->next, psysid++) {
+ int psysid;
+ LISTBASE_FOREACH_INDEX (ParticleSystem *, psys, &ctx->object->particlesystem, psysid) {
/* Particles create one more level for persistent `psys` index. */
DupliContext pctx;
- copy_dupli_context(&pctx, ctx, ctx->object, NULL, psysid);
+ copy_dupli_context(&pctx, ctx, ctx->object, nullptr, psysid);
make_duplis_particle_system(&pctx, psys);
}
}
@@ -1540,17 +1556,17 @@ static const DupliGenerator *get_dupli_generator(const DupliContext *ctx)
int transflag = ctx->object->transflag;
int restrictflag = ctx->object->restrictflag;
- if ((transflag & OB_DUPLI) == 0 && ctx->object->runtime.geometry_set_eval == NULL) {
- return NULL;
+ if ((transflag & OB_DUPLI) == 0 && ctx->object->runtime.geometry_set_eval == nullptr) {
+ return nullptr;
}
/* Should the dupli's be generated for this object? - Respect restrict flags. */
if (DEG_get_mode(ctx->depsgraph) == DAG_EVAL_RENDER ? (restrictflag & OB_RESTRICT_RENDER) :
(restrictflag & OB_RESTRICT_VIEWPORT)) {
- return NULL;
+ return nullptr;
}
- if (ctx->object->runtime.geometry_set_eval != NULL) {
+ if (ctx->object->runtime.geometry_set_eval != nullptr) {
if (BKE_geometry_set_has_instances(ctx->object->runtime.geometry_set_eval)) {
return &gen_dupli_instances_component;
}
@@ -1579,7 +1595,7 @@ static const DupliGenerator *get_dupli_generator(const DupliContext *ctx)
return &gen_dupli_collection;
}
- return NULL;
+ return nullptr;
}
/** \} */
@@ -1593,9 +1609,11 @@ static const DupliGenerator *get_dupli_generator(const DupliContext *ctx)
*/
ListBase *object_duplilist(Depsgraph *depsgraph, Scene *sce, Object *ob)
{
- ListBase *duplilist = MEM_callocN(sizeof(ListBase), "duplilist");
+ ListBase *duplilist = (ListBase *)MEM_callocN(sizeof(ListBase), "duplilist");
DupliContext ctx;
- init_context(&ctx, depsgraph, sce, ob, NULL);
+ Vector<Object *> instance_stack;
+ instance_stack.append(ob);
+ init_context(&ctx, depsgraph, sce, ob, nullptr, instance_stack);
if (ctx.gen) {
ctx.duplilist = duplilist;
ctx.gen->make_duplis(&ctx);
diff --git a/source/blender/blenkernel/intern/object_update.c b/source/blender/blenkernel/intern/object_update.c
index b1ae4abd9bb..e6909127503 100644
--- a/source/blender/blenkernel/intern/object_update.c
+++ b/source/blender/blenkernel/intern/object_update.c
@@ -176,6 +176,13 @@ void BKE_object_handle_data_update(Depsgraph *depsgraph, Scene *scene, Object *o
CustomData_MeshMasks cddata_masks = scene->customdata_mask;
CustomData_MeshMasks_update(&cddata_masks, &CD_MASK_BAREMESH);
+ /* Custom attributes should not be removed automatically. They might be used by the render
+ * engine or scripts. They can still be removed explicitly using geometry nodes. */
+ cddata_masks.vmask |= CD_MASK_PROP_ALL;
+ cddata_masks.emask |= CD_MASK_PROP_ALL;
+ cddata_masks.fmask |= CD_MASK_PROP_ALL;
+ cddata_masks.pmask |= CD_MASK_PROP_ALL;
+ cddata_masks.lmask |= CD_MASK_PROP_ALL;
/* Make sure Freestyle edge/face marks appear in DM for render (see T40315).
* Due to Line Art implementation, edge marks should also be shown in viewport. */
#ifdef WITH_FREESTYLE
diff --git a/source/blender/blenkernel/intern/ocean.c b/source/blender/blenkernel/intern/ocean.c
index d2f4d0702ed..9b9ed0adcf4 100644
--- a/source/blender/blenkernel/intern/ocean.c
+++ b/source/blender/blenkernel/intern/ocean.c
@@ -663,7 +663,7 @@ void BKE_ocean_simulate(struct Ocean *o, float t, float scale, float chop_amount
osd.scale = scale;
osd.chop_amount = chop_amount;
- pool = BLI_task_pool_create(&osd, TASK_PRIORITY_HIGH);
+ pool = BLI_task_pool_create(&osd, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON);
BLI_rw_mutex_lock(&o->oceanmutex, THREAD_LOCK_WRITE);
@@ -911,8 +911,12 @@ void BKE_ocean_init(struct Ocean *o,
for (i = 0; i < o->_M; i++) {
for (j = 0; j < o->_N; j++) {
/* This ensures we get a value tied to the surface location, avoiding dramatic surface
- * change with changing resolution. */
- int new_seed = seed + BLI_hash_int_2d(o->_kx[i] * 360.0f, o->_kz[j] * 360.0f);
+ * change with changing resolution.
+ * Explicitly cast to signed int first to ensure consistent behavior on all processors,
+ * since behavior of float to unsigned int cast is undefined in C. */
+ const int hash_x = o->_kx[i] * 360.0f;
+ const int hash_z = o->_kz[j] * 360.0f;
+ int new_seed = seed + BLI_hash_int_2d(hash_x, hash_z);
BLI_rng_seed(rng, new_seed);
float r1 = gaussRand(rng);
diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c
index e50b321900a..3ae5d039125 100644
--- a/source/blender/blenkernel/intern/particle.c
+++ b/source/blender/blenkernel/intern/particle.c
@@ -101,6 +101,8 @@ static void particle_settings_init(ID *id)
MEMCPY_STRUCT_AFTER(particle_settings, DNA_struct_default_get(ParticleSettings), id);
particle_settings->effector_weights = BKE_effector_add_weights(NULL);
+ particle_settings->pd = BKE_partdeflect_new(PFIELD_NULL);
+ particle_settings->pd2 = BKE_partdeflect_new(PFIELD_NULL);
}
static void particle_settings_copy_data(Main *UNUSED(bmain),
@@ -2417,14 +2419,15 @@ int do_guides(Depsgraph *depsgraph,
cu = (Curve *)eff->ob->data;
if (pd->flag & PFIELD_GUIDE_PATH_ADD) {
- if (where_on_path(
+ if (BKE_where_on_path(
eff->ob, data->strength * guidetime, guidevec, guidedir, NULL, &radius, &weight) ==
0) {
return 0;
}
}
else {
- if (where_on_path(eff->ob, guidetime, guidevec, guidedir, NULL, &radius, &weight) == 0) {
+ if (BKE_where_on_path(eff->ob, guidetime, guidevec, guidedir, NULL, &radius, &weight) ==
+ 0) {
return 0;
}
}
@@ -3176,7 +3179,7 @@ void psys_cache_child_paths(ParticleSimulationData *sim,
return;
}
- task_pool = BLI_task_pool_create(&ctx, TASK_PRIORITY_LOW);
+ task_pool = BLI_task_pool_create(&ctx, TASK_PRIORITY_LOW, TASK_ISOLATION_ON);
totchild = ctx.totchild;
totparent = ctx.totparent;
diff --git a/source/blender/blenkernel/intern/particle_distribute.c b/source/blender/blenkernel/intern/particle_distribute.c
index ad617b4198b..6cae6cd6fa2 100644
--- a/source/blender/blenkernel/intern/particle_distribute.c
+++ b/source/blender/blenkernel/intern/particle_distribute.c
@@ -1330,7 +1330,7 @@ static void distribute_particles_on_dm(ParticleSimulationData *sim, int from)
return;
}
- task_pool = BLI_task_pool_create(&ctx, TASK_PRIORITY_LOW);
+ task_pool = BLI_task_pool_create(&ctx, TASK_PRIORITY_LOW, TASK_ISOLATION_ON);
totpart = (from == PART_FROM_CHILD ? sim->psys->totchild : sim->psys->totpart);
psys_tasks_create(&ctx, 0, totpart, &tasks, &numtasks);
diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c
index c727a144c87..149e345e501 100644
--- a/source/blender/blenkernel/intern/particle_system.c
+++ b/source/blender/blenkernel/intern/particle_system.c
@@ -3312,13 +3312,11 @@ static MDeformVert *hair_set_pinning(MDeformVert *dvert, float weight)
static void hair_create_input_mesh(ParticleSimulationData *sim,
int totpoint,
int totedge,
- Mesh **r_mesh,
- ClothHairData **r_hairdata)
+ Mesh **r_mesh)
{
ParticleSystem *psys = sim->psys;
ParticleSettings *part = psys->part;
Mesh *mesh;
- ClothHairData *hairdata;
MVert *mvert;
MEdge *medge;
MDeformVert *dvert;
@@ -3339,9 +3337,8 @@ static void hair_create_input_mesh(ParticleSimulationData *sim,
medge = mesh->medge;
dvert = mesh->dvert;
- hairdata = *r_hairdata;
- if (!hairdata) {
- *r_hairdata = hairdata = MEM_mallocN(sizeof(ClothHairData) * totpoint, "hair data");
+ if (psys->clmd->hairdata == NULL) {
+ psys->clmd->hairdata = MEM_mallocN(sizeof(ClothHairData) * totpoint, "hair data");
}
/* calculate maximum segment length */
@@ -3493,7 +3490,7 @@ static void do_hair_dynamics(ParticleSimulationData *sim)
}
}
- hair_create_input_mesh(sim, totpoint, totedge, &psys->hair_in_mesh, &psys->clmd->hairdata);
+ hair_create_input_mesh(sim, totpoint, totedge, &psys->hair_in_mesh);
if (psys->hair_out_mesh) {
BKE_id_free(NULL, psys->hair_out_mesh);
@@ -4912,9 +4909,12 @@ void particle_system_update(struct Depsgraph *depsgraph,
sim.psmd->flag |= eParticleSystemFlag_Pars;
}
+ ParticleTexture ptex;
+
LOOP_EXISTING_PARTICLES
{
- pa->size = part->size;
+ psys_get_texture(&sim, pa, &ptex, PAMAP_SIZE, cfra);
+ pa->size = part->size * ptex.size;
if (part->randsize > 0.0f) {
pa->size *= 1.0f - part->randsize * psys_frand(psys, p + 1);
}
diff --git a/source/blender/blenkernel/intern/pbvh_intern.h b/source/blender/blenkernel/intern/pbvh_intern.h
index 63bc8753fc7..948b57578dc 100644
--- a/source/blender/blenkernel/intern/pbvh_intern.h
+++ b/source/blender/blenkernel/intern/pbvh_intern.h
@@ -187,31 +187,31 @@ int BB_widest_axis(const BB *bb);
void pbvh_grow_nodes(PBVH *bvh, int totnode);
bool ray_face_intersection_quad(const float ray_start[3],
struct IsectRayPrecalc *isect_precalc,
- const float *t0,
- const float *t1,
- const float *t2,
- const float *t3,
+ const float t0[3],
+ const float t1[3],
+ const float t2[3],
+ const float t3[3],
float *depth);
bool ray_face_intersection_tri(const float ray_start[3],
struct IsectRayPrecalc *isect_precalc,
- const float *t0,
- const float *t1,
- const float *t2,
+ const float t0[3],
+ const float t1[3],
+ const float t2[3],
float *depth);
bool ray_face_nearest_quad(const float ray_start[3],
const float ray_normal[3],
- const float *t0,
- const float *t1,
- const float *t2,
- const float *t3,
+ const float t0[3],
+ const float t1[3],
+ const float t2[3],
+ const float t3[3],
float *r_depth,
float *r_dist_sq);
bool ray_face_nearest_tri(const float ray_start[3],
const float ray_normal[3],
- const float *t0,
- const float *t1,
- const float *t2,
+ const float t0[3],
+ const float t1[3],
+ const float t2[3],
float *r_depth,
float *r_dist_sq);
diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c
index 17434ee8023..be206f8a642 100644
--- a/source/blender/blenkernel/intern/pointcache.c
+++ b/source/blender/blenkernel/intern/pointcache.c
@@ -1805,7 +1805,7 @@ int BKE_ptcache_mem_pointers_seek(int point_index, PTCacheMem *pm, void *cur[BPH
}
for (i = 0; i < BPHYS_TOT_DATA; i++) {
- cur[i] = data_types & (1 << i) ? (char *)pm->data[i] + index * ptcache_data_size[i] : NULL;
+ cur[i] = (data_types & (1 << i)) ? (char *)pm->data[i] + index * ptcache_data_size[i] : NULL;
}
return 1;
diff --git a/source/blender/blenkernel/intern/rigidbody.c b/source/blender/blenkernel/intern/rigidbody.c
index 19078446009..21b86aa8148 100644
--- a/source/blender/blenkernel/intern/rigidbody.c
+++ b/source/blender/blenkernel/intern/rigidbody.c
@@ -260,10 +260,12 @@ static RigidBodyOb *rigidbody_copy_object(const Object *ob, const int flag)
RigidBodyOb *rboN = NULL;
if (ob->rigidbody_object) {
+ const bool is_orig = (flag & LIB_ID_COPY_SET_COPIED_ON_WRITE) == 0;
+
/* just duplicate the whole struct first (to catch all the settings) */
rboN = MEM_dupallocN(ob->rigidbody_object);
- if ((flag & LIB_ID_CREATE_NO_MAIN) == 0) {
+ if (is_orig) {
/* This is a regular copy, and not a CoW copy for depsgraph evaluation */
rboN->shared = MEM_callocN(sizeof(*rboN->shared), "RigidBodyOb_Shared");
}
@@ -1520,7 +1522,7 @@ void BKE_rigidbody_remove_object(Main *bmain, Scene *scene, Object *ob, const bo
if (rbw) {
/* remove object from array */
- if (rbw && rbw->objects) {
+ if (rbw->objects) {
for (i = 0; i < rbw->numbodies; i++) {
if (rbw->objects[i] == ob) {
rbw->objects[i] = NULL;
diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c
index de3e1023b08..86d4c03d51a 100644
--- a/source/blender/blenkernel/intern/scene.c
+++ b/source/blender/blenkernel/intern/scene.c
@@ -742,7 +742,8 @@ static void scene_foreach_id(ID *id, LibraryForeachIDData *data)
BKE_LIB_FOREACHID_PROCESS(data, view_layer->mat_override, IDWALK_CB_USER);
LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
- BKE_LIB_FOREACHID_PROCESS(data, base->object, IDWALK_CB_NOP);
+ BKE_LIB_FOREACHID_PROCESS(
+ data, base->object, IDWALK_CB_NOP | IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE);
}
scene_foreach_layer_collection(data, &view_layer->layer_collections);
@@ -1984,8 +1985,7 @@ Scene *BKE_scene_duplicate(Main *bmain, Scene *sce, eSceneCopyMethod type)
const bool is_subprocess = false;
if (!is_subprocess) {
- BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false);
- BKE_main_id_clear_newpoins(bmain);
+ BKE_main_id_newptr_and_tag_clear(bmain);
/* In case root duplicated ID is linked, assume we want to get a local copy of it and
* duplicate all expected linked data. */
if (ID_IS_LINKED(sce)) {
@@ -2026,8 +2026,7 @@ Scene *BKE_scene_duplicate(Main *bmain, Scene *sce, eSceneCopyMethod type)
#endif
/* Cleanup. */
- BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false);
- BKE_main_id_clear_newpoins(bmain);
+ BKE_main_id_newptr_and_tag_clear(bmain);
BKE_main_collection_sync(bmain);
}
@@ -2517,6 +2516,18 @@ int BKE_scene_orientation_slot_get_index(const TransformOrientationSlot *orient_
orient_slot->type;
}
+int BKE_scene_orientation_get_index(Scene *scene, int slot_index)
+{
+ TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get(scene, slot_index);
+ return BKE_scene_orientation_slot_get_index(orient_slot);
+}
+
+int BKE_scene_orientation_get_index_from_flag(Scene *scene, int flag)
+{
+ TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get_from_flag(scene, flag);
+ return BKE_scene_orientation_slot_get_index(orient_slot);
+}
+
/** \} */
static bool check_rendered_viewport_visible(Main *bmain)
@@ -2624,6 +2635,7 @@ static void scene_graph_update_tagged(Depsgraph *depsgraph, Main *bmain, bool on
Scene *scene = DEG_get_input_scene(depsgraph);
ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph);
+ bool used_multiple_passes = false;
bool run_callbacks = DEG_id_type_any_updated(depsgraph);
if (run_callbacks) {
@@ -2648,7 +2660,7 @@ static void scene_graph_update_tagged(Depsgraph *depsgraph, Main *bmain, bool on
bmain, &scene->id, depsgraph, BKE_CB_EVT_DEPSGRAPH_UPDATE_POST);
/* It is possible that the custom callback modified scene and removed some IDs from the main
- * database. In this case DEG_ids_clear_recalc() will crash because it iterates over all IDs
+ * database. In this case DEG_editors_update() will crash because it iterates over all IDs
* which depsgraph was built for.
*
* The solution is to update relations prior to this call, avoiding access to freed IDs.
@@ -2660,10 +2672,6 @@ static void scene_graph_update_tagged(Depsgraph *depsgraph, Main *bmain, bool on
* If there are no relations changed by the callback this call will do nothing. */
DEG_graph_relations_update(depsgraph);
}
- /* Inform editors about possible changes. */
- DEG_ids_check_recalc(bmain, depsgraph, scene, view_layer, false);
- /* Clear recalc flags. */
- DEG_ids_clear_recalc(bmain, depsgraph);
/* If user callback did not tag anything for update we can skip second iteration.
* Otherwise we update scene once again, but without running callbacks to bring
@@ -2672,8 +2680,22 @@ static void scene_graph_update_tagged(Depsgraph *depsgraph, Main *bmain, bool on
break;
}
+ /* Clear recalc flags for second pass, but back them up for editors update. */
+ const bool backup = true;
+ DEG_ids_clear_recalc(depsgraph, backup);
+ used_multiple_passes = true;
run_callbacks = false;
}
+
+ /* Inform editors about changes, using recalc flags from both passes. */
+ if (used_multiple_passes) {
+ DEG_ids_restore_recalc(depsgraph);
+ }
+ const bool is_time_update = false;
+ DEG_editors_update(depsgraph, is_time_update);
+
+ const bool backup = false;
+ DEG_ids_clear_recalc(depsgraph, backup);
}
void BKE_scene_graph_update_tagged(Depsgraph *depsgraph, Main *bmain)
@@ -2687,11 +2709,11 @@ void BKE_scene_graph_evaluated_ensure(Depsgraph *depsgraph, Main *bmain)
}
/* applies changes right away, does all sets too */
-void BKE_scene_graph_update_for_newframe(Depsgraph *depsgraph)
+void BKE_scene_graph_update_for_newframe_ex(Depsgraph *depsgraph, const bool clear_recalc)
{
Scene *scene = DEG_get_input_scene(depsgraph);
- ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph);
Main *bmain = DEG_get_bmain(depsgraph);
+ bool used_multiple_passes = false;
/* Keep this first. */
BKE_callback_exec_id(bmain, &scene->id, BKE_CB_EVT_FRAME_CHANGE_PRE);
@@ -2724,24 +2746,44 @@ void BKE_scene_graph_update_for_newframe(Depsgraph *depsgraph)
BKE_callback_exec_id_depsgraph(bmain, &scene->id, depsgraph, BKE_CB_EVT_FRAME_CHANGE_POST);
/* NOTE: Similar to this case in scene_graph_update_tagged(). Need to ensure that
- * DEG_ids_clear_recalc() doesn't access freed memory of possibly removed ID. */
+ * DEG_editors_update() doesn't access freed memory of possibly removed ID. */
DEG_graph_relations_update(depsgraph);
}
- /* Inform editors about possible changes. */
- DEG_ids_check_recalc(bmain, depsgraph, scene, view_layer, true);
- /* clear recalc flags */
- DEG_ids_clear_recalc(bmain, depsgraph);
-
/* If user callback did not tag anything for update we can skip second iteration.
* Otherwise we update scene once again, but without running callbacks to bring
* scene to a fully evaluated state with user modifications taken into account. */
if (DEG_is_fully_evaluated(depsgraph)) {
break;
}
+
+ /* Clear recalc flags for second pass, but back them up for editors update. */
+ const bool backup = true;
+ DEG_ids_clear_recalc(depsgraph, backup);
+ used_multiple_passes = true;
+ }
+
+ /* Inform editors about changes, using recalc flags from both passes. */
+ if (used_multiple_passes) {
+ DEG_ids_restore_recalc(depsgraph);
+ }
+
+ const bool is_time_update = true;
+ DEG_editors_update(depsgraph, is_time_update);
+
+ /* Clear recalc flags, can be skipped for e.g. renderers that will read these
+ * and clear the flags later. */
+ if (clear_recalc) {
+ const bool backup = false;
+ DEG_ids_clear_recalc(depsgraph, backup);
}
}
+void BKE_scene_graph_update_for_newframe(Depsgraph *depsgraph)
+{
+ BKE_scene_graph_update_for_newframe_ex(depsgraph, true);
+}
+
/**
* Ensures given scene/view_layer pair has a valid, up-to-date depsgraph.
*
@@ -3450,6 +3492,9 @@ static Depsgraph **scene_ensure_depsgraph_p(Main *bmain, Scene *scene, ViewLayer
BLI_snprintf(name, sizeof(name), "%s :: %s", scene->id.name, view_layer->name);
DEG_debug_name_set(*depsgraph_ptr, name);
+ /* These viewport depsgraphs communicate changes to the editors. */
+ DEG_enable_editors_update(*depsgraph_ptr);
+
return depsgraph_ptr;
}
@@ -3496,8 +3541,8 @@ GHash *BKE_scene_undo_depsgraphs_extract(Main *bmain)
for (Scene *scene = bmain->scenes.first; scene != NULL; scene = scene->id.next) {
if (scene->depsgraph_hash == NULL) {
- /* In some cases, e.g. when undo has to perform multiple steps at once, no depsgraph will be
- * built so this pointer may be NULL. */
+ /* In some cases, e.g. when undo has to perform multiple steps at once, no depsgraph will
+ * be built so this pointer may be NULL. */
continue;
}
for (ViewLayer *view_layer = scene->view_layers.first; view_layer != NULL;
@@ -3742,9 +3787,7 @@ void BKE_scene_eval_sequencer_sequences(Depsgraph *depsgraph, Scene *scene)
SEQ_ALL_BEGIN (scene->ed, seq) {
if (seq->scene_sound == NULL) {
if (seq->sound != NULL) {
- if (seq->scene_sound == NULL) {
- seq->scene_sound = BKE_sound_add_scene_sound_defaults(scene, seq);
- }
+ seq->scene_sound = BKE_sound_add_scene_sound_defaults(scene, seq);
}
else if (seq->type == SEQ_TYPE_SCENE) {
if (seq->scene != NULL) {
diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c
index 1766ac5b85f..d0d63192ebf 100644
--- a/source/blender/blenkernel/intern/screen.c
+++ b/source/blender/blenkernel/intern/screen.c
@@ -227,7 +227,12 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area
case SPACE_SPREADSHEET: {
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl;
- BKE_LIB_FOREACHID_PROCESS_ID(data, sspreadsheet->pinned_id, IDWALK_CB_NOP);
+ LISTBASE_FOREACH (SpreadsheetContext *, context, &sspreadsheet->context_path) {
+ if (context->type == SPREADSHEET_CONTEXT_OBJECT) {
+ BKE_LIB_FOREACHID_PROCESS(
+ data, ((SpreadsheetContextObject *)context)->object, IDWALK_CB_NOP);
+ }
+ }
break;
}
default:
@@ -1350,6 +1355,34 @@ static void write_area(BlendWriter *writer, ScrArea *area)
}
else if (sl->spacetype == SPACE_SPREADSHEET) {
BLO_write_struct(writer, SpaceSpreadsheet, sl);
+
+ SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl;
+ LISTBASE_FOREACH (SpreadsheetColumn *, column, &sspreadsheet->columns) {
+ BLO_write_struct(writer, SpreadsheetColumn, column);
+ BLO_write_struct(writer, SpreadsheetColumnID, column->id);
+ BLO_write_string(writer, column->id->name);
+ }
+ LISTBASE_FOREACH (SpreadsheetContext *, context, &sspreadsheet->context_path) {
+ switch (context->type) {
+ case SPREADSHEET_CONTEXT_OBJECT: {
+ SpreadsheetContextObject *object_context = (SpreadsheetContextObject *)context;
+ BLO_write_struct(writer, SpreadsheetContextObject, object_context);
+ break;
+ }
+ case SPREADSHEET_CONTEXT_MODIFIER: {
+ SpreadsheetContextModifier *modifier_context = (SpreadsheetContextModifier *)context;
+ BLO_write_struct(writer, SpreadsheetContextModifier, modifier_context);
+ BLO_write_string(writer, modifier_context->modifier_name);
+ break;
+ }
+ case SPREADSHEET_CONTEXT_NODE: {
+ SpreadsheetContextNode *node_context = (SpreadsheetContextNode *)context;
+ BLO_write_struct(writer, SpreadsheetContextNode, node_context);
+ BLO_write_string(writer, node_context->node_name);
+ break;
+ }
+ }
+ }
}
}
}
@@ -1527,9 +1560,6 @@ static void direct_link_area(BlendDataReader *reader, ScrArea *area)
if (sl->spacetype == SPACE_VIEW3D) {
View3D *v3d = (View3D *)sl;
-
- v3d->flag |= V3D_INVALID_BACKBUF;
-
if (v3d->gpd) {
BLO_read_data_address(reader, &v3d->gpd);
BKE_gpencil_blend_read_data(reader, v3d->gpd);
@@ -1705,6 +1735,31 @@ static void direct_link_area(BlendDataReader *reader, ScrArea *area)
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl;
sspreadsheet->runtime = NULL;
+
+ BLO_read_list(reader, &sspreadsheet->columns);
+ LISTBASE_FOREACH (SpreadsheetColumn *, column, &sspreadsheet->columns) {
+ BLO_read_data_address(reader, &column->id);
+ BLO_read_data_address(reader, &column->id->name);
+ }
+
+ BLO_read_list(reader, &sspreadsheet->context_path);
+ LISTBASE_FOREACH (SpreadsheetContext *, context, &sspreadsheet->context_path) {
+ switch (context->type) {
+ case SPREADSHEET_CONTEXT_NODE: {
+ SpreadsheetContextNode *node_context = (SpreadsheetContextNode *)context;
+ BLO_read_data_address(reader, &node_context->node_name);
+ break;
+ }
+ case SPREADSHEET_CONTEXT_MODIFIER: {
+ SpreadsheetContextModifier *modifier_context = (SpreadsheetContextModifier *)context;
+ BLO_read_data_address(reader, &modifier_context->modifier_name);
+ break;
+ }
+ case SPREADSHEET_CONTEXT_OBJECT: {
+ break;
+ }
+ }
+ }
}
}
@@ -1921,7 +1976,12 @@ void BKE_screen_area_blend_read_lib(BlendLibReader *reader, ID *parent_id, ScrAr
}
case SPACE_SPREADSHEET: {
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl;
- BLO_read_id_address(reader, parent_id->lib, &sspreadsheet->pinned_id);
+ LISTBASE_FOREACH (SpreadsheetContext *, context, &sspreadsheet->context_path) {
+ if (context->type == SPREADSHEET_CONTEXT_OBJECT) {
+ BLO_read_id_address(
+ reader, parent_id->lib, &((SpreadsheetContextObject *)context)->object);
+ }
+ }
break;
}
default:
diff --git a/source/blender/blenkernel/intern/softbody.c b/source/blender/blenkernel/intern/softbody.c
index de88e8a941c..fcc1afbc59b 100644
--- a/source/blender/blenkernel/intern/softbody.c
+++ b/source/blender/blenkernel/intern/softbody.c
@@ -499,7 +499,6 @@ static void ccd_mesh_free(ccd_Mesh *ccdm)
}
MEM_freeN(ccdm->mima);
MEM_freeN(ccdm);
- ccdm = NULL;
}
}
@@ -830,13 +829,13 @@ static void calculate_collision_balls(Object *ob)
}
/* creates new softbody if didn't exist yet, makes new points and springs arrays */
-static void renew_softbody(Scene *scene, Object *ob, int totpoint, int totspring)
+static void renew_softbody(Object *ob, int totpoint, int totspring)
{
SoftBody *sb;
int i;
short softflag;
if (ob->soft == NULL) {
- ob->soft = sbNew(scene);
+ ob->soft = sbNew();
}
else {
free_softbody_intern(ob->soft);
@@ -2680,7 +2679,7 @@ static void springs_from_mesh(Object *ob)
}
/* makes totally fresh start situation */
-static void mesh_to_softbody(Scene *scene, Object *ob)
+static void mesh_to_softbody(Object *ob)
{
SoftBody *sb;
Mesh *me = ob->data;
@@ -2698,7 +2697,7 @@ static void mesh_to_softbody(Scene *scene, Object *ob)
}
/* renew ends with ob->soft with points and edges, also checks & makes ob->soft */
- renew_softbody(scene, ob, me->totvert, totedge);
+ renew_softbody(ob, me->totvert, totedge);
/* we always make body points */
sb = ob->soft;
@@ -2910,7 +2909,7 @@ static void makelatticesprings(Lattice *lt, BodySpring *bs, int dostiff, Object
}
/* makes totally fresh start situation */
-static void lattice_to_softbody(Scene *scene, Object *ob)
+static void lattice_to_softbody(Object *ob)
{
Lattice *lt = ob->data;
SoftBody *sb;
@@ -2930,7 +2929,7 @@ static void lattice_to_softbody(Scene *scene, Object *ob)
}
/* renew ends with ob->soft with points and edges, also checks & makes ob->soft */
- renew_softbody(scene, ob, totvert, totspring);
+ renew_softbody(ob, totvert, totspring);
sb = ob->soft; /* can be created in renew_softbody() */
bp = sb->bpoint;
@@ -2973,7 +2972,7 @@ static void lattice_to_softbody(Scene *scene, Object *ob)
}
/* makes totally fresh start situation */
-static void curve_surf_to_softbody(Scene *scene, Object *ob)
+static void curve_surf_to_softbody(Object *ob)
{
Curve *cu = ob->data;
SoftBody *sb;
@@ -2994,7 +2993,7 @@ static void curve_surf_to_softbody(Scene *scene, Object *ob)
}
/* renew ends with ob->soft with points and edges, also checks & makes ob->soft */
- renew_softbody(scene, ob, totvert, totspring);
+ renew_softbody(ob, totvert, totspring);
sb = ob->soft; /* can be created in renew_softbody() */
/* set vars now */
@@ -3118,7 +3117,7 @@ static void sb_new_scratch(SoftBody *sb)
/* ************ Object level, exported functions *************** */
/* allocates and initializes general main data */
-SoftBody *sbNew(Scene *scene)
+SoftBody *sbNew(void)
{
SoftBody *sb;
@@ -3141,12 +3140,6 @@ SoftBody *sbNew(Scene *scene)
/*todo backward file compat should copy inspring to inpush while reading old files*/
sb->inpush = 0.5f;
- sb->interval = 10;
- if (scene != NULL) {
- sb->sfra = scene->r.sfra;
- sb->efra = scene->r.efra;
- }
-
sb->colball = 0.49f;
sb->balldamp = 0.50f;
sb->ballstiff = 1.0f;
@@ -3181,9 +3174,11 @@ void sbFree(Object *ob)
return;
}
+ const bool is_orig = (ob->id.tag & LIB_TAG_COPIED_ON_WRITE) == 0;
+
free_softbody_intern(sb);
- if ((ob->id.tag & LIB_TAG_NO_MAIN) == 0) {
+ if (is_orig) {
/* Only free shared data on non-CoW copies */
BKE_ptcache_free_list(&sb->shared->ptcaches);
sb->shared->pointcache = NULL;
@@ -3576,17 +3571,17 @@ void sbObjectStep(struct Depsgraph *depsgraph,
switch (ob->type) {
case OB_MESH:
- mesh_to_softbody(scene, ob);
+ mesh_to_softbody(ob);
break;
case OB_LATTICE:
- lattice_to_softbody(scene, ob);
+ lattice_to_softbody(ob);
break;
case OB_CURVE:
case OB_SURF:
- curve_surf_to_softbody(scene, ob);
+ curve_surf_to_softbody(ob);
break;
default:
- renew_softbody(scene, ob, numVerts, 0);
+ renew_softbody(ob, numVerts, 0);
break;
}
diff --git a/source/blender/blenkernel/intern/spline_base.cc b/source/blender/blenkernel/intern/spline_base.cc
new file mode 100644
index 00000000000..2781ff1e49a
--- /dev/null
+++ b/source/blender/blenkernel/intern/spline_base.cc
@@ -0,0 +1,380 @@
+/*
+ * 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.
+ */
+
+#include "BLI_array.hh"
+#include "BLI_span.hh"
+#include "BLI_task.hh"
+#include "BLI_timeit.hh"
+
+#include "BKE_spline.hh"
+
+#include "FN_generic_virtual_array.hh"
+
+using blender::Array;
+using blender::float3;
+using blender::IndexRange;
+using blender::MutableSpan;
+using blender::Span;
+using blender::fn::GMutableSpan;
+using blender::fn::GSpan;
+using blender::fn::GVArray;
+using blender::fn::GVArray_For_GSpan;
+using blender::fn::GVArray_Typed;
+using blender::fn::GVArrayPtr;
+
+Spline::Type Spline::type() const
+{
+ return type_;
+}
+
+void Spline::translate(const blender::float3 &translation)
+{
+ for (float3 &position : this->positions()) {
+ position += translation;
+ }
+ this->mark_cache_invalid();
+}
+
+void Spline::transform(const blender::float4x4 &matrix)
+{
+ for (float3 &position : this->positions()) {
+ position = matrix * position;
+ }
+ this->mark_cache_invalid();
+}
+
+int Spline::evaluated_edges_size() const
+{
+ const int eval_size = this->evaluated_points_size();
+ if (eval_size == 1) {
+ return 0;
+ }
+
+ return this->is_cyclic_ ? eval_size : eval_size - 1;
+}
+
+float Spline::length() const
+{
+ Span<float> lengths = this->evaluated_lengths();
+ return (lengths.size() == 0) ? 0 : this->evaluated_lengths().last();
+}
+
+int Spline::segments_size() const
+{
+ const int points_len = this->size();
+
+ return is_cyclic_ ? points_len : points_len - 1;
+}
+
+bool Spline::is_cyclic() const
+{
+ return is_cyclic_;
+}
+
+void Spline::set_cyclic(const bool value)
+{
+ is_cyclic_ = value;
+}
+
+static void accumulate_lengths(Span<float3> positions,
+ const bool is_cyclic,
+ MutableSpan<float> lengths)
+{
+ float length = 0.0f;
+ for (const int i : IndexRange(positions.size() - 1)) {
+ length += float3::distance(positions[i], positions[i + 1]);
+ lengths[i] = length;
+ }
+ if (is_cyclic) {
+ lengths.last() = length + float3::distance(positions.last(), positions.first());
+ }
+}
+
+/**
+ * Return non-owning access to the cache of accumulated lengths along the spline. Each item is the
+ * length of the subsequent segment, i.e. the first value is the length of the first segment rather
+ * than 0. This calculation is rather trivial, and only depends on the evaluated positions.
+ * However, the results are used often, so it makes sense to cache it.
+ */
+Span<float> Spline::evaluated_lengths() const
+{
+ if (!length_cache_dirty_) {
+ return evaluated_lengths_cache_;
+ }
+
+ std::lock_guard lock{length_cache_mutex_};
+ if (!length_cache_dirty_) {
+ return evaluated_lengths_cache_;
+ }
+
+ const int total = evaluated_edges_size();
+ evaluated_lengths_cache_.resize(total);
+
+ Span<float3> positions = this->evaluated_positions();
+ accumulate_lengths(positions, is_cyclic_, evaluated_lengths_cache_);
+
+ length_cache_dirty_ = false;
+ return evaluated_lengths_cache_;
+}
+
+static float3 direction_bisect(const float3 &prev, const float3 &middle, const float3 &next)
+{
+ const float3 dir_prev = (middle - prev).normalized();
+ const float3 dir_next = (next - middle).normalized();
+
+ return (dir_prev + dir_next).normalized();
+}
+
+static void calculate_tangents(Span<float3> positions,
+ const bool is_cyclic,
+ MutableSpan<float3> tangents)
+{
+ if (positions.size() == 1) {
+ return;
+ }
+
+ for (const int i : IndexRange(1, positions.size() - 2)) {
+ tangents[i] = direction_bisect(positions[i - 1], positions[i], positions[i + 1]);
+ }
+
+ if (is_cyclic) {
+ const float3 &second_to_last = positions[positions.size() - 2];
+ const float3 &last = positions.last();
+ const float3 &first = positions.first();
+ const float3 &second = positions[1];
+ tangents.first() = direction_bisect(last, first, second);
+ tangents.last() = direction_bisect(second_to_last, last, first);
+ }
+ else {
+ tangents.first() = (positions[1] - positions[0]).normalized();
+ tangents.last() = (positions.last() - positions[positions.size() - 2]).normalized();
+ }
+}
+
+/**
+ * Return non-owning access to the direction of the curve at each evaluated point.
+ */
+Span<float3> Spline::evaluated_tangents() const
+{
+ if (!tangent_cache_dirty_) {
+ return evaluated_tangents_cache_;
+ }
+
+ std::lock_guard lock{tangent_cache_mutex_};
+ if (!tangent_cache_dirty_) {
+ return evaluated_tangents_cache_;
+ }
+
+ const int eval_size = this->evaluated_points_size();
+ evaluated_tangents_cache_.resize(eval_size);
+
+ Span<float3> positions = this->evaluated_positions();
+
+ if (eval_size == 1) {
+ evaluated_tangents_cache_.first() = float3(1.0f, 0.0f, 0.0f);
+ }
+ else {
+ calculate_tangents(positions, is_cyclic_, evaluated_tangents_cache_);
+ this->correct_end_tangents();
+ }
+
+ tangent_cache_dirty_ = false;
+ return evaluated_tangents_cache_;
+}
+
+static float3 rotate_direction_around_axis(const float3 &direction,
+ const float3 &axis,
+ const float angle)
+{
+ BLI_ASSERT_UNIT_V3(direction);
+ BLI_ASSERT_UNIT_V3(axis);
+
+ const float3 axis_scaled = axis * float3::dot(direction, axis);
+ const float3 diff = direction - axis_scaled;
+ const float3 cross = float3::cross(axis, diff);
+
+ return axis_scaled + diff * std::cos(angle) + cross * std::sin(angle);
+}
+
+static void calculate_normals_z_up(Span<float3> tangents, MutableSpan<float3> normals)
+{
+ for (const int i : normals.index_range()) {
+ normals[i] = float3::cross(tangents[i], float3(0.0f, 0.0f, 1.0f)).normalized();
+ }
+}
+
+/**
+ * Return non-owning access to the direction vectors perpendicular to the tangents at every
+ * evaluated point. The method used to generate the normal vectors depends on Spline.normal_mode.
+ */
+Span<float3> Spline::evaluated_normals() const
+{
+ if (!normal_cache_dirty_) {
+ return evaluated_normals_cache_;
+ }
+
+ std::lock_guard lock{normal_cache_mutex_};
+ if (!normal_cache_dirty_) {
+ return evaluated_normals_cache_;
+ }
+
+ const int eval_size = this->evaluated_points_size();
+ evaluated_normals_cache_.resize(eval_size);
+
+ Span<float3> tangents = evaluated_tangents();
+ MutableSpan<float3> normals = evaluated_normals_cache_;
+
+ /* Only Z up normals are supported at the moment. */
+ calculate_normals_z_up(tangents, normals);
+
+ /* Rotate the generated normals with the interpolated tilt data. */
+ GVArray_Typed<float> tilts = this->interpolate_to_evaluated_points(this->tilts());
+ for (const int i : normals.index_range()) {
+ normals[i] = rotate_direction_around_axis(normals[i], tangents[i], tilts[i]);
+ }
+
+ normal_cache_dirty_ = false;
+ return evaluated_normals_cache_;
+}
+
+Spline::LookupResult Spline::lookup_evaluated_factor(const float factor) const
+{
+ return this->lookup_evaluated_length(this->length() * factor);
+}
+
+/**
+ * \note This does not support extrapolation currently.
+ */
+Spline::LookupResult Spline::lookup_evaluated_length(const float length) const
+{
+ BLI_assert(length >= 0.0f && length <= this->length());
+
+ Span<float> lengths = this->evaluated_lengths();
+
+ const float *offset = std::lower_bound(lengths.begin(), lengths.end(), length);
+ const int index = offset - lengths.begin();
+ const int next_index = (index == this->size() - 1) ? 0 : index + 1;
+
+ const float previous_length = (index == 0) ? 0.0f : lengths[index - 1];
+ const float factor = (length - previous_length) / (lengths[index] - previous_length);
+
+ return LookupResult{index, next_index, factor};
+}
+
+/**
+ * Return an array of evenly spaced samples along the length of the spline. The samples are indices
+ * and factors to the next index encoded in floats. The logic for converting from the float values
+ * to interpolation data is in #lookup_data_from_index_factor.
+ */
+Array<float> Spline::sample_uniform_index_factors(const int samples_size) const
+{
+ const Span<float> lengths = this->evaluated_lengths();
+
+ BLI_assert(samples_size > 0);
+ Array<float> samples(samples_size);
+
+ samples[0] = 0.0f;
+ if (samples_size == 1) {
+ return samples;
+ }
+
+ const float total_length = this->length();
+ const float sample_length = total_length / (samples_size - (is_cyclic_ ? 0 : 1));
+
+ /* Store the length at the previous evaluated point in a variable so it can
+ * start out at zero (the lengths array doesn't contain 0 for the first point). */
+ float prev_length = 0.0f;
+ int i_sample = 1;
+ for (const int i_evaluated : IndexRange(this->evaluated_edges_size())) {
+ const float length = lengths[i_evaluated];
+
+ /* Add every sample that fits in this evaluated edge. */
+ while ((sample_length * i_sample) < length && i_sample < samples_size) {
+ const float factor = (sample_length * i_sample - prev_length) / (length - prev_length);
+ samples[i_sample] = i_evaluated + factor;
+ i_sample++;
+ }
+
+ prev_length = length;
+ }
+
+ if (!is_cyclic_) {
+ /* In rare cases this can prevent overflow of the stored index. */
+ samples.last() = lengths.size();
+ }
+
+ return samples;
+}
+
+Spline::LookupResult Spline::lookup_data_from_index_factor(const float index_factor) const
+{
+ const int points_len = this->evaluated_points_size();
+
+ if (is_cyclic_) {
+ if (index_factor < points_len) {
+ const int index = std::floor(index_factor);
+ const int next_index = (index < points_len - 1) ? index + 1 : 0;
+ return LookupResult{index, next_index, index_factor - index};
+ }
+ return LookupResult{points_len - 1, 0, 1.0f};
+ }
+
+ if (index_factor < points_len - 1) {
+ const int index = std::floor(index_factor);
+ const int next_index = index + 1;
+ return LookupResult{index, next_index, index_factor - index};
+ }
+ return LookupResult{points_len - 2, points_len - 1, 1.0f};
+}
+
+void Spline::bounds_min_max(float3 &min, float3 &max, const bool use_evaluated) const
+{
+ Span<float3> positions = use_evaluated ? this->evaluated_positions() : this->positions();
+ for (const float3 &position : positions) {
+ minmax_v3v3_v3(min, max, position);
+ }
+}
+
+GVArrayPtr Spline::interpolate_to_evaluated_points(GSpan data) const
+{
+ return this->interpolate_to_evaluated_points(GVArray_For_GSpan(data));
+}
+
+/**
+ * Sample any input data with a value for each evaluated point (already interpolated to evaluated
+ * points) to arbitrary parameters in between the evaluated points. The interpolation is quite
+ * simple, but this handles the cyclic and end point special cases.
+ */
+void Spline::sample_based_on_index_factors(const GVArray &src,
+ Span<float> index_factors,
+ GMutableSpan dst) const
+{
+ BLI_assert(src.size() == this->evaluated_points_size());
+
+ blender::attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ const GVArray_Typed<T> src_typed = src.typed<T>();
+ MutableSpan<T> dst_typed = dst.typed<T>();
+ blender::parallel_for(dst_typed.index_range(), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ const LookupResult interp = this->lookup_data_from_index_factor(index_factors[i]);
+ dst_typed[i] = blender::attribute_math::mix2(interp.factor,
+ src_typed[interp.evaluated_index],
+ src_typed[interp.next_evaluated_index]);
+ }
+ });
+ });
+}
diff --git a/source/blender/blenkernel/intern/spline_bezier.cc b/source/blender/blenkernel/intern/spline_bezier.cc
new file mode 100644
index 00000000000..3e421dcfc13
--- /dev/null
+++ b/source/blender/blenkernel/intern/spline_bezier.cc
@@ -0,0 +1,592 @@
+/*
+ * 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.
+ */
+
+#include "BLI_array.hh"
+#include "BLI_span.hh"
+#include "BLI_task.hh"
+
+#include "BKE_spline.hh"
+
+using blender::Array;
+using blender::float3;
+using blender::IndexRange;
+using blender::MutableSpan;
+using blender::Span;
+
+SplinePtr BezierSpline::copy() const
+{
+ return std::make_unique<BezierSpline>(*this);
+}
+
+SplinePtr BezierSpline::copy_settings() const
+{
+ std::unique_ptr<BezierSpline> copy = std::make_unique<BezierSpline>();
+ copy_base_settings(*this, *copy);
+ copy->resolution_ = resolution_;
+ return copy;
+}
+
+int BezierSpline::size() const
+{
+ const int size = positions_.size();
+ BLI_assert(size == handle_types_left_.size());
+ BLI_assert(size == handle_positions_left_.size());
+ BLI_assert(size == handle_types_right_.size());
+ BLI_assert(size == handle_positions_right_.size());
+ BLI_assert(size == radii_.size());
+ BLI_assert(size == tilts_.size());
+ return size;
+}
+
+int BezierSpline::resolution() const
+{
+ return resolution_;
+}
+
+void BezierSpline::set_resolution(const int value)
+{
+ BLI_assert(value > 0);
+ resolution_ = value;
+ this->mark_cache_invalid();
+}
+
+/**
+ * \warning Call #reallocate on the spline's attributes after adding all points.
+ */
+void BezierSpline::add_point(const float3 position,
+ const HandleType handle_type_left,
+ const float3 handle_position_left,
+ const HandleType handle_type_right,
+ const float3 handle_position_right,
+ const float radius,
+ const float tilt)
+{
+ handle_types_left_.append(handle_type_left);
+ handle_positions_left_.append(handle_position_left);
+ positions_.append(position);
+ handle_types_right_.append(handle_type_right);
+ handle_positions_right_.append(handle_position_right);
+ radii_.append(radius);
+ tilts_.append(tilt);
+ this->mark_cache_invalid();
+}
+
+void BezierSpline::resize(const int size)
+{
+ handle_types_left_.resize(size);
+ handle_positions_left_.resize(size);
+ positions_.resize(size);
+ handle_types_right_.resize(size);
+ handle_positions_right_.resize(size);
+ radii_.resize(size);
+ tilts_.resize(size);
+ this->mark_cache_invalid();
+ attributes.reallocate(size);
+}
+
+MutableSpan<float3> BezierSpline::positions()
+{
+ return positions_;
+}
+Span<float3> BezierSpline::positions() const
+{
+ return positions_;
+}
+MutableSpan<float> BezierSpline::radii()
+{
+ return radii_;
+}
+Span<float> BezierSpline::radii() const
+{
+ return radii_;
+}
+MutableSpan<float> BezierSpline::tilts()
+{
+ return tilts_;
+}
+Span<float> BezierSpline::tilts() const
+{
+ return tilts_;
+}
+Span<BezierSpline::HandleType> BezierSpline::handle_types_left() const
+{
+ return handle_types_left_;
+}
+MutableSpan<BezierSpline::HandleType> BezierSpline::handle_types_left()
+{
+ return handle_types_left_;
+}
+Span<float3> BezierSpline::handle_positions_left() const
+{
+ this->ensure_auto_handles();
+ return handle_positions_left_;
+}
+MutableSpan<float3> BezierSpline::handle_positions_left()
+{
+ this->ensure_auto_handles();
+ return handle_positions_left_;
+}
+Span<BezierSpline::HandleType> BezierSpline::handle_types_right() const
+{
+ return handle_types_right_;
+}
+MutableSpan<BezierSpline::HandleType> BezierSpline::handle_types_right()
+{
+ return handle_types_right_;
+}
+Span<float3> BezierSpline::handle_positions_right() const
+{
+ this->ensure_auto_handles();
+ return handle_positions_right_;
+}
+MutableSpan<float3> BezierSpline::handle_positions_right()
+{
+ this->ensure_auto_handles();
+ return handle_positions_right_;
+}
+
+static float3 previous_position(Span<float3> positions, const bool cyclic, const int i)
+{
+ if (i == 0) {
+ if (cyclic) {
+ return positions[positions.size() - 1];
+ }
+ return 2.0f * positions[i] - positions[i + 1];
+ }
+ return positions[i - 1];
+}
+
+static float3 next_position(Span<float3> positions, const bool cyclic, const int i)
+{
+ if (i == positions.size() - 1) {
+ if (cyclic) {
+ return positions[0];
+ }
+ return 2.0f * positions[i] - positions[i - 1];
+ }
+ return positions[i + 1];
+}
+
+/**
+ * Recalculate all #Auto and #Vector handles with positions automatically
+ * derived from the neighboring control points.
+ */
+void BezierSpline::ensure_auto_handles() const
+{
+ if (!auto_handles_dirty_) {
+ return;
+ }
+
+ std::lock_guard lock{auto_handle_mutex_};
+ if (!auto_handles_dirty_) {
+ return;
+ }
+
+ for (const int i : IndexRange(this->size())) {
+ if (ELEM(HandleType::Auto, handle_types_left_[i], handle_types_right_[i])) {
+ const float3 prev_diff = positions_[i] - previous_position(positions_, is_cyclic_, i);
+ const float3 next_diff = next_position(positions_, is_cyclic_, i) - positions_[i];
+ float prev_len = prev_diff.length();
+ float next_len = next_diff.length();
+ if (prev_len == 0.0f) {
+ prev_len = 1.0f;
+ }
+ if (next_len == 0.0f) {
+ next_len = 1.0f;
+ }
+ const float3 dir = next_diff / next_len + prev_diff / prev_len;
+
+ /* This magic number is unfortunate, but comes from elsewhere in Blender. */
+ const float len = dir.length() * 2.5614f;
+ if (len != 0.0f) {
+ if (handle_types_left_[i] == HandleType::Auto) {
+ const float prev_len_clamped = std::min(prev_len, next_len * 5.0f);
+ handle_positions_left_[i] = positions_[i] + dir * -(prev_len_clamped / len);
+ }
+ if (handle_types_right_[i] == HandleType::Auto) {
+ const float next_len_clamped = std::min(next_len, prev_len * 5.0f);
+ handle_positions_right_[i] = positions_[i] + dir * (next_len_clamped / len);
+ }
+ }
+ }
+
+ if (handle_types_left_[i] == HandleType::Vector) {
+ const float3 prev = previous_position(positions_, is_cyclic_, i);
+ handle_positions_left_[i] = float3::interpolate(positions_[i], prev, 1.0f / 3.0f);
+ }
+
+ if (handle_types_right_[i] == HandleType::Vector) {
+ const float3 next = next_position(positions_, is_cyclic_, i);
+ handle_positions_right_[i] = float3::interpolate(positions_[i], next, 1.0f / 3.0f);
+ }
+ }
+
+ auto_handles_dirty_ = false;
+}
+
+void BezierSpline::translate(const blender::float3 &translation)
+{
+ for (float3 &position : this->positions()) {
+ position += translation;
+ }
+ for (float3 &handle_position : this->handle_positions_left()) {
+ handle_position += translation;
+ }
+ for (float3 &handle_position : this->handle_positions_right()) {
+ handle_position += translation;
+ }
+ this->mark_cache_invalid();
+}
+
+void BezierSpline::transform(const blender::float4x4 &matrix)
+{
+ for (float3 &position : this->positions()) {
+ position = matrix * position;
+ }
+ for (float3 &handle_position : this->handle_positions_left()) {
+ handle_position = matrix * handle_position;
+ }
+ for (float3 &handle_position : this->handle_positions_right()) {
+ handle_position = matrix * handle_position;
+ }
+ this->mark_cache_invalid();
+}
+
+bool BezierSpline::point_is_sharp(const int index) const
+{
+ return ELEM(handle_types_left_[index], HandleType::Vector, HandleType::Free) ||
+ ELEM(handle_types_right_[index], HandleType::Vector, HandleType::Free);
+}
+
+bool BezierSpline::segment_is_vector(const int index) const
+{
+ if (index == this->size() - 1) {
+ if (is_cyclic_) {
+ return handle_types_right_.last() == HandleType::Vector &&
+ handle_types_left_.first() == HandleType::Vector;
+ }
+ /* There is actually no segment in this case, but it's nice to avoid
+ * having a special case for the last segment in calling code. */
+ return true;
+ }
+ return handle_types_right_[index] == HandleType::Vector &&
+ handle_types_left_[index + 1] == HandleType::Vector;
+}
+
+void BezierSpline::mark_cache_invalid()
+{
+ offset_cache_dirty_ = true;
+ position_cache_dirty_ = true;
+ mapping_cache_dirty_ = true;
+ tangent_cache_dirty_ = true;
+ normal_cache_dirty_ = true;
+ length_cache_dirty_ = true;
+ auto_handles_dirty_ = true;
+}
+
+int BezierSpline::evaluated_points_size() const
+{
+ BLI_assert(this->size() > 0);
+ return this->control_point_offsets().last();
+}
+
+/**
+ * If the spline is not cyclic, the direction for the first and last points is just the
+ * direction formed by the corresponding handles and control points. In the unlikely situation
+ * that the handles define a zero direction, fallback to using the direction defined by the
+ * first and last evaluated segments already calculated in #Spline::evaluated_tangents().
+ */
+void BezierSpline::correct_end_tangents() const
+{
+ if (is_cyclic_) {
+ return;
+ }
+
+ MutableSpan<float3> tangents(evaluated_tangents_cache_);
+
+ if (handle_positions_right_.first() != positions_.first()) {
+ tangents.first() = (handle_positions_right_.first() - positions_.first()).normalized();
+ }
+ if (handle_positions_left_.last() != positions_.last()) {
+ tangents.last() = (positions_.last() - handle_positions_left_.last()).normalized();
+ }
+}
+
+static void bezier_forward_difference_3d(const float3 &point_0,
+ const float3 &point_1,
+ const float3 &point_2,
+ const float3 &point_3,
+ MutableSpan<float3> result)
+{
+ BLI_assert(result.size() > 0);
+ const float inv_len = 1.0f / static_cast<float>(result.size());
+ const float inv_len_squared = inv_len * inv_len;
+ const float inv_len_cubed = inv_len_squared * inv_len;
+
+ const float3 rt1 = 3.0f * (point_1 - point_0) * inv_len;
+ const float3 rt2 = 3.0f * (point_0 - 2.0f * point_1 + point_2) * inv_len_squared;
+ const float3 rt3 = (point_3 - point_0 + 3.0f * (point_1 - point_2)) * inv_len_cubed;
+
+ float3 q0 = point_0;
+ float3 q1 = rt1 + rt2 + rt3;
+ float3 q2 = 2.0f * rt2 + 6.0f * rt3;
+ float3 q3 = 6.0f * rt3;
+ for (const int i : result.index_range()) {
+ result[i] = q0;
+ q0 += q1;
+ q1 += q2;
+ q2 += q3;
+ }
+}
+
+void BezierSpline::evaluate_bezier_segment(const int index,
+ const int next_index,
+ MutableSpan<float3> positions) const
+{
+ if (this->segment_is_vector(index)) {
+ BLI_assert(positions.size() == 1);
+ positions.first() = positions_[index];
+ }
+ else {
+ bezier_forward_difference_3d(positions_[index],
+ handle_positions_right_[index],
+ handle_positions_left_[next_index],
+ positions_[next_index],
+ positions);
+ }
+}
+
+/**
+ * Returns access to a cache of offsets into the evaluated point array for each control point.
+ * While most control point edges generate the number of edges specified by the resolution, vector
+ * segments only generate one edge.
+ *
+ * \note The length of the result is one greater than the number of points, so that the last item
+ * is the total number of evaluated points. This is useful to avoid recalculating the size of the
+ * last segment everywhere.
+ */
+Span<int> BezierSpline::control_point_offsets() const
+{
+ if (!offset_cache_dirty_) {
+ return offset_cache_;
+ }
+
+ std::lock_guard lock{offset_cache_mutex_};
+ if (!offset_cache_dirty_) {
+ return offset_cache_;
+ }
+
+ const int points_len = this->size();
+ offset_cache_.resize(points_len + 1);
+
+ MutableSpan<int> offsets = offset_cache_;
+
+ int offset = 0;
+ for (const int i : IndexRange(points_len)) {
+ offsets[i] = offset;
+ offset += this->segment_is_vector(i) ? 1 : resolution_;
+ }
+ offsets.last() = offset;
+
+ offset_cache_dirty_ = false;
+ return offsets;
+}
+
+static void calculate_mappings_linear_resolution(Span<int> offsets,
+ const int size,
+ const int resolution,
+ const bool is_cyclic,
+ MutableSpan<float> r_mappings)
+{
+ const float first_segment_len_inv = 1.0f / offsets[1];
+ for (const int i : IndexRange(0, offsets[1])) {
+ r_mappings[i] = i * first_segment_len_inv;
+ }
+
+ const int grain_size = std::max(2048 / resolution, 1);
+ parallel_for(IndexRange(1, size - 2), grain_size, [&](IndexRange range) {
+ for (const int i_control_point : range) {
+ const int segment_len = offsets[i_control_point + 1] - offsets[i_control_point];
+ const float segment_len_inv = 1.0f / segment_len;
+ for (const int i : IndexRange(segment_len)) {
+ r_mappings[offsets[i_control_point] + i] = i_control_point + i * segment_len_inv;
+ }
+ }
+ });
+
+ if (is_cyclic) {
+ const int last_segment_len = offsets[size] - offsets[size - 1];
+ const float last_segment_len_inv = 1.0f / last_segment_len;
+ for (const int i : IndexRange(last_segment_len)) {
+ r_mappings[offsets[size - 1] + i] = size - 1 + i * last_segment_len_inv;
+ }
+ }
+ else {
+ r_mappings.last() = size - 1;
+ }
+}
+
+/**
+ * Returns non-owning access to an array of values containing the information necessary to
+ * interpolate values from the original control points to evaluated points. The control point
+ * index is the integer part of each value, and the factor used for interpolating to the next
+ * control point is the remaining factional part.
+ */
+Span<float> BezierSpline::evaluated_mappings() const
+{
+ if (!mapping_cache_dirty_) {
+ return evaluated_mapping_cache_;
+ }
+
+ std::lock_guard lock{mapping_cache_mutex_};
+ if (!mapping_cache_dirty_) {
+ return evaluated_mapping_cache_;
+ }
+
+ const int size = this->size();
+ const int eval_size = this->evaluated_points_size();
+ evaluated_mapping_cache_.resize(eval_size);
+ MutableSpan<float> mappings = evaluated_mapping_cache_;
+
+ if (eval_size == 1) {
+ mappings.first() = 0.0f;
+ mapping_cache_dirty_ = false;
+ return mappings;
+ }
+
+ Span<int> offsets = this->control_point_offsets();
+
+ calculate_mappings_linear_resolution(offsets, size, resolution_, is_cyclic_, mappings);
+
+ mapping_cache_dirty_ = false;
+ return mappings;
+}
+
+Span<float3> BezierSpline::evaluated_positions() const
+{
+ if (!position_cache_dirty_) {
+ return evaluated_position_cache_;
+ }
+
+ std::lock_guard lock{position_cache_mutex_};
+ if (!position_cache_dirty_) {
+ return evaluated_position_cache_;
+ }
+
+ this->ensure_auto_handles();
+
+ const int size = this->size();
+ const int eval_size = this->evaluated_points_size();
+ evaluated_position_cache_.resize(eval_size);
+
+ MutableSpan<float3> positions = evaluated_position_cache_;
+
+ Span<int> offsets = this->control_point_offsets();
+
+ const int grain_size = std::max(512 / resolution_, 1);
+ parallel_for(IndexRange(size - 1), grain_size, [&](IndexRange range) {
+ for (const int i : range) {
+ this->evaluate_bezier_segment(
+ i, i + 1, positions.slice(offsets[i], offsets[i + 1] - offsets[i]));
+ }
+ });
+ if (is_cyclic_) {
+ this->evaluate_bezier_segment(
+ size - 1, 0, positions.slice(offsets[size - 1], offsets[size] - offsets[size - 1]));
+ }
+ else {
+ /* Since evaluating the bezier segment doesn't add the final point,
+ * it must be added manually in the non-cyclic case. */
+ positions.last() = positions_.last();
+ }
+
+ position_cache_dirty_ = false;
+ return positions;
+}
+
+/**
+ * Convert the data encoded in #evaulated_mappings into its parts-- the information necessary
+ * to interpolate data from control points to evaluated points between them. The next control
+ * point index result will not overflow the size of the control point vectors.
+ */
+BezierSpline::InterpolationData BezierSpline::interpolation_data_from_index_factor(
+ const float index_factor) const
+{
+ const int points_len = this->size();
+
+ if (is_cyclic_) {
+ if (index_factor < points_len) {
+ const int index = std::floor(index_factor);
+ const int next_index = (index < points_len - 1) ? index + 1 : 0;
+ return InterpolationData{index, next_index, index_factor - index};
+ }
+ return InterpolationData{points_len - 1, 0, 1.0f};
+ }
+
+ if (index_factor < points_len - 1) {
+ const int index = std::floor(index_factor);
+ const int next_index = index + 1;
+ return InterpolationData{index, next_index, index_factor - index};
+ }
+ return InterpolationData{points_len - 2, points_len - 1, 1.0f};
+}
+
+/* Use a spline argument to avoid adding this to the header. */
+template<typename T>
+static void interpolate_to_evaluated_points_impl(const BezierSpline &spline,
+ const blender::VArray<T> &source_data,
+ MutableSpan<T> result_data)
+{
+ Span<float> mappings = spline.evaluated_mappings();
+
+ for (const int i : result_data.index_range()) {
+ BezierSpline::InterpolationData interp = spline.interpolation_data_from_index_factor(
+ mappings[i]);
+
+ const T &value = source_data[interp.control_point_index];
+ const T &next_value = source_data[interp.next_control_point_index];
+
+ result_data[i] = blender::attribute_math::mix2(interp.factor, value, next_value);
+ }
+}
+
+blender::fn::GVArrayPtr BezierSpline::interpolate_to_evaluated_points(
+ const blender::fn::GVArray &source_data) const
+{
+ BLI_assert(source_data.size() == this->size());
+
+ if (source_data.is_single()) {
+ return source_data.shallow_copy();
+ }
+
+ const int eval_size = this->evaluated_points_size();
+ if (eval_size == 1) {
+ return source_data.shallow_copy();
+ }
+
+ blender::fn::GVArrayPtr new_varray;
+ blender::attribute_math::convert_to_static_type(source_data.type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ if constexpr (!std::is_void_v<blender::attribute_math::DefaultMixer<T>>) {
+ Array<T> values(eval_size);
+ interpolate_to_evaluated_points_impl<T>(*this, source_data.typed<T>(), values);
+ new_varray = std::make_unique<blender::fn::GVArray_For_ArrayContainer<Array<T>>>(
+ std::move(values));
+ }
+ });
+
+ return new_varray;
+}
diff --git a/source/blender/blenkernel/intern/spline_nurbs.cc b/source/blender/blenkernel/intern/spline_nurbs.cc
new file mode 100644
index 00000000000..bfb0d652b1a
--- /dev/null
+++ b/source/blender/blenkernel/intern/spline_nurbs.cc
@@ -0,0 +1,444 @@
+/*
+ * 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.
+ */
+
+#include "BLI_array.hh"
+#include "BLI_span.hh"
+#include "BLI_virtual_array.hh"
+
+#include "BKE_attribute_math.hh"
+#include "BKE_spline.hh"
+
+using blender::Array;
+using blender::float3;
+using blender::IndexRange;
+using blender::MutableSpan;
+using blender::Span;
+using blender::fn::GVArray_Typed;
+
+SplinePtr NURBSpline::copy() const
+{
+ return std::make_unique<NURBSpline>(*this);
+}
+
+SplinePtr NURBSpline::copy_settings() const
+{
+ std::unique_ptr<NURBSpline> copy = std::make_unique<NURBSpline>();
+ copy_base_settings(*this, *copy);
+ copy->knots_mode = knots_mode;
+ copy->resolution_ = resolution_;
+ copy->order_ = order_;
+ return copy;
+}
+
+int NURBSpline::size() const
+{
+ const int size = positions_.size();
+ BLI_assert(size == radii_.size());
+ BLI_assert(size == tilts_.size());
+ BLI_assert(size == weights_.size());
+ return size;
+}
+
+int NURBSpline::resolution() const
+{
+ return resolution_;
+}
+
+void NURBSpline::set_resolution(const int value)
+{
+ BLI_assert(value > 0);
+ resolution_ = value;
+ this->mark_cache_invalid();
+}
+
+uint8_t NURBSpline::order() const
+{
+ return order_;
+}
+
+void NURBSpline::set_order(const uint8_t value)
+{
+ BLI_assert(value >= 2 && value <= 6);
+ order_ = value;
+ this->mark_cache_invalid();
+}
+
+/**
+ * \warning Call #reallocate on the spline's attributes after adding all points.
+ */
+void NURBSpline::add_point(const float3 position,
+ const float radius,
+ const float tilt,
+ const float weight)
+{
+ positions_.append(position);
+ radii_.append(radius);
+ tilts_.append(tilt);
+ weights_.append(weight);
+ knots_dirty_ = true;
+ this->mark_cache_invalid();
+}
+
+void NURBSpline::resize(const int size)
+{
+ positions_.resize(size);
+ radii_.resize(size);
+ tilts_.resize(size);
+ weights_.resize(size);
+ this->mark_cache_invalid();
+ attributes.reallocate(size);
+}
+
+MutableSpan<float3> NURBSpline::positions()
+{
+ return positions_;
+}
+Span<float3> NURBSpline::positions() const
+{
+ return positions_;
+}
+MutableSpan<float> NURBSpline::radii()
+{
+ return radii_;
+}
+Span<float> NURBSpline::radii() const
+{
+ return radii_;
+}
+MutableSpan<float> NURBSpline::tilts()
+{
+ return tilts_;
+}
+Span<float> NURBSpline::tilts() const
+{
+ return tilts_;
+}
+MutableSpan<float> NURBSpline::weights()
+{
+ return weights_;
+}
+Span<float> NURBSpline::weights() const
+{
+ return weights_;
+}
+
+void NURBSpline::mark_cache_invalid()
+{
+ basis_cache_dirty_ = true;
+ position_cache_dirty_ = true;
+ tangent_cache_dirty_ = true;
+ normal_cache_dirty_ = true;
+ length_cache_dirty_ = true;
+}
+
+int NURBSpline::evaluated_points_size() const
+{
+ if (!this->check_valid_size_and_order()) {
+ return 0;
+ }
+ return resolution_ * this->segments_size();
+}
+
+void NURBSpline::correct_end_tangents() const
+{
+}
+
+bool NURBSpline::check_valid_size_and_order() const
+{
+ if (this->size() < order_) {
+ return false;
+ }
+
+ if (!is_cyclic_ && this->knots_mode == KnotsMode::Bezier) {
+ if (order_ == 4) {
+ if (this->size() < 5) {
+ return false;
+ }
+ }
+ else if (order_ != 3) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+int NURBSpline::knots_size() const
+{
+ const int size = this->size() + order_;
+ return is_cyclic_ ? size + order_ - 1 : size;
+}
+
+void NURBSpline::calculate_knots() const
+{
+ const KnotsMode mode = this->knots_mode;
+ const int length = this->size();
+ const int order = order_;
+
+ knots_.resize(this->knots_size());
+
+ MutableSpan<float> knots = knots_;
+
+ if (mode == NURBSpline::KnotsMode::Normal || is_cyclic_) {
+ for (const int i : knots.index_range()) {
+ knots[i] = static_cast<float>(i);
+ }
+ }
+ else if (mode == NURBSpline::KnotsMode::EndPoint) {
+ float k = 0.0f;
+ for (const int i : IndexRange(1, knots.size())) {
+ knots[i - 1] = k;
+ if (i >= order && i <= length) {
+ k += 1.0f;
+ }
+ }
+ }
+ else if (mode == NURBSpline::KnotsMode::Bezier) {
+ BLI_assert(ELEM(order, 3, 4));
+ if (order == 3) {
+ float k = 0.6f;
+ for (const int i : knots.index_range()) {
+ if (i >= order && i <= length) {
+ k += 0.5f;
+ }
+ knots[i] = std::floor(k);
+ }
+ }
+ else {
+ float k = 0.34f;
+ for (const int i : knots.index_range()) {
+ knots[i] = std::floor(k);
+ k += 1.0f / 3.0f;
+ }
+ }
+ }
+
+ if (is_cyclic_) {
+ const int b = length + order - 1;
+ if (order > 2) {
+ for (const int i : IndexRange(1, order - 2)) {
+ if (knots[b] != knots[b - i]) {
+ if (i == order - 1) {
+ knots[length + order - 2] += 1.0f;
+ break;
+ }
+ }
+ }
+ }
+
+ int c = order;
+ for (int i = b; i < this->knots_size(); i++) {
+ knots[i] = knots[i - 1] + (knots[c] - knots[c - 1]);
+ c--;
+ }
+ }
+}
+
+Span<float> NURBSpline::knots() const
+{
+ if (!knots_dirty_) {
+ BLI_assert(knots_.size() == this->size() + order_);
+ return knots_;
+ }
+
+ std::lock_guard lock{knots_mutex_};
+ if (!knots_dirty_) {
+ BLI_assert(knots_.size() == this->size() + order_);
+ return knots_;
+ }
+
+ this->calculate_knots();
+
+ knots_dirty_ = false;
+
+ return knots_;
+}
+
+static void calculate_basis_for_point(const float parameter,
+ const int points_len,
+ const int order,
+ Span<float> knots,
+ MutableSpan<float> basis_buffer,
+ NURBSpline::BasisCache &basis_cache)
+{
+ /* Clamp parameter due to floating point inaccuracy. */
+ const float t = std::clamp(parameter, knots[0], knots[points_len + order - 1]);
+
+ int start = 0;
+ int end = 0;
+ for (const int i : IndexRange(points_len + order - 1)) {
+ const bool knots_equal = knots[i] == knots[i + 1];
+ if (knots_equal || t < knots[i] || t > knots[i + 1]) {
+ basis_buffer[i] = 0.0f;
+ continue;
+ }
+
+ basis_buffer[i] = 1.0f;
+ start = std::max(i - order - 1, 0);
+ end = i;
+ basis_buffer.slice(i + 1, points_len + order - 1 - i).fill(0.0f);
+ break;
+ }
+ basis_buffer[points_len + order - 1] = 0.0f;
+
+ for (const int i_order : IndexRange(2, order - 1)) {
+ if (end + i_order >= points_len + order) {
+ end = points_len + order - 1 - i_order;
+ }
+ for (const int i : IndexRange(start, end - start + 1)) {
+ float new_basis = 0.0f;
+ if (basis_buffer[i] != 0.0f) {
+ new_basis += ((t - knots[i]) * basis_buffer[i]) / (knots[i + i_order - 1] - knots[i]);
+ }
+
+ if (basis_buffer[i + 1] != 0.0f) {
+ new_basis += ((knots[i + i_order] - t) * basis_buffer[i + 1]) /
+ (knots[i + i_order] - knots[i + 1]);
+ }
+
+ basis_buffer[i] = new_basis;
+ }
+ }
+
+ /* Shrink the range of calculated values to avoid storing unnecessary zeros. */
+ while (basis_buffer[start] == 0.0f && start < end) {
+ start++;
+ }
+ while (basis_buffer[end] == 0.0f && end > start) {
+ end--;
+ }
+
+ basis_cache.weights.clear();
+ basis_cache.weights.extend(basis_buffer.slice(start, end - start + 1));
+ basis_cache.start_index = start;
+}
+
+void NURBSpline::calculate_basis_cache() const
+{
+ if (!basis_cache_dirty_) {
+ return;
+ }
+
+ std::lock_guard lock{basis_cache_mutex_};
+ if (!basis_cache_dirty_) {
+ return;
+ }
+
+ const int points_len = this->size();
+ const int eval_size = this->evaluated_points_size();
+ BLI_assert(this->evaluated_edges_size() > 0);
+ basis_cache_.resize(eval_size);
+
+ const int order = this->order();
+ Span<float> control_weights = this->weights();
+ Span<float> knots = this->knots();
+
+ MutableSpan<BasisCache> basis_cache(basis_cache_);
+
+ /* This buffer is reused by each basis calculation to store temporary values.
+ * Theoretically it could be optimized away in the future. */
+ Array<float> basis_buffer(this->knots_size());
+
+ const float start = knots[order - 1];
+ const float end = is_cyclic_ ? knots[points_len + order - 1] : knots[points_len];
+ const float step = (end - start) / this->evaluated_edges_size();
+ float parameter = start;
+ for (const int i : IndexRange(eval_size)) {
+ BasisCache &basis = basis_cache[i];
+ calculate_basis_for_point(
+ parameter, points_len + (is_cyclic_ ? order - 1 : 0), order, knots, basis_buffer, basis);
+ BLI_assert(basis.weights.size() <= order);
+
+ for (const int j : basis.weights.index_range()) {
+ const int point_index = (basis.start_index + j) % points_len;
+ basis.weights[j] *= control_weights[point_index];
+ }
+
+ parameter += step;
+ }
+
+ basis_cache_dirty_ = false;
+}
+
+template<typename T>
+void interpolate_to_evaluated_points_impl(Span<NURBSpline::BasisCache> weights,
+ const blender::VArray<T> &source_data,
+ MutableSpan<T> result_data)
+{
+ const int points_len = source_data.size();
+ BLI_assert(result_data.size() == weights.size());
+ blender::attribute_math::DefaultMixer<T> mixer(result_data);
+
+ for (const int i : result_data.index_range()) {
+ Span<float> point_weights = weights[i].weights;
+ const int start_index = weights[i].start_index;
+
+ for (const int j : point_weights.index_range()) {
+ const int point_index = (start_index + j) % points_len;
+ mixer.mix_in(i, source_data[point_index], point_weights[j]);
+ }
+ }
+
+ mixer.finalize();
+}
+
+blender::fn::GVArrayPtr NURBSpline::interpolate_to_evaluated_points(
+ const blender::fn::GVArray &source_data) const
+{
+ BLI_assert(source_data.size() == this->size());
+
+ if (source_data.is_single()) {
+ return source_data.shallow_copy();
+ }
+
+ this->calculate_basis_cache();
+ Span<BasisCache> weights(basis_cache_);
+
+ blender::fn::GVArrayPtr new_varray;
+ blender::attribute_math::convert_to_static_type(source_data.type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ if constexpr (!std::is_void_v<blender::attribute_math::DefaultMixer<T>>) {
+ Array<T> values(this->evaluated_points_size());
+ interpolate_to_evaluated_points_impl<T>(weights, source_data.typed<T>(), values);
+ new_varray = std::make_unique<blender::fn::GVArray_For_ArrayContainer<Array<T>>>(
+ std::move(values));
+ }
+ });
+
+ return new_varray;
+}
+
+Span<float3> NURBSpline::evaluated_positions() const
+{
+ if (!position_cache_dirty_) {
+ return evaluated_position_cache_;
+ }
+
+ std::lock_guard lock{position_cache_mutex_};
+ if (!position_cache_dirty_) {
+ return evaluated_position_cache_;
+ }
+
+ const int eval_size = this->evaluated_points_size();
+ evaluated_position_cache_.resize(eval_size);
+
+ /* TODO: Avoid copying the evaluated data from the temporary array. */
+ GVArray_Typed<float3> evaluated = Spline::interpolate_to_evaluated_points(positions_.as_span());
+ evaluated->materialize(evaluated_position_cache_);
+
+ position_cache_dirty_ = false;
+ return evaluated_position_cache_;
+}
diff --git a/source/blender/blenkernel/intern/spline_poly.cc b/source/blender/blenkernel/intern/spline_poly.cc
new file mode 100644
index 00000000000..5f8e81d5ad0
--- /dev/null
+++ b/source/blender/blenkernel/intern/spline_poly.cc
@@ -0,0 +1,124 @@
+/*
+ * 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.
+ */
+
+#include "BLI_span.hh"
+#include "BLI_virtual_array.hh"
+
+#include "BKE_spline.hh"
+
+using blender::float3;
+using blender::MutableSpan;
+using blender::Span;
+
+SplinePtr PolySpline::copy() const
+{
+ return std::make_unique<PolySpline>(*this);
+}
+
+SplinePtr PolySpline::copy_settings() const
+{
+ std::unique_ptr<PolySpline> copy = std::make_unique<PolySpline>();
+ copy_base_settings(*this, *copy);
+ return copy;
+}
+
+int PolySpline::size() const
+{
+ const int size = positions_.size();
+ BLI_assert(size == radii_.size());
+ BLI_assert(size == tilts_.size());
+ return size;
+}
+
+/**
+ * \warning Call #reallocate on the spline's attributes after adding all points.
+ */
+void PolySpline::add_point(const float3 position, const float radius, const float tilt)
+{
+ positions_.append(position);
+ radii_.append(radius);
+ tilts_.append(tilt);
+ this->mark_cache_invalid();
+}
+
+void PolySpline::resize(const int size)
+{
+ positions_.resize(size);
+ radii_.resize(size);
+ tilts_.resize(size);
+ this->mark_cache_invalid();
+ attributes.reallocate(size);
+}
+
+MutableSpan<float3> PolySpline::positions()
+{
+ return positions_;
+}
+Span<float3> PolySpline::positions() const
+{
+ return positions_;
+}
+MutableSpan<float> PolySpline::radii()
+{
+ return radii_;
+}
+Span<float> PolySpline::radii() const
+{
+ return radii_;
+}
+MutableSpan<float> PolySpline::tilts()
+{
+ return tilts_;
+}
+Span<float> PolySpline::tilts() const
+{
+ return tilts_;
+}
+
+void PolySpline::mark_cache_invalid()
+{
+ tangent_cache_dirty_ = true;
+ normal_cache_dirty_ = true;
+ length_cache_dirty_ = true;
+}
+
+int PolySpline::evaluated_points_size() const
+{
+ return this->size();
+}
+
+void PolySpline::correct_end_tangents() const
+{
+}
+
+Span<float3> PolySpline::evaluated_positions() const
+{
+ return this->positions();
+}
+
+/**
+ * Poly spline interpolation from control points to evaluated points is a special case, since
+ * the result data is the same as the input data. This function returns a GVArray that points to
+ * the original data. Therefore the lifetime of the returned virtual array must not be longer than
+ * the source data.
+ */
+blender::fn::GVArrayPtr PolySpline::interpolate_to_evaluated_points(
+ const blender::fn::GVArray &source_data) const
+{
+ BLI_assert(source_data.size() == this->size());
+
+ return source_data.shallow_copy();
+}
diff --git a/source/blender/blenkernel/intern/subdiv_ccg.c b/source/blender/blenkernel/intern/subdiv_ccg.c
index a59f9e0c633..5f732ba91ab 100644
--- a/source/blender/blenkernel/intern/subdiv_ccg.c
+++ b/source/blender/blenkernel/intern/subdiv_ccg.c
@@ -28,12 +28,14 @@
#include "MEM_guardedalloc.h"
+#include "BLI_ghash.h"
#include "BLI_math_bits.h"
#include "BLI_math_vector.h"
#include "BLI_task.h"
#include "BKE_DerivedMesh.h"
#include "BKE_ccg.h"
+#include "BKE_global.h"
#include "BKE_mesh.h"
#include "BKE_subdiv.h"
#include "BKE_subdiv_eval.h"
@@ -50,6 +52,11 @@ static void subdiv_ccg_average_inner_face_grids(SubdivCCG *subdiv_ccg,
CCGKey *key,
SubdivCCGFace *face);
+void subdiv_ccg_average_faces_boundaries_and_corners(SubdivCCG *subdiv_ccg,
+ CCGKey *key,
+ struct CCGFace **effected_faces,
+ int num_effected_faces);
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -889,11 +896,12 @@ void BKE_subdiv_ccg_update_normals(SubdivCCG *subdiv_ccg,
return;
}
subdiv_ccg_recalc_modified_inner_grid_normals(subdiv_ccg, effected_faces, num_effected_faces);
- /* TODO(sergey): Only average elements which are adjacent to modified
- * faces. */
+
CCGKey key;
BKE_subdiv_ccg_key_top_level(&key, subdiv_ccg);
- subdiv_ccg_average_all_boundaries_and_corners(subdiv_ccg, &key);
+
+ subdiv_ccg_average_faces_boundaries_and_corners(
+ subdiv_ccg, &key, effected_faces, num_effected_faces);
}
/** \} */
@@ -1032,6 +1040,9 @@ static void subdiv_ccg_average_inner_grids_task(void *__restrict userdata_v,
typedef struct AverageGridsBoundariesData {
SubdivCCG *subdiv_ccg;
CCGKey *key;
+
+ /* Optional lookup table. Maps task index to index in `subdiv_ccg->adjacent_vertices`. */
+ int *adjacent_edge_index_map;
} AverageGridsBoundariesData;
typedef struct AverageGridsBoundariesTLSData {
@@ -1079,10 +1090,14 @@ static void subdiv_ccg_average_grids_boundary(SubdivCCG *subdiv_ccg,
}
static void subdiv_ccg_average_grids_boundaries_task(void *__restrict userdata_v,
- const int adjacent_edge_index,
+ const int n,
const TaskParallelTLS *__restrict tls_v)
{
AverageGridsBoundariesData *data = userdata_v;
+ const int adjacent_edge_index = data->adjacent_edge_index_map ?
+ data->adjacent_edge_index_map[n] :
+ n;
+
AverageGridsBoundariesTLSData *tls = tls_v->userdata_chunk;
SubdivCCG *subdiv_ccg = data->subdiv_ccg;
CCGKey *key = data->key;
@@ -1100,6 +1115,9 @@ static void subdiv_ccg_average_grids_boundaries_free(const void *__restrict UNUS
typedef struct AverageGridsCornerData {
SubdivCCG *subdiv_ccg;
CCGKey *key;
+
+ /* Optional lookup table. Maps task range index to index in subdiv_ccg->adjacent_vertices*/
+ int *adjacent_vert_index_map;
} AverageGridsCornerData;
static void subdiv_ccg_average_grids_corners(SubdivCCG *subdiv_ccg,
@@ -1128,49 +1146,63 @@ static void subdiv_ccg_average_grids_corners(SubdivCCG *subdiv_ccg,
}
static void subdiv_ccg_average_grids_corners_task(void *__restrict userdata_v,
- const int adjacent_vertex_index,
+ const int n,
const TaskParallelTLS *__restrict UNUSED(tls_v))
{
AverageGridsCornerData *data = userdata_v;
+ const int adjacent_vertex_index = data->adjacent_vert_index_map ?
+ data->adjacent_vert_index_map[n] :
+ n;
SubdivCCG *subdiv_ccg = data->subdiv_ccg;
CCGKey *key = data->key;
SubdivCCGAdjacentVertex *adjacent_vertex = &subdiv_ccg->adjacent_vertices[adjacent_vertex_index];
subdiv_ccg_average_grids_corners(subdiv_ccg, key, adjacent_vertex);
}
-static void subdiv_ccg_average_all_boundaries(SubdivCCG *subdiv_ccg, CCGKey *key)
+static void subdiv_ccg_average_boundaries(SubdivCCG *subdiv_ccg,
+ CCGKey *key,
+ int *adjacent_edge_index_map,
+ int num_adjacent_edges)
{
TaskParallelSettings parallel_range_settings;
BLI_parallel_range_settings_defaults(&parallel_range_settings);
AverageGridsBoundariesData boundaries_data = {
- .subdiv_ccg = subdiv_ccg,
- .key = key,
- };
+ .subdiv_ccg = subdiv_ccg, .key = key, .adjacent_edge_index_map = adjacent_edge_index_map};
AverageGridsBoundariesTLSData tls_data = {NULL};
parallel_range_settings.userdata_chunk = &tls_data;
parallel_range_settings.userdata_chunk_size = sizeof(tls_data);
parallel_range_settings.func_free = subdiv_ccg_average_grids_boundaries_free;
BLI_task_parallel_range(0,
- subdiv_ccg->num_adjacent_edges,
+ num_adjacent_edges,
&boundaries_data,
subdiv_ccg_average_grids_boundaries_task,
&parallel_range_settings);
}
-static void subdiv_ccg_average_all_corners(SubdivCCG *subdiv_ccg, CCGKey *key)
+static void subdiv_ccg_average_all_boundaries(SubdivCCG *subdiv_ccg, CCGKey *key)
+{
+ subdiv_ccg_average_boundaries(subdiv_ccg, key, NULL, subdiv_ccg->num_adjacent_edges);
+}
+
+static void subdiv_ccg_average_corners(SubdivCCG *subdiv_ccg,
+ CCGKey *key,
+ int *adjacent_vert_index_map,
+ int num_adjacent_vertices)
{
TaskParallelSettings parallel_range_settings;
BLI_parallel_range_settings_defaults(&parallel_range_settings);
AverageGridsCornerData corner_data = {
- .subdiv_ccg = subdiv_ccg,
- .key = key,
- };
+ .subdiv_ccg = subdiv_ccg, .key = key, .adjacent_vert_index_map = adjacent_vert_index_map};
BLI_task_parallel_range(0,
- subdiv_ccg->num_adjacent_vertices,
+ num_adjacent_vertices,
&corner_data,
subdiv_ccg_average_grids_corners_task,
&parallel_range_settings);
}
+static void subdiv_ccg_average_all_corners(SubdivCCG *subdiv_ccg, CCGKey *key)
+{
+ subdiv_ccg_average_corners(subdiv_ccg, key, NULL, subdiv_ccg->num_adjacent_vertices);
+}
static void subdiv_ccg_average_all_boundaries_and_corners(SubdivCCG *subdiv_ccg, CCGKey *key)
{
@@ -1198,6 +1230,98 @@ void BKE_subdiv_ccg_average_grids(SubdivCCG *subdiv_ccg)
subdiv_ccg_average_all_boundaries_and_corners(subdiv_ccg, &key);
}
+static void subdiv_ccg_affected_face_adjacency(SubdivCCG *subdiv_ccg,
+ struct CCGFace **effected_faces,
+ int num_effected_faces,
+ GSet *r_adjacent_vertices,
+ GSet *r_adjacent_edges)
+{
+ Subdiv *subdiv = subdiv_ccg->subdiv;
+ OpenSubdiv_TopologyRefiner *topology_refiner = subdiv->topology_refiner;
+
+ StaticOrHeapIntStorage face_vertices_storage;
+ StaticOrHeapIntStorage face_edges_storage;
+
+ static_or_heap_storage_init(&face_vertices_storage);
+ static_or_heap_storage_init(&face_edges_storage);
+
+ for (int i = 0; i < num_effected_faces; i++) {
+ SubdivCCGFace *face = (SubdivCCGFace *)effected_faces[i];
+ int face_index = face - subdiv_ccg->faces;
+ const int num_face_grids = face->num_grids;
+ const int num_face_edges = num_face_grids;
+ int *face_vertices = static_or_heap_storage_get(&face_vertices_storage, num_face_edges);
+ topology_refiner->getFaceVertices(topology_refiner, face_index, face_vertices);
+
+ /* Note that order of edges is same as order of MLoops, which also
+ * means it's the same as order of grids. */
+ int *face_edges = static_or_heap_storage_get(&face_edges_storage, num_face_edges);
+ topology_refiner->getFaceEdges(topology_refiner, face_index, face_edges);
+ for (int corner = 0; corner < num_face_edges; corner++) {
+ const int vertex_index = face_vertices[corner];
+ const int edge_index = face_edges[corner];
+
+ int edge_vertices[2];
+ topology_refiner->getEdgeVertices(topology_refiner, edge_index, edge_vertices);
+
+ SubdivCCGAdjacentEdge *adjacent_edge = &subdiv_ccg->adjacent_edges[edge_index];
+ BLI_gset_add(r_adjacent_edges, adjacent_edge);
+
+ SubdivCCGAdjacentVertex *adjacent_vertex = &subdiv_ccg->adjacent_vertices[vertex_index];
+ BLI_gset_add(r_adjacent_vertices, adjacent_vertex);
+ }
+ }
+
+ static_or_heap_storage_free(&face_vertices_storage);
+ static_or_heap_storage_free(&face_edges_storage);
+}
+
+void subdiv_ccg_average_faces_boundaries_and_corners(SubdivCCG *subdiv_ccg,
+ CCGKey *key,
+ struct CCGFace **effected_faces,
+ int num_effected_faces)
+{
+ GSet *adjacent_vertices = BLI_gset_ptr_new(__func__);
+ GSet *adjacent_edges = BLI_gset_ptr_new(__func__);
+ GSetIterator gi;
+
+ subdiv_ccg_affected_face_adjacency(
+ subdiv_ccg, effected_faces, num_effected_faces, adjacent_vertices, adjacent_edges);
+
+ int *adjacent_vertex_index_map;
+ int *adjacent_edge_index_map;
+
+ StaticOrHeapIntStorage index_heap;
+ static_or_heap_storage_init(&index_heap);
+
+ int i = 0;
+
+ /* Average boundaries. */
+
+ adjacent_edge_index_map = static_or_heap_storage_get(&index_heap, BLI_gset_len(adjacent_edges));
+ GSET_ITER_INDEX (gi, adjacent_edges, i) {
+ SubdivCCGAdjacentEdge *adjacent_edge = BLI_gsetIterator_getKey(&gi);
+ adjacent_edge_index_map[i] = adjacent_edge - subdiv_ccg->adjacent_edges;
+ }
+ subdiv_ccg_average_boundaries(
+ subdiv_ccg, key, adjacent_edge_index_map, BLI_gset_len(adjacent_edges));
+
+ /* Average corners. */
+
+ adjacent_vertex_index_map = static_or_heap_storage_get(&index_heap,
+ BLI_gset_len(adjacent_vertices));
+ GSET_ITER_INDEX (gi, adjacent_vertices, i) {
+ SubdivCCGAdjacentVertex *adjacent_vertex = BLI_gsetIterator_getKey(&gi);
+ adjacent_vertex_index_map[i] = adjacent_vertex - subdiv_ccg->adjacent_vertices;
+ }
+ subdiv_ccg_average_corners(
+ subdiv_ccg, key, adjacent_vertex_index_map, BLI_gset_len(adjacent_vertices));
+
+ BLI_gset_free(adjacent_vertices, NULL);
+ BLI_gset_free(adjacent_edges, NULL);
+ static_or_heap_storage_free(&index_heap);
+}
+
typedef struct StitchFacesInnerGridsData {
SubdivCCG *subdiv_ccg;
CCGKey *key;
diff --git a/source/blender/blenkernel/intern/subsurf_ccg.c b/source/blender/blenkernel/intern/subsurf_ccg.c
index a28136f8527..23eccbfba9b 100644
--- a/source/blender/blenkernel/intern/subsurf_ccg.c
+++ b/source/blender/blenkernel/intern/subsurf_ccg.c
@@ -1879,12 +1879,11 @@ static const MeshElemMap *ccgDM_getPolyMap(Object *ob, DerivedMesh *dm)
/* WARNING! *MUST* be called in an 'loops_cache_rwlock' protected thread context! */
static void ccgDM_recalcLoopTri(DerivedMesh *dm)
{
- MLoopTri *mlooptri = dm->looptris.array;
const int tottri = dm->numPolyData * 2;
int i, poly_index;
DM_ensure_looptri_data(dm);
- mlooptri = dm->looptris.array_wip;
+ MLoopTri *mlooptri = dm->looptris.array_wip;
BLI_assert(tottri == 0 || mlooptri != NULL);
BLI_assert(poly_to_tri_count(dm->numPolyData, dm->numLoopData) == dm->looptris.num);
diff --git a/source/blender/blenkernel/intern/text.c b/source/blender/blenkernel/intern/text.c
index 43d587861a5..27f5593c2ca 100644
--- a/source/blender/blenkernel/intern/text.c
+++ b/source/blender/blenkernel/intern/text.c
@@ -171,6 +171,9 @@ static void text_free_data(ID *id)
static void text_blend_write(BlendWriter *writer, ID *id, const void *id_address)
{
+ if (id->us < 1 && !BLO_write_is_undo(writer)) {
+ return;
+ }
Text *text = (Text *)id;
/* Note: we are clearing local temp data here, *not* the flag in the actual 'real' ID. */
@@ -231,8 +234,6 @@ static void text_blend_read_data(BlendDataReader *reader, ID *id)
}
text->flags = (text->flags) & ~TXT_ISEXT;
-
- id_us_ensure_real(&text->id);
}
IDTypeInfo IDType_ID_TXT = {
@@ -293,8 +294,10 @@ Text *BKE_text_add(Main *bmain, const char *name)
Text *ta;
ta = BKE_id_new(bmain, ID_TXT, name);
- /* Texts always have 'real' user (see also read code). */
- id_us_ensure_real(&ta->id);
+ /* Texts have no users by default... Set the fake user flag to ensure that this text block
+ * doesn't get deleted by default when cleaning up data blocks. */
+ id_us_min(&ta->id);
+ id_fake_user_set(&ta->id);
return ta;
}
@@ -468,7 +471,7 @@ bool BKE_text_reload(Text *text)
* \param is_internal: If \a true, this text data-block only exists in memory,
* not as a file on disk.
*
- * \note text data-blocks have no user by default, only the 'real user' flag.
+ * \note text data-blocks have no real user but have 'fake user' enabled by default
*/
Text *BKE_text_load_ex(Main *bmain, const char *file, const char *relpath, const bool is_internal)
{
@@ -489,9 +492,8 @@ Text *BKE_text_load_ex(Main *bmain, const char *file, const char *relpath, const
}
ta = BKE_libblock_alloc(bmain, ID_TXT, BLI_path_basename(filepath_abs), 0);
- /* Texts have no user by default... Only the 'real' user flag. */
- id_us_ensure_real(&ta->id);
id_us_min(&ta->id);
+ id_fake_user_set(&ta->id);
BLI_listbase_clear(&ta->lines);
ta->curl = ta->sell = NULL;
@@ -523,7 +525,8 @@ Text *BKE_text_load_ex(Main *bmain, const char *file, const char *relpath, const
return ta;
}
-/** Load a text file.
+/**
+ * Load a text file.
*
* \note Text data-blocks have no user by default, only the 'real user' flag.
*/
diff --git a/source/blender/blenkernel/intern/tracking.c b/source/blender/blenkernel/intern/tracking.c
index d124922acd1..f3d6bc4a6e3 100644
--- a/source/blender/blenkernel/intern/tracking.c
+++ b/source/blender/blenkernel/intern/tracking.c
@@ -3251,6 +3251,11 @@ static void tracking_dopesheet_calc_coverage(MovieTracking *tracking)
end_frame = max_ii(end_frame, track->markers[track->markersnr - 1].framenr);
}
+ if (start_frame > end_frame) {
+ /* There are no markers at all, nothing to calculate coverage from. */
+ return;
+ }
+
frames = end_frame - start_frame + 1;
/* this is a per-frame counter of markers (how many markers belongs to the same frame) */
diff --git a/source/blender/blenkernel/intern/unit.c b/source/blender/blenkernel/intern/unit.c
index 9ae1c754846..5cf76bb6452 100644
--- a/source/blender/blenkernel/intern/unit.c
+++ b/source/blender/blenkernel/intern/unit.c
@@ -358,6 +358,7 @@ static const struct bUnitCollection *bUnitSystems[][B_UNIT_TYPE_TOT] = {
NULL,
&buNaturalRotCollection,
&buNaturalTimeCollection,
+ &buNaturalTimeCollection,
NULL,
NULL,
NULL,
@@ -371,6 +372,7 @@ static const struct bUnitCollection *bUnitSystems[][B_UNIT_TYPE_TOT] = {
&buMetricMassCollection,
&buNaturalRotCollection,
&buNaturalTimeCollection,
+ &buNaturalTimeCollection,
&buMetricVelCollection,
&buMetricAclCollection,
&buCameraLenCollection,
@@ -384,12 +386,13 @@ static const struct bUnitCollection *bUnitSystems[][B_UNIT_TYPE_TOT] = {
&buImperialMassCollection,
&buNaturalRotCollection,
&buNaturalTimeCollection,
+ &buNaturalTimeCollection,
&buImperialVelCollection,
&buImperialAclCollection,
&buCameraLenCollection,
&buPowerCollection,
&buImperialTempCollection},
- {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
+ {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
};
static const bUnitCollection *unit_get_system(int system, int type)
@@ -943,7 +946,7 @@ static int unit_scale_str(char *str,
/* Add the addition sign, the bias, and the close parenthesis after the value. */
int value_end_ofs = find_end_of_value_chars(str, len_max, prev_op_ofs + 2);
- int len_bias_num = BLI_snprintf(str_tmp, TEMP_STR_SIZE, "+%.9g)", unit->bias);
+ int len_bias_num = BLI_snprintf_rlen(str_tmp, TEMP_STR_SIZE, "+%.9g)", unit->bias);
if (value_end_ofs + len_bias_num < len_max) {
memmove(str + value_end_ofs + len_bias_num, str + value_end_ofs, len - value_end_ofs + 1);
memcpy(str + value_end_ofs, str_tmp, len_bias_num);
@@ -957,7 +960,8 @@ static int unit_scale_str(char *str,
int len_move = (len - (found_ofs + len_name)) + 1; /* 1+ to copy the string terminator. */
/* "#" Removed later */
- int len_num = BLI_snprintf(str_tmp, TEMP_STR_SIZE, "*%.9g" SEP_STR, unit->scalar / scale_pref);
+ int len_num = BLI_snprintf_rlen(
+ str_tmp, TEMP_STR_SIZE, "*%.9g" SEP_STR, unit->scalar / scale_pref);
if (len_num > len_max) {
len_num = len_max;
diff --git a/source/blender/blenkernel/intern/volume.cc b/source/blender/blenkernel/intern/volume.cc
index 9b5231f7d6f..c0ce57818d1 100644
--- a/source/blender/blenkernel/intern/volume.cc
+++ b/source/blender/blenkernel/intern/volume.cc
@@ -28,7 +28,10 @@
#include "BLI_compiler_compat.h"
#include "BLI_fileops.h"
+#include "BLI_float3.hh"
+#include "BLI_float4x4.hh"
#include "BLI_ghash.h"
+#include "BLI_index_range.hh"
#include "BLI_map.hh"
#include "BLI_math.h"
#include "BLI_path_util.h"
@@ -36,6 +39,7 @@
#include "BLI_utildefines.h"
#include "BKE_anim_data.h"
+#include "BKE_geometry_set.hh"
#include "BKE_global.h"
#include "BKE_idtype.h"
#include "BKE_lib_id.h"
@@ -63,6 +67,10 @@ static CLG_LogRef LOG = {"bke.volume"};
#define VOLUME_FRAME_NONE INT_MAX
+using blender::float3;
+using blender::float4x4;
+using blender::IndexRange;
+
#ifdef WITH_OPENVDB
# include <atomic>
# include <list>
@@ -144,14 +152,14 @@ static struct VolumeFileCache {
blender::Map<int, openvdb::GridBase::Ptr> simplified_grids;
/* Has the grid tree been loaded? */
- bool is_loaded;
+ mutable bool is_loaded;
/* Error message if an error occurred while loading. */
std::string error_msg;
/* User counting. */
int num_metadata_users;
int num_tree_users;
/* Mutex for on-demand reading of tree. */
- std::mutex mutex;
+ mutable std::mutex mutex;
};
struct EntryHasher {
@@ -171,10 +179,6 @@ static struct VolumeFileCache {
};
/* Cache */
- VolumeFileCache()
- {
- }
-
~VolumeFileCache()
{
BLI_assert(cache.empty());
@@ -293,7 +297,7 @@ struct VolumeGrid {
}
}
- void load(const char *volume_name, const char *filepath)
+ void load(const char *volume_name, const char *filepath) const
{
/* If already loaded or not file-backed, nothing to do. */
if (is_loaded || entry == nullptr) {
@@ -335,7 +339,7 @@ struct VolumeGrid {
is_loaded = true;
}
- void unload(const char *volume_name)
+ void unload(const char *volume_name) const
{
/* Not loaded or not file-backed, nothing to do. */
if (!is_loaded || entry == nullptr) {
@@ -436,10 +440,14 @@ struct VolumeGrid {
int simplify_level = 0;
/* OpenVDB grid if it's not shared through the file cache. */
openvdb::GridBase::Ptr local_grid;
- /* Indicates if the tree has been loaded for this grid. Note that vdb.tree()
+ /**
+ * Indicates if the tree has been loaded for this grid. Note that vdb.tree()
* may actually be loaded by another user while this is false. But only after
- * calling load() and is_loaded changes to true is it safe to access. */
- bool is_loaded;
+ * calling load() and is_loaded changes to true is it safe to access.
+ *
+ * Const write access to this must be protected by `entry->mutex`.
+ */
+ mutable bool is_loaded;
};
/* Volume Grid Vector
@@ -472,14 +480,15 @@ struct VolumeGridVector : public std::list<VolumeGrid> {
metadata.reset();
}
+ /* Mutex for file loading of grids list. Const write access to the fields after this must be
+ * protected by locking with this mutex. */
+ mutable std::mutex mutex;
/* Absolute file path that grids have been loaded from. */
char filepath[FILE_MAX];
/* File loading error message. */
std::string error_msg;
/* File Metadata. */
openvdb::MetaMap::Ptr metadata;
- /* Mutex for file loading of grids list. */
- std::mutex mutex;
};
#endif
@@ -766,10 +775,10 @@ bool BKE_volume_is_loaded(const Volume *volume)
#endif
}
-bool BKE_volume_load(Volume *volume, Main *bmain)
+bool BKE_volume_load(const Volume *volume, const Main *bmain)
{
#ifdef WITH_OPENVDB
- VolumeGridVector &grids = *volume->runtime.grids;
+ const VolumeGridVector &const_grids = *volume->runtime.grids;
if (volume->runtime.frame == VOLUME_FRAME_NONE) {
/* Skip loading this frame, outside of sequence range. */
@@ -777,15 +786,19 @@ bool BKE_volume_load(Volume *volume, Main *bmain)
}
if (BKE_volume_is_loaded(volume)) {
- return grids.error_msg.empty();
+ return const_grids.error_msg.empty();
}
/* Double-checked lock. */
- std::lock_guard<std::mutex> lock(grids.mutex);
+ std::lock_guard<std::mutex> lock(const_grids.mutex);
if (BKE_volume_is_loaded(volume)) {
- return grids.error_msg.empty();
+ return const_grids.error_msg.empty();
}
+ /* Guarded by the lock, we can continue to access the grid vector,
+ * adding error messages or a new grid, etc. */
+ VolumeGridVector &grids = const_cast<VolumeGridVector &>(const_grids);
+
/* Get absolute file path at current frame. */
const char *volume_name = volume->id.name + 2;
char filepath[FILE_MAX];
@@ -850,7 +863,10 @@ void BKE_volume_unload(Volume *volume)
/* File Save */
-bool BKE_volume_save(Volume *volume, Main *bmain, ReportList *reports, const char *filepath)
+bool BKE_volume_save(const Volume *volume,
+ const Main *bmain,
+ ReportList *reports,
+ const char *filepath)
{
#ifdef WITH_OPENVDB
if (!BKE_volume_load(volume, bmain)) {
@@ -882,6 +898,32 @@ bool BKE_volume_save(Volume *volume, Main *bmain, ReportList *reports, const cha
#endif
}
+bool BKE_volume_min_max(const Volume *volume, float3 &r_min, float3 &r_max)
+{
+ bool have_minmax = false;
+#ifdef WITH_OPENVDB
+ /* TODO: if we know the volume is going to be displayed, it may be good to
+ * load it as part of dependency graph evaluation for better threading. We
+ * could also share the bounding box computation in the global volume cache. */
+ if (BKE_volume_load(const_cast<Volume *>(volume), G.main)) {
+ for (const int i : IndexRange(BKE_volume_num_grids(volume))) {
+ const VolumeGrid *volume_grid = BKE_volume_grid_get_for_read(volume, i);
+ openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid);
+ float3 grid_min;
+ float3 grid_max;
+ if (BKE_volume_grid_bounds(grid, grid_min, grid_max)) {
+ DO_MIN(grid_min, r_min);
+ DO_MAX(grid_max, r_max);
+ have_minmax = true;
+ }
+ }
+ }
+#else
+ UNUSED_VARS(volume, r_min, r_max);
+#endif
+ return have_minmax;
+}
+
BoundBox *BKE_volume_boundbox_get(Object *ob)
{
BLI_assert(ob->type == OB_VOLUME);
@@ -891,41 +933,20 @@ BoundBox *BKE_volume_boundbox_get(Object *ob)
}
if (ob->runtime.bb == nullptr) {
- Volume *volume = (Volume *)ob->data;
-
- ob->runtime.bb = (BoundBox *)MEM_callocN(sizeof(BoundBox), "volume boundbox");
-
- float min[3], max[3];
- bool have_minmax = false;
- INIT_MINMAX(min, max);
-
- /* TODO: if we know the volume is going to be displayed, it may be good to
- * load it as part of dependency graph evaluation for better threading. We
- * could also share the bounding box computation in the global volume cache. */
- if (BKE_volume_load(volume, G.main)) {
- const int num_grids = BKE_volume_num_grids(volume);
-
- for (int i = 0; i < num_grids; i++) {
- VolumeGrid *grid = BKE_volume_grid_get(volume, i);
- float grid_min[3], grid_max[3];
-
- BKE_volume_grid_load(volume, grid);
- if (BKE_volume_grid_bounds(grid, grid_min, grid_max)) {
- DO_MIN(grid_min, min);
- DO_MAX(grid_max, max);
- have_minmax = true;
- }
- }
- }
+ ob->runtime.bb = (BoundBox *)MEM_callocN(sizeof(BoundBox), __func__);
+ }
- if (!have_minmax) {
- min[0] = min[1] = min[2] = -1.0f;
- max[0] = max[1] = max[2] = 1.0f;
- }
+ const Volume *volume = (Volume *)ob->data;
- BKE_boundbox_init_from_minmax(ob->runtime.bb, min, max);
+ float3 min, max;
+ INIT_MINMAX(min, max);
+ if (!BKE_volume_min_max(volume, min, max)) {
+ min = float3(-1);
+ max = float3(1);
}
+ BKE_boundbox_init_from_minmax(ob->runtime.bb, min, max);
+
return ob->runtime.bb;
}
@@ -957,7 +978,7 @@ bool BKE_volume_is_points_only(const Volume *volume)
}
for (int i = 0; i < num_grids; i++) {
- VolumeGrid *grid = BKE_volume_grid_get(volume, i);
+ const VolumeGrid *grid = BKE_volume_grid_get_for_read(volume, i);
if (BKE_volume_grid_type(grid) != VOLUME_GRID_POINTS) {
return false;
}
@@ -983,13 +1004,11 @@ static void volume_update_simplify_level(Volume *volume, const Depsgraph *depsgr
#endif
}
-static Volume *volume_evaluate_modifiers(struct Depsgraph *depsgraph,
- struct Scene *scene,
- Object *object,
- Volume *volume_input)
+static void volume_evaluate_modifiers(struct Depsgraph *depsgraph,
+ struct Scene *scene,
+ Object *object,
+ GeometrySet &geometry_set)
{
- Volume *volume = volume_input;
-
/* Modifier evaluation modes. */
const bool use_render = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER);
const int required_mode = use_render ? eModifierMode_Render : eModifierMode_Realtime;
@@ -1010,25 +1029,10 @@ static Volume *volume_evaluate_modifiers(struct Depsgraph *depsgraph,
continue;
}
- if (mti->modifyVolume) {
- /* Ensure we are not modifying the input. */
- if (volume == volume_input) {
- volume = BKE_volume_copy_for_eval(volume, true);
- }
-
- Volume *volume_next = mti->modifyVolume(md, &mectx, volume);
-
- if (volume_next && volume_next != volume) {
- /* If the modifier returned a new volume, release the old one. */
- if (volume != volume_input) {
- BKE_id_free(nullptr, volume);
- }
- volume = volume_next;
- }
+ if (mti->modifyGeometrySet) {
+ mti->modifyGeometrySet(md, &mectx, &geometry_set);
}
}
-
- return volume;
}
void BKE_volume_eval_geometry(struct Depsgraph *depsgraph, Volume *volume)
@@ -1052,6 +1056,24 @@ void BKE_volume_eval_geometry(struct Depsgraph *depsgraph, Volume *volume)
}
}
+static Volume *take_volume_ownership_from_geometry_set(GeometrySet &geometry_set)
+{
+ if (!geometry_set.has<VolumeComponent>()) {
+ return nullptr;
+ }
+ VolumeComponent &volume_component = geometry_set.get_component_for_write<VolumeComponent>();
+ Volume *volume = volume_component.release();
+ if (volume != nullptr) {
+ /* Add back, but only as read-only non-owning component. */
+ volume_component.replace(volume, GeometryOwnershipType::ReadOnly);
+ }
+ else {
+ /* The component was empty, we can remove it. */
+ geometry_set.remove<VolumeComponent>();
+ }
+ return volume;
+}
+
void BKE_volume_data_update(struct Depsgraph *depsgraph, struct Scene *scene, Object *object)
{
/* Free any evaluated data and restore original data. */
@@ -1059,11 +1081,21 @@ void BKE_volume_data_update(struct Depsgraph *depsgraph, struct Scene *scene, Ob
/* Evaluate modifiers. */
Volume *volume = (Volume *)object->data;
- Volume *volume_eval = volume_evaluate_modifiers(depsgraph, scene, object, volume);
+ GeometrySet geometry_set;
+ geometry_set.replace_volume(volume, GeometryOwnershipType::ReadOnly);
+ volume_evaluate_modifiers(depsgraph, scene, object, geometry_set);
+
+ Volume *volume_eval = take_volume_ownership_from_geometry_set(geometry_set);
+
+ /* If the geometry set did not contain a volume, we still create an empty one. */
+ if (volume_eval == nullptr) {
+ volume_eval = BKE_volume_new_for_eval(volume);
+ }
/* Assign evaluated object. */
- const bool is_owned = (volume != volume_eval);
- BKE_object_eval_assign_data(object, &volume_eval->id, is_owned);
+ const bool eval_is_owned = (volume != volume_eval);
+ BKE_object_eval_assign_data(object, &volume_eval->id, eval_is_owned);
+ object->runtime.geometry_set_eval = new GeometrySet(std::move(geometry_set));
}
void BKE_volume_grids_backup_restore(Volume *volume, VolumeGridVector *grids, const char *filepath)
@@ -1144,7 +1176,23 @@ const char *BKE_volume_grids_frame_filepath(const Volume *volume)
#endif
}
-VolumeGrid *BKE_volume_grid_get(const Volume *volume, int grid_index)
+const VolumeGrid *BKE_volume_grid_get_for_read(const Volume *volume, int grid_index)
+{
+#ifdef WITH_OPENVDB
+ const VolumeGridVector &grids = *volume->runtime.grids;
+ for (const VolumeGrid &grid : grids) {
+ if (grid_index-- == 0) {
+ return &grid;
+ }
+ }
+ return nullptr;
+#else
+ UNUSED_VARS(volume, grid_index);
+ return nullptr;
+#endif
+}
+
+VolumeGrid *BKE_volume_grid_get_for_write(Volume *volume, int grid_index)
{
#ifdef WITH_OPENVDB
VolumeGridVector &grids = *volume->runtime.grids;
@@ -1160,7 +1208,7 @@ VolumeGrid *BKE_volume_grid_get(const Volume *volume, int grid_index)
#endif
}
-VolumeGrid *BKE_volume_grid_active_get(const Volume *volume)
+const VolumeGrid *BKE_volume_grid_active_get_for_read(const Volume *volume)
{
const int num_grids = BKE_volume_num_grids(volume);
if (num_grids == 0) {
@@ -1168,15 +1216,15 @@ VolumeGrid *BKE_volume_grid_active_get(const Volume *volume)
}
const int index = clamp_i(volume->active_grid, 0, num_grids - 1);
- return BKE_volume_grid_get(volume, index);
+ return BKE_volume_grid_get_for_read(volume, index);
}
/* Tries to find a grid with the given name. Make sure that that the volume has been loaded. */
-VolumeGrid *BKE_volume_grid_find(const Volume *volume, const char *name)
+const VolumeGrid *BKE_volume_grid_find_for_read(const Volume *volume, const char *name)
{
int num_grids = BKE_volume_num_grids(volume);
for (int i = 0; i < num_grids; i++) {
- VolumeGrid *grid = BKE_volume_grid_get(volume, i);
+ const VolumeGrid *grid = BKE_volume_grid_get_for_read(volume, i);
if (STREQ(BKE_volume_grid_name(grid), name)) {
return grid;
}
@@ -1187,7 +1235,7 @@ VolumeGrid *BKE_volume_grid_find(const Volume *volume, const char *name)
/* Grid Loading */
-bool BKE_volume_grid_load(const Volume *volume, VolumeGrid *grid)
+bool BKE_volume_grid_load(const Volume *volume, const VolumeGrid *grid)
{
#ifdef WITH_OPENVDB
VolumeGridVector &grids = *volume->runtime.grids;
@@ -1205,7 +1253,7 @@ bool BKE_volume_grid_load(const Volume *volume, VolumeGrid *grid)
#endif
}
-void BKE_volume_grid_unload(const Volume *volume, VolumeGrid *grid)
+void BKE_volume_grid_unload(const Volume *volume, const VolumeGrid *grid)
{
#ifdef WITH_OPENVDB
const char *volume_name = volume->id.name + 2;
@@ -1335,34 +1383,6 @@ void BKE_volume_grid_transform_matrix(const VolumeGrid *volume_grid, float mat[4
/* Grid Tree and Voxels */
-bool BKE_volume_grid_bounds(const VolumeGrid *volume_grid, float min[3], float max[3])
-{
-#ifdef WITH_OPENVDB
- /* TODO: we can get this from grid metadata in some cases? */
- const openvdb::GridBase::Ptr grid = volume_grid->grid();
- BLI_assert(BKE_volume_grid_is_loaded(volume_grid));
-
- openvdb::CoordBBox coordbbox;
- if (!grid->baseTree().evalLeafBoundingBox(coordbbox)) {
- INIT_MINMAX(min, max);
- return false;
- }
-
- openvdb::BBoxd bbox = grid->transform().indexToWorld(coordbbox);
- min[0] = (float)bbox.min().x();
- min[1] = (float)bbox.min().y();
- min[2] = (float)bbox.min().z();
- max[0] = (float)bbox.max().x();
- max[1] = (float)bbox.max().y();
- max[2] = (float)bbox.max().z();
- return true;
-#else
- UNUSED_VARS(volume_grid);
- INIT_MINMAX(min, max);
- return false;
-#endif
-}
-
/* Volume Editing */
Volume *BKE_volume_new_for_eval(const Volume *volume_src)
@@ -1410,7 +1430,7 @@ VolumeGrid *BKE_volume_grid_add(Volume *volume, const char *name, VolumeGridType
{
#ifdef WITH_OPENVDB
VolumeGridVector &grids = *volume->runtime.grids;
- BLI_assert(BKE_volume_grid_find(volume, name) == nullptr);
+ BLI_assert(BKE_volume_grid_find_for_read(volume, name) == nullptr);
BLI_assert(type != VOLUME_GRID_UNKNOWN);
openvdb::GridBase::Ptr vdb_grid = BKE_volume_grid_type_operation(type, CreateGridOp{});
@@ -1472,13 +1492,45 @@ float BKE_volume_simplify_factor(const Depsgraph *depsgraph)
/* OpenVDB Grid Access */
#ifdef WITH_OPENVDB
+
+bool BKE_volume_grid_bounds(openvdb::GridBase::ConstPtr grid, float3 &r_min, float3 &r_max)
+{
+ /* TODO: we can get this from grid metadata in some cases? */
+ openvdb::CoordBBox coordbbox;
+ if (!grid->baseTree().evalLeafBoundingBox(coordbbox)) {
+ return false;
+ }
+
+ openvdb::BBoxd bbox = grid->transform().indexToWorld(coordbbox);
+
+ r_min = float3((float)bbox.min().x(), (float)bbox.min().y(), (float)bbox.min().z());
+ r_max = float3((float)bbox.max().x(), (float)bbox.max().y(), (float)bbox.max().z());
+
+ return true;
+}
+
+/**
+ * Return a new grid pointer with only the metadata and transform changed.
+ * This is useful for instances, where there is a separate transform on top of the original
+ * grid transform that must be applied for some operations that only take a grid argument.
+ */
+openvdb::GridBase::ConstPtr BKE_volume_grid_shallow_transform(openvdb::GridBase::ConstPtr grid,
+ const blender::float4x4 &transform)
+{
+ openvdb::math::Transform::Ptr grid_transform = grid->transform().copy();
+ grid_transform->postMult(openvdb::Mat4d(((float *)transform.values)));
+
+ /* Create a transformed grid. The underlying tree is shared. */
+ return grid->copyGridReplacingTransform(grid_transform);
+}
+
openvdb::GridBase::ConstPtr BKE_volume_grid_openvdb_for_metadata(const VolumeGrid *grid)
{
return grid->grid();
}
openvdb::GridBase::ConstPtr BKE_volume_grid_openvdb_for_read(const Volume *volume,
- VolumeGrid *grid)
+ const VolumeGrid *grid)
{
BKE_volume_grid_load(volume, grid);
return grid->grid();
diff --git a/source/blender/blenkernel/intern/volume_render.cc b/source/blender/blenkernel/intern/volume_render.cc
index 5c71f1d7eca..6dc497bb616 100644
--- a/source/blender/blenkernel/intern/volume_render.cc
+++ b/source/blender/blenkernel/intern/volume_render.cc
@@ -104,7 +104,7 @@ static void create_texture_to_object_matrix(const openvdb::Mat4d &grid_transform
#endif
bool BKE_volume_grid_dense_floats(const Volume *volume,
- VolumeGrid *volume_grid,
+ const VolumeGrid *volume_grid,
DenseFloatVolumeGrid *r_dense_grid)
{
#ifdef WITH_OPENVDB
@@ -334,7 +334,7 @@ static void boxes_to_cube_mesh(blender::Span<openvdb::CoordBBox> boxes,
#endif
void BKE_volume_grid_wireframe(const Volume *volume,
- VolumeGrid *volume_grid,
+ const VolumeGrid *volume_grid,
BKE_volume_wireframe_cb cb,
void *cb_userdata)
{
@@ -411,7 +411,7 @@ static void grow_triangles(blender::MutableSpan<blender::float3> verts,
#endif /* WITH_OPENVDB */
void BKE_volume_grid_selection_surface(const Volume *volume,
- VolumeGrid *volume_grid,
+ const VolumeGrid *volume_grid,
BKE_volume_selection_surface_cb cb,
void *cb_userdata)
{
diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c
index 0991d804882..560ae30967f 100644
--- a/source/blender/blenkernel/intern/writeffmpeg.c
+++ b/source/blender/blenkernel/intern/writeffmpeg.c
@@ -36,6 +36,7 @@
# include <AUD_Special.h>
# endif
+# include "BLI_endian_defines.h"
# include "BLI_math_base.h"
# include "BLI_threads.h"
# include "BLI_utildefines.h"
@@ -56,6 +57,7 @@
# include <libavcodec/avcodec.h>
# include <libavformat/avformat.h>
# include <libavutil/imgutils.h>
+# include <libavutil/opt.h>
# include <libavutil/rational.h>
# include <libavutil/samplefmt.h>
# include <libswscale/swscale.h>
@@ -80,6 +82,8 @@ typedef struct FFMpegContext {
int ffmpeg_preset; /* see eFFMpegPreset */
AVFormatContext *outfile;
+ AVCodecContext *video_codec;
+ AVCodecContext *audio_codec;
AVStream *video_stream;
AVStream *audio_stream;
AVFrame *current_frame; /* Image frame in output pixel format. */
@@ -91,10 +95,6 @@ typedef struct FFMpegContext {
uint8_t *audio_input_buffer;
uint8_t *audio_deinterleave_buffer;
int audio_input_samples;
-# ifndef FFMPEG_HAVE_ENCODE_AUDIO2
- uint8_t *audio_output_buffer;
- int audio_outbuf_size;
-# endif
double audio_time;
bool audio_deinterleave;
int audio_sample_size;
@@ -141,32 +141,22 @@ static int request_float_audio_buffer(int codec_id)
}
# ifdef WITH_AUDASPACE
+
static int write_audio_frame(FFMpegContext *context)
{
- AVCodecContext *c = NULL;
- AVPacket pkt;
AVFrame *frame = NULL;
- int got_output = 0;
-
- c = context->audio_stream->codec;
-
- av_init_packet(&pkt);
- pkt.size = 0;
- pkt.data = NULL;
+ AVCodecContext *c = context->audio_codec;
AUD_Device_read(
context->audio_mixdown_device, context->audio_input_buffer, context->audio_input_samples);
context->audio_time += (double)context->audio_input_samples / (double)c->sample_rate;
-# ifdef FFMPEG_HAVE_ENCODE_AUDIO2
frame = av_frame_alloc();
- av_frame_unref(frame);
frame->pts = context->audio_time / av_q2d(c->time_base);
frame->nb_samples = context->audio_input_samples;
frame->format = c->sample_fmt;
-# ifdef FFMPEG_HAVE_FRAME_CHANNEL_LAYOUT
+ frame->channels = c->channels;
frame->channel_layout = c->channel_layout;
-# endif
if (context->audio_deinterleave) {
int channel, i;
@@ -194,61 +184,48 @@ static int write_audio_frame(FFMpegContext *context)
context->audio_input_samples * c->channels * context->audio_sample_size,
1);
- if (avcodec_encode_audio2(c, &pkt, frame, &got_output) < 0) {
- // XXX error("Error writing audio packet");
- return -1;
- }
+ int success = 0;
- if (!got_output) {
- av_frame_free(&frame);
- return 0;
+ int ret = avcodec_send_frame(c, frame);
+ if (ret < 0) {
+ /* Can't send frame to encoder. This shouldn't happen. */
+ fprintf(stderr, "Can't send audio frame: %s\n", av_err2str(ret));
+ success = -1;
}
-# else
- pkt.size = avcodec_encode_audio(c,
- context->audio_output_buffer,
- context->audio_outbuf_size,
- (short *)context->audio_input_buffer);
- if (pkt.size < 0) {
- // XXX error("Error writing audio packet");
- return -1;
- }
+ AVPacket *pkt = av_packet_alloc();
- pkt.data = context->audio_output_buffer;
- got_output = 1;
-# endif
+ while (ret >= 0) {
- if (got_output) {
- if (pkt.pts != AV_NOPTS_VALUE) {
- pkt.pts = av_rescale_q(pkt.pts, c->time_base, context->audio_stream->time_base);
- }
- if (pkt.dts != AV_NOPTS_VALUE) {
- pkt.dts = av_rescale_q(pkt.dts, c->time_base, context->audio_stream->time_base);
+ ret = avcodec_receive_packet(c, pkt);
+ if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
+ break;
}
- if (pkt.duration > 0) {
- pkt.duration = av_rescale_q(pkt.duration, c->time_base, context->audio_stream->time_base);
+ if (ret < 0) {
+ fprintf(stderr, "Error encoding audio frame: %s\n", av_err2str(ret));
+ success = -1;
}
- pkt.stream_index = context->audio_stream->index;
+ pkt->stream_index = context->audio_stream->index;
+ av_packet_rescale_ts(pkt, c->time_base, context->audio_stream->time_base);
+# ifdef FFMPEG_USE_DURATION_WORKAROUND
+ my_guess_pkt_duration(context->outfile, context->audio_stream, pkt);
+# endif
- pkt.flags |= AV_PKT_FLAG_KEY;
+ pkt->flags |= AV_PKT_FLAG_KEY;
- if (av_interleaved_write_frame(context->outfile, &pkt) != 0) {
- fprintf(stderr, "Error writing audio packet!\n");
- if (frame) {
- av_frame_free(&frame);
- }
- return -1;
+ int write_ret = av_interleaved_write_frame(context->outfile, pkt);
+ if (write_ret != 0) {
+ fprintf(stderr, "Error writing audio packet: %s\n", av_err2str(write_ret));
+ success = -1;
+ break;
}
-
- av_free_packet(&pkt);
}
- if (frame) {
- av_frame_free(&frame);
- }
+ av_packet_free(&pkt);
+ av_frame_free(&frame);
- return 0;
+ return success;
}
# endif /* #ifdef WITH_AUDASPACE */
@@ -264,14 +241,15 @@ static AVFrame *alloc_picture(int pix_fmt, int width, int height)
if (!f) {
return NULL;
}
- size = avpicture_get_size(pix_fmt, width, height);
+ size = av_image_get_buffer_size(pix_fmt, width, height, 1);
/* allocate the actual picture buffer */
buf = MEM_mallocN(size, "AVFrame buffer");
if (!buf) {
free(f);
return NULL;
}
- avpicture_fill((AVPicture *)f, buf, pix_fmt, width, height);
+
+ av_image_fill_arrays(f->data, f->linesize, buf, pix_fmt, width, height, 1);
f->format = pix_fmt;
f->width = width;
f->height = height;
@@ -341,58 +319,61 @@ static const char **get_file_extensions(int format)
}
/* Write a frame to the output file */
-static int write_video_frame(
- FFMpegContext *context, const RenderData *rd, int cfra, AVFrame *frame, ReportList *reports)
+static int write_video_frame(FFMpegContext *context, int cfra, AVFrame *frame, ReportList *reports)
{
- int got_output;
int ret, success = 1;
- AVCodecContext *c = context->video_stream->codec;
- AVPacket packet = {0};
+ AVPacket *packet = av_packet_alloc();
- av_init_packet(&packet);
+ AVCodecContext *c = context->video_codec;
frame->pts = cfra;
- ret = avcodec_encode_video2(c, &packet, frame, &got_output);
+ ret = avcodec_send_frame(c, frame);
+ if (ret < 0) {
+ /* Can't send frame to encoder. This shouldn't happen. */
+ fprintf(stderr, "Can't send video frame: %s\n", av_err2str(ret));
+ success = -1;
+ }
- if (ret >= 0 && got_output) {
- if (packet.pts != AV_NOPTS_VALUE) {
- packet.pts = av_rescale_q(packet.pts, c->time_base, context->video_stream->time_base);
- PRINT("Video Frame PTS: %d\n", (int)packet.pts);
- }
- else {
- PRINT("Video Frame PTS: not set\n");
- }
- if (packet.dts != AV_NOPTS_VALUE) {
- packet.dts = av_rescale_q(packet.dts, c->time_base, context->video_stream->time_base);
- PRINT("Video Frame DTS: %d\n", (int)packet.dts);
+ while (ret >= 0) {
+ ret = avcodec_receive_packet(c, packet);
+
+ if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
+ /* No more packets available. */
+ break;
}
- else {
- PRINT("Video Frame DTS: not set\n");
+ if (ret < 0) {
+ fprintf(stderr, "Error encoding frame: %s\n", av_err2str(ret));
+ break;
}
- packet.stream_index = context->video_stream->index;
- ret = av_interleaved_write_frame(context->outfile, &packet);
- success = (ret == 0);
- }
- else if (ret < 0) {
- success = 0;
+ packet->stream_index = context->video_stream->index;
+ av_packet_rescale_ts(packet, c->time_base, context->video_stream->time_base);
+# ifdef FFMPEG_USE_DURATION_WORKAROUND
+ my_guess_pkt_duration(context->outfile, context->video_stream, packet);
+# endif
+
+ if (av_interleaved_write_frame(context->outfile, packet) != 0) {
+ success = -1;
+ break;
+ }
}
if (!success) {
BKE_report(reports, RPT_ERROR, "Error writing frame");
+ PRINT("Error writing frame: %s\n", av_err2str(ret));
}
+ av_packet_free(&packet);
+
return success;
}
/* read and encode a frame of audio from the buffer */
-static AVFrame *generate_video_frame(FFMpegContext *context,
- const uint8_t *pixels,
- ReportList *reports)
+static AVFrame *generate_video_frame(FFMpegContext *context, const uint8_t *pixels)
{
- AVCodecContext *c = context->video_stream->codec;
- int height = c->height;
+ AVCodecParameters *codec = context->video_stream->codecpar;
+ int height = codec->height;
AVFrame *rgb_frame;
if (context->img_convert_frame != NULL) {
@@ -437,7 +418,7 @@ static AVFrame *generate_video_frame(FFMpegContext *context,
(const uint8_t *const *)rgb_frame->data,
rgb_frame->linesize,
0,
- c->height,
+ codec->height,
context->current_frame->data,
context->current_frame->linesize);
}
@@ -445,9 +426,7 @@ static AVFrame *generate_video_frame(FFMpegContext *context,
return context->current_frame;
}
-static void set_ffmpeg_property_option(AVCodecContext *c,
- IDProperty *prop,
- AVDictionary **dictionary)
+static void set_ffmpeg_property_option(IDProperty *prop, AVDictionary **dictionary)
{
char name[128];
char *param;
@@ -535,11 +514,53 @@ static void set_ffmpeg_properties(RenderData *rd,
for (curr = prop->data.group.first; curr; curr = curr->next) {
if (ffmpeg_proprty_valid(c, prop_name, curr)) {
- set_ffmpeg_property_option(c, curr, dictionary);
+ set_ffmpeg_property_option(curr, dictionary);
}
}
}
+static AVRational calc_time_base(uint den, double num, int codec_id)
+{
+ /* Convert the input 'num' to an integer. Simply shift the decimal places until we get an integer
+ * (within a floating point error range).
+ * For example if we have `den = 3` and `num = 0.1` then the fps is: `den/num = 30` fps.
+ * When converting this to a FFMPEG time base, we want num to be an integer.
+ * So we simply move the decimal places of both numbers. i.e. `den = 30`, `num = 1`. */
+ float eps = FLT_EPSILON;
+ const uint DENUM_MAX = (codec_id == AV_CODEC_ID_MPEG4) ? (1UL << 16) - 1 : (1UL << 31) - 1;
+
+ /* Calculate the precision of the initial floating point number. */
+ if (num > 1.0) {
+ const uint num_integer_bits = log2_floor_u((unsigned int)num);
+
+ /* Formula for calculating the epsilon value: (power of two range) / (pow mantissa bits)
+ * For example, a float has 23 mantissa bits and the float value 3.5f as a pow2 range of
+ * (4-2=2):
+ * (2) / pow2(23) = floating point precision for 3.5f
+ */
+ eps = (float)(1 << num_integer_bits) * FLT_EPSILON;
+ }
+
+ /* Calculate how many decimal shifts we can do until we run out of precision. */
+ const int max_num_shift = fabsf(log10f(eps));
+ /* Calculate how many times we can shift the denominator. */
+ const int max_den_shift = log10f(DENUM_MAX) - log10f(den);
+ const int max_iter = min_ii(max_num_shift, max_den_shift);
+
+ for (int i = 0; i < max_iter && fabs(num - round(num)) > eps; i++) {
+ /* Increase the number and denominator until both are integers. */
+ num *= 10;
+ den *= 10;
+ eps *= 10;
+ }
+
+ AVRational time_base;
+ time_base.den = den;
+ time_base.num = (int)num;
+
+ return time_base;
+}
+
/* prepare a video stream for the output file */
static AVStream *alloc_video_stream(FFMpegContext *context,
@@ -552,7 +573,6 @@ static AVStream *alloc_video_stream(FFMpegContext *context,
int error_size)
{
AVStream *st;
- AVCodecContext *c;
AVCodec *codec;
AVDictionary *opts = NULL;
@@ -566,20 +586,29 @@ static AVStream *alloc_video_stream(FFMpegContext *context,
/* Set up the codec context */
- c = st->codec;
- c->thread_count = BLI_system_thread_count();
- c->thread_type = FF_THREAD_SLICE;
-
+ context->video_codec = avcodec_alloc_context3(NULL);
+ AVCodecContext *c = context->video_codec;
c->codec_id = codec_id;
c->codec_type = AVMEDIA_TYPE_VIDEO;
+ codec = avcodec_find_encoder(c->codec_id);
+ if (!codec) {
+ fprintf(stderr, "Couldn't find valid video codec\n");
+ avcodec_free_context(&c);
+ context->video_codec = NULL;
+ return NULL;
+ }
+
+ /* Load codec defaults into 'c'. */
+ avcodec_get_context_defaults3(c, codec);
+
/* Get some values from the current render settings */
c->width = rectx;
c->height = recty;
- /* FIXME: Really bad hack (tm) for NTSC support */
if (context->ffmpeg_type == FFMPEG_DV && rd->frs_sec != 25) {
+ /* FIXME: Really bad hack (tm) for NTSC support */
c->time_base.den = 2997;
c->time_base.num = 100;
}
@@ -587,21 +616,23 @@ static AVStream *alloc_video_stream(FFMpegContext *context,
c->time_base.den = rd->frs_sec;
c->time_base.num = (int)rd->frs_sec_base;
}
- else if (compare_ff(rd->frs_sec_base, 1.001f, 0.000001f)) {
- /* This converts xx/1.001 (which is used in presets) to xx000/1001 (which is used in the rest
- * of the world, including FFmpeg). */
- c->time_base.den = (int)(rd->frs_sec * 1000);
- c->time_base.num = (int)(rd->frs_sec_base * 1000);
- }
else {
- /* This calculates a fraction (DENUM_MAX / num) which approximates the scene frame rate
- * (frs_sec / frs_sec_base). It uses the maximum denominator allowed by FFmpeg.
- */
- const double DENUM_MAX = (codec_id == AV_CODEC_ID_MPEG4) ? (1UL << 16) - 1 : (1UL << 31) - 1;
- const double num = (DENUM_MAX / (double)rd->frs_sec) * rd->frs_sec_base;
+ c->time_base = calc_time_base(rd->frs_sec, rd->frs_sec_base, codec_id);
+ }
- c->time_base.den = (int)DENUM_MAX;
- c->time_base.num = (int)num;
+ /* As per the time-base documentation here:
+ * https://www.ffmpeg.org/ffmpeg-codecs.html#Codec-Options
+ * We want to set the time base to (1 / fps) for fixed frame rate video.
+ * If it is not possible, we want to set the time-base numbers to something as
+ * small as possible.
+ */
+ if (c->time_base.num != 1) {
+ AVRational new_time_base;
+ if (av_reduce(
+ &new_time_base.num, &new_time_base.den, c->time_base.num, c->time_base.den, INT_MAX)) {
+ /* Exact reduction was possible. Use the new value. */
+ c->time_base = new_time_base;
+ }
}
st->time_base = c->time_base;
@@ -613,6 +644,11 @@ static AVStream *alloc_video_stream(FFMpegContext *context,
ffmpeg_dict_set_int(&opts, "lossless", 1);
}
else if (context->ffmpeg_crf >= 0) {
+ /* As per https://trac.ffmpeg.org/wiki/Encode/VP9 we must set the bit rate to zero when
+ * encoding with vp9 in crf mode.
+ * Set this to always be zero for other codecs as well.
+ * We don't care about bit rate in crf mode. */
+ c->bit_rate = 0;
ffmpeg_dict_set_int(&opts, "crf", context->ffmpeg_crf);
}
else {
@@ -652,14 +688,6 @@ static AVStream *alloc_video_stream(FFMpegContext *context,
}
}
- /* Deprecated and not doing anything since July 2015, deleted in recent ffmpeg */
- // c->me_method = ME_EPZS;
-
- codec = avcodec_find_encoder(c->codec_id);
- if (!codec) {
- return NULL;
- }
-
/* Be sure to use the correct pixel format(e.g. RGB, YUV) */
if (codec->pix_fmts) {
@@ -676,12 +704,6 @@ static AVStream *alloc_video_stream(FFMpegContext *context,
c->codec_tag = (('D' << 24) + ('I' << 16) + ('V' << 8) + 'X');
}
- if (codec_id == AV_CODEC_ID_H264) {
- /* correct wrong default ffmpeg param which crash x264 */
- c->qmin = 10;
- c->qmax = 51;
- }
-
/* Keep lossless encodes in the RGB domain. */
if (codec_id == AV_CODEC_ID_HUFFYUV) {
if (rd->im_format.planes == R_IMF_PLANES_RGBA) {
@@ -708,6 +730,11 @@ static AVStream *alloc_video_stream(FFMpegContext *context,
}
}
+ /* Use 4:4:4 instead of 4:2:0 pixel format for lossless rendering. */
+ if ((codec_id == AV_CODEC_ID_H264 || codec_id == AV_CODEC_ID_VP9) && context->ffmpeg_crf == 0) {
+ c->pix_fmt = AV_PIX_FMT_YUV444P;
+ }
+
if (codec_id == AV_CODEC_ID_PNG) {
if (rd->im_format.planes == R_IMF_PLANES_RGBA) {
c->pix_fmt = AV_PIX_FMT_RGBA;
@@ -716,7 +743,7 @@ static AVStream *alloc_video_stream(FFMpegContext *context,
if ((of->oformat->flags & AVFMT_GLOBALHEADER)) {
PRINT("Using global header\n");
- c->flags |= CODEC_FLAG_GLOBAL_HEADER;
+ c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
/* xasp & yasp got float lately... */
@@ -727,9 +754,28 @@ static AVStream *alloc_video_stream(FFMpegContext *context,
set_ffmpeg_properties(rd, c, "video", &opts);
- if (avcodec_open2(c, codec, &opts) < 0) {
+ if (codec->capabilities & AV_CODEC_CAP_AUTO_THREADS) {
+ c->thread_count = 0;
+ }
+ else {
+ c->thread_count = BLI_system_thread_count();
+ }
+
+ if (codec->capabilities & AV_CODEC_CAP_FRAME_THREADS) {
+ c->thread_type = FF_THREAD_FRAME;
+ }
+ else if (codec->capabilities & AV_CODEC_CAP_SLICE_THREADS) {
+ c->thread_type = FF_THREAD_SLICE;
+ }
+
+ int ret = avcodec_open2(c, codec, &opts);
+
+ if (ret < 0) {
+ fprintf(stderr, "Couldn't initialize video codec: %s\n", av_err2str(ret));
BLI_strncpy(error, IMB_ffmpeg_last_error(), error_size);
av_dict_free(&opts);
+ avcodec_free_context(&c);
+ context->video_codec = NULL;
return NULL;
}
av_dict_free(&opts);
@@ -757,6 +803,8 @@ static AVStream *alloc_video_stream(FFMpegContext *context,
NULL);
}
+ avcodec_parameters_from_context(st->codecpar, c);
+
return st;
}
@@ -768,7 +816,6 @@ static AVStream *alloc_audio_stream(FFMpegContext *context,
int error_size)
{
AVStream *st;
- AVCodecContext *c;
AVCodec *codec;
AVDictionary *opts = NULL;
@@ -780,19 +827,30 @@ static AVStream *alloc_audio_stream(FFMpegContext *context,
}
st->id = 1;
- c = st->codec;
+ context->audio_codec = avcodec_alloc_context3(NULL);
+ AVCodecContext *c = context->audio_codec;
c->thread_count = BLI_system_thread_count();
c->thread_type = FF_THREAD_SLICE;
c->codec_id = codec_id;
c->codec_type = AVMEDIA_TYPE_AUDIO;
+ codec = avcodec_find_encoder(c->codec_id);
+ if (!codec) {
+ fprintf(stderr, "Couldn't find valid audio codec\n");
+ avcodec_free_context(&c);
+ context->audio_codec = NULL;
+ return NULL;
+ }
+
+ /* Load codec defaults into 'c'. */
+ avcodec_get_context_defaults3(c, codec);
+
c->sample_rate = rd->ffcodecdata.audio_mixrate;
c->bit_rate = context->ffmpeg_audio_bitrate * 1000;
c->sample_fmt = AV_SAMPLE_FMT_S16;
c->channels = rd->ffcodecdata.audio_channels;
-# ifdef FFMPEG_HAVE_FRAME_CHANNEL_LAYOUT
switch (rd->ffcodecdata.audio_channels) {
case FFM_CHANNELS_MONO:
c->channel_layout = AV_CH_LAYOUT_MONO;
@@ -810,7 +868,6 @@ static AVStream *alloc_audio_stream(FFMpegContext *context,
c->channel_layout = AV_CH_LAYOUT_7POINT1;
break;
}
-# endif
if (request_float_audio_buffer(codec_id)) {
/* mainly for AAC codec which is experimental */
@@ -818,12 +875,6 @@ static AVStream *alloc_audio_stream(FFMpegContext *context,
c->sample_fmt = AV_SAMPLE_FMT_FLT;
}
- codec = avcodec_find_encoder(c->codec_id);
- if (!codec) {
- // XXX error("Couldn't find a valid audio codec");
- return NULL;
- }
-
if (codec->sample_fmts) {
/* Check if the preferred sample format for this codec is supported.
* this is because, depending on the version of libav,
@@ -832,13 +883,13 @@ static AVStream *alloc_audio_stream(FFMpegContext *context,
* Float samples in particular are not always supported. */
const enum AVSampleFormat *p = codec->sample_fmts;
for (; *p != -1; p++) {
- if (*p == st->codec->sample_fmt) {
+ if (*p == c->sample_fmt) {
break;
}
}
if (*p == -1) {
/* sample format incompatible with codec. Defaulting to a format known to work */
- st->codec->sample_fmt = codec->sample_fmts[0];
+ c->sample_fmt = codec->sample_fmts[0];
}
}
@@ -847,52 +898,48 @@ static AVStream *alloc_audio_stream(FFMpegContext *context,
int best = 0;
int best_dist = INT_MAX;
for (; *p; p++) {
- int dist = abs(st->codec->sample_rate - *p);
+ int dist = abs(c->sample_rate - *p);
if (dist < best_dist) {
best_dist = dist;
best = *p;
}
}
/* best is the closest supported sample rate (same as selected if best_dist == 0) */
- st->codec->sample_rate = best;
+ c->sample_rate = best;
}
if (of->oformat->flags & AVFMT_GLOBALHEADER) {
- c->flags |= CODEC_FLAG_GLOBAL_HEADER;
+ c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
set_ffmpeg_properties(rd, c, "audio", &opts);
- if (avcodec_open2(c, codec, &opts) < 0) {
- // XXX error("Couldn't initialize audio codec");
+ int ret = avcodec_open2(c, codec, &opts);
+
+ if (ret < 0) {
+ fprintf(stderr, "Couldn't initialize audio codec: %s\n", av_err2str(ret));
BLI_strncpy(error, IMB_ffmpeg_last_error(), error_size);
av_dict_free(&opts);
+ avcodec_free_context(&c);
+ context->audio_codec = NULL;
return NULL;
}
av_dict_free(&opts);
/* need to prevent floating point exception when using vorbis audio codec,
* initialize this value in the same way as it's done in FFmpeg itself (sergey) */
- st->codec->time_base.num = 1;
- st->codec->time_base.den = st->codec->sample_rate;
-
-# ifndef FFMPEG_HAVE_ENCODE_AUDIO2
- context->audio_outbuf_size = FF_MIN_BUFFER_SIZE;
-# endif
+ c->time_base.num = 1;
+ c->time_base.den = c->sample_rate;
if (c->frame_size == 0) {
/* Used to be if ((c->codec_id >= CODEC_ID_PCM_S16LE) && (c->codec_id <= CODEC_ID_PCM_DVD))
* not sure if that is needed anymore, so let's try out if there are any
* complaints regarding some FFmpeg versions users might have. */
- context->audio_input_samples = FF_MIN_BUFFER_SIZE * 8 / c->bits_per_coded_sample / c->channels;
+ context->audio_input_samples = AV_INPUT_BUFFER_MIN_SIZE * 8 / c->bits_per_coded_sample /
+ c->channels;
}
else {
context->audio_input_samples = c->frame_size;
-# ifndef FFMPEG_HAVE_ENCODE_AUDIO2
- if (c->frame_size * c->channels * sizeof(int16_t) * 4 > context->audio_outbuf_size) {
- context->audio_outbuf_size = c->frame_size * c->channels * sizeof(int16_t) * 4;
- }
-# endif
}
context->audio_deinterleave = av_sample_fmt_is_planar(c->sample_fmt);
@@ -901,10 +948,6 @@ static AVStream *alloc_audio_stream(FFMpegContext *context,
context->audio_input_buffer = (uint8_t *)av_malloc(context->audio_input_samples * c->channels *
context->audio_sample_size);
-# ifndef FFMPEG_HAVE_ENCODE_AUDIO2
- context->audio_output_buffer = (uint8_t *)av_malloc(context->audio_outbuf_size);
-# endif
-
if (context->audio_deinterleave) {
context->audio_deinterleave_buffer = (uint8_t *)av_malloc(
context->audio_input_samples * c->channels * context->audio_sample_size);
@@ -912,6 +955,8 @@ static AVStream *alloc_audio_stream(FFMpegContext *context,
context->audio_time = 0.0f;
+ avcodec_parameters_from_context(st->codecpar, c);
+
return st;
}
/* essential functions -- start, append, end */
@@ -937,7 +982,7 @@ static void ffmpeg_dict_set_float(AVDictionary **dict, const char *key, float va
static void ffmpeg_add_metadata_callback(void *data,
const char *propname,
char *propvalue,
- int len)
+ int UNUSED(len))
{
AVDictionary **metadata = (AVDictionary **)data;
av_dict_set(metadata, propname, propvalue, 0);
@@ -1028,7 +1073,7 @@ static int start_ffmpeg_impl(FFMpegContext *context,
fmt->audio_codec = context->ffmpeg_audio_codec;
- BLI_strncpy(of->filename, name, sizeof(of->filename));
+ of->url = av_strdup(name);
/* set the codec to the user's selection */
switch (context->ffmpeg_type) {
case FFMPEG_AVI:
@@ -1093,9 +1138,11 @@ static int start_ffmpeg_impl(FFMpegContext *context,
if (!context->video_stream) {
if (error[0]) {
BKE_report(reports, RPT_ERROR, error);
+ PRINT("Video stream error: %s\n", error);
}
else {
BKE_report(reports, RPT_ERROR, "Error initializing video stream");
+ PRINT("Error initializing video stream");
}
goto fail;
}
@@ -1107,9 +1154,11 @@ static int start_ffmpeg_impl(FFMpegContext *context,
if (!context->audio_stream) {
if (error[0]) {
BKE_report(reports, RPT_ERROR, error);
+ PRINT("Audio stream error: %s\n", error);
}
else {
BKE_report(reports, RPT_ERROR, "Error initializing audio stream");
+ PRINT("Error initializing audio stream");
}
goto fail;
}
@@ -1117,6 +1166,7 @@ static int start_ffmpeg_impl(FFMpegContext *context,
if (!(fmt->flags & AVFMT_NOFILE)) {
if (avio_open(&of->pb, name, AVIO_FLAG_WRITE) < 0) {
BKE_report(reports, RPT_ERROR, "Could not open file for writing");
+ PRINT("Could not open file for writing\n");
goto fail;
}
}
@@ -1126,10 +1176,12 @@ static int start_ffmpeg_impl(FFMpegContext *context,
&of->metadata, context->stamp_data, ffmpeg_add_metadata_callback, false);
}
- if (avformat_write_header(of, NULL) < 0) {
+ int ret = avformat_write_header(of, NULL);
+ if (ret < 0) {
BKE_report(reports,
RPT_ERROR,
"Could not initialize streams, probably unsupported codec combination");
+ PRINT("Could not write media header: %s\n", av_err2str(ret));
goto fail;
}
@@ -1144,13 +1196,11 @@ fail:
avio_close(of->pb);
}
- if (context->video_stream && context->video_stream->codec) {
- avcodec_close(context->video_stream->codec);
+ if (context->video_stream) {
context->video_stream = NULL;
}
- if (context->audio_stream && context->audio_stream->codec) {
- avcodec_close(context->audio_stream->codec);
+ if (context->audio_stream) {
context->audio_stream = NULL;
}
@@ -1178,46 +1228,39 @@ fail:
*/
static void flush_ffmpeg(FFMpegContext *context)
{
- int ret = 0;
+ AVCodecContext *c = context->video_codec;
+ AVPacket *packet = av_packet_alloc();
- AVCodecContext *c = context->video_stream->codec;
- /* get the delayed frames */
- while (1) {
- int got_output;
- AVPacket packet = {0};
- av_init_packet(&packet);
+ avcodec_send_frame(c, NULL);
- ret = avcodec_encode_video2(c, &packet, NULL, &got_output);
- if (ret < 0) {
- fprintf(stderr, "Error encoding delayed frame %d\n", ret);
+ /* Get the packets frames. */
+ int ret = 1;
+ while (ret >= 0) {
+ ret = avcodec_receive_packet(c, packet);
+
+ if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
+ /* No more packets to flush. */
break;
}
- if (!got_output) {
+ if (ret < 0) {
+ fprintf(stderr, "Error encoding delayed frame: %s\n", av_err2str(ret));
break;
}
- if (packet.pts != AV_NOPTS_VALUE) {
- packet.pts = av_rescale_q(packet.pts, c->time_base, context->video_stream->time_base);
- PRINT("Video Frame PTS: %d\n", (int)packet.pts);
- }
- else {
- PRINT("Video Frame PTS: not set\n");
- }
- if (packet.dts != AV_NOPTS_VALUE) {
- packet.dts = av_rescale_q(packet.dts, c->time_base, context->video_stream->time_base);
- PRINT("Video Frame DTS: %d\n", (int)packet.dts);
- }
- else {
- PRINT("Video Frame DTS: not set\n");
- }
- packet.stream_index = context->video_stream->index;
- ret = av_interleaved_write_frame(context->outfile, &packet);
- if (ret != 0) {
- fprintf(stderr, "Error writing delayed frame %d\n", ret);
+ packet->stream_index = context->video_stream->index;
+ av_packet_rescale_ts(packet, c->time_base, context->video_stream->time_base);
+# ifdef FFMPEG_USE_DURATION_WORKAROUND
+ my_guess_pkt_duration(context->outfile, context->video_stream, packet);
+# endif
+
+ int write_ret = av_interleaved_write_frame(context->outfile, packet);
+ if (write_ret != 0) {
+ fprintf(stderr, "Error writing delayed frame: %s\n", av_err2str(write_ret));
break;
}
}
- avcodec_flush_buffers(context->video_stream->codec);
+
+ av_packet_free(&packet);
}
/* **********************************************************************
@@ -1315,7 +1358,8 @@ int BKE_ffmpeg_start(void *context_v,
success = start_ffmpeg_impl(context, rd, rectx, recty, suffix, reports);
# ifdef WITH_AUDASPACE
if (context->audio_stream) {
- AVCodecContext *c = context->audio_stream->codec;
+ AVCodecContext *c = context->audio_codec;
+
AUD_DeviceSpecs specs;
specs.channels = c->channels;
@@ -1342,10 +1386,6 @@ int BKE_ffmpeg_start(void *context_v,
specs.rate = rd->ffcodecdata.audio_mixrate;
context->audio_mixdown_device = BKE_sound_mixdown(
scene, specs, preview ? rd->psfra : rd->sfra, rd->ffcodecdata.audio_volume);
-# ifdef FFMPEG_CODEC_TIME_BASE
- c->time_base.den = specs.rate;
- c->time_base.num = 1;
-# endif
}
# endif
return success;
@@ -1386,8 +1426,8 @@ int BKE_ffmpeg_append(void *context_v,
// write_audio_frames(frame / (((double)rd->frs_sec) / rd->frs_sec_base));
if (context->video_stream) {
- avframe = generate_video_frame(context, (unsigned char *)pixels, reports);
- success = (avframe && write_video_frame(context, rd, frame - start_frame, avframe, reports));
+ avframe = generate_video_frame(context, (unsigned char *)pixels);
+ success = (avframe && write_video_frame(context, frame - start_frame, avframe, reports));
if (context->ffmpeg_autosplit) {
if (avio_tell(context->outfile->pb) > FFMPEG_AUTOSPLIT_SIZE) {
@@ -1416,9 +1456,11 @@ static void end_ffmpeg_impl(FFMpegContext *context, int is_autosplit)
context->audio_mixdown_device = NULL;
}
}
+# else
+ UNUSED_VARS(is_autosplit);
# endif
- if (context->video_stream && context->video_stream->codec) {
+ if (context->video_stream) {
PRINT("Flushing delayed frames...\n");
flush_ffmpeg(context);
}
@@ -1429,14 +1471,12 @@ static void end_ffmpeg_impl(FFMpegContext *context, int is_autosplit)
/* Close the video codec */
- if (context->video_stream != NULL && context->video_stream->codec != NULL) {
- avcodec_close(context->video_stream->codec);
+ if (context->video_stream != NULL) {
PRINT("zero video stream %p\n", context->video_stream);
context->video_stream = NULL;
}
- if (context->audio_stream != NULL && context->audio_stream->codec != NULL) {
- avcodec_close(context->audio_stream->codec);
+ if (context->audio_stream != NULL) {
context->audio_stream = NULL;
}
@@ -1455,6 +1495,16 @@ static void end_ffmpeg_impl(FFMpegContext *context, int is_autosplit)
avio_close(context->outfile->pb);
}
}
+
+ if (context->video_codec != NULL) {
+ avcodec_free_context(&context->video_codec);
+ context->video_codec = NULL;
+ }
+ if (context->audio_codec != NULL) {
+ avcodec_free_context(&context->audio_codec);
+ context->audio_codec = NULL;
+ }
+
if (context->outfile != NULL) {
avformat_free_context(context->outfile);
context->outfile = NULL;
@@ -1463,12 +1513,6 @@ static void end_ffmpeg_impl(FFMpegContext *context, int is_autosplit)
av_free(context->audio_input_buffer);
context->audio_input_buffer = NULL;
}
-# ifndef FFMPEG_HAVE_ENCODE_AUDIO2
- if (context->audio_output_buffer != NULL) {
- av_free(context->audio_output_buffer);
- context->audio_output_buffer = NULL;
- }
-# endif
if (context->audio_deinterleave_buffer != NULL) {
av_free(context->audio_deinterleave_buffer);
@@ -1548,12 +1592,12 @@ static IDProperty *BKE_ffmpeg_property_add(RenderData *rd,
switch (o->type) {
case AV_OPT_TYPE_INT:
case AV_OPT_TYPE_INT64:
- val.i = FFMPEG_DEF_OPT_VAL_INT(o);
+ val.i = o->default_val.i64;
idp_type = IDP_INT;
break;
case AV_OPT_TYPE_DOUBLE:
case AV_OPT_TYPE_FLOAT:
- val.f = FFMPEG_DEF_OPT_VAL_DOUBLE(o);
+ val.f = o->default_val.dbl;
idp_type = IDP_FLOAT;
break;
case AV_OPT_TYPE_STRING:
@@ -1657,56 +1701,7 @@ static void ffmpeg_set_expert_options(RenderData *rd)
IDP_FreePropertyContent(rd->ffcodecdata.properties);
}
- if (codec_id == AV_CODEC_ID_H264) {
- /*
- * All options here are for x264, but must be set via ffmpeg.
- * The names are therefore different - Search for "x264 to FFmpeg option mapping"
- * to get a list.
- */
-
- /*
- * Use CABAC coder. Using "coder:1", which should be equivalent,
- * crashes Blender for some reason. Either way - this is no big deal.
- */
- BKE_ffmpeg_property_add_string(rd, "video", "coder:vlc");
-
- /*
- * The other options were taken from the libx264-default.preset
- * included in the ffmpeg distribution.
- */
-
- /* This breaks compatibility for QT. */
- // BKE_ffmpeg_property_add_string(rd, "video", "flags:loop");
- BKE_ffmpeg_property_add_string(rd, "video", "cmp:chroma");
- BKE_ffmpeg_property_add_string(rd, "video", "partitions:parti4x4"); /* Deprecated. */
- BKE_ffmpeg_property_add_string(rd, "video", "partitions:partp8x8"); /* Deprecated. */
- BKE_ffmpeg_property_add_string(rd, "video", "partitions:partb8x8"); /* Deprecated. */
- BKE_ffmpeg_property_add_string(rd, "video", "me:hex");
- BKE_ffmpeg_property_add_string(rd, "video", "subq:6");
- BKE_ffmpeg_property_add_string(rd, "video", "me_range:16");
- BKE_ffmpeg_property_add_string(rd, "video", "qdiff:4");
- BKE_ffmpeg_property_add_string(rd, "video", "keyint_min:25");
- BKE_ffmpeg_property_add_string(rd, "video", "sc_threshold:40");
- BKE_ffmpeg_property_add_string(rd, "video", "i_qfactor:0.71");
- BKE_ffmpeg_property_add_string(rd, "video", "b_strategy:1");
- BKE_ffmpeg_property_add_string(rd, "video", "bf:3");
- BKE_ffmpeg_property_add_string(rd, "video", "refs:2");
- BKE_ffmpeg_property_add_string(rd, "video", "qcomp:0.6");
-
- BKE_ffmpeg_property_add_string(rd, "video", "trellis:0");
- BKE_ffmpeg_property_add_string(rd, "video", "weightb:1");
-# ifdef FFMPEG_HAVE_DEPRECATED_FLAGS2
- BKE_ffmpeg_property_add_string(rd, "video", "flags2:dct8x8");
- BKE_ffmpeg_property_add_string(rd, "video", "directpred:3");
- BKE_ffmpeg_property_add_string(rd, "video", "flags2:fastpskip");
- BKE_ffmpeg_property_add_string(rd, "video", "flags2:wpred");
-# else
- BKE_ffmpeg_property_add_string(rd, "video", "8x8dct:1");
- BKE_ffmpeg_property_add_string(rd, "video", "fast-pskip:1");
- BKE_ffmpeg_property_add_string(rd, "video", "wpredp:2");
-# endif
- }
- else if (codec_id == AV_CODEC_ID_DNXHD) {
+ if (codec_id == AV_CODEC_ID_DNXHD) {
if (rd->ffcodecdata.flags & FFMPEG_LOSSLESS_OUTPUT) {
BKE_ffmpeg_property_add_string(rd, "video", "mbd:rd");
}
@@ -1859,14 +1854,12 @@ bool BKE_ffmpeg_alpha_channel_is_supported(const RenderData *rd)
{
int codec = rd->ffcodecdata.codec;
-# ifdef FFMPEG_FFV1_ALPHA_SUPPORTED
- /* Visual Studio 2019 doesn't like #ifdef within ELEM(). */
- if (codec == AV_CODEC_ID_FFV1) {
- return true;
- }
-# endif
-
- return ELEM(codec, AV_CODEC_ID_QTRLE, AV_CODEC_ID_PNG, AV_CODEC_ID_VP9, AV_CODEC_ID_HUFFYUV);
+ return ELEM(codec,
+ AV_CODEC_ID_FFV1,
+ AV_CODEC_ID_QTRLE,
+ AV_CODEC_ID_PNG,
+ AV_CODEC_ID_VP9,
+ AV_CODEC_ID_HUFFYUV);
}
void *BKE_ffmpeg_context_create(void)
diff --git a/source/blender/blenkernel/nla_private.h b/source/blender/blenkernel/nla_private.h
index 706bcac4f17..71b5a74ddf7 100644
--- a/source/blender/blenkernel/nla_private.h
+++ b/source/blender/blenkernel/nla_private.h
@@ -82,6 +82,10 @@ typedef struct NlaEvalChannelSnapshot {
/** For an upper snapshot channel, marks values that should be blended. */
NlaValidMask blend_domain;
+ /** Only used for keyframe remapping. Any values not in the \a remap_domain will not be used
+ * for keyframe remapping. */
+ NlaValidMask remap_domain;
+
int length; /* Number of values in the property. */
bool is_base; /* Base snapshot of the channel. */
@@ -196,6 +200,13 @@ void nlasnapshot_blend(NlaEvalData *eval_data,
const float upper_influence,
NlaEvalSnapshot *r_blended_snapshot);
+void nlasnapshot_blend_get_inverted_upper_snapshot(NlaEvalData *eval_data,
+ NlaEvalSnapshot *lower_snapshot,
+ NlaEvalSnapshot *blended_snapshot,
+ const short upper_blendmode,
+ const float upper_influence,
+ NlaEvalSnapshot *r_upper_snapshot);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenlib/BLI_asan.h b/source/blender/blenlib/BLI_asan.h
index a2a44e164ab..c38ad6b39d0 100644
--- a/source/blender/blenlib/BLI_asan.h
+++ b/source/blender/blenlib/BLI_asan.h
@@ -21,7 +21,7 @@
# define __has_feature(x) 0
#endif
-#if defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer)
+#if (defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer)) && !defined(_MSC_VER)
# include "sanitizer/asan_interface.h"
#else
/* Ensure return value is used. Just using UNUSED_VARS results in a warning. */
diff --git a/source/blender/blenlib/BLI_color.hh b/source/blender/blenlib/BLI_color.hh
index e57a5109a66..3b01bbfb86e 100644
--- a/source/blender/blenlib/BLI_color.hh
+++ b/source/blender/blenlib/BLI_color.hh
@@ -22,41 +22,122 @@
namespace blender {
-struct Color4f {
- float r, g, b, a;
+/**
+ * CPP based color structures.
+ *
+ * Strongly typed color storage structures with space and alpha association.
+ * Will increase readability and visibility of typical mistakes when
+ * working with colors.
+ *
+ * The storage structs can hold 4 channels (r, g, b and a).
+ *
+ * Usage:
+ *
+ * Convert a theme byte color to a linearrgb premultiplied.
+ * ```
+ * ColorTheme4b theme_color;
+ * ColorSceneLinear4f<eAlpha::Premultiplied> linearrgb_color =
+ * BLI_color_convert_to_scene_linear(theme_color).premultiply_alpha();
+ * ```
+ *
+ * The API is structured to make most use of inlining. Most notable are space
+ * conversions done via `BLI_color_convert_to*` functions.
+ *
+ * - Conversions between spaces (theme <=> scene linear) should always be done by
+ * invoking the `BLI_color_convert_to*` methods.
+ * - Encoding colors (compressing to store colors inside a less precision storage)
+ * should be done by invoking the `encode` and `decode` methods.
+ * - Changing alpha association should be done by invoking `premultiply_alpha` or
+ * `unpremultiply_alpha` methods.
+ *
+ * # Encoding.
+ *
+ * Color encoding is used to store colors with less precision as in using `uint8_t` in
+ * stead of `float`. This encoding is supported for `eSpace::SceneLinear`.
+ * To make this clear to the developer the `eSpace::SceneLinearByteEncoded`
+ * space is added.
+ *
+ * # Precision
+ *
+ * Colors can be stored using `uint8_t` or `float` colors. The conversion
+ * between the two precisions are available as methods. (`to_4b` and
+ * `to_4f`).
+ *
+ * # Alpha conversion
+ *
+ * Alpha conversion is only supported in SceneLinear space.
+ *
+ * Extending this file:
+ * - This file can be extended with `ColorHex/Hsl/Hsv` for different representations
+ * of rgb based colors. `ColorHsl4f<eSpace::SceneLinear, eAlpha::Premultiplied>`
+ * - Add non RGB spaces/storages ColorXyz.
+ */
+
+/* Enumeration containing the different alpha modes. */
+enum class eAlpha {
+ /* Color and alpha are unassociated. */
+ Straight,
+ /* Color and alpha are associated. */
+ Premultiplied,
+};
+std::ostream &operator<<(std::ostream &stream, const eAlpha &space);
- Color4f() = default;
+/* Enumeration containing internal spaces. */
+enum class eSpace {
+ /* Blender theme color space (sRGB). */
+ Theme,
+ /* Blender internal scene linear color space (maps to SceneReference role in OCIO). */
+ SceneLinear,
+ /* Blender internal scene linear color space compressed to be stored in 4 uint8_t. */
+ SceneLinearByteEncoded,
+};
+std::ostream &operator<<(std::ostream &stream, const eSpace &space);
- Color4f(const float *rgba) : r(rgba[0]), g(rgba[1]), b(rgba[2]), a(rgba[3])
+/* Template class to store RGBA values with different precision, space and alpha association. */
+template<typename ChannelStorageType, eSpace Space, eAlpha Alpha> class ColorRGBA {
+ public:
+ ChannelStorageType r, g, b, a;
+ constexpr ColorRGBA() = default;
+
+ constexpr ColorRGBA(const ChannelStorageType rgba[4])
+ : r(rgba[0]), g(rgba[1]), b(rgba[2]), a(rgba[3])
{
}
- Color4f(float r, float g, float b, float a) : r(r), g(g), b(b), a(a)
+ constexpr ColorRGBA(const ChannelStorageType r,
+ const ChannelStorageType g,
+ const ChannelStorageType b,
+ const ChannelStorageType a)
+ : r(r), g(g), b(b), a(a)
{
}
- operator float *()
+ operator ChannelStorageType *()
{
return &r;
}
- operator const float *() const
+ operator const ChannelStorageType *() const
{
return &r;
}
- friend std::ostream &operator<<(std::ostream &stream, Color4f c)
+ friend std::ostream &operator<<(std::ostream &stream,
+ const ColorRGBA<ChannelStorageType, Space, Alpha> &c)
{
- stream << "(" << c.r << ", " << c.g << ", " << c.b << ", " << c.a << ")";
+
+ stream << Space << Alpha << "(" << c.r << ", " << c.g << ", " << c.b << ", " << c.a << ")";
return stream;
}
- friend bool operator==(const Color4f &a, const Color4f &b)
+ friend bool operator==(const ColorRGBA<ChannelStorageType, Space, Alpha> &a,
+ const ColorRGBA<ChannelStorageType, Space, Alpha> &b)
{
return a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a;
}
- friend bool operator!=(const Color4f &a, const Color4f &b)
+ friend bool operator!=(const ColorRGBA<ChannelStorageType, Space, Alpha> &a,
+ const ColorRGBA<ChannelStorageType, Space, Alpha> &b)
{
return !(a == b);
}
@@ -71,58 +152,209 @@ struct Color4f {
}
};
-struct Color4b {
- uint8_t r, g, b, a;
+/* Forward declarations of concrete color classes. */
+template<eAlpha Alpha> class ColorSceneLinear4f;
+template<eAlpha Alpha> class ColorSceneLinearByteEncoded4b;
+template<typename ChannelStorageType> class ColorTheme4;
- Color4b() = default;
+/* Forward declaration of precision conversion methods. */
+BLI_INLINE ColorTheme4<float> BLI_color_convert_to_theme4f(const ColorTheme4<uint8_t> &srgb4b);
+BLI_INLINE ColorTheme4<uint8_t> BLI_color_convert_to_theme4b(const ColorTheme4<float> &srgb4f);
- Color4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a) : r(r), g(g), b(b), a(a)
+template<eAlpha Alpha>
+class ColorSceneLinear4f final : public ColorRGBA<float, eSpace::SceneLinear, Alpha> {
+ public:
+ constexpr ColorSceneLinear4f<Alpha>() : ColorRGBA<float, eSpace::SceneLinear, Alpha>()
{
}
- Color4b(Color4f other)
+ constexpr ColorSceneLinear4f<Alpha>(const float *rgba)
+ : ColorRGBA<float, eSpace::SceneLinear, Alpha>(rgba)
{
- rgba_float_to_uchar(*this, other);
}
- operator Color4f() const
+ constexpr ColorSceneLinear4f<Alpha>(float r, float g, float b, float a)
+ : ColorRGBA<float, eSpace::SceneLinear, Alpha>(r, g, b, a)
{
- Color4f result;
- rgba_uchar_to_float(result, *this);
- return result;
}
- operator uint8_t *()
+ /**
+ * Convert to its byte encoded counter space.
+ **/
+ ColorSceneLinearByteEncoded4b<Alpha> encode() const
{
- return &r;
+ ColorSceneLinearByteEncoded4b<Alpha> encoded;
+ linearrgb_to_srgb_uchar4(encoded, *this);
+ return encoded;
}
- operator const uint8_t *() const
+ /**
+ * Convert color and alpha association to premultiplied alpha.
+ *
+ * Does nothing when color has already a premultiplied alpha.
+ */
+ ColorSceneLinear4f<eAlpha::Premultiplied> premultiply_alpha() const
{
- return &r;
+ if constexpr (Alpha == eAlpha::Straight) {
+ ColorSceneLinear4f<eAlpha::Premultiplied> premultiplied;
+ straight_to_premul_v4_v4(premultiplied, *this);
+ return premultiplied;
+ }
+ else {
+ return *this;
+ }
}
- friend std::ostream &operator<<(std::ostream &stream, Color4b c)
+ /**
+ * Convert color and alpha association to straight alpha.
+ *
+ * Does nothing when color has straighten alpha.
+ */
+ ColorSceneLinear4f<eAlpha::Straight> unpremultiply_alpha() const
{
- stream << "(" << c.r << ", " << c.g << ", " << c.b << ", " << c.a << ")";
- return stream;
+ if constexpr (Alpha == eAlpha::Premultiplied) {
+ ColorSceneLinear4f<eAlpha::Straight> straighten;
+ premul_to_straight_v4_v4(straighten, *this);
+ return straighten;
+ }
+ else {
+ return *this;
+ }
}
+};
- friend bool operator==(const Color4b &a, const Color4b &b)
+template<eAlpha Alpha>
+class ColorSceneLinearByteEncoded4b final
+ : public ColorRGBA<uint8_t, eSpace::SceneLinearByteEncoded, Alpha> {
+ public:
+ constexpr ColorSceneLinearByteEncoded4b() = default;
+
+ constexpr ColorSceneLinearByteEncoded4b(const uint8_t *rgba)
+ : ColorRGBA<uint8_t, eSpace::SceneLinearByteEncoded, Alpha>(rgba)
{
- return a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a;
}
- friend bool operator!=(const Color4b &a, const Color4b &b)
+ constexpr ColorSceneLinearByteEncoded4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
+ : ColorRGBA<uint8_t, eSpace::SceneLinearByteEncoded, Alpha>(r, g, b, a)
{
- return !(a == b);
}
- uint64_t hash() const
+ /**
+ * Convert to back to float color.
+ */
+ ColorSceneLinear4f<Alpha> decode() const
+ {
+ ColorSceneLinear4f<Alpha> decoded;
+ srgb_to_linearrgb_uchar4(decoded, *this);
+ return decoded;
+ }
+};
+
+/**
+ * Theme color template class.
+ *
+ * Don't use directly, but use `ColorTheme4b/ColorTheme4b`.
+ *
+ * This has been implemented as a template to improve inlining. When implemented as concrete
+ * classes (ColorTheme4b/f) the functions would be hidden in a compile unit what wouldn't be
+ * inlined.
+ */
+template<typename ChannelStorageType>
+class ColorTheme4 final : public ColorRGBA<ChannelStorageType, eSpace::Theme, eAlpha::Straight> {
+ public:
+ constexpr ColorTheme4() : ColorRGBA<ChannelStorageType, eSpace::Theme, eAlpha::Straight>(){};
+
+ constexpr ColorTheme4(const ChannelStorageType *rgba)
+ : ColorRGBA<ChannelStorageType, eSpace::Theme, eAlpha::Straight>(rgba)
+ {
+ }
+
+ constexpr ColorTheme4(ChannelStorageType r,
+ ChannelStorageType g,
+ ChannelStorageType b,
+ ChannelStorageType a)
+ : ColorRGBA<ChannelStorageType, eSpace::Theme, eAlpha::Straight>(r, g, b, a)
+ {
+ }
+
+ /**
+ * Change precision of color to float.
+ */
+ ColorTheme4<float> to_4f() const
{
- return static_cast<uint64_t>(r * 1283591) ^ static_cast<uint64_t>(g * 850177) ^
- static_cast<uint64_t>(b * 735391) ^ static_cast<uint64_t>(a * 442319);
+ if constexpr ((std::is_same_v<ChannelStorageType, uint8_t>)) {
+ return BLI_color_convert_to_theme4f(*this);
+ }
+ else {
+ return *this;
+ }
+ }
+
+ /**
+ * Change precision of color to uint8_t.
+ */
+ ColorTheme4<uint8_t> to_4b() const
+ {
+ if constexpr ((std::is_same_v<ChannelStorageType, float>)) {
+ return BLI_color_convert_to_theme4b(*this);
+ }
+ else {
+ return *this;
+ }
}
};
+using ColorTheme4b = ColorTheme4<uint8_t>;
+using ColorTheme4f = ColorTheme4<float>;
+
+BLI_INLINE ColorTheme4b BLI_color_convert_to_theme4b(const ColorTheme4f &theme4f)
+{
+ ColorTheme4b theme4b;
+ rgba_float_to_uchar(theme4b, theme4f);
+ return theme4b;
+}
+
+BLI_INLINE ColorTheme4f BLI_color_convert_to_theme4f(const ColorTheme4b &theme4b)
+{
+ ColorTheme4f theme4f;
+ rgba_uchar_to_float(theme4f, theme4b);
+ return theme4f;
+}
+
+BLI_INLINE ColorSceneLinear4f<eAlpha::Straight> BLI_color_convert_to_scene_linear(
+ const ColorTheme4f &theme4f)
+{
+ ColorSceneLinear4f<eAlpha::Straight> scene_linear;
+ srgb_to_linearrgb_v4(scene_linear, theme4f);
+ return scene_linear;
+}
+
+BLI_INLINE ColorSceneLinear4f<eAlpha::Straight> BLI_color_convert_to_scene_linear(
+ const ColorTheme4b &theme4b)
+{
+ ColorSceneLinear4f<eAlpha::Straight> scene_linear;
+ srgb_to_linearrgb_uchar4(scene_linear, theme4b);
+ return scene_linear;
+}
+
+BLI_INLINE ColorTheme4f
+BLI_color_convert_to_theme4f(const ColorSceneLinear4f<eAlpha::Straight> &scene_linear)
+{
+ ColorTheme4f theme4f;
+ linearrgb_to_srgb_v4(theme4f, scene_linear);
+ return theme4f;
+}
+
+BLI_INLINE ColorTheme4b
+BLI_color_convert_to_theme4b(const ColorSceneLinear4f<eAlpha::Straight> &scene_linear)
+{
+ ColorTheme4b theme4b;
+ linearrgb_to_srgb_uchar4(theme4b, scene_linear);
+ return theme4b;
+}
+
+/* Internal roles. For convenience to shorten the type names and hide complexity. */
+using ColorGeometry4f = ColorSceneLinear4f<eAlpha::Premultiplied>;
+using ColorGeometry4b = ColorSceneLinearByteEncoded4b<eAlpha::Premultiplied>;
+
} // namespace blender
diff --git a/source/blender/blenlib/BLI_compiler_attrs.h b/source/blender/blenlib/BLI_compiler_attrs.h
index 680c4bc78da..4b5a7d671f2 100644
--- a/source/blender/blenlib/BLI_compiler_attrs.h
+++ b/source/blender/blenlib/BLI_compiler_attrs.h
@@ -98,3 +98,10 @@
#else
# define ATTR_ALIGN(x) __attribute__((aligned(x)))
#endif
+
+/* Alignment directive */
+#ifdef _WIN64
+# define ALIGN_STRUCT __declspec(align(64))
+#else
+# define ALIGN_STRUCT
+#endif
diff --git a/source/blender/blenlib/BLI_endian_defines.h b/source/blender/blenlib/BLI_endian_defines.h
new file mode 100644
index 00000000000..31f28572c79
--- /dev/null
+++ b/source/blender/blenlib/BLI_endian_defines.h
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+/** \file
+ * \ingroup bli
+ */
+
+/* NOTE: these names are historic and could use a more generic prefix.
+ * This could be done as part of a bigger refactor. */
+
+/** ENDIAN_ORDER: indicates what endianness the platform where the file was written had. */
+#if !defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__)
+# error Either __BIG_ENDIAN__ or __LITTLE_ENDIAN__ must be defined.
+#endif
+
+#define L_ENDIAN 1
+#define B_ENDIAN 0
+
+#ifdef __BIG_ENDIAN__
+# define ENDIAN_ORDER B_ENDIAN
+#else
+# define ENDIAN_ORDER L_ENDIAN
+#endif
diff --git a/source/blender/blenlib/BLI_enumerable_thread_specific.hh b/source/blender/blenlib/BLI_enumerable_thread_specific.hh
new file mode 100644
index 00000000000..89be4cad848
--- /dev/null
+++ b/source/blender/blenlib/BLI_enumerable_thread_specific.hh
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#ifdef WITH_TBB
+# include <tbb/enumerable_thread_specific.h>
+#endif
+
+#include <atomic>
+#include <mutex>
+
+#include "BLI_map.hh"
+#include "BLI_utility_mixins.hh"
+
+namespace blender {
+
+namespace enumerable_thread_specific_utils {
+inline std::atomic<int> next_id = 0;
+inline thread_local int thread_id = next_id.fetch_add(1, std::memory_order_relaxed);
+} // namespace enumerable_thread_specific_utils
+
+/**
+ * This is mainly a wrapper for `tbb::enumerable_thread_specific`. The wrapper is needed because we
+ * want to be able to build without tbb.
+ *
+ * More features of the tbb version can be wrapped when they are used.
+ */
+template<typename T> class EnumerableThreadSpecific : NonCopyable, NonMovable {
+#ifdef WITH_TBB
+
+ private:
+ tbb::enumerable_thread_specific<T> values_;
+
+ public:
+ T &local()
+ {
+ return values_.local();
+ }
+
+#else /* WITH_TBB */
+
+ private:
+ std::mutex mutex_;
+ /* Maps thread ids to their corresponding values. The values are not embedded in the map, so that
+ * their addresses do not change when the map grows. */
+ Map<int, std::unique_ptr<T>> values_;
+
+ public:
+ T &local()
+ {
+ const int thread_id = enumerable_thread_specific_utils::thread_id;
+ std::lock_guard lock{mutex_};
+ return *values_.lookup_or_add_cb(thread_id, []() { return std::make_unique<T>(); });
+ }
+
+#endif /* WITH_TBB */
+};
+
+} // namespace blender
diff --git a/source/blender/blenlib/BLI_fileops.h b/source/blender/blenlib/BLI_fileops.h
index df80e720363..7cfecc798a7 100644
--- a/source/blender/blenlib/BLI_fileops.h
+++ b/source/blender/blenlib/BLI_fileops.h
@@ -34,6 +34,7 @@
#include <limits.h> /* for PATH_MAX */
#include "BLI_compiler_attrs.h"
+#include "BLI_fileops_types.h"
#include "BLI_utildefines.h"
#ifdef __cplusplus
@@ -87,7 +88,7 @@ typedef enum eFileAttributes {
FILE_ATTR_RESTRICTED = 1 << 6, /* Protected by OS. */
FILE_ATTR_TEMPORARY = 1 << 7, /* Used for temporary storage. */
FILE_ATTR_SPARSE_FILE = 1 << 8, /* Sparse File. */
- FILE_ATTR_OFFLINE = 1 << 9, /* Data is not immediately available. */
+ FILE_ATTR_OFFLINE = 1 << 9, /* Contents available after a short delay. */
FILE_ATTR_ALIAS = 1 << 10, /* Mac Alias or Windows LNK. File-based redirection. */
FILE_ATTR_REPARSE_POINT = 1 << 11, /* File has associated re-parse point. */
FILE_ATTR_SYMLINK = 1 << 12, /* Reference to another file. */
@@ -125,15 +126,20 @@ void BLI_filelist_free(struct direntry *filelist, const unsigned int nrentries);
void BLI_filelist_entry_size_to_string(const struct stat *st,
const uint64_t sz,
const bool compact,
- char r_size[]);
-void BLI_filelist_entry_mode_to_string(
- const struct stat *st, const bool compact, char r_mode1[], char r_mode2[], char r_mode3[]);
-void BLI_filelist_entry_owner_to_string(const struct stat *st, const bool compact, char r_owner[]);
+ char r_size[FILELIST_DIRENTRY_SIZE_LEN]);
+void BLI_filelist_entry_mode_to_string(const struct stat *st,
+ const bool compact,
+ char r_mode1[FILELIST_DIRENTRY_MODE_LEN],
+ char r_mode2[FILELIST_DIRENTRY_MODE_LEN],
+ char r_mode3[FILELIST_DIRENTRY_MODE_LEN]);
+void BLI_filelist_entry_owner_to_string(const struct stat *st,
+ const bool compact,
+ char r_owner[FILELIST_DIRENTRY_OWNER_LEN]);
void BLI_filelist_entry_datetime_to_string(const struct stat *st,
const int64_t ts,
const bool compact,
- char r_time[],
- char r_date[],
+ char r_time[FILELIST_DIRENTRY_TIME_LEN],
+ char r_date[FILELIST_DIRENTRY_DATE_LEN],
bool *r_is_today,
bool *r_is_yesterday);
diff --git a/source/blender/blenlib/BLI_float3.hh b/source/blender/blenlib/BLI_float3.hh
index cbc4d4ed366..04aae375889 100644
--- a/source/blender/blenlib/BLI_float3.hh
+++ b/source/blender/blenlib/BLI_float3.hh
@@ -245,6 +245,13 @@ struct float3 {
return result;
}
+ static float3 cross(const float3 &a, const float3 &b)
+ {
+ float3 result;
+ cross_v3_v3v3(result, a, b);
+ return result;
+ }
+
static float3 project(const float3 &a, const float3 &b)
{
float3 result;
diff --git a/source/blender/blenlib/BLI_float4x4.hh b/source/blender/blenlib/BLI_float4x4.hh
index f5ba91bc12c..396b0b1bd21 100644
--- a/source/blender/blenlib/BLI_float4x4.hh
+++ b/source/blender/blenlib/BLI_float4x4.hh
@@ -45,6 +45,37 @@ struct float4x4 {
return mat;
}
+ static float4x4 from_normalized_axis_data(const float3 location,
+ const float3 forward,
+ const float3 up)
+ {
+ BLI_ASSERT_UNIT_V3(forward);
+ BLI_ASSERT_UNIT_V3(up);
+ float4x4 matrix;
+ const float3 cross = float3::cross(forward, up);
+ matrix.values[0][0] = forward.x;
+ matrix.values[1][0] = cross.x;
+ matrix.values[2][0] = up.x;
+ matrix.values[3][0] = location.x;
+
+ matrix.values[0][1] = forward.y;
+ matrix.values[1][1] = cross.y;
+ matrix.values[2][1] = up.y;
+ matrix.values[3][1] = location.y;
+
+ matrix.values[0][2] = forward.z;
+ matrix.values[1][2] = cross.z;
+ matrix.values[2][2] = up.z;
+ matrix.values[3][2] = location.z;
+
+ matrix.values[0][3] = 0.0f;
+ matrix.values[1][3] = 0.0f;
+ matrix.values[2][3] = 0.0f;
+ matrix.values[3][3] = 1.0f;
+
+ return matrix;
+ }
+
static float4x4 identity()
{
float4x4 mat;
@@ -116,6 +147,19 @@ struct float4x4 {
return scale;
}
+ void apply_scale(const float scale)
+ {
+ values[0][0] *= scale;
+ values[0][1] *= scale;
+ values[0][2] *= scale;
+ values[1][0] *= scale;
+ values[1][1] *= scale;
+ values[1][2] *= scale;
+ values[2][0] *= scale;
+ values[2][1] *= scale;
+ values[2][2] *= scale;
+ }
+
float4x4 inverted() const
{
float4x4 result;
diff --git a/source/blender/blenlib/BLI_function_ref.hh b/source/blender/blenlib/BLI_function_ref.hh
index 57fffdc09b4..38e1ba593c5 100644
--- a/source/blender/blenlib/BLI_function_ref.hh
+++ b/source/blender/blenlib/BLI_function_ref.hh
@@ -16,6 +16,7 @@
#pragma once
+#include <optional>
#include <type_traits>
#include <utility>
@@ -139,6 +140,29 @@ template<typename Ret, typename... Params> class FunctionRef<Ret(Params...)> {
return callback_(callable_, std::forward<Params>(params)...);
}
+ using OptionalReturnValue = std::conditional_t<std::is_void_v<Ret>, void, std::optional<Ret>>;
+
+ /**
+ * Calls the referenced function if it is available.
+ * The return value is of type `std::optional<Ret>` if `Ret` is not `void`.
+ * Otherwise the return type is `void`.
+ */
+ OptionalReturnValue call_safe(Params... params) const
+ {
+ if constexpr (std::is_void_v<Ret>) {
+ if (callback_ == nullptr) {
+ return;
+ }
+ callback_(callable_, std::forward<Params>(params)...);
+ }
+ else {
+ if (callback_ == nullptr) {
+ return {};
+ }
+ return callback_(callable_, std::forward<Params>(params)...);
+ }
+ }
+
/**
* Returns true, when the `FunctionRef` references a function currently.
* If this returns false, the `FunctionRef` must not be called.
diff --git a/source/blender/blenlib/BLI_hash.hh b/source/blender/blenlib/BLI_hash.hh
index ac0c2e4260d..fbed321534c 100644
--- a/source/blender/blenlib/BLI_hash.hh
+++ b/source/blender/blenlib/BLI_hash.hh
@@ -85,9 +85,12 @@
namespace blender {
/**
- * If there is no other specialization of #DefaultHash for a given type, try to call `hash()` on
- * the value. If there is no such method, this will result in a compiler error. Usually that means
- * that you have to implement a hash function using one of three strategies listed above.
+ * If there is no other specialization of #DefaultHash for a given type, look for a hash function
+ * on the type itself. Implementing a `hash()` method on a type is often significantly easier than
+ * specializing #DefaultHash.
+ *
+ * To support heterogeneous lookup, a type can also implement a static `hash_as(const OtherType &)`
+ * function.
*
* In the case of an enum type, the default hash is just to cast the enum value to an integer.
*/
@@ -95,15 +98,23 @@ template<typename T> struct DefaultHash {
uint64_t operator()(const T &value) const
{
if constexpr (std::is_enum_v<T>) {
+ /* For enums use the value as hash directly. */
return (uint64_t)value;
}
else {
+ /* Try to call the `hash()` function on the value. */
+ /* If this results in a compiler error, no hash function for the type has been found. */
return value.hash();
}
}
template<typename U> uint64_t operator()(const U &value) const
{
+ /* Try calling the static `T::hash_as(value)` function with the given value. The returned hash
+ * should be "compatible" with `T::hash()`. Usually that means that if `value` is converted to
+ * `T` its hash does not change. */
+ /* If this results in a compiler error, no hash function for the heterogeneous lookup has been
+ * found. */
return T::hash_as(value);
}
};
diff --git a/source/blender/blenlib/BLI_index_mask.hh b/source/blender/blenlib/BLI_index_mask.hh
index 48b01edcd6f..f04c0e9c80a 100644
--- a/source/blender/blenlib/BLI_index_mask.hh
+++ b/source/blender/blenlib/BLI_index_mask.hh
@@ -110,7 +110,7 @@ class IndexMask {
}
/**
- * Returns the n-th index referenced by this IndexMask. The `index_mask` method returns an
+ * Returns the n-th index referenced by this IndexMask. The `index_range` method returns an
* IndexRange containing all indices that can be used as parameter here.
*/
int64_t operator[](int64_t n) const
diff --git a/source/blender/blenlib/BLI_iterator.h b/source/blender/blenlib/BLI_iterator.h
index c1cd1c21dac..198e42f340d 100644
--- a/source/blender/blenlib/BLI_iterator.h
+++ b/source/blender/blenlib/BLI_iterator.h
@@ -34,13 +34,19 @@ typedef struct BLI_Iterator {
typedef void (*IteratorCb)(BLI_Iterator *iter);
typedef void (*IteratorBeginCb)(BLI_Iterator *iter, void *data_in);
+#define BLI_ITERATOR_INIT(iter) \
+ { \
+ (iter)->skip = false; \
+ (iter)->valid = true; \
+ } \
+ ((void)0)
+
#define ITER_BEGIN(callback_begin, callback_next, callback_end, _data_in, _type, _instance) \
{ \
_type _instance; \
IteratorCb callback_end_func = callback_end; \
BLI_Iterator iter_macro; \
- iter_macro.skip = false; \
- iter_macro.valid = true; \
+ BLI_ITERATOR_INIT(&iter_macro); \
for (callback_begin(&iter_macro, (_data_in)); iter_macro.valid; callback_next(&iter_macro)) { \
if (iter_macro.skip) { \
iter_macro.skip = false; \
diff --git a/source/blender/blenlib/BLI_linear_allocator.hh b/source/blender/blenlib/BLI_linear_allocator.hh
index 47705b1d40b..7de6bcfdd98 100644
--- a/source/blender/blenlib/BLI_linear_allocator.hh
+++ b/source/blender/blenlib/BLI_linear_allocator.hh
@@ -129,10 +129,28 @@ template<typename Allocator = GuardedAllocator> class LinearAllocator : NonCopya
}
/**
+ * Construct multiple instances of a type in an array. The constructor of is called with the
+ * given arguments. The caller is responsible for calling the destructor (and not `delete`) on
+ * the constructed elements.
+ */
+ template<typename T, typename... Args>
+ MutableSpan<T> construct_array(int64_t size, Args &&... args)
+ {
+ MutableSpan<T> array = this->allocate_array<T>(size);
+ for (const int64_t i : IndexRange(size)) {
+ new (&array[i]) T(std::forward<Args>(args)...);
+ }
+ return array;
+ }
+
+ /**
* Copy the given array into a memory buffer provided by this allocator.
*/
template<typename T> MutableSpan<T> construct_array_copy(Span<T> src)
{
+ if (src.is_empty()) {
+ return {};
+ }
MutableSpan<T> dst = this->allocate_array<T>(src.size());
uninitialized_copy_n(src.data(), src.size(), dst.data());
return dst;
diff --git a/source/blender/blenlib/BLI_map.hh b/source/blender/blenlib/BLI_map.hh
index 9fa69853e44..4d254960f34 100644
--- a/source/blender/blenlib/BLI_map.hh
+++ b/source/blender/blenlib/BLI_map.hh
@@ -247,11 +247,11 @@ class Map {
{
this->add_new_as(std::move(key), std::move(value));
}
- template<typename ForwardKey, typename ForwardValue>
- void add_new_as(ForwardKey &&key, ForwardValue &&value)
+ template<typename ForwardKey, typename... ForwardValue>
+ void add_new_as(ForwardKey &&key, ForwardValue &&... value)
{
this->add_new__impl(
- std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash_(key));
+ std::forward<ForwardKey>(key), hash_(key), std::forward<ForwardValue>(value)...);
}
/**
@@ -277,11 +277,11 @@ class Map {
{
return this->add_as(std::move(key), std::move(value));
}
- template<typename ForwardKey, typename ForwardValue>
- bool add_as(ForwardKey &&key, ForwardValue &&value)
+ template<typename ForwardKey, typename... ForwardValue>
+ bool add_as(ForwardKey &&key, ForwardValue &&... value)
{
return this->add__impl(
- std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash_(key));
+ std::forward<ForwardKey>(key), hash_(key), std::forward<ForwardValue>(value)...);
}
/**
@@ -307,11 +307,11 @@ class Map {
{
return this->add_overwrite_as(std::move(key), std::move(value));
}
- template<typename ForwardKey, typename ForwardValue>
- bool add_overwrite_as(ForwardKey &&key, ForwardValue &&value)
+ template<typename ForwardKey, typename... ForwardValue>
+ bool add_overwrite_as(ForwardKey &&key, ForwardValue &&... value)
{
return this->add_overwrite__impl(
- std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash_(key));
+ std::forward<ForwardKey>(key), hash_(key), std::forward<ForwardValue>(value)...);
}
/**
@@ -413,12 +413,12 @@ class Map {
{
return this->pop_default_as(key, std::move(default_value));
}
- template<typename ForwardKey, typename ForwardValue>
- Value pop_default_as(const ForwardKey &key, ForwardValue &&default_value)
+ template<typename ForwardKey, typename... ForwardValue>
+ Value pop_default_as(const ForwardKey &key, ForwardValue &&... default_value)
{
Slot *slot = this->lookup_slot_ptr(key, hash_(key));
if (slot == nullptr) {
- return std::forward<ForwardValue>(default_value);
+ return Value(std::forward<ForwardValue>(default_value)...);
}
Value value = std::move(*slot->value());
slot->remove();
@@ -525,15 +525,15 @@ class Map {
{
return this->lookup_default_as(key, default_value);
}
- template<typename ForwardKey, typename ForwardValue>
- Value lookup_default_as(const ForwardKey &key, ForwardValue &&default_value) const
+ template<typename ForwardKey, typename... ForwardValue>
+ Value lookup_default_as(const ForwardKey &key, ForwardValue &&... default_value) const
{
const Value *ptr = this->lookup_ptr_as(key);
if (ptr != nullptr) {
return *ptr;
}
else {
- return std::forward<ForwardValue>(default_value);
+ return Value(std::forward<ForwardValue>(default_value)...);
}
}
@@ -557,11 +557,11 @@ class Map {
{
return this->lookup_or_add_as(std::move(key), std::move(value));
}
- template<typename ForwardKey, typename ForwardValue>
- Value &lookup_or_add_as(ForwardKey &&key, ForwardValue &&value)
+ template<typename ForwardKey, typename... ForwardValue>
+ Value &lookup_or_add_as(ForwardKey &&key, ForwardValue &&... value)
{
return this->lookup_or_add__impl(
- std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash_(key));
+ std::forward<ForwardKey>(key), hash_(key), std::forward<ForwardValue>(value)...);
}
/**
@@ -605,6 +605,37 @@ class Map {
}
/**
+ * Returns the key that is stored in the set that compares equal to the given key. This invokes
+ * undefined behavior when the key is not in the map.
+ */
+ const Key &lookup_key(const Key &key) const
+ {
+ return this->lookup_key_as(key);
+ }
+ template<typename ForwardKey> const Key &lookup_key_as(const ForwardKey &key) const
+ {
+ const Slot &slot = this->lookup_slot(key, hash_(key));
+ return *slot.key();
+ }
+
+ /**
+ * Returns a pointer to the key that is stored in the map that compares equal to the given key.
+ * If the key is not in the map, null is returned.
+ */
+ const Key *lookup_key_ptr(const Key &key) const
+ {
+ return this->lookup_key_ptr_as(key);
+ }
+ template<typename ForwardKey> const Key *lookup_key_ptr_as(const ForwardKey &key) const
+ {
+ const Slot *slot = this->lookup_slot_ptr(key, hash_(key));
+ if (slot == nullptr) {
+ return nullptr;
+ }
+ return slot->key();
+ }
+
+ /**
* Calls the provided callback for every key-value-pair in the map. The callback is expected
* to take a `const Key &` as first and a `const Value &` as second parameter.
*/
@@ -621,19 +652,23 @@ class Map {
}
}
- /**
- * A utility iterator that reduces the amount of code when implementing the actual iterators.
- * This uses the "curiously recurring template pattern" (CRTP).
- */
- template<typename SubIterator> struct BaseIterator {
+ /* Common base class for all iterators below. */
+ struct BaseIterator {
+ public:
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
+ protected:
+ /* We could have separate base iterators for const and non-const iterators, but that would add
+ * more complexity than benefits right now. */
Slot *slots_;
int64_t total_slots_;
int64_t current_slot_;
- BaseIterator(const Slot *slots, int64_t total_slots, int64_t current_slot)
+ friend Map;
+
+ public:
+ BaseIterator(const Slot *slots, const int64_t total_slots, const int64_t current_slot)
: slots_(const_cast<Slot *>(slots)), total_slots_(total_slots), current_slot_(current_slot)
{
}
@@ -667,11 +702,29 @@ class Map {
return !(a != b);
}
+ protected:
+ Slot &current_slot() const
+ {
+ return slots_[current_slot_];
+ }
+ };
+
+ /**
+ * A utility iterator that reduces the amount of code when implementing the actual iterators.
+ * This uses the "curiously recurring template pattern" (CRTP).
+ */
+ template<typename SubIterator> class BaseIteratorRange : public BaseIterator {
+ public:
+ BaseIteratorRange(const Slot *slots, int64_t total_slots, int64_t current_slot)
+ : BaseIterator(slots, total_slots, current_slot)
+ {
+ }
+
SubIterator begin() const
{
- for (int64_t i = 0; i < total_slots_; i++) {
- if (slots_[i].is_occupied()) {
- return SubIterator(slots_, total_slots_, i);
+ for (int64_t i = 0; i < this->total_slots_; i++) {
+ if (this->slots_[i].is_occupied()) {
+ return SubIterator(this->slots_, this->total_slots_, i);
}
}
return this->end();
@@ -679,23 +732,18 @@ class Map {
SubIterator end() const
{
- return SubIterator(slots_, total_slots_, total_slots_);
- }
-
- Slot &current_slot() const
- {
- return slots_[current_slot_];
+ return SubIterator(this->slots_, this->total_slots_, this->total_slots_);
}
};
- class KeyIterator final : public BaseIterator<KeyIterator> {
+ class KeyIterator final : public BaseIteratorRange<KeyIterator> {
public:
using value_type = Key;
using pointer = const Key *;
using reference = const Key &;
KeyIterator(const Slot *slots, int64_t total_slots, int64_t current_slot)
- : BaseIterator<KeyIterator>(slots, total_slots, current_slot)
+ : BaseIteratorRange<KeyIterator>(slots, total_slots, current_slot)
{
}
@@ -705,14 +753,14 @@ class Map {
}
};
- class ValueIterator final : public BaseIterator<ValueIterator> {
+ class ValueIterator final : public BaseIteratorRange<ValueIterator> {
public:
using value_type = Value;
using pointer = const Value *;
using reference = const Value &;
ValueIterator(const Slot *slots, int64_t total_slots, int64_t current_slot)
- : BaseIterator<ValueIterator>(slots, total_slots, current_slot)
+ : BaseIteratorRange<ValueIterator>(slots, total_slots, current_slot)
{
}
@@ -722,14 +770,14 @@ class Map {
}
};
- class MutableValueIterator final : public BaseIterator<MutableValueIterator> {
+ class MutableValueIterator final : public BaseIteratorRange<MutableValueIterator> {
public:
using value_type = Value;
using pointer = Value *;
using reference = Value &;
- MutableValueIterator(const Slot *slots, int64_t total_slots, int64_t current_slot)
- : BaseIterator<MutableValueIterator>(slots, total_slots, current_slot)
+ MutableValueIterator(Slot *slots, int64_t total_slots, int64_t current_slot)
+ : BaseIteratorRange<MutableValueIterator>(slots, total_slots, current_slot)
{
}
@@ -754,14 +802,14 @@ class Map {
}
};
- class ItemIterator final : public BaseIterator<ItemIterator> {
+ class ItemIterator final : public BaseIteratorRange<ItemIterator> {
public:
using value_type = Item;
using pointer = Item *;
using reference = Item &;
ItemIterator(const Slot *slots, int64_t total_slots, int64_t current_slot)
- : BaseIterator<ItemIterator>(slots, total_slots, current_slot)
+ : BaseIteratorRange<ItemIterator>(slots, total_slots, current_slot)
{
}
@@ -772,14 +820,14 @@ class Map {
}
};
- class MutableItemIterator final : public BaseIterator<MutableItemIterator> {
+ class MutableItemIterator final : public BaseIteratorRange<MutableItemIterator> {
public:
using value_type = MutableItem;
using pointer = MutableItem *;
using reference = MutableItem &;
- MutableItemIterator(const Slot *slots, int64_t total_slots, int64_t current_slot)
- : BaseIterator<MutableItemIterator>(slots, total_slots, current_slot)
+ MutableItemIterator(Slot *slots, int64_t total_slots, int64_t current_slot)
+ : BaseIteratorRange<MutableItemIterator>(slots, total_slots, current_slot)
{
}
@@ -840,6 +888,19 @@ class Map {
}
/**
+ * Remove the key-value-pair that the iterator is currently pointing at.
+ * It is valid to call this method while iterating over the map. However, after this method has
+ * been called, the removed element must not be accessed anymore.
+ */
+ void remove(const BaseIterator &iterator)
+ {
+ Slot &slot = iterator.current_slot();
+ BLI_assert(slot.is_occupied());
+ slot.remove();
+ removed_slots_++;
+ }
+
+ /**
* Print common statistics like size and collision count. This is useful for debugging purposes.
*/
void print_stats(StringRef name = "") const
@@ -982,7 +1043,7 @@ class Map {
SLOT_PROBING_BEGIN (ProbingStrategy, hash, new_slot_mask, slot_index) {
Slot &slot = new_slots[slot_index];
if (slot.is_empty()) {
- slot.occupy(std::move(*old_slot.key()), std::move(*old_slot.value()), hash);
+ slot.occupy(std::move(*old_slot.key()), hash, std::move(*old_slot.value()));
return;
}
}
@@ -996,8 +1057,8 @@ class Map {
new (this) Map(NoExceptConstructor(), allocator);
}
- template<typename ForwardKey, typename ForwardValue>
- void add_new__impl(ForwardKey &&key, ForwardValue &&value, uint64_t hash)
+ template<typename ForwardKey, typename... ForwardValue>
+ void add_new__impl(ForwardKey &&key, uint64_t hash, ForwardValue &&... value)
{
BLI_assert(!this->contains_as(key));
@@ -1005,7 +1066,7 @@ class Map {
MAP_SLOT_PROBING_BEGIN (hash, slot) {
if (slot.is_empty()) {
- slot.occupy(std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash);
+ slot.occupy(std::forward<ForwardKey>(key), hash, std::forward<ForwardValue>(value)...);
occupied_and_removed_slots_++;
return;
}
@@ -1013,14 +1074,14 @@ class Map {
MAP_SLOT_PROBING_END();
}
- template<typename ForwardKey, typename ForwardValue>
- bool add__impl(ForwardKey &&key, ForwardValue &&value, uint64_t hash)
+ template<typename ForwardKey, typename... ForwardValue>
+ bool add__impl(ForwardKey &&key, uint64_t hash, ForwardValue &&... value)
{
this->ensure_can_add();
MAP_SLOT_PROBING_BEGIN (hash, slot) {
if (slot.is_empty()) {
- slot.occupy(std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash);
+ slot.occupy(std::forward<ForwardKey>(key), hash, std::forward<ForwardValue>(value)...);
occupied_and_removed_slots_++;
return true;
}
@@ -1075,7 +1136,7 @@ class Map {
MAP_SLOT_PROBING_BEGIN (hash, slot) {
if (slot.is_empty()) {
- slot.occupy(std::forward<ForwardKey>(key), create_value(), hash);
+ slot.occupy(std::forward<ForwardKey>(key), hash, create_value());
occupied_and_removed_slots_++;
return *slot.value();
}
@@ -1086,14 +1147,14 @@ class Map {
MAP_SLOT_PROBING_END();
}
- template<typename ForwardKey, typename ForwardValue>
- Value &lookup_or_add__impl(ForwardKey &&key, ForwardValue &&value, uint64_t hash)
+ template<typename ForwardKey, typename... ForwardValue>
+ Value &lookup_or_add__impl(ForwardKey &&key, uint64_t hash, ForwardValue &&... value)
{
this->ensure_can_add();
MAP_SLOT_PROBING_BEGIN (hash, slot) {
if (slot.is_empty()) {
- slot.occupy(std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash);
+ slot.occupy(std::forward<ForwardKey>(key), hash, std::forward<ForwardValue>(value)...);
occupied_and_removed_slots_++;
return *slot.value();
}
@@ -1104,15 +1165,15 @@ class Map {
MAP_SLOT_PROBING_END();
}
- template<typename ForwardKey, typename ForwardValue>
- bool add_overwrite__impl(ForwardKey &&key, ForwardValue &&value, uint64_t hash)
+ template<typename ForwardKey, typename... ForwardValue>
+ bool add_overwrite__impl(ForwardKey &&key, uint64_t hash, ForwardValue &&... value)
{
auto create_func = [&](Value *ptr) {
- new (static_cast<void *>(ptr)) Value(std::forward<ForwardValue>(value));
+ new (static_cast<void *>(ptr)) Value(std::forward<ForwardValue>(value)...);
return true;
};
auto modify_func = [&](Value *ptr) {
- *ptr = std::forward<ForwardValue>(value);
+ *ptr = Value(std::forward<ForwardValue>(value)...);
return false;
};
return this->add_or_modify__impl(
@@ -1221,16 +1282,18 @@ template<typename Key, typename Value> class StdUnorderedMapWrapper {
map_.reserve(n);
}
- template<typename ForwardKey, typename ForwardValue>
- void add_new(ForwardKey &&key, ForwardValue &&value)
+ template<typename ForwardKey, typename... ForwardValue>
+ void add_new(ForwardKey &&key, ForwardValue &&... value)
{
- map_.insert({std::forward<ForwardKey>(key), std::forward<ForwardValue>(value)});
+ map_.insert({std::forward<ForwardKey>(key), Value(std::forward<ForwardValue>(value)...)});
}
- template<typename ForwardKey, typename ForwardValue>
- bool add(ForwardKey &&key, ForwardValue &&value)
+ template<typename ForwardKey, typename... ForwardValue>
+ bool add(ForwardKey &&key, ForwardValue &&... value)
{
- return map_.insert({std::forward<ForwardKey>(key), std::forward<ForwardValue>(value)}).second;
+ return map_
+ .insert({std::forward<ForwardKey>(key), Value(std::forward<ForwardValue>(value)...)})
+ .second;
}
bool contains(const Key &key) const
diff --git a/source/blender/blenlib/BLI_map_slots.hh b/source/blender/blenlib/BLI_map_slots.hh
index c0cb3091a8e..1b4ca11af41 100644
--- a/source/blender/blenlib/BLI_map_slots.hh
+++ b/source/blender/blenlib/BLI_map_slots.hh
@@ -195,11 +195,11 @@ template<typename Key, typename Value> class SimpleMapSlot {
* Change the state of this slot from empty/removed to occupied. The key/value has to be
* constructed by calling the constructor with the given key/value as parameter.
*/
- template<typename ForwardKey, typename ForwardValue>
- void occupy(ForwardKey &&key, ForwardValue &&value, uint64_t hash)
+ template<typename ForwardKey, typename... ForwardValue>
+ void occupy(ForwardKey &&key, uint64_t hash, ForwardValue &&... value)
{
BLI_assert(!this->is_occupied());
- new (&value_buffer_) Value(std::forward<ForwardValue>(value));
+ new (&value_buffer_) Value(std::forward<ForwardValue>(value)...);
this->occupy_no_value(std::forward<ForwardKey>(key), hash);
state_ = Occupied;
}
@@ -315,12 +315,12 @@ template<typename Key, typename Value, typename KeyInfo> class IntrusiveMapSlot
return is_equal(key, key_);
}
- template<typename ForwardKey, typename ForwardValue>
- void occupy(ForwardKey &&key, ForwardValue &&value, uint64_t hash)
+ template<typename ForwardKey, typename... ForwardValue>
+ void occupy(ForwardKey &&key, uint64_t hash, ForwardValue &&... value)
{
BLI_assert(!this->is_occupied());
BLI_assert(KeyInfo::is_not_empty_or_removed(key));
- new (&value_buffer_) Value(std::forward<ForwardValue>(value));
+ new (&value_buffer_) Value(std::forward<ForwardValue>(value)...);
this->occupy_no_value(std::forward<ForwardKey>(key), hash);
}
diff --git a/source/blender/blenlib/BLI_math_base.h b/source/blender/blenlib/BLI_math_base.h
index 028ca31a059..46219ad5493 100644
--- a/source/blender/blenlib/BLI_math_base.h
+++ b/source/blender/blenlib/BLI_math_base.h
@@ -154,6 +154,9 @@ MINLINE int max_iii(int a, int b, int c);
MINLINE int min_iiii(int a, int b, int c, int d);
MINLINE int max_iiii(int a, int b, int c, int d);
+MINLINE uint min_uu(uint a, uint b);
+MINLINE uint max_uu(uint a, uint b);
+
MINLINE size_t min_zz(size_t a, size_t b);
MINLINE size_t max_zz(size_t a, size_t b);
diff --git a/source/blender/blenlib/BLI_math_color.h b/source/blender/blenlib/BLI_math_color.h
index 26d2f1fcb29..28257ba418a 100644
--- a/source/blender/blenlib/BLI_math_color.h
+++ b/source/blender/blenlib/BLI_math_color.h
@@ -105,8 +105,8 @@ int constrain_rgb(float *r, float *g, float *b);
void minmax_rgb(short c[3]);
void hsv_clamp_v(float hsv[3], float v_max);
-void rgb_float_set_hue_float_offset(float *rgb, float hue_offset);
-void rgb_byte_set_hue_float_offset(unsigned char *rgb, float hue_offset);
+void rgb_float_set_hue_float_offset(float rgb[3], float hue_offset);
+void rgb_byte_set_hue_float_offset(unsigned char rgb[3], float hue_offset);
void rgb_uchar_to_float(float r_col[3], const unsigned char col_ub[3]);
void rgba_uchar_to_float(float r_col[4], const unsigned char col_ub[4]);
@@ -143,6 +143,7 @@ MINLINE void rgba_uchar_args_test_set(unsigned char col[4],
MINLINE void cpack_cpy_3ub(unsigned char r_col[3], const unsigned int pack);
void blackbody_temperature_to_rgb_table(float *r_table, int width, float min, float max);
+void wavelength_to_xyz_table(float *r_table, int width);
/********* lift/gamma/gain / ASC-CDL conversion ***********/
diff --git a/source/blender/blenlib/BLI_math_geom.h b/source/blender/blenlib/BLI_math_geom.h
index d767c2924d1..c744c5d13d3 100644
--- a/source/blender/blenlib/BLI_math_geom.h
+++ b/source/blender/blenlib/BLI_math_geom.h
@@ -119,10 +119,10 @@ float dist_signed_to_plane_v3(const float p[3], const float plane[4]);
float dist_to_plane_v3(const float p[3], const float plane[4]);
/* plane3 versions */
-float dist_signed_squared_to_plane3_v3(const float p[3], const float plane[4]);
-float dist_squared_to_plane3_v3(const float p[3], const float plane[4]);
-float dist_signed_to_plane3_v3(const float p[3], const float plane[4]);
-float dist_to_plane3_v3(const float p[3], const float plane[4]);
+float dist_signed_squared_to_plane3_v3(const float p[3], const float plane[3]);
+float dist_squared_to_plane3_v3(const float p[3], const float plane[3]);
+float dist_signed_to_plane3_v3(const float p[3], const float plane[3]);
+float dist_to_plane3_v3(const float p[3], const float plane[3]);
float dist_squared_to_line_segment_v3(const float p[3], const float l1[3], const float l2[3]);
float dist_to_line_segment_v3(const float p[3], const float l1[3], const float l2[3]);
@@ -778,7 +778,7 @@ MINLINE float dot_shsh(const float a[9], const float b[9]);
MINLINE float eval_shv3(float r[9], const float v[3]);
MINLINE float diffuse_shv3(float r[9], const float v[3]);
MINLINE void vec_fac_to_sh(float r[9], const float v[3], const float f);
-MINLINE void madd_sh_shfl(float r[9], const float sh[3], const float f);
+MINLINE void madd_sh_shfl(float r[9], const float sh[9], const float f);
/********************************* Form Factor *******************************/
diff --git a/source/blender/blenlib/BLI_math_matrix.h b/source/blender/blenlib/BLI_math_matrix.h
index 6324963f06a..54df88ca541 100644
--- a/source/blender/blenlib/BLI_math_matrix.h
+++ b/source/blender/blenlib/BLI_math_matrix.h
@@ -266,6 +266,9 @@ void orthogonalize_m4(float R[4][4], int axis);
void orthogonalize_m3_stable(float R[3][3], int axis, bool normalize);
void orthogonalize_m4_stable(float R[4][4], int axis, bool normalize);
+bool orthogonalize_m3_zero_axes(float R[3][3], const float unit_length);
+bool orthogonalize_m4_zero_axes(float R[4][4], const float unit_length);
+
bool is_orthogonal_m3(const float mat[3][3]);
bool is_orthogonal_m4(const float mat[4][4]);
bool is_orthonormal_m3(const float mat[3][3]);
@@ -359,7 +362,7 @@ void loc_quat_size_to_mat4(float R[4][4],
const float size[3]);
void loc_axisangle_size_to_mat4(float R[4][4],
const float loc[3],
- const float axis[4],
+ const float axis[3],
const float angle,
const float size[3]);
diff --git a/source/blender/blenlib/BLI_math_solvers.h b/source/blender/blenlib/BLI_math_solvers.h
index 13481e27e2a..39a79efc7e2 100644
--- a/source/blender/blenlib/BLI_math_solvers.h
+++ b/source/blender/blenlib/BLI_math_solvers.h
@@ -41,7 +41,7 @@ bool BLI_eigen_solve_selfadjoint_m3(const float m3[3][3],
float r_eigen_values[3],
float r_eigen_vectors[3][3]);
-void BLI_svd_m3(const float m3[3][3], float r_U[3][3], float r_S[], float r_V[3][3]);
+void BLI_svd_m3(const float m3[3][3], float r_U[3][3], float r_S[3], float r_V[3][3]);
/***************************** Simple Solvers ************************************/
diff --git a/source/blender/blenlib/BLI_math_vector.h b/source/blender/blenlib/BLI_math_vector.h
index bb1e1a1c38d..b43f86af670 100644
--- a/source/blender/blenlib/BLI_math_vector.h
+++ b/source/blender/blenlib/BLI_math_vector.h
@@ -146,7 +146,7 @@ MINLINE void mul_v3_v3(float r[3], const float a[3]);
MINLINE void mul_v3_v3v3(float r[3], const float a[3], const float b[3]);
MINLINE void mul_v4_fl(float r[4], float f);
MINLINE void mul_v4_v4(float r[4], const float a[4]);
-MINLINE void mul_v4_v4fl(float r[3], const float a[4], float f);
+MINLINE void mul_v4_v4fl(float r[4], const float a[4], float f);
MINLINE void mul_v2_v2_cw(float r[2], const float mat[2], const float vec[2]);
MINLINE void mul_v2_v2_ccw(float r[2], const float mat[2], const float vec[2]);
MINLINE float mul_project_m4_v3_zfac(const float mat[4][4],
@@ -177,7 +177,7 @@ MINLINE void negate_v2_v2(float r[2], const float a[2]);
MINLINE void negate_v3(float r[3]);
MINLINE void negate_v3_v3(float r[3], const float a[3]);
MINLINE void negate_v4(float r[4]);
-MINLINE void negate_v4_v4(float r[4], const float a[3]);
+MINLINE void negate_v4_v4(float r[4], const float a[4]);
MINLINE void negate_v3_short(short r[3]);
MINLINE void negate_v3_db(double r[3]);
@@ -323,11 +323,11 @@ void flip_v2_v2v2(float v[2], const float v1[2], const float v2[2]);
/********************************* Comparison ********************************/
-MINLINE bool is_zero_v2(const float a[3]) ATTR_WARN_UNUSED_RESULT;
+MINLINE bool is_zero_v2(const float a[2]) ATTR_WARN_UNUSED_RESULT;
MINLINE bool is_zero_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT;
MINLINE bool is_zero_v4(const float a[4]) ATTR_WARN_UNUSED_RESULT;
-bool is_finite_v2(const float a[3]) ATTR_WARN_UNUSED_RESULT;
+bool is_finite_v2(const float a[2]) ATTR_WARN_UNUSED_RESULT;
bool is_finite_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT;
bool is_finite_v4(const float a[4]) ATTR_WARN_UNUSED_RESULT;
diff --git a/source/blender/blenlib/BLI_memarena.h b/source/blender/blenlib/BLI_memarena.h
index 87a320e336d..d7798f12fcc 100644
--- a/source/blender/blenlib/BLI_memarena.h
+++ b/source/blender/blenlib/BLI_memarena.h
@@ -38,7 +38,8 @@ extern "C" {
struct MemArena;
typedef struct MemArena MemArena;
-struct MemArena *BLI_memarena_new(const size_t bufsize, const char *name) ATTR_WARN_UNUSED_RESULT
+struct MemArena *BLI_memarena_new(const size_t bufsize,
+ const char *name) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL
ATTR_NONNULL(2) ATTR_MALLOC;
void BLI_memarena_free(struct MemArena *ma) ATTR_NONNULL(1);
void BLI_memarena_use_malloc(struct MemArena *ma) ATTR_NONNULL(1);
diff --git a/source/blender/blenlib/BLI_memiter.h b/source/blender/blenlib/BLI_memiter.h
index c7a715309e1..abb1bec809d 100644
--- a/source/blender/blenlib/BLI_memiter.h
+++ b/source/blender/blenlib/BLI_memiter.h
@@ -36,15 +36,14 @@ struct BLI_memiter;
typedef struct BLI_memiter BLI_memiter;
/* warning, ATTR_MALLOC flag on BLI_memiter_alloc causes crash, see: D2756 */
-BLI_memiter *BLI_memiter_create(unsigned int chunk_size) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT;
-void *BLI_memiter_alloc(BLI_memiter *mi,
- unsigned int size) ATTR_RETURNS_NONNULL ATTR_WARN_UNUSED_RESULT
- ATTR_NONNULL(1);
+BLI_memiter *BLI_memiter_create(unsigned int chunk_size)
+ ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL;
+void *BLI_memiter_alloc(BLI_memiter *mi, unsigned int size)
+ ATTR_RETURNS_NONNULL ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(1);
void BLI_memiter_alloc_from(BLI_memiter *mi, uint elem_size, const void *data_from)
ATTR_NONNULL(1, 3);
-void *BLI_memiter_calloc(BLI_memiter *mi,
- unsigned int size) ATTR_RETURNS_NONNULL ATTR_WARN_UNUSED_RESULT
- ATTR_NONNULL(1);
+void *BLI_memiter_calloc(BLI_memiter *mi, unsigned int size)
+ ATTR_RETURNS_NONNULL ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(1);
void BLI_memiter_destroy(BLI_memiter *mi) ATTR_NONNULL(1);
void BLI_memiter_clear(BLI_memiter *mi) ATTR_NONNULL(1);
unsigned int BLI_memiter_count(const BLI_memiter *mi) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1);
@@ -59,11 +58,11 @@ typedef struct BLI_memiter_handle {
uint elem_left;
} BLI_memiter_handle;
-void BLI_memiter_iter_init(BLI_memiter *mi, BLI_memiter_handle *iter) ATTR_NONNULL();
-bool BLI_memiter_iter_done(const BLI_memiter_handle *iter) ATTR_NONNULL();
-void *BLI_memiter_iter_step(BLI_memiter_handle *iter) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
+void BLI_memiter_iter_init(BLI_memiter *mi, BLI_memiter_handle *iter) ATTR_NONNULL(1, 2);
+bool BLI_memiter_iter_done(const BLI_memiter_handle *iter) ATTR_NONNULL(1);
+void *BLI_memiter_iter_step(BLI_memiter_handle *iter) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1);
void *BLI_memiter_iter_step_size(BLI_memiter_handle *iter, uint *r_size) ATTR_WARN_UNUSED_RESULT
- ATTR_NONNULL();
+ ATTR_NONNULL(1, 2);
#ifdef __cplusplus
}
diff --git a/source/blender/blenlib/BLI_mempool.h b/source/blender/blenlib/BLI_mempool.h
index 3ee6f182593..4d9381093c7 100644
--- a/source/blender/blenlib/BLI_mempool.h
+++ b/source/blender/blenlib/BLI_mempool.h
@@ -38,9 +38,12 @@ typedef struct BLI_mempool BLI_mempool;
BLI_mempool *BLI_mempool_create(unsigned int esize,
unsigned int totelem,
unsigned int pchunk,
- unsigned int flag) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT;
-void *BLI_mempool_alloc(BLI_mempool *pool) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1);
-void *BLI_mempool_calloc(BLI_mempool *pool) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1);
+ unsigned int flag)
+ ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL;
+void *BLI_mempool_alloc(BLI_mempool *pool) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL
+ ATTR_NONNULL(1);
+void *BLI_mempool_calloc(BLI_mempool *pool)
+ ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(1);
void BLI_mempool_free(BLI_mempool *pool, void *addr) ATTR_NONNULL(1, 2);
void BLI_mempool_clear_ex(BLI_mempool *pool, const int totelem_reserve) ATTR_NONNULL(1);
void BLI_mempool_clear(BLI_mempool *pool) ATTR_NONNULL(1);
@@ -68,8 +71,6 @@ typedef struct BLI_mempool_iter {
BLI_mempool *pool;
struct BLI_mempool_chunk *curchunk;
unsigned int curindex;
-
- struct BLI_mempool_chunk **curchunk_threaded_shared;
} BLI_mempool_iter;
/* flag */
@@ -88,11 +89,6 @@ enum {
void BLI_mempool_iternew(BLI_mempool *pool, BLI_mempool_iter *iter) ATTR_NONNULL();
void *BLI_mempool_iterstep(BLI_mempool_iter *iter) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
-BLI_mempool_iter *BLI_mempool_iter_threadsafe_create(BLI_mempool *pool,
- const size_t num_iter) ATTR_WARN_UNUSED_RESULT
- ATTR_NONNULL();
-void BLI_mempool_iter_threadsafe_free(BLI_mempool_iter *iter_arr) ATTR_NONNULL();
-
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenlib/BLI_mesh_intersect.hh b/source/blender/blenlib/BLI_mesh_intersect.hh
index a7996939bb1..6b8e79f376c 100644
--- a/source/blender/blenlib/BLI_mesh_intersect.hh
+++ b/source/blender/blenlib/BLI_mesh_intersect.hh
@@ -29,6 +29,7 @@
# include "BLI_array.hh"
# include "BLI_double3.hh"
+# include "BLI_float3.hh"
# include "BLI_index_range.hh"
# include "BLI_map.hh"
# include "BLI_math_mpq.hh"
@@ -340,6 +341,63 @@ class IMesh {
std::ostream &operator<<(std::ostream &os, const IMesh &mesh);
/**
+ * A Bounding Box using floats, and a routine to detect possible
+ * intersection.
+ */
+struct BoundingBox {
+ float3 min{FLT_MAX, FLT_MAX, FLT_MAX};
+ float3 max{-FLT_MAX, -FLT_MAX, -FLT_MAX};
+
+ BoundingBox() = default;
+ BoundingBox(const float3 &min, const float3 &max) : min(min), max(max)
+ {
+ }
+
+ void combine(const float3 &p)
+ {
+ min.x = min_ff(min.x, p.x);
+ min.y = min_ff(min.y, p.y);
+ min.z = min_ff(min.z, p.z);
+ max.x = max_ff(max.x, p.x);
+ max.y = max_ff(max.y, p.y);
+ max.z = max_ff(max.z, p.z);
+ }
+
+ void combine(const double3 &p)
+ {
+ min.x = min_ff(min.x, static_cast<float>(p.x));
+ min.y = min_ff(min.y, static_cast<float>(p.y));
+ min.z = min_ff(min.z, static_cast<float>(p.z));
+ max.x = max_ff(max.x, static_cast<float>(p.x));
+ max.y = max_ff(max.y, static_cast<float>(p.y));
+ max.z = max_ff(max.z, static_cast<float>(p.z));
+ }
+
+ void combine(const BoundingBox &bb)
+ {
+ min.x = min_ff(min.x, bb.min.x);
+ min.y = min_ff(min.y, bb.min.y);
+ min.z = min_ff(min.z, bb.min.z);
+ max.x = max_ff(max.x, bb.max.x);
+ max.y = max_ff(max.y, bb.max.y);
+ max.z = max_ff(max.z, bb.max.z);
+ }
+
+ void expand(float pad)
+ {
+ min.x -= pad;
+ min.y -= pad;
+ min.z -= pad;
+ max.x += pad;
+ max.y += pad;
+ max.z += pad;
+ }
+};
+
+/** Assume bounding boxes have been expanded by a sufficient epsilon. */
+bool bbs_might_intersect(const BoundingBox &bb_a, const BoundingBox &bb_b);
+
+/**
* The output will have duplicate vertices merged and degenerate triangles ignored.
* If the input has overlapping co-planar triangles, then there will be
* as many duplicates as there are overlaps in each overlapping triangular region.
@@ -357,6 +415,9 @@ IMesh trimesh_nary_intersect(const IMesh &tm_in,
bool use_self,
IMeshArena *arena);
+/** Return an IMesh that is a triangulation of a mesh with general polygonal faces. */
+IMesh triangulate_polymesh(IMesh &imesh, IMeshArena *arena);
+
/** This has the side effect of populating verts in the #IMesh. */
void write_obj_mesh(IMesh &m, const std::string &objname);
diff --git a/source/blender/blenlib/BLI_mpq3.hh b/source/blender/blenlib/BLI_mpq3.hh
index fb5e2b61cdb..b9eda2ad7e1 100644
--- a/source/blender/blenlib/BLI_mpq3.hh
+++ b/source/blender/blenlib/BLI_mpq3.hh
@@ -218,6 +218,15 @@ struct mpq3 {
return a.x * b.x + a.y * b.y + a.z * b.z;
}
+ static mpq_class dot_with_buffer(const mpq3 &a, const mpq3 &b, mpq3 &buffer)
+ {
+ buffer = a;
+ buffer *= b;
+ buffer.x += buffer.y;
+ buffer.x += buffer.z;
+ return buffer.x;
+ }
+
static mpq3 cross(const mpq3 &a, const mpq3 &b)
{
return mpq3(a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]);
@@ -246,6 +255,13 @@ struct mpq3 {
return mpq3::dot(diff, diff);
}
+ static mpq_class distance_squared_with_buffer(const mpq3 &a, const mpq3 &b, mpq3 &buffer)
+ {
+ buffer = a;
+ buffer -= b;
+ return mpq3::dot(buffer, buffer);
+ }
+
static mpq3 interpolate(const mpq3 &a, const mpq3 &b, mpq_class t)
{
mpq_class s = 1 - t;
diff --git a/source/blender/blenlib/BLI_multi_value_map.hh b/source/blender/blenlib/BLI_multi_value_map.hh
index 98b55067a5c..fb52ac78243 100644
--- a/source/blender/blenlib/BLI_multi_value_map.hh
+++ b/source/blender/blenlib/BLI_multi_value_map.hh
@@ -73,6 +73,12 @@ template<typename Key, typename Value> class MultiValueMap {
vector.append(std::forward<ForwardValue>(value));
}
+ void add_non_duplicates(const Key &key, const Value &value)
+ {
+ Vector<Value> &vector = map_.lookup_or_add_default_as(key);
+ vector.append_non_duplicates(value);
+ }
+
/**
* Add all given values to the key.
*/
diff --git a/source/blender/blenlib/BLI_resource_collector.hh b/source/blender/blenlib/BLI_resource_scope.hh
index 70804ceb1f1..e5a698f25f1 100644
--- a/source/blender/blenlib/BLI_resource_collector.hh
+++ b/source/blender/blenlib/BLI_resource_scope.hh
@@ -19,12 +19,24 @@
/** \file
* \ingroup bli
*
- * A ResourceCollector holds an arbitrary set of resources, that will be destructed and/or freed
- * when the ResourceCollector is destructed. This is useful when some object has to take ownership
- * of other objects, but it does not know the type of those other objects.
+ * A `ResourceScope` takes ownership of arbitrary data/resources. Those resources will be
+ * destructed and/or freed when the `ResourceScope` is destructed. Destruction happens in reverse
+ * order. That allows resources do depend on other resources that have been added before.
*
- * Resources owned by the ResourceCollector will be freed in reverse order. That allows resources
- * that are added later to depend on resources that have been added before.
+ * A `ResourceScope` can also be thought of as a dynamic/runtime version of normal scopes in C++
+ * that are surrounded by braces.
+ *
+ * The main purpose of a `ResourceScope` is to allow functions to inject data into the scope of the
+ * caller. Traditionally, that can only be done by returning a value that owns everything it needs.
+ * This is fine until one has to deal with optional ownership. There are many ways to have a type
+ * optionally own something else, all of which are fairly annoying. A `ResourceScope` can be used
+ * to avoid having to deal with optional ownership. If some value would be owned, it can just be
+ * added to the resource scope, otherwise not.
+ *
+ * When a function takes a `ResourceScope` as parameter, it usually means that its return value
+ * will live at least as long as the passed in resources scope. However, it might also live longer.
+ * That can happen when the function returns a reference to statically allocated data or
+ * dynamically allocated data depending on some condition.
*/
#include "BLI_linear_allocator.hh"
@@ -33,7 +45,7 @@
namespace blender {
-class ResourceCollector : NonCopyable, NonMovable {
+class ResourceScope : NonCopyable, NonMovable {
private:
struct ResourceData {
void *data;
@@ -45,9 +57,9 @@ class ResourceCollector : NonCopyable, NonMovable {
Vector<ResourceData> m_resources;
public:
- ResourceCollector() = default;
+ ResourceScope() = default;
- ~ResourceCollector()
+ ~ResourceScope()
{
/* Free in reversed order. */
for (int64_t i = m_resources.size(); i--;) {
@@ -57,45 +69,53 @@ class ResourceCollector : NonCopyable, NonMovable {
}
/**
- * Pass ownership of the resource to the ResourceCollector. It will be destructed and freed when
+ * Pass ownership of the resource to the ResourceScope. It will be destructed and freed when
* the collector is destructed.
*/
- template<typename T> void add(std::unique_ptr<T> resource, const char *name)
+ template<typename T> T *add(std::unique_ptr<T> resource, const char *name)
{
BLI_assert(resource.get() != nullptr);
+ T *ptr = resource.release();
+ if (ptr == nullptr) {
+ return nullptr;
+ }
this->add(
- resource.release(),
+ ptr,
[](void *data) {
T *typed_data = reinterpret_cast<T *>(data);
delete typed_data;
},
name);
+ return ptr;
}
/**
- * Pass ownership of the resource to the ResourceCollector. It will be destructed when the
+ * Pass ownership of the resource to the ResourceScope. It will be destructed when the
* collector is destructed.
*/
- template<typename T> void add(destruct_ptr<T> resource, const char *name)
+ template<typename T> T *add(destruct_ptr<T> resource, const char *name)
{
+ T *ptr = resource.release();
+ if (ptr == nullptr) {
+ return nullptr;
+ }
/* There is no need to keep track of such types. */
if (std::is_trivially_destructible_v<T>) {
- resource.release();
- return;
+ return ptr;
}
- BLI_assert(resource.get() != nullptr);
this->add(
- resource.release(),
+ ptr,
[](void *data) {
T *typed_data = reinterpret_cast<T *>(data);
typed_data->~T();
},
name);
+ return ptr;
}
/**
- * Pass ownership of some resource to the ResourceCollector. The given free function will be
+ * Pass ownership of some resource to the ResourceScope. The given free function will be
* called when the collector is destructed.
*/
void add(void *userdata, void (*free)(void *), const char *name)
@@ -108,7 +128,7 @@ class ResourceCollector : NonCopyable, NonMovable {
}
/**
- * Construct an object with the same value in the ResourceCollector and return a reference to the
+ * Construct an object with the same value in the ResourceScope and return a reference to the
* new value.
*/
template<typename T> T &add_value(T &&value, const char *name)
@@ -126,7 +146,7 @@ class ResourceCollector : NonCopyable, NonMovable {
}
/**
- * Utility method to construct an instance of type T that will be owned by the ResourceCollector.
+ * Utility method to construct an instance of type T that will be owned by the ResourceScope.
*/
template<typename T, typename... Args> T &construct(const char *name, Args &&... args)
{
@@ -137,7 +157,7 @@ class ResourceCollector : NonCopyable, NonMovable {
}
/**
- * Print the names of all the resources that are owned by this ResourceCollector. This can be
+ * Print the names of all the resources that are owned by this ResourceScope. This can be
* useful for debugging.
*/
void print(StringRef name) const
diff --git a/source/blender/blenlib/BLI_span.hh b/source/blender/blenlib/BLI_span.hh
index fe511793c46..c3876d4eaf8 100644
--- a/source/blender/blenlib/BLI_span.hh
+++ b/source/blender/blenlib/BLI_span.hh
@@ -94,7 +94,7 @@ template<typename T> class Span {
using iterator = const T *;
using size_type = int64_t;
- private:
+ protected:
const T *data_ = nullptr;
int64_t size_ = 0;
@@ -477,7 +477,7 @@ template<typename T> class MutableSpan {
using iterator = T *;
using size_type = int64_t;
- private:
+ protected:
T *data_;
int64_t size_;
@@ -662,6 +662,16 @@ template<typename T> class MutableSpan {
}
/**
+ * Return a reference to the first element in the array. This invokes undefined behavior when the
+ * array is empty.
+ */
+ constexpr T &first() const
+ {
+ BLI_assert(size_ > 0);
+ return data_[0];
+ }
+
+ /**
* Returns a reference to the last element. This invokes undefined behavior when the array is
* empty.
*/
diff --git a/source/blender/blenlib/BLI_stack.hh b/source/blender/blenlib/BLI_stack.hh
index 19f77078c5b..d66316a95d9 100644
--- a/source/blender/blenlib/BLI_stack.hh
+++ b/source/blender/blenlib/BLI_stack.hh
@@ -232,13 +232,14 @@ class Stack {
{
this->push_as(std::move(value));
}
- template<typename ForwardT> void push_as(ForwardT &&value)
+ /* This is similar to `std::stack::emplace`. */
+ template<typename... ForwardT> void push_as(ForwardT &&... value)
{
if (top_ == top_chunk_->capacity_end) {
this->activate_next_chunk(1);
}
try {
- new (top_) T(std::forward<ForwardT>(value));
+ new (top_) T(std::forward<ForwardT>(value)...);
top_++;
size_++;
}
diff --git a/source/blender/blenlib/BLI_string.h b/source/blender/blenlib/BLI_string.h
index 096e7818013..5a80680c350 100644
--- a/source/blender/blenlib/BLI_string.h
+++ b/source/blender/blenlib/BLI_string.h
@@ -68,6 +68,11 @@ char *BLI_str_replaceN(const char *__restrict str,
void BLI_str_replace_char(char *string, char src, char dst) ATTR_NONNULL();
+bool BLI_str_replace_table_exact(char *string,
+ const size_t string_len,
+ const char *replace_table[][2],
+ int replace_table_len);
+
size_t BLI_snprintf(char *__restrict dst, size_t maxncpy, const char *__restrict format, ...)
ATTR_NONNULL(1, 3) ATTR_PRINTF_FORMAT(3, 4);
size_t BLI_snprintf_rlen(char *__restrict dst, size_t maxncpy, const char *__restrict format, ...)
diff --git a/source/blender/blenlib/BLI_task.h b/source/blender/blenlib/BLI_task.h
index 9e61686b37a..83ccfda7e38 100644
--- a/source/blender/blenlib/BLI_task.h
+++ b/source/blender/blenlib/BLI_task.h
@@ -67,17 +67,55 @@ typedef enum TaskPriority {
TASK_PRIORITY_HIGH,
} TaskPriority;
+/**
+ * Task isolation helps avoid unexpected task scheduling decisions that can lead to bugs if wrong
+ * assumptions were made. Typically that happens when doing "nested threading", i.e. one thread
+ * schedules a bunch of main-tasks and those spawn new sub-tasks.
+ *
+ * What can happen is that when a main-task waits for its sub-tasks to complete on other threads,
+ * another main-task is scheduled within the already running main-task. Generally, this is good,
+ * because it leads to better performance. However, sometimes code (often unintentionally) makes
+ * the assumption that at most one main-task runs on a thread at a time.
+ *
+ * The bugs often show themselves in two ways:
+ * - Deadlock, when a main-task holds a mutex while waiting for its sub-tasks to complete.
+ * - Data corruption, when a main-task makes wrong assumptions about a thread-local variable.
+ *
+ * Task isolation can avoid these bugs by making sure that a main-task does not start executing
+ * another main-task while waiting for its sub-tasks. More precisely, a function that runs in an
+ * isolated region is only allowed to run sub-tasks that were spawned in the same isolated region.
+ *
+ * Unfortunately, incorrect use of task isolation can lead to deadlocks itself. This can happen
+ * when threading primitives are used that separate spawning tasks from executing them. The problem
+ * occurs when a task is spawned in one isolated region while the tasks are waited for in another
+ * isolated region. In this setup, the thread that is waiting for the spawned tasks to complete
+ * cannot run the tasks itself. On a single thread, that causes a deadlock already. When there are
+ * multiple threads, another thread will typically run the task and avoid the deadlock. However, if
+ * this situation happens on all threads at the same time, all threads will deadlock. This happened
+ * in T88598.
+ */
+typedef enum TaskIsolation {
+ /* Do not use task isolation. Always use this when tasks are pushed recursively. */
+ TASK_ISOLATION_OFF,
+ /* Run each task in its own isolated region. */
+ TASK_ISOLATION_ON,
+} TaskIsolation;
+
typedef struct TaskPool TaskPool;
typedef void (*TaskRunFunction)(TaskPool *__restrict pool, void *taskdata);
typedef void (*TaskFreeFunction)(TaskPool *__restrict pool, void *taskdata);
/* Regular task pool that immediately starts executing tasks as soon as they
* are pushed, either on the current or another thread. */
-TaskPool *BLI_task_pool_create(void *userdata, TaskPriority priority);
+TaskPool *BLI_task_pool_create(void *userdata,
+ TaskPriority priority,
+ TaskIsolation task_isolation);
/* Background: always run tasks in a background thread, never immediately
* execute them. For running background jobs. */
-TaskPool *BLI_task_pool_create_background(void *userdata, TaskPriority priority);
+TaskPool *BLI_task_pool_create_background(void *userdata,
+ TaskPriority priority,
+ TaskIsolation task_isolation);
/* Background Serial: run tasks one after the other in the background,
* without parallelization between the tasks. */
@@ -87,7 +125,9 @@ TaskPool *BLI_task_pool_create_background_serial(void *userdata, TaskPriority pr
* as threads can't immediately start working. But it can be used if the data
* structures the threads operate on are not fully initialized until all tasks
* are created. */
-TaskPool *BLI_task_pool_create_suspended(void *userdata, TaskPriority priority);
+TaskPool *BLI_task_pool_create_suspended(void *userdata,
+ TaskPriority priority,
+ TaskIsolation task_isolation);
/* No threads: immediately executes tasks on the same thread. For debugging. */
TaskPool *BLI_task_pool_create_no_threads(void *userdata);
@@ -221,11 +261,14 @@ void BLI_task_parallel_listbase(struct ListBase *listbase,
const TaskParallelSettings *settings);
typedef struct MempoolIterData MempoolIterData;
-typedef void (*TaskParallelMempoolFunc)(void *userdata, MempoolIterData *iter);
+
+typedef void (*TaskParallelMempoolFunc)(void *userdata,
+ MempoolIterData *iter,
+ const TaskParallelTLS *__restrict tls);
void BLI_task_parallel_mempool(struct BLI_mempool *mempool,
void *userdata,
TaskParallelMempoolFunc func,
- const bool use_threading);
+ const TaskParallelSettings *settings);
/* TODO(sergey): Think of a better place for this. */
BLI_INLINE void BLI_parallel_range_settings_defaults(TaskParallelSettings *settings)
@@ -236,6 +279,12 @@ BLI_INLINE void BLI_parallel_range_settings_defaults(TaskParallelSettings *setti
settings->min_iter_per_thread = 0;
}
+BLI_INLINE void BLI_parallel_mempool_settings_defaults(TaskParallelSettings *settings)
+{
+ memset(settings, 0, sizeof(*settings));
+ settings->use_threading = true;
+}
+
/* Don't use this, store any thread specific data in tls->userdata_chunk instead.
* Only here for code to be removed. */
int BLI_task_parallel_thread_id(const TaskParallelTLS *tls);
diff --git a/source/blender/blenlib/BLI_vector.hh b/source/blender/blenlib/BLI_vector.hh
index 328d623787b..f6f546c90b9 100644
--- a/source/blender/blenlib/BLI_vector.hh
+++ b/source/blender/blenlib/BLI_vector.hh
@@ -161,7 +161,7 @@ class Vector {
}
/**
- * Create a vector from an array ref. The values in the vector are copy constructed.
+ * Create a vector from a span. The values in the vector are copy constructed.
*/
template<typename U, typename std::enable_if_t<std::is_convertible_v<U, T>> * = nullptr>
Vector(Span<U> values, Allocator allocator = {}) : Vector(NoExceptConstructor(), allocator)
@@ -437,13 +437,17 @@ class Vector {
*/
void append(const T &value)
{
- this->ensure_space_for_one();
- this->append_unchecked(value);
+ this->append_as(value);
}
void append(T &&value)
{
+ this->append_as(std::move(value));
+ }
+ /* This is similar to `std::vector::emplace_back`. */
+ template<typename... ForwardValue> void append_as(ForwardValue &&... value)
+ {
this->ensure_space_for_one();
- this->append_unchecked(std::move(value));
+ this->append_unchecked_as(std::forward<ForwardValue>(value)...);
}
/**
@@ -474,10 +478,18 @@ class Vector {
* behavior when not enough capacity has been reserved beforehand. Only use this in performance
* critical code.
*/
- template<typename ForwardT> void append_unchecked(ForwardT &&value)
+ void append_unchecked(const T &value)
+ {
+ this->append_unchecked_as(value);
+ }
+ void append_unchecked(T &&value)
+ {
+ this->append_unchecked_as(std::move(value));
+ }
+ template<typename... ForwardT> void append_unchecked_as(ForwardT &&... value)
{
BLI_assert(end_ < capacity_end_);
- new (end_) T(std::forward<ForwardT>(value));
+ new (end_) T(std::forward<ForwardT>(value)...);
end_++;
UPDATE_VECTOR_SIZE(this);
}
@@ -657,6 +669,21 @@ class Vector {
}
/**
+ * Return a reference to the first element in the vector.
+ * This invokes undefined behavior when the vector is empty.
+ */
+ const T &first() const
+ {
+ BLI_assert(this->size() > 0);
+ return *begin_;
+ }
+ T &first()
+ {
+ BLI_assert(this->size() > 0);
+ return *begin_;
+ }
+
+ /**
* Return how many values are currently stored in the vector.
*/
int64_t size() const
@@ -713,11 +740,12 @@ class Vector {
BLI_assert(index >= 0);
BLI_assert(index < this->size());
T *element_to_remove = begin_ + index;
- if (element_to_remove < end_) {
- *element_to_remove = std::move(*(end_ - 1));
+ T *last_element = end_ - 1;
+ if (element_to_remove < last_element) {
+ *element_to_remove = std::move(*last_element);
}
- end_--;
- end_->~T();
+ end_ = last_element;
+ last_element->~T();
UPDATE_VECTOR_SIZE(this);
}
diff --git a/source/blender/blenlib/BLI_vector_set.hh b/source/blender/blenlib/BLI_vector_set.hh
index 14b38d564cb..567e4fd8128 100644
--- a/source/blender/blenlib/BLI_vector_set.hh
+++ b/source/blender/blenlib/BLI_vector_set.hh
@@ -398,6 +398,55 @@ class VectorSet {
}
/**
+ * Return the index of the key in the vector. If the key is not in the set, add it and return its
+ * index.
+ */
+ int64_t index_of_or_add(const Key &key)
+ {
+ return this->index_of_or_add_as(key);
+ }
+ int64_t index_of_or_add(Key &&key)
+ {
+ return this->index_of_or_add_as(std::move(key));
+ }
+ template<typename ForwardKey> int64_t index_of_or_add_as(ForwardKey &&key)
+ {
+ return this->index_of_or_add__impl(std::forward<ForwardKey>(key), hash_(key));
+ }
+
+ /**
+ * Returns the key that is stored in the vector set that compares equal to the given key. This
+ * invokes undefined behavior when the key is not in the set.
+ */
+ const Key &lookup_key(const Key &key) const
+ {
+ return this->lookup_key_as(key);
+ }
+ template<typename ForwardKey> const Key &lookup_key_as(const ForwardKey &key) const
+ {
+ const Key *key_ptr = this->lookup_key_ptr_as(key);
+ BLI_assert(key_ptr != nullptr);
+ return *key_ptr;
+ }
+
+ /**
+ * Returns a pointer to the key that is stored in the vector set that compares equal to the given
+ * key. If the key is not in the set, null is returned.
+ */
+ const Key *lookup_key_ptr(const Key &key) const
+ {
+ return this->lookup_key_ptr_as(key);
+ }
+ template<typename ForwardKey> const Key *lookup_key_ptr_as(const ForwardKey &key) const
+ {
+ const int64_t index = this->index_of_try__impl(key, hash_(key));
+ if (index >= 0) {
+ return keys_ + index;
+ }
+ return nullptr;
+ }
+
+ /**
* Get a pointer to the beginning of the array containing all keys.
*/
const Key *data() const
@@ -484,6 +533,14 @@ class VectorSet {
}
/**
+ * Remove all keys from the vector set.
+ */
+ void clear()
+ {
+ this->noexcept_reset();
+ }
+
+ /**
* Get the number of collisions that the probing strategy has to go through to find the key or
* determine that it is not in the set.
*/
@@ -652,6 +709,26 @@ class VectorSet {
VECTOR_SET_SLOT_PROBING_END();
}
+ template<typename ForwardKey>
+ int64_t index_of_or_add__impl(ForwardKey &&key, const uint64_t hash)
+ {
+ this->ensure_can_add();
+
+ VECTOR_SET_SLOT_PROBING_BEGIN (hash, slot) {
+ if (slot.contains(key, is_equal_, hash, keys_)) {
+ return slot.index();
+ }
+ if (slot.is_empty()) {
+ const int64_t index = this->size();
+ new (keys_ + index) Key(std::forward<ForwardKey>(key));
+ slot.occupy(index, hash);
+ occupied_and_removed_slots_++;
+ return index;
+ }
+ }
+ VECTOR_SET_SLOT_PROBING_END();
+ }
+
Key pop__impl()
{
BLI_assert(this->size() > 0);
diff --git a/source/blender/blenlib/BLI_virtual_array.hh b/source/blender/blenlib/BLI_virtual_array.hh
index f9b0aaa7de6..1c02bce8411 100644
--- a/source/blender/blenlib/BLI_virtual_array.hh
+++ b/source/blender/blenlib/BLI_virtual_array.hh
@@ -37,6 +37,8 @@
* see of the increased compile time and binary size is worth it.
*/
+#include "BLI_array.hh"
+#include "BLI_index_mask.hh"
#include "BLI_span.hh"
namespace blender {
@@ -71,6 +73,11 @@ template<typename T> class VArray {
return size_ == 0;
}
+ IndexRange index_range() const
+ {
+ return IndexRange(size_);
+ }
+
/* Returns true when the virtual array is stored as a span internally. */
bool is_span() const
{
@@ -82,13 +89,13 @@ template<typename T> class VArray {
/* Returns the internally used span of the virtual array. This invokes undefined behavior is the
* virtual array is not stored as a span internally. */
- Span<T> get_span() const
+ Span<T> get_internal_span() const
{
BLI_assert(this->is_span());
if (size_ == 0) {
return {};
}
- return this->get_span_impl();
+ return this->get_internal_span_impl();
}
/* Returns true when the virtual array returns the same value for every index. */
@@ -102,20 +109,46 @@ template<typename T> class VArray {
/* Returns the value that is returned for every index. This invokes undefined behavior if the
* virtual array would not return the same value for every index. */
- T get_single() const
+ T get_internal_single() const
{
BLI_assert(this->is_single());
if (size_ == 1) {
return this->get(0);
}
- return this->get_single_impl();
+ return this->get_internal_single_impl();
}
+ /* Get the element at a specific index. Note that this operator cannot be used to assign values
+ * to an index, because the return value is not a reference. */
T operator[](const int64_t index) const
{
return this->get(index);
}
+ /* Copy the entire virtual array into a span. */
+ void materialize(MutableSpan<T> r_span) const
+ {
+ this->materialize(IndexMask(size_), r_span);
+ }
+
+ /* Copy some indices of the virtual array into a span. */
+ void materialize(IndexMask mask, MutableSpan<T> r_span) const
+ {
+ BLI_assert(mask.min_array_size() <= size_);
+ this->materialize_impl(mask, r_span);
+ }
+
+ void materialize_to_uninitialized(MutableSpan<T> r_span) const
+ {
+ this->materialize_to_uninitialized(IndexMask(size_), r_span);
+ }
+
+ void materialize_to_uninitialized(IndexMask mask, MutableSpan<T> r_span) const
+ {
+ BLI_assert(mask.min_array_size() <= size_);
+ this->materialize_to_uninitialized_impl(mask, r_span);
+ }
+
protected:
virtual T get_impl(const int64_t index) const = 0;
@@ -124,7 +157,7 @@ template<typename T> class VArray {
return false;
}
- virtual Span<T> get_span_impl() const
+ virtual Span<T> get_internal_span_impl() const
{
BLI_assert_unreachable();
return {};
@@ -135,56 +168,196 @@ template<typename T> class VArray {
return false;
}
- virtual T get_single_impl() const
+ virtual T get_internal_single_impl() const
{
/* Provide a default implementation, so that subclasses don't have to provide it. This method
* should never be called because `is_single_impl` returns false by default. */
BLI_assert_unreachable();
return T();
}
+
+ virtual void materialize_impl(IndexMask mask, MutableSpan<T> r_span) const
+ {
+ T *dst = r_span.data();
+ if (this->is_span()) {
+ const T *src = this->get_internal_span().data();
+ mask.foreach_index([&](const int64_t i) { dst[i] = src[i]; });
+ }
+ else if (this->is_single()) {
+ const T single = this->get_internal_single();
+ mask.foreach_index([&](const int64_t i) { dst[i] = single; });
+ }
+ else {
+ mask.foreach_index([&](const int64_t i) { dst[i] = this->get(i); });
+ }
+ }
+
+ virtual void materialize_to_uninitialized_impl(IndexMask mask, MutableSpan<T> r_span) const
+ {
+ T *dst = r_span.data();
+ if (this->is_span()) {
+ const T *src = this->get_internal_span().data();
+ mask.foreach_index([&](const int64_t i) { new (dst + i) T(src[i]); });
+ }
+ else if (this->is_single()) {
+ const T single = this->get_internal_single();
+ mask.foreach_index([&](const int64_t i) { new (dst + i) T(single); });
+ }
+ else {
+ mask.foreach_index([&](const int64_t i) { new (dst + i) T(this->get(i)); });
+ }
+ }
+};
+
+/* Similar to VArray, but the elements are mutable. */
+template<typename T> class VMutableArray : public VArray<T> {
+ public:
+ VMutableArray(const int64_t size) : VArray<T>(size)
+ {
+ }
+
+ void set(const int64_t index, T value)
+ {
+ BLI_assert(index >= 0);
+ BLI_assert(index < this->size_);
+ this->set_impl(index, std::move(value));
+ }
+
+ /* Copy the values from the source span to all elements in the virtual array. */
+ void set_all(Span<T> src)
+ {
+ BLI_assert(src.size() == this->size_);
+ this->set_all_impl(src);
+ }
+
+ MutableSpan<T> get_internal_span()
+ {
+ BLI_assert(this->is_span());
+ Span<T> span = static_cast<const VArray<T> *>(this)->get_internal_span();
+ return MutableSpan<T>(const_cast<T *>(span.data()), span.size());
+ }
+
+ protected:
+ virtual void set_impl(const int64_t index, T value) = 0;
+
+ virtual void set_all_impl(Span<T> src)
+ {
+ if (this->is_span()) {
+ const MutableSpan<T> span = this->get_internal_span();
+ initialized_copy_n(src.data(), this->size_, span.data());
+ }
+ else {
+ const int64_t size = this->size_;
+ for (int64_t i = 0; i < size; i++) {
+ this->set(i, src[i]);
+ }
+ }
+ }
};
+template<typename T> using VArrayPtr = std::unique_ptr<VArray<T>>;
+template<typename T> using VMutableArrayPtr = std::unique_ptr<VMutableArray<T>>;
+
/**
- * A virtual array implementation for a span. This class is final so that it can be devirtualized
- * by the compiler in some cases (e.g. when #devirtualize_varray is used).
+ * A virtual array implementation for a span. Methods in this class are final so that it can be
+ * devirtualized by the compiler in some cases (e.g. when #devirtualize_varray is used).
*/
-template<typename T> class VArrayForSpan final : public VArray<T> {
- private:
- const T *data_;
+template<typename T> class VArray_For_Span : public VArray<T> {
+ protected:
+ const T *data_ = nullptr;
public:
- VArrayForSpan(const Span<T> data) : VArray<T>(data.size()), data_(data.data())
+ VArray_For_Span(const Span<T> data) : VArray<T>(data.size()), data_(data.data())
{
}
protected:
- T get_impl(const int64_t index) const override
+ VArray_For_Span(const int64_t size) : VArray<T>(size)
+ {
+ }
+
+ T get_impl(const int64_t index) const final
+ {
+ return data_[index];
+ }
+
+ bool is_span_impl() const final
+ {
+ return true;
+ }
+
+ Span<T> get_internal_span_impl() const final
+ {
+ return Span<T>(data_, this->size_);
+ }
+};
+
+template<typename T> class VMutableArray_For_MutableSpan : public VMutableArray<T> {
+ protected:
+ T *data_ = nullptr;
+
+ public:
+ VMutableArray_For_MutableSpan(const MutableSpan<T> data)
+ : VMutableArray<T>(data.size()), data_(data.data())
+ {
+ }
+
+ protected:
+ VMutableArray_For_MutableSpan(const int64_t size) : VMutableArray<T>(size)
+ {
+ }
+
+ T get_impl(const int64_t index) const final
{
return data_[index];
}
+ void set_impl(const int64_t index, T value) final
+ {
+ data_[index] = value;
+ }
+
bool is_span_impl() const override
{
return true;
}
- Span<T> get_span_impl() const override
+ Span<T> get_internal_span_impl() const override
{
return Span<T>(data_, this->size_);
}
};
/**
+ * A variant of `VArray_For_Span` that owns the underlying data.
+ * The `Container` type has to implement a `size()` and `data()` method.
+ * The `data()` method has to return a pointer to the first element in the continuous array of
+ * elements.
+ */
+template<typename Container, typename T = typename Container::value_type>
+class VArray_For_ArrayContainer : public VArray_For_Span<T> {
+ private:
+ Container container_;
+
+ public:
+ VArray_For_ArrayContainer(Container container)
+ : VArray_For_Span<T>((int64_t)container.size()), container_(std::move(container))
+ {
+ this->data_ = container_.data();
+ }
+};
+
+/**
* A virtual array implementation that returns the same value for every index. This class is final
* so that it can be devirtualized by the compiler in some cases (e.g. when #devirtualize_varray is
* used).
*/
-template<typename T> class VArrayForSingle final : public VArray<T> {
+template<typename T> class VArray_For_Single final : public VArray<T> {
private:
T value_;
public:
- VArrayForSingle(T value, const int64_t size) : VArray<T>(size), value_(std::move(value))
+ VArray_For_Single(T value, const int64_t size) : VArray<T>(size), value_(std::move(value))
{
}
@@ -199,7 +372,7 @@ template<typename T> class VArrayForSingle final : public VArray<T> {
return this->size_ == 1;
}
- Span<T> get_span_impl() const override
+ Span<T> get_internal_span_impl() const override
{
return Span<T>(&value_, 1);
}
@@ -209,13 +382,207 @@ template<typename T> class VArrayForSingle final : public VArray<T> {
return true;
}
- T get_single_impl() const override
+ T get_internal_single_impl() const override
{
return value_;
}
};
/**
+ * In many cases a virtual array is a span internally. In those cases, access to individual could
+ * be much more efficient than calling a virtual method. When the underlying virtual array is not a
+ * span, this class allocates a new array and copies the values over.
+ *
+ * This should be used in those cases:
+ * - All elements in the virtual array are accessed multiple times.
+ * - In most cases, the underlying virtual array is a span, so no copy is necessary to benefit
+ * from faster access.
+ * - An API is called, that does not accept virtual arrays, but only spans.
+ */
+template<typename T> class VArray_Span final : public Span<T> {
+ private:
+ const VArray<T> &varray_;
+ Array<T> owned_data_;
+
+ public:
+ VArray_Span(const VArray<T> &varray) : Span<T>(), varray_(varray)
+ {
+ this->size_ = varray_.size();
+ if (varray_.is_span()) {
+ this->data_ = varray_.get_internal_span().data();
+ }
+ else {
+ owned_data_.~Array();
+ new (&owned_data_) Array<T>(varray_.size(), NoInitialization{});
+ varray_.materialize_to_uninitialized(owned_data_);
+ this->data_ = owned_data_.data();
+ }
+ }
+};
+
+/**
+ * Same as VArray_Span, but for a mutable span.
+ * The important thing to note is that when changing this span, the results might not be
+ * immediately reflected in the underlying virtual array (only when the virtual array is a span
+ * internally). The #save method can be used to write all changes to the underlying virtual array,
+ * if necessary.
+ */
+template<typename T> class VMutableArray_Span final : public MutableSpan<T> {
+ private:
+ VMutableArray<T> &varray_;
+ Array<T> owned_data_;
+ bool save_has_been_called_ = false;
+ bool show_not_saved_warning_ = true;
+
+ public:
+ /* Create a span for any virtual array. This is cheap when the virtual array is a span itself. If
+ * not, a new array has to be allocated as a wrapper for the underlying virtual array. */
+ VMutableArray_Span(VMutableArray<T> &varray, const bool copy_values_to_span = true)
+ : MutableSpan<T>(), varray_(varray)
+ {
+ this->size_ = varray_.size();
+ if (varray_.is_span()) {
+ this->data_ = varray_.get_internal_span().data();
+ }
+ else {
+ if (copy_values_to_span) {
+ owned_data_.~Array();
+ new (&owned_data_) Array<T>(varray_.size(), NoInitialization{});
+ varray_.materialize_to_uninitialized(owned_data_);
+ }
+ else {
+ owned_data_.reinitialize(varray_.size());
+ }
+ this->data_ = owned_data_.data();
+ }
+ }
+
+ ~VMutableArray_Span()
+ {
+ if (show_not_saved_warning_) {
+ if (!save_has_been_called_) {
+ std::cout << "Warning: Call `save()` to make sure that changes persist in all cases.\n";
+ }
+ }
+ }
+
+ /* Write back all values from a temporary allocated array to the underlying virtual array. */
+ void save()
+ {
+ save_has_been_called_ = true;
+ if (this->data_ != owned_data_.data()) {
+ return;
+ }
+ varray_.set_all(owned_data_);
+ }
+
+ void disable_not_applied_warning()
+ {
+ show_not_saved_warning_ = false;
+ }
+};
+
+/**
+ * This class makes it easy to create a virtual array for an existing function or lambda. The
+ * `GetFunc` should take a single `index` argument and return the value at that index.
+ */
+template<typename T, typename GetFunc> class VArray_For_Func final : public VArray<T> {
+ private:
+ GetFunc get_func_;
+
+ public:
+ VArray_For_Func(const int64_t size, GetFunc get_func)
+ : VArray<T>(size), get_func_(std::move(get_func))
+ {
+ }
+
+ private:
+ T get_impl(const int64_t index) const override
+ {
+ return get_func_(index);
+ }
+
+ void materialize_impl(IndexMask mask, MutableSpan<T> r_span) const override
+ {
+ T *dst = r_span.data();
+ mask.foreach_index([&](const int64_t i) { dst[i] = get_func_(i); });
+ }
+
+ void materialize_to_uninitialized_impl(IndexMask mask, MutableSpan<T> r_span) const override
+ {
+ T *dst = r_span.data();
+ mask.foreach_index([&](const int64_t i) { new (dst + i) T(get_func_(i)); });
+ }
+};
+
+template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)>
+class VArray_For_DerivedSpan : public VArray<ElemT> {
+ private:
+ const StructT *data_;
+
+ public:
+ VArray_For_DerivedSpan(const Span<StructT> data) : VArray<ElemT>(data.size()), data_(data.data())
+ {
+ }
+
+ private:
+ ElemT get_impl(const int64_t index) const override
+ {
+ return GetFunc(data_[index]);
+ }
+
+ void materialize_impl(IndexMask mask, MutableSpan<ElemT> r_span) const override
+ {
+ ElemT *dst = r_span.data();
+ mask.foreach_index([&](const int64_t i) { dst[i] = GetFunc(data_[i]); });
+ }
+
+ void materialize_to_uninitialized_impl(IndexMask mask, MutableSpan<ElemT> r_span) const override
+ {
+ ElemT *dst = r_span.data();
+ mask.foreach_index([&](const int64_t i) { new (dst + i) ElemT(GetFunc(data_[i])); });
+ }
+};
+
+template<typename StructT,
+ typename ElemT,
+ ElemT (*GetFunc)(const StructT &),
+ void (*SetFunc)(StructT &, ElemT)>
+class VMutableArray_For_DerivedSpan : public VMutableArray<ElemT> {
+ private:
+ StructT *data_;
+
+ public:
+ VMutableArray_For_DerivedSpan(const MutableSpan<StructT> data)
+ : VMutableArray<ElemT>(data.size()), data_(data.data())
+ {
+ }
+
+ private:
+ ElemT get_impl(const int64_t index) const override
+ {
+ return GetFunc(data_[index]);
+ }
+
+ void set_impl(const int64_t index, ElemT value) override
+ {
+ SetFunc(data_[index], std::move(value));
+ }
+
+ void materialize_impl(IndexMask mask, MutableSpan<ElemT> r_span) const override
+ {
+ ElemT *dst = r_span.data();
+ mask.foreach_index([&](const int64_t i) { dst[i] = GetFunc(data_[i]); });
+ }
+
+ void materialize_to_uninitialized_impl(IndexMask mask, MutableSpan<ElemT> r_span) const override
+ {
+ ElemT *dst = r_span.data();
+ mask.foreach_index([&](const int64_t i) { new (dst + i) ElemT(GetFunc(data_[i])); });
+ }
+};
+
+/**
* Generate multiple versions of the given function optimized for different virtual arrays.
* One has to be careful with nesting multiple devirtualizations, because that results in an
* exponential number of function instantiations (increasing compile time and binary size).
@@ -229,14 +596,14 @@ inline void devirtualize_varray(const VArray<T> &varray, const Func &func, bool
/* Support disabling the devirtualization to simplify benchmarking. */
if (enable) {
if (varray.is_single()) {
- /* `VArrayForSingle` can be used for devirtualization, because it is declared `final`. */
- const VArrayForSingle<T> varray_single{varray.get_single(), varray.size()};
+ /* `VArray_For_Single` can be used for devirtualization, because it is declared `final`. */
+ const VArray_For_Single<T> varray_single{varray.get_internal_single(), varray.size()};
func(varray_single);
return;
}
if (varray.is_span()) {
- /* `VArrayForSpan` can be used for devirtualization, because it is declared `final`. */
- const VArrayForSpan<T> varray_span{varray.get_span()};
+ /* `VArray_For_Span` can be used for devirtualization, because it is declared `final`. */
+ const VArray_For_Span<T> varray_span{varray.get_internal_span()};
func(varray_span);
return;
}
@@ -262,26 +629,26 @@ inline void devirtualize_varray2(const VArray<T1> &varray1,
const bool is_single1 = varray1.is_single();
const bool is_single2 = varray2.is_single();
if (is_span1 && is_span2) {
- const VArrayForSpan<T1> varray1_span{varray1.get_span()};
- const VArrayForSpan<T2> varray2_span{varray2.get_span()};
+ const VArray_For_Span<T1> varray1_span{varray1.get_internal_span()};
+ const VArray_For_Span<T2> varray2_span{varray2.get_internal_span()};
func(varray1_span, varray2_span);
return;
}
if (is_span1 && is_single2) {
- const VArrayForSpan<T1> varray1_span{varray1.get_span()};
- const VArrayForSingle<T2> varray2_single{varray2.get_single(), varray2.size()};
+ const VArray_For_Span<T1> varray1_span{varray1.get_internal_span()};
+ const VArray_For_Single<T2> varray2_single{varray2.get_internal_single(), varray2.size()};
func(varray1_span, varray2_single);
return;
}
if (is_single1 && is_span2) {
- const VArrayForSingle<T1> varray1_single{varray1.get_single(), varray1.size()};
- const VArrayForSpan<T2> varray2_span{varray2.get_span()};
+ const VArray_For_Single<T1> varray1_single{varray1.get_internal_single(), varray1.size()};
+ const VArray_For_Span<T2> varray2_span{varray2.get_internal_span()};
func(varray1_single, varray2_span);
return;
}
if (is_single1 && is_single2) {
- const VArrayForSingle<T1> varray1_single{varray1.get_single(), varray1.size()};
- const VArrayForSingle<T2> varray2_single{varray2.get_single(), varray2.size()};
+ const VArray_For_Single<T1> varray1_single{varray1.get_internal_single(), varray1.size()};
+ const VArray_For_Single<T2> varray2_single{varray2.get_internal_single(), varray2.size()};
func(varray1_single, varray2_single);
return;
}
diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt
index f4aef277595..26b9d641d12 100644
--- a/source/blender/blenlib/CMakeLists.txt
+++ b/source/blender/blenlib/CMakeLists.txt
@@ -39,6 +39,7 @@ set(SRC
intern/BLI_args.c
intern/BLI_array.c
intern/BLI_assert.c
+ intern/BLI_color.cc
intern/BLI_dial_2d.c
intern/BLI_dynstr.c
intern/BLI_filelist.c
@@ -144,6 +145,9 @@ set(SRC
intern/winstuff.c
intern/winstuff_dir.c
+ # Private headers.
+ intern/BLI_mempool_private.h
+
# Header as source (included in C files above).
intern/kdtree_impl.h
intern/list_sort_impl.h
@@ -185,6 +189,7 @@ set(SRC
BLI_edgehash.h
BLI_endian_switch.h
BLI_endian_switch_inline.h
+ BLI_enumerable_thread_specific.hh
BLI_expr_pylike_eval.h
BLI_fileops.h
BLI_fileops_types.h
@@ -259,11 +264,12 @@ set(SRC
BLI_rand.h
BLI_rand.hh
BLI_rect.h
- BLI_resource_collector.hh
+ BLI_resource_scope.hh
BLI_scanfill.h
BLI_session_uuid.h
BLI_set.hh
BLI_set_slots.hh
+ BLI_simd.h
BLI_smallhash.h
BLI_sort.h
BLI_sort_utils.h
@@ -388,6 +394,7 @@ if(WITH_GTESTS)
tests/BLI_array_store_test.cc
tests/BLI_array_test.cc
tests/BLI_array_utils_test.cc
+ tests/BLI_color_test.cc
tests/BLI_delaunay_2d_test.cc
tests/BLI_disjoint_set_test.cc
tests/BLI_edgehash_test.cc
diff --git a/source/blender/blenlib/intern/BLI_color.cc b/source/blender/blenlib/intern/BLI_color.cc
new file mode 100644
index 00000000000..6dcef4f4688
--- /dev/null
+++ b/source/blender/blenlib/intern/BLI_color.cc
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+#include "BLI_color.hh"
+
+namespace blender {
+
+std::ostream &operator<<(std::ostream &stream, const eAlpha &space)
+{
+ switch (space) {
+ case eAlpha::Straight: {
+ stream << "Straight";
+ break;
+ }
+ case eAlpha::Premultiplied: {
+ stream << "Premultiplied";
+ break;
+ }
+ }
+ return stream;
+}
+
+std::ostream &operator<<(std::ostream &stream, const eSpace &space)
+{
+ switch (space) {
+ case eSpace::Theme: {
+ stream << "Theme";
+ break;
+ }
+ case eSpace::SceneLinear: {
+ stream << "SceneLinear";
+ break;
+ }
+ case eSpace::SceneLinearByteEncoded: {
+ stream << "SceneLinearByteEncoded";
+ break;
+ }
+ }
+ return stream;
+}
+
+} // namespace blender
diff --git a/source/blender/blenlib/intern/BLI_dial_2d.c b/source/blender/blenlib/intern/BLI_dial_2d.c
index 7363233d573..786fdd219a3 100644
--- a/source/blender/blenlib/intern/BLI_dial_2d.c
+++ b/source/blender/blenlib/intern/BLI_dial_2d.c
@@ -78,7 +78,7 @@ float BLI_dial_angle(Dial *dial, const float current_position[2])
cosval = dot_v2v2(current_direction, dial->initial_direction);
sinval = cross_v2v2(current_direction, dial->initial_direction);
- /* clamp to avoid nans in acos */
+ /* Clamp to avoid NAN's in #acos */
angle = atan2f(sinval, cosval);
/* change of sign, we passed the 180 degree threshold. This means we need to add a turn.
diff --git a/source/blender/blenlib/intern/BLI_mempool.c b/source/blender/blenlib/intern/BLI_mempool.c
index 75f934c1fb8..d3000ef38e8 100644
--- a/source/blender/blenlib/intern/BLI_mempool.c
+++ b/source/blender/blenlib/intern/BLI_mempool.c
@@ -36,7 +36,8 @@
#include "BLI_utildefines.h"
-#include "BLI_mempool.h" /* own include */
+#include "BLI_mempool.h" /* own include */
+#include "BLI_mempool_private.h" /* own include */
#include "MEM_guardedalloc.h"
@@ -541,8 +542,12 @@ void BLI_mempool_iternew(BLI_mempool *pool, BLI_mempool_iter *iter)
iter->pool = pool;
iter->curchunk = pool->chunks;
iter->curindex = 0;
+}
- iter->curchunk_threaded_shared = NULL;
+static void mempool_threadsafe_iternew(BLI_mempool *pool, BLI_mempool_threadsafe_iter *ts_iter)
+{
+ BLI_mempool_iternew(pool, &ts_iter->iter);
+ ts_iter->curchunk_threaded_shared = NULL;
}
/**
@@ -558,33 +563,31 @@ void BLI_mempool_iternew(BLI_mempool *pool, BLI_mempool_iter *iter)
*
* See BLI_task_parallel_mempool implementation for detailed usage example.
*/
-BLI_mempool_iter *BLI_mempool_iter_threadsafe_create(BLI_mempool *pool, const size_t num_iter)
+ParallelMempoolTaskData *mempool_iter_threadsafe_create(BLI_mempool *pool, const size_t num_iter)
{
BLI_assert(pool->flag & BLI_MEMPOOL_ALLOW_ITER);
- BLI_mempool_iter *iter_arr = MEM_mallocN(sizeof(*iter_arr) * num_iter, __func__);
+ ParallelMempoolTaskData *iter_arr = MEM_mallocN(sizeof(*iter_arr) * num_iter, __func__);
BLI_mempool_chunk **curchunk_threaded_shared = MEM_mallocN(sizeof(void *), __func__);
- BLI_mempool_iternew(pool, iter_arr);
-
- *curchunk_threaded_shared = iter_arr->curchunk;
- iter_arr->curchunk_threaded_shared = curchunk_threaded_shared;
+ mempool_threadsafe_iternew(pool, &iter_arr->ts_iter);
+ *curchunk_threaded_shared = iter_arr->ts_iter.iter.curchunk;
+ iter_arr->ts_iter.curchunk_threaded_shared = curchunk_threaded_shared;
for (size_t i = 1; i < num_iter; i++) {
- iter_arr[i] = iter_arr[0];
- *curchunk_threaded_shared = iter_arr[i].curchunk = ((*curchunk_threaded_shared) ?
- (*curchunk_threaded_shared)->next :
- NULL);
+ iter_arr[i].ts_iter = iter_arr[0].ts_iter;
+ *curchunk_threaded_shared = iter_arr[i].ts_iter.iter.curchunk =
+ ((*curchunk_threaded_shared) ? (*curchunk_threaded_shared)->next : NULL);
}
return iter_arr;
}
-void BLI_mempool_iter_threadsafe_free(BLI_mempool_iter *iter_arr)
+void mempool_iter_threadsafe_destroy(ParallelMempoolTaskData *iter_arr)
{
- BLI_assert(iter_arr->curchunk_threaded_shared != NULL);
+ BLI_assert(iter_arr->ts_iter.curchunk_threaded_shared != NULL);
- MEM_freeN(iter_arr->curchunk_threaded_shared);
+ MEM_freeN(iter_arr->ts_iter.curchunk_threaded_shared);
MEM_freeN(iter_arr);
}
@@ -605,19 +608,6 @@ static void *bli_mempool_iternext(BLI_mempool_iter *iter)
if (iter->curindex == iter->pool->pchunk) {
iter->curindex = 0;
- if (iter->curchunk_threaded_shared) {
- while (1) {
- iter->curchunk = *iter->curchunk_threaded_shared;
- if (iter->curchunk == NULL) {
- return ret;
- }
- if (atomic_cas_ptr((void **)iter->curchunk_threaded_shared,
- iter->curchunk,
- iter->curchunk->next) == iter->curchunk) {
- break;
- }
- }
- }
iter->curchunk = iter->curchunk->next;
}
@@ -659,19 +649,54 @@ void *BLI_mempool_iterstep(BLI_mempool_iter *iter)
}
else {
iter->curindex = 0;
- if (iter->curchunk_threaded_shared) {
- for (iter->curchunk = *iter->curchunk_threaded_shared;
- (iter->curchunk != NULL) && (atomic_cas_ptr((void **)iter->curchunk_threaded_shared,
- iter->curchunk,
- iter->curchunk->next) != iter->curchunk);
- iter->curchunk = *iter->curchunk_threaded_shared) {
- /* pass. */
- }
-
- if (UNLIKELY(iter->curchunk == NULL)) {
- return (ret->freeword == FREEWORD) ? NULL : ret;
- }
+ iter->curchunk = iter->curchunk->next;
+ if (UNLIKELY(iter->curchunk == NULL)) {
+ return (ret->freeword == FREEWORD) ? NULL : ret;
}
+ curnode = CHUNK_DATA(iter->curchunk);
+ }
+ } while (ret->freeword == FREEWORD);
+
+ return ret;
+}
+
+/**
+ * A version of #BLI_mempool_iterstep that uses
+ * #BLI_mempool_threadsafe_iter.curchunk_threaded_shared for threaded iteration support.
+ * (threaded section noted in comments).
+ */
+void *mempool_iter_threadsafe_step(BLI_mempool_threadsafe_iter *ts_iter)
+{
+ BLI_mempool_iter *iter = &ts_iter->iter;
+ if (UNLIKELY(iter->curchunk == NULL)) {
+ return NULL;
+ }
+
+ const uint esize = iter->pool->esize;
+ BLI_freenode *curnode = POINTER_OFFSET(CHUNK_DATA(iter->curchunk), (esize * iter->curindex));
+ BLI_freenode *ret;
+ do {
+ ret = curnode;
+
+ if (++iter->curindex != iter->pool->pchunk) {
+ curnode = POINTER_OFFSET(curnode, esize);
+ }
+ else {
+ iter->curindex = 0;
+
+ /* Begin unique to the `threadsafe` version of this function. */
+ for (iter->curchunk = *ts_iter->curchunk_threaded_shared;
+ (iter->curchunk != NULL) && (atomic_cas_ptr((void **)ts_iter->curchunk_threaded_shared,
+ iter->curchunk,
+ iter->curchunk->next) != iter->curchunk);
+ iter->curchunk = *ts_iter->curchunk_threaded_shared) {
+ /* pass. */
+ }
+ if (UNLIKELY(iter->curchunk == NULL)) {
+ return (ret->freeword == FREEWORD) ? NULL : ret;
+ }
+ /* End `threadsafe` exception. */
+
iter->curchunk = iter->curchunk->next;
if (UNLIKELY(iter->curchunk == NULL)) {
return (ret->freeword == FREEWORD) ? NULL : ret;
diff --git a/source/blender/blenlib/intern/BLI_mempool_private.h b/source/blender/blenlib/intern/BLI_mempool_private.h
new file mode 100644
index 00000000000..e1c8205c80c
--- /dev/null
+++ b/source/blender/blenlib/intern/BLI_mempool_private.h
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2008 Blender Foundation.
+ * All rights reserved.
+ */
+
+#pragma once
+
+/** \file
+ * \ingroup bli
+ *
+ * Shared logic for #BLI_task_parallel_mempool to create a threaded iterator,
+ * without exposing the these functions publicly.
+ */
+
+#include "BLI_compiler_attrs.h"
+
+#include "BLI_mempool.h"
+#include "BLI_task.h"
+
+typedef struct BLI_mempool_threadsafe_iter {
+ BLI_mempool_iter iter;
+ struct BLI_mempool_chunk **curchunk_threaded_shared;
+} BLI_mempool_threadsafe_iter;
+
+typedef struct ParallelMempoolTaskData {
+ BLI_mempool_threadsafe_iter ts_iter;
+ TaskParallelTLS tls;
+} ParallelMempoolTaskData;
+
+ParallelMempoolTaskData *mempool_iter_threadsafe_create(BLI_mempool *pool, const size_t num_iter)
+ ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
+void mempool_iter_threadsafe_destroy(ParallelMempoolTaskData *iter_arr) ATTR_NONNULL();
+
+void *mempool_iter_threadsafe_step(BLI_mempool_threadsafe_iter *iter);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/blenlib/intern/array_utils.c b/source/blender/blenlib/intern/array_utils.c
index 57302f1ff81..25261e82cc9 100644
--- a/source/blender/blenlib/intern/array_utils.c
+++ b/source/blender/blenlib/intern/array_utils.c
@@ -337,10 +337,10 @@ bool _bli_array_iter_spiral_square(const void *arr_v,
center[1] < arr_shape[1]);
const char *arr = arr_v;
- const int stride[2] = {arr_shape[1] * (int)elem_size, (int)elem_size};
+ const int stride[2] = {arr_shape[0] * (int)elem_size, (int)elem_size};
/* Test center first. */
- int ofs[2] = {center[0] * stride[0], center[1] * stride[1]};
+ int ofs[2] = {center[0] * stride[1], center[1] * stride[0]};
if (test_fn(arr + ofs[0] + ofs[1], user_data)) {
return true;
}
diff --git a/source/blender/blenlib/intern/delaunay_2d.cc b/source/blender/blenlib/intern/delaunay_2d.cc
index 06a749ab921..9444d1a29cb 100644
--- a/source/blender/blenlib/intern/delaunay_2d.cc
+++ b/source/blender/blenlib/intern/delaunay_2d.cc
@@ -334,9 +334,6 @@ template<typename T> class CDT_state {
T epsilon;
explicit CDT_state(int num_input_verts, int num_input_edges, int num_input_faces, T epsilon);
- ~CDT_state()
- {
- }
};
template<typename T> CDTArrangement<T>::~CDTArrangement()
diff --git a/source/blender/blenlib/intern/fileops.c b/source/blender/blenlib/intern/fileops.c
index 106bd5bc793..107c27da6a2 100644
--- a/source/blender/blenlib/intern/fileops.c
+++ b/source/blender/blenlib/intern/fileops.c
@@ -171,7 +171,7 @@ size_t BLI_gzip_mem_to_file_at_pos(
z_stream strm;
unsigned char out[CHUNK];
- fseek(file, gz_stream_offset, 0);
+ BLI_fseek(file, gz_stream_offset, 0);
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
@@ -217,7 +217,7 @@ size_t BLI_ungzip_file_to_mem_at_pos(void *buf, size_t len, FILE *file, size_t g
size_t chunk = 256 * 1024;
unsigned char in[CHUNK];
- fseek(file, gz_stream_offset, 0);
+ BLI_fseek(file, gz_stream_offset, 0);
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
diff --git a/source/blender/blenlib/intern/freetypefont.c b/source/blender/blenlib/intern/freetypefont.c
index e755a8e75b5..98da85e3a8c 100644
--- a/source/blender/blenlib/intern/freetypefont.c
+++ b/source/blender/blenlib/intern/freetypefont.c
@@ -135,7 +135,6 @@ static VChar *freetypechar_to_vchar(FT_Face face, FT_ULong charcode, VFontData *
nu->type = CU_BEZIER;
nu->pntsu = onpoints[j];
nu->resolu = 8;
- nu->flag = CU_2D;
nu->flagu = CU_NURB_CYCLIC;
nu->bezt = bezt;
diff --git a/source/blender/blenlib/intern/list_sort_impl.h b/source/blender/blenlib/intern/list_sort_impl.h
index 8f979ba5b0b..680044f9ccb 100644
--- a/source/blender/blenlib/intern/list_sort_impl.h
+++ b/source/blender/blenlib/intern/list_sort_impl.h
@@ -17,13 +17,13 @@
/** \file
* \ingroup bli
*
- * Common implementation of linked-list a non-recursive mergesort.
+ * Common implementation of linked-list a non-recursive merge-sort.
*
- * Originally from Mono's eglib, adapted for portable inclusion.
+ * Originally from Mono's `eglib`, adapted for portable inclusion.
* This file is to be directly included in C-source,
* with defines to control its use.
*
- * This code requires a typedef named `SORT_IMPL_LINKTYPE` for the list node.
+ * This code requires a `typedef` named `SORT_IMPL_LINKTYPE` for the list node.
* It is assumed that the list type is the type of a pointer to a list
* node, and that the node has a field named 'next' that implements to
* the linked list. No additional invariant is maintained
diff --git a/source/blender/blenlib/intern/math_base_inline.c b/source/blender/blenlib/intern/math_base_inline.c
index 6481fac5a14..c1db9ec1a69 100644
--- a/source/blender/blenlib/intern/math_base_inline.c
+++ b/source/blender/blenlib/intern/math_base_inline.c
@@ -192,6 +192,13 @@ MINLINE double ratiod(double min, double max, double pos)
return range == 0 ? 0 : ((pos - min) / range);
}
+/* Map a normalized value, i.e. from interval [0, 1] to interval [a, b] */
+MINLINE float scalenorm(float a, float b, float x)
+{
+ BLI_assert(x <= 1 && x >= 0);
+ return (x * (b - a)) + a;
+}
+
/* used for zoom values*/
MINLINE float power_of_2(float val)
{
@@ -507,6 +514,15 @@ MINLINE int max_ii(int a, int b)
return (b < a) ? a : b;
}
+MINLINE uint min_uu(uint a, uint b)
+{
+ return (a < b) ? a : b;
+}
+MINLINE uint max_uu(uint a, uint b)
+{
+ return (b < a) ? a : b;
+}
+
MINLINE float min_fff(float a, float b, float c)
{
return min_ff(min_ff(a, b), c);
diff --git a/source/blender/blenlib/intern/math_color.c b/source/blender/blenlib/intern/math_color.c
index 8fd2802a547..abcb3139dc7 100644
--- a/source/blender/blenlib/intern/math_color.c
+++ b/source/blender/blenlib/intern/math_color.c
@@ -713,3 +713,75 @@ void blackbody_temperature_to_rgb_table(float *r_table, int width, float min, fl
r_table[i * 4 + 3] = 0.0f;
}
}
+
+/* ****************************** wavelength ******************************** */
+/* Wavelength to RGB. */
+
+/* CIE colour matching functions xBar, yBar, and zBar for
+ * wavelengths from 380 through 780 nanometers, every 5
+ * nanometers.
+ * For a wavelength lambda in this range:
+ * cie_colour_match[(lambda - 380) / 5][0] = xBar
+ * cie_colour_match[(lambda - 380) / 5][1] = yBar
+ * cie_colour_match[(lambda - 380) / 5][2] = zBar */
+
+static float cie_colour_match[81][3] = {
+ {0.0014f, 0.0000f, 0.0065f}, {0.0022f, 0.0001f, 0.0105f}, {0.0042f, 0.0001f, 0.0201f},
+ {0.0076f, 0.0002f, 0.0362f}, {0.0143f, 0.0004f, 0.0679f}, {0.0232f, 0.0006f, 0.1102f},
+ {0.0435f, 0.0012f, 0.2074f}, {0.0776f, 0.0022f, 0.3713f}, {0.1344f, 0.0040f, 0.6456f},
+ {0.2148f, 0.0073f, 1.0391f}, {0.2839f, 0.0116f, 1.3856f}, {0.3285f, 0.0168f, 1.6230f},
+ {0.3483f, 0.0230f, 1.7471f}, {0.3481f, 0.0298f, 1.7826f}, {0.3362f, 0.0380f, 1.7721f},
+ {0.3187f, 0.0480f, 1.7441f}, {0.2908f, 0.0600f, 1.6692f}, {0.2511f, 0.0739f, 1.5281f},
+ {0.1954f, 0.0910f, 1.2876f}, {0.1421f, 0.1126f, 1.0419f}, {0.0956f, 0.1390f, 0.8130f},
+ {0.0580f, 0.1693f, 0.6162f}, {0.0320f, 0.2080f, 0.4652f}, {0.0147f, 0.2586f, 0.3533f},
+ {0.0049f, 0.3230f, 0.2720f}, {0.0024f, 0.4073f, 0.2123f}, {0.0093f, 0.5030f, 0.1582f},
+ {0.0291f, 0.6082f, 0.1117f}, {0.0633f, 0.7100f, 0.0782f}, {0.1096f, 0.7932f, 0.0573f},
+ {0.1655f, 0.8620f, 0.0422f}, {0.2257f, 0.9149f, 0.0298f}, {0.2904f, 0.9540f, 0.0203f},
+ {0.3597f, 0.9803f, 0.0134f}, {0.4334f, 0.9950f, 0.0087f}, {0.5121f, 1.0000f, 0.0057f},
+ {0.5945f, 0.9950f, 0.0039f}, {0.6784f, 0.9786f, 0.0027f}, {0.7621f, 0.9520f, 0.0021f},
+ {0.8425f, 0.9154f, 0.0018f}, {0.9163f, 0.8700f, 0.0017f}, {0.9786f, 0.8163f, 0.0014f},
+ {1.0263f, 0.7570f, 0.0011f}, {1.0567f, 0.6949f, 0.0010f}, {1.0622f, 0.6310f, 0.0008f},
+ {1.0456f, 0.5668f, 0.0006f}, {1.0026f, 0.5030f, 0.0003f}, {0.9384f, 0.4412f, 0.0002f},
+ {0.8544f, 0.3810f, 0.0002f}, {0.7514f, 0.3210f, 0.0001f}, {0.6424f, 0.2650f, 0.0000f},
+ {0.5419f, 0.2170f, 0.0000f}, {0.4479f, 0.1750f, 0.0000f}, {0.3608f, 0.1382f, 0.0000f},
+ {0.2835f, 0.1070f, 0.0000f}, {0.2187f, 0.0816f, 0.0000f}, {0.1649f, 0.0610f, 0.0000f},
+ {0.1212f, 0.0446f, 0.0000f}, {0.0874f, 0.0320f, 0.0000f}, {0.0636f, 0.0232f, 0.0000f},
+ {0.0468f, 0.0170f, 0.0000f}, {0.0329f, 0.0119f, 0.0000f}, {0.0227f, 0.0082f, 0.0000f},
+ {0.0158f, 0.0057f, 0.0000f}, {0.0114f, 0.0041f, 0.0000f}, {0.0081f, 0.0029f, 0.0000f},
+ {0.0058f, 0.0021f, 0.0000f}, {0.0041f, 0.0015f, 0.0000f}, {0.0029f, 0.0010f, 0.0000f},
+ {0.0020f, 0.0007f, 0.0000f}, {0.0014f, 0.0005f, 0.0000f}, {0.0010f, 0.0004f, 0.0000f},
+ {0.0007f, 0.0002f, 0.0000f}, {0.0005f, 0.0002f, 0.0000f}, {0.0003f, 0.0001f, 0.0000f},
+ {0.0002f, 0.0001f, 0.0000f}, {0.0002f, 0.0001f, 0.0000f}, {0.0001f, 0.0000f, 0.0000f},
+ {0.0001f, 0.0000f, 0.0000f}, {0.0001f, 0.0000f, 0.0000f}, {0.0000f, 0.0000f, 0.0000f}};
+
+static void wavelength_to_xyz(float xyz[3], float lambda_nm)
+{
+ float ii = (lambda_nm - 380.0f) * (1.0f / 5.0f); /* Scaled 0..80. */
+ int i = (int)ii;
+
+ if (i < 0 || i >= 80) {
+ xyz[0] = 0.0f;
+ xyz[1] = 0.0f;
+ xyz[2] = 0.0f;
+ }
+ else {
+ ii -= (float)i;
+ const float *c = cie_colour_match[i];
+ xyz[0] = c[0] + ii * (c[3] - c[0]);
+ xyz[1] = c[1] + ii * (c[4] - c[1]);
+ xyz[2] = c[2] + ii * (c[5] - c[2]);
+ }
+}
+
+void wavelength_to_xyz_table(float *r_table, int width)
+{
+ for (int i = 0; i < width; i++) {
+ float temperature = 380 + 400 / (float)width * (float)i;
+
+ float rgb[3];
+ wavelength_to_xyz(rgb, temperature);
+
+ copy_v3_v3(&r_table[i * 4], rgb);
+ r_table[i * 4 + 3] = 0.0f;
+ }
+}
diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c
index 01cda6c9e4a..508de506ae8 100644
--- a/source/blender/blenlib/intern/math_geom.c
+++ b/source/blender/blenlib/intern/math_geom.c
@@ -2353,7 +2353,7 @@ bool isect_planes_v3_fn(
for (i_test = 0; i_test < planes_len; i_test++) {
const float *np_test = planes[i_test];
if (((dot_v3v3(np_test, co_test) + np_test[3]) > eps_isect)) {
- /* For low epsilon values the point could intersect it's own plane. */
+ /* For low epsilon values the point could intersect its own plane. */
if (!ELEM(i_test, i, j, k)) {
break;
}
@@ -5393,7 +5393,7 @@ void accumulate_vertex_normals_poly_v3(float **vertnos,
void tangent_from_uv_v3(const float uv1[2],
const float uv2[2],
- const float uv3[3],
+ const float uv3[2],
const float co1[3],
const float co2[3],
const float co3[3],
diff --git a/source/blender/blenlib/intern/math_matrix.c b/source/blender/blenlib/intern/math_matrix.c
index 2ada05d2965..d447da4de64 100644
--- a/source/blender/blenlib/intern/math_matrix.c
+++ b/source/blender/blenlib/intern/math_matrix.c
@@ -1700,6 +1700,89 @@ void orthogonalize_m4_stable(float R[4][4], int axis, bool normalize)
}
}
+/* -------------------------------------------------------------------- */
+/** \name Orthogonalize Matrix Zeroed Axes
+ *
+ * Set any zeroed axes to an orthogonal vector in relation to the other axes.
+ *
+ * Typically used so matrix inversion can be performed.
+ *
+ * \note If an object has a zero scaled axis, this function can be used to "clean" the matrix
+ * to behave as if the scale on that axis was `unit_length`. So it can be inverted
+ * or used in matrix multiply without creating degenerate matrices, see: T50103
+ * \{ */
+
+/**
+ * \return true if any axis needed to be modified.
+ */
+static bool orthogonalize_m3_zero_axes_impl(float *mat[3], const float unit_length)
+{
+ enum { X = 1 << 0, Y = 1 << 1, Z = 1 << 2 };
+ int flag = 0;
+ for (int i = 0; i < 3; i++) {
+ flag |= (len_squared_v3(mat[i]) == 0.0f) ? (1 << i) : 0;
+ }
+
+ /* Either all or none are zero, either way we can't properly resolve this
+ * since we need to fill invalid axes from valid ones. */
+ if (ELEM(flag, 0, X | Y | Z)) {
+ return false;
+ }
+
+ switch (flag) {
+ case X | Y: {
+ ortho_v3_v3(mat[1], mat[2]);
+ ATTR_FALLTHROUGH;
+ }
+ case X: {
+ cross_v3_v3v3(mat[0], mat[1], mat[2]);
+ break;
+ }
+
+ case Y | Z: {
+ ortho_v3_v3(mat[2], mat[0]);
+ ATTR_FALLTHROUGH;
+ }
+ case Y: {
+ cross_v3_v3v3(mat[1], mat[0], mat[2]);
+ break;
+ }
+
+ case Z | X: {
+ ortho_v3_v3(mat[0], mat[1]);
+ ATTR_FALLTHROUGH;
+ }
+ case Z: {
+ cross_v3_v3v3(mat[2], mat[0], mat[1]);
+ break;
+ }
+ default: {
+ BLI_assert(0); /* Unreachable! */
+ }
+ }
+
+ for (int i = 0; i < 3; i++) {
+ if (flag & (1 << i)) {
+ if (UNLIKELY(normalize_v3_length(mat[i], unit_length) == 0.0f)) {
+ mat[i][i] = unit_length;
+ }
+ }
+ }
+
+ return true;
+}
+
+bool orthogonalize_m3_zero_axes(float m[3][3], const float unit_length)
+{
+ return orthogonalize_m3_zero_axes_impl((float *[3]){UNPACK3(m)}, unit_length);
+}
+bool orthogonalize_m4_zero_axes(float m[4][4], const float unit_length)
+{
+ return orthogonalize_m3_zero_axes_impl((float *[3]){UNPACK3(m)}, unit_length);
+}
+
+/** \} */
+
bool is_orthogonal_m3(const float m[3][3])
{
int i, j;
@@ -3196,68 +3279,6 @@ void invert_m4_m4_safe(float Ainv[4][4], const float A[4][4])
* \{ */
/**
- * Return true if invert should be attempted again.
- *
- * \note Takes an array of points to be usable from 3x3 and 4x4 matrices.
- */
-static bool invert_m3_m3_safe_ortho_prepare(float *mat[3])
-{
- enum { X = 1 << 0, Y = 1 << 1, Z = 1 << 2 };
- int flag = 0;
- for (int i = 0; i < 3; i++) {
- flag |= (len_squared_v3(mat[i]) == 0.0f) ? (1 << i) : 0;
- }
-
- /* Either all or none are zero, either way we can't properly resolve this
- * since we need to fill invalid axes from valid ones. */
- if (ELEM(flag, 0, X | Y | Z)) {
- return false;
- }
-
- switch (flag) {
- case X | Y: {
- ortho_v3_v3(mat[1], mat[2]);
- ATTR_FALLTHROUGH;
- }
- case X: {
- cross_v3_v3v3(mat[0], mat[1], mat[2]);
- break;
- }
-
- case Y | Z: {
- ortho_v3_v3(mat[2], mat[0]);
- ATTR_FALLTHROUGH;
- }
- case Y: {
- cross_v3_v3v3(mat[1], mat[0], mat[2]);
- break;
- }
-
- case Z | X: {
- ortho_v3_v3(mat[0], mat[1]);
- ATTR_FALLTHROUGH;
- }
- case Z: {
- cross_v3_v3v3(mat[2], mat[0], mat[1]);
- break;
- }
- default: {
- BLI_assert(0); /* Unreachable! */
- }
- }
-
- for (int i = 0; i < 3; i++) {
- if (flag & (1 << i)) {
- if (UNLIKELY(normalize_v3(mat[i]) == 0.0f)) {
- mat[i][i] = 1.0f;
- }
- }
- }
-
- return true;
-}
-
-/**
* A safe version of invert that uses valid axes, calculating the zero'd axis
* based on the non-zero ones.
*
@@ -3268,8 +3289,7 @@ void invert_m4_m4_safe_ortho(float Ainv[4][4], const float A[4][4])
if (UNLIKELY(!invert_m4_m4(Ainv, A))) {
float Atemp[4][4];
copy_m4_m4(Atemp, A);
- if (UNLIKELY(!(invert_m3_m3_safe_ortho_prepare((float *[3]){UNPACK3(Atemp)}) &&
- invert_m4_m4(Ainv, Atemp)))) {
+ if (UNLIKELY(!(orthogonalize_m4_zero_axes(Atemp, 1.0f) && invert_m4_m4(Ainv, Atemp)))) {
unit_m4(Ainv);
}
}
@@ -3280,8 +3300,7 @@ void invert_m3_m3_safe_ortho(float Ainv[3][3], const float A[3][3])
if (UNLIKELY(!invert_m3_m3(Ainv, A))) {
float Atemp[3][3];
copy_m3_m3(Atemp, A);
- if (UNLIKELY(!(invert_m3_m3_safe_ortho_prepare((float *[3]){UNPACK3(Atemp)}) &&
- invert_m3_m3(Ainv, Atemp)))) {
+ if (UNLIKELY(!(orthogonalize_m3_zero_axes(Atemp, 1.0f) && invert_m3_m3(Ainv, Atemp)))) {
unit_m3(Ainv);
}
}
diff --git a/source/blender/blenlib/intern/math_vector.c b/source/blender/blenlib/intern/math_vector.c
index a21e0c8f092..fb7b96fde78 100644
--- a/source/blender/blenlib/intern/math_vector.c
+++ b/source/blender/blenlib/intern/math_vector.c
@@ -412,7 +412,7 @@ bool is_finite_v4(const float v[4])
* this would return the angle at the elbow.
*
* note that when v1/v2/v3 represent 3 points along a straight line
- * that the angle returned will be pi (180deg), rather then 0.0
+ * that the angle returned will be pi (180deg), rather than 0.0
*/
float angle_v3v3v3(const float a[3], const float b[3], const float c[3])
{
diff --git a/source/blender/blenlib/intern/memory_utils.c b/source/blender/blenlib/intern/memory_utils.c
index d477c20a242..5ca7b96c136 100644
--- a/source/blender/blenlib/intern/memory_utils.c
+++ b/source/blender/blenlib/intern/memory_utils.c
@@ -31,7 +31,7 @@
#include "BLI_strict_flags.h"
/**
- * Check if memory is zero'd, as with memset(arr, 0, arr_size)
+ * Check if memory is zeroed, as with `memset(arr, 0, arr_size)`.
*/
bool BLI_memory_is_zero(const void *arr, const size_t arr_size)
{
diff --git a/source/blender/blenlib/intern/mesh_boolean.cc b/source/blender/blenlib/intern/mesh_boolean.cc
index bf70b044d0d..c5c830bb4dd 100644
--- a/source/blender/blenlib/intern/mesh_boolean.cc
+++ b/source/blender/blenlib/intern/mesh_boolean.cc
@@ -38,10 +38,10 @@
# include "BLI_math_mpq.hh"
# include "BLI_mesh_intersect.hh"
# include "BLI_mpq3.hh"
-# include "BLI_polyfill_2d.h"
# include "BLI_set.hh"
# include "BLI_span.hh"
# include "BLI_stack.hh"
+# include "BLI_task.hh"
# include "BLI_vector.hh"
# include "BLI_vector_set.hh"
@@ -49,6 +49,10 @@
# include "BLI_mesh_boolean.hh"
+# ifdef WITH_TBB
+# include "tbb/spin_mutex.h"
+# endif
+
// # define PERFDEBUG
namespace blender::meshintersect {
@@ -145,11 +149,9 @@ class TriMeshTopology : NonCopyable {
* Else return NO_INDEX. */
int other_tri_if_manifold(Edge e, int t) const
{
- if (edge_tri_.contains(e)) {
- auto *p = edge_tri_.lookup(e);
- if (p->size() == 2) {
- return ((*p)[0] == t) ? (*p)[1] : (*p)[0];
- }
+ auto p = edge_tri_.lookup_ptr(e);
+ if (p != nullptr && (*p)->size() == 2) {
+ return ((**p)[0] == t) ? (**p)[1] : (**p)[0];
}
return NO_INDEX;
}
@@ -1404,9 +1406,9 @@ static int find_cell_for_point_near_edge(mpq3 p,
int dummy_index = p_sorted_dummy - sorted_tris.begin();
int prev_tri = (dummy_index == 0) ? sorted_tris[sorted_tris.size() - 1] :
sorted_tris[dummy_index - 1];
- int next_tri = (dummy_index == sorted_tris.size() - 1) ? sorted_tris[0] :
- sorted_tris[dummy_index + 1];
if (dbg_level > 0) {
+ int next_tri = (dummy_index == sorted_tris.size() - 1) ? sorted_tris[0] :
+ sorted_tris[dummy_index + 1];
std::cout << "prev tri to dummy = " << prev_tri << "; next tri to dummy = " << next_tri
<< "\n";
}
@@ -1691,9 +1693,24 @@ static int find_containing_cell(const Vert *v,
* If the closest point is on an edge, return 0, 1, or 2
* for edges ab, bc, or ca in *r_edge; else -1.
* (Adapted from #closest_on_tri_to_point_v3()).
+ * The arguments ab, ac, ..., r are used as temporaries
+ * in this routine. Passing them in from the caller can
+ * avoid many allocs and frees of temporary mpq3 values
+ * and the mpq_class values within them.
*/
-static mpq_class closest_on_tri_to_point(
- const mpq3 &p, const mpq3 &a, const mpq3 &b, const mpq3 &c, int *r_edge, int *r_vert)
+static mpq_class closest_on_tri_to_point(const mpq3 &p,
+ const mpq3 &a,
+ const mpq3 &b,
+ const mpq3 &c,
+ mpq3 &ab,
+ mpq3 &ac,
+ mpq3 &ap,
+ mpq3 &bp,
+ mpq3 &cp,
+ mpq3 &m,
+ mpq3 &r,
+ int *r_edge,
+ int *r_vert)
{
constexpr int dbg_level = 0;
if (dbg_level > 0) {
@@ -1701,11 +1718,15 @@ static mpq_class closest_on_tri_to_point(
std::cout << " a = " << a << ", b = " << b << ", c = " << c << "\n";
}
/* Check if p in vertex region outside a. */
- mpq3 ab = b - a;
- mpq3 ac = c - a;
- mpq3 ap = p - a;
- mpq_class d1 = mpq3::dot(ab, ap);
- mpq_class d2 = mpq3::dot(ac, ap);
+ ab = b;
+ ab -= a;
+ ac = c;
+ ac -= a;
+ ap = p;
+ ap -= a;
+
+ mpq_class d1 = mpq3::dot_with_buffer(ab, ap, m);
+ mpq_class d2 = mpq3::dot_with_buffer(ac, ap, m);
if (d1 <= 0 && d2 <= 0) {
/* Barycentric coordinates (1,0,0). */
*r_edge = -1;
@@ -1713,12 +1734,13 @@ static mpq_class closest_on_tri_to_point(
if (dbg_level > 0) {
std::cout << " answer = a\n";
}
- return mpq3::distance_squared(p, a);
+ return mpq3::distance_squared_with_buffer(p, a, m);
}
/* Check if p in vertex region outside b. */
- mpq3 bp = p - b;
- mpq_class d3 = mpq3::dot(ab, bp);
- mpq_class d4 = mpq3::dot(ac, bp);
+ bp = p;
+ bp -= b;
+ mpq_class d3 = mpq3::dot_with_buffer(ab, bp, m);
+ mpq_class d4 = mpq3::dot_with_buffer(ac, bp, m);
if (d3 >= 0 && d4 <= d3) {
/* Barycentric coordinates (0,1,0). */
*r_edge = -1;
@@ -1726,25 +1748,28 @@ static mpq_class closest_on_tri_to_point(
if (dbg_level > 0) {
std::cout << " answer = b\n";
}
- return mpq3::distance_squared(p, b);
+ return mpq3::distance_squared_with_buffer(p, b, m);
}
/* Check if p in region of ab. */
mpq_class vc = d1 * d4 - d3 * d2;
if (vc <= 0 && d1 >= 0 && d3 <= 0) {
mpq_class v = d1 / (d1 - d3);
/* Barycentric coordinates (1-v,v,0). */
- mpq3 r = a + v * ab;
+ r = ab;
+ r *= v;
+ r += a;
*r_vert = -1;
*r_edge = 0;
if (dbg_level > 0) {
std::cout << " answer = on ab at " << r << "\n";
}
- return mpq3::distance_squared(p, r);
+ return mpq3::distance_squared_with_buffer(p, r, m);
}
/* Check if p in vertex region outside c. */
- mpq3 cp = p - c;
- mpq_class d5 = mpq3::dot(ab, cp);
- mpq_class d6 = mpq3::dot(ac, cp);
+ cp = p;
+ cp -= c;
+ mpq_class d5 = mpq3::dot_with_buffer(ab, cp, m);
+ mpq_class d6 = mpq3::dot_with_buffer(ac, cp, m);
if (d6 >= 0 && d5 <= d6) {
/* Barycentric coordinates (0,0,1). */
*r_edge = -1;
@@ -1752,49 +1777,67 @@ static mpq_class closest_on_tri_to_point(
if (dbg_level > 0) {
std::cout << " answer = c\n";
}
- return mpq3::distance_squared(p, c);
+ return mpq3::distance_squared_with_buffer(p, c, m);
}
/* Check if p in edge region of ac. */
mpq_class vb = d5 * d2 - d1 * d6;
if (vb <= 0 && d2 >= 0 && d6 <= 0) {
mpq_class w = d2 / (d2 - d6);
/* Barycentric coordinates (1-w,0,w). */
- mpq3 r = a + w * ac;
+ r = ac;
+ r *= w;
+ r += a;
*r_vert = -1;
*r_edge = 2;
if (dbg_level > 0) {
std::cout << " answer = on ac at " << r << "\n";
}
- return mpq3::distance_squared(p, r);
+ return mpq3::distance_squared_with_buffer(p, r, m);
}
/* Check if p in edge region of bc. */
mpq_class va = d3 * d6 - d5 * d4;
if (va <= 0 && (d4 - d3) >= 0 && (d5 - d6) >= 0) {
mpq_class w = (d4 - d3) / ((d4 - d3) + (d5 - d6));
/* Barycentric coordinates (0,1-w,w). */
- mpq3 r = c - b;
- r = w * r;
- r = r + b;
+ r = c;
+ r -= b;
+ r *= w;
+ r += b;
*r_vert = -1;
*r_edge = 1;
if (dbg_level > 0) {
std::cout << " answer = on bc at " << r << "\n";
}
- return mpq3::distance_squared(p, r);
+ return mpq3::distance_squared_with_buffer(p, r, m);
}
/* p inside face region. Compute barycentric coordinates (u,v,w). */
mpq_class denom = 1 / (va + vb + vc);
mpq_class v = vb * denom;
mpq_class w = vc * denom;
- ac = w * ac;
- mpq3 r = a + v * ab;
- r = r + ac;
+ ac *= w;
+ r = ab;
+ r *= v;
+ r += a;
+ r += ac;
*r_vert = -1;
*r_edge = -1;
if (dbg_level > 0) {
std::cout << " answer = inside at " << r << "\n";
}
- return mpq3::distance_squared(p, r);
+ return mpq3::distance_squared_with_buffer(p, r, m);
+}
+
+static float closest_on_tri_to_point_float_dist_squared(const float3 &p,
+ const double3 &a,
+ const double3 &b,
+ const double3 &c)
+{
+ float3 fa, fb, fc, closest;
+ copy_v3fl_v3db(fa, a);
+ copy_v3fl_v3db(fb, b);
+ copy_v3fl_v3db(fc, c);
+ closest_on_tri_to_point_v3(closest, p, fa, fb, fc);
+ return len_squared_v3v3(p, closest);
}
struct ComponentContainer {
@@ -1820,6 +1863,7 @@ static Vector<ComponentContainer> find_component_containers(int comp,
const IMesh &tm,
const PatchesInfo &pinfo,
const TriMeshTopology &tmtopo,
+ Array<BoundingBox> &comp_bb,
IMeshArena *arena)
{
constexpr int dbg_level = 0;
@@ -1833,6 +1877,11 @@ static Vector<ComponentContainer> find_component_containers(int comp,
if (dbg_level > 0) {
std::cout << "test vertex in comp: " << test_v << "\n";
}
+ const double3 &test_v_d = test_v->co;
+ float3 test_v_f(test_v_d[0], test_v_d[1], test_v_d[2]);
+
+ mpq3 buf[7];
+
for (int comp_other : components.index_range()) {
if (comp == comp_other) {
continue;
@@ -1840,10 +1889,17 @@ static Vector<ComponentContainer> find_component_containers(int comp,
if (dbg_level > 0) {
std::cout << "comp_other = " << comp_other << "\n";
}
+ if (!bbs_might_intersect(comp_bb[comp], comp_bb[comp_other])) {
+ if (dbg_level > 0) {
+ std::cout << "bounding boxes don't overlap\n";
+ }
+ continue;
+ }
int nearest_tri = NO_INDEX;
int nearest_tri_close_vert = -1;
int nearest_tri_close_edge = -1;
mpq_class nearest_tri_dist_squared;
+ float nearest_tri_dist_squared_float = FLT_MAX;
for (int p : components[comp_other]) {
const Patch &patch = pinfo.patch(p);
for (int t : patch.tris()) {
@@ -1853,10 +1909,23 @@ static Vector<ComponentContainer> find_component_containers(int comp,
}
int close_vert;
int close_edge;
+ /* Try a cheap float test first. */
+ float d2_f = closest_on_tri_to_point_float_dist_squared(
+ test_v_f, tri[0]->co, tri[1]->co, tri[2]->co);
+ if (d2_f - FLT_EPSILON > nearest_tri_dist_squared_float) {
+ continue;
+ }
mpq_class d2 = closest_on_tri_to_point(test_v->co_exact,
tri[0]->co_exact,
tri[1]->co_exact,
tri[2]->co_exact,
+ buf[0],
+ buf[1],
+ buf[2],
+ buf[3],
+ buf[4],
+ buf[5],
+ buf[6],
&close_edge,
&close_vert);
if (dbg_level > 1) {
@@ -1868,6 +1937,7 @@ static Vector<ComponentContainer> find_component_containers(int comp,
nearest_tri_close_edge = close_edge;
nearest_tri_close_vert = close_vert;
nearest_tri_dist_squared = d2;
+ nearest_tri_dist_squared_float = d2_f;
}
}
}
@@ -1899,6 +1969,51 @@ static Vector<ComponentContainer> find_component_containers(int comp,
}
/**
+ * Populate the per-component bounding boxes, expanding them
+ * by an appropriate epsilon so that we conservatively will say
+ * that components could intersect if the BBs overlap.
+ */
+static void populate_comp_bbs(const Vector<Vector<int>> &components,
+ const PatchesInfo &pinfo,
+ const IMesh &im,
+ Array<BoundingBox> &comp_bb)
+{
+ const int comp_grainsize = 16;
+ /* To get a good expansion epsilon, we need to find the maximum
+ * absolute value of any coordinate. Do it first per component,
+ * then get the overall max. */
+ Array<double> max_abs(components.size(), 0.0);
+ parallel_for(components.index_range(), comp_grainsize, [&](IndexRange comp_range) {
+ for (int c : comp_range) {
+ BoundingBox &bb = comp_bb[c];
+ double &maxa = max_abs[c];
+ for (int p : components[c]) {
+ const Patch &patch = pinfo.patch(p);
+ for (int t : patch.tris()) {
+ const Face &tri = *im.face(t);
+ for (const Vert *v : tri) {
+ bb.combine(v->co);
+ for (int i = 0; i < 3; ++i) {
+ maxa = max_dd(maxa, fabs(v->co[i]));
+ }
+ }
+ }
+ }
+ }
+ });
+ double all_max_abs = 0.0;
+ for (int c : components.index_range()) {
+ all_max_abs = max_dd(all_max_abs, max_abs[c]);
+ }
+ constexpr float pad_factor = 10.0f;
+ float pad = all_max_abs == 0.0 ? FLT_EPSILON : 2 * FLT_EPSILON * all_max_abs;
+ pad *= pad_factor;
+ for (int c : components.index_range()) {
+ comp_bb[c].expand(pad);
+ }
+}
+
+/**
* The cells and patches are supposed to form a bipartite graph.
* The graph may be disconnected (if parts of meshes are nested or side-by-side
* without intersection with other each other).
@@ -1940,19 +2055,23 @@ static void finish_patch_cell_graph(const IMesh &tm,
}
int tot_components = components.size();
Array<Vector<ComponentContainer>> comp_cont(tot_components);
- for (int comp : components.index_range()) {
- comp_cont[comp] = find_component_containers(
- comp, components, ambient_cell, tm, pinfo, tmtopo, arena);
- }
- if (dbg_level > 0) {
- std::cout << "component containers:\n";
- for (int comp : comp_cont.index_range()) {
- std::cout << comp << ": ";
- for (const ComponentContainer &cc : comp_cont[comp]) {
- std::cout << "[containing_comp=" << cc.containing_component
- << ", nearest_cell=" << cc.nearest_cell << ", d2=" << cc.dist_to_cell << "] ";
+ if (tot_components > 1) {
+ Array<BoundingBox> comp_bb(tot_components);
+ populate_comp_bbs(components, pinfo, tm, comp_bb);
+ for (int comp : components.index_range()) {
+ comp_cont[comp] = find_component_containers(
+ comp, components, ambient_cell, tm, pinfo, tmtopo, comp_bb, arena);
+ }
+ if (dbg_level > 0) {
+ std::cout << "component containers:\n";
+ for (int comp : comp_cont.index_range()) {
+ std::cout << comp << ": ";
+ for (const ComponentContainer &cc : comp_cont[comp]) {
+ std::cout << "[containing_comp=" << cc.containing_component
+ << ", nearest_cell=" << cc.nearest_cell << ", d2=" << cc.dist_to_cell << "] ";
+ }
+ std::cout << "\n";
}
- std::cout << "\n";
}
}
if (dbg_level > 1) {
@@ -2568,47 +2687,58 @@ static IMesh raycast_tris_boolean(const IMesh &tm,
BVHTree *tree = raycast_tree(tm);
Vector<Face *> out_faces;
out_faces.reserve(tm.face_size());
- Array<float> in_shape(nshapes, 0);
- Array<int> winding(nshapes, 0);
- for (int t : tm.face_index_range()) {
- Face &tri = *tm.face(t);
- int shape = shape_fn(tri.orig);
- if (dbg_level > 0) {
- std::cout << "process triangle " << t << " = " << &tri << "\n";
- std::cout << "shape = " << shape << "\n";
- }
- test_tri_inside_shapes(tm, shape_fn, nshapes, t, tree, in_shape);
- for (int other_shape = 0; other_shape < nshapes; ++other_shape) {
- if (other_shape == shape) {
- continue;
- }
- /* The in_shape array has a confidence value for "insideness".
- * For most operations, even a hint of being inside
- * gives good results, but when shape is a cutter in a Difference
- * operation, we want to be pretty sure that the point is inside other_shape.
- * E.g., T75827.
- * Also, when the operation is intersection, we also want high confidence.
- */
- bool need_high_confidence = (op == BoolOpType::Difference && shape != 0) ||
- op == BoolOpType::Intersect;
- bool inside = in_shape[other_shape] >= (need_high_confidence ? 0.5f : 0.1f);
+# ifdef WITH_TBB
+ tbb::spin_mutex mtx;
+# endif
+ const int grainsize = 256;
+ parallel_for(IndexRange(tm.face_size()), grainsize, [&](IndexRange range) {
+ Array<float> in_shape(nshapes, 0);
+ Array<int> winding(nshapes, 0);
+ for (int t : range) {
+ Face &tri = *tm.face(t);
+ int shape = shape_fn(tri.orig);
if (dbg_level > 0) {
- std::cout << "test point is " << (inside ? "inside" : "outside") << " other_shape "
- << other_shape << " val = " << in_shape[other_shape] << "\n";
+ std::cout << "process triangle " << t << " = " << &tri << "\n";
+ std::cout << "shape = " << shape << "\n";
}
- winding[other_shape] = inside;
- }
- bool do_flip;
- bool do_remove = raycast_test_remove(op, winding, shape, &do_flip);
- if (!do_remove) {
- if (!do_flip) {
- out_faces.append(&tri);
+ test_tri_inside_shapes(tm, shape_fn, nshapes, t, tree, in_shape);
+ for (int other_shape = 0; other_shape < nshapes; ++other_shape) {
+ if (other_shape == shape) {
+ continue;
+ }
+ /* The in_shape array has a confidence value for "insideness".
+ * For most operations, even a hint of being inside
+ * gives good results, but when shape is a cutter in a Difference
+ * operation, we want to be pretty sure that the point is inside other_shape.
+ * E.g., T75827.
+ * Also, when the operation is intersection, we also want high confidence.
+ */
+ bool need_high_confidence = (op == BoolOpType::Difference && shape != 0) ||
+ op == BoolOpType::Intersect;
+ bool inside = in_shape[other_shape] >= (need_high_confidence ? 0.5f : 0.1f);
+ if (dbg_level > 0) {
+ std::cout << "test point is " << (inside ? "inside" : "outside") << " other_shape "
+ << other_shape << " val = " << in_shape[other_shape] << "\n";
+ }
+ winding[other_shape] = inside;
}
- else {
- raycast_add_flipped(out_faces, tri, arena);
+ bool do_flip;
+ bool do_remove = raycast_test_remove(op, winding, shape, &do_flip);
+ {
+# ifdef WITH_TBB
+ tbb::spin_mutex::scoped_lock lock(mtx);
+# endif
+ if (!do_remove) {
+ if (!do_flip) {
+ out_faces.append(&tri);
+ }
+ else {
+ raycast_add_flipped(out_faces, tri, arena);
+ }
+ }
}
}
- }
+ });
BLI_bvhtree_free(tree);
ans.set_faces(out_faces);
return ans;
@@ -2680,113 +2810,10 @@ static IMesh raycast_patches_boolean(const IMesh &tm,
}
}
BLI_bvhtree_free(tree);
- ans.set_faces(out_faces);
- return ans;
-}
-
-/**
- * Tessellate face f into triangles and return an array of `const Face *`
- * giving that triangulation. Intended to be used when f has > 4 vertices.
- * Care is taken so that the original edge index associated with
- * each edge in the output triangles either matches the original edge
- * for the (identical) edge of f, or else is -1. So diagonals added
- * for triangulation can later be identified by having #NO_INDEX for original.
- */
-static Array<Face *> triangulate_poly(Face *f, IMeshArena *arena)
-{
- /* Similar to loop body in BM_mesh_calc_tesselation. */
- int flen = f->size();
- BLI_assert(flen > 4);
- if (!f->plane_populated()) {
- f->populate_plane(false);
- }
- /* Project along negative face normal so (x,y) can be used in 2d. */
- const double3 &poly_normal = f->plane->norm;
- float no[3] = {float(poly_normal[0]), float(poly_normal[1]), float(poly_normal[2])};
- normalize_v3(no);
- float axis_mat[3][3];
- float(*projverts)[2];
- unsigned int(*tris)[3];
- const int totfilltri = flen - 2;
- /* Prepare projected vertices and array to receive triangles in tesselation. */
- tris = static_cast<unsigned int(*)[3]>(MEM_malloc_arrayN(totfilltri, sizeof(*tris), __func__));
- projverts = static_cast<float(*)[2]>(MEM_malloc_arrayN(flen, sizeof(*projverts), __func__));
- axis_dominant_v3_to_m3_negate(axis_mat, no);
- for (int j = 0; j < flen; ++j) {
- const double3 &dco = (*f)[j]->co;
- float co[3] = {float(dco[0]), float(dco[1]), float(dco[2])};
- mul_v2_m3v3(projverts[j], axis_mat, co);
- }
- BLI_polyfill_calc(projverts, flen, 1, tris);
- /* Put tesselation triangles into Face form. Record original edges where they exist. */
- Array<Face *> ans(totfilltri);
- for (int t = 0; t < totfilltri; ++t) {
- unsigned int *tri = tris[t];
- int eo[3];
- const Vert *v[3];
- for (int k = 0; k < 3; k++) {
- BLI_assert(tri[k] < flen);
- v[k] = (*f)[tri[k]];
- /* If tri edge goes between two successive indices in
- * the original face, then it is an original edge. */
- if ((tri[k] + 1) % flen == tri[(k + 1) % 3]) {
- eo[k] = f->edge_orig[tri[k]];
- }
- else {
- eo[k] = NO_INDEX;
- }
- ans[t] = arena->add_face(
- {v[0], v[1], v[2]}, f->orig, {eo[0], eo[1], eo[2]}, {false, false, false});
- }
- }
-
- MEM_freeN(tris);
- MEM_freeN(projverts);
+ ans.set_faces(out_faces);
return ans;
}
-
-/**
- * Return an #IMesh that is a triangulation of a mesh with general
- * polygonal faces, #IMesh.
- * Added diagonals will be distinguishable by having edge original
- * indices of #NO_INDEX.
- */
-static IMesh triangulate_polymesh(IMesh &imesh, IMeshArena *arena)
-{
- Vector<Face *> face_tris;
- constexpr int estimated_tris_per_face = 3;
- face_tris.reserve(estimated_tris_per_face * imesh.face_size());
- for (Face *f : imesh.faces()) {
- /* Tessellate face f, following plan similar to #BM_face_calc_tesselation. */
- int flen = f->size();
- if (flen == 3) {
- face_tris.append(f);
- }
- else if (flen == 4) {
- const Vert *v0 = (*f)[0];
- const Vert *v1 = (*f)[1];
- const Vert *v2 = (*f)[2];
- const Vert *v3 = (*f)[3];
- int eo_01 = f->edge_orig[0];
- int eo_12 = f->edge_orig[1];
- int eo_23 = f->edge_orig[2];
- int eo_30 = f->edge_orig[3];
- Face *f0 = arena->add_face({v0, v1, v2}, f->orig, {eo_01, eo_12, -1}, {false, false, false});
- Face *f1 = arena->add_face({v0, v2, v3}, f->orig, {-1, eo_23, eo_30}, {false, false, false});
- face_tris.append(f0);
- face_tris.append(f1);
- }
- else {
- Array<Face *> tris = triangulate_poly(f, arena);
- for (Face *tri : tris) {
- face_tris.append(tri);
- }
- }
- }
- return IMesh(face_tris);
-}
-
/**
* If \a tri1 and \a tri2 have a common edge (in opposite orientation),
* return the indices into \a tri1 and \a tri2 where that common edge starts. Else return (-1,-1).
@@ -3363,9 +3390,13 @@ static IMesh polymesh_from_trimesh_with_dissolve(const IMesh &tm_out,
std::cout << "\nPOLYMESH_FROM_TRIMESH_WITH_DISSOLVE\n";
}
/* For now: need plane normals for all triangles. */
- for (Face *tri : tm_out.faces()) {
- tri->populate_plane(false);
- }
+ const int grainsize = 1024;
+ parallel_for(tm_out.face_index_range(), grainsize, [&](IndexRange range) {
+ for (int i : range) {
+ Face *tri = tm_out.face(i);
+ tri->populate_plane(false);
+ }
+ });
/* Gather all output triangles that are part of each input face.
* face_output_tris[f] will be indices of triangles in tm_out
* that have f as their original face. */
diff --git a/source/blender/blenlib/intern/mesh_intersect.cc b/source/blender/blenlib/intern/mesh_intersect.cc
index b2b8dd4e900..5b7b6d9a929 100644
--- a/source/blender/blenlib/intern/mesh_intersect.cc
+++ b/source/blender/blenlib/intern/mesh_intersect.cc
@@ -39,6 +39,7 @@
# include "BLI_math_mpq.hh"
# include "BLI_mpq2.hh"
# include "BLI_mpq3.hh"
+# include "BLI_polyfill_2d.h"
# include "BLI_set.hh"
# include "BLI_span.hh"
# include "BLI_task.h"
@@ -527,9 +528,7 @@ IMeshArena::IMeshArena()
pimpl_ = std::make_unique<IMeshArenaImpl>();
}
-IMeshArena::~IMeshArena()
-{
-}
+IMeshArena::~IMeshArena() = default;
void IMeshArena::reserve(int vert_num_hint, int face_num_hint)
{
@@ -745,83 +744,12 @@ std::ostream &operator<<(std::ostream &os, const IMesh &mesh)
return os;
}
-struct BoundingBox {
- float3 min{FLT_MAX, FLT_MAX, FLT_MAX};
- float3 max{-FLT_MAX, -FLT_MAX, -FLT_MAX};
-
- BoundingBox() = default;
- BoundingBox(const float3 &min, const float3 &max) : min(min), max(max)
- {
- }
- BoundingBox(const BoundingBox &other) : min(other.min), max(other.max)
- {
- }
- BoundingBox(BoundingBox &&other) noexcept : min(std::move(other.min)), max(std::move(other.max))
- {
- }
- ~BoundingBox() = default;
- BoundingBox operator=(const BoundingBox &other)
- {
- if (this != &other) {
- min = other.min;
- max = other.max;
- }
- return *this;
- }
- BoundingBox operator=(BoundingBox &&other) noexcept
- {
- min = std::move(other.min);
- max = std::move(other.max);
- return *this;
- }
-
- void combine(const float3 &p)
- {
- min.x = min_ff(min.x, p.x);
- min.y = min_ff(min.y, p.y);
- min.z = min_ff(min.z, p.z);
- max.x = max_ff(max.x, p.x);
- max.y = max_ff(max.y, p.y);
- max.z = max_ff(max.z, p.z);
- }
-
- void combine(const double3 &p)
- {
- min.x = min_ff(min.x, static_cast<float>(p.x));
- min.y = min_ff(min.y, static_cast<float>(p.y));
- min.z = min_ff(min.z, static_cast<float>(p.z));
- max.x = max_ff(max.x, static_cast<float>(p.x));
- max.y = max_ff(max.y, static_cast<float>(p.y));
- max.z = max_ff(max.z, static_cast<float>(p.z));
- }
-
- void combine(const BoundingBox &bb)
- {
- min.x = min_ff(min.x, bb.min.x);
- min.y = min_ff(min.y, bb.min.y);
- min.z = min_ff(min.z, bb.min.z);
- max.x = max_ff(max.x, bb.max.x);
- max.y = max_ff(max.y, bb.max.y);
- max.z = max_ff(max.z, bb.max.z);
- }
-
- void expand(float pad)
- {
- min.x -= pad;
- min.y -= pad;
- min.z -= pad;
- max.x += pad;
- max.y += pad;
- max.z += pad;
- }
-};
-
/**
* Assume bounding boxes have been expanded by a sufficient epsilon on all sides
* so that the comparisons against the bb bounds are sufficient to guarantee that
* if an overlap or even touching could happen, this will return true.
*/
-static bool bbs_might_intersect(const BoundingBox &bb_a, const BoundingBox &bb_b)
+bool bbs_might_intersect(const BoundingBox &bb_a, const BoundingBox &bb_b)
{
return isect_aabb_aabb_v3(bb_a.min, bb_a.max, bb_b.min, bb_b.max);
}
@@ -936,28 +864,6 @@ class CoplanarCluster {
{
this->add_tri(t, bb);
}
- CoplanarCluster(const CoplanarCluster &other) : tris_(other.tris_), bb_(other.bb_)
- {
- }
- CoplanarCluster(CoplanarCluster &&other) noexcept
- : tris_(std::move(other.tris_)), bb_(std::move(other.bb_))
- {
- }
- ~CoplanarCluster() = default;
- CoplanarCluster &operator=(const CoplanarCluster &other)
- {
- if (this != &other) {
- tris_ = other.tris_;
- bb_ = other.bb_;
- }
- return *this;
- }
- CoplanarCluster &operator=(CoplanarCluster &&other) noexcept
- {
- tris_ = std::move(other.tris_);
- bb_ = std::move(other.bb_);
- return *this;
- }
/* Assume that caller knows this will not be a duplicate. */
void add_tri(int t, const BoundingBox &bb)
@@ -1073,38 +979,6 @@ struct ITT_value {
ITT_value(ITT_value_kind k, const mpq3 &p1, const mpq3 &p2) : p1(p1), p2(p2), kind(k)
{
}
- ITT_value(const ITT_value &other)
- : p1(other.p1), p2(other.p2), t_source(other.t_source), kind(other.kind)
- {
- }
- ITT_value(ITT_value &&other) noexcept
- : p1(std::move(other.p1)),
- p2(std::move(other.p2)),
- t_source(other.t_source),
- kind(other.kind)
- {
- }
- ~ITT_value()
- {
- }
- ITT_value &operator=(const ITT_value &other)
- {
- if (this != &other) {
- kind = other.kind;
- p1 = other.p1;
- p2 = other.p2;
- t_source = other.t_source;
- }
- return *this;
- }
- ITT_value &operator=(ITT_value &&other) noexcept
- {
- kind = other.kind;
- p1 = std::move(other.p1);
- p2 = std::move(other.p2);
- t_source = other.t_source;
- return *this;
- }
};
static std::ostream &operator<<(std::ostream &os, const ITT_value &itt);
@@ -1241,13 +1115,19 @@ static int filter_plane_side(const double3 &p,
* Assumes ab is not perpendicular to n.
* This works because the ratio of the projections of ab and ac onto n is the same as
* the ratio along the line ab of the intersection point to the whole of ab.
+ * The ab, ac, and dotbuf arguments are used as a temporaries; declaring them
+ * in the caller can avoid many allocs and frees of mpq3 and mpq_class structures.
*/
-static inline mpq3 tti_interp(const mpq3 &a, const mpq3 &b, const mpq3 &c, const mpq3 &n)
-{
- mpq3 ab = a - b;
- mpq_class den = mpq3::dot(ab, n);
+static inline mpq3 tti_interp(
+ const mpq3 &a, const mpq3 &b, const mpq3 &c, const mpq3 &n, mpq3 &ab, mpq3 &ac, mpq3 &dotbuf)
+{
+ ab = a;
+ ab -= b;
+ ac = a;
+ ac -= c;
+ mpq_class den = mpq3::dot_with_buffer(ab, n, dotbuf);
BLI_assert(den != 0);
- mpq_class alpha = mpq3::dot(a - c, n) / den;
+ mpq_class alpha = mpq3::dot_with_buffer(ac, n, dotbuf) / den;
return a - alpha * ab;
}
@@ -1255,11 +1135,28 @@ static inline mpq3 tti_interp(const mpq3 &a, const mpq3 &b, const mpq3 &c, const
* Return +1, 0, -1 as a + ad is above, on, or below the oriented plane containing a, b, c in CCW
* order. This is the same as -oriented(a, b, c, a + ad), but uses fewer arithmetic operations.
* TODO: change arguments to `const Vert *` and use floating filters.
+ * The ba, ca, n, and dotbuf arguments are used as temporaries; declaring them
+ * in the caller can avoid many allocs and frees of mpq3 and mpq_class structures.
*/
-static inline int tti_above(const mpq3 &a, const mpq3 &b, const mpq3 &c, const mpq3 &ad)
+static inline int tti_above(const mpq3 &a,
+ const mpq3 &b,
+ const mpq3 &c,
+ const mpq3 &ad,
+ mpq3 &ba,
+ mpq3 &ca,
+ mpq3 &n,
+ mpq3 &dotbuf)
{
- mpq3 n = mpq3::cross(b - a, c - a);
- return sgn(mpq3::dot(ad, n));
+ ba = b;
+ ba -= a;
+ ca = c;
+ ca -= a;
+
+ n.x = ba.y * ca.z - ba.z * ca.y;
+ n.y = ba.z * ca.x - ba.x * ca.z;
+ n.z = ba.x * ca.y - ba.y * ca.x;
+
+ return sgn(mpq3::dot_with_buffer(ad, n, dotbuf));
}
/**
@@ -1303,20 +1200,21 @@ static ITT_value itt_canon2(const mpq3 &p1,
mpq3 p1p2 = p2 - p1;
mpq3 intersect_1;
mpq3 intersect_2;
+ mpq3 buf[4];
bool no_overlap = false;
/* Top test in classification tree. */
- if (tti_above(p1, q1, r2, p1p2) > 0) {
+ if (tti_above(p1, q1, r2, p1p2, buf[0], buf[1], buf[2], buf[3]) > 0) {
/* Middle right test in classification tree. */
- if (tti_above(p1, r1, r2, p1p2) <= 0) {
+ if (tti_above(p1, r1, r2, p1p2, buf[0], buf[1], buf[2], buf[3]) <= 0) {
/* Bottom right test in classification tree. */
- if (tti_above(p1, r1, q2, p1p2) > 0) {
+ if (tti_above(p1, r1, q2, p1p2, buf[0], buf[1], buf[2], buf[3]) > 0) {
/* Overlap is [k [i l] j]. */
if (dbg_level > 0) {
std::cout << "overlap [k [i l] j]\n";
}
/* i is intersect with p1r1. l is intersect with p2r2. */
- intersect_1 = tti_interp(p1, r1, p2, n2);
- intersect_2 = tti_interp(p2, r2, p1, n1);
+ intersect_1 = tti_interp(p1, r1, p2, n2, buf[0], buf[1], buf[2]);
+ intersect_2 = tti_interp(p2, r2, p1, n1, buf[0], buf[1], buf[2]);
}
else {
/* Overlap is [i [k l] j]. */
@@ -1324,8 +1222,8 @@ static ITT_value itt_canon2(const mpq3 &p1,
std::cout << "overlap [i [k l] j]\n";
}
/* k is intersect with p2q2. l is intersect is p2r2. */
- intersect_1 = tti_interp(p2, q2, p1, n1);
- intersect_2 = tti_interp(p2, r2, p1, n1);
+ intersect_1 = tti_interp(p2, q2, p1, n1, buf[0], buf[1], buf[2]);
+ intersect_2 = tti_interp(p2, r2, p1, n1, buf[0], buf[1], buf[2]);
}
}
else {
@@ -1338,7 +1236,7 @@ static ITT_value itt_canon2(const mpq3 &p1,
}
else {
/* Middle left test in classification tree. */
- if (tti_above(p1, q1, q2, p1p2) < 0) {
+ if (tti_above(p1, q1, q2, p1p2, buf[0], buf[1], buf[2], buf[3]) < 0) {
/* No overlap: [i j] [k l]. */
if (dbg_level > 0) {
std::cout << "no overlap: [i j] [k l]\n";
@@ -1347,14 +1245,14 @@ static ITT_value itt_canon2(const mpq3 &p1,
}
else {
/* Bottom left test in classification tree. */
- if (tti_above(p1, r1, q2, p1p2) >= 0) {
+ if (tti_above(p1, r1, q2, p1p2, buf[0], buf[1], buf[2], buf[3]) >= 0) {
/* Overlap is [k [i j] l]. */
if (dbg_level > 0) {
std::cout << "overlap [k [i j] l]\n";
}
/* i is intersect with p1r1. j is intersect with p1q1. */
- intersect_1 = tti_interp(p1, r1, p2, n2);
- intersect_2 = tti_interp(p1, q1, p2, n2);
+ intersect_1 = tti_interp(p1, r1, p2, n2, buf[0], buf[1], buf[2]);
+ intersect_2 = tti_interp(p1, q1, p2, n2, buf[0], buf[1], buf[2]);
}
else {
/* Overlap is [i [k j] l]. */
@@ -1362,8 +1260,8 @@ static ITT_value itt_canon2(const mpq3 &p1,
std::cout << "overlap [i [k j] l]\n";
}
/* k is intersect with p2q2. j is intersect with p1q1. */
- intersect_1 = tti_interp(p2, q2, p1, n1);
- intersect_2 = tti_interp(p1, q1, p2, n2);
+ intersect_1 = tti_interp(p2, q2, p1, n1, buf[0], buf[1], buf[2]);
+ intersect_2 = tti_interp(p1, q1, p2, n2, buf[0], buf[1], buf[2]);
}
}
}
@@ -1514,6 +1412,7 @@ static ITT_value intersect_tri_tri(const IMesh &tm, int t1, int t2)
return ITT_value(INONE);
}
+ mpq3 buf[2];
const mpq3 &p1 = vp1->co_exact;
const mpq3 &q1 = vq1->co_exact;
const mpq3 &r1 = vr1->co_exact;
@@ -1523,13 +1422,19 @@ static ITT_value intersect_tri_tri(const IMesh &tm, int t1, int t2)
const mpq3 &n2 = tri2.plane->norm_exact;
if (sp1 == 0) {
- sp1 = sgn(mpq3::dot(p1 - r2, n2));
+ buf[0] = p1;
+ buf[0] -= r2;
+ sp1 = sgn(mpq3::dot_with_buffer(buf[0], n2, buf[1]));
}
if (sq1 == 0) {
- sq1 = sgn(mpq3::dot(q1 - r2, n2));
+ buf[0] = q1;
+ buf[0] -= r2;
+ sq1 = sgn(mpq3::dot_with_buffer(buf[0], n2, buf[1]));
}
if (sr1 == 0) {
- sr1 = sgn(mpq3::dot(r1 - r2, n2));
+ buf[0] = r1;
+ buf[0] -= r2;
+ sr1 = sgn(mpq3::dot_with_buffer(buf[0], n2, buf[1]));
}
if (dbg_level > 1) {
@@ -1549,13 +1454,19 @@ static ITT_value intersect_tri_tri(const IMesh &tm, int t1, int t2)
/* Repeat for signs of t2's vertices with respect to plane of t1. */
const mpq3 &n1 = tri1.plane->norm_exact;
if (sp2 == 0) {
- sp2 = sgn(mpq3::dot(p2 - r1, n1));
+ buf[0] = p2;
+ buf[0] -= r1;
+ sp2 = sgn(mpq3::dot_with_buffer(buf[0], n1, buf[1]));
}
if (sq2 == 0) {
- sq2 = sgn(mpq3::dot(q2 - r1, n1));
+ buf[0] = q2;
+ buf[0] -= r1;
+ sq2 = sgn(mpq3::dot_with_buffer(buf[0], n1, buf[1]));
}
if (sr2 == 0) {
- sr2 = sgn(mpq3::dot(r2 - r1, n1));
+ buf[0] = r2;
+ buf[0] -= r1;
+ sr2 = sgn(mpq3::dot_with_buffer(buf[0], n1, buf[1]));
}
if (dbg_level > 1) {
@@ -1653,6 +1564,9 @@ struct CDT_data {
Vector<bool> is_reversed;
/** Result of running CDT on input with (vert, edge, face). */
CDT_result<mpq_class> cdt_out;
+ /** To speed up get_cdt_edge_orig, sometimes populate this map from vertex pair to output edge.
+ */
+ Map<std::pair<int, int>, int> verts_to_edge;
int proj_axis;
};
@@ -1811,6 +1725,30 @@ static CDT_data prepare_cdt_input_for_cluster(const IMesh &tm,
return ans;
}
+/* Return a copy of the argument with the integers ordered in ascending order. */
+static inline std::pair<int, int> sorted_int_pair(std::pair<int, int> pair)
+{
+ if (pair.first <= pair.second) {
+ return pair;
+ }
+ return std::pair<int, int>(pair.second, pair.first);
+}
+
+/**
+ * Build cd.verts_to_edge to map from a pair of cdt output indices to an index in cd.cdt_out.edge.
+ * Order the vertex indices so that the smaller one is first in the pair.
+ */
+static void populate_cdt_edge_map(Map<std::pair<int, int>, int> &verts_to_edge,
+ const CDT_result<mpq_class> &cdt_out)
+{
+ verts_to_edge.reserve(cdt_out.edge.size());
+ for (int e : cdt_out.edge.index_range()) {
+ std::pair<int, int> vpair = sorted_int_pair(cdt_out.edge[e]);
+ /* There should be only one edge for each vertex pair. */
+ verts_to_edge.add(vpair, e);
+ }
+}
+
/**
* Fills in cd.cdt_out with result of doing the cdt calculation on (vert, edge, face).
*/
@@ -1843,6 +1781,10 @@ static void do_cdt(CDT_data &cd)
}
cdt_in.epsilon = 0; /* TODO: needs attention for non-exact T. */
cd.cdt_out = blender::meshintersect::delaunay_2d_calc(cdt_in, CDT_INSIDE);
+ constexpr int make_edge_map_threshold = 15;
+ if (cd.cdt_out.edge.size() >= make_edge_map_threshold) {
+ populate_cdt_edge_map(cd.verts_to_edge, cd.cdt_out);
+ }
if (dbg_level > 0) {
std::cout << "\nCDT result\nVerts:\n";
for (int i : cd.cdt_out.vert.index_range()) {
@@ -1879,39 +1821,52 @@ static int get_cdt_edge_orig(
{
int foff = cd.cdt_out.face_edge_offset;
*r_is_intersect = false;
- for (int e : cd.cdt_out.edge.index_range()) {
- std::pair<int, int> edge = cd.cdt_out.edge[e];
- if ((edge.first == i0 && edge.second == i1) || (edge.first == i1 && edge.second == i0)) {
- /* Pick an arbitrary orig, but not one equal to NO_INDEX, if we can help it. */
- /* TODO: if edge has origs from more than on part of the nary input,
- * then want to set *r_is_intersect to true. */
- for (int orig_index : cd.cdt_out.edge_orig[e]) {
- /* orig_index encodes the triangle and pos within the triangle of the input edge. */
- if (orig_index >= foff) {
- int in_face_index = (orig_index / foff) - 1;
- int pos = orig_index % foff;
- /* We need to retrieve the edge orig field from the Face used to populate the
- * in_face_index'th face of the CDT, at the pos'th position of the face. */
- int in_tm_face_index = cd.input_face[in_face_index];
- BLI_assert(in_tm_face_index < in_tm.face_size());
- const Face *facep = in_tm.face(in_tm_face_index);
- BLI_assert(pos < facep->size());
- bool is_rev = cd.is_reversed[in_face_index];
- int eorig = is_rev ? facep->edge_orig[2 - pos] : facep->edge_orig[pos];
- if (eorig != NO_INDEX) {
- return eorig;
- }
- }
- else {
- /* This edge came from an edge input to the CDT problem,
- * so it is an intersect edge. */
- *r_is_intersect = true;
- /* TODO: maybe there is an orig index:
- * This happens if an input edge was formed by an input face having
- * an edge that is co-planar with the cluster, while the face as a whole is not. */
- return NO_INDEX;
- }
+ int e = NO_INDEX;
+ if (cd.verts_to_edge.size() > 0) {
+ /* Use the populated map to find the edge, if any, between vertices i0 and i1. */
+ std::pair<int, int> vpair = sorted_int_pair(std::pair<int, int>(i0, i1));
+ e = cd.verts_to_edge.lookup_default(vpair, NO_INDEX);
+ }
+ else {
+ for (int ee : cd.cdt_out.edge.index_range()) {
+ std::pair<int, int> edge = cd.cdt_out.edge[ee];
+ if ((edge.first == i0 && edge.second == i1) || (edge.first == i1 && edge.second == i0)) {
+ e = ee;
+ break;
}
+ }
+ }
+ if (e == NO_INDEX) {
+ return NO_INDEX;
+ }
+
+ /* Pick an arbitrary orig, but not one equal to NO_INDEX, if we can help it. */
+ /* TODO: if edge has origs from more than on part of the nary input,
+ * then want to set *r_is_intersect to true. */
+ for (int orig_index : cd.cdt_out.edge_orig[e]) {
+ /* orig_index encodes the triangle and pos within the triangle of the input edge. */
+ if (orig_index >= foff) {
+ int in_face_index = (orig_index / foff) - 1;
+ int pos = orig_index % foff;
+ /* We need to retrieve the edge orig field from the Face used to populate the
+ * in_face_index'th face of the CDT, at the pos'th position of the face. */
+ int in_tm_face_index = cd.input_face[in_face_index];
+ BLI_assert(in_tm_face_index < in_tm.face_size());
+ const Face *facep = in_tm.face(in_tm_face_index);
+ BLI_assert(pos < facep->size());
+ bool is_rev = cd.is_reversed[in_face_index];
+ int eorig = is_rev ? facep->edge_orig[2 - pos] : facep->edge_orig[pos];
+ if (eorig != NO_INDEX) {
+ return eorig;
+ }
+ }
+ else {
+ /* This edge came from an edge input to the CDT problem,
+ * so it is an intersect edge. */
+ *r_is_intersect = true;
+ /* TODO: maybe there is an orig index:
+ * This happens if an input edge was formed by an input face having
+ * an edge that is co-planar with the cluster, while the face as a whole is not. */
return NO_INDEX;
}
}
@@ -1919,6 +1874,256 @@ static int get_cdt_edge_orig(
}
/**
+ * Make a Face from the CDT output triangle cdt_out_t, which has corresponding input triangle
+ * cdt_in_t. The triangle indices that are in cd.input_face are from IMesh tm.
+ * Return a pointer to the newly constructed Face.
+ */
+static Face *cdt_tri_as_imesh_face(
+ int cdt_out_t, int cdt_in_t, const CDT_data &cd, const IMesh &tm, IMeshArena *arena)
+{
+ const CDT_result<mpq_class> &cdt_out = cd.cdt_out;
+ int t_orig = tm.face(cd.input_face[cdt_in_t])->orig;
+ BLI_assert(cdt_out.face[cdt_out_t].size() == 3);
+ int i0 = cdt_out.face[cdt_out_t][0];
+ int i1 = cdt_out.face[cdt_out_t][1];
+ int i2 = cdt_out.face[cdt_out_t][2];
+ mpq3 v0co = unproject_cdt_vert(cd, cdt_out.vert[i0]);
+ mpq3 v1co = unproject_cdt_vert(cd, cdt_out.vert[i1]);
+ mpq3 v2co = unproject_cdt_vert(cd, cdt_out.vert[i2]);
+ /* No need to provide an original index: if coord matches
+ * an original one, then it will already be in the arena
+ * with the correct orig field. */
+ const Vert *v0 = arena->add_or_find_vert(v0co, NO_INDEX);
+ const Vert *v1 = arena->add_or_find_vert(v1co, NO_INDEX);
+ const Vert *v2 = arena->add_or_find_vert(v2co, NO_INDEX);
+ Face *facep;
+ bool is_isect0;
+ bool is_isect1;
+ bool is_isect2;
+ if (cd.is_reversed[cdt_in_t]) {
+ int oe0 = get_cdt_edge_orig(i0, i2, cd, tm, &is_isect0);
+ int oe1 = get_cdt_edge_orig(i2, i1, cd, tm, &is_isect1);
+ int oe2 = get_cdt_edge_orig(i1, i0, cd, tm, &is_isect2);
+ facep = arena->add_face(
+ {v0, v2, v1}, t_orig, {oe0, oe1, oe2}, {is_isect0, is_isect1, is_isect2});
+ }
+ else {
+ int oe0 = get_cdt_edge_orig(i0, i1, cd, tm, &is_isect0);
+ int oe1 = get_cdt_edge_orig(i1, i2, cd, tm, &is_isect1);
+ int oe2 = get_cdt_edge_orig(i2, i0, cd, tm, &is_isect2);
+ facep = arena->add_face(
+ {v0, v1, v2}, t_orig, {oe0, oe1, oe2}, {is_isect0, is_isect1, is_isect2});
+ }
+ facep->populate_plane(false);
+ return facep;
+}
+
+/**
+ * Tessellate face f into triangles and return an array of `const Face *`
+ * giving that triangulation. Intended to be used when f has > 4 vertices.
+ * Care is taken so that the original edge index associated with
+ * each edge in the output triangles either matches the original edge
+ * for the (identical) edge of f, or else is -1. So diagonals added
+ * for triangulation can later be identified by having #NO_INDEX for original.
+ *
+ * This code uses Blenlib's BLI_polyfill_calc to do triangulation, and is therefore quite fast.
+ * Unfortunately, it can product degenerate triangles that mesh_intersect will remove, leaving
+ * the mesh non-PWN.
+ */
+static Array<Face *> polyfill_triangulate_poly(Face *f, IMeshArena *arena)
+{
+ /* Similar to loop body in BM_mesh_calc_tesselation. */
+ int flen = f->size();
+ BLI_assert(flen > 4);
+ if (!f->plane_populated()) {
+ f->populate_plane(false);
+ }
+ /* Project along negative face normal so (x,y) can be used in 2d. */
+ const double3 &poly_normal = f->plane->norm;
+ float no[3] = {float(poly_normal[0]), float(poly_normal[1]), float(poly_normal[2])};
+ normalize_v3(no);
+ float axis_mat[3][3];
+ float(*projverts)[2];
+ unsigned int(*tris)[3];
+ const int totfilltri = flen - 2;
+ /* Prepare projected vertices and array to receive triangles in tesselation. */
+ tris = static_cast<unsigned int(*)[3]>(MEM_malloc_arrayN(totfilltri, sizeof(*tris), __func__));
+ projverts = static_cast<float(*)[2]>(MEM_malloc_arrayN(flen, sizeof(*projverts), __func__));
+ axis_dominant_v3_to_m3_negate(axis_mat, no);
+ for (int j = 0; j < flen; ++j) {
+ const double3 &dco = (*f)[j]->co;
+ float co[3] = {float(dco[0]), float(dco[1]), float(dco[2])};
+ mul_v2_m3v3(projverts[j], axis_mat, co);
+ }
+ BLI_polyfill_calc(projverts, flen, 1, tris);
+ /* Put tesselation triangles into Face form. Record original edges where they exist. */
+ Array<Face *> ans(totfilltri);
+ for (int t = 0; t < totfilltri; ++t) {
+ unsigned int *tri = tris[t];
+ int eo[3];
+ const Vert *v[3];
+ for (int k = 0; k < 3; k++) {
+ BLI_assert(tri[k] < flen);
+ v[k] = (*f)[tri[k]];
+ /* If tri edge goes between two successive indices in
+ * the original face, then it is an original edge. */
+ if ((tri[k] + 1) % flen == tri[(k + 1) % 3]) {
+ eo[k] = f->edge_orig[tri[k]];
+ }
+ else {
+ eo[k] = NO_INDEX;
+ }
+ ans[t] = arena->add_face(
+ {v[0], v[1], v[2]}, f->orig, {eo[0], eo[1], eo[2]}, {false, false, false});
+ }
+ }
+
+ MEM_freeN(tris);
+ MEM_freeN(projverts);
+
+ return ans;
+}
+
+/**
+ * Tessellate face f into triangles and return an array of `const Face *`
+ * giving that triangulation.
+ * Care is taken so that the original edge index associated with
+ * each edge in the output triangles either matches the original edge
+ * for the (identical) edge of f, or else is -1. So diagonals added
+ * for triangulation can later be identified by having #NO_INDEX for original.
+ *
+ * The method used is to use the CDT triangulation. Usually that triangulation
+ * will only use the existing vertices. However, if the face self-intersects
+ * then the CDT triangulation will include the intersection points.
+ * If this happens, we use the polyfill triangulator instead. We don't
+ * use the polyfill triangulator by default because it can create degenerate
+ * triangles (which we can handle but they'll create non-manifold meshes).
+ */
+static Array<Face *> triangulate_poly(Face *f, IMeshArena *arena)
+{
+ int flen = f->size();
+ CDT_input<mpq_class> cdt_in;
+ cdt_in.vert = Array<mpq2>(flen);
+ cdt_in.face = Array<Vector<int>>(1);
+ cdt_in.face[0].reserve(flen);
+ for (int i : f->index_range()) {
+ cdt_in.face[0].append(i);
+ }
+ /* Project poly along dominant axis of normal to get 2d coords. */
+ if (!f->plane_populated()) {
+ f->populate_plane(false);
+ }
+ const double3 &poly_normal = f->plane->norm;
+ int axis = double3::dominant_axis(poly_normal);
+ /* If project down y axis as opposed to x or z, the orientation
+ * of the polygon will be reversed.
+ * Yet another reversal happens if the poly normal in the dominant
+ * direction is opposite that of the positive dominant axis. */
+ bool rev1 = (axis == 1);
+ bool rev2 = poly_normal[axis] < 0;
+ bool rev = rev1 ^ rev2;
+ for (int i = 0; i < flen; ++i) {
+ int ii = rev ? flen - i - 1 : i;
+ mpq2 &p2d = cdt_in.vert[ii];
+ int k = 0;
+ for (int j = 0; j < 3; ++j) {
+ if (j != axis) {
+ p2d[k++] = (*f)[ii]->co_exact[j];
+ }
+ }
+ }
+ CDT_result<mpq_class> cdt_out = delaunay_2d_calc(cdt_in, CDT_INSIDE);
+ int n_tris = cdt_out.face.size();
+ Array<Face *> ans(n_tris);
+ for (int t = 0; t < n_tris; ++t) {
+ int i_v_out[3];
+ const Vert *v[3];
+ int eo[3];
+ bool needs_steiner = false;
+ for (int i = 0; i < 3; ++i) {
+ i_v_out[i] = cdt_out.face[t][i];
+ if (cdt_out.vert_orig[i_v_out[i]].size() == 0) {
+ needs_steiner = true;
+ break;
+ }
+ v[i] = (*f)[cdt_out.vert_orig[i_v_out[i]][0]];
+ }
+ if (needs_steiner) {
+ /* Fall back on the polyfill triangulator. */
+ return polyfill_triangulate_poly(f, arena);
+ }
+ Map<std::pair<int, int>, int> verts_to_edge;
+ populate_cdt_edge_map(verts_to_edge, cdt_out);
+ int foff = cdt_out.face_edge_offset;
+ for (int i = 0; i < 3; ++i) {
+ std::pair<int, int> vpair(i_v_out[i], i_v_out[(i + 1) % 3]);
+ std::pair<int, int> vpair_canon = sorted_int_pair(vpair);
+ int e_out = verts_to_edge.lookup_default(vpair_canon, NO_INDEX);
+ BLI_assert(e_out != NO_INDEX);
+ eo[i] = NO_INDEX;
+ for (int orig : cdt_out.edge_orig[e_out]) {
+ if (orig >= foff) {
+ int pos = orig % foff;
+ BLI_assert(pos < f->size());
+ eo[i] = f->edge_orig[pos];
+ break;
+ }
+ }
+ }
+ if (rev) {
+ ans[t] = arena->add_face(
+ {v[0], v[2], v[1]}, f->orig, {eo[2], eo[1], eo[0]}, {false, false, false});
+ }
+ else {
+ ans[t] = arena->add_face(
+ {v[0], v[1], v[2]}, f->orig, {eo[0], eo[1], eo[2]}, {false, false, false});
+ }
+ }
+ return ans;
+}
+
+/**
+ * Return an #IMesh that is a triangulation of a mesh with general
+ * polygonal faces, #IMesh.
+ * Added diagonals will be distinguishable by having edge original
+ * indices of #NO_INDEX.
+ */
+IMesh triangulate_polymesh(IMesh &imesh, IMeshArena *arena)
+{
+ Vector<Face *> face_tris;
+ constexpr int estimated_tris_per_face = 3;
+ face_tris.reserve(estimated_tris_per_face * imesh.face_size());
+ for (Face *f : imesh.faces()) {
+ /* Tessellate face f, following plan similar to #BM_face_calc_tesselation. */
+ int flen = f->size();
+ if (flen == 3) {
+ face_tris.append(f);
+ }
+ else if (flen == 4) {
+ const Vert *v0 = (*f)[0];
+ const Vert *v1 = (*f)[1];
+ const Vert *v2 = (*f)[2];
+ const Vert *v3 = (*f)[3];
+ int eo_01 = f->edge_orig[0];
+ int eo_12 = f->edge_orig[1];
+ int eo_23 = f->edge_orig[2];
+ int eo_30 = f->edge_orig[3];
+ Face *f0 = arena->add_face({v0, v1, v2}, f->orig, {eo_01, eo_12, -1}, {false, false, false});
+ Face *f1 = arena->add_face({v0, v2, v3}, f->orig, {-1, eo_23, eo_30}, {false, false, false});
+ face_tris.append(f0);
+ face_tris.append(f1);
+ }
+ else {
+ Array<Face *> tris = triangulate_poly(f, arena);
+ for (Face *tri : tris) {
+ face_tris.append(tri);
+ }
+ }
+ }
+ return IMesh(face_tris);
+}
+
+/**
* Using the result of CDT in cd.cdt_out, extract an #IMesh representing the subdivision
* of input triangle t, which should be an element of cd.input_face.
*/
@@ -1939,55 +2144,17 @@ static IMesh extract_subdivided_tri(const CDT_data &cd,
BLI_assert(false);
return IMesh();
}
- int t_orig = in_tm.face(t)->orig;
constexpr int inline_buf_size = 20;
Vector<Face *, inline_buf_size> faces;
for (int f : cdt_out.face.index_range()) {
if (cdt_out.face_orig[f].contains(t_in_cdt)) {
- BLI_assert(cdt_out.face[f].size() == 3);
- int i0 = cdt_out.face[f][0];
- int i1 = cdt_out.face[f][1];
- int i2 = cdt_out.face[f][2];
- mpq3 v0co = unproject_cdt_vert(cd, cdt_out.vert[i0]);
- mpq3 v1co = unproject_cdt_vert(cd, cdt_out.vert[i1]);
- mpq3 v2co = unproject_cdt_vert(cd, cdt_out.vert[i2]);
- /* No need to provide an original index: if coord matches
- * an original one, then it will already be in the arena
- * with the correct orig field. */
- const Vert *v0 = arena->add_or_find_vert(v0co, NO_INDEX);
- const Vert *v1 = arena->add_or_find_vert(v1co, NO_INDEX);
- const Vert *v2 = arena->add_or_find_vert(v2co, NO_INDEX);
- Face *facep;
- bool is_isect0;
- bool is_isect1;
- bool is_isect2;
- if (cd.is_reversed[t_in_cdt]) {
- int oe0 = get_cdt_edge_orig(i0, i2, cd, in_tm, &is_isect0);
- int oe1 = get_cdt_edge_orig(i2, i1, cd, in_tm, &is_isect1);
- int oe2 = get_cdt_edge_orig(i1, i0, cd, in_tm, &is_isect2);
- facep = arena->add_face(
- {v0, v2, v1}, t_orig, {oe0, oe1, oe2}, {is_isect0, is_isect1, is_isect2});
- }
- else {
- int oe0 = get_cdt_edge_orig(i0, i1, cd, in_tm, &is_isect0);
- int oe1 = get_cdt_edge_orig(i1, i2, cd, in_tm, &is_isect1);
- int oe2 = get_cdt_edge_orig(i2, i0, cd, in_tm, &is_isect2);
- facep = arena->add_face(
- {v0, v1, v2}, t_orig, {oe0, oe1, oe2}, {is_isect0, is_isect1, is_isect2});
- }
- facep->populate_plane(false);
+ Face *facep = cdt_tri_as_imesh_face(f, t_in_cdt, cd, in_tm, arena);
faces.append(facep);
}
}
return IMesh(faces);
}
-static IMesh extract_single_tri(const IMesh &tm, int t)
-{
- Face *f = tm.face(t);
- return IMesh({f});
-}
-
static bool bvhtreeverlap_cmp(const BVHTreeOverlap &a, const BVHTreeOverlap &b)
{
if (a.indexA < b.indexA) {
@@ -2203,7 +2370,7 @@ static void calc_overlap_itts(Map<std::pair<int, int>, ITT_value> &itt_map,
}
/**
- * Data needed for parallelization of calc_subdivided_tris.
+ * Data needed for parallelization of calc_subdivided_non_cluster_tris.
*/
struct OverlapTriRange {
int tri_index;
@@ -2280,14 +2447,13 @@ static void calc_subdivided_tri_range_func(void *__restrict userdata,
* r_tri_subdivided with the result of intersecting it with
* all the other triangles in the mesh, if it intersects any others.
* But don't do this for triangles that are part of a cluster.
- * Also, do nothing here if the answer is just the triangle itself.
*/
-static void calc_subdivided_tris(Array<IMesh> &r_tri_subdivided,
- const IMesh &tm,
- const Map<std::pair<int, int>, ITT_value> &itt_map,
- const CoplanarClusterInfo &clinfo,
- const TriOverlaps &ov,
- IMeshArena *arena)
+static void calc_subdivided_non_cluster_tris(Array<IMesh> &r_tri_subdivided,
+ const IMesh &tm,
+ const Map<std::pair<int, int>, ITT_value> &itt_map,
+ const CoplanarClusterInfo &clinfo,
+ const TriOverlaps &ov,
+ IMeshArena *arena)
{
const int dbg_level = 0;
if (dbg_level > 0) {
@@ -2327,6 +2493,54 @@ static void calc_subdivided_tris(Array<IMesh> &r_tri_subdivided,
settings.use_threading = intersect_use_threading;
BLI_task_parallel_range(
0, overlap_tri_range_tot, &data, calc_subdivided_tri_range_func, &settings);
+ /* Now have to put in the triangles that are the same as the input ones, and not in clusters.
+ */
+ for (int t : tm.face_index_range()) {
+ if (r_tri_subdivided[t].face_size() == 0 && clinfo.tri_cluster(t) == NO_INDEX) {
+ r_tri_subdivided[t] = IMesh({tm.face(t)});
+ }
+ }
+}
+
+/**
+ * For each cluster in clinfo, extract the triangles from the cluster
+ * that correspond to each original triangle t that is part of the cluster,
+ * and put the resulting triangles into an IMesh in tri_subdivided[t].
+ * We have already done the CDT for the triangles in the cluster, whose
+ * result is in cluster_subdivided[c] for each cluster c.
+ */
+static void calc_cluster_tris(Array<IMesh> &tri_subdivided,
+ const IMesh &tm,
+ const CoplanarClusterInfo &clinfo,
+ const Array<CDT_data> &cluster_subdivided,
+ IMeshArena *arena)
+{
+ for (int c : clinfo.index_range()) {
+ const CoplanarCluster &cl = clinfo.cluster(c);
+ const CDT_data &cd = cluster_subdivided[c];
+ /* Each triangle in cluster c should be an input triangle in cd.input_faces.
+ * (See prepare_cdt_input_for_cluster.)
+ * So accumulate a Vector of Face* for each input face by going through the
+ * output faces and making a Face for each input face that it is part of.
+ * (The Boolean algorithm wants duplicates if a given output triangle is part
+ * of more than one input triangle.)
+ */
+ int n_cluster_tris = cl.tot_tri();
+ const CDT_result<mpq_class> &cdt_out = cd.cdt_out;
+ BLI_assert(cd.input_face.size() == n_cluster_tris);
+ Array<Vector<Face *>> face_vec(n_cluster_tris);
+ for (int cdt_out_t : cdt_out.face.index_range()) {
+ for (int cdt_in_t : cdt_out.face_orig[cdt_out_t]) {
+ Face *f = cdt_tri_as_imesh_face(cdt_out_t, cdt_in_t, cd, tm, arena);
+ face_vec[cdt_in_t].append(f);
+ }
+ }
+ for (int cdt_in_t : cd.input_face.index_range()) {
+ int tm_t = cd.input_face[cdt_in_t];
+ BLI_assert(tri_subdivided[tm_t].face_size() == 0);
+ tri_subdivided[tm_t] = IMesh(face_vec[cdt_in_t]);
+ }
+ }
}
static CDT_data calc_cluster_subdivided(const CoplanarClusterInfo &clinfo,
@@ -2699,10 +2913,11 @@ IMesh trimesh_nary_intersect(const IMesh &tm_in,
doperfmax(2, tri_ov.overlap().size());
# endif
Array<IMesh> tri_subdivided(tm_clean->face_size());
- calc_subdivided_tris(tri_subdivided, *tm_clean, itt_map, clinfo, tri_ov, arena);
+ calc_subdivided_non_cluster_tris(tri_subdivided, *tm_clean, itt_map, clinfo, tri_ov, arena);
# ifdef PERFDEBUG
double subdivided_tris_time = PIL_check_seconds_timer();
- std::cout << "subdivided tris found, time = " << subdivided_tris_time - itt_time << "\n";
+ std::cout << "subdivided non-cluster tris found, time = " << subdivided_tris_time - itt_time
+ << "\n";
# endif
Array<CDT_data> cluster_subdivided(clinfo.tot_cluster());
for (int c : clinfo.index_range()) {
@@ -2713,19 +2928,11 @@ IMesh trimesh_nary_intersect(const IMesh &tm_in,
std::cout << "subdivided clusters found, time = "
<< cluster_subdivide_time - subdivided_tris_time << "\n";
# endif
- for (int t : tm_clean->face_index_range()) {
- int c = clinfo.tri_cluster(t);
- if (c != NO_INDEX) {
- BLI_assert(tri_subdivided[t].face_size() == 0);
- tri_subdivided[t] = extract_subdivided_tri(cluster_subdivided[c], *tm_clean, t, arena);
- }
- else if (tri_subdivided[t].face_size() == 0) {
- tri_subdivided[t] = extract_single_tri(*tm_clean, t);
- }
- }
+ calc_cluster_tris(tri_subdivided, *tm_clean, clinfo, cluster_subdivided, arena);
# ifdef PERFDEBUG
double extract_time = PIL_check_seconds_timer();
- std::cout << "triangles extracted, time = " << extract_time - cluster_subdivide_time << "\n";
+ std::cout << "subdivided cluster tris found, time = " << extract_time - cluster_subdivide_time
+ << "\n";
# endif
IMesh combined = union_tri_subdivides(tri_subdivided);
if (dbg_level > 1) {
diff --git a/source/blender/blenlib/intern/noise.c b/source/blender/blenlib/intern/noise.c
index 996a1622239..8e28088c9fa 100644
--- a/source/blender/blenlib/intern/noise.c
+++ b/source/blender/blenlib/intern/noise.c
@@ -1121,7 +1121,7 @@ static float voronoi_CrS(float x, float y, float z)
/** \name Cell-Noise Implementation
* \{ */
-/* returns unsigned cellnoise */
+/** Returns unsigned cell-noise. */
static float BLI_cellNoiseU(float x, float y, float z)
{
/* avoid precision issues on unit coordinates */
@@ -1166,7 +1166,9 @@ void BLI_noise_cell_v3(float x, float y, float z, float ca[3])
/** \name Public API's
* \{ */
-/* newnoise: generic noise function for use with different noisebases */
+/**
+ * newnoise: generic noise function for use with different `noisebasis`.
+ */
float BLI_noise_generic_noise(
float noisesize, float x, float y, float z, bool hard, int noisebasis)
{
diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c
index b076d0f09c1..e8027836ed6 100644
--- a/source/blender/blenlib/intern/path_util.c
+++ b/source/blender/blenlib/intern/path_util.c
@@ -1044,17 +1044,17 @@ bool BLI_path_abs(char *path, const char *basepath)
#else
BLI_strncpy(tmp, path, sizeof(tmp));
- /* Check for loading a windows path on a posix system
- * in this case, there is no use in trying C:/ since it
- * will never exist on a unix os.
+ /* Check for loading a MS-Windows path on a POSIX system
+ * in this case, there is no use in trying `C:/` since it
+ * will never exist on a Unix system.
*
- * Add a '/' prefix and lowercase the drive-letter, remove the ':'.
- * C:\foo.JPG -> /c/foo.JPG */
+ * Add a `/` prefix and lowercase the drive-letter, remove the `:`.
+ * `C:\foo.JPG` -> `/c/foo.JPG` */
if (isalpha(tmp[0]) && (tmp[1] == ':') && ELEM(tmp[2], '\\', '/')) {
tmp[1] = tolower(tmp[0]); /* Replace ':' with drive-letter. */
tmp[0] = '/';
- /* '\' the slash will be converted later */
+ /* `\` the slash will be converted later. */
}
#endif
@@ -2013,9 +2013,9 @@ void BLI_path_slash_native(char *path)
{
#ifdef WIN32
if (path && BLI_strnlen(path, 3) > 2) {
- BLI_str_replace_char(path + 2, '/', '\\');
+ BLI_str_replace_char(path + 2, ALTSEP, SEP);
}
#else
- BLI_str_replace_char(path + BLI_path_unc_prefix_len(path), '\\', '/');
+ BLI_str_replace_char(path + BLI_path_unc_prefix_len(path), ALTSEP, SEP);
#endif
}
diff --git a/source/blender/blenlib/intern/polyfill_2d_beautify.c b/source/blender/blenlib/intern/polyfill_2d_beautify.c
index 7bfca149ffb..98fa5c872b0 100644
--- a/source/blender/blenlib/intern/polyfill_2d_beautify.c
+++ b/source/blender/blenlib/intern/polyfill_2d_beautify.c
@@ -375,7 +375,7 @@ void BLI_polyfill_beautify(const float (*coords)[2],
for (uint i = 0, base_index = 0; i < order_edges_len; base_index++) {
const struct OrderEdge *oe_a = &order_edges[i++];
const struct OrderEdge *oe_b = &order_edges[i++];
- BLI_assert(oe_a->verts[0] == oe_a->verts[0] && oe_a->verts[1] == oe_a->verts[1]);
+ BLI_assert(oe_a->verts[0] == oe_b->verts[0] && oe_a->verts[1] == oe_b->verts[1]);
half_edges[oe_a->e_half].e_radial = oe_b->e_half;
half_edges[oe_b->e_half].e_radial = oe_a->e_half;
half_edges[oe_a->e_half].base_index = base_index;
diff --git a/source/blender/blenlib/intern/storage.c b/source/blender/blenlib/intern/storage.c
index 287334a34ee..8964dac31a9 100644
--- a/source/blender/blenlib/intern/storage.c
+++ b/source/blender/blenlib/intern/storage.c
@@ -266,7 +266,8 @@ eFileAttributes BLI_file_attributes(const char *path)
if (attr & FILE_ATTRIBUTE_SPARSE_FILE) {
ret |= FILE_ATTR_SPARSE_FILE;
}
- if (attr & FILE_ATTRIBUTE_OFFLINE) {
+ if (attr & FILE_ATTRIBUTE_OFFLINE || attr & FILE_ATTRIBUTE_RECALL_ON_OPEN ||
+ attr & FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS) {
ret |= FILE_ATTR_OFFLINE;
}
if (attr & FILE_ATTRIBUTE_REPARSE_POINT) {
@@ -292,19 +293,23 @@ bool BLI_file_alias_target(const char *filepath,
/* This parameter can only be `const` on Linux since
* redirections are not supported there.
* NOLINTNEXTLINE: readability-non-const-parameter. */
- char r_targetpath[FILE_MAXDIR])
+ char r_targetpath[/*FILE_MAXDIR*/])
{
# ifdef WIN32
if (!BLI_path_extension_check(filepath, ".lnk")) {
return false;
}
- IShellLinkW *Shortcut = NULL;
- bool success = false;
- CoInitializeEx(NULL, COINIT_MULTITHREADED);
+ HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
+ if (FAILED(hr)) {
+ return false;
+ }
- HRESULT hr = CoCreateInstance(
+ IShellLinkW *Shortcut = NULL;
+ hr = CoCreateInstance(
&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, &IID_IShellLinkW, (LPVOID *)&Shortcut);
+
+ bool success = false;
if (SUCCEEDED(hr)) {
IPersistFile *PersistFile;
hr = Shortcut->lpVtbl->QueryInterface(Shortcut, &IID_IPersistFile, (LPVOID *)&PersistFile);
@@ -328,6 +333,7 @@ bool BLI_file_alias_target(const char *filepath,
Shortcut->lpVtbl->Release(Shortcut);
}
+ CoUninitialize();
return (success && r_targetpath[0]);
# else
UNUSED_VARS(r_targetpath, filepath);
diff --git a/source/blender/blenlib/intern/string.c b/source/blender/blenlib/intern/string.c
index ccc11af9f2b..3d40c6ef146 100644
--- a/source/blender/blenlib/intern/string.c
+++ b/source/blender/blenlib/intern/string.c
@@ -540,6 +540,29 @@ void BLI_str_replace_char(char *str, char src, char dst)
}
/**
+ * Simple exact-match string replacement.
+ *
+ * \param replace_table: Array of source, destination pairs.
+ *
+ * \note Larger tables should use a hash table.
+ */
+bool BLI_str_replace_table_exact(char *string,
+ const size_t string_len,
+ const char *replace_table[][2],
+ int replace_table_len)
+{
+ for (int i = 0; i < replace_table_len; i++) {
+ if (STREQ(string, replace_table[i][0])) {
+ BLI_strncpy(string, replace_table[i][1], string_len);
+ return true;
+ }
+ }
+ return false;
+}
+
+/** \} */
+
+/**
* Compare two strings without regard to case.
*
* \retval True if the strings are equal, false otherwise.
diff --git a/source/blender/blenlib/intern/task_iterator.c b/source/blender/blenlib/intern/task_iterator.c
index 38271e5823f..ee41c277b34 100644
--- a/source/blender/blenlib/intern/task_iterator.c
+++ b/source/blender/blenlib/intern/task_iterator.c
@@ -29,11 +29,16 @@
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_mempool.h"
+#include "BLI_mempool_private.h"
#include "BLI_task.h"
#include "BLI_threads.h"
#include "atomic_ops.h"
+/* -------------------------------------------------------------------- */
+/** \name Macros
+ * \{ */
+
/* Allows to avoid using malloc for userdata_chunk in tasks, when small enough. */
#define MALLOCA(_size) ((_size) <= 8192) ? alloca((_size)) : MEM_mallocN((_size), __func__)
#define MALLOCA_FREE(_mem, _size) \
@@ -42,6 +47,12 @@
} \
((void)0)
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Generic Iteration
+ * \{ */
+
BLI_INLINE void task_parallel_calc_chunk_size(const TaskParallelSettings *settings,
const int tot_items,
int num_tasks,
@@ -182,9 +193,12 @@ static void task_parallel_iterator_no_threads(const TaskParallelSettings *settin
parallel_iterator_func_do(state, userdata_chunk);
- if (use_userdata_chunk && settings->func_free != NULL) {
- /* `func_free` should only free data that was created during execution of `func`. */
- settings->func_free(state->userdata, userdata_chunk_local);
+ if (use_userdata_chunk) {
+ if (settings->func_free != NULL) {
+ /* `func_free` should only free data that was created during execution of `func`. */
+ settings->func_free(state->userdata, userdata_chunk_local);
+ }
+ MALLOCA_FREE(userdata_chunk_local, userdata_chunk_size);
}
}
@@ -223,7 +237,7 @@ static void task_parallel_iterator_do(const TaskParallelSettings *settings,
void *userdata_chunk_array = NULL;
const bool use_userdata_chunk = (userdata_chunk_size != 0) && (userdata_chunk != NULL);
- TaskPool *task_pool = BLI_task_pool_create(state, TASK_PRIORITY_HIGH);
+ TaskPool *task_pool = BLI_task_pool_create(state, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON);
if (use_userdata_chunk) {
userdata_chunk_array = MALLOCA(userdata_chunk_size * num_tasks);
@@ -241,14 +255,16 @@ static void task_parallel_iterator_do(const TaskParallelSettings *settings,
BLI_task_pool_work_and_wait(task_pool);
BLI_task_pool_free(task_pool);
- if (use_userdata_chunk && (settings->func_reduce != NULL || settings->func_free != NULL)) {
- for (size_t i = 0; i < num_tasks; i++) {
- userdata_chunk_local = (char *)userdata_chunk_array + (userdata_chunk_size * i);
- if (settings->func_reduce != NULL) {
- settings->func_reduce(state->userdata, userdata_chunk, userdata_chunk_local);
- }
- if (settings->func_free != NULL) {
- settings->func_free(state->userdata, userdata_chunk_local);
+ if (use_userdata_chunk) {
+ if (settings->func_reduce != NULL || settings->func_free != NULL) {
+ for (size_t i = 0; i < num_tasks; i++) {
+ userdata_chunk_local = (char *)userdata_chunk_array + (userdata_chunk_size * i);
+ if (settings->func_reduce != NULL) {
+ settings->func_reduce(state->userdata, userdata_chunk, userdata_chunk_local);
+ }
+ if (settings->func_free != NULL) {
+ settings->func_free(state->userdata, userdata_chunk_local);
+ }
}
}
MALLOCA_FREE(userdata_chunk_array, userdata_chunk_size * num_tasks);
@@ -294,6 +310,12 @@ void BLI_task_parallel_iterator(void *userdata,
task_parallel_iterator_do(settings, &state);
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name ListBase Iteration
+ * \{ */
+
static void task_parallel_listbase_get(void *__restrict UNUSED(userdata),
const TaskParallelTLS *__restrict UNUSED(tls),
void **r_next_item,
@@ -343,8 +365,11 @@ void BLI_task_parallel_listbase(ListBase *listbase,
task_parallel_iterator_do(settings, &state);
}
-#undef MALLOCA
-#undef MALLOCA_FREE
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name MemPool Iteration
+ * \{ */
typedef struct ParallelMempoolState {
void *userdata;
@@ -354,11 +379,12 @@ typedef struct ParallelMempoolState {
static void parallel_mempool_func(TaskPool *__restrict pool, void *taskdata)
{
ParallelMempoolState *__restrict state = BLI_task_pool_user_data(pool);
- BLI_mempool_iter *iter = taskdata;
- MempoolIterData *item;
+ BLI_mempool_threadsafe_iter *iter = &((ParallelMempoolTaskData *)taskdata)->ts_iter;
+ TaskParallelTLS *tls = &((ParallelMempoolTaskData *)taskdata)->tls;
- while ((item = BLI_mempool_iterstep(iter)) != NULL) {
- state->func(state->userdata, item);
+ MempoolIterData *item;
+ while ((item = mempool_iter_threadsafe_step(iter)) != NULL) {
+ state->func(state->userdata, item, tls);
}
}
@@ -368,16 +394,14 @@ static void parallel_mempool_func(TaskPool *__restrict pool, void *taskdata)
* \param mempool: The iterable BLI_mempool to loop over.
* \param userdata: Common userdata passed to all instances of \a func.
* \param func: Callback function.
- * \param use_threading: If \a true, actually split-execute loop in threads,
- * else just do a sequential for loop
- * (allows caller to use any kind of test to switch on parallelization or not).
+ * \param settings: See public API doc of TaskParallelSettings for description of all settings.
*
* \note There is no static scheduling here.
*/
void BLI_task_parallel_mempool(BLI_mempool *mempool,
void *userdata,
TaskParallelMempoolFunc func,
- const bool use_threading)
+ const TaskParallelSettings *settings)
{
TaskPool *task_pool;
ParallelMempoolState state;
@@ -387,18 +411,38 @@ void BLI_task_parallel_mempool(BLI_mempool *mempool,
return;
}
- if (!use_threading) {
+ void *userdata_chunk = settings->userdata_chunk;
+ const size_t userdata_chunk_size = settings->userdata_chunk_size;
+ void *userdata_chunk_local = NULL;
+ void *userdata_chunk_array = NULL;
+ const bool use_userdata_chunk = (userdata_chunk_size != 0) && (userdata_chunk != NULL);
+
+ if (!settings->use_threading) {
+ TaskParallelTLS tls = {NULL};
+ if (use_userdata_chunk) {
+ userdata_chunk_local = MALLOCA(userdata_chunk_size);
+ memcpy(userdata_chunk_local, userdata_chunk, userdata_chunk_size);
+ tls.userdata_chunk = userdata_chunk_local;
+ }
+
BLI_mempool_iter iter;
BLI_mempool_iternew(mempool, &iter);
- for (void *item = BLI_mempool_iterstep(&iter); item != NULL;
- item = BLI_mempool_iterstep(&iter)) {
- func(userdata, item);
+ void *item;
+ while ((item = BLI_mempool_iterstep(&iter))) {
+ func(userdata, item, &tls);
+ }
+
+ if (settings->func_free != NULL) {
+ /* `func_free` should only free data that was created during execution of `func`. */
+ settings->func_free(userdata, userdata_chunk_local);
}
+
+ MALLOCA_FREE(userdata_chunk_local, userdata_chunk_size);
return;
}
- task_pool = BLI_task_pool_create(&state, TASK_PRIORITY_HIGH);
+ task_pool = BLI_task_pool_create(&state, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON);
num_threads = BLI_task_scheduler_num_threads();
/* The idea here is to prevent creating task for each of the loop iterations
@@ -410,16 +454,46 @@ void BLI_task_parallel_mempool(BLI_mempool *mempool,
state.userdata = userdata;
state.func = func;
- BLI_mempool_iter *mempool_iterators = BLI_mempool_iter_threadsafe_create(mempool,
- (size_t)num_tasks);
+ if (use_userdata_chunk) {
+ userdata_chunk_array = MALLOCA(userdata_chunk_size * num_tasks);
+ }
+
+ ParallelMempoolTaskData *mempool_iterator_data = mempool_iter_threadsafe_create(
+ mempool, (size_t)num_tasks);
for (i = 0; i < num_tasks; i++) {
+ if (use_userdata_chunk) {
+ userdata_chunk_local = (char *)userdata_chunk_array + (userdata_chunk_size * i);
+ memcpy(userdata_chunk_local, userdata_chunk, userdata_chunk_size);
+ }
+ mempool_iterator_data[i].tls.userdata_chunk = userdata_chunk_local;
+
/* Use this pool's pre-allocated tasks. */
- BLI_task_pool_push(task_pool, parallel_mempool_func, &mempool_iterators[i], false, NULL);
+ BLI_task_pool_push(task_pool, parallel_mempool_func, &mempool_iterator_data[i], false, NULL);
}
BLI_task_pool_work_and_wait(task_pool);
BLI_task_pool_free(task_pool);
- BLI_mempool_iter_threadsafe_free(mempool_iterators);
+ if (use_userdata_chunk) {
+ if ((settings->func_free != NULL) || (settings->func_reduce != NULL)) {
+ for (i = 0; i < num_tasks; i++) {
+ if (settings->func_reduce) {
+ settings->func_reduce(
+ userdata, userdata_chunk, mempool_iterator_data[i].tls.userdata_chunk);
+ }
+ if (settings->func_free) {
+ settings->func_free(userdata, mempool_iterator_data[i].tls.userdata_chunk);
+ }
+ }
+ }
+ MALLOCA_FREE(userdata_chunk_array, userdata_chunk_size * num_tasks);
+ }
+
+ mempool_iter_threadsafe_destroy(mempool_iterator_data);
}
+
+#undef MALLOCA
+#undef MALLOCA_FREE
+
+/** \} */
diff --git a/source/blender/blenlib/intern/task_pool.cc b/source/blender/blenlib/intern/task_pool.cc
index 00ba659a9c8..d72674c1c00 100644
--- a/source/blender/blenlib/intern/task_pool.cc
+++ b/source/blender/blenlib/intern/task_pool.cc
@@ -22,6 +22,7 @@
#include <cstdlib>
#include <memory>
+#include <thread>
#include <utility>
#include "MEM_guardedalloc.h"
@@ -111,15 +112,7 @@ class Task {
Task &operator=(const Task &other) = delete;
Task &operator=(Task &&other) = delete;
- /* Execute task. */
- void operator()() const
- {
-#ifdef WITH_TBB
- tbb::this_task_arena::isolate([this] { run(pool, taskdata); });
-#else
- run(pool, taskdata);
-#endif
- }
+ void operator()() const;
};
/* TBB Task Group.
@@ -147,10 +140,6 @@ class TBBTaskGroup : public tbb::task_group {
}
# endif
}
-
- ~TBBTaskGroup()
- {
- }
};
#endif
@@ -167,13 +156,16 @@ enum TaskPoolType {
struct TaskPool {
TaskPoolType type;
bool use_threads;
+ TaskIsolation task_isolation;
ThreadMutex user_mutex;
void *userdata;
- /* TBB task pool. */
#ifdef WITH_TBB
+ /* TBB task pool. */
TBBTaskGroup tbb_group;
+ /* This is used to detect a common way to accidentally create a deadlock with task isolation. */
+ std::thread::id task_pool_create_thread_id;
#endif
volatile bool is_suspended;
BLI_mempool *suspended_mempool;
@@ -184,6 +176,36 @@ struct TaskPool {
volatile bool background_is_canceling;
};
+/* Execute task. */
+void Task::operator()() const
+{
+#ifdef WITH_TBB
+ if (pool->task_isolation == TASK_ISOLATION_ON) {
+ tbb::this_task_arena::isolate([this] { run(pool, taskdata); });
+ return;
+ }
+#endif
+ run(pool, taskdata);
+}
+
+static void assert_on_valid_thread(TaskPool *pool)
+{
+ /* TODO: Remove this `return` to enable the check. */
+ return;
+#ifdef DEBUG
+# ifdef WITH_TBB
+ if (pool->task_isolation == TASK_ISOLATION_ON) {
+ const std::thread::id current_id = std::this_thread::get_id();
+ /* This task pool is modified from different threads. To avoid deadlocks, `TASK_ISOLATION_OFF`
+ * has to be used. Task isolation can still be used in a more fine-grained way within the
+ * tasks, but should not be enabled for the entire task pool. */
+ BLI_assert(pool->task_pool_create_thread_id == current_id);
+ }
+# endif
+#endif
+ UNUSED_VARS_NDEBUG(pool);
+}
+
/* TBB Task Pool.
*
* Task pool using the TBB scheduler for tasks. When building without TBB
@@ -369,7 +391,10 @@ static void background_task_pool_free(TaskPool *pool)
/* Task Pool */
-static TaskPool *task_pool_create_ex(void *userdata, TaskPoolType type, TaskPriority priority)
+static TaskPool *task_pool_create_ex(void *userdata,
+ TaskPoolType type,
+ TaskPriority priority,
+ TaskIsolation task_isolation)
{
const bool use_threads = BLI_task_scheduler_num_threads() > 1 && type != TASK_POOL_NO_THREADS;
@@ -385,6 +410,11 @@ static TaskPool *task_pool_create_ex(void *userdata, TaskPoolType type, TaskPrio
pool->type = type;
pool->use_threads = use_threads;
+ pool->task_isolation = task_isolation;
+
+#ifdef WITH_TBB
+ pool->task_pool_create_thread_id = std::this_thread::get_id();
+#endif
pool->userdata = userdata;
BLI_mutex_init(&pool->user_mutex);
@@ -407,9 +437,9 @@ static TaskPool *task_pool_create_ex(void *userdata, TaskPoolType type, TaskPrio
/**
* Create a normal task pool. Tasks will be executed as soon as they are added.
*/
-TaskPool *BLI_task_pool_create(void *userdata, TaskPriority priority)
+TaskPool *BLI_task_pool_create(void *userdata, TaskPriority priority, TaskIsolation task_isolation)
{
- return task_pool_create_ex(userdata, TASK_POOL_TBB, priority);
+ return task_pool_create_ex(userdata, TASK_POOL_TBB, priority, task_isolation);
}
/**
@@ -424,9 +454,11 @@ TaskPool *BLI_task_pool_create(void *userdata, TaskPriority priority)
* they could end never being executed, since the 'fallback' background thread is already
* busy with parent task in single-threaded context).
*/
-TaskPool *BLI_task_pool_create_background(void *userdata, TaskPriority priority)
+TaskPool *BLI_task_pool_create_background(void *userdata,
+ TaskPriority priority,
+ TaskIsolation task_isolation)
{
- return task_pool_create_ex(userdata, TASK_POOL_BACKGROUND, priority);
+ return task_pool_create_ex(userdata, TASK_POOL_BACKGROUND, priority, task_isolation);
}
/**
@@ -434,9 +466,11 @@ TaskPool *BLI_task_pool_create_background(void *userdata, TaskPriority priority)
* for until BLI_task_pool_work_and_wait() is called. This helps reducing threading
* overhead when pushing huge amount of small initial tasks from the main thread.
*/
-TaskPool *BLI_task_pool_create_suspended(void *userdata, TaskPriority priority)
+TaskPool *BLI_task_pool_create_suspended(void *userdata,
+ TaskPriority priority,
+ TaskIsolation task_isolation)
{
- return task_pool_create_ex(userdata, TASK_POOL_TBB_SUSPENDED, priority);
+ return task_pool_create_ex(userdata, TASK_POOL_TBB_SUSPENDED, priority, task_isolation);
}
/**
@@ -445,7 +479,8 @@ TaskPool *BLI_task_pool_create_suspended(void *userdata, TaskPriority priority)
*/
TaskPool *BLI_task_pool_create_no_threads(void *userdata)
{
- return task_pool_create_ex(userdata, TASK_POOL_NO_THREADS, TASK_PRIORITY_HIGH);
+ return task_pool_create_ex(
+ userdata, TASK_POOL_NO_THREADS, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON);
}
/**
@@ -454,7 +489,7 @@ TaskPool *BLI_task_pool_create_no_threads(void *userdata)
*/
TaskPool *BLI_task_pool_create_background_serial(void *userdata, TaskPriority priority)
{
- return task_pool_create_ex(userdata, TASK_POOL_BACKGROUND_SERIAL, priority);
+ return task_pool_create_ex(userdata, TASK_POOL_BACKGROUND_SERIAL, priority, TASK_ISOLATION_ON);
}
void BLI_task_pool_free(TaskPool *pool)
@@ -482,6 +517,8 @@ void BLI_task_pool_push(TaskPool *pool,
bool free_taskdata,
TaskFreeFunction freedata)
{
+ assert_on_valid_thread(pool);
+
Task task(pool, run, taskdata, free_taskdata, freedata);
switch (pool->type) {
@@ -499,6 +536,8 @@ void BLI_task_pool_push(TaskPool *pool,
void BLI_task_pool_work_and_wait(TaskPool *pool)
{
+ assert_on_valid_thread(pool);
+
switch (pool->type) {
case TASK_POOL_TBB:
case TASK_POOL_TBB_SUSPENDED:
diff --git a/source/blender/blenlib/intern/timecode.c b/source/blender/blenlib/intern/timecode.c
index 9586da941a4..7d7436411ac 100644
--- a/source/blender/blenlib/intern/timecode.c
+++ b/source/blender/blenlib/intern/timecode.c
@@ -216,10 +216,10 @@ size_t BLI_timecode_string_from_time_simple(char *str,
const int hun = ((int)(fmod(time_seconds, 1.0) * 100));
if (hr) {
- rlen = BLI_snprintf(str, maxncpy, "%.2d:%.2d:%.2d.%.2d", hr, min, sec, hun);
+ rlen = BLI_snprintf_rlen(str, maxncpy, "%.2d:%.2d:%.2d.%.2d", hr, min, sec, hun);
}
else {
- rlen = BLI_snprintf(str, maxncpy, "%.2d:%.2d.%.2d", min, sec, hun);
+ rlen = BLI_snprintf_rlen(str, maxncpy, "%.2d:%.2d.%.2d", min, sec, hun);
}
return rlen;
diff --git a/source/blender/blenlib/intern/uvproject.c b/source/blender/blenlib/intern/uvproject.c
index 329c4d48fe8..093d08e643d 100644
--- a/source/blender/blenlib/intern/uvproject.c
+++ b/source/blender/blenlib/intern/uvproject.c
@@ -134,7 +134,7 @@ void BLI_uvproject_from_view(float target[2],
/* 'rotmat' can be `obedit->obmat` when uv project is used.
* 'winx' and 'winy' can be from `scene->r.xsch/ysch` */
-ProjCameraInfo *BLI_uvproject_camera_info(Object *ob, float (*rotmat)[4], float winx, float winy)
+ProjCameraInfo *BLI_uvproject_camera_info(Object *ob, float rotmat[4][4], float winx, float winy)
{
ProjCameraInfo uci;
Camera *camera = ob->data;
diff --git a/source/blender/blenlib/intern/winstuff.c b/source/blender/blenlib/intern/winstuff.c
index 333b6783087..3aa61d1fec5 100644
--- a/source/blender/blenlib/intern/winstuff.c
+++ b/source/blender/blenlib/intern/winstuff.c
@@ -94,9 +94,9 @@ void BLI_windows_register_blend_extension(const bool background)
GetModuleFileName(0, BlPath, MAX_PATH);
/* Replace the actual app name with the wrapper. */
- blender_app = strstr(BlPath, "blender-app.exe");
+ blender_app = strstr(BlPath, "blender.exe");
if (blender_app != NULL) {
- strcpy(blender_app, "blender.exe");
+ strcpy(blender_app, "blender-launcher.exe");
}
/* root is HKLM by default */
diff --git a/source/blender/blenlib/tests/BLI_color_test.cc b/source/blender/blenlib/tests/BLI_color_test.cc
new file mode 100644
index 00000000000..14796e6bf71
--- /dev/null
+++ b/source/blender/blenlib/tests/BLI_color_test.cc
@@ -0,0 +1,133 @@
+/* Apache License, Version 2.0 */
+
+#include "testing/testing.h"
+
+#include "BLI_color.hh"
+
+namespace blender::tests {
+
+/**
+ * \name Conversions
+ * \{ */
+
+TEST(color, ThemeByteToFloat)
+{
+ ColorTheme4b theme_byte(192, 128, 64, 128);
+ ColorTheme4f theme_float = theme_byte.to_4f();
+ EXPECT_NEAR(0.75f, theme_float.r, 0.01f);
+ EXPECT_NEAR(0.5f, theme_float.g, 0.01f);
+ EXPECT_NEAR(0.25f, theme_float.b, 0.01f);
+ EXPECT_NEAR(0.5f, theme_float.a, 0.01f);
+}
+
+TEST(color, SrgbStraightFloatToByte)
+{
+ ColorTheme4f theme_float(0.75f, 0.5f, 0.25f, 0.5f);
+ ColorTheme4b theme_byte = theme_float.to_4b();
+ EXPECT_EQ(191, theme_byte.r);
+ EXPECT_EQ(128, theme_byte.g);
+ EXPECT_EQ(64, theme_byte.b);
+ EXPECT_EQ(128, theme_byte.a);
+}
+
+TEST(color, SrgbStraightToSceneLinearPremultiplied)
+{
+ BLI_init_srgb_conversion();
+
+ ColorTheme4b theme(192, 128, 64, 128);
+ ColorSceneLinear4f<eAlpha::Premultiplied> linear =
+ BLI_color_convert_to_scene_linear(theme).premultiply_alpha();
+ EXPECT_NEAR(0.26f, linear.r, 0.01f);
+ EXPECT_NEAR(0.11f, linear.g, 0.01f);
+ EXPECT_NEAR(0.02f, linear.b, 0.01f);
+ EXPECT_NEAR(0.5f, linear.a, 0.01f);
+}
+
+TEST(color, SceneLinearStraightToPremultiplied)
+{
+ ColorSceneLinear4f<eAlpha::Straight> straight(0.75f, 0.5f, 0.25f, 0.5f);
+ ColorSceneLinear4f<eAlpha::Premultiplied> premultiplied = straight.premultiply_alpha();
+ EXPECT_NEAR(0.37f, premultiplied.r, 0.01f);
+ EXPECT_NEAR(0.25f, premultiplied.g, 0.01f);
+ EXPECT_NEAR(0.12f, premultiplied.b, 0.01f);
+ EXPECT_NEAR(0.5f, premultiplied.a, 0.01f);
+}
+
+TEST(color, SceneLinearPremultipliedToStraight)
+{
+ ColorSceneLinear4f<eAlpha::Premultiplied> premultiplied(0.75f, 0.5f, 0.25f, 0.5f);
+ ColorSceneLinear4f<eAlpha::Straight> straight = premultiplied.unpremultiply_alpha();
+ EXPECT_NEAR(1.5f, straight.r, 0.01f);
+ EXPECT_NEAR(1.0f, straight.g, 0.01f);
+ EXPECT_NEAR(0.5f, straight.b, 0.01f);
+ EXPECT_NEAR(0.5f, straight.a, 0.01f);
+}
+
+TEST(color, SceneLinearStraightSrgbFloat)
+{
+ BLI_init_srgb_conversion();
+ ColorSceneLinear4f<eAlpha::Straight> linear(0.75f, 0.5f, 0.25f, 0.5f);
+ ColorTheme4f theme = BLI_color_convert_to_theme4f(linear);
+ EXPECT_NEAR(0.88f, theme.r, 0.01);
+ EXPECT_NEAR(0.73f, theme.g, 0.01);
+ EXPECT_NEAR(0.53f, theme.b, 0.01);
+ EXPECT_NEAR(0.5f, theme.a, 0.01);
+}
+
+TEST(color, SceneLinearPremultipliedToSrgbFloat)
+{
+ BLI_init_srgb_conversion();
+ ColorSceneLinear4f<eAlpha::Premultiplied> linear(0.75f, 0.5f, 0.25f, 0.5f);
+ ColorTheme4f theme = BLI_color_convert_to_theme4f(linear.unpremultiply_alpha());
+
+ EXPECT_NEAR(1.19f, theme.r, 0.01);
+ EXPECT_NEAR(1.0f, theme.g, 0.01);
+ EXPECT_NEAR(0.74f, theme.b, 0.01);
+ EXPECT_NEAR(0.5f, theme.a, 0.01);
+}
+
+TEST(color, SceneLinearStraightSrgbByte)
+{
+ BLI_init_srgb_conversion();
+ ColorSceneLinear4f<eAlpha::Straight> linear(0.75f, 0.5f, 0.25f, 0.5f);
+ ColorTheme4b theme = BLI_color_convert_to_theme4b(linear);
+ EXPECT_EQ(225, theme.r);
+ EXPECT_EQ(188, theme.g);
+ EXPECT_EQ(137, theme.b);
+ EXPECT_EQ(128, theme.a);
+}
+
+TEST(color, SceneLinearPremultipliedToSrgbByte)
+{
+ BLI_init_srgb_conversion();
+ ColorSceneLinear4f<eAlpha::Premultiplied> linear(0.75f, 0.5f, 0.25f, 0.5f);
+ ColorTheme4b theme = BLI_color_convert_to_theme4b(linear.unpremultiply_alpha());
+ EXPECT_EQ(255, theme.r);
+ EXPECT_EQ(255, theme.g);
+ EXPECT_EQ(188, theme.b);
+ EXPECT_EQ(128, theme.a);
+}
+
+TEST(color, SceneLinearByteEncoding)
+{
+ ColorSceneLinear4f<eAlpha::Premultiplied> linear(0.75f, 0.5f, 0.25f, 0.5f);
+ ColorSceneLinearByteEncoded4b<eAlpha::Premultiplied> encoded = linear.encode();
+ EXPECT_EQ(225, encoded.r);
+ EXPECT_EQ(188, encoded.g);
+ EXPECT_EQ(137, encoded.b);
+ EXPECT_EQ(128, encoded.a);
+}
+
+TEST(color, SceneLinearByteDecoding)
+{
+ ColorSceneLinearByteEncoded4b<eAlpha::Premultiplied> encoded(225, 188, 137, 128);
+ ColorSceneLinear4f<eAlpha::Premultiplied> decoded = encoded.decode();
+ EXPECT_NEAR(0.75f, decoded.r, 0.01f);
+ EXPECT_NEAR(0.5f, decoded.g, 0.01f);
+ EXPECT_NEAR(0.25f, decoded.b, 0.01f);
+ EXPECT_NEAR(0.5f, decoded.a, 0.01f);
+}
+
+/* \} */
+
+} // namespace blender::tests
diff --git a/source/blender/blenlib/tests/BLI_function_ref_test.cc b/source/blender/blenlib/tests/BLI_function_ref_test.cc
index cdcbccc72e8..74f5014142c 100644
--- a/source/blender/blenlib/tests/BLI_function_ref_test.cc
+++ b/source/blender/blenlib/tests/BLI_function_ref_test.cc
@@ -99,4 +99,29 @@ TEST(function_ref, ReferenceAnotherFunctionRef)
EXPECT_EQ(y(), 2);
}
+TEST(function_ref, CallSafe)
+{
+ FunctionRef<int()> f;
+ EXPECT_FALSE(f.call_safe().has_value());
+ auto func = []() { return 10; };
+ f = func;
+ EXPECT_TRUE(f.call_safe().has_value());
+ EXPECT_EQ(*f.call_safe(), 10);
+ f = {};
+ EXPECT_FALSE(f.call_safe().has_value());
+ BLI_STATIC_ASSERT((std::is_same_v<decltype(f.call_safe()), std::optional<int>>), "");
+}
+
+TEST(function_ref, CallSafeVoid)
+{
+ FunctionRef<void()> f;
+ BLI_STATIC_ASSERT((std::is_same_v<decltype(f.call_safe()), void>), "");
+ f.call_safe();
+ int value = 0;
+ auto func = [&]() { value++; };
+ f = func;
+ f.call_safe();
+ EXPECT_EQ(value, 1);
+}
+
} // namespace blender::tests
diff --git a/source/blender/blenlib/tests/BLI_linear_allocator_test.cc b/source/blender/blenlib/tests/BLI_linear_allocator_test.cc
index 977e5dba497..0e0145e592a 100644
--- a/source/blender/blenlib/tests/BLI_linear_allocator_test.cc
+++ b/source/blender/blenlib/tests/BLI_linear_allocator_test.cc
@@ -136,4 +136,17 @@ TEST(linear_allocator, ManyAllocations)
}
}
+TEST(linear_allocator, ConstructArray)
+{
+ LinearAllocator<> allocator;
+ MutableSpan<std::string> strings = allocator.construct_array<std::string>(4, "hello");
+ EXPECT_EQ(strings[0], "hello");
+ EXPECT_EQ(strings[1], "hello");
+ EXPECT_EQ(strings[2], "hello");
+ EXPECT_EQ(strings[3], "hello");
+ for (std::string &string : strings) {
+ string.~basic_string();
+ }
+}
+
} // namespace blender::tests
diff --git a/source/blender/blenlib/tests/BLI_linklist_lockfree_test.cc b/source/blender/blenlib/tests/BLI_linklist_lockfree_test.cc
index e9810aed179..8be89d66062 100644
--- a/source/blender/blenlib/tests/BLI_linklist_lockfree_test.cc
+++ b/source/blender/blenlib/tests/BLI_linklist_lockfree_test.cc
@@ -81,7 +81,7 @@ TEST(LockfreeLinkList, InsertMultipleConcurrent)
LockfreeLinkList list;
BLI_linklist_lockfree_init(&list);
/* Initialize task scheduler and pool. */
- TaskPool *pool = BLI_task_pool_create_suspended(&list, TASK_PRIORITY_HIGH);
+ TaskPool *pool = BLI_task_pool_create_suspended(&list, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON);
/* Push tasks to the pool. */
for (int i = 0; i < num_nodes; ++i) {
BLI_task_pool_push(pool, concurrent_insert, POINTER_FROM_INT(i), false, nullptr);
diff --git a/source/blender/blenlib/tests/BLI_map_test.cc b/source/blender/blenlib/tests/BLI_map_test.cc
index f1ae8fb3921..679a10e9ce0 100644
--- a/source/blender/blenlib/tests/BLI_map_test.cc
+++ b/source/blender/blenlib/tests/BLI_map_test.cc
@@ -604,6 +604,55 @@ TEST(map, GenericAlgorithms)
EXPECT_EQ(std::count(map.keys().begin(), map.keys().end(), 7), 1);
}
+TEST(map, AddAsVariadic)
+{
+ Map<int, StringRef> map;
+ map.add_as(3, "hello", 2);
+ map.add_as(2, "test", 1);
+ EXPECT_EQ(map.lookup(3), "he");
+ EXPECT_EQ(map.lookup(2), "t");
+}
+
+TEST(map, RemoveDuringIteration)
+{
+ Map<int, int> map;
+ map.add(2, 1);
+ map.add(5, 2);
+ map.add(1, 2);
+ map.add(6, 0);
+ map.add(3, 3);
+
+ EXPECT_EQ(map.size(), 5);
+
+ using Iter = Map<int, int>::MutableItemIterator;
+ Iter begin = map.items().begin();
+ Iter end = map.items().end();
+ for (Iter iter = begin; iter != end; ++iter) {
+ Map<int, int>::MutableItem item = *iter;
+ if (item.value == 2) {
+ map.remove(iter);
+ }
+ }
+
+ EXPECT_EQ(map.size(), 3);
+ EXPECT_EQ(map.lookup(2), 1);
+ EXPECT_EQ(map.lookup(6), 0);
+ EXPECT_EQ(map.lookup(3), 3);
+}
+
+TEST(map, LookupKey)
+{
+ Map<std::string, int> map;
+ map.add("a", 0);
+ map.add("b", 1);
+ map.add("c", 2);
+ EXPECT_EQ(map.lookup_key("a"), "a");
+ EXPECT_EQ(map.lookup_key_as("c"), "c");
+ EXPECT_EQ(map.lookup_key_ptr_as("d"), nullptr);
+ EXPECT_EQ(map.lookup_key_ptr_as("b")->size(), 1);
+ EXPECT_EQ(map.lookup_key_ptr("a"), map.lookup_key_ptr_as("a"));
+}
+
/**
* Set this to 1 to activate the benchmark. It is disabled by default, because it prints a lot.
*/
diff --git a/source/blender/blenlib/tests/BLI_stack_cxx_test.cc b/source/blender/blenlib/tests/BLI_stack_cxx_test.cc
index f1fcdae3a52..b3108381d78 100644
--- a/source/blender/blenlib/tests/BLI_stack_cxx_test.cc
+++ b/source/blender/blenlib/tests/BLI_stack_cxx_test.cc
@@ -93,6 +93,15 @@ TEST(stack, Push)
EXPECT_EQ(stack.size(), 2);
}
+TEST(stack, PushAs)
+{
+ Stack<StringRef> stack;
+ stack.push_as("hello", 3);
+ stack.push_as("world", 1);
+ EXPECT_EQ(stack.pop(), "w");
+ EXPECT_EQ(stack.pop(), "hel");
+}
+
TEST(stack, PushMultiple)
{
Stack<int> stack;
diff --git a/source/blender/blenlib/tests/BLI_task_test.cc b/source/blender/blenlib/tests/BLI_task_test.cc
index fce3e56d105..52603428031 100644
--- a/source/blender/blenlib/tests/BLI_task_test.cc
+++ b/source/blender/blenlib/tests/BLI_task_test.cc
@@ -67,7 +67,9 @@ TEST(task, RangeIter)
/* *** Parallel iterations over mempool items. *** */
-static void task_mempool_iter_func(void *userdata, MempoolIterData *item)
+static void task_mempool_iter_func(void *userdata,
+ MempoolIterData *item,
+ const TaskParallelTLS *__restrict UNUSED(tls))
{
int *data = (int *)item;
int *count = (int *)userdata;
@@ -119,7 +121,10 @@ TEST(task, MempoolIter)
}
}
- BLI_task_parallel_mempool(mempool, &num_items, task_mempool_iter_func, true);
+ TaskParallelSettings settings;
+ BLI_parallel_mempool_settings_defaults(&settings);
+
+ BLI_task_parallel_mempool(mempool, &num_items, task_mempool_iter_func, &settings);
/* Those checks should ensure us all items of the mempool were processed once, and only once - as
* expected. */
@@ -134,6 +139,100 @@ TEST(task, MempoolIter)
BLI_threadapi_exit();
}
+/* *** Parallel iterations over mempool items with TLS. *** */
+
+typedef struct TaskMemPool_Chunk {
+ ListBase *accumulate_items;
+} TaskMemPool_Chunk;
+
+static void task_mempool_iter_tls_func(void *UNUSED(userdata),
+ MempoolIterData *item,
+ const TaskParallelTLS *__restrict tls)
+{
+ TaskMemPool_Chunk *task_data = (TaskMemPool_Chunk *)tls->userdata_chunk;
+ int *data = (int *)item;
+
+ EXPECT_TRUE(data != nullptr);
+ if (task_data->accumulate_items == nullptr) {
+ task_data->accumulate_items = (ListBase *)MEM_callocN(sizeof(ListBase), __func__);
+ }
+
+ /* Flip to prove this has been touched. */
+ *data = -*data;
+
+ BLI_addtail(task_data->accumulate_items, BLI_genericNodeN(data));
+}
+
+static void task_mempool_iter_tls_reduce(const void *__restrict UNUSED(userdata),
+ void *__restrict chunk_join,
+ void *__restrict chunk)
+{
+ TaskMemPool_Chunk *join_chunk = (TaskMemPool_Chunk *)chunk_join;
+ TaskMemPool_Chunk *data_chunk = (TaskMemPool_Chunk *)chunk;
+
+ if (data_chunk->accumulate_items != nullptr) {
+ if (join_chunk->accumulate_items == nullptr) {
+ join_chunk->accumulate_items = (ListBase *)MEM_callocN(sizeof(ListBase), __func__);
+ }
+ BLI_movelisttolist(join_chunk->accumulate_items, data_chunk->accumulate_items);
+ }
+}
+
+static void task_mempool_iter_tls_free(const void *UNUSED(userdata),
+ void *__restrict userdata_chunk)
+{
+ TaskMemPool_Chunk *task_data = (TaskMemPool_Chunk *)userdata_chunk;
+ MEM_freeN(task_data->accumulate_items);
+}
+
+TEST(task, MempoolIterTLS)
+{
+ int *data[NUM_ITEMS];
+ BLI_threadapi_init();
+ BLI_mempool *mempool = BLI_mempool_create(
+ sizeof(*data[0]), NUM_ITEMS, 32, BLI_MEMPOOL_ALLOW_ITER);
+
+ int i;
+
+ /* Add numbers negative `1..NUM_ITEMS` inclusive. */
+ int num_items = 0;
+ for (i = 0; i < NUM_ITEMS; i++) {
+ data[i] = (int *)BLI_mempool_alloc(mempool);
+ *data[i] = -(i + 1);
+ num_items++;
+ }
+
+ TaskParallelSettings settings;
+ BLI_parallel_mempool_settings_defaults(&settings);
+
+ TaskMemPool_Chunk tls_data;
+ tls_data.accumulate_items = NULL;
+
+ settings.userdata_chunk = &tls_data;
+ settings.userdata_chunk_size = sizeof(tls_data);
+
+ settings.func_free = task_mempool_iter_tls_free;
+ settings.func_reduce = task_mempool_iter_tls_reduce;
+
+ BLI_task_parallel_mempool(mempool, nullptr, task_mempool_iter_tls_func, &settings);
+
+ EXPECT_EQ(BLI_listbase_count(tls_data.accumulate_items), NUM_ITEMS);
+
+ /* Check that all elements are added into the list once. */
+ int num_accum = 0;
+ for (LinkData *link = (LinkData *)tls_data.accumulate_items->first; link; link = link->next) {
+ int *data = (int *)link->data;
+ num_accum += *data;
+ }
+ EXPECT_EQ(num_accum, (NUM_ITEMS * (NUM_ITEMS + 1)) / 2);
+
+ BLI_freelistN(tls_data.accumulate_items);
+ MEM_freeN(tls_data.accumulate_items);
+
+ BLI_mempool_destroy(mempool);
+ BLI_threadapi_exit();
+}
+
/* *** Parallel iterations over double-linked list items. *** */
static void task_listbase_iter_func(void *userdata,
diff --git a/source/blender/blenlib/tests/BLI_vector_set_test.cc b/source/blender/blenlib/tests/BLI_vector_set_test.cc
index bbbe96f9b7e..c4016ca75e1 100644
--- a/source/blender/blenlib/tests/BLI_vector_set_test.cc
+++ b/source/blender/blenlib/tests/BLI_vector_set_test.cc
@@ -232,4 +232,43 @@ TEST(vector_set, PopExceptions)
EXPECT_EQ(set.size(), 4);
}
+TEST(vector_set, IndexOfOrAdd)
+{
+ VectorSet<int> set;
+ EXPECT_EQ(set.index_of_or_add(3), 0);
+ EXPECT_EQ(set.index_of_or_add(3), 0);
+ EXPECT_EQ(set.index_of_or_add(2), 1);
+ EXPECT_EQ(set.index_of_or_add(0), 2);
+ EXPECT_EQ(set.index_of_or_add(2), 1);
+ EXPECT_EQ(set.index_of_or_add(3), 0);
+ EXPECT_EQ(set.index_of_or_add(5), 3);
+ EXPECT_EQ(set.index_of_or_add(8), 4);
+ EXPECT_EQ(set.index_of_or_add(5), 3);
+}
+
+TEST(vector_set, Clear)
+{
+ VectorSet<int> set = {4, 6, 2, 4};
+ EXPECT_EQ(set.size(), 3);
+ set.clear();
+ EXPECT_EQ(set.size(), 0);
+ set.add_multiple({4, 1, 6, 8, 3, 6, 9, 3});
+ EXPECT_EQ(set.size(), 6);
+ set.clear();
+ EXPECT_EQ(set.size(), 0);
+}
+
+TEST(vector_set, LookupKey)
+{
+ VectorSet<std::string> set;
+ set.add("a");
+ set.add("b");
+ set.add("c");
+ EXPECT_EQ(set.lookup_key("a"), "a");
+ EXPECT_EQ(set.lookup_key_as("c"), "c");
+ EXPECT_EQ(set.lookup_key_ptr_as("d"), nullptr);
+ EXPECT_EQ(set.lookup_key_ptr_as("b")->size(), 1);
+ EXPECT_EQ(set.lookup_key_ptr("a"), set.lookup_key_ptr_as("a"));
+}
+
} // namespace blender::tests
diff --git a/source/blender/blenlib/tests/BLI_vector_test.cc b/source/blender/blenlib/tests/BLI_vector_test.cc
index 462f13c15ab..e8636168308 100644
--- a/source/blender/blenlib/tests/BLI_vector_test.cc
+++ b/source/blender/blenlib/tests/BLI_vector_test.cc
@@ -248,6 +248,15 @@ TEST(vector, Append)
EXPECT_EQ(vec[2], 7);
}
+TEST(vector, AppendAs)
+{
+ Vector<StringRef> vec;
+ vec.append_as("hello", 2);
+ vec.append_as("world", 3);
+ EXPECT_EQ(vec[0], "he");
+ EXPECT_EQ(vec[1], "wor");
+}
+
TEST(vector, AppendAndGetIndex)
{
Vector<int> vec;
diff --git a/source/blender/blenlib/tests/BLI_virtual_array_test.cc b/source/blender/blenlib/tests/BLI_virtual_array_test.cc
index ac25229cd69..a6d2ca10315 100644
--- a/source/blender/blenlib/tests/BLI_virtual_array_test.cc
+++ b/source/blender/blenlib/tests/BLI_virtual_array_test.cc
@@ -1,26 +1,29 @@
/* Apache License, Version 2.0 */
+#include "BLI_array.hh"
#include "BLI_strict_flags.h"
+#include "BLI_vector.hh"
+#include "BLI_vector_set.hh"
#include "BLI_virtual_array.hh"
#include "testing/testing.h"
namespace blender::tests {
-TEST(virtual_array, ForSpan)
+TEST(virtual_array, Span)
{
std::array<int, 5> data = {3, 4, 5, 6, 7};
- VArrayForSpan<int> varray{data};
+ VArray_For_Span<int> varray{data};
EXPECT_EQ(varray.size(), 5);
EXPECT_EQ(varray.get(0), 3);
EXPECT_EQ(varray.get(4), 7);
EXPECT_TRUE(varray.is_span());
EXPECT_FALSE(varray.is_single());
- EXPECT_EQ(varray.get_span().data(), data.data());
+ EXPECT_EQ(varray.get_internal_span().data(), data.data());
}
-TEST(virtual_array, ForSingle)
+TEST(virtual_array, Single)
{
- VArrayForSingle<int> varray{10, 4};
+ VArray_For_Single<int> varray{10, 4};
EXPECT_EQ(varray.size(), 4);
EXPECT_EQ(varray.get(0), 10);
EXPECT_EQ(varray.get(3), 10);
@@ -28,4 +31,124 @@ TEST(virtual_array, ForSingle)
EXPECT_TRUE(varray.is_single());
}
+TEST(virtual_array, Array)
+{
+ Array<int> array = {1, 2, 3, 5, 8};
+ {
+ VArray_For_ArrayContainer varray{array};
+ EXPECT_EQ(varray.size(), 5);
+ EXPECT_EQ(varray[0], 1);
+ EXPECT_EQ(varray[2], 3);
+ EXPECT_EQ(varray[3], 5);
+ EXPECT_TRUE(varray.is_span());
+ }
+ {
+ VArray_For_ArrayContainer varray{std::move(array)};
+ EXPECT_EQ(varray.size(), 5);
+ EXPECT_EQ(varray[0], 1);
+ EXPECT_EQ(varray[2], 3);
+ EXPECT_EQ(varray[3], 5);
+ EXPECT_TRUE(varray.is_span());
+ }
+ {
+ VArray_For_ArrayContainer varray{array}; /* NOLINT: bugprone-use-after-move */
+ EXPECT_TRUE(varray.is_empty());
+ }
+}
+
+TEST(virtual_array, Vector)
+{
+ Vector<int> vector = {9, 8, 7, 6};
+ VArray_For_ArrayContainer varray{std::move(vector)};
+ EXPECT_EQ(varray.size(), 4);
+ EXPECT_EQ(varray[0], 9);
+ EXPECT_EQ(varray[3], 6);
+}
+
+TEST(virtual_array, StdVector)
+{
+ std::vector<int> vector = {5, 6, 7, 8};
+ VArray_For_ArrayContainer varray{std::move(vector)};
+ EXPECT_EQ(varray.size(), 4);
+ EXPECT_EQ(varray[0], 5);
+ EXPECT_EQ(varray[1], 6);
+}
+
+TEST(virtual_array, StdArray)
+{
+ std::array<int, 4> array = {2, 3, 4, 5};
+ VArray_For_ArrayContainer varray{array};
+ EXPECT_EQ(varray.size(), 4);
+ EXPECT_EQ(varray[0], 2);
+ EXPECT_EQ(varray[1], 3);
+}
+
+TEST(virtual_array, VectorSet)
+{
+ VectorSet<int> vector_set = {5, 3, 7, 3, 3, 5, 1};
+ VArray_For_ArrayContainer varray{std::move(vector_set)};
+ EXPECT_TRUE(vector_set.is_empty()); /* NOLINT: bugprone-use-after-move. */
+ EXPECT_EQ(varray.size(), 4);
+ EXPECT_EQ(varray[0], 5);
+ EXPECT_EQ(varray[1], 3);
+ EXPECT_EQ(varray[2], 7);
+ EXPECT_EQ(varray[3], 1);
+}
+
+TEST(virtual_array, Func)
+{
+ auto func = [](int64_t index) { return (int)(index * index); };
+ VArray_For_Func<int, decltype(func)> varray{10, func};
+ EXPECT_EQ(varray.size(), 10);
+ EXPECT_EQ(varray[0], 0);
+ EXPECT_EQ(varray[3], 9);
+ EXPECT_EQ(varray[9], 81);
+}
+
+TEST(virtual_array, AsSpan)
+{
+ auto func = [](int64_t index) { return (int)(10 * index); };
+ VArray_For_Func<int, decltype(func)> func_varray{10, func};
+ VArray_Span span_varray{func_varray};
+ EXPECT_EQ(span_varray.size(), 10);
+ Span<int> span = span_varray;
+ EXPECT_EQ(span.size(), 10);
+ EXPECT_EQ(span[0], 0);
+ EXPECT_EQ(span[3], 30);
+ EXPECT_EQ(span[6], 60);
+}
+
+static int get_x(const std::array<int, 3> &item)
+{
+ return item[0];
+}
+
+static void set_x(std::array<int, 3> &item, int value)
+{
+ item[0] = value;
+}
+
+TEST(virtual_array, DerivedSpan)
+{
+ Vector<std::array<int, 3>> vector;
+ vector.append({3, 4, 5});
+ vector.append({1, 1, 1});
+ {
+ VArray_For_DerivedSpan<std::array<int, 3>, int, get_x> varray{vector};
+ EXPECT_EQ(varray.size(), 2);
+ EXPECT_EQ(varray[0], 3);
+ EXPECT_EQ(varray[1], 1);
+ }
+ {
+ VMutableArray_For_DerivedSpan<std::array<int, 3>, int, get_x, set_x> varray{vector};
+ EXPECT_EQ(varray.size(), 2);
+ EXPECT_EQ(varray[0], 3);
+ EXPECT_EQ(varray[1], 1);
+ varray.set(0, 10);
+ varray.set(1, 20);
+ EXPECT_EQ(vector[0][0], 10);
+ EXPECT_EQ(vector[1][0], 20);
+ }
+}
+
} // namespace blender::tests
diff --git a/source/blender/blenloader/BLO_readfile.h b/source/blender/blenloader/BLO_readfile.h
index d8c613ba900..89db216aada 100644
--- a/source/blender/blenloader/BLO_readfile.h
+++ b/source/blender/blenloader/BLO_readfile.h
@@ -226,7 +226,10 @@ int BLO_library_link_copypaste(struct Main *mainl, BlendHandle *bh, const uint64
* Struct for temporarily loading datablocks from a blend file.
*/
typedef struct TempLibraryContext {
- struct Main *temp_main;
+ /** Temporary main used for library data. */
+ struct Main *bmain_lib;
+ /** Temporary main used to load data into (currently initialized from `real_main`). */
+ struct Main *bmain_base;
struct BlendHandle *blendhandle;
struct LibraryLink_Params liblink_params;
struct Library *lib;
diff --git a/source/blender/blenloader/CMakeLists.txt b/source/blender/blenloader/CMakeLists.txt
index ee9b9a49768..36802fc8842 100644
--- a/source/blender/blenloader/CMakeLists.txt
+++ b/source/blender/blenloader/CMakeLists.txt
@@ -57,6 +57,7 @@ set(SRC
intern/versioning_270.c
intern/versioning_280.c
intern/versioning_290.c
+ intern/versioning_300.c
intern/versioning_cycles.c
intern/versioning_defaults.c
intern/versioning_dna.c
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index b657cb8b2f9..47ed4e5c06f 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -62,6 +62,7 @@
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.h"
+#include "BLI_endian_defines.h"
#include "BLI_endian_switch.h"
#include "BLI_ghash.h"
#include "BLI_linklist.h"
@@ -111,6 +112,7 @@
#include "SEQ_iterator.h"
#include "SEQ_modifier.h"
#include "SEQ_sequencer.h"
+#include "SEQ_utils.h"
#include "readfile.h"
@@ -2696,7 +2698,7 @@ static int lib_link_seq_clipboard_cb(Sequence *seq, void *arg_pt)
static void lib_link_clipboard_restore(struct IDNameLib_Map *id_map)
{
/* update IDs stored in sequencer clipboard */
- SEQ_iterator_seqbase_recursive_apply(&seqbase_clipboard, lib_link_seq_clipboard_cb, id_map);
+ SEQ_seqbase_recursive_apply(&seqbase_clipboard, lib_link_seq_clipboard_cb, id_map);
}
static int lib_link_main_data_restore_cb(LibraryIDLinkCallbackData *cb_data)
@@ -2912,7 +2914,7 @@ static void lib_link_workspace_layout_restore(struct IDNameLib_Map *id_map,
else if (sl->spacetype == SPACE_TEXT) {
SpaceText *st = (SpaceText *)sl;
- st->text = restore_pointer_by_name(id_map, (ID *)st->text, USER_REAL);
+ st->text = restore_pointer_by_name(id_map, (ID *)st->text, USER_IGNORE);
if (st->text == NULL) {
st->text = newmain->texts.first;
}
@@ -3010,8 +3012,13 @@ static void lib_link_workspace_layout_restore(struct IDNameLib_Map *id_map,
else if (sl->spacetype == SPACE_SPREADSHEET) {
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl;
- sspreadsheet->pinned_id = restore_pointer_by_name(
- id_map, sspreadsheet->pinned_id, USER_IGNORE);
+ LISTBASE_FOREACH (SpreadsheetContext *, context, &sspreadsheet->context_path) {
+ if (context->type == SPREADSHEET_CONTEXT_OBJECT) {
+ SpreadsheetContextObject *object_context = (SpreadsheetContextObject *)context;
+ object_context->object = restore_pointer_by_name(
+ id_map, (ID *)object_context->object, USER_IGNORE);
+ }
+ }
}
}
}
@@ -3863,6 +3870,7 @@ static void do_versions(FileData *fd, Library *lib, Main *main)
blo_do_versions_270(fd, lib, main);
blo_do_versions_280(fd, lib, main);
blo_do_versions_290(fd, lib, main);
+ blo_do_versions_300(fd, lib, main);
blo_do_versions_cycles(fd, lib, main);
/* WATCH IT!!!: pointers from libdata have not been converted yet here! */
@@ -3886,6 +3894,7 @@ static void do_versions_after_linking(Main *main, ReportList *reports)
do_versions_after_linking_270(main);
do_versions_after_linking_280(main, reports);
do_versions_after_linking_290(main, reports);
+ do_versions_after_linking_300(main, reports);
do_versions_after_linking_cycles(main);
main->is_locked_for_linking = false;
@@ -4444,7 +4453,9 @@ static void expand_doit_library(void *fdhandle, Main *mainvar, void *old)
if (id == NULL) {
/* ID has not been read yet, add placeholder to the main of the
* library it belongs to, so that it will be read later. */
- read_libblock(fd, libmain, bhead, fd->id_tag_extra | LIB_TAG_INDIRECT, false, NULL);
+ read_libblock(fd, libmain, bhead, fd->id_tag_extra | LIB_TAG_INDIRECT, false, &id);
+ id_sort_by_name(which_libbase(libmain, GS(id->name)), id, id->prev);
+
/* commented because this can print way too much */
// if (G.debug & G_DEBUG) printf("expand_doit: other lib %s\n", lib->filepath);
@@ -4504,7 +4515,8 @@ static void expand_doit_library(void *fdhandle, Main *mainvar, void *old)
bhead,
fd->id_tag_extra | LIB_TAG_NEED_EXPAND | LIB_TAG_INDIRECT,
false,
- NULL);
+ &id);
+ id_sort_by_name(which_libbase(mainvar, GS(id->name)), id, id->prev);
}
else {
/* Convert any previously read weak link to regular link
diff --git a/source/blender/blenloader/intern/readfile.h b/source/blender/blenloader/intern/readfile.h
index 9682b5456d2..d1d4e0b3256 100644
--- a/source/blender/blenloader/intern/readfile.h
+++ b/source/blender/blenloader/intern/readfile.h
@@ -210,6 +210,7 @@ void blo_do_versions_260(struct FileData *fd, struct Library *lib, struct Main *
void blo_do_versions_270(struct FileData *fd, struct Library *lib, struct Main *bmain);
void blo_do_versions_280(struct FileData *fd, struct Library *lib, struct Main *bmain);
void blo_do_versions_290(struct FileData *fd, struct Library *lib, struct Main *bmain);
+void blo_do_versions_300(struct FileData *fd, struct Library *lib, struct Main *bmain);
void blo_do_versions_cycles(struct FileData *fd, struct Library *lib, struct Main *bmain);
void do_versions_after_linking_250(struct Main *bmain);
@@ -217,6 +218,7 @@ void do_versions_after_linking_260(struct Main *bmain);
void do_versions_after_linking_270(struct Main *bmain);
void do_versions_after_linking_280(struct Main *bmain, struct ReportList *reports);
void do_versions_after_linking_290(struct Main *bmain, struct ReportList *reports);
+void do_versions_after_linking_300(struct Main *bmain, struct ReportList *reports);
void do_versions_after_linking_cycles(struct Main *bmain);
/* This is rather unfortunate to have to expose this here, but better use that nasty hack in
diff --git a/source/blender/blenloader/intern/readfile_tempload.c b/source/blender/blenloader/intern/readfile_tempload.c
index 691d8f542ab..4566e1e9b4d 100644
--- a/source/blender/blenloader/intern/readfile_tempload.c
+++ b/source/blender/blenloader/intern/readfile_tempload.c
@@ -21,6 +21,9 @@
#include "MEM_guardedalloc.h"
+#include "BLI_string.h"
+
+#include "BKE_main.h"
#include "BKE_report.h"
#include "DNA_ID.h"
@@ -32,15 +35,20 @@ TempLibraryContext *BLO_library_temp_load_id(struct Main *real_main,
struct ReportList *reports)
{
TempLibraryContext *temp_lib_ctx = MEM_callocN(sizeof(*temp_lib_ctx), __func__);
+ temp_lib_ctx->bmain_base = BKE_main_new();
+
+ /* Copy the file path so any path remapping is performed properly. */
+ STRNCPY(temp_lib_ctx->bmain_base->name, real_main->name);
temp_lib_ctx->blendhandle = BLO_blendhandle_from_file(blend_file_path, reports);
- BLO_library_link_params_init(&temp_lib_ctx->liblink_params, real_main, 0, LIB_TAG_TEMP_MAIN);
+ BLO_library_link_params_init(
+ &temp_lib_ctx->liblink_params, temp_lib_ctx->bmain_base, 0, LIB_TAG_TEMP_MAIN);
- temp_lib_ctx->temp_main = BLO_library_link_begin(
+ temp_lib_ctx->bmain_lib = BLO_library_link_begin(
&temp_lib_ctx->blendhandle, blend_file_path, &temp_lib_ctx->liblink_params);
- temp_lib_ctx->temp_id = BLO_library_link_named_part(temp_lib_ctx->temp_main,
+ temp_lib_ctx->temp_id = BLO_library_link_named_part(temp_lib_ctx->bmain_lib,
&temp_lib_ctx->blendhandle,
idcode,
idname,
@@ -51,8 +59,13 @@ TempLibraryContext *BLO_library_temp_load_id(struct Main *real_main,
void BLO_library_temp_free(TempLibraryContext *temp_lib_ctx)
{
+ /* This moves the temporary ID and any indirectly loaded data into `bmain_base`
+ * only to free `bmain_base`, while redundant this is the typical code-path for library linking,
+ * it's more convenient to follow this convention rather than create a new code-path for this
+ * one-off use case. */
BLO_library_link_end(
- temp_lib_ctx->temp_main, &temp_lib_ctx->blendhandle, &temp_lib_ctx->liblink_params);
+ temp_lib_ctx->bmain_lib, &temp_lib_ctx->blendhandle, &temp_lib_ctx->liblink_params);
BLO_blendhandle_close(temp_lib_ctx->blendhandle);
+ BKE_main_free(temp_lib_ctx->bmain_base);
MEM_freeN(temp_lib_ctx);
}
diff --git a/source/blender/blenloader/intern/versioning_250.c b/source/blender/blenloader/intern/versioning_250.c
index 467fd8b0399..990fc1d65d7 100644
--- a/source/blender/blenloader/intern/versioning_250.c
+++ b/source/blender/blenloader/intern/versioning_250.c
@@ -446,7 +446,7 @@ static void versions_gpencil_add_main(ListBase *lb, ID *id, const char *name)
id->flag = LIB_FAKEUSER;
*((short *)id->name) = ID_GD;
- BKE_id_new_name_validate(lb, id, name);
+ BKE_id_new_name_validate(lb, id, name, false);
/* alphabetic insertion: is in BKE_id_new_name_validate */
if ((id->tag & LIB_TAG_TEMP_MAIN) == 0) {
@@ -779,7 +779,6 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *bmain)
Nurb *nu;
for (nu = cu->nurb.first; nu; nu = nu->next) {
- nu->flag |= (nu->type & CU_2D);
nu->type &= CU_TYPE;
}
}
diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c
index 1ecaee10e6a..cf8f45ca227 100644
--- a/source/blender/blenloader/intern/versioning_280.c
+++ b/source/blender/blenloader/intern/versioning_280.c
@@ -1814,7 +1814,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
if (!MAIN_VERSION_ATLEAST(bmain, 280, 1)) {
- if (!DNA_struct_elem_find(fd->filesdna, "Light", "float", "bleedexp")) {
+ if (!DNA_struct_elem_find(fd->filesdna, "Lamp", "float", "bleedexp")) {
for (Light *la = bmain->lights.first; la; la = la->id.next) {
la->bleedexp = 2.5f;
}
@@ -1840,7 +1840,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
if (!MAIN_VERSION_ATLEAST(bmain, 280, 2)) {
- if (!DNA_struct_elem_find(fd->filesdna, "Light", "float", "cascade_max_dist")) {
+ if (!DNA_struct_elem_find(fd->filesdna, "Lamp", "float", "cascade_max_dist")) {
for (Light *la = bmain->lights.first; la; la = la->id.next) {
la->cascade_max_dist = 1000.0f;
la->cascade_count = 4;
@@ -1849,7 +1849,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
- if (!DNA_struct_elem_find(fd->filesdna, "Light", "float", "contact_dist")) {
+ if (!DNA_struct_elem_find(fd->filesdna, "Lamp", "float", "contact_dist")) {
for (Light *la = bmain->lights.first; la; la = la->id.next) {
la->contact_dist = 0.2f;
la->contact_bias = 0.03f;
@@ -2185,7 +2185,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
if (!MAIN_VERSION_ATLEAST(bmain, 280, 13)) {
/* Initialize specular factor. */
- if (!DNA_struct_elem_find(fd->filesdna, "Light", "float", "spec_fac")) {
+ if (!DNA_struct_elem_find(fd->filesdna, "Lamp", "float", "spec_fac")) {
for (Light *la = bmain->lights.first; la; la = la->id.next) {
la->spec_fac = 1.0f;
}
@@ -3135,7 +3135,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
- if (!DNA_struct_elem_find(fd->filesdna, "Light", "float", "att_dist")) {
+ if (!DNA_struct_elem_find(fd->filesdna, "Lamp", "float", "att_dist")) {
for (Light *la = bmain->lights.first; la; la = la->id.next) {
la->att_dist = la->clipend;
}
@@ -3172,7 +3172,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
bool is_blend = false;
{
- char tool = tool_init;
+ char tool;
switch (tool_init) {
case PAINT_BLEND_MIX:
tool = VPAINT_TOOL_DRAW;
@@ -4051,7 +4051,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
/* Initializes sun lights with the new angular diameter property */
- if (!DNA_struct_elem_find(fd->filesdna, "Light", "float", "sun_angle")) {
+ if (!DNA_struct_elem_find(fd->filesdna, "Lamp", "float", "sun_angle")) {
LISTBASE_FOREACH (Light *, light, &bmain->lights) {
light->sun_angle = 2.0f * atanf(light->area_size);
}
diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c
index 31e4b659c2f..565e62158ff 100644
--- a/source/blender/blenloader/intern/versioning_290.c
+++ b/source/blender/blenloader/intern/versioning_290.c
@@ -27,6 +27,7 @@
#include "BLI_utildefines.h"
#include "DNA_anim_types.h"
+#include "DNA_armature_types.h"
#include "DNA_brush_types.h"
#include "DNA_cachefile_types.h"
#include "DNA_collection_types.h"
@@ -47,6 +48,7 @@
#include "DNA_screen_types.h"
#include "DNA_shader_fx_types.h"
#include "DNA_space_types.h"
+#include "DNA_text_types.h"
#include "DNA_tracking_types.h"
#include "DNA_workspace_types.h"
@@ -56,6 +58,7 @@
#include "BKE_collection.h"
#include "BKE_colortools.h"
#include "BKE_cryptomatte.h"
+#include "BKE_curve.h"
#include "BKE_fcurve.h"
#include "BKE_gpencil.h"
#include "BKE_lib_id.h"
@@ -377,6 +380,37 @@ static void seq_update_meta_disp_range(Editing *ed)
}
}
+static void version_node_socket_duplicate(bNodeTree *ntree,
+ const int node_type,
+ const char *old_name,
+ const char *new_name)
+{
+ /* Duplicate a link going into the original socket. */
+ LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) {
+ if (link->tonode->type == node_type) {
+ bNode *node = link->tonode;
+ bNodeSocket *dest_socket = nodeFindSocket(node, SOCK_IN, new_name);
+ BLI_assert(dest_socket);
+ if (STREQ(link->tosock->name, old_name)) {
+ nodeAddLink(ntree, link->fromnode, link->fromsock, node, dest_socket);
+ }
+ }
+ }
+
+ /* Duplicate the default value from the old socket and assign it to the new socket. */
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
+ if (node->type == node_type) {
+ bNodeSocket *source_socket = nodeFindSocket(node, SOCK_IN, old_name);
+ bNodeSocket *dest_socket = nodeFindSocket(node, SOCK_IN, new_name);
+ BLI_assert(source_socket && dest_socket);
+ if (dest_socket->default_value) {
+ MEM_freeN(dest_socket->default_value);
+ }
+ dest_socket->default_value = MEM_dupallocN(source_socket->default_value);
+ }
+ }
+}
+
void do_versions_after_linking_290(Main *bmain, ReportList *UNUSED(reports))
{
if (!MAIN_VERSION_ATLEAST(bmain, 290, 1)) {
@@ -639,6 +673,29 @@ void do_versions_after_linking_290(Main *bmain, ReportList *UNUSED(reports))
}
}
+ if (!MAIN_VERSION_ATLEAST(bmain, 293, 16)) {
+ LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
+ seq_update_meta_disp_range(SEQ_editing_get(scene, false));
+ }
+
+ /* Add a separate socket for Grid node X and Y size. */
+ FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
+ if (ntree->type == NTREE_GEOMETRY) {
+ version_node_socket_duplicate(ntree, GEO_NODE_MESH_PRIMITIVE_GRID, "Size X", "Size Y");
+ }
+ FOREACH_NODETREE_END;
+ }
+ }
+
+ if (!MAIN_VERSION_ATLEAST(bmain, 293, 20)) {
+ /* Set zero user text objects to have a fake user. */
+ LISTBASE_FOREACH (Text *, text, &bmain->texts) {
+ if (text->id.us == 0) {
+ id_fake_user_set(&text->id);
+ }
+ }
+ }
+
/**
* Versioning code until next subversion bump goes here.
*
@@ -651,10 +708,6 @@ void do_versions_after_linking_290(Main *bmain, ReportList *UNUSED(reports))
*/
{
/* Keep this block, even when empty. */
-
- LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
- seq_update_meta_disp_range(SEQ_editing_get(scene, false));
- }
}
}
@@ -1531,7 +1584,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
- if (scene->ed != NULL) {
+ if (scene->toolsettings->sequencer_tool_settings == NULL) {
scene->toolsettings->sequencer_tool_settings = SEQ_tool_settings_init();
}
}
@@ -1907,7 +1960,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
if (!MAIN_VERSION_ATLEAST(bmain, 293, 14)) {
- if (!DNA_struct_elem_find(fd->filesdna, "Light", "float", "diff_fac")) {
+ if (!DNA_struct_elem_find(fd->filesdna, "Lamp", "float", "diff_fac")) {
LISTBASE_FOREACH (Light *, light, &bmain->lights) {
light->diff_fac = 1.0f;
light->volume_fac = 1.0f;
@@ -1925,6 +1978,115 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
+ if (!MAIN_VERSION_ATLEAST(bmain, 293, 15)) {
+ LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
+ if (ntree->type == NTREE_GEOMETRY) {
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
+ if (STREQ(node->idname, "GeometryNodeMeshPlane")) {
+ STRNCPY(node->idname, "GeometryNodeMeshGrid");
+ }
+ }
+ }
+ }
+ }
+
+ if (!MAIN_VERSION_ATLEAST(bmain, 293, 16)) {
+ FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
+ if (ntree->type == NTREE_GEOMETRY) {
+ version_node_socket_name(ntree, GEO_NODE_MESH_PRIMITIVE_GRID, "Size", "Size X");
+ }
+ FOREACH_NODETREE_END;
+ }
+
+ /* The CU_2D flag has been removed. */
+ LISTBASE_FOREACH (Curve *, cu, &bmain->curves) {
+#define CU_2D (1 << 3)
+ ListBase *nurbs = BKE_curve_nurbs_get(cu);
+ bool is_2d = true;
+
+ LISTBASE_FOREACH (Nurb *, nu, nurbs) {
+ if (nu->flag & CU_2D) {
+ nu->flag &= ~CU_2D;
+ }
+ else {
+ is_2d = false;
+ }
+ }
+#undef CU_2D
+ if (!is_2d && CU_IS_2D(cu)) {
+ cu->flag |= CU_3D;
+ }
+ }
+ }
+
+ if (!MAIN_VERSION_ATLEAST(bmain, 293, 18)) {
+ FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
+ if (ntree->type == NTREE_GEOMETRY) {
+ version_node_socket_name(ntree, GEO_NODE_VOLUME_TO_MESH, "Grid", "Density");
+ }
+ }
+ FOREACH_NODETREE_END;
+
+ if (!DNA_struct_elem_find(fd->filesdna, "bArmature", "float", "axes_position")) {
+ /* Convert the axes draw position to its old default (tip of bone). */
+ LISTBASE_FOREACH (struct bArmature *, arm, &bmain->armatures) {
+ arm->axes_position = 1.0;
+ }
+ }
+
+ /* Initialize the spread parameter for area lights*/
+ if (!DNA_struct_elem_find(fd->filesdna, "Lamp", "float", "area_spread")) {
+ LISTBASE_FOREACH (Light *, la, &bmain->lights) {
+ la->area_spread = DEG2RADF(180.0f);
+ }
+ }
+
+ LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
+ LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
+ if (sl->spacetype == SPACE_NODE) {
+ SpaceNode *snode = (SpaceNode *)sl;
+ LISTBASE_FOREACH (bNodeTreePath *, path, &snode->treepath) {
+ STRNCPY(path->display_name, path->node_name);
+ }
+ }
+ }
+ }
+ }
+
+ /* Consolidate node and final evaluation modes. */
+ LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
+ LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
+ if (sl->spacetype == SPACE_SPREADSHEET) {
+ SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl;
+ if (sspreadsheet->object_eval_state == 2) {
+ sspreadsheet->object_eval_state = SPREADSHEET_OBJECT_EVAL_STATE_EVALUATED;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* Set default value for the new bisect_threshold parameter in the mirror modifier. */
+ if (!MAIN_VERSION_ATLEAST(bmain, 293, 19)) {
+ LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
+ LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
+ if (md->type == eModifierType_Mirror) {
+ MirrorModifierData *mmd = (MirrorModifierData *)md;
+ /* This was the previous hard-coded value. */
+ mmd->bisect_threshold = 0.001f;
+ }
+ }
+ }
+
+ LISTBASE_FOREACH (Curve *, cu, &bmain->curves) {
+ /* Turn on clamping as this was implicit before. */
+ cu->flag |= CU_PATH_CLAMP;
+ }
+ }
+
/**
* Versioning code until next subversion bump goes here.
*
diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c
new file mode 100644
index 00000000000..268598ccc51
--- /dev/null
+++ b/source/blender/blenloader/intern/versioning_300.c
@@ -0,0 +1,263 @@
+/*
+ * 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.
+ */
+
+/** \file
+ * \ingroup blenloader
+ */
+/* allow readfile to use deprecated functionality */
+#define DNA_DEPRECATED_ALLOW
+
+#include "BLI_listbase.h"
+#include "BLI_math_vector.h"
+#include "BLI_string.h"
+#include "BLI_utildefines.h"
+
+#include "DNA_brush_types.h"
+#include "DNA_genfile.h"
+#include "DNA_listBase.h"
+#include "DNA_modifier_types.h"
+#include "DNA_text_types.h"
+
+#include "BKE_lib_id.h"
+#include "BKE_main.h"
+#include "BKE_node.h"
+
+#include "BLO_readfile.h"
+#include "readfile.h"
+
+static void sort_linked_ids(Main *bmain)
+{
+ ListBase *lb;
+ FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) {
+ ListBase temp_list;
+ BLI_listbase_clear(&temp_list);
+ LISTBASE_FOREACH_MUTABLE (ID *, id, lb) {
+ if (ID_IS_LINKED(id)) {
+ BLI_remlink(lb, id);
+ BLI_addtail(&temp_list, id);
+ id_sort_by_name(&temp_list, id, NULL);
+ }
+ }
+ BLI_movelisttolist(lb, &temp_list);
+ }
+ FOREACH_MAIN_LISTBASE_END;
+}
+
+static void assert_sorted_ids(Main *bmain)
+{
+#ifndef NDEBUG
+ ListBase *lb;
+ FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) {
+ ID *id_prev = NULL;
+ LISTBASE_FOREACH (ID *, id, lb) {
+ if (id_prev == NULL) {
+ continue;
+ }
+ BLI_assert(id_prev->lib != id->lib || BLI_strcasecmp(id_prev->name, id->name) < 0);
+ }
+ }
+ FOREACH_MAIN_LISTBASE_END;
+#else
+ UNUSED_VARS_NDEBUG(bmain);
+#endif
+}
+
+void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports))
+{
+ if (MAIN_VERSION_ATLEAST(bmain, 300, 0) && !MAIN_VERSION_ATLEAST(bmain, 300, 1)) {
+ /* Set zero user text objects to have a fake user. */
+ LISTBASE_FOREACH (Text *, text, &bmain->texts) {
+ if (text->id.us == 0) {
+ id_fake_user_set(&text->id);
+ }
+ }
+ }
+
+ if (!MAIN_VERSION_ATLEAST(bmain, 300, 3)) {
+ /* Use new texture socket in Attribute Sample Texture node. */
+ LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
+ if (ntree->type != NTREE_GEOMETRY) {
+ continue;
+ }
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
+ if (node->type != GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE) {
+ continue;
+ }
+ if (node->id == NULL) {
+ continue;
+ }
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
+ if (socket->type == SOCK_TEXTURE) {
+ bNodeSocketValueTexture *socket_value = (bNodeSocketValueTexture *)
+ socket->default_value;
+ socket_value->value = (Tex *)node->id;
+ break;
+ }
+ }
+ node->id = NULL;
+ }
+ }
+
+ sort_linked_ids(bmain);
+ assert_sorted_ids(bmain);
+ }
+ if (MAIN_VERSION_ATLEAST(bmain, 300, 3)) {
+ assert_sorted_ids(bmain);
+ }
+
+ /**
+ * Versioning code until next subversion bump goes here.
+ *
+ * \note Be sure to check when bumping the version:
+ * - #blo_do_versions_300 in this file.
+ * - "versioning_userdef.c", #blo_do_versions_userdef
+ * - "versioning_userdef.c", #do_versions_theme
+ *
+ * \note Keep this message at the bottom of the function.
+ */
+ {
+ /* Keep this block, even when empty. */
+ }
+}
+
+static void version_switch_node_input_prefix(Main *bmain)
+{
+ FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
+ if (ntree->type == NTREE_GEOMETRY) {
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
+ if (node->type == GEO_NODE_SWITCH) {
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
+ /* Skip the "switch" socket. */
+ if (socket == node->inputs.first) {
+ continue;
+ }
+ strcpy(socket->name, socket->name[0] == 'A' ? "False" : "True");
+
+ /* Replace "A" and "B", but keep the unique number suffix at the end. */
+ char number_suffix[8];
+ BLI_strncpy(number_suffix, socket->identifier + 1, sizeof(number_suffix));
+ strcpy(socket->identifier, socket->name);
+ strcat(socket->identifier, number_suffix);
+ }
+ }
+ }
+ }
+ }
+ FOREACH_NODETREE_END;
+}
+
+static void version_node_socket_name(bNodeTree *ntree,
+ const int node_type,
+ const char *old_name,
+ const char *new_name)
+{
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
+ if (node->type == node_type) {
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
+ if (STREQ(socket->name, old_name)) {
+ strcpy(socket->name, new_name);
+ }
+ if (STREQ(socket->identifier, old_name)) {
+ strcpy(socket->identifier, new_name);
+ }
+ }
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) {
+ if (STREQ(socket->name, old_name)) {
+ strcpy(socket->name, new_name);
+ }
+ if (STREQ(socket->identifier, old_name)) {
+ strcpy(socket->identifier, new_name);
+ }
+ }
+ }
+ }
+}
+
+/* NOLINTNEXTLINE: readability-function-size */
+void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
+{
+ if (!MAIN_VERSION_ATLEAST(bmain, 300, 1)) {
+ /* Set default value for the new bisect_threshold parameter in the mirror modifier. */
+ if (!DNA_struct_elem_find(fd->filesdna, "MirrorModifierData", "float", "bisect_threshold")) {
+ LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
+ LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
+ if (md->type == eModifierType_Mirror) {
+ MirrorModifierData *mmd = (MirrorModifierData *)md;
+ /* This was the previous hard-coded value. */
+ mmd->bisect_threshold = 0.001f;
+ }
+ }
+ }
+ }
+ /* Grease Pencil: Set default value for dilate pixels. */
+ if (!DNA_struct_elem_find(fd->filesdna, "BrushGpencilSettings", "int", "dilate_pixels")) {
+ LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) {
+ if (brush->gpencil_settings) {
+ brush->gpencil_settings->dilate_pixels = 1;
+ }
+ }
+ }
+ }
+
+ if (!MAIN_VERSION_ATLEAST(bmain, 300, 2)) {
+ version_switch_node_input_prefix(bmain);
+
+ if (!DNA_struct_elem_find(fd->filesdna, "bPoseChannel", "float", "custom_scale_xyz[3]")) {
+ LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
+ if (ob->pose == NULL) {
+ continue;
+ }
+ LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) {
+ copy_v3_fl(pchan->custom_scale_xyz, pchan->custom_scale);
+ }
+ }
+ }
+ }
+
+ /**
+ * Versioning code until next subversion bump goes here.
+ *
+ * \note Be sure to check when bumping the version:
+ * - "versioning_userdef.c", #blo_do_versions_userdef
+ * - "versioning_userdef.c", #do_versions_theme
+ *
+ * \note Keep this message at the bottom of the function.
+ */
+ {
+ /* Keep this block, even when empty. */
+ FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
+ if (ntree->type == NTREE_GEOMETRY) {
+ version_node_socket_name(ntree, GEO_NODE_BOUNDING_BOX, "Mesh", "Bounding Box");
+ }
+ }
+ FOREACH_NODETREE_END;
+
+ if (!DNA_struct_elem_find(fd->filesdna, "FileAssetSelectParams", "int", "import_type")) {
+ LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
+ LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
+ if (sl->spacetype == SPACE_FILE) {
+ SpaceFile *sfile = (SpaceFile *)sl;
+ if (sfile->asset_params) {
+ sfile->asset_params->import_type = FILE_ASSET_IMPORT_APPEND;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/source/blender/blenloader/intern/versioning_legacy.c b/source/blender/blenloader/intern/versioning_legacy.c
index e43f8153bd1..56b2f18f8a6 100644
--- a/source/blender/blenloader/intern/versioning_legacy.c
+++ b/source/blender/blenloader/intern/versioning_legacy.c
@@ -81,6 +81,7 @@
#include "BKE_pointcache.h"
#include "SEQ_iterator.h"
+#include "SEQ_sequencer.h"
#include "NOD_socket.h"
@@ -1277,12 +1278,6 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *bmain)
if (ob->soft->physics_speed == 0.0f) {
ob->soft->physics_speed = 1.0f;
}
-
- if (ob->soft->interval == 0) {
- ob->soft->interval = 2;
- ob->soft->sfra = 1;
- ob->soft->efra = 100;
- }
}
if (ob->soft && ob->soft->vertgroup == 0) {
bDeformGroup *locGroup = BKE_object_defgroup_find_name(ob, "SOFTGOAL");
@@ -2560,18 +2555,16 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *bmain)
if (bmain->versionfile < 249 && bmain->subversionfile < 2) {
Scene *sce = bmain->scenes.first;
- Sequence *seq;
Editing *ed;
while (sce) {
ed = sce->ed;
if (ed) {
- SEQ_CURRENT_BEGIN (ed, seq) {
+ LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
if (seq->strip && seq->strip->proxy) {
seq->strip->proxy->quality = 90;
}
}
- SEQ_CURRENT_END;
}
sce = sce->id.next;
diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c
index 8cbedb05931..be3834faa5a 100644
--- a/source/blender/blenloader/intern/versioning_userdef.c
+++ b/source/blender/blenloader/intern/versioning_userdef.c
@@ -24,6 +24,7 @@
#include "BLI_listbase.h"
#include "BLI_math.h"
+#include "BLI_string.h"
#include "BLI_utildefines.h"
#ifdef WITH_INTERNATIONAL
@@ -261,17 +262,7 @@ static void do_versions_theme(const UserDef *userdef, bTheme *btheme)
FROM_DEFAULT_V4_UCHAR(space_node.nodeclass_shader);
}
- /**
- * Versioning code until next subversion bump goes here.
- *
- * \note Be sure to check when bumping the version:
- * - #blo_do_versions_userdef in this file.
- * - "versioning_{BLENDER_VERSION}.c"
- *
- * \note Keep this message at the bottom of the function.
- */
- {
- /* Keep this block, even when empty. */
+ if (!USER_VERSION_ATLEAST(293, 15)) {
FROM_DEFAULT_V4_UCHAR(space_properties.active);
FROM_DEFAULT_V4_UCHAR(space_info.info_error);
@@ -286,6 +277,19 @@ static void do_versions_theme(const UserDef *userdef, bTheme *btheme)
btheme->space_spreadsheet = btheme->space_outliner;
}
+ /**
+ * Versioning code until next subversion bump goes here.
+ *
+ * \note Be sure to check when bumping the version:
+ * - #blo_do_versions_userdef in this file.
+ * - "versioning_{BLENDER_VERSION}.c"
+ *
+ * \note Keep this message at the bottom of the function.
+ */
+ {
+ /* Keep this block, even when empty. */
+ }
+
#undef FROM_DEFAULT_V4_UCHAR
#undef USER_VERSION_ATLEAST
@@ -838,6 +842,23 @@ void blo_do_versions_userdef(UserDef *userdef)
}
}
+ if (!USER_VERSION_ATLEAST(293, 1)) {
+ /* This rename was made after 2.93.0, harmless to run when it's not needed. */
+ const char *replace_table[][2] = {
+ {"blender", "Blender"},
+ {"blender_27x", "Blender_27x"},
+ {"industry_compatible", "Industry_Compatible"},
+ };
+ const int replace_table_len = ARRAY_SIZE(replace_table);
+
+ BLI_str_replace_table_exact(
+ userdef->keyconfigstr, sizeof(userdef->keyconfigstr), replace_table, replace_table_len);
+ LISTBASE_FOREACH (wmKeyConfigPref *, kpt, &userdef->user_keyconfig_prefs) {
+ BLI_str_replace_table_exact(
+ kpt->idname, sizeof(kpt->idname), replace_table, replace_table_len);
+ }
+ }
+
if (!USER_VERSION_ATLEAST(293, 2)) {
/* Enable asset browser features by default for alpha testing.
* BLO_sanitize_experimental_features_userpref_blend() will disable it again for non-alpha
diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c
index 4ac49d5aebb..38c00b3f132 100644
--- a/source/blender/blenloader/intern/writefile.c
+++ b/source/blender/blenloader/intern/writefile.c
@@ -100,6 +100,7 @@
#include "BLI_bitmap.h"
#include "BLI_blenlib.h"
+#include "BLI_endian_defines.h"
#include "BLI_mempool.h"
#include "MEM_guardedalloc.h" /* MEM_freeN */
diff --git a/source/blender/blenloader/tests/blendfile_loading_base_test.cc b/source/blender/blenloader/tests/blendfile_loading_base_test.cc
index 8d8dc3aebf7..280a4b42b36 100644
--- a/source/blender/blenloader/tests/blendfile_loading_base_test.cc
+++ b/source/blender/blenloader/tests/blendfile_loading_base_test.cc
@@ -50,10 +50,6 @@
#include "CLG_log.h"
-BlendfileLoadingBaseTest::~BlendfileLoadingBaseTest()
-{
-}
-
void BlendfileLoadingBaseTest::SetUpTestCase()
{
testing::Test::SetUpTestCase();
diff --git a/source/blender/blenloader/tests/blendfile_loading_base_test.h b/source/blender/blenloader/tests/blendfile_loading_base_test.h
index 32bb2959dcc..d352aecfe41 100644
--- a/source/blender/blenloader/tests/blendfile_loading_base_test.h
+++ b/source/blender/blenloader/tests/blendfile_loading_base_test.h
@@ -30,8 +30,6 @@ class BlendfileLoadingBaseTest : public testing::Test {
struct Depsgraph *depsgraph = nullptr;
public:
- virtual ~BlendfileLoadingBaseTest();
-
/* Sets up Blender just enough to not crash on loading
* a blendfile and constructing a depsgraph. */
static void SetUpTestCase();
diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt
index c215cf69e3a..ec282888ffa 100644
--- a/source/blender/bmesh/CMakeLists.txt
+++ b/source/blender/bmesh/CMakeLists.txt
@@ -102,6 +102,12 @@ set(SRC
intern/bmesh_mesh_convert.h
intern/bmesh_mesh_duplicate.c
intern/bmesh_mesh_duplicate.h
+ intern/bmesh_mesh_normals.c
+ intern/bmesh_mesh_normals.h
+ intern/bmesh_mesh_partial_update.c
+ intern/bmesh_mesh_partial_update.h
+ intern/bmesh_mesh_tessellate.c
+ intern/bmesh_mesh_tessellate.h
intern/bmesh_mesh_validate.c
intern/bmesh_mesh_validate.h
intern/bmesh_mods.c
@@ -182,10 +188,6 @@ set(LIB
extern_rangetree
)
-if(MSVC AND NOT MSVC_CLANG)
- string(APPEND CMAKE_C_FLAGS " /WX /wd4101")
-endif()
-
if(WITH_BULLET)
list(APPEND INC_SYS
${BULLET_INCLUDE_DIRS}
@@ -225,6 +227,10 @@ endif()
blender_add_lib(bf_bmesh "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
+if(MSVC AND NOT MSVC_CLANG)
+ target_compile_options(bf_bmesh PRIVATE /WX /wd4101)
+endif()
+
if(WITH_GTESTS)
set(TEST_SRC
tests/bmesh_core_test.cc
diff --git a/source/blender/bmesh/bmesh.h b/source/blender/bmesh/bmesh.h
index 4441ccc0c88..a5241f6b36d 100644
--- a/source/blender/bmesh/bmesh.h
+++ b/source/blender/bmesh/bmesh.h
@@ -215,6 +215,9 @@ extern "C" {
#include "intern/bmesh_mesh.h"
#include "intern/bmesh_mesh_convert.h"
#include "intern/bmesh_mesh_duplicate.h"
+#include "intern/bmesh_mesh_normals.h"
+#include "intern/bmesh_mesh_partial_update.h"
+#include "intern/bmesh_mesh_tessellate.h"
#include "intern/bmesh_mesh_validate.h"
#include "intern/bmesh_mods.h"
#include "intern/bmesh_operators.h"
diff --git a/source/blender/bmesh/intern/bmesh_core.c b/source/blender/bmesh/intern/bmesh_core.c
index cf907862120..e72c689ddfb 100644
--- a/source/blender/bmesh/intern/bmesh_core.c
+++ b/source/blender/bmesh/intern/bmesh_core.c
@@ -1805,7 +1805,8 @@ BMEdge *bmesh_kernel_join_edge_kill_vert(BMesh *bm,
BMVert *v_kill,
const bool do_del,
const bool check_edge_exists,
- const bool kill_degenerate_faces)
+ const bool kill_degenerate_faces,
+ const bool kill_duplicate_faces)
{
BMEdge *e_old;
BMVert *v_old, *v_target;
@@ -1840,6 +1841,9 @@ BMEdge *bmesh_kernel_join_edge_kill_vert(BMesh *bm,
BLI_SMALLSTACK_DECLARE(faces_degenerate, BMFace *);
BMLoop *l_kill_next;
+ /* Candidates for being duplicate. */
+ BLI_SMALLSTACK_DECLARE(faces_duplicate_candidate, BMFace *);
+
#ifndef NDEBUG
/* For verification later, count valence of 'v_old' and 'v_target' */
valence1 = bmesh_disk_count(v_old);
@@ -1877,9 +1881,14 @@ BMEdge *bmesh_kernel_join_edge_kill_vert(BMesh *bm,
/* fix len attribute of face */
l_kill->f->len--;
- if (kill_degenerate_faces) {
- if (l_kill->f->len < 3) {
- BLI_SMALLSTACK_PUSH(faces_degenerate, l_kill->f);
+ if (kill_degenerate_faces && (l_kill->f->len < 3)) {
+ BLI_SMALLSTACK_PUSH(faces_degenerate, l_kill->f);
+ }
+ else {
+ /* The duplicate test isn't reliable at this point as `e_splice` might be set,
+ * so the duplicate test needs to run once the edge has been spliced. */
+ if (kill_duplicate_faces) {
+ BLI_SMALLSTACK_PUSH(faces_duplicate_candidate, l_kill->f);
}
}
l_kill_next = l_kill->radial_next;
@@ -1940,6 +1949,15 @@ BMEdge *bmesh_kernel_join_edge_kill_vert(BMesh *bm,
}
}
+ if (kill_duplicate_faces) {
+ BMFace *f_kill;
+ while ((f_kill = BLI_SMALLSTACK_POP(faces_duplicate_candidate))) {
+ if (BM_face_find_double(f_kill)) {
+ BM_face_kill(bm, f_kill);
+ }
+ }
+ }
+
BM_CHECK_ELEMENT(v_old);
BM_CHECK_ELEMENT(v_target);
BM_CHECK_ELEMENT(e_old);
diff --git a/source/blender/bmesh/intern/bmesh_core.h b/source/blender/bmesh/intern/bmesh_core.h
index df73984e6cf..8f7580714ae 100644
--- a/source/blender/bmesh/intern/bmesh_core.h
+++ b/source/blender/bmesh/intern/bmesh_core.h
@@ -115,7 +115,8 @@ BMEdge *bmesh_kernel_join_edge_kill_vert(BMesh *bm,
BMVert *v_kill,
const bool do_del,
const bool check_edge_exists,
- const bool kill_degenerate_faces);
+ const bool kill_degenerate_faces,
+ const bool kill_duplicate_faces);
BMVert *bmesh_kernel_join_vert_kill_edge(BMesh *bm,
BMEdge *e_kill,
BMVert *v_kill,
diff --git a/source/blender/bmesh/intern/bmesh_iterators_inline.h b/source/blender/bmesh/intern/bmesh_iterators_inline.h
index c384fb03cd9..81b6a58e58b 100644
--- a/source/blender/bmesh/intern/bmesh_iterators_inline.h
+++ b/source/blender/bmesh/intern/bmesh_iterators_inline.h
@@ -188,18 +188,18 @@ BLI_INLINE void BM_iter_parallel(BMesh *bm,
const char itype,
TaskParallelMempoolFunc func,
void *userdata,
- const bool use_threading)
+ const TaskParallelSettings *settings)
{
/* inlining optimizes out this switch when called with the defined type */
switch ((BMIterType)itype) {
case BM_VERTS_OF_MESH:
- BLI_task_parallel_mempool(bm->vpool, userdata, func, use_threading);
+ BLI_task_parallel_mempool(bm->vpool, userdata, func, settings);
break;
case BM_EDGES_OF_MESH:
- BLI_task_parallel_mempool(bm->epool, userdata, func, use_threading);
+ BLI_task_parallel_mempool(bm->epool, userdata, func, settings);
break;
case BM_FACES_OF_MESH:
- BLI_task_parallel_mempool(bm->fpool, userdata, func, use_threading);
+ BLI_task_parallel_mempool(bm->fpool, userdata, func, settings);
break;
default:
/* should never happen */
diff --git a/source/blender/bmesh/intern/bmesh_mesh.c b/source/blender/bmesh/intern/bmesh_mesh.c
index 5e879d41d43..d0c6bc83088 100644
--- a/source/blender/bmesh/intern/bmesh_mesh.c
+++ b/source/blender/bmesh/intern/bmesh_mesh.c
@@ -25,22 +25,14 @@
#include "DNA_listBase.h"
#include "DNA_scene_types.h"
-#include "BLI_bitmap.h"
-#include "BLI_linklist_stack.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
-#include "BLI_stack.h"
-#include "BLI_task.h"
#include "BLI_utildefines.h"
-#include "BKE_editmesh.h"
-#include "BKE_global.h"
+#include "BKE_customdata.h"
#include "BKE_mesh.h"
-#include "BKE_multires.h"
-#include "atomic_ops.h"
-
-#include "intern/bmesh_private.h"
+#include "bmesh.h"
/* used as an extern, defined in bmesh.h */
const BMAllocTemplate bm_mesh_allocsize_default = {512, 1024, 2048, 512};
@@ -319,1653 +311,6 @@ void BM_mesh_free(BMesh *bm)
}
/**
- * Helpers for #BM_mesh_normals_update and #BM_verts_calc_normal_vcos
- */
-
-/* We use that existing internal API flag,
- * assuming no other tool using it would run concurrently to clnors editing. */
-#define BM_LNORSPACE_UPDATE _FLAG_MF
-
-typedef struct BMEdgesCalcVectorsData {
- /* Read-only data. */
- const float (*vcos)[3];
-
- /* Read-write data, but no need to protect it, no concurrency to fear here. */
- float (*edgevec)[3];
-} BMEdgesCalcVectorsData;
-
-static void mesh_edges_calc_vectors_cb(void *userdata, MempoolIterData *mp_e)
-{
- BMEdgesCalcVectorsData *data = userdata;
- BMEdge *e = (BMEdge *)mp_e;
-
- if (e->l) {
- const float *v1_co = data->vcos ? data->vcos[BM_elem_index_get(e->v1)] : e->v1->co;
- const float *v2_co = data->vcos ? data->vcos[BM_elem_index_get(e->v2)] : e->v2->co;
- sub_v3_v3v3(data->edgevec[BM_elem_index_get(e)], v2_co, v1_co);
- normalize_v3(data->edgevec[BM_elem_index_get(e)]);
- }
- else {
- /* the edge vector will not be needed when the edge has no radial */
- }
-}
-
-static void bm_mesh_edges_calc_vectors(BMesh *bm, float (*edgevec)[3], const float (*vcos)[3])
-{
- BM_mesh_elem_index_ensure(bm, BM_EDGE | (vcos ? BM_VERT : 0));
-
- BMEdgesCalcVectorsData data = {
- .vcos = vcos,
- .edgevec = edgevec,
- };
-
- BM_iter_parallel(
- bm, BM_EDGES_OF_MESH, mesh_edges_calc_vectors_cb, &data, bm->totedge >= BM_OMP_LIMIT);
-}
-
-typedef struct BMVertsCalcNormalsData {
- /* Read-only data. */
- const float (*fnos)[3];
- const float (*edgevec)[3];
- const float (*vcos)[3];
-
- /* Read-write data, protected by an atomic-based fake spin-lock like system. */
- float (*vnos)[3];
-} BMVertsCalcNormalsData;
-
-static void mesh_verts_calc_normals_accum_cb(void *userdata, MempoolIterData *mp_f)
-{
-#define FLT_EQ_NONAN(_fa, _fb) (*((const uint32_t *)&_fa) == *((const uint32_t *)&_fb))
-
- BMVertsCalcNormalsData *data = userdata;
- BMFace *f = (BMFace *)mp_f;
-
- const float *f_no = data->fnos ? data->fnos[BM_elem_index_get(f)] : f->no;
-
- BMLoop *l_first, *l_iter;
- l_iter = l_first = BM_FACE_FIRST_LOOP(f);
- do {
- const float *e1diff, *e2diff;
- float dotprod;
- float fac;
-
- /* calculate the dot product of the two edges that
- * meet at the loop's vertex */
- e1diff = data->edgevec[BM_elem_index_get(l_iter->prev->e)];
- e2diff = data->edgevec[BM_elem_index_get(l_iter->e)];
- dotprod = dot_v3v3(e1diff, e2diff);
-
- /* edge vectors are calculated from e->v1 to e->v2, so
- * adjust the dot product if one but not both loops
- * actually runs from from e->v2 to e->v1 */
- if ((l_iter->prev->e->v1 == l_iter->prev->v) ^ (l_iter->e->v1 == l_iter->v)) {
- dotprod = -dotprod;
- }
-
- fac = saacos(-dotprod);
-
- if (fac != fac) { /* NAN detection. */
- /* Degenerated case, nothing to do here, just ignore that vertex. */
- continue;
- }
-
- /* accumulate weighted face normal into the vertex's normal */
- float *v_no = data->vnos ? data->vnos[BM_elem_index_get(l_iter->v)] : l_iter->v->no;
-
- /* This block is a lockless threadsafe madd_v3_v3fl.
- * It uses the first float of the vector as a sort of cheap spin-lock,
- * assuming FLT_MAX is a safe 'illegal' value that cannot be set here otherwise.
- * It also assumes that collisions between threads are highly unlikely,
- * else performances would be quite bad here. */
- float virtual_lock = v_no[0];
- while (true) {
- /* This loops until following conditions are met:
- * - v_no[0] has same value as virtual_lock (i.e. it did not change since last try).
- * - v_no[0] was not FLT_MAX, i.e. it was not locked by another thread.
- */
- const float vl = atomic_cas_float(&v_no[0], virtual_lock, FLT_MAX);
- if (FLT_EQ_NONAN(vl, virtual_lock) && vl != FLT_MAX) {
- break;
- }
- virtual_lock = vl;
- }
- BLI_assert(v_no[0] == FLT_MAX);
- /* Now we own that normal value, and can change it.
- * But first scalar of the vector must not be changed yet, it's our lock! */
- virtual_lock += f_no[0] * fac;
- v_no[1] += f_no[1] * fac;
- v_no[2] += f_no[2] * fac;
- /* Second atomic operation to 'release'
- * our lock on that vector and set its first scalar value. */
- /* Note that we do not need to loop here, since we 'locked' v_no[0],
- * nobody should have changed it in the mean time. */
- virtual_lock = atomic_cas_float(&v_no[0], FLT_MAX, virtual_lock);
- BLI_assert(virtual_lock == FLT_MAX);
-
- } while ((l_iter = l_iter->next) != l_first);
-
-#undef FLT_EQ_NONAN
-}
-
-static void mesh_verts_calc_normals_normalize_cb(void *userdata, MempoolIterData *mp_v)
-{
- BMVertsCalcNormalsData *data = userdata;
- BMVert *v = (BMVert *)mp_v;
-
- float *v_no = data->vnos ? data->vnos[BM_elem_index_get(v)] : v->no;
- if (UNLIKELY(normalize_v3(v_no) == 0.0f)) {
- const float *v_co = data->vcos ? data->vcos[BM_elem_index_get(v)] : v->co;
- normalize_v3_v3(v_no, v_co);
- }
-}
-
-static void bm_mesh_verts_calc_normals(BMesh *bm,
- const float (*edgevec)[3],
- const float (*fnos)[3],
- const float (*vcos)[3],
- float (*vnos)[3])
-{
- BM_mesh_elem_index_ensure(bm, (BM_EDGE | BM_FACE) | ((vnos || vcos) ? BM_VERT : 0));
-
- BMVertsCalcNormalsData data = {
- .fnos = fnos,
- .edgevec = edgevec,
- .vcos = vcos,
- .vnos = vnos,
- };
-
- BM_iter_parallel(
- bm, BM_FACES_OF_MESH, mesh_verts_calc_normals_accum_cb, &data, bm->totface >= BM_OMP_LIMIT);
-
- /* normalize the accumulated vertex normals */
- BM_iter_parallel(bm,
- BM_VERTS_OF_MESH,
- mesh_verts_calc_normals_normalize_cb,
- &data,
- bm->totvert >= BM_OMP_LIMIT);
-}
-
-static void mesh_faces_calc_normals_cb(void *UNUSED(userdata), MempoolIterData *mp_f)
-{
- BMFace *f = (BMFace *)mp_f;
-
- BM_face_normal_update(f);
-}
-
-/**
- * \brief BMesh Compute Normals
- *
- * Updates the normals of a mesh.
- */
-void BM_mesh_normals_update(BMesh *bm)
-{
- float(*edgevec)[3] = MEM_mallocN(sizeof(*edgevec) * bm->totedge, __func__);
-
- /* Parallel mempool iteration does not allow generating indices inline anymore... */
- BM_mesh_elem_index_ensure(bm, (BM_EDGE | BM_FACE));
-
- /* calculate all face normals */
- BM_iter_parallel(
- bm, BM_FACES_OF_MESH, mesh_faces_calc_normals_cb, NULL, bm->totface >= BM_OMP_LIMIT);
-
- /* Zero out vertex normals */
- BMIter viter;
- BMVert *v;
- int i;
-
- BM_ITER_MESH_INDEX (v, &viter, bm, BM_VERTS_OF_MESH, i) {
- BM_elem_index_set(v, i); /* set_inline */
- zero_v3(v->no);
- }
- bm->elem_index_dirty &= ~BM_VERT;
-
- /* Compute normalized direction vectors for each edge.
- * Directions will be used for calculating the weights of the face normals on the vertex normals.
- */
- bm_mesh_edges_calc_vectors(bm, edgevec, NULL);
-
- /* Add weighted face normals to vertices, and normalize vert normals. */
- bm_mesh_verts_calc_normals(bm, (const float(*)[3])edgevec, NULL, NULL, NULL);
- MEM_freeN(edgevec);
-}
-
-/**
- * \brief BMesh Compute Normals from/to external data.
- *
- * Computes the vertex normals of a mesh into vnos,
- * using given vertex coordinates (vcos) and polygon normals (fnos).
- */
-void BM_verts_calc_normal_vcos(BMesh *bm,
- const float (*fnos)[3],
- const float (*vcos)[3],
- float (*vnos)[3])
-{
- float(*edgevec)[3] = MEM_mallocN(sizeof(*edgevec) * bm->totedge, __func__);
-
- /* Compute normalized direction vectors for each edge.
- * Directions will be used for calculating the weights of the face normals on the vertex normals.
- */
- bm_mesh_edges_calc_vectors(bm, edgevec, vcos);
-
- /* Add weighted face normals to vertices, and normalize vert normals. */
- bm_mesh_verts_calc_normals(bm, (const float(*)[3])edgevec, fnos, vcos, vnos);
- MEM_freeN(edgevec);
-}
-
-/**
- * Helpers for #BM_mesh_loop_normals_update and #BM_loops_calc_normal_vcos
- */
-static void bm_mesh_edges_sharp_tag(BMesh *bm,
- const float (*vnos)[3],
- const float (*fnos)[3],
- float (*r_lnos)[3],
- const float split_angle,
- const bool do_sharp_edges_tag)
-{
- BMIter eiter;
- BMEdge *e;
- int i;
-
- const bool check_angle = (split_angle < (float)M_PI);
- const float split_angle_cos = check_angle ? cosf(split_angle) : -1.0f;
-
- {
- char htype = BM_VERT | BM_LOOP;
- if (fnos) {
- htype |= BM_FACE;
- }
- BM_mesh_elem_index_ensure(bm, htype);
- }
-
- /* This first loop checks which edges are actually smooth,
- * and pre-populate lnos with vnos (as if they were all smooth). */
- BM_ITER_MESH_INDEX (e, &eiter, bm, BM_EDGES_OF_MESH, i) {
- BMLoop *l_a, *l_b;
-
- BM_elem_index_set(e, i); /* set_inline */
- BM_elem_flag_disable(e, BM_ELEM_TAG); /* Clear tag (means edge is sharp). */
-
- /* An edge with only two loops, might be smooth... */
- if (BM_edge_loop_pair(e, &l_a, &l_b)) {
- bool is_angle_smooth = true;
- if (check_angle) {
- const float *no_a = fnos ? fnos[BM_elem_index_get(l_a->f)] : l_a->f->no;
- const float *no_b = fnos ? fnos[BM_elem_index_get(l_b->f)] : l_b->f->no;
- is_angle_smooth = (dot_v3v3(no_a, no_b) >= split_angle_cos);
- }
-
- /* We only tag edges that are *really* smooth:
- * If the angle between both its polys' normals is below split_angle value,
- * and it is tagged as such,
- * and both its faces are smooth,
- * and both its faces have compatible (non-flipped) normals,
- * i.e. both loops on the same edge do not share the same vertex.
- */
- if (BM_elem_flag_test(e, BM_ELEM_SMOOTH) && BM_elem_flag_test(l_a->f, BM_ELEM_SMOOTH) &&
- BM_elem_flag_test(l_b->f, BM_ELEM_SMOOTH) && l_a->v != l_b->v) {
- if (is_angle_smooth) {
- const float *no;
- BM_elem_flag_enable(e, BM_ELEM_TAG);
-
- /* linked vertices might be fully smooth, copy their normals to loop ones. */
- if (r_lnos) {
- no = vnos ? vnos[BM_elem_index_get(l_a->v)] : l_a->v->no;
- copy_v3_v3(r_lnos[BM_elem_index_get(l_a)], no);
- no = vnos ? vnos[BM_elem_index_get(l_b->v)] : l_b->v->no;
- copy_v3_v3(r_lnos[BM_elem_index_get(l_b)], no);
- }
- }
- else if (do_sharp_edges_tag) {
- /* Note that we do not care about the other sharp-edge cases
- * (sharp poly, non-manifold edge, etc.),
- * only tag edge as sharp when it is due to angle threshold. */
- BM_elem_flag_disable(e, BM_ELEM_SMOOTH);
- }
- }
- }
- }
-
- bm->elem_index_dirty &= ~BM_EDGE;
-}
-
-/**
- * Check whether given loop is part of an unknown-so-far cyclic smooth fan, or not.
- * Needed because cyclic smooth fans have no obvious 'entry point',
- * and yet we need to walk them once, and only once.
- */
-bool BM_loop_check_cyclic_smooth_fan(BMLoop *l_curr)
-{
- BMLoop *lfan_pivot_next = l_curr;
- BMEdge *e_next = l_curr->e;
-
- BLI_assert(!BM_elem_flag_test(lfan_pivot_next, BM_ELEM_TAG));
- BM_elem_flag_enable(lfan_pivot_next, BM_ELEM_TAG);
-
- while (true) {
- /* Much simpler than in sibling code with basic Mesh data! */
- lfan_pivot_next = BM_vert_step_fan_loop(lfan_pivot_next, &e_next);
-
- if (!lfan_pivot_next || !BM_elem_flag_test(e_next, BM_ELEM_TAG)) {
- /* Sharp loop/edge, so not a cyclic smooth fan... */
- return false;
- }
- /* Smooth loop/edge... */
- if (BM_elem_flag_test(lfan_pivot_next, BM_ELEM_TAG)) {
- if (lfan_pivot_next == l_curr) {
- /* We walked around a whole cyclic smooth fan
- * without finding any already-processed loop,
- * means we can use initial l_curr/l_prev edge as start for this smooth fan. */
- return true;
- }
- /* ... already checked in some previous looping, we can abort. */
- return false;
- }
- /* ... we can skip it in future, and keep checking the smooth fan. */
- BM_elem_flag_enable(lfan_pivot_next, BM_ELEM_TAG);
- }
-}
-
-/**
- * BMesh version of BKE_mesh_normals_loop_split() in mesh_evaluate.c
- * Will use first clnors_data array, and fallback to cd_loop_clnors_offset
- * (use NULL and -1 to not use clnors).
- *
- * \note This sets #BM_ELEM_TAG which is used in tool code (e.g. T84426).
- * we could add a low-level API flag for this, see #BM_ELEM_API_FLAG_ENABLE and friends.
- */
-static void bm_mesh_loops_calc_normals(BMesh *bm,
- const float (*vcos)[3],
- const float (*fnos)[3],
- float (*r_lnos)[3],
- MLoopNorSpaceArray *r_lnors_spacearr,
- const short (*clnors_data)[2],
- const int cd_loop_clnors_offset,
- const bool do_rebuild)
-{
- BMIter fiter;
- BMFace *f_curr;
- const bool has_clnors = clnors_data || (cd_loop_clnors_offset != -1);
-
- MLoopNorSpaceArray _lnors_spacearr = {NULL};
-
- /* Temp normal stack. */
- BLI_SMALLSTACK_DECLARE(normal, float *);
- /* Temp clnors stack. */
- BLI_SMALLSTACK_DECLARE(clnors, short *);
- /* Temp edge vectors stack, only used when computing lnor spacearr. */
- BLI_Stack *edge_vectors = NULL;
-
- {
- char htype = 0;
- if (vcos) {
- htype |= BM_VERT;
- }
- /* Face/Loop indices are set inline below. */
- BM_mesh_elem_index_ensure(bm, htype);
- }
-
- if (!r_lnors_spacearr && has_clnors) {
- /* We need to compute lnor spacearr if some custom lnor data are given to us! */
- r_lnors_spacearr = &_lnors_spacearr;
- }
- if (r_lnors_spacearr) {
- BKE_lnor_spacearr_init(r_lnors_spacearr, bm->totloop, MLNOR_SPACEARR_BMLOOP_PTR);
- edge_vectors = BLI_stack_new(sizeof(float[3]), __func__);
- }
-
- /* Clear all loops' tags (means none are to be skipped for now). */
- int index_face, index_loop = 0;
- BM_ITER_MESH_INDEX (f_curr, &fiter, bm, BM_FACES_OF_MESH, index_face) {
- BMLoop *l_curr, *l_first;
-
- BM_elem_index_set(f_curr, index_face); /* set_inline */
-
- l_curr = l_first = BM_FACE_FIRST_LOOP(f_curr);
- do {
- BM_elem_index_set(l_curr, index_loop++); /* set_inline */
- BM_elem_flag_disable(l_curr, BM_ELEM_TAG);
- } while ((l_curr = l_curr->next) != l_first);
- }
- bm->elem_index_dirty &= ~(BM_FACE | BM_LOOP);
-
- /* We now know edges that can be smoothed (they are tagged),
- * and edges that will be hard (they aren't).
- * Now, time to generate the normals.
- */
- BM_ITER_MESH (f_curr, &fiter, bm, BM_FACES_OF_MESH) {
- BMLoop *l_curr, *l_first;
-
- l_curr = l_first = BM_FACE_FIRST_LOOP(f_curr);
- do {
- if (do_rebuild && !BM_ELEM_API_FLAG_TEST(l_curr, BM_LNORSPACE_UPDATE) &&
- !(bm->spacearr_dirty & BM_SPACEARR_DIRTY_ALL)) {
- continue;
- }
- /* A smooth edge, we have to check for cyclic smooth fan case.
- * If we find a new, never-processed cyclic smooth fan, we can do it now using that loop/edge
- * as 'entry point', otherwise we can skip it. */
-
- /* Note: In theory, we could make bm_mesh_loop_check_cyclic_smooth_fan() store
- * mlfan_pivot's in a stack, to avoid having to fan again around
- * the vert during actual computation of clnor & clnorspace. However, this would complicate
- * the code, add more memory usage, and
- * BM_vert_step_fan_loop() is quite cheap in term of CPU cycles,
- * so really think it's not worth it. */
- if (BM_elem_flag_test(l_curr->e, BM_ELEM_TAG) &&
- (BM_elem_flag_test(l_curr, BM_ELEM_TAG) || !BM_loop_check_cyclic_smooth_fan(l_curr))) {
- }
- else if (!BM_elem_flag_test(l_curr->e, BM_ELEM_TAG) &&
- !BM_elem_flag_test(l_curr->prev->e, BM_ELEM_TAG)) {
- /* Simple case (both edges around that vertex are sharp in related polygon),
- * this vertex just takes its poly normal.
- */
- const int l_curr_index = BM_elem_index_get(l_curr);
- const float *no = fnos ? fnos[BM_elem_index_get(f_curr)] : f_curr->no;
- copy_v3_v3(r_lnos[l_curr_index], no);
-
- /* If needed, generate this (simple!) lnor space. */
- if (r_lnors_spacearr) {
- float vec_curr[3], vec_prev[3];
- MLoopNorSpace *lnor_space = BKE_lnor_space_create(r_lnors_spacearr);
-
- {
- const BMVert *v_pivot = l_curr->v;
- const float *co_pivot = vcos ? vcos[BM_elem_index_get(v_pivot)] : v_pivot->co;
- const BMVert *v_1 = BM_edge_other_vert(l_curr->e, v_pivot);
- const float *co_1 = vcos ? vcos[BM_elem_index_get(v_1)] : v_1->co;
- const BMVert *v_2 = BM_edge_other_vert(l_curr->prev->e, v_pivot);
- const float *co_2 = vcos ? vcos[BM_elem_index_get(v_2)] : v_2->co;
-
- sub_v3_v3v3(vec_curr, co_1, co_pivot);
- normalize_v3(vec_curr);
- sub_v3_v3v3(vec_prev, co_2, co_pivot);
- normalize_v3(vec_prev);
- }
-
- BKE_lnor_space_define(lnor_space, r_lnos[l_curr_index], vec_curr, vec_prev, NULL);
- /* We know there is only one loop in this space,
- * no need to create a linklist in this case... */
- BKE_lnor_space_add_loop(r_lnors_spacearr, lnor_space, l_curr_index, l_curr, true);
-
- if (has_clnors) {
- const short(*clnor)[2] = clnors_data ? &clnors_data[l_curr_index] :
- (const void *)BM_ELEM_CD_GET_VOID_P(
- l_curr, cd_loop_clnors_offset);
- BKE_lnor_space_custom_data_to_normal(lnor_space, *clnor, r_lnos[l_curr_index]);
- }
- }
- }
- /* We *do not need* to check/tag loops as already computed!
- * Due to the fact a loop only links to one of its two edges,
- * a same fan *will never be walked more than once!*
- * Since we consider edges having neighbor faces with inverted (flipped) normals as sharp,
- * we are sure that no fan will be skipped, even only considering the case
- * (sharp curr_edge, smooth prev_edge), and not the alternative
- * (smooth curr_edge, sharp prev_edge).
- * All this due/thanks to link between normals and loop ordering.
- */
- else {
- /* We have to fan around current vertex, until we find the other non-smooth edge,
- * and accumulate face normals into the vertex!
- * Note in case this vertex has only one sharp edge,
- * this is a waste because the normal is the same as the vertex normal,
- * but I do not see any easy way to detect that (would need to count number of sharp edges
- * per vertex, I doubt the additional memory usage would be worth it, especially as it
- * should not be a common case in real-life meshes anyway).
- */
- BMVert *v_pivot = l_curr->v;
- BMEdge *e_next;
- const BMEdge *e_org = l_curr->e;
- BMLoop *lfan_pivot, *lfan_pivot_next;
- int lfan_pivot_index;
- float lnor[3] = {0.0f, 0.0f, 0.0f};
- float vec_curr[3], vec_next[3], vec_org[3];
-
- /* We validate clnors data on the fly - cheapest way to do! */
- int clnors_avg[2] = {0, 0};
- const short(*clnor_ref)[2] = NULL;
- int clnors_nbr = 0;
- bool clnors_invalid = false;
-
- const float *co_pivot = vcos ? vcos[BM_elem_index_get(v_pivot)] : v_pivot->co;
-
- MLoopNorSpace *lnor_space = r_lnors_spacearr ? BKE_lnor_space_create(r_lnors_spacearr) :
- NULL;
-
- BLI_assert((edge_vectors == NULL) || BLI_stack_is_empty(edge_vectors));
-
- lfan_pivot = l_curr;
- lfan_pivot_index = BM_elem_index_get(lfan_pivot);
- e_next = lfan_pivot->e; /* Current edge here, actually! */
-
- /* Only need to compute previous edge's vector once,
- * then we can just reuse old current one! */
- {
- const BMVert *v_2 = BM_edge_other_vert(e_next, v_pivot);
- const float *co_2 = vcos ? vcos[BM_elem_index_get(v_2)] : v_2->co;
-
- sub_v3_v3v3(vec_org, co_2, co_pivot);
- normalize_v3(vec_org);
- copy_v3_v3(vec_curr, vec_org);
-
- if (r_lnors_spacearr) {
- BLI_stack_push(edge_vectors, vec_org);
- }
- }
-
- while (true) {
- /* Much simpler than in sibling code with basic Mesh data! */
- lfan_pivot_next = BM_vert_step_fan_loop(lfan_pivot, &e_next);
- if (lfan_pivot_next) {
- BLI_assert(lfan_pivot_next->v == v_pivot);
- }
- else {
- /* next edge is non-manifold, we have to find it ourselves! */
- e_next = (lfan_pivot->e == e_next) ? lfan_pivot->prev->e : lfan_pivot->e;
- }
-
- /* Compute edge vector.
- * NOTE: We could pre-compute those into an array, in the first iteration,
- * instead of computing them twice (or more) here.
- * However, time gained is not worth memory and time lost,
- * given the fact that this code should not be called that much in real-life meshes.
- */
- {
- const BMVert *v_2 = BM_edge_other_vert(e_next, v_pivot);
- const float *co_2 = vcos ? vcos[BM_elem_index_get(v_2)] : v_2->co;
-
- sub_v3_v3v3(vec_next, co_2, co_pivot);
- normalize_v3(vec_next);
- }
-
- {
- /* Code similar to accumulate_vertex_normals_poly_v3. */
- /* Calculate angle between the two poly edges incident on this vertex. */
- const BMFace *f = lfan_pivot->f;
- const float fac = saacos(dot_v3v3(vec_next, vec_curr));
- const float *no = fnos ? fnos[BM_elem_index_get(f)] : f->no;
- /* Accumulate */
- madd_v3_v3fl(lnor, no, fac);
-
- if (has_clnors) {
- /* Accumulate all clnors, if they are not all equal we have to fix that! */
- const short(*clnor)[2] = clnors_data ? &clnors_data[lfan_pivot_index] :
- (const void *)BM_ELEM_CD_GET_VOID_P(
- lfan_pivot, cd_loop_clnors_offset);
- if (clnors_nbr) {
- clnors_invalid |= ((*clnor_ref)[0] != (*clnor)[0] ||
- (*clnor_ref)[1] != (*clnor)[1]);
- }
- else {
- clnor_ref = clnor;
- }
- clnors_avg[0] += (*clnor)[0];
- clnors_avg[1] += (*clnor)[1];
- clnors_nbr++;
- /* We store here a pointer to all custom lnors processed. */
- BLI_SMALLSTACK_PUSH(clnors, (short *)*clnor);
- }
- }
-
- /* We store here a pointer to all loop-normals processed. */
- BLI_SMALLSTACK_PUSH(normal, (float *)r_lnos[lfan_pivot_index]);
-
- if (r_lnors_spacearr) {
- /* Assign current lnor space to current 'vertex' loop. */
- BKE_lnor_space_add_loop(
- r_lnors_spacearr, lnor_space, lfan_pivot_index, lfan_pivot, false);
- if (e_next != e_org) {
- /* We store here all edges-normalized vectors processed. */
- BLI_stack_push(edge_vectors, vec_next);
- }
- }
-
- if (!BM_elem_flag_test(e_next, BM_ELEM_TAG) || (e_next == e_org)) {
- /* Next edge is sharp, we have finished with this fan of faces around this vert! */
- break;
- }
-
- /* Copy next edge vector to current one. */
- copy_v3_v3(vec_curr, vec_next);
- /* Next pivot loop to current one. */
- lfan_pivot = lfan_pivot_next;
- lfan_pivot_index = BM_elem_index_get(lfan_pivot);
- }
-
- {
- float lnor_len = normalize_v3(lnor);
-
- /* If we are generating lnor spacearr, we can now define the one for this fan. */
- if (r_lnors_spacearr) {
- if (UNLIKELY(lnor_len == 0.0f)) {
- /* Use vertex normal as fallback! */
- copy_v3_v3(lnor, r_lnos[lfan_pivot_index]);
- lnor_len = 1.0f;
- }
-
- BKE_lnor_space_define(lnor_space, lnor, vec_org, vec_next, edge_vectors);
-
- if (has_clnors) {
- if (clnors_invalid) {
- short *clnor;
-
- clnors_avg[0] /= clnors_nbr;
- clnors_avg[1] /= clnors_nbr;
- /* Fix/update all clnors of this fan with computed average value. */
-
- /* Prints continuously when merge custom normals, so commenting. */
- /* printf("Invalid clnors in this fan!\n"); */
-
- while ((clnor = BLI_SMALLSTACK_POP(clnors))) {
- // print_v2("org clnor", clnor);
- clnor[0] = (short)clnors_avg[0];
- clnor[1] = (short)clnors_avg[1];
- }
- // print_v2("new clnors", clnors_avg);
- }
- else {
- /* We still have to consume the stack! */
- while (BLI_SMALLSTACK_POP(clnors)) {
- /* pass */
- }
- }
- BKE_lnor_space_custom_data_to_normal(lnor_space, *clnor_ref, lnor);
- }
- }
-
- /* In case we get a zero normal here, just use vertex normal already set! */
- if (LIKELY(lnor_len != 0.0f)) {
- /* Copy back the final computed normal into all related loop-normals. */
- float *nor;
-
- while ((nor = BLI_SMALLSTACK_POP(normal))) {
- copy_v3_v3(nor, lnor);
- }
- }
- else {
- /* We still have to consume the stack! */
- while (BLI_SMALLSTACK_POP(normal)) {
- /* pass */
- }
- }
- }
-
- /* Tag related vertex as sharp, to avoid fanning around it again
- * (in case it was a smooth one). */
- if (r_lnors_spacearr) {
- BM_elem_flag_enable(l_curr->v, BM_ELEM_TAG);
- }
- }
- } while ((l_curr = l_curr->next) != l_first);
- }
-
- if (r_lnors_spacearr) {
- BLI_stack_free(edge_vectors);
- if (r_lnors_spacearr == &_lnors_spacearr) {
- BKE_lnor_spacearr_free(r_lnors_spacearr);
- }
- }
-}
-
-/* This threshold is a bit touchy (usual float precision issue), this value seems OK. */
-#define LNOR_SPACE_TRIGO_THRESHOLD (1.0f - 1e-4f)
-
-/**
- * Check each current smooth fan (one lnor space per smooth fan!), and if all its
- * matching custom lnors are not (enough) equal, add sharp edges as needed.
- */
-static bool bm_mesh_loops_split_lnor_fans(BMesh *bm,
- MLoopNorSpaceArray *lnors_spacearr,
- const float (*new_lnors)[3])
-{
- BLI_bitmap *done_loops = BLI_BITMAP_NEW((size_t)bm->totloop, __func__);
- bool changed = false;
-
- BLI_assert(lnors_spacearr->data_type == MLNOR_SPACEARR_BMLOOP_PTR);
-
- for (int i = 0; i < bm->totloop; i++) {
- if (!lnors_spacearr->lspacearr[i]) {
- /* This should not happen in theory, but in some rare case (probably ugly geometry)
- * we can get some NULL loopspacearr at this point. :/
- * Maybe we should set those loops' edges as sharp?
- */
- BLI_BITMAP_ENABLE(done_loops, i);
- if (G.debug & G_DEBUG) {
- printf("WARNING! Getting invalid NULL loop space for loop %d!\n", i);
- }
- continue;
- }
-
- if (!BLI_BITMAP_TEST(done_loops, i)) {
- /* Notes:
- * * In case of mono-loop smooth fan, we have nothing to do.
- * * Loops in this linklist are ordered (in reversed order compared to how they were
- * discovered by BKE_mesh_normals_loop_split(), but this is not a problem).
- * Which means if we find a mismatching clnor,
- * we know all remaining loops will have to be in a new, different smooth fan/lnor space.
- * * In smooth fan case, we compare each clnor against a ref one,
- * to avoid small differences adding up into a real big one in the end!
- */
- if (lnors_spacearr->lspacearr[i]->flags & MLNOR_SPACE_IS_SINGLE) {
- BLI_BITMAP_ENABLE(done_loops, i);
- continue;
- }
-
- LinkNode *loops = lnors_spacearr->lspacearr[i]->loops;
- BMLoop *prev_ml = NULL;
- const float *org_nor = NULL;
-
- while (loops) {
- BMLoop *ml = loops->link;
- const int lidx = BM_elem_index_get(ml);
- const float *nor = new_lnors[lidx];
-
- if (!org_nor) {
- org_nor = nor;
- }
- else if (dot_v3v3(org_nor, nor) < LNOR_SPACE_TRIGO_THRESHOLD) {
- /* Current normal differs too much from org one, we have to tag the edge between
- * previous loop's face and current's one as sharp.
- * We know those two loops do not point to the same edge,
- * since we do not allow reversed winding in a same smooth fan.
- */
- BMEdge *e = (prev_ml->e == ml->prev->e) ? prev_ml->e : ml->e;
-
- BM_elem_flag_disable(e, BM_ELEM_TAG | BM_ELEM_SMOOTH);
- changed = true;
-
- org_nor = nor;
- }
-
- prev_ml = ml;
- loops = loops->next;
- BLI_BITMAP_ENABLE(done_loops, lidx);
- }
-
- /* We also have to check between last and first loops,
- * otherwise we may miss some sharp edges here!
- * This is just a simplified version of above while loop.
- * See T45984. */
- loops = lnors_spacearr->lspacearr[i]->loops;
- if (loops && org_nor) {
- BMLoop *ml = loops->link;
- const int lidx = BM_elem_index_get(ml);
- const float *nor = new_lnors[lidx];
-
- if (dot_v3v3(org_nor, nor) < LNOR_SPACE_TRIGO_THRESHOLD) {
- BMEdge *e = (prev_ml->e == ml->prev->e) ? prev_ml->e : ml->e;
-
- BM_elem_flag_disable(e, BM_ELEM_TAG | BM_ELEM_SMOOTH);
- changed = true;
- }
- }
- }
- }
-
- MEM_freeN(done_loops);
- return changed;
-}
-
-/**
- * Assign custom normal data from given normal vectors, averaging normals
- * from one smooth fan as necessary.
- */
-static void bm_mesh_loops_assign_normal_data(BMesh *bm,
- MLoopNorSpaceArray *lnors_spacearr,
- short (*r_clnors_data)[2],
- const int cd_loop_clnors_offset,
- const float (*new_lnors)[3])
-{
- BLI_bitmap *done_loops = BLI_BITMAP_NEW((size_t)bm->totloop, __func__);
-
- BLI_SMALLSTACK_DECLARE(clnors_data, short *);
-
- BLI_assert(lnors_spacearr->data_type == MLNOR_SPACEARR_BMLOOP_PTR);
-
- for (int i = 0; i < bm->totloop; i++) {
- if (!lnors_spacearr->lspacearr[i]) {
- BLI_BITMAP_ENABLE(done_loops, i);
- if (G.debug & G_DEBUG) {
- printf("WARNING! Still getting invalid NULL loop space in second loop for loop %d!\n", i);
- }
- continue;
- }
-
- if (!BLI_BITMAP_TEST(done_loops, i)) {
- /* Note we accumulate and average all custom normals in current smooth fan,
- * to avoid getting different clnors data (tiny differences in plain custom normals can
- * give rather huge differences in computed 2D factors).
- */
- LinkNode *loops = lnors_spacearr->lspacearr[i]->loops;
-
- if (lnors_spacearr->lspacearr[i]->flags & MLNOR_SPACE_IS_SINGLE) {
- BMLoop *ml = (BMLoop *)loops;
- const int lidx = BM_elem_index_get(ml);
-
- BLI_assert(lidx == i);
-
- const float *nor = new_lnors[lidx];
- short *clnor = r_clnors_data ? &r_clnors_data[lidx] :
- BM_ELEM_CD_GET_VOID_P(ml, cd_loop_clnors_offset);
-
- BKE_lnor_space_custom_normal_to_data(lnors_spacearr->lspacearr[i], nor, clnor);
- BLI_BITMAP_ENABLE(done_loops, i);
- }
- else {
- int nbr_nors = 0;
- float avg_nor[3];
- short clnor_data_tmp[2], *clnor_data;
-
- zero_v3(avg_nor);
-
- while (loops) {
- BMLoop *ml = loops->link;
- const int lidx = BM_elem_index_get(ml);
- const float *nor = new_lnors[lidx];
- short *clnor = r_clnors_data ? &r_clnors_data[lidx] :
- BM_ELEM_CD_GET_VOID_P(ml, cd_loop_clnors_offset);
-
- nbr_nors++;
- add_v3_v3(avg_nor, nor);
- BLI_SMALLSTACK_PUSH(clnors_data, clnor);
-
- loops = loops->next;
- BLI_BITMAP_ENABLE(done_loops, lidx);
- }
-
- mul_v3_fl(avg_nor, 1.0f / (float)nbr_nors);
- BKE_lnor_space_custom_normal_to_data(
- lnors_spacearr->lspacearr[i], avg_nor, clnor_data_tmp);
-
- while ((clnor_data = BLI_SMALLSTACK_POP(clnors_data))) {
- clnor_data[0] = clnor_data_tmp[0];
- clnor_data[1] = clnor_data_tmp[1];
- }
- }
- }
- }
-
- MEM_freeN(done_loops);
-}
-
-/**
- * Compute internal representation of given custom normals (as an array of float[2] or data layer).
- *
- * It also makes sure the mesh matches those custom normals, by marking new sharp edges to split
- * the smooth fans when loop normals for the same vertex are different, or averaging the normals
- * instead, depending on the do_split_fans parameter.
- */
-static void bm_mesh_loops_custom_normals_set(BMesh *bm,
- const float (*vcos)[3],
- const float (*vnos)[3],
- const float (*fnos)[3],
- MLoopNorSpaceArray *r_lnors_spacearr,
- short (*r_clnors_data)[2],
- const int cd_loop_clnors_offset,
- float (*new_lnors)[3],
- const int cd_new_lnors_offset,
- bool do_split_fans)
-{
- BMFace *f;
- BMLoop *l;
- BMIter liter, fiter;
- float(*cur_lnors)[3] = MEM_mallocN(sizeof(*cur_lnors) * bm->totloop, __func__);
-
- BKE_lnor_spacearr_clear(r_lnors_spacearr);
-
- /* Tag smooth edges and set lnos from vnos when they might be completely smooth...
- * When using custom loop normals, disable the angle feature! */
- bm_mesh_edges_sharp_tag(bm, vnos, fnos, cur_lnors, (float)M_PI, false);
-
- /* Finish computing lnos by accumulating face normals
- * in each fan of faces defined by sharp edges. */
- bm_mesh_loops_calc_normals(
- bm, vcos, fnos, cur_lnors, r_lnors_spacearr, r_clnors_data, cd_loop_clnors_offset, false);
-
- /* Extract new normals from the data layer if necessary. */
- float(*custom_lnors)[3] = new_lnors;
-
- if (new_lnors == NULL) {
- custom_lnors = MEM_mallocN(sizeof(*new_lnors) * bm->totloop, __func__);
-
- BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
- BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
- const float *normal = BM_ELEM_CD_GET_VOID_P(l, cd_new_lnors_offset);
- copy_v3_v3(custom_lnors[BM_elem_index_get(l)], normal);
- }
- }
- }
-
- /* Validate the new normals. */
- for (int i = 0; i < bm->totloop; i++) {
- if (is_zero_v3(custom_lnors[i])) {
- copy_v3_v3(custom_lnors[i], cur_lnors[i]);
- }
- else {
- normalize_v3(custom_lnors[i]);
- }
- }
-
- /* Now, check each current smooth fan (one lnor space per smooth fan!),
- * and if all its matching custom lnors are not equal, add sharp edges as needed. */
- if (do_split_fans && bm_mesh_loops_split_lnor_fans(bm, r_lnors_spacearr, custom_lnors)) {
- /* If any sharp edges were added, run bm_mesh_loops_calc_normals() again to get lnor
- * spacearr/smooth fans matching the given custom lnors. */
- BKE_lnor_spacearr_clear(r_lnors_spacearr);
-
- bm_mesh_loops_calc_normals(
- bm, vcos, fnos, cur_lnors, r_lnors_spacearr, r_clnors_data, cd_loop_clnors_offset, false);
- }
-
- /* And we just have to convert plain object-space custom normals to our
- * lnor space-encoded ones. */
- bm_mesh_loops_assign_normal_data(
- bm, r_lnors_spacearr, r_clnors_data, cd_loop_clnors_offset, custom_lnors);
-
- MEM_freeN(cur_lnors);
-
- if (custom_lnors != new_lnors) {
- MEM_freeN(custom_lnors);
- }
-}
-
-static void bm_mesh_loops_calc_normals_no_autosmooth(BMesh *bm,
- const float (*vnos)[3],
- const float (*fnos)[3],
- float (*r_lnos)[3])
-{
- BMIter fiter;
- BMFace *f_curr;
-
- {
- char htype = BM_LOOP;
- if (vnos) {
- htype |= BM_VERT;
- }
- if (fnos) {
- htype |= BM_FACE;
- }
- BM_mesh_elem_index_ensure(bm, htype);
- }
-
- BM_ITER_MESH (f_curr, &fiter, bm, BM_FACES_OF_MESH) {
- BMLoop *l_curr, *l_first;
- const bool is_face_flat = !BM_elem_flag_test(f_curr, BM_ELEM_SMOOTH);
-
- l_curr = l_first = BM_FACE_FIRST_LOOP(f_curr);
- do {
- const float *no = is_face_flat ? (fnos ? fnos[BM_elem_index_get(f_curr)] : f_curr->no) :
- (vnos ? vnos[BM_elem_index_get(l_curr->v)] : l_curr->v->no);
- copy_v3_v3(r_lnos[BM_elem_index_get(l_curr)], no);
-
- } while ((l_curr = l_curr->next) != l_first);
- }
-}
-
-#if 0 /* Unused currently */
-/**
- * \brief BMesh Compute Loop Normals
- *
- * Updates the loop normals of a mesh.
- * Assumes vertex and face normals are valid (else call BM_mesh_normals_update() first)!
- */
-void BM_mesh_loop_normals_update(BMesh *bm,
- const bool use_split_normals,
- const float split_angle,
- float (*r_lnos)[3],
- MLoopNorSpaceArray *r_lnors_spacearr,
- const short (*clnors_data)[2],
- const int cd_loop_clnors_offset)
-{
- const bool has_clnors = clnors_data || (cd_loop_clnors_offset != -1);
-
- if (use_split_normals) {
- /* Tag smooth edges and set lnos from vnos when they might be completely smooth...
- * When using custom loop normals, disable the angle feature! */
- bm_mesh_edges_sharp_tag(bm, NULL, NULL, has_clnors ? (float)M_PI : split_angle, r_lnos);
-
- /* Finish computing lnos by accumulating face normals
- * in each fan of faces defined by sharp edges. */
- bm_mesh_loops_calc_normals(
- bm, NULL, NULL, r_lnos, r_lnors_spacearr, clnors_data, cd_loop_clnors_offset);
- }
- else {
- BLI_assert(!r_lnors_spacearr);
- bm_mesh_loops_calc_normals_no_autosmooth(bm, NULL, NULL, r_lnos);
- }
-}
-#endif
-
-/**
- * \brief BMesh Compute Loop Normals from/to external data.
- *
- * Compute split normals, i.e. vertex normals associated with each poly (hence 'loop normals').
- * Useful to materialize sharp edges (or non-smooth faces) without actually modifying the geometry
- * (splitting edges).
- */
-void BM_loops_calc_normal_vcos(BMesh *bm,
- const float (*vcos)[3],
- const float (*vnos)[3],
- const float (*fnos)[3],
- const bool use_split_normals,
- const float split_angle,
- float (*r_lnos)[3],
- MLoopNorSpaceArray *r_lnors_spacearr,
- short (*clnors_data)[2],
- const int cd_loop_clnors_offset,
- const bool do_rebuild)
-{
- const bool has_clnors = clnors_data || (cd_loop_clnors_offset != -1);
-
- if (use_split_normals) {
- /* Tag smooth edges and set lnos from vnos when they might be completely smooth...
- * When using custom loop normals, disable the angle feature! */
- bm_mesh_edges_sharp_tag(bm, vnos, fnos, r_lnos, has_clnors ? (float)M_PI : split_angle, false);
-
- /* Finish computing lnos by accumulating face normals
- * in each fan of faces defined by sharp edges. */
- bm_mesh_loops_calc_normals(
- bm, vcos, fnos, r_lnos, r_lnors_spacearr, clnors_data, cd_loop_clnors_offset, do_rebuild);
- }
- else {
- BLI_assert(!r_lnors_spacearr);
- bm_mesh_loops_calc_normals_no_autosmooth(bm, vnos, fnos, r_lnos);
- }
-}
-
-/**
- * Define sharp edges as needed to mimic 'autosmooth' from angle threshold.
- *
- * Used when defining an empty custom loop normals data layer,
- * to keep same shading as with autosmooth!
- */
-void BM_edges_sharp_from_angle_set(BMesh *bm, const float split_angle)
-{
- if (split_angle >= (float)M_PI) {
- /* Nothing to do! */
- return;
- }
-
- bm_mesh_edges_sharp_tag(bm, NULL, NULL, NULL, split_angle, true);
-}
-
-void BM_lnorspacearr_store(BMesh *bm, float (*r_lnors)[3])
-{
- BLI_assert(bm->lnor_spacearr != NULL);
-
- if (!CustomData_has_layer(&bm->ldata, CD_CUSTOMLOOPNORMAL)) {
- BM_data_layer_add(bm, &bm->ldata, CD_CUSTOMLOOPNORMAL);
- }
-
- int cd_loop_clnors_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL);
-
- BM_loops_calc_normal_vcos(bm,
- NULL,
- NULL,
- NULL,
- true,
- M_PI,
- r_lnors,
- bm->lnor_spacearr,
- NULL,
- cd_loop_clnors_offset,
- false);
- bm->spacearr_dirty &= ~(BM_SPACEARR_DIRTY | BM_SPACEARR_DIRTY_ALL);
-}
-
-#define CLEAR_SPACEARRAY_THRESHOLD(x) ((x) / 2)
-
-void BM_lnorspace_invalidate(BMesh *bm, const bool do_invalidate_all)
-{
- if (bm->spacearr_dirty & BM_SPACEARR_DIRTY_ALL) {
- return;
- }
- if (do_invalidate_all || bm->totvertsel > CLEAR_SPACEARRAY_THRESHOLD(bm->totvert)) {
- bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
- return;
- }
- if (bm->lnor_spacearr == NULL) {
- bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
- return;
- }
-
- BMVert *v;
- BMLoop *l;
- BMIter viter, liter;
- /* Note: we could use temp tag of BMItem for that,
- * but probably better not use it in such a low-level func?
- * --mont29 */
- BLI_bitmap *done_verts = BLI_BITMAP_NEW(bm->totvert, __func__);
-
- BM_mesh_elem_index_ensure(bm, BM_VERT);
-
- /* When we affect a given vertex, we may affect following smooth fans:
- * - all smooth fans of said vertex;
- * - all smooth fans of all immediate loop-neighbors vertices;
- * This can be simplified as 'all loops of selected vertices and their immediate neighbors'
- * need to be tagged for update.
- */
- BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
- if (BM_elem_flag_test(v, BM_ELEM_SELECT)) {
- BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) {
- BM_ELEM_API_FLAG_ENABLE(l, BM_LNORSPACE_UPDATE);
-
- /* Note that we only handle unselected neighbor vertices here, main loop will take care of
- * selected ones. */
- if ((!BM_elem_flag_test(l->prev->v, BM_ELEM_SELECT)) &&
- !BLI_BITMAP_TEST(done_verts, BM_elem_index_get(l->prev->v))) {
-
- BMLoop *l_prev;
- BMIter liter_prev;
- BM_ITER_ELEM (l_prev, &liter_prev, l->prev->v, BM_LOOPS_OF_VERT) {
- BM_ELEM_API_FLAG_ENABLE(l_prev, BM_LNORSPACE_UPDATE);
- }
- BLI_BITMAP_ENABLE(done_verts, BM_elem_index_get(l_prev->v));
- }
-
- if ((!BM_elem_flag_test(l->next->v, BM_ELEM_SELECT)) &&
- !BLI_BITMAP_TEST(done_verts, BM_elem_index_get(l->next->v))) {
-
- BMLoop *l_next;
- BMIter liter_next;
- BM_ITER_ELEM (l_next, &liter_next, l->next->v, BM_LOOPS_OF_VERT) {
- BM_ELEM_API_FLAG_ENABLE(l_next, BM_LNORSPACE_UPDATE);
- }
- BLI_BITMAP_ENABLE(done_verts, BM_elem_index_get(l_next->v));
- }
- }
-
- BLI_BITMAP_ENABLE(done_verts, BM_elem_index_get(v));
- }
- }
-
- MEM_freeN(done_verts);
- bm->spacearr_dirty |= BM_SPACEARR_DIRTY;
-}
-
-void BM_lnorspace_rebuild(BMesh *bm, bool preserve_clnor)
-{
- BLI_assert(bm->lnor_spacearr != NULL);
-
- if (!(bm->spacearr_dirty & (BM_SPACEARR_DIRTY | BM_SPACEARR_DIRTY_ALL))) {
- return;
- }
- BMFace *f;
- BMLoop *l;
- BMIter fiter, liter;
-
- float(*r_lnors)[3] = MEM_callocN(sizeof(*r_lnors) * bm->totloop, __func__);
- float(*oldnors)[3] = preserve_clnor ? MEM_mallocN(sizeof(*oldnors) * bm->totloop, __func__) :
- NULL;
-
- int cd_loop_clnors_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL);
-
- BM_mesh_elem_index_ensure(bm, BM_LOOP);
-
- if (preserve_clnor) {
- BLI_assert(bm->lnor_spacearr->lspacearr != NULL);
-
- BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
- BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
- if (BM_ELEM_API_FLAG_TEST(l, BM_LNORSPACE_UPDATE) ||
- bm->spacearr_dirty & BM_SPACEARR_DIRTY_ALL) {
- short(*clnor)[2] = BM_ELEM_CD_GET_VOID_P(l, cd_loop_clnors_offset);
- int l_index = BM_elem_index_get(l);
-
- BKE_lnor_space_custom_data_to_normal(
- bm->lnor_spacearr->lspacearr[l_index], *clnor, oldnors[l_index]);
- }
- }
- }
- }
-
- if (bm->spacearr_dirty & BM_SPACEARR_DIRTY_ALL) {
- BKE_lnor_spacearr_clear(bm->lnor_spacearr);
- }
- BM_loops_calc_normal_vcos(bm,
- NULL,
- NULL,
- NULL,
- true,
- M_PI,
- r_lnors,
- bm->lnor_spacearr,
- NULL,
- cd_loop_clnors_offset,
- true);
- MEM_freeN(r_lnors);
-
- BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
- BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
- if (BM_ELEM_API_FLAG_TEST(l, BM_LNORSPACE_UPDATE) ||
- bm->spacearr_dirty & BM_SPACEARR_DIRTY_ALL) {
- if (preserve_clnor) {
- short(*clnor)[2] = BM_ELEM_CD_GET_VOID_P(l, cd_loop_clnors_offset);
- int l_index = BM_elem_index_get(l);
- BKE_lnor_space_custom_normal_to_data(
- bm->lnor_spacearr->lspacearr[l_index], oldnors[l_index], *clnor);
- }
- BM_ELEM_API_FLAG_DISABLE(l, BM_LNORSPACE_UPDATE);
- }
- }
- }
-
- MEM_SAFE_FREE(oldnors);
- bm->spacearr_dirty &= ~(BM_SPACEARR_DIRTY | BM_SPACEARR_DIRTY_ALL);
-
-#ifndef NDEBUG
- BM_lnorspace_err(bm);
-#endif
-}
-
-/**
- * \warning This function sets #BM_ELEM_TAG on loops & edges via #bm_mesh_loops_calc_normals,
- * take care to run this before setting up tags.
- */
-void BM_lnorspace_update(BMesh *bm)
-{
- if (bm->lnor_spacearr == NULL) {
- bm->lnor_spacearr = MEM_callocN(sizeof(*bm->lnor_spacearr), __func__);
- }
- if (bm->lnor_spacearr->lspacearr == NULL) {
- float(*lnors)[3] = MEM_callocN(sizeof(*lnors) * bm->totloop, __func__);
-
- BM_lnorspacearr_store(bm, lnors);
-
- MEM_freeN(lnors);
- }
- else if (bm->spacearr_dirty & (BM_SPACEARR_DIRTY | BM_SPACEARR_DIRTY_ALL)) {
- BM_lnorspace_rebuild(bm, false);
- }
-}
-
-void BM_normals_loops_edges_tag(BMesh *bm, const bool do_edges)
-{
- BMFace *f;
- BMEdge *e;
- BMIter fiter, eiter;
- BMLoop *l_curr, *l_first;
-
- if (do_edges) {
- int index_edge;
- BM_ITER_MESH_INDEX (e, &eiter, bm, BM_EDGES_OF_MESH, index_edge) {
- BMLoop *l_a, *l_b;
-
- BM_elem_index_set(e, index_edge); /* set_inline */
- BM_elem_flag_disable(e, BM_ELEM_TAG);
- if (BM_edge_loop_pair(e, &l_a, &l_b)) {
- if (BM_elem_flag_test(e, BM_ELEM_SMOOTH) && l_a->v != l_b->v) {
- BM_elem_flag_enable(e, BM_ELEM_TAG);
- }
- }
- }
- bm->elem_index_dirty &= ~BM_EDGE;
- }
-
- int index_face, index_loop = 0;
- BM_ITER_MESH_INDEX (f, &fiter, bm, BM_FACES_OF_MESH, index_face) {
- BM_elem_index_set(f, index_face); /* set_inline */
- l_curr = l_first = BM_FACE_FIRST_LOOP(f);
- do {
- BM_elem_index_set(l_curr, index_loop++); /* set_inline */
- BM_elem_flag_disable(l_curr, BM_ELEM_TAG);
- } while ((l_curr = l_curr->next) != l_first);
- }
- bm->elem_index_dirty &= ~(BM_FACE | BM_LOOP);
-}
-
-/**
- * Auxiliary function only used by rebuild to detect if any spaces were not marked as invalid.
- * Reports error if any of the lnor spaces change after rebuilding, meaning that all the possible
- * lnor spaces to be rebuilt were not correctly marked.
- */
-#ifndef NDEBUG
-void BM_lnorspace_err(BMesh *bm)
-{
- bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
- bool clear = true;
-
- MLoopNorSpaceArray *temp = MEM_callocN(sizeof(*temp), __func__);
- temp->lspacearr = NULL;
-
- BKE_lnor_spacearr_init(temp, bm->totloop, MLNOR_SPACEARR_BMLOOP_PTR);
-
- int cd_loop_clnors_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL);
- float(*lnors)[3] = MEM_callocN(sizeof(*lnors) * bm->totloop, __func__);
- BM_loops_calc_normal_vcos(
- bm, NULL, NULL, NULL, true, M_PI, lnors, temp, NULL, cd_loop_clnors_offset, true);
-
- for (int i = 0; i < bm->totloop; i++) {
- int j = 0;
- j += compare_ff(
- temp->lspacearr[i]->ref_alpha, bm->lnor_spacearr->lspacearr[i]->ref_alpha, 1e-4f);
- j += compare_ff(
- temp->lspacearr[i]->ref_beta, bm->lnor_spacearr->lspacearr[i]->ref_beta, 1e-4f);
- j += compare_v3v3(
- temp->lspacearr[i]->vec_lnor, bm->lnor_spacearr->lspacearr[i]->vec_lnor, 1e-4f);
- j += compare_v3v3(
- temp->lspacearr[i]->vec_ortho, bm->lnor_spacearr->lspacearr[i]->vec_ortho, 1e-4f);
- j += compare_v3v3(
- temp->lspacearr[i]->vec_ref, bm->lnor_spacearr->lspacearr[i]->vec_ref, 1e-4f);
-
- if (j != 5) {
- clear = false;
- break;
- }
- }
- BKE_lnor_spacearr_free(temp);
- MEM_freeN(temp);
- MEM_freeN(lnors);
- BLI_assert(clear);
-
- bm->spacearr_dirty &= ~BM_SPACEARR_DIRTY_ALL;
-}
-#endif
-
-static void bm_loop_normal_mark_indiv_do_loop(BMLoop *l,
- BLI_bitmap *loops,
- MLoopNorSpaceArray *lnor_spacearr,
- int *totloopsel,
- const bool do_all_loops_of_vert)
-{
- if (l != NULL) {
- const int l_idx = BM_elem_index_get(l);
-
- if (!BLI_BITMAP_TEST(loops, l_idx)) {
- /* If vert and face selected share a loop, mark it for editing. */
- BLI_BITMAP_ENABLE(loops, l_idx);
- (*totloopsel)++;
-
- if (do_all_loops_of_vert) {
- /* If required, also mark all loops shared by that vertex.
- * This is needed when loop spaces may change
- * (i.e. when some faces or edges might change of smooth/sharp status). */
- BMIter liter;
- BMLoop *lfan;
- BM_ITER_ELEM (lfan, &liter, l->v, BM_LOOPS_OF_VERT) {
- const int lfan_idx = BM_elem_index_get(lfan);
- if (!BLI_BITMAP_TEST(loops, lfan_idx)) {
- BLI_BITMAP_ENABLE(loops, lfan_idx);
- (*totloopsel)++;
- }
- }
- }
- else {
- /* Mark all loops in same loop normal space (aka smooth fan). */
- if ((lnor_spacearr->lspacearr[l_idx]->flags & MLNOR_SPACE_IS_SINGLE) == 0) {
- for (LinkNode *node = lnor_spacearr->lspacearr[l_idx]->loops; node; node = node->next) {
- const int lfan_idx = BM_elem_index_get((BMLoop *)node->link);
- if (!BLI_BITMAP_TEST(loops, lfan_idx)) {
- BLI_BITMAP_ENABLE(loops, lfan_idx);
- (*totloopsel)++;
- }
- }
- }
- }
- }
- }
-}
-
-/* Mark the individual clnors to be edited, if multiple selection methods are used. */
-static int bm_loop_normal_mark_indiv(BMesh *bm, BLI_bitmap *loops, const bool do_all_loops_of_vert)
-{
- BMEditSelection *ese, *ese_prev;
- int totloopsel = 0;
-
- const bool sel_verts = (bm->selectmode & SCE_SELECT_VERTEX) != 0;
- const bool sel_edges = (bm->selectmode & SCE_SELECT_EDGE) != 0;
- const bool sel_faces = (bm->selectmode & SCE_SELECT_FACE) != 0;
- const bool use_sel_face_history = sel_faces && (sel_edges || sel_verts);
-
- BM_mesh_elem_index_ensure(bm, BM_LOOP);
-
- BLI_assert(bm->lnor_spacearr != NULL);
- BLI_assert(bm->lnor_spacearr->data_type == MLNOR_SPACEARR_BMLOOP_PTR);
-
- if (use_sel_face_history) {
- /* Using face history allows to select a single loop from a single face...
- * Note that this is On² piece of code,
- * but it is not designed to be used with huge selection sets,
- * rather with only a few items selected at most.*/
- /* Goes from last selected to the first selected element. */
- for (ese = bm->selected.last; ese; ese = ese->prev) {
- if (ese->htype == BM_FACE) {
- /* If current face is selected,
- * then any verts to be edited must have been selected before it. */
- for (ese_prev = ese->prev; ese_prev; ese_prev = ese_prev->prev) {
- if (ese_prev->htype == BM_VERT) {
- bm_loop_normal_mark_indiv_do_loop(
- BM_face_vert_share_loop((BMFace *)ese->ele, (BMVert *)ese_prev->ele),
- loops,
- bm->lnor_spacearr,
- &totloopsel,
- do_all_loops_of_vert);
- }
- else if (ese_prev->htype == BM_EDGE) {
- BMEdge *e = (BMEdge *)ese_prev->ele;
- bm_loop_normal_mark_indiv_do_loop(BM_face_vert_share_loop((BMFace *)ese->ele, e->v1),
- loops,
- bm->lnor_spacearr,
- &totloopsel,
- do_all_loops_of_vert);
-
- bm_loop_normal_mark_indiv_do_loop(BM_face_vert_share_loop((BMFace *)ese->ele, e->v2),
- loops,
- bm->lnor_spacearr,
- &totloopsel,
- do_all_loops_of_vert);
- }
- }
- }
- }
- }
- else {
- if (sel_faces) {
- /* Only select all loops of selected faces. */
- BMLoop *l;
- BMFace *f;
- BMIter liter, fiter;
- BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
- if (BM_elem_flag_test(f, BM_ELEM_SELECT)) {
- BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
- bm_loop_normal_mark_indiv_do_loop(
- l, loops, bm->lnor_spacearr, &totloopsel, do_all_loops_of_vert);
- }
- }
- }
- }
- if (sel_edges) {
- /* Only select all loops of selected edges. */
- BMLoop *l;
- BMEdge *e;
- BMIter liter, eiter;
- BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
- if (BM_elem_flag_test(e, BM_ELEM_SELECT)) {
- BM_ITER_ELEM (l, &liter, e, BM_LOOPS_OF_EDGE) {
- bm_loop_normal_mark_indiv_do_loop(
- l, loops, bm->lnor_spacearr, &totloopsel, do_all_loops_of_vert);
- /* Loops actually 'have' two edges, or said otherwise, a selected edge actually selects
- * *two* loops in each of its faces. We have to find the other one too. */
- if (BM_vert_in_edge(e, l->next->v)) {
- bm_loop_normal_mark_indiv_do_loop(
- l->next, loops, bm->lnor_spacearr, &totloopsel, do_all_loops_of_vert);
- }
- else {
- BLI_assert(BM_vert_in_edge(e, l->prev->v));
- bm_loop_normal_mark_indiv_do_loop(
- l->prev, loops, bm->lnor_spacearr, &totloopsel, do_all_loops_of_vert);
- }
- }
- }
- }
- }
- if (sel_verts) {
- /* Select all loops of selected verts. */
- BMLoop *l;
- BMVert *v;
- BMIter liter, viter;
- BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
- if (BM_elem_flag_test(v, BM_ELEM_SELECT)) {
- BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) {
- bm_loop_normal_mark_indiv_do_loop(
- l, loops, bm->lnor_spacearr, &totloopsel, do_all_loops_of_vert);
- }
- }
- }
- }
- }
-
- return totloopsel;
-}
-
-static void loop_normal_editdata_init(
- BMesh *bm, BMLoopNorEditData *lnor_ed, BMVert *v, BMLoop *l, const int offset)
-{
- BLI_assert(bm->lnor_spacearr != NULL);
- BLI_assert(bm->lnor_spacearr->lspacearr != NULL);
-
- const int l_index = BM_elem_index_get(l);
- short *clnors_data = BM_ELEM_CD_GET_VOID_P(l, offset);
-
- lnor_ed->loop_index = l_index;
- lnor_ed->loop = l;
-
- float custom_normal[3];
- BKE_lnor_space_custom_data_to_normal(
- bm->lnor_spacearr->lspacearr[l_index], clnors_data, custom_normal);
-
- lnor_ed->clnors_data = clnors_data;
- copy_v3_v3(lnor_ed->nloc, custom_normal);
- copy_v3_v3(lnor_ed->niloc, custom_normal);
-
- lnor_ed->loc = v->co;
-}
-
-BMLoopNorEditDataArray *BM_loop_normal_editdata_array_init(BMesh *bm,
- const bool do_all_loops_of_vert)
-{
- BMLoop *l;
- BMVert *v;
- BMIter liter, viter;
-
- int totloopsel = 0;
-
- BLI_assert(bm->spacearr_dirty == 0);
-
- BMLoopNorEditDataArray *lnors_ed_arr = MEM_callocN(sizeof(*lnors_ed_arr), __func__);
- lnors_ed_arr->lidx_to_lnor_editdata = MEM_callocN(
- sizeof(*lnors_ed_arr->lidx_to_lnor_editdata) * bm->totloop, __func__);
-
- if (!CustomData_has_layer(&bm->ldata, CD_CUSTOMLOOPNORMAL)) {
- BM_data_layer_add(bm, &bm->ldata, CD_CUSTOMLOOPNORMAL);
- }
- const int cd_custom_normal_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL);
-
- BM_mesh_elem_index_ensure(bm, BM_LOOP);
-
- BLI_bitmap *loops = BLI_BITMAP_NEW(bm->totloop, __func__);
-
- /* This function define loop normals to edit, based on selection modes and history. */
- totloopsel = bm_loop_normal_mark_indiv(bm, loops, do_all_loops_of_vert);
-
- if (totloopsel) {
- BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata = MEM_mallocN(
- sizeof(*lnor_ed) * totloopsel, __func__);
-
- BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
- BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) {
- if (BLI_BITMAP_TEST(loops, BM_elem_index_get(l))) {
- loop_normal_editdata_init(bm, lnor_ed, v, l, cd_custom_normal_offset);
- lnors_ed_arr->lidx_to_lnor_editdata[BM_elem_index_get(l)] = lnor_ed;
- lnor_ed++;
- }
- }
- }
- lnors_ed_arr->totloop = totloopsel;
- }
-
- MEM_freeN(loops);
- lnors_ed_arr->cd_custom_normal_offset = cd_custom_normal_offset;
- return lnors_ed_arr;
-}
-
-void BM_loop_normal_editdata_array_free(BMLoopNorEditDataArray *lnors_ed_arr)
-{
- MEM_SAFE_FREE(lnors_ed_arr->lnor_editdata);
- MEM_SAFE_FREE(lnors_ed_arr->lidx_to_lnor_editdata);
- MEM_freeN(lnors_ed_arr);
-}
-
-/**
- * \warning This function sets #BM_ELEM_TAG on loops & edges via #bm_mesh_loops_calc_normals,
- * take care to run this before setting up tags.
- */
-bool BM_custom_loop_normals_to_vector_layer(BMesh *bm)
-{
- BMFace *f;
- BMLoop *l;
- BMIter liter, fiter;
-
- if (!CustomData_has_layer(&bm->ldata, CD_CUSTOMLOOPNORMAL)) {
- return false;
- }
-
- BM_lnorspace_update(bm);
- BM_mesh_elem_index_ensure(bm, BM_LOOP);
-
- /* Create a loop normal layer. */
- if (!CustomData_has_layer(&bm->ldata, CD_NORMAL)) {
- BM_data_layer_add(bm, &bm->ldata, CD_NORMAL);
-
- CustomData_set_layer_flag(&bm->ldata, CD_NORMAL, CD_FLAG_TEMPORARY);
- }
-
- const int cd_custom_normal_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL);
- const int cd_normal_offset = CustomData_get_offset(&bm->ldata, CD_NORMAL);
-
- BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
- BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
- const int l_index = BM_elem_index_get(l);
- const short *clnors_data = BM_ELEM_CD_GET_VOID_P(l, cd_custom_normal_offset);
- float *normal = BM_ELEM_CD_GET_VOID_P(l, cd_normal_offset);
-
- BKE_lnor_space_custom_data_to_normal(
- bm->lnor_spacearr->lspacearr[l_index], clnors_data, normal);
- }
- }
-
- return true;
-}
-
-void BM_custom_loop_normals_from_vector_layer(BMesh *bm, bool add_sharp_edges)
-{
- if (!CustomData_has_layer(&bm->ldata, CD_CUSTOMLOOPNORMAL) ||
- !CustomData_has_layer(&bm->ldata, CD_NORMAL)) {
- return;
- }
-
- const int cd_custom_normal_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL);
- const int cd_normal_offset = CustomData_get_offset(&bm->ldata, CD_NORMAL);
-
- if (bm->lnor_spacearr == NULL) {
- bm->lnor_spacearr = MEM_callocN(sizeof(*bm->lnor_spacearr), __func__);
- }
-
- bm_mesh_loops_custom_normals_set(bm,
- NULL,
- NULL,
- NULL,
- bm->lnor_spacearr,
- NULL,
- cd_custom_normal_offset,
- NULL,
- cd_normal_offset,
- add_sharp_edges);
-
- bm->spacearr_dirty &= ~(BM_SPACEARR_DIRTY | BM_SPACEARR_DIRTY_ALL);
-}
-
-/**
* \brief BMesh Begin Edit
*
* Functions for setting up a mesh for editing and cleaning up after
diff --git a/source/blender/bmesh/intern/bmesh_mesh.h b/source/blender/bmesh/intern/bmesh_mesh.h
index c1c2f17d7c1..456275cf157 100644
--- a/source/blender/bmesh/intern/bmesh_mesh.h
+++ b/source/blender/bmesh/intern/bmesh_mesh.h
@@ -25,6 +25,7 @@
struct BMAllocTemplate;
struct BMLoopNorEditDataArray;
struct MLoopNorSpaceArray;
+struct BMPartialUpdate;
void BM_mesh_elem_toolflags_ensure(BMesh *bm);
void BM_mesh_elem_toolflags_clear(BMesh *bm);
@@ -40,43 +41,6 @@ void BM_mesh_free(BMesh *bm);
void BM_mesh_data_free(BMesh *bm);
void BM_mesh_clear(BMesh *bm);
-void BM_mesh_normals_update(BMesh *bm);
-void BM_verts_calc_normal_vcos(BMesh *bm,
- const float (*fnos)[3],
- const float (*vcos)[3],
- float (*vnos)[3]);
-void BM_loops_calc_normal_vcos(BMesh *bm,
- const float (*vcos)[3],
- const float (*vnos)[3],
- const float (*fnos)[3],
- const bool use_split_normals,
- const float split_angle,
- float (*r_lnos)[3],
- struct MLoopNorSpaceArray *r_lnors_spacearr,
- short (*clnors_data)[2],
- const int cd_loop_clnors_offset,
- const bool do_rebuild);
-
-bool BM_loop_check_cyclic_smooth_fan(BMLoop *l_curr);
-void BM_lnorspacearr_store(BMesh *bm, float (*r_lnors)[3]);
-void BM_lnorspace_invalidate(BMesh *bm, const bool do_invalidate_all);
-void BM_lnorspace_rebuild(BMesh *bm, bool preserve_clnor);
-void BM_lnorspace_update(BMesh *bm);
-void BM_normals_loops_edges_tag(BMesh *bm, const bool do_edges);
-#ifndef NDEBUG
-void BM_lnorspace_err(BMesh *bm);
-#endif
-
-/* Loop Generics */
-struct BMLoopNorEditDataArray *BM_loop_normal_editdata_array_init(BMesh *bm,
- const bool do_all_loops_of_vert);
-void BM_loop_normal_editdata_array_free(struct BMLoopNorEditDataArray *lnors_ed_arr);
-
-bool BM_custom_loop_normals_to_vector_layer(struct BMesh *bm);
-void BM_custom_loop_normals_from_vector_layer(struct BMesh *bm, bool add_sharp_edges);
-
-void BM_edges_sharp_from_angle_set(BMesh *bm, const float split_angle);
-
void bmesh_edit_begin(BMesh *bm, const BMOpTypeFlag type_flag);
void bmesh_edit_end(BMesh *bm, const BMOpTypeFlag type_flag);
diff --git a/source/blender/bmesh/intern/bmesh_mesh_normals.c b/source/blender/bmesh/intern/bmesh_mesh_normals.c
new file mode 100644
index 00000000000..a3eae6dabe8
--- /dev/null
+++ b/source/blender/bmesh/intern/bmesh_mesh_normals.c
@@ -0,0 +1,1859 @@
+/*
+ * 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.
+ */
+
+/** \file
+ * \ingroup bmesh
+ *
+ * BM mesh normal calculation functions.
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_scene_types.h"
+
+#include "BLI_bitmap.h"
+#include "BLI_linklist_stack.h"
+#include "BLI_math.h"
+#include "BLI_stack.h"
+#include "BLI_task.h"
+#include "BLI_utildefines.h"
+
+#include "BKE_editmesh.h"
+#include "BKE_global.h"
+#include "BKE_mesh.h"
+
+#include "intern/bmesh_private.h"
+
+/* -------------------------------------------------------------------- */
+/** \name Update Vertex & Face Normals
+ * \{ */
+
+/**
+ * Helpers for #BM_mesh_normals_update and #BM_verts_calc_normal_vcos
+ */
+
+/* We use that existing internal API flag,
+ * assuming no other tool using it would run concurrently to clnors editing. */
+#define BM_LNORSPACE_UPDATE _FLAG_MF
+
+typedef struct BMEdgesCalcVectorsData {
+ /* Read-only data. */
+ const float (*vcos)[3];
+
+ /* Read-write data, but no need to protect it, no concurrency to fear here. */
+ float (*edgevec)[3];
+} BMEdgesCalcVectorsData;
+
+static void bm_edge_calc_vectors_cb(void *userdata,
+ MempoolIterData *mp_e,
+ const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ BMEdge *e = (BMEdge *)mp_e;
+ /* The edge vector will not be needed when the edge has no radial. */
+ if (e->l != NULL) {
+ float(*edgevec)[3] = userdata;
+ float *e_diff = edgevec[BM_elem_index_get(e)];
+ sub_v3_v3v3(e_diff, e->v2->co, e->v1->co);
+ normalize_v3(e_diff);
+ }
+}
+
+static void bm_edge_calc_vectors_with_coords_cb(void *userdata,
+ MempoolIterData *mp_e,
+ const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ BMEdge *e = (BMEdge *)mp_e;
+ /* The edge vector will not be needed when the edge has no radial. */
+ if (e->l != NULL) {
+ BMEdgesCalcVectorsData *data = userdata;
+ float *e_diff = data->edgevec[BM_elem_index_get(e)];
+ sub_v3_v3v3(
+ e_diff, data->vcos[BM_elem_index_get(e->v2)], data->vcos[BM_elem_index_get(e->v1)]);
+ normalize_v3(e_diff);
+ }
+}
+
+static void bm_mesh_edges_calc_vectors(BMesh *bm, float (*edgevec)[3], const float (*vcos)[3])
+{
+ BM_mesh_elem_index_ensure(bm, BM_EDGE | (vcos ? BM_VERT : 0));
+
+ TaskParallelSettings settings;
+ BLI_parallel_mempool_settings_defaults(&settings);
+ settings.use_threading = bm->totedge >= BM_OMP_LIMIT;
+
+ if (vcos == NULL) {
+ BM_iter_parallel(bm, BM_EDGES_OF_MESH, bm_edge_calc_vectors_cb, edgevec, &settings);
+ }
+ else {
+ BMEdgesCalcVectorsData data = {
+ .edgevec = edgevec,
+ .vcos = vcos,
+ };
+ BM_iter_parallel(bm, BM_EDGES_OF_MESH, bm_edge_calc_vectors_with_coords_cb, &data, &settings);
+ }
+}
+
+typedef struct BMVertsCalcNormalsWithCoordsData {
+ /* Read-only data. */
+ const float (*fnos)[3];
+ const float (*edgevec)[3];
+ const float (*vcos)[3];
+
+ /* Write data. */
+ float (*vnos)[3];
+} BMVertsCalcNormalsWithCoordsData;
+
+BLI_INLINE void bm_vert_calc_normals_accum_loop(const BMLoop *l_iter,
+ const float (*edgevec)[3],
+ const float f_no[3],
+ float v_no[3])
+{
+ /* Calculate the dot product of the two edges that meet at the loop's vertex. */
+ const float *e1diff = edgevec[BM_elem_index_get(l_iter->prev->e)];
+ const float *e2diff = edgevec[BM_elem_index_get(l_iter->e)];
+ /* Edge vectors are calculated from e->v1 to e->v2, so adjust the dot product if one but not
+ * both loops actually runs from from e->v2 to e->v1. */
+ float dotprod = dot_v3v3(e1diff, e2diff);
+ if ((l_iter->prev->e->v1 == l_iter->prev->v) ^ (l_iter->e->v1 == l_iter->v)) {
+ dotprod = -dotprod;
+ }
+ const float fac = saacos(-dotprod);
+ /* NAN detection, otherwise this is a degenerated case, ignore that vertex in this case. */
+ if (fac == fac) { /* NAN detection. */
+ madd_v3_v3fl(v_no, f_no, fac);
+ }
+}
+
+static void bm_vert_calc_normals_impl(const float (*edgevec)[3], BMVert *v)
+{
+ float *v_no = v->no;
+ zero_v3(v_no);
+ BMEdge *e_first = v->e;
+ if (e_first != NULL) {
+ BMEdge *e_iter = e_first;
+ do {
+ BMLoop *l_first = e_iter->l;
+ if (l_first != NULL) {
+ BMLoop *l_iter = l_first;
+ do {
+ if (l_iter->v == v) {
+ bm_vert_calc_normals_accum_loop(l_iter, edgevec, l_iter->f->no, v_no);
+ }
+ } while ((l_iter = l_iter->radial_next) != l_first);
+ }
+ } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v)) != e_first);
+
+ if (LIKELY(normalize_v3(v_no) != 0.0f)) {
+ return;
+ }
+ }
+ /* Fallback normal. */
+ normalize_v3_v3(v_no, v->co);
+}
+
+static void bm_vert_calc_normals_cb(void *userdata,
+ MempoolIterData *mp_v,
+ const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ const float(*edgevec)[3] = userdata;
+ BMVert *v = (BMVert *)mp_v;
+ bm_vert_calc_normals_impl(edgevec, v);
+}
+
+static void bm_vert_calc_normals_with_coords(BMVert *v, BMVertsCalcNormalsWithCoordsData *data)
+{
+ float *v_no = data->vnos[BM_elem_index_get(v)];
+ zero_v3(v_no);
+
+ /* Loop over edges. */
+ BMEdge *e_first = v->e;
+ if (e_first != NULL) {
+ BMEdge *e_iter = e_first;
+ do {
+ BMLoop *l_first = e_iter->l;
+ if (l_first != NULL) {
+ BMLoop *l_iter = l_first;
+ do {
+ if (l_iter->v == v) {
+ bm_vert_calc_normals_accum_loop(
+ l_iter, data->edgevec, data->fnos[BM_elem_index_get(l_iter->f)], v_no);
+ }
+ } while ((l_iter = l_iter->radial_next) != l_first);
+ }
+ } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v)) != e_first);
+
+ if (LIKELY(normalize_v3(v_no) != 0.0f)) {
+ return;
+ }
+ }
+ /* Fallback normal. */
+ normalize_v3_v3(v_no, data->vcos[BM_elem_index_get(v)]);
+}
+
+static void bm_vert_calc_normals_with_coords_cb(void *userdata,
+ MempoolIterData *mp_v,
+ const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ BMVertsCalcNormalsWithCoordsData *data = userdata;
+ BMVert *v = (BMVert *)mp_v;
+ bm_vert_calc_normals_with_coords(v, data);
+}
+
+static void bm_mesh_verts_calc_normals(BMesh *bm,
+ const float (*edgevec)[3],
+ const float (*fnos)[3],
+ const float (*vcos)[3],
+ float (*vnos)[3])
+{
+ BM_mesh_elem_index_ensure(bm, (BM_EDGE | BM_FACE) | ((vnos || vcos) ? BM_VERT : 0));
+
+ TaskParallelSettings settings;
+ BLI_parallel_mempool_settings_defaults(&settings);
+ settings.use_threading = bm->totvert >= BM_OMP_LIMIT;
+
+ if (vcos == NULL) {
+ BM_iter_parallel(bm, BM_VERTS_OF_MESH, bm_vert_calc_normals_cb, (void *)edgevec, &settings);
+ }
+ else {
+ BLI_assert(!ELEM(NULL, fnos, vnos));
+ BMVertsCalcNormalsWithCoordsData data = {
+ .edgevec = edgevec,
+ .fnos = fnos,
+ .vcos = vcos,
+ .vnos = vnos,
+ };
+ BM_iter_parallel(bm, BM_VERTS_OF_MESH, bm_vert_calc_normals_with_coords_cb, &data, &settings);
+ }
+}
+
+static void bm_face_calc_normals_cb(void *UNUSED(userdata),
+ MempoolIterData *mp_f,
+ const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ BMFace *f = (BMFace *)mp_f;
+
+ BM_face_calc_normal(f, f->no);
+}
+
+/**
+ * \brief BMesh Compute Normals
+ *
+ * Updates the normals of a mesh.
+ */
+void BM_mesh_normals_update(BMesh *bm)
+{
+ float(*edgevec)[3] = MEM_mallocN(sizeof(*edgevec) * bm->totedge, __func__);
+
+ /* Parallel mempool iteration does not allow generating indices inline anymore. */
+ BM_mesh_elem_index_ensure(bm, (BM_EDGE | BM_FACE));
+
+ /* Calculate all face normals. */
+ TaskParallelSettings settings;
+ BLI_parallel_mempool_settings_defaults(&settings);
+ settings.use_threading = bm->totedge >= BM_OMP_LIMIT;
+
+ BM_iter_parallel(bm, BM_FACES_OF_MESH, bm_face_calc_normals_cb, NULL, &settings);
+
+ bm_mesh_edges_calc_vectors(bm, edgevec, NULL);
+
+ /* Add weighted face normals to vertices, and normalize vert normals. */
+ bm_mesh_verts_calc_normals(bm, edgevec, NULL, NULL, NULL);
+ MEM_freeN(edgevec);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Update Vertex & Face Normals (Partial Updates)
+ * \{ */
+
+static void bm_partial_faces_parallel_range_calc_normals_cb(
+ void *userdata, const int iter, const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ BMFace *f = ((BMFace **)userdata)[iter];
+ BM_face_calc_normal(f, f->no);
+}
+
+static void bm_partial_edges_parallel_range_calc_vectors_cb(
+ void *userdata, const int iter, const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ BMEdge *e = ((BMEdge **)((void **)userdata)[0])[iter];
+ float *r_edgevec = ((float(*)[3])((void **)userdata)[1])[iter];
+ sub_v3_v3v3(r_edgevec, e->v1->co, e->v2->co);
+ normalize_v3(r_edgevec);
+}
+
+static void bm_partial_verts_parallel_range_calc_normal_cb(
+ void *userdata, const int iter, const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ BMVert *v = ((BMVert **)((void **)userdata)[0])[iter];
+ const float(*edgevec)[3] = (const float(*)[3])((void **)userdata)[1];
+ bm_vert_calc_normals_impl(edgevec, v);
+}
+
+/**
+ * A version of #BM_mesh_normals_update that updates a subset of geometry,
+ * used to avoid the overhead of updating everything.
+ */
+void BM_mesh_normals_update_with_partial(BMesh *bm, const BMPartialUpdate *bmpinfo)
+{
+ BLI_assert(bmpinfo->params.do_normals);
+
+ BMVert **verts = bmpinfo->verts;
+ BMEdge **edges = bmpinfo->edges;
+ BMFace **faces = bmpinfo->faces;
+ const int verts_len = bmpinfo->verts_len;
+ const int edges_len = bmpinfo->edges_len;
+ const int faces_len = bmpinfo->faces_len;
+
+ float(*edgevec)[3] = MEM_mallocN(sizeof(*edgevec) * edges_len, __func__);
+
+ TaskParallelSettings settings;
+ BLI_parallel_range_settings_defaults(&settings);
+
+ /* Faces. */
+ BLI_task_parallel_range(
+ 0, faces_len, faces, bm_partial_faces_parallel_range_calc_normals_cb, &settings);
+
+ /* Temporarily override the edge indices,
+ * storing the correct indices in the case they're not dirty.
+ *
+ * \note in most cases indices are modified and #BMesh.elem_index_dirty is set.
+ * This is an exceptional case where indices are restored because the worst case downside
+ * of marking the edge indices dirty would require a full loop over all edges to
+ * correct the indices in other functions which need them to be valid.
+ * When moving a few vertices on a high poly mesh setting and restoring connected
+ * edges has very little overhead compared with restoring all edge indices. */
+ int *edge_index_value = NULL;
+ if ((bm->elem_index_dirty & BM_EDGE) == 0) {
+ edge_index_value = MEM_mallocN(sizeof(*edge_index_value) * edges_len, __func__);
+
+ for (int i = 0; i < edges_len; i++) {
+ BMEdge *e = edges[i];
+ edge_index_value[i] = BM_elem_index_get(e);
+ BM_elem_index_set(e, i); /* set_dirty! (restore before this function exits). */
+ }
+ }
+ else {
+ for (int i = 0; i < edges_len; i++) {
+ BMEdge *e = edges[i];
+ BM_elem_index_set(e, i); /* set_dirty! (already dirty) */
+ }
+ }
+
+ {
+ /* Verts. */
+
+ /* Compute normalized direction vectors for each edge.
+ * Directions will be used for calculating the weights of the face normals on the vertex
+ * normals. */
+ void *data[2] = {edges, edgevec};
+ BLI_task_parallel_range(
+ 0, edges_len, data, bm_partial_edges_parallel_range_calc_vectors_cb, &settings);
+
+ /* Calculate vertex normals. */
+ data[0] = verts;
+ BLI_task_parallel_range(
+ 0, verts_len, data, bm_partial_verts_parallel_range_calc_normal_cb, &settings);
+ }
+
+ if (edge_index_value != NULL) {
+ for (int i = 0; i < edges_len; i++) {
+ BMEdge *e = edges[i];
+ BM_elem_index_set(e, edge_index_value[i]); /* set_ok (restore) */
+ }
+
+ MEM_freeN(edge_index_value);
+ }
+
+ MEM_freeN(edgevec);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Update Vertex & Face Normals (Custom Coords)
+ * \{ */
+
+/**
+ * \brief BMesh Compute Normals from/to external data.
+ *
+ * Computes the vertex normals of a mesh into vnos,
+ * using given vertex coordinates (vcos) and polygon normals (fnos).
+ */
+void BM_verts_calc_normal_vcos(BMesh *bm,
+ const float (*fnos)[3],
+ const float (*vcos)[3],
+ float (*vnos)[3])
+{
+ float(*edgevec)[3] = MEM_mallocN(sizeof(*edgevec) * bm->totedge, __func__);
+
+ /* Compute normalized direction vectors for each edge.
+ * Directions will be used for calculating the weights of the face normals on the vertex normals.
+ */
+ bm_mesh_edges_calc_vectors(bm, edgevec, vcos);
+
+ /* Add weighted face normals to vertices, and normalize vert normals. */
+ bm_mesh_verts_calc_normals(bm, edgevec, fnos, vcos, vnos);
+ MEM_freeN(edgevec);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Tagging Utility Functions
+ * \{ */
+
+void BM_normals_loops_edges_tag(BMesh *bm, const bool do_edges)
+{
+ BMFace *f;
+ BMEdge *e;
+ BMIter fiter, eiter;
+ BMLoop *l_curr, *l_first;
+
+ if (do_edges) {
+ int index_edge;
+ BM_ITER_MESH_INDEX (e, &eiter, bm, BM_EDGES_OF_MESH, index_edge) {
+ BMLoop *l_a, *l_b;
+
+ BM_elem_index_set(e, index_edge); /* set_inline */
+ BM_elem_flag_disable(e, BM_ELEM_TAG);
+ if (BM_edge_loop_pair(e, &l_a, &l_b)) {
+ if (BM_elem_flag_test(e, BM_ELEM_SMOOTH) && l_a->v != l_b->v) {
+ BM_elem_flag_enable(e, BM_ELEM_TAG);
+ }
+ }
+ }
+ bm->elem_index_dirty &= ~BM_EDGE;
+ }
+
+ int index_face, index_loop = 0;
+ BM_ITER_MESH_INDEX (f, &fiter, bm, BM_FACES_OF_MESH, index_face) {
+ BM_elem_index_set(f, index_face); /* set_inline */
+ l_curr = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ BM_elem_index_set(l_curr, index_loop++); /* set_inline */
+ BM_elem_flag_disable(l_curr, BM_ELEM_TAG);
+ } while ((l_curr = l_curr->next) != l_first);
+ }
+ bm->elem_index_dirty &= ~(BM_FACE | BM_LOOP);
+}
+
+/**
+ * Helpers for #BM_mesh_loop_normals_update and #BM_loops_calc_normal_vcos
+ */
+static void bm_mesh_edges_sharp_tag(BMesh *bm,
+ const float (*vnos)[3],
+ const float (*fnos)[3],
+ float (*r_lnos)[3],
+ const float split_angle,
+ const bool do_sharp_edges_tag)
+{
+ BMIter eiter;
+ BMEdge *e;
+ int i;
+
+ const bool check_angle = (split_angle < (float)M_PI);
+ const float split_angle_cos = check_angle ? cosf(split_angle) : -1.0f;
+
+ {
+ char htype = BM_VERT | BM_LOOP;
+ if (fnos) {
+ htype |= BM_FACE;
+ }
+ BM_mesh_elem_index_ensure(bm, htype);
+ }
+
+ /* This first loop checks which edges are actually smooth,
+ * and pre-populate lnos with vnos (as if they were all smooth). */
+ BM_ITER_MESH_INDEX (e, &eiter, bm, BM_EDGES_OF_MESH, i) {
+ BMLoop *l_a, *l_b;
+
+ BM_elem_index_set(e, i); /* set_inline */
+ BM_elem_flag_disable(e, BM_ELEM_TAG); /* Clear tag (means edge is sharp). */
+
+ /* An edge with only two loops, might be smooth... */
+ if (BM_edge_loop_pair(e, &l_a, &l_b)) {
+ bool is_angle_smooth = true;
+ if (check_angle) {
+ const float *no_a = fnos ? fnos[BM_elem_index_get(l_a->f)] : l_a->f->no;
+ const float *no_b = fnos ? fnos[BM_elem_index_get(l_b->f)] : l_b->f->no;
+ is_angle_smooth = (dot_v3v3(no_a, no_b) >= split_angle_cos);
+ }
+
+ /* We only tag edges that are *really* smooth:
+ * If the angle between both its polys' normals is below split_angle value,
+ * and it is tagged as such,
+ * and both its faces are smooth,
+ * and both its faces have compatible (non-flipped) normals,
+ * i.e. both loops on the same edge do not share the same vertex.
+ */
+ if (BM_elem_flag_test(e, BM_ELEM_SMOOTH) && BM_elem_flag_test(l_a->f, BM_ELEM_SMOOTH) &&
+ BM_elem_flag_test(l_b->f, BM_ELEM_SMOOTH) && l_a->v != l_b->v) {
+ if (is_angle_smooth) {
+ const float *no;
+ BM_elem_flag_enable(e, BM_ELEM_TAG);
+
+ /* linked vertices might be fully smooth, copy their normals to loop ones. */
+ if (r_lnos) {
+ no = vnos ? vnos[BM_elem_index_get(l_a->v)] : l_a->v->no;
+ copy_v3_v3(r_lnos[BM_elem_index_get(l_a)], no);
+ no = vnos ? vnos[BM_elem_index_get(l_b->v)] : l_b->v->no;
+ copy_v3_v3(r_lnos[BM_elem_index_get(l_b)], no);
+ }
+ }
+ else if (do_sharp_edges_tag) {
+ /* Note that we do not care about the other sharp-edge cases
+ * (sharp poly, non-manifold edge, etc.),
+ * only tag edge as sharp when it is due to angle threshold. */
+ BM_elem_flag_disable(e, BM_ELEM_SMOOTH);
+ }
+ }
+ }
+ }
+
+ bm->elem_index_dirty &= ~BM_EDGE;
+}
+
+/**
+ * Define sharp edges as needed to mimic 'autosmooth' from angle threshold.
+ *
+ * Used when defining an empty custom loop normals data layer,
+ * to keep same shading as with auto-smooth!
+ */
+void BM_edges_sharp_from_angle_set(BMesh *bm, const float split_angle)
+{
+ if (split_angle >= (float)M_PI) {
+ /* Nothing to do! */
+ return;
+ }
+
+ bm_mesh_edges_sharp_tag(bm, NULL, NULL, NULL, split_angle, true);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Loop Normals Calculation API
+ * \{ */
+
+/**
+ * Check whether given loop is part of an unknown-so-far cyclic smooth fan, or not.
+ * Needed because cyclic smooth fans have no obvious 'entry point',
+ * and yet we need to walk them once, and only once.
+ */
+bool BM_loop_check_cyclic_smooth_fan(BMLoop *l_curr)
+{
+ BMLoop *lfan_pivot_next = l_curr;
+ BMEdge *e_next = l_curr->e;
+
+ BLI_assert(!BM_elem_flag_test(lfan_pivot_next, BM_ELEM_TAG));
+ BM_elem_flag_enable(lfan_pivot_next, BM_ELEM_TAG);
+
+ while (true) {
+ /* Much simpler than in sibling code with basic Mesh data! */
+ lfan_pivot_next = BM_vert_step_fan_loop(lfan_pivot_next, &e_next);
+
+ if (!lfan_pivot_next || !BM_elem_flag_test(e_next, BM_ELEM_TAG)) {
+ /* Sharp loop/edge, so not a cyclic smooth fan... */
+ return false;
+ }
+ /* Smooth loop/edge... */
+ if (BM_elem_flag_test(lfan_pivot_next, BM_ELEM_TAG)) {
+ if (lfan_pivot_next == l_curr) {
+ /* We walked around a whole cyclic smooth fan
+ * without finding any already-processed loop,
+ * means we can use initial l_curr/l_prev edge as start for this smooth fan. */
+ return true;
+ }
+ /* ... already checked in some previous looping, we can abort. */
+ return false;
+ }
+ /* ... we can skip it in future, and keep checking the smooth fan. */
+ BM_elem_flag_enable(lfan_pivot_next, BM_ELEM_TAG);
+ }
+}
+
+/**
+ * BMesh version of BKE_mesh_normals_loop_split() in mesh_evaluate.c
+ * Will use first clnors_data array, and fallback to cd_loop_clnors_offset
+ * (use NULL and -1 to not use clnors).
+ *
+ * \note This sets #BM_ELEM_TAG which is used in tool code (e.g. T84426).
+ * we could add a low-level API flag for this, see #BM_ELEM_API_FLAG_ENABLE and friends.
+ */
+static void bm_mesh_loops_calc_normals(BMesh *bm,
+ const float (*vcos)[3],
+ const float (*fnos)[3],
+ float (*r_lnos)[3],
+ MLoopNorSpaceArray *r_lnors_spacearr,
+ const short (*clnors_data)[2],
+ const int cd_loop_clnors_offset,
+ const bool do_rebuild)
+{
+ BMIter fiter;
+ BMFace *f_curr;
+ const bool has_clnors = clnors_data || (cd_loop_clnors_offset != -1);
+
+ MLoopNorSpaceArray _lnors_spacearr = {NULL};
+
+ /* Temp normal stack. */
+ BLI_SMALLSTACK_DECLARE(normal, float *);
+ /* Temp clnors stack. */
+ BLI_SMALLSTACK_DECLARE(clnors, short *);
+ /* Temp edge vectors stack, only used when computing lnor spacearr. */
+ BLI_Stack *edge_vectors = NULL;
+
+ {
+ char htype = 0;
+ if (vcos) {
+ htype |= BM_VERT;
+ }
+ /* Face/Loop indices are set inline below. */
+ BM_mesh_elem_index_ensure(bm, htype);
+ }
+
+ if (!r_lnors_spacearr && has_clnors) {
+ /* We need to compute lnor spacearr if some custom lnor data are given to us! */
+ r_lnors_spacearr = &_lnors_spacearr;
+ }
+ if (r_lnors_spacearr) {
+ BKE_lnor_spacearr_init(r_lnors_spacearr, bm->totloop, MLNOR_SPACEARR_BMLOOP_PTR);
+ edge_vectors = BLI_stack_new(sizeof(float[3]), __func__);
+ }
+
+ /* Clear all loops' tags (means none are to be skipped for now). */
+ int index_face, index_loop = 0;
+ BM_ITER_MESH_INDEX (f_curr, &fiter, bm, BM_FACES_OF_MESH, index_face) {
+ BMLoop *l_curr, *l_first;
+
+ BM_elem_index_set(f_curr, index_face); /* set_inline */
+
+ l_curr = l_first = BM_FACE_FIRST_LOOP(f_curr);
+ do {
+ BM_elem_index_set(l_curr, index_loop++); /* set_inline */
+ BM_elem_flag_disable(l_curr, BM_ELEM_TAG);
+ } while ((l_curr = l_curr->next) != l_first);
+ }
+ bm->elem_index_dirty &= ~(BM_FACE | BM_LOOP);
+
+ /* We now know edges that can be smoothed (they are tagged),
+ * and edges that will be hard (they aren't).
+ * Now, time to generate the normals.
+ */
+ BM_ITER_MESH (f_curr, &fiter, bm, BM_FACES_OF_MESH) {
+ BMLoop *l_curr, *l_first;
+
+ l_curr = l_first = BM_FACE_FIRST_LOOP(f_curr);
+ do {
+ if (do_rebuild && !BM_ELEM_API_FLAG_TEST(l_curr, BM_LNORSPACE_UPDATE) &&
+ !(bm->spacearr_dirty & BM_SPACEARR_DIRTY_ALL)) {
+ continue;
+ }
+ /* A smooth edge, we have to check for cyclic smooth fan case.
+ * If we find a new, never-processed cyclic smooth fan, we can do it now using that loop/edge
+ * as 'entry point', otherwise we can skip it. */
+
+ /* Note: In theory, we could make bm_mesh_loop_check_cyclic_smooth_fan() store
+ * mlfan_pivot's in a stack, to avoid having to fan again around
+ * the vert during actual computation of clnor & clnorspace. However, this would complicate
+ * the code, add more memory usage, and
+ * BM_vert_step_fan_loop() is quite cheap in term of CPU cycles,
+ * so really think it's not worth it. */
+ if (BM_elem_flag_test(l_curr->e, BM_ELEM_TAG) &&
+ (BM_elem_flag_test(l_curr, BM_ELEM_TAG) || !BM_loop_check_cyclic_smooth_fan(l_curr))) {
+ }
+ else if (!BM_elem_flag_test(l_curr->e, BM_ELEM_TAG) &&
+ !BM_elem_flag_test(l_curr->prev->e, BM_ELEM_TAG)) {
+ /* Simple case (both edges around that vertex are sharp in related polygon),
+ * this vertex just takes its poly normal.
+ */
+ const int l_curr_index = BM_elem_index_get(l_curr);
+ const float *no = fnos ? fnos[BM_elem_index_get(f_curr)] : f_curr->no;
+ copy_v3_v3(r_lnos[l_curr_index], no);
+
+ /* If needed, generate this (simple!) lnor space. */
+ if (r_lnors_spacearr) {
+ float vec_curr[3], vec_prev[3];
+ MLoopNorSpace *lnor_space = BKE_lnor_space_create(r_lnors_spacearr);
+
+ {
+ const BMVert *v_pivot = l_curr->v;
+ const float *co_pivot = vcos ? vcos[BM_elem_index_get(v_pivot)] : v_pivot->co;
+ const BMVert *v_1 = BM_edge_other_vert(l_curr->e, v_pivot);
+ const float *co_1 = vcos ? vcos[BM_elem_index_get(v_1)] : v_1->co;
+ const BMVert *v_2 = BM_edge_other_vert(l_curr->prev->e, v_pivot);
+ const float *co_2 = vcos ? vcos[BM_elem_index_get(v_2)] : v_2->co;
+
+ sub_v3_v3v3(vec_curr, co_1, co_pivot);
+ normalize_v3(vec_curr);
+ sub_v3_v3v3(vec_prev, co_2, co_pivot);
+ normalize_v3(vec_prev);
+ }
+
+ BKE_lnor_space_define(lnor_space, r_lnos[l_curr_index], vec_curr, vec_prev, NULL);
+ /* We know there is only one loop in this space,
+ * no need to create a linklist in this case... */
+ BKE_lnor_space_add_loop(r_lnors_spacearr, lnor_space, l_curr_index, l_curr, true);
+
+ if (has_clnors) {
+ const short(*clnor)[2] = clnors_data ? &clnors_data[l_curr_index] :
+ (const void *)BM_ELEM_CD_GET_VOID_P(
+ l_curr, cd_loop_clnors_offset);
+ BKE_lnor_space_custom_data_to_normal(lnor_space, *clnor, r_lnos[l_curr_index]);
+ }
+ }
+ }
+ /* We *do not need* to check/tag loops as already computed!
+ * Due to the fact a loop only links to one of its two edges,
+ * a same fan *will never be walked more than once!*
+ * Since we consider edges having neighbor faces with inverted (flipped) normals as sharp,
+ * we are sure that no fan will be skipped, even only considering the case
+ * (sharp curr_edge, smooth prev_edge), and not the alternative
+ * (smooth curr_edge, sharp prev_edge).
+ * All this due/thanks to link between normals and loop ordering.
+ */
+ else {
+ /* We have to fan around current vertex, until we find the other non-smooth edge,
+ * and accumulate face normals into the vertex!
+ * Note in case this vertex has only one sharp edge,
+ * this is a waste because the normal is the same as the vertex normal,
+ * but I do not see any easy way to detect that (would need to count number of sharp edges
+ * per vertex, I doubt the additional memory usage would be worth it, especially as it
+ * should not be a common case in real-life meshes anyway).
+ */
+ BMVert *v_pivot = l_curr->v;
+ BMEdge *e_next;
+ const BMEdge *e_org = l_curr->e;
+ BMLoop *lfan_pivot, *lfan_pivot_next;
+ int lfan_pivot_index;
+ float lnor[3] = {0.0f, 0.0f, 0.0f};
+ float vec_curr[3], vec_next[3], vec_org[3];
+
+ /* We validate clnors data on the fly - cheapest way to do! */
+ int clnors_avg[2] = {0, 0};
+ const short(*clnor_ref)[2] = NULL;
+ int clnors_nbr = 0;
+ bool clnors_invalid = false;
+
+ const float *co_pivot = vcos ? vcos[BM_elem_index_get(v_pivot)] : v_pivot->co;
+
+ MLoopNorSpace *lnor_space = r_lnors_spacearr ? BKE_lnor_space_create(r_lnors_spacearr) :
+ NULL;
+
+ BLI_assert((edge_vectors == NULL) || BLI_stack_is_empty(edge_vectors));
+
+ lfan_pivot = l_curr;
+ lfan_pivot_index = BM_elem_index_get(lfan_pivot);
+ e_next = lfan_pivot->e; /* Current edge here, actually! */
+
+ /* Only need to compute previous edge's vector once,
+ * then we can just reuse old current one! */
+ {
+ const BMVert *v_2 = BM_edge_other_vert(e_next, v_pivot);
+ const float *co_2 = vcos ? vcos[BM_elem_index_get(v_2)] : v_2->co;
+
+ sub_v3_v3v3(vec_org, co_2, co_pivot);
+ normalize_v3(vec_org);
+ copy_v3_v3(vec_curr, vec_org);
+
+ if (r_lnors_spacearr) {
+ BLI_stack_push(edge_vectors, vec_org);
+ }
+ }
+
+ while (true) {
+ /* Much simpler than in sibling code with basic Mesh data! */
+ lfan_pivot_next = BM_vert_step_fan_loop(lfan_pivot, &e_next);
+ if (lfan_pivot_next) {
+ BLI_assert(lfan_pivot_next->v == v_pivot);
+ }
+ else {
+ /* next edge is non-manifold, we have to find it ourselves! */
+ e_next = (lfan_pivot->e == e_next) ? lfan_pivot->prev->e : lfan_pivot->e;
+ }
+
+ /* Compute edge vector.
+ * NOTE: We could pre-compute those into an array, in the first iteration,
+ * instead of computing them twice (or more) here.
+ * However, time gained is not worth memory and time lost,
+ * given the fact that this code should not be called that much in real-life meshes.
+ */
+ {
+ const BMVert *v_2 = BM_edge_other_vert(e_next, v_pivot);
+ const float *co_2 = vcos ? vcos[BM_elem_index_get(v_2)] : v_2->co;
+
+ sub_v3_v3v3(vec_next, co_2, co_pivot);
+ normalize_v3(vec_next);
+ }
+
+ {
+ /* Code similar to accumulate_vertex_normals_poly_v3. */
+ /* Calculate angle between the two poly edges incident on this vertex. */
+ const BMFace *f = lfan_pivot->f;
+ const float fac = saacos(dot_v3v3(vec_next, vec_curr));
+ const float *no = fnos ? fnos[BM_elem_index_get(f)] : f->no;
+ /* Accumulate */
+ madd_v3_v3fl(lnor, no, fac);
+
+ if (has_clnors) {
+ /* Accumulate all clnors, if they are not all equal we have to fix that! */
+ const short(*clnor)[2] = clnors_data ? &clnors_data[lfan_pivot_index] :
+ (const void *)BM_ELEM_CD_GET_VOID_P(
+ lfan_pivot, cd_loop_clnors_offset);
+ if (clnors_nbr) {
+ clnors_invalid |= ((*clnor_ref)[0] != (*clnor)[0] ||
+ (*clnor_ref)[1] != (*clnor)[1]);
+ }
+ else {
+ clnor_ref = clnor;
+ }
+ clnors_avg[0] += (*clnor)[0];
+ clnors_avg[1] += (*clnor)[1];
+ clnors_nbr++;
+ /* We store here a pointer to all custom lnors processed. */
+ BLI_SMALLSTACK_PUSH(clnors, (short *)*clnor);
+ }
+ }
+
+ /* We store here a pointer to all loop-normals processed. */
+ BLI_SMALLSTACK_PUSH(normal, (float *)r_lnos[lfan_pivot_index]);
+
+ if (r_lnors_spacearr) {
+ /* Assign current lnor space to current 'vertex' loop. */
+ BKE_lnor_space_add_loop(
+ r_lnors_spacearr, lnor_space, lfan_pivot_index, lfan_pivot, false);
+ if (e_next != e_org) {
+ /* We store here all edges-normalized vectors processed. */
+ BLI_stack_push(edge_vectors, vec_next);
+ }
+ }
+
+ if (!BM_elem_flag_test(e_next, BM_ELEM_TAG) || (e_next == e_org)) {
+ /* Next edge is sharp, we have finished with this fan of faces around this vert! */
+ break;
+ }
+
+ /* Copy next edge vector to current one. */
+ copy_v3_v3(vec_curr, vec_next);
+ /* Next pivot loop to current one. */
+ lfan_pivot = lfan_pivot_next;
+ lfan_pivot_index = BM_elem_index_get(lfan_pivot);
+ }
+
+ {
+ float lnor_len = normalize_v3(lnor);
+
+ /* If we are generating lnor spacearr, we can now define the one for this fan. */
+ if (r_lnors_spacearr) {
+ if (UNLIKELY(lnor_len == 0.0f)) {
+ /* Use vertex normal as fallback! */
+ copy_v3_v3(lnor, r_lnos[lfan_pivot_index]);
+ lnor_len = 1.0f;
+ }
+
+ BKE_lnor_space_define(lnor_space, lnor, vec_org, vec_next, edge_vectors);
+
+ if (has_clnors) {
+ if (clnors_invalid) {
+ short *clnor;
+
+ clnors_avg[0] /= clnors_nbr;
+ clnors_avg[1] /= clnors_nbr;
+ /* Fix/update all clnors of this fan with computed average value. */
+
+ /* Prints continuously when merge custom normals, so commenting. */
+ /* printf("Invalid clnors in this fan!\n"); */
+
+ while ((clnor = BLI_SMALLSTACK_POP(clnors))) {
+ // print_v2("org clnor", clnor);
+ clnor[0] = (short)clnors_avg[0];
+ clnor[1] = (short)clnors_avg[1];
+ }
+ // print_v2("new clnors", clnors_avg);
+ }
+ else {
+ /* We still have to consume the stack! */
+ while (BLI_SMALLSTACK_POP(clnors)) {
+ /* pass */
+ }
+ }
+ BKE_lnor_space_custom_data_to_normal(lnor_space, *clnor_ref, lnor);
+ }
+ }
+
+ /* In case we get a zero normal here, just use vertex normal already set! */
+ if (LIKELY(lnor_len != 0.0f)) {
+ /* Copy back the final computed normal into all related loop-normals. */
+ float *nor;
+
+ while ((nor = BLI_SMALLSTACK_POP(normal))) {
+ copy_v3_v3(nor, lnor);
+ }
+ }
+ else {
+ /* We still have to consume the stack! */
+ while (BLI_SMALLSTACK_POP(normal)) {
+ /* pass */
+ }
+ }
+ }
+
+ /* Tag related vertex as sharp, to avoid fanning around it again
+ * (in case it was a smooth one). */
+ if (r_lnors_spacearr) {
+ BM_elem_flag_enable(l_curr->v, BM_ELEM_TAG);
+ }
+ }
+ } while ((l_curr = l_curr->next) != l_first);
+ }
+
+ if (r_lnors_spacearr) {
+ BLI_stack_free(edge_vectors);
+ if (r_lnors_spacearr == &_lnors_spacearr) {
+ BKE_lnor_spacearr_free(r_lnors_spacearr);
+ }
+ }
+}
+
+/* This threshold is a bit touchy (usual float precision issue), this value seems OK. */
+#define LNOR_SPACE_TRIGO_THRESHOLD (1.0f - 1e-4f)
+
+/**
+ * Check each current smooth fan (one lnor space per smooth fan!), and if all its
+ * matching custom lnors are not (enough) equal, add sharp edges as needed.
+ */
+static bool bm_mesh_loops_split_lnor_fans(BMesh *bm,
+ MLoopNorSpaceArray *lnors_spacearr,
+ const float (*new_lnors)[3])
+{
+ BLI_bitmap *done_loops = BLI_BITMAP_NEW((size_t)bm->totloop, __func__);
+ bool changed = false;
+
+ BLI_assert(lnors_spacearr->data_type == MLNOR_SPACEARR_BMLOOP_PTR);
+
+ for (int i = 0; i < bm->totloop; i++) {
+ if (!lnors_spacearr->lspacearr[i]) {
+ /* This should not happen in theory, but in some rare case (probably ugly geometry)
+ * we can get some NULL loopspacearr at this point. :/
+ * Maybe we should set those loops' edges as sharp?
+ */
+ BLI_BITMAP_ENABLE(done_loops, i);
+ if (G.debug & G_DEBUG) {
+ printf("WARNING! Getting invalid NULL loop space for loop %d!\n", i);
+ }
+ continue;
+ }
+
+ if (!BLI_BITMAP_TEST(done_loops, i)) {
+ /* Notes:
+ * * In case of mono-loop smooth fan, we have nothing to do.
+ * * Loops in this linklist are ordered (in reversed order compared to how they were
+ * discovered by BKE_mesh_normals_loop_split(), but this is not a problem).
+ * Which means if we find a mismatching clnor,
+ * we know all remaining loops will have to be in a new, different smooth fan/lnor space.
+ * * In smooth fan case, we compare each clnor against a ref one,
+ * to avoid small differences adding up into a real big one in the end!
+ */
+ if (lnors_spacearr->lspacearr[i]->flags & MLNOR_SPACE_IS_SINGLE) {
+ BLI_BITMAP_ENABLE(done_loops, i);
+ continue;
+ }
+
+ LinkNode *loops = lnors_spacearr->lspacearr[i]->loops;
+ BMLoop *prev_ml = NULL;
+ const float *org_nor = NULL;
+
+ while (loops) {
+ BMLoop *ml = loops->link;
+ const int lidx = BM_elem_index_get(ml);
+ const float *nor = new_lnors[lidx];
+
+ if (!org_nor) {
+ org_nor = nor;
+ }
+ else if (dot_v3v3(org_nor, nor) < LNOR_SPACE_TRIGO_THRESHOLD) {
+ /* Current normal differs too much from org one, we have to tag the edge between
+ * previous loop's face and current's one as sharp.
+ * We know those two loops do not point to the same edge,
+ * since we do not allow reversed winding in a same smooth fan.
+ */
+ BMEdge *e = (prev_ml->e == ml->prev->e) ? prev_ml->e : ml->e;
+
+ BM_elem_flag_disable(e, BM_ELEM_TAG | BM_ELEM_SMOOTH);
+ changed = true;
+
+ org_nor = nor;
+ }
+
+ prev_ml = ml;
+ loops = loops->next;
+ BLI_BITMAP_ENABLE(done_loops, lidx);
+ }
+
+ /* We also have to check between last and first loops,
+ * otherwise we may miss some sharp edges here!
+ * This is just a simplified version of above while loop.
+ * See T45984. */
+ loops = lnors_spacearr->lspacearr[i]->loops;
+ if (loops && org_nor) {
+ BMLoop *ml = loops->link;
+ const int lidx = BM_elem_index_get(ml);
+ const float *nor = new_lnors[lidx];
+
+ if (dot_v3v3(org_nor, nor) < LNOR_SPACE_TRIGO_THRESHOLD) {
+ BMEdge *e = (prev_ml->e == ml->prev->e) ? prev_ml->e : ml->e;
+
+ BM_elem_flag_disable(e, BM_ELEM_TAG | BM_ELEM_SMOOTH);
+ changed = true;
+ }
+ }
+ }
+ }
+
+ MEM_freeN(done_loops);
+ return changed;
+}
+
+/**
+ * Assign custom normal data from given normal vectors, averaging normals
+ * from one smooth fan as necessary.
+ */
+static void bm_mesh_loops_assign_normal_data(BMesh *bm,
+ MLoopNorSpaceArray *lnors_spacearr,
+ short (*r_clnors_data)[2],
+ const int cd_loop_clnors_offset,
+ const float (*new_lnors)[3])
+{
+ BLI_bitmap *done_loops = BLI_BITMAP_NEW((size_t)bm->totloop, __func__);
+
+ BLI_SMALLSTACK_DECLARE(clnors_data, short *);
+
+ BLI_assert(lnors_spacearr->data_type == MLNOR_SPACEARR_BMLOOP_PTR);
+
+ for (int i = 0; i < bm->totloop; i++) {
+ if (!lnors_spacearr->lspacearr[i]) {
+ BLI_BITMAP_ENABLE(done_loops, i);
+ if (G.debug & G_DEBUG) {
+ printf("WARNING! Still getting invalid NULL loop space in second loop for loop %d!\n", i);
+ }
+ continue;
+ }
+
+ if (!BLI_BITMAP_TEST(done_loops, i)) {
+ /* Note we accumulate and average all custom normals in current smooth fan,
+ * to avoid getting different clnors data (tiny differences in plain custom normals can
+ * give rather huge differences in computed 2D factors).
+ */
+ LinkNode *loops = lnors_spacearr->lspacearr[i]->loops;
+
+ if (lnors_spacearr->lspacearr[i]->flags & MLNOR_SPACE_IS_SINGLE) {
+ BMLoop *ml = (BMLoop *)loops;
+ const int lidx = BM_elem_index_get(ml);
+
+ BLI_assert(lidx == i);
+
+ const float *nor = new_lnors[lidx];
+ short *clnor = r_clnors_data ? &r_clnors_data[lidx] :
+ BM_ELEM_CD_GET_VOID_P(ml, cd_loop_clnors_offset);
+
+ BKE_lnor_space_custom_normal_to_data(lnors_spacearr->lspacearr[i], nor, clnor);
+ BLI_BITMAP_ENABLE(done_loops, i);
+ }
+ else {
+ int nbr_nors = 0;
+ float avg_nor[3];
+ short clnor_data_tmp[2], *clnor_data;
+
+ zero_v3(avg_nor);
+
+ while (loops) {
+ BMLoop *ml = loops->link;
+ const int lidx = BM_elem_index_get(ml);
+ const float *nor = new_lnors[lidx];
+ short *clnor = r_clnors_data ? &r_clnors_data[lidx] :
+ BM_ELEM_CD_GET_VOID_P(ml, cd_loop_clnors_offset);
+
+ nbr_nors++;
+ add_v3_v3(avg_nor, nor);
+ BLI_SMALLSTACK_PUSH(clnors_data, clnor);
+
+ loops = loops->next;
+ BLI_BITMAP_ENABLE(done_loops, lidx);
+ }
+
+ mul_v3_fl(avg_nor, 1.0f / (float)nbr_nors);
+ BKE_lnor_space_custom_normal_to_data(
+ lnors_spacearr->lspacearr[i], avg_nor, clnor_data_tmp);
+
+ while ((clnor_data = BLI_SMALLSTACK_POP(clnors_data))) {
+ clnor_data[0] = clnor_data_tmp[0];
+ clnor_data[1] = clnor_data_tmp[1];
+ }
+ }
+ }
+ }
+
+ MEM_freeN(done_loops);
+}
+
+/**
+ * Compute internal representation of given custom normals (as an array of float[2] or data layer).
+ *
+ * It also makes sure the mesh matches those custom normals, by marking new sharp edges to split
+ * the smooth fans when loop normals for the same vertex are different, or averaging the normals
+ * instead, depending on the do_split_fans parameter.
+ */
+static void bm_mesh_loops_custom_normals_set(BMesh *bm,
+ const float (*vcos)[3],
+ const float (*vnos)[3],
+ const float (*fnos)[3],
+ MLoopNorSpaceArray *r_lnors_spacearr,
+ short (*r_clnors_data)[2],
+ const int cd_loop_clnors_offset,
+ float (*new_lnors)[3],
+ const int cd_new_lnors_offset,
+ bool do_split_fans)
+{
+ BMFace *f;
+ BMLoop *l;
+ BMIter liter, fiter;
+ float(*cur_lnors)[3] = MEM_mallocN(sizeof(*cur_lnors) * bm->totloop, __func__);
+
+ BKE_lnor_spacearr_clear(r_lnors_spacearr);
+
+ /* Tag smooth edges and set lnos from vnos when they might be completely smooth...
+ * When using custom loop normals, disable the angle feature! */
+ bm_mesh_edges_sharp_tag(bm, vnos, fnos, cur_lnors, (float)M_PI, false);
+
+ /* Finish computing lnos by accumulating face normals
+ * in each fan of faces defined by sharp edges. */
+ bm_mesh_loops_calc_normals(
+ bm, vcos, fnos, cur_lnors, r_lnors_spacearr, r_clnors_data, cd_loop_clnors_offset, false);
+
+ /* Extract new normals from the data layer if necessary. */
+ float(*custom_lnors)[3] = new_lnors;
+
+ if (new_lnors == NULL) {
+ custom_lnors = MEM_mallocN(sizeof(*new_lnors) * bm->totloop, __func__);
+
+ BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
+ BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
+ const float *normal = BM_ELEM_CD_GET_VOID_P(l, cd_new_lnors_offset);
+ copy_v3_v3(custom_lnors[BM_elem_index_get(l)], normal);
+ }
+ }
+ }
+
+ /* Validate the new normals. */
+ for (int i = 0; i < bm->totloop; i++) {
+ if (is_zero_v3(custom_lnors[i])) {
+ copy_v3_v3(custom_lnors[i], cur_lnors[i]);
+ }
+ else {
+ normalize_v3(custom_lnors[i]);
+ }
+ }
+
+ /* Now, check each current smooth fan (one lnor space per smooth fan!),
+ * and if all its matching custom lnors are not equal, add sharp edges as needed. */
+ if (do_split_fans && bm_mesh_loops_split_lnor_fans(bm, r_lnors_spacearr, custom_lnors)) {
+ /* If any sharp edges were added, run bm_mesh_loops_calc_normals() again to get lnor
+ * spacearr/smooth fans matching the given custom lnors. */
+ BKE_lnor_spacearr_clear(r_lnors_spacearr);
+
+ bm_mesh_loops_calc_normals(
+ bm, vcos, fnos, cur_lnors, r_lnors_spacearr, r_clnors_data, cd_loop_clnors_offset, false);
+ }
+
+ /* And we just have to convert plain object-space custom normals to our
+ * lnor space-encoded ones. */
+ bm_mesh_loops_assign_normal_data(
+ bm, r_lnors_spacearr, r_clnors_data, cd_loop_clnors_offset, custom_lnors);
+
+ MEM_freeN(cur_lnors);
+
+ if (custom_lnors != new_lnors) {
+ MEM_freeN(custom_lnors);
+ }
+}
+
+static void bm_mesh_loops_calc_normals_no_autosmooth(BMesh *bm,
+ const float (*vnos)[3],
+ const float (*fnos)[3],
+ float (*r_lnos)[3])
+{
+ BMIter fiter;
+ BMFace *f_curr;
+
+ {
+ char htype = BM_LOOP;
+ if (vnos) {
+ htype |= BM_VERT;
+ }
+ if (fnos) {
+ htype |= BM_FACE;
+ }
+ BM_mesh_elem_index_ensure(bm, htype);
+ }
+
+ BM_ITER_MESH (f_curr, &fiter, bm, BM_FACES_OF_MESH) {
+ BMLoop *l_curr, *l_first;
+ const bool is_face_flat = !BM_elem_flag_test(f_curr, BM_ELEM_SMOOTH);
+
+ l_curr = l_first = BM_FACE_FIRST_LOOP(f_curr);
+ do {
+ const float *no = is_face_flat ? (fnos ? fnos[BM_elem_index_get(f_curr)] : f_curr->no) :
+ (vnos ? vnos[BM_elem_index_get(l_curr->v)] : l_curr->v->no);
+ copy_v3_v3(r_lnos[BM_elem_index_get(l_curr)], no);
+
+ } while ((l_curr = l_curr->next) != l_first);
+ }
+}
+
+#if 0 /* Unused currently */
+/**
+ * \brief BMesh Compute Loop Normals
+ *
+ * Updates the loop normals of a mesh.
+ * Assumes vertex and face normals are valid (else call BM_mesh_normals_update() first)!
+ */
+void BM_mesh_loop_normals_update(BMesh *bm,
+ const bool use_split_normals,
+ const float split_angle,
+ float (*r_lnos)[3],
+ MLoopNorSpaceArray *r_lnors_spacearr,
+ const short (*clnors_data)[2],
+ const int cd_loop_clnors_offset)
+{
+ const bool has_clnors = clnors_data || (cd_loop_clnors_offset != -1);
+
+ if (use_split_normals) {
+ /* Tag smooth edges and set lnos from vnos when they might be completely smooth...
+ * When using custom loop normals, disable the angle feature! */
+ bm_mesh_edges_sharp_tag(bm, NULL, NULL, has_clnors ? (float)M_PI : split_angle, r_lnos);
+
+ /* Finish computing lnos by accumulating face normals
+ * in each fan of faces defined by sharp edges. */
+ bm_mesh_loops_calc_normals(
+ bm, NULL, NULL, r_lnos, r_lnors_spacearr, clnors_data, cd_loop_clnors_offset);
+ }
+ else {
+ BLI_assert(!r_lnors_spacearr);
+ bm_mesh_loops_calc_normals_no_autosmooth(bm, NULL, NULL, r_lnos);
+ }
+}
+#endif
+
+/**
+ * \brief BMesh Compute Loop Normals from/to external data.
+ *
+ * Compute split normals, i.e. vertex normals associated with each poly (hence 'loop normals').
+ * Useful to materialize sharp edges (or non-smooth faces) without actually modifying the geometry
+ * (splitting edges).
+ */
+void BM_loops_calc_normal_vcos(BMesh *bm,
+ const float (*vcos)[3],
+ const float (*vnos)[3],
+ const float (*fnos)[3],
+ const bool use_split_normals,
+ const float split_angle,
+ float (*r_lnos)[3],
+ MLoopNorSpaceArray *r_lnors_spacearr,
+ short (*clnors_data)[2],
+ const int cd_loop_clnors_offset,
+ const bool do_rebuild)
+{
+ const bool has_clnors = clnors_data || (cd_loop_clnors_offset != -1);
+
+ if (use_split_normals) {
+ /* Tag smooth edges and set lnos from vnos when they might be completely smooth...
+ * When using custom loop normals, disable the angle feature! */
+ bm_mesh_edges_sharp_tag(bm, vnos, fnos, r_lnos, has_clnors ? (float)M_PI : split_angle, false);
+
+ /* Finish computing lnos by accumulating face normals
+ * in each fan of faces defined by sharp edges. */
+ bm_mesh_loops_calc_normals(
+ bm, vcos, fnos, r_lnos, r_lnors_spacearr, clnors_data, cd_loop_clnors_offset, do_rebuild);
+ }
+ else {
+ BLI_assert(!r_lnors_spacearr);
+ bm_mesh_loops_calc_normals_no_autosmooth(bm, vnos, fnos, r_lnos);
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Loop Normal Space API
+ * \{ */
+
+void BM_lnorspacearr_store(BMesh *bm, float (*r_lnors)[3])
+{
+ BLI_assert(bm->lnor_spacearr != NULL);
+
+ if (!CustomData_has_layer(&bm->ldata, CD_CUSTOMLOOPNORMAL)) {
+ BM_data_layer_add(bm, &bm->ldata, CD_CUSTOMLOOPNORMAL);
+ }
+
+ int cd_loop_clnors_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL);
+
+ BM_loops_calc_normal_vcos(bm,
+ NULL,
+ NULL,
+ NULL,
+ true,
+ M_PI,
+ r_lnors,
+ bm->lnor_spacearr,
+ NULL,
+ cd_loop_clnors_offset,
+ false);
+ bm->spacearr_dirty &= ~(BM_SPACEARR_DIRTY | BM_SPACEARR_DIRTY_ALL);
+}
+
+#define CLEAR_SPACEARRAY_THRESHOLD(x) ((x) / 2)
+
+void BM_lnorspace_invalidate(BMesh *bm, const bool do_invalidate_all)
+{
+ if (bm->spacearr_dirty & BM_SPACEARR_DIRTY_ALL) {
+ return;
+ }
+ if (do_invalidate_all || bm->totvertsel > CLEAR_SPACEARRAY_THRESHOLD(bm->totvert)) {
+ bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
+ return;
+ }
+ if (bm->lnor_spacearr == NULL) {
+ bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
+ return;
+ }
+
+ BMVert *v;
+ BMLoop *l;
+ BMIter viter, liter;
+ /* Note: we could use temp tag of BMItem for that,
+ * but probably better not use it in such a low-level func?
+ * --mont29 */
+ BLI_bitmap *done_verts = BLI_BITMAP_NEW(bm->totvert, __func__);
+
+ BM_mesh_elem_index_ensure(bm, BM_VERT);
+
+ /* When we affect a given vertex, we may affect following smooth fans:
+ * - all smooth fans of said vertex;
+ * - all smooth fans of all immediate loop-neighbors vertices;
+ * This can be simplified as 'all loops of selected vertices and their immediate neighbors'
+ * need to be tagged for update.
+ */
+ BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
+ if (BM_elem_flag_test(v, BM_ELEM_SELECT)) {
+ BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) {
+ BM_ELEM_API_FLAG_ENABLE(l, BM_LNORSPACE_UPDATE);
+
+ /* Note that we only handle unselected neighbor vertices here, main loop will take care of
+ * selected ones. */
+ if ((!BM_elem_flag_test(l->prev->v, BM_ELEM_SELECT)) &&
+ !BLI_BITMAP_TEST(done_verts, BM_elem_index_get(l->prev->v))) {
+
+ BMLoop *l_prev;
+ BMIter liter_prev;
+ BM_ITER_ELEM (l_prev, &liter_prev, l->prev->v, BM_LOOPS_OF_VERT) {
+ BM_ELEM_API_FLAG_ENABLE(l_prev, BM_LNORSPACE_UPDATE);
+ }
+ BLI_BITMAP_ENABLE(done_verts, BM_elem_index_get(l_prev->v));
+ }
+
+ if ((!BM_elem_flag_test(l->next->v, BM_ELEM_SELECT)) &&
+ !BLI_BITMAP_TEST(done_verts, BM_elem_index_get(l->next->v))) {
+
+ BMLoop *l_next;
+ BMIter liter_next;
+ BM_ITER_ELEM (l_next, &liter_next, l->next->v, BM_LOOPS_OF_VERT) {
+ BM_ELEM_API_FLAG_ENABLE(l_next, BM_LNORSPACE_UPDATE);
+ }
+ BLI_BITMAP_ENABLE(done_verts, BM_elem_index_get(l_next->v));
+ }
+ }
+
+ BLI_BITMAP_ENABLE(done_verts, BM_elem_index_get(v));
+ }
+ }
+
+ MEM_freeN(done_verts);
+ bm->spacearr_dirty |= BM_SPACEARR_DIRTY;
+}
+
+void BM_lnorspace_rebuild(BMesh *bm, bool preserve_clnor)
+{
+ BLI_assert(bm->lnor_spacearr != NULL);
+
+ if (!(bm->spacearr_dirty & (BM_SPACEARR_DIRTY | BM_SPACEARR_DIRTY_ALL))) {
+ return;
+ }
+ BMFace *f;
+ BMLoop *l;
+ BMIter fiter, liter;
+
+ float(*r_lnors)[3] = MEM_callocN(sizeof(*r_lnors) * bm->totloop, __func__);
+ float(*oldnors)[3] = preserve_clnor ? MEM_mallocN(sizeof(*oldnors) * bm->totloop, __func__) :
+ NULL;
+
+ int cd_loop_clnors_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL);
+
+ BM_mesh_elem_index_ensure(bm, BM_LOOP);
+
+ if (preserve_clnor) {
+ BLI_assert(bm->lnor_spacearr->lspacearr != NULL);
+
+ BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
+ BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
+ if (BM_ELEM_API_FLAG_TEST(l, BM_LNORSPACE_UPDATE) ||
+ bm->spacearr_dirty & BM_SPACEARR_DIRTY_ALL) {
+ short(*clnor)[2] = BM_ELEM_CD_GET_VOID_P(l, cd_loop_clnors_offset);
+ int l_index = BM_elem_index_get(l);
+
+ BKE_lnor_space_custom_data_to_normal(
+ bm->lnor_spacearr->lspacearr[l_index], *clnor, oldnors[l_index]);
+ }
+ }
+ }
+ }
+
+ if (bm->spacearr_dirty & BM_SPACEARR_DIRTY_ALL) {
+ BKE_lnor_spacearr_clear(bm->lnor_spacearr);
+ }
+ BM_loops_calc_normal_vcos(bm,
+ NULL,
+ NULL,
+ NULL,
+ true,
+ M_PI,
+ r_lnors,
+ bm->lnor_spacearr,
+ NULL,
+ cd_loop_clnors_offset,
+ true);
+ MEM_freeN(r_lnors);
+
+ BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
+ BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
+ if (BM_ELEM_API_FLAG_TEST(l, BM_LNORSPACE_UPDATE) ||
+ bm->spacearr_dirty & BM_SPACEARR_DIRTY_ALL) {
+ if (preserve_clnor) {
+ short(*clnor)[2] = BM_ELEM_CD_GET_VOID_P(l, cd_loop_clnors_offset);
+ int l_index = BM_elem_index_get(l);
+ BKE_lnor_space_custom_normal_to_data(
+ bm->lnor_spacearr->lspacearr[l_index], oldnors[l_index], *clnor);
+ }
+ BM_ELEM_API_FLAG_DISABLE(l, BM_LNORSPACE_UPDATE);
+ }
+ }
+ }
+
+ MEM_SAFE_FREE(oldnors);
+ bm->spacearr_dirty &= ~(BM_SPACEARR_DIRTY | BM_SPACEARR_DIRTY_ALL);
+
+#ifndef NDEBUG
+ BM_lnorspace_err(bm);
+#endif
+}
+
+/**
+ * \warning This function sets #BM_ELEM_TAG on loops & edges via #bm_mesh_loops_calc_normals,
+ * take care to run this before setting up tags.
+ */
+void BM_lnorspace_update(BMesh *bm)
+{
+ if (bm->lnor_spacearr == NULL) {
+ bm->lnor_spacearr = MEM_callocN(sizeof(*bm->lnor_spacearr), __func__);
+ }
+ if (bm->lnor_spacearr->lspacearr == NULL) {
+ float(*lnors)[3] = MEM_callocN(sizeof(*lnors) * bm->totloop, __func__);
+
+ BM_lnorspacearr_store(bm, lnors);
+
+ MEM_freeN(lnors);
+ }
+ else if (bm->spacearr_dirty & (BM_SPACEARR_DIRTY | BM_SPACEARR_DIRTY_ALL)) {
+ BM_lnorspace_rebuild(bm, false);
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Loop Normal Edit Data Array API
+ *
+ * Utilities for creating/freeing #BMLoopNorEditDataArray.
+ * \{ */
+
+/**
+ * Auxiliary function only used by rebuild to detect if any spaces were not marked as invalid.
+ * Reports error if any of the lnor spaces change after rebuilding, meaning that all the possible
+ * lnor spaces to be rebuilt were not correctly marked.
+ */
+#ifndef NDEBUG
+void BM_lnorspace_err(BMesh *bm)
+{
+ bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
+ bool clear = true;
+
+ MLoopNorSpaceArray *temp = MEM_callocN(sizeof(*temp), __func__);
+ temp->lspacearr = NULL;
+
+ BKE_lnor_spacearr_init(temp, bm->totloop, MLNOR_SPACEARR_BMLOOP_PTR);
+
+ int cd_loop_clnors_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL);
+ float(*lnors)[3] = MEM_callocN(sizeof(*lnors) * bm->totloop, __func__);
+ BM_loops_calc_normal_vcos(
+ bm, NULL, NULL, NULL, true, M_PI, lnors, temp, NULL, cd_loop_clnors_offset, true);
+
+ for (int i = 0; i < bm->totloop; i++) {
+ int j = 0;
+ j += compare_ff(
+ temp->lspacearr[i]->ref_alpha, bm->lnor_spacearr->lspacearr[i]->ref_alpha, 1e-4f);
+ j += compare_ff(
+ temp->lspacearr[i]->ref_beta, bm->lnor_spacearr->lspacearr[i]->ref_beta, 1e-4f);
+ j += compare_v3v3(
+ temp->lspacearr[i]->vec_lnor, bm->lnor_spacearr->lspacearr[i]->vec_lnor, 1e-4f);
+ j += compare_v3v3(
+ temp->lspacearr[i]->vec_ortho, bm->lnor_spacearr->lspacearr[i]->vec_ortho, 1e-4f);
+ j += compare_v3v3(
+ temp->lspacearr[i]->vec_ref, bm->lnor_spacearr->lspacearr[i]->vec_ref, 1e-4f);
+
+ if (j != 5) {
+ clear = false;
+ break;
+ }
+ }
+ BKE_lnor_spacearr_free(temp);
+ MEM_freeN(temp);
+ MEM_freeN(lnors);
+ BLI_assert(clear);
+
+ bm->spacearr_dirty &= ~BM_SPACEARR_DIRTY_ALL;
+}
+#endif
+
+static void bm_loop_normal_mark_indiv_do_loop(BMLoop *l,
+ BLI_bitmap *loops,
+ MLoopNorSpaceArray *lnor_spacearr,
+ int *totloopsel,
+ const bool do_all_loops_of_vert)
+{
+ if (l != NULL) {
+ const int l_idx = BM_elem_index_get(l);
+
+ if (!BLI_BITMAP_TEST(loops, l_idx)) {
+ /* If vert and face selected share a loop, mark it for editing. */
+ BLI_BITMAP_ENABLE(loops, l_idx);
+ (*totloopsel)++;
+
+ if (do_all_loops_of_vert) {
+ /* If required, also mark all loops shared by that vertex.
+ * This is needed when loop spaces may change
+ * (i.e. when some faces or edges might change of smooth/sharp status). */
+ BMIter liter;
+ BMLoop *lfan;
+ BM_ITER_ELEM (lfan, &liter, l->v, BM_LOOPS_OF_VERT) {
+ const int lfan_idx = BM_elem_index_get(lfan);
+ if (!BLI_BITMAP_TEST(loops, lfan_idx)) {
+ BLI_BITMAP_ENABLE(loops, lfan_idx);
+ (*totloopsel)++;
+ }
+ }
+ }
+ else {
+ /* Mark all loops in same loop normal space (aka smooth fan). */
+ if ((lnor_spacearr->lspacearr[l_idx]->flags & MLNOR_SPACE_IS_SINGLE) == 0) {
+ for (LinkNode *node = lnor_spacearr->lspacearr[l_idx]->loops; node; node = node->next) {
+ const int lfan_idx = BM_elem_index_get((BMLoop *)node->link);
+ if (!BLI_BITMAP_TEST(loops, lfan_idx)) {
+ BLI_BITMAP_ENABLE(loops, lfan_idx);
+ (*totloopsel)++;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+/* Mark the individual clnors to be edited, if multiple selection methods are used. */
+static int bm_loop_normal_mark_indiv(BMesh *bm, BLI_bitmap *loops, const bool do_all_loops_of_vert)
+{
+ BMEditSelection *ese, *ese_prev;
+ int totloopsel = 0;
+
+ const bool sel_verts = (bm->selectmode & SCE_SELECT_VERTEX) != 0;
+ const bool sel_edges = (bm->selectmode & SCE_SELECT_EDGE) != 0;
+ const bool sel_faces = (bm->selectmode & SCE_SELECT_FACE) != 0;
+ const bool use_sel_face_history = sel_faces && (sel_edges || sel_verts);
+
+ BM_mesh_elem_index_ensure(bm, BM_LOOP);
+
+ BLI_assert(bm->lnor_spacearr != NULL);
+ BLI_assert(bm->lnor_spacearr->data_type == MLNOR_SPACEARR_BMLOOP_PTR);
+
+ if (use_sel_face_history) {
+ /* Using face history allows to select a single loop from a single face...
+ * Note that this is O(n^2) piece of code,
+ * but it is not designed to be used with huge selection sets,
+ * rather with only a few items selected at most.*/
+ /* Goes from last selected to the first selected element. */
+ for (ese = bm->selected.last; ese; ese = ese->prev) {
+ if (ese->htype == BM_FACE) {
+ /* If current face is selected,
+ * then any verts to be edited must have been selected before it. */
+ for (ese_prev = ese->prev; ese_prev; ese_prev = ese_prev->prev) {
+ if (ese_prev->htype == BM_VERT) {
+ bm_loop_normal_mark_indiv_do_loop(
+ BM_face_vert_share_loop((BMFace *)ese->ele, (BMVert *)ese_prev->ele),
+ loops,
+ bm->lnor_spacearr,
+ &totloopsel,
+ do_all_loops_of_vert);
+ }
+ else if (ese_prev->htype == BM_EDGE) {
+ BMEdge *e = (BMEdge *)ese_prev->ele;
+ bm_loop_normal_mark_indiv_do_loop(BM_face_vert_share_loop((BMFace *)ese->ele, e->v1),
+ loops,
+ bm->lnor_spacearr,
+ &totloopsel,
+ do_all_loops_of_vert);
+
+ bm_loop_normal_mark_indiv_do_loop(BM_face_vert_share_loop((BMFace *)ese->ele, e->v2),
+ loops,
+ bm->lnor_spacearr,
+ &totloopsel,
+ do_all_loops_of_vert);
+ }
+ }
+ }
+ }
+ }
+ else {
+ if (sel_faces) {
+ /* Only select all loops of selected faces. */
+ BMLoop *l;
+ BMFace *f;
+ BMIter liter, fiter;
+ BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
+ if (BM_elem_flag_test(f, BM_ELEM_SELECT)) {
+ BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
+ bm_loop_normal_mark_indiv_do_loop(
+ l, loops, bm->lnor_spacearr, &totloopsel, do_all_loops_of_vert);
+ }
+ }
+ }
+ }
+ if (sel_edges) {
+ /* Only select all loops of selected edges. */
+ BMLoop *l;
+ BMEdge *e;
+ BMIter liter, eiter;
+ BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
+ if (BM_elem_flag_test(e, BM_ELEM_SELECT)) {
+ BM_ITER_ELEM (l, &liter, e, BM_LOOPS_OF_EDGE) {
+ bm_loop_normal_mark_indiv_do_loop(
+ l, loops, bm->lnor_spacearr, &totloopsel, do_all_loops_of_vert);
+ /* Loops actually 'have' two edges, or said otherwise, a selected edge actually selects
+ * *two* loops in each of its faces. We have to find the other one too. */
+ if (BM_vert_in_edge(e, l->next->v)) {
+ bm_loop_normal_mark_indiv_do_loop(
+ l->next, loops, bm->lnor_spacearr, &totloopsel, do_all_loops_of_vert);
+ }
+ else {
+ BLI_assert(BM_vert_in_edge(e, l->prev->v));
+ bm_loop_normal_mark_indiv_do_loop(
+ l->prev, loops, bm->lnor_spacearr, &totloopsel, do_all_loops_of_vert);
+ }
+ }
+ }
+ }
+ }
+ if (sel_verts) {
+ /* Select all loops of selected verts. */
+ BMLoop *l;
+ BMVert *v;
+ BMIter liter, viter;
+ BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
+ if (BM_elem_flag_test(v, BM_ELEM_SELECT)) {
+ BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) {
+ bm_loop_normal_mark_indiv_do_loop(
+ l, loops, bm->lnor_spacearr, &totloopsel, do_all_loops_of_vert);
+ }
+ }
+ }
+ }
+ }
+
+ return totloopsel;
+}
+
+static void loop_normal_editdata_init(
+ BMesh *bm, BMLoopNorEditData *lnor_ed, BMVert *v, BMLoop *l, const int offset)
+{
+ BLI_assert(bm->lnor_spacearr != NULL);
+ BLI_assert(bm->lnor_spacearr->lspacearr != NULL);
+
+ const int l_index = BM_elem_index_get(l);
+ short *clnors_data = BM_ELEM_CD_GET_VOID_P(l, offset);
+
+ lnor_ed->loop_index = l_index;
+ lnor_ed->loop = l;
+
+ float custom_normal[3];
+ BKE_lnor_space_custom_data_to_normal(
+ bm->lnor_spacearr->lspacearr[l_index], clnors_data, custom_normal);
+
+ lnor_ed->clnors_data = clnors_data;
+ copy_v3_v3(lnor_ed->nloc, custom_normal);
+ copy_v3_v3(lnor_ed->niloc, custom_normal);
+
+ lnor_ed->loc = v->co;
+}
+
+BMLoopNorEditDataArray *BM_loop_normal_editdata_array_init(BMesh *bm,
+ const bool do_all_loops_of_vert)
+{
+ BMLoop *l;
+ BMVert *v;
+ BMIter liter, viter;
+
+ int totloopsel = 0;
+
+ BLI_assert(bm->spacearr_dirty == 0);
+
+ BMLoopNorEditDataArray *lnors_ed_arr = MEM_callocN(sizeof(*lnors_ed_arr), __func__);
+ lnors_ed_arr->lidx_to_lnor_editdata = MEM_callocN(
+ sizeof(*lnors_ed_arr->lidx_to_lnor_editdata) * bm->totloop, __func__);
+
+ if (!CustomData_has_layer(&bm->ldata, CD_CUSTOMLOOPNORMAL)) {
+ BM_data_layer_add(bm, &bm->ldata, CD_CUSTOMLOOPNORMAL);
+ }
+ const int cd_custom_normal_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL);
+
+ BM_mesh_elem_index_ensure(bm, BM_LOOP);
+
+ BLI_bitmap *loops = BLI_BITMAP_NEW(bm->totloop, __func__);
+
+ /* This function define loop normals to edit, based on selection modes and history. */
+ totloopsel = bm_loop_normal_mark_indiv(bm, loops, do_all_loops_of_vert);
+
+ if (totloopsel) {
+ BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata = MEM_mallocN(
+ sizeof(*lnor_ed) * totloopsel, __func__);
+
+ BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
+ BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) {
+ if (BLI_BITMAP_TEST(loops, BM_elem_index_get(l))) {
+ loop_normal_editdata_init(bm, lnor_ed, v, l, cd_custom_normal_offset);
+ lnors_ed_arr->lidx_to_lnor_editdata[BM_elem_index_get(l)] = lnor_ed;
+ lnor_ed++;
+ }
+ }
+ }
+ lnors_ed_arr->totloop = totloopsel;
+ }
+
+ MEM_freeN(loops);
+ lnors_ed_arr->cd_custom_normal_offset = cd_custom_normal_offset;
+ return lnors_ed_arr;
+}
+
+void BM_loop_normal_editdata_array_free(BMLoopNorEditDataArray *lnors_ed_arr)
+{
+ MEM_SAFE_FREE(lnors_ed_arr->lnor_editdata);
+ MEM_SAFE_FREE(lnors_ed_arr->lidx_to_lnor_editdata);
+ MEM_freeN(lnors_ed_arr);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Custom Normals / Vector Layer Conversion
+ * \{ */
+
+/**
+ * \warning This function sets #BM_ELEM_TAG on loops & edges via #bm_mesh_loops_calc_normals,
+ * take care to run this before setting up tags.
+ */
+bool BM_custom_loop_normals_to_vector_layer(BMesh *bm)
+{
+ BMFace *f;
+ BMLoop *l;
+ BMIter liter, fiter;
+
+ if (!CustomData_has_layer(&bm->ldata, CD_CUSTOMLOOPNORMAL)) {
+ return false;
+ }
+
+ BM_lnorspace_update(bm);
+ BM_mesh_elem_index_ensure(bm, BM_LOOP);
+
+ /* Create a loop normal layer. */
+ if (!CustomData_has_layer(&bm->ldata, CD_NORMAL)) {
+ BM_data_layer_add(bm, &bm->ldata, CD_NORMAL);
+
+ CustomData_set_layer_flag(&bm->ldata, CD_NORMAL, CD_FLAG_TEMPORARY);
+ }
+
+ const int cd_custom_normal_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL);
+ const int cd_normal_offset = CustomData_get_offset(&bm->ldata, CD_NORMAL);
+
+ BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
+ BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
+ const int l_index = BM_elem_index_get(l);
+ const short *clnors_data = BM_ELEM_CD_GET_VOID_P(l, cd_custom_normal_offset);
+ float *normal = BM_ELEM_CD_GET_VOID_P(l, cd_normal_offset);
+
+ BKE_lnor_space_custom_data_to_normal(
+ bm->lnor_spacearr->lspacearr[l_index], clnors_data, normal);
+ }
+ }
+
+ return true;
+}
+
+void BM_custom_loop_normals_from_vector_layer(BMesh *bm, bool add_sharp_edges)
+{
+ if (!CustomData_has_layer(&bm->ldata, CD_CUSTOMLOOPNORMAL) ||
+ !CustomData_has_layer(&bm->ldata, CD_NORMAL)) {
+ return;
+ }
+
+ const int cd_custom_normal_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL);
+ const int cd_normal_offset = CustomData_get_offset(&bm->ldata, CD_NORMAL);
+
+ if (bm->lnor_spacearr == NULL) {
+ bm->lnor_spacearr = MEM_callocN(sizeof(*bm->lnor_spacearr), __func__);
+ }
+
+ bm_mesh_loops_custom_normals_set(bm,
+ NULL,
+ NULL,
+ NULL,
+ bm->lnor_spacearr,
+ NULL,
+ cd_custom_normal_offset,
+ NULL,
+ cd_normal_offset,
+ add_sharp_edges);
+
+ bm->spacearr_dirty &= ~(BM_SPACEARR_DIRTY | BM_SPACEARR_DIRTY_ALL);
+}
+
+/** \} */
diff --git a/source/blender/bmesh/intern/bmesh_mesh_normals.h b/source/blender/bmesh/intern/bmesh_mesh_normals.h
new file mode 100644
index 00000000000..41191340e9e
--- /dev/null
+++ b/source/blender/bmesh/intern/bmesh_mesh_normals.h
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+/** \file
+ * \ingroup bmesh
+ */
+
+#include "bmesh_class.h"
+
+void BM_mesh_normals_update(BMesh *bm);
+void BM_mesh_normals_update_with_partial(BMesh *bm, const struct BMPartialUpdate *bmpinfo);
+
+void BM_verts_calc_normal_vcos(BMesh *bm,
+ const float (*fnos)[3],
+ const float (*vcos)[3],
+ float (*vnos)[3]);
+void BM_loops_calc_normal_vcos(BMesh *bm,
+ const float (*vcos)[3],
+ const float (*vnos)[3],
+ const float (*fnos)[3],
+ const bool use_split_normals,
+ const float split_angle,
+ float (*r_lnos)[3],
+ struct MLoopNorSpaceArray *r_lnors_spacearr,
+ short (*clnors_data)[2],
+ const int cd_loop_clnors_offset,
+ const bool do_rebuild);
+
+bool BM_loop_check_cyclic_smooth_fan(BMLoop *l_curr);
+void BM_lnorspacearr_store(BMesh *bm, float (*r_lnors)[3]);
+void BM_lnorspace_invalidate(BMesh *bm, const bool do_invalidate_all);
+void BM_lnorspace_rebuild(BMesh *bm, bool preserve_clnor);
+void BM_lnorspace_update(BMesh *bm);
+void BM_normals_loops_edges_tag(BMesh *bm, const bool do_edges);
+#ifndef NDEBUG
+void BM_lnorspace_err(BMesh *bm);
+#endif
+
+/* Loop Generics */
+struct BMLoopNorEditDataArray *BM_loop_normal_editdata_array_init(BMesh *bm,
+ const bool do_all_loops_of_vert);
+void BM_loop_normal_editdata_array_free(struct BMLoopNorEditDataArray *lnors_ed_arr);
+
+bool BM_custom_loop_normals_to_vector_layer(struct BMesh *bm);
+void BM_custom_loop_normals_from_vector_layer(struct BMesh *bm, bool add_sharp_edges);
+
+void BM_edges_sharp_from_angle_set(BMesh *bm, const float split_angle);
diff --git a/source/blender/bmesh/intern/bmesh_mesh_partial_update.c b/source/blender/bmesh/intern/bmesh_mesh_partial_update.c
new file mode 100644
index 00000000000..7b01b61d4fa
--- /dev/null
+++ b/source/blender/bmesh/intern/bmesh_mesh_partial_update.c
@@ -0,0 +1,254 @@
+/*
+ * 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.
+ */
+
+/** \file
+ * \ingroup bmesh
+ *
+ * Generate data needed for partially updating mesh information.
+ * Currently this is used for normals and tessellation.
+ *
+ * Transform is the obvious use case where there is no need to update normals or tessellation
+ * for geometry which has not been modified.
+ *
+ * In the future this could be integrated into GPU updates too.
+ *
+ * Potential Improvements
+ * ======================
+ *
+ * Some calculations could be significantly limited in the case of affine transformations
+ * (tessellation is an obvious candidate). Where only faces which have a mix
+ * of tagged and untagged vertices would need to be recalculated.
+ *
+ * In general this would work well besides some corner cases such as scaling to zero.
+ * Although the exact result may depend on the normal (for N-GONS),
+ * so for now update the tessellation of all tagged geometry.
+ */
+
+#include "DNA_object_types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_alloca.h"
+#include "BLI_bitmap.h"
+#include "BLI_math_vector.h"
+
+#include "bmesh.h"
+
+/**
+ * Grow by 1.5x (rounding up).
+ *
+ * \note Use conservative reallocation since the initial sizes reserved
+ * may be close to (or exactly) the number of elements needed.
+ */
+#define GROW(len_alloc) ((len_alloc) + ((len_alloc) - ((len_alloc) / 2)))
+#define GROW_ARRAY(mem, len_alloc) \
+ { \
+ mem = MEM_reallocN(mem, (sizeof(*mem)) * ((len_alloc) = GROW(len_alloc))); \
+ } \
+ ((void)0)
+
+#define GROW_ARRAY_AS_NEEDED(mem, len_alloc, index) \
+ if (UNLIKELY(len_alloc == index)) { \
+ GROW_ARRAY(mem, len_alloc); \
+ }
+
+BLI_INLINE bool partial_elem_vert_ensure(BMPartialUpdate *bmpinfo,
+ BLI_bitmap *verts_tag,
+ BMVert *v)
+{
+ const int i = BM_elem_index_get(v);
+ if (!BLI_BITMAP_TEST(verts_tag, i)) {
+ BLI_BITMAP_ENABLE(verts_tag, i);
+ GROW_ARRAY_AS_NEEDED(bmpinfo->verts, bmpinfo->verts_len_alloc, bmpinfo->verts_len);
+ bmpinfo->verts[bmpinfo->verts_len++] = v;
+ return true;
+ }
+ return false;
+}
+
+BLI_INLINE bool partial_elem_edge_ensure(BMPartialUpdate *bmpinfo,
+ BLI_bitmap *edges_tag,
+ BMEdge *e)
+{
+ const int i = BM_elem_index_get(e);
+ if (!BLI_BITMAP_TEST(edges_tag, i)) {
+ BLI_BITMAP_ENABLE(edges_tag, i);
+ GROW_ARRAY_AS_NEEDED(bmpinfo->edges, bmpinfo->edges_len_alloc, bmpinfo->edges_len);
+ bmpinfo->edges[bmpinfo->edges_len++] = e;
+ return true;
+ }
+ return false;
+}
+
+BLI_INLINE bool partial_elem_face_ensure(BMPartialUpdate *bmpinfo,
+ BLI_bitmap *faces_tag,
+ BMFace *f)
+{
+ const int i = BM_elem_index_get(f);
+ if (!BLI_BITMAP_TEST(faces_tag, i)) {
+ BLI_BITMAP_ENABLE(faces_tag, i);
+ GROW_ARRAY_AS_NEEDED(bmpinfo->faces, bmpinfo->faces_len_alloc, bmpinfo->faces_len);
+ bmpinfo->faces[bmpinfo->faces_len++] = f;
+ return true;
+ }
+ return false;
+}
+
+BMPartialUpdate *BM_mesh_partial_create_from_verts(BMesh *bm,
+ const BMPartialUpdate_Params *params,
+ const int verts_len,
+ bool (*filter_fn)(BMVert *, void *user_data),
+ void *user_data)
+{
+ /* The caller is doing something wrong if this isn't the case. */
+ BLI_assert(verts_len <= bm->totvert);
+
+ BMPartialUpdate *bmpinfo = MEM_callocN(sizeof(*bmpinfo), __func__);
+
+ /* Reserve more edges than vertices since it's common for a grid topology
+ * to use around twice as many edges as vertices. */
+ const int default_verts_len_alloc = verts_len;
+ const int default_edges_len_alloc = min_ii(bm->totedge, verts_len * 2);
+ const int default_faces_len_alloc = min_ii(bm->totface, verts_len);
+
+ /* Allocate tags instead of using #BM_ELEM_TAG because the caller may already be using tags.
+ * Further, walking over all geometry to clear the tags isn't so efficient. */
+ BLI_bitmap *verts_tag = NULL;
+ BLI_bitmap *edges_tag = NULL;
+ BLI_bitmap *faces_tag = NULL;
+
+ /* Set vert inline. */
+ BM_mesh_elem_index_ensure(bm, (BM_EDGE | BM_FACE));
+
+ if (params->do_normals || params->do_tessellate) {
+ /* - Extend to all vertices connected faces:
+ * In the case of tessellation this is enough.
+ *
+ * In the case of vertex normal calculation,
+ * All the relevant connectivity data can be accessed from the faces
+ * (there is no advantage in storing connected edges or vertices in this pass).
+ *
+ * NOTE: In the future it may be useful to differentiate between vertices
+ * that are directly marked (by the filter function when looping over all vertices).
+ * And vertices marked from indirect connections.
+ * This would require an extra tag array, so avoid this unless it's needed.
+ */
+
+ /* Faces. */
+ if (bmpinfo->faces == NULL) {
+ bmpinfo->faces_len_alloc = default_faces_len_alloc;
+ bmpinfo->faces = MEM_mallocN((sizeof(BMFace *) * bmpinfo->faces_len_alloc), __func__);
+ faces_tag = BLI_BITMAP_NEW((size_t)bm->totface, __func__);
+ }
+
+ BMVert *v;
+ BMIter iter;
+ int i;
+ BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) {
+ BM_elem_index_set(v, i); /* set_inline */
+ if (!filter_fn(v, user_data)) {
+ continue;
+ }
+ BMEdge *e_iter = v->e;
+ if (e_iter != NULL) {
+ /* Loop over edges. */
+ BMEdge *e_first = v->e;
+ do {
+ BMLoop *l_iter = e_iter->l;
+ if (e_iter->l != NULL) {
+ BMLoop *l_first = e_iter->l;
+ /* Loop over radial loops. */
+ do {
+ if (l_iter->v == v) {
+ partial_elem_face_ensure(bmpinfo, faces_tag, l_iter->f);
+ }
+ } while ((l_iter = l_iter->radial_next) != l_first);
+ }
+ } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v)) != e_first);
+ }
+ }
+ }
+
+ if (params->do_normals) {
+ /* - Extend to all faces vertices:
+ * Any changes to the faces normal needs to update all surrounding vertices.
+ *
+ * - Extend to all these vertices connected edges:
+ * These and needed to access those vertices edge vectors in normal calculation logic.
+ */
+
+ /* Vertices. */
+ if (bmpinfo->verts == NULL) {
+ bmpinfo->verts_len_alloc = default_verts_len_alloc;
+ bmpinfo->verts = MEM_mallocN((sizeof(BMVert *) * bmpinfo->verts_len_alloc), __func__);
+ verts_tag = BLI_BITMAP_NEW((size_t)bm->totvert, __func__);
+ }
+
+ /* Edges. */
+ if (bmpinfo->edges == NULL) {
+ bmpinfo->edges_len_alloc = default_edges_len_alloc;
+ bmpinfo->edges = MEM_mallocN((sizeof(BMEdge *) * bmpinfo->edges_len_alloc), __func__);
+ edges_tag = BLI_BITMAP_NEW((size_t)bm->totedge, __func__);
+ }
+
+ for (int i = 0; i < bmpinfo->faces_len; i++) {
+ BMFace *f = bmpinfo->faces[i];
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ if (!partial_elem_vert_ensure(bmpinfo, verts_tag, l_iter->v)) {
+ continue;
+ }
+ BMVert *v = l_iter->v;
+ BMEdge *e_first = v->e;
+ BMEdge *e_iter = e_first;
+ do {
+ if (e_iter->l) {
+ partial_elem_edge_ensure(bmpinfo, edges_tag, e_iter);
+ }
+ } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v)) != e_first);
+ } while ((l_iter = l_iter->next) != l_first);
+ }
+ }
+
+ if (verts_tag) {
+ MEM_freeN(verts_tag);
+ }
+ if (edges_tag) {
+ MEM_freeN(edges_tag);
+ }
+ if (faces_tag) {
+ MEM_freeN(faces_tag);
+ }
+
+ bmpinfo->params = *params;
+
+ return bmpinfo;
+}
+
+void BM_mesh_partial_destroy(BMPartialUpdate *bmpinfo)
+{
+ if (bmpinfo->verts) {
+ MEM_freeN(bmpinfo->verts);
+ }
+ if (bmpinfo->edges) {
+ MEM_freeN(bmpinfo->edges);
+ }
+ if (bmpinfo->faces) {
+ MEM_freeN(bmpinfo->faces);
+ }
+ MEM_freeN(bmpinfo);
+}
diff --git a/source/blender/bmesh/intern/bmesh_mesh_partial_update.h b/source/blender/bmesh/intern/bmesh_mesh_partial_update.h
new file mode 100644
index 00000000000..b31ec127744
--- /dev/null
+++ b/source/blender/bmesh/intern/bmesh_mesh_partial_update.h
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+/** \file
+ * \ingroup bmesh
+ */
+
+#include "BLI_compiler_attrs.h"
+
+/**
+ * Parameters used to determine which kinds of data needs to be generated.
+ */
+typedef struct BMPartialUpdate_Params {
+ bool do_normals;
+ bool do_tessellate;
+} BMPartialUpdate_Params;
+
+/**
+ * Cached data to speed up partial updates.
+ *
+ * Hints:
+ *
+ * - Avoid creating this data for single updates,
+ * it should be created and reused across multiple updates to gain a significant benefit
+ * (while transforming geometry for example).
+ *
+ * - Partial normal updates use face & loop indices,
+ * setting them to dirty values between updates will slow down normal recalculation.
+ */
+typedef struct BMPartialUpdate {
+ BMVert **verts;
+ BMEdge **edges;
+ BMFace **faces;
+ int verts_len, verts_len_alloc;
+ int edges_len, edges_len_alloc;
+ int faces_len, faces_len_alloc;
+
+ /** Store the parameters used in creation so invalid use can be asserted. */
+ BMPartialUpdate_Params params;
+} BMPartialUpdate;
+
+BMPartialUpdate *BM_mesh_partial_create_from_verts(BMesh *bm,
+ const BMPartialUpdate_Params *params,
+ const int verts_len,
+ bool (*filter_fn)(BMVert *, void *user_data),
+ void *user_data)
+ ATTR_NONNULL(1, 2, 4) ATTR_WARN_UNUSED_RESULT;
+
+void BM_mesh_partial_destroy(BMPartialUpdate *bmpinfo) ATTR_NONNULL(1);
diff --git a/source/blender/bmesh/intern/bmesh_mesh_tessellate.c b/source/blender/bmesh/intern/bmesh_mesh_tessellate.c
new file mode 100644
index 00000000000..7a95e52ce25
--- /dev/null
+++ b/source/blender/bmesh/intern/bmesh_mesh_tessellate.c
@@ -0,0 +1,457 @@
+/*
+ * 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.
+ */
+
+/** \file
+ * \ingroup bmesh
+ *
+ * This file contains code for polygon tessellation
+ * (creating triangles from polygons).
+ */
+
+#include "DNA_meshdata_types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_alloca.h"
+#include "BLI_heap.h"
+#include "BLI_linklist.h"
+#include "BLI_math.h"
+#include "BLI_memarena.h"
+#include "BLI_polyfill_2d.h"
+#include "BLI_polyfill_2d_beautify.h"
+#include "BLI_task.h"
+
+#include "bmesh.h"
+#include "bmesh_tools.h"
+
+/**
+ * On systems with 32+ cores,
+ * only a very small number of faces has any advantage single threading (in the 100's).
+ * Note that between 500-2000 quads, the difference isn't so much
+ * (tessellation isn't a bottleneck in this case anyway).
+ * Avoid the slight overhead of using threads in this case.
+ */
+#define BM_FACE_TESSELLATE_THREADED_LIMIT 1024
+
+/* -------------------------------------------------------------------- */
+/** \name Default Mesh Tessellation
+ * \{ */
+
+static int mesh_calc_tessellation_for_face(BMLoop *(*looptris)[3],
+ BMFace *efa,
+ MemArena **pf_arena_p)
+{
+ switch (efa->len) {
+ case 3: {
+ /* `0 1 2` -> `0 1 2` */
+ BMLoop *l;
+ BMLoop **l_ptr = looptris[0];
+ l_ptr[0] = l = BM_FACE_FIRST_LOOP(efa);
+ l_ptr[1] = l = l->next;
+ l_ptr[2] = l->next;
+ return 1;
+ }
+ case 4: {
+ /* `0 1 2 3` -> (`0 1 2`, `0 2 3`) */
+ BMLoop *l;
+ BMLoop **l_ptr_a = looptris[0];
+ BMLoop **l_ptr_b = looptris[1];
+ (l_ptr_a[0] = l_ptr_b[0] = l = BM_FACE_FIRST_LOOP(efa));
+ (l_ptr_a[1] = l = l->next);
+ (l_ptr_a[2] = l_ptr_b[1] = l = l->next);
+ (l_ptr_b[2] = l->next);
+
+ if (UNLIKELY(is_quad_flip_v3_first_third_fast(
+ l_ptr_a[0]->v->co, l_ptr_a[1]->v->co, l_ptr_a[2]->v->co, l_ptr_b[2]->v->co))) {
+ /* Flip out of degenerate 0-2 state. */
+ l_ptr_a[2] = l_ptr_b[2];
+ l_ptr_b[0] = l_ptr_a[1];
+ }
+ return 2;
+ }
+ default: {
+ BMLoop *l_iter, *l_first;
+ BMLoop **l_arr;
+
+ float axis_mat[3][3];
+ float(*projverts)[2];
+ uint(*tris)[3];
+
+ const int tris_len = efa->len - 2;
+
+ MemArena *pf_arena = *pf_arena_p;
+ if (UNLIKELY(pf_arena == NULL)) {
+ pf_arena = *pf_arena_p = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
+ }
+
+ tris = BLI_memarena_alloc(pf_arena, sizeof(*tris) * tris_len);
+ l_arr = BLI_memarena_alloc(pf_arena, sizeof(*l_arr) * efa->len);
+ projverts = BLI_memarena_alloc(pf_arena, sizeof(*projverts) * efa->len);
+
+ axis_dominant_v3_to_m3_negate(axis_mat, efa->no);
+
+ int i = 0;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
+ do {
+ l_arr[i] = l_iter;
+ mul_v2_m3v3(projverts[i], axis_mat, l_iter->v->co);
+ i++;
+ } while ((l_iter = l_iter->next) != l_first);
+
+ BLI_polyfill_calc_arena(projverts, efa->len, 1, tris, pf_arena);
+
+ for (i = 0; i < tris_len; i++) {
+ BMLoop **l_ptr = looptris[i];
+ uint *tri = tris[i];
+
+ l_ptr[0] = l_arr[tri[0]];
+ l_ptr[1] = l_arr[tri[1]];
+ l_ptr[2] = l_arr[tri[2]];
+ }
+
+ BLI_memarena_clear(pf_arena);
+ return tris_len;
+ }
+ }
+}
+
+/**
+ * \brief BM_mesh_calc_tessellation get the looptris and its number from a certain bmesh
+ * \param looptris:
+ *
+ * \note \a looptris Must be pre-allocated to at least the size of given by: poly_to_tri_count
+ */
+static void bm_mesh_calc_tessellation__single_threaded(BMesh *bm, BMLoop *(*looptris)[3])
+{
+#ifndef NDEBUG
+ const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop);
+#endif
+
+ BMIter iter;
+ BMFace *efa;
+ int i = 0;
+
+ MemArena *pf_arena = NULL;
+
+ BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
+ BLI_assert(efa->len >= 3);
+ i += mesh_calc_tessellation_for_face(looptris + i, efa, &pf_arena);
+ }
+
+ if (pf_arena) {
+ BLI_memarena_free(pf_arena);
+ pf_arena = NULL;
+ }
+
+ BLI_assert(i <= looptris_tot);
+}
+
+struct TessellationUserTLS {
+ MemArena *pf_arena;
+};
+
+static void mesh_calc_tessellation_for_face_fn(void *__restrict userdata,
+ MempoolIterData *mp_f,
+ const TaskParallelTLS *__restrict tls)
+{
+ struct TessellationUserTLS *tls_data = tls->userdata_chunk;
+ BMLoop *(*looptris)[3] = userdata;
+ BMFace *f = (BMFace *)mp_f;
+ BMLoop *l = BM_FACE_FIRST_LOOP(f);
+ const int offset = BM_elem_index_get(l) - (BM_elem_index_get(f) * 2);
+ mesh_calc_tessellation_for_face(looptris + offset, f, &tls_data->pf_arena);
+}
+
+static void mesh_calc_tessellation_for_face_free_fn(const void *__restrict UNUSED(userdata),
+ void *__restrict tls_v)
+{
+ struct TessellationUserTLS *tls_data = tls_v;
+ if (tls_data->pf_arena) {
+ BLI_memarena_free(tls_data->pf_arena);
+ }
+}
+
+static void bm_mesh_calc_tessellation__multi_threaded(BMesh *bm, BMLoop *(*looptris)[3])
+{
+ BM_mesh_elem_index_ensure(bm, BM_LOOP | BM_FACE);
+
+ TaskParallelSettings settings;
+ struct TessellationUserTLS tls_dummy = {NULL};
+ BLI_parallel_mempool_settings_defaults(&settings);
+ settings.userdata_chunk = &tls_dummy;
+ settings.userdata_chunk_size = sizeof(tls_dummy);
+ settings.func_free = mesh_calc_tessellation_for_face_free_fn;
+ BM_iter_parallel(bm, BM_FACES_OF_MESH, mesh_calc_tessellation_for_face_fn, looptris, &settings);
+}
+
+void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3])
+{
+ if (bm->totface < BM_FACE_TESSELLATE_THREADED_LIMIT) {
+ bm_mesh_calc_tessellation__single_threaded(bm, looptris);
+ }
+ else {
+ bm_mesh_calc_tessellation__multi_threaded(bm, looptris);
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Default Tessellation (Partial Updates)
+ * \{ */
+
+struct PartialTessellationUserData {
+ BMFace **faces;
+ BMLoop *(*looptris)[3];
+};
+
+struct PartialTessellationUserTLS {
+ MemArena *pf_arena;
+};
+
+static void mesh_calc_tessellation_for_face_partial_fn(void *__restrict userdata,
+ const int index,
+ const TaskParallelTLS *__restrict tls)
+{
+ struct PartialTessellationUserTLS *tls_data = tls->userdata_chunk;
+ struct PartialTessellationUserData *data = userdata;
+ BMFace *f = data->faces[index];
+ BMLoop *l = BM_FACE_FIRST_LOOP(f);
+ const int offset = BM_elem_index_get(l) - (BM_elem_index_get(f) * 2);
+ mesh_calc_tessellation_for_face(data->looptris + offset, f, &tls_data->pf_arena);
+}
+
+static void mesh_calc_tessellation_for_face_partial_free_fn(
+ const void *__restrict UNUSED(userdata), void *__restrict tls_v)
+{
+ struct PartialTessellationUserTLS *tls_data = tls_v;
+ if (tls_data->pf_arena) {
+ BLI_memarena_free(tls_data->pf_arena);
+ }
+}
+
+static void bm_mesh_calc_tessellation_with_partial__multi_threaded(BMLoop *(*looptris)[3],
+ const BMPartialUpdate *bmpinfo)
+{
+ const int faces_len = bmpinfo->faces_len;
+ BMFace **faces = bmpinfo->faces;
+
+ struct PartialTessellationUserData data = {
+ .faces = faces,
+ .looptris = looptris,
+ };
+ struct PartialTessellationUserTLS tls_dummy = {NULL};
+ TaskParallelSettings settings;
+ BLI_parallel_range_settings_defaults(&settings);
+ settings.use_threading = true;
+ settings.userdata_chunk = &tls_dummy;
+ settings.userdata_chunk_size = sizeof(tls_dummy);
+ settings.func_free = mesh_calc_tessellation_for_face_partial_free_fn;
+
+ BLI_task_parallel_range(
+ 0, faces_len, &data, mesh_calc_tessellation_for_face_partial_fn, &settings);
+}
+
+static void bm_mesh_calc_tessellation_with_partial__single_threaded(BMLoop *(*looptris)[3],
+ const BMPartialUpdate *bmpinfo)
+{
+ const int faces_len = bmpinfo->faces_len;
+ BMFace **faces = bmpinfo->faces;
+
+ MemArena *pf_arena = NULL;
+
+ for (int index = 0; index < faces_len; index++) {
+ BMFace *f = faces[index];
+ BMLoop *l = BM_FACE_FIRST_LOOP(f);
+ const int offset = BM_elem_index_get(l) - (BM_elem_index_get(f) * 2);
+ mesh_calc_tessellation_for_face(looptris + offset, f, &pf_arena);
+ }
+
+ if (pf_arena) {
+ BLI_memarena_free(pf_arena);
+ }
+}
+
+void BM_mesh_calc_tessellation_with_partial(BMesh *bm,
+ BMLoop *(*looptris)[3],
+ const BMPartialUpdate *bmpinfo)
+{
+ BLI_assert(bmpinfo->params.do_tessellate);
+
+ BM_mesh_elem_index_ensure(bm, BM_LOOP | BM_FACE);
+
+ if (bmpinfo->faces_len < BM_FACE_TESSELLATE_THREADED_LIMIT) {
+ bm_mesh_calc_tessellation_with_partial__single_threaded(looptris, bmpinfo);
+ }
+ else {
+ bm_mesh_calc_tessellation_with_partial__multi_threaded(looptris, bmpinfo);
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Beauty Mesh Tessellation
+ *
+ * Avoid degenerate triangles.
+ * \{ */
+
+static int mesh_calc_tessellation_for_face_beauty(BMLoop *(*looptris)[3],
+ BMFace *efa,
+ MemArena **pf_arena_p,
+ Heap **pf_heap_p)
+{
+ switch (efa->len) {
+ case 3: {
+ BMLoop *l;
+ BMLoop **l_ptr = looptris[0];
+ l_ptr[0] = l = BM_FACE_FIRST_LOOP(efa);
+ l_ptr[1] = l = l->next;
+ l_ptr[2] = l->next;
+ return 1;
+ }
+ case 4: {
+ BMLoop *l_v1 = BM_FACE_FIRST_LOOP(efa);
+ BMLoop *l_v2 = l_v1->next;
+ BMLoop *l_v3 = l_v2->next;
+ BMLoop *l_v4 = l_v1->prev;
+
+ /* #BM_verts_calc_rotate_beauty performs excessive checks we don't need!
+ * It's meant for rotating edges, it also calculates a new normal.
+ *
+ * Use #BLI_polyfill_beautify_quad_rotate_calc since we have the normal.
+ */
+#if 0
+ const bool split_13 = (BM_verts_calc_rotate_beauty(
+ l_v1->v, l_v2->v, l_v3->v, l_v4->v, 0, 0) < 0.0f);
+#else
+ float axis_mat[3][3], v_quad[4][2];
+ axis_dominant_v3_to_m3(axis_mat, efa->no);
+ mul_v2_m3v3(v_quad[0], axis_mat, l_v1->v->co);
+ mul_v2_m3v3(v_quad[1], axis_mat, l_v2->v->co);
+ mul_v2_m3v3(v_quad[2], axis_mat, l_v3->v->co);
+ mul_v2_m3v3(v_quad[3], axis_mat, l_v4->v->co);
+
+ const bool split_13 = BLI_polyfill_beautify_quad_rotate_calc(
+ v_quad[0], v_quad[1], v_quad[2], v_quad[3]) < 0.0f;
+#endif
+
+ BMLoop **l_ptr_a = looptris[0];
+ BMLoop **l_ptr_b = looptris[1];
+ if (split_13) {
+ l_ptr_a[0] = l_v1;
+ l_ptr_a[1] = l_v2;
+ l_ptr_a[2] = l_v3;
+
+ l_ptr_b[0] = l_v1;
+ l_ptr_b[1] = l_v3;
+ l_ptr_b[2] = l_v4;
+ }
+ else {
+ l_ptr_a[0] = l_v1;
+ l_ptr_a[1] = l_v2;
+ l_ptr_a[2] = l_v4;
+
+ l_ptr_b[0] = l_v2;
+ l_ptr_b[1] = l_v3;
+ l_ptr_b[2] = l_v4;
+ }
+ return 2;
+ }
+ default: {
+ MemArena *pf_arena = *pf_arena_p;
+ Heap *pf_heap = *pf_heap_p;
+ if (UNLIKELY(pf_arena == NULL)) {
+ pf_arena = *pf_arena_p = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
+ pf_heap = *pf_heap_p = BLI_heap_new_ex(BLI_POLYFILL_ALLOC_NGON_RESERVE);
+ }
+
+ BMLoop *l_iter, *l_first;
+ BMLoop **l_arr;
+
+ float axis_mat[3][3];
+ float(*projverts)[2];
+ uint(*tris)[3];
+
+ const int tris_len = efa->len - 2;
+
+ tris = BLI_memarena_alloc(pf_arena, sizeof(*tris) * tris_len);
+ l_arr = BLI_memarena_alloc(pf_arena, sizeof(*l_arr) * efa->len);
+ projverts = BLI_memarena_alloc(pf_arena, sizeof(*projverts) * efa->len);
+
+ axis_dominant_v3_to_m3_negate(axis_mat, efa->no);
+
+ int i = 0;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
+ do {
+ l_arr[i] = l_iter;
+ mul_v2_m3v3(projverts[i], axis_mat, l_iter->v->co);
+ i++;
+ } while ((l_iter = l_iter->next) != l_first);
+
+ BLI_polyfill_calc_arena(projverts, efa->len, 1, tris, pf_arena);
+
+ BLI_polyfill_beautify(projverts, efa->len, tris, pf_arena, pf_heap);
+
+ for (i = 0; i < tris_len; i++) {
+ BMLoop **l_ptr = looptris[i];
+ uint *tri = tris[i];
+
+ l_ptr[0] = l_arr[tri[0]];
+ l_ptr[1] = l_arr[tri[1]];
+ l_ptr[2] = l_arr[tri[2]];
+ }
+
+ BLI_memarena_clear(pf_arena);
+
+ return tris_len;
+ }
+ }
+}
+
+/**
+ * A version of #BM_mesh_calc_tessellation that avoids degenerate triangles.
+ */
+void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3])
+{
+#ifndef NDEBUG
+ const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop);
+#endif
+
+ BMIter iter;
+ BMFace *efa;
+ int i = 0;
+
+ MemArena *pf_arena = NULL;
+
+ /* use_beauty */
+ Heap *pf_heap = NULL;
+
+ BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
+ BLI_assert(efa->len >= 3);
+ i += mesh_calc_tessellation_for_face_beauty(looptris + i, efa, &pf_arena, &pf_heap);
+ }
+
+ if (pf_arena) {
+ BLI_memarena_free(pf_arena);
+
+ BLI_heap_free(pf_heap, NULL);
+ }
+
+ BLI_assert(i <= looptris_tot);
+}
+
+/** \} */
diff --git a/source/blender/bmesh/intern/bmesh_mesh_tessellate.h b/source/blender/bmesh/intern/bmesh_mesh_tessellate.h
new file mode 100644
index 00000000000..f68a91cb988
--- /dev/null
+++ b/source/blender/bmesh/intern/bmesh_mesh_tessellate.h
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+/** \file
+ * \ingroup bmesh
+ */
+
+struct BMPartialUpdate;
+
+void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3]);
+void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3]);
+
+void BM_mesh_calc_tessellation_with_partial(BMesh *bm,
+ BMLoop *(*looptris)[3],
+ const struct BMPartialUpdate *bmpinfo);
diff --git a/source/blender/bmesh/intern/bmesh_mods.c b/source/blender/bmesh/intern/bmesh_mods.c
index 8e5ed9c3bf0..76e32667804 100644
--- a/source/blender/bmesh/intern/bmesh_mods.c
+++ b/source/blender/bmesh/intern/bmesh_mods.c
@@ -72,7 +72,7 @@ bool BM_vert_dissolve(BMesh *bm, BMVert *v)
}
if (!v->e->l) {
if (len == 2) {
- return (BM_vert_collapse_edge(bm, v->e, v, true, true) != NULL);
+ return (BM_vert_collapse_edge(bm, v->e, v, true, true, true) != NULL);
}
/* used to kill the vertex here, but it may be connected to faces.
* so better do nothing */
@@ -82,7 +82,7 @@ bool BM_vert_dissolve(BMesh *bm, BMVert *v)
}
if (len == 2 && BM_vert_face_count_is_equal(v, 1)) {
/* boundary vertex on a face */
- return (BM_vert_collapse_edge(bm, v->e, v, true, true) != NULL);
+ return (BM_vert_collapse_edge(bm, v->e, v, true, true, true) != NULL);
}
return BM_disk_dissolve(bm, v);
}
@@ -133,7 +133,7 @@ bool BM_disk_dissolve(BMesh *bm, BMVert *v)
if (UNLIKELY(!BM_faces_join_pair(bm, e->l, e->l->radial_next, true))) {
return false;
}
- if (UNLIKELY(!BM_vert_collapse_faces(bm, v->e, v, 1.0, true, false, true))) {
+ if (UNLIKELY(!BM_vert_collapse_faces(bm, v->e, v, 1.0, true, false, true, true))) {
return false;
}
#endif
@@ -141,7 +141,7 @@ bool BM_disk_dissolve(BMesh *bm, BMVert *v)
}
if (keepedge == NULL && len == 2) {
/* collapse the vertex */
- e = BM_vert_collapse_faces(bm, v->e, v, 1.0, true, true, true);
+ e = BM_vert_collapse_faces(bm, v->e, v, 1.0, true, true, true, true);
if (!e) {
return false;
@@ -184,7 +184,8 @@ bool BM_disk_dissolve(BMesh *bm, BMVert *v)
/* collapse the vertex */
/* note, the baseedge can be a boundary of manifold, use this as join_faces arg */
- e = BM_vert_collapse_faces(bm, baseedge, v, 1.0, true, !BM_edge_is_boundary(baseedge), true);
+ e = BM_vert_collapse_faces(
+ bm, baseedge, v, 1.0, true, !BM_edge_is_boundary(baseedge), true, true);
if (!e) {
return false;
@@ -432,7 +433,8 @@ BMEdge *BM_vert_collapse_faces(BMesh *bm,
float fac,
const bool do_del,
const bool join_faces,
- const bool kill_degenerate_faces)
+ const bool kill_degenerate_faces,
+ const bool kill_duplicate_faces)
{
BMEdge *e_new = NULL;
BMVert *tv = BM_edge_other_vert(e_kill, v_kill);
@@ -503,7 +505,8 @@ BMEdge *BM_vert_collapse_faces(BMesh *bm,
/* same as BM_vert_collapse_edge() however we already
* have vars to perform this operation so don't call. */
e_new = bmesh_kernel_join_edge_kill_vert(
- bm, e_kill, v_kill, do_del, true, kill_degenerate_faces);
+ bm, e_kill, v_kill, do_del, true, kill_degenerate_faces, kill_duplicate_faces);
+
/* e_new = BM_edge_exists(tv, tv2); */ /* same as return above */
}
@@ -517,8 +520,12 @@ BMEdge *BM_vert_collapse_faces(BMesh *bm,
*
* \return The New Edge
*/
-BMEdge *BM_vert_collapse_edge(
- BMesh *bm, BMEdge *e_kill, BMVert *v_kill, const bool do_del, const bool kill_degenerate_faces)
+BMEdge *BM_vert_collapse_edge(BMesh *bm,
+ BMEdge *e_kill,
+ BMVert *v_kill,
+ const bool do_del,
+ const bool kill_degenerate_faces,
+ const bool kill_duplicate_faces)
{
/* nice example implementation but we want loops to have their customdata
* accounted for */
@@ -546,7 +553,8 @@ BMEdge *BM_vert_collapse_edge(
#else
/* with these args faces are never joined, same as above
* but account for loop customdata */
- return BM_vert_collapse_faces(bm, e_kill, v_kill, 1.0f, do_del, false, kill_degenerate_faces);
+ return BM_vert_collapse_faces(
+ bm, e_kill, v_kill, 1.0f, do_del, false, kill_degenerate_faces, kill_duplicate_faces);
#endif
}
diff --git a/source/blender/bmesh/intern/bmesh_mods.h b/source/blender/bmesh/intern/bmesh_mods.h
index 7491c309754..4328187b95e 100644
--- a/source/blender/bmesh/intern/bmesh_mods.h
+++ b/source/blender/bmesh/intern/bmesh_mods.h
@@ -51,12 +51,14 @@ BMEdge *BM_vert_collapse_faces(BMesh *bm,
float fac,
const bool do_del,
const bool join_faces,
- const bool kill_degenerate_faces);
+ const bool kill_degenerate_faces,
+ const bool kill_duplicate_faces);
BMEdge *BM_vert_collapse_edge(BMesh *bm,
BMEdge *e_kill,
BMVert *v_kill,
const bool do_del,
- const bool kill_degenerate_faces);
+ const bool kill_degenerate_faces,
+ const bool kill_duplicate_faces);
BMVert *BM_edge_collapse(BMesh *bm,
BMEdge *e_kill,
diff --git a/source/blender/bmesh/intern/bmesh_operators.c b/source/blender/bmesh/intern/bmesh_operators.c
index c2421939aa8..e47fd1c035d 100644
--- a/source/blender/bmesh/intern/bmesh_operators.c
+++ b/source/blender/bmesh/intern/bmesh_operators.c
@@ -421,10 +421,10 @@ void BMO_slot_mat_set(BMOperator *op,
slot->data.p = BLI_memarena_alloc(op->arena, sizeof(float[4][4]));
if (size == 4) {
- copy_m4_m4(slot->data.p, (float(*)[4])mat);
+ copy_m4_m4(slot->data.p, (const float(*)[4])mat);
}
else if (size == 3) {
- copy_m4_m3(slot->data.p, (float(*)[3])mat);
+ copy_m4_m3(slot->data.p, (const float(*)[3])mat);
}
else {
fprintf(stderr, "%s: invalid size argument %d (bmesh internal error)\n", __func__, size);
diff --git a/source/blender/bmesh/intern/bmesh_polygon.c b/source/blender/bmesh/intern/bmesh_polygon.c
index 4ae2cc67140..5397098a7f3 100644
--- a/source/blender/bmesh/intern/bmesh_polygon.c
+++ b/source/blender/bmesh/intern/bmesh_polygon.c
@@ -18,8 +18,7 @@
* \ingroup bmesh
*
* This file contains code for dealing
- * with polygons (normal/area calculation,
- * tessellation, etc)
+ * with polygons (normal/area calculation, tessellation, etc)
*/
#include "DNA_listBase.h"
@@ -1523,289 +1522,3 @@ void BM_face_as_array_loop_quad(BMFace *f, BMLoop *r_loops[4])
l = l->next;
r_loops[3] = l;
}
-
-/**
- * \brief BM_mesh_calc_tessellation get the looptris and its number from a certain bmesh
- * \param looptris:
- *
- * \note \a looptris Must be pre-allocated to at least the size of given by: poly_to_tri_count
- */
-void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3], int *r_looptris_tot)
-{
- /* use this to avoid locking pthread for _every_ polygon
- * and calling the fill function */
-#define USE_TESSFACE_SPEEDUP
-
- /* this assumes all faces can be scan-filled, which isn't always true,
- * worst case we over alloc a little which is acceptable */
-#ifndef NDEBUG
- const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop);
-#endif
-
- BMIter iter;
- BMFace *efa;
- int i = 0;
-
- MemArena *arena = NULL;
-
- BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
- /* don't consider two-edged faces */
- if (UNLIKELY(efa->len < 3)) {
- /* do nothing */
- }
-
-#ifdef USE_TESSFACE_SPEEDUP
-
- /* no need to ensure the loop order, we know its ok */
-
- else if (efa->len == 3) {
-# if 0
- int j;
- BM_ITER_ELEM_INDEX(l, &liter, efa, BM_LOOPS_OF_FACE, j) {
- looptris[i][j] = l;
- }
- i += 1;
-# else
- /* more cryptic but faster */
- BMLoop *l;
- BMLoop **l_ptr = looptris[i++];
- l_ptr[0] = l = BM_FACE_FIRST_LOOP(efa);
- l_ptr[1] = l = l->next;
- l_ptr[2] = l->next;
-# endif
- }
- else if (efa->len == 4) {
-# if 0
- BMLoop *ltmp[4];
- int j;
- BLI_array_grow_items(looptris, 2);
- BM_ITER_ELEM_INDEX(l, &liter, efa, BM_LOOPS_OF_FACE, j) {
- ltmp[j] = l;
- }
-
- looptris[i][0] = ltmp[0];
- looptris[i][1] = ltmp[1];
- looptris[i][2] = ltmp[2];
- i += 1;
-
- looptris[i][0] = ltmp[0];
- looptris[i][1] = ltmp[2];
- looptris[i][2] = ltmp[3];
- i += 1;
-# else
- /* more cryptic but faster */
- BMLoop *l;
- BMLoop **l_ptr_a = looptris[i++];
- BMLoop **l_ptr_b = looptris[i++];
- (l_ptr_a[0] = l_ptr_b[0] = l = BM_FACE_FIRST_LOOP(efa));
- (l_ptr_a[1] = l = l->next);
- (l_ptr_a[2] = l_ptr_b[1] = l = l->next);
- (l_ptr_b[2] = l->next);
-# endif
-
- if (UNLIKELY(is_quad_flip_v3_first_third_fast(
- l_ptr_a[0]->v->co, l_ptr_a[1]->v->co, l_ptr_a[2]->v->co, l_ptr_b[2]->v->co))) {
- /* flip out of degenerate 0-2 state. */
- l_ptr_a[2] = l_ptr_b[2];
- l_ptr_b[0] = l_ptr_a[1];
- }
- }
-
-#endif /* USE_TESSFACE_SPEEDUP */
-
- else {
- int j;
-
- BMLoop *l_iter;
- BMLoop *l_first;
- BMLoop **l_arr;
-
- float axis_mat[3][3];
- float(*projverts)[2];
- uint(*tris)[3];
-
- const int totfilltri = efa->len - 2;
-
- if (UNLIKELY(arena == NULL)) {
- arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
- }
-
- tris = BLI_memarena_alloc(arena, sizeof(*tris) * totfilltri);
- l_arr = BLI_memarena_alloc(arena, sizeof(*l_arr) * efa->len);
- projverts = BLI_memarena_alloc(arena, sizeof(*projverts) * efa->len);
-
- axis_dominant_v3_to_m3_negate(axis_mat, efa->no);
-
- j = 0;
- l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
- do {
- l_arr[j] = l_iter;
- mul_v2_m3v3(projverts[j], axis_mat, l_iter->v->co);
- j++;
- } while ((l_iter = l_iter->next) != l_first);
-
- BLI_polyfill_calc_arena(projverts, efa->len, 1, tris, arena);
-
- for (j = 0; j < totfilltri; j++) {
- BMLoop **l_ptr = looptris[i++];
- uint *tri = tris[j];
-
- l_ptr[0] = l_arr[tri[0]];
- l_ptr[1] = l_arr[tri[1]];
- l_ptr[2] = l_arr[tri[2]];
- }
-
- BLI_memarena_clear(arena);
- }
- }
-
- if (arena) {
- BLI_memarena_free(arena);
- arena = NULL;
- }
-
- *r_looptris_tot = i;
-
- BLI_assert(i <= looptris_tot);
-
-#undef USE_TESSFACE_SPEEDUP
-}
-
-/**
- * A version of #BM_mesh_calc_tessellation that avoids degenerate triangles.
- */
-void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3], int *r_looptris_tot)
-{
- /* this assumes all faces can be scan-filled, which isn't always true,
- * worst case we over alloc a little which is acceptable */
-#ifndef NDEBUG
- const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop);
-#endif
-
- BMIter iter;
- BMFace *efa;
- int i = 0;
-
- MemArena *pf_arena = NULL;
-
- /* use_beauty */
- Heap *pf_heap = NULL;
-
- BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
- /* don't consider two-edged faces */
- if (UNLIKELY(efa->len < 3)) {
- /* do nothing */
- }
- else if (efa->len == 3) {
- BMLoop *l;
- BMLoop **l_ptr = looptris[i++];
- l_ptr[0] = l = BM_FACE_FIRST_LOOP(efa);
- l_ptr[1] = l = l->next;
- l_ptr[2] = l->next;
- }
- else if (efa->len == 4) {
- BMLoop *l_v1 = BM_FACE_FIRST_LOOP(efa);
- BMLoop *l_v2 = l_v1->next;
- BMLoop *l_v3 = l_v2->next;
- BMLoop *l_v4 = l_v1->prev;
-
- /* #BM_verts_calc_rotate_beauty performs excessive checks we don't need!
- * It's meant for rotating edges, it also calculates a new normal.
- *
- * Use #BLI_polyfill_beautify_quad_rotate_calc since we have the normal.
- */
-#if 0
- const bool split_13 = (BM_verts_calc_rotate_beauty(
- l_v1->v, l_v2->v, l_v3->v, l_v4->v, 0, 0) < 0.0f);
-#else
- float axis_mat[3][3], v_quad[4][2];
- axis_dominant_v3_to_m3(axis_mat, efa->no);
- mul_v2_m3v3(v_quad[0], axis_mat, l_v1->v->co);
- mul_v2_m3v3(v_quad[1], axis_mat, l_v2->v->co);
- mul_v2_m3v3(v_quad[2], axis_mat, l_v3->v->co);
- mul_v2_m3v3(v_quad[3], axis_mat, l_v4->v->co);
-
- const bool split_13 = BLI_polyfill_beautify_quad_rotate_calc(
- v_quad[0], v_quad[1], v_quad[2], v_quad[3]) < 0.0f;
-#endif
-
- BMLoop **l_ptr_a = looptris[i++];
- BMLoop **l_ptr_b = looptris[i++];
- if (split_13) {
- l_ptr_a[0] = l_v1;
- l_ptr_a[1] = l_v2;
- l_ptr_a[2] = l_v3;
-
- l_ptr_b[0] = l_v1;
- l_ptr_b[1] = l_v3;
- l_ptr_b[2] = l_v4;
- }
- else {
- l_ptr_a[0] = l_v1;
- l_ptr_a[1] = l_v2;
- l_ptr_a[2] = l_v4;
-
- l_ptr_b[0] = l_v2;
- l_ptr_b[1] = l_v3;
- l_ptr_b[2] = l_v4;
- }
- }
- else {
- int j;
-
- BMLoop *l_iter;
- BMLoop *l_first;
- BMLoop **l_arr;
-
- float axis_mat[3][3];
- float(*projverts)[2];
- unsigned int(*tris)[3];
-
- const int totfilltri = efa->len - 2;
-
- if (UNLIKELY(pf_arena == NULL)) {
- pf_arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
- pf_heap = BLI_heap_new_ex(BLI_POLYFILL_ALLOC_NGON_RESERVE);
- }
-
- tris = BLI_memarena_alloc(pf_arena, sizeof(*tris) * totfilltri);
- l_arr = BLI_memarena_alloc(pf_arena, sizeof(*l_arr) * efa->len);
- projverts = BLI_memarena_alloc(pf_arena, sizeof(*projverts) * efa->len);
-
- axis_dominant_v3_to_m3_negate(axis_mat, efa->no);
-
- j = 0;
- l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
- do {
- l_arr[j] = l_iter;
- mul_v2_m3v3(projverts[j], axis_mat, l_iter->v->co);
- j++;
- } while ((l_iter = l_iter->next) != l_first);
-
- BLI_polyfill_calc_arena(projverts, efa->len, 1, tris, pf_arena);
-
- BLI_polyfill_beautify(projverts, efa->len, tris, pf_arena, pf_heap);
-
- for (j = 0; j < totfilltri; j++) {
- BMLoop **l_ptr = looptris[i++];
- unsigned int *tri = tris[j];
-
- l_ptr[0] = l_arr[tri[0]];
- l_ptr[1] = l_arr[tri[1]];
- l_ptr[2] = l_arr[tri[2]];
- }
-
- BLI_memarena_clear(pf_arena);
- }
- }
-
- if (pf_arena) {
- BLI_memarena_free(pf_arena);
-
- BLI_heap_free(pf_heap, NULL);
- }
-
- *r_looptris_tot = i;
-
- BLI_assert(i <= looptris_tot);
-}
diff --git a/source/blender/bmesh/intern/bmesh_polygon.h b/source/blender/bmesh/intern/bmesh_polygon.h
index 8c2b9ee0bff..2c32cd39002 100644
--- a/source/blender/bmesh/intern/bmesh_polygon.h
+++ b/source/blender/bmesh/intern/bmesh_polygon.h
@@ -21,12 +21,10 @@
*/
struct Heap;
+struct BMPartialUpdate;
#include "BLI_compiler_attrs.h"
-void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3], int *r_looptris_tot);
-void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3], int *r_looptris_tot);
-
void BM_face_calc_tessellation(const BMFace *f,
const bool use_fixed_quad,
BMLoop **r_loops,
diff --git a/source/blender/bmesh/intern/bmesh_polygon_edgenet.c b/source/blender/bmesh/intern/bmesh_polygon_edgenet.c
index dcf9717465c..0754564fa47 100644
--- a/source/blender/bmesh/intern/bmesh_polygon_edgenet.c
+++ b/source/blender/bmesh/intern/bmesh_polygon_edgenet.c
@@ -106,7 +106,7 @@ static void normalize_v2_m3_v3v3(float out[2],
/**
* \note Be sure to update #bm_face_split_edgenet_find_loop_pair_exists
- * when making changed to edge picking logic.
+ * when making changes to edge picking logic.
*/
static bool bm_face_split_edgenet_find_loop_pair(BMVert *v_init,
const float face_normal[3],
@@ -456,8 +456,8 @@ static bool bm_face_split_edgenet_find_loop(BMVert *v_init,
* Splits a face into many smaller faces defined by an edge-net.
* handle customdata and degenerate cases.
*
- * - isolated holes or unsupported face configurations, will be ignored.
- * - customdata calculations aren't efficient
+ * - Isolated holes or unsupported face configurations, will be ignored.
+ * - Customdata calculations aren't efficient
* (need to calculate weights for each vert).
*/
bool BM_face_split_edgenet(BMesh *bm,
@@ -593,7 +593,7 @@ bool BM_face_split_edgenet(BMesh *bm,
BMIter iter;
BMLoop *l_other;
- /* see: #BM_loop_interp_from_face for similar logic */
+ /* See: #BM_loop_interp_from_face for similar logic. */
void **blocks = BLI_array_alloca(blocks, f->len);
float(*cos_2d)[2] = BLI_array_alloca(cos_2d, f->len);
float *w = BLI_array_alloca(w, f->len);
@@ -1064,7 +1064,7 @@ static int bm_face_split_edgenet_find_connection(const struct EdgeGroup_FindConn
#ifdef USE_PARTIAL_CONNECT
/**
- * Used to identify edges that get split off when making island from partial connection.
+ * Used to identify edges that get split off when making island from partial connection.
* fptr should be a BMFace*, but is a void* for general interface to BM_vert_separate_tested_edges
*/
static bool test_tagged_and_notface(BMEdge *e, void *fptr)
@@ -1211,7 +1211,7 @@ static bool bm_vert_partial_connect_check_overlap(const int *remap,
const int v_a_index,
const int v_b_index)
{
- /* connected to eachother */
+ /* Connected to each other. */
if (UNLIKELY((remap[v_a_index] == v_b_index) || (remap[v_b_index] == v_a_index))) {
return true;
}
@@ -1226,7 +1226,7 @@ static bool bm_vert_partial_connect_check_overlap(const int *remap,
* \param use_partial_connect: Support for handling islands connected by only a single edge,
* \note that this is quite slow so avoid using where possible.
* \param mem_arena: Avoids many small allocs & should be cleared after each use.
- * take care since \a r_edge_net_new is stored in \a r_edge_net_new.
+ * take care since \a edge_net_new is stored in \a r_edge_net_new.
*/
bool BM_face_split_edgenet_connect_islands(BMesh *bm,
BMFace *f,
@@ -1246,7 +1246,7 @@ bool BM_face_split_edgenet_connect_islands(BMesh *bm,
*
* Keep the first part fast since it will run very often for edge-nets that have no holes.
*
- * \note Don't use the mem_arena unless he have holes to fill.
+ * \note Don't use the mem_arena unless we have holes to fill.
* (avoid thrashing the area when the initial check isn't so intensive on the stack).
*/
@@ -1572,7 +1572,7 @@ bool BM_face_split_edgenet_connect_islands(BMesh *bm,
if (g->has_prev_edge == false) {
BMVert *v_origin = g->vert_span.min;
-
+ /* Index of BMVert for the edge group connection with `v_origin`. */
const int index_other = bm_face_split_edgenet_find_connection(&args, v_origin, false);
// BLI_assert(index_other >= 0 && index_other < (int)vert_arr_len);
@@ -1598,7 +1598,7 @@ bool BM_face_split_edgenet_connect_islands(BMesh *bm,
{
BMVert *v_origin = g->vert_span.max;
-
+ /* Index of BMVert for the edge group connection with `v_origin`. */
const int index_other = bm_face_split_edgenet_find_connection(&args, v_origin, true);
// BLI_assert(index_other >= 0 && index_other < (int)vert_arr_len);
@@ -1660,7 +1660,7 @@ finally:
struct TempVertPair *tvp = temp_vert_pairs.list;
do {
/* its _very_ unlikely the edge exists,
- * however splicing may case this. see: T48012 */
+ * however splicing may cause this. see: T48012 */
if (!BM_edge_exists(tvp->v_orig, tvp->v_temp)) {
BM_vert_splice(bm, tvp->v_orig, tvp->v_temp);
}
diff --git a/source/blender/bmesh/operators/bmo_connect_pair.c b/source/blender/bmesh/operators/bmo_connect_pair.c
index 1c861df0150..8a0673c9b33 100644
--- a/source/blender/bmesh/operators/bmo_connect_pair.c
+++ b/source/blender/bmesh/operators/bmo_connect_pair.c
@@ -180,8 +180,8 @@ static void min_dist_dir_update(MinDistDir *dist, const float dist_dir[3])
static int state_isect_co_pair(const PathContext *pc, const float co_a[3], const float co_b[3])
{
- const float diff_a = dot_m3_v3_row_x((float(*)[3])pc->matrix, co_a) - pc->axis_sep;
- const float diff_b = dot_m3_v3_row_x((float(*)[3])pc->matrix, co_b) - pc->axis_sep;
+ const float diff_a = dot_m3_v3_row_x(pc->matrix, co_a) - pc->axis_sep;
+ const float diff_b = dot_m3_v3_row_x(pc->matrix, co_b) - pc->axis_sep;
const int test_a = (fabsf(diff_a) < CONNECT_EPS) ? 0 : (diff_a < 0.0f) ? -1 : 1;
const int test_b = (fabsf(diff_b) < CONNECT_EPS) ? 0 : (diff_b < 0.0f) ? -1 : 1;
@@ -194,7 +194,7 @@ static int state_isect_co_pair(const PathContext *pc, const float co_a[3], const
static int state_isect_co_exact(const PathContext *pc, const float co[3])
{
- const float diff = dot_m3_v3_row_x((float(*)[3])pc->matrix, co) - pc->axis_sep;
+ const float diff = dot_m3_v3_row_x(pc->matrix, co) - pc->axis_sep;
return (fabsf(diff) <= CONNECT_EPS);
}
@@ -204,8 +204,8 @@ static float state_calc_co_pair_fac(const PathContext *pc,
{
float diff_a, diff_b, diff_tot;
- diff_a = fabsf(dot_m3_v3_row_x((float(*)[3])pc->matrix, co_a) - pc->axis_sep);
- diff_b = fabsf(dot_m3_v3_row_x((float(*)[3])pc->matrix, co_b) - pc->axis_sep);
+ diff_a = fabsf(dot_m3_v3_row_x(pc->matrix, co_a) - pc->axis_sep);
+ diff_b = fabsf(dot_m3_v3_row_x(pc->matrix, co_b) - pc->axis_sep);
diff_tot = (diff_a + diff_b);
return (diff_tot > FLT_EPSILON) ? (diff_a / diff_tot) : 0.5f;
}
diff --git a/source/blender/bmesh/operators/bmo_dissolve.c b/source/blender/bmesh/operators/bmo_dissolve.c
index da2603ad8cd..7813e30e2a8 100644
--- a/source/blender/bmesh/operators/bmo_dissolve.c
+++ b/source/blender/bmesh/operators/bmo_dissolve.c
@@ -242,7 +242,7 @@ void bmo_dissolve_faces_exec(BMesh *bm, BMOperator *op)
BM_ITER_MESH_MUTABLE (v, v_next, &viter, bm, BM_VERTS_OF_MESH) {
if (BMO_vert_flag_test(bm, v, VERT_MARK)) {
if (BM_vert_is_edge_pair(v)) {
- BM_vert_collapse_edge(bm, v->e, v, true, true);
+ BM_vert_collapse_edge(bm, v->e, v, true, true, true);
}
}
}
@@ -327,6 +327,10 @@ void bmo_dissolve_edges_exec(BMesh *bm, BMOperator *op)
/* join faces */
f_new = BM_faces_join_pair(bm, l_a, l_b, false);
+ if (f_new && BM_face_find_double(f_new)) {
+ BM_face_kill(bm, f_new);
+ f_new = NULL;
+ }
if (f_new) {
/* maintain active face */
@@ -355,7 +359,7 @@ void bmo_dissolve_edges_exec(BMesh *bm, BMOperator *op)
BM_ITER_MESH_MUTABLE (v, v_next, &iter, bm, BM_VERTS_OF_MESH) {
if (BMO_vert_flag_test(bm, v, VERT_MARK)) {
if (BM_vert_is_edge_pair(v)) {
- BM_vert_collapse_edge(bm, v->e, v, true, true);
+ BM_vert_collapse_edge(bm, v->e, v, true, true, true);
}
}
}
@@ -441,10 +445,16 @@ void bmo_dissolve_verts_exec(BMesh *bm, BMOperator *op)
/* join faces */
f_new = BM_faces_join_pair(bm, l_a, l_b, false);
+ if (f_new && BM_face_find_double(f_new)) {
+ BM_face_kill(bm, f_new);
+ f_new = NULL;
+ }
- /* maintain active face */
- if (act_face && bm->act_face == NULL) {
- bm->act_face = f_new;
+ if (f_new) {
+ /* maintain active face */
+ if (act_face && bm->act_face == NULL) {
+ bm->act_face = f_new;
+ }
}
}
}
@@ -462,7 +472,7 @@ void bmo_dissolve_verts_exec(BMesh *bm, BMOperator *op)
/* final cleanup */
BMO_ITER (v, &oiter, op->slots_in, "verts", BM_VERT) {
if (BM_vert_is_edge_pair(v)) {
- BM_vert_collapse_edge(bm, v->e, v, false, true);
+ BM_vert_collapse_edge(bm, v->e, v, false, true, true);
}
}
diff --git a/source/blender/bmesh/operators/bmo_offset_edgeloops.c b/source/blender/bmesh/operators/bmo_offset_edgeloops.c
index d723e128bf1..2c7e478b549 100644
--- a/source/blender/bmesh/operators/bmo_offset_edgeloops.c
+++ b/source/blender/bmesh/operators/bmo_offset_edgeloops.c
@@ -263,7 +263,7 @@ void bmo_offset_edgeloops_exec(BMesh *bm, BMOperator *op)
}
while ((v = STACK_POP(varr))) {
- bmesh_kernel_join_edge_kill_vert(bm, v->e, v, true, false, false);
+ bmesh_kernel_join_edge_kill_vert(bm, v->e, v, true, false, false, true);
}
}
}
diff --git a/source/blender/bmesh/operators/bmo_primitive.c b/source/blender/bmesh/operators/bmo_primitive.c
index a0a31ab6154..f47c8dfb405 100644
--- a/source/blender/bmesh/operators/bmo_primitive.c
+++ b/source/blender/bmesh/operators/bmo_primitive.c
@@ -867,19 +867,19 @@ void bmo_create_uvsphere_exec(BMesh *bm, BMOperator *op)
BMIter iter;
const float axis[3] = {0, 0, 1};
float vec[3], mat[4][4], cmat[3][3];
- float phi, phid;
int a;
BMO_slot_mat4_get(op->slots_in, "matrix", mat);
- phid = 2.0f * (float)M_PI / tot;
+ const float phid = (float)M_PI / tot;
/* phi = 0.25f * (float)M_PI; */ /* UNUSED */
/* one segment first */
- phi = 0;
- phid /= 2;
for (a = 0; a <= tot; a++) {
/* Going in this direction, then edge extruding, makes normals face outward */
+ /* Calculate with doubles for higher precision, see: T87779. */
+ const float phi = M_PI * ((double)a / (double)tot);
+
vec[0] = 0.0;
vec[1] = dia * sinf(phi);
vec[2] = dia * cosf(phi);
@@ -891,7 +891,6 @@ void bmo_create_uvsphere_exec(BMesh *bm, BMOperator *op)
BMO_edge_flag_enable(bm, e, EDGE_ORIG);
}
- phi += phid;
preveve = eve;
}
@@ -1272,7 +1271,7 @@ void bmo_create_circle_exec(BMesh *bm, BMOperator *op)
const bool calc_uvs = (cd_loop_uv_offset != -1) && BMO_slot_bool_get(op->slots_in, "calc_uvs");
BMVert *v1, *lastv1 = NULL, *cent1, *firstv1 = NULL;
- float vec[3], mat[4][4], phi, phid;
+ float vec[3], mat[4][4];
int a;
if (!segs) {
@@ -1281,9 +1280,6 @@ void bmo_create_circle_exec(BMesh *bm, BMOperator *op)
BMO_slot_mat4_get(op->slots_in, "matrix", mat);
- phid = 2.0f * (float)M_PI / segs;
- phi = 0;
-
if (cap_ends) {
zero_v3(vec);
mul_m4_v3(mat, vec);
@@ -1292,8 +1288,11 @@ void bmo_create_circle_exec(BMesh *bm, BMOperator *op)
BMO_vert_flag_enable(bm, cent1, VERT_MARK);
}
- for (a = 0; a < segs; a++, phi += phid) {
+ for (a = 0; a < segs; a++) {
/* Going this way ends up with normal(s) upward */
+
+ /* Calculate with doubles for higher precision, see: T87779. */
+ const float phi = (2.0 * M_PI) * ((double)a / (double)segs);
vec[0] = -radius * sinf(phi);
vec[1] = radius * cosf(phi);
vec[2] = 0.0f;
@@ -1392,7 +1391,7 @@ void bmo_create_cone_exec(BMesh *bm, BMOperator *op)
{
BMVert *v1, *v2, *lastv1 = NULL, *lastv2 = NULL, *cent1, *cent2, *firstv1, *firstv2;
BMFace *f;
- float vec[3], mat[4][4], phi, phid;
+ float vec[3], mat[4][4];
const float dia1 = BMO_slot_float_get(op->slots_in, "diameter1");
const float dia2 = BMO_slot_float_get(op->slots_in, "diameter2");
const float depth_half = 0.5f * BMO_slot_float_get(op->slots_in, "depth");
@@ -1409,9 +1408,6 @@ void bmo_create_cone_exec(BMesh *bm, BMOperator *op)
BMO_slot_mat4_get(op->slots_in, "matrix", mat);
- phid = 2.0f * (float)M_PI / segs;
- phi = 0;
-
if (cap_ends) {
vec[0] = vec[1] = 0.0f;
vec[2] = -depth_half;
@@ -1432,7 +1428,10 @@ void bmo_create_cone_exec(BMesh *bm, BMOperator *op)
const int side_faces_len = segs - 1;
BMFace **side_faces = MEM_mallocN(sizeof(*side_faces) * side_faces_len, __func__);
- for (int i = 0; i < segs; i++, phi += phid) {
+ for (int i = 0; i < segs; i++) {
+ /* Calculate with doubles for higher precision, see: T87779. */
+ const float phi = (2.0 * M_PI) * ((double)i / (double)segs);
+
vec[0] = dia1 * sinf(phi);
vec[1] = dia1 * cosf(phi);
vec[2] = -depth_half;
diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c
index cef97f20a6a..d39fb5e81f1 100644
--- a/source/blender/bmesh/tools/bmesh_bevel.c
+++ b/source/blender/bmesh/tools/bmesh_bevel.c
@@ -7361,7 +7361,6 @@ static float geometry_collide_offset(BevelParams *bp, EdgeHalf *eb)
static float vertex_collide_offset(BevelParams *bp, EdgeHalf *ea)
{
float no_collide_offset = bp->offset + 1e6;
- float limit = no_collide_offset;
if (bp->offset == 0.0f) {
return no_collide_offset;
}
@@ -7373,8 +7372,7 @@ static float vertex_collide_offset(BevelParams *bp, EdgeHalf *ea)
if (kab <= 0.0f) {
return no_collide_offset;
}
- limit = la / kab;
- return limit;
+ return la / kab;
}
/**
diff --git a/source/blender/bmesh/tools/bmesh_bisect_plane.c b/source/blender/bmesh/tools/bmesh_bisect_plane.c
index 85de065daff..e7d0fe6a0c6 100644
--- a/source/blender/bmesh/tools/bmesh_bisect_plane.c
+++ b/source/blender/bmesh/tools/bmesh_bisect_plane.c
@@ -39,12 +39,13 @@
#include "BLI_utildefines_stack.h"
#include "bmesh.h"
-#include "bmesh_bisect_plane.h" /* own include */
+#include "bmesh_bisect_plane.h" /* Own include. */
-#include "BLI_strict_flags.h" /* keep last */
+#include "BLI_strict_flags.h" /* Keep last. */
/* -------------------------------------------------------------------- */
-/* Math utils */
+/** \name Math Functions
+ * \{ */
static short plane_point_test_v3(const float plane[4],
const float co[3],
@@ -63,10 +64,15 @@ static short plane_point_test_v3(const float plane[4],
return 0;
}
+/** \} */
+
/* -------------------------------------------------------------------- */
-/* Wrappers to hide internal data-structure abuse,
+/** \name BMesh Element Accessors
+ *
+ * Wrappers to hide internal data-structure abuse,
* later we may want to move this into some hash lookup
- * to a separate struct, but for now we can store in BMesh data */
+ * to a separate struct, but for now we can store in #BMesh data.
+ * \{ */
#define BM_VERT_DIR(v) ((short *)(&(v)->head.index))[0] /* Direction -1/0/1 */
#define BM_VERT_SKIP(v) ((short *)(&(v)->head.index))[1] /* Skip Vert 0/1 */
@@ -75,12 +81,16 @@ static short plane_point_test_v3(const float plane[4],
#define BM_VERT_LOOPINDEX(v) /* The verts index within a face (temp var) */ \
(*((uint *)(&(v)->no[2])))
-/**
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name BMesh Flag Accessors
+ *
* Hide flag access
- * (for more readable code since same flag is used differently for vert/edgeface)...
+ * (for more readable code since same flag is used differently for vert/edge-face).
*/
-/* enable when vertex is in the center and its faces have been added to the stack */
+/** Enable when vertex is in the center and its faces have been added to the stack. */
BLI_INLINE void vert_is_center_enable(BMVert *v)
{
BM_elem_flag_enable(v, BM_ELEM_TAG);
@@ -94,7 +104,13 @@ BLI_INLINE bool vert_is_center_test(BMVert *v)
return (BM_elem_flag_test(v, BM_ELEM_TAG) != 0);
}
-/* enable when the edge can be cut */
+BLI_INLINE bool vert_pair_adjacent_in_orig_face(BMVert *v_a, BMVert *v_b, const uint f_len_orig)
+{
+ const uint delta = (uint)abs((int)BM_VERT_LOOPINDEX(v_a) - (int)BM_VERT_LOOPINDEX(v_b));
+ return ELEM(delta, 1, (uint)(f_len_orig - 1));
+}
+
+/** Enable when the edge can be cut. */
BLI_INLINE void edge_is_cut_enable(BMEdge *e)
{
BM_elem_flag_enable(e, BM_ELEM_TAG);
@@ -108,7 +124,7 @@ BLI_INLINE bool edge_is_cut_test(BMEdge *e)
return (BM_elem_flag_test(e, BM_ELEM_TAG) != 0);
}
-/* enable when the faces are added to the stack */
+/** Enable when the faces are added to the stack. */
BLI_INLINE void face_in_stack_enable(BMFace *f)
{
BM_elem_flag_disable(f, BM_ELEM_TAG);
@@ -122,8 +138,11 @@ BLI_INLINE bool face_in_stack_test(BMFace *f)
return (BM_elem_flag_test(f, BM_ELEM_TAG) == 0);
}
+/** \} */
+
/* -------------------------------------------------------------------- */
-/* BMesh utils */
+/** \name BMesh Face Bisect
+ * \{ */
static int bm_vert_sortval_cb(const void *v_a_v, const void *v_b_v)
{
@@ -142,32 +161,39 @@ static int bm_vert_sortval_cb(const void *v_a_v, const void *v_b_v)
static void bm_face_bisect_verts(
BMesh *bm, BMFace *f, const float plane[4], const short oflag_center, const short oflag_new)
{
- /* unlikely more than 2 verts are needed */
+ /* Unlikely more than 2 verts are needed. */
const uint f_len_orig = (uint)f->len;
BMVert **vert_split_arr = BLI_array_alloca(vert_split_arr, f_len_orig);
STACK_DECLARE(vert_split_arr);
BMLoop *l_iter, *l_first;
bool use_dirs[3] = {false, false, false};
bool is_inside = false;
+ /* True when the face contains one or more edges with both it's vertices on the plane.
+ * When set, centered loops are walked over to check if they need to be skipped. */
+ bool face_has_center_edge = false;
STACK_INIT(vert_split_arr, f_len_orig);
l_first = BM_FACE_FIRST_LOOP(f);
- /* add plane-aligned verts to the stack
- * and check we have verts from both sides in this face,
- * ... that the face doesn't only have boundary verts on the plane for eg. */
+ /* Add plane-aligned verts to the stack and check we have verts from both sides in this face
+ * (that the face doesn't only have boundary verts on the plane for eg). */
l_iter = l_first;
do {
if (vert_is_center_test(l_iter->v)) {
BLI_assert(BM_VERT_DIR(l_iter->v) == 0);
- /* if both are -1 or 1, or both are zero:
- * don't flip 'inside' var while walking */
+ /* If both are -1 or 1, or both are zero: don't flip 'inside' var while walking. */
BM_VERT_SKIP(l_iter->v) = (((BM_VERT_DIR(l_iter->prev->v) ^ BM_VERT_DIR(l_iter->next->v))) ==
0);
STACK_PUSH(vert_split_arr, l_iter->v);
+
+ if (face_has_center_edge == false) {
+ if (vert_is_center_test(l_iter->prev->v)) {
+ face_has_center_edge = true;
+ }
+ }
}
use_dirs[BM_VERT_DIR(l_iter->v) + 1] = true;
} while ((l_iter = l_iter->next) != l_first);
@@ -180,7 +206,7 @@ static void bm_face_bisect_verts(
l_a = BM_face_vert_share_loop(f, vert_split_arr[0]);
l_b = BM_face_vert_share_loop(f, vert_split_arr[1]);
- /* common case, just cut the face once */
+ /* Common case, just cut the face once. */
BM_face_split(bm, f, l_a, l_b, &l_new, NULL, true);
if (l_new) {
if (oflag_center | oflag_new) {
@@ -192,7 +218,63 @@ static void bm_face_bisect_verts(
}
}
else {
- /* less common case, _complicated_ we need to calculate how to do multiple cuts */
+ /* Less common case, _complicated_ we need to calculate how to do multiple cuts. */
+
+ uint i = 0;
+
+ /* ---- */
+ /* Check contiguous spans of centered vertices (skipping when necessary). */
+ if (face_has_center_edge) {
+
+ /* Loop indices need to be set for adjacency checks. */
+ l_iter = l_first;
+ do {
+ BM_VERT_LOOPINDEX(l_iter->v) = i++;
+ } while ((l_iter = l_iter->next) != l_first);
+
+ /* Start out on a non-centered vertex so a span of centered vertices can be looped over
+ * without having to scan backwards as well as forwards. */
+ BMLoop *l_first_non_center = l_first;
+ while (vert_is_center_test(l_first_non_center->v)) {
+ l_first_non_center = l_first_non_center->next;
+ }
+
+ l_iter = l_first_non_center;
+ do {
+ if (BM_VERT_SKIP(l_iter->v)) {
+ continue;
+ }
+ /* No need to check the previous as the iteration starts on a non-centered vertex. */
+ if (!(vert_is_center_test(l_iter->v) && vert_is_center_test(l_iter->next->v))) {
+ continue;
+ }
+
+ /* Walk over the next loops as long as they are centered. */
+ BMLoop *l_prev = l_iter->prev;
+ BMLoop *l_next = l_iter->next->next;
+ /* No need to scan the previous vertices,
+ * these will have been dealt with in previous steps. */
+ BLI_assert(!vert_is_center_test(l_prev->v));
+ while (vert_is_center_test(l_next->v)) {
+ l_next = l_next->next;
+ }
+
+ /* Skip all vertices when the edges connected to the beginning/end
+ * of the range are on a different side of the bisecting plane. */
+ if (!(BM_VERT_DIR(l_prev->v) ^ BM_VERT_DIR(l_next->v))) {
+ BLI_assert(!vert_is_center_test(l_prev->v));
+ l_iter = l_prev->next;
+ while (l_iter != l_next) {
+ BLI_assert(vert_is_center_test(l_iter->v));
+ BM_VERT_SKIP(l_iter->v) = true;
+ l_iter = l_iter->next;
+ }
+ }
+ /* Step over the span already handled, even if skip wasn't set. */
+ l_iter = l_next->prev;
+ } while ((l_iter = l_iter->next) != l_first_non_center);
+ }
+
float(*face_verts_proj_2d)[2] = BLI_array_alloca(face_verts_proj_2d, f_len_orig);
float axis_mat[3][3];
@@ -200,15 +282,12 @@ static void bm_face_bisect_verts(
STACK_DECLARE(face_split_arr);
float sort_dir[3];
- uint i;
/* ---- */
/* Calculate the direction to sort verts in the face intersecting the plane */
- /* exact dir isn't so important,
- * just need a dir for sorting verts across face,
- * 'sort_dir' could be flipped either way, it not important, we only need to order the array
- */
+ /* The exact direction isn't important, vertices just need to be sorted across the face.
+ * (`sort_dir` could be flipped either way). */
cross_v3_v3v3(sort_dir, f->no, plane);
if (UNLIKELY(normalize_v3(sort_dir) == 0.0f)) {
/* find any 2 verts and get their direction */
@@ -219,8 +298,8 @@ static void bm_face_bisect_verts(
}
}
if (UNLIKELY(i == STACK_SIZE(vert_split_arr))) {
- /* ok, we can't do anything useful here,
- * face has no area or so, bail out, this is highly unlikely but not impossible */
+ /* Ok, we can't do anything useful here,
+ * face has no area or so, bail out, this is highly unlikely but not impossible. */
goto finally;
}
}
@@ -228,20 +307,19 @@ static void bm_face_bisect_verts(
/* ---- */
/* Calculate 2d coords to use for intersection checks */
- /* get the faces 2d coords */
+ /* Get the faces 2d coords. */
BLI_assert(BM_face_is_normal_valid(f));
axis_dominant_v3_to_m3(axis_mat, f->no);
l_iter = l_first;
i = 0;
do {
- BM_VERT_LOOPINDEX(l_iter->v) = i;
mul_v2_m3v3(face_verts_proj_2d[i], axis_mat, l_iter->v->co);
i++;
} while ((l_iter = l_iter->next) != l_first);
/* ---- */
- /* Sort the verts across the face from one side to another */
+ /* Sort the verts across the face from one side to another. */
for (i = 0; i < STACK_SIZE(vert_split_arr); i++) {
BMVert *v = vert_split_arr[i];
BM_VERT_SORTVAL(v) = dot_v3v3(sort_dir, v->co);
@@ -251,9 +329,9 @@ static void bm_face_bisect_verts(
vert_split_arr, STACK_SIZE(vert_split_arr), sizeof(*vert_split_arr), bm_vert_sortval_cb);
/* ---- */
- /* Split the face across sorted splits */
+ /* Split the face across sorted splits. */
- /* note: we don't know which face gets which splits,
+ /* NOTE: we don't know which face gets which splits,
* so at the moment we have to search all faces for the vert pair,
* while not all that nice, typically there are < 5 resulting faces,
* so its not _that_ bad. */
@@ -265,6 +343,12 @@ static void bm_face_bisect_verts(
BMVert *v_a = vert_split_arr[i];
BMVert *v_b = vert_split_arr[i + 1];
+ if (face_has_center_edge) {
+ if (vert_pair_adjacent_in_orig_face(v_a, v_b, f_len_orig)) {
+ continue;
+ }
+ }
+
if (!BM_VERT_SKIP(v_a)) {
is_inside = !is_inside;
}
@@ -275,8 +359,8 @@ static void bm_face_bisect_verts(
uint j;
for (j = 0; j < STACK_SIZE(face_split_arr); j++) {
- /* would be nice to avoid loop lookup here,
- * but we need to know which face the verts are in */
+ /* It would be nice to avoid loop lookup here,
+ * but we need to know which face the verts are in. */
if ((l_a = BM_face_vert_share_loop(face_split_arr[j], v_a)) &&
(l_b = BM_face_vert_share_loop(face_split_arr[j], v_b))) {
found = true;
@@ -284,11 +368,10 @@ static void bm_face_bisect_verts(
}
}
- /* ideally wont happen, but it can for self intersecting faces */
+ /* Ideally wont happen, but it can for self intersecting faces. */
// BLI_assert(found == true);
- /* in fact this simple test is good enough,
- * test if the loops are adjacent */
+ /* In fact this simple test is good enough, test if the loops are adjacent. */
if (found && !BM_loop_is_adjacent(l_a, l_b)) {
BMLoop *l_new;
BMFace *f_tmp;
@@ -322,8 +405,11 @@ finally:
(void)vert_split_arr;
}
+/** \} */
+
/* -------------------------------------------------------------------- */
-/* Main logic */
+/** \name Public BMesh Bisect Function
+ * \{ */
/**
* \param use_snap_center: Snap verts onto the plane.
@@ -350,25 +436,25 @@ void BM_mesh_bisect_plane(BMesh *bm,
BMIter iter;
if (use_tag) {
- /* build tagged edge array */
+ /* Build tagged edge array. */
BMEdge *e;
einput_len = 0;
- /* flush edge tags to verts */
+ /* Flush edge tags to verts. */
BM_mesh_elem_hflag_disable_all(bm, BM_VERT, BM_ELEM_TAG, false);
- /* keep face tags as is */
+ /* Keep face tags as is. */
BM_ITER_MESH_INDEX (e, &iter, bm, BM_EDGES_OF_MESH, i) {
if (edge_is_cut_test(e)) {
edges_arr[einput_len++] = e;
- /* flush edge tags to verts */
+ /* Flush edge tags to verts. */
BM_elem_flag_enable(e->v1, BM_ELEM_TAG);
BM_elem_flag_enable(e->v2, BM_ELEM_TAG);
}
}
- /* face tags are set by caller */
+ /* Face tags are set by caller. */
}
else {
BMEdge *e;
@@ -388,7 +474,7 @@ void BM_mesh_bisect_plane(BMesh *bm,
if (use_tag && !BM_elem_flag_test(v, BM_ELEM_TAG)) {
vert_is_center_disable(v);
- /* these should never be accessed */
+ /* These should never be accessed. */
BM_VERT_DIR(v) = 0;
BM_VERT_DIST(v) = 0.0f;
@@ -408,11 +494,11 @@ void BM_mesh_bisect_plane(BMesh *bm,
}
}
- /* store a stack of faces to be evaluated for splitting */
+ /* Store a stack of faces to be evaluated for splitting. */
BLI_LINKSTACK_INIT(face_stack);
for (i = 0; i < einput_len; i++) {
- /* we could check edge_is_cut_test(e) but there is no point */
+ /* We could check `edge_is_cut_test(e)` but there is no point. */
BMEdge *e = edges_arr[i];
const int side[2] = {BM_VERT_DIR(e->v1), BM_VERT_DIR(e->v2)};
const float dist[2] = {BM_VERT_DIST(e->v1), BM_VERT_DIST(e->v2)};
@@ -449,8 +535,8 @@ void BM_mesh_bisect_plane(BMesh *bm,
BM_VERT_DIST(v_new) = 0.0f;
}
else if (side[0] == 0 || side[1] == 0) {
- /* check if either edge verts are aligned,
- * if so - tag and push all faces that use it into the stack */
+ /* Check if either edge verts are aligned,
+ * if so - tag and push all faces that use it into the stack. */
uint j;
BM_ITER_ELEM_INDEX (v, &iter, e, BM_VERTS_OF_EDGE, j) {
if (side[j] == 0) {
@@ -470,7 +556,7 @@ void BM_mesh_bisect_plane(BMesh *bm,
}
}
- /* if both verts are on the center - tag it */
+ /* If both verts are on the center - tag it. */
if (oflag_center) {
if (side[0] == 0 && side[1] == 0) {
BMO_edge_flag_enable(bm, e, oflag_center);
@@ -485,9 +571,11 @@ void BM_mesh_bisect_plane(BMesh *bm,
bm_face_bisect_verts(bm, f, plane, oflag_center, oflag_new);
}
- /* Caused by access macros: BM_VERT_DIR, BM_VERT_SKIP. */
+ /* Caused by access macros: #BM_VERT_DIR, #BM_VERT_SKIP. */
bm->elem_index_dirty |= BM_VERT;
- /* now we have all faces to split in the stack */
+ /* Now we have all faces to split in the stack. */
BLI_LINKSTACK_FREE(face_stack);
}
+
+/** \} */
diff --git a/source/blender/bmesh/tools/bmesh_boolean.cc b/source/blender/bmesh/tools/bmesh_boolean.cc
index fec33a04e6f..487ef6427af 100644
--- a/source/blender/bmesh/tools/bmesh_boolean.cc
+++ b/source/blender/bmesh/tools/bmesh_boolean.cc
@@ -38,7 +38,8 @@ namespace blender::meshintersect {
#ifdef WITH_GMP
-/** Make a #blender::meshintersect::Mesh from #BMesh bm.
+/**
+ * Make a #blender::meshintersect::Mesh from #BMesh bm.
* We are given a triangulation of it from the caller via #looptris,
* which are looptris_tot triples of loops that together tessellate
* the faces of bm.
diff --git a/source/blender/bmesh/tools/bmesh_decimate_dissolve.c b/source/blender/bmesh/tools/bmesh_decimate_dissolve.c
index 4a024f745ed..8b4a9bb26ac 100644
--- a/source/blender/bmesh/tools/bmesh_decimate_dissolve.c
+++ b/source/blender/bmesh/tools/bmesh_decimate_dissolve.c
@@ -439,7 +439,7 @@ void BM_mesh_decimate_dissolve_ex(BMesh *bm,
for (i = 0; i < vinput_len; i++) {
BMVert *v = vinput_arr[i];
if (LIKELY(v != NULL) && BM_vert_is_edge_pair(v)) {
- BM_vert_collapse_edge(bm, v->e, v, true, true); /* join edges */
+ BM_vert_collapse_edge(bm, v->e, v, true, true, true); /* join edges */
}
}
}
@@ -482,7 +482,7 @@ void BM_mesh_decimate_dissolve_ex(BMesh *bm,
BM_vert_is_edge_pair(v)
#endif
) {
- e_new = BM_vert_collapse_edge(bm, v->e, v, true, true); /* join edges */
+ e_new = BM_vert_collapse_edge(bm, v->e, v, true, true, true); /* join edges */
if (e_new) {
diff --git a/source/blender/bmesh/tools/bmesh_decimate_unsubdivide.c b/source/blender/bmesh/tools/bmesh_decimate_unsubdivide.c
index 0a512fdd592..c96a7be1adf 100644
--- a/source/blender/bmesh/tools/bmesh_decimate_unsubdivide.c
+++ b/source/blender/bmesh/tools/bmesh_decimate_unsubdivide.c
@@ -110,7 +110,7 @@ static bool bm_vert_dissolve_fan(BMesh *bm, BMVert *v)
if (tot_edge == 2) {
/* check for 2 wire verts only */
if (tot_edge_wire == 2) {
- return (BM_vert_collapse_edge(bm, v->e, v, true, true) != NULL);
+ return (BM_vert_collapse_edge(bm, v->e, v, true, true, true) != NULL);
}
}
else if (tot_edge == 4) {
diff --git a/source/blender/bmesh/tools/bmesh_intersect.c b/source/blender/bmesh/tools/bmesh_intersect.c
index 81b016e9601..710d7f79637 100644
--- a/source/blender/bmesh/tools/bmesh_intersect.c
+++ b/source/blender/bmesh/tools/bmesh_intersect.c
@@ -1622,7 +1622,7 @@ bool BM_mesh_intersect(BMesh *bm,
}
if (ok) {
- BM_vert_collapse_edge(bm, v->e, v, true, false);
+ BM_vert_collapse_edge(bm, v->e, v, true, false, false);
}
}
}
@@ -1660,5 +1660,9 @@ bool BM_mesh_intersect(BMesh *bm,
BLI_memarena_free(s.mem_arena);
+ /* It's unlikely the selection history is useful at this point,
+ * if this is not called this array would need to be validated, see: T86799. */
+ BM_select_history_clear(bm);
+
return (has_edit_isect || has_edit_boolean);
}
diff --git a/source/blender/bmesh/tools/bmesh_intersect.h b/source/blender/bmesh/tools/bmesh_intersect.h
index adb88f2fd76..d09ea67a3bb 100644
--- a/source/blender/bmesh/tools/bmesh_intersect.h
+++ b/source/blender/bmesh/tools/bmesh_intersect.h
@@ -20,6 +20,10 @@
* \ingroup bmesh
*/
+#ifdef __cplusplus
+extern "C" {
+#endif
+
bool BM_mesh_intersect(BMesh *bm,
struct BMLoop *(*looptris)[3],
const int looptris_tot,
@@ -41,3 +45,7 @@ enum {
BMESH_ISECT_BOOLEAN_UNION = 1,
BMESH_ISECT_BOOLEAN_DIFFERENCE = 2,
};
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt
index 64033cbe5c4..ac59d832013 100644
--- a/source/blender/compositor/CMakeLists.txt
+++ b/source/blender/compositor/CMakeLists.txt
@@ -49,6 +49,8 @@ set(SRC
COM_compositor.h
COM_defines.h
+ intern/COM_BufferOperation.cc
+ intern/COM_BufferOperation.h
intern/COM_CPUDevice.cc
intern/COM_CPUDevice.h
intern/COM_ChunkOrder.cc
@@ -63,16 +65,23 @@ set(SRC
intern/COM_Debug.h
intern/COM_Device.cc
intern/COM_Device.h
+ intern/COM_Enums.cc
intern/COM_ExecutionGroup.cc
intern/COM_ExecutionGroup.h
+ intern/COM_ExecutionModel.cc
+ intern/COM_ExecutionModel.h
intern/COM_ExecutionSystem.cc
intern/COM_ExecutionSystem.h
+ intern/COM_FullFrameExecutionModel.cc
+ intern/COM_FullFrameExecutionModel.h
intern/COM_MemoryBuffer.cc
intern/COM_MemoryBuffer.h
intern/COM_MemoryProxy.cc
intern/COM_MemoryProxy.h
intern/COM_MetaData.cc
intern/COM_MetaData.h
+ intern/COM_MultiThreadedOperation.cc
+ intern/COM_MultiThreadedOperation.h
intern/COM_Node.cc
intern/COM_Node.h
intern/COM_NodeConverter.cc
@@ -85,10 +94,12 @@ set(SRC
intern/COM_NodeOperationBuilder.h
intern/COM_OpenCLDevice.cc
intern/COM_OpenCLDevice.h
+ intern/COM_SharedOperationBuffers.cc
+ intern/COM_SharedOperationBuffers.h
intern/COM_SingleThreadedOperation.cc
intern/COM_SingleThreadedOperation.h
- intern/COM_SocketReader.cc
- intern/COM_SocketReader.h
+ intern/COM_TiledExecutionModel.cc
+ intern/COM_TiledExecutionModel.h
intern/COM_WorkPackage.cc
intern/COM_WorkPackage.h
intern/COM_WorkScheduler.cc
@@ -280,6 +291,8 @@ set(SRC
nodes/COM_VectorBlurNode.h
operations/COM_VectorBlurOperation.cc
operations/COM_VectorBlurOperation.h
+ nodes/COM_AntiAliasingNode.cc
+ nodes/COM_AntiAliasingNode.h
nodes/COM_BlurNode.cc
nodes/COM_BlurNode.h
nodes/COM_BokehBlurNode.cc
@@ -296,6 +309,7 @@ set(SRC
nodes/COM_FilterNode.h
nodes/COM_InpaintNode.cc
nodes/COM_InpaintNode.h
+
operations/COM_BlurBaseOperation.cc
operations/COM_BlurBaseOperation.h
operations/COM_BokehBlurOperation.cc
@@ -320,6 +334,8 @@ set(SRC
operations/COM_MovieClipAttributeOperation.h
operations/COM_MovieDistortionOperation.cc
operations/COM_MovieDistortionOperation.h
+ operations/COM_SMAAOperation.cc
+ operations/COM_SMAAOperation.h
operations/COM_VariableSizeBokehBlurOperation.cc
operations/COM_VariableSizeBokehBlurOperation.h
@@ -568,6 +584,23 @@ data_to_c(
add_definitions(-DCL_USE_DEPRECATED_OPENCL_1_1_APIS)
+set(GENSRC_DIR ${CMAKE_CURRENT_BINARY_DIR}/operations)
+set(GENSRC ${GENSRC_DIR}/COM_SMAAAreaTexture.h)
+add_custom_command(
+ OUTPUT ${GENSRC}
+ COMMAND ${CMAKE_COMMAND} -E make_directory ${GENSRC_DIR}
+ COMMAND "$<TARGET_FILE:smaa_areatex>" ${GENSRC}
+ DEPENDS smaa_areatex
+)
+add_custom_target(smaa_areatex_header
+ SOURCES ${GENSRC}
+)
+list(APPEND SRC
+ ${GENSRC}
+)
+unset(GENSRC)
+unset(GENSRC_DIR)
+
if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
@@ -586,3 +619,9 @@ if(WITH_OPENIMAGEDENOISE)
endif()
blender_add_lib(bf_compositor "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
+
+if(CXX_WARN_NO_SUGGEST_OVERRIDE)
+ target_compile_options(bf_compositor PRIVATE "-Wsuggest-override")
+endif()
+
+add_dependencies(bf_compositor smaa_areatex_header)
diff --git a/source/blender/compositor/COM_compositor.h b/source/blender/compositor/COM_compositor.h
index 8e3caf7aaf5..a7f9081f3fc 100644
--- a/source/blender/compositor/COM_compositor.h
+++ b/source/blender/compositor/COM_compositor.h
@@ -113,11 +113,11 @@ extern "C" {
*
* When the chunk-order is determined, the first few chunks will be checked if they can be scheduled.
* Chunks can have three states:
- * - [@ref eChunkExecutionState.NOT_SCHEDULED]:
+ * - [@ref eWorkPackageState.NotScheduled]:
* Chunk is not yet scheduled, or dependencies are not met.
- * - [@ref eChunkExecutionState.SCHEDULED]:
+ * - [@ref eWorkPackageState.Scheduled]:
* All dependencies are met, chunk is scheduled, but not finished.
- * - [@ref eChunkExecutionState.EXECUTED]:
+ * - [@ref eWorkPackageState.Executed]:
* Chunk is finished.
*
* \see ExecutionGroup.execute
diff --git a/source/blender/compositor/COM_defines.h b/source/blender/compositor/COM_defines.h
index 266f532ebb8..5a52d216117 100644
--- a/source/blender/compositor/COM_defines.h
+++ b/source/blender/compositor/COM_defines.h
@@ -18,6 +18,18 @@
#pragma once
+namespace blender::compositor {
+
+enum class eExecutionModel {
+ /**
+ * Operations are executed from outputs to inputs grouped in execution groups and rendered
+ * in tiles.
+ */
+ Tiled,
+ /** Operations are fully rendered in order from inputs to outputs. */
+ FullFrame
+};
+
/**
* \brief possible data types for sockets
* \ingroup Model
@@ -32,37 +44,27 @@ enum class DataType {
};
/**
- * \brief Possible quality settings
- * \see CompositorContext.quality
- * \ingroup Execution
+ * Utility to get the number of channels of the given data type.
*/
-enum class CompositorQuality {
- /** \brief High quality setting */
- High = 0,
- /** \brief Medium quality setting */
- Medium = 1,
- /** \brief Low quality setting */
- Low = 2,
-};
+constexpr int COM_data_type_num_channels(const DataType datatype)
+{
+ switch (datatype) {
+ case DataType::Value:
+ return 1;
+ case DataType::Vector:
+ return 3;
+ case DataType::Color:
+ default:
+ return 4;
+ }
+}
-/**
- * \brief Possible priority settings
- * \ingroup Execution
- */
-enum class CompositorPriority {
- /** \brief High quality setting */
- High = 2,
- /** \brief Medium quality setting */
- Medium = 1,
- /** \brief Low quality setting */
- Low = 0,
-};
+constexpr int COM_DATA_TYPE_VALUE_CHANNELS = COM_data_type_num_channels(DataType::Value);
+constexpr int COM_DATA_TYPE_COLOR_CHANNELS = COM_data_type_num_channels(DataType::Color);
// configurable items
// chunk size determination
-#define COM_PREVIEW_SIZE 140.0f
-//#define COM_DEBUG
// chunk order
/**
@@ -82,10 +84,8 @@ enum class ChunkOrdering {
Default = ChunkOrdering::CenterOut,
};
-#define COM_RULE_OF_THIRDS_DIVIDER 100.0f
-
-#define COM_NUM_CHANNELS_VALUE 1
-#define COM_NUM_CHANNELS_VECTOR 3
-#define COM_NUM_CHANNELS_COLOR 4
+constexpr float COM_PREVIEW_SIZE = 140.f;
+constexpr float COM_RULE_OF_THIRDS_DIVIDER = 100.0f;
+constexpr float COM_BLUR_BOKEH_PIXELS = 512;
-#define COM_BLUR_BOKEH_PIXELS 512
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_BufferOperation.cc b/source/blender/compositor/intern/COM_BufferOperation.cc
new file mode 100644
index 00000000000..8464d01801f
--- /dev/null
+++ b/source/blender/compositor/intern/COM_BufferOperation.cc
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+#include "COM_BufferOperation.h"
+
+namespace blender::compositor {
+
+BufferOperation::BufferOperation(MemoryBuffer *buffer, DataType data_type)
+{
+ buffer_ = buffer;
+ /* TODO: Implement a MemoryBuffer get_size() method returning a Size2d type. Shorten following
+ * code to: set_resolution(buffer.get_size()) */
+ unsigned int resolution[2];
+ resolution[0] = buffer->getWidth();
+ resolution[1] = buffer->getHeight();
+ setResolution(resolution);
+ addOutputSocket(data_type);
+}
+
+void *BufferOperation::initializeTileData(rcti * /*rect*/)
+{
+ return buffer_;
+}
+
+void BufferOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler)
+{
+ switch (sampler) {
+ case PixelSampler::Nearest:
+ buffer_->read(output, x, y);
+ break;
+ case PixelSampler::Bilinear:
+ default:
+ buffer_->readBilinear(output, x, y);
+ break;
+ case PixelSampler::Bicubic:
+ /* No bicubic. Same implementation as ReadBufferOperation. */
+ buffer_->readBilinear(output, x, y);
+ break;
+ }
+}
+
+void BufferOperation::executePixelFiltered(
+ float output[4], float x, float y, float dx[2], float dy[2])
+{
+ const float uv[2] = {x, y};
+ const float deriv[2][2] = {{dx[0], dx[1]}, {dy[0], dy[1]}};
+ buffer_->readEWA(output, uv, deriv);
+}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_BufferOperation.h b/source/blender/compositor/intern/COM_BufferOperation.h
new file mode 100644
index 00000000000..f87cd4db94e
--- /dev/null
+++ b/source/blender/compositor/intern/COM_BufferOperation.h
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+#pragma once
+
+#include "COM_NodeOperation.h"
+
+namespace blender::compositor {
+
+class BufferOperation : public NodeOperation {
+ private:
+ MemoryBuffer *buffer_;
+
+ public:
+ BufferOperation(MemoryBuffer *buffer, DataType data_type);
+
+ void *initializeTileData(rcti *rect) override;
+ void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+ void executePixelFiltered(float output[4], float x, float y, float dx[2], float dy[2]) override;
+};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_CPUDevice.cc b/source/blender/compositor/intern/COM_CPUDevice.cc
index b520a437008..2ca5557e278 100644
--- a/source/blender/compositor/intern/COM_CPUDevice.cc
+++ b/source/blender/compositor/intern/COM_CPUDevice.cc
@@ -18,19 +18,36 @@
#include "COM_CPUDevice.h"
+#include "COM_ExecutionGroup.h"
+
+#include "BLI_rect.h"
+
+namespace blender::compositor {
+
CPUDevice::CPUDevice(int thread_id) : m_thread_id(thread_id)
{
}
-void CPUDevice::execute(WorkPackage *work)
+void CPUDevice::execute(WorkPackage *work_package)
{
- const unsigned int chunkNumber = work->chunk_number;
- ExecutionGroup *executionGroup = work->execution_group;
- rcti rect;
+ switch (work_package->type) {
+ case eWorkPackageType::Tile: {
+ const unsigned int chunkNumber = work_package->chunk_number;
+ ExecutionGroup *executionGroup = work_package->execution_group;
- executionGroup->determineChunkRect(&rect, chunkNumber);
+ executionGroup->getOutputOperation()->executeRegion(&work_package->rect, chunkNumber);
+ executionGroup->finalizeChunkExecution(chunkNumber, nullptr);
+ break;
+ }
+ case eWorkPackageType::CustomFunction: {
+ work_package->execute_fn();
+ break;
+ }
+ }
- executionGroup->getOutputOperation()->executeRegion(&rect, chunkNumber);
-
- executionGroup->finalizeChunkExecution(chunkNumber, nullptr);
+ if (work_package->executed_fn) {
+ work_package->executed_fn();
+ }
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_CPUDevice.h b/source/blender/compositor/intern/COM_CPUDevice.h
index 6df1f41419d..99629890b30 100644
--- a/source/blender/compositor/intern/COM_CPUDevice.h
+++ b/source/blender/compositor/intern/COM_CPUDevice.h
@@ -20,6 +20,8 @@
#include "COM_Device.h"
+namespace blender::compositor {
+
/**
* \brief class representing a CPU device.
* \note for every hardware thread in the system a CPUDevice instance
@@ -43,3 +45,5 @@ class CPUDevice : public Device {
protected:
int m_thread_id;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_ChunkOrder.cc b/source/blender/compositor/intern/COM_ChunkOrder.cc
index 9687154120d..a03718d720d 100644
--- a/source/blender/compositor/intern/COM_ChunkOrder.cc
+++ b/source/blender/compositor/intern/COM_ChunkOrder.cc
@@ -20,6 +20,8 @@
#include "BLI_math.h"
+namespace blender::compositor {
+
void ChunkOrder::update_distance(ChunkOrderHotspot *hotspots, unsigned int len_hotspots)
{
double new_distance = DBL_MAX;
@@ -36,3 +38,5 @@ bool operator<(const ChunkOrder &a, const ChunkOrder &b)
{
return a.distance < b.distance;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_ChunkOrder.h b/source/blender/compositor/intern/COM_ChunkOrder.h
index a634309f345..a697f9231d9 100644
--- a/source/blender/compositor/intern/COM_ChunkOrder.h
+++ b/source/blender/compositor/intern/COM_ChunkOrder.h
@@ -24,6 +24,8 @@
#include "COM_ChunkOrderHotspot.h"
+namespace blender::compositor {
+
/** Helper to determine the order how chunks are prioritized during execution. */
struct ChunkOrder {
unsigned int index = 0;
@@ -39,3 +41,5 @@ struct ChunkOrder {
MEM_CXX_CLASS_ALLOC_FUNCS("COM:ChunkOrderHotspot")
#endif
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_ChunkOrderHotspot.cc b/source/blender/compositor/intern/COM_ChunkOrderHotspot.cc
index d31ff518ecd..b8e19fc2c34 100644
--- a/source/blender/compositor/intern/COM_ChunkOrderHotspot.cc
+++ b/source/blender/compositor/intern/COM_ChunkOrderHotspot.cc
@@ -19,6 +19,8 @@
#include "COM_ChunkOrderHotspot.h"
#include <cmath>
+namespace blender::compositor {
+
double ChunkOrderHotspot::calc_distance(int x, int y)
{
int dx = this->x - x;
@@ -27,3 +29,5 @@ double ChunkOrderHotspot::calc_distance(int x, int y)
result += (double)this->addition;
return result;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_ChunkOrderHotspot.h b/source/blender/compositor/intern/COM_ChunkOrderHotspot.h
index d7f40921836..249b328f5c2 100644
--- a/source/blender/compositor/intern/COM_ChunkOrderHotspot.h
+++ b/source/blender/compositor/intern/COM_ChunkOrderHotspot.h
@@ -22,6 +22,8 @@
# include "MEM_guardedalloc.h"
#endif
+namespace blender::compositor {
+
struct ChunkOrderHotspot {
int x;
int y;
@@ -37,3 +39,5 @@ struct ChunkOrderHotspot {
MEM_CXX_CLASS_ALLOC_FUNCS("COM:ChunkOrderHotspot")
#endif
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_CompositorContext.cc b/source/blender/compositor/intern/COM_CompositorContext.cc
index e2447fb5c13..61e299c045e 100644
--- a/source/blender/compositor/intern/COM_CompositorContext.cc
+++ b/source/blender/compositor/intern/COM_CompositorContext.cc
@@ -20,22 +20,43 @@
#include "COM_defines.h"
#include <cstdio>
+#include "BLI_assert.h"
+#include "DNA_userdef_types.h"
+
+namespace blender::compositor {
+
CompositorContext::CompositorContext()
{
this->m_scene = nullptr;
this->m_rd = nullptr;
- this->m_quality = CompositorQuality::High;
+ this->m_quality = eCompositorQuality::High;
this->m_hasActiveOpenCLDevices = false;
this->m_fastCalculation = false;
this->m_viewSettings = nullptr;
this->m_displaySettings = nullptr;
+ this->m_bnodetree = nullptr;
}
int CompositorContext::getFramenumber() const
{
- if (this->m_rd) {
- return this->m_rd->cfra;
- }
+ BLI_assert(m_rd);
+ return m_rd->cfra;
+}
- return -1; /* this should never happen */
+eExecutionModel CompositorContext::get_execution_model() const
+{
+ if (U.experimental.use_full_frame_compositor) {
+ BLI_assert(m_bnodetree != nullptr);
+ switch (m_bnodetree->execution_mode) {
+ case 1:
+ return eExecutionModel::FullFrame;
+ case 0:
+ return eExecutionModel::Tiled;
+ default:
+ BLI_assert(!"Invalid execution mode");
+ }
+ }
+ return eExecutionModel::Tiled;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_CompositorContext.h b/source/blender/compositor/intern/COM_CompositorContext.h
index 46cf65bbb79..56251511576 100644
--- a/source/blender/compositor/intern/COM_CompositorContext.h
+++ b/source/blender/compositor/intern/COM_CompositorContext.h
@@ -19,13 +19,18 @@
#pragma once
#include "BLI_rect.h"
-#include "COM_defines.h"
+
+#include "COM_Enums.h"
+
#include "DNA_color_types.h"
#include "DNA_node_types.h"
#include "DNA_scene_types.h"
+
#include <string>
#include <vector>
+namespace blender::compositor {
+
/**
* \brief Overall context of the compositor
*/
@@ -33,8 +38,8 @@ class CompositorContext {
private:
/**
* \brief The rendering field describes if we are rendering (F12) or if we are editing (Node
- * editor) This field is initialized in ExecutionSystem and must only be read from that point on.
- * \see ExecutionSystem
+ * editor) This field is initialized in ExecutionSystem and must only be read from that point
+ * on. \see ExecutionSystem
*/
bool m_rendering;
@@ -43,7 +48,7 @@ class CompositorContext {
* This field is initialized in ExecutionSystem and must only be read from that point on.
* \see ExecutionSystem
*/
- CompositorQuality m_quality;
+ eCompositorQuality m_quality;
Scene *m_scene;
@@ -200,7 +205,7 @@ class CompositorContext {
/**
* \brief set the quality
*/
- void setQuality(CompositorQuality quality)
+ void setQuality(eCompositorQuality quality)
{
this->m_quality = quality;
}
@@ -208,7 +213,7 @@ class CompositorContext {
/**
* \brief get the quality
*/
- CompositorQuality getQuality() const
+ eCompositorQuality getQuality() const
{
return this->m_quality;
}
@@ -276,4 +281,11 @@ class CompositorContext {
{
return m_rd->size * 0.01f;
}
+
+ /**
+ * Get active execution model.
+ */
+ eExecutionModel get_execution_model() const;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_Converter.cc b/source/blender/compositor/intern/COM_Converter.cc
index d5bce636b8c..af593b2e1b5 100644
--- a/source/blender/compositor/intern/COM_Converter.cc
+++ b/source/blender/compositor/intern/COM_Converter.cc
@@ -26,6 +26,7 @@
#include "COM_NodeOperationBuilder.h"
#include "COM_AlphaOverNode.h"
+#include "COM_AntiAliasingNode.h"
#include "COM_BilateralBlurNode.h"
#include "COM_BlurNode.h"
#include "COM_BokehBlurNode.h"
@@ -115,6 +116,8 @@
#include "COM_ViewerNode.h"
#include "COM_ZCombineNode.h"
+namespace blender::compositor {
+
bool COM_bnode_is_fast_node(const bNode &b_node)
{
return !ELEM(b_node.type,
@@ -418,6 +421,9 @@ Node *COM_convert_bnode(bNode *b_node)
case CMP_NODE_EXPOSURE:
node = new ExposureNode(b_node);
break;
+ case CMP_NODE_ANTIALIASING:
+ node = new AntiAliasingNode(b_node);
+ break;
}
return node;
}
@@ -454,7 +460,7 @@ void COM_convert_resolution(NodeOperationBuilder &builder,
NodeOperationOutput *fromSocket,
NodeOperationInput *toSocket)
{
- InputResizeMode mode = toSocket->getResizeMode();
+ ResizeMode mode = toSocket->getResizeMode();
NodeOperation *toOperation = &toSocket->getOperation();
const float toWidth = toOperation->getWidth();
@@ -470,22 +476,22 @@ void COM_convert_resolution(NodeOperationBuilder &builder,
float scaleY = 0;
switch (mode) {
- case COM_SC_NO_RESIZE:
+ case ResizeMode::None:
break;
- case COM_SC_CENTER:
+ case ResizeMode::Center:
doCenter = true;
break;
- case COM_SC_FIT_WIDTH:
+ case ResizeMode::FitWidth:
doCenter = true;
doScale = true;
scaleX = scaleY = toWidth / fromWidth;
break;
- case COM_SC_FIT_HEIGHT:
+ case ResizeMode::FitHeight:
doCenter = true;
doScale = true;
scaleX = scaleY = toHeight / fromHeight;
break;
- case COM_SC_FIT:
+ case ResizeMode::FitAny:
doCenter = true;
doScale = true;
scaleX = toWidth / fromWidth;
@@ -497,7 +503,7 @@ void COM_convert_resolution(NodeOperationBuilder &builder,
scaleY = scaleX;
}
break;
- case COM_SC_STRETCH:
+ case ResizeMode::Stretch:
doCenter = true;
doScale = true;
scaleX = toWidth / fromWidth;
@@ -510,8 +516,8 @@ void COM_convert_resolution(NodeOperationBuilder &builder,
ScaleOperation *scaleOperation = nullptr;
if (doScale) {
scaleOperation = new ScaleOperation();
- scaleOperation->getInputSocket(1)->setResizeMode(COM_SC_NO_RESIZE);
- scaleOperation->getInputSocket(2)->setResizeMode(COM_SC_NO_RESIZE);
+ scaleOperation->getInputSocket(1)->setResizeMode(ResizeMode::None);
+ scaleOperation->getInputSocket(2)->setResizeMode(ResizeMode::None);
first = scaleOperation;
SetValueOperation *sxop = new SetValueOperation();
sxop->setValue(scaleX);
@@ -530,8 +536,8 @@ void COM_convert_resolution(NodeOperationBuilder &builder,
}
TranslateOperation *translateOperation = new TranslateOperation();
- translateOperation->getInputSocket(1)->setResizeMode(COM_SC_NO_RESIZE);
- translateOperation->getInputSocket(2)->setResizeMode(COM_SC_NO_RESIZE);
+ translateOperation->getInputSocket(1)->setResizeMode(ResizeMode::None);
+ translateOperation->getInputSocket(2)->setResizeMode(ResizeMode::None);
if (!first) {
first = translateOperation;
}
@@ -551,15 +557,17 @@ void COM_convert_resolution(NodeOperationBuilder &builder,
builder.addOperation(translateOperation);
if (doScale) {
- translateOperation->getInputSocket(0)->setResizeMode(COM_SC_NO_RESIZE);
+ translateOperation->getInputSocket(0)->setResizeMode(ResizeMode::None);
builder.addLink(scaleOperation->getOutputSocket(), translateOperation->getInputSocket(0));
}
/* remove previous link and replace */
builder.removeInputLink(toSocket);
- first->getInputSocket(0)->setResizeMode(COM_SC_NO_RESIZE);
- toSocket->setResizeMode(COM_SC_NO_RESIZE);
+ first->getInputSocket(0)->setResizeMode(ResizeMode::None);
+ toSocket->setResizeMode(ResizeMode::None);
builder.addLink(fromSocket, first->getInputSocket(0));
builder.addLink(translateOperation->getOutputSocket(), toSocket);
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_Converter.h b/source/blender/compositor/intern/COM_Converter.h
index 59be34bf0e3..28136437103 100644
--- a/source/blender/compositor/intern/COM_Converter.h
+++ b/source/blender/compositor/intern/COM_Converter.h
@@ -18,14 +18,17 @@
#pragma once
+#include "COM_NodeOperation.h"
+
#ifdef WITH_CXX_GUARDEDALLOC
# include "MEM_guardedalloc.h"
#endif
struct bNode;
+namespace blender::compositor {
+
class Node;
-class NodeOperation;
class NodeOperationInput;
class NodeOperationOutput;
class NodeOperationBuilder;
@@ -65,3 +68,5 @@ NodeOperation *COM_convert_data_type(const NodeOperationOutput &from,
void COM_convert_resolution(NodeOperationBuilder &builder,
NodeOperationOutput *fromSocket,
NodeOperationInput *toSocket);
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_Debug.cc b/source/blender/compositor/intern/COM_Debug.cc
index c299bd1c72d..4cf7e09a7d8 100644
--- a/source/blender/compositor/intern/COM_Debug.cc
+++ b/source/blender/compositor/intern/COM_Debug.cc
@@ -18,30 +18,29 @@
#include "COM_Debug.h"
-#ifdef COM_DEBUG
-
-# include <map>
-# include <typeinfo>
-# include <vector>
+#include <map>
+#include <typeinfo>
+#include <vector>
extern "C" {
-# include "BLI_fileops.h"
-# include "BLI_path_util.h"
-# include "BLI_string.h"
-# include "BLI_sys_types.h"
-
-# include "BKE_appdir.h"
-# include "BKE_node.h"
-# include "DNA_node_types.h"
+#include "BLI_fileops.h"
+#include "BLI_path_util.h"
+#include "BLI_string.h"
+#include "BLI_sys_types.h"
+
+#include "BKE_appdir.h"
+#include "BKE_node.h"
+#include "DNA_node_types.h"
}
-# include "COM_ExecutionGroup.h"
-# include "COM_ExecutionSystem.h"
-# include "COM_Node.h"
+#include "COM_ExecutionSystem.h"
+#include "COM_Node.h"
+
+#include "COM_ReadBufferOperation.h"
+#include "COM_ViewerOperation.h"
+#include "COM_WriteBufferOperation.h"
-# include "COM_ReadBufferOperation.h"
-# include "COM_ViewerOperation.h"
-# include "COM_WriteBufferOperation.h"
+namespace blender::compositor {
int DebugInfo::m_file_index = 0;
DebugInfo::NodeNameMap DebugInfo::m_node_names;
@@ -68,52 +67,8 @@ std::string DebugInfo::operation_name(const NodeOperation *op)
return "";
}
-void DebugInfo::convert_started()
-{
- m_op_names.clear();
-}
-
-void DebugInfo::execute_started(const ExecutionSystem *system)
-{
- m_file_index = 1;
- m_group_states.clear();
- for (ExecutionGroup *execution_group : system->m_groups) {
- m_group_states[execution_group] = EG_WAIT;
- }
-}
-
-void DebugInfo::node_added(const Node *node)
-{
- m_node_names[node] = std::string(node->getbNode() ? node->getbNode()->name : "");
-}
-
-void DebugInfo::node_to_operations(const Node *node)
-{
- m_current_node_name = m_node_names[node];
-}
-
-void DebugInfo::operation_added(const NodeOperation *operation)
-{
- m_op_names[operation] = m_current_node_name;
-}
-
-void DebugInfo::operation_read_write_buffer(const NodeOperation *operation)
-{
- m_current_op_name = m_op_names[operation];
-}
-
-void DebugInfo::execution_group_started(const ExecutionGroup *group)
-{
- m_group_states[group] = EG_RUNNING;
-}
-
-void DebugInfo::execution_group_finished(const ExecutionGroup *group)
-{
- m_group_states[group] = EG_FINISHED;
-}
-
int DebugInfo::graphviz_operation(const ExecutionSystem *system,
- const NodeOperation *operation,
+ NodeOperation *operation,
const ExecutionGroup *group,
char *str,
int maxlen)
@@ -121,7 +76,7 @@ int DebugInfo::graphviz_operation(const ExecutionSystem *system,
int len = 0;
std::string fillcolor = "gainsboro";
- if (operation->isViewerOperation()) {
+ if (operation->get_flags().is_viewer_operation) {
const ViewerOperation *viewer = (const ViewerOperation *)operation;
if (viewer->isActiveViewerOutput()) {
fillcolor = "lightskyblue1";
@@ -133,13 +88,13 @@ int DebugInfo::graphviz_operation(const ExecutionSystem *system,
else if (operation->isOutputOperation(system->getContext().isRendering())) {
fillcolor = "dodgerblue1";
}
- else if (operation->isSetOperation()) {
+ else if (operation->get_flags().is_set_operation) {
fillcolor = "khaki1";
}
- else if (operation->isReadBufferOperation()) {
+ else if (operation->get_flags().is_read_buffer_operation) {
fillcolor = "darkolivegreen3";
}
- else if (operation->isWriteBufferOperation()) {
+ else if (operation->get_flags().is_write_buffer_operation) {
fillcolor = "darkorange";
}
@@ -256,12 +211,14 @@ int DebugInfo::graphviz_legend_group(
return len;
}
-int DebugInfo::graphviz_legend(char *str, int maxlen)
+int DebugInfo::graphviz_legend(char *str, int maxlen, const bool has_execution_groups)
{
int len = 0;
len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "{\r\n");
- len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "rank = sink;\r\n");
+ if (has_execution_groups) {
+ len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "rank = sink;\r\n");
+ }
len += snprintf(
str + len, maxlen > len ? maxlen - len : 0, "Legend [shape=none, margin=0, label=<\r\n");
@@ -281,21 +238,24 @@ int DebugInfo::graphviz_legend(char *str, int maxlen)
"Viewer", "lightskyblue3", str + len, maxlen > len ? maxlen - len : 0);
len += graphviz_legend_color(
"Active Viewer", "lightskyblue1", str + len, maxlen > len ? maxlen - len : 0);
- len += graphviz_legend_color(
- "Write Buffer", "darkorange", str + len, maxlen > len ? maxlen - len : 0);
- len += graphviz_legend_color(
- "Read Buffer", "darkolivegreen3", str + len, maxlen > len ? maxlen - len : 0);
+ if (has_execution_groups) {
+ len += graphviz_legend_color(
+ "Write Buffer", "darkorange", str + len, maxlen > len ? maxlen - len : 0);
+ len += graphviz_legend_color(
+ "Read Buffer", "darkolivegreen3", str + len, maxlen > len ? maxlen - len : 0);
+ }
len += graphviz_legend_color(
"Input Value", "khaki1", str + len, maxlen > len ? maxlen - len : 0);
- len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "<TR><TD></TD></TR>\r\n");
-
- len += graphviz_legend_group(
- "Group Waiting", "white", "dashed", str + len, maxlen > len ? maxlen - len : 0);
- len += graphviz_legend_group(
- "Group Running", "firebrick1", "solid", str + len, maxlen > len ? maxlen - len : 0);
- len += graphviz_legend_group(
- "Group Finished", "chartreuse4", "solid", str + len, maxlen > len ? maxlen - len : 0);
+ if (has_execution_groups) {
+ len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "<TR><TD></TD></TR>\r\n");
+ len += graphviz_legend_group(
+ "Group Waiting", "white", "dashed", str + len, maxlen > len ? maxlen - len : 0);
+ len += graphviz_legend_group(
+ "Group Running", "firebrick1", "solid", str + len, maxlen > len ? maxlen - len : 0);
+ len += graphviz_legend_group(
+ "Group Finished", "chartreuse4", "solid", str + len, maxlen > len ? maxlen - len : 0);
+ }
len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "</TABLE>\r\n");
len += snprintf(str + len, maxlen > len ? maxlen - len : 0, ">];\r\n");
@@ -360,7 +320,7 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma
}
for (NodeOperation *operation : system->m_operations) {
- if (operation->isReadBufferOperation()) {
+ if (operation->get_flags().is_read_buffer_operation) {
ReadBufferOperation *read = (ReadBufferOperation *)operation;
WriteBufferOperation *write = read->getMemoryProxy()->getWriteBufferOperation();
std::vector<std::string> &read_groups = op_groups[read];
@@ -381,8 +341,8 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma
}
for (NodeOperation *op : system->m_operations) {
- for (NodeOperationInput *to : op->m_inputs) {
- NodeOperationOutput *from = to->getLink();
+ for (NodeOperationInput &to : op->m_inputs) {
+ NodeOperationOutput *from = to.getLink();
if (!from) {
continue;
@@ -401,7 +361,7 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma
break;
}
- NodeOperation *to_op = &to->getOperation();
+ NodeOperation *to_op = &to.getOperation();
NodeOperation *from_op = &from->getOperation();
std::vector<std::string> &from_groups = op_groups[from_op];
std::vector<std::string> &to_groups = op_groups[to_op];
@@ -412,7 +372,7 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma
from_op,
from,
to_op,
- to);
+ &to);
for (int k = 0; k < from_groups.size(); k++) {
for (int l = 0; l < to_groups.size(); l++) {
len += snprintf(str + len,
@@ -423,7 +383,7 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma
from,
to_op,
to_groups[l].c_str(),
- to);
+ &to);
len += snprintf(
str + len, maxlen > len ? maxlen - len : 0, " [color=%s]", color.c_str());
len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "\r\n");
@@ -432,7 +392,9 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma
}
}
- len += graphviz_legend(str + len, maxlen > len ? maxlen - len : 0);
+ const bool has_execution_groups = system->getContext().get_execution_model() ==
+ eExecutionModel::Tiled;
+ len += graphviz_legend(str + len, maxlen > len ? maxlen - len : 0, has_execution_groups);
len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "}\r\n");
@@ -441,6 +403,9 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma
void DebugInfo::graphviz(const ExecutionSystem *system)
{
+ if (!COM_EXPORT_GRAPHVIZ) {
+ return;
+ }
char str[1000000];
if (graphviz_system(system, str, sizeof(str) - 1)) {
char basename[FILE_MAX];
@@ -450,48 +415,12 @@ void DebugInfo::graphviz(const ExecutionSystem *system)
BLI_join_dirfile(filename, sizeof(filename), BKE_tempdir_session(), basename);
m_file_index++;
+ std::cout << "Writing compositor debug to: " << filename << "\n";
+
FILE *fp = BLI_fopen(filename, "wb");
fputs(str, fp);
fclose(fp);
}
}
-#else
-
-std::string DebugInfo::node_name(const Node * /*node*/)
-{
- return "";
-}
-std::string DebugInfo::operation_name(const NodeOperation * /*op*/)
-{
- return "";
-}
-void DebugInfo::convert_started()
-{
-}
-void DebugInfo::execute_started(const ExecutionSystem * /*system*/)
-{
-}
-void DebugInfo::node_added(const Node * /*node*/)
-{
-}
-void DebugInfo::node_to_operations(const Node * /*node*/)
-{
-}
-void DebugInfo::operation_added(const NodeOperation * /*operation*/)
-{
-}
-void DebugInfo::operation_read_write_buffer(const NodeOperation * /*operation*/)
-{
-}
-void DebugInfo::execution_group_started(const ExecutionGroup * /*group*/)
-{
-}
-void DebugInfo::execution_group_finished(const ExecutionGroup * /*group*/)
-{
-}
-void DebugInfo::graphviz(const ExecutionSystem * /*system*/)
-{
-}
-
-#endif
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_Debug.h b/source/blender/compositor/intern/COM_Debug.h
index 0107d8b396d..0de3a5e39dc 100644
--- a/source/blender/compositor/intern/COM_Debug.h
+++ b/source/blender/compositor/intern/COM_Debug.h
@@ -21,10 +21,14 @@
#include <map>
#include <string>
+#include "COM_ExecutionSystem.h"
+#include "COM_NodeOperation.h"
#include "COM_defines.h"
+namespace blender::compositor {
+
+static constexpr bool COM_EXPORT_GRAPHVIZ = false;
class Node;
-class NodeOperation;
class ExecutionSystem;
class ExecutionGroup;
@@ -39,23 +43,84 @@ class DebugInfo {
static std::string node_name(const Node *node);
static std::string operation_name(const NodeOperation *op);
- static void convert_started();
- static void execute_started(const ExecutionSystem *system);
+ private:
+ static int m_file_index;
+ /** Map nodes to usable names for debug output. */
+ static NodeNameMap m_node_names;
+ /** Map operations to usable names for debug output. */
+ static OpNameMap m_op_names;
+ /** Base name for all operations added by a node. */
+ static std::string m_current_node_name;
+ /** Base name for automatic sub-operations. */
+ static std::string m_current_op_name;
+ /** For visualizing group states. */
+ static GroupStateMap m_group_states;
+
+ public:
+ static void convert_started()
+ {
+ if (COM_EXPORT_GRAPHVIZ) {
+ m_op_names.clear();
+ }
+ }
+
+ static void execute_started(const ExecutionSystem *system)
+ {
+ if (COM_EXPORT_GRAPHVIZ) {
+ m_file_index = 1;
+ m_group_states.clear();
+ for (ExecutionGroup *execution_group : system->m_groups) {
+ m_group_states[execution_group] = EG_WAIT;
+ }
+ }
+ };
+
+ static void node_added(const Node *node)
+ {
+ if (COM_EXPORT_GRAPHVIZ) {
+ m_node_names[node] = std::string(node->getbNode() ? node->getbNode()->name : "");
+ }
+ }
- static void node_added(const Node *node);
- static void node_to_operations(const Node *node);
- static void operation_added(const NodeOperation *operation);
- static void operation_read_write_buffer(const NodeOperation *operation);
+ static void node_to_operations(const Node *node)
+ {
+ if (COM_EXPORT_GRAPHVIZ) {
+ m_current_node_name = m_node_names[node];
+ }
+ }
- static void execution_group_started(const ExecutionGroup *group);
- static void execution_group_finished(const ExecutionGroup *group);
+ static void operation_added(const NodeOperation *operation)
+ {
+ if (COM_EXPORT_GRAPHVIZ) {
+ m_op_names[operation] = m_current_node_name;
+ }
+ };
+
+ static void operation_read_write_buffer(const NodeOperation *operation)
+ {
+ if (COM_EXPORT_GRAPHVIZ) {
+ m_current_op_name = m_op_names[operation];
+ }
+ };
+
+ static void execution_group_started(const ExecutionGroup *group)
+ {
+ if (COM_EXPORT_GRAPHVIZ) {
+ m_group_states[group] = EG_RUNNING;
+ }
+ };
+ static void execution_group_finished(const ExecutionGroup *group)
+ {
+ if (COM_EXPORT_GRAPHVIZ) {
+ m_group_states[group] = EG_FINISHED;
+ }
+ };
static void graphviz(const ExecutionSystem *system);
-#ifdef COM_DEBUG
protected:
static int graphviz_operation(const ExecutionSystem *system,
- const NodeOperation *operation,
+ NodeOperation *operation,
const ExecutionGroup *group,
char *str,
int maxlen);
@@ -64,20 +129,8 @@ class DebugInfo {
const char *name, const char *color, const char *style, char *str, int maxlen);
static int graphviz_legend_group(
const char *name, const char *color, const char *style, char *str, int maxlen);
- static int graphviz_legend(char *str, int maxlen);
+ static int graphviz_legend(char *str, int maxlen, bool has_execution_groups);
static bool graphviz_system(const ExecutionSystem *system, char *str, int maxlen);
-
- private:
- static int m_file_index;
- /** Map nodes to usable names for debug output. */
- static NodeNameMap m_node_names;
- /** Map operations to usable names for debug output. */
- static OpNameMap m_op_names;
- /** Base name for all operations added by a node. */
- static std::string m_current_node_name;
- /** Base name for automatic sub-operations. */
- static std::string m_current_op_name;
- /** For visualizing group states. */
- static GroupStateMap m_group_states;
-#endif
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_Device.h b/source/blender/compositor/intern/COM_Device.h
index 0a456760045..c848672a405 100644
--- a/source/blender/compositor/intern/COM_Device.h
+++ b/source/blender/compositor/intern/COM_Device.h
@@ -20,6 +20,8 @@
#include "COM_WorkPackage.h"
+namespace blender::compositor {
+
/**
* \brief Abstract class for device implementations to be used by the Compositor.
* devices are queried, initialized and used by the WorkScheduler.
@@ -28,6 +30,14 @@
class Device {
public:
+ Device() = default;
+
+ Device(const Device &other) = delete;
+ Device(Device &&other) noexcept = default;
+
+ Device &operator=(const Device &other) = delete;
+ Device &operator=(Device &&other) = delete;
+
/**
* \brief Declaration of the virtual destructor
* \note resolve warning gcc 4.7
@@ -46,3 +56,5 @@ class Device {
MEM_CXX_CLASS_ALLOC_FUNCS("COM:Device")
#endif
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_Enums.cc b/source/blender/compositor/intern/COM_Enums.cc
new file mode 100644
index 00000000000..d218de92544
--- /dev/null
+++ b/source/blender/compositor/intern/COM_Enums.cc
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+#include "COM_Enums.h"
+
+namespace blender::compositor {
+
+std::ostream &operator<<(std::ostream &os, const eCompositorPriority &priority)
+{
+ switch (priority) {
+ case eCompositorPriority::High: {
+ os << "Priority::High";
+ break;
+ }
+ case eCompositorPriority::Medium: {
+ os << "Priority::Medium";
+ break;
+ }
+ case eCompositorPriority::Low: {
+ os << "Priority::Low";
+ break;
+ }
+ }
+ return os;
+}
+
+std::ostream &operator<<(std::ostream &os, const eWorkPackageState &execution_state)
+{
+ switch (execution_state) {
+ case eWorkPackageState::NotScheduled: {
+ os << "ExecutionState::NotScheduled";
+ break;
+ }
+ case eWorkPackageState::Scheduled: {
+ os << "ExecutionState::Scheduled";
+ break;
+ }
+ case eWorkPackageState::Executed: {
+ os << "ExecutionState::Executed";
+ break;
+ }
+ }
+ return os;
+}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_Enums.h b/source/blender/compositor/intern/COM_Enums.h
new file mode 100644
index 00000000000..519e7df940e
--- /dev/null
+++ b/source/blender/compositor/intern/COM_Enums.h
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+#pragma once
+
+#include "COM_defines.h"
+
+#include <ostream>
+
+namespace blender::compositor {
+
+/**
+ * \brief Possible quality settings
+ * \see CompositorContext.quality
+ * \ingroup Execution
+ */
+enum class eCompositorQuality {
+ /** \brief High quality setting */
+ High = 0,
+ /** \brief Medium quality setting */
+ Medium = 1,
+ /** \brief Low quality setting */
+ Low = 2,
+};
+
+/**
+ * \brief Possible priority settings
+ * \ingroup Execution
+ */
+enum class eCompositorPriority {
+ /** \brief High quality setting */
+ High = 2,
+ /** \brief Medium quality setting */
+ Medium = 1,
+ /** \brief Low quality setting */
+ Low = 0,
+};
+
+/**
+ * \brief the execution state of a chunk in an ExecutionGroup
+ * \ingroup Execution
+ */
+enum class eWorkPackageState {
+ /**
+ * \brief chunk is not yet scheduled
+ */
+ NotScheduled = 0,
+ /**
+ * \brief chunk is scheduled, but not yet executed
+ */
+ Scheduled = 1,
+ /**
+ * \brief chunk is executed.
+ */
+ Executed = 2,
+};
+
+/**
+ * \brief Work type to execute.
+ * \ingroup Execution
+ */
+enum class eWorkPackageType {
+ /**
+ * \brief Executes an execution group tile.
+ */
+ Tile = 0,
+ /**
+ * \brief Executes a custom function.
+ */
+ CustomFunction = 1
+};
+
+std::ostream &operator<<(std::ostream &os, const eCompositorPriority &priority);
+std::ostream &operator<<(std::ostream &os, const eWorkPackageState &execution_state);
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_ExecutionGroup.cc b/source/blender/compositor/intern/COM_ExecutionGroup.cc
index f500327b7a7..68bda8c70d6 100644
--- a/source/blender/compositor/intern/COM_ExecutionGroup.cc
+++ b/source/blender/compositor/intern/COM_ExecutionGroup.cc
@@ -46,10 +46,31 @@
#include "WM_api.h"
#include "WM_types.h"
-ExecutionGroup::ExecutionGroup()
+namespace blender::compositor {
+
+std::ostream &operator<<(std::ostream &os, const ExecutionGroupFlags &flags)
+{
+ if (flags.initialized) {
+ os << "init,";
+ }
+ if (flags.is_output) {
+ os << "output,";
+ }
+ if (flags.complex) {
+ os << "complex,";
+ }
+ if (flags.open_cl) {
+ os << "open_cl,";
+ }
+ if (flags.single_threaded) {
+ os << "single_threaded,";
+ }
+ return os;
+}
+
+ExecutionGroup::ExecutionGroup(int id)
{
- this->m_is_output = false;
- this->m_complex = false;
+ m_id = id;
this->m_bTree = nullptr;
this->m_height = 0;
this->m_width = 0;
@@ -57,42 +78,48 @@ ExecutionGroup::ExecutionGroup()
this->m_x_chunks_len = 0;
this->m_y_chunks_len = 0;
this->m_chunks_len = 0;
- this->m_initialized = false;
- this->m_openCL = false;
- this->m_singleThreaded = false;
this->m_chunks_finished = 0;
BLI_rcti_init(&this->m_viewerBorder, 0, 0, 0, 0);
this->m_executionStartTime = 0;
}
-CompositorPriority ExecutionGroup::getRenderPriority()
+std::ostream &operator<<(std::ostream &os, const ExecutionGroup &execution_group)
+{
+ os << "ExecutionGroup(id=" << execution_group.get_id();
+ os << ",flags={" << execution_group.get_flags() << "}";
+ os << ",operation=" << *execution_group.getOutputOperation() << "";
+ os << ")";
+ return os;
+}
+
+eCompositorPriority ExecutionGroup::getRenderPriority()
{
return this->getOutputOperation()->getRenderPriority();
}
bool ExecutionGroup::can_contain(NodeOperation &operation)
{
- if (!this->m_initialized) {
+ if (!m_flags.initialized) {
return true;
}
- if (operation.isReadBufferOperation()) {
+ if (operation.get_flags().is_read_buffer_operation) {
return true;
}
- if (operation.isWriteBufferOperation()) {
+ if (operation.get_flags().is_write_buffer_operation) {
return false;
}
- if (operation.isSetOperation()) {
+ if (operation.get_flags().is_set_operation) {
return true;
}
/* complex groups don't allow further ops (except read buffer and values, see above) */
- if (m_complex) {
+ if (m_flags.complex) {
return false;
}
/* complex ops can't be added to other groups (except their own, which they initialize, see
* above) */
- if (operation.isComplex()) {
+ if (operation.get_flags().complex) {
return false;
}
@@ -105,11 +132,12 @@ bool ExecutionGroup::addOperation(NodeOperation *operation)
return false;
}
- if (!operation->isReadBufferOperation() && !operation->isWriteBufferOperation()) {
- m_complex = operation->isComplex();
- m_openCL = operation->isOpenCL();
- m_singleThreaded = operation->isSingleThreaded();
- m_initialized = true;
+ if (!operation->get_flags().is_read_buffer_operation &&
+ !operation->get_flags().is_write_buffer_operation) {
+ m_flags.complex = operation->get_flags().complex;
+ m_flags.open_cl = operation->get_flags().open_cl;
+ m_flags.single_threaded = operation->get_flags().single_threaded;
+ m_flags.initialized = true;
}
m_operations.append(operation);
@@ -123,20 +151,26 @@ NodeOperation *ExecutionGroup::getOutputOperation() const
->m_operations[0]; /* the first operation of the group is always the output operation. */
}
-void ExecutionGroup::initExecution()
+void ExecutionGroup::init_work_packages()
{
- m_chunk_execution_states.clear();
- determineNumberOfChunks();
-
+ m_work_packages.clear();
if (this->m_chunks_len != 0) {
- m_chunk_execution_states.resize(this->m_chunks_len);
- m_chunk_execution_states.fill(eChunkExecutionState::NOT_SCHEDULED);
+ m_work_packages.resize(this->m_chunks_len);
+ for (unsigned int index = 0; index < m_chunks_len; index++) {
+ m_work_packages[index].type = eWorkPackageType::Tile;
+ m_work_packages[index].state = eWorkPackageState::NotScheduled;
+ m_work_packages[index].execution_group = this;
+ m_work_packages[index].chunk_number = index;
+ determineChunkRect(&m_work_packages[index].rect, index);
+ }
}
+}
+void ExecutionGroup::init_read_buffer_operations()
+{
unsigned int max_offset = 0;
-
for (NodeOperation *operation : m_operations) {
- if (operation->isReadBufferOperation()) {
+ if (operation->get_flags().is_read_buffer_operation) {
ReadBufferOperation *readOperation = static_cast<ReadBufferOperation *>(operation);
this->m_read_operations.append(readOperation);
max_offset = MAX2(max_offset, readOperation->getOffset());
@@ -146,15 +180,23 @@ void ExecutionGroup::initExecution()
this->m_max_read_buffer_offset = max_offset;
}
+void ExecutionGroup::initExecution()
+{
+ init_number_of_chunks();
+ init_work_packages();
+ init_read_buffer_operations();
+}
+
void ExecutionGroup::deinitExecution()
{
- m_chunk_execution_states.clear();
+ m_work_packages.clear();
this->m_chunks_len = 0;
this->m_x_chunks_len = 0;
this->m_y_chunks_len = 0;
this->m_read_operations.clear();
this->m_bTree = nullptr;
}
+
void ExecutionGroup::determineResolution(unsigned int resolution[2])
{
NodeOperation *operation = this->getOutputOperation();
@@ -164,9 +206,9 @@ void ExecutionGroup::determineResolution(unsigned int resolution[2])
BLI_rcti_init(&this->m_viewerBorder, 0, this->m_width, 0, this->m_height);
}
-void ExecutionGroup::determineNumberOfChunks()
+void ExecutionGroup::init_number_of_chunks()
{
- if (this->m_singleThreaded) {
+ if (this->m_flags.single_threaded) {
this->m_x_chunks_len = 1;
this->m_y_chunks_len = 1;
this->m_chunks_len = 1;
@@ -181,7 +223,7 @@ void ExecutionGroup::determineNumberOfChunks()
}
}
-blender::Array<unsigned int> ExecutionGroup::determine_chunk_execution_order() const
+blender::Array<unsigned int> ExecutionGroup::get_execution_order() const
{
blender::Array<unsigned int> chunk_order(m_chunks_len);
for (int chunk_index = 0; chunk_index < this->m_chunks_len; chunk_index++) {
@@ -193,7 +235,7 @@ blender::Array<unsigned int> ExecutionGroup::determine_chunk_execution_order() c
float centerY = 0.5f;
ChunkOrdering order_type = ChunkOrdering::Default;
- if (operation->isViewerOperation()) {
+ if (operation->get_flags().is_viewer_operation) {
ViewerOperation *viewer = (ViewerOperation *)operation;
centerX = viewer->getCenterX();
centerY = viewer->getCenterY();
@@ -216,11 +258,10 @@ blender::Array<unsigned int> ExecutionGroup::determine_chunk_execution_order() c
ChunkOrderHotspot hotspot(border_width * centerX, border_height * centerY, 0.0f);
blender::Array<ChunkOrder> chunk_orders(m_chunks_len);
for (index = 0; index < this->m_chunks_len; index++) {
- rcti rect;
- determineChunkRect(&rect, index);
+ const WorkPackage &work_package = m_work_packages[index];
chunk_orders[index].index = index;
- chunk_orders[index].x = rect.xmin - this->m_viewerBorder.xmin;
- chunk_orders[index].y = rect.ymin - this->m_viewerBorder.ymin;
+ chunk_orders[index].x = work_package.rect.xmin - this->m_viewerBorder.xmin;
+ chunk_orders[index].y = work_package.rect.ymin - this->m_viewerBorder.ymin;
chunk_orders[index].update_distance(&hotspot, 1);
}
@@ -254,11 +295,10 @@ blender::Array<unsigned int> ExecutionGroup::determine_chunk_execution_order() c
blender::Array<ChunkOrder> chunk_orders(m_chunks_len);
for (index = 0; index < this->m_chunks_len; index++) {
- rcti rect;
- determineChunkRect(&rect, index);
+ const WorkPackage &work_package = m_work_packages[index];
chunk_orders[index].index = index;
- chunk_orders[index].x = rect.xmin - this->m_viewerBorder.xmin;
- chunk_orders[index].y = rect.ymin - this->m_viewerBorder.ymin;
+ chunk_orders[index].x = work_package.rect.xmin - this->m_viewerBorder.xmin;
+ chunk_orders[index].y = work_package.rect.ymin - this->m_viewerBorder.ymin;
chunk_orders[index].update_distance(hotspots, 9);
}
@@ -301,7 +341,7 @@ void ExecutionGroup::execute(ExecutionSystem *graph)
this->m_chunks_finished = 0;
this->m_bTree = bTree;
- blender::Array<unsigned int> chunk_order = determine_chunk_execution_order();
+ blender::Array<unsigned int> chunk_order = get_execution_order();
DebugInfo::execution_group_started(this);
DebugInfo::graphviz(graph);
@@ -322,8 +362,9 @@ void ExecutionGroup::execute(ExecutionSystem *graph)
chunk_index = chunk_order[index];
int yChunk = chunk_index / this->m_x_chunks_len;
int xChunk = chunk_index - (yChunk * this->m_x_chunks_len);
- switch (m_chunk_execution_states[chunk_index]) {
- case eChunkExecutionState::NOT_SCHEDULED: {
+ const WorkPackage &work_package = m_work_packages[chunk_index];
+ switch (work_package.state) {
+ case eWorkPackageState::NotScheduled: {
scheduleChunkWhenPossible(graph, xChunk, yChunk);
finished = false;
startEvaluated = true;
@@ -334,13 +375,13 @@ void ExecutionGroup::execute(ExecutionSystem *graph)
}
break;
}
- case eChunkExecutionState::SCHEDULED: {
+ case eWorkPackageState::Scheduled: {
finished = false;
startEvaluated = true;
numberEvaluated++;
break;
}
- case eChunkExecutionState::EXECUTED: {
+ case eWorkPackageState::Executed: {
if (!startEvaluated) {
startIndex = index + 1;
}
@@ -360,15 +401,14 @@ void ExecutionGroup::execute(ExecutionSystem *graph)
MemoryBuffer **ExecutionGroup::getInputBuffersOpenCL(int chunkNumber)
{
- rcti rect;
- determineChunkRect(&rect, chunkNumber);
+ WorkPackage &work_package = m_work_packages[chunkNumber];
MemoryBuffer **memoryBuffers = (MemoryBuffer **)MEM_callocN(
sizeof(MemoryBuffer *) * this->m_max_read_buffer_offset, __func__);
rcti output;
for (ReadBufferOperation *readOperation : m_read_operations) {
MemoryProxy *memoryProxy = readOperation->getMemoryProxy();
- this->determineDependingAreaOfInterest(&rect, readOperation, &output);
+ this->determineDependingAreaOfInterest(&work_package.rect, readOperation, &output);
MemoryBuffer *memoryBuffer = memoryProxy->getExecutor()->constructConsolidatedMemoryBuffer(
*memoryProxy, output);
memoryBuffers[readOperation->getOffset()] = memoryBuffer;
@@ -387,8 +427,9 @@ MemoryBuffer *ExecutionGroup::constructConsolidatedMemoryBuffer(MemoryProxy &mem
void ExecutionGroup::finalizeChunkExecution(int chunkNumber, MemoryBuffer **memoryBuffers)
{
- if (this->m_chunk_execution_states[chunkNumber] == eChunkExecutionState::SCHEDULED) {
- this->m_chunk_execution_states[chunkNumber] = eChunkExecutionState::EXECUTED;
+ WorkPackage &work_package = m_work_packages[chunkNumber];
+ if (work_package.state == eWorkPackageState::Scheduled) {
+ work_package.state = eWorkPackageState::Executed;
}
atomic_add_and_fetch_u(&this->m_chunks_finished, 1);
@@ -420,23 +461,23 @@ void ExecutionGroup::finalizeChunkExecution(int chunkNumber, MemoryBuffer **memo
}
}
-inline void ExecutionGroup::determineChunkRect(rcti *rect,
+inline void ExecutionGroup::determineChunkRect(rcti *r_rect,
const unsigned int xChunk,
const unsigned int yChunk) const
{
const int border_width = BLI_rcti_size_x(&this->m_viewerBorder);
const int border_height = BLI_rcti_size_y(&this->m_viewerBorder);
- if (this->m_singleThreaded) {
+ if (this->m_flags.single_threaded) {
BLI_rcti_init(
- rect, this->m_viewerBorder.xmin, border_width, this->m_viewerBorder.ymin, border_height);
+ r_rect, this->m_viewerBorder.xmin, border_width, this->m_viewerBorder.ymin, border_height);
}
else {
const unsigned int minx = xChunk * this->m_chunkSize + this->m_viewerBorder.xmin;
const unsigned int miny = yChunk * this->m_chunkSize + this->m_viewerBorder.ymin;
const unsigned int width = MIN2((unsigned int)this->m_viewerBorder.xmax, this->m_width);
const unsigned int height = MIN2((unsigned int)this->m_viewerBorder.ymax, this->m_height);
- BLI_rcti_init(rect,
+ BLI_rcti_init(r_rect,
MIN2(minx, this->m_width),
MIN2(minx + this->m_chunkSize, width),
MIN2(miny, this->m_height),
@@ -444,18 +485,18 @@ inline void ExecutionGroup::determineChunkRect(rcti *rect,
}
}
-void ExecutionGroup::determineChunkRect(rcti *rect, const unsigned int chunkNumber) const
+void ExecutionGroup::determineChunkRect(rcti *r_rect, const unsigned int chunkNumber) const
{
const unsigned int yChunk = chunkNumber / this->m_x_chunks_len;
const unsigned int xChunk = chunkNumber - (yChunk * this->m_x_chunks_len);
- determineChunkRect(rect, xChunk, yChunk);
+ determineChunkRect(r_rect, xChunk, yChunk);
}
MemoryBuffer *ExecutionGroup::allocateOutputBuffer(rcti &rect)
{
// we assume that this method is only called from complex execution groups.
NodeOperation *operation = this->getOutputOperation();
- if (operation->isWriteBufferOperation()) {
+ if (operation->get_flags().is_write_buffer_operation) {
WriteBufferOperation *writeOperation = (WriteBufferOperation *)operation;
MemoryBuffer *buffer = new MemoryBuffer(
writeOperation->getMemoryProxy(), rect, MemoryBufferState::Temporary);
@@ -466,7 +507,7 @@ MemoryBuffer *ExecutionGroup::allocateOutputBuffer(rcti &rect)
bool ExecutionGroup::scheduleAreaWhenPossible(ExecutionSystem *graph, rcti *area)
{
- if (this->m_singleThreaded) {
+ if (this->m_flags.single_threaded) {
return scheduleChunkWhenPossible(graph, 0, 0);
}
// find all chunks inside the rect
@@ -500,9 +541,10 @@ bool ExecutionGroup::scheduleAreaWhenPossible(ExecutionSystem *graph, rcti *area
bool ExecutionGroup::scheduleChunk(unsigned int chunkNumber)
{
- if (this->m_chunk_execution_states[chunkNumber] == eChunkExecutionState::NOT_SCHEDULED) {
- this->m_chunk_execution_states[chunkNumber] = eChunkExecutionState::SCHEDULED;
- WorkScheduler::schedule(this, chunkNumber);
+ WorkPackage &work_package = m_work_packages[chunkNumber];
+ if (work_package.state == eWorkPackageState::NotScheduled) {
+ work_package.state = eWorkPackageState::Scheduled;
+ WorkScheduler::schedule(&work_package);
return true;
}
return false;
@@ -521,22 +563,21 @@ bool ExecutionGroup::scheduleChunkWhenPossible(ExecutionSystem *graph,
// Check if chunk is already executed or scheduled and not yet executed.
const int chunk_index = chunk_y * this->m_x_chunks_len + chunk_x;
- if (this->m_chunk_execution_states[chunk_index] == eChunkExecutionState::EXECUTED) {
+ WorkPackage &work_package = m_work_packages[chunk_index];
+ if (work_package.state == eWorkPackageState::Executed) {
return true;
}
- if (this->m_chunk_execution_states[chunk_index] == eChunkExecutionState::SCHEDULED) {
+ if (work_package.state == eWorkPackageState::Scheduled) {
return false;
}
- rcti rect;
- determineChunkRect(&rect, chunk_x, chunk_y);
bool can_be_executed = true;
rcti area;
for (ReadBufferOperation *read_operation : m_read_operations) {
BLI_rcti_init(&area, 0, 0, 0, 0);
MemoryProxy *memory_proxy = read_operation->getMemoryProxy();
- determineDependingAreaOfInterest(&rect, read_operation, &area);
+ determineDependingAreaOfInterest(&work_package.rect, read_operation, &area);
ExecutionGroup *group = memory_proxy->getExecutor();
if (!group->scheduleAreaWhenPossible(graph, &area)) {
@@ -558,16 +599,10 @@ void ExecutionGroup::determineDependingAreaOfInterest(rcti *input,
this->getOutputOperation()->determineDependingAreaOfInterest(input, readOperation, output);
}
-bool ExecutionGroup::isOpenCL()
-{
- return this->m_openCL;
-}
-
void ExecutionGroup::setViewerBorder(float xmin, float xmax, float ymin, float ymax)
{
- NodeOperation *operation = this->getOutputOperation();
-
- if (operation->isViewerOperation() || operation->isPreviewOperation()) {
+ const NodeOperation &operation = *this->getOutputOperation();
+ if (operation.get_flags().use_viewer_border) {
BLI_rcti_init(&this->m_viewerBorder,
xmin * this->m_width,
xmax * this->m_width,
@@ -578,32 +613,14 @@ void ExecutionGroup::setViewerBorder(float xmin, float xmax, float ymin, float y
void ExecutionGroup::setRenderBorder(float xmin, float xmax, float ymin, float ymax)
{
- NodeOperation *operation = this->getOutputOperation();
-
- if (operation->isOutputOperation(true)) {
- /* Basically, setting border need to happen for only operations
- * which operates in render resolution buffers (like compositor
- * output nodes).
- *
- * In this cases adding border will lead to mapping coordinates
- * from output buffer space to input buffer spaces when executing
- * operation.
- *
- * But nodes like viewer and file output just shall display or
- * safe the same exact buffer which goes to their input, no need
- * in any kind of coordinates mapping.
- */
-
- bool operationNeedsBorder = !(operation->isViewerOperation() ||
- operation->isPreviewOperation() ||
- operation->isFileOutputOperation());
-
- if (operationNeedsBorder) {
- BLI_rcti_init(&this->m_viewerBorder,
- xmin * this->m_width,
- xmax * this->m_width,
- ymin * this->m_height,
- ymax * this->m_height);
- }
+ const NodeOperation &operation = *this->getOutputOperation();
+ if (operation.isOutputOperation(true) && operation.get_flags().use_render_border) {
+ BLI_rcti_init(&this->m_viewerBorder,
+ xmin * this->m_width,
+ xmax * this->m_width,
+ ymin * this->m_height,
+ ymax * this->m_height);
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_ExecutionGroup.h b/source/blender/compositor/intern/COM_ExecutionGroup.h
index 13ff06cd5d1..cb593feabb0 100644
--- a/source/blender/compositor/intern/COM_ExecutionGroup.h
+++ b/source/blender/compositor/intern/COM_ExecutionGroup.h
@@ -31,32 +31,50 @@
#include "COM_MemoryProxy.h"
#include "COM_Node.h"
#include "COM_NodeOperation.h"
+#include "COM_WorkPackage.h"
#include <vector>
+namespace blender::compositor {
+
class ExecutionSystem;
class MemoryProxy;
+class MemoryBuffer;
class ReadBufferOperation;
class Device;
-/**
- * \brief the execution state of a chunk in an ExecutionGroup
- * \ingroup Execution
- */
-enum class eChunkExecutionState {
+struct ExecutionGroupFlags {
+ bool initialized : 1;
/**
- * \brief chunk is not yet scheduled
+ * Is this ExecutionGroup an output ExecutionGroup
+ * An OutputExecution group are groups containing a
+ * ViewerOperation, CompositeOperation, PreviewOperation.
*/
- NOT_SCHEDULED = 0,
+ bool is_output : 1;
+ bool complex : 1;
+
/**
- * \brief chunk is scheduled, but not yet executed
+ * Can this ExecutionGroup be scheduled on an OpenCLDevice.
*/
- SCHEDULED = 1,
+ bool open_cl : 1;
+
/**
- * \brief chunk is executed.
+ * Schedule this execution group as a single chunk. This
+ * chunk will be executed by a single thread.
*/
- EXECUTED = 2,
+ bool single_threaded : 1;
+
+ ExecutionGroupFlags()
+ {
+ initialized = false;
+ is_output = false;
+ complex = false;
+ open_cl = false;
+ single_threaded = false;
+ }
};
+std::ostream &operator<<(std::ostream &os, const ExecutionGroupFlags &flags);
+
/**
* \brief Class ExecutionGroup is a group of Operations that are executed as one.
* This grouping is used to combine Operations that can be executed as one whole when
@@ -66,18 +84,17 @@ enum class eChunkExecutionState {
class ExecutionGroup {
private:
// fields
-
/**
- * \brief list of operations in this ExecutionGroup
+ * Id of the execution group. For debugging purposes.
*/
- blender::Vector<NodeOperation *> m_operations;
+ int m_id;
/**
- * \brief is this ExecutionGroup an input ExecutionGroup
- * an input execution group is a group that is at the end of the calculation
- * (the output is important for the user).
+ * \brief list of operations in this ExecutionGroup
*/
- bool m_is_output;
+ Vector<NodeOperation *> m_operations;
+
+ ExecutionGroupFlags m_flags;
/**
* \brief Width of the output
@@ -111,21 +128,6 @@ class ExecutionGroup {
unsigned int m_chunks_len;
/**
- * \brief contains this ExecutionGroup a complex NodeOperation.
- */
- bool m_complex;
-
- /**
- * \brief can this ExecutionGroup be scheduled on an OpenCLDevice
- */
- bool m_openCL;
-
- /**
- * \brief Is this Execution group SingleThreaded
- */
- bool m_singleThreaded;
-
- /**
* \brief what is the maximum number field of all ReadBufferOperation in this ExecutionGroup.
* \note this is used to construct the MemoryBuffers that will be passed during execution.
*/
@@ -134,7 +136,7 @@ class ExecutionGroup {
/**
* \brief All read operations of this execution group.
*/
- blender::Vector<ReadBufferOperation *> m_read_operations;
+ Vector<ReadBufferOperation *> m_read_operations;
/**
* \brief reference to the original bNodeTree,
@@ -149,24 +151,9 @@ class ExecutionGroup {
unsigned int m_chunks_finished;
/**
- * \brief m_chunk_execution_states holds per chunk the execution state. this state can be
- * - eChunkExecutionState::NOT_SCHEDULED: not scheduled
- * - eChunkExecutionState::SCHEDULED: scheduled
- * - eChunkExecutionState::EXECUTED: executed
- */
- blender::Vector<eChunkExecutionState> m_chunk_execution_states;
-
- /**
- * \brief indicator when this ExecutionGroup has valid Operations in its vector for Execution
- * \note When building the ExecutionGroup Operations are added via recursion.
- * First a WriteBufferOperations is added, then the.
- * \note Operation containing the settings that is important for the ExecutiongGroup is added,
- * \note When this occurs, these settings are copied over from the node to the ExecutionGroup
- * \note and the Initialized flag is set to true.
- * \see complex
- * \see openCL
+ * \brief m_work_packages holds all unit of work.
*/
- bool m_initialized;
+ Vector<WorkPackage> m_work_packages;
/**
* \brief denotes boundary for border compositing
@@ -187,25 +174,17 @@ class ExecutionGroup {
bool can_contain(NodeOperation &operation);
/**
- * \brief calculate the actual chunk size of this execution group.
- * \note A chunk size is an unsigned int that is both the height and width of a chunk.
- * \note The chunk size will not be stored in the chunkSize field. This needs to be done
- * \note by the calling method.
- */
- unsigned int determineChunkSize();
-
- /**
* \brief Determine the rect (minx, maxx, miny, maxy) of a chunk at a position.
- * \note Only gives useful results after the determination of the chunksize
- * \see determineChunkSize()
*/
- void determineChunkRect(rcti *rect, const unsigned int xChunk, const unsigned int yChunk) const;
+ void determineChunkRect(rcti *r_rect,
+ const unsigned int xChunk,
+ const unsigned int yChunk) const;
/**
* \brief determine the number of chunks, based on the chunkSize, width and height.
* \note The result are stored in the fields numberOfChunks, numberOfXChunks, numberOfYChunks
*/
- void determineNumberOfChunks();
+ void init_number_of_chunks();
/**
* \brief try to schedule a specific chunk.
@@ -252,11 +231,24 @@ class ExecutionGroup {
/**
* Return the execution order of the user visible chunks.
*/
- blender::Array<unsigned int> determine_chunk_execution_order() const;
+ blender::Array<unsigned int> get_execution_order() const;
+
+ void init_read_buffer_operations();
+ void init_work_packages();
public:
// constructors
- ExecutionGroup();
+ ExecutionGroup(int id);
+
+ int get_id() const
+ {
+ return m_id;
+ }
+
+ const ExecutionGroupFlags get_flags() const
+ {
+ return m_flags;
+ }
// methods
/**
@@ -270,23 +262,12 @@ class ExecutionGroup {
bool addOperation(NodeOperation *operation);
/**
- * \brief is this ExecutionGroup an output ExecutionGroup
- * \note An OutputExecution group are groups containing a
- * \note ViewerOperation, CompositeOperation, PreviewOperation.
- * \see NodeOperation.isOutputOperation
- */
- bool isOutputExecutionGroup() const
- {
- return this->m_is_output;
- }
-
- /**
* \brief set whether this ExecutionGroup is an output
* \param isOutput:
*/
void setOutputExecutionGroup(bool is_output)
{
- this->m_is_output = is_output;
+ this->m_flags.is_output = is_output;
}
/**
@@ -322,14 +303,6 @@ class ExecutionGroup {
}
/**
- * \brief does this ExecutionGroup contains a complex NodeOperation
- */
- bool isComplex() const
- {
- return m_complex;
- }
-
- /**
* \brief get the output operation of this ExecutionGroup
* \return NodeOperation *output operation
*/
@@ -404,16 +377,8 @@ class ExecutionGroup {
/**
* \brief Determine the rect (minx, maxx, miny, maxy) of a chunk.
- * \note Only gives useful results after the determination of the chunksize
- * \see determineChunkSize()
- */
- void determineChunkRect(rcti *rect, const unsigned int chunkNumber) const;
-
- /**
- * \brief can this ExecutionGroup be scheduled on an OpenCLDevice
- * \see WorkScheduler.schedule
*/
- bool isOpenCL();
+ void determineChunkRect(rcti *r_rect, const unsigned int chunkNumber) const;
void setChunksize(int chunksize)
{
@@ -424,7 +389,7 @@ class ExecutionGroup {
* \brief get the Render priority of this ExecutionGroup
* \see ExecutionSystem.execute
*/
- CompositorPriority getRenderPriority();
+ eCompositorPriority getRenderPriority();
/**
* \brief set border for viewer operation
@@ -441,3 +406,7 @@ class ExecutionGroup {
MEM_CXX_CLASS_ALLOC_FUNCS("COM:ExecutionGroup")
#endif
};
+
+std::ostream &operator<<(std::ostream &os, const ExecutionGroup &execution_group);
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_ExecutionModel.cc b/source/blender/compositor/intern/COM_ExecutionModel.cc
new file mode 100644
index 00000000000..4d7f62e091b
--- /dev/null
+++ b/source/blender/compositor/intern/COM_ExecutionModel.cc
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+#include "COM_ExecutionModel.h"
+
+namespace blender::compositor {
+
+ExecutionModel::ExecutionModel(CompositorContext &context, Span<NodeOperation *> operations)
+ : context_(context), operations_(operations)
+{
+ const bNodeTree *node_tree = context_.getbNodeTree();
+
+ const rctf *viewer_border = &node_tree->viewer_border;
+ border_.use_viewer_border = (node_tree->flag & NTREE_VIEWER_BORDER) &&
+ viewer_border->xmin < viewer_border->xmax &&
+ viewer_border->ymin < viewer_border->ymax;
+ border_.viewer_border = viewer_border;
+
+ const RenderData *rd = context_.getRenderData();
+ /* Case when cropping to render border happens is handled in
+ * compositor output and render layer nodes. */
+ border_.use_render_border = context.isRendering() && (rd->mode & R_BORDER) &&
+ !(rd->mode & R_CROP);
+ border_.render_border = &rd->border;
+}
+
+bool ExecutionModel::is_breaked() const
+{
+ const bNodeTree *btree = context_.getbNodeTree();
+ return btree->test_break(btree->tbh);
+}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_ExecutionModel.h b/source/blender/compositor/intern/COM_ExecutionModel.h
new file mode 100644
index 00000000000..9e8466b9282
--- /dev/null
+++ b/source/blender/compositor/intern/COM_ExecutionModel.h
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+#pragma once
+
+#include "BLI_rect.h"
+#include "BLI_vector.hh"
+
+#include "COM_ExecutionSystem.h"
+
+#include <functional>
+
+#ifdef WITH_CXX_GUARDEDALLOC
+# include "MEM_guardedalloc.h"
+#endif
+
+namespace blender::compositor {
+
+class NodeOperation;
+
+/**
+ * Base class for execution models. Contains shared implementation.
+ */
+class ExecutionModel {
+ protected:
+ /**
+ * Render and viewer border info. Coordinates are normalized.
+ */
+ struct {
+ bool use_render_border;
+ const rctf *render_border;
+ bool use_viewer_border;
+ const rctf *viewer_border;
+ } border_;
+
+ /**
+ * Context used during execution.
+ */
+ CompositorContext &context_;
+
+ /**
+ * All operations being executed.
+ */
+ Span<NodeOperation *> operations_;
+
+ public:
+ ExecutionModel(CompositorContext &context, Span<NodeOperation *> operations);
+
+ virtual ~ExecutionModel()
+ {
+ }
+
+ virtual void execute(ExecutionSystem &exec_system) = 0;
+
+ virtual void execute_work(const rcti &UNUSED(work_rect),
+ std::function<void(const rcti &split_rect)> UNUSED(work_func))
+ {
+ BLI_assert(!"Method not supported by current execution model");
+ }
+
+ protected:
+ bool is_breaked() const;
+
+#ifdef WITH_CXX_GUARDEDALLOC
+ MEM_CXX_CLASS_ALLOC_FUNCS("COM:BaseExecutionModel")
+#endif
+};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_ExecutionSystem.cc b/source/blender/compositor/intern/COM_ExecutionSystem.cc
index df97b8079b2..a12ec774032 100644
--- a/source/blender/compositor/intern/COM_ExecutionSystem.cc
+++ b/source/blender/compositor/intern/COM_ExecutionSystem.cc
@@ -21,22 +21,19 @@
#include "BLI_utildefines.h"
#include "PIL_time.h"
-#include "BKE_node.h"
-
-#include "BLT_translation.h"
-
-#include "COM_Converter.h"
#include "COM_Debug.h"
-#include "COM_ExecutionGroup.h"
+#include "COM_FullFrameExecutionModel.h"
#include "COM_NodeOperation.h"
#include "COM_NodeOperationBuilder.h"
-#include "COM_ReadBufferOperation.h"
+#include "COM_TiledExecutionModel.h"
#include "COM_WorkScheduler.h"
#ifdef WITH_CXX_GUARDEDALLOC
# include "MEM_guardedalloc.h"
#endif
+namespace blender::compositor {
+
ExecutionSystem::ExecutionSystem(RenderData *rd,
Scene *scene,
bNodeTree *editingtree,
@@ -53,10 +50,10 @@ ExecutionSystem::ExecutionSystem(RenderData *rd,
this->m_context.setFastCalculation(fastcalculation);
/* initialize the CompositorContext */
if (rendering) {
- this->m_context.setQuality((CompositorQuality)editingtree->render_quality);
+ this->m_context.setQuality((eCompositorQuality)editingtree->render_quality);
}
else {
- this->m_context.setQuality((CompositorQuality)editingtree->edit_quality);
+ this->m_context.setQuality((eCompositorQuality)editingtree->edit_quality);
}
this->m_context.setRendering(rendering);
this->m_context.setHasActiveOpenCLDevices(WorkScheduler::has_gpu_devices() &&
@@ -71,41 +68,23 @@ ExecutionSystem::ExecutionSystem(RenderData *rd,
builder.convertToOperations(this);
}
- unsigned int resolution[2];
-
- rctf *viewer_border = &editingtree->viewer_border;
- bool use_viewer_border = (editingtree->flag & NTREE_VIEWER_BORDER) &&
- viewer_border->xmin < viewer_border->xmax &&
- viewer_border->ymin < viewer_border->ymax;
-
- editingtree->stats_draw(editingtree->sdh, TIP_("Compositing | Determining resolution"));
-
- for (ExecutionGroup *executionGroup : m_groups) {
- resolution[0] = 0;
- resolution[1] = 0;
- executionGroup->determineResolution(resolution);
-
- if (rendering) {
- /* case when cropping to render border happens is handled in
- * compositor output and render layer nodes
- */
- if ((rd->mode & R_BORDER) && !(rd->mode & R_CROP)) {
- executionGroup->setRenderBorder(
- rd->border.xmin, rd->border.xmax, rd->border.ymin, rd->border.ymax);
- }
- }
-
- if (use_viewer_border) {
- executionGroup->setViewerBorder(
- viewer_border->xmin, viewer_border->xmax, viewer_border->ymin, viewer_border->ymax);
- }
+ switch (m_context.get_execution_model()) {
+ case eExecutionModel::Tiled:
+ execution_model_ = new TiledExecutionModel(m_context, m_operations, m_groups);
+ break;
+ case eExecutionModel::FullFrame:
+ execution_model_ = new FullFrameExecutionModel(m_context, active_buffers_, m_operations);
+ break;
+ default:
+ BLI_assert(!"Non implemented execution model");
+ break;
}
-
- // DebugInfo::graphviz(this);
}
ExecutionSystem::~ExecutionSystem()
{
+ delete execution_model_;
+
for (NodeOperation *operation : m_operations) {
delete operation;
}
@@ -117,105 +96,23 @@ ExecutionSystem::~ExecutionSystem()
this->m_groups.clear();
}
-void ExecutionSystem::set_operations(const blender::Vector<NodeOperation *> &operations,
- const blender::Vector<ExecutionGroup *> &groups)
+void ExecutionSystem::set_operations(const Vector<NodeOperation *> &operations,
+ const Vector<ExecutionGroup *> &groups)
{
m_operations = operations;
m_groups = groups;
}
-static void update_read_buffer_offset(blender::Vector<NodeOperation *> &operations)
-{
- unsigned int order = 0;
- for (NodeOperation *operation : operations) {
- if (operation->isReadBufferOperation()) {
- ReadBufferOperation *readOperation = (ReadBufferOperation *)operation;
- readOperation->setOffset(order);
- order++;
- }
- }
-}
-
-static void init_write_operations_for_execution(blender::Vector<NodeOperation *> &operations,
- const bNodeTree *bTree)
-{
- for (NodeOperation *operation : operations) {
- if (operation->isWriteBufferOperation()) {
- operation->setbNodeTree(bTree);
- operation->initExecution();
- }
- }
-}
-
-static void link_write_buffers(blender::Vector<NodeOperation *> &operations)
-{
- for (NodeOperation *operation : operations) {
- if (operation->isReadBufferOperation()) {
- ReadBufferOperation *readOperation = static_cast<ReadBufferOperation *>(operation);
- readOperation->updateMemoryBuffer();
- }
- }
-}
-
-static void init_non_write_operations_for_execution(blender::Vector<NodeOperation *> &operations,
- const bNodeTree *bTree)
-{
- for (NodeOperation *operation : operations) {
- if (!operation->isWriteBufferOperation()) {
- operation->setbNodeTree(bTree);
- operation->initExecution();
- }
- }
-}
-
-static void init_execution_groups_for_execution(blender::Vector<ExecutionGroup *> &groups,
- const int chunk_size)
-{
- for (ExecutionGroup *execution_group : groups) {
- execution_group->setChunksize(chunk_size);
- execution_group->initExecution();
- }
-}
-
void ExecutionSystem::execute()
{
- const bNodeTree *editingtree = this->m_context.getbNodeTree();
- editingtree->stats_draw(editingtree->sdh, TIP_("Compositing | Initializing execution"));
-
DebugInfo::execute_started(this);
- update_read_buffer_offset(m_operations);
-
- init_write_operations_for_execution(m_operations, m_context.getbNodeTree());
- link_write_buffers(m_operations);
- init_non_write_operations_for_execution(m_operations, m_context.getbNodeTree());
- init_execution_groups_for_execution(m_groups, m_context.getChunksize());
-
- WorkScheduler::start(this->m_context);
- execute_groups(CompositorPriority::High);
- if (!this->getContext().isFastCalculation()) {
- execute_groups(CompositorPriority::Medium);
- execute_groups(CompositorPriority::Low);
- }
- WorkScheduler::finish();
- WorkScheduler::stop();
-
- editingtree->stats_draw(editingtree->sdh, TIP_("Compositing | De-initializing execution"));
-
- for (NodeOperation *operation : m_operations) {
- operation->deinitExecution();
- }
-
- for (ExecutionGroup *execution_group : m_groups) {
- execution_group->deinitExecution();
- }
+ execution_model_->execute(*this);
}
-void ExecutionSystem::execute_groups(CompositorPriority priority)
+void ExecutionSystem::execute_work(const rcti &work_rect,
+ std::function<void(const rcti &split_rect)> work_func)
{
- for (ExecutionGroup *execution_group : m_groups) {
- if (execution_group->isOutputExecutionGroup() &&
- execution_group->getRenderPriority() == priority) {
- execution_group->execute(this);
- }
- }
+ execution_model_->execute_work(work_rect, work_func);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_ExecutionSystem.h b/source/blender/compositor/intern/COM_ExecutionSystem.h
index c12380fe839..e106209651c 100644
--- a/source/blender/compositor/intern/COM_ExecutionSystem.h
+++ b/source/blender/compositor/intern/COM_ExecutionSystem.h
@@ -25,12 +25,15 @@ class ExecutionGroup;
#include "COM_ExecutionGroup.h"
#include "COM_Node.h"
#include "COM_NodeOperation.h"
+#include "COM_SharedOperationBuffers.h"
#include "DNA_color_types.h"
#include "DNA_node_types.h"
#include "BLI_vector.hh"
+namespace blender::compositor {
+
/**
* \page execution Execution model
* In order to get to an efficient model for execution, several steps are being done. these steps
@@ -70,17 +73,17 @@ class ExecutionGroup;
*
* - Image size conversions: the system can automatically convert when resolutions do not match.
* An NodeInput has a resize mode. This can be any of the following settings.
- * - [@ref InputSocketResizeMode.COM_SC_CENTER]:
+ * - [@ref InputSocketResizeMode.ResizeMode::Center]:
* The center of both images are aligned
- * - [@ref InputSocketResizeMode.COM_SC_FIT_WIDTH]:
+ * - [@ref InputSocketResizeMode.ResizeMode::FitWidth]:
* The width of both images are aligned
- * - [@ref InputSocketResizeMode.COM_SC_FIT_HEIGHT]:
+ * - [@ref InputSocketResizeMode.ResizeMode::FitHeight]:
* The height of both images are aligned
- * - [@ref InputSocketResizeMode.COM_SC_FIT]:
+ * - [@ref InputSocketResizeMode.ResizeMode::FitAny]:
* The width, or the height of both images are aligned to make sure that it fits.
- * - [@ref InputSocketResizeMode.COM_SC_STRETCH]:
+ * - [@ref InputSocketResizeMode.ResizeMode::Stretch]:
* The width and the height of both images are aligned.
- * - [@ref InputSocketResizeMode.COM_SC_NO_RESIZE]:
+ * - [@ref InputSocketResizeMode.ResizeMode::None]:
* Bottom left of the images are aligned.
*
* \see COM_convert_data_type Datatype conversions
@@ -113,13 +116,21 @@ class ExecutionGroup;
* \see ExecutionGroup class representing the ExecutionGroup
*/
+/* Forward declarations. */
+class ExecutionModel;
+
/**
* \brief the ExecutionSystem contains the whole compositor tree.
*/
class ExecutionSystem {
-
private:
/**
+ * Contains operations active buffers data. Buffers will be disposed once reader operations are
+ * finished.
+ */
+ SharedOperationBuffers active_buffers_;
+
+ /**
* \brief the context used during execution
*/
CompositorContext m_context;
@@ -127,12 +138,17 @@ class ExecutionSystem {
/**
* \brief vector of operations
*/
- blender::Vector<NodeOperation *> m_operations;
+ Vector<NodeOperation *> m_operations;
/**
* \brief vector of groups
*/
- blender::Vector<ExecutionGroup *> m_groups;
+ Vector<ExecutionGroup *> m_groups;
+
+ /**
+ * Active execution model implementation.
+ */
+ ExecutionModel *execution_model_;
private: // methods
public:
@@ -157,8 +173,8 @@ class ExecutionSystem {
*/
~ExecutionSystem();
- void set_operations(const blender::Vector<NodeOperation *> &operations,
- const blender::Vector<ExecutionGroup *> &groups);
+ void set_operations(const Vector<NodeOperation *> &operations,
+ const Vector<ExecutionGroup *> &groups);
/**
* \brief execute this system
@@ -176,9 +192,14 @@ class ExecutionSystem {
return this->m_context;
}
- private:
- void execute_groups(CompositorPriority priority);
+ SharedOperationBuffers &get_active_buffers()
+ {
+ return active_buffers_;
+ }
+ void execute_work(const rcti &work_rect, std::function<void(const rcti &split_rect)> work_func);
+
+ private:
/* allow the DebugInfo class to look at internals */
friend class DebugInfo;
@@ -186,3 +207,5 @@ class ExecutionSystem {
MEM_CXX_CLASS_ALLOC_FUNCS("COM:ExecutionSystem")
#endif
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc b/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc
new file mode 100644
index 00000000000..21075bb7255
--- /dev/null
+++ b/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc
@@ -0,0 +1,362 @@
+/*
+ * 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.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+#include "COM_FullFrameExecutionModel.h"
+#include "COM_Debug.h"
+#include "COM_ExecutionGroup.h"
+#include "COM_ReadBufferOperation.h"
+#include "COM_WorkScheduler.h"
+
+#include "BLT_translation.h"
+
+#ifdef WITH_CXX_GUARDEDALLOC
+# include "MEM_guardedalloc.h"
+#endif
+
+namespace blender::compositor {
+
+FullFrameExecutionModel::FullFrameExecutionModel(CompositorContext &context,
+ SharedOperationBuffers &shared_buffers,
+ Span<NodeOperation *> operations)
+ : ExecutionModel(context, operations),
+ active_buffers_(shared_buffers),
+ num_operations_finished_(0),
+ work_mutex_(),
+ work_finished_cond_()
+{
+ priorities_.append(eCompositorPriority::High);
+ if (!context.isFastCalculation()) {
+ priorities_.append(eCompositorPriority::Medium);
+ priorities_.append(eCompositorPriority::Low);
+ }
+
+ BLI_mutex_init(&work_mutex_);
+ BLI_condition_init(&work_finished_cond_);
+}
+
+FullFrameExecutionModel::~FullFrameExecutionModel()
+{
+ BLI_condition_end(&work_finished_cond_);
+ BLI_mutex_end(&work_mutex_);
+}
+
+void FullFrameExecutionModel::execute(ExecutionSystem &exec_system)
+{
+ const bNodeTree *node_tree = this->context_.getbNodeTree();
+ node_tree->stats_draw(node_tree->sdh, TIP_("Compositing | Initializing execution"));
+
+ DebugInfo::graphviz(&exec_system);
+
+ determine_areas_to_render_and_reads();
+ render_operations(exec_system);
+}
+
+void FullFrameExecutionModel::determine_areas_to_render_and_reads()
+{
+ const bool is_rendering = context_.isRendering();
+ const bNodeTree *node_tree = context_.getbNodeTree();
+
+ rcti area;
+ for (eCompositorPriority priority : priorities_) {
+ for (NodeOperation *op : operations_) {
+ op->setbNodeTree(node_tree);
+ if (op->isOutputOperation(is_rendering) && op->getRenderPriority() == priority) {
+ get_output_render_area(op, area);
+ determine_areas_to_render(op, area);
+ determine_reads(op);
+ }
+ }
+ }
+}
+
+Vector<MemoryBuffer *> FullFrameExecutionModel::get_input_buffers(NodeOperation *op)
+{
+ const int num_inputs = op->getNumberOfInputSockets();
+ Vector<MemoryBuffer *> inputs_buffers(num_inputs);
+ for (int i = 0; i < num_inputs; i++) {
+ NodeOperation *input_op = op->get_input_operation(i);
+ inputs_buffers[i] = active_buffers_.get_rendered_buffer(input_op);
+ }
+ return inputs_buffers;
+}
+
+MemoryBuffer *FullFrameExecutionModel::create_operation_buffer(NodeOperation *op)
+{
+ rcti op_rect;
+ BLI_rcti_init(&op_rect, 0, op->getWidth(), 0, op->getHeight());
+
+ const DataType data_type = op->getOutputSocket(0)->getDataType();
+ /* TODO: We should check if the operation is constant instead of is_set_operation. Finding a way
+ * to know if an operation is constant has to be implemented yet. */
+ const bool is_a_single_elem = op->get_flags().is_set_operation;
+ return new MemoryBuffer(data_type, op_rect, is_a_single_elem);
+}
+
+void FullFrameExecutionModel::render_operation(NodeOperation *op, ExecutionSystem &exec_system)
+{
+ Vector<MemoryBuffer *> input_bufs = get_input_buffers(op);
+
+ const bool has_outputs = op->getNumberOfOutputSockets() > 0;
+ MemoryBuffer *op_buf = has_outputs ? create_operation_buffer(op) : nullptr;
+ Span<rcti> areas = active_buffers_.get_areas_to_render(op);
+ op->render(op_buf, areas, input_bufs, exec_system);
+ active_buffers_.set_rendered_buffer(op, std::unique_ptr<MemoryBuffer>(op_buf));
+
+ operation_finished(op);
+}
+
+/**
+ * Render output operations in order of priority.
+ */
+void FullFrameExecutionModel::render_operations(ExecutionSystem &exec_system)
+{
+ const bool is_rendering = context_.isRendering();
+
+ WorkScheduler::start(this->context_);
+ for (eCompositorPriority priority : priorities_) {
+ for (NodeOperation *op : operations_) {
+ if (op->isOutputOperation(is_rendering) && op->getRenderPriority() == priority) {
+ render_output_dependencies(op, exec_system);
+ render_operation(op, exec_system);
+ }
+ }
+ }
+ WorkScheduler::stop();
+}
+
+/**
+ * Returns all dependencies from inputs to outputs. A dependency may be repeated when
+ * several operations depend on it.
+ */
+static Vector<NodeOperation *> get_operation_dependencies(NodeOperation *operation)
+{
+ /* Get dependencies from outputs to inputs. */
+ Vector<NodeOperation *> dependencies;
+ Vector<NodeOperation *> next_outputs;
+ next_outputs.append(operation);
+ while (next_outputs.size() > 0) {
+ Vector<NodeOperation *> outputs(next_outputs);
+ next_outputs.clear();
+ for (NodeOperation *output : outputs) {
+ for (int i = 0; i < output->getNumberOfInputSockets(); i++) {
+ next_outputs.append(output->get_input_operation(i));
+ }
+ }
+ dependencies.extend(next_outputs);
+ }
+
+ /* Reverse to get dependencies from inputs to outputs. */
+ std::reverse(dependencies.begin(), dependencies.end());
+
+ return dependencies;
+}
+
+void FullFrameExecutionModel::render_output_dependencies(NodeOperation *output_op,
+ ExecutionSystem &exec_system)
+{
+ BLI_assert(output_op->isOutputOperation(context_.isRendering()));
+ Vector<NodeOperation *> dependencies = get_operation_dependencies(output_op);
+ for (NodeOperation *op : dependencies) {
+ if (!active_buffers_.is_operation_rendered(op)) {
+ render_operation(op, exec_system);
+ }
+ }
+}
+
+/**
+ * Determines all operations areas needed to render given output area.
+ */
+void FullFrameExecutionModel::determine_areas_to_render(NodeOperation *output_op,
+ const rcti &output_area)
+{
+ BLI_assert(output_op->isOutputOperation(context_.isRendering()));
+
+ Vector<std::pair<NodeOperation *, const rcti>> stack;
+ stack.append({output_op, output_area});
+ while (stack.size() > 0) {
+ std::pair<NodeOperation *, rcti> pair = stack.pop_last();
+ NodeOperation *operation = pair.first;
+ const rcti &render_area = pair.second;
+ if (active_buffers_.is_area_registered(operation, render_area)) {
+ continue;
+ }
+
+ active_buffers_.register_area(operation, render_area);
+
+ const int num_inputs = operation->getNumberOfInputSockets();
+ for (int i = 0; i < num_inputs; i++) {
+ NodeOperation *input_op = operation->get_input_operation(i);
+ rcti input_op_rect, input_area;
+ BLI_rcti_init(&input_op_rect, 0, input_op->getWidth(), 0, input_op->getHeight());
+ operation->get_area_of_interest(input_op, render_area, input_area);
+
+ /* Ensure area of interest is within operation bounds, cropping areas outside. */
+ BLI_rcti_isect(&input_area, &input_op_rect, &input_area);
+
+ stack.append({input_op, input_area});
+ }
+ }
+}
+
+/**
+ * Determines reads to receive by operations in output operation tree (i.e: Number of dependent
+ * operations each operation has).
+ */
+void FullFrameExecutionModel::determine_reads(NodeOperation *output_op)
+{
+ BLI_assert(output_op->isOutputOperation(context_.isRendering()));
+
+ Vector<NodeOperation *> stack;
+ stack.append(output_op);
+ while (stack.size() > 0) {
+ NodeOperation *operation = stack.pop_last();
+ const int num_inputs = operation->getNumberOfInputSockets();
+ for (int i = 0; i < num_inputs; i++) {
+ NodeOperation *input_op = operation->get_input_operation(i);
+ if (!active_buffers_.has_registered_reads(input_op)) {
+ stack.append(input_op);
+ }
+ active_buffers_.register_read(input_op);
+ }
+ }
+}
+
+/**
+ * Calculates given output operation area to be rendered taking into account viewer and render
+ * borders.
+ */
+void FullFrameExecutionModel::get_output_render_area(NodeOperation *output_op, rcti &r_area)
+{
+ BLI_assert(output_op->isOutputOperation(context_.isRendering()));
+
+ /* By default return operation bounds (no border). */
+ const int op_width = output_op->getWidth();
+ const int op_height = output_op->getHeight();
+ BLI_rcti_init(&r_area, 0, op_width, 0, op_height);
+
+ const bool has_viewer_border = border_.use_viewer_border &&
+ (output_op->get_flags().is_viewer_operation ||
+ output_op->get_flags().is_preview_operation);
+ const bool has_render_border = border_.use_render_border;
+ if (has_viewer_border || has_render_border) {
+ /* Get border with normalized coordinates. */
+ const rctf *norm_border = has_viewer_border ? border_.viewer_border : border_.render_border;
+
+ /* Return de-normalized border. */
+ BLI_rcti_init(&r_area,
+ norm_border->xmin * op_width,
+ norm_border->xmax * op_width,
+ norm_border->ymin * op_height,
+ norm_border->ymax * op_height);
+ }
+}
+
+/**
+ * Multi-threadedly execute given work function passing work_rect splits as argument.
+ */
+void FullFrameExecutionModel::execute_work(const rcti &work_rect,
+ std::function<void(const rcti &split_rect)> work_func)
+{
+ if (is_breaked()) {
+ return;
+ }
+
+ /* Split work vertically to maximize continuous memory. */
+ const int work_height = BLI_rcti_size_y(&work_rect);
+ const int num_sub_works = MIN2(WorkScheduler::get_num_cpu_threads(), work_height);
+ const int split_height = num_sub_works == 0 ? 0 : work_height / num_sub_works;
+ int remaining_height = work_height - split_height * num_sub_works;
+
+ Vector<WorkPackage> sub_works(num_sub_works);
+ int sub_work_y = work_rect.ymin;
+ int num_sub_works_finished = 0;
+ for (int i = 0; i < num_sub_works; i++) {
+ int sub_work_height = split_height;
+
+ /* Distribute remaining height between sub-works. */
+ if (remaining_height > 0) {
+ sub_work_height++;
+ remaining_height--;
+ }
+
+ WorkPackage &sub_work = sub_works[i];
+ sub_work.type = eWorkPackageType::CustomFunction;
+ sub_work.execute_fn = [=, &work_func, &work_rect]() {
+ if (is_breaked()) {
+ return;
+ }
+ rcti split_rect;
+ BLI_rcti_init(
+ &split_rect, work_rect.xmin, work_rect.xmax, sub_work_y, sub_work_y + sub_work_height);
+ work_func(split_rect);
+ };
+ sub_work.executed_fn = [&]() {
+ BLI_mutex_lock(&work_mutex_);
+ num_sub_works_finished++;
+ if (num_sub_works_finished == num_sub_works) {
+ BLI_condition_notify_one(&work_finished_cond_);
+ }
+ BLI_mutex_unlock(&work_mutex_);
+ };
+ WorkScheduler::schedule(&sub_work);
+ sub_work_y += sub_work_height;
+ }
+ BLI_assert(sub_work_y == work_rect.ymax);
+
+ WorkScheduler::finish();
+
+ /* Ensure all sub-works finished.
+ * TODO: This a workaround for WorkScheduler::finish() not waiting all works on queue threading
+ * model. Sync code should be removed once it's fixed. */
+ BLI_mutex_lock(&work_mutex_);
+ if (num_sub_works_finished < num_sub_works) {
+ BLI_condition_wait(&work_finished_cond_, &work_mutex_);
+ }
+ BLI_mutex_unlock(&work_mutex_);
+}
+
+void FullFrameExecutionModel::operation_finished(NodeOperation *operation)
+{
+ /* Report inputs reads so that buffers may be freed/reused. */
+ const int num_inputs = operation->getNumberOfInputSockets();
+ for (int i = 0; i < num_inputs; i++) {
+ active_buffers_.read_finished(operation->get_input_operation(i));
+ }
+
+ num_operations_finished_++;
+ update_progress_bar();
+}
+
+void FullFrameExecutionModel::update_progress_bar()
+{
+ const bNodeTree *tree = context_.getbNodeTree();
+ if (tree) {
+ const float progress = num_operations_finished_ / static_cast<float>(operations_.size());
+ tree->progress(tree->prh, progress);
+
+ char buf[128];
+ BLI_snprintf(buf,
+ sizeof(buf),
+ TIP_("Compositing | Operation %i-%li"),
+ num_operations_finished_ + 1,
+ operations_.size());
+ tree->stats_draw(tree->sdh, buf);
+ }
+}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_FullFrameExecutionModel.h b/source/blender/compositor/intern/COM_FullFrameExecutionModel.h
new file mode 100644
index 00000000000..e68ad93b407
--- /dev/null
+++ b/source/blender/compositor/intern/COM_FullFrameExecutionModel.h
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+#pragma once
+
+#include "COM_ExecutionModel.h"
+
+#ifdef WITH_CXX_GUARDEDALLOC
+# include "MEM_guardedalloc.h"
+#endif
+
+namespace blender::compositor {
+
+/* Forward declarations. */
+class ExecutionGroup;
+
+/**
+ * Fully renders operations in order from inputs to outputs.
+ */
+class FullFrameExecutionModel : public ExecutionModel {
+ private:
+ /**
+ * Contains operations active buffers data. Buffers will be disposed once reader operations are
+ * finished.
+ */
+ SharedOperationBuffers &active_buffers_;
+
+ /**
+ * Number of operations finished.
+ */
+ int num_operations_finished_;
+
+ /**
+ * Order of priorities for output operations execution.
+ */
+ Vector<eCompositorPriority> priorities_;
+
+ ThreadMutex work_mutex_;
+ ThreadCondition work_finished_cond_;
+
+ public:
+ FullFrameExecutionModel(CompositorContext &context,
+ SharedOperationBuffers &shared_buffers,
+ Span<NodeOperation *> operations);
+ ~FullFrameExecutionModel();
+
+ void execute(ExecutionSystem &exec_system) override;
+
+ void execute_work(const rcti &work_rect,
+ std::function<void(const rcti &split_rect)> work_func) override;
+
+ private:
+ void determine_areas_to_render_and_reads();
+ void render_operations(ExecutionSystem &exec_system);
+ void render_output_dependencies(NodeOperation *output_op, ExecutionSystem &exec_system);
+ Vector<MemoryBuffer *> get_input_buffers(NodeOperation *op);
+ MemoryBuffer *create_operation_buffer(NodeOperation *op);
+ void render_operation(NodeOperation *op, ExecutionSystem &exec_system);
+
+ void operation_finished(NodeOperation *operation);
+
+ void get_output_render_area(NodeOperation *output_op, rcti &r_area);
+ void determine_areas_to_render(NodeOperation *output_op, const rcti &output_area);
+ void determine_reads(NodeOperation *output_op);
+
+ void update_progress_bar();
+
+#ifdef WITH_CXX_GUARDEDALLOC
+ MEM_CXX_CLASS_ALLOC_FUNCS("COM:FullFrameExecutionModel")
+#endif
+};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.cc b/source/blender/compositor/intern/COM_MemoryBuffer.cc
index 0b28168720e..8c30d3215d7 100644
--- a/source/blender/compositor/intern/COM_MemoryBuffer.cc
+++ b/source/blender/compositor/intern/COM_MemoryBuffer.cc
@@ -20,45 +20,54 @@
#include "MEM_guardedalloc.h"
-static unsigned int determine_num_channels(DataType datatype)
-{
- switch (datatype) {
- case DataType::Value:
- return COM_NUM_CHANNELS_VALUE;
- case DataType::Vector:
- return COM_NUM_CHANNELS_VECTOR;
- case DataType::Color:
- default:
- return COM_NUM_CHANNELS_COLOR;
- }
-}
+namespace blender::compositor {
MemoryBuffer::MemoryBuffer(MemoryProxy *memoryProxy, const rcti &rect, MemoryBufferState state)
{
m_rect = rect;
+ this->m_is_a_single_elem = false;
this->m_memoryProxy = memoryProxy;
- this->m_num_channels = determine_num_channels(memoryProxy->getDataType());
+ this->m_num_channels = COM_data_type_num_channels(memoryProxy->getDataType());
this->m_buffer = (float *)MEM_mallocN_aligned(
sizeof(float) * buffer_len() * this->m_num_channels, 16, "COM_MemoryBuffer");
this->m_state = state;
this->m_datatype = memoryProxy->getDataType();
+
+ set_strides();
}
-MemoryBuffer::MemoryBuffer(DataType dataType, const rcti &rect)
+MemoryBuffer::MemoryBuffer(DataType dataType, const rcti &rect, bool is_a_single_elem)
{
m_rect = rect;
+ this->m_is_a_single_elem = is_a_single_elem;
this->m_memoryProxy = nullptr;
- this->m_num_channels = determine_num_channels(dataType);
+ this->m_num_channels = COM_data_type_num_channels(dataType);
this->m_buffer = (float *)MEM_mallocN_aligned(
sizeof(float) * buffer_len() * this->m_num_channels, 16, "COM_MemoryBuffer");
this->m_state = MemoryBufferState::Temporary;
this->m_datatype = dataType;
+
+ set_strides();
}
MemoryBuffer::MemoryBuffer(const MemoryBuffer &src)
- : MemoryBuffer(src.m_memoryProxy, src.m_rect, MemoryBufferState::Temporary)
+ : MemoryBuffer(src.m_datatype, src.m_rect, false)
+{
+ m_memoryProxy = src.m_memoryProxy;
+ /* src may be single elem buffer */
+ fill_from(src);
+}
+
+void MemoryBuffer::set_strides()
{
- memcpy(m_buffer, src.m_buffer, buffer_len() * m_num_channels * sizeof(float));
+ if (m_is_a_single_elem) {
+ this->elem_stride = 0;
+ this->row_stride = 0;
+ }
+ else {
+ this->elem_stride = m_num_channels;
+ this->row_stride = getWidth() * m_num_channels;
+ }
}
void MemoryBuffer::clear()
@@ -111,6 +120,8 @@ MemoryBuffer::~MemoryBuffer()
void MemoryBuffer::fill_from(const MemoryBuffer &src)
{
+ BLI_assert(!this->is_a_single_elem());
+
unsigned int otherY;
unsigned int minX = MAX2(this->m_rect.xmin, src.m_rect.xmin);
unsigned int maxX = MIN2(this->m_rect.xmax, src.m_rect.xmax);
@@ -120,10 +131,8 @@ void MemoryBuffer::fill_from(const MemoryBuffer &src)
int otherOffset;
for (otherY = minY; otherY < maxY; otherY++) {
- otherOffset = ((otherY - src.m_rect.ymin) * src.getWidth() + minX - src.m_rect.xmin) *
- this->m_num_channels;
- offset = ((otherY - this->m_rect.ymin) * getWidth() + minX - this->m_rect.xmin) *
- this->m_num_channels;
+ otherOffset = src.get_coords_offset(minX, otherY);
+ offset = this->get_coords_offset(minX, otherY);
memcpy(&this->m_buffer[offset],
&src.m_buffer[otherOffset],
(maxX - minX) * this->m_num_channels * sizeof(float));
@@ -134,8 +143,7 @@ void MemoryBuffer::writePixel(int x, int y, const float color[4])
{
if (x >= this->m_rect.xmin && x < this->m_rect.xmax && y >= this->m_rect.ymin &&
y < this->m_rect.ymax) {
- const int offset = (getWidth() * (y - this->m_rect.ymin) + x - this->m_rect.xmin) *
- this->m_num_channels;
+ const int offset = get_coords_offset(x, y);
memcpy(&this->m_buffer[offset], color, sizeof(float) * this->m_num_channels);
}
}
@@ -144,8 +152,7 @@ void MemoryBuffer::addPixel(int x, int y, const float color[4])
{
if (x >= this->m_rect.xmin && x < this->m_rect.xmax && y >= this->m_rect.ymin &&
y < this->m_rect.ymax) {
- const int offset = (getWidth() * (y - this->m_rect.ymin) + x - this->m_rect.xmin) *
- this->m_num_channels;
+ const int offset = get_coords_offset(x, y);
float *dst = &this->m_buffer[offset];
const float *src = color;
for (int i = 0; i < this->m_num_channels; i++, dst++, src++) {
@@ -162,24 +169,31 @@ static void read_ewa_pixel_sampled(void *userdata, int x, int y, float result[4]
void MemoryBuffer::readEWA(float *result, const float uv[2], const float derivatives[2][2])
{
- BLI_assert(this->m_datatype == DataType::Color);
- float inv_width = 1.0f / (float)this->getWidth(), inv_height = 1.0f / (float)this->getHeight();
- /* TODO(sergey): Render pipeline uses normalized coordinates and derivatives,
- * but compositor uses pixel space. For now let's just divide the values and
- * switch compositor to normalized space for EWA later.
- */
- float uv_normal[2] = {uv[0] * inv_width, uv[1] * inv_height};
- float du_normal[2] = {derivatives[0][0] * inv_width, derivatives[0][1] * inv_height};
- float dv_normal[2] = {derivatives[1][0] * inv_width, derivatives[1][1] * inv_height};
-
- BLI_ewa_filter(this->getWidth(),
- this->getHeight(),
- false,
- true,
- uv_normal,
- du_normal,
- dv_normal,
- read_ewa_pixel_sampled,
- this,
- result);
+ if (m_is_a_single_elem) {
+ memcpy(result, m_buffer, sizeof(float) * this->m_num_channels);
+ }
+ else {
+ BLI_assert(this->m_datatype == DataType::Color);
+ float inv_width = 1.0f / (float)this->getWidth(), inv_height = 1.0f / (float)this->getHeight();
+ /* TODO(sergey): Render pipeline uses normalized coordinates and derivatives,
+ * but compositor uses pixel space. For now let's just divide the values and
+ * switch compositor to normalized space for EWA later.
+ */
+ float uv_normal[2] = {uv[0] * inv_width, uv[1] * inv_height};
+ float du_normal[2] = {derivatives[0][0] * inv_width, derivatives[0][1] * inv_height};
+ float dv_normal[2] = {derivatives[1][0] * inv_width, derivatives[1][1] * inv_height};
+
+ BLI_ewa_filter(this->getWidth(),
+ this->getHeight(),
+ false,
+ true,
+ uv_normal,
+ du_normal,
+ dv_normal,
+ read_ewa_pixel_sampled,
+ this,
+ result);
+ }
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.h b/source/blender/compositor/intern/COM_MemoryBuffer.h
index 6f719b61122..97b220508e0 100644
--- a/source/blender/compositor/intern/COM_MemoryBuffer.h
+++ b/source/blender/compositor/intern/COM_MemoryBuffer.h
@@ -16,17 +16,16 @@
* Copyright 2011, Blender Foundation.
*/
-class MemoryBuffer;
-
#pragma once
#include "COM_ExecutionGroup.h"
#include "COM_MemoryProxy.h"
-#include "COM_SocketReader.h"
#include "BLI_math.h"
#include "BLI_rect.h"
+namespace blender::compositor {
+
/**
* \brief state of a memory buffer
* \ingroup Memory
@@ -51,6 +50,25 @@ class MemoryProxy;
* \brief a MemoryBuffer contains access to the data of a chunk
*/
class MemoryBuffer {
+ public:
+ /**
+ * Offset between elements.
+ *
+ * Should always be used for the x dimension when calculating buffer offsets.
+ * It will be 0 when is_a_single_elem=true.
+ * e.g: buffer_index = y * buffer.row_stride + x * buffer.elem_stride
+ */
+ int elem_stride;
+
+ /**
+ * Offset between rows.
+ *
+ * Should always be used for the y dimension when calculating buffer offsets.
+ * It will be 0 when is_a_single_elem=true.
+ * e.g: buffer_index = y * buffer.row_stride + x * buffer.elem_stride
+ */
+ int row_stride;
+
private:
/**
* \brief proxy of the memory (same for all chunks in the same buffer)
@@ -83,6 +101,11 @@ class MemoryBuffer {
*/
uint8_t m_num_channels;
+ /**
+ * Whether buffer is a single element in memory.
+ */
+ bool m_is_a_single_elem;
+
public:
/**
* \brief construct new temporarily MemoryBuffer for an area
@@ -92,7 +115,7 @@ class MemoryBuffer {
/**
* \brief construct new temporarily MemoryBuffer for an area
*/
- MemoryBuffer(DataType datatype, const rcti &rect);
+ MemoryBuffer(DataType datatype, const rcti &rect, bool is_a_single_elem = false);
/**
* Copy constructor
@@ -104,6 +127,102 @@ class MemoryBuffer {
*/
~MemoryBuffer();
+ /**
+ * Whether buffer is a single element in memory independently of its resolution. True for set
+ * operations buffers.
+ */
+ bool is_a_single_elem() const
+ {
+ return m_is_a_single_elem;
+ }
+
+ float &operator[](int index)
+ {
+ BLI_assert(m_is_a_single_elem ? index < m_num_channels :
+ index < get_coords_offset(getWidth(), getHeight()));
+ return m_buffer[index];
+ }
+
+ const float &operator[](int index) const
+ {
+ BLI_assert(m_is_a_single_elem ? index < m_num_channels :
+ index < get_coords_offset(getWidth(), getHeight()));
+ return m_buffer[index];
+ }
+
+ /**
+ * Get offset needed to jump from buffer start to given coordinates.
+ */
+ int get_coords_offset(int x, int y) const
+ {
+ return (y - m_rect.ymin) * row_stride + (x - m_rect.xmin) * elem_stride;
+ }
+
+ /**
+ * Get buffer element at given coordinates.
+ */
+ float *get_elem(int x, int y)
+ {
+ BLI_assert(x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax);
+ return m_buffer + get_coords_offset(x, y);
+ }
+
+ /**
+ * Get buffer element at given coordinates.
+ */
+ const float *get_elem(int x, int y) const
+ {
+ BLI_assert(x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax);
+ return m_buffer + get_coords_offset(x, y);
+ }
+
+ /**
+ * Get channel value at given coordinates.
+ */
+ float &get_value(int x, int y, int channel)
+ {
+ BLI_assert(x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax &&
+ channel >= 0 && channel < m_num_channels);
+ return m_buffer[get_coords_offset(x, y) + channel];
+ }
+
+ /**
+ * Get channel value at given coordinates.
+ */
+ const float &get_value(int x, int y, int channel) const
+ {
+ BLI_assert(x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax &&
+ channel >= 0 && channel < m_num_channels);
+ return m_buffer[get_coords_offset(x, y) + channel];
+ }
+
+ /**
+ * Get the buffer row end.
+ */
+ const float *get_row_end(int y) const
+ {
+ BLI_assert(y >= 0 && y < getHeight());
+ return m_buffer + (is_a_single_elem() ? m_num_channels : get_coords_offset(getWidth(), y));
+ }
+
+ /**
+ * Get the number of elements in memory for a row. For single element buffers it will always
+ * be 1.
+ */
+ int get_memory_width() const
+ {
+ return is_a_single_elem() ? 1 : getWidth();
+ }
+
+ /**
+ * Get number of elements in memory for a column. For single element buffers it will
+ * always be 1.
+ */
+ int get_memory_height() const
+ {
+ return is_a_single_elem() ? 1 : getHeight();
+ }
+
uint8_t get_num_channels()
{
return this->m_num_channels;
@@ -217,7 +336,7 @@ class MemoryBuffer {
int u = x;
int v = y;
this->wrap_pixel(u, v, extend_x, extend_y);
- const int offset = (getWidth() * y + x) * this->m_num_channels;
+ const int offset = get_coords_offset(u, v);
float *buffer = &this->m_buffer[offset];
memcpy(result, buffer, sizeof(float) * this->m_num_channels);
}
@@ -233,7 +352,7 @@ class MemoryBuffer {
int v = y;
this->wrap_pixel(u, v, extend_x, extend_y);
- const int offset = (getWidth() * v + u) * this->m_num_channels;
+ const int offset = get_coords_offset(u, v);
BLI_assert(offset >= 0);
BLI_assert(offset < this->buffer_len() * this->m_num_channels);
@@ -259,15 +378,20 @@ class MemoryBuffer {
copy_vn_fl(result, this->m_num_channels, 0.0f);
return;
}
- BLI_bilinear_interpolation_wrap_fl(this->m_buffer,
- result,
- getWidth(),
- getHeight(),
- this->m_num_channels,
- u,
- v,
- extend_x == MemoryBufferExtend::Repeat,
- extend_y == MemoryBufferExtend::Repeat);
+ if (m_is_a_single_elem) {
+ memcpy(result, m_buffer, sizeof(float) * this->m_num_channels);
+ }
+ else {
+ BLI_bilinear_interpolation_wrap_fl(this->m_buffer,
+ result,
+ getWidth(),
+ getHeight(),
+ this->m_num_channels,
+ u,
+ v,
+ extend_x == MemoryBufferExtend::Repeat,
+ extend_y == MemoryBufferExtend::Repeat);
+ }
}
void readEWA(float *result, const float uv[2], const float derivatives[2][2]);
@@ -322,12 +446,15 @@ class MemoryBuffer {
float get_max_value(const rcti &rect) const;
private:
+ void set_strides();
const int buffer_len() const
{
- return getWidth() * getHeight();
+ return get_memory_width() * get_memory_height();
}
#ifdef WITH_CXX_GUARDEDALLOC
MEM_CXX_CLASS_ALLOC_FUNCS("COM:MemoryBuffer")
#endif
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_MemoryProxy.cc b/source/blender/compositor/intern/COM_MemoryProxy.cc
index 8ef834e1efe..6023850c944 100644
--- a/source/blender/compositor/intern/COM_MemoryProxy.cc
+++ b/source/blender/compositor/intern/COM_MemoryProxy.cc
@@ -18,6 +18,12 @@
#include "COM_MemoryProxy.h"
+#include "COM_MemoryBuffer.h"
+
+#include "BLI_rect.h"
+
+namespace blender::compositor {
+
MemoryProxy::MemoryProxy(DataType datatype)
{
this->m_writeBufferOperation = nullptr;
@@ -43,3 +49,5 @@ void MemoryProxy::free()
this->m_buffer = nullptr;
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_MemoryProxy.h b/source/blender/compositor/intern/COM_MemoryProxy.h
index ee98ff41630..6814afada74 100644
--- a/source/blender/compositor/intern/COM_MemoryProxy.h
+++ b/source/blender/compositor/intern/COM_MemoryProxy.h
@@ -16,13 +16,18 @@
* Copyright 2011, Blender Foundation.
*/
-class MemoryProxy;
-
#pragma once
-#include "COM_ExecutionGroup.h"
-#include "COM_MemoryBuffer.h"
+#ifdef WITH_CXX_GUARDEDALLOC
+# include "MEM_guardedalloc.h"
+#endif
+
+#include "COM_defines.h"
+
+namespace blender::compositor {
+/* Forward declarations. */
+class MemoryBuffer;
class ExecutionGroup;
class WriteBufferOperation;
@@ -69,7 +74,7 @@ class MemoryProxy {
/**
* \brief get the ExecutionGroup that can be scheduled to calculate a certain chunk.
*/
- ExecutionGroup *getExecutor()
+ ExecutionGroup *getExecutor() const
{
return this->m_executor;
}
@@ -87,7 +92,7 @@ class MemoryProxy {
* \brief get the WriteBufferOperation that is responsible for writing to this MemoryProxy
* \return WriteBufferOperation
*/
- WriteBufferOperation *getWriteBufferOperation()
+ WriteBufferOperation *getWriteBufferOperation() const
{
return this->m_writeBufferOperation;
}
@@ -119,3 +124,5 @@ class MemoryProxy {
MEM_CXX_CLASS_ALLOC_FUNCS("COM:MemoryProxy")
#endif
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_MetaData.cc b/source/blender/compositor/intern/COM_MetaData.cc
index ad72b242c8c..88bfa385514 100644
--- a/source/blender/compositor/intern/COM_MetaData.cc
+++ b/source/blender/compositor/intern/COM_MetaData.cc
@@ -24,6 +24,8 @@
#include <string_view>
+namespace blender::compositor {
+
void MetaData::add(const blender::StringRef key, const blender::StringRef value)
{
entries_.add(key, value);
@@ -64,7 +66,7 @@ void MetaData::replaceHashNeutralCryptomatteKeys(const blender::StringRef layer_
void MetaData::addToRenderResult(RenderResult *render_result) const
{
- for (blender::Map<std::string, std::string>::Item entry : entries_.items()) {
+ for (Map<std::string, std::string>::Item entry : entries_.items()) {
BKE_render_result_stamp_data(render_result, entry.key.c_str(), entry.value.c_str());
}
}
@@ -104,3 +106,5 @@ void MetaDataExtractCallbackData::extract_cryptomatte_meta_data(void *_data,
data->addMetaData(META_DATA_KEY_CRYPTOMATTE_MANIFEST, propvalue);
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_MetaData.h b/source/blender/compositor/intern/COM_MetaData.h
index fa3de895b4e..a76540dc3af 100644
--- a/source/blender/compositor/intern/COM_MetaData.h
+++ b/source/blender/compositor/intern/COM_MetaData.h
@@ -28,6 +28,8 @@
/* Forward declarations. */
struct RenderResult;
+namespace blender::compositor {
+
/* Cryptomatte includes hash in its meta data keys. The hash is generated from the render
* layer/pass name. Compositing happens without the knowledge of the original layer and pass. The
* next keys are used to transfer the cryptomatte meta data in a neutral way. The file output node
@@ -41,7 +43,7 @@ constexpr blender::StringRef META_DATA_KEY_CRYPTOMATTE_NAME("cryptomatte/{hash}/
class MetaData {
private:
- blender::Map<std::string, std::string> entries_;
+ Map<std::string, std::string> entries_;
void addCryptomatteEntry(const blender::StringRef layer_name,
const blender::StringRefNull key,
const blender::StringRef value);
@@ -69,3 +71,5 @@ struct MetaDataExtractCallbackData {
char *propvalue,
int UNUSED(len));
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_MultiThreadedOperation.cc b/source/blender/compositor/intern/COM_MultiThreadedOperation.cc
new file mode 100644
index 00000000000..c54c2edccb0
--- /dev/null
+++ b/source/blender/compositor/intern/COM_MultiThreadedOperation.cc
@@ -0,0 +1,26 @@
+#include "COM_MultiThreadedOperation.h"
+#include "COM_ExecutionSystem.h"
+
+namespace blender::compositor {
+
+MultiThreadedOperation::MultiThreadedOperation()
+{
+ m_num_passes = 1;
+ flags.is_fullframe_operation = true;
+}
+
+void MultiThreadedOperation::update_memory_buffer(MemoryBuffer *output,
+ const rcti &output_area,
+ blender::Span<MemoryBuffer *> inputs,
+ ExecutionSystem &exec_system)
+{
+ for (int current_pass = 0; current_pass < m_num_passes; current_pass++) {
+ update_memory_buffer_started(output, output_area, inputs, exec_system, current_pass);
+ exec_system.execute_work(output_area, [=, &exec_system](const rcti &split_rect) {
+ update_memory_buffer_partial(output, split_rect, inputs, exec_system, current_pass);
+ });
+ update_memory_buffer_finished(output, output_area, inputs, exec_system, current_pass);
+ }
+}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_MultiThreadedOperation.h b/source/blender/compositor/intern/COM_MultiThreadedOperation.h
new file mode 100644
index 00000000000..97c5fba4ead
--- /dev/null
+++ b/source/blender/compositor/intern/COM_MultiThreadedOperation.h
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+#pragma once
+
+#include "COM_NodeOperation.h"
+
+namespace blender::compositor {
+
+class MultiThreadedOperation : public NodeOperation {
+ protected:
+ /**
+ * Number of execution passes.
+ */
+ int m_num_passes;
+
+ protected:
+ MultiThreadedOperation();
+
+ /**
+ * Called before an update memory buffer pass is executed. Single-threaded calls.
+ */
+ virtual void update_memory_buffer_started(MemoryBuffer *UNUSED(output),
+ const rcti &UNUSED(output_rect),
+ blender::Span<MemoryBuffer *> UNUSED(inputs),
+ ExecutionSystem &UNUSED(exec_system),
+ int UNUSED(current_pass))
+ {
+ }
+
+ /**
+ * Executes operation updating output memory buffer on output_rect area. Multi-threaded calls.
+ */
+ virtual void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &output_rect,
+ blender::Span<MemoryBuffer *> inputs,
+ ExecutionSystem &exec_system,
+ int current_pass) = 0;
+
+ /**
+ * Called after an update memory buffer pass is executed. Single-threaded calls.
+ */
+ virtual void update_memory_buffer_finished(MemoryBuffer *UNUSED(output),
+ const rcti &UNUSED(output_rect),
+ blender::Span<MemoryBuffer *> UNUSED(inputs),
+ ExecutionSystem &UNUSED(exec_system),
+ int UNUSED(current_pass))
+ {
+ }
+
+ private:
+ void update_memory_buffer(MemoryBuffer *output,
+ const rcti &output_area,
+ blender::Span<MemoryBuffer *> inputs,
+ ExecutionSystem &exec_system) override;
+};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_Node.cc b/source/blender/compositor/intern/COM_Node.cc
index 819f1c02b14..6ac48e3646c 100644
--- a/source/blender/compositor/intern/COM_Node.cc
+++ b/source/blender/compositor/intern/COM_Node.cc
@@ -32,6 +32,8 @@
#include "COM_Node.h" /* own include */
+namespace blender::compositor {
+
/**************
**** Node ****
**************/
@@ -74,13 +76,11 @@ Node::Node(bNode *editorNode, bool create_sockets)
Node::~Node()
{
- while (!this->m_outputsockets.empty()) {
- delete (this->m_outputsockets.back());
- this->m_outputsockets.pop_back();
+ while (!this->outputs.is_empty()) {
+ delete (this->outputs.pop_last());
}
- while (!this->m_inputsockets.empty()) {
- delete (this->m_inputsockets.back());
- this->m_inputsockets.pop_back();
+ while (!this->inputs.is_empty()) {
+ delete (this->inputs.pop_last());
}
}
@@ -92,7 +92,7 @@ void Node::addInputSocket(DataType datatype)
void Node::addInputSocket(DataType datatype, bNodeSocket *bSocket)
{
NodeInput *socket = new NodeInput(this, bSocket, datatype);
- this->m_inputsockets.push_back(socket);
+ this->inputs.append(socket);
}
void Node::addOutputSocket(DataType datatype)
@@ -102,19 +102,17 @@ void Node::addOutputSocket(DataType datatype)
void Node::addOutputSocket(DataType datatype, bNodeSocket *bSocket)
{
NodeOutput *socket = new NodeOutput(this, bSocket, datatype);
- this->m_outputsockets.push_back(socket);
+ outputs.append(socket);
}
NodeOutput *Node::getOutputSocket(unsigned int index) const
{
- BLI_assert(index < this->m_outputsockets.size());
- return this->m_outputsockets[index];
+ return outputs[index];
}
NodeInput *Node::getInputSocket(unsigned int index) const
{
- BLI_assert(index < this->m_inputsockets.size());
- return this->m_inputsockets[index];
+ return inputs[index];
}
bNodeSocket *Node::getEditorInputSocket(int editorNodeInputSocketIndex)
@@ -208,3 +206,5 @@ void NodeOutput::getEditorValueVector(float *value)
RNA_pointer_create((ID *)getNode()->getbNodeTree(), &RNA_NodeSocket, getbNodeSocket(), &ptr);
return RNA_float_get_array(&ptr, "default_value", value);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_Node.h b/source/blender/compositor/intern/COM_Node.h
index 99f1b58b5ef..28b59397af9 100644
--- a/source/blender/compositor/intern/COM_Node.h
+++ b/source/blender/compositor/intern/COM_Node.h
@@ -18,10 +18,12 @@
#pragma once
+#include "BLI_vector.hh"
+
#include "DNA_node_types.h"
+
#include <algorithm>
#include <string>
-#include <vector>
/* common node includes
* added here so node files don't have to include themselves
@@ -29,7 +31,8 @@
#include "COM_CompositorContext.h"
#include "COM_NodeConverter.h"
-class Node;
+namespace blender::compositor {
+
class NodeOperation;
class NodeConverter;
@@ -37,10 +40,6 @@ class NodeConverter;
* My node documentation.
*/
class Node {
- public:
- typedef std::vector<NodeInput *> Inputs;
- typedef std::vector<NodeOutput *> Outputs;
-
private:
/**
* \brief stores the reference to the SDNA bNode struct
@@ -53,16 +52,6 @@ class Node {
bNode *m_editorNode;
/**
- * \brief the list of actual inputsockets \see NodeInput
- */
- Inputs m_inputsockets;
-
- /**
- * \brief the list of actual outputsockets \see NodeOutput
- */
- Outputs m_outputsockets;
-
- /**
* \brief Is this node part of the active group
*/
bool m_inActiveGroup;
@@ -74,20 +63,14 @@ class Node {
protected:
/**
- * \brief get access to the vector of input sockets
+ * \brief the list of actual input-sockets \see NodeInput
*/
- const Inputs &getInputSockets() const
- {
- return this->m_inputsockets;
- }
+ Vector<NodeInput *> inputs;
/**
- * \brief get access to the vector of input sockets
+ * \brief the list of actual output-sockets \see NodeOutput
*/
- const Outputs &getOutputSockets() const
- {
- return this->m_outputsockets;
- }
+ Vector<NodeOutput *> outputs;
public:
Node(bNode *editorNode, bool create_sockets = true);
@@ -130,19 +113,19 @@ class Node {
}
/**
- * \brief Return the number of input sockets of this node.
+ * \brief get access to the vector of input sockets
*/
- unsigned int getNumberOfInputSockets() const
+ const Vector<NodeInput *> &getInputSockets() const
{
- return this->m_inputsockets.size();
+ return this->inputs;
}
/**
- * \brief Return the number of output sockets of this node.
+ * \brief get access to the vector of input sockets
*/
- unsigned int getNumberOfOutputSockets() const
+ const Vector<NodeOutput *> &getOutputSockets() const
{
- return this->m_outputsockets.size();
+ return this->outputs;
}
/**
@@ -150,17 +133,7 @@ class Node {
* \param index:
* the index of the needed outputsocket
*/
- NodeOutput *getOutputSocket(const unsigned int index) const;
-
- /**
- * get the reference to the first outputsocket
- * \param index:
- * the index of the needed outputsocket
- */
- inline NodeOutput *getOutputSocket() const
- {
- return getOutputSocket(0);
- }
+ NodeOutput *getOutputSocket(const unsigned int index = 0) const;
/**
* get the reference to a certain inputsocket
@@ -169,14 +142,6 @@ class Node {
*/
NodeInput *getInputSocket(const unsigned int index) const;
- /** Check if this is an input node
- * An input node is a node that only has output sockets and no input sockets
- */
- bool isInputNode() const
- {
- return m_inputsockets.empty();
- }
-
/**
* \brief Is this node in the active group (the group that is being edited)
* \param isInActiveGroup:
@@ -219,7 +184,7 @@ class Node {
protected:
/**
- * \brief add an NodeInput to the collection of inputsockets
+ * \brief add an NodeInput to the collection of input-sockets
* \note may only be called in an constructor
* \param socket: the NodeInput to add
*/
@@ -227,7 +192,7 @@ class Node {
void addInputSocket(DataType datatype, bNodeSocket *socket);
/**
- * \brief add an NodeOutput to the collection of outputsockets
+ * \brief add an NodeOutput to the collection of output-sockets
* \note may only be called in an constructor
* \param socket: the NodeOutput to add
*/
@@ -317,3 +282,5 @@ class NodeOutput {
void getEditorValueColor(float *value);
void getEditorValueVector(float *value);
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_NodeConverter.cc b/source/blender/compositor/intern/COM_NodeConverter.cc
index 2db31bd4133..49a2e7988c4 100644
--- a/source/blender/compositor/intern/COM_NodeConverter.cc
+++ b/source/blender/compositor/intern/COM_NodeConverter.cc
@@ -29,6 +29,8 @@
#include "COM_NodeConverter.h" /* own include */
+namespace blender::compositor {
+
NodeConverter::NodeConverter(NodeOperationBuilder *builder) : m_builder(builder)
{
}
@@ -160,3 +162,5 @@ ViewerOperation *NodeConverter::active_viewer() const
{
return m_builder->active_viewer();
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_NodeConverter.h b/source/blender/compositor/intern/COM_NodeConverter.h
index e9b05184857..b3f03485249 100644
--- a/source/blender/compositor/intern/COM_NodeConverter.h
+++ b/source/blender/compositor/intern/COM_NodeConverter.h
@@ -22,6 +22,8 @@
# include "MEM_guardedalloc.h"
#endif
+namespace blender::compositor {
+
class NodeInput;
class NodeOutput;
@@ -120,3 +122,5 @@ class NodeConverter {
MEM_CXX_CLASS_ALLOC_FUNCS("COM:NodeCompiler")
#endif
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_NodeGraph.cc b/source/blender/compositor/intern/COM_NodeGraph.cc
index d8220099f1f..fbe56dd4b5a 100644
--- a/source/blender/compositor/intern/COM_NodeGraph.cc
+++ b/source/blender/compositor/intern/COM_NodeGraph.cc
@@ -33,19 +33,16 @@
#include "COM_NodeGraph.h" /* own include */
+namespace blender::compositor {
+
/*******************
**** NodeGraph ****
*******************/
-NodeGraph::NodeGraph()
-{
-}
-
NodeGraph::~NodeGraph()
{
- for (int index = 0; index < this->m_nodes.size(); index++) {
- Node *node = this->m_nodes[index];
- delete node;
+ while (m_nodes.size()) {
+ delete m_nodes.pop_last();
}
}
@@ -83,7 +80,7 @@ void NodeGraph::add_node(Node *node,
node->setInstanceKey(key);
node->setIsInActiveGroup(is_active_group);
- m_nodes.push_back(node);
+ m_nodes.append(node);
DebugInfo::node_added(node);
}
@@ -153,27 +150,11 @@ void NodeGraph::add_bNode(const CompositorContext &context,
}
}
-NodeGraph::NodeInputs NodeGraph::find_inputs(const NodeRange &node_range, bNodeSocket *b_socket)
-{
- NodeInputs result;
- for (NodeGraph::NodeIterator it = node_range.first; it != node_range.second; ++it) {
- Node *node = *it;
- for (int index = 0; index < node->getNumberOfInputSockets(); index++) {
- NodeInput *input = node->getInputSocket(index);
- if (input->getbNodeSocket() == b_socket) {
- result.push_back(input);
- }
- }
- }
- return result;
-}
-
NodeOutput *NodeGraph::find_output(const NodeRange &node_range, bNodeSocket *b_socket)
{
- for (NodeGraph::NodeIterator it = node_range.first; it != node_range.second; ++it) {
+ for (Vector<Node *>::iterator it = node_range.first; it != node_range.second; ++it) {
Node *node = *it;
- for (int index = 0; index < node->getNumberOfOutputSockets(); index++) {
- NodeOutput *output = node->getOutputSocket(index);
+ for (NodeOutput *output : node->getOutputSockets()) {
if (output->getbNodeSocket() == b_socket) {
return output;
}
@@ -202,12 +183,13 @@ void NodeGraph::add_bNodeLink(const NodeRange &node_range, bNodeLink *b_nodelink
return;
}
- NodeInputs inputs = find_inputs(node_range, b_nodelink->tosock);
- for (NodeInput *input : inputs) {
- if (input->isLinked()) {
- continue;
+ for (Vector<Node *>::iterator it = node_range.first; it != node_range.second; ++it) {
+ Node *node = *it;
+ for (NodeInput *input : node->getInputSockets()) {
+ if (input->getbNodeSocket() == b_nodelink->tosock && !input->isLinked()) {
+ add_link(output, input);
+ }
}
- add_link(output, input);
}
}
@@ -331,3 +313,5 @@ void NodeGraph::add_proxies_reroute(bNodeTree *b_ntree,
b_node, (bNodeSocket *)b_node->inputs.first, (bNodeSocket *)b_node->outputs.first, false);
add_node(proxy, b_ntree, key, is_active_group);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_NodeGraph.h b/source/blender/compositor/intern/COM_NodeGraph.h
index 990e3a30831..7fa01593e1e 100644
--- a/source/blender/compositor/intern/COM_NodeGraph.h
+++ b/source/blender/compositor/intern/COM_NodeGraph.h
@@ -22,7 +22,6 @@
#include <map>
#include <set>
-#include <vector>
#include "DNA_node_types.h"
@@ -30,6 +29,8 @@
# include "MEM_guardedalloc.h"
#endif
+namespace blender::compositor {
+
class CompositorContext;
class Node;
class NodeInput;
@@ -50,22 +51,18 @@ class NodeGraph {
}
};
- typedef std::vector<Node *> Nodes;
- typedef Nodes::iterator NodeIterator;
-
private:
- Nodes m_nodes;
- blender::Vector<Link> m_links;
+ Vector<Node *> m_nodes;
+ Vector<Link> m_links;
public:
- NodeGraph();
~NodeGraph();
- const Nodes &nodes() const
+ const Vector<Node *> &nodes() const
{
return m_nodes;
}
- const blender::Vector<Link> &links() const
+ const Vector<Link> &links() const
{
return m_links;
}
@@ -73,8 +70,7 @@ class NodeGraph {
void from_bNodeTree(const CompositorContext &context, bNodeTree *tree);
protected:
- typedef std::pair<NodeIterator, NodeIterator> NodeRange;
- typedef std::vector<NodeInput *> NodeInputs;
+ typedef std::pair<Vector<Node *>::iterator, Vector<Node *>::iterator> NodeRange;
static bNodeSocket *find_b_node_input(bNode *b_node, const char *identifier);
static bNodeSocket *find_b_node_output(bNode *b_node, const char *identifier);
@@ -93,7 +89,6 @@ class NodeGraph {
bNodeInstanceKey key,
bool is_active_group);
- NodeInputs find_inputs(const NodeRange &node_range, bNodeSocket *b_socket);
NodeOutput *find_output(const NodeRange &node_range, bNodeSocket *b_socket);
void add_bNodeLink(const NodeRange &node_range, bNodeLink *b_nodelink);
@@ -124,3 +119,5 @@ class NodeGraph {
MEM_CXX_CLASS_ALLOC_FUNCS("COM:NodeGraph")
#endif
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_NodeOperation.cc b/source/blender/compositor/intern/COM_NodeOperation.cc
index 0cc642479ac..83de8a751c4 100644
--- a/source/blender/compositor/intern/COM_NodeOperation.cc
+++ b/source/blender/compositor/intern/COM_NodeOperation.cc
@@ -17,13 +17,18 @@
*/
#include <cstdio>
+#include <memory>
#include <typeinfo>
+#include "COM_BufferOperation.h"
#include "COM_ExecutionSystem.h"
+#include "COM_ReadBufferOperation.h"
#include "COM_defines.h"
#include "COM_NodeOperation.h" /* own include */
+namespace blender::compositor {
+
/*******************
**** NodeOperation ****
*******************/
@@ -31,76 +36,66 @@
NodeOperation::NodeOperation()
{
this->m_resolutionInputSocketIndex = 0;
- this->m_complex = false;
this->m_width = 0;
this->m_height = 0;
- this->m_isResolutionSet = false;
- this->m_openCL = false;
this->m_btree = nullptr;
}
-NodeOperation::~NodeOperation()
+NodeOperationOutput *NodeOperation::getOutputSocket(unsigned int index)
{
- while (!this->m_outputs.empty()) {
- delete (this->m_outputs.back());
- this->m_outputs.pop_back();
- }
- while (!this->m_inputs.empty()) {
- delete (this->m_inputs.back());
- this->m_inputs.pop_back();
- }
+ return &m_outputs[index];
}
-NodeOperationOutput *NodeOperation::getOutputSocket(unsigned int index) const
+NodeOperationInput *NodeOperation::getInputSocket(unsigned int index)
{
- BLI_assert(index < m_outputs.size());
- return m_outputs[index];
+ return &m_inputs[index];
}
-NodeOperationInput *NodeOperation::getInputSocket(unsigned int index) const
+void NodeOperation::addInputSocket(DataType datatype, ResizeMode resize_mode)
{
- BLI_assert(index < m_inputs.size());
- return m_inputs[index];
-}
-
-void NodeOperation::addInputSocket(DataType datatype, InputResizeMode resize_mode)
-{
- NodeOperationInput *socket = new NodeOperationInput(this, datatype, resize_mode);
- m_inputs.push_back(socket);
+ m_inputs.append(NodeOperationInput(this, datatype, resize_mode));
}
void NodeOperation::addOutputSocket(DataType datatype)
{
- NodeOperationOutput *socket = new NodeOperationOutput(this, datatype);
- m_outputs.push_back(socket);
+ m_outputs.append(NodeOperationOutput(this, datatype));
}
void NodeOperation::determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2])
{
- unsigned int temp[2];
- unsigned int temp2[2];
-
- for (unsigned int index = 0; index < m_inputs.size(); index++) {
- NodeOperationInput *input = m_inputs[index];
- if (input->isConnected()) {
- if (index == this->m_resolutionInputSocketIndex) {
- input->determineResolution(resolution, preferredResolution);
- temp2[0] = resolution[0];
- temp2[1] = resolution[1];
+ unsigned int used_resolution_index = 0;
+ if (m_resolutionInputSocketIndex == RESOLUTION_INPUT_ANY) {
+ for (NodeOperationInput &input : m_inputs) {
+ unsigned int any_resolution[2] = {0, 0};
+ input.determineResolution(any_resolution, preferredResolution);
+ if (any_resolution[0] * any_resolution[1] > 0) {
+ resolution[0] = any_resolution[0];
+ resolution[1] = any_resolution[1];
break;
}
+ used_resolution_index += 1;
}
}
+ else if (m_resolutionInputSocketIndex < m_inputs.size()) {
+ NodeOperationInput &input = m_inputs[m_resolutionInputSocketIndex];
+ input.determineResolution(resolution, preferredResolution);
+ used_resolution_index = m_resolutionInputSocketIndex;
+ }
+ unsigned int temp2[2] = {resolution[0], resolution[1]};
+
+ unsigned int temp[2];
for (unsigned int index = 0; index < m_inputs.size(); index++) {
- NodeOperationInput *input = m_inputs[index];
- if (input->isConnected()) {
- if (index != this->m_resolutionInputSocketIndex) {
- input->determineResolution(temp, temp2);
- }
+ if (index == used_resolution_index) {
+ continue;
+ }
+ NodeOperationInput &input = m_inputs[index];
+ if (input.isConnected()) {
+ input.determineResolution(temp, temp2);
}
}
}
+
void NodeOperation::setResolutionInputSocketIndex(unsigned int index)
{
this->m_resolutionInputSocketIndex = index;
@@ -149,20 +144,11 @@ NodeOperation *NodeOperation::getInputOperation(unsigned int inputSocketIndex)
return nullptr;
}
-void NodeOperation::getConnectedInputSockets(Inputs *sockets)
-{
- for (NodeOperationInput *input : m_inputs) {
- if (input->isConnected()) {
- sockets->push_back(input);
- }
- }
-}
-
bool NodeOperation::determineDependingAreaOfInterest(rcti *input,
ReadBufferOperation *readOperation,
rcti *output)
{
- if (isInputOperation()) {
+ if (m_inputs.size() == 0) {
BLI_rcti_init(output, input->xmin, input->xmax, input->ymin, input->ymax);
return false;
}
@@ -191,13 +177,182 @@ bool NodeOperation::determineDependingAreaOfInterest(rcti *input,
return !first;
}
+/* -------------------------------------------------------------------- */
+/** \name Full Frame Methods
+ * \{ */
+
+/**
+ * \brief Get input operation area being read by this operation on rendering given output area.
+ *
+ * Implementation don't need to ensure r_input_area is within input operation bounds. The
+ * caller must clamp it.
+ * TODO: See if it's possible to use parameter overloading (input_id for example).
+ *
+ * \param input_op_idx: Input operation index for which we want to calculate the area being read.
+ * \param output_area: Area being rendered by this operation.
+ * \param r_input_area: Returned input operation area that needs to be read in order to render
+ * given output area.
+ */
+void NodeOperation::get_area_of_interest(const int input_op_idx,
+ const rcti &output_area,
+ rcti &r_input_area)
+{
+ if (get_flags().is_fullframe_operation) {
+ r_input_area = output_area;
+ }
+ else {
+ /* Non full-frame operations never implement this method. To ensure correctness assume
+ * whole area is used. */
+ NodeOperation *input_op = getInputOperation(input_op_idx);
+ BLI_rcti_init(&r_input_area, 0, input_op->getWidth(), 0, input_op->getHeight());
+ }
+}
+
+void NodeOperation::get_area_of_interest(NodeOperation *input_op,
+ const rcti &output_area,
+ rcti &r_input_area)
+{
+ for (int i = 0; i < getNumberOfInputSockets(); i++) {
+ if (input_op == getInputOperation(i)) {
+ get_area_of_interest(i, output_area, r_input_area);
+ return;
+ }
+ }
+ BLI_assert(!"input_op is not an input operation.");
+}
+
+/**
+ * Executes operation image manipulation algorithm rendering given areas.
+ * \param output_buf: Buffer to write result to.
+ * \param areas: Areas within this operation bounds to render.
+ * \param inputs_bufs: Inputs operations buffers.
+ * \param exec_system: Execution system.
+ */
+void NodeOperation::render(MemoryBuffer *output_buf,
+ Span<rcti> areas,
+ Span<MemoryBuffer *> inputs_bufs,
+ ExecutionSystem &exec_system)
+{
+ if (get_flags().is_fullframe_operation) {
+ render_full_frame(output_buf, areas, inputs_bufs, exec_system);
+ }
+ else {
+ render_full_frame_fallback(output_buf, areas, inputs_bufs, exec_system);
+ }
+}
+
+/**
+ * Renders given areas using operations full frame implementation.
+ */
+void NodeOperation::render_full_frame(MemoryBuffer *output_buf,
+ Span<rcti> areas,
+ Span<MemoryBuffer *> inputs_bufs,
+ ExecutionSystem &exec_system)
+{
+ initExecution();
+ for (const rcti &area : areas) {
+ update_memory_buffer(output_buf, area, inputs_bufs, exec_system);
+ }
+ deinitExecution();
+}
+
+/**
+ * Renders given areas using operations tiled implementation.
+ */
+void NodeOperation::render_full_frame_fallback(MemoryBuffer *output_buf,
+ Span<rcti> areas,
+ Span<MemoryBuffer *> inputs_bufs,
+ ExecutionSystem &exec_system)
+{
+ Vector<NodeOperationOutput *> orig_input_links = replace_inputs_with_buffers(inputs_bufs);
+
+ initExecution();
+ const bool is_output_operation = getNumberOfOutputSockets() == 0;
+ if (!is_output_operation && output_buf->is_a_single_elem()) {
+ float *output_elem = output_buf->get_elem(0, 0);
+ readSampled(output_elem, 0, 0, PixelSampler::Nearest);
+ }
+ else {
+ for (const rcti &rect : areas) {
+ exec_system.execute_work(rect, [=](const rcti &split_rect) {
+ rcti tile_rect = split_rect;
+ if (is_output_operation) {
+ executeRegion(&tile_rect, 0);
+ }
+ else {
+ render_tile(output_buf, &tile_rect);
+ }
+ });
+ }
+ }
+ deinitExecution();
+
+ remove_buffers_and_restore_original_inputs(orig_input_links);
+}
+
+void NodeOperation::render_tile(MemoryBuffer *output_buf, rcti *tile_rect)
+{
+ const bool is_complex = get_flags().complex;
+ void *tile_data = is_complex ? initializeTileData(tile_rect) : nullptr;
+ const int elem_stride = output_buf->elem_stride;
+ for (int y = tile_rect->ymin; y < tile_rect->ymax; y++) {
+ float *output_elem = output_buf->get_elem(tile_rect->xmin, y);
+ if (is_complex) {
+ for (int x = tile_rect->xmin; x < tile_rect->xmax; x++) {
+ read(output_elem, x, y, tile_data);
+ output_elem += elem_stride;
+ }
+ }
+ else {
+ for (int x = tile_rect->xmin; x < tile_rect->xmax; x++) {
+ readSampled(output_elem, x, y, PixelSampler::Nearest);
+ output_elem += elem_stride;
+ }
+ }
+ }
+ if (tile_data) {
+ deinitializeTileData(tile_rect, tile_data);
+ }
+}
+
+/**
+ * \return Replaced inputs links.
+ */
+Vector<NodeOperationOutput *> NodeOperation::replace_inputs_with_buffers(
+ Span<MemoryBuffer *> inputs_bufs)
+{
+ BLI_assert(inputs_bufs.size() == getNumberOfInputSockets());
+ Vector<NodeOperationOutput *> orig_links(inputs_bufs.size());
+ for (int i = 0; i < inputs_bufs.size(); i++) {
+ NodeOperationInput *input_socket = getInputSocket(i);
+ BufferOperation *buffer_op = new BufferOperation(inputs_bufs[i], input_socket->getDataType());
+ orig_links[i] = input_socket->getLink();
+ input_socket->setLink(buffer_op->getOutputSocket());
+ }
+ return orig_links;
+}
+
+void NodeOperation::remove_buffers_and_restore_original_inputs(
+ Span<NodeOperationOutput *> original_inputs_links)
+{
+ BLI_assert(original_inputs_links.size() == getNumberOfInputSockets());
+ for (int i = 0; i < original_inputs_links.size(); i++) {
+ NodeOperation *buffer_op = get_input_operation(i);
+ BLI_assert(buffer_op != nullptr);
+ BLI_assert(typeid(*buffer_op) == typeid(BufferOperation));
+ NodeOperationInput *input_socket = getInputSocket(i);
+ input_socket->setLink(original_inputs_links[i]);
+ delete buffer_op;
+ }
+}
+
+/** \} */
+
/*****************
**** OpInput ****
*****************/
-NodeOperationInput::NodeOperationInput(NodeOperation *op,
- DataType datatype,
- InputResizeMode resizeMode)
+NodeOperationInput::NodeOperationInput(NodeOperation *op, DataType datatype, ResizeMode resizeMode)
: m_operation(op), m_datatype(datatype), m_resizeMode(resizeMode), m_link(nullptr)
{
}
@@ -232,12 +387,88 @@ void NodeOperationOutput::determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2])
{
NodeOperation &operation = getOperation();
- if (operation.isResolutionSet()) {
+ if (operation.get_flags().is_resolution_set) {
resolution[0] = operation.getWidth();
resolution[1] = operation.getHeight();
}
else {
operation.determineResolution(resolution, preferredResolution);
- operation.setResolution(resolution);
+ if (resolution[0] > 0 && resolution[1] > 0) {
+ operation.setResolution(resolution);
+ }
+ }
+}
+
+std::ostream &operator<<(std::ostream &os, const NodeOperationFlags &node_operation_flags)
+{
+ if (node_operation_flags.complex) {
+ os << "complex,";
+ }
+ if (node_operation_flags.open_cl) {
+ os << "open_cl,";
+ }
+ if (node_operation_flags.single_threaded) {
+ os << "single_threaded,";
+ }
+ if (node_operation_flags.use_render_border) {
+ os << "render_border,";
+ }
+ if (node_operation_flags.use_viewer_border) {
+ os << "view_border,";
+ }
+ if (node_operation_flags.is_resolution_set) {
+ os << "resolution_set,";
+ }
+ if (node_operation_flags.is_set_operation) {
+ os << "set_operation,";
+ }
+ if (node_operation_flags.is_write_buffer_operation) {
+ os << "write_buffer,";
+ }
+ if (node_operation_flags.is_read_buffer_operation) {
+ os << "read_buffer,";
+ }
+ if (node_operation_flags.is_proxy_operation) {
+ os << "proxy,";
+ }
+ if (node_operation_flags.is_viewer_operation) {
+ os << "viewer,";
}
+ if (node_operation_flags.is_preview_operation) {
+ os << "preview,";
+ }
+ if (!node_operation_flags.use_datatype_conversion) {
+ os << "no_conversion,";
+ }
+ if (node_operation_flags.is_fullframe_operation) {
+ os << "full_frame,";
+ }
+
+ return os;
}
+
+std::ostream &operator<<(std::ostream &os, const NodeOperation &node_operation)
+{
+ NodeOperationFlags flags = node_operation.get_flags();
+ os << "NodeOperation(";
+ os << "id=" << node_operation.get_id();
+ if (!node_operation.get_name().empty()) {
+ os << ",name=" << node_operation.get_name();
+ }
+ os << ",flags={" << flags << "}";
+ if (flags.is_read_buffer_operation) {
+ const ReadBufferOperation *read_operation = (const ReadBufferOperation *)&node_operation;
+ const MemoryProxy *proxy = read_operation->getMemoryProxy();
+ if (proxy) {
+ const WriteBufferOperation *write_operation = proxy->getWriteBufferOperation();
+ if (write_operation) {
+ os << ",write=" << (NodeOperation &)*write_operation;
+ }
+ }
+ }
+ os << ")";
+
+ return os;
+}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_NodeOperation.h b/source/blender/compositor/intern/COM_NodeOperation.h
index 7857837a95d..5c4d6dd19ba 100644
--- a/source/blender/compositor/intern/COM_NodeOperation.h
+++ b/source/blender/compositor/intern/COM_NodeOperation.h
@@ -26,77 +26,258 @@
#include "BLI_math_vector.h"
#include "BLI_threads.h"
+#include "COM_Enums.h"
#include "COM_MemoryBuffer.h"
#include "COM_MemoryProxy.h"
+#include "COM_MetaData.h"
#include "COM_Node.h"
-#include "COM_SocketReader.h"
#include "clew.h"
+namespace blender::compositor {
+
class OpenCLDevice;
class ReadBufferOperation;
class WriteBufferOperation;
+class ExecutionSystem;
+
+class NodeOperation;
+typedef NodeOperation SocketReader;
-class NodeOperationInput;
-class NodeOperationOutput;
+/**
+ * RESOLUTION_INPUT_ANY is a wildcard when any resolution of an input can be used.
+ * This solves the issue that the FileInputNode in a group node cannot find the
+ * correct resolution.
+ */
+static constexpr unsigned int RESOLUTION_INPUT_ANY = 999999;
/**
* \brief Resize modes of inputsockets
* How are the input and working resolutions matched
* \ingroup Model
*/
-typedef enum InputResizeMode {
+enum class ResizeMode {
/** \brief Center the input image to the center of the working area of the node, no resizing
* occurs */
- COM_SC_CENTER = NS_CR_CENTER,
+ Center = NS_CR_CENTER,
/** \brief The bottom left of the input image is the bottom left of the working area of the node,
* no resizing occurs */
- COM_SC_NO_RESIZE = NS_CR_NONE,
+ None = NS_CR_NONE,
/** \brief Fit the width of the input image to the width of the working area of the node */
- COM_SC_FIT_WIDTH = NS_CR_FIT_WIDTH,
+ FitWidth = NS_CR_FIT_WIDTH,
/** \brief Fit the height of the input image to the height of the working area of the node */
- COM_SC_FIT_HEIGHT = NS_CR_FIT_HEIGHT,
+ FitHeight = NS_CR_FIT_HEIGHT,
/** \brief Fit the width or the height of the input image to the width or height of the working
* area of the node, image will be larger than the working area */
- COM_SC_FIT = NS_CR_FIT,
+ FitAny = NS_CR_FIT,
/** \brief Fit the width and the height of the input image to the width and height of the working
* area of the node, image will be equally larger than the working area */
- COM_SC_STRETCH = NS_CR_STRETCH,
-} InputResizeMode;
+ Stretch = NS_CR_STRETCH,
+};
+
+enum class PixelSampler {
+ Nearest = 0,
+ Bilinear = 1,
+ Bicubic = 2,
+};
+
+class NodeOperationInput {
+ private:
+ NodeOperation *m_operation;
+
+ /** Datatype of this socket. Is used for automatically data transformation.
+ * \section data-conversion
+ */
+ DataType m_datatype;
+
+ /** Resize mode of this socket */
+ ResizeMode m_resizeMode;
+
+ /** Connected output */
+ NodeOperationOutput *m_link;
-/**
- * \brief NodeOperation contains calculation logic
- *
- * Subclasses needs to implement the execution method (defined in SocketReader) to implement logic.
- * \ingroup Model
- */
-class NodeOperation : public SocketReader {
public:
- typedef std::vector<NodeOperationInput *> Inputs;
- typedef std::vector<NodeOperationOutput *> Outputs;
+ NodeOperationInput(NodeOperation *op,
+ DataType datatype,
+ ResizeMode resizeMode = ResizeMode::Center);
+ NodeOperation &getOperation() const
+ {
+ return *m_operation;
+ }
+ DataType getDataType() const
+ {
+ return m_datatype;
+ }
+
+ void setLink(NodeOperationOutput *link)
+ {
+ m_link = link;
+ }
+ NodeOperationOutput *getLink() const
+ {
+ return m_link;
+ }
+ bool isConnected() const
+ {
+ return m_link;
+ }
+
+ void setResizeMode(ResizeMode resizeMode)
+ {
+ this->m_resizeMode = resizeMode;
+ }
+ ResizeMode getResizeMode() const
+ {
+ return this->m_resizeMode;
+ }
+
+ SocketReader *getReader();
+
+ void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]);
+
+#ifdef WITH_CXX_GUARDEDALLOC
+ MEM_CXX_CLASS_ALLOC_FUNCS("COM:NodeOperation")
+#endif
+};
+
+class NodeOperationOutput {
private:
- Inputs m_inputs;
- Outputs m_outputs;
+ NodeOperation *m_operation;
+
+ /** Datatype of this socket. Is used for automatically data transformation.
+ * \section data-conversion
+ */
+ DataType m_datatype;
+
+ public:
+ NodeOperationOutput(NodeOperation *op, DataType datatype);
+
+ NodeOperation &getOperation() const
+ {
+ return *m_operation;
+ }
+ DataType getDataType() const
+ {
+ return m_datatype;
+ }
/**
- * \brief the index of the input socket that will be used to determine the resolution
+ * \brief determine the resolution of this data going through this socket
+ * \param resolution: the result of this operation
+ * \param preferredResolution: the preferable resolution as no resolution could be determined
*/
- unsigned int m_resolutionInputSocketIndex;
+ void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]);
+#ifdef WITH_CXX_GUARDEDALLOC
+ MEM_CXX_CLASS_ALLOC_FUNCS("COM:NodeOperation")
+#endif
+};
+
+struct NodeOperationFlags {
/**
- * \brief is this operation a complex one.
+ * Is this an complex operation.
+ *
+ * The input and output buffers of Complex operations are stored in buffers. It allows
+ * sequential and read/write.
*
* Complex operations are typically doing many reads to calculate the output of a single pixel.
* Mostly Filter types (Blurs, Convolution, Defocus etc) need this to be set to true.
*/
- bool m_complex;
+ bool complex : 1;
+
+ /**
+ * Does this operation support OpenCL.
+ */
+ bool open_cl : 1;
+
+ /**
+ * TODO: Remove this flag and #SingleThreadedOperation if tiled implementation is removed.
+ * Full-frame implementation doesn't need it.
+ */
+ bool single_threaded : 1;
/**
- * \brief can this operation be scheduled on an OpenCL device.
- * \note Only applicable if complex is True
+ * Does the operation needs a viewer border.
+ * Basically, setting border need to happen for only operations
+ * which operates in render resolution buffers (like compositor
+ * output nodes).
+ *
+ * In this cases adding border will lead to mapping coordinates
+ * from output buffer space to input buffer spaces when executing
+ * operation.
+ *
+ * But nodes like viewer and file output just shall display or
+ * safe the same exact buffer which goes to their input, no need
+ * in any kind of coordinates mapping.
+ */
+ bool use_render_border : 1;
+ bool use_viewer_border : 1;
+
+ /**
+ * Is the resolution of the operation set.
*/
- bool m_openCL;
+ bool is_resolution_set : 1;
+
+ /**
+ * Is this a set operation (value, color, vector).
+ */
+ bool is_set_operation : 1;
+ bool is_write_buffer_operation : 1;
+ bool is_read_buffer_operation : 1;
+ bool is_proxy_operation : 1;
+ bool is_viewer_operation : 1;
+ bool is_preview_operation : 1;
+
+ /**
+ * When set additional data conversion operations are added to
+ * convert the data. SocketProxyOperation don't always need to do data conversions.
+ *
+ * By default data conversions are enabled.
+ */
+ bool use_datatype_conversion : 1;
+
+ /**
+ * Has this operation fullframe implementation.
+ */
+ bool is_fullframe_operation : 1;
+
+ NodeOperationFlags()
+ {
+ complex = false;
+ single_threaded = false;
+ open_cl = false;
+ use_render_border = false;
+ use_viewer_border = false;
+ is_resolution_set = false;
+ is_set_operation = false;
+ is_read_buffer_operation = false;
+ is_write_buffer_operation = false;
+ is_proxy_operation = false;
+ is_viewer_operation = false;
+ is_preview_operation = false;
+ use_datatype_conversion = true;
+ is_fullframe_operation = false;
+ }
+};
+
+/**
+ * \brief NodeOperation contains calculation logic
+ *
+ * Subclasses needs to implement the execution method (defined in SocketReader) to implement logic.
+ * \ingroup Model
+ */
+class NodeOperation {
+ private:
+ int m_id;
+ std::string m_name;
+ Vector<NodeOperationInput> m_inputs;
+ Vector<NodeOperationOutput> m_outputs;
+
+ /**
+ * \brief the index of the input socket that will be used to determine the resolution
+ */
+ unsigned int m_resolutionInputSocketIndex;
/**
* \brief mutex reference for very special node initializations
@@ -114,13 +295,61 @@ class NodeOperation : public SocketReader {
*/
const bNodeTree *m_btree;
+ protected:
/**
- * \brief set to truth when resolution for this operation is set
+ * Compositor execution model.
*/
- bool m_isResolutionSet;
+ eExecutionModel execution_model_;
+
+ /**
+ * Width of the output of this operation.
+ */
+ unsigned int m_width;
+
+ /**
+ * Height of the output of this operation.
+ */
+ unsigned int m_height;
+
+ /**
+ * Flags how to evaluate this operation.
+ */
+ NodeOperationFlags flags;
public:
- virtual ~NodeOperation();
+ virtual ~NodeOperation()
+ {
+ }
+
+ void set_execution_model(const eExecutionModel model)
+ {
+ execution_model_ = model;
+ }
+
+ void set_name(const std::string name)
+ {
+ m_name = name;
+ }
+
+ const std::string get_name() const
+ {
+ return m_name;
+ }
+
+ void set_id(const int id)
+ {
+ m_id = id;
+ }
+
+ const int get_id() const
+ {
+ return m_id;
+ }
+
+ const NodeOperationFlags get_flags() const
+ {
+ return flags;
+ }
unsigned int getNumberOfInputSockets() const
{
@@ -130,19 +359,14 @@ class NodeOperation : public SocketReader {
{
return m_outputs.size();
}
- NodeOperationOutput *getOutputSocket(unsigned int index) const;
- NodeOperationOutput *getOutputSocket() const
- {
- return getOutputSocket(0);
- }
- NodeOperationInput *getInputSocket(unsigned int index) const;
+ NodeOperationOutput *getOutputSocket(unsigned int index = 0);
+ NodeOperationInput *getInputSocket(unsigned int index);
- /** Check if this is an input operation
- * An input operation is an operation that only has output sockets and no input sockets
- */
- bool isInputOperation() const
+ NodeOperation *get_input_operation(int index)
{
- return m_inputs.empty();
+ /* TODO: Rename protected getInputOperation to get_input_operation and make it public replacing
+ * this method. */
+ return getInputOperation(index);
}
/**
@@ -155,11 +379,11 @@ class NodeOperation : public SocketReader {
unsigned int preferredResolution[2]);
/**
- * \brief isOutputOperation determines whether this operation is an output of the ExecutionSystem
- * during rendering or editing.
+ * \brief isOutputOperation determines whether this operation is an output of the
+ * ExecutionSystem during rendering or editing.
*
- * Default behavior if not overridden, this operation will not be evaluated as being an output of
- * the ExecutionSystem.
+ * Default behavior if not overridden, this operation will not be evaluated as being an output
+ * of the ExecutionSystem.
*
* \see ExecutionSystem
* \ingroup check
@@ -174,11 +398,6 @@ class NodeOperation : public SocketReader {
return false;
}
- virtual int isSingleThreaded()
- {
- return false;
- }
-
void setbNodeTree(const bNodeTree *tree)
{
this->m_btree = tree;
@@ -241,62 +460,19 @@ class NodeOperation : public SocketReader {
}
virtual void deinitExecution();
- bool isResolutionSet()
- {
- return this->m_isResolutionSet;
- }
-
/**
* \brief set the resolution
* \param resolution: the resolution to set
*/
void setResolution(unsigned int resolution[2])
{
- if (!isResolutionSet()) {
+ if (!this->flags.is_resolution_set) {
this->m_width = resolution[0];
this->m_height = resolution[1];
- this->m_isResolutionSet = true;
+ this->flags.is_resolution_set = true;
}
}
- void getConnectedInputSockets(Inputs *sockets);
-
- /**
- * \brief is this operation complex
- *
- * Complex operations are typically doing many reads to calculate the output of a single pixel.
- * Mostly Filter types (Blurs, Convolution, Defocus etc) need this to be set to true.
- */
- bool isComplex() const
- {
- return this->m_complex;
- }
-
- virtual bool isSetOperation() const
- {
- return false;
- }
-
- /**
- * \brief is this operation of type ReadBufferOperation
- * \return [true:false]
- * \see ReadBufferOperation
- */
- virtual bool isReadBufferOperation() const
- {
- return false;
- }
-
- /**
- * \brief is this operation of type WriteBufferOperation
- * \return [true:false]
- * \see WriteBufferOperation
- */
- virtual bool isWriteBufferOperation() const
- {
- return false;
- }
-
/**
* \brief is this operation the active viewer output
* user can select an ViewerNode to be active
@@ -314,80 +490,123 @@ class NodeOperation : public SocketReader {
rcti *output);
/**
- * \brief set the index of the input socket that will determine the resolution of this operation
- * \param index: the index to set
+ * \brief set the index of the input socket that will determine the resolution of this
+ * operation \param index: the index to set
*/
void setResolutionInputSocketIndex(unsigned int index);
/**
* \brief get the render priority of this node.
* \note only applicable for output operations like ViewerOperation
- * \return CompositorPriority
+ * \return eCompositorPriority
*/
- virtual CompositorPriority getRenderPriority() const
+ virtual eCompositorPriority getRenderPriority() const
{
- return CompositorPriority::Low;
+ return eCompositorPriority::Low;
}
- /**
- * \brief can this NodeOperation be scheduled on an OpenCLDevice
- * \see WorkScheduler.schedule
- * \see ExecutionGroup.addOperation
- */
- bool isOpenCL() const
+ inline bool isBraked() const
{
- return this->m_openCL;
+ return this->m_btree->test_break(this->m_btree->tbh);
}
- virtual bool isViewerOperation() const
+ inline void updateDraw()
{
- return false;
+ if (this->m_btree->update_draw) {
+ this->m_btree->update_draw(this->m_btree->udh);
+ }
}
- virtual bool isPreviewOperation() const
+
+ unsigned int getWidth() const
{
- return false;
+ return m_width;
}
- virtual bool isFileOutputOperation() const
+
+ unsigned int getHeight() const
{
- return false;
+ return m_height;
}
- virtual bool isProxyOperation() const
+
+ inline void readSampled(float result[4], float x, float y, PixelSampler sampler)
{
- return false;
+ executePixelSampled(result, x, y, sampler);
}
- virtual bool useDatatypeConversion() const
+ inline void readFiltered(float result[4], float x, float y, float dx[2], float dy[2])
{
- return true;
+ executePixelFiltered(result, x, y, dx, dy);
}
- inline bool isBraked() const
+ inline void read(float result[4], int x, int y, void *chunkData)
{
- return this->m_btree->test_break(this->m_btree->tbh);
+ executePixel(result, x, y, chunkData);
}
- inline void updateDraw()
+ virtual void *initializeTileData(rcti * /*rect*/)
+ {
+ return 0;
+ }
+
+ virtual void deinitializeTileData(rcti * /*rect*/, void * /*data*/)
{
- if (this->m_btree->update_draw) {
- this->m_btree->update_draw(this->m_btree->udh);
- }
}
+ virtual MemoryBuffer *getInputMemoryBuffer(MemoryBuffer ** /*memoryBuffers*/)
+ {
+ return 0;
+ }
+
+ /**
+ * Return the meta data associated with this branch.
+ *
+ * The return parameter holds an instance or is an nullptr. */
+ virtual std::unique_ptr<MetaData> getMetaData()
+ {
+ return std::unique_ptr<MetaData>();
+ }
+
+ /* -------------------------------------------------------------------- */
+ /** \name Full Frame Methods
+ * \{ */
+
+ void render(MemoryBuffer *output_buf,
+ Span<rcti> areas,
+ Span<MemoryBuffer *> inputs_bufs,
+ ExecutionSystem &exec_system);
+
+ /**
+ * Executes operation updating output memory buffer. Single-threaded calls.
+ */
+ virtual void update_memory_buffer(MemoryBuffer *UNUSED(output),
+ const rcti &UNUSED(output_area),
+ Span<MemoryBuffer *> UNUSED(inputs),
+ ExecutionSystem &UNUSED(exec_system))
+ {
+ }
+
+ /**
+ * Get input operation area being read by this operation on rendering given output area.
+ */
+ virtual void get_area_of_interest(int input_op_idx, const rcti &output_area, rcti &r_input_area);
+ void get_area_of_interest(NodeOperation *input_op, const rcti &output_area, rcti &r_input_area);
+
+ /** \} */
+
protected:
NodeOperation();
- void addInputSocket(DataType datatype, InputResizeMode resize_mode = COM_SC_CENTER);
+ void addInputSocket(DataType datatype, ResizeMode resize_mode = ResizeMode::Center);
void addOutputSocket(DataType datatype);
void setWidth(unsigned int width)
{
this->m_width = width;
- this->m_isResolutionSet = true;
+ this->flags.is_resolution_set = true;
}
void setHeight(unsigned int height)
{
this->m_height = height;
- this->m_isResolutionSet = true;
+ this->flags.is_resolution_set = true;
}
SocketReader *getInputSocketReader(unsigned int inputSocketindex);
NodeOperation *getInputOperation(unsigned int inputSocketindex);
@@ -405,114 +624,83 @@ class NodeOperation : public SocketReader {
*/
void setComplex(bool complex)
{
- this->m_complex = complex;
+ this->flags.complex = complex;
}
/**
- * \brief set if this NodeOperation can be scheduled on a OpenCLDevice
+ * \brief calculate a single pixel
+ * \note this method is called for non-complex
+ * \param result: is a float[4] array to store the result
+ * \param x: the x-coordinate of the pixel to calculate in image space
+ * \param y: the y-coordinate of the pixel to calculate in image space
+ * \param inputBuffers: chunks that can be read by their ReadBufferOperation.
*/
- void setOpenCL(bool openCL)
+ virtual void executePixelSampled(float /*output*/[4],
+ float /*x*/,
+ float /*y*/,
+ PixelSampler /*sampler*/)
{
- this->m_openCL = openCL;
}
- /* allow the DebugInfo class to look at internals */
- friend class DebugInfo;
-
-#ifdef WITH_CXX_GUARDEDALLOC
- MEM_CXX_CLASS_ALLOC_FUNCS("COM:NodeOperation")
-#endif
-};
-
-class NodeOperationInput {
- private:
- NodeOperation *m_operation;
-
- /** Datatype of this socket. Is used for automatically data transformation.
- * \section data-conversion
+ /**
+ * \brief calculate a single pixel
+ * \note this method is called for complex
+ * \param result: is a float[4] array to store the result
+ * \param x: the x-coordinate of the pixel to calculate in image space
+ * \param y: the y-coordinate of the pixel to calculate in image space
+ * \param inputBuffers: chunks that can be read by their ReadBufferOperation.
+ * \param chunkData: chunk specific data a during execution time.
*/
- DataType m_datatype;
-
- /** Resize mode of this socket */
- InputResizeMode m_resizeMode;
-
- /** Connected output */
- NodeOperationOutput *m_link;
-
- public:
- NodeOperationInput(NodeOperation *op,
- DataType datatype,
- InputResizeMode resizeMode = COM_SC_CENTER);
-
- NodeOperation &getOperation() const
- {
- return *m_operation;
- }
- DataType getDataType() const
- {
- return m_datatype;
- }
-
- void setLink(NodeOperationOutput *link)
+ virtual void executePixel(float output[4], int x, int y, void * /*chunkData*/)
{
- m_link = link;
- }
- NodeOperationOutput *getLink() const
- {
- return m_link;
- }
- bool isConnected() const
- {
- return m_link;
+ executePixelSampled(output, x, y, PixelSampler::Nearest);
}
- void setResizeMode(InputResizeMode resizeMode)
- {
- this->m_resizeMode = resizeMode;
- }
- InputResizeMode getResizeMode() const
+ /**
+ * \brief calculate a single pixel using an EWA filter
+ * \note this method is called for complex
+ * \param result: is a float[4] array to store the result
+ * \param x: the x-coordinate of the pixel to calculate in image space
+ * \param y: the y-coordinate of the pixel to calculate in image space
+ * \param dx:
+ * \param dy:
+ * \param inputBuffers: chunks that can be read by their ReadBufferOperation.
+ */
+ virtual void executePixelFiltered(
+ float /*output*/[4], float /*x*/, float /*y*/, float /*dx*/[2], float /*dy*/[2])
{
- return this->m_resizeMode;
}
- SocketReader *getReader();
+ private:
+ /* -------------------------------------------------------------------- */
+ /** \name Full Frame Methods
+ * \{ */
+
+ void render_full_frame(MemoryBuffer *output_buf,
+ Span<rcti> areas,
+ Span<MemoryBuffer *> inputs_bufs,
+ ExecutionSystem &exec_system);
+
+ void render_full_frame_fallback(MemoryBuffer *output_buf,
+ Span<rcti> areas,
+ Span<MemoryBuffer *> inputs,
+ ExecutionSystem &exec_system);
+ void render_tile(MemoryBuffer *output_buf, rcti *tile_rect);
+ Vector<NodeOperationOutput *> replace_inputs_with_buffers(Span<MemoryBuffer *> inputs_bufs);
+ void remove_buffers_and_restore_original_inputs(
+ Span<NodeOperationOutput *> original_inputs_links);
+
+ /** \} */
- void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]);
+ /* allow the DebugInfo class to look at internals */
+ friend class DebugInfo;
#ifdef WITH_CXX_GUARDEDALLOC
MEM_CXX_CLASS_ALLOC_FUNCS("COM:NodeOperation")
#endif
};
-class NodeOperationOutput {
- private:
- NodeOperation *m_operation;
-
- /** Datatype of this socket. Is used for automatically data transformation.
- * \section data-conversion
- */
- DataType m_datatype;
-
- public:
- NodeOperationOutput(NodeOperation *op, DataType datatype);
+std::ostream &operator<<(std::ostream &os, const NodeOperationFlags &node_operation_flags);
+std::ostream &operator<<(std::ostream &os, const NodeOperation &node_operation);
- NodeOperation &getOperation() const
- {
- return *m_operation;
- }
- DataType getDataType() const
- {
- return m_datatype;
- }
-
- /**
- * \brief determine the resolution of this data going through this socket
- * \param resolution: the result of this operation
- * \param preferredResolution: the preferable resolution as no resolution could be determined
- */
- void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]);
-
-#ifdef WITH_CXX_GUARDEDALLOC
- MEM_CXX_CLASS_ALLOC_FUNCS("COM:NodeOperation")
-#endif
-};
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_NodeOperationBuilder.cc b/source/blender/compositor/intern/COM_NodeOperationBuilder.cc
index 1c741283c7b..1beb83bb477 100644
--- a/source/blender/compositor/intern/COM_NodeOperationBuilder.cc
+++ b/source/blender/compositor/intern/COM_NodeOperationBuilder.cc
@@ -38,24 +38,20 @@
#include "COM_NodeOperationBuilder.h" /* own include */
+namespace blender::compositor {
+
NodeOperationBuilder::NodeOperationBuilder(const CompositorContext *context, bNodeTree *b_nodetree)
: m_context(context), m_current_node(nullptr), m_active_viewer(nullptr)
{
m_graph.from_bNodeTree(*context, b_nodetree);
}
-NodeOperationBuilder::~NodeOperationBuilder()
-{
-}
-
void NodeOperationBuilder::convertToOperations(ExecutionSystem *system)
{
/* interface handle for nodes */
NodeConverter converter(this);
- for (int index = 0; index < m_graph.nodes().size(); index++) {
- Node *node = (Node *)m_graph.nodes()[index];
-
+ for (Node *node : m_graph.nodes()) {
m_current_node = node;
DebugInfo::node_to_operations(node);
@@ -69,7 +65,7 @@ void NodeOperationBuilder::convertToOperations(ExecutionSystem *system)
* so multiple operations can use the same node input.
*/
blender::MultiValueMap<NodeInput *, NodeOperationInput *> inverse_input_map;
- for (blender::Map<NodeOperationInput *, NodeInput *>::MutableItem item : m_input_map.items()) {
+ for (Map<NodeOperationInput *, NodeInput *>::MutableItem item : m_input_map.items()) {
inverse_input_map.add(item.value, item.key);
}
@@ -103,8 +99,10 @@ void NodeOperationBuilder::convertToOperations(ExecutionSystem *system)
determineResolutions();
- /* surround complex ops with read/write buffer */
- add_complex_operation_buffers();
+ if (m_context->get_execution_model() == eExecutionModel::Tiled) {
+ /* surround complex ops with read/write buffer */
+ add_complex_operation_buffers();
+ }
/* links not available from here on */
/* XXX make m_links a local variable to avoid confusion! */
@@ -115,8 +113,10 @@ void NodeOperationBuilder::convertToOperations(ExecutionSystem *system)
/* ensure topological (link-based) order of nodes */
/*sort_operations();*/ /* not needed yet */
- /* create execution groups */
- group_operations();
+ if (m_context->get_execution_model() == eExecutionModel::Tiled) {
+ /* create execution groups */
+ group_operations();
+ }
/* transfer resulting operations to the system */
system->set_operations(m_operations, m_groups);
@@ -124,7 +124,12 @@ void NodeOperationBuilder::convertToOperations(ExecutionSystem *system)
void NodeOperationBuilder::addOperation(NodeOperation *operation)
{
+ operation->set_id(m_operations.size());
m_operations.append(operation);
+ if (m_current_node) {
+ operation->set_name(m_current_node->getbNode()->name);
+ }
+ operation->set_execution_model(m_context->get_execution_model());
}
void NodeOperationBuilder::mapInputSocket(NodeInput *node_socket,
@@ -251,12 +256,13 @@ void NodeOperationBuilder::registerViewer(ViewerOperation *viewer)
void NodeOperationBuilder::add_datatype_conversions()
{
- blender::Vector<Link> convert_links;
+ Vector<Link> convert_links;
for (const Link &link : m_links) {
/* proxy operations can skip data type conversion */
NodeOperation *from_op = &link.from()->getOperation();
NodeOperation *to_op = &link.to()->getOperation();
- if (!(from_op->useDatatypeConversion() || to_op->useDatatypeConversion())) {
+ if (!(from_op->get_flags().use_datatype_conversion ||
+ to_op->get_flags().use_datatype_conversion)) {
continue;
}
@@ -281,7 +287,7 @@ void NodeOperationBuilder::add_operation_input_constants()
/* Note: unconnected inputs cached first to avoid modifying
* m_operations while iterating over it
*/
- blender::Vector<NodeOperationInput *> pending_inputs;
+ Vector<NodeOperationInput *> pending_inputs;
for (NodeOperation *op : m_operations) {
for (int k = 0; k < op->getNumberOfInputSockets(); ++k) {
NodeOperationInput *input = op->getInputSocket(k);
@@ -349,11 +355,11 @@ void NodeOperationBuilder::add_input_constant_value(NodeOperationInput *input,
void NodeOperationBuilder::resolve_proxies()
{
- blender::Vector<Link> proxy_links;
+ Vector<Link> proxy_links;
for (const Link &link : m_links) {
/* don't replace links from proxy to proxy, since we may need them for replacing others! */
- if (link.from()->getOperation().isProxyOperation() &&
- !link.to()->getOperation().isProxyOperation()) {
+ if (link.from()->getOperation().get_flags().is_proxy_operation &&
+ !link.to()->getOperation().get_flags().is_proxy_operation) {
proxy_links.append(link);
}
}
@@ -364,7 +370,7 @@ void NodeOperationBuilder::resolve_proxies()
do {
/* walk upstream bypassing the proxy operation */
from = from->getOperation().getInputSocket(0)->getLink();
- } while (from && from->getOperation().isProxyOperation());
+ } while (from && from->getOperation().get_flags().is_proxy_operation);
removeInputLink(to);
/* we may not have a final proxy input link,
@@ -380,7 +386,7 @@ void NodeOperationBuilder::determineResolutions()
{
/* determine all resolutions of the operations (Width/Height) */
for (NodeOperation *op : m_operations) {
- if (op->isOutputOperation(m_context->isRendering()) && !op->isPreviewOperation()) {
+ if (op->isOutputOperation(m_context->isRendering()) && !op->get_flags().is_preview_operation) {
unsigned int resolution[2] = {0, 0};
unsigned int preferredResolution[2] = {0, 0};
op->determineResolution(resolution, preferredResolution);
@@ -389,7 +395,7 @@ void NodeOperationBuilder::determineResolutions()
}
for (NodeOperation *op : m_operations) {
- if (op->isOutputOperation(m_context->isRendering()) && op->isPreviewOperation()) {
+ if (op->isOutputOperation(m_context->isRendering()) && op->get_flags().is_preview_operation) {
unsigned int resolution[2] = {0, 0};
unsigned int preferredResolution[2] = {0, 0};
op->determineResolution(resolution, preferredResolution);
@@ -399,9 +405,9 @@ void NodeOperationBuilder::determineResolutions()
/* add convert resolution operations when needed */
{
- blender::Vector<Link> convert_links;
+ Vector<Link> convert_links;
for (const Link &link : m_links) {
- if (link.to()->getResizeMode() != COM_SC_NO_RESIZE) {
+ if (link.to()->getResizeMode() != ResizeMode::None) {
NodeOperation &from_op = link.from()->getOperation();
NodeOperation &to_op = link.to()->getOperation();
if (from_op.getWidth() != to_op.getWidth() || from_op.getHeight() != to_op.getHeight()) {
@@ -415,10 +421,10 @@ void NodeOperationBuilder::determineResolutions()
}
}
-blender::Vector<NodeOperationInput *> NodeOperationBuilder::cache_output_links(
+Vector<NodeOperationInput *> NodeOperationBuilder::cache_output_links(
NodeOperationOutput *output) const
{
- blender::Vector<NodeOperationInput *> inputs;
+ Vector<NodeOperationInput *> inputs;
for (const Link &link : m_links) {
if (link.from() == output) {
inputs.append(link.to());
@@ -433,7 +439,7 @@ WriteBufferOperation *NodeOperationBuilder::find_attached_write_buffer_operation
for (const Link &link : m_links) {
if (link.from() == output) {
NodeOperation &op = link.to()->getOperation();
- if (op.isWriteBufferOperation()) {
+ if (op.get_flags().is_write_buffer_operation) {
return (WriteBufferOperation *)(&op);
}
}
@@ -449,7 +455,7 @@ void NodeOperationBuilder::add_input_buffers(NodeOperation * /*operation*/,
}
NodeOperationOutput *output = input->getLink();
- if (output->getOperation().isReadBufferOperation()) {
+ if (output->getOperation().get_flags().is_read_buffer_operation) {
/* input is already buffered, no need to add another */
return;
}
@@ -483,7 +489,7 @@ void NodeOperationBuilder::add_output_buffers(NodeOperation *operation,
NodeOperationOutput *output)
{
/* cache connected sockets, so we can safely remove links first before replacing them */
- blender::Vector<NodeOperationInput *> targets = cache_output_links(output);
+ Vector<NodeOperationInput *> targets = cache_output_links(output);
if (targets.is_empty()) {
return;
}
@@ -491,7 +497,7 @@ void NodeOperationBuilder::add_output_buffers(NodeOperation *operation,
WriteBufferOperation *writeOperation = nullptr;
for (NodeOperationInput *target : targets) {
/* try to find existing write buffer operation */
- if (target->getOperation().isWriteBufferOperation()) {
+ if (target->getOperation().get_flags().is_write_buffer_operation) {
BLI_assert(writeOperation == nullptr); /* there should only be one write op connected */
writeOperation = (WriteBufferOperation *)(&target->getOperation());
}
@@ -534,9 +540,9 @@ void NodeOperationBuilder::add_complex_operation_buffers()
/* note: complex ops and get cached here first, since adding operations
* will invalidate iterators over the main m_operations
*/
- blender::Vector<NodeOperation *> complex_ops;
+ Vector<NodeOperation *> complex_ops;
for (NodeOperation *operation : m_operations) {
- if (operation->isComplex()) {
+ if (operation->get_flags().complex) {
complex_ops.append(operation);
}
}
@@ -571,7 +577,7 @@ static void find_reachable_operations_recursive(Tags &reachable, NodeOperation *
}
/* associated write-buffer operations are executed as well */
- if (op->isReadBufferOperation()) {
+ if (op->get_flags().is_read_buffer_operation) {
ReadBufferOperation *read_op = (ReadBufferOperation *)op;
MemoryProxy *memproxy = read_op->getMemoryProxy();
find_reachable_operations_recursive(reachable, memproxy->getWriteBufferOperation());
@@ -589,7 +595,7 @@ void NodeOperationBuilder::prune_operations()
}
/* delete unreachable operations */
- blender::Vector<NodeOperation *> reachable_ops;
+ Vector<NodeOperation *> reachable_ops;
for (NodeOperation *op : m_operations) {
if (reachable.find(op) != reachable.end()) {
reachable_ops.append(op);
@@ -603,7 +609,7 @@ void NodeOperationBuilder::prune_operations()
}
/* topological (depth-first) sorting of operations */
-static void sort_operations_recursive(blender::Vector<NodeOperation *> &sorted,
+static void sort_operations_recursive(Vector<NodeOperation *> &sorted,
Tags &visited,
NodeOperation *op)
{
@@ -624,7 +630,7 @@ static void sort_operations_recursive(blender::Vector<NodeOperation *> &sorted,
void NodeOperationBuilder::sort_operations()
{
- blender::Vector<NodeOperation *> sorted;
+ Vector<NodeOperation *> sorted;
sorted.reserve(m_operations.size());
Tags visited;
@@ -657,7 +663,7 @@ static void add_group_operations_recursive(Tags &visited, NodeOperation *op, Exe
ExecutionGroup *NodeOperationBuilder::make_group(NodeOperation *op)
{
- ExecutionGroup *group = new ExecutionGroup();
+ ExecutionGroup *group = new ExecutionGroup(this->m_groups.size());
m_groups.append(group);
Tags visited;
@@ -675,7 +681,7 @@ void NodeOperationBuilder::group_operations()
}
/* add new groups for associated memory proxies where needed */
- if (op->isReadBufferOperation()) {
+ if (op->get_flags().is_read_buffer_operation) {
ReadBufferOperation *read_op = (ReadBufferOperation *)op;
MemoryProxy *memproxy = read_op->getMemoryProxy();
@@ -686,3 +692,42 @@ void NodeOperationBuilder::group_operations()
}
}
}
+
+/** Create a graphviz representation of the NodeOperationBuilder. */
+std::ostream &operator<<(std::ostream &os, const NodeOperationBuilder &builder)
+{
+ os << "# Builder start\n";
+ os << "digraph G {\n";
+ os << " rankdir=LR;\n";
+ os << " node [shape=box];\n";
+ for (const NodeOperation *operation : builder.get_operations()) {
+ os << " op" << operation->get_id() << " [label=\"" << *operation << "\"];\n";
+ }
+
+ os << "\n";
+ for (const NodeOperationBuilder::Link &link : builder.get_links()) {
+ os << " op" << link.from()->getOperation().get_id() << " -> op"
+ << link.to()->getOperation().get_id() << ";\n";
+ }
+ for (const NodeOperation *operation : builder.get_operations()) {
+ if (operation->get_flags().is_read_buffer_operation) {
+ const ReadBufferOperation &read_operation = static_cast<const ReadBufferOperation &>(
+ *operation);
+ const WriteBufferOperation &write_operation =
+ *read_operation.getMemoryProxy()->getWriteBufferOperation();
+ os << " op" << write_operation.get_id() << " -> op" << read_operation.get_id() << ";\n";
+ }
+ }
+
+ os << "}\n";
+ os << "# Builder end\n";
+ return os;
+}
+
+std::ostream &operator<<(std::ostream &os, const NodeOperationBuilder::Link &link)
+{
+ os << link.from()->getOperation().get_id() << " -> " << link.to()->getOperation().get_id();
+ return os;
+}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_NodeOperationBuilder.h b/source/blender/compositor/intern/COM_NodeOperationBuilder.h
index 8f55574fa88..b2fb822af25 100644
--- a/source/blender/compositor/intern/COM_NodeOperationBuilder.h
+++ b/source/blender/compositor/intern/COM_NodeOperationBuilder.h
@@ -24,6 +24,8 @@
#include "COM_NodeGraph.h"
+namespace blender::compositor {
+
class CompositorContext;
class Node;
@@ -66,14 +68,14 @@ class NodeOperationBuilder {
const CompositorContext *m_context;
NodeGraph m_graph;
- blender::Vector<NodeOperation *> m_operations;
- blender::Vector<Link> m_links;
- blender::Vector<ExecutionGroup *> m_groups;
+ Vector<NodeOperation *> m_operations;
+ Vector<Link> m_links;
+ Vector<ExecutionGroup *> m_groups;
/** Maps operation inputs to node inputs */
- blender::Map<NodeOperationInput *, NodeInput *> m_input_map;
+ Map<NodeOperationInput *, NodeInput *> m_input_map;
/** Maps node outputs to operation outputs */
- blender::Map<NodeOutput *, NodeOperationOutput *> m_output_map;
+ Map<NodeOutput *, NodeOperationOutput *> m_output_map;
Node *m_current_node;
@@ -85,7 +87,6 @@ class NodeOperationBuilder {
public:
NodeOperationBuilder(const CompositorContext *context, bNodeTree *b_nodetree);
- ~NodeOperationBuilder();
const CompositorContext &context() const
{
@@ -117,6 +118,16 @@ class NodeOperationBuilder {
return m_active_viewer;
}
+ const Vector<NodeOperation *> &get_operations() const
+ {
+ return m_operations;
+ }
+
+ const Vector<Link> &get_links() const
+ {
+ return m_links;
+ }
+
protected:
/** Add datatype conversion where needed */
void add_datatype_conversions();
@@ -132,7 +143,7 @@ class NodeOperationBuilder {
void determineResolutions();
/** Helper function to store connected inputs for replacement */
- blender::Vector<NodeOperationInput *> cache_output_links(NodeOperationOutput *output) const;
+ Vector<NodeOperationInput *> cache_output_links(NodeOperationOutput *output) const;
/** Find a connected write buffer operation to an OpOutput */
WriteBufferOperation *find_attached_write_buffer_operation(NodeOperationOutput *output) const;
/** Add read/write buffer operations around complex operations */
@@ -157,3 +168,8 @@ class NodeOperationBuilder {
MEM_CXX_CLASS_ALLOC_FUNCS("COM:NodeCompilerImpl")
#endif
};
+
+std::ostream &operator<<(std::ostream &os, const NodeOperationBuilder &builder);
+std::ostream &operator<<(std::ostream &os, const NodeOperationBuilder::Link &link);
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_OpenCLDevice.cc b/source/blender/compositor/intern/COM_OpenCLDevice.cc
index 4ac6bd50380..0f6ed0dbd2b 100644
--- a/source/blender/compositor/intern/COM_OpenCLDevice.cc
+++ b/source/blender/compositor/intern/COM_OpenCLDevice.cc
@@ -19,6 +19,8 @@
#include "COM_OpenCLDevice.h"
#include "COM_WorkScheduler.h"
+namespace blender::compositor {
+
enum COM_VendorID { NVIDIA = 0x10DE, AMD = 0x1002 };
const cl_image_format IMAGE_FORMAT_COLOR = {
CL_RGBA,
@@ -48,6 +50,16 @@ OpenCLDevice::OpenCLDevice(cl_context context,
this->m_queue = clCreateCommandQueue(this->m_context, this->m_device, 0, &error);
}
+OpenCLDevice::OpenCLDevice(OpenCLDevice &&other) noexcept
+ : m_context(other.m_context),
+ m_device(other.m_device),
+ m_program(other.m_program),
+ m_queue(other.m_queue),
+ m_vendorID(other.m_vendorID)
+{
+ other.m_queue = nullptr;
+}
+
OpenCLDevice::~OpenCLDevice()
{
if (this->m_queue) {
@@ -55,18 +67,16 @@ OpenCLDevice::~OpenCLDevice()
}
}
-void OpenCLDevice::execute(WorkPackage *work)
+void OpenCLDevice::execute(WorkPackage *work_package)
{
- const unsigned int chunkNumber = work->chunk_number;
- ExecutionGroup *executionGroup = work->execution_group;
- rcti rect;
+ const unsigned int chunkNumber = work_package->chunk_number;
+ ExecutionGroup *executionGroup = work_package->execution_group;
- executionGroup->determineChunkRect(&rect, chunkNumber);
MemoryBuffer **inputBuffers = executionGroup->getInputBuffersOpenCL(chunkNumber);
- MemoryBuffer *outputBuffer = executionGroup->allocateOutputBuffer(rect);
+ MemoryBuffer *outputBuffer = executionGroup->allocateOutputBuffer(work_package->rect);
executionGroup->getOutputOperation()->executeOpenCLRegion(
- this, &rect, chunkNumber, inputBuffers, outputBuffer);
+ this, &work_package->rect, chunkNumber, inputBuffers, outputBuffer);
delete outputBuffer;
@@ -270,3 +280,5 @@ cl_kernel OpenCLDevice::COM_clCreateKernel(const char *kernelname,
}
return kernel;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_OpenCLDevice.h b/source/blender/compositor/intern/COM_OpenCLDevice.h
index 30d9a59d182..826b0457a49 100644
--- a/source/blender/compositor/intern/COM_OpenCLDevice.h
+++ b/source/blender/compositor/intern/COM_OpenCLDevice.h
@@ -25,6 +25,8 @@ class OpenCLDevice;
#include "COM_WorkScheduler.h"
#include "clew.h"
+namespace blender::compositor {
+
/**
* \brief device representing an GPU OpenCL device.
* an instance of this class represents a single cl_device
@@ -65,6 +67,9 @@ class OpenCLDevice : public Device {
* \param vendorID:
*/
OpenCLDevice(cl_context context, cl_device_id device, cl_program program, cl_int vendorId);
+
+ OpenCLDevice(OpenCLDevice &&other) noexcept;
+
~OpenCLDevice();
/**
@@ -117,3 +122,5 @@ class OpenCLDevice : public Device {
NodeOperation *operation);
cl_kernel COM_clCreateKernel(const char *kernelname, std::list<cl_kernel> *clKernelsToCleanUp);
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_SharedOperationBuffers.cc b/source/blender/compositor/intern/COM_SharedOperationBuffers.cc
new file mode 100644
index 00000000000..7e0486b0f54
--- /dev/null
+++ b/source/blender/compositor/intern/COM_SharedOperationBuffers.cc
@@ -0,0 +1,129 @@
+/*
+ * 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.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+#include "COM_SharedOperationBuffers.h"
+#include "BLI_rect.h"
+#include "COM_NodeOperation.h"
+
+namespace blender::compositor {
+
+SharedOperationBuffers::BufferData::BufferData()
+ : buffer(nullptr), registered_reads(0), received_reads(0), is_rendered(false)
+{
+}
+
+SharedOperationBuffers::BufferData &SharedOperationBuffers::get_buffer_data(NodeOperation *op)
+{
+ return buffers_.lookup_or_add_cb(op, []() { return BufferData(); });
+}
+
+/**
+ * Whether given operation area to render is already registered.
+ * TODO: Possibly refactor to "request_area". Current implementation is incomplete: partial
+ * overlapping, etc. Leading to more rendering than necessary.
+ */
+bool SharedOperationBuffers::is_area_registered(NodeOperation *op, const rcti &area_to_render)
+{
+ BufferData &buf_data = get_buffer_data(op);
+ for (rcti &reg_rect : buf_data.render_areas) {
+ if (BLI_rcti_inside_rcti(&reg_rect, &area_to_render)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Registers an operation area to render.
+ */
+void SharedOperationBuffers::register_area(NodeOperation *op, const rcti &area_to_render)
+{
+ get_buffer_data(op).render_areas.append(area_to_render);
+}
+
+/**
+ * Whether given operation has any registered reads (other operation registered it depends on given
+ * operation).
+ */
+bool SharedOperationBuffers::has_registered_reads(NodeOperation *op)
+{
+ return get_buffer_data(op).registered_reads > 0;
+}
+
+/**
+ * Registers an operation read (other operation depends on given operation).
+ */
+void SharedOperationBuffers::register_read(NodeOperation *read_op)
+{
+ get_buffer_data(read_op).registered_reads++;
+}
+
+/**
+ * Get registered areas given operation needs to render.
+ */
+blender::Span<rcti> SharedOperationBuffers::get_areas_to_render(NodeOperation *op)
+{
+ return get_buffer_data(op).render_areas.as_span();
+}
+
+/**
+ * Whether this operation buffer has already been rendered.
+ */
+bool SharedOperationBuffers::is_operation_rendered(NodeOperation *op)
+{
+ return get_buffer_data(op).is_rendered;
+}
+
+/**
+ * Stores given operation rendered buffer.
+ */
+void SharedOperationBuffers::set_rendered_buffer(NodeOperation *op,
+ std::unique_ptr<MemoryBuffer> buffer)
+{
+ BufferData &buf_data = get_buffer_data(op);
+ BLI_assert(buf_data.received_reads == 0);
+ BLI_assert(buf_data.buffer == nullptr);
+ buf_data.buffer = std::move(buffer);
+ buf_data.is_rendered = true;
+}
+
+/**
+ * Get given operation rendered buffer.
+ */
+MemoryBuffer *SharedOperationBuffers::get_rendered_buffer(NodeOperation *op)
+{
+ BLI_assert(is_operation_rendered(op));
+ return get_buffer_data(op).buffer.get();
+}
+
+/**
+ * Reports an operation has finished reading given operation. If all given operation dependencies
+ * have finished its buffer will be disposed.
+ */
+void SharedOperationBuffers::read_finished(NodeOperation *read_op)
+{
+ BufferData &buf_data = get_buffer_data(read_op);
+ buf_data.received_reads++;
+ BLI_assert(buf_data.received_reads > 0 && buf_data.received_reads <= buf_data.registered_reads);
+ if (buf_data.received_reads == buf_data.registered_reads) {
+ /* Dispose buffer. */
+ buf_data.buffer = nullptr;
+ }
+}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_SharedOperationBuffers.h b/source/blender/compositor/intern/COM_SharedOperationBuffers.h
new file mode 100644
index 00000000000..f7763cd8ae4
--- /dev/null
+++ b/source/blender/compositor/intern/COM_SharedOperationBuffers.h
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+#pragma once
+
+#include "BLI_map.hh"
+#include "BLI_span.hh"
+#include "BLI_vector.hh"
+#include "COM_MemoryBuffer.h"
+#ifdef WITH_CXX_GUARDEDALLOC
+# include "MEM_guardedalloc.h"
+#endif
+#include <memory>
+
+namespace blender::compositor {
+
+/**
+ * Stores and shares operations rendered buffers including render data. Buffers are
+ * disposed once all dependent operations have finished reading them.
+ */
+class SharedOperationBuffers {
+ private:
+ typedef struct BufferData {
+ public:
+ BufferData();
+ std::unique_ptr<MemoryBuffer> buffer;
+ blender::Vector<rcti> render_areas;
+ int registered_reads;
+ int received_reads;
+ bool is_rendered;
+ } BufferData;
+ blender::Map<NodeOperation *, BufferData> buffers_;
+
+ public:
+ bool is_area_registered(NodeOperation *op, const rcti &area_to_render);
+ void register_area(NodeOperation *op, const rcti &area_to_render);
+
+ bool has_registered_reads(NodeOperation *op);
+ void register_read(NodeOperation *read_op);
+
+ blender::Span<rcti> get_areas_to_render(NodeOperation *op);
+ bool is_operation_rendered(NodeOperation *op);
+ void set_rendered_buffer(NodeOperation *op, std::unique_ptr<MemoryBuffer> buffer);
+ MemoryBuffer *get_rendered_buffer(NodeOperation *op);
+
+ void read_finished(NodeOperation *read_op);
+
+ private:
+ BufferData &get_buffer_data(NodeOperation *op);
+
+#ifdef WITH_CXX_GUARDEDALLOC
+ MEM_CXX_CLASS_ALLOC_FUNCS("COM:SharedOperationBuffers")
+#endif
+};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_SingleThreadedOperation.cc b/source/blender/compositor/intern/COM_SingleThreadedOperation.cc
index 5febf3802de..01be6e1afed 100644
--- a/source/blender/compositor/intern/COM_SingleThreadedOperation.cc
+++ b/source/blender/compositor/intern/COM_SingleThreadedOperation.cc
@@ -18,10 +18,13 @@
#include "COM_SingleThreadedOperation.h"
+namespace blender::compositor {
+
SingleThreadedOperation::SingleThreadedOperation()
{
this->m_cachedInstance = nullptr;
- setComplex(true);
+ flags.complex = true;
+ flags.single_threaded = true;
}
void SingleThreadedOperation::initExecution()
@@ -56,3 +59,5 @@ void *SingleThreadedOperation::initializeTileData(rcti *rect)
unlockMutex();
return this->m_cachedInstance;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_SingleThreadedOperation.h b/source/blender/compositor/intern/COM_SingleThreadedOperation.h
index 82326b57b57..9945f938ff9 100644
--- a/source/blender/compositor/intern/COM_SingleThreadedOperation.h
+++ b/source/blender/compositor/intern/COM_SingleThreadedOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class SingleThreadedOperation : public NodeOperation {
private:
MemoryBuffer *m_cachedInstance;
@@ -51,9 +53,6 @@ class SingleThreadedOperation : public NodeOperation {
void *initializeTileData(rcti *rect) override;
virtual MemoryBuffer *createMemoryBuffer(rcti *rect) = 0;
-
- int isSingleThreaded() override
- {
- return true;
- }
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_SocketReader.h b/source/blender/compositor/intern/COM_SocketReader.h
deleted file mode 100644
index 7c4132efe60..00000000000
--- a/source/blender/compositor/intern/COM_SocketReader.h
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * 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.
- *
- * Copyright 2011, Blender Foundation.
- */
-
-#pragma once
-
-#include "BLI_rect.h"
-#include "COM_MetaData.h"
-#include "COM_defines.h"
-
-#include <memory>
-#include <optional>
-
-#ifdef WITH_CXX_GUARDEDALLOC
-# include "MEM_guardedalloc.h"
-#endif
-
-typedef enum PixelSampler {
- COM_PS_NEAREST = 0,
- COM_PS_BILINEAR = 1,
- COM_PS_BICUBIC = 2,
-} PixelSampler;
-
-class MemoryBuffer;
-
-/**
- * \brief Helper class for reading socket data.
- * Only use this class for dispatching (un-ary and n-ary) executions.
- * \ingroup Execution
- */
-class SocketReader {
- private:
- protected:
- /**
- * \brief Holds the width of the output of this operation.
- */
- unsigned int m_width;
-
- /**
- * \brief Holds the height of the output of this operation.
- */
- unsigned int m_height;
-
- /**
- * \brief calculate a single pixel
- * \note this method is called for non-complex
- * \param result: is a float[4] array to store the result
- * \param x: the x-coordinate of the pixel to calculate in image space
- * \param y: the y-coordinate of the pixel to calculate in image space
- * \param inputBuffers: chunks that can be read by their ReadBufferOperation.
- */
- virtual void executePixelSampled(float /*output*/[4],
- float /*x*/,
- float /*y*/,
- PixelSampler /*sampler*/)
- {
- }
-
- /**
- * \brief calculate a single pixel
- * \note this method is called for complex
- * \param result: is a float[4] array to store the result
- * \param x: the x-coordinate of the pixel to calculate in image space
- * \param y: the y-coordinate of the pixel to calculate in image space
- * \param inputBuffers: chunks that can be read by their ReadBufferOperation.
- * \param chunkData: chunk specific data a during execution time.
- */
- virtual void executePixel(float output[4], int x, int y, void * /*chunkData*/)
- {
- executePixelSampled(output, x, y, COM_PS_NEAREST);
- }
-
- /**
- * \brief calculate a single pixel using an EWA filter
- * \note this method is called for complex
- * \param result: is a float[4] array to store the result
- * \param x: the x-coordinate of the pixel to calculate in image space
- * \param y: the y-coordinate of the pixel to calculate in image space
- * \param dx:
- * \param dy:
- * \param inputBuffers: chunks that can be read by their ReadBufferOperation.
- */
- virtual void executePixelFiltered(
- float /*output*/[4], float /*x*/, float /*y*/, float /*dx*/[2], float /*dy*/[2])
- {
- }
-
- public:
- inline void readSampled(float result[4], float x, float y, PixelSampler sampler)
- {
- executePixelSampled(result, x, y, sampler);
- }
- inline void read(float result[4], int x, int y, void *chunkData)
- {
- executePixel(result, x, y, chunkData);
- }
- inline void readFiltered(float result[4], float x, float y, float dx[2], float dy[2])
- {
- executePixelFiltered(result, x, y, dx, dy);
- }
-
- virtual void *initializeTileData(rcti * /*rect*/)
- {
- return 0;
- }
- virtual void deinitializeTileData(rcti * /*rect*/, void * /*data*/)
- {
- }
-
- virtual ~SocketReader()
- {
- }
-
- virtual MemoryBuffer *getInputMemoryBuffer(MemoryBuffer ** /*memoryBuffers*/)
- {
- return 0;
- }
-
- inline unsigned int getWidth() const
- {
- return this->m_width;
- }
- inline unsigned int getHeight() const
- {
- return this->m_height;
- }
-
- /* Return the meta data associated with this branch.
- *
- * The return parameter holds an instance or is an nullptr. */
- virtual std::unique_ptr<MetaData> getMetaData() const
- {
- return std::unique_ptr<MetaData>();
- }
-
-#ifdef WITH_CXX_GUARDEDALLOC
- MEM_CXX_CLASS_ALLOC_FUNCS("COM:SocketReader")
-#endif
-};
diff --git a/source/blender/compositor/intern/COM_TiledExecutionModel.cc b/source/blender/compositor/intern/COM_TiledExecutionModel.cc
new file mode 100644
index 00000000000..d025ce53330
--- /dev/null
+++ b/source/blender/compositor/intern/COM_TiledExecutionModel.cc
@@ -0,0 +1,158 @@
+/*
+ * 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.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+#include "COM_TiledExecutionModel.h"
+#include "COM_Debug.h"
+#include "COM_ExecutionGroup.h"
+#include "COM_ReadBufferOperation.h"
+#include "COM_WorkScheduler.h"
+
+#include "BLT_translation.h"
+
+#ifdef WITH_CXX_GUARDEDALLOC
+# include "MEM_guardedalloc.h"
+#endif
+
+namespace blender::compositor {
+
+TiledExecutionModel::TiledExecutionModel(CompositorContext &context,
+ Span<NodeOperation *> operations,
+ Span<ExecutionGroup *> groups)
+ : ExecutionModel(context, operations), groups_(groups)
+{
+ const bNodeTree *node_tree = context.getbNodeTree();
+ node_tree->stats_draw(node_tree->sdh, TIP_("Compositing | Determining resolution"));
+
+ unsigned int resolution[2];
+ for (ExecutionGroup *group : groups_) {
+ resolution[0] = 0;
+ resolution[1] = 0;
+ group->determineResolution(resolution);
+
+ if (border_.use_render_border) {
+ const rctf *render_border = border_.viewer_border;
+ group->setRenderBorder(
+ render_border->xmin, render_border->xmax, render_border->ymin, render_border->ymax);
+ }
+
+ if (border_.use_viewer_border) {
+ const rctf *viewer_border = border_.viewer_border;
+ group->setViewerBorder(
+ viewer_border->xmin, viewer_border->xmax, viewer_border->ymin, viewer_border->ymax);
+ }
+ }
+}
+
+static void update_read_buffer_offset(Span<NodeOperation *> operations)
+{
+ unsigned int order = 0;
+ for (NodeOperation *operation : operations) {
+ if (operation->get_flags().is_read_buffer_operation) {
+ ReadBufferOperation *readOperation = (ReadBufferOperation *)operation;
+ readOperation->setOffset(order);
+ order++;
+ }
+ }
+}
+
+static void init_write_operations_for_execution(Span<NodeOperation *> operations,
+ const bNodeTree *bTree)
+{
+ for (NodeOperation *operation : operations) {
+ if (operation->get_flags().is_write_buffer_operation) {
+ operation->setbNodeTree(bTree);
+ operation->initExecution();
+ }
+ }
+}
+
+static void link_write_buffers(Span<NodeOperation *> operations)
+{
+ for (NodeOperation *operation : operations) {
+ if (operation->get_flags().is_read_buffer_operation) {
+ ReadBufferOperation *readOperation = static_cast<ReadBufferOperation *>(operation);
+ readOperation->updateMemoryBuffer();
+ }
+ }
+}
+
+static void init_non_write_operations_for_execution(Span<NodeOperation *> operations,
+ const bNodeTree *bTree)
+{
+ for (NodeOperation *operation : operations) {
+ if (!operation->get_flags().is_write_buffer_operation) {
+ operation->setbNodeTree(bTree);
+ operation->initExecution();
+ }
+ }
+}
+
+static void init_execution_groups_for_execution(Span<ExecutionGroup *> groups,
+ const int chunk_size)
+{
+ for (ExecutionGroup *execution_group : groups) {
+ execution_group->setChunksize(chunk_size);
+ execution_group->initExecution();
+ }
+}
+
+void TiledExecutionModel::execute(ExecutionSystem &exec_system)
+{
+ const bNodeTree *editingtree = this->context_.getbNodeTree();
+
+ editingtree->stats_draw(editingtree->sdh, TIP_("Compositing | Initializing execution"));
+
+ update_read_buffer_offset(operations_);
+
+ init_write_operations_for_execution(operations_, context_.getbNodeTree());
+ link_write_buffers(operations_);
+ init_non_write_operations_for_execution(operations_, context_.getbNodeTree());
+ init_execution_groups_for_execution(groups_, context_.getChunksize());
+
+ WorkScheduler::start(context_);
+ execute_groups(eCompositorPriority::High, exec_system);
+ if (!context_.isFastCalculation()) {
+ execute_groups(eCompositorPriority::Medium, exec_system);
+ execute_groups(eCompositorPriority::Low, exec_system);
+ }
+ WorkScheduler::finish();
+ WorkScheduler::stop();
+
+ editingtree->stats_draw(editingtree->sdh, TIP_("Compositing | De-initializing execution"));
+
+ for (NodeOperation *operation : operations_) {
+ operation->deinitExecution();
+ }
+
+ for (ExecutionGroup *execution_group : groups_) {
+ execution_group->deinitExecution();
+ }
+}
+
+void TiledExecutionModel::execute_groups(eCompositorPriority priority,
+ ExecutionSystem &exec_system)
+{
+ for (ExecutionGroup *execution_group : groups_) {
+ if (execution_group->get_flags().is_output &&
+ execution_group->getRenderPriority() == priority) {
+ execution_group->execute(&exec_system);
+ }
+ }
+}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_TiledExecutionModel.h b/source/blender/compositor/intern/COM_TiledExecutionModel.h
new file mode 100644
index 00000000000..05a795b9f07
--- /dev/null
+++ b/source/blender/compositor/intern/COM_TiledExecutionModel.h
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+#pragma once
+
+#include "COM_ExecutionModel.h"
+
+#ifdef WITH_CXX_GUARDEDALLOC
+# include "MEM_guardedalloc.h"
+#endif
+
+namespace blender::compositor {
+
+class ExecutionGroup;
+
+/**
+ * Operations are executed from outputs to inputs grouped in execution groups and rendered in
+ * tiles.
+ */
+class TiledExecutionModel : public ExecutionModel {
+ private:
+ Span<ExecutionGroup *> groups_;
+
+ public:
+ TiledExecutionModel(CompositorContext &context,
+ Span<NodeOperation *> operations,
+ Span<ExecutionGroup *> groups);
+
+ void execute(ExecutionSystem &exec_system) override;
+
+ private:
+ void execute_groups(eCompositorPriority priority, ExecutionSystem &exec_system);
+
+#ifdef WITH_CXX_GUARDEDALLOC
+ MEM_CXX_CLASS_ALLOC_FUNCS("COM:TiledExecutionModel")
+#endif
+};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_WorkPackage.cc b/source/blender/compositor/intern/COM_WorkPackage.cc
index 60684f2c45c..ea78c0d6333 100644
--- a/source/blender/compositor/intern/COM_WorkPackage.cc
+++ b/source/blender/compositor/intern/COM_WorkPackage.cc
@@ -18,8 +18,20 @@
#include "COM_WorkPackage.h"
-WorkPackage::WorkPackage(ExecutionGroup *execution_group, unsigned int chunk_number)
+#include "COM_Enums.h"
+#include "COM_ExecutionGroup.h"
+
+namespace blender::compositor {
+
+std::ostream &operator<<(std::ostream &os, const WorkPackage &work_package)
{
- this->execution_group = execution_group;
- this->chunk_number = chunk_number;
+ os << "WorkPackage(execution_group=" << *work_package.execution_group;
+ os << ",chunk=" << work_package.chunk_number;
+ os << ",state=" << work_package.state;
+ os << ",rect=(" << work_package.rect.xmin << "," << work_package.rect.ymin << ")-("
+ << work_package.rect.xmax << "," << work_package.rect.ymax << ")";
+ os << ")";
+ return os;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_WorkPackage.h b/source/blender/compositor/intern/COM_WorkPackage.h
index db5eb3d3072..f0f53f300a5 100644
--- a/source/blender/compositor/intern/COM_WorkPackage.h
+++ b/source/blender/compositor/intern/COM_WorkPackage.h
@@ -18,14 +18,30 @@
#pragma once
+#ifdef WITH_CXX_GUARDEDALLOC
+# include "MEM_guardedalloc.h"
+#endif
+
+#include "COM_Enums.h"
+
+#include "BLI_rect.h"
+
+#include <functional>
+#include <ostream>
+
+namespace blender::compositor {
+// Forward Declarations.
class ExecutionGroup;
-#include "COM_ExecutionGroup.h"
/**
* \brief contains data about work that can be scheduled
* \see WorkScheduler
*/
struct WorkPackage {
+ eWorkPackageType type;
+
+ eWorkPackageState state = eWorkPackageState::NotScheduled;
+
/**
* \brief executionGroup with the operations-setup to be evaluated
*/
@@ -37,13 +53,25 @@ struct WorkPackage {
unsigned int chunk_number;
/**
- * constructor
- * \param group: the ExecutionGroup
- * \param chunk_number: the number of the chunk
+ * Area of the execution group that the work package calculates.
*/
- WorkPackage(ExecutionGroup *group, unsigned int chunk_number);
+ rcti rect;
+
+ /**
+ * Custom function to execute when work package type is CustomFunction.
+ */
+ std::function<void()> execute_fn;
+
+ /**
+ * Called when work execution is finished.
+ */
+ std::function<void()> executed_fn;
#ifdef WITH_CXX_GUARDEDALLOC
MEM_CXX_CLASS_ALLOC_FUNCS("COM:WorkPackage")
#endif
};
+
+std::ostream &operator<<(std::ostream &os, const WorkPackage &work_package);
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_WorkScheduler.cc b/source/blender/compositor/intern/COM_WorkScheduler.cc
index 7d9dd502762..cd0139fd18e 100644
--- a/source/blender/compositor/intern/COM_WorkScheduler.cc
+++ b/source/blender/compositor/intern/COM_WorkScheduler.cc
@@ -37,6 +37,8 @@
#include "BKE_global.h"
+namespace blender::compositor {
+
enum class ThreadingModel {
/** Everything is executed in the caller thread. easy for debugging. */
SingleThreaded,
@@ -70,7 +72,7 @@ static struct {
/** \brief list of all CPUDevices. for every hardware thread an instance of CPUDevice is
* created
*/
- blender::Vector<CPUDevice> devices;
+ Vector<CPUDevice> devices;
/** \brief list of all thread for every CPUDevice in cpudevices a thread exists. */
ListBase threads;
@@ -89,13 +91,15 @@ static struct {
cl_program program;
/** \brief list of all OpenCLDevices. for every OpenCL GPU device an instance of OpenCLDevice
* is created. */
- blender::Vector<OpenCLDevice> devices;
+ Vector<OpenCLDevice> devices;
/** \brief list of all thread for every GPUDevice in cpudevices a thread exists. */
ListBase threads;
/** \brief all scheduled work for the GPU. */
bool active = false;
bool initialized = false;
} opencl;
+
+ int num_cpu_threads;
} g_work_scheduler;
/* -------------------------------------------------------------------- */
@@ -117,7 +121,6 @@ static void *thread_execute_gpu(void *data)
while ((work = (WorkPackage *)BLI_thread_queue_pop(g_work_scheduler.opencl.queue))) {
device->execute(work);
- delete work;
}
return nullptr;
@@ -142,7 +145,8 @@ static void opencl_start(CompositorContext &context)
static bool opencl_schedule(WorkPackage *package)
{
- if (package->execution_group->isOpenCL() && g_work_scheduler.opencl.active) {
+ if (package->type == eWorkPackageType::Tile && package->execution_group->get_flags().open_cl &&
+ g_work_scheduler.opencl.active) {
BLI_thread_queue_push(g_work_scheduler.opencl.queue, package);
return true;
}
@@ -262,10 +266,10 @@ static void opencl_initialize(const bool use_opencl)
if (error2 != CL_SUCCESS) {
printf("CLERROR[%d]: %s\n", error2, clewErrorString(error2));
}
- g_work_scheduler.opencl.devices.append(OpenCLDevice(g_work_scheduler.opencl.context,
- device,
- g_work_scheduler.opencl.program,
- vendorID));
+ g_work_scheduler.opencl.devices.append_as(g_work_scheduler.opencl.context,
+ device,
+ g_work_scheduler.opencl.program,
+ vendorID);
}
}
MEM_freeN(cldevices);
@@ -304,7 +308,6 @@ static void threading_model_single_thread_execute(WorkPackage *package)
{
CPUDevice device(0);
device.execute(package);
- delete package;
}
/* \} */
@@ -320,7 +323,6 @@ static void *threading_model_queue_execute(void *data)
BLI_thread_local_set(g_thread_device, device);
while ((work = (WorkPackage *)BLI_thread_queue_pop(g_work_scheduler.queue.queue))) {
device->execute(work);
- delete work;
}
return nullptr;
@@ -369,7 +371,7 @@ static void threading_model_queue_initialize(const int num_cpu_threads)
/* Initialize CPU threads. */
if (!g_work_scheduler.queue.initialized) {
for (int index = 0; index < num_cpu_threads; index++) {
- g_work_scheduler.queue.devices.append(CPUDevice(index));
+ g_work_scheduler.queue.devices.append_as(index);
}
BLI_thread_local_create(g_thread_device);
g_work_scheduler.queue.initialized = true;
@@ -398,7 +400,6 @@ static void threading_model_task_execute(TaskPool *__restrict UNUSED(pool), void
CPUDevice device(BLI_task_parallel_thread_id(nullptr));
BLI_thread_local_set(g_thread_device, &device);
device.execute(package);
- delete package;
}
static void threading_model_task_schedule(WorkPackage *package)
@@ -410,7 +411,8 @@ static void threading_model_task_schedule(WorkPackage *package)
static void threading_model_task_start()
{
BLI_thread_local_create(g_thread_device);
- g_work_scheduler.task.pool = BLI_task_pool_create(nullptr, TASK_PRIORITY_HIGH);
+ g_work_scheduler.task.pool = BLI_task_pool_create(
+ nullptr, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON);
}
static void threading_model_task_finish()
@@ -431,10 +433,8 @@ static void threading_model_task_stop()
/** \name Public API
* \{ */
-void WorkScheduler::schedule(ExecutionGroup *group, int chunkNumber)
+void WorkScheduler::schedule(WorkPackage *package)
{
- WorkPackage *package = new WorkPackage(group, chunkNumber);
-
if (COM_is_opencl_enabled()) {
if (opencl_schedule(package)) {
return;
@@ -536,11 +536,12 @@ void WorkScheduler::initialize(bool use_opencl, int num_cpu_threads)
opencl_initialize(use_opencl);
}
+ g_work_scheduler.num_cpu_threads = num_cpu_threads;
switch (COM_threading_model()) {
case ThreadingModel::SingleThreaded:
+ g_work_scheduler.num_cpu_threads = 1;
/* Nothing to do. */
break;
-
case ThreadingModel::Queue:
threading_model_queue_initialize(num_cpu_threads);
break;
@@ -572,6 +573,11 @@ void WorkScheduler::deinitialize()
}
}
+int WorkScheduler::get_num_cpu_threads()
+{
+ return g_work_scheduler.num_cpu_threads;
+}
+
int WorkScheduler::current_thread_id()
{
if (COM_threading_model() == ThreadingModel::SingleThreaded) {
@@ -583,3 +589,5 @@ int WorkScheduler::current_thread_id()
}
/* \} */
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_WorkScheduler.h b/source/blender/compositor/intern/COM_WorkScheduler.h
index 6b53cc3efd6..be88859be7c 100644
--- a/source/blender/compositor/intern/COM_WorkScheduler.h
+++ b/source/blender/compositor/intern/COM_WorkScheduler.h
@@ -24,6 +24,8 @@
#include "COM_WorkPackage.h"
#include "COM_defines.h"
+namespace blender::compositor {
+
/** \brief the workscheduler
* \ingroup execution
*/
@@ -31,13 +33,11 @@ struct WorkScheduler {
/**
* \brief schedule a chunk of a group to be calculated.
* An execution group schedules a chunk in the WorkScheduler
- * when ExecutionGroup.isOpenCL is set the work will be handled by a OpenCLDevice
+ * when ExecutionGroup.get_flags().open_cl is set the work will be handled by a OpenCLDevice
* otherwise the work is scheduled for an CPUDevice
* \see ExecutionGroup.execute
- * \param group: the execution group
- * \param chunkNumber: the number of the chunk in the group to be executed
*/
- static void schedule(ExecutionGroup *group, int chunkNumber);
+ static void schedule(WorkPackage *package);
/**
* \brief initialize the WorkScheduler
@@ -87,9 +87,13 @@ struct WorkScheduler {
*/
static bool has_gpu_devices();
+ static int get_num_cpu_threads();
+
static int current_thread_id();
#ifdef WITH_CXX_GUARDEDALLOC
MEM_CXX_CLASS_ALLOC_FUNCS("COM:WorkScheduler")
#endif
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_compositor.cc b/source/blender/compositor/intern/COM_compositor.cc
index 68e4f80f91f..5839f53976b 100644
--- a/source/blender/compositor/intern/COM_compositor.cc
+++ b/source/blender/compositor/intern/COM_compositor.cc
@@ -46,12 +46,12 @@ static void compositor_init_node_previews(const RenderData *render_data, bNodeTr
1.0f;
int preview_width, preview_height;
if (aspect < 1.0f) {
- preview_width = COM_PREVIEW_SIZE;
- preview_height = (int)(COM_PREVIEW_SIZE * aspect);
+ preview_width = blender::compositor::COM_PREVIEW_SIZE;
+ preview_height = (int)(blender::compositor::COM_PREVIEW_SIZE * aspect);
}
else {
- preview_width = (int)(COM_PREVIEW_SIZE / aspect);
- preview_height = COM_PREVIEW_SIZE;
+ preview_width = (int)(blender::compositor::COM_PREVIEW_SIZE / aspect);
+ preview_height = blender::compositor::COM_PREVIEW_SIZE;
}
BKE_node_preview_init_tree(node_tree, preview_width, preview_height, false);
}
@@ -92,12 +92,12 @@ void COM_execute(RenderData *render_data,
/* Initialize workscheduler. */
const bool use_opencl = (node_tree->flag & NTREE_COM_OPENCL) != 0;
- WorkScheduler::initialize(use_opencl, BKE_render_num_threads(render_data));
+ blender::compositor::WorkScheduler::initialize(use_opencl, BKE_render_num_threads(render_data));
/* Execute. */
const bool twopass = (node_tree->flag & NTREE_TWO_PASS) && !rendering;
if (twopass) {
- ExecutionSystem fast_pass(
+ blender::compositor::ExecutionSystem fast_pass(
render_data, scene, node_tree, rendering, true, viewSettings, displaySettings, viewName);
fast_pass.execute();
@@ -107,7 +107,7 @@ void COM_execute(RenderData *render_data,
}
}
- ExecutionSystem system(
+ blender::compositor::ExecutionSystem system(
render_data, scene, node_tree, rendering, false, viewSettings, displaySettings, viewName);
system.execute();
@@ -118,7 +118,7 @@ void COM_deinitialize()
{
if (g_compositor.is_initialized) {
BLI_mutex_lock(&g_compositor.mutex);
- WorkScheduler::deinitialize();
+ blender::compositor::WorkScheduler::deinitialize();
g_compositor.is_initialized = false;
BLI_mutex_unlock(&g_compositor.mutex);
BLI_mutex_end(&g_compositor.mutex);
diff --git a/source/blender/compositor/nodes/COM_AlphaOverNode.cc b/source/blender/compositor/nodes/COM_AlphaOverNode.cc
index 640fbf49808..5e09902aee2 100644
--- a/source/blender/compositor/nodes/COM_AlphaOverNode.cc
+++ b/source/blender/compositor/nodes/COM_AlphaOverNode.cc
@@ -26,6 +26,8 @@
#include "COM_SetValueOperation.h"
#include "DNA_material_types.h" /* the ramp types */
+namespace blender::compositor {
+
void AlphaOverNode::convertToOperations(NodeConverter &converter,
const CompositorContext & /*context*/) const
{
@@ -64,3 +66,5 @@ void AlphaOverNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(getInputSocket(2), convertProg->getInputSocket(2));
converter.mapOutputSocket(getOutputSocket(0), convertProg->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_AlphaOverNode.h b/source/blender/compositor/nodes/COM_AlphaOverNode.h
index cbfda2885d7..201c8ed5b6e 100644
--- a/source/blender/compositor/nodes/COM_AlphaOverNode.h
+++ b/source/blender/compositor/nodes/COM_AlphaOverNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief AlphaOverNode
* \ingroup Node
@@ -32,3 +34,5 @@ class AlphaOverNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_AntiAliasingNode.cc b/source/blender/compositor/nodes/COM_AntiAliasingNode.cc
new file mode 100644
index 00000000000..af4832665df
--- /dev/null
+++ b/source/blender/compositor/nodes/COM_AntiAliasingNode.cc
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2017, Blender Foundation.
+ *
+ * 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.
+ *
+ * Contributor: IRIE Shinsuke
+ */
+
+#include "COM_AntiAliasingNode.h"
+#include "COM_SMAAOperation.h"
+#include "DNA_node_types.h"
+
+namespace blender::compositor {
+
+void AntiAliasingNode::convertToOperations(NodeConverter &converter,
+ const CompositorContext & /*context*/) const
+{
+ bNode *node = this->getbNode();
+ NodeAntiAliasingData *data = (NodeAntiAliasingData *)node->storage;
+
+ /* Edge Detection (First Pass) */
+ SMAAEdgeDetectionOperation *operation1 = nullptr;
+
+ operation1 = new SMAAEdgeDetectionOperation();
+ operation1->setThreshold(data->threshold);
+ operation1->setLocalContrastAdaptationFactor(data->contrast_limit);
+ converter.addOperation(operation1);
+
+ converter.mapInputSocket(getInputSocket(0), operation1->getInputSocket(0));
+
+ /* Blending Weight Calculation Pixel Shader (Second Pass) */
+ SMAABlendingWeightCalculationOperation *operation2 =
+ new SMAABlendingWeightCalculationOperation();
+ operation2->setCornerRounding(data->corner_rounding);
+ converter.addOperation(operation2);
+
+ converter.addLink(operation1->getOutputSocket(), operation2->getInputSocket(0));
+
+ /* Neighborhood Blending Pixel Shader (Third Pass) */
+ SMAANeighborhoodBlendingOperation *operation3 = new SMAANeighborhoodBlendingOperation();
+ converter.addOperation(operation3);
+
+ converter.mapInputSocket(getInputSocket(0), operation3->getInputSocket(0));
+ converter.addLink(operation2->getOutputSocket(), operation3->getInputSocket(1));
+ converter.mapOutputSocket(getOutputSocket(0), operation3->getOutputSocket());
+}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_AntiAliasingNode.h b/source/blender/compositor/nodes/COM_AntiAliasingNode.h
new file mode 100644
index 00000000000..d4a6d0d26dc
--- /dev/null
+++ b/source/blender/compositor/nodes/COM_AntiAliasingNode.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2017, Blender Foundation.
+ *
+ * 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.
+ *
+ * Contributor: IRIE Shinsuke
+ */
+
+#pragma once
+
+#include "COM_Node.h"
+
+namespace blender::compositor {
+
+/**
+ * @brief AntiAliasingNode
+ * @ingroup Node
+ */
+class AntiAliasingNode : public Node {
+ public:
+ AntiAliasingNode(bNode *editorNode) : Node(editorNode)
+ {
+ }
+ void convertToOperations(NodeConverter &converter,
+ const CompositorContext &context) const override;
+};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_BilateralBlurNode.cc b/source/blender/compositor/nodes/COM_BilateralBlurNode.cc
index e8037f923f2..1b9da789d3c 100644
--- a/source/blender/compositor/nodes/COM_BilateralBlurNode.cc
+++ b/source/blender/compositor/nodes/COM_BilateralBlurNode.cc
@@ -21,6 +21,8 @@
#include "COM_ExecutionSystem.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
BilateralBlurNode::BilateralBlurNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -39,3 +41,5 @@ void BilateralBlurNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1));
converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_BilateralBlurNode.h b/source/blender/compositor/nodes/COM_BilateralBlurNode.h
index 39abc47bfaa..fed2612ac02 100644
--- a/source/blender/compositor/nodes/COM_BilateralBlurNode.h
+++ b/source/blender/compositor/nodes/COM_BilateralBlurNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief BilateralBlurNode
* \ingroup Node
@@ -30,3 +32,5 @@ class BilateralBlurNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_BlurNode.cc b/source/blender/compositor/nodes/COM_BlurNode.cc
index b82bede8443..c10bc2a05f0 100644
--- a/source/blender/compositor/nodes/COM_BlurNode.cc
+++ b/source/blender/compositor/nodes/COM_BlurNode.cc
@@ -29,6 +29,8 @@
#include "COM_SetValueOperation.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
BlurNode::BlurNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -45,7 +47,7 @@ void BlurNode::convertToOperations(NodeConverter &converter,
const float size = this->getInputSocket(1)->getEditorValueFloat();
const bool extend_bounds = (editorNode->custom1 & CMP_NODEFLAG_BLUR_EXTEND_BOUNDS) != 0;
- CompositorQuality quality = context.getQuality();
+ eCompositorQuality quality = context.getQuality();
NodeOperation *input_operation = nullptr, *output_operation = nullptr;
if (data->filtertype == R_FILTER_FAST_GAUSS) {
@@ -168,3 +170,5 @@ void BlurNode::convertToOperations(NodeConverter &converter,
converter.addPreview(output_operation->getOutputSocket());
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_BlurNode.h b/source/blender/compositor/nodes/COM_BlurNode.h
index 6f756445ea2..61cdc17f3a9 100644
--- a/source/blender/compositor/nodes/COM_BlurNode.h
+++ b/source/blender/compositor/nodes/COM_BlurNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief BlurNode
* \ingroup Node
@@ -30,3 +32,5 @@ class BlurNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_BokehBlurNode.cc b/source/blender/compositor/nodes/COM_BokehBlurNode.cc
index 5096dbef608..1d2a0dae390 100644
--- a/source/blender/compositor/nodes/COM_BokehBlurNode.cc
+++ b/source/blender/compositor/nodes/COM_BokehBlurNode.cc
@@ -25,6 +25,8 @@
#include "DNA_node_types.h"
#include "DNA_object_types.h"
+namespace blender::compositor {
+
BokehBlurNode::BokehBlurNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -75,3 +77,5 @@ void BokehBlurNode::convertToOperations(NodeConverter &converter,
}
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_BokehBlurNode.h b/source/blender/compositor/nodes/COM_BokehBlurNode.h
index 3d73fc50dbc..2c060936025 100644
--- a/source/blender/compositor/nodes/COM_BokehBlurNode.h
+++ b/source/blender/compositor/nodes/COM_BokehBlurNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief BokehBlurNode
* \ingroup Node
@@ -30,3 +32,5 @@ class BokehBlurNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_BokehImageNode.cc b/source/blender/compositor/nodes/COM_BokehImageNode.cc
index 87fe4979c1d..2b0a47c76bc 100644
--- a/source/blender/compositor/nodes/COM_BokehImageNode.cc
+++ b/source/blender/compositor/nodes/COM_BokehImageNode.cc
@@ -20,6 +20,8 @@
#include "COM_BokehImageOperation.h"
#include "COM_ExecutionSystem.h"
+namespace blender::compositor {
+
BokehImageNode::BokehImageNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -36,3 +38,5 @@ void BokehImageNode::convertToOperations(NodeConverter &converter,
converter.addPreview(operation->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_BokehImageNode.h b/source/blender/compositor/nodes/COM_BokehImageNode.h
index 0adb671ddc3..323561a7e4f 100644
--- a/source/blender/compositor/nodes/COM_BokehImageNode.h
+++ b/source/blender/compositor/nodes/COM_BokehImageNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief BokehImageNode
* \ingroup Node
@@ -30,3 +32,5 @@ class BokehImageNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_BoxMaskNode.cc b/source/blender/compositor/nodes/COM_BoxMaskNode.cc
index fe59bd32939..14f42cc42f7 100644
--- a/source/blender/compositor/nodes/COM_BoxMaskNode.cc
+++ b/source/blender/compositor/nodes/COM_BoxMaskNode.cc
@@ -23,6 +23,8 @@
#include "COM_ScaleOperation.h"
#include "COM_SetValueOperation.h"
+namespace blender::compositor {
+
BoxMaskNode::BoxMaskNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -60,7 +62,7 @@ void BoxMaskNode::convertToOperations(NodeConverter &converter,
scaleOperation->setOffset(0.0f, 0.0f);
scaleOperation->setNewWidth(rd->xsch * render_size_factor);
scaleOperation->setNewHeight(rd->ysch * render_size_factor);
- scaleOperation->getInputSocket(0)->setResizeMode(COM_SC_NO_RESIZE);
+ scaleOperation->getInputSocket(0)->setResizeMode(ResizeMode::None);
converter.addOperation(scaleOperation);
converter.addLink(valueOperation->getOutputSocket(0), scaleOperation->getInputSocket(0));
@@ -70,3 +72,5 @@ void BoxMaskNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_BoxMaskNode.h b/source/blender/compositor/nodes/COM_BoxMaskNode.h
index 572228d2684..46cedf7af75 100644
--- a/source/blender/compositor/nodes/COM_BoxMaskNode.h
+++ b/source/blender/compositor/nodes/COM_BoxMaskNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief BoxMaskNode
* \ingroup Node
@@ -30,3 +32,5 @@ class BoxMaskNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_BrightnessNode.cc b/source/blender/compositor/nodes/COM_BrightnessNode.cc
index fcd2a6de1f4..b64f1fea99f 100644
--- a/source/blender/compositor/nodes/COM_BrightnessNode.cc
+++ b/source/blender/compositor/nodes/COM_BrightnessNode.cc
@@ -20,6 +20,8 @@
#include "COM_BrightnessOperation.h"
#include "COM_ExecutionSystem.h"
+namespace blender::compositor {
+
BrightnessNode::BrightnessNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -38,3 +40,5 @@ void BrightnessNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(getInputSocket(2), operation->getInputSocket(2));
converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_BrightnessNode.h b/source/blender/compositor/nodes/COM_BrightnessNode.h
index 4ae50e23798..1084108b1c3 100644
--- a/source/blender/compositor/nodes/COM_BrightnessNode.h
+++ b/source/blender/compositor/nodes/COM_BrightnessNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief BrightnessNode
* \ingroup Node
@@ -30,3 +32,5 @@ class BrightnessNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ChannelMatteNode.cc b/source/blender/compositor/nodes/COM_ChannelMatteNode.cc
index 598cd7b7745..d0f72274aea 100644
--- a/source/blender/compositor/nodes/COM_ChannelMatteNode.cc
+++ b/source/blender/compositor/nodes/COM_ChannelMatteNode.cc
@@ -22,6 +22,8 @@
#include "COM_ConvertOperation.h"
#include "COM_SetAlphaMultiplyOperation.h"
+namespace blender::compositor {
+
ChannelMatteNode::ChannelMatteNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -93,3 +95,5 @@ void ChannelMatteNode::convertToOperations(NodeConverter &converter,
converter.addPreview(operationAlpha->getOutputSocket());
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ChannelMatteNode.h b/source/blender/compositor/nodes/COM_ChannelMatteNode.h
index f282ae81afc..46100b3f7ea 100644
--- a/source/blender/compositor/nodes/COM_ChannelMatteNode.h
+++ b/source/blender/compositor/nodes/COM_ChannelMatteNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief ChannelMatteNode
* \ingroup Node
@@ -30,3 +32,5 @@ class ChannelMatteNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ChromaMatteNode.cc b/source/blender/compositor/nodes/COM_ChromaMatteNode.cc
index 83e88b35f92..9abf183a843 100644
--- a/source/blender/compositor/nodes/COM_ChromaMatteNode.cc
+++ b/source/blender/compositor/nodes/COM_ChromaMatteNode.cc
@@ -22,6 +22,8 @@
#include "COM_ConvertOperation.h"
#include "COM_SetAlphaMultiplyOperation.h"
+namespace blender::compositor {
+
ChromaMatteNode::ChromaMatteNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -63,3 +65,5 @@ void ChromaMatteNode::convertToOperations(NodeConverter &converter,
converter.addPreview(operationAlpha->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ChromaMatteNode.h b/source/blender/compositor/nodes/COM_ChromaMatteNode.h
index 196c69bfe9d..f3ddd013fa4 100644
--- a/source/blender/compositor/nodes/COM_ChromaMatteNode.h
+++ b/source/blender/compositor/nodes/COM_ChromaMatteNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief ChromaMatteNode
* \ingroup Node
@@ -30,3 +32,5 @@ class ChromaMatteNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ColorBalanceNode.cc b/source/blender/compositor/nodes/COM_ColorBalanceNode.cc
index 596d9631297..03e4e143061 100644
--- a/source/blender/compositor/nodes/COM_ColorBalanceNode.cc
+++ b/source/blender/compositor/nodes/COM_ColorBalanceNode.cc
@@ -23,6 +23,8 @@
#include "COM_ExecutionSystem.h"
#include "COM_MixOperation.h"
+namespace blender::compositor {
+
ColorBalanceNode::ColorBalanceNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -71,3 +73,5 @@ void ColorBalanceNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(inputImageSocket, operation->getInputSocket(1));
converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ColorBalanceNode.h b/source/blender/compositor/nodes/COM_ColorBalanceNode.h
index 0391fbe25c3..243713b4912 100644
--- a/source/blender/compositor/nodes/COM_ColorBalanceNode.h
+++ b/source/blender/compositor/nodes/COM_ColorBalanceNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief ColorBalanceNode
* \ingroup Node
@@ -30,3 +32,5 @@ class ColorBalanceNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ColorCorrectionNode.cc b/source/blender/compositor/nodes/COM_ColorCorrectionNode.cc
index 92b334fddb9..6397b1d8e4b 100644
--- a/source/blender/compositor/nodes/COM_ColorCorrectionNode.cc
+++ b/source/blender/compositor/nodes/COM_ColorCorrectionNode.cc
@@ -20,6 +20,8 @@
#include "COM_ColorCorrectionOperation.h"
#include "COM_ExecutionSystem.h"
+namespace blender::compositor {
+
ColorCorrectionNode::ColorCorrectionNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -41,3 +43,5 @@ void ColorCorrectionNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1));
converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ColorCorrectionNode.h b/source/blender/compositor/nodes/COM_ColorCorrectionNode.h
index 35878dde32f..aee07ee07a3 100644
--- a/source/blender/compositor/nodes/COM_ColorCorrectionNode.h
+++ b/source/blender/compositor/nodes/COM_ColorCorrectionNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief ColorCorrectionNode
* \ingroup Node
@@ -30,3 +32,5 @@ class ColorCorrectionNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ColorCurveNode.cc b/source/blender/compositor/nodes/COM_ColorCurveNode.cc
index e1888f3f0bc..774dd689a46 100644
--- a/source/blender/compositor/nodes/COM_ColorCurveNode.cc
+++ b/source/blender/compositor/nodes/COM_ColorCurveNode.cc
@@ -20,6 +20,8 @@
#include "COM_ColorCurveOperation.h"
#include "COM_ExecutionSystem.h"
+namespace blender::compositor {
+
ColorCurveNode::ColorCurveNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -55,3 +57,5 @@ void ColorCurveNode::convertToOperations(NodeConverter &converter,
converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket());
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ColorCurveNode.h b/source/blender/compositor/nodes/COM_ColorCurveNode.h
index 18806c00264..89786b47cf5 100644
--- a/source/blender/compositor/nodes/COM_ColorCurveNode.h
+++ b/source/blender/compositor/nodes/COM_ColorCurveNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief ColorCurveNode
* \ingroup Node
@@ -30,3 +32,5 @@ class ColorCurveNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ColorExposureNode.cc b/source/blender/compositor/nodes/COM_ColorExposureNode.cc
index cd0285ac373..a8f164e6b66 100644
--- a/source/blender/compositor/nodes/COM_ColorExposureNode.cc
+++ b/source/blender/compositor/nodes/COM_ColorExposureNode.cc
@@ -20,6 +20,8 @@
#include "COM_ColorExposureOperation.h"
#include "COM_ExecutionSystem.h"
+namespace blender::compositor {
+
ExposureNode::ExposureNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -35,3 +37,5 @@ void ExposureNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1));
converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ColorExposureNode.h b/source/blender/compositor/nodes/COM_ColorExposureNode.h
index 97fa9c97650..df9bfc65f81 100644
--- a/source/blender/compositor/nodes/COM_ColorExposureNode.h
+++ b/source/blender/compositor/nodes/COM_ColorExposureNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief ExposureNode
* \ingroup Node
@@ -30,3 +32,5 @@ class ExposureNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ColorMatteNode.cc b/source/blender/compositor/nodes/COM_ColorMatteNode.cc
index 865eee5427f..eadb8ce4f96 100644
--- a/source/blender/compositor/nodes/COM_ColorMatteNode.cc
+++ b/source/blender/compositor/nodes/COM_ColorMatteNode.cc
@@ -22,6 +22,8 @@
#include "COM_ConvertOperation.h"
#include "COM_SetAlphaMultiplyOperation.h"
+namespace blender::compositor {
+
ColorMatteNode::ColorMatteNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -61,3 +63,5 @@ void ColorMatteNode::convertToOperations(NodeConverter &converter,
converter.addPreview(operationAlpha->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ColorMatteNode.h b/source/blender/compositor/nodes/COM_ColorMatteNode.h
index 3f47d707d38..9d70b6d8416 100644
--- a/source/blender/compositor/nodes/COM_ColorMatteNode.h
+++ b/source/blender/compositor/nodes/COM_ColorMatteNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief ColorMatteNode
* \ingroup Node
@@ -30,3 +32,5 @@ class ColorMatteNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ColorNode.cc b/source/blender/compositor/nodes/COM_ColorNode.cc
index e6f8bfa01fe..f8277645a4b 100644
--- a/source/blender/compositor/nodes/COM_ColorNode.cc
+++ b/source/blender/compositor/nodes/COM_ColorNode.cc
@@ -20,6 +20,8 @@
#include "COM_ExecutionSystem.h"
#include "COM_SetColorOperation.h"
+namespace blender::compositor {
+
ColorNode::ColorNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -37,3 +39,5 @@ void ColorNode::convertToOperations(NodeConverter &converter,
converter.mapOutputSocket(output, operation->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ColorNode.h b/source/blender/compositor/nodes/COM_ColorNode.h
index 5df60dfbe1c..ae3bf575bb4 100644
--- a/source/blender/compositor/nodes/COM_ColorNode.h
+++ b/source/blender/compositor/nodes/COM_ColorNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief ColorNode
* \ingroup Node
@@ -30,3 +32,5 @@ class ColorNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ColorRampNode.cc b/source/blender/compositor/nodes/COM_ColorRampNode.cc
index 1504a76cee7..6b44ef3057e 100644
--- a/source/blender/compositor/nodes/COM_ColorRampNode.cc
+++ b/source/blender/compositor/nodes/COM_ColorRampNode.cc
@@ -23,6 +23,8 @@
#include "COM_ExecutionSystem.h"
#include "DNA_texture_types.h"
+namespace blender::compositor {
+
ColorRampNode::ColorRampNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -50,3 +52,5 @@ void ColorRampNode::convertToOperations(NodeConverter &converter,
converter.addLink(operation->getOutputSocket(), operation2->getInputSocket(0));
converter.mapOutputSocket(outputSocketAlpha, operation2->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ColorRampNode.h b/source/blender/compositor/nodes/COM_ColorRampNode.h
index 81c3a2f78dc..d0c0e43d56c 100644
--- a/source/blender/compositor/nodes/COM_ColorRampNode.h
+++ b/source/blender/compositor/nodes/COM_ColorRampNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief ColorRampNode
* \ingroup Node
@@ -30,3 +32,5 @@ class ColorRampNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ColorSpillNode.cc b/source/blender/compositor/nodes/COM_ColorSpillNode.cc
index d1a3099e998..119cff93e84 100644
--- a/source/blender/compositor/nodes/COM_ColorSpillNode.cc
+++ b/source/blender/compositor/nodes/COM_ColorSpillNode.cc
@@ -20,6 +20,8 @@
#include "BKE_node.h"
#include "COM_ColorSpillOperation.h"
+namespace blender::compositor {
+
ColorSpillNode::ColorSpillNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -45,3 +47,5 @@ void ColorSpillNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(inputSocketFac, operation->getInputSocket(1));
converter.mapOutputSocket(outputSocketImage, operation->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ColorSpillNode.h b/source/blender/compositor/nodes/COM_ColorSpillNode.h
index 3026f335ba6..731a76e8811 100644
--- a/source/blender/compositor/nodes/COM_ColorSpillNode.h
+++ b/source/blender/compositor/nodes/COM_ColorSpillNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief ColorSpillNode
* \ingroup Node
@@ -30,3 +32,5 @@ class ColorSpillNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ColorToBWNode.cc b/source/blender/compositor/nodes/COM_ColorToBWNode.cc
index 4115bad5d3f..dcedfc19e4d 100644
--- a/source/blender/compositor/nodes/COM_ColorToBWNode.cc
+++ b/source/blender/compositor/nodes/COM_ColorToBWNode.cc
@@ -21,6 +21,8 @@
#include "COM_ConvertOperation.h"
#include "COM_ExecutionSystem.h"
+namespace blender::compositor {
+
ColorToBWNode::ColorToBWNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -38,3 +40,5 @@ void ColorToBWNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(colorSocket, convertProg->getInputSocket(0));
converter.mapOutputSocket(valueSocket, convertProg->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ColorToBWNode.h b/source/blender/compositor/nodes/COM_ColorToBWNode.h
index 475563f0830..60c08a3c886 100644
--- a/source/blender/compositor/nodes/COM_ColorToBWNode.h
+++ b/source/blender/compositor/nodes/COM_ColorToBWNode.h
@@ -20,6 +20,9 @@
#include "COM_Node.h"
#include "DNA_node_types.h"
+
+namespace blender::compositor {
+
/**
* \brief ColorToBWNode
* \ingroup Node
@@ -30,3 +33,5 @@ class ColorToBWNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_CombineColorNode.cc b/source/blender/compositor/nodes/COM_CombineColorNode.cc
index 12968f06a10..8a2bbba1c1e 100644
--- a/source/blender/compositor/nodes/COM_CombineColorNode.cc
+++ b/source/blender/compositor/nodes/COM_CombineColorNode.cc
@@ -20,6 +20,8 @@
#include "COM_ConvertOperation.h"
+namespace blender::compositor {
+
CombineColorNode::CombineColorNode(bNode *editorNode) : Node(editorNode)
{
}
@@ -87,3 +89,5 @@ NodeOperation *CombineYUVANode::getColorConverter(const CompositorContext & /*co
{
return new ConvertYUVToRGBOperation();
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_CombineColorNode.h b/source/blender/compositor/nodes/COM_CombineColorNode.h
index e6a8f5acfb7..29d3fa37817 100644
--- a/source/blender/compositor/nodes/COM_CombineColorNode.h
+++ b/source/blender/compositor/nodes/COM_CombineColorNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
class CombineColorNode : public Node {
public:
CombineColorNode(bNode *editorNode);
@@ -65,3 +67,5 @@ class CombineYUVANode : public CombineColorNode {
NodeOperation *getColorConverter(const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_CompositorNode.cc b/source/blender/compositor/nodes/COM_CompositorNode.cc
index 32ac1fccec9..262fa550915 100644
--- a/source/blender/compositor/nodes/COM_CompositorNode.cc
+++ b/source/blender/compositor/nodes/COM_CompositorNode.cc
@@ -20,6 +20,8 @@
#include "COM_CompositorOperation.h"
#include "COM_ExecutionSystem.h"
+namespace blender::compositor {
+
CompositorNode::CompositorNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -59,3 +61,5 @@ void CompositorNode::convertToOperations(NodeConverter &converter,
converter.addNodeInputPreview(imageSocket);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_CompositorNode.h b/source/blender/compositor/nodes/COM_CompositorNode.h
index 488cfe88d24..4da9f9a766f 100644
--- a/source/blender/compositor/nodes/COM_CompositorNode.h
+++ b/source/blender/compositor/nodes/COM_CompositorNode.h
@@ -20,6 +20,9 @@
#include "COM_Node.h"
#include "DNA_node_types.h"
+
+namespace blender::compositor {
+
/**
* \brief CompositorNode
* \ingroup Node
@@ -30,3 +33,5 @@ class CompositorNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ConvertAlphaNode.cc b/source/blender/compositor/nodes/COM_ConvertAlphaNode.cc
index 2921b44c95b..ac4e45357dc 100644
--- a/source/blender/compositor/nodes/COM_ConvertAlphaNode.cc
+++ b/source/blender/compositor/nodes/COM_ConvertAlphaNode.cc
@@ -20,6 +20,8 @@
#include "COM_ConvertOperation.h"
#include "COM_ExecutionSystem.h"
+namespace blender::compositor {
+
void ConvertAlphaNode::convertToOperations(NodeConverter &converter,
const CompositorContext & /*context*/) const
{
@@ -39,3 +41,5 @@ void ConvertAlphaNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0));
converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ConvertAlphaNode.h b/source/blender/compositor/nodes/COM_ConvertAlphaNode.h
index 45be8925ad2..f3d0ef2cd5b 100644
--- a/source/blender/compositor/nodes/COM_ConvertAlphaNode.h
+++ b/source/blender/compositor/nodes/COM_ConvertAlphaNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief ConvertAlphaNode
* \ingroup Node
@@ -32,3 +34,5 @@ class ConvertAlphaNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_CornerPinNode.cc b/source/blender/compositor/nodes/COM_CornerPinNode.cc
index efe847bbfbf..6a120cffe0a 100644
--- a/source/blender/compositor/nodes/COM_CornerPinNode.cc
+++ b/source/blender/compositor/nodes/COM_CornerPinNode.cc
@@ -20,6 +20,8 @@
#include "COM_PlaneCornerPinOperation.h"
+namespace blender::compositor {
+
CornerPinNode::CornerPinNode(bNode *editorNode) : Node(editorNode)
{
}
@@ -53,3 +55,5 @@ void CornerPinNode::convertToOperations(NodeConverter &converter,
converter.mapOutputSocket(output_warped_image, warp_image_operation->getOutputSocket());
converter.mapOutputSocket(output_plane, plane_mask_operation->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_CornerPinNode.h b/source/blender/compositor/nodes/COM_CornerPinNode.h
index 5cbedcb7057..779e057ebb5 100644
--- a/source/blender/compositor/nodes/COM_CornerPinNode.h
+++ b/source/blender/compositor/nodes/COM_CornerPinNode.h
@@ -21,6 +21,8 @@
#include "DNA_node_types.h"
+namespace blender::compositor {
+
/**
* \brief CornerPinNode
* \ingroup Node
@@ -31,3 +33,5 @@ class CornerPinNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_CropNode.cc b/source/blender/compositor/nodes/COM_CropNode.cc
index 0f0883b0151..3f01062c789 100644
--- a/source/blender/compositor/nodes/COM_CropNode.cc
+++ b/source/blender/compositor/nodes/COM_CropNode.cc
@@ -19,6 +19,8 @@
#include "COM_CropNode.h"
#include "COM_CropOperation.h"
+namespace blender::compositor {
+
CropNode::CropNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -45,3 +47,5 @@ void CropNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0));
converter.mapOutputSocket(getOutputSocket(), operation->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_CropNode.h b/source/blender/compositor/nodes/COM_CropNode.h
index 54ada91a5cc..be3c9a268f9 100644
--- a/source/blender/compositor/nodes/COM_CropNode.h
+++ b/source/blender/compositor/nodes/COM_CropNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief CropNode
* \ingroup Node
@@ -30,3 +32,5 @@ class CropNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_CryptomatteNode.cc b/source/blender/compositor/nodes/COM_CryptomatteNode.cc
index 591db8a96e5..4032a655633 100644
--- a/source/blender/compositor/nodes/COM_CryptomatteNode.cc
+++ b/source/blender/compositor/nodes/COM_CryptomatteNode.cc
@@ -31,6 +31,8 @@
#include <iterator>
#include <string>
+namespace blender::compositor {
+
/** \name Cryptomatte base
* \{ */
@@ -75,10 +77,10 @@ void CryptomatteBaseNode::convertToOperations(NodeConverter &converter,
/** \name Cryptomatte V2
* \{ */
-static std::string prefix_from_node(const bNode &node)
+static std::string prefix_from_node(const CompositorContext &context, const bNode &node)
{
char prefix[MAX_NAME];
- ntreeCompositCryptomatteLayerPrefix(&node, prefix, sizeof(prefix));
+ ntreeCompositCryptomatteLayerPrefix(context.getScene(), &node, prefix, sizeof(prefix));
return std::string(prefix, BLI_strnlen(prefix, sizeof(prefix)));
}
@@ -101,7 +103,7 @@ static std::string combined_layer_pass_name(RenderLayer *render_layer, RenderPas
void CryptomatteNode::input_operations_from_render_source(
const CompositorContext &context,
const bNode &node,
- blender::Vector<NodeOperation *> &r_input_operations)
+ Vector<NodeOperation *> &r_input_operations)
{
Scene *scene = (Scene *)node.id;
if (!scene) {
@@ -116,9 +118,9 @@ void CryptomatteNode::input_operations_from_render_source(
return;
}
- const short cryptomatte_layer_id = 0;
- const std::string prefix = prefix_from_node(node);
- LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) {
+ short view_layer_id = 0;
+ const std::string prefix = prefix_from_node(context, node);
+ LISTBASE_FOREACH_INDEX (ViewLayer *, view_layer, &scene->view_layers, view_layer_id) {
RenderLayer *render_layer = RE_GetRenderLayer(render_result, view_layer->name);
if (render_layer) {
LISTBASE_FOREACH (RenderPass *, render_pass, &render_layer->passes) {
@@ -127,7 +129,7 @@ void CryptomatteNode::input_operations_from_render_source(
RenderLayersProg *op = new RenderLayersProg(
render_pass->name, DataType::Color, render_pass->channels);
op->setScene(scene);
- op->setLayerId(cryptomatte_layer_id);
+ op->setLayerId(view_layer_id);
op->setRenderData(context.getRenderData());
op->setViewName(context.getViewName());
r_input_operations.append(op);
@@ -141,7 +143,7 @@ void CryptomatteNode::input_operations_from_render_source(
void CryptomatteNode::input_operations_from_image_source(
const CompositorContext &context,
const bNode &node,
- blender::Vector<NodeOperation *> &r_input_operations)
+ Vector<NodeOperation *> &r_input_operations)
{
NodeCryptomatte *cryptomatte_settings = (NodeCryptomatte *)node.storage;
Image *image = (Image *)node.id;
@@ -175,8 +177,13 @@ void CryptomatteNode::input_operations_from_image_source(
}
}
- const std::string prefix = prefix_from_node(node);
- LISTBASE_FOREACH (RenderLayer *, render_layer, &image->rr->layers) {
+ const std::string prefix = prefix_from_node(context, node);
+ int layer_index;
+ LISTBASE_FOREACH_INDEX (RenderLayer *, render_layer, &image->rr->layers, layer_index) {
+ if (!blender::StringRef(prefix).startswith(blender::StringRef(
+ render_layer->name, BLI_strnlen(render_layer->name, sizeof(render_layer->name))))) {
+ continue;
+ }
LISTBASE_FOREACH (RenderPass *, render_pass, &render_layer->passes) {
const std::string combined_name = combined_layer_pass_name(render_layer, render_pass);
if (blender::StringRef(combined_name).startswith(prefix)) {
@@ -184,19 +191,21 @@ void CryptomatteNode::input_operations_from_image_source(
render_layer, render_pass, view);
op->setImage(image);
op->setImageUser(iuser);
+ iuser->layer = layer_index;
op->setFramenumber(context.getFramenumber());
r_input_operations.append(op);
}
}
+ break;
}
}
BKE_image_release_ibuf(image, ibuf, nullptr);
}
-blender::Vector<NodeOperation *> CryptomatteNode::create_input_operations(
- const CompositorContext &context, const bNode &node)
+Vector<NodeOperation *> CryptomatteNode::create_input_operations(const CompositorContext &context,
+ const bNode &node)
{
- blender::Vector<NodeOperation *> input_operations;
+ Vector<NodeOperation *> input_operations;
switch (node.custom1) {
case CMP_CRYPTOMATTE_SRC_RENDER:
input_operations_from_render_source(context, node, input_operations);
@@ -222,7 +231,7 @@ CryptomatteOperation *CryptomatteNode::create_cryptomatte_operation(
const bNode &node,
const NodeCryptomatte *cryptomatte_settings) const
{
- blender::Vector<NodeOperation *> input_operations = create_input_operations(context, node);
+ Vector<NodeOperation *> input_operations = create_input_operations(context, node);
CryptomatteOperation *operation = new CryptomatteOperation(input_operations.size());
LISTBASE_FOREACH (CryptomatteEntry *, cryptomatte_entry, &cryptomatte_settings->entries) {
operation->addObjectIndex(cryptomatte_entry->encoded_hash);
@@ -245,7 +254,7 @@ CryptomatteOperation *CryptomatteLegacyNode::create_cryptomatte_operation(
const bNode &UNUSED(node),
const NodeCryptomatte *cryptomatte_settings) const
{
- const int num_inputs = getNumberOfInputSockets() - 1;
+ const int num_inputs = inputs.size() - 1;
CryptomatteOperation *operation = new CryptomatteOperation(num_inputs);
if (cryptomatte_settings) {
LISTBASE_FOREACH (CryptomatteEntry *, cryptomatte_entry, &cryptomatte_settings->entries) {
@@ -261,3 +270,5 @@ CryptomatteOperation *CryptomatteLegacyNode::create_cryptomatte_operation(
}
/* \} */
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_CryptomatteNode.h b/source/blender/compositor/nodes/COM_CryptomatteNode.h
index 0ea05eb50f7..eacb49e2033 100644
--- a/source/blender/compositor/nodes/COM_CryptomatteNode.h
+++ b/source/blender/compositor/nodes/COM_CryptomatteNode.h
@@ -24,6 +24,8 @@
#include "COM_CryptomatteOperation.h"
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief CryptomatteNode
* \ingroup Node
@@ -62,16 +64,14 @@ class CryptomatteNode : public CryptomatteBaseNode {
const NodeCryptomatte *cryptomatte_settings) const override;
private:
- static blender::Vector<NodeOperation *> create_input_operations(const CompositorContext &context,
- const bNode &node);
- static void input_operations_from_render_source(
- const CompositorContext &context,
- const bNode &node,
- blender::Vector<NodeOperation *> &r_input_operations);
- static void input_operations_from_image_source(
- const CompositorContext &context,
- const bNode &node,
- blender::Vector<NodeOperation *> &r_input_operations);
+ static Vector<NodeOperation *> create_input_operations(const CompositorContext &context,
+ const bNode &node);
+ static void input_operations_from_render_source(const CompositorContext &context,
+ const bNode &node,
+ Vector<NodeOperation *> &r_input_operations);
+ static void input_operations_from_image_source(const CompositorContext &context,
+ const bNode &node,
+ Vector<NodeOperation *> &r_input_operations);
};
class CryptomatteLegacyNode : public CryptomatteBaseNode {
@@ -88,3 +88,5 @@ class CryptomatteLegacyNode : public CryptomatteBaseNode {
const bNode &node,
const NodeCryptomatte *cryptomatte_settings) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_DefocusNode.cc b/source/blender/compositor/nodes/COM_DefocusNode.cc
index 2343b14f68d..2023e4f7118 100644
--- a/source/blender/compositor/nodes/COM_DefocusNode.cc
+++ b/source/blender/compositor/nodes/COM_DefocusNode.cc
@@ -30,6 +30,8 @@
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
+namespace blender::compositor {
+
DefocusNode::DefocusNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -110,7 +112,7 @@ void DefocusNode::convertToOperations(NodeConverter &converter,
VariableSizeBokehBlurOperation *operation = new VariableSizeBokehBlurOperation();
if (data->preview) {
- operation->setQuality(CompositorQuality::Low);
+ operation->setQuality(eCompositorQuality::Low);
}
else {
operation->setQuality(context.getQuality());
@@ -141,3 +143,5 @@ void DefocusNode::convertToOperations(NodeConverter &converter,
converter.mapOutputSocket(getOutputSocket(), operation->getOutputSocket());
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_DefocusNode.h b/source/blender/compositor/nodes/COM_DefocusNode.h
index 447950c66dd..5e51a0ccd52 100644
--- a/source/blender/compositor/nodes/COM_DefocusNode.h
+++ b/source/blender/compositor/nodes/COM_DefocusNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief DefocusNode
* \ingroup Node
@@ -30,3 +32,5 @@ class DefocusNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_DenoiseNode.cc b/source/blender/compositor/nodes/COM_DenoiseNode.cc
index 1aae81e1e7b..e58a9c7ba9a 100644
--- a/source/blender/compositor/nodes/COM_DenoiseNode.cc
+++ b/source/blender/compositor/nodes/COM_DenoiseNode.cc
@@ -21,6 +21,8 @@
#include "COM_SetValueOperation.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
DenoiseNode::DenoiseNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -41,3 +43,5 @@ void DenoiseNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(getInputSocket(2), operation->getInputSocket(2));
converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_DenoiseNode.h b/source/blender/compositor/nodes/COM_DenoiseNode.h
index eb0857fe514..91be8e3e3ad 100644
--- a/source/blender/compositor/nodes/COM_DenoiseNode.h
+++ b/source/blender/compositor/nodes/COM_DenoiseNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief DenoiseNode
* \ingroup Node
@@ -30,3 +32,5 @@ class DenoiseNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_DespeckleNode.cc b/source/blender/compositor/nodes/COM_DespeckleNode.cc
index 58734917831..beda479025d 100644
--- a/source/blender/compositor/nodes/COM_DespeckleNode.cc
+++ b/source/blender/compositor/nodes/COM_DespeckleNode.cc
@@ -22,6 +22,8 @@
#include "COM_ExecutionSystem.h"
#include "DNA_scene_types.h"
+namespace blender::compositor {
+
DespeckleNode::DespeckleNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -46,3 +48,5 @@ void DespeckleNode::convertToOperations(NodeConverter &converter,
converter.addPreview(operation->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_DespeckleNode.h b/source/blender/compositor/nodes/COM_DespeckleNode.h
index 47879bc3d3c..2f268e99e1b 100644
--- a/source/blender/compositor/nodes/COM_DespeckleNode.h
+++ b/source/blender/compositor/nodes/COM_DespeckleNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief DespeckleNode
* \ingroup Node
@@ -30,3 +32,5 @@ class DespeckleNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_DifferenceMatteNode.cc b/source/blender/compositor/nodes/COM_DifferenceMatteNode.cc
index 3d538e9b4a0..8c989bfc04e 100644
--- a/source/blender/compositor/nodes/COM_DifferenceMatteNode.cc
+++ b/source/blender/compositor/nodes/COM_DifferenceMatteNode.cc
@@ -21,6 +21,8 @@
#include "COM_DifferenceMatteOperation.h"
#include "COM_SetAlphaMultiplyOperation.h"
+namespace blender::compositor {
+
DifferenceMatteNode::DifferenceMatteNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -52,3 +54,5 @@ void DifferenceMatteNode::convertToOperations(NodeConverter &converter,
converter.addPreview(operation->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_DifferenceMatteNode.h b/source/blender/compositor/nodes/COM_DifferenceMatteNode.h
index db149a02c07..a173c723192 100644
--- a/source/blender/compositor/nodes/COM_DifferenceMatteNode.h
+++ b/source/blender/compositor/nodes/COM_DifferenceMatteNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief DifferenceMatteNode
* \ingroup Node
@@ -30,3 +32,5 @@ class DifferenceMatteNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_DilateErodeNode.cc b/source/blender/compositor/nodes/COM_DilateErodeNode.cc
index e90707618e5..1867014f64b 100644
--- a/source/blender/compositor/nodes/COM_DilateErodeNode.cc
+++ b/source/blender/compositor/nodes/COM_DilateErodeNode.cc
@@ -24,6 +24,8 @@
#include "COM_GaussianAlphaXBlurOperation.h"
#include "COM_GaussianAlphaYBlurOperation.h"
+namespace blender::compositor {
+
DilateErodeNode::DilateErodeNode(bNode *editorNode) : Node(editorNode)
{
/* initialize node data */
@@ -83,7 +85,7 @@ void DilateErodeNode::convertToOperations(NodeConverter &converter,
}
else if (editorNode->custom1 == CMP_NODE_DILATEERODE_DISTANCE_FEATHER) {
/* this uses a modified gaussian blur function otherwise its far too slow */
- CompositorQuality quality = context.getQuality();
+ eCompositorQuality quality = context.getQuality();
GaussianAlphaXBlurOperation *operationx = new GaussianAlphaXBlurOperation();
operationx->setData(&m_alpha_blur);
@@ -147,3 +149,5 @@ void DilateErodeNode::convertToOperations(NodeConverter &converter,
}
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_DilateErodeNode.h b/source/blender/compositor/nodes/COM_DilateErodeNode.h
index 78cc2d21a7a..7684d7e3834 100644
--- a/source/blender/compositor/nodes/COM_DilateErodeNode.h
+++ b/source/blender/compositor/nodes/COM_DilateErodeNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief DilateErodeNode
* \ingroup Node
@@ -33,3 +35,5 @@ class DilateErodeNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_DirectionalBlurNode.cc b/source/blender/compositor/nodes/COM_DirectionalBlurNode.cc
index f8d0eaf4675..90c4236bce8 100644
--- a/source/blender/compositor/nodes/COM_DirectionalBlurNode.cc
+++ b/source/blender/compositor/nodes/COM_DirectionalBlurNode.cc
@@ -21,6 +21,8 @@
#include "COM_ExecutionSystem.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
DirectionalBlurNode::DirectionalBlurNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -38,3 +40,5 @@ void DirectionalBlurNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0));
converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_DirectionalBlurNode.h b/source/blender/compositor/nodes/COM_DirectionalBlurNode.h
index 2f64559e378..ce3ef378aaf 100644
--- a/source/blender/compositor/nodes/COM_DirectionalBlurNode.h
+++ b/source/blender/compositor/nodes/COM_DirectionalBlurNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief DirectionalBlurNode
* \ingroup Node
@@ -30,3 +32,5 @@ class DirectionalBlurNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_DisplaceNode.cc b/source/blender/compositor/nodes/COM_DisplaceNode.cc
index 5b63bc1f393..f2ec750c595 100644
--- a/source/blender/compositor/nodes/COM_DisplaceNode.cc
+++ b/source/blender/compositor/nodes/COM_DisplaceNode.cc
@@ -21,6 +21,8 @@
#include "COM_DisplaceSimpleOperation.h"
#include "COM_ExecutionSystem.h"
+namespace blender::compositor {
+
DisplaceNode::DisplaceNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -30,7 +32,7 @@ void DisplaceNode::convertToOperations(NodeConverter &converter,
const CompositorContext &context) const
{
NodeOperation *operation;
- if (context.getQuality() == CompositorQuality::Low) {
+ if (context.getQuality() == eCompositorQuality::Low) {
operation = new DisplaceSimpleOperation();
}
else {
@@ -44,3 +46,5 @@ void DisplaceNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(getInputSocket(3), operation->getInputSocket(3));
converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_DisplaceNode.h b/source/blender/compositor/nodes/COM_DisplaceNode.h
index 5496d6833c1..b2495839da3 100644
--- a/source/blender/compositor/nodes/COM_DisplaceNode.h
+++ b/source/blender/compositor/nodes/COM_DisplaceNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief DisplaceNode
* \ingroup Node
@@ -30,3 +32,5 @@ class DisplaceNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_DistanceMatteNode.cc b/source/blender/compositor/nodes/COM_DistanceMatteNode.cc
index 37aeb5c8504..4450c4a2f4a 100644
--- a/source/blender/compositor/nodes/COM_DistanceMatteNode.cc
+++ b/source/blender/compositor/nodes/COM_DistanceMatteNode.cc
@@ -23,6 +23,8 @@
#include "COM_DistanceYCCMatteOperation.h"
#include "COM_SetAlphaMultiplyOperation.h"
+namespace blender::compositor {
+
DistanceMatteNode::DistanceMatteNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -96,3 +98,5 @@ void DistanceMatteNode::convertToOperations(NodeConverter &converter,
converter.addPreview(operationAlpha->getOutputSocket());
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_DistanceMatteNode.h b/source/blender/compositor/nodes/COM_DistanceMatteNode.h
index c237edc4d2d..0baa531b4d2 100644
--- a/source/blender/compositor/nodes/COM_DistanceMatteNode.h
+++ b/source/blender/compositor/nodes/COM_DistanceMatteNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief DistanceMatteNode
* \ingroup Node
@@ -30,3 +32,5 @@ class DistanceMatteNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_DoubleEdgeMaskNode.cc b/source/blender/compositor/nodes/COM_DoubleEdgeMaskNode.cc
index 907a9f49353..847dcc2f8f1 100644
--- a/source/blender/compositor/nodes/COM_DoubleEdgeMaskNode.cc
+++ b/source/blender/compositor/nodes/COM_DoubleEdgeMaskNode.cc
@@ -20,6 +20,8 @@
#include "COM_DoubleEdgeMaskOperation.h"
#include "COM_ExecutionSystem.h"
+namespace blender::compositor {
+
DoubleEdgeMaskNode::DoubleEdgeMaskNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -40,3 +42,5 @@ void DoubleEdgeMaskNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1));
converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_DoubleEdgeMaskNode.h b/source/blender/compositor/nodes/COM_DoubleEdgeMaskNode.h
index 0244ba2cbe2..90e009747c1 100644
--- a/source/blender/compositor/nodes/COM_DoubleEdgeMaskNode.h
+++ b/source/blender/compositor/nodes/COM_DoubleEdgeMaskNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief DoubleEdgeMaskNode
* \ingroup Node
@@ -30,3 +32,5 @@ class DoubleEdgeMaskNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_EllipseMaskNode.cc b/source/blender/compositor/nodes/COM_EllipseMaskNode.cc
index 1ae855c0f1d..3b4f5ca8c94 100644
--- a/source/blender/compositor/nodes/COM_EllipseMaskNode.cc
+++ b/source/blender/compositor/nodes/COM_EllipseMaskNode.cc
@@ -23,6 +23,8 @@
#include "COM_ScaleOperation.h"
#include "COM_SetValueOperation.h"
+namespace blender::compositor {
+
EllipseMaskNode::EllipseMaskNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -60,7 +62,7 @@ void EllipseMaskNode::convertToOperations(NodeConverter &converter,
scaleOperation->setOffset(0.0f, 0.0f);
scaleOperation->setNewWidth(rd->xsch * render_size_factor);
scaleOperation->setNewHeight(rd->ysch * render_size_factor);
- scaleOperation->getInputSocket(0)->setResizeMode(COM_SC_NO_RESIZE);
+ scaleOperation->getInputSocket(0)->setResizeMode(ResizeMode::None);
converter.addOperation(scaleOperation);
converter.addLink(valueOperation->getOutputSocket(0), scaleOperation->getInputSocket(0));
@@ -70,3 +72,5 @@ void EllipseMaskNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_EllipseMaskNode.h b/source/blender/compositor/nodes/COM_EllipseMaskNode.h
index 87f2c498165..cbe189be9f6 100644
--- a/source/blender/compositor/nodes/COM_EllipseMaskNode.h
+++ b/source/blender/compositor/nodes/COM_EllipseMaskNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief EllipseMaskNode
* \ingroup Node
@@ -30,3 +32,5 @@ class EllipseMaskNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_FilterNode.cc b/source/blender/compositor/nodes/COM_FilterNode.cc
index 1147c11794f..351219155c2 100644
--- a/source/blender/compositor/nodes/COM_FilterNode.cc
+++ b/source/blender/compositor/nodes/COM_FilterNode.cc
@@ -23,6 +23,8 @@
#include "COM_ExecutionSystem.h"
#include "COM_MixOperation.h"
+namespace blender::compositor {
+
FilterNode::FilterNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -94,3 +96,5 @@ void FilterNode::convertToOperations(NodeConverter &converter,
converter.addPreview(operation->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_FilterNode.h b/source/blender/compositor/nodes/COM_FilterNode.h
index c86e65b5807..f7f4176cea5 100644
--- a/source/blender/compositor/nodes/COM_FilterNode.h
+++ b/source/blender/compositor/nodes/COM_FilterNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief FilterNode
* \ingroup Node
@@ -30,3 +32,5 @@ class FilterNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_FlipNode.cc b/source/blender/compositor/nodes/COM_FlipNode.cc
index d89e6b7b844..bca6cd3c4f7 100644
--- a/source/blender/compositor/nodes/COM_FlipNode.cc
+++ b/source/blender/compositor/nodes/COM_FlipNode.cc
@@ -21,6 +21,8 @@
#include "COM_ExecutionSystem.h"
#include "COM_FlipOperation.h"
+namespace blender::compositor {
+
FlipNode::FlipNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -52,3 +54,5 @@ void FlipNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(inputSocket, operation->getInputSocket(0));
converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_FlipNode.h b/source/blender/compositor/nodes/COM_FlipNode.h
index 29fa904c4f6..ee61d09fbba 100644
--- a/source/blender/compositor/nodes/COM_FlipNode.h
+++ b/source/blender/compositor/nodes/COM_FlipNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief FlipNode
* \ingroup Node
@@ -30,3 +32,5 @@ class FlipNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_GammaNode.cc b/source/blender/compositor/nodes/COM_GammaNode.cc
index 1ce17faa0dc..52148a80a8f 100644
--- a/source/blender/compositor/nodes/COM_GammaNode.cc
+++ b/source/blender/compositor/nodes/COM_GammaNode.cc
@@ -20,6 +20,8 @@
#include "COM_ExecutionSystem.h"
#include "COM_GammaOperation.h"
+namespace blender::compositor {
+
GammaNode::GammaNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -35,3 +37,5 @@ void GammaNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1));
converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_GammaNode.h b/source/blender/compositor/nodes/COM_GammaNode.h
index b3bf9b649b9..29c9ed170fa 100644
--- a/source/blender/compositor/nodes/COM_GammaNode.h
+++ b/source/blender/compositor/nodes/COM_GammaNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief GammaNode
* \ingroup Node
@@ -30,3 +32,5 @@ class GammaNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_GlareNode.cc b/source/blender/compositor/nodes/COM_GlareNode.cc
index ef088e42205..0537074552a 100644
--- a/source/blender/compositor/nodes/COM_GlareNode.cc
+++ b/source/blender/compositor/nodes/COM_GlareNode.cc
@@ -27,6 +27,8 @@
#include "COM_SetValueOperation.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
GlareNode::GlareNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -61,11 +63,11 @@ void GlareNode::convertToOperations(NodeConverter &converter,
thresholdOperation->setGlareSettings(glare);
SetValueOperation *mixvalueoperation = new SetValueOperation();
- mixvalueoperation->setValue(0.5f + glare->mix * 0.5f);
+ mixvalueoperation->setValue(glare->mix);
MixGlareOperation *mixoperation = new MixGlareOperation();
mixoperation->setResolutionInputSocketIndex(1);
- mixoperation->getInputSocket(2)->setResizeMode(COM_SC_FIT);
+ mixoperation->getInputSocket(2)->setResizeMode(ResizeMode::FitAny);
converter.addOperation(glareoperation);
converter.addOperation(thresholdOperation);
@@ -80,3 +82,5 @@ void GlareNode::convertToOperations(NodeConverter &converter,
converter.addLink(glareoperation->getOutputSocket(), mixoperation->getInputSocket(2));
converter.mapOutputSocket(getOutputSocket(), mixoperation->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_GlareNode.h b/source/blender/compositor/nodes/COM_GlareNode.h
index dbc02656006..7db5fa85e04 100644
--- a/source/blender/compositor/nodes/COM_GlareNode.h
+++ b/source/blender/compositor/nodes/COM_GlareNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief GlareNode
* \ingroup Node
@@ -30,3 +32,5 @@ class GlareNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.cc b/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.cc
index 00125ba2ea5..5042d217f9a 100644
--- a/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.cc
+++ b/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.cc
@@ -26,6 +26,8 @@
#include "COM_SetValueOperation.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
HueSaturationValueCorrectNode::HueSaturationValueCorrectNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -62,3 +64,5 @@ void HueSaturationValueCorrectNode::convertToOperations(
converter.mapInputSocket(valueSocket, blend->getInputSocket(0));
converter.mapOutputSocket(outputSocket, blend->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.h b/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.h
index e7656d2296c..d75b2ba51ca 100644
--- a/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.h
+++ b/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief HueSaturationValueCorrectNode
* \ingroup Node
@@ -30,3 +32,5 @@ class HueSaturationValueCorrectNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_HueSaturationValueNode.cc b/source/blender/compositor/nodes/COM_HueSaturationValueNode.cc
index dc2e5187e8f..54d2caa75af 100644
--- a/source/blender/compositor/nodes/COM_HueSaturationValueNode.cc
+++ b/source/blender/compositor/nodes/COM_HueSaturationValueNode.cc
@@ -26,6 +26,8 @@
#include "COM_SetValueOperation.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
HueSaturationValueNode::HueSaturationValueNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -65,3 +67,5 @@ void HueSaturationValueNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(facSocket, blend->getInputSocket(0));
converter.mapOutputSocket(outputSocket, blend->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_HueSaturationValueNode.h b/source/blender/compositor/nodes/COM_HueSaturationValueNode.h
index b35f6ae14b7..0b295158cc7 100644
--- a/source/blender/compositor/nodes/COM_HueSaturationValueNode.h
+++ b/source/blender/compositor/nodes/COM_HueSaturationValueNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief HueSaturationValueNode
* \ingroup Node
@@ -30,3 +32,5 @@ class HueSaturationValueNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_IDMaskNode.cc b/source/blender/compositor/nodes/COM_IDMaskNode.cc
index 5ba54d75bcd..9798dabd035 100644
--- a/source/blender/compositor/nodes/COM_IDMaskNode.cc
+++ b/source/blender/compositor/nodes/COM_IDMaskNode.cc
@@ -21,6 +21,8 @@
#include "COM_ExecutionSystem.h"
#include "COM_IDMaskOperation.h"
+namespace blender::compositor {
+
IDMaskNode::IDMaskNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -47,3 +49,5 @@ void IDMaskNode::convertToOperations(NodeConverter &converter,
converter.mapOutputSocket(getOutputSocket(0), antiAliasOperation->getOutputSocket(0));
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_IDMaskNode.h b/source/blender/compositor/nodes/COM_IDMaskNode.h
index 2766377e47a..f702732a8ed 100644
--- a/source/blender/compositor/nodes/COM_IDMaskNode.h
+++ b/source/blender/compositor/nodes/COM_IDMaskNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief IDMaskNode
* \ingroup Node
@@ -30,3 +32,5 @@ class IDMaskNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ImageNode.cc b/source/blender/compositor/nodes/COM_ImageNode.cc
index 711399ccd63..f0bfda0f40e 100644
--- a/source/blender/compositor/nodes/COM_ImageNode.cc
+++ b/source/blender/compositor/nodes/COM_ImageNode.cc
@@ -29,6 +29,8 @@
#include "COM_SetValueOperation.h"
#include "COM_SetVectorOperation.h"
+namespace blender::compositor {
+
ImageNode::ImageNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -77,7 +79,6 @@ void ImageNode::convertToOperations(NodeConverter &converter,
Image *image = (Image *)editorNode->id;
ImageUser *imageuser = (ImageUser *)editorNode->storage;
int framenumber = context.getFramenumber();
- int numberOfOutputs = this->getNumberOfOutputSockets();
bool outputStraightAlpha = (editorNode->custom1 & CMP_NODE_IMAGE_USE_STRAIGHT_OUTPUT) != 0;
BKE_image_user_frame_calc(image, imageuser, context.getFramenumber());
/* force a load, we assume iuser index will be set OK anyway */
@@ -87,14 +88,11 @@ void ImageNode::convertToOperations(NodeConverter &converter,
if (image->rr) {
RenderLayer *rl = (RenderLayer *)BLI_findlink(&image->rr->layers, imageuser->layer);
if (rl) {
- NodeOutput *socket;
- int index;
-
is_multilayer_ok = true;
- for (index = 0; index < numberOfOutputs; index++) {
+ for (int64_t index = 0; index < outputs.size(); index++) {
+ NodeOutput *socket = outputs[index];
NodeOperation *operation = nullptr;
- socket = this->getOutputSocket(index);
bNodeSocket *bnodeSocket = socket->getbNodeSocket();
NodeImageLayer *storage = (NodeImageLayer *)bnodeSocket->storage;
RenderPass *rpass = (RenderPass *)BLI_findstring(
@@ -171,8 +169,7 @@ void ImageNode::convertToOperations(NodeConverter &converter,
converter.addPreview(operation->getOutputSocket());
}
if (STREQ(rpass->name, RE_PASSNAME_COMBINED)) {
- for (int alphaIndex = 0; alphaIndex < numberOfOutputs; alphaIndex++) {
- NodeOutput *alphaSocket = this->getOutputSocket(alphaIndex);
+ for (NodeOutput *alphaSocket : getOutputSockets()) {
bNodeSocket *bnodeAlphaSocket = alphaSocket->getbNodeSocket();
if (!STREQ(bnodeAlphaSocket->name, "Alpha")) {
continue;
@@ -204,12 +201,13 @@ void ImageNode::convertToOperations(NodeConverter &converter,
/* without this, multilayer that fail to load will crash blender T32490. */
if (is_multilayer_ok == false) {
- for (int i = 0; i < getNumberOfOutputSockets(); i++) {
- converter.setInvalidOutput(getOutputSocket(i));
+ for (NodeOutput *output : getOutputSockets()) {
+ converter.setInvalidOutput(output);
}
}
}
else {
+ const int64_t numberOfOutputs = getOutputSockets().size();
if (numberOfOutputs > 0) {
ImageOperation *operation = new ImageOperation();
operation->setImage(image);
@@ -296,4 +294,6 @@ void ImageNode::convertToOperations(NodeConverter &converter,
}
}
}
-}
+} // namespace blender::compositor
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ImageNode.h b/source/blender/compositor/nodes/COM_ImageNode.h
index 716687c91b4..047cc496f83 100644
--- a/source/blender/compositor/nodes/COM_ImageNode.h
+++ b/source/blender/compositor/nodes/COM_ImageNode.h
@@ -26,6 +26,8 @@
#include "RE_engine.h"
#include "RE_pipeline.h"
+namespace blender::compositor {
+
/**
* \brief ImageNode
* \ingroup Node
@@ -47,3 +49,5 @@ class ImageNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_InpaintNode.cc b/source/blender/compositor/nodes/COM_InpaintNode.cc
index 40fe63ec9f3..01ec5523939 100644
--- a/source/blender/compositor/nodes/COM_InpaintNode.cc
+++ b/source/blender/compositor/nodes/COM_InpaintNode.cc
@@ -22,6 +22,8 @@
#include "COM_InpaintOperation.h"
#include "DNA_scene_types.h"
+namespace blender::compositor {
+
InpaintNode::InpaintNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -43,3 +45,5 @@ void InpaintNode::convertToOperations(NodeConverter &converter,
converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0));
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_InpaintNode.h b/source/blender/compositor/nodes/COM_InpaintNode.h
index 864d8bbd5cd..3a10c11bf61 100644
--- a/source/blender/compositor/nodes/COM_InpaintNode.h
+++ b/source/blender/compositor/nodes/COM_InpaintNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief InpaintNode
* \ingroup Node
@@ -30,3 +32,5 @@ class InpaintNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_InvertNode.cc b/source/blender/compositor/nodes/COM_InvertNode.cc
index 913452c42c8..5fe2033227f 100644
--- a/source/blender/compositor/nodes/COM_InvertNode.cc
+++ b/source/blender/compositor/nodes/COM_InvertNode.cc
@@ -21,6 +21,8 @@
#include "COM_ExecutionSystem.h"
#include "COM_InvertOperation.h"
+namespace blender::compositor {
+
InvertNode::InvertNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -39,3 +41,5 @@ void InvertNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1));
converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_InvertNode.h b/source/blender/compositor/nodes/COM_InvertNode.h
index ab5f80a5e87..1cc975b8236 100644
--- a/source/blender/compositor/nodes/COM_InvertNode.h
+++ b/source/blender/compositor/nodes/COM_InvertNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief InvertNode
* \ingroup Node
@@ -30,3 +32,5 @@ class InvertNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_KeyingNode.cc b/source/blender/compositor/nodes/COM_KeyingNode.cc
index 9b493d3f332..0af328a3601 100644
--- a/source/blender/compositor/nodes/COM_KeyingNode.cc
+++ b/source/blender/compositor/nodes/COM_KeyingNode.cc
@@ -37,6 +37,8 @@
#include "COM_GaussianAlphaXBlurOperation.h"
#include "COM_GaussianAlphaYBlurOperation.h"
+namespace blender::compositor {
+
KeyingNode::KeyingNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -142,7 +144,7 @@ NodeOperationOutput *KeyingNode::setupFeather(NodeConverter &converter,
int distance) const
{
/* this uses a modified gaussian blur function otherwise its far too slow */
- CompositorQuality quality = context.getQuality();
+ eCompositorQuality quality = context.getQuality();
/* initialize node data */
NodeBlurData data;
@@ -348,3 +350,5 @@ void KeyingNode::convertToOperations(NodeConverter &converter,
converter.mapOutputSocket(outputEdges, edgesMatte);
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_KeyingNode.h b/source/blender/compositor/nodes/COM_KeyingNode.h
index 64eaaf2a537..6d5e3ca1883 100644
--- a/source/blender/compositor/nodes/COM_KeyingNode.h
+++ b/source/blender/compositor/nodes/COM_KeyingNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief KeyingNode
* \ingroup Node
@@ -58,3 +60,5 @@ class KeyingNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_KeyingScreenNode.cc b/source/blender/compositor/nodes/COM_KeyingScreenNode.cc
index 93a9a071226..cbe4f165a45 100644
--- a/source/blender/compositor/nodes/COM_KeyingScreenNode.cc
+++ b/source/blender/compositor/nodes/COM_KeyingScreenNode.cc
@@ -22,6 +22,8 @@
#include "DNA_movieclip_types.h"
+namespace blender::compositor {
+
KeyingScreenNode::KeyingScreenNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -45,3 +47,5 @@ void KeyingScreenNode::convertToOperations(NodeConverter &converter,
converter.mapOutputSocket(outputScreen, operation->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_KeyingScreenNode.h b/source/blender/compositor/nodes/COM_KeyingScreenNode.h
index 2c84b71773e..f2ad3b344f1 100644
--- a/source/blender/compositor/nodes/COM_KeyingScreenNode.h
+++ b/source/blender/compositor/nodes/COM_KeyingScreenNode.h
@@ -21,6 +21,8 @@
#include "COM_Node.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
/**
* \brief KeyingScreenNode
* \ingroup Node
@@ -31,3 +33,5 @@ class KeyingScreenNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_LensDistortionNode.cc b/source/blender/compositor/nodes/COM_LensDistortionNode.cc
index 34d2fba6433..f5226d31989 100644
--- a/source/blender/compositor/nodes/COM_LensDistortionNode.cc
+++ b/source/blender/compositor/nodes/COM_LensDistortionNode.cc
@@ -21,6 +21,8 @@
#include "COM_ProjectorLensDistortionOperation.h"
#include "COM_ScreenLensDistortionOperation.h"
+namespace blender::compositor {
+
LensDistortionNode::LensDistortionNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -59,3 +61,5 @@ void LensDistortionNode::convertToOperations(NodeConverter &converter,
converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0));
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_LensDistortionNode.h b/source/blender/compositor/nodes/COM_LensDistortionNode.h
index 866e2ec8884..4de1b0fe4da 100644
--- a/source/blender/compositor/nodes/COM_LensDistortionNode.h
+++ b/source/blender/compositor/nodes/COM_LensDistortionNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief LensDistortionNode
* \ingroup Node
@@ -30,3 +32,5 @@ class LensDistortionNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_LuminanceMatteNode.cc b/source/blender/compositor/nodes/COM_LuminanceMatteNode.cc
index 8bfea1eff49..920da53231f 100644
--- a/source/blender/compositor/nodes/COM_LuminanceMatteNode.cc
+++ b/source/blender/compositor/nodes/COM_LuminanceMatteNode.cc
@@ -22,6 +22,8 @@
#include "COM_LuminanceMatteOperation.h"
#include "COM_SetAlphaMultiplyOperation.h"
+namespace blender::compositor {
+
LuminanceMatteNode::LuminanceMatteNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -51,3 +53,5 @@ void LuminanceMatteNode::convertToOperations(NodeConverter &converter,
converter.addPreview(operation->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_LuminanceMatteNode.h b/source/blender/compositor/nodes/COM_LuminanceMatteNode.h
index 148a0d302d4..ef4ebc8ad92 100644
--- a/source/blender/compositor/nodes/COM_LuminanceMatteNode.h
+++ b/source/blender/compositor/nodes/COM_LuminanceMatteNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief LuminanceMatteNode
* \ingroup Node
@@ -30,3 +32,5 @@ class LuminanceMatteNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_MapRangeNode.cc b/source/blender/compositor/nodes/COM_MapRangeNode.cc
index 352bc0dd48d..718a6d9e47b 100644
--- a/source/blender/compositor/nodes/COM_MapRangeNode.cc
+++ b/source/blender/compositor/nodes/COM_MapRangeNode.cc
@@ -21,6 +21,8 @@
#include "COM_ExecutionSystem.h"
#include "COM_MapRangeOperation.h"
+namespace blender::compositor {
+
MapRangeNode::MapRangeNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -47,3 +49,5 @@ void MapRangeNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(destMaxSocket, operation->getInputSocket(4));
converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_MapRangeNode.h b/source/blender/compositor/nodes/COM_MapRangeNode.h
index 55c7020635a..ad6fd78a7d5 100644
--- a/source/blender/compositor/nodes/COM_MapRangeNode.h
+++ b/source/blender/compositor/nodes/COM_MapRangeNode.h
@@ -20,6 +20,9 @@
#include "COM_Node.h"
#include "DNA_node_types.h"
+
+namespace blender::compositor {
+
/**
* \brief MapRangeNode
* \ingroup Node
@@ -30,3 +33,5 @@ class MapRangeNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_MapUVNode.cc b/source/blender/compositor/nodes/COM_MapUVNode.cc
index feb9c75ec56..4b7a9e8af0f 100644
--- a/source/blender/compositor/nodes/COM_MapUVNode.cc
+++ b/source/blender/compositor/nodes/COM_MapUVNode.cc
@@ -20,6 +20,8 @@
#include "COM_ExecutionSystem.h"
#include "COM_MapUVOperation.h"
+namespace blender::compositor {
+
MapUVNode::MapUVNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -39,3 +41,5 @@ void MapUVNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1));
converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_MapUVNode.h b/source/blender/compositor/nodes/COM_MapUVNode.h
index ff565372d71..f7f4db167ea 100644
--- a/source/blender/compositor/nodes/COM_MapUVNode.h
+++ b/source/blender/compositor/nodes/COM_MapUVNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief MapUVNode
* \ingroup Node
@@ -30,3 +32,5 @@ class MapUVNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_MapValueNode.cc b/source/blender/compositor/nodes/COM_MapValueNode.cc
index e07df8ad367..ae48bda6cb8 100644
--- a/source/blender/compositor/nodes/COM_MapValueNode.cc
+++ b/source/blender/compositor/nodes/COM_MapValueNode.cc
@@ -21,6 +21,8 @@
#include "COM_ExecutionSystem.h"
#include "COM_MapValueOperation.h"
+namespace blender::compositor {
+
MapValueNode::MapValueNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -41,3 +43,5 @@ void MapValueNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(colorSocket, convertProg->getInputSocket(0));
converter.mapOutputSocket(valueSocket, convertProg->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_MapValueNode.h b/source/blender/compositor/nodes/COM_MapValueNode.h
index a8a5028a71f..dcac1d6e3c5 100644
--- a/source/blender/compositor/nodes/COM_MapValueNode.h
+++ b/source/blender/compositor/nodes/COM_MapValueNode.h
@@ -20,6 +20,9 @@
#include "COM_Node.h"
#include "DNA_node_types.h"
+
+namespace blender::compositor {
+
/**
* \brief MapValueNode
* \ingroup Node
@@ -30,3 +33,5 @@ class MapValueNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_MaskNode.cc b/source/blender/compositor/nodes/COM_MaskNode.cc
index a6415a3992e..ef171c01653 100644
--- a/source/blender/compositor/nodes/COM_MaskNode.cc
+++ b/source/blender/compositor/nodes/COM_MaskNode.cc
@@ -22,6 +22,8 @@
#include "DNA_mask_types.h"
+namespace blender::compositor {
+
MaskNode::MaskNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -68,3 +70,5 @@ void MaskNode::convertToOperations(NodeConverter &converter,
converter.addOperation(operation);
converter.mapOutputSocket(outputMask, operation->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_MaskNode.h b/source/blender/compositor/nodes/COM_MaskNode.h
index 3d813fb61f2..5890cf5957a 100644
--- a/source/blender/compositor/nodes/COM_MaskNode.h
+++ b/source/blender/compositor/nodes/COM_MaskNode.h
@@ -21,6 +21,8 @@
#include "COM_Node.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
/**
* \brief MaskNode
* \ingroup Node
@@ -31,3 +33,5 @@ class MaskNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_MathNode.cc b/source/blender/compositor/nodes/COM_MathNode.cc
index 0edf880400f..dd0d8931d58 100644
--- a/source/blender/compositor/nodes/COM_MathNode.cc
+++ b/source/blender/compositor/nodes/COM_MathNode.cc
@@ -20,6 +20,8 @@
#include "COM_ExecutionSystem.h"
#include "COM_MathBaseOperation.h"
+namespace blender::compositor {
+
void MathNode::convertToOperations(NodeConverter &converter,
const CompositorContext & /*context*/) const
{
@@ -159,3 +161,5 @@ void MathNode::convertToOperations(NodeConverter &converter,
converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket());
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_MathNode.h b/source/blender/compositor/nodes/COM_MathNode.h
index a7036e9fd36..5db59e62bab 100644
--- a/source/blender/compositor/nodes/COM_MathNode.h
+++ b/source/blender/compositor/nodes/COM_MathNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief MathNode
* \ingroup Node
@@ -32,3 +34,5 @@ class MathNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_MixNode.cc b/source/blender/compositor/nodes/COM_MixNode.cc
index d082590d21b..cfa8d0ee6a6 100644
--- a/source/blender/compositor/nodes/COM_MixNode.cc
+++ b/source/blender/compositor/nodes/COM_MixNode.cc
@@ -24,6 +24,8 @@
#include "COM_SetValueOperation.h"
#include "DNA_material_types.h" /* the ramp types */
+namespace blender::compositor {
+
MixNode::MixNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -110,3 +112,5 @@ void MixNode::convertToOperations(NodeConverter &converter,
converter.addPreview(convertProg->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_MixNode.h b/source/blender/compositor/nodes/COM_MixNode.h
index 6f4a7a2cb88..81f9c41871e 100644
--- a/source/blender/compositor/nodes/COM_MixNode.h
+++ b/source/blender/compositor/nodes/COM_MixNode.h
@@ -20,6 +20,9 @@
#include "COM_Node.h"
#include "DNA_node_types.h"
+
+namespace blender::compositor {
+
/**
* \brief MixNode
* \ingroup Node
@@ -30,3 +33,5 @@ class MixNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_MovieClipNode.cc b/source/blender/compositor/nodes/COM_MovieClipNode.cc
index 7cc8f2ea19c..50bd9b4d71b 100644
--- a/source/blender/compositor/nodes/COM_MovieClipNode.cc
+++ b/source/blender/compositor/nodes/COM_MovieClipNode.cc
@@ -29,6 +29,8 @@
#include "IMB_imbuf.h"
+namespace blender::compositor {
+
MovieClipNode::MovieClipNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -106,3 +108,5 @@ void MovieClipNode::convertToOperations(NodeConverter &converter,
IMB_freeImBuf(ibuf);
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_MovieClipNode.h b/source/blender/compositor/nodes/COM_MovieClipNode.h
index c46033f944a..a469ce9e2a4 100644
--- a/source/blender/compositor/nodes/COM_MovieClipNode.h
+++ b/source/blender/compositor/nodes/COM_MovieClipNode.h
@@ -21,6 +21,8 @@
#include "COM_Node.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
/**
* \brief MovieClipNode
* \ingroup Node
@@ -31,3 +33,5 @@ class MovieClipNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_MovieDistortionNode.cc b/source/blender/compositor/nodes/COM_MovieDistortionNode.cc
index ebace6d5fff..8f17ef8bb98 100644
--- a/source/blender/compositor/nodes/COM_MovieDistortionNode.cc
+++ b/source/blender/compositor/nodes/COM_MovieDistortionNode.cc
@@ -22,6 +22,8 @@
#include "COM_MovieDistortionOperation.h"
#include "DNA_movieclip_types.h"
+namespace blender::compositor {
+
MovieDistortionNode::MovieDistortionNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -44,3 +46,5 @@ void MovieDistortionNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(inputSocket, operation->getInputSocket(0));
converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_MovieDistortionNode.h b/source/blender/compositor/nodes/COM_MovieDistortionNode.h
index 60349be0793..0c1610aa3d3 100644
--- a/source/blender/compositor/nodes/COM_MovieDistortionNode.h
+++ b/source/blender/compositor/nodes/COM_MovieDistortionNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief MovieDistortionNode
* \ingroup Node
@@ -30,3 +32,5 @@ class MovieDistortionNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_NormalNode.cc b/source/blender/compositor/nodes/COM_NormalNode.cc
index 1f48a26fd75..5a97b0932ef 100644
--- a/source/blender/compositor/nodes/COM_NormalNode.cc
+++ b/source/blender/compositor/nodes/COM_NormalNode.cc
@@ -22,6 +22,8 @@
#include "COM_ExecutionSystem.h"
#include "COM_SetVectorOperation.h"
+namespace blender::compositor {
+
NormalNode::NormalNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -54,3 +56,5 @@ void NormalNode::convertToOperations(NodeConverter &converter,
converter.addLink(operationSet->getOutputSocket(0), operation->getInputSocket(1));
converter.mapOutputSocket(outputSocketDotproduct, operation->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_NormalNode.h b/source/blender/compositor/nodes/COM_NormalNode.h
index 9b32e57a141..6d5cbb394a0 100644
--- a/source/blender/compositor/nodes/COM_NormalNode.h
+++ b/source/blender/compositor/nodes/COM_NormalNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief NormalNode
* \ingroup Node
@@ -30,3 +32,5 @@ class NormalNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_NormalizeNode.cc b/source/blender/compositor/nodes/COM_NormalizeNode.cc
index 72459fd477c..639dd8e5a51 100644
--- a/source/blender/compositor/nodes/COM_NormalizeNode.cc
+++ b/source/blender/compositor/nodes/COM_NormalizeNode.cc
@@ -20,6 +20,8 @@
#include "COM_ExecutionSystem.h"
#include "COM_NormalizeOperation.h"
+namespace blender::compositor {
+
NormalizeNode::NormalizeNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -34,3 +36,5 @@ void NormalizeNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0));
converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_NormalizeNode.h b/source/blender/compositor/nodes/COM_NormalizeNode.h
index bf8055245e7..7770fc49b61 100644
--- a/source/blender/compositor/nodes/COM_NormalizeNode.h
+++ b/source/blender/compositor/nodes/COM_NormalizeNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief NormalizeNode
* \ingroup Node
@@ -30,3 +32,5 @@ class NormalizeNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_OutputFileNode.cc b/source/blender/compositor/nodes/COM_OutputFileNode.cc
index dcc1fbdec67..e5b9cfb8cc2 100644
--- a/source/blender/compositor/nodes/COM_OutputFileNode.cc
+++ b/source/blender/compositor/nodes/COM_OutputFileNode.cc
@@ -18,18 +18,44 @@
#include "COM_OutputFileNode.h"
#include "COM_ExecutionSystem.h"
-#include "COM_OutputFileMultiViewOperation.h"
#include "COM_OutputFileOperation.h"
#include "BKE_scene.h"
#include "BLI_path_util.h"
+namespace blender::compositor {
+
OutputFileNode::OutputFileNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
}
+void OutputFileNode::add_input_sockets(OutputOpenExrMultiLayerOperation &operation) const
+{
+ for (NodeInput *input : inputs) {
+ NodeImageMultiFileSocket *sockdata =
+ (NodeImageMultiFileSocket *)input->getbNodeSocket()->storage;
+ /* note: layer becomes an empty placeholder if the input is not linked */
+ operation.add_layer(sockdata->layer, input->getDataType(), input->isLinked());
+ }
+}
+
+void OutputFileNode::map_input_sockets(NodeConverter &converter,
+ OutputOpenExrMultiLayerOperation &operation) const
+{
+ bool previewAdded = false;
+ int index = 0;
+ for (NodeInput *input : inputs) {
+ converter.mapInputSocket(input, operation.getInputSocket(index++));
+
+ if (!previewAdded) {
+ converter.addNodeInputPreview(input);
+ previewAdded = true;
+ }
+ }
+}
+
void OutputFileNode::convertToOperations(NodeConverter &converter,
const CompositorContext &context) const
{
@@ -69,29 +95,15 @@ void OutputFileNode::convertToOperations(NodeConverter &converter,
}
converter.addOperation(outputOperation);
- int num_inputs = getNumberOfInputSockets();
- bool previewAdded = false;
- for (int i = 0; i < num_inputs; i++) {
- NodeInput *input = getInputSocket(i);
- NodeImageMultiFileSocket *sockdata =
- (NodeImageMultiFileSocket *)input->getbNodeSocket()->storage;
-
- /* note: layer becomes an empty placeholder if the input is not linked */
- outputOperation->add_layer(sockdata->layer, input->getDataType(), input->isLinked());
-
- converter.mapInputSocket(input, outputOperation->getInputSocket(i));
-
- if (!previewAdded) {
- converter.addNodeInputPreview(input);
- previewAdded = true;
- }
- }
+ /* First add all inputs. Inputs are stored in a Vector and can be moved to a different
+ * memory address during this time.*/
+ add_input_sockets(*outputOperation);
+ /* After adding the sockets the memory addresses will stick. */
+ map_input_sockets(converter, *outputOperation);
}
else { /* single layer format */
- int num_inputs = getNumberOfInputSockets();
bool previewAdded = false;
- for (int i = 0; i < num_inputs; i++) {
- NodeInput *input = getInputSocket(i);
+ for (NodeInput *input : inputs) {
if (input->isLinked()) {
NodeImageMultiFileSocket *sockdata =
(NodeImageMultiFileSocket *)input->getbNodeSocket()->storage;
@@ -151,3 +163,5 @@ void OutputFileNode::convertToOperations(NodeConverter &converter,
}
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_OutputFileNode.h b/source/blender/compositor/nodes/COM_OutputFileNode.h
index 2b37a1ae252..c64128a708f 100644
--- a/source/blender/compositor/nodes/COM_OutputFileNode.h
+++ b/source/blender/compositor/nodes/COM_OutputFileNode.h
@@ -19,8 +19,13 @@
#pragma once
#include "COM_Node.h"
+
+#include "COM_OutputFileMultiViewOperation.h"
+
#include "DNA_node_types.h"
+namespace blender::compositor {
+
/**
* \brief OutputFileNode
* \ingroup Node
@@ -30,4 +35,11 @@ class OutputFileNode : public Node {
OutputFileNode(bNode *editorNode);
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
+
+ private:
+ void add_input_sockets(OutputOpenExrMultiLayerOperation &operation) const;
+ void map_input_sockets(NodeConverter &converter,
+ OutputOpenExrMultiLayerOperation &operation) const;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_PixelateNode.cc b/source/blender/compositor/nodes/COM_PixelateNode.cc
index f238f68727e..396f339e5a2 100644
--- a/source/blender/compositor/nodes/COM_PixelateNode.cc
+++ b/source/blender/compositor/nodes/COM_PixelateNode.cc
@@ -21,6 +21,8 @@
#include "COM_ExecutionSystem.h"
#include "COM_PixelateOperation.h"
+namespace blender::compositor {
+
PixelateNode::PixelateNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -44,3 +46,5 @@ void PixelateNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(inputSocket, operation->getInputSocket(0));
converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_PixelateNode.h b/source/blender/compositor/nodes/COM_PixelateNode.h
index 19ae352ce80..1a6555550cf 100644
--- a/source/blender/compositor/nodes/COM_PixelateNode.h
+++ b/source/blender/compositor/nodes/COM_PixelateNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief PixelateNode
* \ingroup Node
@@ -30,3 +32,5 @@ class PixelateNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.cc b/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.cc
index 6b9b51631ec..54a0f4d0452 100644
--- a/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.cc
+++ b/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.cc
@@ -25,6 +25,8 @@
#include "BKE_node.h"
#include "BKE_tracking.h"
+namespace blender::compositor {
+
PlaneTrackDeformNode::PlaneTrackDeformNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -70,3 +72,5 @@ void PlaneTrackDeformNode::convertToOperations(NodeConverter &converter,
converter.mapOutputSocket(output_plane, plane_mask_operation->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.h b/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.h
index 29347ec79c4..307738fcaf0 100644
--- a/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.h
+++ b/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.h
@@ -23,6 +23,8 @@
#include "DNA_movieclip_types.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
/**
* \brief PlaneTrackDeformNode
* \ingroup Node
@@ -33,3 +35,5 @@ class PlaneTrackDeformNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_RenderLayersNode.cc b/source/blender/compositor/nodes/COM_RenderLayersNode.cc
index ea3eeb13393..851d0366546 100644
--- a/source/blender/compositor/nodes/COM_RenderLayersNode.cc
+++ b/source/blender/compositor/nodes/COM_RenderLayersNode.cc
@@ -25,6 +25,8 @@
#include "COM_SetVectorOperation.h"
#include "COM_TranslateOperation.h"
+namespace blender::compositor {
+
RenderLayersNode::RenderLayersNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -72,9 +74,8 @@ void RenderLayersNode::testRenderLink(NodeConverter &converter,
missingRenderLink(converter);
return;
}
- const int num_outputs = this->getNumberOfOutputSockets();
- for (int i = 0; i < num_outputs; i++) {
- NodeOutput *output = this->getOutputSocket(i);
+
+ for (NodeOutput *output : getOutputSockets()) {
NodeImageLayer *storage = (NodeImageLayer *)output->getbNodeSocket()->storage;
RenderPass *rpass = (RenderPass *)BLI_findstring(
&rl->passes, storage->pass_name, offsetof(RenderPass, name));
@@ -153,9 +154,7 @@ void RenderLayersNode::missingSocketLink(NodeConverter &converter, NodeOutput *o
void RenderLayersNode::missingRenderLink(NodeConverter &converter) const
{
- const int num_outputs = this->getNumberOfOutputSockets();
- for (int i = 0; i < num_outputs; i++) {
- NodeOutput *output = this->getOutputSocket(i);
+ for (NodeOutput *output : outputs) {
missingSocketLink(converter, output);
}
}
@@ -174,3 +173,5 @@ void RenderLayersNode::convertToOperations(NodeConverter &converter,
missingRenderLink(converter);
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_RenderLayersNode.h b/source/blender/compositor/nodes/COM_RenderLayersNode.h
index 92be0f7daa1..4eb2427c8e0 100644
--- a/source/blender/compositor/nodes/COM_RenderLayersNode.h
+++ b/source/blender/compositor/nodes/COM_RenderLayersNode.h
@@ -23,6 +23,7 @@
#include "DNA_node_types.h"
struct Render;
+namespace blender::compositor {
/**
* \brief RenderLayersNode
@@ -49,3 +50,5 @@ class RenderLayersNode : public Node {
void missingSocketLink(NodeConverter &converter, NodeOutput *output) const;
void missingRenderLink(NodeConverter &converter) const;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_RotateNode.cc b/source/blender/compositor/nodes/COM_RotateNode.cc
index cbade778bcb..af5baa733dc 100644
--- a/source/blender/compositor/nodes/COM_RotateNode.cc
+++ b/source/blender/compositor/nodes/COM_RotateNode.cc
@@ -22,6 +22,8 @@
#include "COM_RotateOperation.h"
#include "COM_SetSamplerOperation.h"
+namespace blender::compositor {
+
RotateNode::RotateNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -45,3 +47,5 @@ void RotateNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(inputDegreeSocket, operation->getInputSocket(1));
converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_RotateNode.h b/source/blender/compositor/nodes/COM_RotateNode.h
index 0f3b6497cb7..5d8bcb2e3e4 100644
--- a/source/blender/compositor/nodes/COM_RotateNode.h
+++ b/source/blender/compositor/nodes/COM_RotateNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief RotateNode
* \ingroup Node
@@ -30,3 +32,5 @@ class RotateNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ScaleNode.cc b/source/blender/compositor/nodes/COM_ScaleNode.cc
index 9ffcd5306b0..50d2902f375 100644
--- a/source/blender/compositor/nodes/COM_ScaleNode.cc
+++ b/source/blender/compositor/nodes/COM_ScaleNode.cc
@@ -24,6 +24,8 @@
#include "COM_SetSamplerOperation.h"
#include "COM_SetValueOperation.h"
+namespace blender::compositor {
+
ScaleNode::ScaleNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -79,7 +81,7 @@ void ScaleNode::convertToOperations(NodeConverter &converter,
operation->setOffset(bnode->custom3, bnode->custom4);
operation->setNewWidth(rd->xsch * render_size_factor);
operation->setNewHeight(rd->ysch * render_size_factor);
- operation->getInputSocket(0)->setResizeMode(COM_SC_NO_RESIZE);
+ operation->getInputSocket(0)->setResizeMode(ResizeMode::None);
converter.addOperation(operation);
converter.mapInputSocket(inputSocket, operation->getInputSocket(0));
@@ -105,3 +107,5 @@ void ScaleNode::convertToOperations(NodeConverter &converter,
}
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ScaleNode.h b/source/blender/compositor/nodes/COM_ScaleNode.h
index 5babec2b7aa..186ffa8bdce 100644
--- a/source/blender/compositor/nodes/COM_ScaleNode.h
+++ b/source/blender/compositor/nodes/COM_ScaleNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief ScaleNode
* \ingroup Node
@@ -30,3 +32,5 @@ class ScaleNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_SeparateColorNode.cc b/source/blender/compositor/nodes/COM_SeparateColorNode.cc
index 203aa25c9e9..fcaf52c701d 100644
--- a/source/blender/compositor/nodes/COM_SeparateColorNode.cc
+++ b/source/blender/compositor/nodes/COM_SeparateColorNode.cc
@@ -20,6 +20,8 @@
#include "COM_ConvertOperation.h"
+namespace blender::compositor {
+
SeparateColorNode::SeparateColorNode(bNode *editorNode) : Node(editorNode)
{
}
@@ -119,3 +121,5 @@ NodeOperation *SeparateYUVANode::getColorConverter(const CompositorContext & /*c
{
return new ConvertRGBToYUVOperation();
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_SeparateColorNode.h b/source/blender/compositor/nodes/COM_SeparateColorNode.h
index 4772c62f93c..eaf543df51f 100644
--- a/source/blender/compositor/nodes/COM_SeparateColorNode.h
+++ b/source/blender/compositor/nodes/COM_SeparateColorNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
class SeparateColorNode : public Node {
public:
SeparateColorNode(bNode *editorNode);
@@ -65,3 +67,5 @@ class SeparateYUVANode : public SeparateColorNode {
NodeOperation *getColorConverter(const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_SetAlphaNode.cc b/source/blender/compositor/nodes/COM_SetAlphaNode.cc
index 233a5e96ff4..dc41c126ba8 100644
--- a/source/blender/compositor/nodes/COM_SetAlphaNode.cc
+++ b/source/blender/compositor/nodes/COM_SetAlphaNode.cc
@@ -21,6 +21,8 @@
#include "COM_SetAlphaMultiplyOperation.h"
#include "COM_SetAlphaReplaceOperation.h"
+namespace blender::compositor {
+
void SetAlphaNode::convertToOperations(NodeConverter &converter,
const CompositorContext & /*context*/) const
{
@@ -46,3 +48,5 @@ void SetAlphaNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1));
converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_SetAlphaNode.h b/source/blender/compositor/nodes/COM_SetAlphaNode.h
index 06dd93012ff..c8d340eb64b 100644
--- a/source/blender/compositor/nodes/COM_SetAlphaNode.h
+++ b/source/blender/compositor/nodes/COM_SetAlphaNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief SetAlphaNode
* \ingroup Node
@@ -32,3 +34,5 @@ class SetAlphaNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_SocketProxyNode.cc b/source/blender/compositor/nodes/COM_SocketProxyNode.cc
index 93d8d399cad..b3aa1770551 100644
--- a/source/blender/compositor/nodes/COM_SocketProxyNode.cc
+++ b/source/blender/compositor/nodes/COM_SocketProxyNode.cc
@@ -25,6 +25,8 @@
#include "COM_SocketProxyOperation.h"
#include "COM_WriteBufferOperation.h"
+namespace blender::compositor {
+
SocketProxyNode::SocketProxyNode(bNode *editorNode,
bNodeSocket *editorInput,
bNodeSocket *editorOutput,
@@ -101,3 +103,5 @@ void SocketBufferNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(input, writeOperation->getInputSocket(0));
converter.mapOutputSocket(output, readOperation->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_SocketProxyNode.h b/source/blender/compositor/nodes/COM_SocketProxyNode.h
index 8616fe6dccf..d19fb802767 100644
--- a/source/blender/compositor/nodes/COM_SocketProxyNode.h
+++ b/source/blender/compositor/nodes/COM_SocketProxyNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief SocketProxyNode
* \ingroup Node
@@ -53,3 +55,5 @@ class SocketBufferNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_SplitViewerNode.cc b/source/blender/compositor/nodes/COM_SplitViewerNode.cc
index 681adeaf1d3..582c650f205 100644
--- a/source/blender/compositor/nodes/COM_SplitViewerNode.cc
+++ b/source/blender/compositor/nodes/COM_SplitViewerNode.cc
@@ -25,6 +25,8 @@
#include "COM_SplitOperation.h"
#include "COM_ViewerOperation.h"
+namespace blender::compositor {
+
SplitViewerNode::SplitViewerNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -73,3 +75,5 @@ void SplitViewerNode::convertToOperations(NodeConverter &converter,
converter.registerViewer(viewerOperation);
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_SplitViewerNode.h b/source/blender/compositor/nodes/COM_SplitViewerNode.h
index 0be586041e9..8a42775eb0d 100644
--- a/source/blender/compositor/nodes/COM_SplitViewerNode.h
+++ b/source/blender/compositor/nodes/COM_SplitViewerNode.h
@@ -20,6 +20,9 @@
#include "COM_Node.h"
#include "DNA_node_types.h"
+
+namespace blender::compositor {
+
/**
* \brief SplitViewerNode
* \ingroup Node
@@ -30,3 +33,5 @@ class SplitViewerNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_Stabilize2dNode.cc b/source/blender/compositor/nodes/COM_Stabilize2dNode.cc
index 38db080a154..fc72b48eca2 100644
--- a/source/blender/compositor/nodes/COM_Stabilize2dNode.cc
+++ b/source/blender/compositor/nodes/COM_Stabilize2dNode.cc
@@ -28,6 +28,8 @@
#include "DNA_movieclip_types.h"
+namespace blender::compositor {
+
Stabilize2dNode::Stabilize2dNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -111,3 +113,5 @@ void Stabilize2dNode::convertToOperations(NodeConverter &converter,
converter.addLink(translateOperation->getOutputSocket(), psoperation->getInputSocket(0));
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_Stabilize2dNode.h b/source/blender/compositor/nodes/COM_Stabilize2dNode.h
index 2c7cfafb297..34ed8871e33 100644
--- a/source/blender/compositor/nodes/COM_Stabilize2dNode.h
+++ b/source/blender/compositor/nodes/COM_Stabilize2dNode.h
@@ -21,6 +21,8 @@
#include "COM_Node.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
/**
* \brief Stabilize2dNode
* \ingroup Node
@@ -31,3 +33,5 @@ class Stabilize2dNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_SunBeamsNode.cc b/source/blender/compositor/nodes/COM_SunBeamsNode.cc
index d899a54c03c..1e5aa0b8020 100644
--- a/source/blender/compositor/nodes/COM_SunBeamsNode.cc
+++ b/source/blender/compositor/nodes/COM_SunBeamsNode.cc
@@ -18,6 +18,8 @@
#include "COM_SunBeamsNode.h"
#include "COM_SunBeamsOperation.h"
+namespace blender::compositor {
+
SunBeamsNode::SunBeamsNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -37,3 +39,5 @@ void SunBeamsNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(inputSocket, operation->getInputSocket(0));
converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_SunBeamsNode.h b/source/blender/compositor/nodes/COM_SunBeamsNode.h
index 8b02f13211f..8b68d3f4cb5 100644
--- a/source/blender/compositor/nodes/COM_SunBeamsNode.h
+++ b/source/blender/compositor/nodes/COM_SunBeamsNode.h
@@ -19,6 +19,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief SunBeamsNode
* \ingroup Node
@@ -29,3 +31,5 @@ class SunBeamsNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_SwitchNode.cc b/source/blender/compositor/nodes/COM_SwitchNode.cc
index 947774e98ae..4006d10dafb 100644
--- a/source/blender/compositor/nodes/COM_SwitchNode.cc
+++ b/source/blender/compositor/nodes/COM_SwitchNode.cc
@@ -18,6 +18,8 @@
#include "COM_SwitchNode.h"
+namespace blender::compositor {
+
SwitchNode::SwitchNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -38,3 +40,5 @@ void SwitchNode::convertToOperations(NodeConverter &converter,
converter.mapOutputSocket(getOutputSocket(0), result);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_SwitchNode.h b/source/blender/compositor/nodes/COM_SwitchNode.h
index c8ee503154c..aa6caa2e59f 100644
--- a/source/blender/compositor/nodes/COM_SwitchNode.h
+++ b/source/blender/compositor/nodes/COM_SwitchNode.h
@@ -21,6 +21,9 @@
#include "COM_Node.h"
#include "COM_NodeOperation.h"
#include "DNA_node_types.h"
+
+namespace blender::compositor {
+
/**
* \brief SwitchNode
* \ingroup Node
@@ -31,3 +34,5 @@ class SwitchNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_SwitchViewNode.cc b/source/blender/compositor/nodes/COM_SwitchViewNode.cc
index e534ebfac9a..395122dd11b 100644
--- a/source/blender/compositor/nodes/COM_SwitchViewNode.cc
+++ b/source/blender/compositor/nodes/COM_SwitchViewNode.cc
@@ -19,6 +19,8 @@
#include "COM_SwitchViewNode.h"
#include "BLI_listbase.h"
+namespace blender::compositor {
+
SwitchViewNode::SwitchViewNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -38,3 +40,5 @@ void SwitchViewNode::convertToOperations(NodeConverter &converter,
result = converter.addInputProxy(getInputSocket(nr), false);
converter.mapOutputSocket(getOutputSocket(0), result);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_SwitchViewNode.h b/source/blender/compositor/nodes/COM_SwitchViewNode.h
index 419fd1b7463..ce6de52182c 100644
--- a/source/blender/compositor/nodes/COM_SwitchViewNode.h
+++ b/source/blender/compositor/nodes/COM_SwitchViewNode.h
@@ -21,6 +21,9 @@
#include "COM_Node.h"
#include "COM_NodeOperation.h"
#include "DNA_node_types.h"
+
+namespace blender::compositor {
+
/**
* \brief SwitchViewNode
* \ingroup Node
@@ -31,3 +34,5 @@ class SwitchViewNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_TextureNode.cc b/source/blender/compositor/nodes/COM_TextureNode.cc
index 3381b5098d7..317355b8c9a 100644
--- a/source/blender/compositor/nodes/COM_TextureNode.cc
+++ b/source/blender/compositor/nodes/COM_TextureNode.cc
@@ -20,6 +20,8 @@
#include "COM_ExecutionSystem.h"
#include "COM_TextureOperation.h"
+namespace blender::compositor {
+
TextureNode::TextureNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -54,3 +56,5 @@ void TextureNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(getInputSocket(1), alphaOperation->getInputSocket(1));
converter.mapOutputSocket(getOutputSocket(0), alphaOperation->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_TextureNode.h b/source/blender/compositor/nodes/COM_TextureNode.h
index 5a9a56b748d..b886e3b74e1 100644
--- a/source/blender/compositor/nodes/COM_TextureNode.h
+++ b/source/blender/compositor/nodes/COM_TextureNode.h
@@ -21,6 +21,8 @@
#include "COM_Node.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
/**
* \brief TextureNode
* \ingroup Node
@@ -31,3 +33,5 @@ class TextureNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_TimeNode.cc b/source/blender/compositor/nodes/COM_TimeNode.cc
index 247e0d11df6..c14c5344eee 100644
--- a/source/blender/compositor/nodes/COM_TimeNode.cc
+++ b/source/blender/compositor/nodes/COM_TimeNode.cc
@@ -24,6 +24,8 @@
#include "BLI_utildefines.h"
+namespace blender::compositor {
+
TimeNode::TimeNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -56,3 +58,5 @@ void TimeNode::convertToOperations(NodeConverter &converter,
converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_TimeNode.h b/source/blender/compositor/nodes/COM_TimeNode.h
index 0f2b624fd1f..5688e2cff03 100644
--- a/source/blender/compositor/nodes/COM_TimeNode.h
+++ b/source/blender/compositor/nodes/COM_TimeNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief TimeNode
* \ingroup Node
@@ -30,3 +32,5 @@ class TimeNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_TonemapNode.cc b/source/blender/compositor/nodes/COM_TonemapNode.cc
index db329e56f9b..844fe3e8cb6 100644
--- a/source/blender/compositor/nodes/COM_TonemapNode.cc
+++ b/source/blender/compositor/nodes/COM_TonemapNode.cc
@@ -20,6 +20,8 @@
#include "COM_ExecutionSystem.h"
#include "COM_TonemapOperation.h"
+namespace blender::compositor {
+
TonemapNode::TonemapNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -38,3 +40,5 @@ void TonemapNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0));
converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_TonemapNode.h b/source/blender/compositor/nodes/COM_TonemapNode.h
index db7ae5fa290..cac9004c32a 100644
--- a/source/blender/compositor/nodes/COM_TonemapNode.h
+++ b/source/blender/compositor/nodes/COM_TonemapNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief TonemapNode
* \ingroup Node
@@ -30,3 +32,5 @@ class TonemapNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_TrackPositionNode.cc b/source/blender/compositor/nodes/COM_TrackPositionNode.cc
index 52e7f7d832b..3fb5fc02f20 100644
--- a/source/blender/compositor/nodes/COM_TrackPositionNode.cc
+++ b/source/blender/compositor/nodes/COM_TrackPositionNode.cc
@@ -26,6 +26,8 @@
#include "BKE_node.h"
+namespace blender::compositor {
+
TrackPositionNode::TrackPositionNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -109,3 +111,5 @@ void TrackPositionNode::convertToOperations(NodeConverter &converter,
converter.addLink(operationMotionPostY->getOutputSocket(), combine_operation->getInputSocket(3));
converter.mapOutputSocket(outputSpeed, combine_operation->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_TrackPositionNode.h b/source/blender/compositor/nodes/COM_TrackPositionNode.h
index f2062828ca5..665ba36fe09 100644
--- a/source/blender/compositor/nodes/COM_TrackPositionNode.h
+++ b/source/blender/compositor/nodes/COM_TrackPositionNode.h
@@ -21,6 +21,8 @@
#include "COM_Node.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
/**
* \brief TrackPositionNode
* \ingroup Node
@@ -31,3 +33,5 @@ class TrackPositionNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_TransformNode.cc b/source/blender/compositor/nodes/COM_TransformNode.cc
index cd5ba8ba201..cd12939ab43 100644
--- a/source/blender/compositor/nodes/COM_TransformNode.cc
+++ b/source/blender/compositor/nodes/COM_TransformNode.cc
@@ -24,6 +24,8 @@
#include "COM_SetValueOperation.h"
#include "COM_TranslateOperation.h"
+namespace blender::compositor {
+
TransformNode::TransformNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -66,3 +68,5 @@ void TransformNode::convertToOperations(NodeConverter &converter,
converter.mapOutputSocket(getOutputSocket(), translateOperation->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_TransformNode.h b/source/blender/compositor/nodes/COM_TransformNode.h
index 56d05cd4c94..137e162256d 100644
--- a/source/blender/compositor/nodes/COM_TransformNode.h
+++ b/source/blender/compositor/nodes/COM_TransformNode.h
@@ -21,6 +21,8 @@
#include "COM_Node.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
/**
* \brief TransformNode
* \ingroup Node
@@ -31,3 +33,5 @@ class TransformNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_TranslateNode.cc b/source/blender/compositor/nodes/COM_TranslateNode.cc
index 13a73953ea0..1b2ce341a66 100644
--- a/source/blender/compositor/nodes/COM_TranslateNode.cc
+++ b/source/blender/compositor/nodes/COM_TranslateNode.cc
@@ -23,6 +23,8 @@
#include "COM_WrapOperation.h"
#include "COM_WriteBufferOperation.h"
+namespace blender::compositor {
+
TranslateNode::TranslateNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -54,7 +56,10 @@ void TranslateNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(inputYSocket, operation->getInputSocket(2));
converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0));
- if (data->wrap_axis) {
+ /* FullFrame does not support using WriteBufferOperation.
+ * TODO: Implement TranslateOperation with wrap support in FullFrame.
+ */
+ if (data->wrap_axis && context.get_execution_model() != eExecutionModel::FullFrame) {
WriteBufferOperation *writeOperation = new WriteBufferOperation(DataType::Color);
WrapOperation *wrapOperation = new WrapOperation(DataType::Color);
wrapOperation->setMemoryProxy(writeOperation->getMemoryProxy());
@@ -69,3 +74,5 @@ void TranslateNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(inputSocket, operation->getInputSocket(0));
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_TranslateNode.h b/source/blender/compositor/nodes/COM_TranslateNode.h
index e6fd230572c..0cea234bff8 100644
--- a/source/blender/compositor/nodes/COM_TranslateNode.h
+++ b/source/blender/compositor/nodes/COM_TranslateNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief TranslateNode
* \ingroup Node
@@ -30,3 +32,5 @@ class TranslateNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ValueNode.cc b/source/blender/compositor/nodes/COM_ValueNode.cc
index 4227db0d10e..6b640fa2a3a 100644
--- a/source/blender/compositor/nodes/COM_ValueNode.cc
+++ b/source/blender/compositor/nodes/COM_ValueNode.cc
@@ -20,6 +20,8 @@
#include "COM_ExecutionSystem.h"
#include "COM_SetValueOperation.h"
+namespace blender::compositor {
+
ValueNode::ValueNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -35,3 +37,5 @@ void ValueNode::convertToOperations(NodeConverter &converter,
converter.mapOutputSocket(output, operation->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ValueNode.h b/source/blender/compositor/nodes/COM_ValueNode.h
index 18162b7d4b8..1401b2c7e0a 100644
--- a/source/blender/compositor/nodes/COM_ValueNode.h
+++ b/source/blender/compositor/nodes/COM_ValueNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief ValueNode
* \ingroup Node
@@ -30,3 +32,5 @@ class ValueNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_VectorBlurNode.cc b/source/blender/compositor/nodes/COM_VectorBlurNode.cc
index a92991c8b49..7aa5f5668c9 100644
--- a/source/blender/compositor/nodes/COM_VectorBlurNode.cc
+++ b/source/blender/compositor/nodes/COM_VectorBlurNode.cc
@@ -20,6 +20,8 @@
#include "COM_VectorBlurOperation.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
VectorBlurNode::VectorBlurNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -41,3 +43,5 @@ void VectorBlurNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(getInputSocket(2), operation->getInputSocket(2));
converter.mapOutputSocket(getOutputSocket(), operation->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_VectorBlurNode.h b/source/blender/compositor/nodes/COM_VectorBlurNode.h
index 2a55ca4457d..8c98a0b81a1 100644
--- a/source/blender/compositor/nodes/COM_VectorBlurNode.h
+++ b/source/blender/compositor/nodes/COM_VectorBlurNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief VectorBlurNode
* \ingroup Node
@@ -30,3 +32,5 @@ class VectorBlurNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_VectorCurveNode.cc b/source/blender/compositor/nodes/COM_VectorCurveNode.cc
index 1201a9f9613..f2fd80cd93e 100644
--- a/source/blender/compositor/nodes/COM_VectorCurveNode.cc
+++ b/source/blender/compositor/nodes/COM_VectorCurveNode.cc
@@ -20,6 +20,8 @@
#include "COM_ExecutionSystem.h"
#include "COM_VectorCurveOperation.h"
+namespace blender::compositor {
+
VectorCurveNode::VectorCurveNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -35,3 +37,5 @@ void VectorCurveNode::convertToOperations(NodeConverter &converter,
converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0));
converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_VectorCurveNode.h b/source/blender/compositor/nodes/COM_VectorCurveNode.h
index 4e84b7454d9..ee4df5d3a42 100644
--- a/source/blender/compositor/nodes/COM_VectorCurveNode.h
+++ b/source/blender/compositor/nodes/COM_VectorCurveNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief VectorCurveNode
* \ingroup Node
@@ -30,3 +32,5 @@ class VectorCurveNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ViewLevelsNode.cc b/source/blender/compositor/nodes/COM_ViewLevelsNode.cc
index 7b86fb1d64d..dc454b95080 100644
--- a/source/blender/compositor/nodes/COM_ViewLevelsNode.cc
+++ b/source/blender/compositor/nodes/COM_ViewLevelsNode.cc
@@ -22,6 +22,8 @@
#include "COM_ExecutionSystem.h"
#include "COM_SetValueOperation.h"
+namespace blender::compositor {
+
ViewLevelsNode::ViewLevelsNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -59,3 +61,5 @@ void ViewLevelsNode::convertToOperations(NodeConverter &converter,
converter.addOutputValue(getOutputSocket(1), 0.0f);
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ViewLevelsNode.h b/source/blender/compositor/nodes/COM_ViewLevelsNode.h
index 20dbe967286..055d871498e 100644
--- a/source/blender/compositor/nodes/COM_ViewLevelsNode.h
+++ b/source/blender/compositor/nodes/COM_ViewLevelsNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief ViewLevelsNode
* \ingroup Node
@@ -30,3 +32,5 @@ class ViewLevelsNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ViewerNode.cc b/source/blender/compositor/nodes/COM_ViewerNode.cc
index 359c3d3031d..3833a8d7ca8 100644
--- a/source/blender/compositor/nodes/COM_ViewerNode.cc
+++ b/source/blender/compositor/nodes/COM_ViewerNode.cc
@@ -25,6 +25,8 @@
#include "COM_ExecutionSystem.h"
#include "COM_ViewerOperation.h"
+namespace blender::compositor {
+
ViewerNode::ViewerNode(bNode *editorNode) : Node(editorNode)
{
/* pass */
@@ -82,3 +84,5 @@ void ViewerNode::convertToOperations(NodeConverter &converter,
converter.registerViewer(viewerOperation);
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ViewerNode.h b/source/blender/compositor/nodes/COM_ViewerNode.h
index 0e13f165794..544a5e6a504 100644
--- a/source/blender/compositor/nodes/COM_ViewerNode.h
+++ b/source/blender/compositor/nodes/COM_ViewerNode.h
@@ -20,6 +20,9 @@
#include "COM_Node.h"
#include "DNA_node_types.h"
+
+namespace blender::compositor {
+
/**
* \brief ViewerNode
* \ingroup Node
@@ -30,3 +33,5 @@ class ViewerNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ZCombineNode.cc b/source/blender/compositor/nodes/COM_ZCombineNode.cc
index b61c018d029..a76049ff249 100644
--- a/source/blender/compositor/nodes/COM_ZCombineNode.cc
+++ b/source/blender/compositor/nodes/COM_ZCombineNode.cc
@@ -28,6 +28,8 @@
#include "DNA_material_types.h" /* the ramp types */
+namespace blender::compositor {
+
void ZCombineNode::convertToOperations(NodeConverter &converter,
const CompositorContext &context) const
{
@@ -99,3 +101,5 @@ void ZCombineNode::convertToOperations(NodeConverter &converter,
converter.mapOutputSocket(getOutputSocket(1), zoperation->getOutputSocket());
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/nodes/COM_ZCombineNode.h b/source/blender/compositor/nodes/COM_ZCombineNode.h
index aa8bd979691..82f2f30fb3c 100644
--- a/source/blender/compositor/nodes/COM_ZCombineNode.h
+++ b/source/blender/compositor/nodes/COM_ZCombineNode.h
@@ -20,6 +20,8 @@
#include "COM_Node.h"
+namespace blender::compositor {
+
/**
* \brief ZCombineNode
* \ingroup Node
@@ -32,3 +34,5 @@ class ZCombineNode : public Node {
void convertToOperations(NodeConverter &converter,
const CompositorContext &context) const override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_AlphaOverKeyOperation.cc b/source/blender/compositor/operations/COM_AlphaOverKeyOperation.cc
index 668d07c7c3d..0c656753a51 100644
--- a/source/blender/compositor/operations/COM_AlphaOverKeyOperation.cc
+++ b/source/blender/compositor/operations/COM_AlphaOverKeyOperation.cc
@@ -18,10 +18,7 @@
#include "COM_AlphaOverKeyOperation.h"
-AlphaOverKeyOperation::AlphaOverKeyOperation()
-{
- /* pass */
-}
+namespace blender::compositor {
void AlphaOverKeyOperation::executePixelSampled(float output[4],
float x,
@@ -52,3 +49,5 @@ void AlphaOverKeyOperation::executePixelSampled(float output[4],
output[3] = (mul * inputColor1[3]) + value[0] * inputOverColor[3];
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_AlphaOverKeyOperation.h b/source/blender/compositor/operations/COM_AlphaOverKeyOperation.h
index b62105d7c41..83713d18971 100644
--- a/source/blender/compositor/operations/COM_AlphaOverKeyOperation.h
+++ b/source/blender/compositor/operations/COM_AlphaOverKeyOperation.h
@@ -20,6 +20,8 @@
#include "COM_MixOperation.h"
+namespace blender::compositor {
+
/**
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -27,12 +29,9 @@
class AlphaOverKeyOperation : public MixBaseOperation {
public:
/**
- * Default constructor
- */
- AlphaOverKeyOperation();
-
- /**
* The inner loop of this operation.
*/
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_AlphaOverMixedOperation.cc b/source/blender/compositor/operations/COM_AlphaOverMixedOperation.cc
index b8465ab7ccf..c68c79d2263 100644
--- a/source/blender/compositor/operations/COM_AlphaOverMixedOperation.cc
+++ b/source/blender/compositor/operations/COM_AlphaOverMixedOperation.cc
@@ -18,6 +18,8 @@
#include "COM_AlphaOverMixedOperation.h"
+namespace blender::compositor {
+
AlphaOverMixedOperation::AlphaOverMixedOperation()
{
this->m_x = 0.0f;
@@ -53,3 +55,5 @@ void AlphaOverMixedOperation::executePixelSampled(float output[4],
output[3] = (mul * inputColor1[3]) + value[0] * inputOverColor[3];
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_AlphaOverMixedOperation.h b/source/blender/compositor/operations/COM_AlphaOverMixedOperation.h
index 3f7137a8d36..e2b3af84162 100644
--- a/source/blender/compositor/operations/COM_AlphaOverMixedOperation.h
+++ b/source/blender/compositor/operations/COM_AlphaOverMixedOperation.h
@@ -20,6 +20,8 @@
#include "COM_MixOperation.h"
+namespace blender::compositor {
+
/**
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -44,3 +46,5 @@ class AlphaOverMixedOperation : public MixBaseOperation {
this->m_x = x;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc b/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc
index 4510c027d46..3dd4607e273 100644
--- a/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc
+++ b/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc
@@ -18,10 +18,7 @@
#include "COM_AlphaOverPremultiplyOperation.h"
-AlphaOverPremultiplyOperation::AlphaOverPremultiplyOperation()
-{
- /* pass */
-}
+namespace blender::compositor {
void AlphaOverPremultiplyOperation::executePixelSampled(float output[4],
float x,
@@ -52,3 +49,5 @@ void AlphaOverPremultiplyOperation::executePixelSampled(float output[4],
output[3] = (mul * inputColor1[3]) + value[0] * inputOverColor[3];
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.h b/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.h
index cb60f8393d0..f1d4b668fce 100644
--- a/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.h
+++ b/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.h
@@ -20,6 +20,8 @@
#include "COM_MixOperation.h"
+namespace blender::compositor {
+
/**
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -27,12 +29,9 @@
class AlphaOverPremultiplyOperation : public MixBaseOperation {
public:
/**
- * Default constructor
- */
- AlphaOverPremultiplyOperation();
-
- /**
* The inner loop of this operation.
*/
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_AntiAliasOperation.cc b/source/blender/compositor/operations/COM_AntiAliasOperation.cc
index 740cd3ff609..23d6f4b80c7 100644
--- a/source/blender/compositor/operations/COM_AntiAliasOperation.cc
+++ b/source/blender/compositor/operations/COM_AntiAliasOperation.cc
@@ -24,6 +24,8 @@
#include "RE_texture.h"
+namespace blender::compositor {
+
/* An implementation of the Scale3X edge-extrapolation algorithm.
*
* Code from GIMP plugin, based on code from Adam D. Moss <adam@gimp.org>
@@ -117,7 +119,7 @@ AntiAliasOperation::AntiAliasOperation()
this->addInputSocket(DataType::Value);
this->addOutputSocket(DataType::Value);
this->m_valueReader = nullptr;
- this->setComplex(true);
+ this->flags.complex = true;
}
void AntiAliasOperation::initExecution()
@@ -199,3 +201,5 @@ void *AntiAliasOperation::initializeTileData(rcti *rect)
{
return getInputOperation(0)->initializeTileData(rect);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_AntiAliasOperation.h b/source/blender/compositor/operations/COM_AntiAliasOperation.h
index 691fb970b11..fc9102b5b4c 100644
--- a/source/blender/compositor/operations/COM_AntiAliasOperation.h
+++ b/source/blender/compositor/operations/COM_AntiAliasOperation.h
@@ -21,6 +21,8 @@
#include "COM_NodeOperation.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
/**
* \brief AntiAlias operations
* it only supports anti aliasing on BW buffers.
@@ -56,3 +58,5 @@ class AntiAliasOperation : public NodeOperation {
ReadBufferOperation *readOperation,
rcti *output) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_BilateralBlurOperation.cc b/source/blender/compositor/operations/COM_BilateralBlurOperation.cc
index d04fade2e93..64448e2ae95 100644
--- a/source/blender/compositor/operations/COM_BilateralBlurOperation.cc
+++ b/source/blender/compositor/operations/COM_BilateralBlurOperation.cc
@@ -21,12 +21,14 @@
#include "RE_pipeline.h"
+namespace blender::compositor {
+
BilateralBlurOperation::BilateralBlurOperation()
{
this->addInputSocket(DataType::Color);
this->addInputSocket(DataType::Color);
this->addOutputSocket(DataType::Color);
- this->setComplex(true);
+ this->flags.complex = true;
this->m_inputColorProgram = nullptr;
this->m_inputDeterminatorProgram = nullptr;
@@ -112,3 +114,5 @@ bool BilateralBlurOperation::determineDependingAreaOfInterest(rcti *input,
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_BilateralBlurOperation.h b/source/blender/compositor/operations/COM_BilateralBlurOperation.h
index bdc77325aaf..c56cef35050 100644
--- a/source/blender/compositor/operations/COM_BilateralBlurOperation.h
+++ b/source/blender/compositor/operations/COM_BilateralBlurOperation.h
@@ -21,6 +21,8 @@
#include "COM_NodeOperation.h"
#include "COM_QualityStepHelper.h"
+namespace blender::compositor {
+
class BilateralBlurOperation : public NodeOperation, public QualityStepHelper {
private:
SocketReader *m_inputColorProgram;
@@ -55,3 +57,5 @@ class BilateralBlurOperation : public NodeOperation, public QualityStepHelper {
this->m_data = data;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_BlurBaseOperation.cc b/source/blender/compositor/operations/COM_BlurBaseOperation.cc
index fe6ca1cfd4e..8b73624ca79 100644
--- a/source/blender/compositor/operations/COM_BlurBaseOperation.cc
+++ b/source/blender/compositor/operations/COM_BlurBaseOperation.cc
@@ -22,13 +22,15 @@
#include "RE_pipeline.h"
+namespace blender::compositor {
+
BlurBaseOperation::BlurBaseOperation(DataType data_type)
{
/* data_type is almost always DataType::Color except for alpha-blur */
this->addInputSocket(data_type);
this->addInputSocket(DataType::Value);
this->addOutputSocket(data_type);
- this->setComplex(true);
+ this->flags.complex = true;
this->m_inputProgram = nullptr;
memset(&m_data, 0, sizeof(NodeBlurData));
this->m_size = 1.0f;
@@ -167,7 +169,7 @@ void BlurBaseOperation::updateSize()
{
if (!this->m_sizeavailable) {
float result[4];
- this->getInputSocketReader(1)->readSampled(result, 0, 0, COM_PS_NEAREST);
+ this->getInputSocketReader(1)->readSampled(result, 0, 0, PixelSampler::Nearest);
this->m_size = result[0];
this->m_sizeavailable = true;
}
@@ -182,3 +184,5 @@ void BlurBaseOperation::determineResolution(unsigned int resolution[2],
resolution[1] += 2 * this->m_size * m_data.sizey;
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_BlurBaseOperation.h b/source/blender/compositor/operations/COM_BlurBaseOperation.h
index 84f94dfe124..7937ebd69dc 100644
--- a/source/blender/compositor/operations/COM_BlurBaseOperation.h
+++ b/source/blender/compositor/operations/COM_BlurBaseOperation.h
@@ -25,6 +25,8 @@
#include "BLI_simd.h"
+namespace blender::compositor {
+
class BlurBaseOperation : public NodeOperation, public QualityStepHelper {
private:
protected:
@@ -76,3 +78,5 @@ class BlurBaseOperation : public NodeOperation, public QualityStepHelper {
void determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2]) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_BokehBlurOperation.cc b/source/blender/compositor/operations/COM_BokehBlurOperation.cc
index 7bb8cd49bfc..3f98732b403 100644
--- a/source/blender/compositor/operations/COM_BokehBlurOperation.cc
+++ b/source/blender/compositor/operations/COM_BokehBlurOperation.cc
@@ -22,15 +22,18 @@
#include "RE_pipeline.h"
+namespace blender::compositor {
+
BokehBlurOperation::BokehBlurOperation()
{
this->addInputSocket(DataType::Color);
- this->addInputSocket(DataType::Color, COM_SC_NO_RESIZE);
+ this->addInputSocket(DataType::Color, ResizeMode::None);
this->addInputSocket(DataType::Value);
this->addInputSocket(DataType::Value);
this->addOutputSocket(DataType::Color);
- this->setComplex(true);
- this->setOpenCL(true);
+
+ flags.complex = true;
+ flags.open_cl = true;
this->m_size = 1.0f;
this->m_sizeavailable = false;
@@ -76,7 +79,7 @@ void BokehBlurOperation::executePixel(float output[4], int x, int y, void *data)
float tempBoundingBox[4];
float bokeh[4];
- this->m_inputBoundingBoxReader->readSampled(tempBoundingBox, x, y, COM_PS_NEAREST);
+ this->m_inputBoundingBoxReader->readSampled(tempBoundingBox, x, y, PixelSampler::Nearest);
if (tempBoundingBox[0] > 0.0f) {
float multiplier_accum[4] = {0.0f, 0.0f, 0.0f, 0.0f};
MemoryBuffer *inputBuffer = (MemoryBuffer *)data;
@@ -90,7 +93,7 @@ void BokehBlurOperation::executePixel(float output[4], int x, int y, void *data)
zero_v4(color_accum);
if (pixelSize < 2) {
- this->m_inputProgram->readSampled(color_accum, x, y, COM_PS_NEAREST);
+ this->m_inputProgram->readSampled(color_accum, x, y, PixelSampler::Nearest);
multiplier_accum[0] = 1.0f;
multiplier_accum[1] = 1.0f;
multiplier_accum[2] = 1.0f;
@@ -106,16 +109,16 @@ void BokehBlurOperation::executePixel(float output[4], int x, int y, void *data)
maxx = MIN2(maxx, input_rect.xmax);
int step = getStep();
- int offsetadd = getOffsetAdd() * COM_NUM_CHANNELS_COLOR;
+ int offsetadd = getOffsetAdd() * COM_DATA_TYPE_COLOR_CHANNELS;
float m = this->m_bokehDimension / pixelSize;
for (int ny = miny; ny < maxy; ny += step) {
- int bufferindex = ((minx - bufferstartx) * COM_NUM_CHANNELS_COLOR) +
- ((ny - bufferstarty) * COM_NUM_CHANNELS_COLOR * bufferwidth);
+ int bufferindex = ((minx - bufferstartx) * COM_DATA_TYPE_COLOR_CHANNELS) +
+ ((ny - bufferstarty) * COM_DATA_TYPE_COLOR_CHANNELS * bufferwidth);
for (int nx = minx; nx < maxx; nx += step) {
float u = this->m_bokehMidX - (nx - x) * m;
float v = this->m_bokehMidY - (ny - y) * m;
- this->m_inputBokehProgram->readSampled(bokeh, u, v, COM_PS_NEAREST);
+ this->m_inputBokehProgram->readSampled(bokeh, u, v, PixelSampler::Nearest);
madd_v4_v4v4(color_accum, bokeh, &buffer[bufferindex]);
add_v4_v4(multiplier_accum, bokeh);
bufferindex += offsetadd;
@@ -127,7 +130,7 @@ void BokehBlurOperation::executePixel(float output[4], int x, int y, void *data)
output[3] = color_accum[3] * (1.0f / multiplier_accum[3]);
}
else {
- this->m_inputProgram->readSampled(output, x, y, COM_PS_NEAREST);
+ this->m_inputProgram->readSampled(output, x, y, PixelSampler::Nearest);
}
}
@@ -224,7 +227,7 @@ void BokehBlurOperation::updateSize()
{
if (!this->m_sizeavailable) {
float result[4];
- this->getInputSocketReader(3)->readSampled(result, 0, 0, COM_PS_NEAREST);
+ this->getInputSocketReader(3)->readSampled(result, 0, 0, PixelSampler::Nearest);
this->m_size = result[0];
CLAMP(this->m_size, 0.0f, 10.0f);
this->m_sizeavailable = true;
@@ -241,3 +244,5 @@ void BokehBlurOperation::determineResolution(unsigned int resolution[2],
resolution[1] += 2 * this->m_size * max_dim / 100.0f;
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_BokehBlurOperation.h b/source/blender/compositor/operations/COM_BokehBlurOperation.h
index 9bdf8bc4035..3ce06adb5d6 100644
--- a/source/blender/compositor/operations/COM_BokehBlurOperation.h
+++ b/source/blender/compositor/operations/COM_BokehBlurOperation.h
@@ -21,6 +21,8 @@
#include "COM_NodeOperation.h"
#include "COM_QualityStepHelper.h"
+namespace blender::compositor {
+
class BokehBlurOperation : public NodeOperation, public QualityStepHelper {
private:
SocketReader *m_inputProgram;
@@ -78,3 +80,5 @@ class BokehBlurOperation : public NodeOperation, public QualityStepHelper {
void determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2]) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_BokehImageOperation.cc b/source/blender/compositor/operations/COM_BokehImageOperation.cc
index 01f8c81b3b7..63f283b6acc 100644
--- a/source/blender/compositor/operations/COM_BokehImageOperation.cc
+++ b/source/blender/compositor/operations/COM_BokehImageOperation.cc
@@ -19,6 +19,8 @@
#include "COM_BokehImageOperation.h"
#include "BLI_math.h"
+namespace blender::compositor {
+
BokehImageOperation::BokehImageOperation()
{
this->addOutputSocket(DataType::Color);
@@ -124,3 +126,5 @@ void BokehImageOperation::determineResolution(unsigned int resolution[2],
resolution[0] = COM_BLUR_BOKEH_PIXELS;
resolution[1] = COM_BLUR_BOKEH_PIXELS;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_BokehImageOperation.h b/source/blender/compositor/operations/COM_BokehImageOperation.h
index 34fd4f6411b..2e0bc8a34dc 100644
--- a/source/blender/compositor/operations/COM_BokehImageOperation.h
+++ b/source/blender/compositor/operations/COM_BokehImageOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
/**
* \brief The BokehImageOperation class is an operation that creates an image useful to mimic the
*internals of a camera.
@@ -150,3 +152,5 @@ class BokehImageOperation : public NodeOperation {
this->m_deleteData = true;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_BoxMaskOperation.cc b/source/blender/compositor/operations/COM_BoxMaskOperation.cc
index 51b1ea98456..9938d4a85ed 100644
--- a/source/blender/compositor/operations/COM_BoxMaskOperation.cc
+++ b/source/blender/compositor/operations/COM_BoxMaskOperation.cc
@@ -20,6 +20,8 @@
#include "BLI_math.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
BoxMaskOperation::BoxMaskOperation()
{
this->addInputSocket(DataType::Value);
@@ -108,3 +110,5 @@ void BoxMaskOperation::deinitExecution()
this->m_inputMask = nullptr;
this->m_inputValue = nullptr;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_BoxMaskOperation.h b/source/blender/compositor/operations/COM_BoxMaskOperation.h
index 6ffa7d98aff..fdec7bdd8ca 100644
--- a/source/blender/compositor/operations/COM_BoxMaskOperation.h
+++ b/source/blender/compositor/operations/COM_BoxMaskOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class BoxMaskOperation : public NodeOperation {
private:
/**
@@ -63,3 +65,5 @@ class BoxMaskOperation : public NodeOperation {
this->m_maskType = maskType;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_BrightnessOperation.cc b/source/blender/compositor/operations/COM_BrightnessOperation.cc
index 3a6ddd178e8..92cab47318a 100644
--- a/source/blender/compositor/operations/COM_BrightnessOperation.cc
+++ b/source/blender/compositor/operations/COM_BrightnessOperation.cc
@@ -18,6 +18,8 @@
#include "COM_BrightnessOperation.h"
+namespace blender::compositor {
+
BrightnessOperation::BrightnessOperation()
{
this->addInputSocket(DataType::Color);
@@ -89,3 +91,5 @@ void BrightnessOperation::deinitExecution()
this->m_inputBrightnessProgram = nullptr;
this->m_inputContrastProgram = nullptr;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_BrightnessOperation.h b/source/blender/compositor/operations/COM_BrightnessOperation.h
index fa2c80ab3f1..7c33e0b35ec 100644
--- a/source/blender/compositor/operations/COM_BrightnessOperation.h
+++ b/source/blender/compositor/operations/COM_BrightnessOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class BrightnessOperation : public NodeOperation {
private:
/**
@@ -51,3 +53,5 @@ class BrightnessOperation : public NodeOperation {
void setUsePremultiply(bool use_premultiply);
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_CalculateMeanOperation.cc b/source/blender/compositor/operations/COM_CalculateMeanOperation.cc
index fdeecd0cb29..a7ea49aed8d 100644
--- a/source/blender/compositor/operations/COM_CalculateMeanOperation.cc
+++ b/source/blender/compositor/operations/COM_CalculateMeanOperation.cc
@@ -22,14 +22,16 @@
#include "IMB_colormanagement.h"
+namespace blender::compositor {
+
CalculateMeanOperation::CalculateMeanOperation()
{
- this->addInputSocket(DataType::Color, COM_SC_NO_RESIZE);
+ this->addInputSocket(DataType::Color, ResizeMode::None);
this->addOutputSocket(DataType::Value);
this->m_imageReader = nullptr;
this->m_iscalculated = false;
this->m_setting = 1;
- this->setComplex(true);
+ this->flags.complex = true;
}
void CalculateMeanOperation::initExecution()
{
@@ -125,3 +127,5 @@ void CalculateMeanOperation::calculateMean(MemoryBuffer *tile)
}
this->m_result = sum / pixels;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_CalculateMeanOperation.h b/source/blender/compositor/operations/COM_CalculateMeanOperation.h
index e1a5bc3fd75..8b3bf281c93 100644
--- a/source/blender/compositor/operations/COM_CalculateMeanOperation.h
+++ b/source/blender/compositor/operations/COM_CalculateMeanOperation.h
@@ -21,6 +21,8 @@
#include "COM_NodeOperation.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
/**
* \brief base class of CalculateMean, implementing the simple CalculateMean
* \ingroup operation
@@ -67,3 +69,5 @@ class CalculateMeanOperation : public NodeOperation {
protected:
void calculateMean(MemoryBuffer *tile);
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.cc b/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.cc
index 9a1e48177ed..ed554b9ac06 100644
--- a/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.cc
+++ b/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.cc
@@ -22,10 +22,7 @@
#include "IMB_colormanagement.h"
-CalculateStandardDeviationOperation::CalculateStandardDeviationOperation()
-{
- /* pass */
-}
+namespace blender::compositor {
void CalculateStandardDeviationOperation::executePixel(float output[4],
int /*x*/,
@@ -98,3 +95,5 @@ void *CalculateStandardDeviationOperation::initializeTileData(rcti *rect)
unlockMutex();
return nullptr;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.h b/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.h
index f377f96418a..bc4aca69546 100644
--- a/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.h
+++ b/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.h
@@ -21,6 +21,9 @@
#include "COM_CalculateMeanOperation.h"
#include "COM_NodeOperation.h"
#include "DNA_node_types.h"
+
+namespace blender::compositor {
+
/**
* \brief base class of CalculateStandardDeviation,
* implementing the simple CalculateStandardDeviation.
@@ -31,8 +34,6 @@ class CalculateStandardDeviationOperation : public CalculateMeanOperation {
float m_standardDeviation;
public:
- CalculateStandardDeviationOperation();
-
/**
* The inner loop of this operation.
*/
@@ -40,3 +41,5 @@ class CalculateStandardDeviationOperation : public CalculateMeanOperation {
void *initializeTileData(rcti *rect) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ChangeHSVOperation.cc b/source/blender/compositor/operations/COM_ChangeHSVOperation.cc
index e53a416f0dd..eee007ce9e6 100644
--- a/source/blender/compositor/operations/COM_ChangeHSVOperation.cc
+++ b/source/blender/compositor/operations/COM_ChangeHSVOperation.cc
@@ -18,6 +18,8 @@
#include "COM_ChangeHSVOperation.h"
+namespace blender::compositor {
+
ChangeHSVOperation::ChangeHSVOperation()
{
this->addInputSocket(DataType::Color);
@@ -68,3 +70,5 @@ void ChangeHSVOperation::executePixelSampled(float output[4],
output[2] = inputColor1[2] * value[0];
output[3] = inputColor1[3];
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ChangeHSVOperation.h b/source/blender/compositor/operations/COM_ChangeHSVOperation.h
index f448956df16..d38b4be3efe 100644
--- a/source/blender/compositor/operations/COM_ChangeHSVOperation.h
+++ b/source/blender/compositor/operations/COM_ChangeHSVOperation.h
@@ -20,6 +20,8 @@
#include "COM_MixOperation.h"
+namespace blender::compositor {
+
/**
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -45,3 +47,5 @@ class ChangeHSVOperation : public NodeOperation {
*/
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ChannelMatteOperation.cc b/source/blender/compositor/operations/COM_ChannelMatteOperation.cc
index 98105a9dfde..89290978608 100644
--- a/source/blender/compositor/operations/COM_ChannelMatteOperation.cc
+++ b/source/blender/compositor/operations/COM_ChannelMatteOperation.cc
@@ -19,6 +19,8 @@
#include "COM_ChannelMatteOperation.h"
#include "BLI_math.h"
+namespace blender::compositor {
+
ChannelMatteOperation::ChannelMatteOperation()
{
addInputSocket(DataType::Color);
@@ -118,3 +120,5 @@ void ChannelMatteOperation::executePixelSampled(float output[4],
/* Don't make something that was more transparent less transparent. */
output[0] = MIN2(alpha, inColor[3]);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ChannelMatteOperation.h b/source/blender/compositor/operations/COM_ChannelMatteOperation.h
index b295f7709d6..6e9dcccd36e 100644
--- a/source/blender/compositor/operations/COM_ChannelMatteOperation.h
+++ b/source/blender/compositor/operations/COM_ChannelMatteOperation.h
@@ -20,6 +20,8 @@
#include "COM_MixOperation.h"
+namespace blender::compositor {
+
/**
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -70,3 +72,5 @@ class ChannelMatteOperation : public NodeOperation {
this->m_matte_channel = custom2;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ChromaMatteOperation.cc b/source/blender/compositor/operations/COM_ChromaMatteOperation.cc
index 09bf9c76e55..69aa4aac163 100644
--- a/source/blender/compositor/operations/COM_ChromaMatteOperation.cc
+++ b/source/blender/compositor/operations/COM_ChromaMatteOperation.cc
@@ -19,6 +19,8 @@
#include "COM_ChromaMatteOperation.h"
#include "BLI_math.h"
+namespace blender::compositor {
+
ChromaMatteOperation::ChromaMatteOperation()
{
addInputSocket(DataType::Color);
@@ -107,3 +109,5 @@ void ChromaMatteOperation::executePixelSampled(float output[4],
output[0] = inImage[3]; /* make pixel just as transparent as it was before */
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ChromaMatteOperation.h b/source/blender/compositor/operations/COM_ChromaMatteOperation.h
index 11f8c540a4b..48c3a785011 100644
--- a/source/blender/compositor/operations/COM_ChromaMatteOperation.h
+++ b/source/blender/compositor/operations/COM_ChromaMatteOperation.h
@@ -20,6 +20,8 @@
#include "COM_MixOperation.h"
+namespace blender::compositor {
+
/**
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -49,3 +51,5 @@ class ChromaMatteOperation : public NodeOperation {
this->m_settings = nodeChroma;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.cc b/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.cc
index b21e453699b..d1d3752e402 100644
--- a/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.cc
+++ b/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.cc
@@ -19,6 +19,8 @@
#include "COM_ColorBalanceASCCDLOperation.h"
#include "BLI_math.h"
+namespace blender::compositor {
+
inline float colorbalance_cdl(float in, float offset, float power, float slope)
{
float x = in * slope + offset;
@@ -79,3 +81,5 @@ void ColorBalanceASCCDLOperation::deinitExecution()
this->m_inputValueOperation = nullptr;
this->m_inputColorOperation = nullptr;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.h b/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.h
index b7ee5d341ad..5851600190f 100644
--- a/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.h
+++ b/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
/**
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -70,3 +72,5 @@ class ColorBalanceASCCDLOperation : public NodeOperation {
copy_v3_v3(this->m_slope, slope);
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cc b/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cc
index b6ff636bce9..cac16a3f7b0 100644
--- a/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cc
+++ b/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cc
@@ -19,6 +19,8 @@
#include "COM_ColorBalanceLGGOperation.h"
#include "BLI_math.h"
+namespace blender::compositor {
+
inline float colorbalance_lgg(float in, float lift_lgg, float gamma_inv, float gain)
{
/* 1:1 match with the sequencer with linear/srgb conversions, the conversion isn't pretty
@@ -84,3 +86,5 @@ void ColorBalanceLGGOperation::deinitExecution()
this->m_inputValueOperation = nullptr;
this->m_inputColorOperation = nullptr;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.h b/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.h
index 4b2db3da134..23f70247b66 100644
--- a/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.h
+++ b/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
/**
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -70,3 +72,5 @@ class ColorBalanceLGGOperation : public NodeOperation {
copy_v3_v3(this->m_gamma_inv, gamma_inv);
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ColorCorrectionOperation.cc b/source/blender/compositor/operations/COM_ColorCorrectionOperation.cc
index b2578451180..168e9b57eb2 100644
--- a/source/blender/compositor/operations/COM_ColorCorrectionOperation.cc
+++ b/source/blender/compositor/operations/COM_ColorCorrectionOperation.cc
@@ -21,6 +21,8 @@
#include "IMB_colormanagement.h"
+namespace blender::compositor {
+
ColorCorrectionOperation::ColorCorrectionOperation()
{
this->addInputSocket(DataType::Color);
@@ -160,3 +162,5 @@ void ColorCorrectionOperation::deinitExecution()
this->m_inputImage = nullptr;
this->m_inputMask = nullptr;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ColorCorrectionOperation.h b/source/blender/compositor/operations/COM_ColorCorrectionOperation.h
index be0c9c198cf..c5826ed0152 100644
--- a/source/blender/compositor/operations/COM_ColorCorrectionOperation.h
+++ b/source/blender/compositor/operations/COM_ColorCorrectionOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class ColorCorrectionOperation : public NodeOperation {
private:
/**
@@ -68,3 +70,5 @@ class ColorCorrectionOperation : public NodeOperation {
this->m_blueChannelEnabled = enabled;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ColorCurveOperation.cc b/source/blender/compositor/operations/COM_ColorCurveOperation.cc
index 35218cef7cc..cb0565a81a2 100644
--- a/source/blender/compositor/operations/COM_ColorCurveOperation.cc
+++ b/source/blender/compositor/operations/COM_ColorCurveOperation.cc
@@ -22,6 +22,8 @@
#include "MEM_guardedalloc.h"
+namespace blender::compositor {
+
ColorCurveOperation::ColorCurveOperation()
{
this->addInputSocket(DataType::Value);
@@ -151,3 +153,5 @@ void ConstantLevelColorCurveOperation::deinitExecution()
this->m_inputFacProgram = nullptr;
this->m_inputImageProgram = nullptr;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ColorCurveOperation.h b/source/blender/compositor/operations/COM_ColorCurveOperation.h
index d57ff81bbe7..6fc7759b8d2 100644
--- a/source/blender/compositor/operations/COM_ColorCurveOperation.h
+++ b/source/blender/compositor/operations/COM_ColorCurveOperation.h
@@ -22,6 +22,8 @@
#include "COM_NodeOperation.h"
#include "DNA_color_types.h"
+namespace blender::compositor {
+
class ColorCurveOperation : public CurveBaseOperation {
private:
/**
@@ -88,3 +90,5 @@ class ConstantLevelColorCurveOperation : public CurveBaseOperation {
copy_v3_v3(this->m_white, white);
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ColorExposureOperation.cc b/source/blender/compositor/operations/COM_ColorExposureOperation.cc
index 0e24620846e..1512ff87658 100644
--- a/source/blender/compositor/operations/COM_ColorExposureOperation.cc
+++ b/source/blender/compositor/operations/COM_ColorExposureOperation.cc
@@ -18,6 +18,8 @@
#include "COM_ColorExposureOperation.h"
+namespace blender::compositor {
+
ExposureOperation::ExposureOperation()
{
this->addInputSocket(DataType::Color);
@@ -55,3 +57,5 @@ void ExposureOperation::deinitExecution()
this->m_inputProgram = nullptr;
this->m_inputExposureProgram = nullptr;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ColorExposureOperation.h b/source/blender/compositor/operations/COM_ColorExposureOperation.h
index 981caeb2663..0cfaa059e41 100644
--- a/source/blender/compositor/operations/COM_ColorExposureOperation.h
+++ b/source/blender/compositor/operations/COM_ColorExposureOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class ExposureOperation : public NodeOperation {
private:
/**
@@ -46,3 +48,5 @@ class ExposureOperation : public NodeOperation {
*/
void deinitExecution() override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ColorMatteOperation.cc b/source/blender/compositor/operations/COM_ColorMatteOperation.cc
index cc7a81f68d1..89f56ac4aae 100644
--- a/source/blender/compositor/operations/COM_ColorMatteOperation.cc
+++ b/source/blender/compositor/operations/COM_ColorMatteOperation.cc
@@ -19,6 +19,8 @@
#include "COM_ColorMatteOperation.h"
#include "BLI_math.h"
+namespace blender::compositor {
+
ColorMatteOperation::ColorMatteOperation()
{
addInputSocket(DataType::Color);
@@ -79,3 +81,5 @@ void ColorMatteOperation::executePixelSampled(float output[4],
output[0] = inColor[3]; /* make pixel just as transparent as it was before */
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ColorMatteOperation.h b/source/blender/compositor/operations/COM_ColorMatteOperation.h
index 643dc83b3d9..439a3b0741d 100644
--- a/source/blender/compositor/operations/COM_ColorMatteOperation.h
+++ b/source/blender/compositor/operations/COM_ColorMatteOperation.h
@@ -20,6 +20,8 @@
#include "COM_MixOperation.h"
+namespace blender::compositor {
+
/**
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -49,3 +51,5 @@ class ColorMatteOperation : public NodeOperation {
this->m_settings = nodeChroma;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ColorRampOperation.cc b/source/blender/compositor/operations/COM_ColorRampOperation.cc
index 4fde951fd3c..0ee65a6529e 100644
--- a/source/blender/compositor/operations/COM_ColorRampOperation.cc
+++ b/source/blender/compositor/operations/COM_ColorRampOperation.cc
@@ -20,6 +20,8 @@
#include "BKE_colorband.h"
+namespace blender::compositor {
+
ColorRampOperation::ColorRampOperation()
{
this->addInputSocket(DataType::Value);
@@ -48,3 +50,5 @@ void ColorRampOperation::deinitExecution()
{
this->m_inputProgram = nullptr;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ColorRampOperation.h b/source/blender/compositor/operations/COM_ColorRampOperation.h
index 8e725ccf709..d32af9bea24 100644
--- a/source/blender/compositor/operations/COM_ColorRampOperation.h
+++ b/source/blender/compositor/operations/COM_ColorRampOperation.h
@@ -21,6 +21,8 @@
#include "COM_NodeOperation.h"
#include "DNA_texture_types.h"
+namespace blender::compositor {
+
class ColorRampOperation : public NodeOperation {
private:
/**
@@ -52,3 +54,5 @@ class ColorRampOperation : public NodeOperation {
this->m_colorBand = colorBand;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ColorSpillOperation.cc b/source/blender/compositor/operations/COM_ColorSpillOperation.cc
index 8e8eaabfc6b..7dc7e2775fc 100644
--- a/source/blender/compositor/operations/COM_ColorSpillOperation.cc
+++ b/source/blender/compositor/operations/COM_ColorSpillOperation.cc
@@ -20,6 +20,8 @@
#include "BLI_math.h"
#define AVG(a, b) ((a + b) / 2)
+namespace blender::compositor {
+
ColorSpillOperation::ColorSpillOperation()
{
addInputSocket(DataType::Color);
@@ -115,3 +117,5 @@ void ColorSpillOperation::executePixelSampled(float output[4],
copy_v4_v4(output, input);
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ColorSpillOperation.h b/source/blender/compositor/operations/COM_ColorSpillOperation.h
index e8cd15fb3bc..9b82e720527 100644
--- a/source/blender/compositor/operations/COM_ColorSpillOperation.h
+++ b/source/blender/compositor/operations/COM_ColorSpillOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
/**
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -64,3 +66,5 @@ class ColorSpillOperation : public NodeOperation {
float calculateMapValue(float fac, float *input);
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_CompositorOperation.cc b/source/blender/compositor/operations/COM_CompositorOperation.cc
index 0454bae8b38..94d41b28f5d 100644
--- a/source/blender/compositor/operations/COM_CompositorOperation.cc
+++ b/source/blender/compositor/operations/COM_CompositorOperation.cc
@@ -31,6 +31,8 @@
#include "PIL_time.h"
+namespace blender::compositor {
+
CompositorOperation::CompositorOperation()
{
this->addInputSocket(DataType::Color);
@@ -50,6 +52,8 @@ CompositorOperation::CompositorOperation()
this->m_scene = nullptr;
this->m_sceneName[0] = '\0';
this->m_viewName = nullptr;
+
+ flags.use_render_border = true;
}
void CompositorOperation::initExecution()
@@ -147,7 +151,7 @@ void CompositorOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/)
int y2 = rect->ymax;
int offset = (y1 * this->getWidth() + x1);
int add = (this->getWidth() - (x2 - x1));
- int offset4 = offset * COM_NUM_CHANNELS_COLOR;
+ int offset4 = offset * COM_DATA_TYPE_COLOR_CHANNELS;
int x;
int y;
bool breaked = false;
@@ -196,23 +200,23 @@ void CompositorOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/)
for (x = x1; x < x2 && (!breaked); x++) {
int input_x = x + dx, input_y = y + dy;
- this->m_imageInput->readSampled(color, input_x, input_y, COM_PS_NEAREST);
+ this->m_imageInput->readSampled(color, input_x, input_y, PixelSampler::Nearest);
if (this->m_useAlphaInput) {
- this->m_alphaInput->readSampled(&(color[3]), input_x, input_y, COM_PS_NEAREST);
+ this->m_alphaInput->readSampled(&(color[3]), input_x, input_y, PixelSampler::Nearest);
}
copy_v4_v4(buffer + offset4, color);
- this->m_depthInput->readSampled(color, input_x, input_y, COM_PS_NEAREST);
+ this->m_depthInput->readSampled(color, input_x, input_y, PixelSampler::Nearest);
zbuffer[offset] = color[0];
- offset4 += COM_NUM_CHANNELS_COLOR;
+ offset4 += COM_DATA_TYPE_COLOR_CHANNELS;
offset++;
if (isBraked()) {
breaked = true;
}
}
offset += add;
- offset4 += add * COM_NUM_CHANNELS_COLOR;
+ offset4 += add * COM_DATA_TYPE_COLOR_CHANNELS;
}
}
@@ -242,3 +246,5 @@ void CompositorOperation::determineResolution(unsigned int resolution[2],
resolution[0] = width;
resolution[1] = height;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_CompositorOperation.h b/source/blender/compositor/operations/COM_CompositorOperation.h
index 1c964a80093..65988c86cc5 100644
--- a/source/blender/compositor/operations/COM_CompositorOperation.h
+++ b/source/blender/compositor/operations/COM_CompositorOperation.h
@@ -24,6 +24,8 @@
struct Scene;
+namespace blender::compositor {
+
/**
* \brief Compositor output operation
*/
@@ -109,9 +111,9 @@ class CompositorOperation : public NodeOperation {
}
void initExecution() override;
void deinitExecution() override;
- CompositorPriority getRenderPriority() const override
+ eCompositorPriority getRenderPriority() const override
{
- return CompositorPriority::Medium;
+ return eCompositorPriority::Medium;
}
void determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2]) override;
@@ -124,3 +126,5 @@ class CompositorOperation : public NodeOperation {
this->m_active = active;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ConvertColorProfileOperation.cc b/source/blender/compositor/operations/COM_ConvertColorProfileOperation.cc
index 431f0e9c880..c00fe5d5f61 100644
--- a/source/blender/compositor/operations/COM_ConvertColorProfileOperation.cc
+++ b/source/blender/compositor/operations/COM_ConvertColorProfileOperation.cc
@@ -20,6 +20,8 @@
#include "IMB_imbuf.h"
+namespace blender::compositor {
+
ConvertColorProfileOperation::ConvertColorProfileOperation()
{
this->addInputSocket(DataType::Color);
@@ -48,3 +50,5 @@ void ConvertColorProfileOperation::deinitExecution()
{
this->m_inputOperation = nullptr;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ConvertColorProfileOperation.h b/source/blender/compositor/operations/COM_ConvertColorProfileOperation.h
index 927a5a7fef1..6162408501b 100644
--- a/source/blender/compositor/operations/COM_ConvertColorProfileOperation.h
+++ b/source/blender/compositor/operations/COM_ConvertColorProfileOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
/**
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -80,3 +82,5 @@ class ConvertColorProfileOperation : public NodeOperation {
this->m_predivided = predivided;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.cc b/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.cc
index 17b351cb14c..57027c11949 100644
--- a/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.cc
+++ b/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.cc
@@ -21,6 +21,8 @@
#include "BLI_math.h"
#include "DNA_camera_types.h"
+namespace blender::compositor {
+
ConvertDepthToRadiusOperation::ConvertDepthToRadiusOperation()
{
this->addInputSocket(DataType::Value);
@@ -113,3 +115,5 @@ void ConvertDepthToRadiusOperation::deinitExecution()
{
this->m_inputOperation = nullptr;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.h b/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.h
index 6a7cc4e004a..1f4e856b128 100644
--- a/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.h
+++ b/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.h
@@ -21,6 +21,9 @@
#include "COM_FastGaussianBlurOperation.h"
#include "COM_NodeOperation.h"
#include "DNA_object_types.h"
+
+namespace blender::compositor {
+
/**
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -81,3 +84,5 @@ class ConvertDepthToRadiusOperation : public NodeOperation {
this->m_blurPostOperation = operation;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ConvertOperation.cc b/source/blender/compositor/operations/COM_ConvertOperation.cc
index 648b3f0b30a..384936533c7 100644
--- a/source/blender/compositor/operations/COM_ConvertOperation.cc
+++ b/source/blender/compositor/operations/COM_ConvertOperation.cc
@@ -18,8 +18,12 @@
#include "COM_ConvertOperation.h"
+#include "BLI_color.hh"
+
#include "IMB_colormanagement.h"
+namespace blender::compositor {
+
ConvertBaseOperation::ConvertBaseOperation()
{
this->m_inputOperation = nullptr;
@@ -353,21 +357,10 @@ void ConvertPremulToStraightOperation::executePixelSampled(float output[4],
float y,
PixelSampler sampler)
{
- float inputValue[4];
- float alpha;
-
- this->m_inputOperation->readSampled(inputValue, x, y, sampler);
- alpha = inputValue[3];
-
- if (fabsf(alpha) < 1e-5f) {
- zero_v3(output);
- }
- else {
- mul_v3_v3fl(output, inputValue, 1.0f / alpha);
- }
-
- /* never touches the alpha */
- output[3] = alpha;
+ ColorSceneLinear4f<eAlpha::Premultiplied> input;
+ this->m_inputOperation->readSampled(input, x, y, sampler);
+ ColorSceneLinear4f<eAlpha::Straight> converted = input.unpremultiply_alpha();
+ copy_v4_v4(output, converted);
}
/* ******** Straight to Premul ******** */
@@ -383,16 +376,10 @@ void ConvertStraightToPremulOperation::executePixelSampled(float output[4],
float y,
PixelSampler sampler)
{
- float inputValue[4];
- float alpha;
-
- this->m_inputOperation->readSampled(inputValue, x, y, sampler);
- alpha = inputValue[3];
-
- mul_v3_v3fl(output, inputValue, alpha);
-
- /* never touches the alpha */
- output[3] = alpha;
+ ColorSceneLinear4f<eAlpha::Straight> input;
+ this->m_inputOperation->readSampled(input, x, y, sampler);
+ ColorSceneLinear4f<eAlpha::Premultiplied> converted = input.premultiply_alpha();
+ copy_v4_v4(output, converted);
}
/* ******** Separate Channels ******** */
@@ -478,3 +465,5 @@ void CombineChannelsOperation::executePixelSampled(float output[4],
output[3] = input[0];
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ConvertOperation.h b/source/blender/compositor/operations/COM_ConvertOperation.h
index 7e359a30d6c..7a726e35c7c 100644
--- a/source/blender/compositor/operations/COM_ConvertOperation.h
+++ b/source/blender/compositor/operations/COM_ConvertOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class ConvertBaseOperation : public NodeOperation {
protected:
SocketReader *m_inputOperation;
@@ -182,3 +184,5 @@ class CombineChannelsOperation : public NodeOperation {
void initExecution() override;
void deinitExecution() override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.cc b/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.cc
index a5f2ae404e3..5ead300a368 100644
--- a/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.cc
+++ b/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.cc
@@ -19,10 +19,7 @@
#include "COM_ConvolutionEdgeFilterOperation.h"
#include "BLI_math.h"
-ConvolutionEdgeFilterOperation::ConvolutionEdgeFilterOperation()
-{
- /* pass */
-}
+namespace blender::compositor {
void ConvolutionEdgeFilterOperation::executePixel(float output[4], int x, int y, void * /*data*/)
{
@@ -97,3 +94,5 @@ void ConvolutionEdgeFilterOperation::executePixel(float output[4], int x, int y,
output[2] = MAX2(output[2], 0.0f);
output[3] = MAX2(output[3], 0.0f);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.h b/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.h
index 4ee72ffdd97..319b424bd4a 100644
--- a/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.h
+++ b/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.h
@@ -20,8 +20,11 @@
#include "COM_ConvolutionFilterOperation.h"
+namespace blender::compositor {
+
class ConvolutionEdgeFilterOperation : public ConvolutionFilterOperation {
public:
- ConvolutionEdgeFilterOperation();
void executePixel(float output[4], int x, int y, void *data) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc b/source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc
index f80144fb06d..72cbbf4283a 100644
--- a/source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc
+++ b/source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc
@@ -22,6 +22,8 @@
#include "MEM_guardedalloc.h"
+namespace blender::compositor {
+
ConvolutionFilterOperation::ConvolutionFilterOperation()
{
this->addInputSocket(DataType::Color);
@@ -29,7 +31,7 @@ ConvolutionFilterOperation::ConvolutionFilterOperation()
this->addOutputSocket(DataType::Color);
this->setResolutionInputSocketIndex(0);
this->m_inputOperation = nullptr;
- this->setComplex(true);
+ this->flags.complex = true;
}
void ConvolutionFilterOperation::initExecution()
{
@@ -124,3 +126,5 @@ bool ConvolutionFilterOperation::determineDependingAreaOfInterest(
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ConvolutionFilterOperation.h b/source/blender/compositor/operations/COM_ConvolutionFilterOperation.h
index 08353fecca3..16dee502929 100644
--- a/source/blender/compositor/operations/COM_ConvolutionFilterOperation.h
+++ b/source/blender/compositor/operations/COM_ConvolutionFilterOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class ConvolutionFilterOperation : public NodeOperation {
private:
int m_filterWidth;
@@ -42,3 +44,5 @@ class ConvolutionFilterOperation : public NodeOperation {
void initExecution() override;
void deinitExecution() override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_CropOperation.cc b/source/blender/compositor/operations/COM_CropOperation.cc
index 55a1e505ec8..f12d93bc8d3 100644
--- a/source/blender/compositor/operations/COM_CropOperation.cc
+++ b/source/blender/compositor/operations/COM_CropOperation.cc
@@ -19,9 +19,11 @@
#include "COM_CropOperation.h"
#include "BLI_math.h"
+namespace blender::compositor {
+
CropBaseOperation::CropBaseOperation()
{
- this->addInputSocket(DataType::Color, COM_SC_NO_RESIZE);
+ this->addInputSocket(DataType::Color, ResizeMode::None);
this->addOutputSocket(DataType::Color);
this->m_inputOperation = nullptr;
this->m_settings = nullptr;
@@ -133,3 +135,5 @@ void CropImageOperation::executePixelSampled(float output[4],
zero_v4(output);
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_CropOperation.h b/source/blender/compositor/operations/COM_CropOperation.h
index d2b9aee0bf3..acdff79a77c 100644
--- a/source/blender/compositor/operations/COM_CropOperation.h
+++ b/source/blender/compositor/operations/COM_CropOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class CropBaseOperation : public NodeOperation {
protected:
SocketReader *m_inputOperation;
@@ -64,3 +66,5 @@ class CropImageOperation : public CropBaseOperation {
unsigned int preferredResolution[2]) override;
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_CryptomatteOperation.cc b/source/blender/compositor/operations/COM_CryptomatteOperation.cc
index 8d42a756f51..52ae1d6d5b5 100644
--- a/source/blender/compositor/operations/COM_CryptomatteOperation.cc
+++ b/source/blender/compositor/operations/COM_CryptomatteOperation.cc
@@ -18,14 +18,16 @@
#include "COM_CryptomatteOperation.h"
+namespace blender::compositor {
+
CryptomatteOperation::CryptomatteOperation(size_t num_inputs)
{
+ inputs.resize(num_inputs);
for (size_t i = 0; i < num_inputs; i++) {
this->addInputSocket(DataType::Color);
}
- inputs.resize(num_inputs);
this->addOutputSocket(DataType::Color);
- this->setComplex(true);
+ this->flags.complex = true;
}
void CryptomatteOperation::initExecution()
@@ -38,7 +40,7 @@ void CryptomatteOperation::initExecution()
void CryptomatteOperation::addObjectIndex(float objectIndex)
{
if (objectIndex != 0.0f) {
- m_objectIndex.push_back(objectIndex);
+ m_objectIndex.append(objectIndex);
}
}
@@ -58,13 +60,15 @@ void CryptomatteOperation::executePixel(float output[4], int x, int y, void *dat
output[1] = ((float)((m3hash << 8)) / (float)UINT32_MAX);
output[2] = ((float)((m3hash << 16)) / (float)UINT32_MAX);
}
- for (size_t i = 0; i < m_objectIndex.size(); i++) {
- if (m_objectIndex[i] == input[0]) {
+ for (float hash : m_objectIndex) {
+ if (input[0] == hash) {
output[3] += input[1];
}
- if (m_objectIndex[i] == input[2]) {
+ if (input[2] == hash) {
output[3] += input[3];
}
}
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_CryptomatteOperation.h b/source/blender/compositor/operations/COM_CryptomatteOperation.h
index a9cef7fe24b..1b91358d228 100644
--- a/source/blender/compositor/operations/COM_CryptomatteOperation.h
+++ b/source/blender/compositor/operations/COM_CryptomatteOperation.h
@@ -20,12 +20,14 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class CryptomatteOperation : public NodeOperation {
private:
- std::vector<float> m_objectIndex;
+ Vector<float> m_objectIndex;
public:
- std::vector<SocketReader *> inputs;
+ Vector<SocketReader *> inputs;
CryptomatteOperation(size_t num_inputs = 6);
@@ -34,3 +36,5 @@ class CryptomatteOperation : public NodeOperation {
void addObjectIndex(float objectIndex);
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_CurveBaseOperation.cc b/source/blender/compositor/operations/COM_CurveBaseOperation.cc
index b58efcf0cca..8f655964570 100644
--- a/source/blender/compositor/operations/COM_CurveBaseOperation.cc
+++ b/source/blender/compositor/operations/COM_CurveBaseOperation.cc
@@ -20,6 +20,8 @@
#include "BKE_colortools.h"
+namespace blender::compositor {
+
CurveBaseOperation::CurveBaseOperation()
{
this->m_curveMapping = nullptr;
@@ -53,3 +55,5 @@ void CurveBaseOperation::setCurveMapping(CurveMapping *mapping)
}
this->m_curveMapping = BKE_curvemapping_copy(mapping);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_CurveBaseOperation.h b/source/blender/compositor/operations/COM_CurveBaseOperation.h
index 15e484f4762..fff0f3168ba 100644
--- a/source/blender/compositor/operations/COM_CurveBaseOperation.h
+++ b/source/blender/compositor/operations/COM_CurveBaseOperation.h
@@ -21,6 +21,8 @@
#include "COM_NodeOperation.h"
#include "DNA_color_types.h"
+namespace blender::compositor {
+
class CurveBaseOperation : public NodeOperation {
protected:
/**
@@ -40,3 +42,5 @@ class CurveBaseOperation : public NodeOperation {
void setCurveMapping(CurveMapping *mapping);
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DenoiseOperation.cc b/source/blender/compositor/operations/COM_DenoiseOperation.cc
index 66d0f3fd9bd..ec11ad4d69a 100644
--- a/source/blender/compositor/operations/COM_DenoiseOperation.cc
+++ b/source/blender/compositor/operations/COM_DenoiseOperation.cc
@@ -26,6 +26,8 @@ static pthread_mutex_t oidn_lock = BLI_MUTEX_INITIALIZER;
#endif
#include <iostream>
+namespace blender::compositor {
+
DenoiseOperation::DenoiseOperation()
{
this->addInputSocket(DataType::Color);
@@ -99,6 +101,11 @@ void DenoiseOperation::generateDenoise(float *data,
if (BLI_cpu_support_sse41())
# endif
{
+ /* Since it's memory intensive, it's better to run only one instance of OIDN at a time.
+ * OpenImageDenoise is multithreaded internally and should use all available cores nonetheless.
+ */
+ BLI_mutex_lock(&oidn_lock);
+
oidn::DeviceRef device = oidn::newDevice();
device.commit();
@@ -143,10 +150,6 @@ void DenoiseOperation::generateDenoise(float *data,
}
filter.commit();
- /* Since it's memory intensive, it's better to run only one instance of OIDN at a time.
- * OpenImageDenoise is multithreaded internally and should use all available cores nonetheless.
- */
- BLI_mutex_lock(&oidn_lock);
filter.execute();
BLI_mutex_unlock(&oidn_lock);
@@ -164,3 +167,5 @@ void DenoiseOperation::generateDenoise(float *data,
inputBufferColor,
sizeof(float[4]) * inputTileColor->getWidth() * inputTileColor->getHeight());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DenoiseOperation.h b/source/blender/compositor/operations/COM_DenoiseOperation.h
index 162fe70617f..a9298c17e92 100644
--- a/source/blender/compositor/operations/COM_DenoiseOperation.h
+++ b/source/blender/compositor/operations/COM_DenoiseOperation.h
@@ -21,6 +21,8 @@
#include "COM_SingleThreadedOperation.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
class DenoiseOperation : public SingleThreadedOperation {
private:
/**
@@ -64,3 +66,5 @@ class DenoiseOperation : public SingleThreadedOperation {
MemoryBuffer *createMemoryBuffer(rcti *rect) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DespeckleOperation.cc b/source/blender/compositor/operations/COM_DespeckleOperation.cc
index 813ae07a97a..fc8778c7d2e 100644
--- a/source/blender/compositor/operations/COM_DespeckleOperation.cc
+++ b/source/blender/compositor/operations/COM_DespeckleOperation.cc
@@ -22,6 +22,8 @@
#include "BLI_utildefines.h"
+namespace blender::compositor {
+
DespeckleOperation::DespeckleOperation()
{
this->addInputSocket(DataType::Color);
@@ -29,7 +31,7 @@ DespeckleOperation::DespeckleOperation()
this->addOutputSocket(DataType::Color);
this->setResolutionInputSocketIndex(0);
this->m_inputOperation = nullptr;
- this->setComplex(true);
+ this->flags.complex = true;
}
void DespeckleOperation::initExecution()
{
@@ -141,3 +143,5 @@ bool DespeckleOperation::determineDependingAreaOfInterest(rcti *input,
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DespeckleOperation.h b/source/blender/compositor/operations/COM_DespeckleOperation.h
index dee355696af..e8d3461d2ec 100644
--- a/source/blender/compositor/operations/COM_DespeckleOperation.h
+++ b/source/blender/compositor/operations/COM_DespeckleOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class DespeckleOperation : public NodeOperation {
private:
float m_threshold;
@@ -51,3 +53,5 @@ class DespeckleOperation : public NodeOperation {
void initExecution() override;
void deinitExecution() override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DifferenceMatteOperation.cc b/source/blender/compositor/operations/COM_DifferenceMatteOperation.cc
index 1ce91aeb4c3..e380131634f 100644
--- a/source/blender/compositor/operations/COM_DifferenceMatteOperation.cc
+++ b/source/blender/compositor/operations/COM_DifferenceMatteOperation.cc
@@ -19,6 +19,8 @@
#include "COM_DifferenceMatteOperation.h"
#include "BLI_math.h"
+namespace blender::compositor {
+
DifferenceMatteOperation::DifferenceMatteOperation()
{
addInputSocket(DataType::Color);
@@ -83,3 +85,5 @@ void DifferenceMatteOperation::executePixelSampled(float output[4],
output[0] = inColor1[3];
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DifferenceMatteOperation.h b/source/blender/compositor/operations/COM_DifferenceMatteOperation.h
index ded819850f8..d3963fee1c1 100644
--- a/source/blender/compositor/operations/COM_DifferenceMatteOperation.h
+++ b/source/blender/compositor/operations/COM_DifferenceMatteOperation.h
@@ -20,6 +20,8 @@
#include "COM_MixOperation.h"
+namespace blender::compositor {
+
/**
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -49,3 +51,5 @@ class DifferenceMatteOperation : public NodeOperation {
this->m_settings = nodeChroma;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DilateErodeOperation.cc b/source/blender/compositor/operations/COM_DilateErodeOperation.cc
index 33ddf9187b0..9e18a8e2f2c 100644
--- a/source/blender/compositor/operations/COM_DilateErodeOperation.cc
+++ b/source/blender/compositor/operations/COM_DilateErodeOperation.cc
@@ -22,12 +22,14 @@
#include "MEM_guardedalloc.h"
+namespace blender::compositor {
+
// DilateErode Distance Threshold
DilateErodeThresholdOperation::DilateErodeThresholdOperation()
{
this->addInputSocket(DataType::Value);
this->addOutputSocket(DataType::Value);
- this->setComplex(true);
+ this->flags.complex = true;
this->m_inputProgram = nullptr;
this->m_inset = 0.0f;
this->m__switch = 0.5f;
@@ -163,10 +165,10 @@ DilateDistanceOperation::DilateDistanceOperation()
{
this->addInputSocket(DataType::Value);
this->addOutputSocket(DataType::Value);
- this->setComplex(true);
this->m_inputProgram = nullptr;
this->m_distance = 0.0f;
- this->setOpenCL(true);
+ flags.complex = true;
+ flags.open_cl = true;
}
void DilateDistanceOperation::initExecution()
{
@@ -321,7 +323,7 @@ DilateStepOperation::DilateStepOperation()
{
this->addInputSocket(DataType::Value);
this->addOutputSocket(DataType::Value);
- this->setComplex(true);
+ this->flags.complex = true;
this->m_inputProgram = nullptr;
}
void DilateStepOperation::initExecution()
@@ -568,3 +570,5 @@ void *ErodeStepOperation::initializeTileData(rcti *rect)
return result;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DilateErodeOperation.h b/source/blender/compositor/operations/COM_DilateErodeOperation.h
index 7497fb8ab08..a489e293e8e 100644
--- a/source/blender/compositor/operations/COM_DilateErodeOperation.h
+++ b/source/blender/compositor/operations/COM_DilateErodeOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class DilateErodeThresholdOperation : public NodeOperation {
private:
/**
@@ -180,3 +182,5 @@ class ErodeStepOperation : public DilateStepOperation {
void *initializeTileData(rcti *rect) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DirectionalBlurOperation.cc b/source/blender/compositor/operations/COM_DirectionalBlurOperation.cc
index 1a2701a681d..97bdc25af3b 100644
--- a/source/blender/compositor/operations/COM_DirectionalBlurOperation.cc
+++ b/source/blender/compositor/operations/COM_DirectionalBlurOperation.cc
@@ -23,13 +23,14 @@
#include "RE_pipeline.h"
+namespace blender::compositor {
+
DirectionalBlurOperation::DirectionalBlurOperation()
{
this->addInputSocket(DataType::Color);
this->addOutputSocket(DataType::Color);
- this->setComplex(true);
-
- this->setOpenCL(true);
+ flags.complex = true;
+ flags.open_cl = true;
this->m_inputProgram = nullptr;
}
@@ -66,7 +67,7 @@ void DirectionalBlurOperation::executePixel(float output[4], int x, int y, void
const int iterations = pow(2.0f, this->m_data->iter);
float col[4] = {0.0f, 0.0f, 0.0f, 0.0f};
float col2[4] = {0.0f, 0.0f, 0.0f, 0.0f};
- this->m_inputProgram->readSampled(col2, x, y, COM_PS_BILINEAR);
+ this->m_inputProgram->readSampled(col2, x, y, PixelSampler::Bilinear);
float ltx = this->m_tx;
float lty = this->m_ty;
float lsc = this->m_sc;
@@ -82,7 +83,7 @@ void DirectionalBlurOperation::executePixel(float output[4], int x, int y, void
this->m_inputProgram->readSampled(col,
cs * u + ss * v + this->m_center_x_pix,
cs * v - ss * u + this->m_center_y_pix,
- COM_PS_BILINEAR);
+ PixelSampler::Bilinear);
add_v4_v4(col2, col);
@@ -144,3 +145,5 @@ bool DirectionalBlurOperation::determineDependingAreaOfInterest(rcti * /*input*/
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DirectionalBlurOperation.h b/source/blender/compositor/operations/COM_DirectionalBlurOperation.h
index daf6bc75254..5555520462b 100644
--- a/source/blender/compositor/operations/COM_DirectionalBlurOperation.h
+++ b/source/blender/compositor/operations/COM_DirectionalBlurOperation.h
@@ -21,6 +21,8 @@
#include "COM_NodeOperation.h"
#include "COM_QualityStepHelper.h"
+namespace blender::compositor {
+
class DirectionalBlurOperation : public NodeOperation, public QualityStepHelper {
private:
SocketReader *m_inputProgram;
@@ -64,3 +66,5 @@ class DirectionalBlurOperation : public NodeOperation, public QualityStepHelper
std::list<cl_mem> *clMemToCleanUp,
std::list<cl_kernel> *clKernelsToCleanUp) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DisplaceOperation.cc b/source/blender/compositor/operations/COM_DisplaceOperation.cc
index 12c7d29a210..9f3f5cfe489 100644
--- a/source/blender/compositor/operations/COM_DisplaceOperation.cc
+++ b/source/blender/compositor/operations/COM_DisplaceOperation.cc
@@ -20,6 +20,8 @@
#include "BLI_math.h"
#include "BLI_utildefines.h"
+namespace blender::compositor {
+
DisplaceOperation::DisplaceOperation()
{
this->addInputSocket(DataType::Color);
@@ -27,7 +29,7 @@ DisplaceOperation::DisplaceOperation()
this->addInputSocket(DataType::Value);
this->addInputSocket(DataType::Value);
this->addOutputSocket(DataType::Color);
- this->setComplex(true);
+ this->flags.complex = true;
this->m_inputColorProgram = nullptr;
this->m_inputVectorProgram = nullptr;
@@ -56,7 +58,7 @@ void DisplaceOperation::executePixelSampled(float output[4],
pixelTransform(xy, uv, deriv);
if (is_zero_v2(deriv[0]) && is_zero_v2(deriv[1])) {
- this->m_inputColorProgram->readSampled(output, uv[0], uv[1], COM_PS_BILINEAR);
+ this->m_inputColorProgram->readSampled(output, uv[0], uv[1], PixelSampler::Bilinear);
}
else {
/* EWA filtering (without nearest it gets blurry with NO distortion) */
@@ -76,7 +78,7 @@ bool DisplaceOperation::read_displacement(
}
float col[4];
- m_inputVectorProgram->readSampled(col, x, y, COM_PS_BILINEAR);
+ m_inputVectorProgram->readSampled(col, x, y, PixelSampler::Bilinear);
r_u = origin[0] - col[0] * xscale;
r_v = origin[1] - col[1] * yscale;
return true;
@@ -88,9 +90,9 @@ void DisplaceOperation::pixelTransform(const float xy[2], float r_uv[2], float r
float uv[2]; /* temporary variables for derivative estimation */
int num;
- m_inputScaleXProgram->readSampled(col, xy[0], xy[1], COM_PS_NEAREST);
+ m_inputScaleXProgram->readSampled(col, xy[0], xy[1], PixelSampler::Nearest);
float xs = col[0];
- m_inputScaleYProgram->readSampled(col, xy[0], xy[1], COM_PS_NEAREST);
+ m_inputScaleYProgram->readSampled(col, xy[0], xy[1], PixelSampler::Nearest);
float ys = col[0];
/* clamp x and y displacement to triple image resolution -
* to prevent hangs from huge values mistakenly plugged in eg. z buffers */
@@ -192,3 +194,5 @@ bool DisplaceOperation::determineDependingAreaOfInterest(rcti *input,
return false;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DisplaceOperation.h b/source/blender/compositor/operations/COM_DisplaceOperation.h
index 368ed29ff0b..fd82692f687 100644
--- a/source/blender/compositor/operations/COM_DisplaceOperation.h
+++ b/source/blender/compositor/operations/COM_DisplaceOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class DisplaceOperation : public NodeOperation {
private:
/**
@@ -64,3 +66,5 @@ class DisplaceOperation : public NodeOperation {
bool read_displacement(
float x, float y, float xscale, float yscale, const float origin[2], float &r_u, float &r_v);
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc b/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc
index 9d00c2cb148..f4b77f5d32c 100644
--- a/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc
+++ b/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc
@@ -20,6 +20,8 @@
#include "BLI_math.h"
#include "BLI_utildefines.h"
+namespace blender::compositor {
+
DisplaceSimpleOperation::DisplaceSimpleOperation()
{
this->addInputSocket(DataType::Color);
@@ -129,3 +131,5 @@ bool DisplaceSimpleOperation::determineDependingAreaOfInterest(rcti *input,
return false;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DisplaceSimpleOperation.h b/source/blender/compositor/operations/COM_DisplaceSimpleOperation.h
index 63fe569c812..15e6fcd0523 100644
--- a/source/blender/compositor/operations/COM_DisplaceSimpleOperation.h
+++ b/source/blender/compositor/operations/COM_DisplaceSimpleOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class DisplaceSimpleOperation : public NodeOperation {
private:
/**
@@ -58,3 +60,5 @@ class DisplaceSimpleOperation : public NodeOperation {
*/
void deinitExecution() override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.cc b/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.cc
index ae024d497d6..12cb7e7d075 100644
--- a/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.cc
+++ b/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.cc
@@ -19,6 +19,8 @@
#include "COM_DistanceRGBMatteOperation.h"
#include "BLI_math.h"
+namespace blender::compositor {
+
DistanceRGBMatteOperation::DistanceRGBMatteOperation()
{
this->addInputSocket(DataType::Color);
@@ -90,3 +92,5 @@ void DistanceRGBMatteOperation::executePixelSampled(float output[4],
output[0] = inImage[3];
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.h b/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.h
index 0593dc6b976..6fe603233b7 100644
--- a/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.h
+++ b/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.h
@@ -20,6 +20,8 @@
#include "COM_MixOperation.h"
+namespace blender::compositor {
+
/**
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -51,3 +53,5 @@ class DistanceRGBMatteOperation : public NodeOperation {
this->m_settings = nodeChroma;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.cc b/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.cc
index f333cc1ecd9..597545dd706 100644
--- a/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.cc
+++ b/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.cc
@@ -19,13 +19,12 @@
#include "COM_DistanceYCCMatteOperation.h"
#include "BLI_math.h"
-DistanceYCCMatteOperation::DistanceYCCMatteOperation()
-{
- /* pass */
-}
+namespace blender::compositor {
float DistanceYCCMatteOperation::calculateDistance(float key[4], float image[4])
{
/* only measure the second 2 values */
return len_v2v2(key + 1, image + 1);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.h b/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.h
index ce91e3a62ba..a87e885e5d8 100644
--- a/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.h
+++ b/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.h
@@ -21,6 +21,8 @@
#include "COM_DistanceRGBMatteOperation.h"
#include "COM_MixOperation.h"
+namespace blender::compositor {
+
/**
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -28,10 +30,6 @@
class DistanceYCCMatteOperation : public DistanceRGBMatteOperation {
protected:
float calculateDistance(float key[4], float image[4]) override;
-
- public:
- /**
- * Default constructor
- */
- DistanceYCCMatteOperation();
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DotproductOperation.cc b/source/blender/compositor/operations/COM_DotproductOperation.cc
index c5b89bb7fae..07075ae1d9d 100644
--- a/source/blender/compositor/operations/COM_DotproductOperation.cc
+++ b/source/blender/compositor/operations/COM_DotproductOperation.cc
@@ -18,6 +18,8 @@
#include "COM_DotproductOperation.h"
+namespace blender::compositor {
+
DotproductOperation::DotproductOperation()
{
this->addInputSocket(DataType::Vector);
@@ -52,3 +54,5 @@ void DotproductOperation::executePixelSampled(float output[4],
this->m_input2Operation->readSampled(input2, x, y, sampler);
output[0] = -(input1[0] * input2[0] + input1[1] * input2[1] + input1[2] * input2[2]);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DotproductOperation.h b/source/blender/compositor/operations/COM_DotproductOperation.h
index deff3ae5896..728033bcf32 100644
--- a/source/blender/compositor/operations/COM_DotproductOperation.h
+++ b/source/blender/compositor/operations/COM_DotproductOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class DotproductOperation : public NodeOperation {
private:
SocketReader *m_input1Operation;
@@ -32,3 +34,5 @@ class DotproductOperation : public NodeOperation {
void initExecution() override;
void deinitExecution() override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc
index 4c7cbb3cc7e..a3a86a6c502 100644
--- a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc
+++ b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc
@@ -23,6 +23,8 @@
#include "DNA_node_types.h"
#include "MEM_guardedalloc.h"
+namespace blender::compositor {
+
// this part has been copied from the double edge mask
static void do_adjacentKeepBorders(unsigned int t,
unsigned int rw,
@@ -1315,7 +1317,7 @@ DoubleEdgeMaskOperation::DoubleEdgeMaskOperation()
this->m_inputOuterMask = nullptr;
this->m_adjacentOnly = false;
this->m_keepInside = false;
- this->setComplex(true);
+ this->flags.complex = true;
}
bool DoubleEdgeMaskOperation::determineDependingAreaOfInterest(rcti * /*input*/,
@@ -1379,3 +1381,5 @@ void DoubleEdgeMaskOperation::deinitExecution()
this->m_cachedInstance = nullptr;
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.h b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.h
index 228c8edc53e..e956e8edc3e 100644
--- a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.h
+++ b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class DoubleEdgeMaskOperation : public NodeOperation {
private:
/**
@@ -65,3 +67,5 @@ class DoubleEdgeMaskOperation : public NodeOperation {
this->m_keepInside = keepInside;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_EllipseMaskOperation.cc b/source/blender/compositor/operations/COM_EllipseMaskOperation.cc
index 956b5e50edc..5a4503fecec 100644
--- a/source/blender/compositor/operations/COM_EllipseMaskOperation.cc
+++ b/source/blender/compositor/operations/COM_EllipseMaskOperation.cc
@@ -20,6 +20,8 @@
#include "BLI_math.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
EllipseMaskOperation::EllipseMaskOperation()
{
this->addInputSocket(DataType::Value);
@@ -117,3 +119,5 @@ void EllipseMaskOperation::deinitExecution()
this->m_inputMask = nullptr;
this->m_inputValue = nullptr;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_EllipseMaskOperation.h b/source/blender/compositor/operations/COM_EllipseMaskOperation.h
index c7d7336600d..64afe0145cf 100644
--- a/source/blender/compositor/operations/COM_EllipseMaskOperation.h
+++ b/source/blender/compositor/operations/COM_EllipseMaskOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class EllipseMaskOperation : public NodeOperation {
private:
/**
@@ -63,3 +65,5 @@ class EllipseMaskOperation : public NodeOperation {
this->m_maskType = maskType;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc
index 4dded61fba5..2be6e4d1be7 100644
--- a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc
+++ b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc
@@ -22,6 +22,8 @@
#include "COM_FastGaussianBlurOperation.h"
#include "MEM_guardedalloc.h"
+namespace blender::compositor {
+
FastGaussianBlurOperation::FastGaussianBlurOperation() : BlurBaseOperation(DataType::Color)
{
this->m_iirgaus = nullptr;
@@ -88,18 +90,18 @@ void *FastGaussianBlurOperation::initializeTileData(rcti *rect)
this->m_sy = this->m_data.sizey * this->m_size / 2.0f;
if ((this->m_sx == this->m_sy) && (this->m_sx > 0.0f)) {
- for (c = 0; c < COM_NUM_CHANNELS_COLOR; c++) {
+ for (c = 0; c < COM_DATA_TYPE_COLOR_CHANNELS; c++) {
IIR_gauss(copy, this->m_sx, c, 3);
}
}
else {
if (this->m_sx > 0.0f) {
- for (c = 0; c < COM_NUM_CHANNELS_COLOR; c++) {
+ for (c = 0; c < COM_DATA_TYPE_COLOR_CHANNELS; c++) {
IIR_gauss(copy, this->m_sx, c, 1);
}
}
if (this->m_sy > 0.0f) {
- for (c = 0; c < COM_NUM_CHANNELS_COLOR; c++) {
+ for (c = 0; c < COM_DATA_TYPE_COLOR_CHANNELS; c++) {
IIR_gauss(copy, this->m_sy, c, 2);
}
}
@@ -264,7 +266,7 @@ FastGaussianBlurValueOperation::FastGaussianBlurValueOperation()
this->m_inputprogram = nullptr;
this->m_sigma = 1.0f;
this->m_overlay = 0;
- setComplex(true);
+ flags.complex = true;
}
void FastGaussianBlurValueOperation::executePixel(float output[4], int x, int y, void *data)
@@ -317,7 +319,7 @@ void *FastGaussianBlurValueOperation::initializeTileData(rcti *rect)
float *src = newBuf->getBuffer();
float *dst = copy->getBuffer();
for (int i = copy->getWidth() * copy->getHeight(); i != 0;
- i--, src += COM_NUM_CHANNELS_VALUE, dst += COM_NUM_CHANNELS_VALUE) {
+ i--, src += COM_DATA_TYPE_VALUE_CHANNELS, dst += COM_DATA_TYPE_VALUE_CHANNELS) {
if (*src < *dst) {
*dst = *src;
}
@@ -327,7 +329,7 @@ void *FastGaussianBlurValueOperation::initializeTileData(rcti *rect)
float *src = newBuf->getBuffer();
float *dst = copy->getBuffer();
for (int i = copy->getWidth() * copy->getHeight(); i != 0;
- i--, src += COM_NUM_CHANNELS_VALUE, dst += COM_NUM_CHANNELS_VALUE) {
+ i--, src += COM_DATA_TYPE_VALUE_CHANNELS, dst += COM_DATA_TYPE_VALUE_CHANNELS) {
if (*src > *dst) {
*dst = *src;
}
@@ -341,3 +343,5 @@ void *FastGaussianBlurValueOperation::initializeTileData(rcti *rect)
unlockMutex();
return this->m_iirgaus;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.h b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.h
index c710baaaff8..c25afe6c4a4 100644
--- a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.h
+++ b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.h
@@ -21,6 +21,8 @@
#include "COM_BlurBaseOperation.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
class FastGaussianBlurOperation : public BlurBaseOperation {
private:
float m_sx;
@@ -79,3 +81,5 @@ class FastGaussianBlurValueOperation : public NodeOperation {
this->m_overlay = overlay;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_FlipOperation.cc b/source/blender/compositor/operations/COM_FlipOperation.cc
index 4837766b5eb..8afbec4ddbe 100644
--- a/source/blender/compositor/operations/COM_FlipOperation.cc
+++ b/source/blender/compositor/operations/COM_FlipOperation.cc
@@ -18,6 +18,8 @@
#include "COM_FlipOperation.h"
+namespace blender::compositor {
+
FlipOperation::FlipOperation()
{
this->addInputSocket(DataType::Color);
@@ -72,3 +74,5 @@ bool FlipOperation::determineDependingAreaOfInterest(rcti *input,
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_FlipOperation.h b/source/blender/compositor/operations/COM_FlipOperation.h
index e571e57913f..f26d587fde6 100644
--- a/source/blender/compositor/operations/COM_FlipOperation.h
+++ b/source/blender/compositor/operations/COM_FlipOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class FlipOperation : public NodeOperation {
private:
SocketReader *m_inputOperation;
@@ -44,3 +46,5 @@ class FlipOperation : public NodeOperation {
this->m_flipY = flipY;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GammaCorrectOperation.cc b/source/blender/compositor/operations/COM_GammaCorrectOperation.cc
index 51c033498ef..16b79fddd06 100644
--- a/source/blender/compositor/operations/COM_GammaCorrectOperation.cc
+++ b/source/blender/compositor/operations/COM_GammaCorrectOperation.cc
@@ -19,6 +19,8 @@
#include "COM_GammaCorrectOperation.h"
#include "BLI_math.h"
+namespace blender::compositor {
+
GammaCorrectOperation::GammaCorrectOperation()
{
this->addInputSocket(DataType::Color);
@@ -102,3 +104,5 @@ void GammaUncorrectOperation::deinitExecution()
{
this->m_inputProgram = nullptr;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GammaCorrectOperation.h b/source/blender/compositor/operations/COM_GammaCorrectOperation.h
index 81724581f55..ac3d45b94b1 100644
--- a/source/blender/compositor/operations/COM_GammaCorrectOperation.h
+++ b/source/blender/compositor/operations/COM_GammaCorrectOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class GammaCorrectOperation : public NodeOperation {
private:
/**
@@ -71,3 +73,5 @@ class GammaUncorrectOperation : public NodeOperation {
*/
void deinitExecution() override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GammaOperation.cc b/source/blender/compositor/operations/COM_GammaOperation.cc
index 327c5c24929..343e335070a 100644
--- a/source/blender/compositor/operations/COM_GammaOperation.cc
+++ b/source/blender/compositor/operations/COM_GammaOperation.cc
@@ -19,6 +19,8 @@
#include "COM_GammaOperation.h"
#include "BLI_math.h"
+namespace blender::compositor {
+
GammaOperation::GammaOperation()
{
this->addInputSocket(DataType::Color);
@@ -54,3 +56,5 @@ void GammaOperation::deinitExecution()
this->m_inputProgram = nullptr;
this->m_inputGammaProgram = nullptr;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GammaOperation.h b/source/blender/compositor/operations/COM_GammaOperation.h
index c18f3bc3688..034046106d6 100644
--- a/source/blender/compositor/operations/COM_GammaOperation.h
+++ b/source/blender/compositor/operations/COM_GammaOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class GammaOperation : public NodeOperation {
private:
/**
@@ -46,3 +48,5 @@ class GammaOperation : public NodeOperation {
*/
void deinitExecution() override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.cc b/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.cc
index 1f03bb8d9cb..7ca5dc4ca76 100644
--- a/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.cc
+++ b/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.cc
@@ -22,6 +22,8 @@
#include "RE_pipeline.h"
+namespace blender::compositor {
+
GaussianAlphaXBlurOperation::GaussianAlphaXBlurOperation() : BlurBaseOperation(DataType::Value)
{
this->m_gausstab = nullptr;
@@ -190,3 +192,5 @@ bool GaussianAlphaXBlurOperation::determineDependingAreaOfInterest(
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.h b/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.h
index 51ca8359067..949956fae04 100644
--- a/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.h
+++ b/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.h
@@ -21,6 +21,8 @@
#include "COM_BlurBaseOperation.h"
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class GaussianAlphaXBlurOperation : public BlurBaseOperation {
private:
float *m_gausstab;
@@ -65,3 +67,5 @@ class GaussianAlphaXBlurOperation : public BlurBaseOperation {
this->m_falloff = falloff;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.cc b/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.cc
index de35c164fc7..d2385a972dd 100644
--- a/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.cc
+++ b/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.cc
@@ -22,6 +22,8 @@
#include "RE_pipeline.h"
+namespace blender::compositor {
+
GaussianAlphaYBlurOperation::GaussianAlphaYBlurOperation() : BlurBaseOperation(DataType::Value)
{
this->m_gausstab = nullptr;
@@ -189,3 +191,5 @@ bool GaussianAlphaYBlurOperation::determineDependingAreaOfInterest(
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.h b/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.h
index edd60acf2fc..d25770386c4 100644
--- a/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.h
+++ b/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.h
@@ -21,6 +21,8 @@
#include "COM_BlurBaseOperation.h"
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class GaussianAlphaYBlurOperation : public BlurBaseOperation {
private:
float *m_gausstab;
@@ -65,3 +67,5 @@ class GaussianAlphaYBlurOperation : public BlurBaseOperation {
this->m_falloff = falloff;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc b/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc
index 73b0914c086..b2c65ff2c96 100644
--- a/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc
+++ b/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc
@@ -22,6 +22,8 @@
#include "RE_pipeline.h"
+namespace blender::compositor {
+
GaussianBokehBlurOperation::GaussianBokehBlurOperation() : BlurBaseOperation(DataType::Color)
{
this->m_gausstab = nullptr;
@@ -303,7 +305,7 @@ void GaussianBlurReferenceOperation::executePixel(float output[4], int x, int y,
int minyr = y - refrady < 0 ? -y : -refrady;
int maxyr = y + refrady > imgy ? imgy - y : refrady;
- float *srcd = buffer + COM_NUM_CHANNELS_COLOR * ((y + minyr) * imgx + x + minxr);
+ float *srcd = buffer + COM_DATA_TYPE_COLOR_CHANNELS * ((y + minyr) * imgx + x + minxr);
gausstabx = m_maintabs[refradx - 1];
gausstabcentx = gausstabx + refradx;
@@ -311,9 +313,9 @@ void GaussianBlurReferenceOperation::executePixel(float output[4], int x, int y,
gausstabcenty = gausstaby + refrady;
sum = gval = rval = bval = aval = 0.0f;
- for (i = minyr; i < maxyr; i++, srcd += COM_NUM_CHANNELS_COLOR * imgx) {
+ for (i = minyr; i < maxyr; i++, srcd += COM_DATA_TYPE_COLOR_CHANNELS * imgx) {
src = srcd;
- for (j = minxr; j < maxxr; j++, src += COM_NUM_CHANNELS_COLOR) {
+ for (j = minxr; j < maxxr; j++, src += COM_DATA_TYPE_COLOR_CHANNELS) {
val = gausstabcenty[i] * gausstabcentx[j];
sum += val;
@@ -360,3 +362,5 @@ bool GaussianBlurReferenceOperation::determineDependingAreaOfInterest(
newInput.ymin = input->ymin - addy;
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.h b/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.h
index 0eb8379dfc8..59ba3d06619 100644
--- a/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.h
+++ b/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.h
@@ -22,6 +22,8 @@
#include "COM_NodeOperation.h"
#include "COM_QualityStepHelper.h"
+namespace blender::compositor {
+
class GaussianBokehBlurOperation : public BlurBaseOperation {
private:
float *m_gausstab;
@@ -75,3 +77,5 @@ class GaussianBlurReferenceOperation : public BlurBaseOperation {
ReadBufferOperation *readOperation,
rcti *output) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GaussianXBlurOperation.cc b/source/blender/compositor/operations/COM_GaussianXBlurOperation.cc
index 43bf961cfc4..4b46cfc8776 100644
--- a/source/blender/compositor/operations/COM_GaussianXBlurOperation.cc
+++ b/source/blender/compositor/operations/COM_GaussianXBlurOperation.cc
@@ -23,6 +23,8 @@
#include "RE_pipeline.h"
+namespace blender::compositor {
+
GaussianXBlurOperation::GaussianXBlurOperation() : BlurBaseOperation(DataType::Color)
{
this->m_gausstab = nullptr;
@@ -205,3 +207,5 @@ bool GaussianXBlurOperation::determineDependingAreaOfInterest(rcti *input,
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GaussianXBlurOperation.h b/source/blender/compositor/operations/COM_GaussianXBlurOperation.h
index 44e6044e59b..15277f0a42d 100644
--- a/source/blender/compositor/operations/COM_GaussianXBlurOperation.h
+++ b/source/blender/compositor/operations/COM_GaussianXBlurOperation.h
@@ -21,6 +21,8 @@
#include "COM_BlurBaseOperation.h"
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class GaussianXBlurOperation : public BlurBaseOperation {
private:
float *m_gausstab;
@@ -62,6 +64,8 @@ class GaussianXBlurOperation : public BlurBaseOperation {
void checkOpenCL()
{
- this->setOpenCL(m_data.sizex >= 128);
+ flags.open_cl = (m_data.sizex >= 128);
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GaussianYBlurOperation.cc b/source/blender/compositor/operations/COM_GaussianYBlurOperation.cc
index 1e853dfb8f9..590ac5faa6a 100644
--- a/source/blender/compositor/operations/COM_GaussianYBlurOperation.cc
+++ b/source/blender/compositor/operations/COM_GaussianYBlurOperation.cc
@@ -23,6 +23,8 @@
#include "RE_pipeline.h"
+namespace blender::compositor {
+
GaussianYBlurOperation::GaussianYBlurOperation() : BlurBaseOperation(DataType::Color)
{
this->m_gausstab = nullptr;
@@ -205,3 +207,5 @@ bool GaussianYBlurOperation::determineDependingAreaOfInterest(rcti *input,
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GaussianYBlurOperation.h b/source/blender/compositor/operations/COM_GaussianYBlurOperation.h
index a0abb0dd050..56d40849ba4 100644
--- a/source/blender/compositor/operations/COM_GaussianYBlurOperation.h
+++ b/source/blender/compositor/operations/COM_GaussianYBlurOperation.h
@@ -21,6 +21,8 @@
#include "COM_BlurBaseOperation.h"
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class GaussianYBlurOperation : public BlurBaseOperation {
private:
float *m_gausstab;
@@ -62,6 +64,8 @@ class GaussianYBlurOperation : public BlurBaseOperation {
void checkOpenCL()
{
- this->setOpenCL(m_data.sizex >= 128);
+ flags.open_cl = (m_data.sizex >= 128);
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GlareBaseOperation.cc b/source/blender/compositor/operations/COM_GlareBaseOperation.cc
index cdf64ed8b5a..90755d9f27a 100644
--- a/source/blender/compositor/operations/COM_GlareBaseOperation.cc
+++ b/source/blender/compositor/operations/COM_GlareBaseOperation.cc
@@ -19,6 +19,8 @@
#include "COM_GlareBaseOperation.h"
#include "BLI_math.h"
+namespace blender::compositor {
+
GlareBaseOperation::GlareBaseOperation()
{
this->addInputSocket(DataType::Color);
@@ -66,3 +68,5 @@ bool GlareBaseOperation::determineDependingAreaOfInterest(rcti * /*input*/,
newInput.ymin = 0;
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GlareBaseOperation.h b/source/blender/compositor/operations/COM_GlareBaseOperation.h
index 563877b734c..7ae15595e3b 100644
--- a/source/blender/compositor/operations/COM_GlareBaseOperation.h
+++ b/source/blender/compositor/operations/COM_GlareBaseOperation.h
@@ -21,6 +21,8 @@
#include "COM_SingleThreadedOperation.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
/* utility functions used by glare, tonemap and lens distortion */
/* soms macros for color handling */
typedef float fRGB[4];
@@ -73,3 +75,5 @@ class GlareBaseOperation : public SingleThreadedOperation {
MemoryBuffer *createMemoryBuffer(rcti *rect) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GlareFogGlowOperation.cc b/source/blender/compositor/operations/COM_GlareFogGlowOperation.cc
index 23dfc95f9e3..1c1eaebd331 100644
--- a/source/blender/compositor/operations/COM_GlareFogGlowOperation.cc
+++ b/source/blender/compositor/operations/COM_GlareFogGlowOperation.cc
@@ -19,6 +19,8 @@
#include "COM_GlareFogGlowOperation.h"
#include "MEM_guardedalloc.h"
+namespace blender::compositor {
+
/*
* 2D Fast Hartley Transform, used for convolution
*/
@@ -271,7 +273,7 @@ static void convolve(float *dst, MemoryBuffer *in1, MemoryBuffer *in2)
MemoryBuffer *rdst = new MemoryBuffer(DataType::Color, in1->get_rect());
memset(rdst->getBuffer(),
0,
- rdst->getWidth() * rdst->getHeight() * COM_NUM_CHANNELS_COLOR * sizeof(float));
+ rdst->getWidth() * rdst->getHeight() * COM_DATA_TYPE_COLOR_CHANNELS * sizeof(float));
// convolution result width & height
w2 = 2 * kernelWidth - 1;
@@ -287,7 +289,7 @@ static void convolve(float *dst, MemoryBuffer *in1, MemoryBuffer *in2)
// normalize convolutor
wt[0] = wt[1] = wt[2] = 0.0f;
for (y = 0; y < kernelHeight; y++) {
- colp = (fRGB *)&kernelBuffer[y * kernelWidth * COM_NUM_CHANNELS_COLOR];
+ colp = (fRGB *)&kernelBuffer[y * kernelWidth * COM_DATA_TYPE_COLOR_CHANNELS];
for (x = 0; x < kernelWidth; x++) {
add_v3_v3(wt, colp[x]);
}
@@ -302,7 +304,7 @@ static void convolve(float *dst, MemoryBuffer *in1, MemoryBuffer *in2)
wt[2] = 1.0f / wt[2];
}
for (y = 0; y < kernelHeight; y++) {
- colp = (fRGB *)&kernelBuffer[y * kernelWidth * COM_NUM_CHANNELS_COLOR];
+ colp = (fRGB *)&kernelBuffer[y * kernelWidth * COM_DATA_TYPE_COLOR_CHANNELS];
for (x = 0; x < kernelWidth; x++) {
mul_v3_v3(colp[x], wt);
}
@@ -336,7 +338,7 @@ static void convolve(float *dst, MemoryBuffer *in1, MemoryBuffer *in2)
// in2, channel ch -> data1
for (y = 0; y < kernelHeight; y++) {
fp = &data1ch[y * w2];
- colp = (fRGB *)&kernelBuffer[y * kernelWidth * COM_NUM_CHANNELS_COLOR];
+ colp = (fRGB *)&kernelBuffer[y * kernelWidth * COM_DATA_TYPE_COLOR_CHANNELS];
for (x = 0; x < kernelWidth; x++) {
fp[x] = colp[x][ch];
}
@@ -351,7 +353,7 @@ static void convolve(float *dst, MemoryBuffer *in1, MemoryBuffer *in2)
continue;
}
fp = &data2[y * w2];
- colp = (fRGB *)&imageBuffer[yy * imageWidth * COM_NUM_CHANNELS_COLOR];
+ colp = (fRGB *)&imageBuffer[yy * imageWidth * COM_DATA_TYPE_COLOR_CHANNELS];
for (x = 0; x < xbsz; x++) {
int xx = xbl * xbsz + x;
if (xx >= imageWidth) {
@@ -381,7 +383,7 @@ static void convolve(float *dst, MemoryBuffer *in1, MemoryBuffer *in2)
continue;
}
fp = &data2[y * w2];
- colp = (fRGB *)&rdst->getBuffer()[yy * imageWidth * COM_NUM_CHANNELS_COLOR];
+ colp = (fRGB *)&rdst->getBuffer()[yy * imageWidth * COM_DATA_TYPE_COLOR_CHANNELS];
for (x = 0; x < (int)w2; x++) {
const int xx = xbl * xbsz + x - hw;
if ((xx < 0) || (xx >= imageWidth)) {
@@ -397,8 +399,9 @@ static void convolve(float *dst, MemoryBuffer *in1, MemoryBuffer *in2)
MEM_freeN(data2);
MEM_freeN(data1);
- memcpy(
- dst, rdst->getBuffer(), sizeof(float) * imageWidth * imageHeight * COM_NUM_CHANNELS_COLOR);
+ memcpy(dst,
+ rdst->getBuffer(),
+ sizeof(float) * imageWidth * imageHeight * COM_DATA_TYPE_COLOR_CHANNELS);
delete (rdst);
}
@@ -442,3 +445,5 @@ void GlareFogGlowOperation::generateGlare(float *data,
convolve(data, inputTile, ckrn);
delete ckrn;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GlareFogGlowOperation.h b/source/blender/compositor/operations/COM_GlareFogGlowOperation.h
index 844600527ee..5701f76ab13 100644
--- a/source/blender/compositor/operations/COM_GlareFogGlowOperation.h
+++ b/source/blender/compositor/operations/COM_GlareFogGlowOperation.h
@@ -22,6 +22,8 @@
#include "COM_NodeOperation.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
class GlareFogGlowOperation : public GlareBaseOperation {
public:
GlareFogGlowOperation() : GlareBaseOperation()
@@ -31,3 +33,5 @@ class GlareFogGlowOperation : public GlareBaseOperation {
protected:
void generateGlare(float *data, MemoryBuffer *inputTile, NodeGlare *settings) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GlareGhostOperation.cc b/source/blender/compositor/operations/COM_GlareGhostOperation.cc
index a4cd6dc60c1..22c8767632e 100644
--- a/source/blender/compositor/operations/COM_GlareGhostOperation.cc
+++ b/source/blender/compositor/operations/COM_GlareGhostOperation.cc
@@ -20,6 +20,8 @@
#include "BLI_math.h"
#include "COM_FastGaussianBlurOperation.h"
+namespace blender::compositor {
+
static float smoothMask(float x, float y)
{
float t;
@@ -123,7 +125,7 @@ void GlareGhostOperation::generateGlare(float *data, MemoryBuffer *inputTile, No
memset(tbuf1.getBuffer(),
0,
- tbuf1.getWidth() * tbuf1.getHeight() * COM_NUM_CHANNELS_COLOR * sizeof(float));
+ tbuf1.getWidth() * tbuf1.getHeight() * COM_DATA_TYPE_COLOR_CHANNELS * sizeof(float));
for (n = 1; n < settings->iter && (!breaked); n++) {
for (y = 0; y < gbuf.getHeight() && (!breaked); y++) {
v = ((float)y + 0.5f) / (float)gbuf.getHeight();
@@ -147,9 +149,11 @@ void GlareGhostOperation::generateGlare(float *data, MemoryBuffer *inputTile, No
}
memcpy(gbuf.getBuffer(),
tbuf1.getBuffer(),
- tbuf1.getWidth() * tbuf1.getHeight() * COM_NUM_CHANNELS_COLOR * sizeof(float));
+ tbuf1.getWidth() * tbuf1.getHeight() * COM_DATA_TYPE_COLOR_CHANNELS * sizeof(float));
}
memcpy(data,
gbuf.getBuffer(),
- gbuf.getWidth() * gbuf.getHeight() * COM_NUM_CHANNELS_COLOR * sizeof(float));
+ gbuf.getWidth() * gbuf.getHeight() * COM_DATA_TYPE_COLOR_CHANNELS * sizeof(float));
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GlareGhostOperation.h b/source/blender/compositor/operations/COM_GlareGhostOperation.h
index 1b6bf3b2603..60256d8e0ef 100644
--- a/source/blender/compositor/operations/COM_GlareGhostOperation.h
+++ b/source/blender/compositor/operations/COM_GlareGhostOperation.h
@@ -22,6 +22,8 @@
#include "COM_NodeOperation.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
class GlareGhostOperation : public GlareBaseOperation {
public:
GlareGhostOperation() : GlareBaseOperation()
@@ -31,3 +33,5 @@ class GlareGhostOperation : public GlareBaseOperation {
protected:
void generateGlare(float *data, MemoryBuffer *inputTile, NodeGlare *settings) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GlareSimpleStarOperation.cc b/source/blender/compositor/operations/COM_GlareSimpleStarOperation.cc
index a2cecb7e171..cc24a50a307 100644
--- a/source/blender/compositor/operations/COM_GlareSimpleStarOperation.cc
+++ b/source/blender/compositor/operations/COM_GlareSimpleStarOperation.cc
@@ -18,6 +18,8 @@
#include "COM_GlareSimpleStarOperation.h"
+namespace blender::compositor {
+
void GlareSimpleStarOperation::generateGlare(float *data,
MemoryBuffer *inputTile,
NodeGlare *settings)
@@ -97,3 +99,5 @@ void GlareSimpleStarOperation::generateGlare(float *data,
data[i] = tbuf1.getBuffer()[i] + tbuf2.getBuffer()[i];
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GlareSimpleStarOperation.h b/source/blender/compositor/operations/COM_GlareSimpleStarOperation.h
index ab92e0019fa..4a074f53e7b 100644
--- a/source/blender/compositor/operations/COM_GlareSimpleStarOperation.h
+++ b/source/blender/compositor/operations/COM_GlareSimpleStarOperation.h
@@ -22,6 +22,8 @@
#include "COM_NodeOperation.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
class GlareSimpleStarOperation : public GlareBaseOperation {
public:
GlareSimpleStarOperation() : GlareBaseOperation()
@@ -31,3 +33,5 @@ class GlareSimpleStarOperation : public GlareBaseOperation {
protected:
void generateGlare(float *data, MemoryBuffer *inputTile, NodeGlare *settings) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GlareStreaksOperation.cc b/source/blender/compositor/operations/COM_GlareStreaksOperation.cc
index 0ea277881da..0af4eb43624 100644
--- a/source/blender/compositor/operations/COM_GlareStreaksOperation.cc
+++ b/source/blender/compositor/operations/COM_GlareStreaksOperation.cc
@@ -19,6 +19,8 @@
#include "COM_GlareStreaksOperation.h"
#include "BLI_math.h"
+namespace blender::compositor {
+
void GlareStreaksOperation::generateGlare(float *data,
MemoryBuffer *inputTile,
NodeGlare *settings)
@@ -97,3 +99,5 @@ void GlareStreaksOperation::generateGlare(float *data,
nump++;
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GlareStreaksOperation.h b/source/blender/compositor/operations/COM_GlareStreaksOperation.h
index 833dbcfbb95..487c910960a 100644
--- a/source/blender/compositor/operations/COM_GlareStreaksOperation.h
+++ b/source/blender/compositor/operations/COM_GlareStreaksOperation.h
@@ -22,6 +22,8 @@
#include "COM_NodeOperation.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
class GlareStreaksOperation : public GlareBaseOperation {
public:
GlareStreaksOperation() : GlareBaseOperation()
@@ -31,3 +33,5 @@ class GlareStreaksOperation : public GlareBaseOperation {
protected:
void generateGlare(float *data, MemoryBuffer *inputTile, NodeGlare *settings) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GlareThresholdOperation.cc b/source/blender/compositor/operations/COM_GlareThresholdOperation.cc
index 984b433469a..1d3402f5b7b 100644
--- a/source/blender/compositor/operations/COM_GlareThresholdOperation.cc
+++ b/source/blender/compositor/operations/COM_GlareThresholdOperation.cc
@@ -21,9 +21,11 @@
#include "IMB_colormanagement.h"
+namespace blender::compositor {
+
GlareThresholdOperation::GlareThresholdOperation()
{
- this->addInputSocket(DataType::Color, COM_SC_FIT);
+ this->addInputSocket(DataType::Color, ResizeMode::FitAny);
this->addOutputSocket(DataType::Color);
this->m_inputProgram = nullptr;
}
@@ -67,3 +69,5 @@ void GlareThresholdOperation::deinitExecution()
{
this->m_inputProgram = nullptr;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GlareThresholdOperation.h b/source/blender/compositor/operations/COM_GlareThresholdOperation.h
index 34df10a0f81..a6e971dada7 100644
--- a/source/blender/compositor/operations/COM_GlareThresholdOperation.h
+++ b/source/blender/compositor/operations/COM_GlareThresholdOperation.h
@@ -21,6 +21,8 @@
#include "COM_NodeOperation.h"
#include "DNA_light_types.h"
+namespace blender::compositor {
+
class GlareThresholdOperation : public NodeOperation {
private:
/**
@@ -59,3 +61,5 @@ class GlareThresholdOperation : public NodeOperation {
void determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2]) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.cc b/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.cc
index 0ef15b2b3d7..e341a88ff71 100644
--- a/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.cc
+++ b/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.cc
@@ -22,6 +22,8 @@
#include "BKE_colortools.h"
+namespace blender::compositor {
+
HueSaturationValueCorrectOperation::HueSaturationValueCorrectOperation()
{
this->addInputSocket(DataType::Color);
@@ -70,3 +72,5 @@ void HueSaturationValueCorrectOperation::deinitExecution()
CurveBaseOperation::deinitExecution();
this->m_inputProgram = nullptr;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.h b/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.h
index 79eb40bbf60..703b2894bdb 100644
--- a/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.h
+++ b/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.h
@@ -21,6 +21,8 @@
#include "COM_CurveBaseOperation.h"
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class HueSaturationValueCorrectOperation : public CurveBaseOperation {
private:
/**
@@ -46,3 +48,5 @@ class HueSaturationValueCorrectOperation : public CurveBaseOperation {
*/
void deinitExecution() override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_IDMaskOperation.cc b/source/blender/compositor/operations/COM_IDMaskOperation.cc
index 8757908e354..1bb247e9bc5 100644
--- a/source/blender/compositor/operations/COM_IDMaskOperation.cc
+++ b/source/blender/compositor/operations/COM_IDMaskOperation.cc
@@ -18,11 +18,13 @@
#include "COM_IDMaskOperation.h"
+namespace blender::compositor {
+
IDMaskOperation::IDMaskOperation()
{
this->addInputSocket(DataType::Value);
this->addOutputSocket(DataType::Value);
- this->setComplex(true);
+ this->flags.complex = true;
}
void *IDMaskOperation::initializeTileData(rcti *rect)
@@ -39,3 +41,5 @@ void IDMaskOperation::executePixel(float output[4], int x, int y, void *data)
int buffer_index = (y * buffer_width + x);
output[0] = (roundf(buffer[buffer_index]) == this->m_objectIndex) ? 1.0f : 0.0f;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_IDMaskOperation.h b/source/blender/compositor/operations/COM_IDMaskOperation.h
index 9a8d553b184..79b7e53b67c 100644
--- a/source/blender/compositor/operations/COM_IDMaskOperation.h
+++ b/source/blender/compositor/operations/COM_IDMaskOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class IDMaskOperation : public NodeOperation {
private:
float m_objectIndex;
@@ -35,3 +37,5 @@ class IDMaskOperation : public NodeOperation {
this->m_objectIndex = objectIndex;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ImageOperation.cc b/source/blender/compositor/operations/COM_ImageOperation.cc
index 06e8db7b467..a1d401d4499 100644
--- a/source/blender/compositor/operations/COM_ImageOperation.cc
+++ b/source/blender/compositor/operations/COM_ImageOperation.cc
@@ -31,6 +31,8 @@
#include "RE_pipeline.h"
#include "RE_texture.h"
+namespace blender::compositor {
+
BaseImageOperation::BaseImageOperation()
{
this->m_image = nullptr;
@@ -123,13 +125,13 @@ static void sampleImageAtLocation(
{
if (ibuf->rect_float) {
switch (sampler) {
- case COM_PS_NEAREST:
+ case PixelSampler::Nearest:
nearest_interpolation_color(ibuf, nullptr, color, x, y);
break;
- case COM_PS_BILINEAR:
+ case PixelSampler::Bilinear:
bilinear_interpolation_color(ibuf, nullptr, color, x, y);
break;
- case COM_PS_BICUBIC:
+ case PixelSampler::Bicubic:
bicubic_interpolation_color(ibuf, nullptr, color, x, y);
break;
}
@@ -137,13 +139,13 @@ static void sampleImageAtLocation(
else {
unsigned char byte_color[4];
switch (sampler) {
- case COM_PS_NEAREST:
+ case PixelSampler::Nearest:
nearest_interpolation_color(ibuf, byte_color, nullptr, x, y);
break;
- case COM_PS_BILINEAR:
+ case PixelSampler::Bilinear:
bilinear_interpolation_color(ibuf, byte_color, nullptr, x, y);
break;
- case COM_PS_BICUBIC:
+ case PixelSampler::Bicubic:
bicubic_interpolation_color(ibuf, byte_color, nullptr, x, y);
break;
}
@@ -203,3 +205,5 @@ void ImageDepthOperation::executePixelSampled(float output[4],
}
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ImageOperation.h b/source/blender/compositor/operations/COM_ImageOperation.h
index 0bb92ce1b7e..58373663db5 100644
--- a/source/blender/compositor/operations/COM_ImageOperation.h
+++ b/source/blender/compositor/operations/COM_ImageOperation.h
@@ -27,6 +27,8 @@
#include "RE_pipeline.h"
#include "RE_texture.h"
+namespace blender::compositor {
+
/**
* \brief Base class for all image operations
*/
@@ -102,3 +104,5 @@ class ImageDepthOperation : public BaseImageOperation {
ImageDepthOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_InpaintOperation.cc b/source/blender/compositor/operations/COM_InpaintOperation.cc
index f4e38c85e50..413ed2694a9 100644
--- a/source/blender/compositor/operations/COM_InpaintOperation.cc
+++ b/source/blender/compositor/operations/COM_InpaintOperation.cc
@@ -23,6 +23,8 @@
#include "BLI_math.h"
+namespace blender::compositor {
+
#define ASSERT_XY_RANGE(x, y) \
BLI_assert(x >= 0 && x < this->getWidth() && y >= 0 && y < this->getHeight())
@@ -31,7 +33,7 @@ InpaintSimpleOperation::InpaintSimpleOperation()
{
this->addInputSocket(DataType::Color);
this->addOutputSocket(DataType::Color);
- this->setComplex(true);
+ this->flags.complex = true;
this->m_inputImageProgram = nullptr;
this->m_pixelorder = nullptr;
this->m_manhattan_distance = nullptr;
@@ -76,7 +78,8 @@ float *InpaintSimpleOperation::get_pixel(int x, int y)
ASSERT_XY_RANGE(x, y);
- return &this->m_cached_buffer[y * width * COM_NUM_CHANNELS_COLOR + x * COM_NUM_CHANNELS_COLOR];
+ return &this->m_cached_buffer[y * width * COM_DATA_TYPE_COLOR_CHANNELS +
+ x * COM_DATA_TYPE_COLOR_CHANNELS];
}
int InpaintSimpleOperation::mdist(int x, int y)
@@ -282,3 +285,5 @@ bool InpaintSimpleOperation::determineDependingAreaOfInterest(rcti * /*input*/,
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_InpaintOperation.h b/source/blender/compositor/operations/COM_InpaintOperation.h
index 8fefa475b4a..e3d27bf7704 100644
--- a/source/blender/compositor/operations/COM_InpaintOperation.h
+++ b/source/blender/compositor/operations/COM_InpaintOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class InpaintSimpleOperation : public NodeOperation {
protected:
/**
@@ -72,3 +74,5 @@ class InpaintSimpleOperation : public NodeOperation {
bool next_pixel(int &x, int &y, int &curr, int iters);
void pix_step(int x, int y);
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_InvertOperation.cc b/source/blender/compositor/operations/COM_InvertOperation.cc
index 53e32baaa3d..339e40a5d1f 100644
--- a/source/blender/compositor/operations/COM_InvertOperation.cc
+++ b/source/blender/compositor/operations/COM_InvertOperation.cc
@@ -18,6 +18,8 @@
#include "COM_InvertOperation.h"
+namespace blender::compositor {
+
InvertOperation::InvertOperation()
{
this->addInputSocket(DataType::Value);
@@ -67,3 +69,5 @@ void InvertOperation::deinitExecution()
this->m_inputValueProgram = nullptr;
this->m_inputColorProgram = nullptr;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_InvertOperation.h b/source/blender/compositor/operations/COM_InvertOperation.h
index a7b0f823ae2..17e5eb95f3e 100644
--- a/source/blender/compositor/operations/COM_InvertOperation.h
+++ b/source/blender/compositor/operations/COM_InvertOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class InvertOperation : public NodeOperation {
private:
/**
@@ -58,3 +60,5 @@ class InvertOperation : public NodeOperation {
this->m_alpha = alpha;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_KeyingBlurOperation.cc b/source/blender/compositor/operations/COM_KeyingBlurOperation.cc
index 0d9613eea96..994b00cd3f4 100644
--- a/source/blender/compositor/operations/COM_KeyingBlurOperation.cc
+++ b/source/blender/compositor/operations/COM_KeyingBlurOperation.cc
@@ -23,6 +23,8 @@
#include "BLI_listbase.h"
#include "BLI_math.h"
+namespace blender::compositor {
+
KeyingBlurOperation::KeyingBlurOperation()
{
this->addInputSocket(DataType::Value);
@@ -31,7 +33,7 @@ KeyingBlurOperation::KeyingBlurOperation()
this->m_size = 0;
this->m_axis = BLUR_AXIS_X;
- this->setComplex(true);
+ this->flags.complex = true;
}
void *KeyingBlurOperation::initializeTileData(rcti *rect)
@@ -93,3 +95,5 @@ bool KeyingBlurOperation::determineDependingAreaOfInterest(rcti *input,
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_KeyingBlurOperation.h b/source/blender/compositor/operations/COM_KeyingBlurOperation.h
index b1ea4229b6d..b055d7713f1 100644
--- a/source/blender/compositor/operations/COM_KeyingBlurOperation.h
+++ b/source/blender/compositor/operations/COM_KeyingBlurOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
/**
* Class with implementation of blurring for keying node
*/
@@ -53,3 +55,5 @@ class KeyingBlurOperation : public NodeOperation {
ReadBufferOperation *readOperation,
rcti *output) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_KeyingClipOperation.cc b/source/blender/compositor/operations/COM_KeyingClipOperation.cc
index e8556d9d8c9..4029be4e077 100644
--- a/source/blender/compositor/operations/COM_KeyingClipOperation.cc
+++ b/source/blender/compositor/operations/COM_KeyingClipOperation.cc
@@ -23,6 +23,8 @@
#include "BLI_listbase.h"
#include "BLI_math.h"
+namespace blender::compositor {
+
KeyingClipOperation::KeyingClipOperation()
{
this->addInputSocket(DataType::Value);
@@ -36,7 +38,7 @@ KeyingClipOperation::KeyingClipOperation()
this->m_isEdgeMatte = false;
- this->setComplex(true);
+ this->flags.complex = true;
}
void *KeyingClipOperation::initializeTileData(rcti *rect)
@@ -127,3 +129,5 @@ bool KeyingClipOperation::determineDependingAreaOfInterest(rcti *input,
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_KeyingClipOperation.h b/source/blender/compositor/operations/COM_KeyingClipOperation.h
index 1b2fa886814..0a21fb48c99 100644
--- a/source/blender/compositor/operations/COM_KeyingClipOperation.h
+++ b/source/blender/compositor/operations/COM_KeyingClipOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
/**
* Class with implementation of black/white clipping for keying node
*/
@@ -67,3 +69,5 @@ class KeyingClipOperation : public NodeOperation {
ReadBufferOperation *readOperation,
rcti *output) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_KeyingDespillOperation.cc b/source/blender/compositor/operations/COM_KeyingDespillOperation.cc
index 5caa450a878..d31a88cb91e 100644
--- a/source/blender/compositor/operations/COM_KeyingDespillOperation.cc
+++ b/source/blender/compositor/operations/COM_KeyingDespillOperation.cc
@@ -23,6 +23,8 @@
#include "BLI_listbase.h"
#include "BLI_math.h"
+namespace blender::compositor {
+
KeyingDespillOperation::KeyingDespillOperation()
{
this->addInputSocket(DataType::Color);
@@ -79,3 +81,5 @@ void KeyingDespillOperation::executePixelSampled(float output[4],
output[screen_primary_channel] = pixelColor[screen_primary_channel] - amount_despill;
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_KeyingDespillOperation.h b/source/blender/compositor/operations/COM_KeyingDespillOperation.h
index c201388c1c1..279ac60e6e9 100644
--- a/source/blender/compositor/operations/COM_KeyingDespillOperation.h
+++ b/source/blender/compositor/operations/COM_KeyingDespillOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
/**
* Class with implementation of keying despill node
*/
@@ -47,3 +49,5 @@ class KeyingDespillOperation : public NodeOperation {
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_KeyingOperation.cc b/source/blender/compositor/operations/COM_KeyingOperation.cc
index 108b7c60874..e786e4b8219 100644
--- a/source/blender/compositor/operations/COM_KeyingOperation.cc
+++ b/source/blender/compositor/operations/COM_KeyingOperation.cc
@@ -23,6 +23,8 @@
#include "BLI_listbase.h"
#include "BLI_math.h"
+namespace blender::compositor {
+
static float get_pixel_saturation(const float pixelColor[4],
float screen_balance,
int primary_channel)
@@ -107,3 +109,5 @@ void KeyingOperation::executePixelSampled(float output[4], float x, float y, Pix
}
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_KeyingOperation.h b/source/blender/compositor/operations/COM_KeyingOperation.h
index 13f4e5e247b..3d41ecaa0f6 100644
--- a/source/blender/compositor/operations/COM_KeyingOperation.h
+++ b/source/blender/compositor/operations/COM_KeyingOperation.h
@@ -24,6 +24,8 @@
#include "BLI_listbase.h"
+namespace blender::compositor {
+
/**
* Class with implementation of keying node
*/
@@ -47,3 +49,5 @@ class KeyingOperation : public NodeOperation {
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_KeyingScreenOperation.cc b/source/blender/compositor/operations/COM_KeyingScreenOperation.cc
index 801750d99d0..17b613246ad 100644
--- a/source/blender/compositor/operations/COM_KeyingScreenOperation.cc
+++ b/source/blender/compositor/operations/COM_KeyingScreenOperation.cc
@@ -30,13 +30,15 @@
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
+namespace blender::compositor {
+
KeyingScreenOperation::KeyingScreenOperation()
{
this->addOutputSocket(DataType::Color);
this->m_movieClip = nullptr;
this->m_framenumber = 0;
this->m_trackingObject[0] = 0;
- setComplex(true);
+ flags.complex = true;
}
void KeyingScreenOperation::initExecution()
@@ -344,3 +346,5 @@ void KeyingScreenOperation::executePixel(float output[4], int x, int y, void *da
}
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_KeyingScreenOperation.h b/source/blender/compositor/operations/COM_KeyingScreenOperation.h
index 86cd94e308d..4118d229be9 100644
--- a/source/blender/compositor/operations/COM_KeyingScreenOperation.h
+++ b/source/blender/compositor/operations/COM_KeyingScreenOperation.h
@@ -29,6 +29,8 @@
#include "BLI_voronoi_2d.h"
+namespace blender::compositor {
+
/**
* Class with implementation of green screen gradient rasterization
*/
@@ -83,3 +85,5 @@ class KeyingScreenOperation : public NodeOperation {
void executePixel(float output[4], int x, int y, void *data) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_LuminanceMatteOperation.cc b/source/blender/compositor/operations/COM_LuminanceMatteOperation.cc
index 43cc8f8bf71..0afc4278a45 100644
--- a/source/blender/compositor/operations/COM_LuminanceMatteOperation.cc
+++ b/source/blender/compositor/operations/COM_LuminanceMatteOperation.cc
@@ -21,6 +21,8 @@
#include "IMB_colormanagement.h"
+namespace blender::compositor {
+
LuminanceMatteOperation::LuminanceMatteOperation()
{
addInputSocket(DataType::Color);
@@ -75,3 +77,5 @@ void LuminanceMatteOperation::executePixelSampled(float output[4],
/* don't make something that was more transparent less transparent */
output[0] = min_ff(alpha, inColor[3]);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_LuminanceMatteOperation.h b/source/blender/compositor/operations/COM_LuminanceMatteOperation.h
index 4e716468568..035c68b9d59 100644
--- a/source/blender/compositor/operations/COM_LuminanceMatteOperation.h
+++ b/source/blender/compositor/operations/COM_LuminanceMatteOperation.h
@@ -20,6 +20,8 @@
#include "COM_MixOperation.h"
+namespace blender::compositor {
+
/**
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -48,3 +50,5 @@ class LuminanceMatteOperation : public NodeOperation {
this->m_settings = nodeChroma;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MapRangeOperation.cc b/source/blender/compositor/operations/COM_MapRangeOperation.cc
index 78ba446051e..ada3cd6f159 100644
--- a/source/blender/compositor/operations/COM_MapRangeOperation.cc
+++ b/source/blender/compositor/operations/COM_MapRangeOperation.cc
@@ -18,6 +18,8 @@
#include "COM_MapRangeOperation.h"
+namespace blender::compositor {
+
MapRangeOperation::MapRangeOperation()
{
this->addInputSocket(DataType::Value);
@@ -101,3 +103,5 @@ void MapRangeOperation::deinitExecution()
this->m_destMinOperation = nullptr;
this->m_destMaxOperation = nullptr;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MapRangeOperation.h b/source/blender/compositor/operations/COM_MapRangeOperation.h
index 27a7cfd90c3..a544c59887e 100644
--- a/source/blender/compositor/operations/COM_MapRangeOperation.h
+++ b/source/blender/compositor/operations/COM_MapRangeOperation.h
@@ -21,6 +21,8 @@
#include "COM_NodeOperation.h"
#include "DNA_texture_types.h"
+namespace blender::compositor {
+
/**
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -67,3 +69,5 @@ class MapRangeOperation : public NodeOperation {
this->m_useClamp = value;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MapUVOperation.cc b/source/blender/compositor/operations/COM_MapUVOperation.cc
index 7328de7f49f..74e3d965d41 100644
--- a/source/blender/compositor/operations/COM_MapUVOperation.cc
+++ b/source/blender/compositor/operations/COM_MapUVOperation.cc
@@ -19,13 +19,15 @@
#include "COM_MapUVOperation.h"
#include "BLI_math.h"
+namespace blender::compositor {
+
MapUVOperation::MapUVOperation()
{
- this->addInputSocket(DataType::Color, COM_SC_NO_RESIZE);
+ this->addInputSocket(DataType::Color, ResizeMode::None);
this->addInputSocket(DataType::Vector);
this->addOutputSocket(DataType::Color);
this->m_alpha = 0.0f;
- this->setComplex(true);
+ this->flags.complex = true;
setResolutionInputSocketIndex(1);
this->m_inputUVProgram = nullptr;
@@ -89,7 +91,7 @@ bool MapUVOperation::read_uv(float x, float y, float &r_u, float &r_v, float &r_
}
float vector[3];
- m_inputUVProgram->readSampled(vector, x, y, COM_PS_BILINEAR);
+ m_inputUVProgram->readSampled(vector, x, y, PixelSampler::Bilinear);
r_u = vector[0] * m_inputColorProgram->getWidth();
r_v = vector[1] * m_inputColorProgram->getHeight();
r_alpha = vector[2];
@@ -183,3 +185,5 @@ bool MapUVOperation::determineDependingAreaOfInterest(rcti *input,
return false;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MapUVOperation.h b/source/blender/compositor/operations/COM_MapUVOperation.h
index e3481b9cae9..eb5f7d49122 100644
--- a/source/blender/compositor/operations/COM_MapUVOperation.h
+++ b/source/blender/compositor/operations/COM_MapUVOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class MapUVOperation : public NodeOperation {
private:
/**
@@ -65,3 +67,5 @@ class MapUVOperation : public NodeOperation {
private:
bool read_uv(float x, float y, float &r_u, float &r_v, float &r_alpha);
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MapValueOperation.cc b/source/blender/compositor/operations/COM_MapValueOperation.cc
index 9c28e3c2577..03fa80d220d 100644
--- a/source/blender/compositor/operations/COM_MapValueOperation.cc
+++ b/source/blender/compositor/operations/COM_MapValueOperation.cc
@@ -18,6 +18,8 @@
#include "COM_MapValueOperation.h"
+namespace blender::compositor {
+
MapValueOperation::MapValueOperation()
{
this->addInputSocket(DataType::Value);
@@ -57,3 +59,5 @@ void MapValueOperation::deinitExecution()
{
this->m_inputOperation = nullptr;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MapValueOperation.h b/source/blender/compositor/operations/COM_MapValueOperation.h
index 98417001f4a..eb7714714e9 100644
--- a/source/blender/compositor/operations/COM_MapValueOperation.h
+++ b/source/blender/compositor/operations/COM_MapValueOperation.h
@@ -21,6 +21,8 @@
#include "COM_NodeOperation.h"
#include "DNA_texture_types.h"
+namespace blender::compositor {
+
/**
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -62,3 +64,5 @@ class MapValueOperation : public NodeOperation {
this->m_settings = settings;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MaskOperation.cc b/source/blender/compositor/operations/COM_MaskOperation.cc
index 64d8c9a5492..c7763f08e71 100644
--- a/source/blender/compositor/operations/COM_MaskOperation.cc
+++ b/source/blender/compositor/operations/COM_MaskOperation.cc
@@ -26,6 +26,8 @@
#include "BKE_lib_id.h"
#include "BKE_mask.h"
+namespace blender::compositor {
+
MaskOperation::MaskOperation()
{
this->addOutputSocket(DataType::Value);
@@ -158,3 +160,5 @@ void MaskOperation::executePixelSampled(float output[4],
output[0] /= this->m_rasterMaskHandleTot;
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MaskOperation.h b/source/blender/compositor/operations/COM_MaskOperation.h
index 909af921625..48fd54b00fe 100644
--- a/source/blender/compositor/operations/COM_MaskOperation.h
+++ b/source/blender/compositor/operations/COM_MaskOperation.h
@@ -23,6 +23,11 @@
#include "DNA_mask_types.h"
#include "IMB_imbuf_types.h"
+/* Forward declarations. */
+struct MaskRasterHandle;
+
+namespace blender::compositor {
+
/**
* Class with implementation of mask rasterization
*/
@@ -94,3 +99,5 @@ class MaskOperation : public NodeOperation {
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MathBaseOperation.cc b/source/blender/compositor/operations/COM_MathBaseOperation.cc
index 57ccbe7792b..a94c14347fb 100644
--- a/source/blender/compositor/operations/COM_MathBaseOperation.cc
+++ b/source/blender/compositor/operations/COM_MathBaseOperation.cc
@@ -20,6 +20,8 @@
#include "BLI_math.h"
+namespace blender::compositor {
+
MathBaseOperation::MathBaseOperation()
{
this->addInputSocket(DataType::Value);
@@ -748,3 +750,5 @@ void MathSmoothMaxOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MathBaseOperation.h b/source/blender/compositor/operations/COM_MathBaseOperation.h
index 27a01667da5..69555524274 100644
--- a/source/blender/compositor/operations/COM_MathBaseOperation.h
+++ b/source/blender/compositor/operations/COM_MathBaseOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
/**
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -368,3 +370,5 @@ class MathSmoothMaxOperation : public MathBaseOperation {
}
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MixOperation.cc b/source/blender/compositor/operations/COM_MixOperation.cc
index e70c59c6a01..58fa09fa2a8 100644
--- a/source/blender/compositor/operations/COM_MixOperation.cc
+++ b/source/blender/compositor/operations/COM_MixOperation.cc
@@ -20,6 +20,8 @@
#include "BLI_math.h"
+namespace blender::compositor {
+
/* ******** Mix Base Operation ******** */
MixBaseOperation::MixBaseOperation()
@@ -97,11 +99,6 @@ void MixBaseOperation::deinitExecution()
/* ******** Mix Add Operation ******** */
-MixAddOperation::MixAddOperation()
-{
- /* pass */
-}
-
void MixAddOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler)
{
float inputColor1[4];
@@ -126,11 +123,6 @@ void MixAddOperation::executePixelSampled(float output[4], float x, float y, Pix
/* ******** Mix Blend Operation ******** */
-MixBlendOperation::MixBlendOperation()
-{
- /* pass */
-}
-
void MixBlendOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -160,11 +152,6 @@ void MixBlendOperation::executePixelSampled(float output[4],
/* ******** Mix Burn Operation ******** */
-MixColorBurnOperation::MixColorBurnOperation()
-{
- /* pass */
-}
-
void MixColorBurnOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -243,11 +230,6 @@ void MixColorBurnOperation::executePixelSampled(float output[4],
/* ******** Mix Color Operation ******** */
-MixColorOperation::MixColorOperation()
-{
- /* pass */
-}
-
void MixColorOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -288,11 +270,6 @@ void MixColorOperation::executePixelSampled(float output[4],
/* ******** Mix Darken Operation ******** */
-MixDarkenOperation::MixDarkenOperation()
-{
- /* pass */
-}
-
void MixDarkenOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -321,11 +298,6 @@ void MixDarkenOperation::executePixelSampled(float output[4],
/* ******** Mix Difference Operation ******** */
-MixDifferenceOperation::MixDifferenceOperation()
-{
- /* pass */
-}
-
void MixDifferenceOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -354,11 +326,6 @@ void MixDifferenceOperation::executePixelSampled(float output[4],
/* ******** Mix Difference Operation ******** */
-MixDivideOperation::MixDivideOperation()
-{
- /* pass */
-}
-
void MixDivideOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -404,11 +371,6 @@ void MixDivideOperation::executePixelSampled(float output[4],
/* ******** Mix Dodge Operation ******** */
-MixDodgeOperation::MixDodgeOperation()
-{
- /* pass */
-}
-
void MixDodgeOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -492,11 +454,6 @@ void MixDodgeOperation::executePixelSampled(float output[4],
/* ******** Mix Glare Operation ******** */
-MixGlareOperation::MixGlareOperation()
-{
- /* pass */
-}
-
void MixGlareOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -505,27 +462,26 @@ void MixGlareOperation::executePixelSampled(float output[4],
float inputColor1[4];
float inputColor2[4];
float inputValue[4];
- float value;
+ float value, input_weight, glare_weight;
this->m_inputValueOperation->readSampled(inputValue, x, y, sampler);
this->m_inputColor1Operation->readSampled(inputColor1, x, y, sampler);
this->m_inputColor2Operation->readSampled(inputColor2, x, y, sampler);
value = inputValue[0];
- float mf = 2.0f - 2.0f * fabsf(value - 0.5f);
-
- if (inputColor1[0] < 0.0f) {
- inputColor1[0] = 0.0f;
+ /* Linear interpolation between 3 cases:
+ * value=-1:output=input value=0:output=input+glare value=1:output=glare
+ */
+ if (value < 0.0f) {
+ input_weight = 1.0f;
+ glare_weight = 1.0f + value;
}
- if (inputColor1[1] < 0.0f) {
- inputColor1[1] = 0.0f;
- }
- if (inputColor1[2] < 0.0f) {
- inputColor1[2] = 0.0f;
+ else {
+ input_weight = 1.0f - value;
+ glare_weight = 1.0f;
}
-
- output[0] = mf * MAX2(inputColor1[0] + value * (inputColor2[0] - inputColor1[0]), 0.0f);
- output[1] = mf * MAX2(inputColor1[1] + value * (inputColor2[1] - inputColor1[1]), 0.0f);
- output[2] = mf * MAX2(inputColor1[2] + value * (inputColor2[2] - inputColor1[2]), 0.0f);
+ output[0] = input_weight * MAX2(inputColor1[0], 0.0f) + glare_weight * inputColor2[0];
+ output[1] = input_weight * MAX2(inputColor1[1], 0.0f) + glare_weight * inputColor2[1];
+ output[2] = input_weight * MAX2(inputColor1[2], 0.0f) + glare_weight * inputColor2[2];
output[3] = inputColor1[3];
clampIfNeeded(output);
@@ -533,11 +489,6 @@ void MixGlareOperation::executePixelSampled(float output[4],
/* ******** Mix Hue Operation ******** */
-MixHueOperation::MixHueOperation()
-{
- /* pass */
-}
-
void MixHueOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler)
{
float inputColor1[4];
@@ -575,11 +526,6 @@ void MixHueOperation::executePixelSampled(float output[4], float x, float y, Pix
/* ******** Mix Lighten Operation ******** */
-MixLightenOperation::MixLightenOperation()
-{
- /* pass */
-}
-
void MixLightenOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -626,11 +572,6 @@ void MixLightenOperation::executePixelSampled(float output[4],
/* ******** Mix Linear Light Operation ******** */
-MixLinearLightOperation::MixLinearLightOperation()
-{
- /* pass */
-}
-
void MixLinearLightOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -674,11 +615,6 @@ void MixLinearLightOperation::executePixelSampled(float output[4],
/* ******** Mix Multiply Operation ******** */
-MixMultiplyOperation::MixMultiplyOperation()
-{
- /* pass */
-}
-
void MixMultiplyOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -707,11 +643,6 @@ void MixMultiplyOperation::executePixelSampled(float output[4],
/* ******** Mix Overlay Operation ******** */
-MixOverlayOperation::MixOverlayOperation()
-{
- /* pass */
-}
-
void MixOverlayOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -757,11 +688,6 @@ void MixOverlayOperation::executePixelSampled(float output[4],
/* ******** Mix Saturation Operation ******** */
-MixSaturationOperation::MixSaturationOperation()
-{
- /* pass */
-}
-
void MixSaturationOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -799,11 +725,6 @@ void MixSaturationOperation::executePixelSampled(float output[4],
/* ******** Mix Screen Operation ******** */
-MixScreenOperation::MixScreenOperation()
-{
- /* pass */
-}
-
void MixScreenOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -833,11 +754,6 @@ void MixScreenOperation::executePixelSampled(float output[4],
/* ******** Mix Soft Light Operation ******** */
-MixSoftLightOperation::MixSoftLightOperation()
-{
- /* pass */
-}
-
void MixSoftLightOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -879,11 +795,6 @@ void MixSoftLightOperation::executePixelSampled(float output[4],
/* ******** Mix Subtract Operation ******** */
-MixSubtractOperation::MixSubtractOperation()
-{
- /* pass */
-}
-
void MixSubtractOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -911,11 +822,6 @@ void MixSubtractOperation::executePixelSampled(float output[4],
/* ******** Mix Value Operation ******** */
-MixValueOperation::MixValueOperation()
-{
- /* pass */
-}
-
void MixValueOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -944,3 +850,5 @@ void MixValueOperation::executePixelSampled(float output[4],
clampIfNeeded(output);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MixOperation.h b/source/blender/compositor/operations/COM_MixOperation.h
index dd1f34a5322..6c241bc5762 100644
--- a/source/blender/compositor/operations/COM_MixOperation.h
+++ b/source/blender/compositor/operations/COM_MixOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
/**
* All this programs converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -83,114 +85,97 @@ class MixBaseOperation : public NodeOperation {
class MixAddOperation : public MixBaseOperation {
public:
- MixAddOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixBlendOperation : public MixBaseOperation {
public:
- MixBlendOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixColorBurnOperation : public MixBaseOperation {
public:
- MixColorBurnOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixColorOperation : public MixBaseOperation {
public:
- MixColorOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixDarkenOperation : public MixBaseOperation {
public:
- MixDarkenOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixDifferenceOperation : public MixBaseOperation {
public:
- MixDifferenceOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixDivideOperation : public MixBaseOperation {
public:
- MixDivideOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixDodgeOperation : public MixBaseOperation {
public:
- MixDodgeOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixGlareOperation : public MixBaseOperation {
public:
- MixGlareOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixHueOperation : public MixBaseOperation {
public:
- MixHueOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixLightenOperation : public MixBaseOperation {
public:
- MixLightenOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixLinearLightOperation : public MixBaseOperation {
public:
- MixLinearLightOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixMultiplyOperation : public MixBaseOperation {
public:
- MixMultiplyOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixOverlayOperation : public MixBaseOperation {
public:
- MixOverlayOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixSaturationOperation : public MixBaseOperation {
public:
- MixSaturationOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixScreenOperation : public MixBaseOperation {
public:
- MixScreenOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixSoftLightOperation : public MixBaseOperation {
public:
- MixSoftLightOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixSubtractOperation : public MixBaseOperation {
public:
- MixSubtractOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
class MixValueOperation : public MixBaseOperation {
public:
- MixValueOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc
index 3920b4c02bd..a9f187258b2 100644
--- a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc
+++ b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc
@@ -21,6 +21,8 @@
#include "BKE_movieclip.h"
#include "BKE_tracking.h"
+namespace blender::compositor {
+
MovieClipAttributeOperation::MovieClipAttributeOperation()
{
this->addOutputSocket(DataType::Value);
@@ -80,3 +82,5 @@ void MovieClipAttributeOperation::determineResolution(unsigned int resolution[2]
resolution[0] = preferredResolution[0];
resolution[1] = preferredResolution[1];
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h
index 6a6920247e6..8507e98d08f 100644
--- a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h
+++ b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h
@@ -21,6 +21,8 @@
#include "COM_NodeOperation.h"
#include "DNA_movieclip_types.h"
+namespace blender::compositor {
+
typedef enum MovieClipAttribute {
MCA_SCALE,
MCA_X,
@@ -71,3 +73,5 @@ class MovieClipAttributeOperation : public NodeOperation {
this->m_invert = invert;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MovieClipOperation.cc b/source/blender/compositor/operations/COM_MovieClipOperation.cc
index 5d5880e14b0..d93a75407c4 100644
--- a/source/blender/compositor/operations/COM_MovieClipOperation.cc
+++ b/source/blender/compositor/operations/COM_MovieClipOperation.cc
@@ -26,6 +26,8 @@
#include "IMB_imbuf.h"
+namespace blender::compositor {
+
MovieClipBaseOperation::MovieClipBaseOperation()
{
this->m_movieClip = nullptr;
@@ -101,13 +103,13 @@ void MovieClipBaseOperation::executePixelSampled(float output[4],
}
else {
switch (sampler) {
- case COM_PS_NEAREST:
+ case PixelSampler::Nearest:
nearest_interpolation_color(ibuf, nullptr, output, x, y);
break;
- case COM_PS_BILINEAR:
+ case PixelSampler::Bilinear:
bilinear_interpolation_color(ibuf, nullptr, output, x, y);
break;
- case COM_PS_BICUBIC:
+ case PixelSampler::Bicubic:
bicubic_interpolation_color(ibuf, nullptr, output, x, y);
break;
}
@@ -133,3 +135,5 @@ void MovieClipAlphaOperation::executePixelSampled(float output[4],
MovieClipBaseOperation::executePixelSampled(result, x, y, sampler);
output[0] = result[3];
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MovieClipOperation.h b/source/blender/compositor/operations/COM_MovieClipOperation.h
index 78cd1cc0c90..c853ea43762 100644
--- a/source/blender/compositor/operations/COM_MovieClipOperation.h
+++ b/source/blender/compositor/operations/COM_MovieClipOperation.h
@@ -23,6 +23,8 @@
#include "DNA_movieclip_types.h"
#include "IMB_imbuf_types.h"
+namespace blender::compositor {
+
/**
* Base class for movie clip
*/
@@ -77,3 +79,5 @@ class MovieClipAlphaOperation : public MovieClipBaseOperation {
MovieClipAlphaOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MovieDistortionOperation.cc b/source/blender/compositor/operations/COM_MovieDistortionOperation.cc
index fcd482a69ef..c8e045ea117 100644
--- a/source/blender/compositor/operations/COM_MovieDistortionOperation.cc
+++ b/source/blender/compositor/operations/COM_MovieDistortionOperation.cc
@@ -23,6 +23,8 @@
#include "BLI_linklist.h"
+namespace blender::compositor {
+
MovieDistortionOperation::MovieDistortionOperation(bool distortion)
{
this->addInputSocket(DataType::Color);
@@ -107,10 +109,10 @@ void MovieDistortionOperation::executePixelSampled(float output[4],
float u = out[0] * aspx /* + 0.5 * overscan * w */,
v = (out[1] * aspy /* + 0.5 * overscan * h */) * pixel_aspect;
- this->m_inputOperation->readSampled(output, u, v, COM_PS_BILINEAR);
+ this->m_inputOperation->readSampled(output, u, v, PixelSampler::Bilinear);
}
else {
- this->m_inputOperation->readSampled(output, x, y, COM_PS_BILINEAR);
+ this->m_inputOperation->readSampled(output, x, y, PixelSampler::Bilinear);
}
}
@@ -125,3 +127,5 @@ bool MovieDistortionOperation::determineDependingAreaOfInterest(rcti *input,
newInput.ymax = input->ymax + m_margin[1];
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MovieDistortionOperation.h b/source/blender/compositor/operations/COM_MovieDistortionOperation.h
index ef9d6be697b..631a62f7ebf 100644
--- a/source/blender/compositor/operations/COM_MovieDistortionOperation.h
+++ b/source/blender/compositor/operations/COM_MovieDistortionOperation.h
@@ -24,6 +24,8 @@
#include "BKE_tracking.h"
+namespace blender::compositor {
+
class MovieDistortionOperation : public NodeOperation {
private:
SocketReader *m_inputOperation;
@@ -57,3 +59,5 @@ class MovieDistortionOperation : public NodeOperation {
ReadBufferOperation *readOperation,
rcti *output) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MultilayerImageOperation.cc b/source/blender/compositor/operations/COM_MultilayerImageOperation.cc
index 60936ee1939..647e93225e5 100644
--- a/source/blender/compositor/operations/COM_MultilayerImageOperation.cc
+++ b/source/blender/compositor/operations/COM_MultilayerImageOperation.cc
@@ -21,6 +21,8 @@
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
+namespace blender::compositor {
+
MultilayerBaseOperation::MultilayerBaseOperation(RenderLayer *render_layer,
RenderPass *render_pass,
int view)
@@ -49,7 +51,7 @@ ImBuf *MultilayerBaseOperation::getImBuf()
return nullptr;
}
-std::unique_ptr<MetaData> MultilayerColorOperation::getMetaData() const
+std::unique_ptr<MetaData> MultilayerColorOperation::getMetaData()
{
BLI_assert(this->m_buffer);
MetaDataExtractCallbackData callback_data = {nullptr};
@@ -86,13 +88,13 @@ void MultilayerColorOperation::executePixelSampled(float output[4],
else {
if (this->m_numberOfChannels == 4) {
switch (sampler) {
- case COM_PS_NEAREST:
+ case PixelSampler::Nearest:
nearest_interpolation_color(this->m_buffer, nullptr, output, x, y);
break;
- case COM_PS_BILINEAR:
+ case PixelSampler::Bilinear:
bilinear_interpolation_color(this->m_buffer, nullptr, output, x, y);
break;
- case COM_PS_BICUBIC:
+ case PixelSampler::Bicubic:
bicubic_interpolation_color(this->m_buffer, nullptr, output, x, y);
break;
}
@@ -155,3 +157,5 @@ void MultilayerVectorOperation::executePixelSampled(float output[4],
}
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MultilayerImageOperation.h b/source/blender/compositor/operations/COM_MultilayerImageOperation.h
index dceb57de140..6e6062cf854 100644
--- a/source/blender/compositor/operations/COM_MultilayerImageOperation.h
+++ b/source/blender/compositor/operations/COM_MultilayerImageOperation.h
@@ -20,6 +20,8 @@
#include "COM_ImageOperation.h"
+namespace blender::compositor {
+
class MultilayerBaseOperation : public BaseImageOperation {
private:
int m_passId;
@@ -45,7 +47,7 @@ class MultilayerColorOperation : public MultilayerBaseOperation {
this->addOutputSocket(DataType::Color);
}
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
- std::unique_ptr<MetaData> getMetaData() const override;
+ std::unique_ptr<MetaData> getMetaData() override;
};
class MultilayerValueOperation : public MultilayerBaseOperation {
@@ -67,3 +69,5 @@ class MultilayerVectorOperation : public MultilayerBaseOperation {
}
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_NormalizeOperation.cc b/source/blender/compositor/operations/COM_NormalizeOperation.cc
index fd3b951c842..faacb429f71 100644
--- a/source/blender/compositor/operations/COM_NormalizeOperation.cc
+++ b/source/blender/compositor/operations/COM_NormalizeOperation.cc
@@ -18,13 +18,15 @@
#include "COM_NormalizeOperation.h"
+namespace blender::compositor {
+
NormalizeOperation::NormalizeOperation()
{
this->addInputSocket(DataType::Value);
this->addOutputSocket(DataType::Value);
this->m_imageReader = nullptr;
this->m_cachedInstance = nullptr;
- this->setComplex(true);
+ this->flags.complex = true;
}
void NormalizeOperation::initExecution()
{
@@ -124,3 +126,5 @@ void NormalizeOperation::deinitializeTileData(rcti * /*rect*/, void * /*data*/)
{
/* pass */
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_NormalizeOperation.h b/source/blender/compositor/operations/COM_NormalizeOperation.h
index 6447aa930f1..93d4a0fc67d 100644
--- a/source/blender/compositor/operations/COM_NormalizeOperation.h
+++ b/source/blender/compositor/operations/COM_NormalizeOperation.h
@@ -21,6 +21,8 @@
#include "COM_NodeOperation.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
/**
* \brief base class of normalize, implementing the simple normalize
* \ingroup operation
@@ -63,3 +65,5 @@ class NormalizeOperation : public NodeOperation {
ReadBufferOperation *readOperation,
rcti *output) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc
index 7044fe402eb..5b6f650d40e 100644
--- a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc
+++ b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc
@@ -37,6 +37,8 @@
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
+namespace blender::compositor {
+
/************************************ OpenEXR Singlelayer Multiview ******************************/
OutputOpenExrSingleLayerMultiViewOperation::OutputOpenExrSingleLayerMultiViewOperation(
@@ -380,3 +382,5 @@ void OutputStereoOperation::deinitExecution()
}
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h
index 90c683a57b5..6230a6f306b 100644
--- a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h
+++ b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h
@@ -28,6 +28,8 @@
#include "intern/openexr/openexr_multi.h"
+namespace blender::compositor {
+
class OutputOpenExrSingleLayerMultiViewOperation : public OutputSingleLayerOperation {
private:
public:
@@ -80,3 +82,5 @@ class OutputStereoOperation : public OutputSingleLayerOperation {
void *get_handle(const char *filename);
void deinitExecution() override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_OutputFileOperation.cc b/source/blender/compositor/operations/COM_OutputFileOperation.cc
index a6135ef064c..1ee749b1a49 100644
--- a/source/blender/compositor/operations/COM_OutputFileOperation.cc
+++ b/source/blender/compositor/operations/COM_OutputFileOperation.cc
@@ -40,6 +40,8 @@
#include "RE_pipeline.h"
+namespace blender::compositor {
+
void add_exr_channels(void *exrhandle,
const char *layerName,
const DataType datatype,
@@ -191,7 +193,7 @@ static void write_buffer_rect(rcti *rect,
for (y = y1; y < y2 && (!breaked); y++) {
for (x = x1; x < x2 && (!breaked); x++) {
- reader->readSampled(color, x, y, COM_PS_NEAREST);
+ reader->readSampled(color, x, y, PixelSampler::Nearest);
for (i = 0; i < size; i++) {
buffer[offset + i] = color[i];
@@ -321,6 +323,7 @@ OutputOpenExrMultiLayerOperation::OutputOpenExrMultiLayerOperation(const Scene *
this->m_exr_codec = exr_codec;
this->m_exr_half_float = exr_half_float;
this->m_viewName = viewName;
+ this->setResolutionInputSocketIndex(RESOLUTION_INPUT_ANY);
}
void OutputOpenExrMultiLayerOperation::add_layer(const char *name,
@@ -328,7 +331,7 @@ void OutputOpenExrMultiLayerOperation::add_layer(const char *name,
bool use_layer)
{
this->addInputSocket(datatype);
- this->m_layers.push_back(OutputOpenExrLayer(name, datatype, use_layer));
+ this->m_layers.append(OutputOpenExrLayer(name, datatype, use_layer));
}
StampData *OutputOpenExrMultiLayerOperation::createStampData() const
@@ -338,17 +341,16 @@ StampData *OutputOpenExrMultiLayerOperation::createStampData() const
RenderResult render_result;
StampData *stamp_data = BKE_stamp_info_from_scene_static(m_scene);
render_result.stamp_data = stamp_data;
- for (int i = 0; i < this->m_layers.size(); i++) {
- const OutputOpenExrLayer *layer = &this->m_layers[i];
+ for (const OutputOpenExrLayer &layer : m_layers) {
/* Skip unconnected sockets. */
- if (layer->imageInput == nullptr) {
+ if (layer.imageInput == nullptr) {
continue;
}
- std::unique_ptr<MetaData> meta_data = layer->imageInput->getMetaData();
+ std::unique_ptr<MetaData> meta_data = layer.imageInput->getMetaData();
if (meta_data) {
blender::StringRef layer_name =
blender::bke::cryptomatte::BKE_cryptomatte_extract_layer_name(
- blender::StringRef(layer->name, BLI_strnlen(layer->name, sizeof(layer->name))));
+ blender::StringRef(layer.name, BLI_strnlen(layer.name, sizeof(layer.name))));
meta_data->replaceHashNeutralCryptomatteKeys(layer_name);
meta_data->addToRenderResult(&render_result);
}
@@ -441,3 +443,5 @@ void OutputOpenExrMultiLayerOperation::deinitExecution()
BKE_stamp_data_free(stamp_data);
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_OutputFileOperation.h b/source/blender/compositor/operations/COM_OutputFileOperation.h
index aa7ba35ddee..64ab4c06e7c 100644
--- a/source/blender/compositor/operations/COM_OutputFileOperation.h
+++ b/source/blender/compositor/operations/COM_OutputFileOperation.h
@@ -27,6 +27,8 @@
#include "intern/openexr/openexr_multi.h"
+namespace blender::compositor {
+
/* Writes the image to a single-layer file. */
class OutputSingleLayerOperation : public NodeOperation {
protected:
@@ -64,14 +66,9 @@ class OutputSingleLayerOperation : public NodeOperation {
}
void initExecution() override;
void deinitExecution() override;
- CompositorPriority getRenderPriority() const override
- {
- return CompositorPriority::Low;
- }
-
- bool isFileOutputOperation() const override
+ eCompositorPriority getRenderPriority() const override
{
- return true;
+ return eCompositorPriority::Low;
}
};
@@ -91,8 +88,6 @@ struct OutputOpenExrLayer {
/* Writes inputs into OpenEXR multilayer channels. */
class OutputOpenExrMultiLayerOperation : public NodeOperation {
protected:
- typedef std::vector<OutputOpenExrLayer> LayerList;
-
const Scene *m_scene;
const RenderData *m_rd;
const bNodeTree *m_tree;
@@ -100,7 +95,7 @@ class OutputOpenExrMultiLayerOperation : public NodeOperation {
char m_path[FILE_MAX];
char m_exr_codec;
bool m_exr_half_float;
- LayerList m_layers;
+ Vector<OutputOpenExrLayer> m_layers;
const char *m_viewName;
StampData *createStampData() const;
@@ -123,14 +118,9 @@ class OutputOpenExrMultiLayerOperation : public NodeOperation {
}
void initExecution() override;
void deinitExecution() override;
- CompositorPriority getRenderPriority() const override
+ eCompositorPriority getRenderPriority() const override
{
- return CompositorPriority::Low;
- }
-
- bool isFileOutputOperation() const override
- {
- return true;
+ return eCompositorPriority::Low;
}
};
@@ -146,3 +136,5 @@ void free_exr_channels(void *exrhandle,
const char *layerName,
const DataType datatype);
int get_datatype_size(DataType datatype);
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_PixelateOperation.cc b/source/blender/compositor/operations/COM_PixelateOperation.cc
index 0d810c80ab4..94827cd1b02 100644
--- a/source/blender/compositor/operations/COM_PixelateOperation.cc
+++ b/source/blender/compositor/operations/COM_PixelateOperation.cc
@@ -18,6 +18,8 @@
#include "COM_PixelateOperation.h"
+namespace blender::compositor {
+
PixelateOperation::PixelateOperation(DataType datatype)
{
this->addInputSocket(datatype);
@@ -45,3 +47,5 @@ void PixelateOperation::executePixelSampled(float output[4],
float ny = round(y);
this->m_inputOperation->readSampled(output, nx, ny, sampler);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_PixelateOperation.h b/source/blender/compositor/operations/COM_PixelateOperation.h
index aae54965aa4..e8b272853da 100644
--- a/source/blender/compositor/operations/COM_PixelateOperation.h
+++ b/source/blender/compositor/operations/COM_PixelateOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
/**
* \brief Pixelate operation
*
@@ -60,3 +62,5 @@ class PixelateOperation : public NodeOperation {
*/
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc
index bcf79e1c6c6..3577860b93d 100644
--- a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc
+++ b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc
@@ -26,6 +26,8 @@
#include "BKE_node.h"
+namespace blender::compositor {
+
static bool check_corners(float corners[4][2])
{
int i, next, prev;
@@ -60,7 +62,7 @@ static void readCornersFromSockets(rcti *rect, SocketReader *readers[4], float c
{
for (int i = 0; i < 4; i++) {
float result[4] = {0.0f, 0.0f, 0.0f, 0.0f};
- readers[i]->readSampled(result, rect->xmin, rect->ymin, COM_PS_NEAREST);
+ readers[i]->readSampled(result, rect->xmin, rect->ymin, PixelSampler::Nearest);
corners[i][0] = result[0];
corners[i][1] = result[1];
}
@@ -98,7 +100,7 @@ PlaneCornerPinMaskOperation::PlaneCornerPinMaskOperation() : m_corners_ready(fal
* so we can use the initializeTileData function
* to read corners from input sockets ...
*/
- setComplex(true);
+ flags.complex = true;
}
void PlaneCornerPinMaskOperation::initExecution()
@@ -224,3 +226,5 @@ bool PlaneCornerPinWarpImageOperation::determineDependingAreaOfInterest(
input, readOperation, output);
#endif
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.h b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.h
index 6edecff0d54..91c0cd9e16b 100644
--- a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.h
+++ b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.h
@@ -27,6 +27,8 @@
#include "BLI_listbase.h"
#include "BLI_string.h"
+namespace blender::compositor {
+
class PlaneCornerPinMaskOperation : public PlaneDistortMaskOperation {
private:
bool m_corners_ready;
@@ -59,3 +61,5 @@ class PlaneCornerPinWarpImageOperation : public PlaneDistortWarpImageOperation {
ReadBufferOperation *readOperation,
rcti *output) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc
index 87f837973d2..4edcc206f5b 100644
--- a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc
+++ b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc
@@ -29,6 +29,33 @@
#include "BKE_node.h"
#include "BKE_tracking.h"
+namespace blender::compositor {
+
+PlaneDistortBaseOperation::PlaneDistortBaseOperation()
+ : m_motion_blur_samples(1), m_motion_blur_shutter(0.5f)
+{
+}
+
+void PlaneDistortBaseOperation::calculateCorners(const float corners[4][2],
+ bool normalized,
+ int sample)
+{
+ BLI_assert(sample < this->m_motion_blur_samples);
+ MotionSample *sample_data = &this->m_samples[sample];
+ if (normalized) {
+ for (int i = 0; i < 4; i++) {
+ sample_data->frameSpaceCorners[i][0] = corners[i][0] * this->getWidth();
+ sample_data->frameSpaceCorners[i][1] = corners[i][1] * this->getHeight();
+ }
+ }
+ else {
+ for (int i = 0; i < 4; i++) {
+ sample_data->frameSpaceCorners[i][0] = corners[i][0];
+ sample_data->frameSpaceCorners[i][1] = corners[i][1];
+ }
+ }
+}
+
/* ******** PlaneDistort WarpImage ******** */
BLI_INLINE void warpCoord(float x, float y, float matrix[3][3], float uv[2], float deriv[2][2])
@@ -44,38 +71,25 @@ BLI_INLINE void warpCoord(float x, float y, float matrix[3][3], float uv[2], flo
deriv[1][1] = (matrix[1][1] - matrix[1][2] * uv[1]) / vec[2];
}
-PlaneDistortWarpImageOperation::PlaneDistortWarpImageOperation()
+PlaneDistortWarpImageOperation::PlaneDistortWarpImageOperation() : PlaneDistortBaseOperation()
{
- this->addInputSocket(DataType::Color, COM_SC_NO_RESIZE);
+ this->addInputSocket(DataType::Color, ResizeMode::None);
this->addOutputSocket(DataType::Color);
this->m_pixelReader = nullptr;
- this->m_motion_blur_samples = 1;
- this->m_motion_blur_shutter = 0.5f;
- this->setComplex(true);
+ this->flags.complex = true;
}
void PlaneDistortWarpImageOperation::calculateCorners(const float corners[4][2],
bool normalized,
int sample)
{
- BLI_assert(sample < this->m_motion_blur_samples);
+ PlaneDistortBaseOperation::calculateCorners(corners, normalized, sample);
+
const int width = this->m_pixelReader->getWidth();
const int height = this->m_pixelReader->getHeight();
float frame_corners[4][2] = {
{0.0f, 0.0f}, {(float)width, 0.0f}, {(float)width, (float)height}, {0.0f, (float)height}};
MotionSample *sample_data = &this->m_samples[sample];
- if (normalized) {
- for (int i = 0; i < 4; i++) {
- sample_data->frameSpaceCorners[i][0] = corners[i][0] * this->getWidth();
- sample_data->frameSpaceCorners[i][1] = corners[i][1] * this->getHeight();
- }
- }
- else {
- for (int i = 0; i < 4; i++) {
- sample_data->frameSpaceCorners[i][0] = corners[i][0];
- sample_data->frameSpaceCorners[i][1] = corners[i][1];
- }
- }
BKE_tracking_homography_between_two_quads(
sample_data->frameSpaceCorners, frame_corners, sample_data->perspectiveMatrix);
}
@@ -145,34 +159,12 @@ bool PlaneDistortWarpImageOperation::determineDependingAreaOfInterest(
/* ******** PlaneDistort Mask ******** */
-PlaneDistortMaskOperation::PlaneDistortMaskOperation()
+PlaneDistortMaskOperation::PlaneDistortMaskOperation() : PlaneDistortBaseOperation()
{
addOutputSocket(DataType::Value);
/* Currently hardcoded to 8 samples. */
m_osa = 8;
- this->m_motion_blur_samples = 1;
- this->m_motion_blur_shutter = 0.5f;
-}
-
-void PlaneDistortMaskOperation::calculateCorners(const float corners[4][2],
- bool normalized,
- int sample)
-{
- BLI_assert(sample < this->m_motion_blur_samples);
- MotionSample *sample_data = &this->m_samples[sample];
- if (normalized) {
- for (int i = 0; i < 4; i++) {
- sample_data->frameSpaceCorners[i][0] = corners[i][0] * this->getWidth();
- sample_data->frameSpaceCorners[i][1] = corners[i][1] * this->getHeight();
- }
- }
- else {
- for (int i = 0; i < 4; i++) {
- sample_data->frameSpaceCorners[i][0] = corners[i][0];
- sample_data->frameSpaceCorners[i][1] = corners[i][1];
- }
- }
}
void PlaneDistortMaskOperation::initExecution()
@@ -226,3 +218,5 @@ void PlaneDistortMaskOperation::executePixelSampled(float output[4],
output[0] = (float)inside_counter / (this->m_osa * this->m_motion_blur_samples);
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h
index c17f99a6710..cc6e4d00d71 100644
--- a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h
+++ b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h
@@ -28,23 +28,47 @@
#include "BLI_listbase.h"
#include "BLI_string.h"
+namespace blender::compositor {
+
#define PLANE_DISTORT_MAX_SAMPLES 64
-class PlaneDistortWarpImageOperation : public NodeOperation {
+class PlaneDistortBaseOperation : public NodeOperation {
protected:
struct MotionSample {
float frameSpaceCorners[4][2]; /* Corners coordinates in pixel space. */
float perspectiveMatrix[3][3];
};
- SocketReader *m_pixelReader;
MotionSample m_samples[PLANE_DISTORT_MAX_SAMPLES];
int m_motion_blur_samples;
float m_motion_blur_shutter;
public:
+ PlaneDistortBaseOperation();
+
+ void setMotionBlurSamples(int samples)
+ {
+ BLI_assert(samples <= PLANE_DISTORT_MAX_SAMPLES);
+ this->m_motion_blur_samples = samples;
+ }
+ void setMotionBlurShutter(float shutter)
+ {
+ this->m_motion_blur_shutter = shutter;
+ }
+
+ virtual void calculateCorners(const float corners[4][2], bool normalized, int sample);
+
+ private:
+ friend class PlaneTrackCommon;
+};
+
+class PlaneDistortWarpImageOperation : public PlaneDistortBaseOperation {
+ protected:
+ SocketReader *m_pixelReader;
+
+ public:
PlaneDistortWarpImageOperation();
- void calculateCorners(const float corners[4][2], bool normalized, int sample);
+ void calculateCorners(const float corners[4][2], bool normalized, int sample) override;
void initExecution() override;
void deinitExecution() override;
@@ -54,45 +78,19 @@ class PlaneDistortWarpImageOperation : public NodeOperation {
bool determineDependingAreaOfInterest(rcti *input,
ReadBufferOperation *readOperation,
rcti *output) override;
-
- void setMotionBlurSamples(int samples)
- {
- BLI_assert(samples <= PLANE_DISTORT_MAX_SAMPLES);
- this->m_motion_blur_samples = samples;
- }
- void setMotionBlurShutter(float shutter)
- {
- this->m_motion_blur_shutter = shutter;
- }
};
-class PlaneDistortMaskOperation : public NodeOperation {
+class PlaneDistortMaskOperation : public PlaneDistortBaseOperation {
protected:
- struct MotionSample {
- float frameSpaceCorners[4][2]; /* Corners coordinates in pixel space. */
- };
int m_osa;
- MotionSample m_samples[PLANE_DISTORT_MAX_SAMPLES];
float m_jitter[32][2];
- int m_motion_blur_samples;
- float m_motion_blur_shutter;
public:
PlaneDistortMaskOperation();
- void calculateCorners(const float corners[4][2], bool normalized, int sample);
-
void initExecution() override;
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
-
- void setMotionBlurSamples(int samples)
- {
- BLI_assert(samples <= PLANE_DISTORT_MAX_SAMPLES);
- this->m_motion_blur_samples = samples;
- }
- void setMotionBlurShutter(float shutter)
- {
- this->m_motion_blur_shutter = shutter;
- }
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_PlaneTrackOperation.cc b/source/blender/compositor/operations/COM_PlaneTrackOperation.cc
index 81a598e937b..0884f2ad979 100644
--- a/source/blender/compositor/operations/COM_PlaneTrackOperation.cc
+++ b/source/blender/compositor/operations/COM_PlaneTrackOperation.cc
@@ -29,6 +29,8 @@
#include "BKE_node.h"
#include "BKE_tracking.h"
+namespace blender::compositor {
+
/* ******** PlaneTrackCommon ******** */
PlaneTrackCommon::PlaneTrackCommon()
@@ -39,6 +41,26 @@ PlaneTrackCommon::PlaneTrackCommon()
this->m_planeTrackName[0] = '\0';
}
+void PlaneTrackCommon::read_and_calculate_corners(PlaneDistortBaseOperation *distort_op)
+{
+ float corners[4][2];
+ if (distort_op->m_motion_blur_samples == 1) {
+ readCornersFromTrack(corners, this->m_framenumber);
+ distort_op->calculateCorners(corners, true, 0);
+ }
+ else {
+ const float frame = (float)this->m_framenumber - distort_op->m_motion_blur_shutter;
+ const float frame_step = (distort_op->m_motion_blur_shutter * 2.0f) /
+ distort_op->m_motion_blur_samples;
+ float frame_iter = frame;
+ for (int sample = 0; sample < distort_op->m_motion_blur_samples; sample++) {
+ readCornersFromTrack(corners, frame_iter);
+ distort_op->calculateCorners(corners, true, sample);
+ frame_iter += frame_step;
+ }
+ }
+}
+
void PlaneTrackCommon::readCornersFromTrack(float corners[4][2], float frame)
{
MovieTracking *tracking;
@@ -82,21 +104,7 @@ void PlaneTrackCommon::determineResolution(unsigned int resolution[2],
void PlaneTrackMaskOperation::initExecution()
{
PlaneDistortMaskOperation::initExecution();
- float corners[4][2];
- if (this->m_motion_blur_samples == 1) {
- readCornersFromTrack(corners, this->m_framenumber);
- calculateCorners(corners, true, 0);
- }
- else {
- const float frame = (float)this->m_framenumber - this->m_motion_blur_shutter;
- const float frame_step = (this->m_motion_blur_shutter * 2.0f) / this->m_motion_blur_samples;
- float frame_iter = frame;
- for (int sample = 0; sample < this->m_motion_blur_samples; sample++) {
- readCornersFromTrack(corners, frame_iter);
- calculateCorners(corners, true, sample);
- frame_iter += frame_step;
- }
- }
+ PlaneTrackCommon::read_and_calculate_corners(this);
}
/* ******** PlaneTrackWarpImageOperation ******** */
@@ -104,20 +112,7 @@ void PlaneTrackMaskOperation::initExecution()
void PlaneTrackWarpImageOperation::initExecution()
{
PlaneDistortWarpImageOperation::initExecution();
- /* TODO(sergey): De-duplicate with mask operation. */
- float corners[4][2];
- if (this->m_motion_blur_samples == 1) {
- readCornersFromTrack(corners, this->m_framenumber);
- calculateCorners(corners, true, 0);
- }
- else {
- const float frame = (float)this->m_framenumber - this->m_motion_blur_shutter;
- const float frame_step = (this->m_motion_blur_shutter * 2.0f) / this->m_motion_blur_samples;
- float frame_iter = frame;
- for (int sample = 0; sample < this->m_motion_blur_samples; sample++) {
- readCornersFromTrack(corners, frame_iter);
- calculateCorners(corners, true, sample);
- frame_iter += frame_step;
- }
- }
+ PlaneTrackCommon::read_and_calculate_corners(this);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_PlaneTrackOperation.h b/source/blender/compositor/operations/COM_PlaneTrackOperation.h
index 6cfb3accaa3..d240c8b06e9 100644
--- a/source/blender/compositor/operations/COM_PlaneTrackOperation.h
+++ b/source/blender/compositor/operations/COM_PlaneTrackOperation.h
@@ -28,6 +28,8 @@
#include "BLI_listbase.h"
#include "BLI_string.h"
+namespace blender::compositor {
+
class PlaneTrackCommon {
protected:
MovieClip *m_movieClip;
@@ -38,7 +40,7 @@ class PlaneTrackCommon {
/* note: this class is not an operation itself (to prevent virtual inheritance issues)
* implementation classes must make wrappers to use these methods, see below.
*/
- void readCornersFromTrack(float corners[4][2], float frame);
+ void read_and_calculate_corners(PlaneDistortBaseOperation *distort_op);
void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]);
public:
@@ -60,6 +62,9 @@ class PlaneTrackCommon {
{
this->m_framenumber = framenumber;
}
+
+ private:
+ void readCornersFromTrack(float corners[4][2], float frame);
};
class PlaneTrackMaskOperation : public PlaneDistortMaskOperation, public PlaneTrackCommon {
@@ -97,3 +102,5 @@ class PlaneTrackWarpImageOperation : public PlaneDistortWarpImageOperation,
NodeOperation::determineResolution(temp, resolution);
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_PreviewOperation.cc b/source/blender/compositor/operations/COM_PreviewOperation.cc
index 6d1199ab118..e7c11613aa3 100644
--- a/source/blender/compositor/operations/COM_PreviewOperation.cc
+++ b/source/blender/compositor/operations/COM_PreviewOperation.cc
@@ -33,13 +33,15 @@
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
+namespace blender::compositor {
+
PreviewOperation::PreviewOperation(const ColorManagedViewSettings *viewSettings,
const ColorManagedDisplaySettings *displaySettings,
const unsigned int defaultWidth,
const unsigned int defaultHeight)
{
- this->addInputSocket(DataType::Color, COM_SC_NO_RESIZE);
+ this->addInputSocket(DataType::Color, ResizeMode::None);
this->m_preview = nullptr;
this->m_outputBuffer = nullptr;
this->m_input = nullptr;
@@ -48,6 +50,8 @@ PreviewOperation::PreviewOperation(const ColorManagedViewSettings *viewSettings,
this->m_displaySettings = displaySettings;
this->m_defaultWidth = defaultWidth;
this->m_defaultHeight = defaultHeight;
+ flags.use_viewer_border = true;
+ flags.is_preview_operation = true;
}
void PreviewOperation::verifyPreview(bNodeInstanceHash *previews, bNodeInstanceKey key)
@@ -104,7 +108,7 @@ void PreviewOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/)
color[1] = 0.0f;
color[2] = 0.0f;
color[3] = 1.0f;
- this->m_input->readSampled(color, rx, ry, COM_PS_NEAREST);
+ this->m_input->readSampled(color, rx, ry, PixelSampler::Nearest);
IMB_colormanagement_processor_apply_v4(cm_processor, color);
rgba_float_to_uchar(this->m_outputBuffer + offset, color);
offset += 4;
@@ -162,7 +166,9 @@ void PreviewOperation::determineResolution(unsigned int resolution[2],
resolution[1] = height;
}
-CompositorPriority PreviewOperation::getRenderPriority() const
+eCompositorPriority PreviewOperation::getRenderPriority() const
{
- return CompositorPriority::Low;
+ return eCompositorPriority::Low;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_PreviewOperation.h b/source/blender/compositor/operations/COM_PreviewOperation.h
index 36ae5f59bb9..0f43f01c5d6 100644
--- a/source/blender/compositor/operations/COM_PreviewOperation.h
+++ b/source/blender/compositor/operations/COM_PreviewOperation.h
@@ -24,6 +24,8 @@
#include "DNA_color_types.h"
#include "DNA_image_types.h"
+namespace blender::compositor {
+
class PreviewOperation : public NodeOperation {
protected:
unsigned char *m_outputBuffer;
@@ -53,7 +55,7 @@ class PreviewOperation : public NodeOperation {
}
void initExecution() override;
void deinitExecution() override;
- CompositorPriority getRenderPriority() const override;
+ eCompositorPriority getRenderPriority() const override;
void executeRegion(rcti *rect, unsigned int tileNumber) override;
void determineResolution(unsigned int resolution[2],
@@ -61,8 +63,6 @@ class PreviewOperation : public NodeOperation {
bool determineDependingAreaOfInterest(rcti *input,
ReadBufferOperation *readOperation,
rcti *output) override;
- bool isPreviewOperation() const override
- {
- return true;
- }
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc
index 5494c3cd46b..93702d3f0cf 100644
--- a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc
+++ b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc
@@ -20,12 +20,14 @@
#include "BLI_math.h"
#include "BLI_utildefines.h"
+namespace blender::compositor {
+
ProjectorLensDistortionOperation::ProjectorLensDistortionOperation()
{
this->addInputSocket(DataType::Color);
this->addInputSocket(DataType::Value);
this->addOutputSocket(DataType::Color);
- this->setComplex(true);
+ this->flags.complex = true;
this->m_inputProgram = nullptr;
this->m_dispersionAvailable = false;
this->m_dispersion = 0.0f;
@@ -103,7 +105,7 @@ void ProjectorLensDistortionOperation::updateDispersion()
this->lockMutex();
if (!this->m_dispersionAvailable) {
float result[4];
- this->getInputSocketReader(1)->readSampled(result, 1, 1, COM_PS_NEAREST);
+ this->getInputSocketReader(1)->readSampled(result, 1, 1, PixelSampler::Nearest);
this->m_dispersion = result[0];
this->m_kr = 0.25f * max_ff(min_ff(this->m_dispersion, 1.0f), 0.0f);
this->m_kr2 = this->m_kr * 20;
@@ -111,3 +113,5 @@ void ProjectorLensDistortionOperation::updateDispersion()
}
this->unlockMutex();
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.h b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.h
index b47d4f37589..bce61d3de15 100644
--- a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.h
+++ b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.h
@@ -21,6 +21,8 @@
#include "COM_NodeOperation.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
class ProjectorLensDistortionOperation : public NodeOperation {
private:
/**
@@ -58,3 +60,5 @@ class ProjectorLensDistortionOperation : public NodeOperation {
void updateDispersion();
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_QualityStepHelper.cc b/source/blender/compositor/operations/COM_QualityStepHelper.cc
index c0d86314fb7..e347d278ce4 100644
--- a/source/blender/compositor/operations/COM_QualityStepHelper.cc
+++ b/source/blender/compositor/operations/COM_QualityStepHelper.cc
@@ -18,9 +18,11 @@
#include "COM_QualityStepHelper.h"
+namespace blender::compositor {
+
QualityStepHelper::QualityStepHelper()
{
- this->m_quality = CompositorQuality::High;
+ this->m_quality = eCompositorQuality::High;
this->m_step = 1;
this->m_offsetadd = 4;
}
@@ -30,16 +32,16 @@ void QualityStepHelper::initExecution(QualityHelper helper)
switch (helper) {
case COM_QH_INCREASE:
switch (this->m_quality) {
- case CompositorQuality::High:
+ case eCompositorQuality::High:
default:
this->m_step = 1;
this->m_offsetadd = 1;
break;
- case CompositorQuality::Medium:
+ case eCompositorQuality::Medium:
this->m_step = 2;
this->m_offsetadd = 2;
break;
- case CompositorQuality::Low:
+ case eCompositorQuality::Low:
this->m_step = 3;
this->m_offsetadd = 3;
break;
@@ -47,16 +49,16 @@ void QualityStepHelper::initExecution(QualityHelper helper)
break;
case COM_QH_MULTIPLY:
switch (this->m_quality) {
- case CompositorQuality::High:
+ case eCompositorQuality::High:
default:
this->m_step = 1;
this->m_offsetadd = 4;
break;
- case CompositorQuality::Medium:
+ case eCompositorQuality::Medium:
this->m_step = 2;
this->m_offsetadd = 8;
break;
- case CompositorQuality::Low:
+ case eCompositorQuality::Low:
this->m_step = 4;
this->m_offsetadd = 16;
break;
@@ -64,3 +66,5 @@ void QualityStepHelper::initExecution(QualityHelper helper)
break;
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_QualityStepHelper.h b/source/blender/compositor/operations/COM_QualityStepHelper.h
index e437613fb29..c5f16a58686 100644
--- a/source/blender/compositor/operations/COM_QualityStepHelper.h
+++ b/source/blender/compositor/operations/COM_QualityStepHelper.h
@@ -18,7 +18,9 @@
#pragma once
-#include "COM_defines.h"
+#include "COM_Enums.h"
+
+namespace blender::compositor {
typedef enum QualityHelper {
COM_QH_INCREASE,
@@ -27,7 +29,7 @@ typedef enum QualityHelper {
class QualityStepHelper {
private:
- CompositorQuality m_quality;
+ eCompositorQuality m_quality;
int m_step;
int m_offsetadd;
@@ -49,8 +51,10 @@ class QualityStepHelper {
public:
QualityStepHelper();
- void setQuality(CompositorQuality quality)
+ void setQuality(eCompositorQuality quality)
{
this->m_quality = quality;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ReadBufferOperation.cc b/source/blender/compositor/operations/COM_ReadBufferOperation.cc
index 2977e6685d2..cc58f29e8d9 100644
--- a/source/blender/compositor/operations/COM_ReadBufferOperation.cc
+++ b/source/blender/compositor/operations/COM_ReadBufferOperation.cc
@@ -20,12 +20,15 @@
#include "COM_WriteBufferOperation.h"
#include "COM_defines.h"
+namespace blender::compositor {
+
ReadBufferOperation::ReadBufferOperation(DataType datatype)
{
this->addOutputSocket(datatype);
this->m_single_value = false;
this->m_offset = 0;
this->m_buffer = nullptr;
+ flags.is_read_buffer_operation = true;
}
void *ReadBufferOperation::initializeTileData(rcti * /*rect*/)
@@ -60,14 +63,14 @@ void ReadBufferOperation::executePixelSampled(float output[4],
}
else {
switch (sampler) {
- case COM_PS_NEAREST:
+ case PixelSampler::Nearest:
m_buffer->read(output, x, y);
break;
- case COM_PS_BILINEAR:
+ case PixelSampler::Bilinear:
default:
m_buffer->readBilinear(output, x, y);
break;
- case COM_PS_BICUBIC:
+ case PixelSampler::Bicubic:
m_buffer->readBilinear(output, x, y);
break;
}
@@ -85,7 +88,7 @@ void ReadBufferOperation::executePixelExtend(float output[4],
/* write buffer has a single value stored at (0,0) */
m_buffer->read(output, 0, 0);
}
- else if (sampler == COM_PS_NEAREST) {
+ else if (sampler == PixelSampler::Nearest) {
m_buffer->read(output, x, y, extend_x, extend_y);
}
else {
@@ -131,3 +134,5 @@ void ReadBufferOperation::updateMemoryBuffer()
{
this->m_buffer = this->getMemoryProxy()->getBuffer();
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ReadBufferOperation.h b/source/blender/compositor/operations/COM_ReadBufferOperation.h
index 21ef716b727..8b96b961a43 100644
--- a/source/blender/compositor/operations/COM_ReadBufferOperation.h
+++ b/source/blender/compositor/operations/COM_ReadBufferOperation.h
@@ -22,6 +22,8 @@
#include "COM_MemoryProxy.h"
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class ReadBufferOperation : public NodeOperation {
private:
MemoryProxy *m_memoryProxy;
@@ -35,10 +37,12 @@ class ReadBufferOperation : public NodeOperation {
{
this->m_memoryProxy = memoryProxy;
}
- MemoryProxy *getMemoryProxy()
+
+ MemoryProxy *getMemoryProxy() const
{
return this->m_memoryProxy;
}
+
void determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2]) override;
@@ -51,10 +55,6 @@ class ReadBufferOperation : public NodeOperation {
MemoryBufferExtend extend_x,
MemoryBufferExtend extend_y);
void executePixelFiltered(float output[4], float x, float y, float dx[2], float dy[2]) override;
- bool isReadBufferOperation() const override
- {
- return true;
- }
void setOffset(unsigned int offset)
{
this->m_offset = offset;
@@ -73,3 +73,5 @@ class ReadBufferOperation : public NodeOperation {
void readResolutionFromWriteBuffer();
void updateMemoryBuffer();
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_RenderLayersProg.cc b/source/blender/compositor/operations/COM_RenderLayersProg.cc
index d622e14b585..1ac451b95c2 100644
--- a/source/blender/compositor/operations/COM_RenderLayersProg.cc
+++ b/source/blender/compositor/operations/COM_RenderLayersProg.cc
@@ -32,6 +32,8 @@
#include "RE_pipeline.h"
#include "RE_texture.h"
+namespace blender::compositor {
+
/* ******** Render Layers Base Prog ******** */
RenderLayersProg::RenderLayersProg(const char *passName, DataType type, int elementsize)
@@ -92,7 +94,7 @@ void RenderLayersProg::doInterpolation(float output[4], float x, float y, PixelS
}
switch (sampler) {
- case COM_PS_NEAREST: {
+ case PixelSampler::Nearest: {
offset = (iy * width + ix) * this->m_elementsize;
if (this->m_elementsize == 1) {
@@ -107,12 +109,12 @@ void RenderLayersProg::doInterpolation(float output[4], float x, float y, PixelS
break;
}
- case COM_PS_BILINEAR:
+ case PixelSampler::Bilinear:
BLI_bilinear_interpolation_fl(
this->m_inputBuffer, output, width, height, this->m_elementsize, x, y);
break;
- case COM_PS_BICUBIC:
+ case PixelSampler::Bicubic:
BLI_bicubic_interpolation_fl(
this->m_inputBuffer, output, width, height, this->m_elementsize, x, y);
break;
@@ -216,7 +218,7 @@ void RenderLayersProg::determineResolution(unsigned int resolution[2],
}
}
-std::unique_ptr<MetaData> RenderLayersProg::getMetaData() const
+std::unique_ptr<MetaData> RenderLayersProg::getMetaData()
{
Scene *scene = this->getScene();
Render *re = (scene) ? RE_GetSceneRender(scene) : nullptr;
@@ -306,3 +308,5 @@ void RenderLayersDepthProg::executePixelSampled(float output[4],
output[0] = inputBuffer[offset];
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_RenderLayersProg.h b/source/blender/compositor/operations/COM_RenderLayersProg.h
index f3e1c892a83..33e4fb163c5 100644
--- a/source/blender/compositor/operations/COM_RenderLayersProg.h
+++ b/source/blender/compositor/operations/COM_RenderLayersProg.h
@@ -26,6 +26,8 @@
#include "RE_pipeline.h"
+namespace blender::compositor {
+
/**
* Base class for all renderlayeroperations
*
@@ -123,7 +125,7 @@ class RenderLayersProg : public NodeOperation {
void deinitExecution() override;
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
- std::unique_ptr<MetaData> getMetaData() const override;
+ std::unique_ptr<MetaData> getMetaData() override;
};
class RenderLayersAOOperation : public RenderLayersProg {
@@ -152,3 +154,5 @@ class RenderLayersDepthProg : public RenderLayersProg {
}
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_RotateOperation.cc b/source/blender/compositor/operations/COM_RotateOperation.cc
index c2105efe246..4fb3d324992 100644
--- a/source/blender/compositor/operations/COM_RotateOperation.cc
+++ b/source/blender/compositor/operations/COM_RotateOperation.cc
@@ -19,6 +19,8 @@
#include "COM_RotateOperation.h"
#include "BLI_math.h"
+namespace blender::compositor {
+
RotateOperation::RotateOperation()
{
this->addInputSocket(DataType::Color);
@@ -48,7 +50,7 @@ inline void RotateOperation::ensureDegree()
{
if (!this->m_isDegreeSet) {
float degree[4];
- this->m_degreeSocket->readSampled(degree, 0, 0, COM_PS_NEAREST);
+ this->m_degreeSocket->readSampled(degree, 0, 0, PixelSampler::Nearest);
double rad;
if (this->m_doDegree2RadConversion) {
rad = DEG2RAD((double)degree[0]);
@@ -105,3 +107,5 @@ bool RotateOperation::determineDependingAreaOfInterest(rcti *input,
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_RotateOperation.h b/source/blender/compositor/operations/COM_RotateOperation.h
index 9b9d5987a8e..d76507f9816 100644
--- a/source/blender/compositor/operations/COM_RotateOperation.h
+++ b/source/blender/compositor/operations/COM_RotateOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class RotateOperation : public NodeOperation {
private:
SocketReader *m_imageSocket;
@@ -46,3 +48,5 @@ class RotateOperation : public NodeOperation {
void ensureDegree();
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SMAAOperation.cc b/source/blender/compositor/operations/COM_SMAAOperation.cc
new file mode 100644
index 00000000000..74807f281d7
--- /dev/null
+++ b/source/blender/compositor/operations/COM_SMAAOperation.cc
@@ -0,0 +1,868 @@
+/*
+ * Copyright 2017, Blender Foundation.
+ *
+ * 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.
+ *
+ * Contributor: IRIE Shinsuke
+ */
+
+#include "COM_SMAAOperation.h"
+#include "BLI_math.h"
+#include "COM_SMAAAreaTexture.h"
+
+extern "C" {
+#include "IMB_colormanagement.h"
+}
+
+namespace blender::compositor {
+
+/*
+ * An implementation of Enhanced Subpixel Morphological Antialiasing (SMAA)
+ *
+ * The algorithm was proposed by:
+ * Jorge Jimenez, Jose I. Echevarria, Tiago Sousa, Diego Gutierrez
+ *
+ * http://www.iryoku.com/smaa/
+ *
+ * This file is based on smaa-cpp:
+ *
+ * https://github.com/iRi-E/smaa-cpp
+ *
+ * Currently only SMAA 1x mode is provided, so the operation will be done
+ * with no spatial multisampling nor temporal supersampling.
+ *
+ * Note: This program assumes the screen coordinates are DirectX style, so
+ * the vertical direction is upside-down. "top" and "bottom" actually mean
+ * bottom and top, respectively.
+ */
+
+/*-----------------------------------------------------------------------------*/
+/* Non-Configurable Defines */
+
+#define SMAA_AREATEX_SIZE 80
+#define SMAA_AREATEX_MAX_DISTANCE 20
+#define SMAA_AREATEX_MAX_DISTANCE_DIAG 20
+#define SMAA_MAX_SEARCH_STEPS 362 /* 362 - 1 = 19^2 */
+#define SMAA_MAX_SEARCH_STEPS_DIAG 19
+
+/*-----------------------------------------------------------------------------*/
+/* Internal Functions to Sample Pixel Color from Image */
+
+static inline void sample(SocketReader *reader, int x, int y, float color[4])
+{
+ if (x < 0 || x >= reader->getWidth() || y < 0 || y >= reader->getHeight()) {
+ color[0] = color[1] = color[2] = color[3] = 0.0;
+ return;
+ }
+
+ reader->read(color, x, y, nullptr);
+}
+
+static void sample_bilinear_vertical(
+ SocketReader *reader, int x, int y, float yoffset, float color[4])
+{
+ float iy = floorf(yoffset);
+ float fy = yoffset - iy;
+ y += (int)iy;
+
+ float color00[4], color01[4];
+
+ sample(reader, x + 0, y + 0, color00);
+ sample(reader, x + 0, y + 1, color01);
+
+ color[0] = interpf(color01[0], color00[0], fy);
+ color[1] = interpf(color01[1], color00[1], fy);
+ color[2] = interpf(color01[2], color00[2], fy);
+ color[3] = interpf(color01[3], color00[3], fy);
+}
+
+static void sample_bilinear_horizontal(
+ SocketReader *reader, int x, int y, float xoffset, float color[4])
+{
+ float ix = floorf(xoffset);
+ float fx = xoffset - ix;
+ x += (int)ix;
+
+ float color00[4], color10[4];
+
+ sample(reader, x + 0, y + 0, color00);
+ sample(reader, x + 1, y + 0, color10);
+
+ color[0] = interpf(color10[0], color00[0], fx);
+ color[1] = interpf(color10[1], color00[1], fx);
+ color[2] = interpf(color10[2], color00[2], fx);
+ color[3] = interpf(color10[3], color00[3], fx);
+}
+
+/*-----------------------------------------------------------------------------*/
+/* Internal Functions to Sample Blending Weights from AreaTex */
+
+static inline const float *areatex_sample_internal(const float *areatex, int x, int y)
+{
+ return &areatex[(CLAMPIS(x, 0, SMAA_AREATEX_SIZE - 1) +
+ CLAMPIS(y, 0, SMAA_AREATEX_SIZE - 1) * SMAA_AREATEX_SIZE) *
+ 2];
+}
+
+/**
+ * We have the distance and both crossing edges. So, what are the areas
+ * at each side of current edge?
+ */
+static void area(int d1, int d2, int e1, int e2, float weights[2])
+{
+ /* The areas texture is compressed quadratically: */
+ float x = (float)(SMAA_AREATEX_MAX_DISTANCE * e1) + sqrtf((float)d1);
+ float y = (float)(SMAA_AREATEX_MAX_DISTANCE * e2) + sqrtf((float)d2);
+
+ float ix = floorf(x), iy = floorf(y);
+ float fx = x - ix, fy = y - iy;
+ int X = (int)ix, Y = (int)iy;
+
+ const float *weights00 = areatex_sample_internal(areatex, X + 0, Y + 0);
+ const float *weights10 = areatex_sample_internal(areatex, X + 1, Y + 0);
+ const float *weights01 = areatex_sample_internal(areatex, X + 0, Y + 1);
+ const float *weights11 = areatex_sample_internal(areatex, X + 1, Y + 1);
+
+ weights[0] = interpf(
+ interpf(weights11[0], weights01[0], fx), interpf(weights10[0], weights00[0], fx), fy);
+ weights[1] = interpf(
+ interpf(weights11[1], weights01[1], fx), interpf(weights10[1], weights00[1], fx), fy);
+}
+
+/**
+ * Similar to area(), this calculates the area corresponding to a certain
+ * diagonal distance and crossing edges 'e'.
+ */
+static void area_diag(int d1, int d2, int e1, int e2, float weights[2])
+{
+ int x = SMAA_AREATEX_MAX_DISTANCE_DIAG * e1 + d1;
+ int y = SMAA_AREATEX_MAX_DISTANCE_DIAG * e2 + d2;
+
+ const float *w = areatex_sample_internal(areatex_diag, x, y);
+ copy_v2_v2(weights, w);
+}
+
+/*-----------------------------------------------------------------------------*/
+/* Edge Detection (First Pass) */
+/*-----------------------------------------------------------------------------*/
+
+SMAAEdgeDetectionOperation::SMAAEdgeDetectionOperation()
+{
+ this->addInputSocket(DataType::Color); /* image */
+ this->addInputSocket(DataType::Value); /* depth, material ID, etc. */
+ this->addOutputSocket(DataType::Color);
+ this->flags.complex = true;
+ this->m_imageReader = nullptr;
+ this->m_valueReader = nullptr;
+ this->m_threshold = 0.1f;
+ this->m_contrast_limit = 2.0f;
+}
+
+void SMAAEdgeDetectionOperation::initExecution()
+{
+ this->m_imageReader = this->getInputSocketReader(0);
+ this->m_valueReader = this->getInputSocketReader(1);
+}
+
+void SMAAEdgeDetectionOperation::deinitExecution()
+{
+ this->m_imageReader = nullptr;
+ this->m_valueReader = nullptr;
+}
+
+void SMAAEdgeDetectionOperation::setThreshold(float threshold)
+{
+ /* UI values are between 0 and 1 for simplicity but algorithm expects values between 0 and 0.5 */
+ m_threshold = scalenorm(0, 0.5, threshold);
+}
+
+void SMAAEdgeDetectionOperation::setLocalContrastAdaptationFactor(float factor)
+{
+ /* UI values are between 0 and 1 for simplicity but algorithm expects values between 1 and 10 */
+ m_contrast_limit = scalenorm(1, 10, factor);
+}
+
+bool SMAAEdgeDetectionOperation::determineDependingAreaOfInterest(
+ rcti *input, ReadBufferOperation *readOperation, rcti *output)
+{
+ rcti newInput;
+ newInput.xmax = input->xmax + 1;
+ newInput.xmin = input->xmin - 2;
+ newInput.ymax = input->ymax + 1;
+ newInput.ymin = input->ymin - 2;
+
+ return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
+}
+
+void SMAAEdgeDetectionOperation::executePixel(float output[4], int x, int y, void * /*data*/)
+{
+ float color[4];
+
+ /* Calculate luma deltas: */
+ sample(m_imageReader, x, y, color);
+ float L = IMB_colormanagement_get_luminance(color);
+ sample(m_imageReader, x - 1, y, color);
+ float Lleft = IMB_colormanagement_get_luminance(color);
+ sample(m_imageReader, x, y - 1, color);
+ float Ltop = IMB_colormanagement_get_luminance(color);
+ float Dleft = fabsf(L - Lleft);
+ float Dtop = fabsf(L - Ltop);
+
+ /* We do the usual threshold: */
+ output[0] = (x > 0 && Dleft >= m_threshold) ? 1.0f : 0.0f;
+ output[1] = (y > 0 && Dtop >= m_threshold) ? 1.0f : 0.0f;
+ output[2] = 0.0f;
+ output[3] = 1.0f;
+
+ /* Then discard if there is no edge: */
+ if (is_zero_v2(output)) {
+ return;
+ }
+
+ /* Calculate right and bottom deltas: */
+ sample(m_imageReader, x + 1, y, color);
+ float Lright = IMB_colormanagement_get_luminance(color);
+ sample(m_imageReader, x, y + 1, color);
+ float Lbottom = IMB_colormanagement_get_luminance(color);
+ float Dright = fabsf(L - Lright);
+ float Dbottom = fabsf(L - Lbottom);
+
+ /* Calculate the maximum delta in the direct neighborhood: */
+ float maxDelta = fmaxf(fmaxf(Dleft, Dright), fmaxf(Dtop, Dbottom));
+
+ /* Calculate luma used for both left and top edges: */
+ sample(m_imageReader, x - 1, y - 1, color);
+ float Llefttop = IMB_colormanagement_get_luminance(color);
+
+ /* Left edge */
+ if (output[0] != 0.0f) {
+ /* Calculate deltas around the left pixel: */
+ sample(m_imageReader, x - 2, y, color);
+ float Lleftleft = IMB_colormanagement_get_luminance(color);
+ sample(m_imageReader, x - 1, y + 1, color);
+ float Lleftbottom = IMB_colormanagement_get_luminance(color);
+ float Dleftleft = fabsf(Lleft - Lleftleft);
+ float Dlefttop = fabsf(Lleft - Llefttop);
+ float Dleftbottom = fabsf(Lleft - Lleftbottom);
+
+ /* Calculate the final maximum delta: */
+ maxDelta = fmaxf(maxDelta, fmaxf(Dleftleft, fmaxf(Dlefttop, Dleftbottom)));
+
+ /* Local contrast adaptation: */
+ if (maxDelta > m_contrast_limit * Dleft) {
+ output[0] = 0.0f;
+ }
+ }
+
+ /* Top edge */
+ if (output[1] != 0.0f) {
+ /* Calculate top-top delta: */
+ sample(m_imageReader, x, y - 2, color);
+ float Ltoptop = IMB_colormanagement_get_luminance(color);
+ sample(m_imageReader, x + 1, y - 1, color);
+ float Ltopright = IMB_colormanagement_get_luminance(color);
+ float Dtoptop = fabsf(Ltop - Ltoptop);
+ float Dtopleft = fabsf(Ltop - Llefttop);
+ float Dtopright = fabsf(Ltop - Ltopright);
+
+ /* Calculate the final maximum delta: */
+ maxDelta = fmaxf(maxDelta, fmaxf(Dtoptop, fmaxf(Dtopleft, Dtopright)));
+
+ /* Local contrast adaptation: */
+ if (maxDelta > m_contrast_limit * Dtop) {
+ output[1] = 0.0f;
+ }
+ }
+}
+
+/*-----------------------------------------------------------------------------*/
+/* Blending Weight Calculation (Second Pass) */
+/*-----------------------------------------------------------------------------*/
+
+SMAABlendingWeightCalculationOperation::SMAABlendingWeightCalculationOperation()
+{
+ this->addInputSocket(DataType::Color); /* edges */
+ this->addOutputSocket(DataType::Color);
+ this->flags.complex = true;
+ this->m_imageReader = nullptr;
+ this->m_corner_rounding = 25;
+}
+
+void *SMAABlendingWeightCalculationOperation::initializeTileData(rcti *rect)
+{
+ return getInputOperation(0)->initializeTileData(rect);
+}
+
+void SMAABlendingWeightCalculationOperation::initExecution()
+{
+ this->m_imageReader = this->getInputSocketReader(0);
+}
+
+void SMAABlendingWeightCalculationOperation::setCornerRounding(float rounding)
+{
+ /* UI values are between 0 and 1 for simplicity but algorithm expects values between 0 and 100 */
+ m_corner_rounding = static_cast<int>(scalenorm(0, 100, rounding));
+}
+
+void SMAABlendingWeightCalculationOperation::executePixel(float output[4],
+ int x,
+ int y,
+ void * /*data*/)
+{
+ float edges[4], c[4];
+
+ zero_v4(output);
+ sample(m_imageReader, x, y, edges);
+
+ /* Edge at north */
+ if (edges[1] > 0.0f) {
+ /* Diagonals have both north and west edges, so calculating weights for them */
+ /* in one of the boundaries is enough. */
+ calculateDiagWeights(x, y, edges, output);
+
+ /* We give priority to diagonals, so if we find a diagonal we skip */
+ /* horizontal/vertical processing. */
+ if (!is_zero_v2(output)) {
+ return;
+ }
+
+ /* Find the distance to the left and the right: */
+ int left = searchXLeft(x, y);
+ int right = searchXRight(x, y);
+ int d1 = x - left, d2 = right - x;
+
+ /* Fetch the left and right crossing edges: */
+ int e1 = 0, e2 = 0;
+ sample(m_imageReader, left, y - 1, c);
+ if (c[0] > 0.0) {
+ e1 += 1;
+ }
+ sample(m_imageReader, left, y, c);
+ if (c[0] > 0.0) {
+ e1 += 2;
+ }
+ sample(m_imageReader, right + 1, y - 1, c);
+ if (c[0] > 0.0) {
+ e2 += 1;
+ }
+ sample(m_imageReader, right + 1, y, c);
+ if (c[0] > 0.0) {
+ e2 += 2;
+ }
+
+ /* Ok, we know how this pattern looks like, now it is time for getting */
+ /* the actual area: */
+ area(d1, d2, e1, e2, output); /* R, G */
+
+ /* Fix corners: */
+ if (m_corner_rounding) {
+ detectHorizontalCornerPattern(output, left, right, y, d1, d2);
+ }
+ }
+
+ /* Edge at west */
+ if (edges[0] > 0.0f) {
+ /* Did we already do diagonal search for this west edge from the left neighboring pixel? */
+ if (isVerticalSearchUnneeded(x, y)) {
+ return;
+ }
+
+ /* Find the distance to the top and the bottom: */
+ int top = searchYUp(x, y);
+ int bottom = searchYDown(x, y);
+ int d1 = y - top, d2 = bottom - y;
+
+ /* Fetch the top and bottom crossing edges: */
+ int e1 = 0, e2 = 0;
+ sample(m_imageReader, x - 1, top, c);
+ if (c[1] > 0.0) {
+ e1 += 1;
+ }
+ sample(m_imageReader, x, top, c);
+ if (c[1] > 0.0) {
+ e1 += 2;
+ }
+ sample(m_imageReader, x - 1, bottom + 1, c);
+ if (c[1] > 0.0) {
+ e2 += 1;
+ }
+ sample(m_imageReader, x, bottom + 1, c);
+ if (c[1] > 0.0) {
+ e2 += 2;
+ }
+
+ /* Get the area for this direction: */
+ area(d1, d2, e1, e2, output + 2); /* B, A */
+
+ /* Fix corners: */
+ if (m_corner_rounding) {
+ detectVerticalCornerPattern(output + 2, x, top, bottom, d1, d2);
+ }
+ }
+}
+
+void SMAABlendingWeightCalculationOperation::deinitExecution()
+{
+ this->m_imageReader = nullptr;
+}
+
+bool SMAABlendingWeightCalculationOperation::determineDependingAreaOfInterest(
+ rcti *input, ReadBufferOperation *readOperation, rcti *output)
+{
+ rcti newInput;
+
+ newInput.xmax = input->xmax + fmax(SMAA_MAX_SEARCH_STEPS, SMAA_MAX_SEARCH_STEPS_DIAG + 1);
+ newInput.xmin = input->xmin -
+ fmax(fmax(SMAA_MAX_SEARCH_STEPS - 1, 1), SMAA_MAX_SEARCH_STEPS_DIAG + 1);
+ newInput.ymax = input->ymax + fmax(SMAA_MAX_SEARCH_STEPS, SMAA_MAX_SEARCH_STEPS_DIAG);
+ newInput.ymin = input->ymin -
+ fmax(fmax(SMAA_MAX_SEARCH_STEPS - 1, 1), SMAA_MAX_SEARCH_STEPS_DIAG);
+
+ return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
+}
+
+/*-----------------------------------------------------------------------------*/
+/* Diagonal Search Functions */
+
+/**
+ * These functions allows to perform diagonal pattern searches.
+ */
+int SMAABlendingWeightCalculationOperation::searchDiag1(int x, int y, int dir, bool *found)
+{
+ float e[4];
+ int end = x + SMAA_MAX_SEARCH_STEPS_DIAG * dir;
+ *found = false;
+
+ while (x != end) {
+ x += dir;
+ y -= dir;
+ sample(m_imageReader, x, y, e);
+ if (e[1] == 0.0f) {
+ *found = true;
+ break;
+ }
+ if (e[0] == 0.0f) {
+ *found = true;
+ return (dir < 0) ? x : x - dir;
+ }
+ }
+
+ return x - dir;
+}
+
+int SMAABlendingWeightCalculationOperation::searchDiag2(int x, int y, int dir, bool *found)
+{
+ float e[4];
+ int end = x + SMAA_MAX_SEARCH_STEPS_DIAG * dir;
+ *found = false;
+
+ while (x != end) {
+ x += dir;
+ y += dir;
+ sample(m_imageReader, x, y, e);
+ if (e[1] == 0.0f) {
+ *found = true;
+ break;
+ }
+ sample(m_imageReader, x + 1, y, e);
+ if (e[0] == 0.0f) {
+ *found = true;
+ return (dir > 0) ? x : x - dir;
+ }
+ }
+
+ return x - dir;
+}
+
+/**
+ * This searches for diagonal patterns and returns the corresponding weights.
+ */
+void SMAABlendingWeightCalculationOperation::calculateDiagWeights(int x,
+ int y,
+ const float edges[2],
+ float weights[2])
+{
+ int d1, d2;
+ bool d1_found, d2_found;
+ float e[4], c[4];
+
+ zero_v2(weights);
+
+ if (SMAA_MAX_SEARCH_STEPS_DIAG <= 0) {
+ return;
+ }
+
+ /* Search for the line ends: */
+ if (edges[0] > 0.0f) {
+ d1 = x - searchDiag1(x, y, -1, &d1_found);
+ }
+ else {
+ d1 = 0;
+ d1_found = true;
+ }
+ d2 = searchDiag1(x, y, 1, &d2_found) - x;
+
+ if (d1 + d2 > 2) { /* d1 + d2 + 1 > 3 */
+ int e1 = 0, e2 = 0;
+
+ if (d1_found) {
+ /* Fetch the crossing edges: */
+ int left = x - d1, bottom = y + d1;
+
+ sample(m_imageReader, left - 1, bottom, c);
+ if (c[1] > 0.0) {
+ e1 += 2;
+ }
+ sample(m_imageReader, left, bottom, c);
+ if (c[0] > 0.0) {
+ e1 += 1;
+ }
+ }
+
+ if (d2_found) {
+ /* Fetch the crossing edges: */
+ int right = x + d2, top = y - d2;
+
+ sample(m_imageReader, right + 1, top, c);
+ if (c[1] > 0.0) {
+ e2 += 2;
+ }
+ sample(m_imageReader, right + 1, top - 1, c);
+ if (c[0] > 0.0) {
+ e2 += 1;
+ }
+ }
+
+ /* Fetch the areas for this line: */
+ area_diag(d1, d2, e1, e2, weights);
+ }
+
+ /* Search for the line ends: */
+ d1 = x - searchDiag2(x, y, -1, &d1_found);
+ sample(m_imageReader, x + 1, y, e);
+ if (e[0] > 0.0f) {
+ d2 = searchDiag2(x, y, 1, &d2_found) - x;
+ }
+ else {
+ d2 = 0;
+ d2_found = true;
+ }
+
+ if (d1 + d2 > 2) { /* d1 + d2 + 1 > 3 */
+ int e1 = 0, e2 = 0;
+
+ if (d1_found) {
+ /* Fetch the crossing edges: */
+ int left = x - d1, top = y - d1;
+
+ sample(m_imageReader, left - 1, top, c);
+ if (c[1] > 0.0) {
+ e1 += 2;
+ }
+ sample(m_imageReader, left, top - 1, c);
+ if (c[0] > 0.0) {
+ e1 += 1;
+ }
+ }
+
+ if (d2_found) {
+ /* Fetch the crossing edges: */
+ int right = x + d2, bottom = y + d2;
+
+ sample(m_imageReader, right + 1, bottom, c);
+ if (c[1] > 0.0) {
+ e2 += 2;
+ }
+ if (c[0] > 0.0) {
+ e2 += 1;
+ }
+ }
+
+ /* Fetch the areas for this line: */
+ float w[2];
+ area_diag(d1, d2, e1, e2, w);
+ weights[0] += w[1];
+ weights[1] += w[0];
+ }
+}
+
+bool SMAABlendingWeightCalculationOperation::isVerticalSearchUnneeded(int x, int y)
+{
+ int d1, d2;
+ bool found;
+ float e[4];
+
+ if (SMAA_MAX_SEARCH_STEPS_DIAG <= 0) {
+ return false;
+ }
+
+ /* Search for the line ends: */
+ sample(m_imageReader, x - 1, y, e);
+ if (e[1] > 0.0f) {
+ d1 = x - searchDiag2(x - 1, y, -1, &found);
+ }
+ else {
+ d1 = 0;
+ }
+ d2 = searchDiag2(x - 1, y, 1, &found) - x;
+
+ return (d1 + d2 > 2); /* d1 + d2 + 1 > 3 */
+}
+
+/*-----------------------------------------------------------------------------*/
+/* Horizontal/Vertical Search Functions */
+
+int SMAABlendingWeightCalculationOperation::searchXLeft(int x, int y)
+{
+ int end = x - SMAA_MAX_SEARCH_STEPS;
+ float e[4];
+
+ while (x > end) {
+ sample(m_imageReader, x, y, e);
+ if (e[1] == 0.0f) { /* Is the edge not activated? */
+ break;
+ }
+ if (e[0] != 0.0f) { /* Or is there a crossing edge that breaks the line? */
+ return x;
+ }
+ sample(m_imageReader, x, y - 1, e);
+ if (e[0] != 0.0f) { /* Or is there a crossing edge that breaks the line? */
+ return x;
+ }
+ x--;
+ }
+
+ return x + 1;
+}
+
+int SMAABlendingWeightCalculationOperation::searchXRight(int x, int y)
+{
+ int end = x + SMAA_MAX_SEARCH_STEPS;
+ float e[4];
+
+ while (x < end) {
+ x++;
+ sample(m_imageReader, x, y, e);
+ if (e[1] == 0.0f || /* Is the edge not activated? */
+ e[0] != 0.0f) { /* Or is there a crossing edge that breaks the line? */
+ break;
+ }
+ sample(m_imageReader, x, y - 1, e);
+ if (e[0] != 0.0f) { /* Or is there a crossing edge that breaks the line? */
+ break;
+ }
+ }
+
+ return x - 1;
+}
+
+int SMAABlendingWeightCalculationOperation::searchYUp(int x, int y)
+{
+ int end = y - SMAA_MAX_SEARCH_STEPS;
+ float e[4];
+
+ while (y > end) {
+ sample(m_imageReader, x, y, e);
+ if (e[0] == 0.0f) { /* Is the edge not activated? */
+ break;
+ }
+ if (e[1] != 0.0f) { /* Or is there a crossing edge that breaks the line? */
+ return y;
+ }
+ sample(m_imageReader, x - 1, y, e);
+ if (e[1] != 0.0f) { /* Or is there a crossing edge that breaks the line? */
+ return y;
+ }
+ y--;
+ }
+
+ return y + 1;
+}
+
+int SMAABlendingWeightCalculationOperation::searchYDown(int x, int y)
+{
+ int end = y + SMAA_MAX_SEARCH_STEPS;
+ float e[4];
+
+ while (y < end) {
+ y++;
+ sample(m_imageReader, x, y, e);
+ if (e[0] == 0.0f || /* Is the edge not activated? */
+ e[1] != 0.0f) { /* Or is there a crossing edge that breaks the line? */
+ break;
+ }
+ sample(m_imageReader, x - 1, y, e);
+ if (e[1] != 0.0f) { /* Or is there a crossing edge that breaks the line? */
+ break;
+ }
+ }
+
+ return y - 1;
+}
+
+/*-----------------------------------------------------------------------------*/
+/* Corner Detection Functions */
+
+void SMAABlendingWeightCalculationOperation::detectHorizontalCornerPattern(
+ float weights[2], int left, int right, int y, int d1, int d2)
+{
+ float factor[2] = {1.0f, 1.0f};
+ float rounding = m_corner_rounding / 100.0f;
+ float e[4];
+
+ /* Reduce blending for pixels in the center of a line. */
+ rounding *= (d1 == d2) ? 0.5f : 1.0f;
+
+ /* Near the left corner */
+ if (d1 <= d2) {
+ sample(m_imageReader, left, y + 1, e);
+ factor[0] -= rounding * e[0];
+ sample(m_imageReader, left, y - 2, e);
+ factor[1] -= rounding * e[0];
+ }
+ /* Near the right corner */
+ if (d1 >= d2) {
+ sample(m_imageReader, right + 1, y + 1, e);
+ factor[0] -= rounding * e[0];
+ sample(m_imageReader, right + 1, y - 2, e);
+ factor[1] -= rounding * e[0];
+ }
+
+ weights[0] *= CLAMPIS(factor[0], 0.0f, 1.0f);
+ weights[1] *= CLAMPIS(factor[1], 0.0f, 1.0f);
+}
+
+void SMAABlendingWeightCalculationOperation::detectVerticalCornerPattern(
+ float weights[2], int x, int top, int bottom, int d1, int d2)
+{
+ float factor[2] = {1.0f, 1.0f};
+ float rounding = m_corner_rounding / 100.0f;
+ float e[4];
+
+ /* Reduce blending for pixels in the center of a line. */
+ rounding *= (d1 == d2) ? 0.5f : 1.0f;
+
+ /* Near the top corner */
+ if (d1 <= d2) {
+ sample(m_imageReader, x + 1, top, e);
+ factor[0] -= rounding * e[1];
+ sample(m_imageReader, x - 2, top, e);
+ factor[1] -= rounding * e[1];
+ }
+ /* Near the bottom corner */
+ if (d1 >= d2) {
+ sample(m_imageReader, x + 1, bottom + 1, e);
+ factor[0] -= rounding * e[1];
+ sample(m_imageReader, x - 2, bottom + 1, e);
+ factor[1] -= rounding * e[1];
+ }
+
+ weights[0] *= CLAMPIS(factor[0], 0.0f, 1.0f);
+ weights[1] *= CLAMPIS(factor[1], 0.0f, 1.0f);
+}
+
+/*-----------------------------------------------------------------------------*/
+/* Neighborhood Blending (Third Pass) */
+/*-----------------------------------------------------------------------------*/
+
+SMAANeighborhoodBlendingOperation::SMAANeighborhoodBlendingOperation()
+{
+ this->addInputSocket(DataType::Color); /* image */
+ this->addInputSocket(DataType::Color); /* blend */
+ this->addOutputSocket(DataType::Color);
+ this->flags.complex = true;
+ this->m_image1Reader = nullptr;
+ this->m_image2Reader = nullptr;
+}
+
+void *SMAANeighborhoodBlendingOperation::initializeTileData(rcti *rect)
+{
+ return getInputOperation(0)->initializeTileData(rect);
+}
+
+void SMAANeighborhoodBlendingOperation::initExecution()
+{
+ this->m_image1Reader = this->getInputSocketReader(0);
+ this->m_image2Reader = this->getInputSocketReader(1);
+}
+
+void SMAANeighborhoodBlendingOperation::executePixel(float output[4],
+ int x,
+ int y,
+ void * /*data*/)
+{
+ float w[4];
+
+ /* Fetch the blending weights for current pixel: */
+ sample(m_image2Reader, x, y, w);
+ float left = w[2], top = w[0];
+ sample(m_image2Reader, x + 1, y, w);
+ float right = w[3];
+ sample(m_image2Reader, x, y + 1, w);
+ float bottom = w[1];
+
+ /* Is there any blending weight with a value greater than 0.0? */
+ if (right + bottom + left + top < 1e-5f) {
+ sample(m_image1Reader, x, y, output);
+ return;
+ }
+
+ /* Calculate the blending offsets: */
+ void (*samplefunc)(SocketReader * reader, int x, int y, float xoffset, float color[4]);
+ float offset1, offset2, weight1, weight2, color1[4], color2[4];
+
+ if (fmaxf(right, left) > fmaxf(bottom, top)) { /* max(horizontal) > max(vertical) */
+ samplefunc = sample_bilinear_horizontal;
+ offset1 = right;
+ offset2 = -left;
+ weight1 = right / (right + left);
+ weight2 = left / (right + left);
+ }
+ else {
+ samplefunc = sample_bilinear_vertical;
+ offset1 = bottom;
+ offset2 = -top;
+ weight1 = bottom / (bottom + top);
+ weight2 = top / (bottom + top);
+ }
+
+ /* We exploit bilinear filtering to mix current pixel with the chosen neighbor: */
+ samplefunc(m_image1Reader, x, y, offset1, color1);
+ samplefunc(m_image1Reader, x, y, offset2, color2);
+
+ mul_v4_v4fl(output, color1, weight1);
+ madd_v4_v4fl(output, color2, weight2);
+}
+
+void SMAANeighborhoodBlendingOperation::deinitExecution()
+{
+ this->m_image1Reader = nullptr;
+ this->m_image2Reader = nullptr;
+}
+
+bool SMAANeighborhoodBlendingOperation::determineDependingAreaOfInterest(
+ rcti *input, ReadBufferOperation *readOperation, rcti *output)
+{
+ rcti newInput;
+
+ newInput.xmax = input->xmax + 1;
+ newInput.xmin = input->xmin - 1;
+ newInput.ymax = input->ymax + 1;
+ newInput.ymin = input->ymin - 1;
+
+ return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
+}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SMAAOperation.h b/source/blender/compositor/operations/COM_SMAAOperation.h
new file mode 100644
index 00000000000..781762202b4
--- /dev/null
+++ b/source/blender/compositor/operations/COM_SMAAOperation.h
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2017, Blender Foundation.
+ *
+ * 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.
+ *
+ * Contributor: IRIE Shinsuke
+ */
+
+#pragma once
+
+#include "COM_NodeOperation.h"
+
+namespace blender::compositor {
+
+/*-----------------------------------------------------------------------------*/
+/* Edge Detection (First Pass) */
+
+class SMAAEdgeDetectionOperation : public NodeOperation {
+ protected:
+ SocketReader *m_imageReader;
+ SocketReader *m_valueReader;
+
+ float m_threshold;
+ float m_contrast_limit;
+
+ public:
+ SMAAEdgeDetectionOperation();
+
+ /**
+ * the inner loop of this program
+ */
+ virtual void executePixel(float output[4], int x, int y, void *data) override;
+
+ /**
+ * Initialize the execution
+ */
+ void initExecution() override;
+
+ /**
+ * Deinitialize the execution
+ */
+ void deinitExecution() override;
+
+ void setThreshold(float threshold);
+
+ void setLocalContrastAdaptationFactor(float factor);
+
+ bool determineDependingAreaOfInterest(rcti *input,
+ ReadBufferOperation *readOperation,
+ rcti *output) override;
+};
+
+/*-----------------------------------------------------------------------------*/
+/* Blending Weight Calculation (Second Pass) */
+
+class SMAABlendingWeightCalculationOperation : public NodeOperation {
+ private:
+ SocketReader *m_imageReader;
+
+ int m_corner_rounding;
+
+ public:
+ SMAABlendingWeightCalculationOperation();
+
+ /**
+ * the inner loop of this program
+ */
+ void executePixel(float output[4], int x, int y, void *data) override;
+
+ /**
+ * Initialize the execution
+ */
+ void initExecution() override;
+ void *initializeTileData(rcti *rect) override;
+
+ /**
+ * Deinitialize the execution
+ */
+ void deinitExecution() override;
+
+ void setCornerRounding(float rounding);
+
+ bool determineDependingAreaOfInterest(rcti *input,
+ ReadBufferOperation *readOperation,
+ rcti *output) override;
+
+ private:
+ /* Diagonal Search Functions */
+ int searchDiag1(int x, int y, int dir, bool *found);
+ int searchDiag2(int x, int y, int dir, bool *found);
+ void calculateDiagWeights(int x, int y, const float edges[2], float weights[2]);
+ bool isVerticalSearchUnneeded(int x, int y);
+
+ /* Horizontal/Vertical Search Functions */
+ int searchXLeft(int x, int y);
+ int searchXRight(int x, int y);
+ int searchYUp(int x, int y);
+ int searchYDown(int x, int y);
+
+ /* Corner Detection Functions */
+ void detectHorizontalCornerPattern(float weights[2], int left, int right, int y, int d1, int d2);
+ void detectVerticalCornerPattern(float weights[2], int x, int top, int bottom, int d1, int d2);
+};
+
+/*-----------------------------------------------------------------------------*/
+/* Neighborhood Blending (Third Pass) */
+
+class SMAANeighborhoodBlendingOperation : public NodeOperation {
+ private:
+ SocketReader *m_image1Reader;
+ SocketReader *m_image2Reader;
+
+ public:
+ SMAANeighborhoodBlendingOperation();
+
+ /**
+ * the inner loop of this program
+ */
+ void executePixel(float output[4], int x, int y, void *data) override;
+
+ /**
+ * Initialize the execution
+ */
+ void initExecution() override;
+ void *initializeTileData(rcti *rect) override;
+
+ /**
+ * Deinitialize the execution
+ */
+ void deinitExecution() override;
+
+ bool determineDependingAreaOfInterest(rcti *input,
+ ReadBufferOperation *readOperation,
+ rcti *output) override;
+};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ScaleOperation.cc b/source/blender/compositor/operations/COM_ScaleOperation.cc
index b0c9bfb2663..03525d4ea01 100644
--- a/source/blender/compositor/operations/COM_ScaleOperation.cc
+++ b/source/blender/compositor/operations/COM_ScaleOperation.cc
@@ -18,6 +18,8 @@
#include "COM_ScaleOperation.h"
+namespace blender::compositor {
+
#define USE_FORCE_BILINEAR
/* XXX - ignore input and use default from old compositor,
* could become an option like the transform node - campbell
@@ -28,7 +30,7 @@
BaseScaleOperation::BaseScaleOperation()
{
#ifdef USE_FORCE_BILINEAR
- m_sampler = (int)COM_PS_BILINEAR;
+ m_sampler = (int)PixelSampler::Bilinear;
#else
m_sampler = -1;
#endif
@@ -89,8 +91,8 @@ bool ScaleOperation::determineDependingAreaOfInterest(rcti *input,
float scaleX[4];
float scaleY[4];
- this->m_inputXOperation->readSampled(scaleX, 0, 0, COM_PS_NEAREST);
- this->m_inputYOperation->readSampled(scaleY, 0, 0, COM_PS_NEAREST);
+ this->m_inputXOperation->readSampled(scaleX, 0, 0, PixelSampler::Nearest);
+ this->m_inputYOperation->readSampled(scaleY, 0, 0, PixelSampler::Nearest);
const float scx = scaleX[0];
const float scy = scaleY[0];
@@ -174,8 +176,8 @@ bool ScaleAbsoluteOperation::determineDependingAreaOfInterest(rcti *input,
float scaleX[4];
float scaleY[4];
- this->m_inputXOperation->readSampled(scaleX, 0, 0, COM_PS_NEAREST);
- this->m_inputYOperation->readSampled(scaleY, 0, 0, COM_PS_NEAREST);
+ this->m_inputXOperation->readSampled(scaleX, 0, 0, PixelSampler::Nearest);
+ this->m_inputYOperation->readSampled(scaleY, 0, 0, PixelSampler::Nearest);
const float scx = scaleX[0];
const float scy = scaleY[0];
@@ -203,7 +205,7 @@ bool ScaleAbsoluteOperation::determineDependingAreaOfInterest(rcti *input,
// Absolute fixed size
ScaleFixedSizeOperation::ScaleFixedSizeOperation() : BaseScaleOperation()
{
- this->addInputSocket(DataType::Color, COM_SC_NO_RESIZE);
+ this->addInputSocket(DataType::Color, ResizeMode::None);
this->addOutputSocket(DataType::Color);
this->setResolutionInputSocketIndex(0);
this->m_inputOperation = nullptr;
@@ -308,3 +310,5 @@ void ScaleFixedSizeOperation::determineResolution(unsigned int resolution[2],
resolution[0] = this->m_newWidth;
resolution[1] = this->m_newHeight;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ScaleOperation.h b/source/blender/compositor/operations/COM_ScaleOperation.h
index 780107910f9..dc3de3602bf 100644
--- a/source/blender/compositor/operations/COM_ScaleOperation.h
+++ b/source/blender/compositor/operations/COM_ScaleOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class BaseScaleOperation : public NodeOperation {
public:
void setSampler(PixelSampler sampler)
@@ -129,3 +131,5 @@ class ScaleFixedSizeOperation : public BaseScaleOperation {
this->m_offsetY = y;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc
index d5918dfa6f5..634fe66b0dd 100644
--- a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc
+++ b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc
@@ -24,13 +24,15 @@
#include "PIL_time.h"
+namespace blender::compositor {
+
ScreenLensDistortionOperation::ScreenLensDistortionOperation()
{
this->addInputSocket(DataType::Color);
this->addInputSocket(DataType::Value);
this->addInputSocket(DataType::Value);
this->addOutputSocket(DataType::Color);
- this->setComplex(true);
+ this->flags.complex = true;
this->m_inputProgram = nullptr;
this->m_distortion = 0.0f;
this->m_dispersion = 0.0f;
@@ -83,12 +85,12 @@ void *ScreenLensDistortionOperation::initializeTileData(rcti * /*rect*/)
if (!m_distortion_const) {
float result[4];
- getInputSocketReader(1)->readSampled(result, 0, 0, COM_PS_NEAREST);
+ getInputSocketReader(1)->readSampled(result, 0, 0, PixelSampler::Nearest);
m_distortion = result[0];
}
if (!m_dispersion_const) {
float result[4];
- getInputSocketReader(2)->readSampled(result, 0, 0, COM_PS_NEAREST);
+ getInputSocketReader(2)->readSampled(result, 0, 0, PixelSampler::Nearest);
m_dispersion = result[0];
}
@@ -351,3 +353,5 @@ void ScreenLensDistortionOperation::updateVariables(float distortion, float disp
mul_v3_v3fl(m_k4, m_k, 4.0f);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.h b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.h
index 793fc95dc3d..98872bfe142 100644
--- a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.h
+++ b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.h
@@ -21,6 +21,10 @@
#include "COM_NodeOperation.h"
#include "DNA_node_types.h"
+struct RNG;
+
+namespace blender::compositor {
+
class ScreenLensDistortionOperation : public NodeOperation {
private:
/**
@@ -96,3 +100,5 @@ class ScreenLensDistortionOperation : public NodeOperation {
float sum[4],
int count[3]) const;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.cc b/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.cc
index 3151bad5e4a..24edbc61d40 100644
--- a/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.cc
+++ b/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.cc
@@ -18,6 +18,8 @@
#include "COM_SetAlphaMultiplyOperation.h"
+namespace blender::compositor {
+
SetAlphaMultiplyOperation::SetAlphaMultiplyOperation()
{
this->addInputSocket(DataType::Color);
@@ -53,3 +55,5 @@ void SetAlphaMultiplyOperation::deinitExecution()
this->m_inputColor = nullptr;
this->m_inputAlpha = nullptr;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.h b/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.h
index 094da1fd493..b4eea659fa2 100644
--- a/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.h
+++ b/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
/**
* This operation will apply a mask to its input image.
*
@@ -38,3 +40,5 @@ class SetAlphaMultiplyOperation : public NodeOperation {
void initExecution() override;
void deinitExecution() override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.cc b/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.cc
index cd9bf039f3e..90bfc814b09 100644
--- a/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.cc
+++ b/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.cc
@@ -18,6 +18,8 @@
#include "COM_SetAlphaReplaceOperation.h"
+namespace blender::compositor {
+
SetAlphaReplaceOperation::SetAlphaReplaceOperation()
{
this->addInputSocket(DataType::Color);
@@ -51,3 +53,5 @@ void SetAlphaReplaceOperation::deinitExecution()
this->m_inputColor = nullptr;
this->m_inputAlpha = nullptr;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.h b/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.h
index 313d5f581eb..c84299b6d82 100644
--- a/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.h
+++ b/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
/**
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -43,3 +45,5 @@ class SetAlphaReplaceOperation : public NodeOperation {
void initExecution() override;
void deinitExecution() override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SetColorOperation.cc b/source/blender/compositor/operations/COM_SetColorOperation.cc
index b08381963fd..dbe45fa60db 100644
--- a/source/blender/compositor/operations/COM_SetColorOperation.cc
+++ b/source/blender/compositor/operations/COM_SetColorOperation.cc
@@ -18,9 +18,12 @@
#include "COM_SetColorOperation.h"
+namespace blender::compositor {
+
SetColorOperation::SetColorOperation()
{
this->addOutputSocket(DataType::Color);
+ flags.is_set_operation = true;
}
void SetColorOperation::executePixelSampled(float output[4],
@@ -37,3 +40,5 @@ void SetColorOperation::determineResolution(unsigned int resolution[2],
resolution[0] = preferredResolution[0];
resolution[1] = preferredResolution[1];
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SetColorOperation.h b/source/blender/compositor/operations/COM_SetColorOperation.h
index 147e76c433f..4b9b80013d4 100644
--- a/source/blender/compositor/operations/COM_SetColorOperation.h
+++ b/source/blender/compositor/operations/COM_SetColorOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
/**
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -78,8 +80,6 @@ class SetColorOperation : public NodeOperation {
void determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2]) override;
- bool isSetOperation() const override
- {
- return true;
- }
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SetSamplerOperation.cc b/source/blender/compositor/operations/COM_SetSamplerOperation.cc
index 8272ad7583d..e68774736f3 100644
--- a/source/blender/compositor/operations/COM_SetSamplerOperation.cc
+++ b/source/blender/compositor/operations/COM_SetSamplerOperation.cc
@@ -18,6 +18,8 @@
#include "COM_SetSamplerOperation.h"
+namespace blender::compositor {
+
SetSamplerOperation::SetSamplerOperation()
{
this->addInputSocket(DataType::Color);
@@ -40,3 +42,5 @@ void SetSamplerOperation::executePixelSampled(float output[4],
{
this->m_reader->readSampled(output, x, y, this->m_sampler);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SetSamplerOperation.h b/source/blender/compositor/operations/COM_SetSamplerOperation.h
index 4a0bd4563bb..d355d937806 100644
--- a/source/blender/compositor/operations/COM_SetSamplerOperation.h
+++ b/source/blender/compositor/operations/COM_SetSamplerOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
/**
* this program converts an input color to an output Sampler.
* it assumes we are in sRGB color space.
@@ -47,3 +49,5 @@ class SetSamplerOperation : public NodeOperation {
void initExecution() override;
void deinitExecution() override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SetValueOperation.cc b/source/blender/compositor/operations/COM_SetValueOperation.cc
index 98d0ad630ad..ef43cf64653 100644
--- a/source/blender/compositor/operations/COM_SetValueOperation.cc
+++ b/source/blender/compositor/operations/COM_SetValueOperation.cc
@@ -18,9 +18,12 @@
#include "COM_SetValueOperation.h"
+namespace blender::compositor {
+
SetValueOperation::SetValueOperation()
{
this->addOutputSocket(DataType::Value);
+ flags.is_set_operation = true;
}
void SetValueOperation::executePixelSampled(float output[4],
@@ -37,3 +40,5 @@ void SetValueOperation::determineResolution(unsigned int resolution[2],
resolution[0] = preferredResolution[0];
resolution[1] = preferredResolution[1];
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SetValueOperation.h b/source/blender/compositor/operations/COM_SetValueOperation.h
index b3edb215010..5383f3b5fd3 100644
--- a/source/blender/compositor/operations/COM_SetValueOperation.h
+++ b/source/blender/compositor/operations/COM_SetValueOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
/**
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -49,9 +51,6 @@ class SetValueOperation : public NodeOperation {
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
void determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2]) override;
-
- bool isSetOperation() const override
- {
- return true;
- }
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SetVectorOperation.cc b/source/blender/compositor/operations/COM_SetVectorOperation.cc
index 6b6bcad02f3..7152d5e61d4 100644
--- a/source/blender/compositor/operations/COM_SetVectorOperation.cc
+++ b/source/blender/compositor/operations/COM_SetVectorOperation.cc
@@ -19,9 +19,12 @@
#include "COM_SetVectorOperation.h"
#include "COM_defines.h"
+namespace blender::compositor {
+
SetVectorOperation::SetVectorOperation()
{
this->addOutputSocket(DataType::Vector);
+ flags.is_set_operation = true;
}
void SetVectorOperation::executePixelSampled(float output[4],
@@ -40,3 +43,5 @@ void SetVectorOperation::determineResolution(unsigned int resolution[2],
resolution[0] = preferredResolution[0];
resolution[1] = preferredResolution[1];
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SetVectorOperation.h b/source/blender/compositor/operations/COM_SetVectorOperation.h
index a48584d7ded..b444339fcb2 100644
--- a/source/blender/compositor/operations/COM_SetVectorOperation.h
+++ b/source/blender/compositor/operations/COM_SetVectorOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
/**
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -77,10 +79,6 @@ class SetVectorOperation : public NodeOperation {
void determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2]) override;
- bool isSetOperation() const override
- {
- return true;
- }
void setVector(const float vector[3])
{
@@ -89,3 +87,5 @@ class SetVectorOperation : public NodeOperation {
setZ(vector[2]);
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SocketProxyOperation.cc b/source/blender/compositor/operations/COM_SocketProxyOperation.cc
index 53f5fea8795..39876439b7b 100644
--- a/source/blender/compositor/operations/COM_SocketProxyOperation.cc
+++ b/source/blender/compositor/operations/COM_SocketProxyOperation.cc
@@ -18,14 +18,19 @@
#include "COM_SocketProxyOperation.h"
+namespace blender::compositor {
+
SocketProxyOperation::SocketProxyOperation(DataType type, bool use_conversion)
- : m_use_conversion(use_conversion)
{
this->addInputSocket(type);
this->addOutputSocket(type);
+ flags.is_proxy_operation = true;
+ flags.use_datatype_conversion = use_conversion;
}
-std::unique_ptr<MetaData> SocketProxyOperation::getMetaData() const
+std::unique_ptr<MetaData> SocketProxyOperation::getMetaData()
{
return this->getInputSocket(0)->getReader()->getMetaData();
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SocketProxyOperation.h b/source/blender/compositor/operations/COM_SocketProxyOperation.h
index 712347a8ea2..1d3b76055bd 100644
--- a/source/blender/compositor/operations/COM_SocketProxyOperation.h
+++ b/source/blender/compositor/operations/COM_SocketProxyOperation.h
@@ -20,29 +20,13 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class SocketProxyOperation : public NodeOperation {
public:
SocketProxyOperation(DataType type, bool use_conversion);
- bool isProxyOperation() const override
- {
- return true;
- }
- bool useDatatypeConversion() const override
- {
- return m_use_conversion;
- }
-
- bool getUseConversion() const
- {
- return m_use_conversion;
- }
- void setUseConversion(bool use_conversion)
- {
- m_use_conversion = use_conversion;
- }
- std::unique_ptr<MetaData> getMetaData() const override;
-
- private:
- bool m_use_conversion;
+ std::unique_ptr<MetaData> getMetaData() override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SplitOperation.cc b/source/blender/compositor/operations/COM_SplitOperation.cc
index 25438259973..a4754de370d 100644
--- a/source/blender/compositor/operations/COM_SplitOperation.cc
+++ b/source/blender/compositor/operations/COM_SplitOperation.cc
@@ -27,6 +27,8 @@
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
+namespace blender::compositor {
+
SplitOperation::SplitOperation()
{
this->addInputSocket(DataType::Color);
@@ -58,10 +60,10 @@ void SplitOperation::executePixelSampled(float output[4],
this->m_splitPercentage * this->getHeight() / 100.0f;
bool image1 = this->m_xSplit ? x > perc : y > perc;
if (image1) {
- this->m_image1Input->readSampled(output, x, y, COM_PS_NEAREST);
+ this->m_image1Input->readSampled(output, x, y, PixelSampler::Nearest);
}
else {
- this->m_image2Input->readSampled(output, x, y, COM_PS_NEAREST);
+ this->m_image2Input->readSampled(output, x, y, PixelSampler::Nearest);
}
}
@@ -76,3 +78,5 @@ void SplitOperation::determineResolution(unsigned int resolution[2],
NodeOperation::determineResolution(resolution, preferredResolution);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SplitOperation.h b/source/blender/compositor/operations/COM_SplitOperation.h
index ace5fd62eb2..09e48821dd0 100644
--- a/source/blender/compositor/operations/COM_SplitOperation.h
+++ b/source/blender/compositor/operations/COM_SplitOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class SplitOperation : public NodeOperation {
private:
SocketReader *m_image1Input;
@@ -44,3 +46,5 @@ class SplitOperation : public NodeOperation {
this->m_xSplit = xsplit;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SunBeamsOperation.cc b/source/blender/compositor/operations/COM_SunBeamsOperation.cc
index 23bf5897297..839eeb9ff8f 100644
--- a/source/blender/compositor/operations/COM_SunBeamsOperation.cc
+++ b/source/blender/compositor/operations/COM_SunBeamsOperation.cc
@@ -19,13 +19,15 @@
#include "COM_SunBeamsOperation.h"
+namespace blender::compositor {
+
SunBeamsOperation::SunBeamsOperation()
{
this->addInputSocket(DataType::Color);
this->addOutputSocket(DataType::Color);
this->setResolutionInputSocketIndex(0);
- this->setComplex(true);
+ this->flags.complex = true;
}
void SunBeamsOperation::initExecution()
@@ -138,7 +140,7 @@ template<int fxu, int fxv, int fyu, int fyv> struct BufferLineAccumulator {
falloff_factor = dist_max > dist_min ? dr / (float)(dist_max - dist_min) : 0.0f;
- float *iter = input->getBuffer() + COM_NUM_CHANNELS_COLOR * (x + input->getWidth() * y);
+ float *iter = input->getBuffer() + COM_DATA_TYPE_COLOR_CHANNELS * (x + input->getWidth() * y);
return iter;
}
@@ -167,7 +169,7 @@ template<int fxu, int fxv, int fyu, int fyv> struct BufferLineAccumulator {
if ((int)(co[0] - source[0]) == 0 && (int)(co[1] - source[1]) == 0) {
copy_v4_v4(output,
- input->getBuffer() + COM_NUM_CHANNELS_COLOR *
+ input->getBuffer() + COM_DATA_TYPE_COLOR_CHANNELS *
((int)source[0] + input->getWidth() * (int)source[1]));
return;
}
@@ -208,7 +210,7 @@ template<int fxu, int fxv, int fyu, int fyv> struct BufferLineAccumulator {
/* decrement u */
x -= fxu;
y -= fyu;
- buffer -= (fxu + fyu * buffer_width) * COM_NUM_CHANNELS_COLOR;
+ buffer -= (fxu + fyu * buffer_width) * COM_DATA_TYPE_COLOR_CHANNELS;
/* decrement v (in steps of dv < 1) */
v_local -= dv;
@@ -217,7 +219,7 @@ template<int fxu, int fxv, int fyu, int fyv> struct BufferLineAccumulator {
x -= fxv;
y -= fyv;
- buffer -= (fxv + fyv * buffer_width) * COM_NUM_CHANNELS_COLOR;
+ buffer -= (fxv + fyv * buffer_width) * COM_DATA_TYPE_COLOR_CHANNELS;
}
}
@@ -353,3 +355,5 @@ bool SunBeamsOperation::determineDependingAreaOfInterest(rcti *input,
return NodeOperation::determineDependingAreaOfInterest(&rect, readOperation, output);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SunBeamsOperation.h b/source/blender/compositor/operations/COM_SunBeamsOperation.h
index 7cf5cf9971e..d3725021cde 100644
--- a/source/blender/compositor/operations/COM_SunBeamsOperation.h
+++ b/source/blender/compositor/operations/COM_SunBeamsOperation.h
@@ -19,6 +19,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class SunBeamsOperation : public NodeOperation {
public:
SunBeamsOperation();
@@ -44,3 +46,5 @@ class SunBeamsOperation : public NodeOperation {
float m_source_px[2];
float m_ray_length_px;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_TextureOperation.cc b/source/blender/compositor/operations/COM_TextureOperation.cc
index 146f43dbe3a..059a289ae4d 100644
--- a/source/blender/compositor/operations/COM_TextureOperation.cc
+++ b/source/blender/compositor/operations/COM_TextureOperation.cc
@@ -25,6 +25,8 @@
#include "BLI_listbase.h"
#include "BLI_threads.h"
+namespace blender::compositor {
+
TextureBaseOperation::TextureBaseOperation()
{
this->addInputSocket(DataType::Vector); // offset
@@ -35,7 +37,7 @@ TextureBaseOperation::TextureBaseOperation()
this->m_rd = nullptr;
this->m_pool = nullptr;
this->m_sceneColorManage = false;
- setComplex(true);
+ flags.complex = true;
}
TextureOperation::TextureOperation() : TextureBaseOperation()
{
@@ -73,15 +75,31 @@ void TextureBaseOperation::deinitExecution()
void TextureBaseOperation::determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2])
{
- if (preferredResolution[0] == 0 || preferredResolution[1] == 0) {
- int width = this->m_rd->xsch * this->m_rd->size / 100;
- int height = this->m_rd->ysch * this->m_rd->size / 100;
- resolution[0] = width;
- resolution[1] = height;
- }
- else {
- resolution[0] = preferredResolution[0];
- resolution[1] = preferredResolution[1];
+ switch (execution_model_) {
+ case eExecutionModel::Tiled: {
+ if (preferredResolution[0] == 0 || preferredResolution[1] == 0) {
+ int width = this->m_rd->xsch * this->m_rd->size / 100;
+ int height = this->m_rd->ysch * this->m_rd->size / 100;
+ resolution[0] = width;
+ resolution[1] = height;
+ }
+ else {
+ resolution[0] = preferredResolution[0];
+ resolution[1] = preferredResolution[1];
+ }
+ break;
+ }
+ case eExecutionModel::FullFrame: {
+ /* Determine inputs resolutions. */
+ unsigned int temp[2];
+ NodeOperation::determineResolution(temp, preferredResolution);
+
+ /* We don't use inputs resolutions because they are only used as parameters, not image data.
+ */
+ resolution[0] = preferredResolution[0];
+ resolution[1] = preferredResolution[1];
+ break;
+ }
}
}
@@ -155,3 +173,5 @@ void TextureBaseOperation::executePixelSampled(float output[4],
output[0] = output[1] = output[2] = output[3];
}
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_TextureOperation.h b/source/blender/compositor/operations/COM_TextureOperation.h
index 1a6e005f752..e5f56673694 100644
--- a/source/blender/compositor/operations/COM_TextureOperation.h
+++ b/source/blender/compositor/operations/COM_TextureOperation.h
@@ -26,6 +26,8 @@
#include "RE_pipeline.h"
#include "RE_texture.h"
+namespace blender::compositor {
+
/**
* Base class for all renderlayeroperations
*
@@ -42,7 +44,7 @@ class TextureBaseOperation : public NodeOperation {
protected:
/**
- * Determine the output resolution. The resolution is retrieved from the Renderer
+ * Determine the output resolution.
*/
void determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2]) override;
@@ -80,3 +82,5 @@ class TextureAlphaOperation : public TextureBaseOperation {
TextureAlphaOperation();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_TonemapOperation.cc b/source/blender/compositor/operations/COM_TonemapOperation.cc
index 85011171432..6bfacb0c75d 100644
--- a/source/blender/compositor/operations/COM_TonemapOperation.cc
+++ b/source/blender/compositor/operations/COM_TonemapOperation.cc
@@ -22,14 +22,16 @@
#include "IMB_colormanagement.h"
+namespace blender::compositor {
+
TonemapOperation::TonemapOperation()
{
- this->addInputSocket(DataType::Color, COM_SC_NO_RESIZE);
+ this->addInputSocket(DataType::Color, ResizeMode::None);
this->addOutputSocket(DataType::Color);
this->m_imageReader = nullptr;
this->m_data = nullptr;
this->m_cachedInstance = nullptr;
- this->setComplex(true);
+ this->flags.complex = true;
}
void TonemapOperation::initExecution()
{
@@ -150,3 +152,5 @@ void TonemapOperation::deinitializeTileData(rcti * /*rect*/, void * /*data*/)
{
/* pass */
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_TonemapOperation.h b/source/blender/compositor/operations/COM_TonemapOperation.h
index e7da983fe61..7ecb179504d 100644
--- a/source/blender/compositor/operations/COM_TonemapOperation.h
+++ b/source/blender/compositor/operations/COM_TonemapOperation.h
@@ -21,6 +21,8 @@
#include "COM_NodeOperation.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
/**
* \brief temporarily storage during execution of Tone-map
* \ingroup operation
@@ -98,3 +100,5 @@ class PhotoreceptorTonemapOperation : public TonemapOperation {
*/
void executePixel(float output[4], int x, int y, void *data) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_TrackPositionOperation.cc b/source/blender/compositor/operations/COM_TrackPositionOperation.cc
index 97d602aa458..993410e3e84 100644
--- a/source/blender/compositor/operations/COM_TrackPositionOperation.cc
+++ b/source/blender/compositor/operations/COM_TrackPositionOperation.cc
@@ -28,6 +28,8 @@
#include "BKE_node.h"
#include "BKE_tracking.h"
+namespace blender::compositor {
+
TrackPositionOperation::TrackPositionOperation()
{
this->addOutputSocket(DataType::Value);
@@ -39,6 +41,7 @@ TrackPositionOperation::TrackPositionOperation()
this->m_position = CMP_TRACKPOS_ABSOLUTE;
this->m_relativeFrame = 0;
this->m_speed_output = false;
+ flags.is_set_operation = true;
}
void TrackPositionOperation::initExecution()
@@ -134,3 +137,5 @@ void TrackPositionOperation::determineResolution(unsigned int resolution[2],
resolution[0] = preferredResolution[0];
resolution[1] = preferredResolution[1];
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_TrackPositionOperation.h b/source/blender/compositor/operations/COM_TrackPositionOperation.h
index 2e2c1f36c52..b0b0a123bd6 100644
--- a/source/blender/compositor/operations/COM_TrackPositionOperation.h
+++ b/source/blender/compositor/operations/COM_TrackPositionOperation.h
@@ -28,6 +28,8 @@
#include "BLI_listbase.h"
#include "BLI_string.h"
+namespace blender::compositor {
+
/**
* Class with implementation of green screen gradient rasterization
*/
@@ -91,9 +93,6 @@ class TrackPositionOperation : public NodeOperation {
void initExecution() override;
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
-
- bool isSetOperation() const override
- {
- return true;
- }
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_TranslateOperation.cc b/source/blender/compositor/operations/COM_TranslateOperation.cc
index 7efd655b1df..49135f25320 100644
--- a/source/blender/compositor/operations/COM_TranslateOperation.cc
+++ b/source/blender/compositor/operations/COM_TranslateOperation.cc
@@ -18,6 +18,8 @@
#include "COM_TranslateOperation.h"
+namespace blender::compositor {
+
TranslateOperation::TranslateOperation()
{
this->addInputSocket(DataType::Color);
@@ -56,7 +58,7 @@ void TranslateOperation::executePixelSampled(float output[4],
float originalXPos = x - this->getDeltaX();
float originalYPos = y - this->getDeltaY();
- this->m_inputOperation->readSampled(output, originalXPos, originalYPos, COM_PS_BILINEAR);
+ this->m_inputOperation->readSampled(output, originalXPos, originalYPos, PixelSampler::Bilinear);
}
bool TranslateOperation::determineDependingAreaOfInterest(rcti *input,
@@ -80,3 +82,5 @@ void TranslateOperation::setFactorXY(float factorX, float factorY)
m_factorX = factorX;
m_factorY = factorY;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_TranslateOperation.h b/source/blender/compositor/operations/COM_TranslateOperation.h
index d55cc9096c0..eb3a664159f 100644
--- a/source/blender/compositor/operations/COM_TranslateOperation.h
+++ b/source/blender/compositor/operations/COM_TranslateOperation.h
@@ -20,6 +20,8 @@
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class TranslateOperation : public NodeOperation {
private:
SocketReader *m_inputOperation;
@@ -54,9 +56,9 @@ class TranslateOperation : public NodeOperation {
{
if (!this->m_isDeltaSet) {
float tempDelta[4];
- this->m_inputXOperation->readSampled(tempDelta, 0, 0, COM_PS_NEAREST);
+ this->m_inputXOperation->readSampled(tempDelta, 0, 0, PixelSampler::Nearest);
this->m_deltaX = tempDelta[0];
- this->m_inputYOperation->readSampled(tempDelta, 0, 0, COM_PS_NEAREST);
+ this->m_inputYOperation->readSampled(tempDelta, 0, 0, PixelSampler::Nearest);
this->m_deltaY = tempDelta[0];
this->m_isDeltaSet = true;
}
@@ -64,3 +66,5 @@ class TranslateOperation : public NodeOperation {
void setFactorXY(float factorX, float factorY);
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc
index ea33f3cd787..19cd5a53084 100644
--- a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc
+++ b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc
@@ -22,18 +22,20 @@
#include "RE_pipeline.h"
+namespace blender::compositor {
+
VariableSizeBokehBlurOperation::VariableSizeBokehBlurOperation()
{
this->addInputSocket(DataType::Color);
- this->addInputSocket(DataType::Color, COM_SC_NO_RESIZE); // do not resize the bokeh image.
+ this->addInputSocket(DataType::Color, ResizeMode::None); // do not resize the bokeh image.
this->addInputSocket(DataType::Value); // radius
#ifdef COM_DEFOCUS_SEARCH
this->addInputSocket(DataType::Color,
- COM_SC_NO_RESIZE); // inverse search radius optimization structure.
+ ResizeMode::None); // inverse search radius optimization structure.
#endif
this->addOutputSocket(DataType::Color);
- this->setComplex(true);
- this->setOpenCL(true);
+ flags.complex = true;
+ flags.open_cl = true;
this->m_inputProgram = nullptr;
this->m_inputBokehProgram = nullptr;
@@ -135,14 +137,14 @@ void VariableSizeBokehBlurOperation::executePixel(float output[4], int x, int y,
const int addXStepValue = QualityStepHelper::getStep();
const int addYStepValue = addXStepValue;
- const int addXStepColor = addXStepValue * COM_NUM_CHANNELS_COLOR;
+ const int addXStepColor = addXStepValue * COM_DATA_TYPE_COLOR_CHANNELS;
if (size_center > this->m_threshold) {
for (int ny = miny; ny < maxy; ny += addYStepValue) {
float dy = ny - y;
int offsetValueNy = ny * inputSizeBuffer->getWidth();
int offsetValueNxNy = offsetValueNy + (minx);
- int offsetColorNxNy = offsetValueNxNy * COM_NUM_CHANNELS_COLOR;
+ int offsetColorNxNy = offsetValueNxNy * COM_DATA_TYPE_COLOR_CHANNELS;
for (int nx = minx; nx < maxx; nx += addXStepValue) {
if (nx != x || ny != y) {
float size = MIN2(inputSizeFloatBuffer[offsetValueNxNy] * scalar, size_center);
@@ -278,9 +280,9 @@ bool VariableSizeBokehBlurOperation::determineDependingAreaOfInterest(
// InverseSearchRadiusOperation
InverseSearchRadiusOperation::InverseSearchRadiusOperation()
{
- this->addInputSocket(DataType::Value, COM_SC_NO_RESIZE); // radius
+ this->addInputSocket(DataType::Value, ResizeMode::None); // radius
this->addOutputSocket(DataType::Color);
- this->setComplex(true);
+ this->flags.complex = true;
this->m_inputRadius = nullptr;
}
@@ -319,7 +321,7 @@ void *InverseSearchRadiusOperation::initializeTileData(rcti *rect)
// for (int x2 = 0 ; x2 < DIVIDER ; x2 ++) {
// for (int y2 = 0 ; y2 < DIVIDER ; y2 ++) {
- // this->m_inputRadius->read(temp, rx+x2, ry+y2, COM_PS_NEAREST);
+ // this->m_inputRadius->read(temp, rx+x2, ry+y2, PixelSampler::Nearest);
// if (radius < temp[0]) {
// radius = temp[0];
// maxx = x2;
@@ -381,3 +383,5 @@ bool InverseSearchRadiusOperation::determineDependingAreaOfInterest(
return NodeOperation::determineDependingAreaOfInterest(&newRect, readOperation, output);
}
#endif
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.h b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.h
index 5b859e5a2fd..baeab6a646e 100644
--- a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.h
+++ b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.h
@@ -21,6 +21,8 @@
#include "COM_NodeOperation.h"
#include "COM_QualityStepHelper.h"
+namespace blender::compositor {
+
//#define COM_DEFOCUS_SEARCH
class VariableSizeBokehBlurOperation : public NodeOperation, public QualityStepHelper {
@@ -124,3 +126,5 @@ class InverseSearchRadiusOperation : public NodeOperation {
}
};
#endif
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_VectorBlurOperation.cc b/source/blender/compositor/operations/COM_VectorBlurOperation.cc
index ff9eef8a7e1..fd64bda156b 100644
--- a/source/blender/compositor/operations/COM_VectorBlurOperation.cc
+++ b/source/blender/compositor/operations/COM_VectorBlurOperation.cc
@@ -25,6 +25,8 @@
#include "COM_VectorBlurOperation.h"
+namespace blender::compositor {
+
/* Defined */
#define PASS_VECTOR_MAX 10000.0f
@@ -54,7 +56,7 @@ VectorBlurOperation::VectorBlurOperation()
this->m_inputImageProgram = nullptr;
this->m_inputSpeedProgram = nullptr;
this->m_inputZProgram = nullptr;
- setComplex(true);
+ flags.complex = true;
}
void VectorBlurOperation::initExecution()
{
@@ -69,7 +71,7 @@ void VectorBlurOperation::initExecution()
void VectorBlurOperation::executePixel(float output[4], int x, int y, void *data)
{
float *buffer = (float *)data;
- int index = (y * this->getWidth() + x) * COM_NUM_CHANNELS_COLOR;
+ int index = (y * this->getWidth() + x) * COM_DATA_TYPE_COLOR_CHANNELS;
copy_v4_v4(output, &buffer[index]);
}
@@ -897,3 +899,5 @@ void zbuf_accumulate_vecblur(NodeBlurData *nbd,
}
zbuf_free_span(&zspan);
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_VectorBlurOperation.h b/source/blender/compositor/operations/COM_VectorBlurOperation.h
index 10affb48f20..dfcf1fb16f7 100644
--- a/source/blender/compositor/operations/COM_VectorBlurOperation.h
+++ b/source/blender/compositor/operations/COM_VectorBlurOperation.h
@@ -22,6 +22,8 @@
#include "COM_QualityStepHelper.h"
#include "DNA_node_types.h"
+namespace blender::compositor {
+
class VectorBlurOperation : public NodeOperation, public QualityStepHelper {
private:
/**
@@ -72,3 +74,5 @@ class VectorBlurOperation : public NodeOperation, public QualityStepHelper {
MemoryBuffer *inputSpeed,
MemoryBuffer *inputZ);
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_VectorCurveOperation.cc b/source/blender/compositor/operations/COM_VectorCurveOperation.cc
index a6638a78e88..9d53ed5d8ee 100644
--- a/source/blender/compositor/operations/COM_VectorCurveOperation.cc
+++ b/source/blender/compositor/operations/COM_VectorCurveOperation.cc
@@ -20,6 +20,8 @@
#include "BKE_colortools.h"
+namespace blender::compositor {
+
VectorCurveOperation::VectorCurveOperation()
{
this->addInputSocket(DataType::Vector);
@@ -50,3 +52,5 @@ void VectorCurveOperation::deinitExecution()
CurveBaseOperation::deinitExecution();
this->m_inputProgram = nullptr;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_VectorCurveOperation.h b/source/blender/compositor/operations/COM_VectorCurveOperation.h
index 0c164a1f3c8..8cbb80e27c7 100644
--- a/source/blender/compositor/operations/COM_VectorCurveOperation.h
+++ b/source/blender/compositor/operations/COM_VectorCurveOperation.h
@@ -21,6 +21,8 @@
#include "COM_CurveBaseOperation.h"
#include "COM_NodeOperation.h"
+namespace blender::compositor {
+
class VectorCurveOperation : public CurveBaseOperation {
private:
/**
@@ -46,3 +48,5 @@ class VectorCurveOperation : public CurveBaseOperation {
*/
void deinitExecution() override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ViewerOperation.cc b/source/blender/compositor/operations/COM_ViewerOperation.cc
index ea5937d8afb..860f56e23fa 100644
--- a/source/blender/compositor/operations/COM_ViewerOperation.cc
+++ b/source/blender/compositor/operations/COM_ViewerOperation.cc
@@ -32,6 +32,8 @@
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
+namespace blender::compositor {
+
ViewerOperation::ViewerOperation()
{
this->setImage(nullptr);
@@ -53,6 +55,8 @@ ViewerOperation::ViewerOperation()
this->m_depthInput = nullptr;
this->m_rd = nullptr;
this->m_viewName = nullptr;
+ flags.use_viewer_border = true;
+ flags.is_viewer_operation = true;
}
void ViewerOperation::initExecution()
@@ -98,12 +102,12 @@ void ViewerOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/)
for (y = y1; y < y2 && (!breaked); y++) {
for (x = x1; x < x2; x++) {
- this->m_imageInput->readSampled(&(buffer[offset4]), x, y, COM_PS_NEAREST);
+ this->m_imageInput->readSampled(&(buffer[offset4]), x, y, PixelSampler::Nearest);
if (this->m_useAlphaInput) {
- this->m_alphaInput->readSampled(alpha, x, y, COM_PS_NEAREST);
+ this->m_alphaInput->readSampled(alpha, x, y, PixelSampler::Nearest);
buffer[offset4 + 3] = alpha[0];
}
- this->m_depthInput->readSampled(depth, x, y, COM_PS_NEAREST);
+ this->m_depthInput->readSampled(depth, x, y, PixelSampler::Nearest);
depthbuffer[offset] = depth[0];
offset++;
@@ -205,11 +209,13 @@ void ViewerOperation::updateImage(rcti *rect)
this->updateDraw();
}
-CompositorPriority ViewerOperation::getRenderPriority() const
+eCompositorPriority ViewerOperation::getRenderPriority() const
{
if (this->isActiveViewerOutput()) {
- return CompositorPriority::High;
+ return eCompositorPriority::High;
}
- return CompositorPriority::Low;
+ return eCompositorPriority::Low;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ViewerOperation.h b/source/blender/compositor/operations/COM_ViewerOperation.h
index a3df19d5e90..c0f13ff79fc 100644
--- a/source/blender/compositor/operations/COM_ViewerOperation.h
+++ b/source/blender/compositor/operations/COM_ViewerOperation.h
@@ -23,6 +23,8 @@
#include "COM_NodeOperation.h"
#include "DNA_image_types.h"
+namespace blender::compositor {
+
class ViewerOperation : public NodeOperation {
private:
float *m_outputBuffer;
@@ -100,11 +102,7 @@ class ViewerOperation : public NodeOperation {
{
return this->m_chunkOrder;
}
- CompositorPriority getRenderPriority() const override;
- bool isViewerOperation() const override
- {
- return true;
- }
+ eCompositorPriority getRenderPriority() const override;
void setUseAlphaInput(bool value)
{
this->m_useAlphaInput = value;
@@ -131,3 +129,5 @@ class ViewerOperation : public NodeOperation {
void updateImage(rcti *rect);
void initImage();
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_WrapOperation.cc b/source/blender/compositor/operations/COM_WrapOperation.cc
index a869666967e..d0d2fcac3ac 100644
--- a/source/blender/compositor/operations/COM_WrapOperation.cc
+++ b/source/blender/compositor/operations/COM_WrapOperation.cc
@@ -20,6 +20,8 @@
#include "COM_WrapOperation.h"
+namespace blender::compositor {
+
WrapOperation::WrapOperation(DataType datatype) : ReadBufferOperation(datatype)
{
this->m_wrappingType = CMP_NODE_WRAP_NONE;
@@ -115,3 +117,5 @@ void WrapOperation::setWrapping(int wrapping_type)
{
m_wrappingType = wrapping_type;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_WrapOperation.h b/source/blender/compositor/operations/COM_WrapOperation.h
index 21123ed490c..6279129a550 100644
--- a/source/blender/compositor/operations/COM_WrapOperation.h
+++ b/source/blender/compositor/operations/COM_WrapOperation.h
@@ -20,6 +20,8 @@
#include "COM_ReadBufferOperation.h"
+namespace blender::compositor {
+
class WrapOperation : public ReadBufferOperation {
private:
int m_wrappingType;
@@ -37,3 +39,5 @@ class WrapOperation : public ReadBufferOperation {
void setFactorXY(float factorX, float factorY);
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_WriteBufferOperation.cc b/source/blender/compositor/operations/COM_WriteBufferOperation.cc
index e426bc76ef3..1aa19f26e2b 100644
--- a/source/blender/compositor/operations/COM_WriteBufferOperation.cc
+++ b/source/blender/compositor/operations/COM_WriteBufferOperation.cc
@@ -21,12 +21,15 @@
#include "COM_defines.h"
#include <cstdio>
+namespace blender::compositor {
+
WriteBufferOperation::WriteBufferOperation(DataType datatype)
{
this->addInputSocket(datatype);
this->m_memoryProxy = new MemoryProxy(datatype);
this->m_memoryProxy->setWriteBufferOperation(this);
this->m_memoryProxy->setExecutor(nullptr);
+ flags.is_write_buffer_operation = true;
}
WriteBufferOperation::~WriteBufferOperation()
{
@@ -61,7 +64,7 @@ void WriteBufferOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/
MemoryBuffer *memoryBuffer = this->m_memoryProxy->getBuffer();
float *buffer = memoryBuffer->getBuffer();
const uint8_t num_channels = memoryBuffer->get_num_channels();
- if (this->m_input->isComplex()) {
+ if (this->m_input->get_flags().complex) {
void *data = this->m_input->initializeTileData(rect);
int x1 = rect->xmin;
int y1 = rect->ymin;
@@ -97,7 +100,7 @@ void WriteBufferOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/
for (y = y1; y < y2 && (!breaked); y++) {
int offset4 = (y * memoryBuffer->getWidth() + x1) * num_channels;
for (x = x1; x < x2; x++) {
- this->m_input->readSampled(&(buffer[offset4]), x, y, COM_PS_NEAREST);
+ this->m_input->readSampled(&(buffer[offset4]), x, y, PixelSampler::Nearest);
offset4 += num_channels;
}
if (isBraked()) {
@@ -225,3 +228,5 @@ void WriteBufferOperation::readResolutionFromInputSocket()
this->setWidth(inputOperation->getWidth());
this->setHeight(inputOperation->getHeight());
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_WriteBufferOperation.h b/source/blender/compositor/operations/COM_WriteBufferOperation.h
index 00d48e38fca..2817fbe24b9 100644
--- a/source/blender/compositor/operations/COM_WriteBufferOperation.h
+++ b/source/blender/compositor/operations/COM_WriteBufferOperation.h
@@ -20,7 +20,12 @@
#include "COM_MemoryProxy.h"
#include "COM_NodeOperation.h"
-#include "COM_SocketReader.h"
+
+namespace blender::compositor {
+
+class OpenCLDevice;
+class MemoryProxy;
+
/**
* \brief NodeOperation to write to a tile
* \ingroup Operation
@@ -38,10 +43,6 @@ class WriteBufferOperation : public NodeOperation {
return this->m_memoryProxy;
}
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
- bool isWriteBufferOperation() const override
- {
- return true;
- }
bool isSingleValue() const
{
return m_single_value;
@@ -63,3 +64,5 @@ class WriteBufferOperation : public NodeOperation {
return m_input;
}
};
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ZCombineOperation.cc b/source/blender/compositor/operations/COM_ZCombineOperation.cc
index 8afdbcc7c2d..9d3ca7e736e 100644
--- a/source/blender/compositor/operations/COM_ZCombineOperation.cc
+++ b/source/blender/compositor/operations/COM_ZCombineOperation.cc
@@ -19,6 +19,8 @@
#include "COM_ZCombineOperation.h"
#include "BLI_utildefines.h"
+namespace blender::compositor {
+
ZCombineOperation::ZCombineOperation()
{
this->addInputSocket(DataType::Color);
@@ -158,3 +160,5 @@ void ZCombineMaskOperation::deinitExecution()
this->m_maskReader = nullptr;
this->m_image2Reader = nullptr;
}
+
+} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ZCombineOperation.h b/source/blender/compositor/operations/COM_ZCombineOperation.h
index 91044d44985..d0b1aee7310 100644
--- a/source/blender/compositor/operations/COM_ZCombineOperation.h
+++ b/source/blender/compositor/operations/COM_ZCombineOperation.h
@@ -20,6 +20,8 @@
#include "COM_MixOperation.h"
+namespace blender::compositor {
+
/**
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
@@ -66,3 +68,5 @@ class ZCombineMaskOperation : public NodeOperation {
class ZCombineMaskAlphaOperation : public ZCombineMaskOperation {
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
};
+
+} // namespace blender::compositor
diff --git a/source/blender/datatoc/datatoc_icon.c b/source/blender/datatoc/datatoc_icon.c
index f4f510891e0..7f1d90f20ea 100644
--- a/source/blender/datatoc/datatoc_icon.c
+++ b/source/blender/datatoc/datatoc_icon.c
@@ -66,9 +66,9 @@ static bool path_test_extension(const char *str, const char *ext)
return !(a == 0 || b == 0 || b >= a) && (strcmp(ext, str + a - b) == 0);
}
-static void endian_switch_uint32(unsigned int *val)
+static void endian_switch_uint32(uint *val)
{
- unsigned int tval = *val;
+ uint tval = *val;
*val = ((tval >> 24)) | ((tval << 8) & 0x00ff0000) | ((tval >> 8) & 0x0000ff00) | ((tval << 24));
}
@@ -96,10 +96,7 @@ static const char *path_basename(const char *path)
/* -------------------------------------------------------------------- */
/* Write a PNG from RGBA pixels */
-static bool write_png(const char *name,
- const unsigned int *pixels,
- const int width,
- const int height)
+static bool write_png(const char *name, const uint *pixels, const int width, const int height)
{
png_structp png_ptr;
png_infop info_ptr;
@@ -199,9 +196,9 @@ static bool write_png(const char *name,
/* Merge icon-data from files */
struct IconHead {
- unsigned int icon_w, icon_h;
- unsigned int orig_x, orig_y;
- unsigned int canvas_w, canvas_h;
+ uint icon_w, icon_h;
+ uint orig_x, orig_y;
+ uint canvas_w, canvas_h;
};
struct IconInfo {
@@ -289,10 +286,10 @@ static bool icon_decode_head(FILE *f_src, struct IconHead *r_head)
return false;
}
-static bool icon_decode(FILE *f_src, struct IconHead *r_head, unsigned int **r_pixels)
+static bool icon_decode(FILE *f_src, struct IconHead *r_head, uint **r_pixels)
{
- unsigned int *pixels;
- unsigned int pixels_size;
+ uint *pixels;
+ uint pixels_size;
if (!icon_decode_head(f_src, r_head)) {
printf("%s: failed to read header\n", __func__);
@@ -316,7 +313,7 @@ static bool icon_decode(FILE *f_src, struct IconHead *r_head, unsigned int **r_p
return true;
}
-static bool icon_read(const char *file_src, struct IconHead *r_head, unsigned int **r_pixels)
+static bool icon_read(const char *file_src, struct IconHead *r_head, uint **r_pixels)
{
FILE *f_src;
bool success;
@@ -335,18 +332,18 @@ static bool icon_read(const char *file_src, struct IconHead *r_head, unsigned in
static bool icon_merge(struct IconMergeContext *context,
const char *file_src,
- unsigned int **r_pixels_canvas,
- unsigned int *r_canvas_w,
- unsigned int *r_canvas_h)
+ uint32_t **r_pixels_canvas,
+ uint *r_canvas_w,
+ uint *r_canvas_h)
{
struct IconHead head;
- unsigned int *pixels;
+ uint *pixels;
- unsigned int x, y;
+ uint x, y;
/* canvas */
- unsigned int *pixels_canvas;
- unsigned int canvas_w, canvas_h;
+ uint32_t *pixels_canvas;
+ uint canvas_w, canvas_h;
if (!icon_read(file_src, &head, &pixels)) {
return false;
@@ -365,7 +362,7 @@ static bool icon_merge(struct IconMergeContext *context,
/* init once */
*r_canvas_w = head.canvas_w;
*r_canvas_h = head.canvas_h;
- *r_pixels_canvas = calloc(1, (head.canvas_w * head.canvas_h) * sizeof(const unsigned char[4]));
+ *r_pixels_canvas = calloc(1, (head.canvas_w * head.canvas_h) * sizeof(uint32_t));
}
canvas_w = *r_canvas_w;
@@ -377,9 +374,9 @@ static bool icon_merge(struct IconMergeContext *context,
for (x = 0; x < head.icon_w; x++) {
for (y = 0; y < head.icon_h; y++) {
- unsigned int pixel;
- unsigned int dst_x, dst_y;
- unsigned int pixel_xy_dst;
+ uint pixel;
+ uint dst_x, dst_y;
+ uint pixel_xy_dst;
/* get pixel */
pixel = pixels[(y * head.icon_w) + x];
@@ -413,8 +410,8 @@ static bool icondir_to_png(const char *path_src, const char *file_dst)
struct IconMergeContext context;
- unsigned int *pixels_canvas = NULL;
- unsigned int canvas_w = 0, canvas_h = 0;
+ uint32_t *pixels_canvas = NULL;
+ uint canvas_w = 0, canvas_h = 0;
icon_merge_context_init(&context);
diff --git a/source/blender/depsgraph/DEG_depsgraph.h b/source/blender/depsgraph/DEG_depsgraph.h
index 567916fdebe..740124f6113 100644
--- a/source/blender/depsgraph/DEG_depsgraph.h
+++ b/source/blender/depsgraph/DEG_depsgraph.h
@@ -139,16 +139,19 @@ void DEG_graph_time_tag_update(struct Depsgraph *depsgraph);
void DEG_graph_id_type_tag(struct Depsgraph *depsgraph, short id_type);
void DEG_id_type_tag(struct Main *bmain, short id_type);
-void DEG_ids_clear_recalc(struct Main *bmain, Depsgraph *depsgraph);
+/* Set a depsgraph to flush updates to editors. This would be done
+ * for viewport depsgraphs, but not render or export depsgraph for example. */
+void DEG_enable_editors_update(struct Depsgraph *depsgraph);
-/* Check if something was changed in the database and inform
- * editors about this.
- */
-void DEG_ids_check_recalc(struct Main *bmain,
- struct Depsgraph *depsgraph,
- struct Scene *scene,
- struct ViewLayer *view_layer,
- bool time);
+/* Check if something was changed in the database and inform editors about this. */
+void DEG_editors_update(struct Depsgraph *depsgraph, bool time);
+
+/* Clear recalc flags after editors or renderers have handled updates. */
+void DEG_ids_clear_recalc(Depsgraph *depsgraph, const bool backup);
+
+/* Restore recalc flags, backed up by a previous call to DEG_ids_clear_recalc.
+ * This also clears the backup. */
+void DEG_ids_restore_recalc(Depsgraph *depsgraph);
/* ************************************************ */
/* Evaluation Engine API */
diff --git a/source/blender/depsgraph/DEG_depsgraph_build.h b/source/blender/depsgraph/DEG_depsgraph_build.h
index 4e618d8625d..b4acf9b010c 100644
--- a/source/blender/depsgraph/DEG_depsgraph_build.h
+++ b/source/blender/depsgraph/DEG_depsgraph_build.h
@@ -40,6 +40,7 @@ struct Object;
struct Scene;
struct Simulation;
struct bNodeTree;
+struct Collection;
#include "BLI_sys_types.h"
@@ -137,6 +138,12 @@ void DEG_add_object_relation(struct DepsNodeHandle *node_handle,
struct Object *object,
eDepsObjectComponentType component,
const char *description);
+void DEG_add_collection_geometry_relation(struct DepsNodeHandle *node_handle,
+ struct Collection *collection,
+ const char *description);
+void DEG_add_collection_geometry_customdata_mask(struct DepsNodeHandle *node_handle,
+ struct Collection *collection,
+ const struct CustomData_MeshMasks *masks);
void DEG_add_simulation_relation(struct DepsNodeHandle *node_handle,
struct Simulation *simulation,
const char *description);
@@ -182,6 +189,8 @@ void DEG_add_customdata_mask(struct DepsNodeHandle *handle,
struct ID *DEG_get_id_from_handle(struct DepsNodeHandle *node_handle);
struct Depsgraph *DEG_get_graph_from_handle(struct DepsNodeHandle *node_handle);
+bool DEG_object_has_geometry_component(struct Object *object);
+
/* ************************************************ */
#ifdef __cplusplus
diff --git a/source/blender/depsgraph/intern/builder/deg_builder.cc b/source/blender/depsgraph/intern/builder/deg_builder.cc
index e4660c34762..f4d65698bee 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder.cc
@@ -77,10 +77,6 @@ DepsgraphBuilder::DepsgraphBuilder(Main *bmain, Depsgraph *graph, DepsgraphBuild
{
}
-DepsgraphBuilder::~DepsgraphBuilder()
-{
-}
-
bool DepsgraphBuilder::need_pull_base_into_graph(Base *base)
{
/* Simple check: enabled bases are always part of dependency graph. */
diff --git a/source/blender/depsgraph/intern/builder/deg_builder.h b/source/blender/depsgraph/intern/builder/deg_builder.h
index 36b6b1bf17d..6e1c8d8526f 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder.h
@@ -37,7 +37,7 @@ class DepsgraphBuilderCache;
class DepsgraphBuilder {
public:
- virtual ~DepsgraphBuilder();
+ virtual ~DepsgraphBuilder() = default;
virtual bool need_pull_base_into_graph(Base *base);
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_cache.cc b/source/blender/depsgraph/intern/builder/deg_builder_cache.cc
index df108072142..af7717d7595 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_cache.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_cache.cc
@@ -149,10 +149,6 @@ bool AnimatedPropertyStorage::isPropertyAnimated(const PointerRNA *pointer_rna,
/* Builder cache itself. */
-DepsgraphBuilderCache::DepsgraphBuilderCache()
-{
-}
-
DepsgraphBuilderCache::~DepsgraphBuilderCache()
{
for (AnimatedPropertyStorage *animated_property_storage :
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_cache.h b/source/blender/depsgraph/intern/builder/deg_builder_cache.h
index e04ae3a3727..c955a22a5cf 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_cache.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_cache.h
@@ -81,7 +81,6 @@ class AnimatedPropertyStorage {
/* Cached data which can be re-used by multiple builders. */
class DepsgraphBuilderCache {
public:
- DepsgraphBuilderCache();
~DepsgraphBuilderCache();
/* Makes sure storage for animated properties exists and initialized for the given ID. */
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_map.cc b/source/blender/depsgraph/intern/builder/deg_builder_map.cc
index 5da54350cfc..6e926da6b29 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_map.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_map.cc
@@ -27,14 +27,6 @@
namespace blender::deg {
-BuilderMap::BuilderMap()
-{
-}
-
-BuilderMap::~BuilderMap()
-{
-}
-
bool BuilderMap::checkIsBuilt(ID *id, int tag) const
{
return (getIDTag(id) & tag) == tag;
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_map.h b/source/blender/depsgraph/intern/builder/deg_builder_map.h
index 8b23d3d0d3b..53f6a722e85 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_map.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_map.h
@@ -47,9 +47,6 @@ class BuilderMap {
TAG_SCENE_COMPOSITOR | TAG_SCENE_SEQUENCER | TAG_SCENE_AUDIO),
};
- BuilderMap();
- ~BuilderMap();
-
/* Check whether given ID is already handled by builder (or if it's being handled). */
bool checkIsBuilt(ID *id, int tag = TAG_COMPLETE) const;
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
index d8dc66883a0..ae530cc010e 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
@@ -84,6 +84,7 @@
#include "BKE_lattice.h"
#include "BKE_layer.h"
#include "BKE_lib_id.h"
+#include "BKE_lib_query.h"
#include "BKE_light.h"
#include "BKE_mask.h"
#include "BKE_material.h"
@@ -114,6 +115,7 @@
#include "intern/builder/deg_builder.h"
#include "intern/depsgraph.h"
+#include "intern/depsgraph_tag.h"
#include "intern/depsgraph_type.h"
#include "intern/eval/deg_eval_copy_on_write.h"
#include "intern/node/deg_node.h"
@@ -360,7 +362,103 @@ void DepsgraphNodeBuilder::begin_build()
graph_->entry_tags.clear();
}
-void DepsgraphNodeBuilder::end_build()
+/* Util callbacks for `BKE_library_foreach_ID_link`, used to detect when a COW ID is using ID
+ * pointers that are either:
+ * - COW ID pointers that do not exist anymore in current depsgraph.
+ * - Orig ID pointers that do have now a COW version in current depsgraph.
+ * In both cases, it means the COW ID user needs to be flushed, to ensure its pointers are properly
+ * remapped.
+ *
+ * NOTE: This is split in two, a static function and a public method of the node builder, to allow
+ * the code to access the builder's data more easily. */
+
+/* `id_cow_self` is the user of `id_pointer`, see also `LibraryIDLinkCallbackData` struct
+ * definition. */
+int DepsgraphNodeBuilder::foreach_id_cow_detect_need_for_update_callback(ID *id_cow_self,
+ ID *id_pointer)
+{
+ if (id_pointer->orig_id == nullptr) {
+ /* `id_cow_self` uses a non-cow ID, if that ID has a COW copy in current depsgraph its owner
+ * needs to be remapped, i.e. COW-flushed. */
+ IDNode *id_node = find_id_node(id_pointer);
+ if (id_node != nullptr && id_node->id_cow != nullptr) {
+ graph_id_tag_update(bmain_,
+ graph_,
+ id_cow_self->orig_id,
+ ID_RECALC_COPY_ON_WRITE,
+ DEG_UPDATE_SOURCE_RELATIONS);
+ return IDWALK_RET_STOP_ITER;
+ }
+ }
+ else {
+ /* `id_cow_self` uses a COW ID, if that COW copy is removed from current depsgraph its owner
+ * needs to be remapped, i.e. COW-flushed. */
+ /* NOTE: at that stage, old existing COW copies that are to be removed from current state of
+ * evaluated depsgraph are still valid pointers, they are freed later (typically during
+ * destruction of the builder itself). */
+ IDNode *id_node = find_id_node(id_pointer->orig_id);
+ if (id_node == nullptr) {
+ graph_id_tag_update(bmain_,
+ graph_,
+ id_cow_self->orig_id,
+ ID_RECALC_COPY_ON_WRITE,
+ DEG_UPDATE_SOURCE_RELATIONS);
+ return IDWALK_RET_STOP_ITER;
+ }
+ }
+ return IDWALK_RET_NOP;
+}
+
+static int foreach_id_cow_detect_need_for_update_callback(LibraryIDLinkCallbackData *cb_data)
+{
+ ID *id = *cb_data->id_pointer;
+ if (id == nullptr) {
+ return IDWALK_RET_NOP;
+ }
+
+ DepsgraphNodeBuilder *builder = static_cast<DepsgraphNodeBuilder *>(cb_data->user_data);
+ ID *id_cow_self = cb_data->id_self;
+
+ return builder->foreach_id_cow_detect_need_for_update_callback(id_cow_self, id);
+}
+
+/* Check for IDs that need to be flushed (COW-updated) because the depsgraph itself created or
+ * removed some of their evaluated dependencies.
+ *
+ * NOTE: Currently the only ID types that depsgraph may decide to not evaluate/generate COW
+ * copies for, even though they are referenced by other data-blocks, are Collections and Objects
+ * (through their various visibility flags, and the ones from LayerCollections too). However, this
+ * code is kept generic as it makes it more future-proof, and optimization here would give
+ * negligible performance improvements in typical cases.
+ *
+ * NOTE: This mechanism may also 'fix' some missing update tagging from non-depsgraph code in
+ * some cases. This is slightly unfortunate (as it may hide issues in other parts of Blender
+ * code), but cannot really be avoided currently.
+ */
+void DepsgraphNodeBuilder::update_invalid_cow_pointers()
+{
+ for (const IDNode *id_node : graph_->id_nodes) {
+ if (id_node->previously_visible_components_mask == 0) {
+ /* Newly added node/ID, no need to check it. */
+ continue;
+ }
+ if (ELEM(id_node->id_cow, id_node->id_orig, nullptr)) {
+ /* Node/ID with no COW data, no need to check it. */
+ continue;
+ }
+ if ((id_node->id_cow->recalc & ID_RECALC_COPY_ON_WRITE) != 0) {
+ /* Node/ID already tagged for COW flush, no need to check it. */
+ continue;
+ }
+ BKE_library_foreach_ID_link(nullptr,
+ id_node->id_cow,
+ deg::foreach_id_cow_detect_need_for_update_callback,
+ this,
+ IDWALK_IGNORE_EMBEDDED_ID | IDWALK_READONLY);
+ }
+}
+
+void DepsgraphNodeBuilder::tag_previously_tagged_nodes()
{
for (const SavedEntryTag &entry_tag : saved_entry_tags_) {
IDNode *id_node = find_id_node(entry_tag.id_orig);
@@ -382,6 +480,12 @@ void DepsgraphNodeBuilder::end_build()
}
}
+void DepsgraphNodeBuilder::end_build()
+{
+ tag_previously_tagged_nodes();
+ update_invalid_cow_pointers();
+}
+
void DepsgraphNodeBuilder::build_id(ID *id)
{
if (id == nullptr) {
@@ -554,6 +658,7 @@ void DepsgraphNodeBuilder::build_collection(LayerCollection *from_layer_collecti
id_node->is_directly_visible = is_collection_visible;
build_idproperties(collection->id.properties);
+ add_operation_node(&collection->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_DONE);
}
if (from_layer_collection != nullptr) {
/* If we came from layer collection we don't go deeper, view layer
@@ -1556,6 +1661,12 @@ void DepsgraphNodeBuilder::build_nodetree_socket(bNodeSocket *socket)
else if (socket->type == SOCK_COLLECTION) {
build_id((ID *)((bNodeSocketValueCollection *)socket->default_value)->value);
}
+ else if (socket->type == SOCK_TEXTURE) {
+ build_id((ID *)((bNodeSocketValueTexture *)socket->default_value)->value);
+ }
+ else if (socket->type == SOCK_MATERIAL) {
+ build_id((ID *)((bNodeSocketValueMaterial *)socket->default_value)->value);
+ }
}
void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree)
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h
index a7033c8c8f3..151e0d844b6 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h
@@ -101,6 +101,8 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder {
virtual void begin_build();
virtual void end_build();
+ int foreach_id_cow_detect_need_for_update_callback(ID *id_cow_self, ID *id_pointer);
+
IDNode *add_id_node(ID *id);
IDNode *find_id_node(ID *id);
TimeSourceNode *add_time_source();
@@ -276,6 +278,9 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder {
bool is_reference,
void *user_data);
+ void tag_previously_tagged_nodes();
+ void update_invalid_cow_pointers();
+
/* State which demotes currently built entities. */
Scene *scene_;
ViewLayer *view_layer_;
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc
index 13caba67713..00c78b8edce 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc
@@ -169,7 +169,7 @@ void DepsgraphNodeBuilder::build_rig(Object *object, bool is_object_visible)
}
/* Speed optimization for animation lookups. */
if (object->pose != nullptr) {
- BKE_pose_channels_hash_make(object->pose);
+ BKE_pose_channels_hash_ensure(object->pose);
if (object->pose->flag & POSE_CONSTRAINTS_NEED_UPDATE_FLAGS) {
BKE_pose_update_constraint_flags(object->pose);
}
@@ -318,7 +318,7 @@ void DepsgraphNodeBuilder::build_proxy_rig(Object *object, bool is_object_visibl
/* Armature. */
build_armature(armature);
/* speed optimization for animation lookups */
- BKE_pose_channels_hash_make(object->pose);
+ BKE_pose_channels_hash_ensure(object->pose);
if (object->pose->flag & POSE_CONSTRAINTS_NEED_UPDATE_FLAGS) {
BKE_pose_update_constraint_flags(object->pose);
}
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.cc b/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.cc
index 197e14c1a21..17c2925b7f4 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.cc
@@ -30,14 +30,6 @@
namespace blender::deg {
-RootPChanMap::RootPChanMap()
-{
-}
-
-RootPChanMap::~RootPChanMap()
-{
-}
-
/* Debug contents of map */
void RootPChanMap::print_debug()
{
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.h b/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.h
index 7a6ea38a0f0..0dd4062c353 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.h
@@ -29,10 +29,6 @@ namespace blender {
namespace deg {
struct RootPChanMap {
- /* Constructor and destructor - Create and free the internal map respectively. */
- RootPChanMap();
- ~RootPChanMap();
-
/* Debug contents of map. */
void print_debug();
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
index 3cc2ec02165..8a02228146a 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
@@ -635,11 +635,38 @@ void DepsgraphRelationBuilder::build_collection(LayerCollection *from_layer_coll
ComponentKey duplicator_key(object != nullptr ? &object->id : nullptr, NodeType::DUPLI);
if (!group_done) {
build_idproperties(collection->id.properties);
+ OperationKey collection_geometry_key{
+ &collection->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_DONE};
LISTBASE_FOREACH (CollectionObject *, cob, &collection->gobject) {
build_object(cob->ob);
+
+ /* The geometry of a collection depends on the positions of the elements in it. */
+ OperationKey object_transform_key{
+ &cob->ob->id, NodeType::TRANSFORM, OperationCode::TRANSFORM_FINAL};
+ add_relation(object_transform_key, collection_geometry_key, "Collection Geometry");
+
+ /* Only create geometry relations to child objects, if they have a geometry component. */
+ OperationKey object_geometry_key{
+ &cob->ob->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL};
+ if (find_node(object_geometry_key) != nullptr) {
+ add_relation(object_geometry_key, collection_geometry_key, "Collection Geometry");
+ }
+
+ /* An instance is part of the geometry of the collection. */
+ if (cob->ob->type == OB_EMPTY) {
+ Collection *collection_instance = cob->ob->instance_collection;
+ if (collection_instance != nullptr) {
+ OperationKey collection_instance_key{
+ &collection_instance->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_DONE};
+ add_relation(collection_instance_key, collection_geometry_key, "Collection Geometry");
+ }
+ }
}
LISTBASE_FOREACH (CollectionChild *, child, &collection->children) {
build_collection(nullptr, nullptr, child->collection);
+ OperationKey child_collection_geometry_key{
+ &child->collection->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_DONE};
+ add_relation(child_collection_geometry_key, collection_geometry_key, "Collection Geometry");
}
}
if (object != nullptr) {
@@ -2374,6 +2401,18 @@ void DepsgraphRelationBuilder::build_nodetree_socket(bNodeSocket *socket)
build_collection(nullptr, nullptr, collection);
}
}
+ else if (socket->type == SOCK_TEXTURE) {
+ Tex *texture = ((bNodeSocketValueTexture *)socket->default_value)->value;
+ if (texture != nullptr) {
+ build_texture(texture);
+ }
+ }
+ else if (socket->type == SOCK_MATERIAL) {
+ Material *material = ((bNodeSocketValueMaterial *)socket->default_value)->value;
+ if (material != nullptr) {
+ build_material(material);
+ }
+ }
}
void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree)
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_rna.cc b/source/blender/depsgraph/intern/builder/deg_builder_rna.cc
index 4064058f231..54c51adec66 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_rna.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_rna.cc
@@ -119,9 +119,7 @@ RNANodeQuery::RNANodeQuery(Depsgraph *depsgraph, DepsgraphBuilder *builder)
{
}
-RNANodeQuery::~RNANodeQuery()
-{
-}
+RNANodeQuery::~RNANodeQuery() = default;
Node *RNANodeQuery::find_node(const PointerRNA *ptr,
const PropertyRNA *prop,
diff --git a/source/blender/depsgraph/intern/builder/pipeline.cc b/source/blender/depsgraph/intern/builder/pipeline.cc
index f7feeea9593..10bc7213061 100644
--- a/source/blender/depsgraph/intern/builder/pipeline.cc
+++ b/source/blender/depsgraph/intern/builder/pipeline.cc
@@ -40,10 +40,6 @@ AbstractBuilderPipeline::AbstractBuilderPipeline(::Depsgraph *graph)
{
}
-AbstractBuilderPipeline::~AbstractBuilderPipeline()
-{
-}
-
void AbstractBuilderPipeline::build()
{
double start_time = 0.0;
@@ -98,7 +94,7 @@ void AbstractBuilderPipeline::build_step_finalize()
if (G.debug_value == 799) {
deg_graph_transitive_reduction(deg_graph_);
}
- /* Store pointers to commonly used valuated datablocks. */
+ /* Store pointers to commonly used evaluated datablocks. */
deg_graph_->scene_cow = (Scene *)deg_graph_->get_cow_id(&deg_graph_->scene->id);
/* Flush visibility layer and re-schedule nodes for update. */
deg_graph_build_finalize(bmain_, deg_graph_);
diff --git a/source/blender/depsgraph/intern/builder/pipeline.h b/source/blender/depsgraph/intern/builder/pipeline.h
index dcd1bc6f26e..d0ccf352c21 100644
--- a/source/blender/depsgraph/intern/builder/pipeline.h
+++ b/source/blender/depsgraph/intern/builder/pipeline.h
@@ -50,7 +50,7 @@ class DepsgraphRelationBuilder;
class AbstractBuilderPipeline {
public:
AbstractBuilderPipeline(::Depsgraph *graph);
- virtual ~AbstractBuilderPipeline();
+ virtual ~AbstractBuilderPipeline() = default;
void build();
diff --git a/source/blender/depsgraph/intern/depsgraph.cc b/source/blender/depsgraph/intern/depsgraph.cc
index 3d30e7e79b9..8e1ab23fae0 100644
--- a/source/blender/depsgraph/intern/depsgraph.cc
+++ b/source/blender/depsgraph/intern/depsgraph.cc
@@ -70,7 +70,8 @@ Depsgraph::Depsgraph(Main *bmain, Scene *scene, ViewLayer *view_layer, eEvaluati
scene_cow(nullptr),
is_active(false),
is_evaluating(false),
- is_render_pipeline_depsgraph(false)
+ is_render_pipeline_depsgraph(false),
+ use_editors_update(false)
{
BLI_spin_init(&lock);
memset(id_type_updated, 0, sizeof(id_type_updated));
@@ -285,7 +286,9 @@ Depsgraph *DEG_graph_new(Main *bmain, Scene *scene, ViewLayer *view_layer, eEval
}
/* Replace the "owner" pointers (currently Main/Scene/ViewLayer) of this depsgraph.
- * Used during undo steps when we do want to re-use the old depsgraph data as much as possible. */
+ * Used for:
+ * - Undo steps when we do want to re-use the old depsgraph data as much as possible.
+ * - Rendering where we want to re-use objects between different view layers. */
void DEG_graph_replace_owners(struct Depsgraph *depsgraph,
Main *bmain,
Scene *scene,
diff --git a/source/blender/depsgraph/intern/depsgraph.h b/source/blender/depsgraph/intern/depsgraph.h
index df8c8215d2f..b87ce94709a 100644
--- a/source/blender/depsgraph/intern/depsgraph.h
+++ b/source/blender/depsgraph/intern/depsgraph.h
@@ -161,6 +161,9 @@ struct Depsgraph {
* does not need any bases. */
bool is_render_pipeline_depsgraph;
+ /* Notify editors about changes to IDs in this depsgraph. */
+ bool use_editors_update;
+
/* Cached list of colliders/effectors for collections and the scene
* created along with relations, for fast lookup during evaluation. */
Map<const ID *, ListBase *> *physics_relations[DEG_PHYSICS_RELATIONS_NUM];
diff --git a/source/blender/depsgraph/intern/depsgraph_build.cc b/source/blender/depsgraph/intern/depsgraph_build.cc
index 6717ba521f6..9e9191c5ab9 100644
--- a/source/blender/depsgraph/intern/depsgraph_build.cc
+++ b/source/blender/depsgraph/intern/depsgraph_build.cc
@@ -32,11 +32,13 @@
#include "PIL_time_utildefines.h"
#include "DNA_cachefile_types.h"
+#include "DNA_collection_types.h"
#include "DNA_node_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_simulation_types.h"
+#include "BKE_collection.h"
#include "BKE_main.h"
#include "BKE_scene.h"
@@ -60,6 +62,7 @@
#include "intern/depsgraph_registry.h"
#include "intern/depsgraph_relation.h"
+#include "intern/depsgraph_tag.h"
#include "intern/depsgraph_type.h"
/* ****************** */
@@ -107,6 +110,34 @@ void DEG_add_object_relation(DepsNodeHandle *node_handle,
deg_node_handle->builder->add_node_handle_relation(comp_key, deg_node_handle, description);
}
+bool DEG_object_has_geometry_component(Object *object)
+{
+ return deg::geometry_tag_to_component(&object->id) != deg::NodeType::UNDEFINED;
+}
+
+void DEG_add_collection_geometry_relation(DepsNodeHandle *node_handle,
+ Collection *collection,
+ const char *description)
+{
+ deg::OperationKey operation_key{
+ &collection->id, deg::NodeType::GEOMETRY, deg::OperationCode::GEOMETRY_EVAL_DONE};
+ deg::DepsNodeHandle *deg_node_handle = get_node_handle(node_handle);
+ deg_node_handle->builder->add_node_handle_relation(operation_key, deg_node_handle, description);
+}
+
+void DEG_add_collection_geometry_customdata_mask(DepsNodeHandle *node_handle,
+ Collection *collection,
+ const CustomData_MeshMasks *masks)
+{
+ FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (collection, ob) {
+ DEG_add_customdata_mask(node_handle, ob, masks);
+ if (ob->type == OB_EMPTY && ob->instance_collection != nullptr) {
+ DEG_add_collection_geometry_customdata_mask(node_handle, ob->instance_collection, masks);
+ }
+ }
+ FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
+}
+
void DEG_add_simulation_relation(DepsNodeHandle *node_handle,
Simulation *simulation,
const char *description)
diff --git a/source/blender/depsgraph/intern/depsgraph_query.cc b/source/blender/depsgraph/intern/depsgraph_query.cc
index 57de62e1880..aab4c3ca0f6 100644
--- a/source/blender/depsgraph/intern/depsgraph_query.cc
+++ b/source/blender/depsgraph/intern/depsgraph_query.cc
@@ -168,7 +168,7 @@ ViewLayer *DEG_get_evaluated_view_layer(const Depsgraph *graph)
const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(graph);
Scene *scene_cow = DEG_get_evaluated_scene(graph);
if (scene_cow == nullptr) {
- return nullptr; /* Happens with new, not-yet-built/evaluated graphes. */
+ return nullptr; /* Happens with new, not-yet-built/evaluated graphs. */
}
/* Do name-based lookup. */
/* TODO(sergey): Can this be optimized? */
diff --git a/source/blender/depsgraph/intern/depsgraph_query_iter.cc b/source/blender/depsgraph/intern/depsgraph_query_iter.cc
index ed002321729..df1cf8cc771 100644
--- a/source/blender/depsgraph/intern/depsgraph_query_iter.cc
+++ b/source/blender/depsgraph/intern/depsgraph_query_iter.cc
@@ -219,6 +219,29 @@ bool deg_iterator_components_step(BLI_Iterator *iter)
}
}
+ /* The curve component. */
+ if (data->geometry_component_id == 3) {
+ data->geometry_component_id++;
+
+ const CurveComponent *component = geometry_set->get_component_for_read<CurveComponent>();
+ if (component != nullptr) {
+ const Curve *curve = component->get_curve_for_render();
+
+ if (curve != nullptr) {
+ Object *temp_object = &data->temp_geometry_component_object;
+ *temp_object = *data->geometry_component_owner;
+ temp_object->type = OB_CURVE;
+ temp_object->data = (void *)curve;
+ /* Assign data_eval here too, because curve rendering code tries
+ * to use a mesh if it can find one in this pointer. */
+ temp_object->runtime.data_eval = (ID *)curve;
+ temp_object->runtime.select_id = data->geometry_component_owner->runtime.select_id;
+ iter->current = temp_object;
+ return true;
+ }
+ }
+ }
+
data->geometry_component_owner = nullptr;
return false;
}
diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc
index 2051ee3657a..204143d7cbd 100644
--- a/source/blender/depsgraph/intern/depsgraph_tag.cc
+++ b/source/blender/depsgraph/intern/depsgraph_tag.cc
@@ -594,6 +594,7 @@ NodeType geometry_tag_to_component(const ID *id)
case ID_HA:
case ID_PT:
case ID_VO:
+ case ID_GR:
return NodeType::GEOMETRY;
case ID_PA: /* Particles */
return NodeType::UNDEFINED;
@@ -816,11 +817,24 @@ void DEG_on_visible_update(Main *bmain, const bool do_time)
}
}
+void DEG_enable_editors_update(Depsgraph *depsgraph)
+{
+ deg::Depsgraph *graph = (deg::Depsgraph *)depsgraph;
+ graph->use_editors_update = true;
+}
+
/* Check if something was changed in the database and inform
* editors about this. */
-void DEG_ids_check_recalc(
- Main *bmain, Depsgraph *depsgraph, Scene *scene, ViewLayer *view_layer, bool time)
+void DEG_editors_update(Depsgraph *depsgraph, bool time)
{
+ deg::Depsgraph *graph = (deg::Depsgraph *)depsgraph;
+ if (!graph->use_editors_update) {
+ return;
+ }
+
+ Scene *scene = DEG_get_input_scene(depsgraph);
+ ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph);
+ Main *bmain = DEG_get_bmain(depsgraph);
bool updated = time || DEG_id_type_any_updated(depsgraph);
DEGEditorUpdateContext update_ctx = {nullptr};
@@ -842,7 +856,7 @@ static void deg_graph_clear_id_recalc_flags(ID *id)
/* XXX And what about scene's master collection here? */
}
-void DEG_ids_clear_recalc(Main *UNUSED(bmain), Depsgraph *depsgraph)
+void DEG_ids_clear_recalc(Depsgraph *depsgraph, const bool backup)
{
deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(depsgraph);
/* TODO(sergey): Re-implement POST_UPDATE_HANDLER_WORKAROUND using entry_tags
@@ -852,6 +866,9 @@ void DEG_ids_clear_recalc(Main *UNUSED(bmain), Depsgraph *depsgraph)
}
/* Go over all ID nodes nodes, clearing tags. */
for (deg::IDNode *id_node : deg_graph->id_nodes) {
+ if (backup) {
+ id_node->id_cow_recalc_backup |= id_node->id_cow->recalc;
+ }
/* TODO: we clear original ID recalc flags here, but this may not work
* correctly when there are multiple depsgraph with others still using
* the recalc flag. */
@@ -863,3 +880,13 @@ void DEG_ids_clear_recalc(Main *UNUSED(bmain), Depsgraph *depsgraph)
}
memset(deg_graph->id_type_updated, 0, sizeof(deg_graph->id_type_updated));
}
+
+void DEG_ids_restore_recalc(Depsgraph *depsgraph)
+{
+ deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(depsgraph);
+
+ for (deg::IDNode *id_node : deg_graph->id_nodes) {
+ id_node->id_cow->recalc |= id_node->id_cow_recalc_backup;
+ id_node->id_cow_recalc_backup = 0;
+ }
+}
diff --git a/source/blender/depsgraph/intern/eval/deg_eval.cc b/source/blender/depsgraph/intern/eval/deg_eval.cc
index 620e86550cc..2107e075139 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval.cc
@@ -353,7 +353,8 @@ static TaskPool *deg_evaluate_task_pool_create(DepsgraphEvalState *state)
return BLI_task_pool_create_no_threads(state);
}
- return BLI_task_pool_create_suspended(state, TASK_PRIORITY_HIGH);
+ /* TODO: Disable task isolation. */
+ return BLI_task_pool_create_suspended(state, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON);
}
/**
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc
index 2544bb1642c..0d367762b00 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc
@@ -304,7 +304,8 @@ bool id_copy_inplace_no_main(const ID *id, ID *newid)
bool result = (BKE_id_copy_ex(nullptr,
(ID *)id_for_copy,
&newid,
- LIB_ID_COPY_LOCALIZE | LIB_ID_CREATE_NO_ALLOCATE) != nullptr);
+ (LIB_ID_COPY_LOCALIZE | LIB_ID_CREATE_NO_ALLOCATE |
+ LIB_ID_COPY_SET_COPIED_ON_WRITE)) != nullptr);
#ifdef NESTED_ID_NASTY_WORKAROUND
if (result) {
@@ -319,7 +320,6 @@ bool id_copy_inplace_no_main(const ID *id, ID *newid)
* is already allocated. */
bool scene_copy_inplace_no_main(const Scene *scene, Scene *new_scene)
{
- const ID *id_for_copy = &scene->id;
if (G.debug & G_DEBUG_DEPSGRAPH_UUID) {
SEQ_relations_check_uuids_unique_and_report(scene);
@@ -327,13 +327,15 @@ bool scene_copy_inplace_no_main(const Scene *scene, Scene *new_scene)
#ifdef NESTED_ID_NASTY_WORKAROUND
NestedIDHackTempStorage id_hack_storage;
- id_for_copy = nested_id_hack_get_discarded_pointers(&id_hack_storage, &scene->id);
+ const ID *id_for_copy = nested_id_hack_get_discarded_pointers(&id_hack_storage, &scene->id);
+#else
+ const ID *id_for_copy = &scene->id;
#endif
-
bool result = (BKE_id_copy_ex(nullptr,
id_for_copy,
(ID **)&new_scene,
- LIB_ID_COPY_LOCALIZE | LIB_ID_CREATE_NO_ALLOCATE) != nullptr);
+ (LIB_ID_COPY_LOCALIZE | LIB_ID_CREATE_NO_ALLOCATE |
+ LIB_ID_COPY_SET_COPIED_ON_WRITE)) != nullptr);
#ifdef NESTED_ID_NASTY_WORKAROUND
if (result) {
@@ -604,20 +606,12 @@ void update_lattice_edit_mode_pointers(const Depsgraph * /*depsgraph*/,
void update_mesh_edit_mode_pointers(const ID *id_orig, ID *id_cow)
{
- /* For meshes we need to update edit_mesh to make it to point
- * to the CoW version of object.
- *
- * This is kind of confusing, because actual bmesh is not owned by
- * the CoW object, so need to be accurate about using link from
- * edit_mesh to object. */
const Mesh *mesh_orig = (const Mesh *)id_orig;
Mesh *mesh_cow = (Mesh *)id_cow;
if (mesh_orig->edit_mesh == nullptr) {
return;
}
- mesh_cow->edit_mesh = (BMEditMesh *)MEM_dupallocN(mesh_orig->edit_mesh);
- mesh_cow->edit_mesh->mesh_eval_cage = nullptr;
- mesh_cow->edit_mesh->mesh_eval_final = nullptr;
+ mesh_cow->edit_mesh = mesh_orig->edit_mesh;
}
/* Edit data is stored and owned by original datablocks, copied ones
@@ -653,11 +647,17 @@ void update_list_orig_pointers(const ListBase *listbase_orig,
{
T *element_orig = reinterpret_cast<T *>(listbase_orig->first);
T *element_cow = reinterpret_cast<T *>(listbase->first);
- while (element_orig != nullptr) {
+
+ /* Both lists should have the same number of elements, so the check on
+ * `element_cow` is just to prevent a crash if this is not the case. */
+ while (element_orig != nullptr && element_cow != nullptr) {
element_cow->*orig_field = element_orig;
element_cow = element_cow->next;
element_orig = element_orig->next;
}
+
+ BLI_assert((element_orig == nullptr && element_cow == nullptr) ||
+ !"list of pointers of different sizes, unable to reliably set orig pointer");
}
void update_particle_system_orig_pointers(const Object *object_orig, Object *object_cow)
@@ -993,11 +993,6 @@ void discard_lattice_edit_mode_pointers(ID *id_cow)
void discard_mesh_edit_mode_pointers(ID *id_cow)
{
Mesh *mesh_cow = (Mesh *)id_cow;
- if (mesh_cow->edit_mesh == nullptr) {
- return;
- }
- BKE_editmesh_free_derivedmesh(mesh_cow->edit_mesh);
- MEM_freeN(mesh_cow->edit_mesh);
mesh_cow->edit_mesh = nullptr;
}
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc
index 7a0c1b5b693..7893e8c64c1 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc
@@ -33,6 +33,7 @@ namespace blender::deg {
RuntimeBackup::RuntimeBackup(const Depsgraph *depsgraph)
: have_backup(false),
+ id_data({nullptr}),
animation_backup(depsgraph),
scene_backup(depsgraph),
sound_backup(depsgraph),
@@ -51,6 +52,8 @@ void RuntimeBackup::init_from_id(ID *id)
}
have_backup = true;
+ id_data.py_instance = id->py_instance;
+
animation_backup.init_from_id(id);
const ID_Type id_type = GS(id->name);
@@ -89,6 +92,8 @@ void RuntimeBackup::restore_to_id(ID *id)
return;
}
+ id->py_instance = id_data.py_instance;
+
animation_backup.restore_to_id(id);
const ID_Type id_type = GS(id->name);
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.h
index c6249c83daa..0629dbe62b4 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.h
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.h
@@ -58,6 +58,11 @@ class RuntimeBackup {
* copy-on-write mechanism. */
bool have_backup;
+ /* Struct members of the ID pointer. */
+ struct {
+ void *py_instance;
+ } id_data;
+
AnimationBackup animation_backup;
SceneBackup scene_backup;
SoundBackup sound_backup;
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.cc b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.cc
index c1d2dd8b6cc..bd872d40825 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.cc
@@ -75,19 +75,11 @@ void animated_property_store_cb(ID *id, FCurve *fcurve, void *data_v)
} // namespace
-AnimationValueBackup::AnimationValueBackup()
-{
-}
-
AnimationValueBackup::AnimationValueBackup(const string &rna_path, int array_index, float value)
: rna_path(rna_path), array_index(array_index), value(value)
{
}
-AnimationValueBackup::~AnimationValueBackup()
-{
-}
-
AnimationBackup::AnimationBackup(const Depsgraph *depsgraph)
{
meed_value_backup = !depsgraph->is_active;
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.h
index 6b5d5eab75f..8f71457ae6f 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.h
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.h
@@ -34,9 +34,8 @@ struct Depsgraph;
class AnimationValueBackup {
public:
- AnimationValueBackup();
+ AnimationValueBackup() = default;
AnimationValueBackup(const string &rna_path, int array_index, float value);
- ~AnimationValueBackup();
AnimationValueBackup(const AnimationValueBackup &other) = default;
AnimationValueBackup(AnimationValueBackup &&other) noexcept = default;
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 61299a2d49c..30ec9e948fd 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
@@ -55,7 +55,7 @@ void ObjectRuntimeBackup::init_from_object(Object *object)
/* Make a backup of base flags. */
base_flag = object->base_flag;
base_local_view_bits = object->base_local_view_bits;
- /* Backup tuntime data of all modifiers. */
+ /* Backup runtime data of all modifiers. */
backup_modifier_runtime_data(object);
/* Backup runtime data of all pose channels. */
backup_pose_channel_runtime_data(object);
@@ -98,7 +98,7 @@ void ObjectRuntimeBackup::restore_to_object(Object *object)
object->runtime = runtime;
object->runtime.data_orig = data_orig;
object->runtime.bb = bb;
- if (object->type == OB_MESH && data_eval != nullptr) {
+ if (ELEM(object->type, OB_MESH, OB_LATTICE) && data_eval != nullptr) {
if (object->id.recalc & ID_RECALC_GEOMETRY) {
/* If geometry is tagged for update it means, that part of
* evaluated mesh are not valid anymore. In this case we can not
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequence.cc b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequence.cc
index 35720140f97..d481e0c39a8 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequence.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequence.cc
@@ -35,24 +35,28 @@ SequenceBackup::SequenceBackup(const Depsgraph * /*depsgraph*/)
void SequenceBackup::reset()
{
scene_sound = nullptr;
+ BLI_listbase_clear(&anims);
}
void SequenceBackup::init_from_sequence(Sequence *sequence)
{
scene_sound = sequence->scene_sound;
+ anims = sequence->anims;
sequence->scene_sound = nullptr;
+ BLI_listbase_clear(&sequence->anims);
}
void SequenceBackup::restore_to_sequence(Sequence *sequence)
{
sequence->scene_sound = scene_sound;
+ sequence->anims = anims;
reset();
}
bool SequenceBackup::isEmpty() const
{
- return (scene_sound == nullptr);
+ return (scene_sound == nullptr) && BLI_listbase_is_empty(&anims);
}
} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequence.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequence.h
index eb38dc3dc5b..3b3aa496496 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequence.h
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequence.h
@@ -23,6 +23,8 @@
#pragma once
+#include "BLI_listbase.h"
+
struct Sequence;
namespace blender {
@@ -43,6 +45,7 @@ class SequenceBackup {
bool isEmpty() const;
void *scene_sound;
+ ListBase anims;
};
} // namespace deg
diff --git a/source/blender/depsgraph/intern/node/deg_node_id.cc b/source/blender/depsgraph/intern/node/deg_node_id.cc
index 8e159a7ff08..688afe141e9 100644
--- a/source/blender/depsgraph/intern/node/deg_node_id.cc
+++ b/source/blender/depsgraph/intern/node/deg_node_id.cc
@@ -90,6 +90,7 @@ void IDNode::init(const ID *id, const char *UNUSED(subdata))
is_collection_fully_expanded = false;
has_base = false;
is_user_modified = false;
+ id_cow_recalc_backup = 0;
visible_components_mask = 0;
previously_visible_components_mask = 0;
diff --git a/source/blender/depsgraph/intern/node/deg_node_id.h b/source/blender/depsgraph/intern/node/deg_node_id.h
index e2d3b3fc36f..073469598dc 100644
--- a/source/blender/depsgraph/intern/node/deg_node_id.h
+++ b/source/blender/depsgraph/intern/node/deg_node_id.h
@@ -120,6 +120,9 @@ struct IDNode : public Node {
/* Accumulated flag from operation. Is initialized and used during updates flush. */
bool is_user_modified;
+ /* Accumulate recalc flags from multiple update passes. */
+ int id_cow_recalc_backup;
+
IDComponentsMask visible_components_mask;
IDComponentsMask previously_visible_components_mask;
diff --git a/source/blender/depsgraph/intern/node/deg_node_operation.cc b/source/blender/depsgraph/intern/node/deg_node_operation.cc
index 97aca6280be..7e57467f905 100644
--- a/source/blender/depsgraph/intern/node/deg_node_operation.cc
+++ b/source/blender/depsgraph/intern/node/deg_node_operation.cc
@@ -213,10 +213,6 @@ OperationNode::OperationNode() : name_tag(-1), flag(0)
{
}
-OperationNode::~OperationNode()
-{
-}
-
string OperationNode::identifier() const
{
return string(operationCodeAsString(opcode)) + "(" + name + ")";
diff --git a/source/blender/depsgraph/intern/node/deg_node_operation.h b/source/blender/depsgraph/intern/node/deg_node_operation.h
index 40369de08f5..1d966cffd5d 100644
--- a/source/blender/depsgraph/intern/node/deg_node_operation.h
+++ b/source/blender/depsgraph/intern/node/deg_node_operation.h
@@ -231,7 +231,6 @@ enum OperationFlag {
/* Atomic Operation - Base type for all operations */
struct OperationNode : public Node {
OperationNode();
- ~OperationNode();
virtual string identifier() const override;
string full_identifier() const;
diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt
index 196b46953c4..e99bf1f5b0c 100644
--- a/source/blender/draw/CMakeLists.txt
+++ b/source/blender/draw/CMakeLists.txt
@@ -31,6 +31,7 @@ set(INC
../depsgraph
../editors/include
../editors/space_view3d
+ ../functions
../gpu
../imbuf
../makesdna
@@ -50,8 +51,17 @@ set(INC
set(SRC
intern/draw_cache.c
- intern/draw_cache_extract_mesh.c
- intern/draw_cache_impl_curve.c
+ intern/draw_cache_extract_mesh_extractors.c
+ intern/draw_cache_extract_mesh_render_data.c
+ intern/draw_cache_extract_mesh.cc
+ intern/mesh_extractors/extract_mesh_ibo_edituv.cc
+ intern/mesh_extractors/extract_mesh_ibo_fdots.cc
+ intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc
+ intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc
+ intern/mesh_extractors/extract_mesh_ibo_lines.cc
+ intern/mesh_extractors/extract_mesh_ibo_points.cc
+ intern/mesh_extractors/extract_mesh_ibo_tris.cc
+ intern/draw_cache_impl_curve.cc
intern/draw_cache_impl_displist.c
intern/draw_cache_impl_gpencil.c
intern/draw_cache_impl_hair.c
@@ -146,6 +156,7 @@ set(SRC
engines/overlay/overlay_image.c
engines/overlay/overlay_lattice.c
engines/overlay/overlay_metaball.c
+ engines/overlay/overlay_mode_transfer.c
engines/overlay/overlay_motion_path.c
engines/overlay/overlay_outline.c
engines/overlay/overlay_paint.c
@@ -161,6 +172,7 @@ set(SRC
intern/DRW_render.h
intern/draw_cache.h
intern/draw_cache_extract.h
+ intern/draw_cache_extract_mesh_private.h
intern/draw_cache_impl.h
intern/draw_cache_inline.h
intern/draw_color_management.h
@@ -321,6 +333,7 @@ data_to_c_simple(intern/shaders/common_globals_lib.glsl SRC)
data_to_c_simple(intern/shaders/common_pointcloud_lib.glsl SRC)
data_to_c_simple(intern/shaders/common_hair_lib.glsl SRC)
data_to_c_simple(intern/shaders/common_hair_refine_vert.glsl SRC)
+data_to_c_simple(intern/shaders/common_hair_refine_comp.glsl SRC)
data_to_c_simple(intern/shaders/common_math_lib.glsl SRC)
data_to_c_simple(intern/shaders/common_math_geom_lib.glsl SRC)
data_to_c_simple(intern/shaders/common_view_lib.glsl SRC)
@@ -478,6 +491,7 @@ add_dependencies(bf_draw bf_dna)
if(WITH_GTESTS)
if(WITH_OPENGL_DRAW_TESTS)
set(TEST_SRC
+ tests/draw_testing.cc
tests/shaders_test.cc
)
set(TEST_INC
diff --git a/source/blender/draw/DRW_engine.h b/source/blender/draw/DRW_engine.h
index 2d5b93f4272..8a35ab2aeb9 100644
--- a/source/blender/draw/DRW_engine.h
+++ b/source/blender/draw/DRW_engine.h
@@ -118,8 +118,7 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph,
void DRW_draw_depth_loop(struct Depsgraph *depsgraph,
struct ARegion *region,
struct View3D *v3d,
- struct GPUViewport *viewport,
- bool use_opengl_context);
+ struct GPUViewport *viewport);
void DRW_draw_depth_loop_gpencil(struct Depsgraph *depsgraph,
struct ARegion *region,
struct View3D *v3d,
diff --git a/source/blender/draw/engines/eevee/eevee_cryptomatte.c b/source/blender/draw/engines/eevee/eevee_cryptomatte.c
index 0cb2d55d1eb..7fe984b4397 100644
--- a/source/blender/draw/engines/eevee/eevee_cryptomatte.c
+++ b/source/blender/draw/engines/eevee/eevee_cryptomatte.c
@@ -121,7 +121,7 @@ void EEVEE_cryptomatte_renderpasses_init(EEVEE_Data *vedata)
ViewLayer *view_layer = draw_ctx->view_layer;
/* Cryptomatte is only rendered for final image renders */
- if (!DRW_state_is_image_render()) {
+ if (!DRW_state_is_scene_render()) {
return;
}
const eViewLayerCryptomatteFlags active_layers = eevee_cryptomatte_active_layers(view_layer);
@@ -265,8 +265,7 @@ void EEVEE_cryptomatte_object_hair_cache_populate(EEVEE_Data *vedata,
Object *ob)
{
BLI_assert(ob->type == OB_HAIR);
- Hair *hair = ob->data;
- Material *material = hair->mat ? hair->mat[HAIR_MATERIAL_NR - 1] : NULL;
+ Material *material = BKE_object_material_get_eval(ob, HAIR_MATERIAL_NR);
eevee_cryptomatte_hair_cache_populate(vedata, sldata, ob, NULL, NULL, material);
}
@@ -291,8 +290,7 @@ void EEVEE_cryptomatte_particle_hair_cache_populate(EEVEE_Data *vedata,
if (draw_as != PART_DRAW_PATH) {
continue;
}
- Mesh *mesh = ob->data;
- Material *material = part->omat - 1 < mesh->totcol ? NULL : mesh->mat[part->omat - 1];
+ Material *material = BKE_object_material_get_eval(ob, part->omat);
eevee_cryptomatte_hair_cache_populate(vedata, sldata, ob, psys, md, material);
}
}
@@ -318,7 +316,7 @@ void EEVEE_cryptomatte_cache_populate(EEVEE_Data *vedata, EEVEE_ViewLayerData *s
if (geom == NULL) {
continue;
}
- Material *material = BKE_object_material_get(ob, i + 1);
+ Material *material = BKE_object_material_get_eval(ob, i + 1);
DRWShadingGroup *grp = eevee_cryptomatte_shading_group_create(
vedata, sldata, ob, material, false);
DRW_shgroup_call(grp, geom, ob);
diff --git a/source/blender/draw/engines/eevee/eevee_effects.c b/source/blender/draw/engines/eevee/eevee_effects.c
index f4f7acb8862..3a38edecec6 100644
--- a/source/blender/draw/engines/eevee/eevee_effects.c
+++ b/source/blender/draw/engines/eevee/eevee_effects.c
@@ -171,7 +171,8 @@ void EEVEE_effects_init(EEVEE_ViewLayerData *sldata,
});
}
else {
- txl->filtered_radiance = NULL;
+ DRW_TEXTURE_FREE_SAFE(txl->filtered_radiance);
+ GPU_FRAMEBUFFER_FREE_SAFE(fbl->radiance_filtered_fb);
}
/**
diff --git a/source/blender/draw/engines/eevee/eevee_engine.c b/source/blender/draw/engines/eevee/eevee_engine.c
index ae726d7af9a..88fd823bc72 100644
--- a/source/blender/draw/engines/eevee/eevee_engine.c
+++ b/source/blender/draw/engines/eevee/eevee_engine.c
@@ -494,12 +494,7 @@ static void eevee_render_to_image(void *vedata,
/* Previous motion step. */
if (do_motion_blur_fx) {
- if (i > 0) {
- /* The previous step of this iteration N is exactly the next step of iteration N - 1.
- * So we just swap the resources to avoid too much re-evaluation. */
- EEVEE_motion_blur_swap_data(vedata);
- }
- else {
+ if (i == 0) {
EEVEE_motion_blur_step_set(ved, MB_PREV);
DRW_render_set_time(engine, depsgraph, floorf(time_prev), fractf(time_prev));
EEVEE_render_modules_init(vedata, engine, depsgraph);
@@ -561,6 +556,11 @@ static void eevee_render_to_image(void *vedata,
EEVEE_renderpasses_output_init(
sldata, vedata, g_data->render_sample_count_per_timestep * time_steps_tot);
+ if (scene->world) {
+ /* Update world in case of animated world material. */
+ eevee_id_world_update(vedata, scene->world);
+ }
+
EEVEE_temporal_sampling_create_view(vedata);
EEVEE_render_draw(vedata, engine, render_layer, rect);
@@ -570,6 +570,14 @@ static void eevee_render_to_image(void *vedata,
DRW_cache_restart();
}
}
+
+ if (do_motion_blur_fx) {
+ /* The previous step of next iteration N is exactly the next step of this iteration N - 1.
+ * So we just swap the resources to avoid too much re-evaluation.
+ * Note that this also clears the VBO references from the GPUBatches of deformed
+ * geometries. */
+ EEVEE_motion_blur_swap_data(vedata);
+ }
}
EEVEE_volumes_free_smoke_textures();
diff --git a/source/blender/draw/engines/eevee/eevee_lights.c b/source/blender/draw/engines/eevee/eevee_lights.c
index cba86d058ea..e23a5a81169 100644
--- a/source/blender/draw/engines/eevee/eevee_lights.c
+++ b/source/blender/draw/engines/eevee/eevee_lights.c
@@ -96,7 +96,7 @@ static float light_shape_power_get(const Light *la, const EEVEE_Light *evli)
}
}
else if (ELEM(la->type, LA_SPOT, LA_LOCAL)) {
- power = 1.0f / (4.0f * evli->radius * evli->radius * M_PI * M_PI); /* 1/(4*r²*Pi²) */
+ power = 1.0f / (4.0f * evli->radius * evli->radius * M_PI * M_PI); /* `1/(4*(r^2)*(Pi^2))` */
/* for point lights (a.k.a radius == 0.0) */
// power = M_PI * M_PI * 0.78; /* XXX : Empirical, Fit cycles power */
@@ -106,7 +106,7 @@ static float light_shape_power_get(const Light *la, const EEVEE_Light *evli)
/* Make illumination power closer to cycles for bigger radii. Cycles uses a cos^3 term that we
* cannot reproduce so we account for that by scaling the light power. This function is the
* result of a rough manual fitting. */
- power += 1.0f / (2.0f * M_PI); /* power *= 1 + r²/2 */
+ power += 1.0f / (2.0f * M_PI); /* `power *= 1 + (r^2)/2` */
}
return power;
}
@@ -257,7 +257,7 @@ void EEVEE_lights_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
float power = max_fff(UNPACK3(evli->color)) * evli->volume;
if (power > 0.0f && evli->light_type != LA_SUN) {
/* The limit of the power attenuation function when the distance to the light goes to 0 is
- * 2 / r² where r is the light radius. We need to find the right radius that emits at most
+ * `2 / r^2` where r is the light radius. We need to find the right radius that emits at most
* the volume light upper bound. Inverting the function we get: */
float min_radius = 1.0f / sqrtf(0.5f * upper_bound / power);
/* Square it here to avoid a multiplication inside the shader. */
diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c
index 042fa621117..d2e0c8308c5 100644
--- a/source/blender/draw/engines/eevee/eevee_materials.c
+++ b/source/blender/draw/engines/eevee/eevee_materials.c
@@ -623,6 +623,11 @@ static EeveeMaterialCache material_opaque(EEVEE_Data *vedata,
/* This GPUShader has already been used by another material.
* Add new shading group just after to avoid shader switching cost. */
grp = DRW_shgroup_create_sub(*grp_p);
+
+ /* Per material uniforms. */
+ if (use_ssrefract) {
+ DRW_shgroup_uniform_float_copy(grp, "refractionDepth", ma->refract_depth);
+ }
}
else {
*grp_p = grp = DRW_shgroup_create(sh, shading_pass);
@@ -718,7 +723,7 @@ BLI_INLINE Material *eevee_object_material_get(Object *ob, int slot, bool holdou
if (holdout) {
return BKE_material_default_holdout();
}
- Material *ma = BKE_object_material_get(ob, slot + 1);
+ Material *ma = BKE_object_material_get_eval(ob, slot + 1);
if (ma == NULL) {
if (ob->type == OB_VOLUME) {
ma = BKE_material_default_volume();
@@ -765,12 +770,15 @@ static void eevee_hair_cache_populate(EEVEE_Data *vedata,
if (matcache.depth_grp) {
*matcache.depth_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.depth_grp);
+ DRW_shgroup_add_material_resources(*matcache.depth_grp_p, matcache.shading_gpumat);
}
if (matcache.shading_grp) {
*matcache.shading_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.shading_grp);
+ DRW_shgroup_add_material_resources(*matcache.shading_grp_p, matcache.shading_gpumat);
}
if (matcache.shadow_grp) {
*matcache.shadow_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.shadow_grp);
+ DRW_shgroup_add_material_resources(*matcache.shadow_grp_p, matcache.shading_gpumat);
*cast_shadow = true;
}
@@ -958,22 +966,9 @@ void EEVEE_material_renderpasses_init(EEVEE_Data *vedata)
}
}
-static void material_renderpass_init(EEVEE_FramebufferList *fbl,
- GPUTexture **output_tx,
- const eGPUTextureFormat format,
- const bool do_clear)
+static void material_renderpass_init(GPUTexture **output_tx, const eGPUTextureFormat format)
{
DRW_texture_ensure_fullscreen_2d(output_tx, format, 0);
- /* Clear texture. */
- if (do_clear) {
- const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
- /* TODO(fclem): replace by GPU_texture_clear once it is fast. */
- GPU_framebuffer_texture_attach(fbl->material_accum_fb, *output_tx, 0, 0);
- GPU_framebuffer_bind(fbl->material_accum_fb);
- GPU_framebuffer_clear_color(fbl->material_accum_fb, clear);
- GPU_framebuffer_bind(fbl->main_fb);
- GPU_framebuffer_texture_detach(fbl->material_accum_fb, *output_tx);
- }
}
void EEVEE_material_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, uint tot_samples)
@@ -988,33 +983,32 @@ void EEVEE_material_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata,
/* Should be enough precision for many samples. */
const eGPUTextureFormat texture_format = (tot_samples > 128) ? GPU_RGBA32F : GPU_RGBA16F;
- const bool do_clear = (effects->taa_current_sample == 1);
/* Create FrameBuffer. */
GPU_framebuffer_ensure_config(&fbl->material_accum_fb,
{GPU_ATTACHMENT_TEXTURE(dtxl->depth), GPU_ATTACHMENT_LEAVE});
if (pd->render_passes & EEVEE_RENDER_PASS_ENVIRONMENT) {
- material_renderpass_init(fbl, &txl->env_accum, texture_format, do_clear);
+ material_renderpass_init(&txl->env_accum, texture_format);
}
if (pd->render_passes & EEVEE_RENDER_PASS_EMIT) {
- material_renderpass_init(fbl, &txl->emit_accum, texture_format, do_clear);
+ material_renderpass_init(&txl->emit_accum, texture_format);
}
if (pd->render_passes & EEVEE_RENDER_PASS_DIFFUSE_COLOR) {
- material_renderpass_init(fbl, &txl->diff_color_accum, texture_format, do_clear);
+ material_renderpass_init(&txl->diff_color_accum, texture_format);
}
if (pd->render_passes & EEVEE_RENDER_PASS_DIFFUSE_LIGHT) {
- material_renderpass_init(fbl, &txl->diff_light_accum, texture_format, do_clear);
+ material_renderpass_init(&txl->diff_light_accum, texture_format);
}
if (pd->render_passes & EEVEE_RENDER_PASS_SPECULAR_COLOR) {
- material_renderpass_init(fbl, &txl->spec_color_accum, texture_format, do_clear);
+ material_renderpass_init(&txl->spec_color_accum, texture_format);
}
if (pd->render_passes & EEVEE_RENDER_PASS_AOV) {
for (int aov_index = 0; aov_index < pd->num_aovs_used; aov_index++) {
- material_renderpass_init(fbl, &txl->aov_surface_accum[aov_index], texture_format, do_clear);
+ material_renderpass_init(&txl->aov_surface_accum[aov_index], texture_format);
}
}
if (pd->render_passes & EEVEE_RENDER_PASS_SPECULAR_LIGHT) {
- material_renderpass_init(fbl, &txl->spec_light_accum, texture_format, do_clear);
+ material_renderpass_init(&txl->spec_light_accum, texture_format);
if (effects->enabled_effects & EFFECT_SSR) {
EEVEE_reflection_output_init(sldata, vedata, tot_samples);
@@ -1022,7 +1016,8 @@ void EEVEE_material_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata,
}
}
-static void material_renderpass_accumulate(EEVEE_FramebufferList *fbl,
+static void material_renderpass_accumulate(EEVEE_EffectsInfo *effects,
+ EEVEE_FramebufferList *fbl,
DRWPass *renderpass,
DRWPass *renderpass2,
EEVEE_PrivateData *pd,
@@ -1032,6 +1027,11 @@ static void material_renderpass_accumulate(EEVEE_FramebufferList *fbl,
GPU_framebuffer_texture_attach(fbl->material_accum_fb, output_tx, 0, 0);
GPU_framebuffer_bind(fbl->material_accum_fb);
+ if (effects->taa_current_sample == 1) {
+ const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+ GPU_framebuffer_clear_color(fbl->material_accum_fb, clear);
+ }
+
pd->renderpass_ubo = renderpass_option_ubo;
DRW_draw_pass(renderpass);
if (renderpass2) {
@@ -1053,15 +1053,21 @@ void EEVEE_material_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *v
DRWPass *material_accum_ps = psl->material_accum_ps;
DRWPass *background_accum_ps = psl->background_accum_ps;
if (pd->render_passes & EEVEE_RENDER_PASS_ENVIRONMENT) {
- material_renderpass_accumulate(
- fbl, background_accum_ps, NULL, pd, txl->env_accum, sldata->renderpass_ubo.environment);
+ material_renderpass_accumulate(effects,
+ fbl,
+ background_accum_ps,
+ NULL,
+ pd,
+ txl->env_accum,
+ sldata->renderpass_ubo.environment);
}
if (pd->render_passes & EEVEE_RENDER_PASS_EMIT) {
material_renderpass_accumulate(
- fbl, material_accum_ps, NULL, pd, txl->emit_accum, sldata->renderpass_ubo.emit);
+ effects, fbl, material_accum_ps, NULL, pd, txl->emit_accum, sldata->renderpass_ubo.emit);
}
if (pd->render_passes & EEVEE_RENDER_PASS_DIFFUSE_COLOR) {
- material_renderpass_accumulate(fbl,
+ material_renderpass_accumulate(effects,
+ fbl,
material_accum_ps,
NULL,
pd,
@@ -1069,7 +1075,8 @@ void EEVEE_material_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *v
sldata->renderpass_ubo.diff_color);
}
if (pd->render_passes & EEVEE_RENDER_PASS_DIFFUSE_LIGHT) {
- material_renderpass_accumulate(fbl,
+ material_renderpass_accumulate(effects,
+ fbl,
material_accum_ps,
NULL,
pd,
@@ -1081,15 +1088,27 @@ void EEVEE_material_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *v
}
}
if (pd->render_passes & EEVEE_RENDER_PASS_SPECULAR_COLOR) {
- material_renderpass_accumulate(fbl,
+ bool prev_ssr = sldata->common_data.ssr_toggle;
+ if (prev_ssr) {
+ /* We need to disable ssr here so output radiance is not directed to the ssr buffer. */
+ sldata->common_data.ssr_toggle = false;
+ GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data);
+ }
+ material_renderpass_accumulate(effects,
+ fbl,
material_accum_ps,
NULL,
pd,
txl->spec_color_accum,
sldata->renderpass_ubo.spec_color);
+ if (prev_ssr) {
+ sldata->common_data.ssr_toggle = prev_ssr;
+ GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data);
+ }
}
if (pd->render_passes & EEVEE_RENDER_PASS_SPECULAR_LIGHT) {
- material_renderpass_accumulate(fbl,
+ material_renderpass_accumulate(effects,
+ fbl,
material_accum_ps,
NULL,
pd,
@@ -1102,7 +1121,8 @@ void EEVEE_material_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *v
}
if (pd->render_passes & EEVEE_RENDER_PASS_AOV) {
for (int aov_index = 0; aov_index < pd->num_aovs_used; aov_index++) {
- material_renderpass_accumulate(fbl,
+ material_renderpass_accumulate(effects,
+ fbl,
material_accum_ps,
background_accum_ps,
pd,
diff --git a/source/blender/draw/engines/eevee/eevee_mist.c b/source/blender/draw/engines/eevee/eevee_mist.c
index 2b448695528..d4490d6fd4c 100644
--- a/source/blender/draw/engines/eevee/eevee_mist.c
+++ b/source/blender/draw/engines/eevee/eevee_mist.c
@@ -40,12 +40,9 @@ void EEVEE_mist_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
EEVEE_TextureList *txl = vedata->txl;
EEVEE_StorageList *stl = vedata->stl;
EEVEE_PassList *psl = vedata->psl;
- EEVEE_EffectsInfo *effects = stl->effects;
EEVEE_PrivateData *g_data = stl->g_data;
Scene *scene = draw_ctx->scene;
- const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
-
/* Create FrameBuffer. */
/* Should be enough precision for many samples. */
DRW_texture_ensure_fullscreen_2d(&txl->mist_accum, GPU_R32F, 0);
@@ -53,12 +50,6 @@ void EEVEE_mist_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
GPU_framebuffer_ensure_config(&fbl->mist_accum_fb,
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->mist_accum)});
- /* Clear texture. */
- if (effects->taa_current_sample == 1) {
- GPU_framebuffer_bind(fbl->mist_accum_fb);
- GPU_framebuffer_clear_color(fbl->mist_accum_fb, clear);
- }
-
/* Mist settings. */
if (scene && scene->world) {
g_data->mist_start = scene->world->miststa;
@@ -103,9 +94,17 @@ void EEVEE_mist_output_accumulate(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Dat
{
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_PassList *psl = vedata->psl;
+ EEVEE_EffectsInfo *effects = vedata->stl->effects;
if (fbl->mist_accum_fb != NULL) {
GPU_framebuffer_bind(fbl->mist_accum_fb);
+
+ /* Clear texture. */
+ if (effects->taa_current_sample == 1) {
+ const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+ GPU_framebuffer_clear_color(fbl->mist_accum_fb, clear);
+ }
+
DRW_draw_pass(psl->mist_accum_ps);
/* Restore */
diff --git a/source/blender/draw/engines/eevee/eevee_occlusion.c b/source/blender/draw/engines/eevee/eevee_occlusion.c
index 3f198063c47..4c2024a6f65 100644
--- a/source/blender/draw/engines/eevee/eevee_occlusion.c
+++ b/source/blender/draw/engines/eevee/eevee_occlusion.c
@@ -61,7 +61,7 @@ int EEVEE_occlusion_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
const int fs_size[2] = {(int)viewport_size[0], (int)viewport_size[1]};
common_data->ao_dist = scene_eval->eevee.gtao_distance;
- common_data->ao_factor = scene_eval->eevee.gtao_factor;
+ common_data->ao_factor = max_ff(1e-4f, scene_eval->eevee.gtao_factor);
common_data->ao_quality = scene_eval->eevee.gtao_quality;
if (scene_eval->eevee.flag & SCE_EEVEE_GTAO_ENABLED) {
@@ -120,7 +120,6 @@ void EEVEE_occlusion_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata
const eGPUTextureFormat texture_format = (tot_samples > 128) ? GPU_R32F : GPU_R16F;
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
- const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
/* Should be enough precision for many samples. */
DRW_texture_ensure_fullscreen_2d(&txl->ao_accum, texture_format, 0);
@@ -128,12 +127,6 @@ void EEVEE_occlusion_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata
GPU_framebuffer_ensure_config(&fbl->ao_accum_fb,
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->ao_accum)});
- /* Clear texture. */
- if (effects->taa_current_sample == 1) {
- GPU_framebuffer_bind(fbl->ao_accum_fb);
- GPU_framebuffer_clear_color(fbl->ao_accum_fb, clear);
- }
-
/* Accumulation pass */
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD;
DRW_PASS_CREATE(psl->ao_accum_ps, state);
@@ -246,6 +239,7 @@ void EEVEE_occlusion_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *
{
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_PassList *psl = vedata->psl;
+ EEVEE_EffectsInfo *effects = vedata->stl->effects;
if (fbl->ao_accum_fb != NULL) {
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
@@ -255,6 +249,13 @@ void EEVEE_occlusion_output_accumulate(EEVEE_ViewLayerData *sldata, EEVEE_Data *
EEVEE_occlusion_compute(sldata, vedata);
GPU_framebuffer_bind(fbl->ao_accum_fb);
+
+ /* Clear texture. */
+ if (effects->taa_current_sample == 1) {
+ const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+ GPU_framebuffer_clear_color(fbl->ao_accum_fb, clear);
+ }
+
DRW_draw_pass(psl->ao_accum_ps);
/* Restore */
diff --git a/source/blender/draw/engines/eevee/eevee_renderpasses.c b/source/blender/draw/engines/eevee/eevee_renderpasses.c
index 5739024993e..5ada53ab98c 100644
--- a/source/blender/draw/engines/eevee/eevee_renderpasses.c
+++ b/source/blender/draw/engines/eevee/eevee_renderpasses.c
@@ -79,7 +79,7 @@ bool EEVEE_renderpasses_only_first_sample_pass_active(EEVEE_Data *vedata)
* type the rest of the bits are used for the name hash. */
int EEVEE_renderpasses_aov_hash(const ViewLayerAOV *aov)
{
- int hash = BLI_hash_string(aov->name);
+ int hash = BLI_hash_string(aov->name) << 1;
SET_FLAG_FROM_TEST(hash, aov->type == AOV_TYPE_COLOR, EEVEE_AOV_HASH_COLOR_TYPE_MASK);
return hash;
}
diff --git a/source/blender/draw/engines/eevee/eevee_screen_raytrace.c b/source/blender/draw/engines/eevee/eevee_screen_raytrace.c
index 54f23073bd0..7af0f60748b 100644
--- a/source/blender/draw/engines/eevee/eevee_screen_raytrace.c
+++ b/source/blender/draw/engines/eevee/eevee_screen_raytrace.c
@@ -234,10 +234,6 @@ void EEVEE_reflection_output_init(EEVEE_ViewLayerData *UNUSED(sldata),
{
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_TextureList *txl = vedata->txl;
- EEVEE_StorageList *stl = vedata->stl;
- EEVEE_EffectsInfo *effects = stl->effects;
-
- const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
/* Create FrameBuffer. */
const eGPUTextureFormat texture_format = (tot_samples > 256) ? GPU_RGBA32F : GPU_RGBA16F;
@@ -245,12 +241,6 @@ void EEVEE_reflection_output_init(EEVEE_ViewLayerData *UNUSED(sldata),
GPU_framebuffer_ensure_config(&fbl->ssr_accum_fb,
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->ssr_accum)});
-
- /* Clear texture. */
- if (effects->taa_current_sample == 1) {
- GPU_framebuffer_bind(fbl->ssr_accum_fb);
- GPU_framebuffer_clear_color(fbl->ssr_accum_fb, clear);
- }
}
void EEVEE_reflection_output_accumulate(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
@@ -258,9 +248,17 @@ void EEVEE_reflection_output_accumulate(EEVEE_ViewLayerData *UNUSED(sldata), EEV
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_PassList *psl = vedata->psl;
EEVEE_StorageList *stl = vedata->stl;
+ EEVEE_EffectsInfo *effects = vedata->stl->effects;
if (stl->g_data->valid_double_buffer) {
GPU_framebuffer_bind(fbl->ssr_accum_fb);
+
+ /* Clear texture. */
+ if (effects->taa_current_sample == 1) {
+ const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+ GPU_framebuffer_clear_color(fbl->ssr_accum_fb, clear);
+ }
+
DRW_draw_pass(psl->ssr_resolve);
}
}
diff --git a/source/blender/draw/engines/eevee/eevee_shadows.c b/source/blender/draw/engines/eevee/eevee_shadows.c
index 51fd1cad41e..6a98c3316f3 100644
--- a/source/blender/draw/engines/eevee/eevee_shadows.c
+++ b/source/blender/draw/engines/eevee/eevee_shadows.c
@@ -361,12 +361,8 @@ void EEVEE_shadow_output_init(EEVEE_ViewLayerData *sldata,
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_TextureList *txl = vedata->txl;
EEVEE_PassList *psl = vedata->psl;
- EEVEE_StorageList *stl = vedata->stl;
- EEVEE_EffectsInfo *effects = stl->effects;
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
- const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
-
/* Create FrameBuffer. */
const eGPUTextureFormat texture_format = GPU_R32F;
DRW_texture_ensure_fullscreen_2d(&txl->shadow_accum, texture_format, 0);
@@ -374,12 +370,6 @@ void EEVEE_shadow_output_init(EEVEE_ViewLayerData *sldata,
GPU_framebuffer_ensure_config(&fbl->shadow_accum_fb,
{GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->shadow_accum)});
- /* Clear texture. */
- if (effects->taa_current_sample == 1) {
- GPU_framebuffer_bind(fbl->shadow_accum_fb);
- GPU_framebuffer_clear_color(fbl->shadow_accum_fb, clear);
- }
-
/* Create Pass and shgroup. */
DRW_PASS_CREATE(psl->shadow_accum_pass,
DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_ALWAYS | DRW_STATE_BLEND_ADD_FULL);
@@ -404,9 +394,17 @@ void EEVEE_shadow_output_accumulate(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_D
{
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_PassList *psl = vedata->psl;
+ EEVEE_EffectsInfo *effects = vedata->stl->effects;
if (fbl->shadow_accum_fb != NULL) {
GPU_framebuffer_bind(fbl->shadow_accum_fb);
+
+ /* Clear texture. */
+ if (effects->taa_current_sample == 1) {
+ const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+ GPU_framebuffer_clear_color(fbl->shadow_accum_fb, clear);
+ }
+
DRW_draw_pass(psl->shadow_accum_pass);
/* Restore */
diff --git a/source/blender/draw/engines/eevee/eevee_volumes.c b/source/blender/draw/engines/eevee/eevee_volumes.c
index aee260a542e..eed36221fcb 100644
--- a/source/blender/draw/engines/eevee/eevee_volumes.c
+++ b/source/blender/draw/engines/eevee/eevee_volumes.c
@@ -332,7 +332,7 @@ static bool eevee_volume_object_grids_init(Object *ob, ListBase *gpu_grids, DRWS
bool multiple_transforms = true;
LISTBASE_FOREACH (GPUMaterialVolumeGrid *, gpu_grid, gpu_grids) {
- VolumeGrid *volume_grid = BKE_volume_grid_find(volume, gpu_grid->name);
+ const VolumeGrid *volume_grid = BKE_volume_grid_find_for_read(volume, gpu_grid->name);
DRWVolumeGrid *drw_grid = (volume_grid) ?
DRW_volume_batch_cache_get_grid(volume, volume_grid) :
NULL;
@@ -385,7 +385,7 @@ static bool eevee_volume_object_grids_init(Object *ob, ListBase *gpu_grids, DRWS
/* Bind volume grid textures. */
LISTBASE_FOREACH (GPUMaterialVolumeGrid *, gpu_grid, gpu_grids) {
- VolumeGrid *volume_grid = BKE_volume_grid_find(volume, gpu_grid->name);
+ const VolumeGrid *volume_grid = BKE_volume_grid_find_for_read(volume, gpu_grid->name);
DRWVolumeGrid *drw_grid = (volume_grid) ?
DRW_volume_batch_cache_get_grid(volume, volume_grid) :
NULL;
@@ -501,7 +501,7 @@ void EEVEE_volumes_cache_object_add(EEVEE_ViewLayerData *sldata,
Scene *scene,
Object *ob)
{
- Material *ma = BKE_object_material_get(ob, 1);
+ Material *ma = BKE_object_material_get_eval(ob, 1);
if (ma == NULL) {
if (ob->type == OB_VOLUME) {
@@ -800,8 +800,6 @@ void EEVEE_volumes_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata,
EEVEE_PassList *psl = vedata->psl;
EEVEE_EffectsInfo *effects = stl->effects;
- const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
-
/* Create FrameBuffer. */
/* Should be enough precision for many samples. */
@@ -814,12 +812,6 @@ void EEVEE_volumes_output_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata,
GPU_ATTACHMENT_TEXTURE(txl->volume_scatter_accum),
GPU_ATTACHMENT_TEXTURE(txl->volume_transmittance_accum)});
- /* Clear texture. */
- if (effects->taa_current_sample == 1) {
- GPU_framebuffer_bind(fbl->volumetric_accum_fb);
- GPU_framebuffer_clear_color(fbl->volumetric_accum_fb, clear);
- }
-
/* Create Pass and shgroup. */
DRW_PASS_CREATE(psl->volumetric_accum_ps, DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD_FULL);
DRWShadingGroup *grp = NULL;
@@ -843,10 +835,18 @@ void EEVEE_volumes_output_accumulate(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_
{
EEVEE_FramebufferList *fbl = vedata->fbl;
EEVEE_PassList *psl = vedata->psl;
+ EEVEE_EffectsInfo *effects = vedata->stl->effects;
if (fbl->volumetric_accum_fb != NULL) {
/* Accum pass */
GPU_framebuffer_bind(fbl->volumetric_accum_fb);
+
+ /* Clear texture. */
+ if (effects->taa_current_sample == 1) {
+ const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+ GPU_framebuffer_clear_color(fbl->volumetric_accum_fb, clear);
+ }
+
DRW_draw_pass(psl->volumetric_accum_ps);
/* Restore */
diff --git a/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl b/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl
index 69b9ecaf77d..cdc453eed40 100644
--- a/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl
@@ -210,14 +210,14 @@ void occlusion_eval(OcclusionData data,
bool early_out = (inverted != 0.0) ? (max_v4(abs(data.horizons)) == 0.0) :
(min_v4(abs(data.horizons)) == M_PI);
if (early_out) {
- visibility = dot(N, Ng) * 0.5 + 0.5;
+ visibility = saturate(dot(N, Ng) * 0.5 + 0.5);
visibility = min(visibility, data.custom_occlusion);
if ((int(aoSettings) & USE_BENT_NORMAL) == 0) {
bent_normal = N;
}
else {
- bent_normal = normalize(N + Ng);
+ bent_normal = safe_normalize(N + Ng);
}
return;
}
@@ -283,7 +283,9 @@ void occlusion_eval(OcclusionData data,
bent_normal = N;
}
else {
- bent_normal = normalize(mix(bent_normal, N, sqr(sqr(sqr(visibility)))));
+ /* Note: using pow(visibility, 6.0) produces NaN (see T87369). */
+ float tmp = saturate(pow6(visibility));
+ bent_normal = normalize(mix(bent_normal, N, tmp));
}
}
@@ -386,7 +388,8 @@ float specular_occlusion(
float specular_solid_angle = spherical_cap_intersection(M_PI_2, spec_angle, cone_nor_dist);
float specular_occlusion = isect_solid_angle / specular_solid_angle;
/* Mix because it is unstable in unoccluded areas. */
- visibility = mix(specular_occlusion, 1.0, pow(visibility, 8.0));
+ float tmp = saturate(pow8(visibility));
+ visibility = mix(specular_occlusion, 1.0, tmp);
/* Scale by user factor */
visibility = pow(saturate(visibility), aoFactor);
diff --git a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl
index c8eaa06094e..05496ad4ab0 100644
--- a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl
@@ -164,8 +164,8 @@ vec3 ensure_valid_reflection(vec3 Ng, vec3 I, vec3 N)
vec2 N_new;
if (valid1 && valid2) {
/* If both are possible, do the expensive reflection-based check. */
- vec2 N1 = vec2(sqrt(1.0 - N1_z2), sqrt(N1_z2));
- vec2 N2 = vec2(sqrt(1.0 - N2_z2), sqrt(N2_z2));
+ vec2 N1 = vec2(safe_sqrt(1.0 - N1_z2), safe_sqrt(N1_z2));
+ vec2 N2 = vec2(safe_sqrt(1.0 - N2_z2), safe_sqrt(N2_z2));
float R1 = 2.0 * (N1.x * Ix + N1.y * Iz) * N1.y - Iz;
float R2 = 2.0 * (N2.x * Ix + N2.y * Iz) * N2.y - Iz;
@@ -181,7 +181,7 @@ vec3 ensure_valid_reflection(vec3 Ng, vec3 I, vec3 N)
}
else if (valid1 || valid2) {
float Nz2 = valid1 ? N1_z2 : N2_z2;
- N_new = vec2(sqrt(1.0 - Nz2), sqrt(Nz2));
+ N_new = vec2(safe_sqrt(1.0 - Nz2), safe_sqrt(Nz2));
}
else {
return Ng;
diff --git a/source/blender/draw/engines/eevee/shaders/closure_eval_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_eval_lib.glsl
index b554fd113df..94dd1a439db 100644
--- a/source/blender/draw/engines/eevee/shaders/closure_eval_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/closure_eval_lib.glsl
@@ -51,53 +51,72 @@
closure_##t2##_##subroutine(in_##t2##_2, eval_##t2##_2, cl_common, sub_data, out_##t2##_2); \
closure_##t3##_##subroutine(in_##t3##_3, eval_##t3##_3, cl_common, sub_data, out_##t3##_3);
+#ifndef DEPTH_SHADER
/* Inputs are inout so that callers can get the final inputs used for evaluation. */
-#define CLOSURE_EVAL_FUNCTION_DECLARE(name, t0, t1, t2, t3) \
- void closure_##name##_eval(ClosureInputCommon in_common, \
- inout ClosureInput##t0 in_##t0##_0, \
- inout ClosureInput##t1 in_##t1##_1, \
- inout ClosureInput##t2 in_##t2##_2, \
- inout ClosureInput##t3 in_##t3##_3, \
- out ClosureOutput##t0 out_##t0##_0, \
- out ClosureOutput##t1 out_##t1##_1, \
- out ClosureOutput##t2 out_##t2##_2, \
- out ClosureOutput##t3 out_##t3##_3) \
- { \
- CLOSURE_EVAL_DECLARE(t0, t1, t2, t3); \
+# define CLOSURE_EVAL_FUNCTION_DECLARE(name, t0, t1, t2, t3) \
+ void closure_##name##_eval(ClosureInputCommon in_common, \
+ inout ClosureInput##t0 in_##t0##_0, \
+ inout ClosureInput##t1 in_##t1##_1, \
+ inout ClosureInput##t2 in_##t2##_2, \
+ inout ClosureInput##t3 in_##t3##_3, \
+ out ClosureOutput##t0 out_##t0##_0, \
+ out ClosureOutput##t1 out_##t1##_1, \
+ out ClosureOutput##t2 out_##t2##_2, \
+ out ClosureOutput##t3 out_##t3##_3) \
+ { \
+ CLOSURE_EVAL_DECLARE(t0, t1, t2, t3); \
\
- /* Starts at 1 because 0 is world cubemap. */ \
- for (int i = 1; cl_common.specular_accum > 0.0 && i < prbNumRenderCube && i < MAX_PROBE; \
- i++) { \
- ClosureCubemapData cube = closure_cubemap_eval_init(i, cl_common); \
- if (cube.attenuation > 1e-8) { \
- CLOSURE_META_SUBROUTINE_DATA(cubemap_eval, cube, t0, t1, t2, t3); \
+ /* Starts at 1 because 0 is world cubemap. */ \
+ for (int i = 1; cl_common.specular_accum > 0.0 && i < prbNumRenderCube && i < MAX_PROBE; \
+ i++) { \
+ ClosureCubemapData cube = closure_cubemap_eval_init(i, cl_common); \
+ if (cube.attenuation > 1e-8) { \
+ CLOSURE_META_SUBROUTINE_DATA(cubemap_eval, cube, t0, t1, t2, t3); \
+ } \
} \
- } \
\
- /* Starts at 1 because 0 is world irradiance. */ \
- for (int i = 1; cl_common.diffuse_accum > 0.0 && i < prbNumRenderGrid && i < MAX_GRID; i++) { \
- ClosureGridData grid = closure_grid_eval_init(i, cl_common); \
- if (grid.attenuation > 1e-8) { \
- CLOSURE_META_SUBROUTINE_DATA(grid_eval, grid, t0, t1, t2, t3); \
+ /* Starts at 1 because 0 is world irradiance. */ \
+ for (int i = 1; cl_common.diffuse_accum > 0.0 && i < prbNumRenderGrid && i < MAX_GRID; \
+ i++) { \
+ ClosureGridData grid = closure_grid_eval_init(i, cl_common); \
+ if (grid.attenuation > 1e-8) { \
+ CLOSURE_META_SUBROUTINE_DATA(grid_eval, grid, t0, t1, t2, t3); \
+ } \
} \
- } \
\
- CLOSURE_META_SUBROUTINE(indirect_end, t0, t1, t2, t3); \
+ CLOSURE_META_SUBROUTINE(indirect_end, t0, t1, t2, t3); \
\
- ClosurePlanarData planar = closure_planar_eval_init(cl_common); \
- if (planar.attenuation > 1e-8) { \
- CLOSURE_META_SUBROUTINE_DATA(planar_eval, planar, t0, t1, t2, t3); \
- } \
+ ClosurePlanarData planar = closure_planar_eval_init(cl_common); \
+ if (planar.attenuation > 1e-8) { \
+ CLOSURE_META_SUBROUTINE_DATA(planar_eval, planar, t0, t1, t2, t3); \
+ } \
\
- for (int i = 0; i < laNumLight && i < MAX_LIGHT; i++) { \
- ClosureLightData light = closure_light_eval_init(cl_common, i); \
- if (light.vis > 1e-8) { \
- CLOSURE_META_SUBROUTINE_DATA(light_eval, light, t0, t1, t2, t3); \
+ for (int i = 0; i < laNumLight && i < MAX_LIGHT; i++) { \
+ ClosureLightData light = closure_light_eval_init(cl_common, i); \
+ if (light.vis > 1e-8) { \
+ CLOSURE_META_SUBROUTINE_DATA(light_eval, light, t0, t1, t2, t3); \
+ } \
} \
- } \
\
- CLOSURE_META_SUBROUTINE(eval_end, t0, t1, t2, t3); \
- }
+ CLOSURE_META_SUBROUTINE(eval_end, t0, t1, t2, t3); \
+ }
+
+#else
+/* Inputs are inout so that callers can get the final inputs used for evaluation. */
+# define CLOSURE_EVAL_FUNCTION_DECLARE(name, t0, t1, t2, t3) \
+ void closure_##name##_eval(ClosureInputCommon in_common, \
+ inout ClosureInput##t0 in_##t0##_0, \
+ inout ClosureInput##t1 in_##t1##_1, \
+ inout ClosureInput##t2 in_##t2##_2, \
+ inout ClosureInput##t3 in_##t3##_3, \
+ out ClosureOutput##t0 out_##t0##_0, \
+ out ClosureOutput##t1 out_##t1##_1, \
+ out ClosureOutput##t2 out_##t2##_2, \
+ out ClosureOutput##t3 out_##t3##_3) \
+ { \
+ CLOSURE_EVAL_DECLARE(t0, t1, t2, t3); \
+ }
+#endif
#define CLOSURE_EVAL_FUNCTION(name, t0, t1, t2, t3) \
closure_##name##_eval(in_common, \
diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_lib.glsl b/source/blender/draw/engines/eevee/shaders/effect_dof_lib.glsl
index faac216480b..dac53719149 100644
--- a/source/blender/draw/engines/eevee/shaders/effect_dof_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/effect_dof_lib.glsl
@@ -136,7 +136,7 @@ const float layer_offset_fg = 0.5 + 1.0;
/* Extra offset for convolution layers to avoid light leaking from background. */
const float layer_offset = 0.5 + 0.5;
-#define DOF_MAX_SLIGHT_FOCUS_RADIUS 5
+#define DOF_MAX_SLIGHT_FOCUS_RADIUS 16
float dof_layer_weight(float coc, const bool is_foreground)
{
diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_resolve_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_dof_resolve_frag.glsl
index cd69a0e2ab1..32841b7749c 100644
--- a/source/blender/draw/engines/eevee/shaders/effect_dof_resolve_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/effect_dof_resolve_frag.glsl
@@ -40,7 +40,7 @@ void dof_slight_focus_gather(float radius, out vec4 out_color, out float out_wei
DofGatherData fg_accum = GATHER_DATA_INIT;
DofGatherData bg_accum = GATHER_DATA_INIT;
- int i_radius = clamp(int(radius), 0, int(layer_threshold));
+ int i_radius = clamp(int(radius + 0.5), 0, int(layer_threshold));
const int resolve_ring_density = DOF_SLIGHT_FOCUS_DENSITY;
ivec2 texel = ivec2(gl_FragCoord.xy);
diff --git a/source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl
index 737ef7dc509..356ed102928 100644
--- a/source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl
@@ -183,16 +183,70 @@ vec3 light_translucent(LightData ld, vec3 P, vec3 N, vec4 l_vector, vec2 rand, f
#undef scube
#undef scsmd
+/* Similar to https://atyuwen.github.io/posts/normal-reconstruction/.
+ * This samples the depth buffer 4 time for each direction to get the most correct
+ * implicit normal reconstruction out of the depth buffer. */
+vec3 view_position_derivative_from_depth(vec2 uvs, vec2 ofs, vec3 vP, float depth_center)
+{
+ vec2 uv1 = uvs - ofs * 2.0;
+ vec2 uv2 = uvs - ofs;
+ vec2 uv3 = uvs + ofs;
+ vec2 uv4 = uvs + ofs * 2.0;
+ vec4 H;
+ H.x = textureLod(depthBuffer, uv1, 0.0).r;
+ H.y = textureLod(depthBuffer, uv2, 0.0).r;
+ H.z = textureLod(depthBuffer, uv3, 0.0).r;
+ H.w = textureLod(depthBuffer, uv4, 0.0).r;
+ /* Fix issue with depth precision. Take even larger diff. */
+ vec4 diff = abs(vec4(depth_center, H.yzw) - H.x);
+ if (max_v4(diff) < 2.4e-7 && all(lessThan(diff.xyz, diff.www))) {
+ return 0.25 * (get_view_space_from_depth(uv3, H.w) - get_view_space_from_depth(uv1, H.x));
+ }
+ /* Simplified (H.xw + 2.0 * (H.yz - H.xw)) - depth_center */
+ vec2 deltas = abs((2.0 * H.yz - H.xw) - depth_center);
+ if (deltas.x < deltas.y) {
+ return vP - get_view_space_from_depth(uv2, H.y);
+ }
+ else {
+ return get_view_space_from_depth(uv3, H.z) - vP;
+ }
+}
+
+/* TODO(fclem) port to a common place for other effects to use. */
+bool reconstruct_view_position_and_normal_from_depth(vec2 uvs, out vec3 vP, out vec3 vNg)
+{
+ vec2 texel_size = vec2(abs(dFdx(uvs.x)), abs(dFdy(uvs.y)));
+ float depth_center = textureLod(depthBuffer, uvs, 0.0).r;
+
+ vP = get_view_space_from_depth(uvs, depth_center);
+
+ vec3 dPdx = view_position_derivative_from_depth(uvs, texel_size * vec2(1, 0), vP, depth_center);
+ vec3 dPdy = view_position_derivative_from_depth(uvs, texel_size * vec2(0, 1), vP, depth_center);
+
+ vNg = safe_normalize(cross(dPdx, dPdy));
+
+ /* Background case. */
+ if (depth_center == 1.0) {
+ return false;
+ }
+
+ return true;
+}
+
void main(void)
{
vec2 uvs = uvcoordsvar.xy;
float sss_scale = texture(sssRadius, uvs).r;
- vec3 P = get_world_space_from_depth(uvs, texture(depthBuffer, uvs).r);
- vec3 N = normalize(cross(dFdx(P), dFdy(P)));
vec3 rand = texelfetch_noise_tex(gl_FragCoord.xy).zwy;
rand.xy *= fast_sqrt(rand.z);
+ vec3 vP, vNg;
+ reconstruct_view_position_and_normal_from_depth(uvs, vP, vNg);
+
+ vec3 P = point_view_to_world(vP);
+ vec3 Ng = normal_view_to_world(vNg);
+
vec3 accum = vec3(0.0);
for (int i = 0; i < MAX_LIGHT && i < laNumLight; i++) {
LightData ld = lights_data[i];
@@ -211,7 +265,7 @@ void main(void)
continue;
}
- accum += att * ld.l_color * light_translucent(ld, P, -N, l_vector, rand.xy, sss_scale);
+ accum += att * ld.l_color * light_translucent(ld, P, -Ng, l_vector, rand.xy, sss_scale);
}
FragColor = vec4(accum, 1.0);
diff --git a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c
index ee51b751187..af8b029a08e 100644
--- a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c
+++ b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c
@@ -60,7 +60,8 @@ GPENCIL_tObject *gpencil_object_cache_add(GPENCIL_PrivateData *pd, Object *ob)
/* Check if any material with holdout flag enabled. */
tgp_ob->do_mat_holdout = false;
- for (int i = 0; i < ob->totcol; i++) {
+ const int tot_materials = BKE_object_material_count_eval(ob);
+ for (int i = 0; i < tot_materials; i++) {
MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, i + 1);
if (((gp_style != NULL) && (gp_style->flag & GP_MATERIAL_IS_STROKE_HOLDOUT)) ||
((gp_style->flag & GP_MATERIAL_IS_FILL_HOLDOUT))) {
@@ -273,7 +274,13 @@ GPENCIL_tLayer *gpencil_layer_cache_add(GPENCIL_PrivateData *pd,
const bool override_vertcol = (pd->v3d_color_type != -1);
const bool is_vert_col_mode = (pd->v3d_color_type == V3D_SHADING_VERTEX_COLOR) ||
GPENCIL_VERTEX_MODE(gpd) || pd->is_render;
- bool is_masked = (gpl->flag & GP_LAYER_USE_MASK) && !BLI_listbase_is_empty(&gpl->mask_layers);
+ const bool is_viewlayer_render = pd->is_render && (gpl->viewlayername[0] != '\0') &&
+ STREQ(pd->view_layer->name, gpl->viewlayername);
+ const bool disable_masks_render = is_viewlayer_render &&
+ (gpl->flag & GP_LAYER_DISABLE_MASKS_IN_VIEWLAYER);
+ bool is_masked = disable_masks_render ? false :
+ (gpl->flag & GP_LAYER_USE_MASK) &&
+ !BLI_listbase_is_empty(&gpl->mask_layers);
float vert_col_opacity = (override_vertcol) ?
(is_vert_col_mode ? pd->vertex_paint_opacity : 0.0f) :
diff --git a/source/blender/draw/engines/gpencil/gpencil_draw_data.c b/source/blender/draw/engines/gpencil/gpencil_draw_data.c
index 28c322b5e08..e3e84dd4c8c 100644
--- a/source/blender/draw/engines/gpencil/gpencil_draw_data.c
+++ b/source/blender/draw/engines/gpencil/gpencil_draw_data.c
@@ -105,10 +105,12 @@ static void gpencil_shade_color(float color[3])
}
/* Apply all overrides from the solid viewport mode to the GPencil material. */
-static MaterialGPencilStyle *gpencil_viewport_material_overrides(GPENCIL_PrivateData *pd,
- Object *ob,
- int color_type,
- MaterialGPencilStyle *gp_style)
+static MaterialGPencilStyle *gpencil_viewport_material_overrides(
+ GPENCIL_PrivateData *pd,
+ Object *ob,
+ int color_type,
+ MaterialGPencilStyle *gp_style,
+ const eV3DShadingLightingMode lighting_mode)
{
static MaterialGPencilStyle gp_style_tmp;
@@ -148,7 +150,9 @@ static MaterialGPencilStyle *gpencil_viewport_material_overrides(GPENCIL_Private
copy_v3_v3(gp_style->fill_rgba, pd->v3d_single_color);
gp_style->fill_rgba[3] = 1.0f;
copy_v4_v4(gp_style->stroke_rgba, gp_style->fill_rgba);
- gpencil_shade_color(gp_style->stroke_rgba);
+ if (lighting_mode != V3D_LIGHTING_FLAT) {
+ gpencil_shade_color(gp_style->fill_rgba);
+ }
break;
case V3D_SHADING_OBJECT_COLOR:
gp_style = &gp_style_tmp;
@@ -156,7 +160,9 @@ static MaterialGPencilStyle *gpencil_viewport_material_overrides(GPENCIL_Private
gp_style->fill_style = GP_MATERIAL_FILL_STYLE_SOLID;
copy_v4_v4(gp_style->fill_rgba, ob->color);
copy_v4_v4(gp_style->stroke_rgba, ob->color);
- gpencil_shade_color(gp_style->stroke_rgba);
+ if (lighting_mode != V3D_LIGHTING_FLAT) {
+ gpencil_shade_color(gp_style->fill_rgba);
+ }
break;
case V3D_SHADING_VERTEX_COLOR:
gp_style = &gp_style_tmp;
@@ -180,7 +186,7 @@ GPENCIL_MaterialPool *gpencil_material_pool_create(GPENCIL_PrivateData *pd, Obje
{
GPENCIL_MaterialPool *matpool = pd->last_material_pool;
- int mat_len = max_ii(1, ob->totcol);
+ int mat_len = max_ii(1, BKE_object_material_count_eval(ob));
bool reuse_matpool = matpool && ((matpool->used_count + mat_len) <= GP_MATERIAL_BUFFER_LEN);
@@ -198,6 +204,8 @@ GPENCIL_MaterialPool *gpencil_material_pool_create(GPENCIL_PrivateData *pd, Obje
int color_type = (pd->v3d_color_type != -1 && GPENCIL_VERTEX_MODE(gpd)) ?
V3D_SHADING_VERTEX_COLOR :
pd->v3d_color_type;
+ const eV3DShadingLightingMode lighting_mode = (pd->v3d != NULL) ? pd->v3d->shading.light :
+ V3D_LIGHTING_STUDIO;
GPENCIL_MaterialPool *pool = matpool;
for (int i = 0; i < mat_len; i++) {
@@ -245,7 +253,7 @@ GPENCIL_MaterialPool *gpencil_material_pool_create(GPENCIL_PrivateData *pd, Obje
mat_data->flag |= GP_FILL_HOLDOUT;
}
- gp_style = gpencil_viewport_material_overrides(pd, ob, color_type, gp_style);
+ gp_style = gpencil_viewport_material_overrides(pd, ob, color_type, gp_style, lighting_mode);
/* Dots or Squares rotation. */
mat_data->alignment_rot_cos = cosf(gp_style->alignment_rotation);
diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.c b/source/blender/draw/engines/gpencil/gpencil_engine.c
index 8bb336ebc96..32884eb9e3f 100644
--- a/source/blender/draw/engines/gpencil/gpencil_engine.c
+++ b/source/blender/draw/engines/gpencil/gpencil_engine.c
@@ -819,7 +819,10 @@ static void gpencil_draw_mask(GPENCIL_Data *vedata, GPENCIL_tObject *ob, GPENCIL
}
GPENCIL_tLayer *mask_layer = gpencil_layer_cache_get(ob, i);
- BLI_assert(mask_layer);
+ /* When filtering by viewlayer, the mask could be null and must be ignored. */
+ if (mask_layer == NULL) {
+ continue;
+ }
DRW_draw_pass(mask_layer->geom_ps);
}
diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.h b/source/blender/draw/engines/gpencil/gpencil_engine.h
index 5ceb909bc88..34fe29055d6 100644
--- a/source/blender/draw/engines/gpencil/gpencil_engine.h
+++ b/source/blender/draw/engines/gpencil/gpencil_engine.h
@@ -297,7 +297,9 @@ typedef struct GPENCIL_PrivateData {
int v3d_color_type;
/* Current frame */
int cfra;
- /* If we are rendering for final render (F12). */
+ /* If we are rendering for final render (F12).
+ * NOTE: set to false for viewport and opengl rendering (including VSE scene rendering), but set
+ * to true when rendering in `OB_RENDER` shading mode (viewport or opengl rendering) */
bool is_render;
/* If we are in viewport display (used for VFX). */
bool is_viewport;
diff --git a/source/blender/draw/engines/gpencil/gpencil_shader_fx.c b/source/blender/draw/engines/gpencil/gpencil_shader_fx.c
index 0b66141e51a..21d55357a2a 100644
--- a/source/blender/draw/engines/gpencil/gpencil_shader_fx.c
+++ b/source/blender/draw/engines/gpencil/gpencil_shader_fx.c
@@ -424,22 +424,26 @@ static void gpencil_vfx_glow(GlowShaderFxData *fx, Object *UNUSED(ob), gpIterVfx
GPUShader *sh = GPENCIL_shader_fx_glow_get();
- float ref_col[3];
+ float ref_col[4];
if (fx->mode == eShaderFxGlowMode_Luminance) {
+ /* Only pass in the first value for luminance. */
ref_col[0] = fx->threshold;
ref_col[1] = -1.0f;
ref_col[2] = -1.0f;
+ ref_col[3] = -1.0f;
}
else {
+ /* First three values are the RGB for the selected color, last value the threshold. */
copy_v3_v3(ref_col, fx->select_color);
+ ref_col[3] = fx->threshold;
}
DRWState state = DRW_STATE_WRITE_COLOR;
grp = gpencil_vfx_pass_create("Fx Glow H", state, iter, sh);
DRW_shgroup_uniform_vec2_copy(grp, "offset", (float[2]){fx->blur[0] * c, fx->blur[0] * s});
DRW_shgroup_uniform_int_copy(grp, "sampCount", max_ii(1, min_ii(fx->samples, fx->blur[0])));
- DRW_shgroup_uniform_vec3_copy(grp, "threshold", ref_col);
+ DRW_shgroup_uniform_vec4_copy(grp, "threshold", ref_col);
DRW_shgroup_uniform_vec4_copy(grp, "glowColor", fx->glow_color);
DRW_shgroup_uniform_bool_copy(grp, "glowUnder", use_glow_under);
DRW_shgroup_uniform_bool_copy(grp, "firstPass", true);
@@ -473,7 +477,7 @@ static void gpencil_vfx_glow(GlowShaderFxData *fx, Object *UNUSED(ob), gpIterVfx
grp = gpencil_vfx_pass_create("Fx Glow V", state, iter, sh);
DRW_shgroup_uniform_vec2_copy(grp, "offset", (float[2]){-fx->blur[1] * s, fx->blur[1] * c});
DRW_shgroup_uniform_int_copy(grp, "sampCount", max_ii(1, min_ii(fx->samples, fx->blur[0])));
- DRW_shgroup_uniform_vec3_copy(grp, "threshold", (float[3]){-1.0f, -1.0f, -1.0f});
+ DRW_shgroup_uniform_vec4_copy(grp, "threshold", (float[4]){-1.0f, -1.0f, -1.0f, -1.0});
DRW_shgroup_uniform_vec4_copy(grp, "glowColor", (float[4]){1.0f, 1.0f, 1.0f, fx->glow_color[3]});
DRW_shgroup_uniform_bool_copy(grp, "firstPass", false);
DRW_shgroup_uniform_int_copy(grp, "blendMode", fx->blend_mode);
diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl
index 7412959a30b..ac48b94fea9 100644
--- a/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl
+++ b/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl
@@ -442,6 +442,10 @@ void stroke_vertex()
if (is_dot) {
# ifdef GP_MATERIAL_BUFFER_LEN
int alignement = GP_FLAG(m) & GP_STROKE_ALIGNMENT;
+ /* For one point strokes use object aligment. */
+ if (ma.x == -1 && ma2.x == -1 && alignement == GP_STROKE_ALIGNMENT_STROKE) {
+ alignement = GP_STROKE_ALIGNMENT_OBJECT;
+ }
# endif
vec2 x_axis;
diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_vfx_frag.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_vfx_frag.glsl
index bb905f8694b..269ed49c4d0 100644
--- a/source/blender/draw/engines/gpencil/shaders/gpencil_vfx_frag.glsl
+++ b/source/blender/draw/engines/gpencil/shaders/gpencil_vfx_frag.glsl
@@ -145,7 +145,7 @@ void main()
uniform vec4 glowColor;
uniform vec2 offset;
uniform int sampCount;
-uniform vec3 threshold;
+uniform vec4 threshold;
uniform bool firstPass;
uniform bool glowUnder;
uniform int blendMode;
@@ -168,7 +168,7 @@ void main()
vec3 rev = texture(revealBuf, uv).rgb;
if (threshold.x > -1.0) {
if (threshold.y > -1.0) {
- if (any(greaterThan(abs(col - threshold), vec3(0.05)))) {
+ if (any(greaterThan(abs(col - vec3(threshold)), vec3(threshold.w)))) {
weight = 0.0;
}
}
diff --git a/source/blender/draw/engines/image/image_engine.c b/source/blender/draw/engines/image/image_engine.c
index d75f887ce2b..2522a445d8e 100644
--- a/source/blender/draw/engines/image/image_engine.c
+++ b/source/blender/draw/engines/image/image_engine.c
@@ -107,7 +107,7 @@ static void space_image_gpu_texture_get(Image *image,
const DRWContextState *draw_ctx = DRW_context_state_get();
SpaceImage *sima = (SpaceImage *)draw_ctx->space_data;
if (BKE_image_is_multilayer(image)) {
- /* update multiindex and pass for the current eye */
+ /* Update multi-index and pass for the current eye. */
BKE_image_multilayer_index(image->rr, &sima->iuser);
}
BKE_image_multiview_index(image, &sima->iuser);
diff --git a/source/blender/draw/engines/overlay/overlay_armature.c b/source/blender/draw/engines/overlay/overlay_armature.c
index 7042d095b56..2f9ececc198 100644
--- a/source/blender/draw/engines/overlay/overlay_armature.c
+++ b/source/blender/draw/engines/overlay/overlay_armature.c
@@ -1031,7 +1031,7 @@ static void pchan_draw_data_init(bPoseChannel *pchan)
static void draw_bone_update_disp_matrix_default(EditBone *eBone, bPoseChannel *pchan)
{
float ebmat[4][4];
- float length;
+ float bone_scale[3];
float(*bone_mat)[4];
float(*disp_mat)[4];
float(*disp_tail_mat)[4];
@@ -1040,23 +1040,23 @@ static void draw_bone_update_disp_matrix_default(EditBone *eBone, bPoseChannel *
* and not be tight to the draw pass creation.
* This would refresh armature without invalidating the draw cache */
if (pchan) {
- length = pchan->bone->length;
bone_mat = pchan->pose_mat;
disp_mat = pchan->disp_mat;
disp_tail_mat = pchan->disp_tail_mat;
+ copy_v3_fl(bone_scale, pchan->bone->length);
}
else {
eBone->length = len_v3v3(eBone->tail, eBone->head);
ED_armature_ebone_to_mat4(eBone, ebmat);
- length = eBone->length;
+ copy_v3_fl(bone_scale, eBone->length);
bone_mat = ebmat;
disp_mat = eBone->disp_mat;
disp_tail_mat = eBone->disp_tail_mat;
}
copy_m4_m4(disp_mat, bone_mat);
- rescale_m4(disp_mat, (float[3]){length, length, length});
+ rescale_m4(disp_mat, bone_scale);
copy_m4_m4(disp_tail_mat, disp_mat);
translate_m4(disp_tail_mat, 0.0f, 1.0f, 0.0f);
}
@@ -1255,19 +1255,27 @@ static void draw_bone_update_disp_matrix_bbone(EditBone *eBone, bPoseChannel *pc
static void draw_bone_update_disp_matrix_custom(bPoseChannel *pchan)
{
- float length;
+ float bone_scale[3];
float(*bone_mat)[4];
float(*disp_mat)[4];
float(*disp_tail_mat)[4];
+ float rot_mat[3][3];
/* See TODO above */
- length = PCHAN_CUSTOM_DRAW_SIZE(pchan);
+ mul_v3_v3fl(bone_scale, pchan->custom_scale_xyz, PCHAN_CUSTOM_BONE_LENGTH(pchan));
bone_mat = pchan->custom_tx ? pchan->custom_tx->pose_mat : pchan->pose_mat;
disp_mat = pchan->disp_mat;
disp_tail_mat = pchan->disp_tail_mat;
+ eulO_to_mat3(rot_mat, pchan->custom_rotation_euler, ROT_MODE_XYZ);
+
copy_m4_m4(disp_mat, bone_mat);
- rescale_m4(disp_mat, (float[3]){length, length, length});
+ translate_m4(disp_mat,
+ pchan->custom_translation[0],
+ pchan->custom_translation[1],
+ pchan->custom_translation[2]);
+ mul_m4_m4m3(disp_mat, disp_mat, rot_mat);
+ rescale_m4(disp_mat, bone_scale);
copy_m4_m4(disp_tail_mat, disp_mat);
translate_m4(disp_tail_mat, 0.0f, 1.0f, 0.0f);
}
@@ -1293,11 +1301,15 @@ static void draw_axes(ArmatureDrawContext *ctx,
float length = pchan->bone->length;
copy_m4_m4(axis_mat, pchan->custom_tx ? pchan->custom_tx->pose_mat : pchan->pose_mat);
rescale_m4(axis_mat, (float[3]){length, length, length});
+ translate_m4(axis_mat, 0.0, arm->axes_position - 1.0, 0.0);
drw_shgroup_bone_axes(ctx, axis_mat, final_col);
}
else {
- drw_shgroup_bone_axes(ctx, BONE_VAR(eBone, pchan, disp_mat), final_col);
+ float disp_mat[4][4];
+ copy_m4_m4(disp_mat, BONE_VAR(eBone, pchan, disp_mat));
+ translate_m4(disp_mat, 0.0, arm->axes_position - 1.0, 0.0);
+ drw_shgroup_bone_axes(ctx, disp_mat, final_col);
}
}
@@ -2051,9 +2063,8 @@ static void draw_armature_pose(ArmatureDrawContext *ctx)
set_pchan_colorset(ctx, ob, pchan);
}
- int boneflag = bone->flag;
/* catch exception for bone with hidden parent */
- boneflag = bone->flag;
+ int boneflag = bone->flag;
if ((bone->parent) && (bone->parent->flag & (BONE_HIDDEN_P | BONE_HIDDEN_PG))) {
boneflag &= ~BONE_CONNECTED;
}
diff --git a/source/blender/draw/engines/overlay/overlay_edit_mesh.c b/source/blender/draw/engines/overlay/overlay_edit_mesh.c
index 40a9dbe01c0..7639911286f 100644
--- a/source/blender/draw/engines/overlay/overlay_edit_mesh.c
+++ b/source/blender/draw/engines/overlay/overlay_edit_mesh.c
@@ -149,7 +149,7 @@ void OVERLAY_edit_mesh_cache_init(OVERLAY_Data *vedata)
DRWState state_common = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS_EQUAL |
DRW_STATE_BLEND_ALPHA;
/* Faces */
- /* Cage geom needs to be offsetted to avoid Z-fighting. */
+ /* Cage geom needs an offset applied to avoid Z-fighting. */
for (int j = 0; j < 2; j++) {
DRWPass **edit_face_ps = (j == 0) ? &psl->edit_mesh_faces_ps[i] :
&psl->edit_mesh_faces_cage_ps[i];
@@ -197,7 +197,7 @@ void OVERLAY_edit_mesh_cache_init(OVERLAY_Data *vedata)
grp = pd->edit_mesh_skin_roots_grp[i] = DRW_shgroup_create(sh, psl->edit_mesh_verts_ps[i]);
DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo);
}
- /* Facedots */
+ /* Face-dots */
if (select_face && show_face_dots) {
sh = OVERLAY_shader_edit_mesh_facedot();
grp = pd->edit_mesh_facedots_grp[i] = DRW_shgroup_create(sh, psl->edit_mesh_verts_ps[i]);
diff --git a/source/blender/draw/engines/overlay/overlay_engine.c b/source/blender/draw/engines/overlay/overlay_engine.c
index e9736402ae7..81b07b49784 100644
--- a/source/blender/draw/engines/overlay/overlay_engine.c
+++ b/source/blender/draw/engines/overlay/overlay_engine.c
@@ -207,6 +207,7 @@ static void OVERLAY_cache_init(void *vedata)
OVERLAY_armature_cache_init(vedata);
OVERLAY_background_cache_init(vedata);
OVERLAY_fade_cache_init(vedata);
+ OVERLAY_mode_transfer_cache_init(vedata);
OVERLAY_extra_cache_init(vedata);
OVERLAY_facing_cache_init(vedata);
OVERLAY_gpencil_cache_init(vedata);
@@ -285,10 +286,6 @@ static bool overlay_should_fade_object(Object *ob, Object *active_object)
return false;
}
- if (ob->base_flag & BASE_FROM_DUPLI) {
- return false;
- }
-
return true;
}
@@ -327,6 +324,7 @@ static void OVERLAY_cache_populate(void *vedata, Object *ob)
!is_select;
const bool draw_fade = draw_surface && (pd->overlay.flag & V3D_OVERLAY_FADE_INACTIVE) &&
overlay_should_fade_object(ob, draw_ctx->obact);
+ const bool draw_mode_transfer = draw_surface;
const bool draw_bones = (pd->overlay.flag & V3D_OVERLAY_HIDE_BONES) == 0;
const bool draw_wires = draw_surface && has_surface &&
(pd->wireframe_mode || !pd->hide_overlays);
@@ -353,6 +351,9 @@ static void OVERLAY_cache_populate(void *vedata, Object *ob)
if (draw_facing) {
OVERLAY_facing_cache_populate(vedata, ob);
}
+ if (draw_mode_transfer) {
+ OVERLAY_mode_transfer_cache_populate(vedata, ob);
+ }
if (draw_wires) {
OVERLAY_wireframe_cache_populate(vedata, ob, dupli, do_init);
}
@@ -508,6 +509,7 @@ static void OVERLAY_cache_finish(void *vedata)
{GPU_ATTACHMENT_TEXTURE(dtxl->depth_in_front), GPU_ATTACHMENT_TEXTURE(dtxl->color)});
}
+ OVERLAY_mode_transfer_cache_finish(vedata);
OVERLAY_antialiasing_cache_finish(vedata);
OVERLAY_armature_cache_finish(vedata);
OVERLAY_image_cache_finish(vedata);
@@ -570,6 +572,7 @@ static void OVERLAY_draw_scene(void *vedata)
OVERLAY_image_draw(vedata);
OVERLAY_fade_draw(vedata);
OVERLAY_facing_draw(vedata);
+ OVERLAY_mode_transfer_draw(vedata);
OVERLAY_extra_blend_draw(vedata);
OVERLAY_volume_draw(vedata);
@@ -609,6 +612,7 @@ static void OVERLAY_draw_scene(void *vedata)
OVERLAY_fade_infront_draw(vedata);
OVERLAY_facing_infront_draw(vedata);
+ OVERLAY_mode_transfer_infront_draw(vedata);
if (DRW_state_is_fbo()) {
GPU_framebuffer_bind(fbl->overlay_line_in_front_fb);
diff --git a/source/blender/draw/engines/overlay/overlay_extra.c b/source/blender/draw/engines/overlay/overlay_extra.c
index 00429a19cf4..2e1c0165513 100644
--- a/source/blender/draw/engines/overlay/overlay_extra.c
+++ b/source/blender/draw/engines/overlay/overlay_extra.c
@@ -542,16 +542,15 @@ static void OVERLAY_forcefield(OVERLAY_ExtraCallBuffers *cb, Object *ob, ViewLay
DRW_buffer_add_entry(cb->field_vortex, color, &instdata);
break;
case PFIELD_GUIDE:
- if (cu && (cu->flag & CU_PATH) && ob->runtime.curve_cache->path &&
- ob->runtime.curve_cache->path->data) {
+ if (cu && (cu->flag & CU_PATH) && ob->runtime.curve_cache->anim_path_accum_length) {
instdata.size_x = instdata.size_y = instdata.size_z = pd->f_strength;
float pos[4], tmp[3];
- where_on_path(ob, 0.0f, pos, tmp, NULL, NULL, NULL);
+ BKE_where_on_path(ob, 0.0f, pos, tmp, NULL, NULL, NULL);
copy_v3_v3(instdata.pos, ob->obmat[3]);
translate_m4(instdata.mat, pos[0], pos[1], pos[2]);
DRW_buffer_add_entry(cb->field_curve, color, &instdata);
- where_on_path(ob, 1.0f, pos, tmp, NULL, NULL, NULL);
+ BKE_where_on_path(ob, 1.0f, pos, tmp, NULL, NULL, NULL);
copy_v3_v3(instdata.pos, ob->obmat[3]);
translate_m4(instdata.mat, pos[0], pos[1], pos[2]);
DRW_buffer_add_entry(cb->field_sphere_limit, color, &instdata);
diff --git a/source/blender/draw/engines/overlay/overlay_gpencil.c b/source/blender/draw/engines/overlay/overlay_gpencil.c
index 891142fe0a2..aa26aa47faa 100644
--- a/source/blender/draw/engines/overlay/overlay_gpencil.c
+++ b/source/blender/draw/engines/overlay/overlay_gpencil.c
@@ -382,7 +382,7 @@ static void overlay_gpencil_draw_stroke_color_name(bGPDlayer *UNUSED(gpl),
void *thunk)
{
Object *ob = (Object *)thunk;
- Material *ma = BKE_object_material_get(ob, gps->mat_nr + 1);
+ Material *ma = BKE_object_material_get_eval(ob, gps->mat_nr + 1);
if (ma == NULL) {
return;
}
diff --git a/source/blender/draw/engines/overlay/overlay_mode_transfer.c b/source/blender/draw/engines/overlay/overlay_mode_transfer.c
new file mode 100644
index 00000000000..253f606b086
--- /dev/null
+++ b/source/blender/draw/engines/overlay/overlay_mode_transfer.c
@@ -0,0 +1,159 @@
+/*
+ * 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.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+/** \file
+ * \ingroup draw_engine
+ */
+
+#include "BKE_paint.h"
+#include "DRW_render.h"
+
+#include "ED_view3d.h"
+
+#include "PIL_time.h"
+#include "UI_resources.h"
+
+#include "overlay_private.h"
+
+void OVERLAY_mode_transfer_cache_init(OVERLAY_Data *vedata)
+{
+ OVERLAY_PassList *psl = vedata->psl;
+ OVERLAY_PrivateData *pd = vedata->stl->pd;
+
+ pd->mode_transfer.time = PIL_check_seconds_timer();
+
+ for (int i = 0; i < 2; i++) {
+ /* Non Meshes Pass (Camera, empties, lights ...) */
+ DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL | DRW_STATE_BLEND_ALPHA;
+ DRW_PASS_CREATE(psl->mode_transfer_ps[i], state | pd->clipping_state);
+ }
+}
+
+#define MODE_TRANSFER_FLASH_LENGTH 0.55f
+/* TODO(pablodp606): Remove this option for 3.0 if fade in/out is not used. */
+#define MODE_TRANSFER_FLASH_FADE 0.0f
+#define MODE_TRANSFER_FLASH_MAX_ALPHA 0.25f
+
+static bool mode_transfer_is_animation_running(const float anim_time)
+{
+ return anim_time >= 0.0f && anim_time <= MODE_TRANSFER_FLASH_LENGTH;
+}
+
+static float mode_transfer_alpha_for_animation_time_get(const float anim_time)
+{
+ if (anim_time > MODE_TRANSFER_FLASH_LENGTH) {
+ return 0.0f;
+ }
+
+ if (anim_time < 0.0f) {
+ return 0.0f;
+ }
+
+ if (MODE_TRANSFER_FLASH_FADE <= 0.0f) {
+ return (1.0f - (anim_time / MODE_TRANSFER_FLASH_LENGTH)) * MODE_TRANSFER_FLASH_MAX_ALPHA;
+ }
+
+ const float flash_fade_in_time = MODE_TRANSFER_FLASH_LENGTH * MODE_TRANSFER_FLASH_FADE;
+ const float flash_fade_out_time = MODE_TRANSFER_FLASH_LENGTH - flash_fade_in_time;
+
+ float alpha = 0.0f;
+ if (anim_time < flash_fade_in_time) {
+ alpha = anim_time / flash_fade_in_time;
+ }
+ else {
+ const float fade_out_anim_time = anim_time - flash_fade_in_time;
+ alpha = 1.0f - (fade_out_anim_time / flash_fade_out_time);
+ }
+
+ return alpha * MODE_TRANSFER_FLASH_MAX_ALPHA;
+}
+
+void OVERLAY_mode_transfer_cache_populate(OVERLAY_Data *vedata, Object *ob)
+{
+ OVERLAY_PrivateData *pd = vedata->stl->pd;
+ OVERLAY_PassList *psl = vedata->psl;
+
+ if (pd->xray_enabled) {
+ return;
+ }
+
+ const float animation_time = pd->mode_transfer.time -
+ ob->runtime.overlay_mode_transfer_start_time;
+
+ if (!mode_transfer_is_animation_running(animation_time)) {
+ return;
+ }
+
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ const bool use_sculpt_pbvh = BKE_sculptsession_use_pbvh_draw(ob, draw_ctx->v3d) &&
+ !DRW_state_is_image_render();
+ const bool is_xray = (ob->dtx & OB_DRAW_IN_FRONT) != 0;
+
+ DRWShadingGroup *mode_transfer_grp[2];
+
+ for (int i = 0; i < 2; i++) {
+ GPUShader *sh = OVERLAY_shader_uniform_color();
+ mode_transfer_grp[i] = DRW_shgroup_create(sh, psl->mode_transfer_ps[i]);
+ DRW_shgroup_uniform_block(mode_transfer_grp[i], "globalsBlock", G_draw.block_ubo);
+
+ float color[4];
+ UI_GetThemeColor3fv(TH_VERTEX_SELECT, color);
+ color[3] = mode_transfer_alpha_for_animation_time_get(animation_time);
+ srgb_to_linearrgb_v4(color, color);
+ DRW_shgroup_uniform_vec4_copy(mode_transfer_grp[i], "color", color);
+ }
+
+ if (!pd->use_in_front) {
+ mode_transfer_grp[IN_FRONT] = mode_transfer_grp[NOT_IN_FRONT];
+ }
+
+ pd->mode_transfer.any_animated = true;
+
+ if (use_sculpt_pbvh) {
+ DRW_shgroup_call_sculpt(mode_transfer_grp[is_xray], ob, false, false);
+ }
+ else {
+ struct GPUBatch *geom = DRW_cache_object_surface_get(ob);
+ if (geom) {
+ DRW_shgroup_call(mode_transfer_grp[is_xray], geom, ob);
+ }
+ }
+}
+
+void OVERLAY_mode_transfer_draw(OVERLAY_Data *vedata)
+{
+ OVERLAY_PassList *psl = vedata->psl;
+
+ DRW_draw_pass(psl->mode_transfer_ps[NOT_IN_FRONT]);
+}
+
+void OVERLAY_mode_transfer_infront_draw(OVERLAY_Data *vedata)
+{
+ OVERLAY_PassList *psl = vedata->psl;
+
+ DRW_draw_pass(psl->mode_transfer_ps[IN_FRONT]);
+}
+
+void OVERLAY_mode_transfer_cache_finish(OVERLAY_Data *vedata)
+{
+ OVERLAY_PrivateData *pd = vedata->stl->pd;
+ if (pd->mode_transfer.any_animated) {
+ DRW_viewport_request_redraw();
+ }
+ pd->mode_transfer.any_animated = false;
+}
diff --git a/source/blender/draw/engines/overlay/overlay_motion_path.c b/source/blender/draw/engines/overlay/overlay_motion_path.c
index 48b7b53a5ba..a92f11aca38 100644
--- a/source/blender/draw/engines/overlay/overlay_motion_path.c
+++ b/source/blender/draw/engines/overlay/overlay_motion_path.c
@@ -190,7 +190,7 @@ static void motion_path_cache(OVERLAY_Data *vedata,
bool is_keyframe = (mpv->flag & MOTIONPATH_VERT_KEY) != 0;
if ((show_keyframes && show_keyframes_no && is_keyframe) || (show_frame_no && (i == 0))) {
- numstr_len = BLI_snprintf(numstr, sizeof(numstr), " %d", frame);
+ numstr_len = BLI_snprintf_rlen(numstr, sizeof(numstr), " %d", frame);
DRW_text_cache_add(
dt, mpv->co, numstr, numstr_len, 0, 0, txt_flag, (is_keyframe) ? col_kf : col);
}
@@ -200,7 +200,7 @@ static void motion_path_cache(OVERLAY_Data *vedata,
/* Only draw frame number if several consecutive highlighted points
* don't occur on same point. */
if ((equals_v3v3(mpv->co, mpvP->co) == 0) || (equals_v3v3(mpv->co, mpvN->co) == 0)) {
- numstr_len = BLI_snprintf(numstr, sizeof(numstr), " %d", frame);
+ numstr_len = BLI_snprintf_rlen(numstr, sizeof(numstr), " %d", frame);
DRW_text_cache_add(dt, mpv->co, numstr, numstr_len, 0, 0, txt_flag, col);
}
}
diff --git a/source/blender/draw/engines/overlay/overlay_paint.c b/source/blender/draw/engines/overlay/overlay_paint.c
index 89e724bcfcc..d52640ed174 100644
--- a/source/blender/draw/engines/overlay/overlay_paint.c
+++ b/source/blender/draw/engines/overlay/overlay_paint.c
@@ -48,7 +48,7 @@ static bool paint_object_is_rendered_transparent(View3D *v3d, Object *ob)
v3d->shading.color_type == V3D_SHADING_MATERIAL_COLOR) {
Mesh *me = ob->data;
for (int i = 0; i < me->totcol; i++) {
- Material *mat = me->mat[i];
+ Material *mat = BKE_object_material_get_eval(ob, i + 1);
if (mat && mat->a < 1.0f) {
return true;
}
diff --git a/source/blender/draw/engines/overlay/overlay_particle.c b/source/blender/draw/engines/overlay/overlay_particle.c
index 71064e7ff47..5fa74a8c3a6 100644
--- a/source/blender/draw/engines/overlay/overlay_particle.c
+++ b/source/blender/draw/engines/overlay/overlay_particle.c
@@ -186,7 +186,7 @@ void OVERLAY_particle_cache_populate(OVERLAY_Data *vedata, Object *ob)
/* TODO(fclem): Here would be a good place for preemptive culling. */
/* NOTE(fclem): Is color even useful in our modern context? */
- Material *ma = BKE_object_material_get(ob, part->omat);
+ Material *ma = BKE_object_material_get_eval(ob, part->omat);
float color[4] = {0.6f, 0.6f, 0.6f, part->draw_size};
if (ma != NULL) {
copy_v3_v3(color, &ma->r);
diff --git a/source/blender/draw/engines/overlay/overlay_private.h b/source/blender/draw/engines/overlay/overlay_private.h
index db43136e308..969289a3219 100644
--- a/source/blender/draw/engines/overlay/overlay_private.h
+++ b/source/blender/draw/engines/overlay/overlay_private.h
@@ -107,6 +107,7 @@ typedef struct OVERLAY_PassList {
DRWPass *gpencil_canvas_ps;
DRWPass *facing_ps[2];
DRWPass *fade_ps[2];
+ DRWPass *mode_transfer_ps[2];
DRWPass *grid_ps;
DRWPass *image_background_ps;
DRWPass *image_background_scene_ps;
@@ -282,6 +283,7 @@ typedef struct OVERLAY_PrivateData {
DRWShadingGroup *extra_grid_grp;
DRWShadingGroup *facing_grp[2];
DRWShadingGroup *fade_grp[2];
+ DRWShadingGroup *flash_grp[2];
DRWShadingGroup *motion_path_lines_grp;
DRWShadingGroup *motion_path_points_grp;
DRWShadingGroup *outlines_grp;
@@ -414,6 +416,10 @@ typedef struct OVERLAY_PrivateData {
struct {
DRWCallBuffer *handle[2];
} mball;
+ struct {
+ double time;
+ bool any_animated;
+ } mode_transfer;
} OVERLAY_PrivateData; /* Transient data */
typedef struct OVERLAY_StorageList {
@@ -607,6 +613,12 @@ void OVERLAY_fade_cache_populate(OVERLAY_Data *vedata, Object *ob);
void OVERLAY_fade_draw(OVERLAY_Data *vedata);
void OVERLAY_fade_infront_draw(OVERLAY_Data *vedata);
+void OVERLAY_mode_transfer_cache_init(OVERLAY_Data *vedata);
+void OVERLAY_mode_transfer_cache_populate(OVERLAY_Data *vedata, Object *ob);
+void OVERLAY_mode_transfer_draw(OVERLAY_Data *vedata);
+void OVERLAY_mode_transfer_infront_draw(OVERLAY_Data *vedata);
+void OVERLAY_mode_transfer_cache_finish(OVERLAY_Data *vedata);
+
void OVERLAY_grid_init(OVERLAY_Data *vedata);
void OVERLAY_grid_cache_init(OVERLAY_Data *vedata);
void OVERLAY_grid_draw(OVERLAY_Data *vedata);
diff --git a/source/blender/draw/engines/overlay/overlay_wireframe.c b/source/blender/draw/engines/overlay/overlay_wireframe.c
index 7ba5fb3a426..b8a61ecc403 100644
--- a/source/blender/draw/engines/overlay/overlay_wireframe.c
+++ b/source/blender/draw/engines/overlay/overlay_wireframe.c
@@ -177,8 +177,23 @@ void OVERLAY_wireframe_cache_populate(OVERLAY_Data *vedata,
const bool all_wires = (ob->dtx & OB_DRAW_ALL_EDGES) != 0;
const bool is_xray = (ob->dtx & OB_DRAW_IN_FRONT) != 0;
const bool is_mesh = ob->type == OB_MESH;
- const bool is_mesh_verts_only = is_mesh && (((Mesh *)ob->data)->totedge == 0 &&
- ((Mesh *)ob->data)->totvert > 0);
+ const bool is_edit_mode = DRW_object_is_in_edit_mode(ob);
+ bool has_edit_mesh_cage = false;
+ bool is_mesh_verts_only = false;
+ if (is_mesh) {
+ /* TODO: Should be its own function. */
+ Mesh *me = ob->data;
+ if (is_edit_mode) {
+ BLI_assert(me->edit_mesh);
+ BMEditMesh *embm = me->edit_mesh;
+ has_edit_mesh_cage = embm->mesh_eval_cage && (embm->mesh_eval_cage != embm->mesh_eval_final);
+ if (embm->mesh_eval_final) {
+ me = embm->mesh_eval_final;
+ }
+ }
+ is_mesh_verts_only = me->totedge == 0 && me->totvert > 0;
+ }
+
const bool use_wire = !is_mesh_verts_only && ((pd->overlay.flag & V3D_OVERLAY_WIREFRAMES) ||
(ob->dtx & OB_DRAWWIRE) || (ob->dt == OB_WIRE));
@@ -261,17 +276,6 @@ void OVERLAY_wireframe_cache_populate(OVERLAY_Data *vedata,
}
}
- const bool is_edit_mode = DRW_object_is_in_edit_mode(ob);
- bool has_edit_mesh_cage = false;
- if (is_mesh && is_edit_mode) {
- /* TODO: Should be its own function. */
- Mesh *me = (Mesh *)ob->data;
- BMEditMesh *embm = me->edit_mesh;
- if (embm) {
- has_edit_mesh_cage = embm->mesh_eval_cage && (embm->mesh_eval_cage != embm->mesh_eval_final);
- }
- }
-
/* Don't do that in edit Mesh mode, unless there is a modifier preview. */
if (use_wire && (!is_mesh || (!is_edit_mode || has_edit_mesh_cage))) {
const bool is_sculpt_mode = ((ob->mode & OB_MODE_SCULPT) != 0) && (ob->sculpt != NULL);
diff --git a/source/blender/draw/engines/workbench/workbench_materials.c b/source/blender/draw/engines/workbench/workbench_materials.c
index 6e9118bfe46..800d1085505 100644
--- a/source/blender/draw/engines/workbench/workbench_materials.c
+++ b/source/blender/draw/engines/workbench/workbench_materials.c
@@ -93,7 +93,7 @@ void workbench_material_ubo_data(WORKBENCH_PrivateData *wpd,
/* Return correct material or empty default material if slot is empty. */
BLI_INLINE Material *workbench_object_material_get(Object *ob, int mat_nr)
{
- Material *ma = BKE_object_material_get(ob, mat_nr);
+ Material *ma = BKE_object_material_get_eval(ob, mat_nr);
if (ma == NULL) {
ma = BKE_material_default_empty();
}
diff --git a/source/blender/draw/engines/workbench/workbench_volume.c b/source/blender/draw/engines/workbench/workbench_volume.c
index c76f4a4c470..ddda6d7b58e 100644
--- a/source/blender/draw/engines/workbench/workbench_volume.c
+++ b/source/blender/draw/engines/workbench/workbench_volume.c
@@ -202,7 +202,7 @@ static void workbench_volume_material_color(WORKBENCH_PrivateData *wpd,
eV3DShadingColorType color_type,
float color[3])
{
- Material *ma = BKE_object_material_get(ob, VOLUME_MATERIAL_NR);
+ Material *ma = BKE_object_material_get_eval(ob, VOLUME_MATERIAL_NR);
WORKBENCH_UBO_Material ubo_data;
workbench_material_ubo_data(wpd, ob, ma, &ubo_data, color_type);
copy_v3_v3(color, ubo_data.base_color);
@@ -215,7 +215,7 @@ static void workbench_volume_object_cache_populate(WORKBENCH_Data *vedata,
/* Create 3D textures. */
Volume *volume = ob->data;
BKE_volume_load(volume, G.main);
- VolumeGrid *volume_grid = BKE_volume_grid_active_get(volume);
+ const VolumeGrid *volume_grid = BKE_volume_grid_active_get_for_read(volume);
if (volume_grid == NULL) {
return;
}
diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h
index 2545cfa65dc..5071658fd82 100644
--- a/source/blender/draw/intern/DRW_render.h
+++ b/source/blender/draw/intern/DRW_render.h
@@ -438,6 +438,10 @@ void DRW_shgroup_call_range(
void DRW_shgroup_call_instance_range(
DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, uint i_sta, uint i_ct);
+void DRW_shgroup_call_compute(DRWShadingGroup *shgroup,
+ int groups_x_len,
+ int groups_y_len,
+ int groups_z_len);
void DRW_shgroup_call_procedural_points(DRWShadingGroup *sh, Object *ob, uint point_count);
void DRW_shgroup_call_procedural_lines(DRWShadingGroup *sh, Object *ob, uint line_count);
void DRW_shgroup_call_procedural_triangles(DRWShadingGroup *sh, Object *ob, uint tri_count);
@@ -575,6 +579,9 @@ void DRW_shgroup_uniform_vec4_array_copy(DRWShadingGroup *shgroup,
const char *name,
const float (*value)[4],
int arraysize);
+void DRW_shgroup_vertex_buffer(DRWShadingGroup *shgroup,
+ const char *name,
+ struct GPUVertBuf *vertex_buffer);
bool DRW_shgroup_is_empty(DRWShadingGroup *shgroup);
diff --git a/source/blender/draw/intern/draw_cache.h b/source/blender/draw/intern/draw_cache.h
index c15c246992d..c58dd85b6ed 100644
--- a/source/blender/draw/intern/draw_cache.h
+++ b/source/blender/draw/intern/draw_cache.h
@@ -244,7 +244,8 @@ typedef struct DRWVolumeGrid {
float bounds_to_texture[4][4];
} DRWVolumeGrid;
-DRWVolumeGrid *DRW_volume_batch_cache_get_grid(struct Volume *volume, struct VolumeGrid *grid);
+DRWVolumeGrid *DRW_volume_batch_cache_get_grid(struct Volume *volume,
+ const struct VolumeGrid *grid);
struct GPUBatch *DRW_cache_volume_face_wireframe_get(struct Object *ob);
struct GPUBatch *DRW_cache_volume_selection_surface_get(struct Object *ob);
diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h
index c929fe7dfd3..304cae1d2dc 100644
--- a/source/blender/draw/intern/draw_cache_extract.h
+++ b/source/blender/draw/intern/draw_cache_extract.h
@@ -24,6 +24,10 @@
struct TaskGraph;
+#include "GPU_batch.h"
+#include "GPU_index_buffer.h"
+#include "GPU_vertex_buffer.h"
+
/* Vertex Group Selection and display options */
typedef struct DRW_MeshWeightState {
int defgroup_active;
@@ -64,30 +68,34 @@ typedef struct DRW_MeshCDMask {
* bit-wise and atomic operations are used to compare and update the struct.
* See `mesh_cd_layers_type_*` functions. */
BLI_STATIC_ASSERT(sizeof(DRW_MeshCDMask) <= sizeof(uint64_t), "DRW_MeshCDMask exceeds 64 bits")
-
typedef enum eMRIterType {
MR_ITER_LOOPTRI = 1 << 0,
MR_ITER_POLY = 1 << 1,
MR_ITER_LEDGE = 1 << 2,
MR_ITER_LVERT = 1 << 3,
} eMRIterType;
+ENUM_OPERATORS(eMRIterType, MR_ITER_LVERT)
typedef enum eMRDataType {
+ MR_DATA_NONE = 0,
MR_DATA_POLY_NOR = 1 << 1,
MR_DATA_LOOP_NOR = 1 << 2,
MR_DATA_LOOPTRI = 1 << 3,
/** Force loop normals calculation. */
MR_DATA_TAN_LOOP_NOR = 1 << 4,
} eMRDataType;
+ENUM_OPERATORS(eMRDataType, MR_DATA_TAN_LOOP_NOR)
-typedef enum eMRExtractType {
- MR_EXTRACT_BMESH,
- MR_EXTRACT_MAPPED,
- MR_EXTRACT_MESH,
-} eMRExtractType;
+#ifdef __cplusplus
+extern "C" {
+#endif
-BLI_INLINE int mesh_render_mat_len_get(Mesh *me)
+BLI_INLINE int mesh_render_mat_len_get(const Mesh *me)
{
+ /* In edit mode, the displayed mesh is stored in the edit-mesh. */
+ if (me->edit_mesh && me->edit_mesh->mesh_eval_final) {
+ return MAX2(1, me->edit_mesh->mesh_eval_final->totcol);
+ }
return MAX2(1, me->totcol);
}
@@ -146,6 +154,18 @@ typedef struct MeshBufferCache {
GPUIndexBuf **tris_per_mat;
} MeshBufferCache;
+/**
+ * Data that are kept around between extractions to reduce rebuilding time.
+ *
+ * - Loose geometry.
+ */
+typedef struct MeshBufferExtractionCache {
+ int edge_loose_len;
+ int vert_loose_len;
+ int *lverts;
+ int *ledges;
+} MeshBufferExtractionCache;
+
typedef enum DRWBatchFlag {
MBC_SURFACE = (1 << 0),
MBC_SURFACE_WEIGHTS = (1 << 1),
@@ -191,6 +211,10 @@ typedef enum DRWBatchFlag {
typedef struct MeshBatchCache {
MeshBufferCache final, cage, uv_cage;
+ MeshBufferExtractionCache final_extraction_cache;
+ MeshBufferExtractionCache cage_extraction_cache;
+ MeshBufferExtractionCache uv_cage_extraction_cache;
+
struct {
/* Surfaces / Render */
GPUBatch *surface;
@@ -260,9 +284,14 @@ typedef struct MeshBatchCache {
bool no_loose_wire;
} MeshBatchCache;
+#define MBC_BATCH_LEN (sizeof(((MeshBatchCache){0}).batch) / sizeof(void *))
+#define MBC_VBO_LEN (sizeof(((MeshBufferCache){0}).vbo) / sizeof(void *))
+#define MBC_IBO_LEN (sizeof(((MeshBufferCache){0}).ibo) / sizeof(void *))
+
void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph,
MeshBatchCache *cache,
- MeshBufferCache mbc,
+ MeshBufferCache *mbc,
+ MeshBufferExtractionCache *extraction_cache,
Mesh *me,
const bool is_editmode,
const bool is_paint_mode,
@@ -271,7 +300,10 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph,
const bool do_final,
const bool do_uvedit,
const bool use_subsurf_fdots,
- const DRW_MeshCDMask *cd_layer_used,
const Scene *scene,
const ToolSettings *ts,
const bool use_hide);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.c b/source/blender/draw/intern/draw_cache_extract_mesh.c
deleted file mode 100644
index f167ea3d540..00000000000
--- a/source/blender/draw/intern/draw_cache_extract_mesh.c
+++ /dev/null
@@ -1,6165 +0,0 @@
-/*
- * 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.
- *
- * The Original Code is Copyright (C) 2017 by Blender Foundation.
- * All rights reserved.
- */
-
-/** \file
- * \ingroup draw
- *
- * \brief Extraction of Mesh data into VBO to feed to GPU.
- */
-
-#include "MEM_guardedalloc.h"
-
-#include "BLI_alloca.h"
-#include "BLI_bitmap.h"
-#include "BLI_buffer.h"
-#include "BLI_edgehash.h"
-#include "BLI_jitter_2d.h"
-#include "BLI_math_bits.h"
-#include "BLI_math_vector.h"
-#include "BLI_string.h"
-#include "BLI_task.h"
-#include "BLI_utildefines.h"
-
-#include "DNA_mesh_types.h"
-#include "DNA_meshdata_types.h"
-#include "DNA_object_types.h"
-#include "DNA_scene_types.h"
-
-#include "BKE_bvhutils.h"
-#include "BKE_customdata.h"
-#include "BKE_deform.h"
-#include "BKE_editmesh.h"
-#include "BKE_editmesh_bvh.h"
-#include "BKE_editmesh_cache.h"
-#include "BKE_editmesh_tangent.h"
-#include "BKE_mesh.h"
-#include "BKE_mesh_runtime.h"
-#include "BKE_mesh_tangent.h"
-#include "BKE_modifier.h"
-#include "BKE_object_deform.h"
-#include "BKE_paint.h"
-
-#include "atomic_ops.h"
-
-#include "bmesh.h"
-
-#include "GPU_batch.h"
-#include "GPU_capabilities.h"
-
-#include "DRW_render.h"
-
-#include "ED_mesh.h"
-#include "ED_uvedit.h"
-
-#include "draw_cache_impl.h"
-#include "draw_cache_inline.h"
-
-#include "draw_cache_extract.h"
-
-// #define DEBUG_TIME
-
-#ifdef DEBUG_TIME
-# include "PIL_time_utildefines.h"
-#endif
-
-/* ---------------------------------------------------------------------- */
-/** \name Mesh/BMesh Interface (indirect, partially cached access to complex data).
- * \{ */
-
-typedef struct MeshRenderData {
- eMRExtractType extract_type;
-
- int poly_len, edge_len, vert_len, loop_len;
- int edge_loose_len;
- int vert_loose_len;
- int loop_loose_len;
- int tri_len;
- int mat_len;
-
- bool use_hide;
- bool use_subsurf_fdots;
- bool use_final_mesh;
-
- /** Use for #MeshStatVis calculation which use world-space coords. */
- float obmat[4][4];
-
- const ToolSettings *toolsettings;
- /** Edit Mesh */
- BMEditMesh *edit_bmesh;
- BMesh *bm;
- EditMeshData *edit_data;
-
- /* For deformed edit-mesh data. */
- /* Use for #ME_WRAPPER_TYPE_BMESH. */
- const float (*bm_vert_coords)[3];
- const float (*bm_vert_normals)[3];
- const float (*bm_poly_normals)[3];
- const float (*bm_poly_centers)[3];
-
- int *v_origindex, *e_origindex, *p_origindex;
- int crease_ofs;
- int bweight_ofs;
- int freestyle_edge_ofs;
- int freestyle_face_ofs;
- /** Mesh */
- Mesh *me;
- const MVert *mvert;
- const MEdge *medge;
- const MLoop *mloop;
- const MPoly *mpoly;
- BMVert *eve_act;
- BMEdge *eed_act;
- BMFace *efa_act;
- BMFace *efa_act_uv;
- /* Data created on-demand (usually not for #BMesh based data). */
- MLoopTri *mlooptri;
- float (*loop_normals)[3];
- float (*poly_normals)[3];
- int *lverts, *ledges;
-} MeshRenderData;
-
-static void mesh_render_data_update_loose_geom(MeshRenderData *mr,
- const eMRIterType iter_type,
- const eMRDataType UNUSED(data_flag))
-{
- if (mr->extract_type != MR_EXTRACT_BMESH) {
- /* Mesh */
- if (iter_type & (MR_ITER_LEDGE | MR_ITER_LVERT)) {
- mr->vert_loose_len = 0;
- mr->edge_loose_len = 0;
-
- BLI_bitmap *lvert_map = BLI_BITMAP_NEW(mr->vert_len, __func__);
-
- mr->ledges = MEM_mallocN(mr->edge_len * sizeof(int), __func__);
- const MEdge *med = mr->medge;
- for (int med_index = 0; med_index < mr->edge_len; med_index++, med++) {
- if (med->flag & ME_LOOSEEDGE) {
- mr->ledges[mr->edge_loose_len++] = med_index;
- }
- /* Tag verts as not loose. */
- BLI_BITMAP_ENABLE(lvert_map, med->v1);
- BLI_BITMAP_ENABLE(lvert_map, med->v2);
- }
- if (mr->edge_loose_len < mr->edge_len) {
- mr->ledges = MEM_reallocN(mr->ledges, mr->edge_loose_len * sizeof(*mr->ledges));
- }
-
- mr->lverts = MEM_mallocN(mr->vert_len * sizeof(*mr->lverts), __func__);
- for (int v = 0; v < mr->vert_len; v++) {
- if (!BLI_BITMAP_TEST(lvert_map, v)) {
- mr->lverts[mr->vert_loose_len++] = v;
- }
- }
- if (mr->vert_loose_len < mr->vert_len) {
- mr->lverts = MEM_reallocN(mr->lverts, mr->vert_loose_len * sizeof(*mr->lverts));
- }
-
- MEM_freeN(lvert_map);
-
- mr->loop_loose_len = mr->vert_loose_len + (mr->edge_loose_len * 2);
- }
- }
- else {
- /* #BMesh */
- BMesh *bm = mr->bm;
- if (iter_type & (MR_ITER_LEDGE | MR_ITER_LVERT)) {
- int elem_id;
- BMIter iter;
- BMVert *eve;
- BMEdge *ede;
- mr->vert_loose_len = 0;
- mr->edge_loose_len = 0;
-
- mr->lverts = MEM_mallocN(mr->vert_len * sizeof(*mr->lverts), __func__);
- BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, elem_id) {
- if (eve->e == NULL) {
- mr->lverts[mr->vert_loose_len++] = elem_id;
- }
- }
- if (mr->vert_loose_len < mr->vert_len) {
- mr->lverts = MEM_reallocN(mr->lverts, mr->vert_loose_len * sizeof(*mr->lverts));
- }
-
- mr->ledges = MEM_mallocN(mr->edge_len * sizeof(*mr->ledges), __func__);
- BM_ITER_MESH_INDEX (ede, &iter, bm, BM_EDGES_OF_MESH, elem_id) {
- if (ede->l == NULL) {
- mr->ledges[mr->edge_loose_len++] = elem_id;
- }
- }
- if (mr->edge_loose_len < mr->edge_len) {
- mr->ledges = MEM_reallocN(mr->ledges, mr->edge_loose_len * sizeof(*mr->ledges));
- }
-
- mr->loop_loose_len = mr->vert_loose_len + mr->edge_loose_len * 2;
- }
- }
-}
-
-/**
- * Part of the creation of the #MeshRenderData that happens in a thread.
- */
-static void mesh_render_data_update_looptris(MeshRenderData *mr,
- const eMRIterType iter_type,
- const eMRDataType data_flag)
-{
- Mesh *me = mr->me;
- if (mr->extract_type != MR_EXTRACT_BMESH) {
- /* Mesh */
- if ((iter_type & MR_ITER_LOOPTRI) || (data_flag & MR_DATA_LOOPTRI)) {
- mr->mlooptri = MEM_mallocN(sizeof(*mr->mlooptri) * mr->tri_len, "MR_DATATYPE_LOOPTRI");
- BKE_mesh_recalc_looptri(
- me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, mr->mlooptri);
- }
- }
- else {
- /* #BMesh */
- if ((iter_type & MR_ITER_LOOPTRI) || (data_flag & MR_DATA_LOOPTRI)) {
- /* Edit mode ensures this is valid, no need to calculate. */
- BLI_assert((mr->bm->totloop == 0) || (mr->edit_bmesh->looptris != NULL));
- }
- }
-}
-
-static void mesh_render_data_update_normals(MeshRenderData *mr,
- const eMRIterType UNUSED(iter_type),
- const eMRDataType data_flag)
-{
- Mesh *me = mr->me;
- const bool is_auto_smooth = (me->flag & ME_AUTOSMOOTH) != 0;
- const float split_angle = is_auto_smooth ? me->smoothresh : (float)M_PI;
-
- if (mr->extract_type != MR_EXTRACT_BMESH) {
- /* Mesh */
- if (data_flag & (MR_DATA_POLY_NOR | MR_DATA_LOOP_NOR | MR_DATA_TAN_LOOP_NOR)) {
- mr->poly_normals = MEM_mallocN(sizeof(*mr->poly_normals) * mr->poly_len, __func__);
- BKE_mesh_calc_normals_poly((MVert *)mr->mvert,
- NULL,
- mr->vert_len,
- mr->mloop,
- mr->mpoly,
- mr->loop_len,
- mr->poly_len,
- mr->poly_normals,
- true);
- }
- if (((data_flag & MR_DATA_LOOP_NOR) && is_auto_smooth) || (data_flag & MR_DATA_TAN_LOOP_NOR)) {
- mr->loop_normals = MEM_mallocN(sizeof(*mr->loop_normals) * mr->loop_len, __func__);
- short(*clnors)[2] = CustomData_get_layer(&mr->me->ldata, CD_CUSTOMLOOPNORMAL);
- BKE_mesh_normals_loop_split(mr->me->mvert,
- mr->vert_len,
- mr->me->medge,
- mr->edge_len,
- mr->me->mloop,
- mr->loop_normals,
- mr->loop_len,
- mr->me->mpoly,
- mr->poly_normals,
- mr->poly_len,
- is_auto_smooth,
- split_angle,
- NULL,
- clnors,
- NULL);
- }
- }
- else {
- /* #BMesh */
- if (data_flag & MR_DATA_POLY_NOR) {
- /* Use #BMFace.no instead. */
- }
- if (((data_flag & MR_DATA_LOOP_NOR) && is_auto_smooth) || (data_flag & MR_DATA_TAN_LOOP_NOR)) {
-
- const float(*vert_coords)[3] = NULL;
- const float(*vert_normals)[3] = NULL;
- const float(*poly_normals)[3] = NULL;
-
- if (mr->edit_data && mr->edit_data->vertexCos) {
- vert_coords = mr->bm_vert_coords;
- vert_normals = mr->bm_vert_normals;
- poly_normals = mr->bm_poly_normals;
- }
-
- mr->loop_normals = MEM_mallocN(sizeof(*mr->loop_normals) * mr->loop_len, __func__);
- const int clnors_offset = CustomData_get_offset(&mr->bm->ldata, CD_CUSTOMLOOPNORMAL);
- BM_loops_calc_normal_vcos(mr->bm,
- vert_coords,
- vert_normals,
- poly_normals,
- is_auto_smooth,
- split_angle,
- mr->loop_normals,
- NULL,
- NULL,
- clnors_offset,
- false);
- }
- }
-}
-
-/**
- * \param is_mode_active: When true, use the modifiers from the edit-data,
- * otherwise don't use modifiers as they are not from this object.
- */
-static MeshRenderData *mesh_render_data_create(Mesh *me,
- const bool is_editmode,
- const bool is_paint_mode,
- const bool is_mode_active,
- const float obmat[4][4],
- const bool do_final,
- const bool do_uvedit,
- const DRW_MeshCDMask *UNUSED(cd_used),
- const ToolSettings *ts,
- const eMRIterType iter_type,
- const eMRDataType data_flag)
-{
- MeshRenderData *mr = MEM_callocN(sizeof(*mr), __func__);
- mr->toolsettings = ts;
- mr->mat_len = mesh_render_mat_len_get(me);
-
- copy_m4_m4(mr->obmat, obmat);
-
- if (is_editmode) {
- BLI_assert(me->edit_mesh->mesh_eval_cage && me->edit_mesh->mesh_eval_final);
- mr->bm = me->edit_mesh->bm;
- mr->edit_bmesh = me->edit_mesh;
- mr->me = (do_final) ? me->edit_mesh->mesh_eval_final : me->edit_mesh->mesh_eval_cage;
- mr->edit_data = is_mode_active ? mr->me->runtime.edit_data : NULL;
-
- if (mr->edit_data) {
- EditMeshData *emd = mr->edit_data;
- if (emd->vertexCos) {
- BKE_editmesh_cache_ensure_vert_normals(mr->edit_bmesh, emd);
- BKE_editmesh_cache_ensure_poly_normals(mr->edit_bmesh, emd);
- }
-
- mr->bm_vert_coords = mr->edit_data->vertexCos;
- mr->bm_vert_normals = mr->edit_data->vertexNos;
- mr->bm_poly_normals = mr->edit_data->polyNos;
- mr->bm_poly_centers = mr->edit_data->polyCos;
- }
-
- bool has_mdata = is_mode_active && (mr->me->runtime.wrapper_type == ME_WRAPPER_TYPE_MDATA);
- bool use_mapped = is_mode_active &&
- (has_mdata && !do_uvedit && mr->me && !mr->me->runtime.is_original);
-
- int bm_ensure_types = BM_VERT | BM_EDGE | BM_LOOP | BM_FACE;
-
- BM_mesh_elem_index_ensure(mr->bm, bm_ensure_types);
- BM_mesh_elem_table_ensure(mr->bm, bm_ensure_types & ~BM_LOOP);
-
- mr->efa_act_uv = EDBM_uv_active_face_get(mr->edit_bmesh, false, false);
- mr->efa_act = BM_mesh_active_face_get(mr->bm, false, true);
- mr->eed_act = BM_mesh_active_edge_get(mr->bm);
- mr->eve_act = BM_mesh_active_vert_get(mr->bm);
-
- mr->crease_ofs = CustomData_get_offset(&mr->bm->edata, CD_CREASE);
- mr->bweight_ofs = CustomData_get_offset(&mr->bm->edata, CD_BWEIGHT);
-#ifdef WITH_FREESTYLE
- mr->freestyle_edge_ofs = CustomData_get_offset(&mr->bm->edata, CD_FREESTYLE_EDGE);
- mr->freestyle_face_ofs = CustomData_get_offset(&mr->bm->pdata, CD_FREESTYLE_FACE);
-#endif
-
- if (use_mapped) {
- mr->v_origindex = CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX);
- mr->e_origindex = CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX);
- mr->p_origindex = CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX);
-
- use_mapped = (mr->v_origindex || mr->e_origindex || mr->p_origindex);
- }
-
- mr->extract_type = use_mapped ? MR_EXTRACT_MAPPED : MR_EXTRACT_BMESH;
-
- /* Seems like the mesh_eval_final do not have the right origin indices.
- * Force not mapped in this case. */
- if (has_mdata && do_final && me->edit_mesh->mesh_eval_final != me->edit_mesh->mesh_eval_cage) {
- // mr->edit_bmesh = NULL;
- mr->extract_type = MR_EXTRACT_MESH;
- }
- }
- else {
- mr->me = me;
- mr->edit_bmesh = NULL;
-
- bool use_mapped = is_paint_mode && mr->me && !mr->me->runtime.is_original;
- if (use_mapped) {
- mr->v_origindex = CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX);
- mr->e_origindex = CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX);
- mr->p_origindex = CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX);
-
- use_mapped = (mr->v_origindex || mr->e_origindex || mr->p_origindex);
- }
-
- mr->extract_type = use_mapped ? MR_EXTRACT_MAPPED : MR_EXTRACT_MESH;
- }
-
- if (mr->extract_type != MR_EXTRACT_BMESH) {
- /* Mesh */
- mr->vert_len = mr->me->totvert;
- mr->edge_len = mr->me->totedge;
- mr->loop_len = mr->me->totloop;
- mr->poly_len = mr->me->totpoly;
- mr->tri_len = poly_to_tri_count(mr->poly_len, mr->loop_len);
-
- mr->mvert = CustomData_get_layer(&mr->me->vdata, CD_MVERT);
- mr->medge = CustomData_get_layer(&mr->me->edata, CD_MEDGE);
- mr->mloop = CustomData_get_layer(&mr->me->ldata, CD_MLOOP);
- mr->mpoly = CustomData_get_layer(&mr->me->pdata, CD_MPOLY);
-
- mr->v_origindex = CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX);
- mr->e_origindex = CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX);
- mr->p_origindex = CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX);
- }
- else {
- /* #BMesh */
- BMesh *bm = mr->bm;
-
- mr->vert_len = bm->totvert;
- mr->edge_len = bm->totedge;
- mr->loop_len = bm->totloop;
- mr->poly_len = bm->totface;
- mr->tri_len = poly_to_tri_count(mr->poly_len, mr->loop_len);
- }
- mesh_render_data_update_loose_geom(mr, iter_type, data_flag);
-
- return mr;
-}
-
-static void mesh_render_data_free(MeshRenderData *mr)
-{
- MEM_SAFE_FREE(mr->mlooptri);
- MEM_SAFE_FREE(mr->poly_normals);
- MEM_SAFE_FREE(mr->loop_normals);
-
- MEM_SAFE_FREE(mr->lverts);
- MEM_SAFE_FREE(mr->ledges);
-
- MEM_freeN(mr);
-}
-
-BLI_INLINE BMFace *bm_original_face_get(const MeshRenderData *mr, int idx)
-{
- return ((mr->p_origindex != NULL) && (mr->p_origindex[idx] != ORIGINDEX_NONE) && mr->bm) ?
- BM_face_at_index(mr->bm, mr->p_origindex[idx]) :
- NULL;
-}
-
-BLI_INLINE BMEdge *bm_original_edge_get(const MeshRenderData *mr, int idx)
-{
- return ((mr->e_origindex != NULL) && (mr->e_origindex[idx] != ORIGINDEX_NONE) && mr->bm) ?
- BM_edge_at_index(mr->bm, mr->e_origindex[idx]) :
- NULL;
-}
-
-BLI_INLINE BMVert *bm_original_vert_get(const MeshRenderData *mr, int idx)
-{
- return ((mr->v_origindex != NULL) && (mr->v_origindex[idx] != ORIGINDEX_NONE) && mr->bm) ?
- BM_vert_at_index(mr->bm, mr->v_origindex[idx]) :
- NULL;
-}
-
-BLI_INLINE const float *bm_vert_co_get(const MeshRenderData *mr, const BMVert *eve)
-{
- const float(*vert_coords)[3] = mr->bm_vert_coords;
- if (vert_coords != NULL) {
- return vert_coords[BM_elem_index_get(eve)];
- }
-
- UNUSED_VARS(mr);
- return eve->co;
-}
-
-BLI_INLINE const float *bm_vert_no_get(const MeshRenderData *mr, const BMVert *eve)
-{
- const float(*vert_normals)[3] = mr->bm_vert_normals;
- if (vert_normals != NULL) {
- return vert_normals[BM_elem_index_get(eve)];
- }
-
- UNUSED_VARS(mr);
- return eve->no;
-}
-
-BLI_INLINE const float *bm_face_no_get(const MeshRenderData *mr, const BMFace *efa)
-{
- const float(*poly_normals)[3] = mr->bm_poly_normals;
- if (poly_normals != NULL) {
- return poly_normals[BM_elem_index_get(efa)];
- }
-
- UNUSED_VARS(mr);
- return efa->no;
-}
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Mesh Elements Extract: Loop Triangles
- * \{ */
-
-typedef struct ExtractTriBMesh_Params {
- BMLoop *(*looptris)[3];
- int tri_range[2];
-} ExtractTriBMesh_Params;
-typedef void(ExtractTriBMeshFn)(const MeshRenderData *mr,
- const ExtractTriBMesh_Params *params,
- void *data);
-
-#define EXTRACT_TRIS_LOOPTRI_FOREACH_BM_BEGIN(elem_tri, index_tri, params) \
- CHECK_TYPE(params, const ExtractTriBMesh_Params *); \
- { \
- const int _tri_index_end = (params)->tri_range[1]; \
- BMLoop **elem_tri = (params)->looptris[(params)->tri_range[0]]; \
- for (int index_tri = (params)->tri_range[0]; index_tri < _tri_index_end; \
- index_tri += 1, elem_tri += 3)
-#define EXTRACT_TRIS_LOOPTRI_FOREACH_BM_END }
-
-typedef struct ExtractTriMesh_Params {
- const MLoopTri *mlooptri;
- int tri_range[2];
-} ExtractTriMesh_Params;
-typedef void(ExtractTriMeshFn)(const MeshRenderData *mr,
- const ExtractTriMesh_Params *params,
- void *data);
-
-#define EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_BEGIN(elem_tri, index_tri, params) \
- CHECK_TYPE(params, const ExtractTriMesh_Params *); \
- { \
- const int _tri_index_end = (params)->tri_range[1]; \
- const MLoopTri *elem_tri = &(params)->mlooptri[(params)->tri_range[0]]; \
- for (int index_tri = (params)->tri_range[0]; index_tri < _tri_index_end; \
- index_tri += 1, elem_tri += 1)
-#define EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END }
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Mesh Elements Extract: Polygons, Loops
- * \{ */
-
-typedef struct ExtractPolyBMesh_Params {
- BMLoop *(*looptris)[3];
- int poly_range[2];
-} ExtractPolyBMesh_Params;
-typedef void(ExtractPolyBMeshFn)(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
- void *data);
-
-#define EXTRACT_POLY_FOREACH_BM_BEGIN(elem_poly, index_poly, params, mr) \
- CHECK_TYPE(params, const ExtractPolyBMesh_Params *); \
- { \
- BLI_assert((mr->bm->elem_table_dirty & BM_FACE) == 0); \
- BMFace **_ftable = mr->bm->ftable; \
- const int _poly_index_end = (params)->poly_range[1]; \
- for (int index_poly = (params)->poly_range[0]; index_poly < _poly_index_end; \
- index_poly += 1) { \
- BMFace *elem_poly = _ftable[index_poly]; \
- (void)elem_poly;
-
-#define EXTRACT_POLY_FOREACH_BM_END \
- } \
- }
-
-/* Iterate over polygon and loop. */
-#define EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(elem_loop, index_loop, params, mr) \
- CHECK_TYPE(params, const ExtractPolyBMesh_Params *); \
- { \
- BLI_assert((mr->bm->elem_table_dirty & BM_FACE) == 0); \
- BMFace **_ftable = mr->bm->ftable; \
- const int _poly_index_end = (params)->poly_range[1]; \
- for (int index_poly = (params)->poly_range[0]; index_poly < _poly_index_end; \
- index_poly += 1) { \
- BMFace *elem_face = _ftable[index_poly]; \
- BMLoop *elem_loop, *l_first; \
- elem_loop = l_first = BM_FACE_FIRST_LOOP(elem_face); \
- do { \
- const int index_loop = BM_elem_index_get(elem_loop); \
- (void)index_loop; /* Quiet warning when unused. */
-
-#define EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(elem_loop) \
- } \
- while ((elem_loop = elem_loop->next) != l_first) \
- ; \
- } \
- }
-
-typedef struct ExtractPolyMesh_Params {
- int poly_range[2];
-} ExtractPolyMesh_Params;
-typedef void(ExtractPolyMeshFn)(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *data);
-
-#define EXTRACT_POLY_FOREACH_MESH_BEGIN(elem_poly, index_poly, params, mr) \
- CHECK_TYPE(params, const ExtractPolyMesh_Params *); \
- { \
- const MPoly *_mpoly = mr->mpoly; \
- const int _poly_index_end = (params)->poly_range[1]; \
- for (int index_poly = (params)->poly_range[0]; index_poly < _poly_index_end; \
- index_poly += 1) { \
- const MPoly *elem_poly = &_mpoly[index_poly]; \
- (void)elem_poly;
-
-#define EXTRACT_POLY_FOREACH_MESH_END \
- } \
- }
-
-/* Iterate over polygon and loop. */
-#define EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN( \
- elem_poly, index_poly, elem_loop, index_loop, params, mr) \
- CHECK_TYPE(params, const ExtractPolyMesh_Params *); \
- { \
- const MPoly *_mpoly = mr->mpoly; \
- const MLoop *_mloop = mr->mloop; \
- const int _poly_index_end = (params)->poly_range[1]; \
- for (int index_poly = (params)->poly_range[0]; index_poly < _poly_index_end; \
- index_poly += 1) { \
- const MPoly *elem_poly = &_mpoly[index_poly]; \
- const int _index_end = elem_poly->loopstart + elem_poly->totloop; \
- for (int index_loop = elem_poly->loopstart; index_loop < _index_end; index_loop += 1) { \
- const MLoop *elem_loop = &_mloop[index_loop]; \
- (void)elem_loop;
-
-#define EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END \
- } \
- } \
- }
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Mesh Elements Extract: Loose Edges
- * \{ */
-
-typedef struct ExtractLEdgeBMesh_Params {
- const int *ledge;
- int ledge_range[2];
-} ExtractLEdgeBMesh_Params;
-typedef void(ExtractLEdgeBMeshFn)(const MeshRenderData *mr,
- const ExtractLEdgeBMesh_Params *params,
- void *data);
-
-#define EXTRACT_LEDGE_FOREACH_BM_BEGIN(elem_edge, index_ledge, params) \
- CHECK_TYPE(params, const ExtractLEdgeBMesh_Params *); \
- { \
- BLI_assert((mr->bm->elem_table_dirty & BM_EDGE) == 0); \
- BMEdge **_etable = mr->bm->etable; \
- const int *_ledge = (params)->ledge; \
- const int _ledge_index_end = (params)->ledge_range[1]; \
- for (int index_ledge = (params)->ledge_range[0]; index_ledge < _ledge_index_end; \
- index_ledge += 1) { \
- BMEdge *elem_edge = _etable[_ledge[index_ledge]]; \
- (void)elem_edge; /* Quiet warning when unused. */ \
- {
-#define EXTRACT_LEDGE_FOREACH_BM_END \
- } \
- } \
- }
-
-typedef struct ExtractLEdgeMesh_Params {
- const int *ledge;
- int ledge_range[2];
-} ExtractLEdgeMesh_Params;
-typedef void(ExtractLEdgeMeshFn)(const MeshRenderData *mr,
- const ExtractLEdgeMesh_Params *params,
- void *data);
-
-#define EXTRACT_LEDGE_FOREACH_MESH_BEGIN(elem_edge, index_ledge, params, mr) \
- CHECK_TYPE(params, const ExtractLEdgeMesh_Params *); \
- { \
- const MEdge *_medge = mr->medge; \
- const int *_ledge = (params)->ledge; \
- const int _ledge_index_end = (params)->ledge_range[1]; \
- for (int index_ledge = (params)->ledge_range[0]; index_ledge < _ledge_index_end; \
- index_ledge += 1) { \
- const MEdge *elem_edge = &_medge[_ledge[index_ledge]]; \
- (void)elem_edge; /* Quiet warning when unused. */ \
- {
-#define EXTRACT_LEDGE_FOREACH_MESH_END \
- } \
- } \
- }
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Mesh Elements Extract: Loose Vertices
- * \{ */
-
-typedef struct ExtractLVertBMesh_Params {
- const int *lvert;
- int lvert_range[2];
-} ExtractLVertBMesh_Params;
-typedef void(ExtractLVertBMeshFn)(const MeshRenderData *mr,
- const ExtractLVertBMesh_Params *params,
- void *data);
-
-#define EXTRACT_LVERT_FOREACH_BM_BEGIN(elem_vert, index_lvert, params) \
- CHECK_TYPE(params, const ExtractLVertBMesh_Params *); \
- { \
- BLI_assert((mr->bm->elem_table_dirty & BM_FACE) == 0); \
- BMVert **vtable = mr->bm->vtable; \
- const int *lverts = (params)->lvert; \
- const int _lvert_index_end = (params)->lvert_range[1]; \
- for (int index_lvert = (params)->lvert_range[0]; index_lvert < _lvert_index_end; \
- index_lvert += 1) { \
- BMVert *elem_vert = vtable[lverts[index_lvert]]; \
- (void)elem_vert; /* Quiet warning when unused. */ \
- {
-#define EXTRACT_LVERT_FOREACH_BM_END \
- } \
- } \
- }
-
-typedef struct ExtractLVertMesh_Params {
- const int *lvert;
- int lvert_range[2];
-} ExtractLVertMesh_Params;
-typedef void(ExtractLVertMeshFn)(const MeshRenderData *mr,
- const ExtractLVertMesh_Params *params,
- void *data);
-
-#define EXTRACT_LVERT_FOREACH_MESH_BEGIN(elem, index_lvert, params, mr) \
- CHECK_TYPE(params, const ExtractLVertMesh_Params *); \
- { \
- const MVert *mvert = mr->mvert; \
- const int *lverts = (params)->lvert; \
- const int _lvert_index_end = (params)->lvert_range[1]; \
- for (int index_lvert = (params)->lvert_range[0]; index_lvert < _lvert_index_end; \
- index_lvert += 1) { \
- const MVert *elem = &mvert[lverts[index_lvert]]; \
- (void)elem; /* Quiet warning when unused. */ \
- {
-#define EXTRACT_LVERT_FOREACH_MESH_END \
- } \
- } \
- }
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Mesh Elements Extract Struct
- * \{ */
-
-typedef void *(ExtractInitFn)(const MeshRenderData *mr,
- struct MeshBatchCache *cache,
- void *buffer);
-typedef void(ExtractFinishFn)(const MeshRenderData *mr,
- struct MeshBatchCache *cache,
- void *buffer,
- void *data);
-
-typedef struct MeshExtract {
- /** Executed on main thread and return user data for iteration functions. */
- ExtractInitFn *init;
- /** Executed on one (or more if use_threading) worker thread(s). */
- ExtractTriBMeshFn *iter_looptri_bm;
- ExtractTriMeshFn *iter_looptri_mesh;
- ExtractPolyBMeshFn *iter_poly_bm;
- ExtractPolyMeshFn *iter_poly_mesh;
- ExtractLEdgeBMeshFn *iter_ledge_bm;
- ExtractLEdgeMeshFn *iter_ledge_mesh;
- ExtractLVertBMeshFn *iter_lvert_bm;
- ExtractLVertMeshFn *iter_lvert_mesh;
- /** Executed on one worker thread after all elements iterations. */
- ExtractFinishFn *finish;
- /** Used to request common data. */
- const eMRDataType data_flag;
- /** Used to know if the element callbacks are thread-safe and can be parallelized. */
- const bool use_threading;
-} MeshExtract;
-
-BLI_INLINE eMRIterType mesh_extract_iter_type(const MeshExtract *ext)
-{
- eMRIterType type = 0;
- SET_FLAG_FROM_TEST(type, (ext->iter_looptri_bm || ext->iter_looptri_mesh), MR_ITER_LOOPTRI);
- SET_FLAG_FROM_TEST(type, (ext->iter_poly_bm || ext->iter_poly_mesh), MR_ITER_POLY);
- SET_FLAG_FROM_TEST(type, (ext->iter_ledge_bm || ext->iter_ledge_mesh), MR_ITER_LEDGE);
- SET_FLAG_FROM_TEST(type, (ext->iter_lvert_bm || ext->iter_lvert_mesh), MR_ITER_LVERT);
- return type;
-}
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Triangles Indices
- * \{ */
-
-typedef struct MeshExtract_Tri_Data {
- GPUIndexBufBuilder elb;
- int *tri_mat_start;
- int *tri_mat_end;
-} MeshExtract_Tri_Data;
-
-static void *extract_tris_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *UNUSED(ibo))
-{
- MeshExtract_Tri_Data *data = MEM_callocN(sizeof(*data), __func__);
-
- size_t mat_tri_idx_size = sizeof(int) * mr->mat_len;
- data->tri_mat_start = MEM_callocN(mat_tri_idx_size, __func__);
- data->tri_mat_end = MEM_callocN(mat_tri_idx_size, __func__);
-
- int *mat_tri_len = data->tri_mat_start;
- /* Count how many triangle for each material. */
- if (mr->extract_type == MR_EXTRACT_BMESH) {
- BMIter iter;
- BMFace *efa;
- BM_ITER_MESH (efa, &iter, mr->bm, BM_FACES_OF_MESH) {
- if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
- int mat = min_ii(efa->mat_nr, mr->mat_len - 1);
- mat_tri_len[mat] += efa->len - 2;
- }
- }
- }
- else {
- const MPoly *mp = mr->mpoly;
- for (int mp_index = 0; mp_index < mr->poly_len; mp_index++, mp++) {
- if (!(mr->use_hide && (mp->flag & ME_HIDE))) {
- int mat = min_ii(mp->mat_nr, mr->mat_len - 1);
- mat_tri_len[mat] += mp->totloop - 2;
- }
- }
- }
- /* Accumulate triangle lengths per material to have correct offsets. */
- int ofs = mat_tri_len[0];
- mat_tri_len[0] = 0;
- for (int i = 1; i < mr->mat_len; i++) {
- int tmp = mat_tri_len[i];
- mat_tri_len[i] = ofs;
- ofs += tmp;
- }
-
- memcpy(data->tri_mat_end, mat_tri_len, mat_tri_idx_size);
-
- int visible_tri_tot = ofs;
- GPU_indexbuf_init(&data->elb, GPU_PRIM_TRIS, visible_tri_tot, mr->loop_len);
-
- return data;
-}
-
-static void extract_tris_iter_looptri_bm(const MeshRenderData *mr,
- const struct ExtractTriBMesh_Params *params,
- void *_data)
-{
- MeshExtract_Tri_Data *data = _data;
- const int mat_last = mr->mat_len - 1;
- EXTRACT_TRIS_LOOPTRI_FOREACH_BM_BEGIN(elt, _elt_index, params)
- {
- if (!BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN)) {
- int *mat_tri_ofs = data->tri_mat_end;
- const int mat = min_ii(elt[0]->f->mat_nr, mat_last);
- GPU_indexbuf_set_tri_verts(&data->elb,
- mat_tri_ofs[mat]++,
- BM_elem_index_get(elt[0]),
- BM_elem_index_get(elt[1]),
- BM_elem_index_get(elt[2]));
- }
- }
- EXTRACT_TRIS_LOOPTRI_FOREACH_BM_END;
-}
-
-static void extract_tris_iter_looptri_mesh(const MeshRenderData *mr,
- const struct ExtractTriMesh_Params *params,
- void *_data)
-{
- MeshExtract_Tri_Data *data = _data;
- const int mat_last = mr->mat_len - 1;
- EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_BEGIN(mlt, _mlt_index, params)
- {
- const MPoly *mp = &mr->mpoly[mlt->poly];
- if (!(mr->use_hide && (mp->flag & ME_HIDE))) {
- int *mat_tri_ofs = data->tri_mat_end;
- const int mat = min_ii(mp->mat_nr, mat_last);
- GPU_indexbuf_set_tri_verts(
- &data->elb, mat_tri_ofs[mat]++, mlt->tri[0], mlt->tri[1], mlt->tri[2]);
- }
- }
- EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END;
-}
-
-static void extract_tris_finish(const MeshRenderData *mr,
- struct MeshBatchCache *cache,
- void *ibo,
- void *_data)
-{
- MeshExtract_Tri_Data *data = _data;
- GPU_indexbuf_build_in_place(&data->elb, ibo);
-
- /* Create ibo sub-ranges. Always do this to avoid error when the standard surface batch
- * is created before the surfaces-per-material. */
- if (mr->use_final_mesh && cache->final.tris_per_mat) {
- MeshBufferCache *mbc = &cache->final;
- for (int i = 0; i < mr->mat_len; i++) {
- /* These IBOs have not been queried yet but we create them just in case they are needed
- * later since they are not tracked by mesh_buffer_cache_create_requested(). */
- if (mbc->tris_per_mat[i] == NULL) {
- mbc->tris_per_mat[i] = GPU_indexbuf_calloc();
- }
- /* Multiply by 3 because these are triangle indices. */
- const int mat_start = data->tri_mat_start[i];
- const int mat_end = data->tri_mat_end[i];
- const int start = mat_start * 3;
- const int len = (mat_end - mat_start) * 3;
- GPU_indexbuf_create_subrange_in_place(mbc->tris_per_mat[i], ibo, start, len);
- }
- }
- MEM_freeN(data->tri_mat_start);
- MEM_freeN(data->tri_mat_end);
- MEM_freeN(data);
-}
-
-static const MeshExtract extract_tris = {
- .init = extract_tris_init,
- .iter_looptri_bm = extract_tris_iter_looptri_bm,
- .iter_looptri_mesh = extract_tris_iter_looptri_mesh,
- .finish = extract_tris_finish,
- .data_flag = 0,
- .use_threading = false,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Edges Indices
- * \{ */
-
-static void *extract_lines_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *UNUSED(buf))
-{
- GPUIndexBufBuilder *elb = MEM_mallocN(sizeof(*elb), __func__);
- /* Put loose edges at the end. */
- GPU_indexbuf_init(
- elb, GPU_PRIM_LINES, mr->edge_len + mr->edge_loose_len, mr->loop_len + mr->loop_loose_len);
- return elb;
-}
-
-static void extract_lines_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
- void *elb)
-{
- /* Using poly & loop iterator would complicate accessing the adjacent loop. */
- EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr)
- {
- BMLoop *l_iter, *l_first;
- /* Use #BMLoop.prev to match mesh order (to avoid minor differences in data extraction). */
- l_iter = l_first = BM_FACE_FIRST_LOOP(f)->prev;
- do {
- if (!BM_elem_flag_test(l_iter->e, BM_ELEM_HIDDEN)) {
- GPU_indexbuf_set_line_verts(elb,
- BM_elem_index_get(l_iter->e),
- BM_elem_index_get(l_iter),
- BM_elem_index_get(l_iter->next));
- }
- else {
- GPU_indexbuf_set_line_restart(elb, BM_elem_index_get(l_iter->e));
- }
- } while ((l_iter = l_iter->next) != l_first);
- }
- EXTRACT_POLY_FOREACH_BM_END;
-}
-
-static void extract_lines_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *elb)
-{
- /* Using poly & loop iterator would complicate accessing the adjacent loop. */
- const MLoop *mloop = mr->mloop;
- const MEdge *medge = mr->medge;
- if (mr->use_hide || (mr->extract_type == MR_EXTRACT_MAPPED) || (mr->e_origindex != NULL)) {
- EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr)
- {
- const int ml_index_last = mp->loopstart + (mp->totloop - 1);
- int ml_index = ml_index_last, ml_index_next = mp->loopstart;
- do {
- const MLoop *ml = &mloop[ml_index];
- const MEdge *med = &medge[ml->e];
- if (!((mr->use_hide && (med->flag & ME_HIDE)) ||
- ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) &&
- (mr->e_origindex[ml->e] == ORIGINDEX_NONE)))) {
- GPU_indexbuf_set_line_verts(elb, ml->e, ml_index, ml_index_next);
- }
- else {
- GPU_indexbuf_set_line_restart(elb, ml->e);
- }
- } while ((ml_index = ml_index_next++) != ml_index_last);
- }
- EXTRACT_POLY_FOREACH_MESH_END;
- }
- else {
- EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr)
- {
- const int ml_index_last = mp->loopstart + (mp->totloop - 1);
- int ml_index = ml_index_last, ml_index_next = mp->loopstart;
- do {
- const MLoop *ml = &mloop[ml_index];
- GPU_indexbuf_set_line_verts(elb, ml->e, ml_index, ml_index_next);
- } while ((ml_index = ml_index_next++) != ml_index_last);
- }
- EXTRACT_POLY_FOREACH_MESH_END;
- }
-}
-
-static void extract_lines_iter_ledge_bm(const MeshRenderData *mr,
- const ExtractLEdgeBMesh_Params *params,
- void *elb)
-{
- EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params)
- {
- const int l_index_offset = mr->edge_len + ledge_index;
- if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) {
- const int l_index = mr->loop_len + ledge_index * 2;
- GPU_indexbuf_set_line_verts(elb, l_index_offset, l_index, l_index + 1);
- }
- else {
- GPU_indexbuf_set_line_restart(elb, l_index_offset);
- }
- /* Don't render the edge twice. */
- GPU_indexbuf_set_line_restart(elb, BM_elem_index_get(eed));
- }
- EXTRACT_LEDGE_FOREACH_BM_END;
-}
-
-static void extract_lines_iter_ledge_mesh(const MeshRenderData *mr,
- const ExtractLEdgeMesh_Params *params,
- void *elb)
-{
- EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr)
- {
- const int l_index_offset = mr->edge_len + ledge_index;
- const int e_index = mr->ledges[ledge_index];
- if (!((mr->use_hide && (med->flag & ME_HIDE)) ||
- ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) &&
- (mr->e_origindex[e_index] == ORIGINDEX_NONE)))) {
- const int l_index = mr->loop_len + ledge_index * 2;
- GPU_indexbuf_set_line_verts(elb, l_index_offset, l_index, l_index + 1);
- }
- else {
- GPU_indexbuf_set_line_restart(elb, l_index_offset);
- }
- /* Don't render the edge twice. */
- GPU_indexbuf_set_line_restart(elb, e_index);
- }
- EXTRACT_LEDGE_FOREACH_MESH_END;
-}
-
-static void extract_lines_finish(const MeshRenderData *UNUSED(mr),
- struct MeshBatchCache *UNUSED(cache),
- void *ibo,
- void *elb)
-{
- GPU_indexbuf_build_in_place(elb, ibo);
- MEM_freeN(elb);
-}
-
-static const MeshExtract extract_lines = {
- .init = extract_lines_init,
- .iter_poly_bm = extract_lines_iter_poly_bm,
- .iter_poly_mesh = extract_lines_iter_poly_mesh,
- .iter_ledge_bm = extract_lines_iter_ledge_bm,
- .iter_ledge_mesh = extract_lines_iter_ledge_mesh,
- .finish = extract_lines_finish,
- .data_flag = 0,
- .use_threading = false,
-};
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Loose Edges Sub Buffer
- * \{ */
-
-static void extract_lines_loose_subbuffer(const MeshRenderData *mr, struct MeshBatchCache *cache)
-{
- BLI_assert(cache->final.ibo.lines);
- /* Multiply by 2 because these are edges indices. */
- const int start = mr->edge_len * 2;
- const int len = mr->edge_loose_len * 2;
- GPU_indexbuf_create_subrange_in_place(
- cache->final.ibo.lines_loose, cache->final.ibo.lines, start, len);
- cache->no_loose_wire = (len == 0);
-}
-
-static void extract_lines_with_lines_loose_finish(const MeshRenderData *mr,
- struct MeshBatchCache *cache,
- void *ibo,
- void *elb)
-{
- GPU_indexbuf_build_in_place(elb, ibo);
- extract_lines_loose_subbuffer(mr, cache);
- MEM_freeN(elb);
-}
-
-static const MeshExtract extract_lines_with_lines_loose = {
- .init = extract_lines_init,
- .iter_poly_bm = extract_lines_iter_poly_bm,
- .iter_poly_mesh = extract_lines_iter_poly_mesh,
- .iter_ledge_bm = extract_lines_iter_ledge_bm,
- .iter_ledge_mesh = extract_lines_iter_ledge_mesh,
- .finish = extract_lines_with_lines_loose_finish,
- .data_flag = 0,
- .use_threading = false,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Point Indices
- * \{ */
-
-static void *extract_points_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *UNUSED(buf))
-{
- GPUIndexBufBuilder *elb = MEM_mallocN(sizeof(*elb), __func__);
- GPU_indexbuf_init(elb, GPU_PRIM_POINTS, mr->vert_len, mr->loop_len + mr->loop_loose_len);
- return elb;
-}
-
-BLI_INLINE void vert_set_bm(GPUIndexBufBuilder *elb, BMVert *eve, int l_index)
-{
- const int v_index = BM_elem_index_get(eve);
- if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
- GPU_indexbuf_set_point_vert(elb, v_index, l_index);
- }
- else {
- GPU_indexbuf_set_point_restart(elb, v_index);
- }
-}
-
-BLI_INLINE void vert_set_mesh(GPUIndexBufBuilder *elb,
- const MeshRenderData *mr,
- const int v_index,
- const int l_index)
-{
- const MVert *mv = &mr->mvert[v_index];
- if (!((mr->use_hide && (mv->flag & ME_HIDE)) ||
- ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->v_origindex) &&
- (mr->v_origindex[v_index] == ORIGINDEX_NONE)))) {
- GPU_indexbuf_set_point_vert(elb, v_index, l_index);
- }
- else {
- GPU_indexbuf_set_point_restart(elb, v_index);
- }
-}
-
-static void extract_points_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
- void *elb)
-{
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
- {
- vert_set_bm(elb, l->v, l_index);
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
-}
-
-static void extract_points_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *elb)
-{
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
- vert_set_mesh(elb, mr, ml->v, ml_index);
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
-}
-
-static void extract_points_iter_ledge_bm(const MeshRenderData *mr,
- const ExtractLEdgeBMesh_Params *params,
- void *elb)
-{
- EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params)
- {
- vert_set_bm(elb, eed->v1, mr->loop_len + (ledge_index * 2));
- vert_set_bm(elb, eed->v2, mr->loop_len + (ledge_index * 2) + 1);
- }
- EXTRACT_LEDGE_FOREACH_BM_END;
-}
-
-static void extract_points_iter_ledge_mesh(const MeshRenderData *mr,
- const ExtractLEdgeMesh_Params *params,
- void *elb)
-{
- EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr)
- {
- vert_set_mesh(elb, mr, med->v1, mr->loop_len + (ledge_index * 2));
- vert_set_mesh(elb, mr, med->v2, mr->loop_len + (ledge_index * 2) + 1);
- }
- EXTRACT_LEDGE_FOREACH_MESH_END;
-}
-
-static void extract_points_iter_lvert_bm(const MeshRenderData *mr,
- const ExtractLVertBMesh_Params *params,
- void *elb)
-{
- const int offset = mr->loop_len + (mr->edge_loose_len * 2);
- EXTRACT_LVERT_FOREACH_BM_BEGIN(eve, lvert_index, params)
- {
- vert_set_bm(elb, eve, offset + lvert_index);
- }
- EXTRACT_LVERT_FOREACH_BM_END;
-}
-
-static void extract_points_iter_lvert_mesh(const MeshRenderData *mr,
- const ExtractLVertMesh_Params *params,
- void *elb)
-{
- const int offset = mr->loop_len + (mr->edge_loose_len * 2);
- EXTRACT_LVERT_FOREACH_MESH_BEGIN(mv, lvert_index, params, mr)
- {
- vert_set_mesh(elb, mr, mr->lverts[lvert_index], offset + lvert_index);
- }
- EXTRACT_LVERT_FOREACH_MESH_END;
-}
-
-static void extract_points_finish(const MeshRenderData *UNUSED(mr),
- struct MeshBatchCache *UNUSED(cache),
- void *ibo,
- void *elb)
-{
- GPU_indexbuf_build_in_place(elb, ibo);
- MEM_freeN(elb);
-}
-
-static const MeshExtract extract_points = {
- .init = extract_points_init,
- .iter_poly_bm = extract_points_iter_poly_bm,
- .iter_poly_mesh = extract_points_iter_poly_mesh,
- .iter_ledge_bm = extract_points_iter_ledge_bm,
- .iter_ledge_mesh = extract_points_iter_ledge_mesh,
- .iter_lvert_bm = extract_points_iter_lvert_bm,
- .iter_lvert_mesh = extract_points_iter_lvert_mesh,
- .finish = extract_points_finish,
- .data_flag = 0,
- .use_threading = false,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Facedots Indices
- * \{ */
-
-static void *extract_fdots_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *UNUSED(buf))
-{
- GPUIndexBufBuilder *elb = MEM_mallocN(sizeof(*elb), __func__);
- GPU_indexbuf_init(elb, GPU_PRIM_POINTS, mr->poly_len, mr->poly_len);
- return elb;
-}
-
-static void extract_fdots_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
- void *elb)
-{
- EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr)
- {
- if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
- GPU_indexbuf_set_point_vert(elb, f_index, f_index);
- }
- else {
- GPU_indexbuf_set_point_restart(elb, f_index);
- }
- }
- EXTRACT_POLY_FOREACH_BM_END;
-}
-
-static void extract_fdots_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *elb)
-{
- if (mr->use_subsurf_fdots) {
- /* Check #ME_VERT_FACEDOT. */
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
- const MVert *mv = &mr->mvert[ml->v];
- if ((mv->flag & ME_VERT_FACEDOT) && !(mr->use_hide && (mp->flag & ME_HIDE))) {
- GPU_indexbuf_set_point_vert(elb, mp_index, mp_index);
- }
- else {
- GPU_indexbuf_set_point_restart(elb, mp_index);
- }
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
- }
- else {
- EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr)
- {
- if (!(mr->use_hide && (mp->flag & ME_HIDE))) {
- GPU_indexbuf_set_point_vert(elb, mp_index, mp_index);
- }
- else {
- GPU_indexbuf_set_point_restart(elb, mp_index);
- }
- }
- EXTRACT_POLY_FOREACH_MESH_END;
- }
-}
-
-static void extract_fdots_finish(const MeshRenderData *UNUSED(mr),
- struct MeshBatchCache *UNUSED(cache),
- void *ibo,
- void *elb)
-{
- GPU_indexbuf_build_in_place(elb, ibo);
- MEM_freeN(elb);
-}
-
-static const MeshExtract extract_fdots = {
- .init = extract_fdots_init,
- .iter_poly_bm = extract_fdots_iter_poly_bm,
- .iter_poly_mesh = extract_fdots_iter_poly_mesh,
- .finish = extract_fdots_finish,
- .data_flag = 0,
- .use_threading = false,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Paint Mask Line Indices
- * \{ */
-
-typedef struct MeshExtract_LinePaintMask_Data {
- GPUIndexBufBuilder elb;
- /** One bit per edge set if face is selected. */
- BLI_bitmap select_map[0];
-} MeshExtract_LinePaintMask_Data;
-
-static void *extract_lines_paint_mask_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *UNUSED(buf))
-{
- size_t bitmap_size = BLI_BITMAP_SIZE(mr->edge_len);
- MeshExtract_LinePaintMask_Data *data = MEM_callocN(sizeof(*data) + bitmap_size, __func__);
- GPU_indexbuf_init(&data->elb, GPU_PRIM_LINES, mr->edge_len, mr->loop_len);
- return data;
-}
-
-static void extract_lines_paint_mask_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *_data)
-{
- MeshExtract_LinePaintMask_Data *data = _data;
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
- const int e_index = ml->e;
- const MEdge *me = &mr->medge[e_index];
- if (!((mr->use_hide && (me->flag & ME_HIDE)) ||
- ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) &&
- (mr->e_origindex[e_index] == ORIGINDEX_NONE)))) {
-
- const int ml_index_last = mp->totloop + mp->loopstart - 1;
- const int ml_index_other = (ml_index == ml_index_last) ? mp->loopstart : (ml_index + 1);
- if (mp->flag & ME_FACE_SEL) {
- if (BLI_BITMAP_TEST_AND_SET_ATOMIC(data->select_map, e_index)) {
- /* Hide edge as it has more than 2 selected loop. */
- GPU_indexbuf_set_line_restart(&data->elb, e_index);
- }
- else {
- /* First selected loop. Set edge visible, overwriting any unselected loop. */
- GPU_indexbuf_set_line_verts(&data->elb, e_index, ml_index, ml_index_other);
- }
- }
- else {
- /* Set these unselected loop only if this edge has no other selected loop. */
- if (!BLI_BITMAP_TEST(data->select_map, e_index)) {
- GPU_indexbuf_set_line_verts(&data->elb, e_index, ml_index, ml_index_other);
- }
- }
- }
- else {
- GPU_indexbuf_set_line_restart(&data->elb, e_index);
- }
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
-}
-static void extract_lines_paint_mask_finish(const MeshRenderData *UNUSED(mr),
- struct MeshBatchCache *UNUSED(cache),
- void *ibo,
- void *_data)
-{
- MeshExtract_LinePaintMask_Data *data = _data;
-
- GPU_indexbuf_build_in_place(&data->elb, ibo);
- MEM_freeN(data);
-}
-
-static const MeshExtract extract_lines_paint_mask = {
- .init = extract_lines_paint_mask_init,
- .iter_poly_mesh = extract_lines_paint_mask_iter_poly_mesh,
- .finish = extract_lines_paint_mask_finish,
- .data_flag = 0,
- .use_threading = false,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Line Adjacency Indices
- * \{ */
-
-#define NO_EDGE INT_MAX
-
-typedef struct MeshExtract_LineAdjacency_Data {
- GPUIndexBufBuilder elb;
- EdgeHash *eh;
- bool is_manifold;
- /* Array to convert vert index to any loop index of this vert. */
- uint vert_to_loop[0];
-} MeshExtract_LineAdjacency_Data;
-
-static void *extract_lines_adjacency_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *UNUSED(buf))
-{
- /* Similar to poly_to_tri_count().
- * There is always (loop + triangle - 1) edges inside a polygon.
- * Accumulate for all polys and you get : */
- uint tess_edge_len = mr->loop_len + mr->tri_len - mr->poly_len;
-
- size_t vert_to_loop_size = sizeof(uint) * mr->vert_len;
-
- MeshExtract_LineAdjacency_Data *data = MEM_callocN(sizeof(*data) + vert_to_loop_size, __func__);
- GPU_indexbuf_init(&data->elb, GPU_PRIM_LINES_ADJ, tess_edge_len, mr->loop_len);
- data->eh = BLI_edgehash_new_ex(__func__, tess_edge_len);
- data->is_manifold = true;
- return data;
-}
-
-BLI_INLINE void lines_adjacency_triangle(
- uint v1, uint v2, uint v3, uint l1, uint l2, uint l3, MeshExtract_LineAdjacency_Data *data)
-{
- GPUIndexBufBuilder *elb = &data->elb;
- /* Iterate around the triangle's edges. */
- for (int e = 0; e < 3; e++) {
- SHIFT3(uint, v3, v2, v1);
- SHIFT3(uint, l3, l2, l1);
-
- bool inv_indices = (v2 > v3);
- void **pval;
- bool value_is_init = BLI_edgehash_ensure_p(data->eh, v2, v3, &pval);
- int v_data = POINTER_AS_INT(*pval);
- if (!value_is_init || v_data == NO_EDGE) {
- /* Save the winding order inside the sign bit. Because the
- * Edge-hash sort the keys and we need to compare winding later. */
- int value = (int)l1 + 1; /* 0 cannot be signed so add one. */
- *pval = POINTER_FROM_INT((inv_indices) ? -value : value);
- /* Store loop indices for remaining non-manifold edges. */
- data->vert_to_loop[v2] = l2;
- data->vert_to_loop[v3] = l3;
- }
- else {
- /* HACK Tag as not used. Prevent overhead of BLI_edgehash_remove. */
- *pval = POINTER_FROM_INT(NO_EDGE);
- bool inv_opposite = (v_data < 0);
- uint l_opposite = (uint)abs(v_data) - 1;
- /* TODO Make this part thread-safe. */
- if (inv_opposite == inv_indices) {
- /* Don't share edge if triangles have non matching winding. */
- GPU_indexbuf_add_line_adj_verts(elb, l1, l2, l3, l1);
- GPU_indexbuf_add_line_adj_verts(elb, l_opposite, l2, l3, l_opposite);
- data->is_manifold = false;
- }
- else {
- GPU_indexbuf_add_line_adj_verts(elb, l1, l2, l3, l_opposite);
- }
- }
- }
-}
-
-static void extract_lines_adjacency_iter_looptri_bm(const MeshRenderData *UNUSED(mr),
- const struct ExtractTriBMesh_Params *params,
- void *data)
-{
- EXTRACT_TRIS_LOOPTRI_FOREACH_BM_BEGIN(elt, _elt_index, params)
- {
- if (!BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN)) {
- lines_adjacency_triangle(BM_elem_index_get(elt[0]->v),
- BM_elem_index_get(elt[1]->v),
- BM_elem_index_get(elt[2]->v),
- BM_elem_index_get(elt[0]),
- BM_elem_index_get(elt[1]),
- BM_elem_index_get(elt[2]),
- data);
- }
- }
- EXTRACT_TRIS_LOOPTRI_FOREACH_BM_END;
-}
-
-static void extract_lines_adjacency_iter_looptri_mesh(const MeshRenderData *mr,
- const struct ExtractTriMesh_Params *params,
- void *data)
-{
- EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_BEGIN(mlt, _mlt_index, params)
- {
- const MPoly *mp = &mr->mpoly[mlt->poly];
- if (!(mr->use_hide && (mp->flag & ME_HIDE))) {
- lines_adjacency_triangle(mr->mloop[mlt->tri[0]].v,
- mr->mloop[mlt->tri[1]].v,
- mr->mloop[mlt->tri[2]].v,
- mlt->tri[0],
- mlt->tri[1],
- mlt->tri[2],
- data);
- }
- }
- EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END;
-}
-
-static void extract_lines_adjacency_finish(const MeshRenderData *UNUSED(mr),
- struct MeshBatchCache *cache,
- void *ibo,
- void *_data)
-{
- MeshExtract_LineAdjacency_Data *data = _data;
- /* Create edges for remaining non manifold edges. */
- EdgeHashIterator *ehi = BLI_edgehashIterator_new(data->eh);
- for (; !BLI_edgehashIterator_isDone(ehi); BLI_edgehashIterator_step(ehi)) {
- uint v2, v3, l1, l2, l3;
- int v_data = POINTER_AS_INT(BLI_edgehashIterator_getValue(ehi));
- if (v_data != NO_EDGE) {
- BLI_edgehashIterator_getKey(ehi, &v2, &v3);
- l1 = (uint)abs(v_data) - 1;
- if (v_data < 0) { /* inv_opposite */
- SWAP(uint, v2, v3);
- }
- l2 = data->vert_to_loop[v2];
- l3 = data->vert_to_loop[v3];
- GPU_indexbuf_add_line_adj_verts(&data->elb, l1, l2, l3, l1);
- data->is_manifold = false;
- }
- }
- BLI_edgehashIterator_free(ehi);
- BLI_edgehash_free(data->eh, NULL);
-
- cache->is_manifold = data->is_manifold;
-
- GPU_indexbuf_build_in_place(&data->elb, ibo);
- MEM_freeN(data);
-}
-
-#undef NO_EDGE
-
-static const MeshExtract extract_lines_adjacency = {
- .init = extract_lines_adjacency_init,
- .iter_looptri_bm = extract_lines_adjacency_iter_looptri_bm,
- .iter_looptri_mesh = extract_lines_adjacency_iter_looptri_mesh,
- .finish = extract_lines_adjacency_finish,
- .data_flag = 0,
- .use_threading = false,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Edit UV Triangles Indices
- * \{ */
-
-typedef struct MeshExtract_EditUvElem_Data {
- GPUIndexBufBuilder elb;
- bool sync_selection;
-} MeshExtract_EditUvElem_Data;
-
-static void *extract_edituv_tris_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *UNUSED(ibo))
-{
- MeshExtract_EditUvElem_Data *data = MEM_callocN(sizeof(*data), __func__);
- GPU_indexbuf_init(&data->elb, GPU_PRIM_TRIS, mr->tri_len, mr->loop_len);
- data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0;
- return data;
-}
-
-BLI_INLINE void edituv_tri_add(
- MeshExtract_EditUvElem_Data *data, bool hidden, bool selected, int v1, int v2, int v3)
-{
- if (!hidden && (data->sync_selection || selected)) {
- GPU_indexbuf_add_tri_verts(&data->elb, v1, v2, v3);
- }
-}
-
-static void extract_edituv_tris_iter_looptri_bm(const MeshRenderData *UNUSED(mr),
- const struct ExtractTriBMesh_Params *params,
- void *data)
-{
- EXTRACT_TRIS_LOOPTRI_FOREACH_BM_BEGIN(elt, _elt_index, params)
- {
- edituv_tri_add(data,
- BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN),
- BM_elem_flag_test(elt[0]->f, BM_ELEM_SELECT),
- BM_elem_index_get(elt[0]),
- BM_elem_index_get(elt[1]),
- BM_elem_index_get(elt[2]));
- }
- EXTRACT_TRIS_LOOPTRI_FOREACH_BM_END;
-}
-
-static void extract_edituv_tris_iter_looptri_mesh(const MeshRenderData *mr,
- const struct ExtractTriMesh_Params *params,
- void *data)
-{
- EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_BEGIN(mlt, _mlt_index, params)
- {
- const MPoly *mp = &mr->mpoly[mlt->poly];
- edituv_tri_add(data,
- (mp->flag & ME_HIDE) != 0,
- (mp->flag & ME_FACE_SEL) != 0,
- mlt->tri[0],
- mlt->tri[1],
- mlt->tri[2]);
- }
- EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END;
-}
-
-static void extract_edituv_tris_finish(const MeshRenderData *UNUSED(mr),
- struct MeshBatchCache *UNUSED(cache),
- void *ibo,
- void *data)
-{
- MeshExtract_EditUvElem_Data *extract_data = data;
- GPU_indexbuf_build_in_place(&extract_data->elb, ibo);
- MEM_freeN(extract_data);
-}
-
-static const MeshExtract extract_edituv_tris = {
- .init = extract_edituv_tris_init,
- .iter_looptri_bm = extract_edituv_tris_iter_looptri_bm,
- .iter_looptri_mesh = extract_edituv_tris_iter_looptri_mesh,
- .finish = extract_edituv_tris_finish,
- .data_flag = 0,
- .use_threading = false,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Edit UV Line Indices around faces
- * \{ */
-
-static void *extract_edituv_lines_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *UNUSED(ibo))
-{
- MeshExtract_EditUvElem_Data *data = MEM_callocN(sizeof(*data), __func__);
- GPU_indexbuf_init(&data->elb, GPU_PRIM_LINES, mr->loop_len, mr->loop_len);
-
- data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0;
- return data;
-}
-
-BLI_INLINE void edituv_edge_add(
- MeshExtract_EditUvElem_Data *data, bool hidden, bool selected, int v1, int v2)
-{
- if (!hidden && (data->sync_selection || selected)) {
- GPU_indexbuf_add_line_verts(&data->elb, v1, v2);
- }
-}
-
-static void extract_edituv_lines_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
- void *data)
-{
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(loop, l_index, params, mr)
- {
- edituv_edge_add(data,
- BM_elem_flag_test(loop->f, BM_ELEM_HIDDEN),
- BM_elem_flag_test(loop->f, BM_ELEM_SELECT),
- l_index,
- BM_elem_index_get(loop->next));
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(loop);
-}
-
-static void extract_edituv_lines_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *data)
-{
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
- const int ml_index_last = mp->totloop + mp->loopstart - 1;
- const int ml_index_next = (ml_index == ml_index_last) ? mp->loopstart : (ml_index + 1);
- const bool real_edge = (mr->e_origindex == NULL || mr->e_origindex[ml->e] != ORIGINDEX_NONE);
- edituv_edge_add(data,
- (mp->flag & ME_HIDE) != 0 || !real_edge,
- (mp->flag & ME_FACE_SEL) != 0,
- ml_index,
- ml_index_next);
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
-}
-
-static void extract_edituv_lines_finish(const MeshRenderData *UNUSED(mr),
- struct MeshBatchCache *UNUSED(cache),
- void *ibo,
- void *data)
-{
- MeshExtract_EditUvElem_Data *extract_data = data;
- GPU_indexbuf_build_in_place(&extract_data->elb, ibo);
- MEM_freeN(extract_data);
-}
-
-static const MeshExtract extract_edituv_lines = {
- .init = extract_edituv_lines_init,
- .iter_poly_bm = extract_edituv_lines_iter_poly_bm,
- .iter_poly_mesh = extract_edituv_lines_iter_poly_mesh,
- .finish = extract_edituv_lines_finish,
- .data_flag = 0,
- .use_threading = false,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Edit UV Points Indices
- * \{ */
-
-static void *extract_edituv_points_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *UNUSED(ibo))
-{
- MeshExtract_EditUvElem_Data *data = MEM_callocN(sizeof(*data), __func__);
- GPU_indexbuf_init(&data->elb, GPU_PRIM_POINTS, mr->loop_len, mr->loop_len);
-
- data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0;
- return data;
-}
-
-BLI_INLINE void edituv_point_add(MeshExtract_EditUvElem_Data *data,
- bool hidden,
- bool selected,
- int v1)
-{
- if (!hidden && (data->sync_selection || selected)) {
- GPU_indexbuf_add_point_vert(&data->elb, v1);
- }
-}
-
-static void extract_edituv_points_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
- void *data)
-{
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
- {
- edituv_point_add(data,
- BM_elem_flag_test(l->f, BM_ELEM_HIDDEN),
- BM_elem_flag_test(l->f, BM_ELEM_SELECT),
- l_index);
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
-}
-
-static void extract_edituv_points_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *data)
-{
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
- const bool real_vert = (mr->extract_type == MR_EXTRACT_MAPPED && (mr->v_origindex) &&
- mr->v_origindex[ml->v] != ORIGINDEX_NONE);
- edituv_point_add(
- data, ((mp->flag & ME_HIDE) != 0) || !real_vert, (mp->flag & ME_FACE_SEL) != 0, ml_index);
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
-}
-
-static void extract_edituv_points_finish(const MeshRenderData *UNUSED(mr),
- struct MeshBatchCache *UNUSED(cache),
- void *ibo,
- void *data)
-{
- MeshExtract_EditUvElem_Data *extract_data = data;
- GPU_indexbuf_build_in_place(&extract_data->elb, ibo);
- MEM_freeN(extract_data);
-}
-
-static const MeshExtract extract_edituv_points = {
- .init = extract_edituv_points_init,
- .iter_poly_bm = extract_edituv_points_iter_poly_bm,
- .iter_poly_mesh = extract_edituv_points_iter_poly_mesh,
- .finish = extract_edituv_points_finish,
- .data_flag = 0,
- .use_threading = false,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Edit UV Facedots Indices
- * \{ */
-
-static void *extract_edituv_fdots_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *UNUSED(ibo))
-{
- MeshExtract_EditUvElem_Data *data = MEM_callocN(sizeof(*data), __func__);
- GPU_indexbuf_init(&data->elb, GPU_PRIM_POINTS, mr->poly_len, mr->poly_len);
-
- data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0;
- return data;
-}
-
-BLI_INLINE void edituv_facedot_add(MeshExtract_EditUvElem_Data *data,
- bool hidden,
- bool selected,
- int face_index)
-{
- if (!hidden && (data->sync_selection || selected)) {
- GPU_indexbuf_set_point_vert(&data->elb, face_index, face_index);
- }
- else {
- GPU_indexbuf_set_point_restart(&data->elb, face_index);
- }
-}
-
-static void extract_edituv_fdots_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
- void *data)
-{
- EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr)
- {
- edituv_facedot_add(
- data, BM_elem_flag_test(f, BM_ELEM_HIDDEN), BM_elem_flag_test(f, BM_ELEM_SELECT), f_index);
- }
- EXTRACT_POLY_FOREACH_BM_END;
-}
-
-static void extract_edituv_fdots_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *data)
-{
- if (mr->use_subsurf_fdots) {
- /* Check #ME_VERT_FACEDOT. */
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
- const bool real_fdot = (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex &&
- mr->p_origindex[mp_index] != ORIGINDEX_NONE);
- const bool subd_fdot = (!mr->use_subsurf_fdots ||
- (mr->mvert[ml->v].flag & ME_VERT_FACEDOT) != 0);
- edituv_facedot_add(data,
- ((mp->flag & ME_HIDE) != 0) || !real_fdot || !subd_fdot,
- (mp->flag & ME_FACE_SEL) != 0,
- mp_index);
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
- }
- else {
- EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr)
- {
- const bool real_fdot = (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex &&
- mr->p_origindex[mp_index] != ORIGINDEX_NONE);
- edituv_facedot_add(data,
- ((mp->flag & ME_HIDE) != 0) || !real_fdot,
- (mp->flag & ME_FACE_SEL) != 0,
- mp_index);
- }
- EXTRACT_POLY_FOREACH_MESH_END;
- }
-}
-
-static void extract_edituv_fdots_finish(const MeshRenderData *UNUSED(mr),
- struct MeshBatchCache *UNUSED(cache),
- void *ibo,
- void *_data)
-{
- MeshExtract_EditUvElem_Data *data = _data;
- GPU_indexbuf_build_in_place(&data->elb, ibo);
- MEM_freeN(data);
-}
-
-static const MeshExtract extract_edituv_fdots = {
- .init = extract_edituv_fdots_init,
- .iter_poly_bm = extract_edituv_fdots_iter_poly_bm,
- .iter_poly_mesh = extract_edituv_fdots_iter_poly_mesh,
- .finish = extract_edituv_fdots_finish,
- .data_flag = 0,
- .use_threading = false,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Position and Vertex Normal
- * \{ */
-
-typedef struct PosNorLoop {
- float pos[3];
- GPUPackedNormal nor;
-} PosNorLoop;
-
-typedef struct MeshExtract_PosNor_Data {
- PosNorLoop *vbo_data;
- GPUNormal normals[];
-} MeshExtract_PosNor_Data;
-
-static void *extract_pos_nor_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf)
-{
- static GPUVertFormat format = {0};
- if (format.attr_len == 0) {
- /* WARNING Adjust #PosNorLoop struct accordingly. */
- GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
- GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
- GPU_vertformat_alias_add(&format, "vnor");
- }
- GPUVertBuf *vbo = buf;
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len);
-
- /* Pack normals per vert, reduce amount of computation. */
- size_t packed_nor_len = sizeof(GPUNormal) * mr->vert_len;
- MeshExtract_PosNor_Data *data = MEM_mallocN(sizeof(*data) + packed_nor_len, __func__);
- data->vbo_data = (PosNorLoop *)GPU_vertbuf_get_data(vbo);
-
- /* Quicker than doing it for each loop. */
- if (mr->extract_type == MR_EXTRACT_BMESH) {
- BMIter iter;
- BMVert *eve;
- int v;
- BM_ITER_MESH_INDEX (eve, &iter, mr->bm, BM_VERTS_OF_MESH, v) {
- data->normals[v].low = GPU_normal_convert_i10_v3(bm_vert_no_get(mr, eve));
- }
- }
- else {
- const MVert *mv = mr->mvert;
- for (int v = 0; v < mr->vert_len; v++, mv++) {
- data->normals[v].low = GPU_normal_convert_i10_s3(mv->no);
- }
- }
- return data;
-}
-
-static void extract_pos_nor_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
- void *_data)
-{
- MeshExtract_PosNor_Data *data = _data;
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
- {
- PosNorLoop *vert = &data->vbo_data[l_index];
- copy_v3_v3(vert->pos, bm_vert_co_get(mr, l->v));
- vert->nor = data->normals[BM_elem_index_get(l->v)].low;
- BMFace *efa = l->f;
- vert->nor.w = BM_elem_flag_test(efa, BM_ELEM_HIDDEN) ? -1 : 0;
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
-}
-
-static void extract_pos_nor_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *_data)
-{
- MeshExtract_PosNor_Data *data = _data;
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
- PosNorLoop *vert = &data->vbo_data[ml_index];
- const MVert *mv = &mr->mvert[ml->v];
- copy_v3_v3(vert->pos, mv->co);
- vert->nor = data->normals[ml->v].low;
- /* Flag for paint mode overlay. */
- if (mp->flag & ME_HIDE || mv->flag & ME_HIDE ||
- ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->v_origindex) &&
- (mr->v_origindex[ml->v] == ORIGINDEX_NONE))) {
- vert->nor.w = -1;
- }
- else if (mv->flag & SELECT) {
- vert->nor.w = 1;
- }
- else {
- vert->nor.w = 0;
- }
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
-}
-
-static void extract_pos_nor_iter_ledge_bm(const MeshRenderData *mr,
- const ExtractLEdgeBMesh_Params *params,
- void *_data)
-{
- MeshExtract_PosNor_Data *data = _data;
- EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params)
- {
- int l_index = mr->loop_len + ledge_index * 2;
- PosNorLoop *vert = &data->vbo_data[l_index];
- copy_v3_v3(vert[0].pos, bm_vert_co_get(mr, eed->v1));
- copy_v3_v3(vert[1].pos, bm_vert_co_get(mr, eed->v2));
- vert[0].nor = data->normals[BM_elem_index_get(eed->v1)].low;
- vert[1].nor = data->normals[BM_elem_index_get(eed->v2)].low;
- }
- EXTRACT_LEDGE_FOREACH_BM_END;
-}
-
-static void extract_pos_nor_iter_ledge_mesh(const MeshRenderData *mr,
- const ExtractLEdgeMesh_Params *params,
- void *_data)
-{
- MeshExtract_PosNor_Data *data = _data;
- EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr)
- {
- const int ml_index = mr->loop_len + ledge_index * 2;
- PosNorLoop *vert = &data->vbo_data[ml_index];
- copy_v3_v3(vert[0].pos, mr->mvert[med->v1].co);
- copy_v3_v3(vert[1].pos, mr->mvert[med->v2].co);
- vert[0].nor = data->normals[med->v1].low;
- vert[1].nor = data->normals[med->v2].low;
- }
- EXTRACT_LEDGE_FOREACH_MESH_END;
-}
-
-static void extract_pos_nor_iter_lvert_bm(const MeshRenderData *mr,
- const ExtractLVertBMesh_Params *params,
- void *_data)
-{
- MeshExtract_PosNor_Data *data = _data;
- const int offset = mr->loop_len + (mr->edge_loose_len * 2);
- EXTRACT_LVERT_FOREACH_BM_BEGIN(eve, lvert_index, params)
- {
- const int l_index = offset + lvert_index;
- PosNorLoop *vert = &data->vbo_data[l_index];
- copy_v3_v3(vert->pos, bm_vert_co_get(mr, eve));
- vert->nor = data->normals[BM_elem_index_get(eve)].low;
- }
- EXTRACT_LVERT_FOREACH_BM_END;
-}
-
-static void extract_pos_nor_iter_lvert_mesh(const MeshRenderData *mr,
- const ExtractLVertMesh_Params *params,
- void *_data)
-{
- MeshExtract_PosNor_Data *data = _data;
- const int offset = mr->loop_len + (mr->edge_loose_len * 2);
- EXTRACT_LVERT_FOREACH_MESH_BEGIN(mv, lvert_index, params, mr)
- {
- const int ml_index = offset + lvert_index;
- const int v_index = mr->lverts[lvert_index];
- PosNorLoop *vert = &data->vbo_data[ml_index];
- copy_v3_v3(vert->pos, mv->co);
- vert->nor = data->normals[v_index].low;
- }
- EXTRACT_LVERT_FOREACH_MESH_END;
-}
-
-static void extract_pos_nor_finish(const MeshRenderData *UNUSED(mr),
- struct MeshBatchCache *UNUSED(cache),
- void *UNUSED(vbo),
- void *data)
-{
- MEM_freeN(data);
-}
-
-static const MeshExtract extract_pos_nor = {
- .init = extract_pos_nor_init,
- .iter_poly_bm = extract_pos_nor_iter_poly_bm,
- .iter_poly_mesh = extract_pos_nor_iter_poly_mesh,
- .iter_ledge_bm = extract_pos_nor_iter_ledge_bm,
- .iter_ledge_mesh = extract_pos_nor_iter_ledge_mesh,
- .iter_lvert_bm = extract_pos_nor_iter_lvert_bm,
- .iter_lvert_mesh = extract_pos_nor_iter_lvert_mesh,
- .finish = extract_pos_nor_finish,
- .data_flag = 0,
- .use_threading = true,
-};
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Position and High Quality Vertex Normal
- * \{ */
-
-typedef struct PosNorHQLoop {
- float pos[3];
- short nor[4];
-} PosNorHQLoop;
-
-typedef struct MeshExtract_PosNorHQ_Data {
- PosNorHQLoop *vbo_data;
- GPUNormal normals[];
-} MeshExtract_PosNorHQ_Data;
-
-static void *extract_pos_nor_hq_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf)
-{
- static GPUVertFormat format = {0};
- if (format.attr_len == 0) {
- /* WARNING Adjust #PosNorHQLoop struct accordingly. */
- GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
- GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
- GPU_vertformat_alias_add(&format, "vnor");
- }
- GPUVertBuf *vbo = buf;
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len);
-
- /* Pack normals per vert, reduce amount of computation. */
- size_t packed_nor_len = sizeof(GPUNormal) * mr->vert_len;
- MeshExtract_PosNorHQ_Data *data = MEM_mallocN(sizeof(*data) + packed_nor_len, __func__);
- data->vbo_data = (PosNorHQLoop *)GPU_vertbuf_get_data(vbo);
-
- /* Quicker than doing it for each loop. */
- if (mr->extract_type == MR_EXTRACT_BMESH) {
- BMIter iter;
- BMVert *eve;
- int v;
- BM_ITER_MESH_INDEX (eve, &iter, mr->bm, BM_VERTS_OF_MESH, v) {
- normal_float_to_short_v3(data->normals[v].high, bm_vert_no_get(mr, eve));
- }
- }
- else {
- const MVert *mv = mr->mvert;
- for (int v = 0; v < mr->vert_len; v++, mv++) {
- copy_v3_v3_short(data->normals[v].high, mv->no);
- }
- }
- return data;
-}
-
-static void extract_pos_nor_hq_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
- void *_data)
-{
- MeshExtract_PosNorHQ_Data *data = _data;
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
- {
- PosNorHQLoop *vert = &data->vbo_data[l_index];
- copy_v3_v3(vert->pos, bm_vert_co_get(mr, l->v));
- copy_v3_v3_short(vert->nor, data->normals[BM_elem_index_get(l->v)].high);
-
- BMFace *efa = l->f;
- vert->nor[3] = BM_elem_flag_test(efa, BM_ELEM_HIDDEN) ? -1 : 0;
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
-}
-
-static void extract_pos_nor_hq_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *_data)
-{
- MeshExtract_PosNorHQ_Data *data = _data;
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
- PosNorHQLoop *vert = &data->vbo_data[ml_index];
- const MVert *mv = &mr->mvert[ml->v];
- copy_v3_v3(vert->pos, mv->co);
- copy_v3_v3_short(vert->nor, data->normals[ml->v].high);
-
- /* Flag for paint mode overlay. */
- if (mp->flag & ME_HIDE || mv->flag & ME_HIDE ||
- ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->v_origindex) &&
- (mr->v_origindex[ml->v] == ORIGINDEX_NONE))) {
- vert->nor[3] = -1;
- }
- else if (mv->flag & SELECT) {
- vert->nor[3] = 1;
- }
- else {
- vert->nor[3] = 0;
- }
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
-}
-
-static void extract_pos_nor_hq_iter_ledge_bm(const MeshRenderData *mr,
- const ExtractLEdgeBMesh_Params *params,
- void *_data)
-{
- MeshExtract_PosNorHQ_Data *data = _data;
- EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params)
- {
- int l_index = mr->loop_len + ledge_index * 2;
- PosNorHQLoop *vert = &data->vbo_data[l_index];
- copy_v3_v3(vert[0].pos, bm_vert_co_get(mr, eed->v1));
- copy_v3_v3(vert[1].pos, bm_vert_co_get(mr, eed->v2));
- copy_v3_v3_short(vert[0].nor, data->normals[BM_elem_index_get(eed->v1)].high);
- vert[0].nor[3] = 0;
- copy_v3_v3_short(vert[1].nor, data->normals[BM_elem_index_get(eed->v2)].high);
- vert[1].nor[3] = 0;
- }
- EXTRACT_LEDGE_FOREACH_BM_END;
-}
-
-static void extract_pos_nor_hq_iter_ledge_mesh(const MeshRenderData *mr,
- const ExtractLEdgeMesh_Params *params,
- void *_data)
-{
- MeshExtract_PosNorHQ_Data *data = _data;
- EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr)
- {
- const int ml_index = mr->loop_len + ledge_index * 2;
- PosNorHQLoop *vert = &data->vbo_data[ml_index];
- copy_v3_v3(vert[0].pos, mr->mvert[med->v1].co);
- copy_v3_v3(vert[1].pos, mr->mvert[med->v2].co);
- copy_v3_v3_short(vert[0].nor, data->normals[med->v1].high);
- vert[0].nor[3] = 0;
- copy_v3_v3_short(vert[1].nor, data->normals[med->v2].high);
- vert[1].nor[3] = 0;
- }
- EXTRACT_LEDGE_FOREACH_MESH_END;
-}
-
-static void extract_pos_nor_hq_iter_lvert_bm(const MeshRenderData *mr,
- const ExtractLVertBMesh_Params *params,
- void *_data)
-{
- MeshExtract_PosNorHQ_Data *data = _data;
- const int offset = mr->loop_len + (mr->edge_loose_len * 2);
- EXTRACT_LVERT_FOREACH_BM_BEGIN(eve, lvert_index, params)
- {
- const int l_index = offset + lvert_index;
- PosNorHQLoop *vert = &data->vbo_data[l_index];
- copy_v3_v3(vert->pos, bm_vert_co_get(mr, eve));
- copy_v3_v3_short(vert->nor, data->normals[BM_elem_index_get(eve)].high);
- vert->nor[3] = 0;
- }
- EXTRACT_LVERT_FOREACH_BM_END;
-}
-
-static void extract_pos_nor_hq_iter_lvert_mesh(const MeshRenderData *mr,
- const ExtractLVertMesh_Params *params,
- void *_data)
-{
- MeshExtract_PosNorHQ_Data *data = _data;
- const int offset = mr->loop_len + (mr->edge_loose_len * 2);
- EXTRACT_LVERT_FOREACH_MESH_BEGIN(mv, lvert_index, params, mr)
- {
- const int ml_index = offset + lvert_index;
- const int v_index = mr->lverts[lvert_index];
- PosNorHQLoop *vert = &data->vbo_data[ml_index];
- copy_v3_v3(vert->pos, mv->co);
- copy_v3_v3_short(vert->nor, data->normals[v_index].high);
- vert->nor[3] = 0;
- }
- EXTRACT_LVERT_FOREACH_MESH_END;
-}
-
-static void extract_pos_nor_hq_finish(const MeshRenderData *UNUSED(mr),
- struct MeshBatchCache *UNUSED(cache),
- void *UNUSED(vbo),
- void *data)
-{
- MEM_freeN(data);
-}
-
-static const MeshExtract extract_pos_nor_hq = {
- .init = extract_pos_nor_hq_init,
- .iter_poly_bm = extract_pos_nor_hq_iter_poly_bm,
- .iter_poly_mesh = extract_pos_nor_hq_iter_poly_mesh,
- .iter_ledge_bm = extract_pos_nor_hq_iter_ledge_bm,
- .iter_ledge_mesh = extract_pos_nor_hq_iter_ledge_mesh,
- .iter_lvert_bm = extract_pos_nor_hq_iter_lvert_bm,
- .iter_lvert_mesh = extract_pos_nor_hq_iter_lvert_mesh,
- .finish = extract_pos_nor_hq_finish,
- .data_flag = 0,
- .use_threading = true,
-};
-
-/** \} */
-/* ---------------------------------------------------------------------- */
-/** \name Extract HQ Loop Normal
- * \{ */
-
-typedef struct gpuHQNor {
- short x, y, z, w;
-} gpuHQNor;
-
-static void *extract_lnor_hq_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf)
-{
- static GPUVertFormat format = {0};
- if (format.attr_len == 0) {
- GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
- GPU_vertformat_alias_add(&format, "lnor");
- }
- GPUVertBuf *vbo = buf;
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, mr->loop_len);
-
- return GPU_vertbuf_get_data(vbo);
-}
-
-static void extract_lnor_hq_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
- void *data)
-{
- if (mr->loop_normals) {
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(_l, l_index, params, mr)
- {
- normal_float_to_short_v3(&((gpuHQNor *)data)[l_index].x, mr->loop_normals[l_index]);
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(_l);
- }
- else {
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
- {
- if (BM_elem_flag_test(l->f, BM_ELEM_SMOOTH)) {
- normal_float_to_short_v3(&((gpuHQNor *)data)[l_index].x, bm_vert_no_get(mr, l->v));
- }
- else {
- normal_float_to_short_v3(&((gpuHQNor *)data)[l_index].x, bm_face_no_get(mr, l->f));
- }
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
- }
-}
-
-static void extract_lnor_hq_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *data)
-{
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
- gpuHQNor *lnor_data = &((gpuHQNor *)data)[ml_index];
- if (mr->loop_normals) {
- normal_float_to_short_v3(&lnor_data->x, mr->loop_normals[ml_index]);
- }
- else if (mp->flag & ME_SMOOTH) {
- copy_v3_v3_short(&lnor_data->x, mr->mvert[ml->v].no);
- }
- else {
- normal_float_to_short_v3(&lnor_data->x, mr->poly_normals[mp_index]);
- }
-
- /* Flag for paint mode overlay.
- * Only use #MR_EXTRACT_MAPPED in edit mode where it is used to display the edge-normals.
- * In paint mode it will use the un-mapped data to draw the wire-frame. */
- if (mp->flag & ME_HIDE || (mr->edit_bmesh && mr->extract_type == MR_EXTRACT_MAPPED &&
- (mr->v_origindex) && mr->v_origindex[ml->v] == ORIGINDEX_NONE)) {
- lnor_data->w = -1;
- }
- else if (mp->flag & ME_FACE_SEL) {
- lnor_data->w = 1;
- }
- else {
- lnor_data->w = 0;
- }
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
-}
-
-static const MeshExtract extract_lnor_hq = {
- .init = extract_lnor_hq_init,
- .iter_poly_bm = extract_lnor_hq_iter_poly_bm,
- .iter_poly_mesh = extract_lnor_hq_iter_poly_mesh,
- .data_flag = MR_DATA_LOOP_NOR,
- .use_threading = true,
-};
-
-/** \} */
-/* ---------------------------------------------------------------------- */
-/** \name Extract Loop Normal
- * \{ */
-
-static void *extract_lnor_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf)
-{
- static GPUVertFormat format = {0};
- if (format.attr_len == 0) {
- GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
- GPU_vertformat_alias_add(&format, "lnor");
- }
- GPUVertBuf *vbo = buf;
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, mr->loop_len);
-
- return GPU_vertbuf_get_data(vbo);
-}
-
-static void extract_lnor_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
- void *data)
-{
- if (mr->loop_normals) {
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
- {
- ((GPUPackedNormal *)data)[l_index] = GPU_normal_convert_i10_v3(mr->loop_normals[l_index]);
- BMFace *efa = l->f;
- ((GPUPackedNormal *)data)[l_index].w = BM_elem_flag_test(efa, BM_ELEM_HIDDEN) ? -1 : 0;
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
- }
- else {
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
- {
- if (BM_elem_flag_test(l->f, BM_ELEM_SMOOTH)) {
- ((GPUPackedNormal *)data)[l_index] = GPU_normal_convert_i10_v3(bm_vert_no_get(mr, l->v));
- }
- else {
- ((GPUPackedNormal *)data)[l_index] = GPU_normal_convert_i10_v3(bm_face_no_get(mr, l->f));
- }
- BMFace *efa = l->f;
- ((GPUPackedNormal *)data)[l_index].w = BM_elem_flag_test(efa, BM_ELEM_HIDDEN) ? -1 : 0;
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
- }
-}
-
-static void extract_lnor_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *data)
-{
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
- GPUPackedNormal *lnor_data = &((GPUPackedNormal *)data)[ml_index];
- if (mr->loop_normals) {
- *lnor_data = GPU_normal_convert_i10_v3(mr->loop_normals[ml_index]);
- }
- else if (mp->flag & ME_SMOOTH) {
- *lnor_data = GPU_normal_convert_i10_s3(mr->mvert[ml->v].no);
- }
- else {
- *lnor_data = GPU_normal_convert_i10_v3(mr->poly_normals[mp_index]);
- }
-
- /* Flag for paint mode overlay.
- * Only use MR_EXTRACT_MAPPED in edit mode where it is used to display the edge-normals.
- * In paint mode it will use the un-mapped data to draw the wire-frame. */
- if (mp->flag & ME_HIDE || (mr->edit_bmesh && mr->extract_type == MR_EXTRACT_MAPPED &&
- (mr->v_origindex) && mr->v_origindex[ml->v] == ORIGINDEX_NONE)) {
- lnor_data->w = -1;
- }
- else if (mp->flag & ME_FACE_SEL) {
- lnor_data->w = 1;
- }
- else {
- lnor_data->w = 0;
- }
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
-}
-
-static const MeshExtract extract_lnor = {
- .init = extract_lnor_init,
- .iter_poly_bm = extract_lnor_iter_poly_bm,
- .iter_poly_mesh = extract_lnor_iter_poly_mesh,
- .data_flag = MR_DATA_LOOP_NOR,
- .use_threading = true,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract UV layers
- * \{ */
-
-static void *extract_uv_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf)
-{
- GPUVertFormat format = {0};
- GPU_vertformat_deinterleave(&format);
-
- CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata;
- uint32_t uv_layers = cache->cd_used.uv;
- /* HACK to fix T68857 */
- if (mr->extract_type == MR_EXTRACT_BMESH && cache->cd_used.edit_uv == 1) {
- int layer = CustomData_get_active_layer(cd_ldata, CD_MLOOPUV);
- if (layer != -1) {
- uv_layers |= (1 << layer);
- }
- }
-
- for (int i = 0; i < MAX_MTFACE; i++) {
- if (uv_layers & (1 << i)) {
- char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME];
- const char *layer_name = CustomData_get_layer_name(cd_ldata, CD_MLOOPUV, i);
-
- GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME);
- /* UV layer name. */
- BLI_snprintf(attr_name, sizeof(attr_name), "u%s", attr_safe_name);
- GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- /* Auto layer name. */
- BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name);
- GPU_vertformat_alias_add(&format, attr_name);
- /* Active render layer name. */
- if (i == CustomData_get_render_layer(cd_ldata, CD_MLOOPUV)) {
- GPU_vertformat_alias_add(&format, "u");
- }
- /* Active display layer name. */
- if (i == CustomData_get_active_layer(cd_ldata, CD_MLOOPUV)) {
- GPU_vertformat_alias_add(&format, "au");
- /* Alias to `pos` for edit uvs. */
- GPU_vertformat_alias_add(&format, "pos");
- }
- /* Stencil mask uv layer name. */
- if (i == CustomData_get_stencil_layer(cd_ldata, CD_MLOOPUV)) {
- GPU_vertformat_alias_add(&format, "mu");
- }
- }
- }
-
- int v_len = mr->loop_len;
- if (format.attr_len == 0) {
- GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
- /* VBO will not be used, only allocate minimum of memory. */
- v_len = 1;
- }
-
- GPUVertBuf *vbo = buf;
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, v_len);
-
- float(*uv_data)[2] = (float(*)[2])GPU_vertbuf_get_data(vbo);
- for (int i = 0; i < MAX_MTFACE; i++) {
- if (uv_layers & (1 << i)) {
- if (mr->extract_type == MR_EXTRACT_BMESH) {
- int cd_ofs = CustomData_get_n_offset(cd_ldata, CD_MLOOPUV, i);
- BMIter f_iter;
- BMFace *efa;
- BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) {
- BMLoop *l_iter, *l_first;
- l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
- do {
- MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l_iter, cd_ofs);
- memcpy(uv_data, luv->uv, sizeof(*uv_data));
- uv_data++;
- } while ((l_iter = l_iter->next) != l_first);
- }
- }
- else {
- MLoopUV *layer_data = CustomData_get_layer_n(cd_ldata, CD_MLOOPUV, i);
- for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, uv_data++, layer_data++) {
- memcpy(uv_data, layer_data->uv, sizeof(*uv_data));
- }
- }
- }
- }
-
- return NULL;
-}
-
-static const MeshExtract extract_uv = {
- .init = extract_uv_init,
- .data_flag = 0,
- .use_threading = false,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Tangent layers
- * \{ */
-
-static void extract_tan_ex(const MeshRenderData *mr,
- struct MeshBatchCache *cache,
- GPUVertBuf *vbo,
- const bool do_hq)
-{
- GPUVertCompType comp_type = do_hq ? GPU_COMP_I16 : GPU_COMP_I10;
- GPUVertFetchMode fetch_mode = GPU_FETCH_INT_TO_FLOAT_UNIT;
-
- GPUVertFormat format = {0};
- GPU_vertformat_deinterleave(&format);
-
- CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata;
- CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata;
- uint32_t tan_layers = cache->cd_used.tan;
- float(*orco)[3] = CustomData_get_layer(cd_vdata, CD_ORCO);
- bool orco_allocated = false;
- const bool use_orco_tan = cache->cd_used.tan_orco != 0;
-
- int tan_len = 0;
- char tangent_names[MAX_MTFACE][MAX_CUSTOMDATA_LAYER_NAME];
-
- for (int i = 0; i < MAX_MTFACE; i++) {
- if (tan_layers & (1 << i)) {
- char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME];
- const char *layer_name = CustomData_get_layer_name(cd_ldata, CD_MLOOPUV, i);
- GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME);
- /* Tangent layer name. */
- BLI_snprintf(attr_name, sizeof(attr_name), "t%s", attr_safe_name);
- GPU_vertformat_attr_add(&format, attr_name, comp_type, 4, fetch_mode);
- /* Active render layer name. */
- if (i == CustomData_get_render_layer(cd_ldata, CD_MLOOPUV)) {
- GPU_vertformat_alias_add(&format, "t");
- }
- /* Active display layer name. */
- if (i == CustomData_get_active_layer(cd_ldata, CD_MLOOPUV)) {
- GPU_vertformat_alias_add(&format, "at");
- }
-
- BLI_strncpy(tangent_names[tan_len++], layer_name, MAX_CUSTOMDATA_LAYER_NAME);
- }
- }
- if (use_orco_tan && orco == NULL) {
- /* If `orco` is not available compute it ourselves */
- orco_allocated = true;
- orco = MEM_mallocN(sizeof(*orco) * mr->vert_len, __func__);
-
- if (mr->extract_type == MR_EXTRACT_BMESH) {
- BMesh *bm = mr->bm;
- for (int v = 0; v < mr->vert_len; v++) {
- const BMVert *eve = BM_vert_at_index(bm, v);
- /* Exceptional case where #bm_vert_co_get can be avoided, as we want the original coords.
- * not the distorted ones. */
- copy_v3_v3(orco[v], eve->co);
- }
- }
- else {
- const MVert *mv = mr->mvert;
- for (int v = 0; v < mr->vert_len; v++, mv++) {
- copy_v3_v3(orco[v], mv->co);
- }
- }
- BKE_mesh_orco_verts_transform(mr->me, orco, mr->vert_len, 0);
- }
-
- /* Start Fresh */
- CustomData loop_data;
- CustomData_reset(&loop_data);
- if (tan_len != 0 || use_orco_tan) {
- short tangent_mask = 0;
- bool calc_active_tangent = false;
- if (mr->extract_type == MR_EXTRACT_BMESH) {
- BKE_editmesh_loop_tangent_calc(mr->edit_bmesh,
- calc_active_tangent,
- tangent_names,
- tan_len,
- mr->poly_normals,
- mr->loop_normals,
- orco,
- &loop_data,
- mr->loop_len,
- &tangent_mask);
- }
- else {
- BKE_mesh_calc_loop_tangent_ex(mr->mvert,
- mr->mpoly,
- mr->poly_len,
- mr->mloop,
- mr->mlooptri,
- mr->tri_len,
- cd_ldata,
- calc_active_tangent,
- tangent_names,
- tan_len,
- mr->poly_normals,
- mr->loop_normals,
- orco,
- &loop_data,
- mr->loop_len,
- &tangent_mask);
- }
- }
-
- if (use_orco_tan) {
- char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME];
- const char *layer_name = CustomData_get_layer_name(&loop_data, CD_TANGENT, 0);
- GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME);
- BLI_snprintf(attr_name, sizeof(*attr_name), "t%s", attr_safe_name);
- GPU_vertformat_attr_add(&format, attr_name, comp_type, 4, fetch_mode);
- GPU_vertformat_alias_add(&format, "t");
- GPU_vertformat_alias_add(&format, "at");
- }
-
- if (orco_allocated) {
- MEM_SAFE_FREE(orco);
- }
-
- int v_len = mr->loop_len;
- if (format.attr_len == 0) {
- GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
- /* VBO will not be used, only allocate minimum of memory. */
- v_len = 1;
- }
-
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, v_len);
-
- if (do_hq) {
- short(*tan_data)[4] = (short(*)[4])GPU_vertbuf_get_data(vbo);
- for (int i = 0; i < tan_len; i++) {
- const char *name = tangent_names[i];
- float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_named(
- &loop_data, CD_TANGENT, name);
- for (int ml_index = 0; ml_index < mr->loop_len; ml_index++) {
- normal_float_to_short_v3(*tan_data, layer_data[ml_index]);
- (*tan_data)[3] = (layer_data[ml_index][3] > 0.0f) ? SHRT_MAX : SHRT_MIN;
- tan_data++;
- }
- }
- if (use_orco_tan) {
- float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_n(&loop_data, CD_TANGENT, 0);
- for (int ml_index = 0; ml_index < mr->loop_len; ml_index++) {
- normal_float_to_short_v3(*tan_data, layer_data[ml_index]);
- (*tan_data)[3] = (layer_data[ml_index][3] > 0.0f) ? SHRT_MAX : SHRT_MIN;
- tan_data++;
- }
- }
- }
- else {
- GPUPackedNormal *tan_data = (GPUPackedNormal *)GPU_vertbuf_get_data(vbo);
- for (int i = 0; i < tan_len; i++) {
- const char *name = tangent_names[i];
- float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_named(
- &loop_data, CD_TANGENT, name);
- for (int ml_index = 0; ml_index < mr->loop_len; ml_index++) {
- *tan_data = GPU_normal_convert_i10_v3(layer_data[ml_index]);
- tan_data->w = (layer_data[ml_index][3] > 0.0f) ? 1 : -2;
- tan_data++;
- }
- }
- if (use_orco_tan) {
- float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_n(&loop_data, CD_TANGENT, 0);
- for (int ml_index = 0; ml_index < mr->loop_len; ml_index++) {
- *tan_data = GPU_normal_convert_i10_v3(layer_data[ml_index]);
- tan_data->w = (layer_data[ml_index][3] > 0.0f) ? 1 : -2;
- tan_data++;
- }
- }
- }
-
- CustomData_free(&loop_data, mr->loop_len);
-}
-
-static void *extract_tan_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf)
-{
- extract_tan_ex(mr, cache, buf, false);
- return NULL;
-}
-
-static const MeshExtract extract_tan = {
- .init = extract_tan_init,
- .data_flag = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | MR_DATA_LOOPTRI,
- .use_threading = false,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract HQ Tangent layers
- * \{ */
-
-static void *extract_tan_hq_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf)
-{
- extract_tan_ex(mr, cache, buf, true);
- return NULL;
-}
-
-static const MeshExtract extract_tan_hq = {
- .init = extract_tan_hq_init,
- .data_flag = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | MR_DATA_LOOPTRI,
- .use_threading = false,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Sculpt Data
- * \{ */
-
-static void *extract_sculpt_data_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf)
-{
- GPUVertFormat format = {0};
-
- CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata;
- CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata;
- CustomData *cd_pdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->pdata : &mr->me->pdata;
-
- float *cd_mask = CustomData_get_layer(cd_vdata, CD_PAINT_MASK);
- int *cd_face_set = CustomData_get_layer(cd_pdata, CD_SCULPT_FACE_SETS);
-
- if (format.attr_len == 0) {
- GPU_vertformat_attr_add(&format, "fset", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
- GPU_vertformat_attr_add(&format, "msk", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
- }
-
- GPUVertBuf *vbo = buf;
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, mr->loop_len);
-
- typedef struct gpuSculptData {
- uint8_t face_set_color[4];
- float mask;
- } gpuSculptData;
-
- gpuSculptData *vbo_data = (gpuSculptData *)GPU_vertbuf_get_data(vbo);
- MLoop *loops = CustomData_get_layer(cd_ldata, CD_MLOOP);
-
- if (mr->extract_type == MR_EXTRACT_BMESH) {
- int cd_mask_ofs = CustomData_get_offset(cd_vdata, CD_PAINT_MASK);
- int cd_face_set_ofs = CustomData_get_offset(cd_pdata, CD_SCULPT_FACE_SETS);
- BMIter f_iter;
- BMFace *efa;
- BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) {
- BMLoop *l_iter, *l_first;
- l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
- do {
- float v_mask = 0.0f;
- if (cd_mask) {
- v_mask = BM_ELEM_CD_GET_FLOAT(l_iter->v, cd_mask_ofs);
- }
- vbo_data->mask = v_mask;
- uchar face_set_color[4] = {UCHAR_MAX, UCHAR_MAX, UCHAR_MAX, UCHAR_MAX};
- if (cd_face_set) {
- const int face_set_id = BM_ELEM_CD_GET_INT(l_iter->f, cd_face_set_ofs);
- if (face_set_id != mr->me->face_sets_color_default) {
- BKE_paint_face_set_overlay_color_get(
- face_set_id, mr->me->face_sets_color_seed, face_set_color);
- }
- }
- copy_v3_v3_uchar(vbo_data->face_set_color, face_set_color);
- vbo_data++;
- } while ((l_iter = l_iter->next) != l_first);
- }
- }
- else {
- int mp_loop = 0;
- for (int mp_index = 0; mp_index < mr->poly_len; mp_index++) {
- const MPoly *p = &mr->mpoly[mp_index];
- for (int l = 0; l < p->totloop; l++) {
- float v_mask = 0.0f;
- if (cd_mask) {
- v_mask = cd_mask[loops[mp_loop].v];
- }
- vbo_data->mask = v_mask;
-
- uchar face_set_color[4] = {UCHAR_MAX, UCHAR_MAX, UCHAR_MAX, UCHAR_MAX};
- if (cd_face_set) {
- const int face_set_id = cd_face_set[mp_index];
- /* Skip for the default color Face Set to render it white. */
- if (face_set_id != mr->me->face_sets_color_default) {
- BKE_paint_face_set_overlay_color_get(
- face_set_id, mr->me->face_sets_color_seed, face_set_color);
- }
- }
- copy_v3_v3_uchar(vbo_data->face_set_color, face_set_color);
- mp_loop++;
- vbo_data++;
- }
- }
- }
-
- return NULL;
-}
-
-static const MeshExtract extract_sculpt_data = {
- .init = extract_sculpt_data_init,
- .data_flag = 0,
- /* TODO: enable threading. */
- .use_threading = false,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract VCol
- * \{ */
-
-static void *extract_vcol_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf)
-{
- GPUVertFormat format = {0};
- GPU_vertformat_deinterleave(&format);
-
- CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata;
- CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata;
- uint32_t vcol_layers = cache->cd_used.vcol;
- uint32_t svcol_layers = cache->cd_used.sculpt_vcol;
-
- for (int i = 0; i < MAX_MCOL; i++) {
- if (vcol_layers & (1 << i)) {
- char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME];
- const char *layer_name = CustomData_get_layer_name(cd_ldata, CD_MLOOPCOL, i);
- GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME);
-
- BLI_snprintf(attr_name, sizeof(attr_name), "c%s", attr_safe_name);
- GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_U16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
-
- if (i == CustomData_get_render_layer(cd_ldata, CD_MLOOPCOL)) {
- GPU_vertformat_alias_add(&format, "c");
- }
- if (i == CustomData_get_active_layer(cd_ldata, CD_MLOOPCOL)) {
- GPU_vertformat_alias_add(&format, "ac");
- }
-
- /* Gather number of auto layers. */
- /* We only do `vcols` that are not overridden by `uvs` and sculpt vertex colors. */
- if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, layer_name) == -1 &&
- CustomData_get_named_layer_index(cd_vdata, CD_PROP_COLOR, layer_name) == -1) {
- BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name);
- GPU_vertformat_alias_add(&format, attr_name);
- }
- }
- }
-
- /* Sculpt Vertex Colors */
- if (U.experimental.use_sculpt_vertex_colors) {
- for (int i = 0; i < 8; i++) {
- if (svcol_layers & (1 << i)) {
- char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME];
- const char *layer_name = CustomData_get_layer_name(cd_vdata, CD_PROP_COLOR, i);
- GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME);
-
- BLI_snprintf(attr_name, sizeof(attr_name), "c%s", attr_safe_name);
- GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_U16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
-
- if (i == CustomData_get_render_layer(cd_vdata, CD_PROP_COLOR)) {
- GPU_vertformat_alias_add(&format, "c");
- }
- if (i == CustomData_get_active_layer(cd_vdata, CD_PROP_COLOR)) {
- GPU_vertformat_alias_add(&format, "ac");
- }
- /* Gather number of auto layers. */
- /* We only do `vcols` that are not overridden by `uvs`. */
- if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, layer_name) == -1) {
- BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name);
- GPU_vertformat_alias_add(&format, attr_name);
- }
- }
- }
- }
-
- GPUVertBuf *vbo = buf;
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, mr->loop_len);
-
- typedef struct gpuMeshVcol {
- ushort r, g, b, a;
- } gpuMeshVcol;
-
- gpuMeshVcol *vcol_data = (gpuMeshVcol *)GPU_vertbuf_get_data(vbo);
- MLoop *loops = CustomData_get_layer(cd_ldata, CD_MLOOP);
-
- for (int i = 0; i < MAX_MCOL; i++) {
- if (vcol_layers & (1 << i)) {
- if (mr->extract_type == MR_EXTRACT_BMESH) {
- int cd_ofs = CustomData_get_n_offset(cd_ldata, CD_MLOOPCOL, i);
- BMIter f_iter;
- BMFace *efa;
- BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) {
- BMLoop *l_iter, *l_first;
- l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
- do {
- const MLoopCol *mloopcol = BM_ELEM_CD_GET_VOID_P(l_iter, cd_ofs);
- vcol_data->r = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->r]);
- vcol_data->g = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->g]);
- vcol_data->b = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->b]);
- vcol_data->a = unit_float_to_ushort_clamp(mloopcol->a * (1.0f / 255.0f));
- vcol_data++;
- } while ((l_iter = l_iter->next) != l_first);
- }
- }
- else {
- const MLoopCol *mloopcol = (MLoopCol *)CustomData_get_layer_n(cd_ldata, CD_MLOOPCOL, i);
- for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, mloopcol++, vcol_data++) {
- vcol_data->r = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->r]);
- vcol_data->g = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->g]);
- vcol_data->b = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->b]);
- vcol_data->a = unit_float_to_ushort_clamp(mloopcol->a * (1.0f / 255.0f));
- }
- }
- }
-
- if (svcol_layers & (1 << i) && U.experimental.use_sculpt_vertex_colors) {
- if (mr->extract_type == MR_EXTRACT_BMESH) {
- int cd_ofs = CustomData_get_n_offset(cd_vdata, CD_PROP_COLOR, i);
- BMIter f_iter;
- BMFace *efa;
- BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) {
- BMLoop *l_iter, *l_first;
- l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
- do {
- const MPropCol *prop_col = BM_ELEM_CD_GET_VOID_P(l_iter->v, cd_ofs);
- vcol_data->r = unit_float_to_ushort_clamp(prop_col->color[0]);
- vcol_data->g = unit_float_to_ushort_clamp(prop_col->color[1]);
- vcol_data->b = unit_float_to_ushort_clamp(prop_col->color[2]);
- vcol_data->a = unit_float_to_ushort_clamp(prop_col->color[3]);
- vcol_data++;
- } while ((l_iter = l_iter->next) != l_first);
- }
- }
- else {
- MPropCol *vcol = CustomData_get_layer_n(cd_vdata, CD_PROP_COLOR, i);
- for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, vcol_data++) {
- vcol_data->r = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[0]);
- vcol_data->g = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[1]);
- vcol_data->b = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[2]);
- vcol_data->a = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[3]);
- }
- }
- }
- }
- return NULL;
-}
-
-static const MeshExtract extract_vcol = {
- .init = extract_vcol_init,
- .data_flag = 0,
- .use_threading = false,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Orco
- * \{ */
-
-typedef struct MeshExtract_Orco_Data {
- float (*vbo_data)[4];
- float (*orco)[3];
-} MeshExtract_Orco_Data;
-
-static void *extract_orco_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf)
-{
- static GPUVertFormat format = {0};
- if (format.attr_len == 0) {
- /* FIXME(fclem): We use the last component as a way to differentiate from generic vertex
- * attributes. This is a substantial waste of video-ram and should be done another way.
- * Unfortunately, at the time of writing, I did not found any other "non disruptive"
- * alternative. */
- GPU_vertformat_attr_add(&format, "orco", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
- }
-
- GPUVertBuf *vbo = buf;
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, mr->loop_len);
-
- CustomData *cd_vdata = &mr->me->vdata;
-
- MeshExtract_Orco_Data *data = MEM_mallocN(sizeof(*data), __func__);
- data->vbo_data = (float(*)[4])GPU_vertbuf_get_data(vbo);
- data->orco = CustomData_get_layer(cd_vdata, CD_ORCO);
- /* Make sure `orco` layer was requested only if needed! */
- BLI_assert(data->orco);
- return data;
-}
-
-static void extract_orco_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
- void *data)
-{
- MeshExtract_Orco_Data *orco_data = (MeshExtract_Orco_Data *)data;
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(loop, l_index, params, mr)
- {
- float *loop_orco = orco_data->vbo_data[l_index];
- copy_v3_v3(loop_orco, orco_data->orco[BM_elem_index_get(loop->v)]);
- loop_orco[3] = 0.0; /* Tag as not a generic attribute. */
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(loop);
-}
-
-static void extract_orco_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *data)
-{
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
- MeshExtract_Orco_Data *orco_data = (MeshExtract_Orco_Data *)data;
- float *loop_orco = orco_data->vbo_data[ml_index];
- copy_v3_v3(loop_orco, orco_data->orco[ml->v]);
- loop_orco[3] = 0.0; /* Tag as not a generic attribute. */
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
-}
-
-static void extract_orco_finish(const MeshRenderData *UNUSED(mr),
- struct MeshBatchCache *UNUSED(cache),
- void *UNUSED(buf),
- void *data)
-{
- MEM_freeN(data);
-}
-
-static const MeshExtract extract_orco = {
- .init = extract_orco_init,
- .iter_poly_bm = extract_orco_iter_poly_bm,
- .iter_poly_mesh = extract_orco_iter_poly_mesh,
- .finish = extract_orco_finish,
- .data_flag = 0,
- .use_threading = true,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Edge Factor
- * Defines how much an edge is visible.
- * \{ */
-
-typedef struct MeshExtract_EdgeFac_Data {
- uchar *vbo_data;
- bool use_edge_render;
- /* Number of loop per edge. */
- uchar edge_loop_count[0];
-} MeshExtract_EdgeFac_Data;
-
-static float loop_edge_factor_get(const float f_no[3],
- const float v_co[3],
- const float v_no[3],
- const float v_next_co[3])
-{
- float enor[3], evec[3];
- sub_v3_v3v3(evec, v_next_co, v_co);
- cross_v3_v3v3(enor, v_no, evec);
- normalize_v3(enor);
- float d = fabsf(dot_v3v3(enor, f_no));
- /* Re-scale to the slider range. */
- d *= (1.0f / 0.065f);
- CLAMP(d, 0.0f, 1.0f);
- return d;
-}
-
-static void *extract_edge_fac_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf)
-{
- static GPUVertFormat format = {0};
- if (format.attr_len == 0) {
- GPU_vertformat_attr_add(&format, "wd", GPU_COMP_U8, 1, GPU_FETCH_INT_TO_FLOAT_UNIT);
- }
- GPUVertBuf *vbo = buf;
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len);
-
- MeshExtract_EdgeFac_Data *data;
-
- if (mr->extract_type == MR_EXTRACT_MESH) {
- size_t edge_loop_count_size = sizeof(uint32_t) * mr->edge_len;
- data = MEM_callocN(sizeof(*data) + edge_loop_count_size, __func__);
-
- /* HACK(fclem) Detecting the need for edge render.
- * We could have a flag in the mesh instead or check the modifier stack. */
- const MEdge *med = mr->medge;
- for (int e_index = 0; e_index < mr->edge_len; e_index++, med++) {
- if ((med->flag & ME_EDGERENDER) == 0) {
- data->use_edge_render = true;
- break;
- }
- }
- }
- else {
- data = MEM_callocN(sizeof(*data), __func__);
- /* HACK to bypass non-manifold check in mesh_edge_fac_finish(). */
- data->use_edge_render = true;
- }
-
- data->vbo_data = GPU_vertbuf_get_data(vbo);
- return data;
-}
-
-static void extract_edge_fac_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
- void *_data)
-{
- MeshExtract_EdgeFac_Data *data = _data;
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
- {
- if (BM_edge_is_manifold(l->e)) {
- float ratio = loop_edge_factor_get(bm_face_no_get(mr, l->f),
- bm_vert_co_get(mr, l->v),
- bm_vert_no_get(mr, l->v),
- bm_vert_co_get(mr, l->next->v));
- data->vbo_data[l_index] = ratio * 253 + 1;
- }
- else {
- data->vbo_data[l_index] = 255;
- }
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
-}
-
-static void extract_edge_fac_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *_data)
-{
- MeshExtract_EdgeFac_Data *data = (MeshExtract_EdgeFac_Data *)_data;
-
- if (data->use_edge_render) {
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
- const MEdge *med = &mr->medge[ml->e];
- data->vbo_data[ml_index] = (med->flag & ME_EDGERENDER) ? 255 : 0;
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
- }
- else {
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
- /* Count loop per edge to detect non-manifold. */
- if (data->edge_loop_count[ml->e] < 3) {
- data->edge_loop_count[ml->e]++;
- }
- if (data->edge_loop_count[ml->e] == 2) {
- /* Manifold */
- const int ml_index_last = mp->totloop + mp->loopstart - 1;
- const int ml_index_other = (ml_index == ml_index_last) ? mp->loopstart : (ml_index + 1);
- const MLoop *ml_next = &mr->mloop[ml_index_other];
- const MVert *v1 = &mr->mvert[ml->v];
- const MVert *v2 = &mr->mvert[ml_next->v];
- float vnor_f[3];
- normal_short_to_float_v3(vnor_f, v1->no);
- float ratio = loop_edge_factor_get(mr->poly_normals[mp_index], v1->co, vnor_f, v2->co);
- data->vbo_data[ml_index] = ratio * 253 + 1;
- }
- else {
- /* Non-manifold */
- data->vbo_data[ml_index] = 255;
- }
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
- }
-}
-
-static void extract_edge_fac_iter_ledge_bm(const MeshRenderData *mr,
- const ExtractLEdgeBMesh_Params *params,
- void *_data)
-{
- MeshExtract_EdgeFac_Data *data = _data;
- EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params)
- {
- data->vbo_data[mr->loop_len + (ledge_index * 2) + 0] = 255;
- data->vbo_data[mr->loop_len + (ledge_index * 2) + 1] = 255;
- }
- EXTRACT_LEDGE_FOREACH_BM_END;
-}
-
-static void extract_edge_fac_iter_ledge_mesh(const MeshRenderData *mr,
- const ExtractLEdgeMesh_Params *params,
- void *_data)
-{
- MeshExtract_EdgeFac_Data *data = _data;
- EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr)
- {
- data->vbo_data[mr->loop_len + ledge_index * 2 + 0] = 255;
- data->vbo_data[mr->loop_len + ledge_index * 2 + 1] = 255;
- }
- EXTRACT_LEDGE_FOREACH_MESH_END;
-}
-
-static void extract_edge_fac_finish(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf,
- void *_data)
-{
- MeshExtract_EdgeFac_Data *data = _data;
-
- if (GPU_crappy_amd_driver()) {
- GPUVertBuf *vbo = (GPUVertBuf *)buf;
- /* Some AMD drivers strangely crash with VBO's with a one byte format.
- * To workaround we reinitialize the VBO with another format and convert
- * all bytes to floats. */
- static GPUVertFormat format = {0};
- if (format.attr_len == 0) {
- GPU_vertformat_attr_add(&format, "wd", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
- }
- /* We keep the data reference in data->vbo_data. */
- data->vbo_data = GPU_vertbuf_steal_data(vbo);
- GPU_vertbuf_clear(vbo);
-
- int buf_len = mr->loop_len + mr->loop_loose_len;
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, buf_len);
-
- float *fdata = (float *)GPU_vertbuf_get_data(vbo);
- for (int ml_index = 0; ml_index < buf_len; ml_index++, fdata++) {
- *fdata = data->vbo_data[ml_index] / 255.0f;
- }
- /* Free old byte data. */
- MEM_freeN(data->vbo_data);
- }
- MEM_freeN(data);
-}
-
-static const MeshExtract extract_edge_fac = {
- .init = extract_edge_fac_init,
- .iter_poly_bm = extract_edge_fac_iter_poly_bm,
- .iter_poly_mesh = extract_edge_fac_iter_poly_mesh,
- .iter_ledge_bm = extract_edge_fac_iter_ledge_bm,
- .iter_ledge_mesh = extract_edge_fac_iter_ledge_mesh,
- .finish = extract_edge_fac_finish,
- .data_flag = MR_DATA_POLY_NOR,
- .use_threading = false,
-};
-
-/** \} */
-/* ---------------------------------------------------------------------- */
-/** \name Extract Vertex Weight
- * \{ */
-
-typedef struct MeshExtract_Weight_Data {
- float *vbo_data;
- const DRW_MeshWeightState *wstate;
- const MDeformVert *dvert; /* For #Mesh. */
- int cd_ofs; /* For #BMesh. */
-} MeshExtract_Weight_Data;
-
-static float evaluate_vertex_weight(const MDeformVert *dvert, const DRW_MeshWeightState *wstate)
-{
- /* Error state. */
- if ((wstate->defgroup_active < 0) && (wstate->defgroup_len > 0)) {
- return -2.0f;
- }
- if (dvert == NULL) {
- return (wstate->alert_mode != OB_DRAW_GROUPUSER_NONE) ? -1.0f : 0.0f;
- }
-
- float input = 0.0f;
- if (wstate->flags & DRW_MESH_WEIGHT_STATE_MULTIPAINT) {
- /* Multi-Paint feature */
- bool is_normalized = (wstate->flags & (DRW_MESH_WEIGHT_STATE_AUTO_NORMALIZE |
- DRW_MESH_WEIGHT_STATE_LOCK_RELATIVE));
- input = BKE_defvert_multipaint_collective_weight(dvert,
- wstate->defgroup_len,
- wstate->defgroup_sel,
- wstate->defgroup_sel_count,
- is_normalized);
- /* make it black if the selected groups have no weight on a vertex */
- if (input == 0.0f) {
- return -1.0f;
- }
- }
- else {
- /* default, non tricky behavior */
- input = BKE_defvert_find_weight(dvert, wstate->defgroup_active);
-
- if (input == 0.0f) {
- switch (wstate->alert_mode) {
- case OB_DRAW_GROUPUSER_ACTIVE:
- return -1.0f;
- break;
- case OB_DRAW_GROUPUSER_ALL:
- if (BKE_defvert_is_weight_zero(dvert, wstate->defgroup_len)) {
- return -1.0f;
- }
- break;
- }
- }
- }
-
- /* Lock-Relative: display the fraction of current weight vs total unlocked weight. */
- if (wstate->flags & DRW_MESH_WEIGHT_STATE_LOCK_RELATIVE) {
- input = BKE_defvert_lock_relative_weight(
- input, dvert, wstate->defgroup_len, wstate->defgroup_locked, wstate->defgroup_unlocked);
- }
-
- CLAMP(input, 0.0f, 1.0f);
- return input;
-}
-
-static void *extract_weights_init(const MeshRenderData *mr,
- struct MeshBatchCache *cache,
- void *buf)
-{
- static GPUVertFormat format = {0};
- if (format.attr_len == 0) {
- GPU_vertformat_attr_add(&format, "weight", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
- }
- GPUVertBuf *vbo = buf;
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len);
-
- MeshExtract_Weight_Data *data = MEM_callocN(sizeof(*data), __func__);
- data->vbo_data = (float *)GPU_vertbuf_get_data(vbo);
- data->wstate = &cache->weight_state;
-
- if (data->wstate->defgroup_active == -1) {
- /* Nothing to show. */
- data->dvert = NULL;
- data->cd_ofs = -1;
- }
- else if (mr->extract_type == MR_EXTRACT_BMESH) {
- data->dvert = NULL;
- data->cd_ofs = CustomData_get_offset(&mr->bm->vdata, CD_MDEFORMVERT);
- }
- else {
- data->dvert = CustomData_get_layer(&mr->me->vdata, CD_MDEFORMVERT);
- data->cd_ofs = -1;
- }
- return data;
-}
-
-static void extract_weights_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
- void *_data)
-{
- MeshExtract_Weight_Data *data = _data;
- if (data->cd_ofs != -1) {
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
- {
- const MDeformVert *dvert = BM_ELEM_CD_GET_VOID_P(l->v, data->cd_ofs);
- data->vbo_data[l_index] = evaluate_vertex_weight(dvert, data->wstate);
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
- }
- else {
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
- {
- data->vbo_data[l_index] = evaluate_vertex_weight(NULL, data->wstate);
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
- }
-}
-
-static void extract_weights_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *_data)
-{
- MeshExtract_Weight_Data *data = _data;
- if (data->dvert != NULL) {
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
- const MDeformVert *dvert = &data->dvert[ml->v];
- data->vbo_data[ml_index] = evaluate_vertex_weight(dvert, data->wstate);
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
- }
- else {
- const MDeformVert *dvert = NULL;
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
- data->vbo_data[ml_index] = evaluate_vertex_weight(dvert, data->wstate);
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
- }
-}
-
-static void extract_weights_finish(const MeshRenderData *UNUSED(mr),
- struct MeshBatchCache *UNUSED(cache),
- void *UNUSED(buf),
- void *data)
-{
- MEM_freeN(data);
-}
-
-static const MeshExtract extract_weights = {
- .init = extract_weights_init,
- .iter_poly_bm = extract_weights_iter_poly_bm,
- .iter_poly_mesh = extract_weights_iter_poly_mesh,
- .finish = extract_weights_finish,
- .data_flag = 0,
- .use_threading = true,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Edit Mode Data / Flags
- * \{ */
-
-typedef struct EditLoopData {
- uchar v_flag;
- uchar e_flag;
- uchar crease;
- uchar bweight;
-} EditLoopData;
-
-static void mesh_render_data_face_flag(const MeshRenderData *mr,
- BMFace *efa,
- const int cd_ofs,
- EditLoopData *eattr)
-{
- if (efa == mr->efa_act) {
- eattr->v_flag |= VFLAG_FACE_ACTIVE;
- }
- if (BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
- eattr->v_flag |= VFLAG_FACE_SELECTED;
- }
-
- if (efa == mr->efa_act_uv) {
- eattr->v_flag |= VFLAG_FACE_UV_ACTIVE;
- }
- if ((cd_ofs != -1) && uvedit_face_select_test_ex(mr->toolsettings, (BMFace *)efa, cd_ofs)) {
- eattr->v_flag |= VFLAG_FACE_UV_SELECT;
- }
-
-#ifdef WITH_FREESTYLE
- if (mr->freestyle_face_ofs != -1) {
- const FreestyleFace *ffa = BM_ELEM_CD_GET_VOID_P(efa, mr->freestyle_face_ofs);
- if (ffa->flag & FREESTYLE_FACE_MARK) {
- eattr->v_flag |= VFLAG_FACE_FREESTYLE;
- }
- }
-#endif
-}
-
-static void mesh_render_data_edge_flag(const MeshRenderData *mr, BMEdge *eed, EditLoopData *eattr)
-{
- const ToolSettings *ts = mr->toolsettings;
- const bool is_vertex_select_mode = (ts != NULL) && (ts->selectmode & SCE_SELECT_VERTEX) != 0;
- const bool is_face_only_select_mode = (ts != NULL) && (ts->selectmode == SCE_SELECT_FACE);
-
- if (eed == mr->eed_act) {
- eattr->e_flag |= VFLAG_EDGE_ACTIVE;
- }
- if (!is_vertex_select_mode && BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
- eattr->e_flag |= VFLAG_EDGE_SELECTED;
- }
- if (is_vertex_select_mode && BM_elem_flag_test(eed->v1, BM_ELEM_SELECT) &&
- BM_elem_flag_test(eed->v2, BM_ELEM_SELECT)) {
- eattr->e_flag |= VFLAG_EDGE_SELECTED;
- eattr->e_flag |= VFLAG_VERT_SELECTED;
- }
- if (BM_elem_flag_test(eed, BM_ELEM_SEAM)) {
- eattr->e_flag |= VFLAG_EDGE_SEAM;
- }
- if (!BM_elem_flag_test(eed, BM_ELEM_SMOOTH)) {
- eattr->e_flag |= VFLAG_EDGE_SHARP;
- }
-
- /* Use active edge color for active face edges because
- * specular highlights make it hard to see T55456#510873.
- *
- * This isn't ideal since it can't be used when mixing edge/face modes
- * but it's still better than not being able to see the active face. */
- if (is_face_only_select_mode) {
- if (mr->efa_act != NULL) {
- if (BM_edge_in_face(eed, mr->efa_act)) {
- eattr->e_flag |= VFLAG_EDGE_ACTIVE;
- }
- }
- }
-
- /* Use a byte for value range */
- if (mr->crease_ofs != -1) {
- float crease = BM_ELEM_CD_GET_FLOAT(eed, mr->crease_ofs);
- if (crease > 0) {
- eattr->crease = (uchar)(crease * 255.0f);
- }
- }
- /* Use a byte for value range */
- if (mr->bweight_ofs != -1) {
- float bweight = BM_ELEM_CD_GET_FLOAT(eed, mr->bweight_ofs);
- if (bweight > 0) {
- eattr->bweight = (uchar)(bweight * 255.0f);
- }
- }
-#ifdef WITH_FREESTYLE
- if (mr->freestyle_edge_ofs != -1) {
- const FreestyleEdge *fed = BM_ELEM_CD_GET_VOID_P(eed, mr->freestyle_edge_ofs);
- if (fed->flag & FREESTYLE_EDGE_MARK) {
- eattr->e_flag |= VFLAG_EDGE_FREESTYLE;
- }
- }
-#endif
-}
-
-static void mesh_render_data_loop_flag(const MeshRenderData *mr,
- BMLoop *l,
- const int cd_ofs,
- EditLoopData *eattr)
-{
- if (cd_ofs == -1) {
- return;
- }
- MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_ofs);
- if (luv != NULL && (luv->flag & MLOOPUV_PINNED)) {
- eattr->v_flag |= VFLAG_VERT_UV_PINNED;
- }
- if (uvedit_uv_select_test_ex(mr->toolsettings, l, cd_ofs)) {
- eattr->v_flag |= VFLAG_VERT_UV_SELECT;
- }
-}
-
-static void mesh_render_data_loop_edge_flag(const MeshRenderData *mr,
- BMLoop *l,
- const int cd_ofs,
- EditLoopData *eattr)
-{
- if (cd_ofs == -1) {
- return;
- }
- if (uvedit_edge_select_test_ex(mr->toolsettings, l, cd_ofs)) {
- eattr->v_flag |= VFLAG_EDGE_UV_SELECT;
- eattr->v_flag |= VFLAG_VERT_UV_SELECT;
- }
-}
-
-static void mesh_render_data_vert_flag(const MeshRenderData *mr, BMVert *eve, EditLoopData *eattr)
-{
- if (eve == mr->eve_act) {
- eattr->e_flag |= VFLAG_VERT_ACTIVE;
- }
- if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
- eattr->e_flag |= VFLAG_VERT_SELECTED;
- }
-}
-
-static void *extract_edit_data_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf)
-{
- static GPUVertFormat format = {0};
- if (format.attr_len == 0) {
- /* WARNING: Adjust #EditLoopData struct accordingly. */
- GPU_vertformat_attr_add(&format, "data", GPU_COMP_U8, 4, GPU_FETCH_INT);
- GPU_vertformat_alias_add(&format, "flag");
- }
- GPUVertBuf *vbo = buf;
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len);
- return GPU_vertbuf_get_data(vbo);
-}
-
-static void extract_edit_data_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
- void *_data)
-{
-
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
- {
- EditLoopData *data = (EditLoopData *)_data + l_index;
- memset(data, 0x0, sizeof(*data));
- mesh_render_data_face_flag(mr, l->f, -1, data);
- mesh_render_data_edge_flag(mr, l->e, data);
- mesh_render_data_vert_flag(mr, l->v, data);
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
-}
-
-static void extract_edit_data_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *_data)
-{
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
- EditLoopData *data = (EditLoopData *)_data + ml_index;
- memset(data, 0x0, sizeof(*data));
- BMFace *efa = bm_original_face_get(mr, mp_index);
- BMEdge *eed = bm_original_edge_get(mr, ml->e);
- BMVert *eve = bm_original_vert_get(mr, ml->v);
- if (efa) {
- mesh_render_data_face_flag(mr, efa, -1, data);
- }
- if (eed) {
- mesh_render_data_edge_flag(mr, eed, data);
- }
- if (eve) {
- mesh_render_data_vert_flag(mr, eve, data);
- }
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
-}
-
-static void extract_edit_data_iter_ledge_bm(const MeshRenderData *mr,
- const ExtractLEdgeBMesh_Params *params,
- void *_data)
-{
- EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params)
- {
- EditLoopData *data = (EditLoopData *)_data + mr->loop_len + (ledge_index * 2);
- memset(data, 0x0, sizeof(*data) * 2);
- mesh_render_data_edge_flag(mr, eed, &data[0]);
- data[1] = data[0];
- mesh_render_data_vert_flag(mr, eed->v1, &data[0]);
- mesh_render_data_vert_flag(mr, eed->v2, &data[1]);
- }
- EXTRACT_LEDGE_FOREACH_BM_END;
-}
-
-static void extract_edit_data_iter_ledge_mesh(const MeshRenderData *mr,
- const ExtractLEdgeMesh_Params *params,
- void *_data)
-{
- EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr)
- {
- EditLoopData *data = (EditLoopData *)_data + mr->loop_len + ledge_index * 2;
- memset(data, 0x0, sizeof(*data) * 2);
- const int e_index = mr->ledges[ledge_index];
- BMEdge *eed = bm_original_edge_get(mr, e_index);
- BMVert *eve1 = bm_original_vert_get(mr, med->v1);
- BMVert *eve2 = bm_original_vert_get(mr, med->v2);
- if (eed) {
- mesh_render_data_edge_flag(mr, eed, &data[0]);
- data[1] = data[0];
- }
- if (eve1) {
- mesh_render_data_vert_flag(mr, eve1, &data[0]);
- }
- if (eve2) {
- mesh_render_data_vert_flag(mr, eve2, &data[1]);
- }
- }
- EXTRACT_LEDGE_FOREACH_MESH_END;
-}
-
-static void extract_edit_data_iter_lvert_bm(const MeshRenderData *mr,
- const ExtractLVertBMesh_Params *params,
- void *_data)
-{
- const int offset = mr->loop_len + (mr->edge_loose_len * 2);
- EXTRACT_LVERT_FOREACH_BM_BEGIN(eve, lvert_index, params)
- {
- EditLoopData *data = (EditLoopData *)_data + offset + lvert_index;
- memset(data, 0x0, sizeof(*data));
- mesh_render_data_vert_flag(mr, eve, data);
- }
- EXTRACT_LVERT_FOREACH_BM_END;
-}
-
-static void extract_edit_data_iter_lvert_mesh(const MeshRenderData *mr,
- const ExtractLVertMesh_Params *params,
- void *_data)
-{
- const int offset = mr->loop_len + (mr->edge_loose_len * 2);
- EXTRACT_LVERT_FOREACH_MESH_BEGIN(mv, lvert_index, params, mr)
- {
- EditLoopData *data = (EditLoopData *)_data + offset + lvert_index;
- memset(data, 0x0, sizeof(*data));
- const int v_index = mr->lverts[lvert_index];
- BMVert *eve = bm_original_vert_get(mr, v_index);
- if (eve) {
- mesh_render_data_vert_flag(mr, eve, data);
- }
- }
- EXTRACT_LVERT_FOREACH_MESH_END;
-}
-
-static const MeshExtract extract_edit_data = {
- .init = extract_edit_data_init,
- .iter_poly_bm = extract_edit_data_iter_poly_bm,
- .iter_poly_mesh = extract_edit_data_iter_poly_mesh,
- .iter_ledge_bm = extract_edit_data_iter_ledge_bm,
- .iter_ledge_mesh = extract_edit_data_iter_ledge_mesh,
- .iter_lvert_bm = extract_edit_data_iter_lvert_bm,
- .iter_lvert_mesh = extract_edit_data_iter_lvert_mesh,
- .data_flag = 0,
- .use_threading = true,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Edit UV Data / Flags
- * \{ */
-
-typedef struct MeshExtract_EditUVData_Data {
- EditLoopData *vbo_data;
- int cd_ofs;
-} MeshExtract_EditUVData_Data;
-
-static void *extract_edituv_data_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf)
-{
- static GPUVertFormat format = {0};
- if (format.attr_len == 0) {
- /* WARNING: Adjust #EditLoopData struct accordingly. */
- GPU_vertformat_attr_add(&format, "data", GPU_COMP_U8, 4, GPU_FETCH_INT);
- GPU_vertformat_alias_add(&format, "flag");
- }
-
- GPUVertBuf *vbo = buf;
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, mr->loop_len);
-
- CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata;
-
- MeshExtract_EditUVData_Data *data = MEM_callocN(sizeof(*data), __func__);
- data->vbo_data = (EditLoopData *)GPU_vertbuf_get_data(vbo);
- data->cd_ofs = CustomData_get_offset(cd_ldata, CD_MLOOPUV);
- return data;
-}
-
-static void extract_edituv_data_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
- void *_data)
-{
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
- {
- MeshExtract_EditUVData_Data *data = _data;
- EditLoopData *eldata = &data->vbo_data[l_index];
- memset(eldata, 0x0, sizeof(*eldata));
- mesh_render_data_loop_flag(mr, l, data->cd_ofs, eldata);
- mesh_render_data_face_flag(mr, l->f, data->cd_ofs, eldata);
- mesh_render_data_loop_edge_flag(mr, l, data->cd_ofs, eldata);
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
-}
-
-static void extract_edituv_data_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *_data)
-{
- MeshExtract_EditUVData_Data *data = _data;
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
- EditLoopData *eldata = &data->vbo_data[ml_index];
- memset(eldata, 0x0, sizeof(*eldata));
- BMFace *efa = bm_original_face_get(mr, mp_index);
- if (efa) {
- BMEdge *eed = bm_original_edge_get(mr, ml->e);
- BMVert *eve = bm_original_vert_get(mr, ml->v);
- if (eed && eve) {
- /* Loop on an edge endpoint. */
- BMLoop *l = BM_face_edge_share_loop(efa, eed);
- mesh_render_data_loop_flag(mr, l, data->cd_ofs, eldata);
- mesh_render_data_loop_edge_flag(mr, l, data->cd_ofs, eldata);
- }
- else {
- if (eed == NULL) {
- /* Find if the loop's vert is not part of an edit edge.
- * For this, we check if the previous loop was on an edge. */
- const int ml_index_last = mp->loopstart + mp->totloop - 1;
- const int l_prev = (ml_index == mp->loopstart) ? ml_index_last : (ml_index - 1);
- const MLoop *ml_prev = &mr->mloop[l_prev];
- eed = bm_original_edge_get(mr, ml_prev->e);
- }
- if (eed) {
- /* Mapped points on an edge between two edit verts. */
- BMLoop *l = BM_face_edge_share_loop(efa, eed);
- mesh_render_data_loop_edge_flag(mr, l, data->cd_ofs, eldata);
- }
- }
- }
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
-}
-
-static void extract_edituv_data_finish(const MeshRenderData *UNUSED(mr),
- struct MeshBatchCache *UNUSED(cache),
- void *UNUSED(buf),
- void *data)
-{
- MEM_freeN(data);
-}
-
-static const MeshExtract extract_edituv_data = {
- .init = extract_edituv_data_init,
- .iter_poly_bm = extract_edituv_data_iter_poly_bm,
- .iter_poly_mesh = extract_edituv_data_iter_poly_mesh,
- .finish = extract_edituv_data_finish,
- .data_flag = 0,
- .use_threading = true,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Edit UV area stretch
- * \{ */
-
-static void *extract_edituv_stretch_area_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf)
-{
- static GPUVertFormat format = {0};
- if (format.attr_len == 0) {
- GPU_vertformat_attr_add(&format, "ratio", GPU_COMP_I16, 1, GPU_FETCH_INT_TO_FLOAT_UNIT);
- }
-
- GPUVertBuf *vbo = buf;
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, mr->loop_len);
-
- return NULL;
-}
-
-BLI_INLINE float area_ratio_get(float area, float uvarea)
-{
- if (area >= FLT_EPSILON && uvarea >= FLT_EPSILON) {
- /* Tag inversion by using the sign. */
- return (area > uvarea) ? (uvarea / area) : -(area / uvarea);
- }
- return 0.0f;
-}
-
-BLI_INLINE float area_ratio_to_stretch(float ratio, float tot_ratio, float inv_tot_ratio)
-{
- ratio *= (ratio > 0.0f) ? tot_ratio : -inv_tot_ratio;
- return (ratio > 1.0f) ? (1.0f / ratio) : ratio;
-}
-
-static void mesh_edituv_stretch_area_finish(const MeshRenderData *mr,
- struct MeshBatchCache *cache,
- void *buf,
- void *UNUSED(data))
-{
- float tot_area = 0.0f, tot_uv_area = 0.0f;
- float *area_ratio = MEM_mallocN(sizeof(float) * mr->poly_len, __func__);
-
- if (mr->extract_type == MR_EXTRACT_BMESH) {
- CustomData *cd_ldata = &mr->bm->ldata;
- int uv_ofs = CustomData_get_offset(cd_ldata, CD_MLOOPUV);
-
- BMFace *efa;
- BMIter f_iter;
- int f;
- BM_ITER_MESH_INDEX (efa, &f_iter, mr->bm, BM_FACES_OF_MESH, f) {
- float area = BM_face_calc_area(efa);
- float uvarea = BM_face_calc_area_uv(efa, uv_ofs);
- tot_area += area;
- tot_uv_area += uvarea;
- area_ratio[f] = area_ratio_get(area, uvarea);
- }
- }
- else {
- BLI_assert(ELEM(mr->extract_type, MR_EXTRACT_MAPPED, MR_EXTRACT_MESH));
- const MLoopUV *uv_data = CustomData_get_layer(&mr->me->ldata, CD_MLOOPUV);
- const MPoly *mp = mr->mpoly;
- for (int mp_index = 0; mp_index < mr->poly_len; mp_index++, mp++) {
- float area = BKE_mesh_calc_poly_area(mp, &mr->mloop[mp->loopstart], mr->mvert);
- float uvarea = BKE_mesh_calc_poly_uv_area(mp, uv_data);
- tot_area += area;
- tot_uv_area += uvarea;
- area_ratio[mp_index] = area_ratio_get(area, uvarea);
- }
- }
-
- cache->tot_area = tot_area;
- cache->tot_uv_area = tot_uv_area;
-
- /* Convert in place to avoid an extra allocation */
- uint16_t *poly_stretch = (uint16_t *)area_ratio;
- for (int mp_index = 0; mp_index < mr->poly_len; mp_index++) {
- poly_stretch[mp_index] = area_ratio[mp_index] * SHRT_MAX;
- }
-
- /* Copy face data for each loop. */
- GPUVertBuf *vbo = buf;
- uint16_t *loop_stretch = (uint16_t *)GPU_vertbuf_get_data(vbo);
-
- if (mr->extract_type == MR_EXTRACT_BMESH) {
- BMFace *efa;
- BMIter f_iter;
- int f, l_index = 0;
- BM_ITER_MESH_INDEX (efa, &f_iter, mr->bm, BM_FACES_OF_MESH, f) {
- for (int i = 0; i < efa->len; i++, l_index++) {
- loop_stretch[l_index] = poly_stretch[f];
- }
- }
- }
- else {
- BLI_assert(ELEM(mr->extract_type, MR_EXTRACT_MAPPED, MR_EXTRACT_MESH));
- const MPoly *mp = mr->mpoly;
- for (int mp_index = 0, l_index = 0; mp_index < mr->poly_len; mp_index++, mp++) {
- for (int i = 0; i < mp->totloop; i++, l_index++) {
- loop_stretch[l_index] = poly_stretch[mp_index];
- }
- }
- }
-
- MEM_freeN(area_ratio);
-}
-
-static const MeshExtract extract_edituv_stretch_area = {
- .init = extract_edituv_stretch_area_init,
- .finish = mesh_edituv_stretch_area_finish,
- .data_flag = 0,
- .use_threading = false,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Edit UV angle stretch
- * \{ */
-
-typedef struct UVStretchAngle {
- int16_t angle;
- int16_t uv_angles[2];
-} UVStretchAngle;
-
-typedef struct MeshExtract_StretchAngle_Data {
- UVStretchAngle *vbo_data;
- MLoopUV *luv;
- float auv[2][2], last_auv[2];
- float av[2][3], last_av[3];
- int cd_ofs;
-} MeshExtract_StretchAngle_Data;
-
-static void compute_normalize_edge_vectors(float auv[2][2],
- float av[2][3],
- const float uv[2],
- const float uv_prev[2],
- const float co[3],
- const float co_prev[3])
-{
- /* Move previous edge. */
- copy_v2_v2(auv[0], auv[1]);
- copy_v3_v3(av[0], av[1]);
- /* 2d edge */
- sub_v2_v2v2(auv[1], uv_prev, uv);
- normalize_v2(auv[1]);
- /* 3d edge */
- sub_v3_v3v3(av[1], co_prev, co);
- normalize_v3(av[1]);
-}
-
-static short v2_to_short_angle(const float v[2])
-{
- return atan2f(v[1], v[0]) * (float)M_1_PI * SHRT_MAX;
-}
-
-static void edituv_get_edituv_stretch_angle(float auv[2][2],
- const float av[2][3],
- UVStretchAngle *r_stretch)
-{
- /* Send UV's to the shader and let it compute the aspect corrected angle. */
- r_stretch->uv_angles[0] = v2_to_short_angle(auv[0]);
- r_stretch->uv_angles[1] = v2_to_short_angle(auv[1]);
- /* Compute 3D angle here. */
- r_stretch->angle = angle_normalized_v3v3(av[0], av[1]) * (float)M_1_PI * SHRT_MAX;
-
-#if 0 /* here for reference, this is done in shader now. */
- float uvang = angle_normalized_v2v2(auv0, auv1);
- float ang = angle_normalized_v3v3(av0, av1);
- float stretch = fabsf(uvang - ang) / (float)M_PI;
- return 1.0f - pow2f(1.0f - stretch);
-#endif
-}
-
-static void *extract_edituv_stretch_angle_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf)
-{
- static GPUVertFormat format = {0};
- if (format.attr_len == 0) {
- /* Waning: adjust #UVStretchAngle struct accordingly. */
- GPU_vertformat_attr_add(&format, "angle", GPU_COMP_I16, 1, GPU_FETCH_INT_TO_FLOAT_UNIT);
- GPU_vertformat_attr_add(&format, "uv_angles", GPU_COMP_I16, 2, GPU_FETCH_INT_TO_FLOAT_UNIT);
- }
-
- GPUVertBuf *vbo = buf;
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, mr->loop_len);
-
- MeshExtract_StretchAngle_Data *data = MEM_callocN(sizeof(*data), __func__);
- data->vbo_data = (UVStretchAngle *)GPU_vertbuf_get_data(vbo);
-
- /* Special iterator needed to save about half of the computing cost. */
- if (mr->extract_type == MR_EXTRACT_BMESH) {
- data->cd_ofs = CustomData_get_offset(&mr->bm->ldata, CD_MLOOPUV);
- }
- else {
- BLI_assert(ELEM(mr->extract_type, MR_EXTRACT_MAPPED, MR_EXTRACT_MESH));
- data->luv = CustomData_get_layer(&mr->me->ldata, CD_MLOOPUV);
- }
- return data;
-}
-
-static void extract_edituv_stretch_angle_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
- void *_data)
-{
- MeshExtract_StretchAngle_Data *data = _data;
- float(*auv)[2] = data->auv, *last_auv = data->last_auv;
- float(*av)[3] = data->av, *last_av = data->last_av;
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
- {
- const MLoopUV *luv, *luv_next;
- BMLoop *l_next = l->next;
- BMFace *efa = l->f;
- if (l == BM_FACE_FIRST_LOOP(efa)) {
- /* First loop in face. */
- BMLoop *l_tmp = l->prev;
- BMLoop *l_next_tmp = l;
- luv = BM_ELEM_CD_GET_VOID_P(l_tmp, data->cd_ofs);
- luv_next = BM_ELEM_CD_GET_VOID_P(l_next_tmp, data->cd_ofs);
- compute_normalize_edge_vectors(auv,
- av,
- luv->uv,
- luv_next->uv,
- bm_vert_co_get(mr, l_tmp->v),
- bm_vert_co_get(mr, l_next_tmp->v));
- /* Save last edge. */
- copy_v2_v2(last_auv, auv[1]);
- copy_v3_v3(last_av, av[1]);
- }
- if (l_next == BM_FACE_FIRST_LOOP(efa)) {
- /* Move previous edge. */
- copy_v2_v2(auv[0], auv[1]);
- copy_v3_v3(av[0], av[1]);
- /* Copy already calculated last edge. */
- copy_v2_v2(auv[1], last_auv);
- copy_v3_v3(av[1], last_av);
- }
- else {
- luv = BM_ELEM_CD_GET_VOID_P(l, data->cd_ofs);
- luv_next = BM_ELEM_CD_GET_VOID_P(l_next, data->cd_ofs);
- compute_normalize_edge_vectors(
- auv, av, luv->uv, luv_next->uv, bm_vert_co_get(mr, l->v), bm_vert_co_get(mr, l_next->v));
- }
- edituv_get_edituv_stretch_angle(auv, av, &data->vbo_data[l_index]);
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
-}
-
-static void extract_edituv_stretch_angle_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *_data)
-{
- MeshExtract_StretchAngle_Data *data = _data;
-
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
- float(*auv)[2] = data->auv, *last_auv = data->last_auv;
- float(*av)[3] = data->av, *last_av = data->last_av;
- int l_next = ml_index + 1, ml_index_end = mp->loopstart + mp->totloop;
- const MVert *v, *v_next;
- if (ml_index == mp->loopstart) {
- /* First loop in face. */
- const int ml_index_last = ml_index_end - 1;
- const int l_next_tmp = mp->loopstart;
- v = &mr->mvert[mr->mloop[ml_index_last].v];
- v_next = &mr->mvert[mr->mloop[l_next_tmp].v];
- compute_normalize_edge_vectors(
- auv, av, data->luv[ml_index_last].uv, data->luv[l_next_tmp].uv, v->co, v_next->co);
- /* Save last edge. */
- copy_v2_v2(last_auv, auv[1]);
- copy_v3_v3(last_av, av[1]);
- }
- if (l_next == ml_index_end) {
- l_next = mp->loopstart;
- /* Move previous edge. */
- copy_v2_v2(auv[0], auv[1]);
- copy_v3_v3(av[0], av[1]);
- /* Copy already calculated last edge. */
- copy_v2_v2(auv[1], last_auv);
- copy_v3_v3(av[1], last_av);
- }
- else {
- v = &mr->mvert[mr->mloop[ml_index].v];
- v_next = &mr->mvert[mr->mloop[l_next].v];
- compute_normalize_edge_vectors(
- auv, av, data->luv[ml_index].uv, data->luv[l_next].uv, v->co, v_next->co);
- }
- edituv_get_edituv_stretch_angle(auv, av, &data->vbo_data[ml_index]);
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
-}
-
-static void extract_edituv_stretch_angle_finish(const MeshRenderData *UNUSED(mr),
- struct MeshBatchCache *UNUSED(cache),
- void *UNUSED(buf),
- void *data)
-{
- MEM_freeN(data);
-}
-
-static const MeshExtract extract_edituv_stretch_angle = {
- .init = extract_edituv_stretch_angle_init,
- .iter_poly_bm = extract_edituv_stretch_angle_iter_poly_bm,
- .iter_poly_mesh = extract_edituv_stretch_angle_iter_poly_mesh,
- .finish = extract_edituv_stretch_angle_finish,
- .data_flag = 0,
- .use_threading = false,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Edit Mesh Analysis Colors
- * \{ */
-
-static void *extract_mesh_analysis_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf)
-{
- static GPUVertFormat format = {0};
- if (format.attr_len == 0) {
- GPU_vertformat_attr_add(&format, "weight", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
- }
-
- GPUVertBuf *vbo = buf;
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, mr->loop_len);
-
- return NULL;
-}
-
-static void axis_from_enum_v3(float v[3], const char axis)
-{
- zero_v3(v);
- if (axis < 3) {
- v[axis] = 1.0f;
- }
- else {
- v[axis - 3] = -1.0f;
- }
-}
-
-BLI_INLINE float overhang_remap(float fac, float min, float max, float minmax_irange)
-{
- if (fac < min) {
- fac = 1.0f;
- }
- else if (fac > max) {
- fac = -1.0f;
- }
- else {
- fac = (fac - min) * minmax_irange;
- fac = 1.0f - fac;
- CLAMP(fac, 0.0f, 1.0f);
- }
- return fac;
-}
-
-static void statvis_calc_overhang(const MeshRenderData *mr, float *r_overhang)
-{
- const MeshStatVis *statvis = &mr->toolsettings->statvis;
- const float min = statvis->overhang_min / (float)M_PI;
- const float max = statvis->overhang_max / (float)M_PI;
- const char axis = statvis->overhang_axis;
- BMEditMesh *em = mr->edit_bmesh;
- BMIter iter;
- BMesh *bm = em->bm;
- BMFace *f;
- float dir[3];
- const float minmax_irange = 1.0f / (max - min);
-
- BLI_assert(min <= max);
-
- axis_from_enum_v3(dir, axis);
-
- /* now convert into global space */
- mul_transposed_mat3_m4_v3(mr->obmat, dir);
- normalize_v3(dir);
-
- if (mr->extract_type == MR_EXTRACT_BMESH) {
- int l_index = 0;
- BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
- float fac = angle_normalized_v3v3(bm_face_no_get(mr, f), dir) / (float)M_PI;
- fac = overhang_remap(fac, min, max, minmax_irange);
- for (int i = 0; i < f->len; i++, l_index++) {
- r_overhang[l_index] = fac;
- }
- }
- }
- else {
- const MPoly *mp = mr->mpoly;
- for (int mp_index = 0, l_index = 0; mp_index < mr->poly_len; mp_index++, mp++) {
- float fac = angle_normalized_v3v3(mr->poly_normals[mp_index], dir) / (float)M_PI;
- fac = overhang_remap(fac, min, max, minmax_irange);
- for (int i = 0; i < mp->totloop; i++, l_index++) {
- r_overhang[l_index] = fac;
- }
- }
- }
-}
-
-/**
- * Needed so we can use jitter values for face interpolation.
- */
-static void uv_from_jitter_v2(float uv[2])
-{
- uv[0] += 0.5f;
- uv[1] += 0.5f;
- if (uv[0] + uv[1] > 1.0f) {
- uv[0] = 1.0f - uv[0];
- uv[1] = 1.0f - uv[1];
- }
-
- clamp_v2(uv, 0.0f, 1.0f);
-}
-
-BLI_INLINE float thickness_remap(float fac, float min, float max, float minmax_irange)
-{
- /* important not '<=' */
- if (fac < max) {
- fac = (fac - min) * minmax_irange;
- fac = 1.0f - fac;
- CLAMP(fac, 0.0f, 1.0f);
- }
- else {
- fac = -1.0f;
- }
- return fac;
-}
-
-static void statvis_calc_thickness(const MeshRenderData *mr, float *r_thickness)
-{
- const float eps_offset = 0.00002f; /* values <= 0.00001 give errors */
- /* cheating to avoid another allocation */
- float *face_dists = r_thickness + (mr->loop_len - mr->poly_len);
- BMEditMesh *em = mr->edit_bmesh;
- const float scale = 1.0f / mat4_to_scale(mr->obmat);
- const MeshStatVis *statvis = &mr->toolsettings->statvis;
- const float min = statvis->thickness_min * scale;
- const float max = statvis->thickness_max * scale;
- const float minmax_irange = 1.0f / (max - min);
- const int samples = statvis->thickness_samples;
- float jit_ofs[32][2];
- BLI_assert(samples <= 32);
- BLI_assert(min <= max);
-
- copy_vn_fl(face_dists, mr->poly_len, max);
-
- BLI_jitter_init(jit_ofs, samples);
- for (int j = 0; j < samples; j++) {
- uv_from_jitter_v2(jit_ofs[j]);
- }
-
- if (mr->extract_type == MR_EXTRACT_BMESH) {
- BMesh *bm = em->bm;
- BM_mesh_elem_index_ensure(bm, BM_FACE);
-
- struct BMBVHTree *bmtree = BKE_bmbvh_new_from_editmesh(em, 0, NULL, false);
- struct BMLoop *(*looptris)[3] = em->looptris;
- for (int i = 0; i < mr->tri_len; i++) {
- BMLoop **ltri = looptris[i];
- const int index = BM_elem_index_get(ltri[0]->f);
- const float *cos[3] = {
- bm_vert_co_get(mr, ltri[0]->v),
- bm_vert_co_get(mr, ltri[1]->v),
- bm_vert_co_get(mr, ltri[2]->v),
- };
- float ray_co[3];
- float ray_no[3];
-
- normal_tri_v3(ray_no, cos[2], cos[1], cos[0]);
-
- for (int j = 0; j < samples; j++) {
- float dist = face_dists[index];
- interp_v3_v3v3v3_uv(ray_co, cos[0], cos[1], cos[2], jit_ofs[j]);
- madd_v3_v3fl(ray_co, ray_no, eps_offset);
-
- BMFace *f_hit = BKE_bmbvh_ray_cast(bmtree, ray_co, ray_no, 0.0f, &dist, NULL, NULL);
- if (f_hit && dist < face_dists[index]) {
- float angle_fac = fabsf(
- dot_v3v3(bm_face_no_get(mr, ltri[0]->f), bm_face_no_get(mr, f_hit)));
- angle_fac = 1.0f - angle_fac;
- angle_fac = angle_fac * angle_fac * angle_fac;
- angle_fac = 1.0f - angle_fac;
- dist /= angle_fac;
- if (dist < face_dists[index]) {
- face_dists[index] = dist;
- }
- }
- }
- }
- BKE_bmbvh_free(bmtree);
-
- BMIter iter;
- BMFace *f;
- int l_index = 0;
- BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
- float fac = face_dists[BM_elem_index_get(f)];
- fac = thickness_remap(fac, min, max, minmax_irange);
- for (int i = 0; i < f->len; i++, l_index++) {
- r_thickness[l_index] = fac;
- }
- }
- }
- else {
- BVHTreeFromMesh treeData = {NULL};
-
- BVHTree *tree = BKE_bvhtree_from_mesh_get(&treeData, mr->me, BVHTREE_FROM_LOOPTRI, 4);
- const MLoopTri *mlooptri = mr->mlooptri;
- for (int i = 0; i < mr->tri_len; i++, mlooptri++) {
- const int index = mlooptri->poly;
- const float *cos[3] = {mr->mvert[mr->mloop[mlooptri->tri[0]].v].co,
- mr->mvert[mr->mloop[mlooptri->tri[1]].v].co,
- mr->mvert[mr->mloop[mlooptri->tri[2]].v].co};
- float ray_co[3];
- float ray_no[3];
-
- normal_tri_v3(ray_no, cos[2], cos[1], cos[0]);
-
- for (int j = 0; j < samples; j++) {
- interp_v3_v3v3v3_uv(ray_co, cos[0], cos[1], cos[2], jit_ofs[j]);
- madd_v3_v3fl(ray_co, ray_no, eps_offset);
-
- BVHTreeRayHit hit;
- hit.index = -1;
- hit.dist = face_dists[index];
- if ((BLI_bvhtree_ray_cast(
- tree, ray_co, ray_no, 0.0f, &hit, treeData.raycast_callback, &treeData) != -1) &&
- hit.dist < face_dists[index]) {
- float angle_fac = fabsf(dot_v3v3(mr->poly_normals[index], hit.no));
- angle_fac = 1.0f - angle_fac;
- angle_fac = angle_fac * angle_fac * angle_fac;
- angle_fac = 1.0f - angle_fac;
- hit.dist /= angle_fac;
- if (hit.dist < face_dists[index]) {
- face_dists[index] = hit.dist;
- }
- }
- }
- }
-
- const MPoly *mp = mr->mpoly;
- for (int mp_index = 0, l_index = 0; mp_index < mr->poly_len; mp_index++, mp++) {
- float fac = face_dists[mp_index];
- fac = thickness_remap(fac, min, max, minmax_irange);
- for (int i = 0; i < mp->totloop; i++, l_index++) {
- r_thickness[l_index] = fac;
- }
- }
- }
-}
-
-struct BVHTree_OverlapData {
- const Mesh *me;
- const MLoopTri *mlooptri;
- float epsilon;
-};
-
-static bool bvh_overlap_cb(void *userdata, int index_a, int index_b, int UNUSED(thread))
-{
- struct BVHTree_OverlapData *data = userdata;
- const Mesh *me = data->me;
-
- const MLoopTri *tri_a = &data->mlooptri[index_a];
- const MLoopTri *tri_b = &data->mlooptri[index_b];
-
- if (UNLIKELY(tri_a->poly == tri_b->poly)) {
- return false;
- }
-
- const float *tri_a_co[3] = {me->mvert[me->mloop[tri_a->tri[0]].v].co,
- me->mvert[me->mloop[tri_a->tri[1]].v].co,
- me->mvert[me->mloop[tri_a->tri[2]].v].co};
- const float *tri_b_co[3] = {me->mvert[me->mloop[tri_b->tri[0]].v].co,
- me->mvert[me->mloop[tri_b->tri[1]].v].co,
- me->mvert[me->mloop[tri_b->tri[2]].v].co};
- float ix_pair[2][3];
- int verts_shared = 0;
-
- verts_shared = (ELEM(tri_a_co[0], UNPACK3(tri_b_co)) + ELEM(tri_a_co[1], UNPACK3(tri_b_co)) +
- ELEM(tri_a_co[2], UNPACK3(tri_b_co)));
-
- /* if 2 points are shared, bail out */
- if (verts_shared >= 2) {
- return false;
- }
-
- return (isect_tri_tri_v3(UNPACK3(tri_a_co), UNPACK3(tri_b_co), ix_pair[0], ix_pair[1]) &&
- /* if we share a vertex, check the intersection isn't a 'point' */
- ((verts_shared == 0) || (len_squared_v3v3(ix_pair[0], ix_pair[1]) > data->epsilon)));
-}
-
-static void statvis_calc_intersect(const MeshRenderData *mr, float *r_intersect)
-{
- BMEditMesh *em = mr->edit_bmesh;
-
- for (int l_index = 0; l_index < mr->loop_len; l_index++) {
- r_intersect[l_index] = -1.0f;
- }
-
- if (mr->extract_type == MR_EXTRACT_BMESH) {
- uint overlap_len;
- BMesh *bm = em->bm;
-
- BM_mesh_elem_index_ensure(bm, BM_FACE);
-
- struct BMBVHTree *bmtree = BKE_bmbvh_new_from_editmesh(em, 0, NULL, false);
- BVHTreeOverlap *overlap = BKE_bmbvh_overlap_self(bmtree, &overlap_len);
-
- if (overlap) {
- for (int i = 0; i < overlap_len; i++) {
- BMFace *f_hit_pair[2] = {
- em->looptris[overlap[i].indexA][0]->f,
- em->looptris[overlap[i].indexB][0]->f,
- };
- for (int j = 0; j < 2; j++) {
- BMFace *f_hit = f_hit_pair[j];
- BMLoop *l_first = BM_FACE_FIRST_LOOP(f_hit);
- int l_index = BM_elem_index_get(l_first);
- for (int k = 0; k < f_hit->len; k++, l_index++) {
- r_intersect[l_index] = 1.0f;
- }
- }
- }
- MEM_freeN(overlap);
- }
-
- BKE_bmbvh_free(bmtree);
- }
- else {
- uint overlap_len;
- BVHTreeFromMesh treeData = {NULL};
-
- BVHTree *tree = BKE_bvhtree_from_mesh_get(&treeData, mr->me, BVHTREE_FROM_LOOPTRI, 4);
-
- struct BVHTree_OverlapData data = {
- .me = mr->me, .mlooptri = mr->mlooptri, .epsilon = BLI_bvhtree_get_epsilon(tree)};
-
- BVHTreeOverlap *overlap = BLI_bvhtree_overlap(tree, tree, &overlap_len, bvh_overlap_cb, &data);
- if (overlap) {
- for (int i = 0; i < overlap_len; i++) {
- const MPoly *f_hit_pair[2] = {
- &mr->mpoly[mr->mlooptri[overlap[i].indexA].poly],
- &mr->mpoly[mr->mlooptri[overlap[i].indexB].poly],
- };
- for (int j = 0; j < 2; j++) {
- const MPoly *f_hit = f_hit_pair[j];
- int l_index = f_hit->loopstart;
- for (int k = 0; k < f_hit->totloop; k++, l_index++) {
- r_intersect[l_index] = 1.0f;
- }
- }
- }
- MEM_freeN(overlap);
- }
- }
-}
-
-BLI_INLINE float distort_remap(float fac, float min, float UNUSED(max), float minmax_irange)
-{
- if (fac >= min) {
- fac = (fac - min) * minmax_irange;
- CLAMP(fac, 0.0f, 1.0f);
- }
- else {
- /* fallback */
- fac = -1.0f;
- }
- return fac;
-}
-
-static void statvis_calc_distort(const MeshRenderData *mr, float *r_distort)
-{
- BMEditMesh *em = mr->edit_bmesh;
- const MeshStatVis *statvis = &mr->toolsettings->statvis;
- const float min = statvis->distort_min;
- const float max = statvis->distort_max;
- const float minmax_irange = 1.0f / (max - min);
-
- if (mr->extract_type == MR_EXTRACT_BMESH) {
- BMIter iter;
- BMesh *bm = em->bm;
- BMFace *f;
-
- if (mr->bm_vert_coords != NULL) {
- BKE_editmesh_cache_ensure_poly_normals(em, mr->edit_data);
-
- /* Most likely this is already valid, ensure just in case.
- * Needed for #BM_loop_calc_face_normal_safe_vcos. */
- BM_mesh_elem_index_ensure(em->bm, BM_VERT);
- }
-
- int l_index = 0;
- int f_index = 0;
- BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, f_index) {
- float fac = -1.0f;
-
- if (f->len > 3) {
- BMLoop *l_iter, *l_first;
-
- fac = 0.0f;
- l_iter = l_first = BM_FACE_FIRST_LOOP(f);
- do {
- const float *no_face;
- float no_corner[3];
- if (mr->bm_vert_coords != NULL) {
- no_face = mr->bm_poly_normals[f_index];
- BM_loop_calc_face_normal_safe_vcos(l_iter, no_face, mr->bm_vert_coords, no_corner);
- }
- else {
- no_face = f->no;
- BM_loop_calc_face_normal_safe(l_iter, no_corner);
- }
-
- /* simple way to detect (what is most likely) concave */
- if (dot_v3v3(no_face, no_corner) < 0.0f) {
- negate_v3(no_corner);
- }
- fac = max_ff(fac, angle_normalized_v3v3(no_face, no_corner));
-
- } while ((l_iter = l_iter->next) != l_first);
- fac *= 2.0f;
- }
-
- fac = distort_remap(fac, min, max, minmax_irange);
- for (int i = 0; i < f->len; i++, l_index++) {
- r_distort[l_index] = fac;
- }
- }
- }
- else {
- const MPoly *mp = mr->mpoly;
- for (int mp_index = 0, l_index = 0; mp_index < mr->poly_len; mp_index++, mp++) {
- float fac = -1.0f;
-
- if (mp->totloop > 3) {
- float *f_no = mr->poly_normals[mp_index];
- fac = 0.0f;
-
- for (int i = 1; i <= mp->totloop; i++) {
- const MLoop *l_prev = &mr->mloop[mp->loopstart + (i - 1) % mp->totloop];
- const MLoop *l_curr = &mr->mloop[mp->loopstart + (i + 0) % mp->totloop];
- const MLoop *l_next = &mr->mloop[mp->loopstart + (i + 1) % mp->totloop];
- float no_corner[3];
- normal_tri_v3(no_corner,
- mr->mvert[l_prev->v].co,
- mr->mvert[l_curr->v].co,
- mr->mvert[l_next->v].co);
- /* simple way to detect (what is most likely) concave */
- if (dot_v3v3(f_no, no_corner) < 0.0f) {
- negate_v3(no_corner);
- }
- fac = max_ff(fac, angle_normalized_v3v3(f_no, no_corner));
- }
- fac *= 2.0f;
- }
-
- fac = distort_remap(fac, min, max, minmax_irange);
- for (int i = 0; i < mp->totloop; i++, l_index++) {
- r_distort[l_index] = fac;
- }
- }
- }
-}
-
-BLI_INLINE float sharp_remap(float fac, float min, float UNUSED(max), float minmax_irange)
-{
- /* important not '>=' */
- if (fac > min) {
- fac = (fac - min) * minmax_irange;
- CLAMP(fac, 0.0f, 1.0f);
- }
- else {
- /* fallback */
- fac = -1.0f;
- }
- return fac;
-}
-
-static void statvis_calc_sharp(const MeshRenderData *mr, float *r_sharp)
-{
- BMEditMesh *em = mr->edit_bmesh;
- const MeshStatVis *statvis = &mr->toolsettings->statvis;
- const float min = statvis->sharp_min;
- const float max = statvis->sharp_max;
- const float minmax_irange = 1.0f / (max - min);
-
- /* Can we avoid this extra allocation? */
- float *vert_angles = MEM_mallocN(sizeof(float) * mr->vert_len, __func__);
- copy_vn_fl(vert_angles, mr->vert_len, -M_PI);
-
- if (mr->extract_type == MR_EXTRACT_BMESH) {
- BMIter iter;
- BMesh *bm = em->bm;
- BMFace *efa;
- BMEdge *e;
- /* first assign float values to verts */
- BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
- float angle = BM_edge_calc_face_angle_signed(e);
- float *col1 = &vert_angles[BM_elem_index_get(e->v1)];
- float *col2 = &vert_angles[BM_elem_index_get(e->v2)];
- *col1 = max_ff(*col1, angle);
- *col2 = max_ff(*col2, angle);
- }
- /* Copy vert value to loops. */
- BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
- BMLoop *l_iter, *l_first;
- l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
- do {
- int l_index = BM_elem_index_get(l_iter);
- int v_index = BM_elem_index_get(l_iter->v);
- r_sharp[l_index] = sharp_remap(vert_angles[v_index], min, max, minmax_irange);
- } while ((l_iter = l_iter->next) != l_first);
- }
- }
- else {
- /* first assign float values to verts */
- const MPoly *mp = mr->mpoly;
-
- EdgeHash *eh = BLI_edgehash_new_ex(__func__, mr->edge_len);
-
- for (int mp_index = 0; mp_index < mr->poly_len; mp_index++, mp++) {
- for (int i = 0; i < mp->totloop; i++) {
- const MLoop *l_curr = &mr->mloop[mp->loopstart + (i + 0) % mp->totloop];
- const MLoop *l_next = &mr->mloop[mp->loopstart + (i + 1) % mp->totloop];
- const MVert *v_curr = &mr->mvert[l_curr->v];
- const MVert *v_next = &mr->mvert[l_next->v];
- float angle;
- void **pval;
- bool value_is_init = BLI_edgehash_ensure_p(eh, l_curr->v, l_next->v, &pval);
- if (!value_is_init) {
- *pval = mr->poly_normals[mp_index];
- /* non-manifold edge, yet... */
- continue;
- }
- if (*pval != NULL) {
- const float *f1_no = mr->poly_normals[mp_index];
- const float *f2_no = *pval;
- angle = angle_normalized_v3v3(f1_no, f2_no);
- angle = is_edge_convex_v3(v_curr->co, v_next->co, f1_no, f2_no) ? angle : -angle;
- /* Tag as manifold. */
- *pval = NULL;
- }
- else {
- /* non-manifold edge */
- angle = DEG2RADF(90.0f);
- }
- float *col1 = &vert_angles[l_curr->v];
- float *col2 = &vert_angles[l_next->v];
- *col1 = max_ff(*col1, angle);
- *col2 = max_ff(*col2, angle);
- }
- }
- /* Remaining non manifold edges. */
- EdgeHashIterator *ehi = BLI_edgehashIterator_new(eh);
- for (; !BLI_edgehashIterator_isDone(ehi); BLI_edgehashIterator_step(ehi)) {
- if (BLI_edgehashIterator_getValue(ehi) != NULL) {
- uint v1, v2;
- const float angle = DEG2RADF(90.0f);
- BLI_edgehashIterator_getKey(ehi, &v1, &v2);
- float *col1 = &vert_angles[v1];
- float *col2 = &vert_angles[v2];
- *col1 = max_ff(*col1, angle);
- *col2 = max_ff(*col2, angle);
- }
- }
- BLI_edgehashIterator_free(ehi);
- BLI_edgehash_free(eh, NULL);
-
- const MLoop *ml = mr->mloop;
- for (int l_index = 0; l_index < mr->loop_len; l_index++, ml++) {
- r_sharp[l_index] = sharp_remap(vert_angles[ml->v], min, max, minmax_irange);
- }
- }
-
- MEM_freeN(vert_angles);
-}
-
-static void extract_mesh_analysis_finish(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf,
- void *UNUSED(data))
-{
- BLI_assert(mr->edit_bmesh);
-
- GPUVertBuf *vbo = buf;
- float *l_weight = (float *)GPU_vertbuf_get_data(vbo);
-
- switch (mr->toolsettings->statvis.type) {
- case SCE_STATVIS_OVERHANG:
- statvis_calc_overhang(mr, l_weight);
- break;
- case SCE_STATVIS_THICKNESS:
- statvis_calc_thickness(mr, l_weight);
- break;
- case SCE_STATVIS_INTERSECT:
- statvis_calc_intersect(mr, l_weight);
- break;
- case SCE_STATVIS_DISTORT:
- statvis_calc_distort(mr, l_weight);
- break;
- case SCE_STATVIS_SHARP:
- statvis_calc_sharp(mr, l_weight);
- break;
- }
-}
-
-static const MeshExtract extract_mesh_analysis = {
- .init = extract_mesh_analysis_init,
- .finish = extract_mesh_analysis_finish,
- /* This is not needed for all visualization types.
- * * Maybe split into different extract. */
- .data_flag = MR_DATA_POLY_NOR | MR_DATA_LOOPTRI,
- .use_threading = false,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Facedots positions
- * \{ */
-
-static void *extract_fdots_pos_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf)
-{
- static GPUVertFormat format = {0};
- if (format.attr_len == 0) {
- GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
- }
- GPUVertBuf *vbo = buf;
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, mr->poly_len);
- return GPU_vertbuf_get_data(vbo);
-}
-
-static void extract_fdots_pos_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
- void *data)
-{
- float(*center)[3] = data;
-
- EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr)
- {
- float *co = center[f_index];
- zero_v3(co);
-
- BMLoop *l_iter, *l_first;
- l_iter = l_first = BM_FACE_FIRST_LOOP(f);
- do {
- add_v3_v3(co, bm_vert_co_get(mr, l_iter->v));
- } while ((l_iter = l_iter->next) != l_first);
- mul_v3_fl(co, 1.0f / (float)f->len);
- }
- EXTRACT_POLY_FOREACH_BM_END;
-}
-
-static void extract_fdots_pos_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *data)
-{
- float(*center)[3] = (float(*)[3])data;
- const MVert *mvert = mr->mvert;
- const MLoop *mloop = mr->mloop;
-
- if (mr->use_subsurf_fdots) {
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
- const MVert *mv = &mr->mvert[ml->v];
- if (mv->flag & ME_VERT_FACEDOT) {
- copy_v3_v3(center[mp_index], mv->co);
- }
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
- }
- else {
- EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr)
- {
- float *co = center[mp_index];
- zero_v3(co);
-
- const MLoop *ml = &mloop[mp->loopstart];
- for (int i = 0; i < mp->totloop; i++, ml++) {
- const MVert *mv = &mvert[ml->v];
- add_v3_v3(center[mp_index], mv->co);
- }
- mul_v3_fl(co, 1.0f / (float)mp->totloop);
- }
- EXTRACT_POLY_FOREACH_MESH_END;
- }
-}
-
-static const MeshExtract extract_fdots_pos = {
- .init = extract_fdots_pos_init,
- .iter_poly_bm = extract_fdots_pos_iter_poly_bm,
- .iter_poly_mesh = extract_fdots_pos_iter_poly_mesh,
- .data_flag = 0,
- .use_threading = true,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Facedots Normal and edit flag
- * \{ */
-#define NOR_AND_FLAG_DEFAULT 0
-#define NOR_AND_FLAG_SELECT 1
-#define NOR_AND_FLAG_ACTIVE -1
-#define NOR_AND_FLAG_HIDDEN -2
-
-static void *extract_fdots_nor_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf)
-{
- static GPUVertFormat format = {0};
- if (format.attr_len == 0) {
- GPU_vertformat_attr_add(&format, "norAndFlag", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
- }
- GPUVertBuf *vbo = buf;
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, mr->poly_len);
-
- return NULL;
-}
-
-static void extract_fdots_nor_finish(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf,
- void *UNUSED(data))
-{
- static float invalid_normal[3] = {0.0f, 0.0f, 0.0f};
- GPUVertBuf *vbo = buf;
- GPUPackedNormal *nor = (GPUPackedNormal *)GPU_vertbuf_get_data(vbo);
- BMFace *efa;
-
- /* Quicker than doing it for each loop. */
- if (mr->extract_type == MR_EXTRACT_BMESH) {
- for (int f = 0; f < mr->poly_len; f++) {
- efa = BM_face_at_index(mr->bm, f);
- const bool is_face_hidden = BM_elem_flag_test(efa, BM_ELEM_HIDDEN);
- if (is_face_hidden || (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex &&
- mr->p_origindex[f] == ORIGINDEX_NONE)) {
- nor[f] = GPU_normal_convert_i10_v3(invalid_normal);
- nor[f].w = NOR_AND_FLAG_HIDDEN;
- }
- else {
- nor[f] = GPU_normal_convert_i10_v3(bm_face_no_get(mr, efa));
- /* Select / Active Flag. */
- nor[f].w = (BM_elem_flag_test(efa, BM_ELEM_SELECT) ?
- ((efa == mr->efa_act) ? NOR_AND_FLAG_ACTIVE : NOR_AND_FLAG_SELECT) :
- NOR_AND_FLAG_DEFAULT);
- }
- }
- }
- else {
- for (int f = 0; f < mr->poly_len; f++) {
- efa = bm_original_face_get(mr, f);
- const bool is_face_hidden = efa && BM_elem_flag_test(efa, BM_ELEM_HIDDEN);
- if (is_face_hidden || (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex &&
- mr->p_origindex[f] == ORIGINDEX_NONE)) {
- nor[f] = GPU_normal_convert_i10_v3(invalid_normal);
- nor[f].w = NOR_AND_FLAG_HIDDEN;
- }
- else {
- nor[f] = GPU_normal_convert_i10_v3(bm_face_no_get(mr, efa));
- /* Select / Active Flag. */
- nor[f].w = (BM_elem_flag_test(efa, BM_ELEM_SELECT) ?
- ((efa == mr->efa_act) ? NOR_AND_FLAG_ACTIVE : NOR_AND_FLAG_SELECT) :
- NOR_AND_FLAG_DEFAULT);
- }
- }
- }
-}
-
-static const MeshExtract extract_fdots_nor = {
- .init = extract_fdots_nor_init,
- .finish = extract_fdots_nor_finish,
- .data_flag = MR_DATA_POLY_NOR,
- .use_threading = false,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Facedots High Quality Normal and edit flag
- * \{ */
-static void *extract_fdots_nor_hq_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf)
-{
- static GPUVertFormat format = {0};
- if (format.attr_len == 0) {
- GPU_vertformat_attr_add(&format, "norAndFlag", GPU_COMP_I16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
- }
- GPUVertBuf *vbo = buf;
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, mr->poly_len);
-
- return NULL;
-}
-
-static void extract_fdots_nor_hq_finish(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf,
- void *UNUSED(data))
-{
- static float invalid_normal[3] = {0.0f, 0.0f, 0.0f};
- GPUVertBuf *vbo = buf;
- short *nor = (short *)GPU_vertbuf_get_data(vbo);
- BMFace *efa;
-
- /* Quicker than doing it for each loop. */
- if (mr->extract_type == MR_EXTRACT_BMESH) {
- for (int f = 0; f < mr->poly_len; f++) {
- efa = BM_face_at_index(mr->bm, f);
- const bool is_face_hidden = BM_elem_flag_test(efa, BM_ELEM_HIDDEN);
- if (is_face_hidden || (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex &&
- mr->p_origindex[f] == ORIGINDEX_NONE)) {
- normal_float_to_short_v3(&nor[f * 4], invalid_normal);
- nor[f * 4 + 3] = NOR_AND_FLAG_HIDDEN;
- }
- else {
- normal_float_to_short_v3(&nor[f * 4], bm_face_no_get(mr, efa));
- /* Select / Active Flag. */
- nor[f * 4 + 3] = (BM_elem_flag_test(efa, BM_ELEM_SELECT) ?
- ((efa == mr->efa_act) ? NOR_AND_FLAG_ACTIVE : NOR_AND_FLAG_SELECT) :
- NOR_AND_FLAG_DEFAULT);
- }
- }
- }
- else {
- for (int f = 0; f < mr->poly_len; f++) {
- efa = bm_original_face_get(mr, f);
- const bool is_face_hidden = efa && BM_elem_flag_test(efa, BM_ELEM_HIDDEN);
- if (is_face_hidden || (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex &&
- mr->p_origindex[f] == ORIGINDEX_NONE)) {
- normal_float_to_short_v3(&nor[f * 4], invalid_normal);
- nor[f * 4 + 3] = NOR_AND_FLAG_HIDDEN;
- }
- else {
- normal_float_to_short_v3(&nor[f * 4], bm_face_no_get(mr, efa));
- /* Select / Active Flag. */
- nor[f * 4 + 3] = (BM_elem_flag_test(efa, BM_ELEM_SELECT) ?
- ((efa == mr->efa_act) ? NOR_AND_FLAG_ACTIVE : NOR_AND_FLAG_SELECT) :
- NOR_AND_FLAG_DEFAULT);
- }
- }
- }
-}
-
-static const MeshExtract extract_fdots_nor_hq = {
- .init = extract_fdots_nor_hq_init,
- .finish = extract_fdots_nor_hq_finish,
- .data_flag = MR_DATA_POLY_NOR,
- .use_threading = false,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Facedots UV
- * \{ */
-
-typedef struct MeshExtract_FdotUV_Data {
- float (*vbo_data)[2];
- MLoopUV *uv_data;
- int cd_ofs;
-} MeshExtract_FdotUV_Data;
-
-static void *extract_fdots_uv_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf)
-{
- static GPUVertFormat format = {0};
- if (format.attr_len == 0) {
- GPU_vertformat_attr_add(&format, "u", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- GPU_vertformat_alias_add(&format, "au");
- GPU_vertformat_alias_add(&format, "pos");
- }
- GPUVertBuf *vbo = buf;
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, mr->poly_len);
-
- if (!mr->use_subsurf_fdots) {
- /* Clear so we can accumulate on it. */
- memset(GPU_vertbuf_get_data(vbo), 0x0, mr->poly_len * GPU_vertbuf_get_format(vbo)->stride);
- }
-
- MeshExtract_FdotUV_Data *data = MEM_callocN(sizeof(*data), __func__);
- data->vbo_data = (float(*)[2])GPU_vertbuf_get_data(vbo);
-
- if (mr->extract_type == MR_EXTRACT_BMESH) {
- data->cd_ofs = CustomData_get_offset(&mr->bm->ldata, CD_MLOOPUV);
- }
- else {
- data->uv_data = CustomData_get_layer(&mr->me->ldata, CD_MLOOPUV);
- }
- return data;
-}
-
-static void extract_fdots_uv_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
- void *_data)
-{
- MeshExtract_FdotUV_Data *data = _data;
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
- {
- float w = 1.0f / (float)l->f->len;
- const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, data->cd_ofs);
- madd_v2_v2fl(data->vbo_data[BM_elem_index_get(l->f)], luv->uv, w);
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
-}
-
-static void extract_fdots_uv_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *_data)
-{
- MeshExtract_FdotUV_Data *data = _data;
- if (mr->use_subsurf_fdots) {
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
- const MVert *mv = &mr->mvert[ml->v];
- if (mv->flag & ME_VERT_FACEDOT) {
- copy_v2_v2(data->vbo_data[mp_index], data->uv_data[ml_index].uv);
- }
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
- }
- else {
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
- float w = 1.0f / (float)mp->totloop;
- madd_v2_v2fl(data->vbo_data[mp_index], data->uv_data[ml_index].uv, w);
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
- }
-}
-
-static void extract_fdots_uv_finish(const MeshRenderData *UNUSED(mr),
- struct MeshBatchCache *UNUSED(cache),
- void *UNUSED(buf),
- void *data)
-{
- MEM_freeN(data);
-}
-
-static const MeshExtract extract_fdots_uv = {
- .init = extract_fdots_uv_init,
- .iter_poly_bm = extract_fdots_uv_iter_poly_bm,
- .iter_poly_mesh = extract_fdots_uv_iter_poly_mesh,
- .finish = extract_fdots_uv_finish,
- .data_flag = 0,
- .use_threading = true,
-};
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Facedots Edit UV flag
- * \{ */
-
-typedef struct MeshExtract_EditUVFdotData_Data {
- EditLoopData *vbo_data;
- int cd_ofs;
-} MeshExtract_EditUVFdotData_Data;
-
-static void *extract_fdots_edituv_data_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf)
-{
- static GPUVertFormat format = {0};
- if (format.attr_len == 0) {
- GPU_vertformat_attr_add(&format, "flag", GPU_COMP_U8, 4, GPU_FETCH_INT);
- }
- GPUVertBuf *vbo = buf;
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, mr->poly_len);
-
- MeshExtract_EditUVFdotData_Data *data = MEM_callocN(sizeof(*data), __func__);
- data->vbo_data = (EditLoopData *)GPU_vertbuf_get_data(vbo);
- data->cd_ofs = CustomData_get_offset(&mr->bm->ldata, CD_MLOOPUV);
- return data;
-}
-
-static void extract_fdots_edituv_data_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
- void *_data)
-{
- MeshExtract_EditUVFdotData_Data *data = _data;
- EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr)
- {
- EditLoopData *eldata = &data->vbo_data[BM_elem_index_get(f)];
- memset(eldata, 0x0, sizeof(*eldata));
- mesh_render_data_face_flag(mr, f, data->cd_ofs, eldata);
- }
- EXTRACT_POLY_FOREACH_BM_END;
-}
-
-static void extract_fdots_edituv_data_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *_data)
-{
- MeshExtract_EditUVFdotData_Data *data = _data;
- EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr)
- {
- EditLoopData *eldata = &data->vbo_data[mp_index];
- memset(eldata, 0x0, sizeof(*eldata));
- BMFace *efa = bm_original_face_get(mr, mp_index);
- if (efa) {
- mesh_render_data_face_flag(mr, efa, data->cd_ofs, eldata);
- }
- }
- EXTRACT_POLY_FOREACH_MESH_END;
-}
-
-static void extract_fdots_edituv_data_finish(const MeshRenderData *UNUSED(mr),
- struct MeshBatchCache *UNUSED(cache),
- void *UNUSED(buf),
- void *data)
-{
- MEM_freeN(data);
-}
-
-static const MeshExtract extract_fdots_edituv_data = {
- .init = extract_fdots_edituv_data_init,
- .iter_poly_bm = extract_fdots_edituv_data_iter_poly_bm,
- .iter_poly_mesh = extract_fdots_edituv_data_iter_poly_mesh,
- .finish = extract_fdots_edituv_data_finish,
- .data_flag = 0,
- .use_threading = true,
-};
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Skin Modifier Roots
- * \{ */
-
-typedef struct SkinRootData {
- float size;
- float local_pos[3];
-} SkinRootData;
-
-static void *extract_skin_roots_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf)
-{
- /* Exclusively for edit mode. */
- BLI_assert(mr->bm);
-
- static GPUVertFormat format = {0};
- if (format.attr_len == 0) {
- GPU_vertformat_attr_add(&format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
- GPU_vertformat_attr_add(&format, "local_pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
- }
- GPUVertBuf *vbo = buf;
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, mr->bm->totvert);
-
- SkinRootData *vbo_data = (SkinRootData *)GPU_vertbuf_get_data(vbo);
-
- int root_len = 0;
- int cd_ofs = CustomData_get_offset(&mr->bm->vdata, CD_MVERT_SKIN);
-
- BMIter iter;
- BMVert *eve;
- BM_ITER_MESH (eve, &iter, mr->bm, BM_VERTS_OF_MESH) {
- const MVertSkin *vs = BM_ELEM_CD_GET_VOID_P(eve, cd_ofs);
- if (vs->flag & MVERT_SKIN_ROOT) {
- vbo_data->size = (vs->radius[0] + vs->radius[1]) * 0.5f;
- copy_v3_v3(vbo_data->local_pos, bm_vert_co_get(mr, eve));
- vbo_data++;
- root_len++;
- }
- }
-
- /* It's really unlikely that all verts will be roots. Resize to avoid losing VRAM. */
- GPU_vertbuf_data_len_set(vbo, root_len);
-
- return NULL;
-}
-
-static const MeshExtract extract_skin_roots = {
- .init = extract_skin_roots_init,
- .data_flag = 0,
- .use_threading = false,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract Selection Index
- * \{ */
-
-static void *extract_select_idx_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf)
-{
- static GPUVertFormat format = {0};
- if (format.attr_len == 0) {
- /* TODO rename "color" to something more descriptive. */
- GPU_vertformat_attr_add(&format, "color", GPU_COMP_U32, 1, GPU_FETCH_INT);
- }
- GPUVertBuf *vbo = buf;
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len);
- return GPU_vertbuf_get_data(vbo);
-}
-
-/* TODO Use #glVertexID to get loop index and use the data structure on the CPU to retrieve the
- * select element associated with this loop ID. This would remove the need for this separate
- * index VBO's. We could upload the p/e/v_origindex as a buffer texture and sample it inside the
- * shader to output original index. */
-
-static void extract_poly_idx_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
- void *data)
-{
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
- {
- ((uint32_t *)data)[l_index] = BM_elem_index_get(l->f);
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
-}
-
-static void extract_edge_idx_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
- void *data)
-{
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
- {
- ((uint32_t *)data)[l_index] = BM_elem_index_get(l->e);
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
-}
-
-static void extract_vert_idx_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
- void *data)
-{
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
- {
- ((uint32_t *)data)[l_index] = BM_elem_index_get(l->v);
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
-}
-
-static void extract_edge_idx_iter_ledge_bm(const MeshRenderData *mr,
- const ExtractLEdgeBMesh_Params *params,
- void *data)
-{
- EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params)
- {
- ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = BM_elem_index_get(eed);
- ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = BM_elem_index_get(eed);
- }
- EXTRACT_LEDGE_FOREACH_BM_END;
-}
-
-static void extract_vert_idx_iter_ledge_bm(const MeshRenderData *mr,
- const ExtractLEdgeBMesh_Params *params,
- void *data)
-{
- EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params)
- {
- ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = BM_elem_index_get(eed->v1);
- ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = BM_elem_index_get(eed->v2);
- }
- EXTRACT_LEDGE_FOREACH_BM_END;
-}
-
-static void extract_vert_idx_iter_lvert_bm(const MeshRenderData *mr,
- const ExtractLVertBMesh_Params *params,
- void *data)
-{
- const int offset = mr->loop_len + (mr->edge_loose_len * 2);
- EXTRACT_LVERT_FOREACH_BM_BEGIN(eve, lvert_index, params)
- {
- ((uint32_t *)data)[offset + lvert_index] = BM_elem_index_get(eve);
- }
- EXTRACT_LVERT_FOREACH_BM_END;
-}
-
-static void extract_poly_idx_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *data)
-{
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
- ((uint32_t *)data)[ml_index] = (mr->p_origindex) ? mr->p_origindex[mp_index] : mp_index;
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
-}
-
-static void extract_edge_idx_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *data)
-{
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
- ((uint32_t *)data)[ml_index] = (mr->e_origindex) ? mr->e_origindex[ml->e] : ml->e;
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
-}
-
-static void extract_vert_idx_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *data)
-{
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
- ((uint32_t *)data)[ml_index] = (mr->v_origindex) ? mr->v_origindex[ml->v] : ml->v;
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
-}
-
-static void extract_edge_idx_iter_ledge_mesh(const MeshRenderData *mr,
- const ExtractLEdgeMesh_Params *params,
- void *data)
-{
- EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr)
- {
- const int e_index = mr->ledges[ledge_index];
- const int e_orig = (mr->e_origindex) ? mr->e_origindex[e_index] : e_index;
- ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = e_orig;
- ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = e_orig;
- }
- EXTRACT_LEDGE_FOREACH_MESH_END;
-}
-
-static void extract_vert_idx_iter_ledge_mesh(const MeshRenderData *mr,
- const ExtractLEdgeMesh_Params *params,
- void *data)
-{
- EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr)
- {
- int v1_orig = (mr->v_origindex) ? mr->v_origindex[med->v1] : med->v1;
- int v2_orig = (mr->v_origindex) ? mr->v_origindex[med->v2] : med->v2;
- ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 0] = v1_orig;
- ((uint32_t *)data)[mr->loop_len + ledge_index * 2 + 1] = v2_orig;
- }
- EXTRACT_LEDGE_FOREACH_MESH_END;
-}
-
-static void extract_vert_idx_iter_lvert_mesh(const MeshRenderData *mr,
- const ExtractLVertMesh_Params *params,
- void *data)
-{
- const int offset = mr->loop_len + (mr->edge_loose_len * 2);
- EXTRACT_LVERT_FOREACH_MESH_BEGIN(med, lvert_index, params, mr)
- {
- const int v_index = mr->lverts[lvert_index];
- const int v_orig = (mr->v_origindex) ? mr->v_origindex[v_index] : v_index;
- ((uint32_t *)data)[offset + lvert_index] = v_orig;
- }
- EXTRACT_LVERT_FOREACH_MESH_END;
-}
-
-static const MeshExtract extract_poly_idx = {
- .init = extract_select_idx_init,
- .iter_poly_bm = extract_poly_idx_iter_poly_bm,
- .iter_poly_mesh = extract_poly_idx_iter_poly_mesh,
- .data_flag = 0,
- .use_threading = true,
-};
-
-static const MeshExtract extract_edge_idx = {
- .init = extract_select_idx_init,
- .iter_poly_bm = extract_edge_idx_iter_poly_bm,
- .iter_poly_mesh = extract_edge_idx_iter_poly_mesh,
- .iter_ledge_bm = extract_edge_idx_iter_ledge_bm,
- .iter_ledge_mesh = extract_edge_idx_iter_ledge_mesh,
- .data_flag = 0,
- .use_threading = true,
-};
-
-static const MeshExtract extract_vert_idx = {
- .init = extract_select_idx_init,
- .iter_poly_bm = extract_vert_idx_iter_poly_bm,
- .iter_poly_mesh = extract_vert_idx_iter_poly_mesh,
- .iter_ledge_bm = extract_vert_idx_iter_ledge_bm,
- .iter_ledge_mesh = extract_vert_idx_iter_ledge_mesh,
- .iter_lvert_bm = extract_vert_idx_iter_lvert_bm,
- .iter_lvert_mesh = extract_vert_idx_iter_lvert_mesh,
- .data_flag = 0,
- .use_threading = true,
-};
-
-static void *extract_select_fdot_idx_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf)
-{
- static GPUVertFormat format = {0};
- if (format.attr_len == 0) {
- /* TODO rename "color" to something more descriptive. */
- GPU_vertformat_attr_add(&format, "color", GPU_COMP_U32, 1, GPU_FETCH_INT);
- }
- GPUVertBuf *vbo = buf;
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, mr->poly_len);
- return GPU_vertbuf_get_data(vbo);
-}
-
-static void extract_fdot_idx_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
- void *data)
-{
- EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr)
- {
- ((uint32_t *)data)[f_index] = f_index;
- }
- EXTRACT_POLY_FOREACH_BM_END;
-}
-
-static void extract_fdot_idx_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
- void *data)
-{
- if (mr->p_origindex != NULL) {
- EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr)
- {
- ((uint32_t *)data)[mp_index] = mr->p_origindex[mp_index];
- }
- EXTRACT_POLY_FOREACH_MESH_END;
- }
- else {
- EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr)
- {
- ((uint32_t *)data)[mp_index] = mp_index;
- }
- EXTRACT_POLY_FOREACH_MESH_END;
- }
-}
-
-static const MeshExtract extract_fdot_idx = {
- .init = extract_select_fdot_idx_init,
- .iter_poly_bm = extract_fdot_idx_iter_poly_bm,
- .iter_poly_mesh = extract_fdot_idx_iter_poly_mesh,
- .data_flag = 0,
- .use_threading = true,
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name ExtractTaskData
- * \{ */
-typedef struct ExtractUserData {
- void *user_data;
-} ExtractUserData;
-
-typedef enum ExtractTaskDataType {
- EXTRACT_MESH_EXTRACT,
- EXTRACT_LINES_LOOSE,
-} ExtractTaskDataType;
-
-typedef struct ExtractTaskData {
- void *next, *prev;
- const MeshRenderData *mr;
- struct MeshBatchCache *cache;
- const MeshExtract *extract;
- ExtractTaskDataType tasktype;
- eMRIterType iter_type;
- int start, end;
- /** Decremented each time a task is finished. */
- int32_t *task_counter;
- void *buf;
- ExtractUserData *user_data;
-} ExtractTaskData;
-
-static ExtractTaskData *extract_task_data_create_mesh_extract(const MeshRenderData *mr,
- struct MeshBatchCache *cache,
- const MeshExtract *extract,
- void *buf,
- int32_t *task_counter)
-{
- ExtractTaskData *taskdata = MEM_mallocN(sizeof(*taskdata), __func__);
- taskdata->next = NULL;
- taskdata->prev = NULL;
- taskdata->tasktype = EXTRACT_MESH_EXTRACT;
- taskdata->mr = mr;
- taskdata->cache = cache;
- taskdata->extract = extract;
- taskdata->buf = buf;
-
- /* #ExtractUserData is shared between the iterations as it holds counters to detect if the
- * extraction is finished. To make sure the duplication of the user_data does not create a new
- * instance of the counters we allocate the user_data in its own container.
- *
- * This structure makes sure that when extract_init is called, that the user data of all
- * iterations are updated. */
- taskdata->user_data = MEM_callocN(sizeof(ExtractUserData), __func__);
- taskdata->iter_type = mesh_extract_iter_type(extract);
- taskdata->task_counter = task_counter;
- taskdata->start = 0;
- taskdata->end = INT_MAX;
- return taskdata;
-}
-
-static ExtractTaskData *extract_task_data_create_lines_loose(const MeshRenderData *mr,
- struct MeshBatchCache *cache)
-{
- ExtractTaskData *taskdata = MEM_callocN(sizeof(*taskdata), __func__);
- taskdata->tasktype = EXTRACT_LINES_LOOSE;
- taskdata->mr = mr;
- taskdata->cache = cache;
- return taskdata;
-}
-
-static void extract_task_data_free(void *data)
-{
- ExtractTaskData *task_data = data;
- MEM_SAFE_FREE(task_data->user_data);
- MEM_freeN(task_data);
-}
-
-BLI_INLINE void mesh_extract_iter(const MeshRenderData *mr,
- const eMRIterType iter_type,
- int start,
- int end,
- const MeshExtract *extract,
- void *user_data)
-{
- switch (mr->extract_type) {
- case MR_EXTRACT_BMESH:
- if (iter_type & MR_ITER_LOOPTRI) {
- extract->iter_looptri_bm(mr,
- &(const ExtractTriBMesh_Params){
- .looptris = mr->edit_bmesh->looptris,
- .tri_range = {start, min_ii(mr->tri_len, end)},
- },
- user_data);
- }
- if (iter_type & MR_ITER_POLY) {
- extract->iter_poly_bm(mr,
- &(const ExtractPolyBMesh_Params){
- .poly_range = {start, min_ii(mr->poly_len, end)},
- },
- user_data);
- }
- if (iter_type & MR_ITER_LEDGE) {
- extract->iter_ledge_bm(mr,
- &(const ExtractLEdgeBMesh_Params){
- .ledge = mr->ledges,
- .ledge_range = {start, min_ii(mr->edge_loose_len, end)},
- },
- user_data);
- }
- if (iter_type & MR_ITER_LVERT) {
- extract->iter_lvert_bm(mr,
- &(const ExtractLVertBMesh_Params){
- .lvert = mr->lverts,
- .lvert_range = {start, min_ii(mr->vert_loose_len, end)},
- },
- user_data);
- }
- break;
- case MR_EXTRACT_MAPPED:
- case MR_EXTRACT_MESH:
- if (iter_type & MR_ITER_LOOPTRI) {
- extract->iter_looptri_mesh(mr,
- &(const ExtractTriMesh_Params){
- .mlooptri = mr->mlooptri,
- .tri_range = {start, min_ii(mr->tri_len, end)},
- },
- user_data);
- }
- if (iter_type & MR_ITER_POLY) {
- extract->iter_poly_mesh(mr,
- &(const ExtractPolyMesh_Params){
- .poly_range = {start, min_ii(mr->poly_len, end)},
- },
- user_data);
- }
- if (iter_type & MR_ITER_LEDGE) {
- extract->iter_ledge_mesh(mr,
- &(const ExtractLEdgeMesh_Params){
- .ledge = mr->ledges,
- .ledge_range = {start, min_ii(mr->edge_loose_len, end)},
- },
- user_data);
- }
- if (iter_type & MR_ITER_LVERT) {
- extract->iter_lvert_mesh(mr,
- &(const ExtractLVertMesh_Params){
- .lvert = mr->lverts,
- .lvert_range = {start, min_ii(mr->vert_loose_len, end)},
- },
- user_data);
- }
- break;
- }
-}
-
-static void extract_init(ExtractTaskData *data)
-{
- if (data->tasktype == EXTRACT_MESH_EXTRACT) {
- data->user_data->user_data = data->extract->init(data->mr, data->cache, data->buf);
- }
-}
-
-static void extract_run(void *__restrict taskdata)
-{
- ExtractTaskData *data = (ExtractTaskData *)taskdata;
- if (data->tasktype == EXTRACT_MESH_EXTRACT) {
- mesh_extract_iter(data->mr,
- data->iter_type,
- data->start,
- data->end,
- data->extract,
- data->user_data->user_data);
-
- /* If this is the last task, we do the finish function. */
- int remainin_tasks = atomic_sub_and_fetch_int32(data->task_counter, 1);
- if (remainin_tasks == 0 && data->extract->finish != NULL) {
- data->extract->finish(data->mr, data->cache, data->buf, data->user_data->user_data);
- }
- }
- else if (data->tasktype == EXTRACT_LINES_LOOSE) {
- extract_lines_loose_subbuffer(data->mr, data->cache);
- }
-}
-
-static void extract_init_and_run(void *__restrict taskdata)
-{
- extract_init((ExtractTaskData *)taskdata);
- extract_run(taskdata);
-}
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Task Node - Update Mesh Render Data
- * \{ */
-typedef struct MeshRenderDataUpdateTaskData {
- MeshRenderData *mr;
- eMRIterType iter_type;
- eMRDataType data_flag;
-} MeshRenderDataUpdateTaskData;
-
-static void mesh_render_data_update_task_data_free(MeshRenderDataUpdateTaskData *taskdata)
-{
- BLI_assert(taskdata);
- MeshRenderData *mr = taskdata->mr;
- mesh_render_data_free(mr);
- MEM_freeN(taskdata);
-}
-
-static void mesh_extract_render_data_node_exec(void *__restrict task_data)
-{
- MeshRenderDataUpdateTaskData *update_task_data = task_data;
- MeshRenderData *mr = update_task_data->mr;
- const eMRIterType iter_type = update_task_data->iter_type;
- const eMRDataType data_flag = update_task_data->data_flag;
-
- mesh_render_data_update_normals(mr, iter_type, data_flag);
- mesh_render_data_update_looptris(mr, iter_type, data_flag);
-}
-
-static struct TaskNode *mesh_extract_render_data_node_create(struct TaskGraph *task_graph,
- MeshRenderData *mr,
- const eMRIterType iter_type,
- const eMRDataType data_flag)
-{
- MeshRenderDataUpdateTaskData *task_data = MEM_mallocN(sizeof(MeshRenderDataUpdateTaskData),
- __func__);
- task_data->mr = mr;
- task_data->iter_type = iter_type;
- task_data->data_flag = data_flag;
-
- struct TaskNode *task_node = BLI_task_graph_node_create(
- task_graph,
- mesh_extract_render_data_node_exec,
- task_data,
- (TaskGraphNodeFreeFunction)mesh_render_data_update_task_data_free);
- return task_node;
-}
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Task Node - Extract Single Threaded
- * \{ */
-typedef struct ExtractSingleThreadedTaskData {
- ListBase task_datas;
-} ExtractSingleThreadedTaskData;
-
-static void extract_single_threaded_task_data_free(ExtractSingleThreadedTaskData *taskdata)
-{
- BLI_assert(taskdata);
- LISTBASE_FOREACH_MUTABLE (ExtractTaskData *, td, &taskdata->task_datas) {
- extract_task_data_free(td);
- }
- BLI_listbase_clear(&taskdata->task_datas);
- MEM_freeN(taskdata);
-}
-
-static void extract_single_threaded_task_node_exec(void *__restrict task_data)
-{
- ExtractSingleThreadedTaskData *extract_task_data = task_data;
- LISTBASE_FOREACH (ExtractTaskData *, td, &extract_task_data->task_datas) {
- extract_init_and_run(td);
- }
-}
-
-static struct TaskNode *extract_single_threaded_task_node_create(
- struct TaskGraph *task_graph, ExtractSingleThreadedTaskData *task_data)
-{
- struct TaskNode *task_node = BLI_task_graph_node_create(
- task_graph,
- extract_single_threaded_task_node_exec,
- task_data,
- (TaskGraphNodeFreeFunction)extract_single_threaded_task_data_free);
- return task_node;
-}
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \name Task Node - UserData Initializer
- * \{ */
-typedef struct UserDataInitTaskData {
- ListBase task_datas;
- int32_t *task_counters;
-
-} UserDataInitTaskData;
-
-static void user_data_init_task_data_free(UserDataInitTaskData *taskdata)
-{
- BLI_assert(taskdata);
- LISTBASE_FOREACH_MUTABLE (ExtractTaskData *, td, &taskdata->task_datas) {
- extract_task_data_free(td);
- }
- BLI_listbase_clear(&taskdata->task_datas);
- MEM_SAFE_FREE(taskdata->task_counters);
- MEM_freeN(taskdata);
-}
-
-static void user_data_init_task_data_exec(void *__restrict task_data)
-{
- UserDataInitTaskData *extract_task_data = task_data;
- LISTBASE_FOREACH (ExtractTaskData *, td, &extract_task_data->task_datas) {
- extract_init(td);
- }
-}
-
-static struct TaskNode *user_data_init_task_node_create(struct TaskGraph *task_graph,
- UserDataInitTaskData *task_data)
-{
- struct TaskNode *task_node = BLI_task_graph_node_create(
- task_graph,
- user_data_init_task_data_exec,
- task_data,
- (TaskGraphNodeFreeFunction)user_data_init_task_data_free);
- return task_node;
-}
-
-/** \} */
-/* ---------------------------------------------------------------------- */
-/** \name Extract Loop
- * \{ */
-
-static void extract_range_task_create(struct TaskGraph *task_graph,
- struct TaskNode *task_node_user_data_init,
- ExtractTaskData *taskdata,
- const eMRIterType type,
- int start,
- int length)
-{
- taskdata = MEM_dupallocN(taskdata);
- atomic_add_and_fetch_int32(taskdata->task_counter, 1);
- taskdata->iter_type = type;
- taskdata->start = start;
- taskdata->end = start + length;
- struct TaskNode *task_node = BLI_task_graph_node_create(
- task_graph, extract_run, taskdata, MEM_freeN);
- BLI_task_graph_edge_create(task_node_user_data_init, task_node);
-}
-
-static void extract_task_create(struct TaskGraph *task_graph,
- struct TaskNode *task_node_mesh_render_data,
- struct TaskNode *task_node_user_data_init,
- ListBase *single_threaded_task_datas,
- ListBase *user_data_init_task_datas,
- const Scene *scene,
- const MeshRenderData *mr,
- MeshBatchCache *cache,
- const MeshExtract *extract,
- void *buf,
- int32_t *task_counter)
-{
- BLI_assert(scene != NULL);
- const bool do_hq_normals = (scene->r.perf_flag & SCE_PERF_HQ_NORMALS) != 0 ||
- GPU_use_hq_normals_workaround();
- if (do_hq_normals) {
- if (extract == &extract_lnor) {
- extract = &extract_lnor_hq;
- }
- else if (extract == &extract_pos_nor) {
- extract = &extract_pos_nor_hq;
- }
- else if (extract == &extract_tan) {
- extract = &extract_tan_hq;
- }
- else if (extract == &extract_fdots_nor) {
- extract = &extract_fdots_nor_hq;
- }
- }
-
- /* Divide extraction of the VBO/IBO into sensible chunks of works. */
- ExtractTaskData *taskdata = extract_task_data_create_mesh_extract(
- mr, cache, extract, buf, task_counter);
-
- /* Simple heuristic. */
- const int chunk_size = 8192;
- const bool use_thread = (mr->loop_len + mr->loop_loose_len) > chunk_size;
- if (use_thread && extract->use_threading) {
-
- /* Divide task into sensible chunks. */
- if (taskdata->iter_type & MR_ITER_LOOPTRI) {
- for (int i = 0; i < mr->tri_len; i += chunk_size) {
- extract_range_task_create(
- task_graph, task_node_user_data_init, taskdata, MR_ITER_LOOPTRI, i, chunk_size);
- }
- }
- if (taskdata->iter_type & MR_ITER_POLY) {
- for (int i = 0; i < mr->poly_len; i += chunk_size) {
- extract_range_task_create(
- task_graph, task_node_user_data_init, taskdata, MR_ITER_POLY, i, chunk_size);
- }
- }
- if (taskdata->iter_type & MR_ITER_LEDGE) {
- for (int i = 0; i < mr->edge_loose_len; i += chunk_size) {
- extract_range_task_create(
- task_graph, task_node_user_data_init, taskdata, MR_ITER_LEDGE, i, chunk_size);
- }
- }
- if (taskdata->iter_type & MR_ITER_LVERT) {
- for (int i = 0; i < mr->vert_loose_len; i += chunk_size) {
- extract_range_task_create(
- task_graph, task_node_user_data_init, taskdata, MR_ITER_LVERT, i, chunk_size);
- }
- }
- BLI_addtail(user_data_init_task_datas, taskdata);
- }
- else if (use_thread) {
- /* One task for the whole VBO. */
- (*task_counter)++;
- struct TaskNode *one_task = BLI_task_graph_node_create(
- task_graph, extract_init_and_run, taskdata, extract_task_data_free);
- BLI_task_graph_edge_create(task_node_mesh_render_data, one_task);
- }
- else {
- /* Single threaded extraction. */
- (*task_counter)++;
- BLI_addtail(single_threaded_task_datas, taskdata);
- }
-}
-
-void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph,
- MeshBatchCache *cache,
- MeshBufferCache mbc,
- Mesh *me,
-
- const bool is_editmode,
- const bool is_paint_mode,
- const bool is_mode_active,
- const float obmat[4][4],
- const bool do_final,
- const bool do_uvedit,
- const bool use_subsurf_fdots,
- const DRW_MeshCDMask *cd_layer_used,
- const Scene *scene,
- const ToolSettings *ts,
- const bool use_hide)
-{
- /* For each mesh where batches needs to be updated a sub-graph will be added to the task_graph.
- * This sub-graph starts with an extract_render_data_node. This fills/converts the required data
- * from Mesh.
- *
- * Small extractions and extractions that can't be multi-threaded are grouped in a single
- * `extract_single_threaded_task_node`.
- *
- * Other extractions will create a node for each loop exceeding 8192 items. these nodes are
- * linked to the `user_data_init_task_node`. the `user_data_init_task_node` prepares the
- * user_data needed for the extraction based on the data extracted from the mesh.
- * counters are used to check if the finalize of a task has to be called.
- *
- * Mesh extraction sub graph
- *
- * +----------------------+
- * +-----> | extract_task1_loop_1 |
- * | +----------------------+
- * +------------------+ +----------------------+ +----------------------+
- * | mesh_render_data | --> | | --> | extract_task1_loop_2 |
- * +------------------+ | | +----------------------+
- * | | | +----------------------+
- * | | user_data_init | --> | extract_task2_loop_1 |
- * v | | +----------------------+
- * +------------------+ | | +----------------------+
- * | single_threaded | | | --> | extract_task2_loop_2 |
- * +------------------+ +----------------------+ +----------------------+
- * | +----------------------+
- * +-----> | extract_task2_loop_3 |
- * +----------------------+
- */
- eMRIterType iter_flag = 0;
- eMRDataType data_flag = 0;
-
- const bool do_lines_loose_subbuffer = mbc.ibo.lines_loose != NULL;
-
-#define TEST_ASSIGN(type, type_lowercase, name) \
- do { \
- if (DRW_TEST_ASSIGN_##type(mbc.type_lowercase.name)) { \
- iter_flag |= mesh_extract_iter_type(&extract_##name); \
- data_flag |= extract_##name.data_flag; \
- } \
- } while (0)
-
- TEST_ASSIGN(VBO, vbo, pos_nor);
- TEST_ASSIGN(VBO, vbo, lnor);
- TEST_ASSIGN(VBO, vbo, uv);
- TEST_ASSIGN(VBO, vbo, tan);
- TEST_ASSIGN(VBO, vbo, vcol);
- TEST_ASSIGN(VBO, vbo, sculpt_data);
- TEST_ASSIGN(VBO, vbo, orco);
- TEST_ASSIGN(VBO, vbo, edge_fac);
- TEST_ASSIGN(VBO, vbo, weights);
- TEST_ASSIGN(VBO, vbo, edit_data);
- TEST_ASSIGN(VBO, vbo, edituv_data);
- TEST_ASSIGN(VBO, vbo, edituv_stretch_area);
- TEST_ASSIGN(VBO, vbo, edituv_stretch_angle);
- TEST_ASSIGN(VBO, vbo, mesh_analysis);
- TEST_ASSIGN(VBO, vbo, fdots_pos);
- TEST_ASSIGN(VBO, vbo, fdots_nor);
- TEST_ASSIGN(VBO, vbo, fdots_uv);
- TEST_ASSIGN(VBO, vbo, fdots_edituv_data);
- TEST_ASSIGN(VBO, vbo, poly_idx);
- TEST_ASSIGN(VBO, vbo, edge_idx);
- TEST_ASSIGN(VBO, vbo, vert_idx);
- TEST_ASSIGN(VBO, vbo, fdot_idx);
- TEST_ASSIGN(VBO, vbo, skin_roots);
-
- TEST_ASSIGN(IBO, ibo, tris);
- TEST_ASSIGN(IBO, ibo, lines);
- TEST_ASSIGN(IBO, ibo, points);
- TEST_ASSIGN(IBO, ibo, fdots);
- TEST_ASSIGN(IBO, ibo, lines_paint_mask);
- TEST_ASSIGN(IBO, ibo, lines_adjacency);
- TEST_ASSIGN(IBO, ibo, edituv_tris);
- TEST_ASSIGN(IBO, ibo, edituv_lines);
- TEST_ASSIGN(IBO, ibo, edituv_points);
- TEST_ASSIGN(IBO, ibo, edituv_fdots);
-
- if (do_lines_loose_subbuffer) {
- iter_flag |= MR_ITER_LEDGE;
- }
-
-#undef TEST_ASSIGN
-
-#ifdef DEBUG_TIME
- double rdata_start = PIL_check_seconds_timer();
-#endif
-
- MeshRenderData *mr = mesh_render_data_create(me,
- is_editmode,
- is_paint_mode,
- is_mode_active,
- obmat,
- do_final,
- do_uvedit,
- cd_layer_used,
- ts,
- iter_flag,
- data_flag);
- mr->use_hide = use_hide;
- mr->use_subsurf_fdots = use_subsurf_fdots;
- mr->use_final_mesh = do_final;
-
-#ifdef DEBUG_TIME
- double rdata_end = PIL_check_seconds_timer();
-#endif
-
- size_t counters_size = (sizeof(mbc) / sizeof(void *)) * sizeof(int32_t);
- int32_t *task_counters = MEM_callocN(counters_size, __func__);
- int counter_used = 0;
-
- struct TaskNode *task_node_mesh_render_data = mesh_extract_render_data_node_create(
- task_graph, mr, iter_flag, data_flag);
- ExtractSingleThreadedTaskData *single_threaded_task_data = MEM_callocN(
- sizeof(ExtractSingleThreadedTaskData), __func__);
- UserDataInitTaskData *user_data_init_task_data = MEM_callocN(sizeof(UserDataInitTaskData),
- __func__);
- user_data_init_task_data->task_counters = task_counters;
- struct TaskNode *task_node_user_data_init = user_data_init_task_node_create(
- task_graph, user_data_init_task_data);
-
-#define EXTRACT(buf, name) \
- if (mbc.buf.name) { \
- extract_task_create(task_graph, \
- task_node_mesh_render_data, \
- task_node_user_data_init, \
- &single_threaded_task_data->task_datas, \
- &user_data_init_task_data->task_datas, \
- scene, \
- mr, \
- cache, \
- &extract_##name, \
- mbc.buf.name, \
- &task_counters[counter_used++]); \
- } \
- ((void)0)
-
- EXTRACT(vbo, pos_nor);
- EXTRACT(vbo, lnor);
- EXTRACT(vbo, uv);
- EXTRACT(vbo, tan);
- EXTRACT(vbo, vcol);
- EXTRACT(vbo, sculpt_data);
- EXTRACT(vbo, orco);
- EXTRACT(vbo, edge_fac);
- EXTRACT(vbo, weights);
- EXTRACT(vbo, edit_data);
- EXTRACT(vbo, edituv_data);
- EXTRACT(vbo, edituv_stretch_area);
- EXTRACT(vbo, edituv_stretch_angle);
- EXTRACT(vbo, mesh_analysis);
- EXTRACT(vbo, fdots_pos);
- EXTRACT(vbo, fdots_nor);
- EXTRACT(vbo, fdots_uv);
- EXTRACT(vbo, fdots_edituv_data);
- EXTRACT(vbo, poly_idx);
- EXTRACT(vbo, edge_idx);
- EXTRACT(vbo, vert_idx);
- EXTRACT(vbo, fdot_idx);
- EXTRACT(vbo, skin_roots);
-
- EXTRACT(ibo, tris);
- if (mbc.ibo.lines) {
- /* When `lines` and `lines_loose` are requested, schedule lines extraction that also creates
- * the `lines_loose` sub-buffer. */
- const MeshExtract *lines_extractor = do_lines_loose_subbuffer ?
- &extract_lines_with_lines_loose :
- &extract_lines;
- extract_task_create(task_graph,
- task_node_mesh_render_data,
- task_node_user_data_init,
- &single_threaded_task_data->task_datas,
- &user_data_init_task_data->task_datas,
- scene,
- mr,
- cache,
- lines_extractor,
- mbc.ibo.lines,
- &task_counters[counter_used++]);
- }
- else {
- if (do_lines_loose_subbuffer) {
- ExtractTaskData *taskdata = extract_task_data_create_lines_loose(mr, cache);
- BLI_addtail(&single_threaded_task_data->task_datas, taskdata);
- }
- }
- EXTRACT(ibo, points);
- EXTRACT(ibo, fdots);
- EXTRACT(ibo, lines_paint_mask);
- EXTRACT(ibo, lines_adjacency);
- EXTRACT(ibo, edituv_tris);
- EXTRACT(ibo, edituv_lines);
- EXTRACT(ibo, edituv_points);
- EXTRACT(ibo, edituv_fdots);
-
- /* Only create the edge when there is user data that needs to be initialized.
- * The task is still part of the graph so the task_data will be freed when the graph is freed.
- */
- if (!BLI_listbase_is_empty(&user_data_init_task_data->task_datas)) {
- BLI_task_graph_edge_create(task_node_mesh_render_data, task_node_user_data_init);
- }
-
- if (!BLI_listbase_is_empty(&single_threaded_task_data->task_datas)) {
- struct TaskNode *task_node = extract_single_threaded_task_node_create(
- task_graph, single_threaded_task_data);
- BLI_task_graph_edge_create(task_node_mesh_render_data, task_node);
- }
- else {
- extract_single_threaded_task_data_free(single_threaded_task_data);
- }
-
- /* Trigger the sub-graph for this mesh. */
- BLI_task_graph_node_push_work(task_node_mesh_render_data);
-
-#undef EXTRACT
-
-#ifdef DEBUG_TIME
- BLI_task_graph_work_and_wait(task_graph);
- double end = PIL_check_seconds_timer();
-
- static double avg = 0;
- static double avg_fps = 0;
- static double avg_rdata = 0;
- static double end_prev = 0;
-
- if (end_prev == 0) {
- end_prev = end;
- }
-
- avg = avg * 0.95 + (end - rdata_end) * 0.05;
- avg_fps = avg_fps * 0.95 + (end - end_prev) * 0.05;
- avg_rdata = avg_rdata * 0.95 + (rdata_end - rdata_start) * 0.05;
-
- printf(
- "rdata %.0fms iter %.0fms (frame %.0fms)\n", avg_rdata * 1000, avg * 1000, avg_fps * 1000);
-
- end_prev = end;
-#endif
-}
-
-/** \} */
diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc
new file mode 100644
index 00000000000..c6b749fe11a
--- /dev/null
+++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc
@@ -0,0 +1,813 @@
+/*
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2017 by Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup draw
+ *
+ * \brief Extraction of Mesh data into VBO to feed to GPU.
+ */
+#include "MEM_guardedalloc.h"
+
+#include <optional>
+
+#include "atomic_ops.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_scene_types.h"
+
+#include "BLI_array.hh"
+#include "BLI_math_bits.h"
+#include "BLI_task.h"
+#include "BLI_vector.hh"
+
+#include "BKE_editmesh.h"
+
+#include "GPU_capabilities.h"
+
+#include "draw_cache_extract.h"
+#include "draw_cache_extract_mesh_private.h"
+#include "draw_cache_inline.h"
+
+// #define DEBUG_TIME
+
+#ifdef DEBUG_TIME
+# include "PIL_time_utildefines.h"
+#endif
+
+#define MIM_RANGE_LEN 1024
+
+namespace blender::draw {
+
+/* ---------------------------------------------------------------------- */
+/** \name Mesh Elements Extract Struct
+ * \{ */
+using TaskId = int;
+using TaskLen = int;
+
+struct ExtractorRunData {
+ /* Extractor where this run data belongs to. */
+ const MeshExtract *extractor;
+ /* During iteration the VBO/IBO that is being build. */
+ void *buffer = nullptr;
+ uint32_t data_offset = 0;
+
+ ExtractorRunData(const MeshExtract *extractor) : extractor(extractor)
+ {
+ }
+
+#ifdef WITH_CXX_GUARDEDALLOC
+ MEM_CXX_CLASS_ALLOC_FUNCS("DRAW:ExtractorRunData")
+#endif
+};
+
+class ExtractorRunDatas : public Vector<ExtractorRunData> {
+ public:
+ void filter_into(ExtractorRunDatas &result, eMRIterType iter_type) const
+ {
+ for (const ExtractorRunData &data : *this) {
+ const MeshExtract *extractor = data.extractor;
+ if ((iter_type & MR_ITER_LOOPTRI) && extractor->iter_looptri_bm) {
+ BLI_assert(extractor->iter_looptri_mesh);
+ result.append(data);
+ continue;
+ }
+ if ((iter_type & MR_ITER_POLY) && extractor->iter_poly_bm) {
+ BLI_assert(extractor->iter_poly_mesh);
+ result.append(data);
+ continue;
+ }
+ if ((iter_type & MR_ITER_LEDGE) && extractor->iter_ledge_bm) {
+ BLI_assert(extractor->iter_ledge_mesh);
+ result.append(data);
+ continue;
+ }
+ if ((iter_type & MR_ITER_LVERT) && extractor->iter_lvert_bm) {
+ BLI_assert(extractor->iter_lvert_mesh);
+ result.append(data);
+ continue;
+ }
+ }
+ }
+
+ void filter_threaded_extractors_into(ExtractorRunDatas &result)
+ {
+ for (const ExtractorRunData &data : *this) {
+ const MeshExtract *extractor = data.extractor;
+ if (extractor->use_threading) {
+ result.append(extractor);
+ }
+ }
+ }
+
+ eMRIterType iter_types() const
+ {
+ eMRIterType iter_type = static_cast<eMRIterType>(0);
+
+ for (const ExtractorRunData &data : *this) {
+ const MeshExtract *extractor = data.extractor;
+ iter_type |= mesh_extract_iter_type(extractor);
+ }
+ return iter_type;
+ }
+
+ const uint iter_types_len() const
+ {
+ const eMRIterType iter_type = iter_types();
+ uint bits = static_cast<uint>(iter_type);
+ return count_bits_i(bits);
+ }
+
+ eMRDataType data_types()
+ {
+ eMRDataType data_type = static_cast<eMRDataType>(0);
+ for (const ExtractorRunData &data : *this) {
+ const MeshExtract *extractor = data.extractor;
+ data_type |= extractor->data_type;
+ }
+ return data_type;
+ }
+
+ size_t data_size_total()
+ {
+ size_t data_size = 0;
+ for (const ExtractorRunData &data : *this) {
+ const MeshExtract *extractor = data.extractor;
+ data_size += extractor->data_size;
+ }
+ return data_size;
+ }
+
+#ifdef WITH_CXX_GUARDEDALLOC
+ MEM_CXX_CLASS_ALLOC_FUNCS("DRAW:ExtractorRunDatas")
+#endif
+};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name ExtractTaskData
+ * \{ */
+struct ExtractTaskData {
+ const MeshRenderData *mr = nullptr;
+ MeshBatchCache *cache = nullptr;
+ ExtractorRunDatas *extractors = nullptr;
+ MeshBufferCache *mbc = nullptr;
+
+ eMRIterType iter_type;
+ bool use_threading = false;
+
+ ExtractTaskData(const MeshRenderData *mr,
+ struct MeshBatchCache *cache,
+ ExtractorRunDatas *extractors,
+ MeshBufferCache *mbc,
+ const bool use_threading)
+ : mr(mr), cache(cache), extractors(extractors), mbc(mbc), use_threading(use_threading)
+ {
+ iter_type = extractors->iter_types();
+ };
+
+ ExtractTaskData(const ExtractTaskData &src) = default;
+
+ ~ExtractTaskData()
+ {
+ delete extractors;
+ }
+
+#ifdef WITH_CXX_GUARDEDALLOC
+ MEM_CXX_CLASS_ALLOC_FUNCS("DRW:ExtractTaskData")
+#endif
+};
+
+static void extract_task_data_free(void *data)
+{
+ ExtractTaskData *task_data = static_cast<ExtractTaskData *>(data);
+ delete task_data;
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Init and Finish
+ * \{ */
+
+BLI_INLINE void extract_init(const MeshRenderData *mr,
+ struct MeshBatchCache *cache,
+ ExtractorRunDatas &extractors,
+ MeshBufferCache *mbc,
+ void *data_stack)
+{
+ uint32_t data_offset = 0;
+ for (ExtractorRunData &run_data : extractors) {
+ const MeshExtract *extractor = run_data.extractor;
+ run_data.buffer = mesh_extract_buffer_get(extractor, mbc);
+ run_data.data_offset = data_offset;
+ extractor->init(mr, cache, run_data.buffer, POINTER_OFFSET(data_stack, data_offset));
+ data_offset += (uint32_t)extractor->data_size;
+ }
+}
+
+BLI_INLINE void extract_finish(const MeshRenderData *mr,
+ struct MeshBatchCache *cache,
+ const ExtractorRunDatas &extractors,
+ void *data_stack)
+{
+ for (const ExtractorRunData &run_data : extractors) {
+ const MeshExtract *extractor = run_data.extractor;
+ if (extractor->finish) {
+ extractor->finish(
+ mr, cache, run_data.buffer, POINTER_OFFSET(data_stack, run_data.data_offset));
+ }
+ }
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract In Parallel Ranges
+ * \{ */
+
+struct ExtractorIterData {
+ ExtractorRunDatas extractors;
+ const MeshRenderData *mr = nullptr;
+ const void *elems = nullptr;
+ const int *loose_elems = nullptr;
+
+#ifdef WITH_CXX_GUARDEDALLOC
+ MEM_CXX_CLASS_ALLOC_FUNCS("DRW:MeshRenderDataUpdateTaskData")
+#endif
+};
+
+static void extract_task_reduce(const void *__restrict userdata,
+ void *__restrict chunk_to,
+ void *__restrict chunk_from)
+{
+ const ExtractorIterData *data = static_cast<const ExtractorIterData *>(userdata);
+ for (const ExtractorRunData &run_data : data->extractors) {
+ const MeshExtract *extractor = run_data.extractor;
+ if (extractor->task_reduce) {
+ extractor->task_reduce(POINTER_OFFSET(chunk_to, run_data.data_offset),
+ POINTER_OFFSET(chunk_from, run_data.data_offset));
+ }
+ }
+}
+
+static void extract_range_iter_looptri_bm(void *__restrict userdata,
+ const int iter,
+ const TaskParallelTLS *__restrict tls)
+{
+ const ExtractorIterData *data = static_cast<ExtractorIterData *>(userdata);
+ void *extract_data = tls->userdata_chunk;
+ const MeshRenderData *mr = data->mr;
+ BMLoop **elt = ((BMLoop * (*)[3]) data->elems)[iter];
+ for (const ExtractorRunData &run_data : data->extractors) {
+ run_data.extractor->iter_looptri_bm(
+ mr, elt, iter, POINTER_OFFSET(extract_data, run_data.data_offset));
+ }
+}
+
+static void extract_range_iter_looptri_mesh(void *__restrict userdata,
+ const int iter,
+ const TaskParallelTLS *__restrict tls)
+{
+ void *extract_data = tls->userdata_chunk;
+
+ const ExtractorIterData *data = static_cast<ExtractorIterData *>(userdata);
+ const MeshRenderData *mr = data->mr;
+ const MLoopTri *mlt = &((const MLoopTri *)data->elems)[iter];
+ for (const ExtractorRunData &run_data : data->extractors) {
+ run_data.extractor->iter_looptri_mesh(
+ mr, mlt, iter, POINTER_OFFSET(extract_data, run_data.data_offset));
+ }
+}
+
+static void extract_range_iter_poly_bm(void *__restrict userdata,
+ const int iter,
+ const TaskParallelTLS *__restrict tls)
+{
+ void *extract_data = tls->userdata_chunk;
+
+ const ExtractorIterData *data = static_cast<ExtractorIterData *>(userdata);
+ const MeshRenderData *mr = data->mr;
+ const BMFace *f = ((const BMFace **)data->elems)[iter];
+ for (const ExtractorRunData &run_data : data->extractors) {
+ run_data.extractor->iter_poly_bm(
+ mr, f, iter, POINTER_OFFSET(extract_data, run_data.data_offset));
+ }
+}
+
+static void extract_range_iter_poly_mesh(void *__restrict userdata,
+ const int iter,
+ const TaskParallelTLS *__restrict tls)
+{
+ void *extract_data = tls->userdata_chunk;
+
+ const ExtractorIterData *data = static_cast<ExtractorIterData *>(userdata);
+ const MeshRenderData *mr = data->mr;
+ const MPoly *mp = &((const MPoly *)data->elems)[iter];
+ for (const ExtractorRunData &run_data : data->extractors) {
+ run_data.extractor->iter_poly_mesh(
+ mr, mp, iter, POINTER_OFFSET(extract_data, run_data.data_offset));
+ }
+}
+
+static void extract_range_iter_ledge_bm(void *__restrict userdata,
+ const int iter,
+ const TaskParallelTLS *__restrict tls)
+{
+ void *extract_data = tls->userdata_chunk;
+
+ const ExtractorIterData *data = static_cast<ExtractorIterData *>(userdata);
+ const MeshRenderData *mr = data->mr;
+ const int ledge_index = data->loose_elems[iter];
+ const BMEdge *eed = ((const BMEdge **)data->elems)[ledge_index];
+ for (const ExtractorRunData &run_data : data->extractors) {
+ run_data.extractor->iter_ledge_bm(
+ mr, eed, iter, POINTER_OFFSET(extract_data, run_data.data_offset));
+ }
+}
+
+static void extract_range_iter_ledge_mesh(void *__restrict userdata,
+ const int iter,
+ const TaskParallelTLS *__restrict tls)
+{
+ void *extract_data = tls->userdata_chunk;
+
+ const ExtractorIterData *data = static_cast<ExtractorIterData *>(userdata);
+ const MeshRenderData *mr = data->mr;
+ const int ledge_index = data->loose_elems[iter];
+ const MEdge *med = &((const MEdge *)data->elems)[ledge_index];
+ for (const ExtractorRunData &run_data : data->extractors) {
+ run_data.extractor->iter_ledge_mesh(
+ mr, med, iter, POINTER_OFFSET(extract_data, run_data.data_offset));
+ }
+}
+
+static void extract_range_iter_lvert_bm(void *__restrict userdata,
+ const int iter,
+ const TaskParallelTLS *__restrict tls)
+{
+ void *extract_data = tls->userdata_chunk;
+
+ const ExtractorIterData *data = static_cast<ExtractorIterData *>(userdata);
+ const MeshRenderData *mr = data->mr;
+ const int lvert_index = data->loose_elems[iter];
+ const BMVert *eve = ((const BMVert **)data->elems)[lvert_index];
+ for (const ExtractorRunData &run_data : data->extractors) {
+ run_data.extractor->iter_lvert_bm(
+ mr, eve, iter, POINTER_OFFSET(extract_data, run_data.data_offset));
+ }
+}
+
+static void extract_range_iter_lvert_mesh(void *__restrict userdata,
+ const int iter,
+ const TaskParallelTLS *__restrict tls)
+{
+ void *extract_data = tls->userdata_chunk;
+
+ const ExtractorIterData *data = static_cast<ExtractorIterData *>(userdata);
+ const MeshRenderData *mr = data->mr;
+ const int lvert_index = data->loose_elems[iter];
+ const MVert *mv = &((const MVert *)data->elems)[lvert_index];
+ for (const ExtractorRunData &run_data : data->extractors) {
+ run_data.extractor->iter_lvert_mesh(
+ mr, mv, iter, POINTER_OFFSET(extract_data, run_data.data_offset));
+ }
+}
+
+BLI_INLINE void extract_task_range_run_iter(const MeshRenderData *mr,
+ ExtractorRunDatas *extractors,
+ const eMRIterType iter_type,
+ bool is_mesh,
+ TaskParallelSettings *settings)
+{
+ ExtractorIterData range_data;
+ range_data.mr = mr;
+
+ TaskParallelRangeFunc func;
+ int stop;
+ switch (iter_type) {
+ case MR_ITER_LOOPTRI:
+ range_data.elems = is_mesh ? mr->mlooptri : (void *)mr->edit_bmesh->looptris;
+ func = is_mesh ? extract_range_iter_looptri_mesh : extract_range_iter_looptri_bm;
+ stop = mr->tri_len;
+ break;
+ case MR_ITER_POLY:
+ range_data.elems = is_mesh ? mr->mpoly : (void *)mr->bm->ftable;
+ func = is_mesh ? extract_range_iter_poly_mesh : extract_range_iter_poly_bm;
+ stop = mr->poly_len;
+ break;
+ case MR_ITER_LEDGE:
+ range_data.loose_elems = mr->ledges;
+ range_data.elems = is_mesh ? mr->medge : (void *)mr->bm->etable;
+ func = is_mesh ? extract_range_iter_ledge_mesh : extract_range_iter_ledge_bm;
+ stop = mr->edge_loose_len;
+ break;
+ case MR_ITER_LVERT:
+ range_data.loose_elems = mr->lverts;
+ range_data.elems = is_mesh ? mr->mvert : (void *)mr->bm->vtable;
+ func = is_mesh ? extract_range_iter_lvert_mesh : extract_range_iter_lvert_bm;
+ stop = mr->vert_loose_len;
+ break;
+ default:
+ BLI_assert(false);
+ return;
+ }
+
+ extractors->filter_into(range_data.extractors, iter_type);
+ BLI_task_parallel_range(0, stop, &range_data, func, settings);
+}
+
+static void extract_task_range_run(void *__restrict taskdata)
+{
+ ExtractTaskData *data = (ExtractTaskData *)taskdata;
+ const eMRIterType iter_type = data->iter_type;
+ const bool is_mesh = data->mr->extract_type != MR_EXTRACT_BMESH;
+
+ size_t userdata_chunk_size = data->extractors->data_size_total();
+ char *userdata_chunk = new char[userdata_chunk_size];
+
+ TaskParallelSettings settings;
+ BLI_parallel_range_settings_defaults(&settings);
+ settings.use_threading = data->use_threading;
+ settings.userdata_chunk = userdata_chunk;
+ settings.userdata_chunk_size = userdata_chunk_size;
+ settings.func_reduce = extract_task_reduce;
+ settings.min_iter_per_thread = MIM_RANGE_LEN;
+
+ extract_init(data->mr, data->cache, *data->extractors, data->mbc, (void *)userdata_chunk);
+
+ if (iter_type & MR_ITER_LOOPTRI) {
+ extract_task_range_run_iter(data->mr, data->extractors, MR_ITER_LOOPTRI, is_mesh, &settings);
+ }
+ if (iter_type & MR_ITER_POLY) {
+ extract_task_range_run_iter(data->mr, data->extractors, MR_ITER_POLY, is_mesh, &settings);
+ }
+ if (iter_type & MR_ITER_LEDGE) {
+ extract_task_range_run_iter(data->mr, data->extractors, MR_ITER_LEDGE, is_mesh, &settings);
+ }
+ if (iter_type & MR_ITER_LVERT) {
+ extract_task_range_run_iter(data->mr, data->extractors, MR_ITER_LVERT, is_mesh, &settings);
+ }
+
+ extract_finish(data->mr, data->cache, *data->extractors, (void *)userdata_chunk);
+ delete[] userdata_chunk;
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract In Parallel Ranges
+ * \{ */
+
+static struct TaskNode *extract_task_node_create(struct TaskGraph *task_graph,
+ const MeshRenderData *mr,
+ MeshBatchCache *cache,
+ ExtractorRunDatas *extractors,
+ MeshBufferCache *mbc,
+ const bool use_threading)
+{
+ ExtractTaskData *taskdata = new ExtractTaskData(mr, cache, extractors, mbc, use_threading);
+ struct TaskNode *task_node = BLI_task_graph_node_create(
+ task_graph,
+ extract_task_range_run,
+ taskdata,
+ (TaskGraphNodeFreeFunction)extract_task_data_free);
+ return task_node;
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Task Node - Update Mesh Render Data
+ * \{ */
+struct MeshRenderDataUpdateTaskData {
+ MeshRenderData *mr = nullptr;
+ eMRIterType iter_type;
+ eMRDataType data_flag;
+
+ MeshRenderDataUpdateTaskData(MeshRenderData *mr, eMRIterType iter_type, eMRDataType data_flag)
+ : mr(mr), iter_type(iter_type), data_flag(data_flag)
+ {
+ }
+
+ ~MeshRenderDataUpdateTaskData()
+ {
+ mesh_render_data_free(mr);
+ }
+
+#ifdef WITH_CXX_GUARDEDALLOC
+ MEM_CXX_CLASS_ALLOC_FUNCS("DRW:MeshRenderDataUpdateTaskData")
+#endif
+};
+
+static void mesh_render_data_update_task_data_free(void *data)
+{
+ MeshRenderDataUpdateTaskData *taskdata = static_cast<MeshRenderDataUpdateTaskData *>(data);
+ BLI_assert(taskdata);
+ delete taskdata;
+}
+
+static void mesh_extract_render_data_node_exec(void *__restrict task_data)
+{
+ MeshRenderDataUpdateTaskData *update_task_data = static_cast<MeshRenderDataUpdateTaskData *>(
+ task_data);
+ MeshRenderData *mr = update_task_data->mr;
+ const eMRIterType iter_type = update_task_data->iter_type;
+ const eMRDataType data_flag = update_task_data->data_flag;
+
+ mesh_render_data_update_normals(mr, data_flag);
+ mesh_render_data_update_looptris(mr, iter_type, data_flag);
+}
+
+static struct TaskNode *mesh_extract_render_data_node_create(struct TaskGraph *task_graph,
+ MeshRenderData *mr,
+ const eMRIterType iter_type,
+ const eMRDataType data_flag)
+{
+ MeshRenderDataUpdateTaskData *task_data = new MeshRenderDataUpdateTaskData(
+ mr, iter_type, data_flag);
+
+ struct TaskNode *task_node = BLI_task_graph_node_create(
+ task_graph,
+ mesh_extract_render_data_node_exec,
+ task_data,
+ (TaskGraphNodeFreeFunction)mesh_render_data_update_task_data_free);
+ return task_node;
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Loop
+ * \{ */
+
+static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph,
+ MeshBatchCache *cache,
+ MeshBufferCache *mbc,
+ MeshBufferExtractionCache *extraction_cache,
+ Mesh *me,
+
+ const bool is_editmode,
+ const bool is_paint_mode,
+ const bool is_mode_active,
+ const float obmat[4][4],
+ const bool do_final,
+ const bool do_uvedit,
+ const bool use_subsurf_fdots,
+ const Scene *scene,
+ const ToolSettings *ts,
+ const bool use_hide)
+{
+ /* For each mesh where batches needs to be updated a sub-graph will be added to the task_graph.
+ * This sub-graph starts with an extract_render_data_node. This fills/converts the required
+ * data from Mesh.
+ *
+ * Small extractions and extractions that can't be multi-threaded are grouped in a single
+ * `extract_single_threaded_task_node`.
+ *
+ * Other extractions will create a node for each loop exceeding 8192 items. these nodes are
+ * linked to the `user_data_init_task_node`. the `user_data_init_task_node` prepares the
+ * user_data needed for the extraction based on the data extracted from the mesh.
+ * counters are used to check if the finalize of a task has to be called.
+ *
+ * Mesh extraction sub graph
+ *
+ * +----------------------+
+ * +-----> | extract_task1_loop_1 |
+ * | +----------------------+
+ * +------------------+ +----------------------+ +----------------------+
+ * | mesh_render_data | --> | | --> | extract_task1_loop_2 |
+ * +------------------+ | | +----------------------+
+ * | | | +----------------------+
+ * | | user_data_init | --> | extract_task2_loop_1 |
+ * v | | +----------------------+
+ * +------------------+ | | +----------------------+
+ * | single_threaded | | | --> | extract_task2_loop_2 |
+ * +------------------+ +----------------------+ +----------------------+
+ * | +----------------------+
+ * +-----> | extract_task2_loop_3 |
+ * +----------------------+
+ */
+ const bool do_hq_normals = (scene->r.perf_flag & SCE_PERF_HQ_NORMALS) != 0 ||
+ GPU_use_hq_normals_workaround();
+ const bool override_single_mat = mesh_render_mat_len_get(me) <= 1;
+
+ /* Create an array containing all the extractors that needs to be executed. */
+ ExtractorRunDatas extractors;
+
+#define EXTRACT_ADD_REQUESTED(type, type_lowercase, name) \
+ do { \
+ if (DRW_##type_lowercase##_requested(mbc->type_lowercase.name)) { \
+ const MeshExtract *extractor = mesh_extract_override_get( \
+ &extract_##name, do_hq_normals, override_single_mat); \
+ extractors.append(extractor); \
+ } \
+ } while (0)
+
+ EXTRACT_ADD_REQUESTED(VBO, vbo, pos_nor);
+ EXTRACT_ADD_REQUESTED(VBO, vbo, lnor);
+ EXTRACT_ADD_REQUESTED(VBO, vbo, uv);
+ EXTRACT_ADD_REQUESTED(VBO, vbo, tan);
+ EXTRACT_ADD_REQUESTED(VBO, vbo, vcol);
+ EXTRACT_ADD_REQUESTED(VBO, vbo, sculpt_data);
+ EXTRACT_ADD_REQUESTED(VBO, vbo, orco);
+ EXTRACT_ADD_REQUESTED(VBO, vbo, edge_fac);
+ EXTRACT_ADD_REQUESTED(VBO, vbo, weights);
+ EXTRACT_ADD_REQUESTED(VBO, vbo, edit_data);
+ EXTRACT_ADD_REQUESTED(VBO, vbo, edituv_data);
+ EXTRACT_ADD_REQUESTED(VBO, vbo, edituv_stretch_area);
+ EXTRACT_ADD_REQUESTED(VBO, vbo, edituv_stretch_angle);
+ EXTRACT_ADD_REQUESTED(VBO, vbo, mesh_analysis);
+ EXTRACT_ADD_REQUESTED(VBO, vbo, fdots_pos);
+ EXTRACT_ADD_REQUESTED(VBO, vbo, fdots_nor);
+ EXTRACT_ADD_REQUESTED(VBO, vbo, fdots_uv);
+ EXTRACT_ADD_REQUESTED(VBO, vbo, fdots_edituv_data);
+ EXTRACT_ADD_REQUESTED(VBO, vbo, poly_idx);
+ EXTRACT_ADD_REQUESTED(VBO, vbo, edge_idx);
+ EXTRACT_ADD_REQUESTED(VBO, vbo, vert_idx);
+ EXTRACT_ADD_REQUESTED(VBO, vbo, fdot_idx);
+ EXTRACT_ADD_REQUESTED(VBO, vbo, skin_roots);
+
+ EXTRACT_ADD_REQUESTED(IBO, ibo, tris);
+ if (DRW_ibo_requested(mbc->ibo.lines)) {
+ const MeshExtract *extractor;
+ if (mbc->ibo.lines_loose != nullptr) {
+ /* Update #lines_loose ibo. */
+ extractor = &extract_lines_with_lines_loose;
+ }
+ else {
+ extractor = &extract_lines;
+ }
+ extractors.append(extractor);
+ }
+ else if (DRW_ibo_requested(mbc->ibo.lines_loose)) {
+ /* Note: #ibo.lines must have been created first. */
+ const MeshExtract *extractor = &extract_lines_loose_only;
+ extractors.append(extractor);
+ }
+ EXTRACT_ADD_REQUESTED(IBO, ibo, points);
+ EXTRACT_ADD_REQUESTED(IBO, ibo, fdots);
+ EXTRACT_ADD_REQUESTED(IBO, ibo, lines_paint_mask);
+ EXTRACT_ADD_REQUESTED(IBO, ibo, lines_adjacency);
+ EXTRACT_ADD_REQUESTED(IBO, ibo, edituv_tris);
+ EXTRACT_ADD_REQUESTED(IBO, ibo, edituv_lines);
+ EXTRACT_ADD_REQUESTED(IBO, ibo, edituv_points);
+ EXTRACT_ADD_REQUESTED(IBO, ibo, edituv_fdots);
+
+#undef EXTRACT_ADD_REQUESTED
+
+ if (extractors.is_empty()) {
+ return;
+ }
+
+#ifdef DEBUG_TIME
+ double rdata_start = PIL_check_seconds_timer();
+#endif
+
+ eMRIterType iter_type = extractors.iter_types();
+ eMRDataType data_flag = extractors.data_types();
+
+ MeshRenderData *mr = mesh_render_data_create(me,
+ extraction_cache,
+ is_editmode,
+ is_paint_mode,
+ is_mode_active,
+ obmat,
+ do_final,
+ do_uvedit,
+ ts,
+ iter_type);
+ mr->use_hide = use_hide;
+ mr->use_subsurf_fdots = use_subsurf_fdots;
+ mr->use_final_mesh = do_final;
+
+#ifdef DEBUG_TIME
+ double rdata_end = PIL_check_seconds_timer();
+#endif
+
+ struct TaskNode *task_node_mesh_render_data = mesh_extract_render_data_node_create(
+ task_graph, mr, iter_type, data_flag);
+
+ /* Simple heuristic. */
+ const bool use_thread = (mr->loop_len + mr->loop_loose_len) > MIM_RANGE_LEN;
+
+ if (use_thread) {
+ /* First run the requested extractors that do not support asynchronous ranges. */
+ for (const ExtractorRunData &run_data : extractors) {
+ const MeshExtract *extractor = run_data.extractor;
+ if (!extractor->use_threading) {
+ ExtractorRunDatas *single_threaded_extractors = new ExtractorRunDatas();
+ single_threaded_extractors->append(extractor);
+ struct TaskNode *task_node = extract_task_node_create(
+ task_graph, mr, cache, single_threaded_extractors, mbc, false);
+
+ BLI_task_graph_edge_create(task_node_mesh_render_data, task_node);
+ }
+ }
+
+ /* Distribute the remaining extractors into ranges per core. */
+ ExtractorRunDatas *multi_threaded_extractors = new ExtractorRunDatas();
+ extractors.filter_threaded_extractors_into(*multi_threaded_extractors);
+ if (!multi_threaded_extractors->is_empty()) {
+ struct TaskNode *task_node = extract_task_node_create(
+ task_graph, mr, cache, multi_threaded_extractors, mbc, true);
+
+ BLI_task_graph_edge_create(task_node_mesh_render_data, task_node);
+ }
+ else {
+ /* No tasks created freeing extractors list. */
+ delete multi_threaded_extractors;
+ }
+ }
+ else {
+ /* Run all requests on the same thread. */
+ ExtractorRunDatas *extractors_copy = new ExtractorRunDatas(extractors);
+ struct TaskNode *task_node = extract_task_node_create(
+ task_graph, mr, cache, extractors_copy, mbc, false);
+
+ BLI_task_graph_edge_create(task_node_mesh_render_data, task_node);
+ }
+
+ /* Trigger the sub-graph for this mesh. */
+ BLI_task_graph_node_push_work(task_node_mesh_render_data);
+
+#ifdef DEBUG_TIME
+ BLI_task_graph_work_and_wait(task_graph);
+ double end = PIL_check_seconds_timer();
+
+ static double avg = 0;
+ static double avg_fps = 0;
+ static double avg_rdata = 0;
+ static double end_prev = 0;
+
+ if (end_prev == 0) {
+ end_prev = end;
+ }
+
+ avg = avg * 0.95 + (end - rdata_end) * 0.05;
+ avg_fps = avg_fps * 0.95 + (end - end_prev) * 0.05;
+ avg_rdata = avg_rdata * 0.95 + (rdata_end - rdata_start) * 0.05;
+
+ printf(
+ "rdata %.0fms iter %.0fms (frame %.0fms)\n", avg_rdata * 1000, avg * 1000, avg_fps * 1000);
+
+ end_prev = end;
+#endif
+}
+
+} // namespace blender::draw
+
+extern "C" {
+void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph,
+ MeshBatchCache *cache,
+ MeshBufferCache *mbc,
+ MeshBufferExtractionCache *extraction_cache,
+ Mesh *me,
+
+ const bool is_editmode,
+ const bool is_paint_mode,
+ const bool is_mode_active,
+ const float obmat[4][4],
+ const bool do_final,
+ const bool do_uvedit,
+ const bool use_subsurf_fdots,
+ const Scene *scene,
+ const ToolSettings *ts,
+ const bool use_hide)
+{
+ blender::draw::mesh_buffer_cache_create_requested(task_graph,
+ cache,
+ mbc,
+ extraction_cache,
+ me,
+ is_editmode,
+ is_paint_mode,
+ is_mode_active,
+ obmat,
+ do_final,
+ do_uvedit,
+ use_subsurf_fdots,
+ scene,
+ ts,
+ use_hide);
+}
+
+} // extern "C"
+
+/** \} */
diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c b/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c
new file mode 100644
index 00000000000..3a8edcad463
--- /dev/null
+++ b/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c
@@ -0,0 +1,3687 @@
+/*
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2021 by Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup draw
+ *
+ * \brief Extraction of Mesh data into VBO to feed to GPU.
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "atomic_ops.h"
+
+#include "DNA_object_types.h"
+
+#include "BLI_edgehash.h"
+#include "BLI_jitter_2d.h"
+#include "BLI_kdopbvh.h"
+#include "BLI_string.h"
+
+#include "BKE_bvhutils.h"
+#include "BKE_deform.h"
+#include "BKE_editmesh.h"
+#include "BKE_editmesh_bvh.h"
+#include "BKE_editmesh_cache.h"
+#include "BKE_editmesh_tangent.h"
+#include "BKE_mesh.h"
+#include "BKE_mesh_tangent.h"
+#include "BKE_paint.h"
+
+#include "ED_uvedit.h"
+
+#include "GPU_capabilities.h"
+
+#include "draw_cache_extract_mesh_private.h"
+#include "draw_cache_impl.h"
+
+void *mesh_extract_buffer_get(const MeshExtract *extractor, MeshBufferCache *mbc)
+{
+ /* NOTE: POINTER_OFFSET on windows platforms casts internally to `void *`, but on GCC/CLANG to
+ * `MeshBufferCache *`. What shows a different usage versus intent. */
+ void **buffer_ptr = (void **)POINTER_OFFSET(mbc, extractor->mesh_buffer_offset);
+ void *buffer = *buffer_ptr;
+ BLI_assert(buffer);
+ return buffer;
+}
+
+eMRIterType mesh_extract_iter_type(const MeshExtract *ext)
+{
+ eMRIterType type = 0;
+ SET_FLAG_FROM_TEST(type, (ext->iter_looptri_bm || ext->iter_looptri_mesh), MR_ITER_LOOPTRI);
+ SET_FLAG_FROM_TEST(type, (ext->iter_poly_bm || ext->iter_poly_mesh), MR_ITER_POLY);
+ SET_FLAG_FROM_TEST(type, (ext->iter_ledge_bm || ext->iter_ledge_mesh), MR_ITER_LEDGE);
+ SET_FLAG_FROM_TEST(type, (ext->iter_lvert_bm || ext->iter_lvert_mesh), MR_ITER_LVERT);
+ return type;
+}
+
+/* ---------------------------------------------------------------------- */
+/** \name Override extractors
+ * Extractors can be overridden. When overridden a specialized version is used. The next functions
+ * would check for any needed overrides and usage of the specialized version.
+ * \{ */
+
+static const MeshExtract *mesh_extract_override_hq_normals(const MeshExtract *extractor)
+{
+ if (extractor == &extract_pos_nor) {
+ return &extract_pos_nor_hq;
+ }
+ if (extractor == &extract_lnor) {
+ return &extract_lnor_hq;
+ }
+ if (extractor == &extract_tan) {
+ return &extract_tan_hq;
+ }
+ if (extractor == &extract_fdots_nor) {
+ return &extract_fdots_nor_hq;
+ }
+ return extractor;
+}
+
+static const MeshExtract *mesh_extract_override_single_material(const MeshExtract *extractor)
+{
+ if (extractor == &extract_tris) {
+ return &extract_tris_single_mat;
+ }
+ return extractor;
+}
+
+const MeshExtract *mesh_extract_override_get(const MeshExtract *extractor,
+ const bool do_hq_normals,
+ const bool do_single_mat)
+{
+ if (do_hq_normals) {
+ extractor = mesh_extract_override_hq_normals(extractor);
+ }
+
+ if (do_single_mat) {
+ extractor = mesh_extract_override_single_material(extractor);
+ }
+
+ return extractor;
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Position and Vertex Normal
+ * \{ */
+
+typedef struct PosNorLoop {
+ float pos[3];
+ GPUPackedNormal nor;
+} PosNorLoop;
+
+typedef struct MeshExtract_PosNor_Data {
+ PosNorLoop *vbo_data;
+ GPUNormal *normals;
+} MeshExtract_PosNor_Data;
+
+static void extract_pos_nor_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *tls_data)
+{
+ GPUVertBuf *vbo = buf;
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ /* WARNING Adjust #PosNorLoop struct accordingly. */
+ GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+ GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
+ GPU_vertformat_alias_add(&format, "vnor");
+ }
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len);
+
+ /* Pack normals per vert, reduce amount of computation. */
+ MeshExtract_PosNor_Data *data = tls_data;
+ data->vbo_data = (PosNorLoop *)GPU_vertbuf_get_data(vbo);
+ data->normals = MEM_mallocN(sizeof(GPUNormal) * mr->vert_len, __func__);
+
+ /* Quicker than doing it for each loop. */
+ if (mr->extract_type == MR_EXTRACT_BMESH) {
+ BMIter iter;
+ BMVert *eve;
+ int v;
+ BM_ITER_MESH_INDEX (eve, &iter, mr->bm, BM_VERTS_OF_MESH, v) {
+ data->normals[v].low = GPU_normal_convert_i10_v3(bm_vert_no_get(mr, eve));
+ }
+ }
+ else {
+ const MVert *mv = mr->mvert;
+ for (int v = 0; v < mr->vert_len; v++, mv++) {
+ data->normals[v].low = GPU_normal_convert_i10_s3(mv->no);
+ }
+ }
+}
+
+static void extract_pos_nor_iter_poly_bm(const MeshRenderData *mr,
+ const BMFace *f,
+ const int UNUSED(f_index),
+ void *_data)
+{
+ MeshExtract_PosNor_Data *data = _data;
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ const int l_index = BM_elem_index_get(l_iter);
+ PosNorLoop *vert = &data->vbo_data[l_index];
+ copy_v3_v3(vert->pos, bm_vert_co_get(mr, l_iter->v));
+ vert->nor = data->normals[BM_elem_index_get(l_iter->v)].low;
+ vert->nor.w = BM_elem_flag_test(f, BM_ELEM_HIDDEN) ? -1 : 0;
+ } while ((l_iter = l_iter->next) != l_first);
+}
+
+static void extract_pos_nor_iter_poly_mesh(const MeshRenderData *mr,
+ const MPoly *mp,
+ const int UNUSED(mp_index),
+ void *_data)
+{
+ MeshExtract_PosNor_Data *data = _data;
+
+ const MLoop *mloop = mr->mloop;
+ const int ml_index_end = mp->loopstart + mp->totloop;
+ for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
+ const MLoop *ml = &mloop[ml_index];
+
+ PosNorLoop *vert = &data->vbo_data[ml_index];
+ const MVert *mv = &mr->mvert[ml->v];
+ copy_v3_v3(vert->pos, mv->co);
+ vert->nor = data->normals[ml->v].low;
+ /* Flag for paint mode overlay. */
+ if (mp->flag & ME_HIDE || mv->flag & ME_HIDE ||
+ ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->v_origindex) &&
+ (mr->v_origindex[ml->v] == ORIGINDEX_NONE))) {
+ vert->nor.w = -1;
+ }
+ else if (mv->flag & SELECT) {
+ vert->nor.w = 1;
+ }
+ else {
+ vert->nor.w = 0;
+ }
+ }
+}
+
+static void extract_pos_nor_iter_ledge_bm(const MeshRenderData *mr,
+ const BMEdge *eed,
+ const int ledge_index,
+ void *_data)
+{
+ MeshExtract_PosNor_Data *data = _data;
+
+ int l_index = mr->loop_len + ledge_index * 2;
+ PosNorLoop *vert = &data->vbo_data[l_index];
+ copy_v3_v3(vert[0].pos, bm_vert_co_get(mr, eed->v1));
+ copy_v3_v3(vert[1].pos, bm_vert_co_get(mr, eed->v2));
+ vert[0].nor = data->normals[BM_elem_index_get(eed->v1)].low;
+ vert[1].nor = data->normals[BM_elem_index_get(eed->v2)].low;
+}
+
+static void extract_pos_nor_iter_ledge_mesh(const MeshRenderData *mr,
+ const MEdge *med,
+ const int ledge_index,
+ void *_data)
+{
+ MeshExtract_PosNor_Data *data = _data;
+ const int ml_index = mr->loop_len + ledge_index * 2;
+ PosNorLoop *vert = &data->vbo_data[ml_index];
+ copy_v3_v3(vert[0].pos, mr->mvert[med->v1].co);
+ copy_v3_v3(vert[1].pos, mr->mvert[med->v2].co);
+ vert[0].nor = data->normals[med->v1].low;
+ vert[1].nor = data->normals[med->v2].low;
+}
+
+static void extract_pos_nor_iter_lvert_bm(const MeshRenderData *mr,
+ const BMVert *eve,
+ const int lvert_index,
+ void *_data)
+{
+ MeshExtract_PosNor_Data *data = _data;
+ const int offset = mr->loop_len + (mr->edge_loose_len * 2);
+
+ const int l_index = offset + lvert_index;
+ PosNorLoop *vert = &data->vbo_data[l_index];
+ copy_v3_v3(vert->pos, bm_vert_co_get(mr, eve));
+ vert->nor = data->normals[BM_elem_index_get(eve)].low;
+}
+
+static void extract_pos_nor_iter_lvert_mesh(const MeshRenderData *mr,
+ const MVert *mv,
+ const int lvert_index,
+ void *_data)
+{
+ MeshExtract_PosNor_Data *data = _data;
+ const int offset = mr->loop_len + (mr->edge_loose_len * 2);
+
+ const int ml_index = offset + lvert_index;
+ const int v_index = mr->lverts[lvert_index];
+ PosNorLoop *vert = &data->vbo_data[ml_index];
+ copy_v3_v3(vert->pos, mv->co);
+ vert->nor = data->normals[v_index].low;
+}
+
+static void extract_pos_nor_finish(const MeshRenderData *UNUSED(mr),
+ struct MeshBatchCache *UNUSED(cache),
+ void *UNUSED(buf),
+ void *_data)
+{
+ MeshExtract_PosNor_Data *data = _data;
+ MEM_freeN(data->normals);
+}
+
+const MeshExtract extract_pos_nor = {
+ .init = extract_pos_nor_init,
+ .iter_poly_bm = extract_pos_nor_iter_poly_bm,
+ .iter_poly_mesh = extract_pos_nor_iter_poly_mesh,
+ .iter_ledge_bm = extract_pos_nor_iter_ledge_bm,
+ .iter_ledge_mesh = extract_pos_nor_iter_ledge_mesh,
+ .iter_lvert_bm = extract_pos_nor_iter_lvert_bm,
+ .iter_lvert_mesh = extract_pos_nor_iter_lvert_mesh,
+ .finish = extract_pos_nor_finish,
+ .data_type = 0,
+ .data_size = sizeof(MeshExtract_PosNor_Data),
+ .use_threading = true,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.pos_nor),
+};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Position and High Quality Vertex Normal
+ * \{ */
+
+typedef struct PosNorHQLoop {
+ float pos[3];
+ short nor[4];
+} PosNorHQLoop;
+
+typedef struct MeshExtract_PosNorHQ_Data {
+ PosNorHQLoop *vbo_data;
+ GPUNormal *normals;
+} MeshExtract_PosNorHQ_Data;
+
+static void extract_pos_nor_hq_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *tls_data)
+{
+ GPUVertBuf *vbo = buf;
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ /* WARNING Adjust #PosNorHQLoop struct accordingly. */
+ GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+ GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
+ GPU_vertformat_alias_add(&format, "vnor");
+ }
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len);
+
+ /* Pack normals per vert, reduce amount of computation. */
+ MeshExtract_PosNorHQ_Data *data = tls_data;
+ data->vbo_data = (PosNorHQLoop *)GPU_vertbuf_get_data(vbo);
+ data->normals = MEM_mallocN(sizeof(GPUNormal) * mr->vert_len, __func__);
+
+ /* Quicker than doing it for each loop. */
+ if (mr->extract_type == MR_EXTRACT_BMESH) {
+ BMIter iter;
+ BMVert *eve;
+ int v;
+ BM_ITER_MESH_INDEX (eve, &iter, mr->bm, BM_VERTS_OF_MESH, v) {
+ normal_float_to_short_v3(data->normals[v].high, bm_vert_no_get(mr, eve));
+ }
+ }
+ else {
+ const MVert *mv = mr->mvert;
+ for (int v = 0; v < mr->vert_len; v++, mv++) {
+ copy_v3_v3_short(data->normals[v].high, mv->no);
+ }
+ }
+}
+
+static void extract_pos_nor_hq_iter_poly_bm(const MeshRenderData *mr,
+ const BMFace *f,
+ const int UNUSED(f_index),
+ void *_data)
+{
+ MeshExtract_PosNorHQ_Data *data = _data;
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ const int l_index = BM_elem_index_get(l_iter);
+ PosNorHQLoop *vert = &data->vbo_data[l_index];
+ copy_v3_v3(vert->pos, bm_vert_co_get(mr, l_iter->v));
+ copy_v3_v3_short(vert->nor, data->normals[BM_elem_index_get(l_iter->v)].high);
+
+ BMFace *efa = l_iter->f;
+ vert->nor[3] = BM_elem_flag_test(efa, BM_ELEM_HIDDEN) ? -1 : 0;
+ } while ((l_iter = l_iter->next) != l_first);
+}
+
+static void extract_pos_nor_hq_iter_poly_mesh(const MeshRenderData *mr,
+ const MPoly *mp,
+ const int UNUSED(mp_index),
+ void *_data)
+{
+ MeshExtract_PosNorHQ_Data *data = _data;
+ const MLoop *mloop = mr->mloop;
+ const int ml_index_end = mp->loopstart + mp->totloop;
+ for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
+ const MLoop *ml = &mloop[ml_index];
+
+ PosNorHQLoop *vert = &data->vbo_data[ml_index];
+ const MVert *mv = &mr->mvert[ml->v];
+ copy_v3_v3(vert->pos, mv->co);
+ copy_v3_v3_short(vert->nor, data->normals[ml->v].high);
+
+ /* Flag for paint mode overlay. */
+ if (mp->flag & ME_HIDE || mv->flag & ME_HIDE ||
+ ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->v_origindex) &&
+ (mr->v_origindex[ml->v] == ORIGINDEX_NONE))) {
+ vert->nor[3] = -1;
+ }
+ else if (mv->flag & SELECT) {
+ vert->nor[3] = 1;
+ }
+ else {
+ vert->nor[3] = 0;
+ }
+ }
+}
+
+static void extract_pos_nor_hq_iter_ledge_bm(const MeshRenderData *mr,
+ const BMEdge *eed,
+ const int ledge_index,
+ void *_data)
+{
+ MeshExtract_PosNorHQ_Data *data = _data;
+ int l_index = mr->loop_len + ledge_index * 2;
+ PosNorHQLoop *vert = &data->vbo_data[l_index];
+ copy_v3_v3(vert[0].pos, bm_vert_co_get(mr, eed->v1));
+ copy_v3_v3(vert[1].pos, bm_vert_co_get(mr, eed->v2));
+ copy_v3_v3_short(vert[0].nor, data->normals[BM_elem_index_get(eed->v1)].high);
+ vert[0].nor[3] = 0;
+ copy_v3_v3_short(vert[1].nor, data->normals[BM_elem_index_get(eed->v2)].high);
+ vert[1].nor[3] = 0;
+}
+
+static void extract_pos_nor_hq_iter_ledge_mesh(const MeshRenderData *mr,
+ const MEdge *med,
+ const int ledge_index,
+ void *_data)
+{
+ MeshExtract_PosNorHQ_Data *data = _data;
+ const int ml_index = mr->loop_len + ledge_index * 2;
+ PosNorHQLoop *vert = &data->vbo_data[ml_index];
+ copy_v3_v3(vert[0].pos, mr->mvert[med->v1].co);
+ copy_v3_v3(vert[1].pos, mr->mvert[med->v2].co);
+ copy_v3_v3_short(vert[0].nor, data->normals[med->v1].high);
+ vert[0].nor[3] = 0;
+ copy_v3_v3_short(vert[1].nor, data->normals[med->v2].high);
+ vert[1].nor[3] = 0;
+}
+
+static void extract_pos_nor_hq_iter_lvert_bm(const MeshRenderData *mr,
+ const BMVert *eve,
+ const int lvert_index,
+ void *_data)
+{
+ MeshExtract_PosNorHQ_Data *data = _data;
+ const int offset = mr->loop_len + (mr->edge_loose_len * 2);
+
+ const int l_index = offset + lvert_index;
+ PosNorHQLoop *vert = &data->vbo_data[l_index];
+ copy_v3_v3(vert->pos, bm_vert_co_get(mr, eve));
+ copy_v3_v3_short(vert->nor, data->normals[BM_elem_index_get(eve)].high);
+ vert->nor[3] = 0;
+}
+
+static void extract_pos_nor_hq_iter_lvert_mesh(const MeshRenderData *mr,
+ const MVert *mv,
+ const int lvert_index,
+ void *_data)
+{
+ MeshExtract_PosNorHQ_Data *data = _data;
+ const int offset = mr->loop_len + (mr->edge_loose_len * 2);
+
+ const int ml_index = offset + lvert_index;
+ const int v_index = mr->lverts[lvert_index];
+ PosNorHQLoop *vert = &data->vbo_data[ml_index];
+ copy_v3_v3(vert->pos, mv->co);
+ copy_v3_v3_short(vert->nor, data->normals[v_index].high);
+ vert->nor[3] = 0;
+}
+
+static void extract_pos_nor_hq_finish(const MeshRenderData *UNUSED(mr),
+ struct MeshBatchCache *UNUSED(cache),
+ void *UNUSED(buf),
+ void *_data)
+{
+ MeshExtract_PosNorHQ_Data *data = _data;
+ MEM_freeN(data->normals);
+}
+
+const MeshExtract extract_pos_nor_hq = {
+ .init = extract_pos_nor_hq_init,
+ .iter_poly_bm = extract_pos_nor_hq_iter_poly_bm,
+ .iter_poly_mesh = extract_pos_nor_hq_iter_poly_mesh,
+ .iter_ledge_bm = extract_pos_nor_hq_iter_ledge_bm,
+ .iter_ledge_mesh = extract_pos_nor_hq_iter_ledge_mesh,
+ .iter_lvert_bm = extract_pos_nor_hq_iter_lvert_bm,
+ .iter_lvert_mesh = extract_pos_nor_hq_iter_lvert_mesh,
+ .finish = extract_pos_nor_hq_finish,
+ .data_type = 0,
+ .data_size = sizeof(MeshExtract_PosNorHQ_Data),
+ .use_threading = true,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.pos_nor)};
+
+/** \} */
+/* ---------------------------------------------------------------------- */
+/** \name Extract HQ Loop Normal
+ * \{ */
+
+typedef struct gpuHQNor {
+ short x, y, z, w;
+} gpuHQNor;
+
+static void extract_lnor_hq_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *tls_data)
+{
+ GPUVertBuf *vbo = buf;
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
+ GPU_vertformat_alias_add(&format, "lnor");
+ }
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, mr->loop_len);
+
+ *(gpuHQNor **)tls_data = GPU_vertbuf_get_data(vbo);
+}
+
+static void extract_lnor_hq_iter_poly_bm(const MeshRenderData *mr,
+ const BMFace *f,
+ const int UNUSED(f_index),
+ void *data)
+{
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ const int l_index = BM_elem_index_get(l_iter);
+ if (mr->loop_normals) {
+ normal_float_to_short_v3(&(*(gpuHQNor **)data)[l_index].x, mr->loop_normals[l_index]);
+ }
+ else {
+ if (BM_elem_flag_test(f, BM_ELEM_SMOOTH)) {
+ normal_float_to_short_v3(&(*(gpuHQNor **)data)[l_index].x, bm_vert_no_get(mr, l_iter->v));
+ }
+ else {
+ normal_float_to_short_v3(&(*(gpuHQNor **)data)[l_index].x, bm_face_no_get(mr, f));
+ }
+ }
+ } while ((l_iter = l_iter->next) != l_first);
+}
+
+static void extract_lnor_hq_iter_poly_mesh(const MeshRenderData *mr,
+ const MPoly *mp,
+ const int mp_index,
+ void *data)
+{
+ const MLoop *mloop = mr->mloop;
+ const int ml_index_end = mp->loopstart + mp->totloop;
+ for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
+ const MLoop *ml = &mloop[ml_index];
+ gpuHQNor *lnor_data = &(*(gpuHQNor **)data)[ml_index];
+ if (mr->loop_normals) {
+ normal_float_to_short_v3(&lnor_data->x, mr->loop_normals[ml_index]);
+ }
+ else if (mp->flag & ME_SMOOTH) {
+ copy_v3_v3_short(&lnor_data->x, mr->mvert[ml->v].no);
+ }
+ else {
+ normal_float_to_short_v3(&lnor_data->x, mr->poly_normals[mp_index]);
+ }
+
+ /* Flag for paint mode overlay.
+ * Only use #MR_EXTRACT_MAPPED in edit mode where it is used to display the edge-normals.
+ * In paint mode it will use the un-mapped data to draw the wire-frame. */
+ if (mp->flag & ME_HIDE || (mr->edit_bmesh && mr->extract_type == MR_EXTRACT_MAPPED &&
+ (mr->v_origindex) && mr->v_origindex[ml->v] == ORIGINDEX_NONE)) {
+ lnor_data->w = -1;
+ }
+ else if (mp->flag & ME_FACE_SEL) {
+ lnor_data->w = 1;
+ }
+ else {
+ lnor_data->w = 0;
+ }
+ }
+}
+
+const MeshExtract extract_lnor_hq = {
+ .init = extract_lnor_hq_init,
+ .iter_poly_bm = extract_lnor_hq_iter_poly_bm,
+ .iter_poly_mesh = extract_lnor_hq_iter_poly_mesh,
+ .data_type = MR_DATA_LOOP_NOR,
+ .data_size = sizeof(gpuHQNor *),
+ .use_threading = true,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.lnor),
+};
+
+/** \} */
+/* ---------------------------------------------------------------------- */
+/** \name Extract Loop Normal
+ * \{ */
+
+static void extract_lnor_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *tls_data)
+{
+ GPUVertBuf *vbo = buf;
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
+ GPU_vertformat_alias_add(&format, "lnor");
+ }
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, mr->loop_len);
+
+ *(GPUPackedNormal **)tls_data = GPU_vertbuf_get_data(vbo);
+}
+
+static void extract_lnor_iter_poly_bm(const MeshRenderData *mr,
+ const BMFace *f,
+ const int UNUSED(f_index),
+ void *data)
+{
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ const int l_index = BM_elem_index_get(l_iter);
+ if (mr->loop_normals) {
+ (*(GPUPackedNormal **)data)[l_index] = GPU_normal_convert_i10_v3(mr->loop_normals[l_index]);
+ }
+ else {
+ if (BM_elem_flag_test(f, BM_ELEM_SMOOTH)) {
+ (*(GPUPackedNormal **)data)[l_index] = GPU_normal_convert_i10_v3(
+ bm_vert_no_get(mr, l_iter->v));
+ }
+ else {
+ (*(GPUPackedNormal **)data)[l_index] = GPU_normal_convert_i10_v3(bm_face_no_get(mr, f));
+ }
+ }
+ (*(GPUPackedNormal **)data)[l_index].w = BM_elem_flag_test(f, BM_ELEM_HIDDEN) ? -1 : 0;
+ } while ((l_iter = l_iter->next) != l_first);
+}
+
+static void extract_lnor_iter_poly_mesh(const MeshRenderData *mr,
+ const MPoly *mp,
+ const int mp_index,
+ void *data)
+{
+ const MLoop *mloop = mr->mloop;
+ const int ml_index_end = mp->loopstart + mp->totloop;
+ for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
+ const MLoop *ml = &mloop[ml_index];
+ GPUPackedNormal *lnor_data = &(*(GPUPackedNormal **)data)[ml_index];
+ if (mr->loop_normals) {
+ *lnor_data = GPU_normal_convert_i10_v3(mr->loop_normals[ml_index]);
+ }
+ else if (mp->flag & ME_SMOOTH) {
+ *lnor_data = GPU_normal_convert_i10_s3(mr->mvert[ml->v].no);
+ }
+ else {
+ *lnor_data = GPU_normal_convert_i10_v3(mr->poly_normals[mp_index]);
+ }
+
+ /* Flag for paint mode overlay.
+ * Only use MR_EXTRACT_MAPPED in edit mode where it is used to display the edge-normals.
+ * In paint mode it will use the un-mapped data to draw the wire-frame. */
+ if (mp->flag & ME_HIDE || (mr->edit_bmesh && mr->extract_type == MR_EXTRACT_MAPPED &&
+ (mr->v_origindex) && mr->v_origindex[ml->v] == ORIGINDEX_NONE)) {
+ lnor_data->w = -1;
+ }
+ else if (mp->flag & ME_FACE_SEL) {
+ lnor_data->w = 1;
+ }
+ else {
+ lnor_data->w = 0;
+ }
+ }
+}
+
+const MeshExtract extract_lnor = {
+ .init = extract_lnor_init,
+ .iter_poly_bm = extract_lnor_iter_poly_bm,
+ .iter_poly_mesh = extract_lnor_iter_poly_mesh,
+ .data_type = MR_DATA_LOOP_NOR,
+ .data_size = sizeof(GPUPackedNormal *),
+ .use_threading = true,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.lnor),
+};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract UV layers
+ * \{ */
+
+static void extract_uv_init(const MeshRenderData *mr,
+ struct MeshBatchCache *cache,
+ void *buf,
+ void *UNUSED(tls_data))
+{
+ GPUVertBuf *vbo = buf;
+ GPUVertFormat format = {0};
+ GPU_vertformat_deinterleave(&format);
+
+ CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata;
+ uint32_t uv_layers = cache->cd_used.uv;
+ /* HACK to fix T68857 */
+ if (mr->extract_type == MR_EXTRACT_BMESH && cache->cd_used.edit_uv == 1) {
+ int layer = CustomData_get_active_layer(cd_ldata, CD_MLOOPUV);
+ if (layer != -1) {
+ uv_layers |= (1 << layer);
+ }
+ }
+
+ for (int i = 0; i < MAX_MTFACE; i++) {
+ if (uv_layers & (1 << i)) {
+ char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME];
+ const char *layer_name = CustomData_get_layer_name(cd_ldata, CD_MLOOPUV, i);
+
+ GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME);
+ /* UV layer name. */
+ BLI_snprintf(attr_name, sizeof(attr_name), "u%s", attr_safe_name);
+ GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ /* Auto layer name. */
+ BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name);
+ GPU_vertformat_alias_add(&format, attr_name);
+ /* Active render layer name. */
+ if (i == CustomData_get_render_layer(cd_ldata, CD_MLOOPUV)) {
+ GPU_vertformat_alias_add(&format, "u");
+ }
+ /* Active display layer name. */
+ if (i == CustomData_get_active_layer(cd_ldata, CD_MLOOPUV)) {
+ GPU_vertformat_alias_add(&format, "au");
+ /* Alias to `pos` for edit uvs. */
+ GPU_vertformat_alias_add(&format, "pos");
+ }
+ /* Stencil mask uv layer name. */
+ if (i == CustomData_get_stencil_layer(cd_ldata, CD_MLOOPUV)) {
+ GPU_vertformat_alias_add(&format, "mu");
+ }
+ }
+ }
+
+ int v_len = mr->loop_len;
+ if (format.attr_len == 0) {
+ GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
+ /* VBO will not be used, only allocate minimum of memory. */
+ v_len = 1;
+ }
+
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, v_len);
+
+ float(*uv_data)[2] = (float(*)[2])GPU_vertbuf_get_data(vbo);
+ for (int i = 0; i < MAX_MTFACE; i++) {
+ if (uv_layers & (1 << i)) {
+ if (mr->extract_type == MR_EXTRACT_BMESH) {
+ int cd_ofs = CustomData_get_n_offset(cd_ldata, CD_MLOOPUV, i);
+ BMIter f_iter;
+ BMFace *efa;
+ BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) {
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
+ do {
+ MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l_iter, cd_ofs);
+ memcpy(uv_data, luv->uv, sizeof(*uv_data));
+ uv_data++;
+ } while ((l_iter = l_iter->next) != l_first);
+ }
+ }
+ else {
+ MLoopUV *layer_data = CustomData_get_layer_n(cd_ldata, CD_MLOOPUV, i);
+ for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, uv_data++, layer_data++) {
+ memcpy(uv_data, layer_data->uv, sizeof(*uv_data));
+ }
+ }
+ }
+ }
+}
+
+const MeshExtract extract_uv = {
+ .init = extract_uv_init,
+ .data_type = 0,
+ .data_size = 0,
+ .use_threading = false,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.uv),
+};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Tangent layers
+ * \{ */
+
+static void extract_tan_ex_init(const MeshRenderData *mr,
+ struct MeshBatchCache *cache,
+ GPUVertBuf *vbo,
+ const bool do_hq)
+{
+ GPUVertCompType comp_type = do_hq ? GPU_COMP_I16 : GPU_COMP_I10;
+ GPUVertFetchMode fetch_mode = GPU_FETCH_INT_TO_FLOAT_UNIT;
+
+ GPUVertFormat format = {0};
+ GPU_vertformat_deinterleave(&format);
+
+ CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata;
+ CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata;
+ uint32_t tan_layers = cache->cd_used.tan;
+ float(*orco)[3] = CustomData_get_layer(cd_vdata, CD_ORCO);
+ bool orco_allocated = false;
+ const bool use_orco_tan = cache->cd_used.tan_orco != 0;
+
+ int tan_len = 0;
+ char tangent_names[MAX_MTFACE][MAX_CUSTOMDATA_LAYER_NAME];
+
+ for (int i = 0; i < MAX_MTFACE; i++) {
+ if (tan_layers & (1 << i)) {
+ char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME];
+ const char *layer_name = CustomData_get_layer_name(cd_ldata, CD_MLOOPUV, i);
+ GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME);
+ /* Tangent layer name. */
+ BLI_snprintf(attr_name, sizeof(attr_name), "t%s", attr_safe_name);
+ GPU_vertformat_attr_add(&format, attr_name, comp_type, 4, fetch_mode);
+ /* Active render layer name. */
+ if (i == CustomData_get_render_layer(cd_ldata, CD_MLOOPUV)) {
+ GPU_vertformat_alias_add(&format, "t");
+ }
+ /* Active display layer name. */
+ if (i == CustomData_get_active_layer(cd_ldata, CD_MLOOPUV)) {
+ GPU_vertformat_alias_add(&format, "at");
+ }
+
+ BLI_strncpy(tangent_names[tan_len++], layer_name, MAX_CUSTOMDATA_LAYER_NAME);
+ }
+ }
+ if (use_orco_tan && orco == NULL) {
+ /* If `orco` is not available compute it ourselves */
+ orco_allocated = true;
+ orco = MEM_mallocN(sizeof(*orco) * mr->vert_len, __func__);
+
+ if (mr->extract_type == MR_EXTRACT_BMESH) {
+ BMesh *bm = mr->bm;
+ for (int v = 0; v < mr->vert_len; v++) {
+ const BMVert *eve = BM_vert_at_index(bm, v);
+ /* Exceptional case where #bm_vert_co_get can be avoided, as we want the original coords.
+ * not the distorted ones. */
+ copy_v3_v3(orco[v], eve->co);
+ }
+ }
+ else {
+ const MVert *mv = mr->mvert;
+ for (int v = 0; v < mr->vert_len; v++, mv++) {
+ copy_v3_v3(orco[v], mv->co);
+ }
+ }
+ BKE_mesh_orco_verts_transform(mr->me, orco, mr->vert_len, 0);
+ }
+
+ /* Start Fresh */
+ CustomData loop_data;
+ CustomData_reset(&loop_data);
+ if (tan_len != 0 || use_orco_tan) {
+ short tangent_mask = 0;
+ bool calc_active_tangent = false;
+ if (mr->extract_type == MR_EXTRACT_BMESH) {
+ BKE_editmesh_loop_tangent_calc(mr->edit_bmesh,
+ calc_active_tangent,
+ tangent_names,
+ tan_len,
+ mr->poly_normals,
+ mr->loop_normals,
+ orco,
+ &loop_data,
+ mr->loop_len,
+ &tangent_mask);
+ }
+ else {
+ BKE_mesh_calc_loop_tangent_ex(mr->mvert,
+ mr->mpoly,
+ mr->poly_len,
+ mr->mloop,
+ mr->mlooptri,
+ mr->tri_len,
+ cd_ldata,
+ calc_active_tangent,
+ tangent_names,
+ tan_len,
+ mr->poly_normals,
+ mr->loop_normals,
+ orco,
+ &loop_data,
+ mr->loop_len,
+ &tangent_mask);
+ }
+ }
+
+ if (use_orco_tan) {
+ char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME];
+ const char *layer_name = CustomData_get_layer_name(&loop_data, CD_TANGENT, 0);
+ GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME);
+ BLI_snprintf(attr_name, sizeof(*attr_name), "t%s", attr_safe_name);
+ GPU_vertformat_attr_add(&format, attr_name, comp_type, 4, fetch_mode);
+ GPU_vertformat_alias_add(&format, "t");
+ GPU_vertformat_alias_add(&format, "at");
+ }
+
+ if (orco_allocated) {
+ MEM_SAFE_FREE(orco);
+ }
+
+ int v_len = mr->loop_len;
+ if (format.attr_len == 0) {
+ GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
+ /* VBO will not be used, only allocate minimum of memory. */
+ v_len = 1;
+ }
+
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, v_len);
+
+ if (do_hq) {
+ short(*tan_data)[4] = (short(*)[4])GPU_vertbuf_get_data(vbo);
+ for (int i = 0; i < tan_len; i++) {
+ const char *name = tangent_names[i];
+ float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_named(
+ &loop_data, CD_TANGENT, name);
+ for (int ml_index = 0; ml_index < mr->loop_len; ml_index++) {
+ normal_float_to_short_v3(*tan_data, layer_data[ml_index]);
+ (*tan_data)[3] = (layer_data[ml_index][3] > 0.0f) ? SHRT_MAX : SHRT_MIN;
+ tan_data++;
+ }
+ }
+ if (use_orco_tan) {
+ float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_n(&loop_data, CD_TANGENT, 0);
+ for (int ml_index = 0; ml_index < mr->loop_len; ml_index++) {
+ normal_float_to_short_v3(*tan_data, layer_data[ml_index]);
+ (*tan_data)[3] = (layer_data[ml_index][3] > 0.0f) ? SHRT_MAX : SHRT_MIN;
+ tan_data++;
+ }
+ }
+ }
+ else {
+ GPUPackedNormal *tan_data = (GPUPackedNormal *)GPU_vertbuf_get_data(vbo);
+ for (int i = 0; i < tan_len; i++) {
+ const char *name = tangent_names[i];
+ float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_named(
+ &loop_data, CD_TANGENT, name);
+ for (int ml_index = 0; ml_index < mr->loop_len; ml_index++) {
+ *tan_data = GPU_normal_convert_i10_v3(layer_data[ml_index]);
+ tan_data->w = (layer_data[ml_index][3] > 0.0f) ? 1 : -2;
+ tan_data++;
+ }
+ }
+ if (use_orco_tan) {
+ float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_n(&loop_data, CD_TANGENT, 0);
+ for (int ml_index = 0; ml_index < mr->loop_len; ml_index++) {
+ *tan_data = GPU_normal_convert_i10_v3(layer_data[ml_index]);
+ tan_data->w = (layer_data[ml_index][3] > 0.0f) ? 1 : -2;
+ tan_data++;
+ }
+ }
+ }
+
+ CustomData_free(&loop_data, mr->loop_len);
+}
+
+static void extract_tan_init(const MeshRenderData *mr,
+ struct MeshBatchCache *cache,
+ void *buf,
+ void *UNUSED(tls_data))
+{
+ extract_tan_ex_init(mr, cache, buf, false);
+}
+
+const MeshExtract extract_tan = {
+ .init = extract_tan_init,
+ .data_type = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | MR_DATA_LOOPTRI,
+ .data_size = 0,
+ .use_threading = false,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.tan),
+};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract HQ Tangent layers
+ * \{ */
+
+static void extract_tan_hq_init(const MeshRenderData *mr,
+ struct MeshBatchCache *cache,
+ void *buf,
+ void *UNUSED(tls_data))
+{
+ extract_tan_ex_init(mr, cache, buf, true);
+}
+
+const MeshExtract extract_tan_hq = {
+ .init = extract_tan_hq_init,
+ .data_type = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | MR_DATA_LOOPTRI,
+ .data_size = 0,
+ .use_threading = false,
+};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Sculpt Data
+ * \{ */
+
+static void extract_sculpt_data_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *UNUSED(tls_data))
+{
+ GPUVertBuf *vbo = buf;
+ GPUVertFormat format = {0};
+
+ CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata;
+ CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata;
+ CustomData *cd_pdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->pdata : &mr->me->pdata;
+
+ float *cd_mask = CustomData_get_layer(cd_vdata, CD_PAINT_MASK);
+ int *cd_face_set = CustomData_get_layer(cd_pdata, CD_SCULPT_FACE_SETS);
+
+ if (format.attr_len == 0) {
+ GPU_vertformat_attr_add(&format, "fset", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
+ GPU_vertformat_attr_add(&format, "msk", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
+ }
+
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, mr->loop_len);
+
+ typedef struct gpuSculptData {
+ uint8_t face_set_color[4];
+ float mask;
+ } gpuSculptData;
+
+ gpuSculptData *vbo_data = (gpuSculptData *)GPU_vertbuf_get_data(vbo);
+ MLoop *loops = CustomData_get_layer(cd_ldata, CD_MLOOP);
+
+ if (mr->extract_type == MR_EXTRACT_BMESH) {
+ int cd_mask_ofs = CustomData_get_offset(cd_vdata, CD_PAINT_MASK);
+ int cd_face_set_ofs = CustomData_get_offset(cd_pdata, CD_SCULPT_FACE_SETS);
+ BMIter f_iter;
+ BMFace *efa;
+ BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) {
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
+ do {
+ float v_mask = 0.0f;
+ if (cd_mask) {
+ v_mask = BM_ELEM_CD_GET_FLOAT(l_iter->v, cd_mask_ofs);
+ }
+ vbo_data->mask = v_mask;
+ uchar face_set_color[4] = {UCHAR_MAX, UCHAR_MAX, UCHAR_MAX, UCHAR_MAX};
+ if (cd_face_set) {
+ const int face_set_id = BM_ELEM_CD_GET_INT(l_iter->f, cd_face_set_ofs);
+ if (face_set_id != mr->me->face_sets_color_default) {
+ BKE_paint_face_set_overlay_color_get(
+ face_set_id, mr->me->face_sets_color_seed, face_set_color);
+ }
+ }
+ copy_v3_v3_uchar(vbo_data->face_set_color, face_set_color);
+ vbo_data++;
+ } while ((l_iter = l_iter->next) != l_first);
+ }
+ }
+ else {
+ int mp_loop = 0;
+ for (int mp_index = 0; mp_index < mr->poly_len; mp_index++) {
+ const MPoly *p = &mr->mpoly[mp_index];
+ for (int l = 0; l < p->totloop; l++) {
+ float v_mask = 0.0f;
+ if (cd_mask) {
+ v_mask = cd_mask[loops[mp_loop].v];
+ }
+ vbo_data->mask = v_mask;
+
+ uchar face_set_color[4] = {UCHAR_MAX, UCHAR_MAX, UCHAR_MAX, UCHAR_MAX};
+ if (cd_face_set) {
+ const int face_set_id = cd_face_set[mp_index];
+ /* Skip for the default color Face Set to render it white. */
+ if (face_set_id != mr->me->face_sets_color_default) {
+ BKE_paint_face_set_overlay_color_get(
+ face_set_id, mr->me->face_sets_color_seed, face_set_color);
+ }
+ }
+ copy_v3_v3_uchar(vbo_data->face_set_color, face_set_color);
+ mp_loop++;
+ vbo_data++;
+ }
+ }
+ }
+}
+
+const MeshExtract extract_sculpt_data = {
+ .init = extract_sculpt_data_init,
+ .data_type = 0,
+ .data_size = 0,
+ /* TODO: enable threading. */
+ .use_threading = false,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.sculpt_data)};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract VCol
+ * \{ */
+
+static void extract_vcol_init(const MeshRenderData *mr,
+ struct MeshBatchCache *cache,
+ void *buf,
+ void *UNUSED(tls_data))
+{
+ GPUVertBuf *vbo = buf;
+ GPUVertFormat format = {0};
+ GPU_vertformat_deinterleave(&format);
+
+ CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata;
+ CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata;
+ uint32_t vcol_layers = cache->cd_used.vcol;
+ uint32_t svcol_layers = cache->cd_used.sculpt_vcol;
+
+ for (int i = 0; i < MAX_MCOL; i++) {
+ if (vcol_layers & (1 << i)) {
+ char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME];
+ const char *layer_name = CustomData_get_layer_name(cd_ldata, CD_MLOOPCOL, i);
+ GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME);
+
+ BLI_snprintf(attr_name, sizeof(attr_name), "c%s", attr_safe_name);
+ GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_U16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
+
+ if (i == CustomData_get_render_layer(cd_ldata, CD_MLOOPCOL)) {
+ GPU_vertformat_alias_add(&format, "c");
+ }
+ if (i == CustomData_get_active_layer(cd_ldata, CD_MLOOPCOL)) {
+ GPU_vertformat_alias_add(&format, "ac");
+ }
+
+ /* Gather number of auto layers. */
+ /* We only do `vcols` that are not overridden by `uvs` and sculpt vertex colors. */
+ if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, layer_name) == -1 &&
+ CustomData_get_named_layer_index(cd_vdata, CD_PROP_COLOR, layer_name) == -1) {
+ BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name);
+ GPU_vertformat_alias_add(&format, attr_name);
+ }
+ }
+ }
+
+ /* Sculpt Vertex Colors */
+ if (U.experimental.use_sculpt_vertex_colors) {
+ for (int i = 0; i < 8; i++) {
+ if (svcol_layers & (1 << i)) {
+ char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME];
+ const char *layer_name = CustomData_get_layer_name(cd_vdata, CD_PROP_COLOR, i);
+ GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME);
+
+ BLI_snprintf(attr_name, sizeof(attr_name), "c%s", attr_safe_name);
+ GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_U16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
+
+ if (i == CustomData_get_render_layer(cd_vdata, CD_PROP_COLOR)) {
+ GPU_vertformat_alias_add(&format, "c");
+ }
+ if (i == CustomData_get_active_layer(cd_vdata, CD_PROP_COLOR)) {
+ GPU_vertformat_alias_add(&format, "ac");
+ }
+ /* Gather number of auto layers. */
+ /* We only do `vcols` that are not overridden by `uvs`. */
+ if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, layer_name) == -1) {
+ BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name);
+ GPU_vertformat_alias_add(&format, attr_name);
+ }
+ }
+ }
+ }
+
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, mr->loop_len);
+
+ typedef struct gpuMeshVcol {
+ ushort r, g, b, a;
+ } gpuMeshVcol;
+
+ gpuMeshVcol *vcol_data = (gpuMeshVcol *)GPU_vertbuf_get_data(vbo);
+ MLoop *loops = CustomData_get_layer(cd_ldata, CD_MLOOP);
+
+ for (int i = 0; i < MAX_MCOL; i++) {
+ if (vcol_layers & (1 << i)) {
+ if (mr->extract_type == MR_EXTRACT_BMESH) {
+ int cd_ofs = CustomData_get_n_offset(cd_ldata, CD_MLOOPCOL, i);
+ BMIter f_iter;
+ BMFace *efa;
+ BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) {
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
+ do {
+ const MLoopCol *mloopcol = BM_ELEM_CD_GET_VOID_P(l_iter, cd_ofs);
+ vcol_data->r = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->r]);
+ vcol_data->g = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->g]);
+ vcol_data->b = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->b]);
+ vcol_data->a = unit_float_to_ushort_clamp(mloopcol->a * (1.0f / 255.0f));
+ vcol_data++;
+ } while ((l_iter = l_iter->next) != l_first);
+ }
+ }
+ else {
+ const MLoopCol *mloopcol = (MLoopCol *)CustomData_get_layer_n(cd_ldata, CD_MLOOPCOL, i);
+ for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, mloopcol++, vcol_data++) {
+ vcol_data->r = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->r]);
+ vcol_data->g = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->g]);
+ vcol_data->b = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->b]);
+ vcol_data->a = unit_float_to_ushort_clamp(mloopcol->a * (1.0f / 255.0f));
+ }
+ }
+ }
+
+ if (svcol_layers & (1 << i) && U.experimental.use_sculpt_vertex_colors) {
+ if (mr->extract_type == MR_EXTRACT_BMESH) {
+ int cd_ofs = CustomData_get_n_offset(cd_vdata, CD_PROP_COLOR, i);
+ BMIter f_iter;
+ BMFace *efa;
+ BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) {
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
+ do {
+ const MPropCol *prop_col = BM_ELEM_CD_GET_VOID_P(l_iter->v, cd_ofs);
+ vcol_data->r = unit_float_to_ushort_clamp(prop_col->color[0]);
+ vcol_data->g = unit_float_to_ushort_clamp(prop_col->color[1]);
+ vcol_data->b = unit_float_to_ushort_clamp(prop_col->color[2]);
+ vcol_data->a = unit_float_to_ushort_clamp(prop_col->color[3]);
+ vcol_data++;
+ } while ((l_iter = l_iter->next) != l_first);
+ }
+ }
+ else {
+ MPropCol *vcol = CustomData_get_layer_n(cd_vdata, CD_PROP_COLOR, i);
+ for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, vcol_data++) {
+ vcol_data->r = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[0]);
+ vcol_data->g = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[1]);
+ vcol_data->b = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[2]);
+ vcol_data->a = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[3]);
+ }
+ }
+ }
+ }
+}
+
+const MeshExtract extract_vcol = {
+ .init = extract_vcol_init,
+ .data_type = 0,
+ .data_size = 0,
+ .use_threading = false,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.vcol),
+};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Orco
+ * \{ */
+
+typedef struct MeshExtract_Orco_Data {
+ float (*vbo_data)[4];
+ float (*orco)[3];
+} MeshExtract_Orco_Data;
+
+static void extract_orco_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *tls_data)
+{
+ GPUVertBuf *vbo = buf;
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ /* FIXME(fclem): We use the last component as a way to differentiate from generic vertex
+ * attributes. This is a substantial waste of video-ram and should be done another way.
+ * Unfortunately, at the time of writing, I did not found any other "non disruptive"
+ * alternative. */
+ GPU_vertformat_attr_add(&format, "orco", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+ }
+
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, mr->loop_len);
+
+ CustomData *cd_vdata = &mr->me->vdata;
+
+ MeshExtract_Orco_Data *data = tls_data;
+ data->vbo_data = (float(*)[4])GPU_vertbuf_get_data(vbo);
+ data->orco = CustomData_get_layer(cd_vdata, CD_ORCO);
+ /* Make sure `orco` layer was requested only if needed! */
+ BLI_assert(data->orco);
+}
+
+static void extract_orco_iter_poly_bm(const MeshRenderData *UNUSED(mr),
+ const BMFace *f,
+ const int UNUSED(f_index),
+ void *data)
+{
+ MeshExtract_Orco_Data *orco_data = (MeshExtract_Orco_Data *)data;
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ const int l_index = BM_elem_index_get(l_iter);
+ float *loop_orco = orco_data->vbo_data[l_index];
+ copy_v3_v3(loop_orco, orco_data->orco[BM_elem_index_get(l_iter->v)]);
+ loop_orco[3] = 0.0; /* Tag as not a generic attribute. */
+ } while ((l_iter = l_iter->next) != l_first);
+}
+
+static void extract_orco_iter_poly_mesh(const MeshRenderData *mr,
+ const MPoly *mp,
+ const int UNUSED(mp_index),
+ void *data)
+{
+ const MLoop *mloop = mr->mloop;
+ const int ml_index_end = mp->loopstart + mp->totloop;
+ for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
+ const MLoop *ml = &mloop[ml_index];
+ MeshExtract_Orco_Data *orco_data = (MeshExtract_Orco_Data *)data;
+ float *loop_orco = orco_data->vbo_data[ml_index];
+ copy_v3_v3(loop_orco, orco_data->orco[ml->v]);
+ loop_orco[3] = 0.0; /* Tag as not a generic attribute. */
+ }
+}
+
+const MeshExtract extract_orco = {
+ .init = extract_orco_init,
+ .iter_poly_bm = extract_orco_iter_poly_bm,
+ .iter_poly_mesh = extract_orco_iter_poly_mesh,
+ .data_type = 0,
+ .data_size = sizeof(MeshExtract_Orco_Data),
+ .use_threading = true,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.orco),
+};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Edge Factor
+ * Defines how much an edge is visible.
+ * \{ */
+
+typedef struct MeshExtract_EdgeFac_Data {
+ uchar *vbo_data;
+ bool use_edge_render;
+ /* Number of loop per edge. */
+ uchar *edge_loop_count;
+} MeshExtract_EdgeFac_Data;
+
+static float loop_edge_factor_get(const float f_no[3],
+ const float v_co[3],
+ const float v_no[3],
+ const float v_next_co[3])
+{
+ float enor[3], evec[3];
+ sub_v3_v3v3(evec, v_next_co, v_co);
+ cross_v3_v3v3(enor, v_no, evec);
+ normalize_v3(enor);
+ float d = fabsf(dot_v3v3(enor, f_no));
+ /* Re-scale to the slider range. */
+ d *= (1.0f / 0.065f);
+ CLAMP(d, 0.0f, 1.0f);
+ return d;
+}
+
+static void extract_edge_fac_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *tls_data)
+{
+ GPUVertBuf *vbo = buf;
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ GPU_vertformat_attr_add(&format, "wd", GPU_COMP_U8, 1, GPU_FETCH_INT_TO_FLOAT_UNIT);
+ }
+
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len);
+
+ MeshExtract_EdgeFac_Data *data = tls_data;
+
+ if (mr->extract_type == MR_EXTRACT_MESH) {
+ data->edge_loop_count = MEM_callocN(sizeof(uint32_t) * mr->edge_len, __func__);
+
+ /* HACK(fclem) Detecting the need for edge render.
+ * We could have a flag in the mesh instead or check the modifier stack. */
+ const MEdge *med = mr->medge;
+ for (int e_index = 0; e_index < mr->edge_len; e_index++, med++) {
+ if ((med->flag & ME_EDGERENDER) == 0) {
+ data->use_edge_render = true;
+ break;
+ }
+ }
+ }
+ else {
+ /* HACK to bypass non-manifold check in mesh_edge_fac_finish(). */
+ data->use_edge_render = true;
+ }
+
+ data->vbo_data = GPU_vertbuf_get_data(vbo);
+}
+
+static void extract_edge_fac_iter_poly_bm(const MeshRenderData *mr,
+ const BMFace *f,
+ const int UNUSED(f_index),
+ void *_data)
+{
+ MeshExtract_EdgeFac_Data *data = _data;
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ const int l_index = BM_elem_index_get(l_iter);
+
+ if (BM_edge_is_manifold(l_iter->e)) {
+ float ratio = loop_edge_factor_get(bm_face_no_get(mr, f),
+ bm_vert_co_get(mr, l_iter->v),
+ bm_vert_no_get(mr, l_iter->v),
+ bm_vert_co_get(mr, l_iter->next->v));
+ data->vbo_data[l_index] = ratio * 253 + 1;
+ }
+ else {
+ data->vbo_data[l_index] = 255;
+ }
+ } while ((l_iter = l_iter->next) != l_first);
+}
+
+static void extract_edge_fac_iter_poly_mesh(const MeshRenderData *mr,
+ const MPoly *mp,
+ const int mp_index,
+ void *_data)
+{
+ MeshExtract_EdgeFac_Data *data = (MeshExtract_EdgeFac_Data *)_data;
+
+ const MLoop *mloop = mr->mloop;
+ const int ml_index_end = mp->loopstart + mp->totloop;
+ for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
+ const MLoop *ml = &mloop[ml_index];
+
+ if (data->use_edge_render) {
+ const MEdge *med = &mr->medge[ml->e];
+ data->vbo_data[ml_index] = (med->flag & ME_EDGERENDER) ? 255 : 0;
+ }
+ else {
+
+ /* Count loop per edge to detect non-manifold. */
+ if (data->edge_loop_count[ml->e] < 3) {
+ data->edge_loop_count[ml->e]++;
+ }
+ if (data->edge_loop_count[ml->e] == 2) {
+ /* Manifold */
+ const int ml_index_last = mp->totloop + mp->loopstart - 1;
+ const int ml_index_other = (ml_index == ml_index_last) ? mp->loopstart : (ml_index + 1);
+ const MLoop *ml_next = &mr->mloop[ml_index_other];
+ const MVert *v1 = &mr->mvert[ml->v];
+ const MVert *v2 = &mr->mvert[ml_next->v];
+ float vnor_f[3];
+ normal_short_to_float_v3(vnor_f, v1->no);
+ float ratio = loop_edge_factor_get(mr->poly_normals[mp_index], v1->co, vnor_f, v2->co);
+ data->vbo_data[ml_index] = ratio * 253 + 1;
+ }
+ else {
+ /* Non-manifold */
+ data->vbo_data[ml_index] = 255;
+ }
+ }
+ }
+}
+
+static void extract_edge_fac_iter_ledge_bm(const MeshRenderData *mr,
+ const BMEdge *UNUSED(eed),
+ const int ledge_index,
+ void *_data)
+{
+ MeshExtract_EdgeFac_Data *data = _data;
+ data->vbo_data[mr->loop_len + (ledge_index * 2) + 0] = 255;
+ data->vbo_data[mr->loop_len + (ledge_index * 2) + 1] = 255;
+}
+
+static void extract_edge_fac_iter_ledge_mesh(const MeshRenderData *mr,
+ const MEdge *UNUSED(med),
+ const int ledge_index,
+ void *_data)
+{
+ MeshExtract_EdgeFac_Data *data = _data;
+
+ data->vbo_data[mr->loop_len + ledge_index * 2 + 0] = 255;
+ data->vbo_data[mr->loop_len + ledge_index * 2 + 1] = 255;
+}
+
+static void extract_edge_fac_finish(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *_data)
+{
+ GPUVertBuf *vbo = buf;
+ MeshExtract_EdgeFac_Data *data = _data;
+
+ if (GPU_crappy_amd_driver()) {
+ /* Some AMD drivers strangely crash with VBO's with a one byte format.
+ * To workaround we reinitialize the VBO with another format and convert
+ * all bytes to floats. */
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ GPU_vertformat_attr_add(&format, "wd", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
+ }
+ /* We keep the data reference in data->vbo_data. */
+ data->vbo_data = GPU_vertbuf_steal_data(vbo);
+ GPU_vertbuf_clear(vbo);
+
+ int buf_len = mr->loop_len + mr->loop_loose_len;
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, buf_len);
+
+ float *fdata = (float *)GPU_vertbuf_get_data(vbo);
+ for (int ml_index = 0; ml_index < buf_len; ml_index++, fdata++) {
+ *fdata = data->vbo_data[ml_index] / 255.0f;
+ }
+ /* Free old byte data. */
+ MEM_freeN(data->vbo_data);
+ }
+ MEM_SAFE_FREE(data->edge_loop_count);
+}
+
+const MeshExtract extract_edge_fac = {
+ .init = extract_edge_fac_init,
+ .iter_poly_bm = extract_edge_fac_iter_poly_bm,
+ .iter_poly_mesh = extract_edge_fac_iter_poly_mesh,
+ .iter_ledge_bm = extract_edge_fac_iter_ledge_bm,
+ .iter_ledge_mesh = extract_edge_fac_iter_ledge_mesh,
+ .finish = extract_edge_fac_finish,
+ .data_type = MR_DATA_POLY_NOR,
+ .data_size = sizeof(MeshExtract_EdgeFac_Data),
+ .use_threading = false,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edge_fac)};
+
+/** \} */
+/* ---------------------------------------------------------------------- */
+/** \name Extract Vertex Weight
+ * \{ */
+
+typedef struct MeshExtract_Weight_Data {
+ float *vbo_data;
+ const DRW_MeshWeightState *wstate;
+ const MDeformVert *dvert; /* For #Mesh. */
+ int cd_ofs; /* For #BMesh. */
+} MeshExtract_Weight_Data;
+
+static float evaluate_vertex_weight(const MDeformVert *dvert, const DRW_MeshWeightState *wstate)
+{
+ /* Error state. */
+ if ((wstate->defgroup_active < 0) && (wstate->defgroup_len > 0)) {
+ return -2.0f;
+ }
+ if (dvert == NULL) {
+ return (wstate->alert_mode != OB_DRAW_GROUPUSER_NONE) ? -1.0f : 0.0f;
+ }
+
+ float input = 0.0f;
+ if (wstate->flags & DRW_MESH_WEIGHT_STATE_MULTIPAINT) {
+ /* Multi-Paint feature */
+ bool is_normalized = (wstate->flags & (DRW_MESH_WEIGHT_STATE_AUTO_NORMALIZE |
+ DRW_MESH_WEIGHT_STATE_LOCK_RELATIVE));
+ input = BKE_defvert_multipaint_collective_weight(dvert,
+ wstate->defgroup_len,
+ wstate->defgroup_sel,
+ wstate->defgroup_sel_count,
+ is_normalized);
+ /* make it black if the selected groups have no weight on a vertex */
+ if (input == 0.0f) {
+ return -1.0f;
+ }
+ }
+ else {
+ /* default, non tricky behavior */
+ input = BKE_defvert_find_weight(dvert, wstate->defgroup_active);
+
+ if (input == 0.0f) {
+ switch (wstate->alert_mode) {
+ case OB_DRAW_GROUPUSER_ACTIVE:
+ return -1.0f;
+ break;
+ case OB_DRAW_GROUPUSER_ALL:
+ if (BKE_defvert_is_weight_zero(dvert, wstate->defgroup_len)) {
+ return -1.0f;
+ }
+ break;
+ }
+ }
+ }
+
+ /* Lock-Relative: display the fraction of current weight vs total unlocked weight. */
+ if (wstate->flags & DRW_MESH_WEIGHT_STATE_LOCK_RELATIVE) {
+ input = BKE_defvert_lock_relative_weight(
+ input, dvert, wstate->defgroup_len, wstate->defgroup_locked, wstate->defgroup_unlocked);
+ }
+
+ CLAMP(input, 0.0f, 1.0f);
+ return input;
+}
+
+static void extract_weights_init(const MeshRenderData *mr,
+ struct MeshBatchCache *cache,
+ void *buf,
+ void *tls_data)
+{
+ GPUVertBuf *vbo = buf;
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ GPU_vertformat_attr_add(&format, "weight", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
+ }
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len);
+
+ MeshExtract_Weight_Data *data = tls_data;
+ data->vbo_data = (float *)GPU_vertbuf_get_data(vbo);
+ data->wstate = &cache->weight_state;
+
+ if (data->wstate->defgroup_active == -1) {
+ /* Nothing to show. */
+ data->dvert = NULL;
+ data->cd_ofs = -1;
+ }
+ else if (mr->extract_type == MR_EXTRACT_BMESH) {
+ data->dvert = NULL;
+ data->cd_ofs = CustomData_get_offset(&mr->bm->vdata, CD_MDEFORMVERT);
+ }
+ else {
+ data->dvert = CustomData_get_layer(&mr->me->vdata, CD_MDEFORMVERT);
+ data->cd_ofs = -1;
+ }
+}
+
+static void extract_weights_iter_poly_bm(const MeshRenderData *UNUSED(mr),
+ const BMFace *f,
+ const int UNUSED(f_index),
+ void *_data)
+{
+ MeshExtract_Weight_Data *data = _data;
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ const int l_index = BM_elem_index_get(l_iter);
+ if (data->cd_ofs != -1) {
+ const MDeformVert *dvert = BM_ELEM_CD_GET_VOID_P(l_iter->v, data->cd_ofs);
+ data->vbo_data[l_index] = evaluate_vertex_weight(dvert, data->wstate);
+ }
+ else {
+ data->vbo_data[l_index] = evaluate_vertex_weight(NULL, data->wstate);
+ }
+ } while ((l_iter = l_iter->next) != l_first);
+}
+
+static void extract_weights_iter_poly_mesh(const MeshRenderData *mr,
+ const MPoly *mp,
+ const int UNUSED(mp_index),
+ void *_data)
+{
+ MeshExtract_Weight_Data *data = _data;
+ const MLoop *mloop = mr->mloop;
+ const int ml_index_end = mp->loopstart + mp->totloop;
+ for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
+ const MLoop *ml = &mloop[ml_index];
+ if (data->dvert != NULL) {
+ const MDeformVert *dvert = &data->dvert[ml->v];
+ data->vbo_data[ml_index] = evaluate_vertex_weight(dvert, data->wstate);
+ }
+ else {
+ const MDeformVert *dvert = NULL;
+ data->vbo_data[ml_index] = evaluate_vertex_weight(dvert, data->wstate);
+ }
+ }
+}
+
+const MeshExtract extract_weights = {
+ .init = extract_weights_init,
+ .iter_poly_bm = extract_weights_iter_poly_bm,
+ .iter_poly_mesh = extract_weights_iter_poly_mesh,
+ .data_type = 0,
+ .data_size = sizeof(MeshExtract_Weight_Data),
+ .use_threading = true,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.weights),
+};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Edit Mode Data / Flags
+ * \{ */
+
+typedef struct EditLoopData {
+ uchar v_flag;
+ uchar e_flag;
+ uchar crease;
+ uchar bweight;
+} EditLoopData;
+
+static void mesh_render_data_face_flag(const MeshRenderData *mr,
+ const BMFace *efa,
+ const int cd_ofs,
+ EditLoopData *eattr)
+{
+ if (efa == mr->efa_act) {
+ eattr->v_flag |= VFLAG_FACE_ACTIVE;
+ }
+ if (BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
+ eattr->v_flag |= VFLAG_FACE_SELECTED;
+ }
+
+ if (efa == mr->efa_act_uv) {
+ eattr->v_flag |= VFLAG_FACE_UV_ACTIVE;
+ }
+ if ((cd_ofs != -1) && uvedit_face_select_test_ex(mr->toolsettings, (BMFace *)efa, cd_ofs)) {
+ eattr->v_flag |= VFLAG_FACE_UV_SELECT;
+ }
+
+#ifdef WITH_FREESTYLE
+ if (mr->freestyle_face_ofs != -1) {
+ const FreestyleFace *ffa = BM_ELEM_CD_GET_VOID_P(efa, mr->freestyle_face_ofs);
+ if (ffa->flag & FREESTYLE_FACE_MARK) {
+ eattr->v_flag |= VFLAG_FACE_FREESTYLE;
+ }
+ }
+#endif
+}
+
+static void mesh_render_data_edge_flag(const MeshRenderData *mr,
+ const BMEdge *eed,
+ EditLoopData *eattr)
+{
+ const ToolSettings *ts = mr->toolsettings;
+ const bool is_vertex_select_mode = (ts != NULL) && (ts->selectmode & SCE_SELECT_VERTEX) != 0;
+ const bool is_face_only_select_mode = (ts != NULL) && (ts->selectmode == SCE_SELECT_FACE);
+
+ if (eed == mr->eed_act) {
+ eattr->e_flag |= VFLAG_EDGE_ACTIVE;
+ }
+ if (!is_vertex_select_mode && BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
+ eattr->e_flag |= VFLAG_EDGE_SELECTED;
+ }
+ if (is_vertex_select_mode && BM_elem_flag_test(eed->v1, BM_ELEM_SELECT) &&
+ BM_elem_flag_test(eed->v2, BM_ELEM_SELECT)) {
+ eattr->e_flag |= VFLAG_EDGE_SELECTED;
+ eattr->e_flag |= VFLAG_VERT_SELECTED;
+ }
+ if (BM_elem_flag_test(eed, BM_ELEM_SEAM)) {
+ eattr->e_flag |= VFLAG_EDGE_SEAM;
+ }
+ if (!BM_elem_flag_test(eed, BM_ELEM_SMOOTH)) {
+ eattr->e_flag |= VFLAG_EDGE_SHARP;
+ }
+
+ /* Use active edge color for active face edges because
+ * specular highlights make it hard to see T55456#510873.
+ *
+ * This isn't ideal since it can't be used when mixing edge/face modes
+ * but it's still better than not being able to see the active face. */
+ if (is_face_only_select_mode) {
+ if (mr->efa_act != NULL) {
+ if (BM_edge_in_face(eed, mr->efa_act)) {
+ eattr->e_flag |= VFLAG_EDGE_ACTIVE;
+ }
+ }
+ }
+
+ /* Use a byte for value range */
+ if (mr->crease_ofs != -1) {
+ float crease = BM_ELEM_CD_GET_FLOAT(eed, mr->crease_ofs);
+ if (crease > 0) {
+ eattr->crease = (uchar)(crease * 255.0f);
+ }
+ }
+ /* Use a byte for value range */
+ if (mr->bweight_ofs != -1) {
+ float bweight = BM_ELEM_CD_GET_FLOAT(eed, mr->bweight_ofs);
+ if (bweight > 0) {
+ eattr->bweight = (uchar)(bweight * 255.0f);
+ }
+ }
+#ifdef WITH_FREESTYLE
+ if (mr->freestyle_edge_ofs != -1) {
+ const FreestyleEdge *fed = BM_ELEM_CD_GET_VOID_P(eed, mr->freestyle_edge_ofs);
+ if (fed->flag & FREESTYLE_EDGE_MARK) {
+ eattr->e_flag |= VFLAG_EDGE_FREESTYLE;
+ }
+ }
+#endif
+}
+
+static void mesh_render_data_loop_flag(const MeshRenderData *mr,
+ BMLoop *l,
+ const int cd_ofs,
+ EditLoopData *eattr)
+{
+ if (cd_ofs == -1) {
+ return;
+ }
+ MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_ofs);
+ if (luv != NULL && (luv->flag & MLOOPUV_PINNED)) {
+ eattr->v_flag |= VFLAG_VERT_UV_PINNED;
+ }
+ if (uvedit_uv_select_test_ex(mr->toolsettings, l, cd_ofs)) {
+ eattr->v_flag |= VFLAG_VERT_UV_SELECT;
+ }
+}
+
+static void mesh_render_data_loop_edge_flag(const MeshRenderData *mr,
+ BMLoop *l,
+ const int cd_ofs,
+ EditLoopData *eattr)
+{
+ if (cd_ofs == -1) {
+ return;
+ }
+ if (uvedit_edge_select_test_ex(mr->toolsettings, l, cd_ofs)) {
+ eattr->v_flag |= VFLAG_EDGE_UV_SELECT;
+ eattr->v_flag |= VFLAG_VERT_UV_SELECT;
+ }
+}
+
+static void mesh_render_data_vert_flag(const MeshRenderData *mr,
+ const BMVert *eve,
+ EditLoopData *eattr)
+{
+ if (eve == mr->eve_act) {
+ eattr->e_flag |= VFLAG_VERT_ACTIVE;
+ }
+ if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
+ eattr->e_flag |= VFLAG_VERT_SELECTED;
+ }
+}
+
+static void extract_edit_data_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *tls_data)
+{
+ GPUVertBuf *vbo = buf;
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ /* WARNING: Adjust #EditLoopData struct accordingly. */
+ GPU_vertformat_attr_add(&format, "data", GPU_COMP_U8, 4, GPU_FETCH_INT);
+ GPU_vertformat_alias_add(&format, "flag");
+ }
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len);
+ EditLoopData *vbo_data = GPU_vertbuf_get_data(vbo);
+ *(EditLoopData **)tls_data = vbo_data;
+}
+
+static void extract_edit_data_iter_poly_bm(const MeshRenderData *mr,
+ const BMFace *f,
+ const int UNUSED(f_index),
+ void *_data)
+{
+ EditLoopData *vbo_data = *(EditLoopData **)_data;
+
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ const int l_index = BM_elem_index_get(l_iter);
+
+ EditLoopData *data = vbo_data + l_index;
+ memset(data, 0x0, sizeof(*data));
+ mesh_render_data_face_flag(mr, f, -1, data);
+ mesh_render_data_edge_flag(mr, l_iter->e, data);
+ mesh_render_data_vert_flag(mr, l_iter->v, data);
+ } while ((l_iter = l_iter->next) != l_first);
+}
+
+static void extract_edit_data_iter_poly_mesh(const MeshRenderData *mr,
+ const MPoly *mp,
+ const int mp_index,
+ void *_data)
+{
+ EditLoopData *vbo_data = *(EditLoopData **)_data;
+
+ const MLoop *mloop = mr->mloop;
+ const int ml_index_end = mp->loopstart + mp->totloop;
+ for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
+ const MLoop *ml = &mloop[ml_index];
+ EditLoopData *data = vbo_data + ml_index;
+ memset(data, 0x0, sizeof(*data));
+ BMFace *efa = bm_original_face_get(mr, mp_index);
+ BMEdge *eed = bm_original_edge_get(mr, ml->e);
+ BMVert *eve = bm_original_vert_get(mr, ml->v);
+ if (efa) {
+ mesh_render_data_face_flag(mr, efa, -1, data);
+ }
+ if (eed) {
+ mesh_render_data_edge_flag(mr, eed, data);
+ }
+ if (eve) {
+ mesh_render_data_vert_flag(mr, eve, data);
+ }
+ }
+}
+
+static void extract_edit_data_iter_ledge_bm(const MeshRenderData *mr,
+ const BMEdge *eed,
+ const int ledge_index,
+ void *_data)
+{
+ EditLoopData *vbo_data = *(EditLoopData **)_data;
+ EditLoopData *data = vbo_data + mr->loop_len + (ledge_index * 2);
+ memset(data, 0x0, sizeof(*data) * 2);
+ mesh_render_data_edge_flag(mr, eed, &data[0]);
+ data[1] = data[0];
+ mesh_render_data_vert_flag(mr, eed->v1, &data[0]);
+ mesh_render_data_vert_flag(mr, eed->v2, &data[1]);
+}
+
+static void extract_edit_data_iter_ledge_mesh(const MeshRenderData *mr,
+ const MEdge *med,
+ const int ledge_index,
+ void *_data)
+{
+ EditLoopData *vbo_data = *(EditLoopData **)_data;
+ EditLoopData *data = vbo_data + mr->loop_len + ledge_index * 2;
+ memset(data, 0x0, sizeof(*data) * 2);
+ const int e_index = mr->ledges[ledge_index];
+ BMEdge *eed = bm_original_edge_get(mr, e_index);
+ BMVert *eve1 = bm_original_vert_get(mr, med->v1);
+ BMVert *eve2 = bm_original_vert_get(mr, med->v2);
+ if (eed) {
+ mesh_render_data_edge_flag(mr, eed, &data[0]);
+ data[1] = data[0];
+ }
+ if (eve1) {
+ mesh_render_data_vert_flag(mr, eve1, &data[0]);
+ }
+ if (eve2) {
+ mesh_render_data_vert_flag(mr, eve2, &data[1]);
+ }
+}
+
+static void extract_edit_data_iter_lvert_bm(const MeshRenderData *mr,
+ const BMVert *eve,
+ const int lvert_index,
+ void *_data)
+{
+ EditLoopData *vbo_data = *(EditLoopData **)_data;
+ const int offset = mr->loop_len + (mr->edge_loose_len * 2);
+ EditLoopData *data = vbo_data + offset + lvert_index;
+ memset(data, 0x0, sizeof(*data));
+ mesh_render_data_vert_flag(mr, eve, data);
+}
+
+static void extract_edit_data_iter_lvert_mesh(const MeshRenderData *mr,
+ const MVert *UNUSED(mv),
+ const int lvert_index,
+ void *_data)
+{
+ EditLoopData *vbo_data = *(EditLoopData **)_data;
+ const int offset = mr->loop_len + (mr->edge_loose_len * 2);
+
+ EditLoopData *data = vbo_data + offset + lvert_index;
+ memset(data, 0x0, sizeof(*data));
+ const int v_index = mr->lverts[lvert_index];
+ BMVert *eve = bm_original_vert_get(mr, v_index);
+ if (eve) {
+ mesh_render_data_vert_flag(mr, eve, data);
+ }
+}
+
+const MeshExtract extract_edit_data = {
+ .init = extract_edit_data_init,
+ .iter_poly_bm = extract_edit_data_iter_poly_bm,
+ .iter_poly_mesh = extract_edit_data_iter_poly_mesh,
+ .iter_ledge_bm = extract_edit_data_iter_ledge_bm,
+ .iter_ledge_mesh = extract_edit_data_iter_ledge_mesh,
+ .iter_lvert_bm = extract_edit_data_iter_lvert_bm,
+ .iter_lvert_mesh = extract_edit_data_iter_lvert_mesh,
+ .data_type = 0,
+ .data_size = sizeof(EditLoopData *),
+ .use_threading = true,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edit_data)};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Edit UV Data / Flags
+ * \{ */
+
+typedef struct MeshExtract_EditUVData_Data {
+ EditLoopData *vbo_data;
+ int cd_ofs;
+} MeshExtract_EditUVData_Data;
+
+static void extract_edituv_data_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *tls_data)
+{
+ GPUVertBuf *vbo = buf;
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ /* WARNING: Adjust #EditLoopData struct accordingly. */
+ GPU_vertformat_attr_add(&format, "data", GPU_COMP_U8, 4, GPU_FETCH_INT);
+ GPU_vertformat_alias_add(&format, "flag");
+ }
+
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, mr->loop_len);
+
+ CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata;
+
+ MeshExtract_EditUVData_Data *data = tls_data;
+ data->vbo_data = (EditLoopData *)GPU_vertbuf_get_data(vbo);
+ data->cd_ofs = CustomData_get_offset(cd_ldata, CD_MLOOPUV);
+}
+
+static void extract_edituv_data_iter_poly_bm(const MeshRenderData *mr,
+ const BMFace *f,
+ const int UNUSED(f_index),
+ void *_data)
+{
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ const int l_index = BM_elem_index_get(l_iter);
+ MeshExtract_EditUVData_Data *data = _data;
+ EditLoopData *eldata = &data->vbo_data[l_index];
+ memset(eldata, 0x0, sizeof(*eldata));
+ mesh_render_data_loop_flag(mr, l_iter, data->cd_ofs, eldata);
+ mesh_render_data_face_flag(mr, f, data->cd_ofs, eldata);
+ mesh_render_data_loop_edge_flag(mr, l_iter, data->cd_ofs, eldata);
+ } while ((l_iter = l_iter->next) != l_first);
+}
+
+static void extract_edituv_data_iter_poly_mesh(const MeshRenderData *mr,
+ const MPoly *mp,
+ const int mp_index,
+ void *_data)
+{
+ MeshExtract_EditUVData_Data *data = _data;
+ const MLoop *mloop = mr->mloop;
+ const int ml_index_end = mp->loopstart + mp->totloop;
+ for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
+ const MLoop *ml = &mloop[ml_index];
+
+ EditLoopData *eldata = &data->vbo_data[ml_index];
+ memset(eldata, 0x0, sizeof(*eldata));
+ BMFace *efa = bm_original_face_get(mr, mp_index);
+ if (efa) {
+ BMEdge *eed = bm_original_edge_get(mr, ml->e);
+ BMVert *eve = bm_original_vert_get(mr, ml->v);
+ if (eed && eve) {
+ /* Loop on an edge endpoint. */
+ BMLoop *l = BM_face_edge_share_loop(efa, eed);
+ mesh_render_data_loop_flag(mr, l, data->cd_ofs, eldata);
+ mesh_render_data_loop_edge_flag(mr, l, data->cd_ofs, eldata);
+ }
+ else {
+ if (eed == NULL) {
+ /* Find if the loop's vert is not part of an edit edge.
+ * For this, we check if the previous loop was on an edge. */
+ const int ml_index_last = mp->loopstart + mp->totloop - 1;
+ const int l_prev = (ml_index == mp->loopstart) ? ml_index_last : (ml_index - 1);
+ const MLoop *ml_prev = &mr->mloop[l_prev];
+ eed = bm_original_edge_get(mr, ml_prev->e);
+ }
+ if (eed) {
+ /* Mapped points on an edge between two edit verts. */
+ BMLoop *l = BM_face_edge_share_loop(efa, eed);
+ mesh_render_data_loop_edge_flag(mr, l, data->cd_ofs, eldata);
+ }
+ }
+ }
+ }
+}
+
+const MeshExtract extract_edituv_data = {
+ .init = extract_edituv_data_init,
+ .iter_poly_bm = extract_edituv_data_iter_poly_bm,
+ .iter_poly_mesh = extract_edituv_data_iter_poly_mesh,
+ .data_type = 0,
+ .data_size = sizeof(MeshExtract_EditUVData_Data),
+ .use_threading = true,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edituv_data)};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Edit UV area stretch
+ * \{ */
+
+static void extract_edituv_stretch_area_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *UNUSED(tls_data))
+{
+ GPUVertBuf *vbo = buf;
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ GPU_vertformat_attr_add(&format, "ratio", GPU_COMP_I16, 1, GPU_FETCH_INT_TO_FLOAT_UNIT);
+ }
+
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, mr->loop_len);
+}
+
+BLI_INLINE float area_ratio_get(float area, float uvarea)
+{
+ if (area >= FLT_EPSILON && uvarea >= FLT_EPSILON) {
+ /* Tag inversion by using the sign. */
+ return (area > uvarea) ? (uvarea / area) : -(area / uvarea);
+ }
+ return 0.0f;
+}
+
+BLI_INLINE float area_ratio_to_stretch(float ratio, float tot_ratio, float inv_tot_ratio)
+{
+ ratio *= (ratio > 0.0f) ? tot_ratio : -inv_tot_ratio;
+ return (ratio > 1.0f) ? (1.0f / ratio) : ratio;
+}
+
+static void extract_edituv_stretch_area_finish(const MeshRenderData *mr,
+ struct MeshBatchCache *cache,
+ void *buf,
+ void *UNUSED(data))
+{
+ GPUVertBuf *vbo = buf;
+ float tot_area = 0.0f, tot_uv_area = 0.0f;
+ float *area_ratio = MEM_mallocN(sizeof(float) * mr->poly_len, __func__);
+
+ if (mr->extract_type == MR_EXTRACT_BMESH) {
+ CustomData *cd_ldata = &mr->bm->ldata;
+ int uv_ofs = CustomData_get_offset(cd_ldata, CD_MLOOPUV);
+
+ BMFace *efa;
+ BMIter f_iter;
+ int f;
+ BM_ITER_MESH_INDEX (efa, &f_iter, mr->bm, BM_FACES_OF_MESH, f) {
+ float area = BM_face_calc_area(efa);
+ float uvarea = BM_face_calc_area_uv(efa, uv_ofs);
+ tot_area += area;
+ tot_uv_area += uvarea;
+ area_ratio[f] = area_ratio_get(area, uvarea);
+ }
+ }
+ else {
+ BLI_assert(ELEM(mr->extract_type, MR_EXTRACT_MAPPED, MR_EXTRACT_MESH));
+ const MLoopUV *uv_data = CustomData_get_layer(&mr->me->ldata, CD_MLOOPUV);
+ const MPoly *mp = mr->mpoly;
+ for (int mp_index = 0; mp_index < mr->poly_len; mp_index++, mp++) {
+ float area = BKE_mesh_calc_poly_area(mp, &mr->mloop[mp->loopstart], mr->mvert);
+ float uvarea = BKE_mesh_calc_poly_uv_area(mp, uv_data);
+ tot_area += area;
+ tot_uv_area += uvarea;
+ area_ratio[mp_index] = area_ratio_get(area, uvarea);
+ }
+ }
+
+ cache->tot_area = tot_area;
+ cache->tot_uv_area = tot_uv_area;
+
+ /* Convert in place to avoid an extra allocation */
+ uint16_t *poly_stretch = (uint16_t *)area_ratio;
+ for (int mp_index = 0; mp_index < mr->poly_len; mp_index++) {
+ poly_stretch[mp_index] = area_ratio[mp_index] * SHRT_MAX;
+ }
+
+ /* Copy face data for each loop. */
+ uint16_t *loop_stretch = (uint16_t *)GPU_vertbuf_get_data(vbo);
+
+ if (mr->extract_type == MR_EXTRACT_BMESH) {
+ BMFace *efa;
+ BMIter f_iter;
+ int f, l_index = 0;
+ BM_ITER_MESH_INDEX (efa, &f_iter, mr->bm, BM_FACES_OF_MESH, f) {
+ for (int i = 0; i < efa->len; i++, l_index++) {
+ loop_stretch[l_index] = poly_stretch[f];
+ }
+ }
+ }
+ else {
+ BLI_assert(ELEM(mr->extract_type, MR_EXTRACT_MAPPED, MR_EXTRACT_MESH));
+ const MPoly *mp = mr->mpoly;
+ for (int mp_index = 0, l_index = 0; mp_index < mr->poly_len; mp_index++, mp++) {
+ for (int i = 0; i < mp->totloop; i++, l_index++) {
+ loop_stretch[l_index] = poly_stretch[mp_index];
+ }
+ }
+ }
+
+ MEM_freeN(area_ratio);
+}
+
+const MeshExtract extract_edituv_stretch_area = {
+ .init = extract_edituv_stretch_area_init,
+ .finish = extract_edituv_stretch_area_finish,
+ .data_type = 0,
+ .data_size = 0,
+ .use_threading = false,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edituv_stretch_area)};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Edit UV angle stretch
+ * \{ */
+
+typedef struct UVStretchAngle {
+ int16_t angle;
+ int16_t uv_angles[2];
+} UVStretchAngle;
+
+typedef struct MeshExtract_StretchAngle_Data {
+ UVStretchAngle *vbo_data;
+ MLoopUV *luv;
+ float auv[2][2], last_auv[2];
+ float av[2][3], last_av[3];
+ int cd_ofs;
+} MeshExtract_StretchAngle_Data;
+
+static void compute_normalize_edge_vectors(float auv[2][2],
+ float av[2][3],
+ const float uv[2],
+ const float uv_prev[2],
+ const float co[3],
+ const float co_prev[3])
+{
+ /* Move previous edge. */
+ copy_v2_v2(auv[0], auv[1]);
+ copy_v3_v3(av[0], av[1]);
+ /* 2d edge */
+ sub_v2_v2v2(auv[1], uv_prev, uv);
+ normalize_v2(auv[1]);
+ /* 3d edge */
+ sub_v3_v3v3(av[1], co_prev, co);
+ normalize_v3(av[1]);
+}
+
+static short v2_to_short_angle(const float v[2])
+{
+ return atan2f(v[1], v[0]) * (float)M_1_PI * SHRT_MAX;
+}
+
+static void edituv_get_edituv_stretch_angle(float auv[2][2],
+ const float av[2][3],
+ UVStretchAngle *r_stretch)
+{
+ /* Send UV's to the shader and let it compute the aspect corrected angle. */
+ r_stretch->uv_angles[0] = v2_to_short_angle(auv[0]);
+ r_stretch->uv_angles[1] = v2_to_short_angle(auv[1]);
+ /* Compute 3D angle here. */
+ r_stretch->angle = angle_normalized_v3v3(av[0], av[1]) * (float)M_1_PI * SHRT_MAX;
+
+#if 0 /* here for reference, this is done in shader now. */
+ float uvang = angle_normalized_v2v2(auv0, auv1);
+ float ang = angle_normalized_v3v3(av0, av1);
+ float stretch = fabsf(uvang - ang) / (float)M_PI;
+ return 1.0f - pow2f(1.0f - stretch);
+#endif
+}
+
+static void extract_edituv_stretch_angle_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *tls_data)
+{
+ GPUVertBuf *vbo = buf;
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ /* Waning: adjust #UVStretchAngle struct accordingly. */
+ GPU_vertformat_attr_add(&format, "angle", GPU_COMP_I16, 1, GPU_FETCH_INT_TO_FLOAT_UNIT);
+ GPU_vertformat_attr_add(&format, "uv_angles", GPU_COMP_I16, 2, GPU_FETCH_INT_TO_FLOAT_UNIT);
+ }
+
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, mr->loop_len);
+
+ MeshExtract_StretchAngle_Data *data = tls_data;
+ data->vbo_data = (UVStretchAngle *)GPU_vertbuf_get_data(vbo);
+
+ /* Special iterator needed to save about half of the computing cost. */
+ if (mr->extract_type == MR_EXTRACT_BMESH) {
+ data->cd_ofs = CustomData_get_offset(&mr->bm->ldata, CD_MLOOPUV);
+ }
+ else {
+ BLI_assert(ELEM(mr->extract_type, MR_EXTRACT_MAPPED, MR_EXTRACT_MESH));
+ data->luv = CustomData_get_layer(&mr->me->ldata, CD_MLOOPUV);
+ }
+}
+
+static void extract_edituv_stretch_angle_iter_poly_bm(const MeshRenderData *mr,
+ const BMFace *f,
+ const int UNUSED(f_index),
+ void *_data)
+{
+ MeshExtract_StretchAngle_Data *data = _data;
+ float(*auv)[2] = data->auv, *last_auv = data->last_auv;
+ float(*av)[3] = data->av, *last_av = data->last_av;
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ const int l_index = BM_elem_index_get(l_iter);
+
+ const MLoopUV *luv, *luv_next;
+ BMLoop *l_next = l_iter->next;
+ if (l_iter == BM_FACE_FIRST_LOOP(f)) {
+ /* First loop in face. */
+ BMLoop *l_tmp = l_iter->prev;
+ BMLoop *l_next_tmp = l_iter;
+ luv = BM_ELEM_CD_GET_VOID_P(l_tmp, data->cd_ofs);
+ luv_next = BM_ELEM_CD_GET_VOID_P(l_next_tmp, data->cd_ofs);
+ compute_normalize_edge_vectors(auv,
+ av,
+ luv->uv,
+ luv_next->uv,
+ bm_vert_co_get(mr, l_tmp->v),
+ bm_vert_co_get(mr, l_next_tmp->v));
+ /* Save last edge. */
+ copy_v2_v2(last_auv, auv[1]);
+ copy_v3_v3(last_av, av[1]);
+ }
+ if (l_next == BM_FACE_FIRST_LOOP(f)) {
+ /* Move previous edge. */
+ copy_v2_v2(auv[0], auv[1]);
+ copy_v3_v3(av[0], av[1]);
+ /* Copy already calculated last edge. */
+ copy_v2_v2(auv[1], last_auv);
+ copy_v3_v3(av[1], last_av);
+ }
+ else {
+ luv = BM_ELEM_CD_GET_VOID_P(l_iter, data->cd_ofs);
+ luv_next = BM_ELEM_CD_GET_VOID_P(l_next, data->cd_ofs);
+ compute_normalize_edge_vectors(auv,
+ av,
+ luv->uv,
+ luv_next->uv,
+ bm_vert_co_get(mr, l_iter->v),
+ bm_vert_co_get(mr, l_next->v));
+ }
+ edituv_get_edituv_stretch_angle(auv, av, &data->vbo_data[l_index]);
+ } while ((l_iter = l_iter->next) != l_first);
+}
+
+static void extract_edituv_stretch_angle_iter_poly_mesh(const MeshRenderData *mr,
+ const MPoly *mp,
+ const int UNUSED(mp_index),
+ void *_data)
+{
+ MeshExtract_StretchAngle_Data *data = _data;
+
+ const int ml_index_end = mp->loopstart + mp->totloop;
+ for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
+ float(*auv)[2] = data->auv, *last_auv = data->last_auv;
+ float(*av)[3] = data->av, *last_av = data->last_av;
+ int l_next = ml_index + 1;
+ const MVert *v, *v_next;
+ if (ml_index == mp->loopstart) {
+ /* First loop in face. */
+ const int ml_index_last = ml_index_end - 1;
+ const int l_next_tmp = mp->loopstart;
+ v = &mr->mvert[mr->mloop[ml_index_last].v];
+ v_next = &mr->mvert[mr->mloop[l_next_tmp].v];
+ compute_normalize_edge_vectors(
+ auv, av, data->luv[ml_index_last].uv, data->luv[l_next_tmp].uv, v->co, v_next->co);
+ /* Save last edge. */
+ copy_v2_v2(last_auv, auv[1]);
+ copy_v3_v3(last_av, av[1]);
+ }
+ if (l_next == ml_index_end) {
+ l_next = mp->loopstart;
+ /* Move previous edge. */
+ copy_v2_v2(auv[0], auv[1]);
+ copy_v3_v3(av[0], av[1]);
+ /* Copy already calculated last edge. */
+ copy_v2_v2(auv[1], last_auv);
+ copy_v3_v3(av[1], last_av);
+ }
+ else {
+ v = &mr->mvert[mr->mloop[ml_index].v];
+ v_next = &mr->mvert[mr->mloop[l_next].v];
+ compute_normalize_edge_vectors(
+ auv, av, data->luv[ml_index].uv, data->luv[l_next].uv, v->co, v_next->co);
+ }
+ edituv_get_edituv_stretch_angle(auv, av, &data->vbo_data[ml_index]);
+ }
+}
+
+const MeshExtract extract_edituv_stretch_angle = {
+ .init = extract_edituv_stretch_angle_init,
+ .iter_poly_bm = extract_edituv_stretch_angle_iter_poly_bm,
+ .iter_poly_mesh = extract_edituv_stretch_angle_iter_poly_mesh,
+ .data_type = 0,
+ .data_size = sizeof(MeshExtract_StretchAngle_Data),
+ .use_threading = false,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edituv_stretch_angle)};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Edit Mesh Analysis Colors
+ * \{ */
+
+static void extract_mesh_analysis_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *UNUSED(tls_data))
+{
+ GPUVertBuf *vbo = buf;
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ GPU_vertformat_attr_add(&format, "weight", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
+ }
+
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, mr->loop_len);
+}
+
+static void axis_from_enum_v3(float v[3], const char axis)
+{
+ zero_v3(v);
+ if (axis < 3) {
+ v[axis] = 1.0f;
+ }
+ else {
+ v[axis - 3] = -1.0f;
+ }
+}
+
+BLI_INLINE float overhang_remap(float fac, float min, float max, float minmax_irange)
+{
+ if (fac < min) {
+ fac = 1.0f;
+ }
+ else if (fac > max) {
+ fac = -1.0f;
+ }
+ else {
+ fac = (fac - min) * minmax_irange;
+ fac = 1.0f - fac;
+ CLAMP(fac, 0.0f, 1.0f);
+ }
+ return fac;
+}
+
+static void statvis_calc_overhang(const MeshRenderData *mr, float *r_overhang)
+{
+ const MeshStatVis *statvis = &mr->toolsettings->statvis;
+ const float min = statvis->overhang_min / (float)M_PI;
+ const float max = statvis->overhang_max / (float)M_PI;
+ const char axis = statvis->overhang_axis;
+ BMEditMesh *em = mr->edit_bmesh;
+ BMIter iter;
+ BMesh *bm = em->bm;
+ BMFace *f;
+ float dir[3];
+ const float minmax_irange = 1.0f / (max - min);
+
+ BLI_assert(min <= max);
+
+ axis_from_enum_v3(dir, axis);
+
+ /* now convert into global space */
+ mul_transposed_mat3_m4_v3(mr->obmat, dir);
+ normalize_v3(dir);
+
+ if (mr->extract_type == MR_EXTRACT_BMESH) {
+ int l_index = 0;
+ BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
+ float fac = angle_normalized_v3v3(bm_face_no_get(mr, f), dir) / (float)M_PI;
+ fac = overhang_remap(fac, min, max, minmax_irange);
+ for (int i = 0; i < f->len; i++, l_index++) {
+ r_overhang[l_index] = fac;
+ }
+ }
+ }
+ else {
+ const MPoly *mp = mr->mpoly;
+ for (int mp_index = 0, l_index = 0; mp_index < mr->poly_len; mp_index++, mp++) {
+ float fac = angle_normalized_v3v3(mr->poly_normals[mp_index], dir) / (float)M_PI;
+ fac = overhang_remap(fac, min, max, minmax_irange);
+ for (int i = 0; i < mp->totloop; i++, l_index++) {
+ r_overhang[l_index] = fac;
+ }
+ }
+ }
+}
+
+/**
+ * Needed so we can use jitter values for face interpolation.
+ */
+static void uv_from_jitter_v2(float uv[2])
+{
+ uv[0] += 0.5f;
+ uv[1] += 0.5f;
+ if (uv[0] + uv[1] > 1.0f) {
+ uv[0] = 1.0f - uv[0];
+ uv[1] = 1.0f - uv[1];
+ }
+
+ clamp_v2(uv, 0.0f, 1.0f);
+}
+
+BLI_INLINE float thickness_remap(float fac, float min, float max, float minmax_irange)
+{
+ /* important not '<=' */
+ if (fac < max) {
+ fac = (fac - min) * minmax_irange;
+ fac = 1.0f - fac;
+ CLAMP(fac, 0.0f, 1.0f);
+ }
+ else {
+ fac = -1.0f;
+ }
+ return fac;
+}
+
+static void statvis_calc_thickness(const MeshRenderData *mr, float *r_thickness)
+{
+ const float eps_offset = 0.00002f; /* values <= 0.00001 give errors */
+ /* cheating to avoid another allocation */
+ float *face_dists = r_thickness + (mr->loop_len - mr->poly_len);
+ BMEditMesh *em = mr->edit_bmesh;
+ const float scale = 1.0f / mat4_to_scale(mr->obmat);
+ const MeshStatVis *statvis = &mr->toolsettings->statvis;
+ const float min = statvis->thickness_min * scale;
+ const float max = statvis->thickness_max * scale;
+ const float minmax_irange = 1.0f / (max - min);
+ const int samples = statvis->thickness_samples;
+ float jit_ofs[32][2];
+ BLI_assert(samples <= 32);
+ BLI_assert(min <= max);
+
+ copy_vn_fl(face_dists, mr->poly_len, max);
+
+ BLI_jitter_init(jit_ofs, samples);
+ for (int j = 0; j < samples; j++) {
+ uv_from_jitter_v2(jit_ofs[j]);
+ }
+
+ if (mr->extract_type == MR_EXTRACT_BMESH) {
+ BMesh *bm = em->bm;
+ BM_mesh_elem_index_ensure(bm, BM_FACE);
+
+ struct BMBVHTree *bmtree = BKE_bmbvh_new_from_editmesh(em, 0, NULL, false);
+ struct BMLoop *(*looptris)[3] = em->looptris;
+ for (int i = 0; i < mr->tri_len; i++) {
+ BMLoop **ltri = looptris[i];
+ const int index = BM_elem_index_get(ltri[0]->f);
+ const float *cos[3] = {
+ bm_vert_co_get(mr, ltri[0]->v),
+ bm_vert_co_get(mr, ltri[1]->v),
+ bm_vert_co_get(mr, ltri[2]->v),
+ };
+ float ray_co[3];
+ float ray_no[3];
+
+ normal_tri_v3(ray_no, cos[2], cos[1], cos[0]);
+
+ for (int j = 0; j < samples; j++) {
+ float dist = face_dists[index];
+ interp_v3_v3v3v3_uv(ray_co, cos[0], cos[1], cos[2], jit_ofs[j]);
+ madd_v3_v3fl(ray_co, ray_no, eps_offset);
+
+ BMFace *f_hit = BKE_bmbvh_ray_cast(bmtree, ray_co, ray_no, 0.0f, &dist, NULL, NULL);
+ if (f_hit && dist < face_dists[index]) {
+ float angle_fac = fabsf(
+ dot_v3v3(bm_face_no_get(mr, ltri[0]->f), bm_face_no_get(mr, f_hit)));
+ angle_fac = 1.0f - angle_fac;
+ angle_fac = angle_fac * angle_fac * angle_fac;
+ angle_fac = 1.0f - angle_fac;
+ dist /= angle_fac;
+ if (dist < face_dists[index]) {
+ face_dists[index] = dist;
+ }
+ }
+ }
+ }
+ BKE_bmbvh_free(bmtree);
+
+ BMIter iter;
+ BMFace *f;
+ int l_index = 0;
+ BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
+ float fac = face_dists[BM_elem_index_get(f)];
+ fac = thickness_remap(fac, min, max, minmax_irange);
+ for (int i = 0; i < f->len; i++, l_index++) {
+ r_thickness[l_index] = fac;
+ }
+ }
+ }
+ else {
+ BVHTreeFromMesh treeData = {NULL};
+
+ BVHTree *tree = BKE_bvhtree_from_mesh_get(&treeData, mr->me, BVHTREE_FROM_LOOPTRI, 4);
+ const MLoopTri *mlooptri = mr->mlooptri;
+ for (int i = 0; i < mr->tri_len; i++, mlooptri++) {
+ const int index = mlooptri->poly;
+ const float *cos[3] = {mr->mvert[mr->mloop[mlooptri->tri[0]].v].co,
+ mr->mvert[mr->mloop[mlooptri->tri[1]].v].co,
+ mr->mvert[mr->mloop[mlooptri->tri[2]].v].co};
+ float ray_co[3];
+ float ray_no[3];
+
+ normal_tri_v3(ray_no, cos[2], cos[1], cos[0]);
+
+ for (int j = 0; j < samples; j++) {
+ interp_v3_v3v3v3_uv(ray_co, cos[0], cos[1], cos[2], jit_ofs[j]);
+ madd_v3_v3fl(ray_co, ray_no, eps_offset);
+
+ BVHTreeRayHit hit;
+ hit.index = -1;
+ hit.dist = face_dists[index];
+ if ((BLI_bvhtree_ray_cast(
+ tree, ray_co, ray_no, 0.0f, &hit, treeData.raycast_callback, &treeData) != -1) &&
+ hit.dist < face_dists[index]) {
+ float angle_fac = fabsf(dot_v3v3(mr->poly_normals[index], hit.no));
+ angle_fac = 1.0f - angle_fac;
+ angle_fac = angle_fac * angle_fac * angle_fac;
+ angle_fac = 1.0f - angle_fac;
+ hit.dist /= angle_fac;
+ if (hit.dist < face_dists[index]) {
+ face_dists[index] = hit.dist;
+ }
+ }
+ }
+ }
+
+ const MPoly *mp = mr->mpoly;
+ for (int mp_index = 0, l_index = 0; mp_index < mr->poly_len; mp_index++, mp++) {
+ float fac = face_dists[mp_index];
+ fac = thickness_remap(fac, min, max, minmax_irange);
+ for (int i = 0; i < mp->totloop; i++, l_index++) {
+ r_thickness[l_index] = fac;
+ }
+ }
+ }
+}
+
+struct BVHTree_OverlapData {
+ const Mesh *me;
+ const MLoopTri *mlooptri;
+ float epsilon;
+};
+
+static bool bvh_overlap_cb(void *userdata, int index_a, int index_b, int UNUSED(thread))
+{
+ struct BVHTree_OverlapData *data = userdata;
+ const Mesh *me = data->me;
+
+ const MLoopTri *tri_a = &data->mlooptri[index_a];
+ const MLoopTri *tri_b = &data->mlooptri[index_b];
+
+ if (UNLIKELY(tri_a->poly == tri_b->poly)) {
+ return false;
+ }
+
+ const float *tri_a_co[3] = {me->mvert[me->mloop[tri_a->tri[0]].v].co,
+ me->mvert[me->mloop[tri_a->tri[1]].v].co,
+ me->mvert[me->mloop[tri_a->tri[2]].v].co};
+ const float *tri_b_co[3] = {me->mvert[me->mloop[tri_b->tri[0]].v].co,
+ me->mvert[me->mloop[tri_b->tri[1]].v].co,
+ me->mvert[me->mloop[tri_b->tri[2]].v].co};
+ float ix_pair[2][3];
+ int verts_shared = 0;
+
+ verts_shared = (ELEM(tri_a_co[0], UNPACK3(tri_b_co)) + ELEM(tri_a_co[1], UNPACK3(tri_b_co)) +
+ ELEM(tri_a_co[2], UNPACK3(tri_b_co)));
+
+ /* if 2 points are shared, bail out */
+ if (verts_shared >= 2) {
+ return false;
+ }
+
+ return (isect_tri_tri_v3(UNPACK3(tri_a_co), UNPACK3(tri_b_co), ix_pair[0], ix_pair[1]) &&
+ /* if we share a vertex, check the intersection isn't a 'point' */
+ ((verts_shared == 0) || (len_squared_v3v3(ix_pair[0], ix_pair[1]) > data->epsilon)));
+}
+
+static void statvis_calc_intersect(const MeshRenderData *mr, float *r_intersect)
+{
+ BMEditMesh *em = mr->edit_bmesh;
+
+ for (int l_index = 0; l_index < mr->loop_len; l_index++) {
+ r_intersect[l_index] = -1.0f;
+ }
+
+ if (mr->extract_type == MR_EXTRACT_BMESH) {
+ uint overlap_len;
+ BMesh *bm = em->bm;
+
+ BM_mesh_elem_index_ensure(bm, BM_FACE);
+
+ struct BMBVHTree *bmtree = BKE_bmbvh_new_from_editmesh(em, 0, NULL, false);
+ BVHTreeOverlap *overlap = BKE_bmbvh_overlap_self(bmtree, &overlap_len);
+
+ if (overlap) {
+ for (int i = 0; i < overlap_len; i++) {
+ BMFace *f_hit_pair[2] = {
+ em->looptris[overlap[i].indexA][0]->f,
+ em->looptris[overlap[i].indexB][0]->f,
+ };
+ for (int j = 0; j < 2; j++) {
+ BMFace *f_hit = f_hit_pair[j];
+ BMLoop *l_first = BM_FACE_FIRST_LOOP(f_hit);
+ int l_index = BM_elem_index_get(l_first);
+ for (int k = 0; k < f_hit->len; k++, l_index++) {
+ r_intersect[l_index] = 1.0f;
+ }
+ }
+ }
+ MEM_freeN(overlap);
+ }
+
+ BKE_bmbvh_free(bmtree);
+ }
+ else {
+ uint overlap_len;
+ BVHTreeFromMesh treeData = {NULL};
+
+ BVHTree *tree = BKE_bvhtree_from_mesh_get(&treeData, mr->me, BVHTREE_FROM_LOOPTRI, 4);
+
+ struct BVHTree_OverlapData data = {
+ .me = mr->me, .mlooptri = mr->mlooptri, .epsilon = BLI_bvhtree_get_epsilon(tree)};
+
+ BVHTreeOverlap *overlap = BLI_bvhtree_overlap(tree, tree, &overlap_len, bvh_overlap_cb, &data);
+ if (overlap) {
+ for (int i = 0; i < overlap_len; i++) {
+ const MPoly *f_hit_pair[2] = {
+ &mr->mpoly[mr->mlooptri[overlap[i].indexA].poly],
+ &mr->mpoly[mr->mlooptri[overlap[i].indexB].poly],
+ };
+ for (int j = 0; j < 2; j++) {
+ const MPoly *f_hit = f_hit_pair[j];
+ int l_index = f_hit->loopstart;
+ for (int k = 0; k < f_hit->totloop; k++, l_index++) {
+ r_intersect[l_index] = 1.0f;
+ }
+ }
+ }
+ MEM_freeN(overlap);
+ }
+ }
+}
+
+BLI_INLINE float distort_remap(float fac, float min, float UNUSED(max), float minmax_irange)
+{
+ if (fac >= min) {
+ fac = (fac - min) * minmax_irange;
+ CLAMP(fac, 0.0f, 1.0f);
+ }
+ else {
+ /* fallback */
+ fac = -1.0f;
+ }
+ return fac;
+}
+
+static void statvis_calc_distort(const MeshRenderData *mr, float *r_distort)
+{
+ BMEditMesh *em = mr->edit_bmesh;
+ const MeshStatVis *statvis = &mr->toolsettings->statvis;
+ const float min = statvis->distort_min;
+ const float max = statvis->distort_max;
+ const float minmax_irange = 1.0f / (max - min);
+
+ if (mr->extract_type == MR_EXTRACT_BMESH) {
+ BMIter iter;
+ BMesh *bm = em->bm;
+ BMFace *f;
+
+ if (mr->bm_vert_coords != NULL) {
+ BKE_editmesh_cache_ensure_poly_normals(em, mr->edit_data);
+
+ /* Most likely this is already valid, ensure just in case.
+ * Needed for #BM_loop_calc_face_normal_safe_vcos. */
+ BM_mesh_elem_index_ensure(em->bm, BM_VERT);
+ }
+
+ int l_index = 0;
+ int f_index = 0;
+ BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, f_index) {
+ float fac = -1.0f;
+
+ if (f->len > 3) {
+ BMLoop *l_iter, *l_first;
+
+ fac = 0.0f;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ const float *no_face;
+ float no_corner[3];
+ if (mr->bm_vert_coords != NULL) {
+ no_face = mr->bm_poly_normals[f_index];
+ BM_loop_calc_face_normal_safe_vcos(l_iter, no_face, mr->bm_vert_coords, no_corner);
+ }
+ else {
+ no_face = f->no;
+ BM_loop_calc_face_normal_safe(l_iter, no_corner);
+ }
+
+ /* simple way to detect (what is most likely) concave */
+ if (dot_v3v3(no_face, no_corner) < 0.0f) {
+ negate_v3(no_corner);
+ }
+ fac = max_ff(fac, angle_normalized_v3v3(no_face, no_corner));
+
+ } while ((l_iter = l_iter->next) != l_first);
+ fac *= 2.0f;
+ }
+
+ fac = distort_remap(fac, min, max, minmax_irange);
+ for (int i = 0; i < f->len; i++, l_index++) {
+ r_distort[l_index] = fac;
+ }
+ }
+ }
+ else {
+ const MPoly *mp = mr->mpoly;
+ for (int mp_index = 0, l_index = 0; mp_index < mr->poly_len; mp_index++, mp++) {
+ float fac = -1.0f;
+
+ if (mp->totloop > 3) {
+ float *f_no = mr->poly_normals[mp_index];
+ fac = 0.0f;
+
+ for (int i = 1; i <= mp->totloop; i++) {
+ const MLoop *l_prev = &mr->mloop[mp->loopstart + (i - 1) % mp->totloop];
+ const MLoop *l_curr = &mr->mloop[mp->loopstart + (i + 0) % mp->totloop];
+ const MLoop *l_next = &mr->mloop[mp->loopstart + (i + 1) % mp->totloop];
+ float no_corner[3];
+ normal_tri_v3(no_corner,
+ mr->mvert[l_prev->v].co,
+ mr->mvert[l_curr->v].co,
+ mr->mvert[l_next->v].co);
+ /* simple way to detect (what is most likely) concave */
+ if (dot_v3v3(f_no, no_corner) < 0.0f) {
+ negate_v3(no_corner);
+ }
+ fac = max_ff(fac, angle_normalized_v3v3(f_no, no_corner));
+ }
+ fac *= 2.0f;
+ }
+
+ fac = distort_remap(fac, min, max, minmax_irange);
+ for (int i = 0; i < mp->totloop; i++, l_index++) {
+ r_distort[l_index] = fac;
+ }
+ }
+ }
+}
+
+BLI_INLINE float sharp_remap(float fac, float min, float UNUSED(max), float minmax_irange)
+{
+ /* important not '>=' */
+ if (fac > min) {
+ fac = (fac - min) * minmax_irange;
+ CLAMP(fac, 0.0f, 1.0f);
+ }
+ else {
+ /* fallback */
+ fac = -1.0f;
+ }
+ return fac;
+}
+
+static void statvis_calc_sharp(const MeshRenderData *mr, float *r_sharp)
+{
+ BMEditMesh *em = mr->edit_bmesh;
+ const MeshStatVis *statvis = &mr->toolsettings->statvis;
+ const float min = statvis->sharp_min;
+ const float max = statvis->sharp_max;
+ const float minmax_irange = 1.0f / (max - min);
+
+ /* Can we avoid this extra allocation? */
+ float *vert_angles = MEM_mallocN(sizeof(float) * mr->vert_len, __func__);
+ copy_vn_fl(vert_angles, mr->vert_len, -M_PI);
+
+ if (mr->extract_type == MR_EXTRACT_BMESH) {
+ BMIter iter;
+ BMesh *bm = em->bm;
+ BMFace *efa;
+ BMEdge *e;
+ /* first assign float values to verts */
+ BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
+ float angle = BM_edge_calc_face_angle_signed(e);
+ float *col1 = &vert_angles[BM_elem_index_get(e->v1)];
+ float *col2 = &vert_angles[BM_elem_index_get(e->v2)];
+ *col1 = max_ff(*col1, angle);
+ *col2 = max_ff(*col2, angle);
+ }
+ /* Copy vert value to loops. */
+ BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
+ do {
+ int l_index = BM_elem_index_get(l_iter);
+ int v_index = BM_elem_index_get(l_iter->v);
+ r_sharp[l_index] = sharp_remap(vert_angles[v_index], min, max, minmax_irange);
+ } while ((l_iter = l_iter->next) != l_first);
+ }
+ }
+ else {
+ /* first assign float values to verts */
+ const MPoly *mp = mr->mpoly;
+
+ EdgeHash *eh = BLI_edgehash_new_ex(__func__, mr->edge_len);
+
+ for (int mp_index = 0; mp_index < mr->poly_len; mp_index++, mp++) {
+ for (int i = 0; i < mp->totloop; i++) {
+ const MLoop *l_curr = &mr->mloop[mp->loopstart + (i + 0) % mp->totloop];
+ const MLoop *l_next = &mr->mloop[mp->loopstart + (i + 1) % mp->totloop];
+ const MVert *v_curr = &mr->mvert[l_curr->v];
+ const MVert *v_next = &mr->mvert[l_next->v];
+ float angle;
+ void **pval;
+ bool value_is_init = BLI_edgehash_ensure_p(eh, l_curr->v, l_next->v, &pval);
+ if (!value_is_init) {
+ *pval = mr->poly_normals[mp_index];
+ /* non-manifold edge, yet... */
+ continue;
+ }
+ if (*pval != NULL) {
+ const float *f1_no = mr->poly_normals[mp_index];
+ const float *f2_no = *pval;
+ angle = angle_normalized_v3v3(f1_no, f2_no);
+ angle = is_edge_convex_v3(v_curr->co, v_next->co, f1_no, f2_no) ? angle : -angle;
+ /* Tag as manifold. */
+ *pval = NULL;
+ }
+ else {
+ /* non-manifold edge */
+ angle = DEG2RADF(90.0f);
+ }
+ float *col1 = &vert_angles[l_curr->v];
+ float *col2 = &vert_angles[l_next->v];
+ *col1 = max_ff(*col1, angle);
+ *col2 = max_ff(*col2, angle);
+ }
+ }
+ /* Remaining non manifold edges. */
+ EdgeHashIterator *ehi = BLI_edgehashIterator_new(eh);
+ for (; !BLI_edgehashIterator_isDone(ehi); BLI_edgehashIterator_step(ehi)) {
+ if (BLI_edgehashIterator_getValue(ehi) != NULL) {
+ uint v1, v2;
+ const float angle = DEG2RADF(90.0f);
+ BLI_edgehashIterator_getKey(ehi, &v1, &v2);
+ float *col1 = &vert_angles[v1];
+ float *col2 = &vert_angles[v2];
+ *col1 = max_ff(*col1, angle);
+ *col2 = max_ff(*col2, angle);
+ }
+ }
+ BLI_edgehashIterator_free(ehi);
+ BLI_edgehash_free(eh, NULL);
+
+ const MLoop *ml = mr->mloop;
+ for (int l_index = 0; l_index < mr->loop_len; l_index++, ml++) {
+ r_sharp[l_index] = sharp_remap(vert_angles[ml->v], min, max, minmax_irange);
+ }
+ }
+
+ MEM_freeN(vert_angles);
+}
+
+static void extract_analysis_iter_finish_mesh(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *UNUSED(data))
+{
+ GPUVertBuf *vbo = buf;
+ BLI_assert(mr->edit_bmesh);
+
+ float *l_weight = (float *)GPU_vertbuf_get_data(vbo);
+
+ switch (mr->toolsettings->statvis.type) {
+ case SCE_STATVIS_OVERHANG:
+ statvis_calc_overhang(mr, l_weight);
+ break;
+ case SCE_STATVIS_THICKNESS:
+ statvis_calc_thickness(mr, l_weight);
+ break;
+ case SCE_STATVIS_INTERSECT:
+ statvis_calc_intersect(mr, l_weight);
+ break;
+ case SCE_STATVIS_DISTORT:
+ statvis_calc_distort(mr, l_weight);
+ break;
+ case SCE_STATVIS_SHARP:
+ statvis_calc_sharp(mr, l_weight);
+ break;
+ }
+}
+
+const MeshExtract extract_mesh_analysis = {
+ .init = extract_mesh_analysis_init,
+ .finish = extract_analysis_iter_finish_mesh,
+ /* This is not needed for all visualization types.
+ * * Maybe split into different extract. */
+ .data_type = MR_DATA_POLY_NOR | MR_DATA_LOOPTRI,
+ .data_size = 0,
+ .use_threading = false,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.mesh_analysis)};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Face-dots positions
+ * \{ */
+
+static void extract_fdots_pos_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *tls_data)
+{
+ GPUVertBuf *vbo = buf;
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+ }
+
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, mr->poly_len);
+ *(float(**)[3])tls_data = GPU_vertbuf_get_data(vbo);
+}
+
+static void extract_fdots_pos_iter_poly_bm(const MeshRenderData *mr,
+ const BMFace *f,
+ const int f_index,
+ void *data)
+{
+ float(*center)[3] = *(float(**)[3])data;
+
+ float *co = center[f_index];
+ zero_v3(co);
+
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ add_v3_v3(co, bm_vert_co_get(mr, l_iter->v));
+ } while ((l_iter = l_iter->next) != l_first);
+ mul_v3_fl(co, 1.0f / (float)f->len);
+}
+
+static void extract_fdots_pos_iter_poly_mesh(const MeshRenderData *mr,
+ const MPoly *mp,
+ const int mp_index,
+ void *data)
+{
+ float(*center)[3] = *(float(**)[3])data;
+ float *co = center[mp_index];
+ zero_v3(co);
+
+ const MVert *mvert = mr->mvert;
+ const MLoop *mloop = mr->mloop;
+
+ const int ml_index_end = mp->loopstart + mp->totloop;
+ for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
+ const MLoop *ml = &mloop[ml_index];
+ if (mr->use_subsurf_fdots) {
+ const MVert *mv = &mr->mvert[ml->v];
+ if (mv->flag & ME_VERT_FACEDOT) {
+ copy_v3_v3(center[mp_index], mv->co);
+ break;
+ }
+ }
+ else {
+ const MVert *mv = &mvert[ml->v];
+ add_v3_v3(center[mp_index], mv->co);
+ }
+ }
+
+ if (!mr->use_subsurf_fdots) {
+ mul_v3_fl(co, 1.0f / (float)mp->totloop);
+ }
+}
+
+const MeshExtract extract_fdots_pos = {
+ .init = extract_fdots_pos_init,
+ .iter_poly_bm = extract_fdots_pos_iter_poly_bm,
+ .iter_poly_mesh = extract_fdots_pos_iter_poly_mesh,
+ .data_type = 0,
+ .data_size = sizeof(float (*)[3]),
+ .use_threading = true,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_pos)};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Face-dots Normal and edit flag
+ * \{ */
+#define NOR_AND_FLAG_DEFAULT 0
+#define NOR_AND_FLAG_SELECT 1
+#define NOR_AND_FLAG_ACTIVE -1
+#define NOR_AND_FLAG_HIDDEN -2
+
+static void extract_fdots_nor_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *UNUSED(tls_data))
+{
+ GPUVertBuf *vbo = buf;
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ GPU_vertformat_attr_add(&format, "norAndFlag", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
+ }
+
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, mr->poly_len);
+}
+
+static void extract_fdots_nor_finish(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *UNUSED(data))
+{
+ GPUVertBuf *vbo = buf;
+ static float invalid_normal[3] = {0.0f, 0.0f, 0.0f};
+ GPUPackedNormal *nor = (GPUPackedNormal *)GPU_vertbuf_get_data(vbo);
+ BMFace *efa;
+
+ /* Quicker than doing it for each loop. */
+ if (mr->extract_type == MR_EXTRACT_BMESH) {
+ for (int f = 0; f < mr->poly_len; f++) {
+ efa = BM_face_at_index(mr->bm, f);
+ const bool is_face_hidden = BM_elem_flag_test(efa, BM_ELEM_HIDDEN);
+ if (is_face_hidden || (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex &&
+ mr->p_origindex[f] == ORIGINDEX_NONE)) {
+ nor[f] = GPU_normal_convert_i10_v3(invalid_normal);
+ nor[f].w = NOR_AND_FLAG_HIDDEN;
+ }
+ else {
+ nor[f] = GPU_normal_convert_i10_v3(bm_face_no_get(mr, efa));
+ /* Select / Active Flag. */
+ nor[f].w = (BM_elem_flag_test(efa, BM_ELEM_SELECT) ?
+ ((efa == mr->efa_act) ? NOR_AND_FLAG_ACTIVE : NOR_AND_FLAG_SELECT) :
+ NOR_AND_FLAG_DEFAULT);
+ }
+ }
+ }
+ else {
+ for (int f = 0; f < mr->poly_len; f++) {
+ efa = bm_original_face_get(mr, f);
+ const bool is_face_hidden = efa && BM_elem_flag_test(efa, BM_ELEM_HIDDEN);
+ if (is_face_hidden || (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex &&
+ mr->p_origindex[f] == ORIGINDEX_NONE)) {
+ nor[f] = GPU_normal_convert_i10_v3(invalid_normal);
+ nor[f].w = NOR_AND_FLAG_HIDDEN;
+ }
+ else {
+ nor[f] = GPU_normal_convert_i10_v3(bm_face_no_get(mr, efa));
+ /* Select / Active Flag. */
+ nor[f].w = (BM_elem_flag_test(efa, BM_ELEM_SELECT) ?
+ ((efa == mr->efa_act) ? NOR_AND_FLAG_ACTIVE : NOR_AND_FLAG_SELECT) :
+ NOR_AND_FLAG_DEFAULT);
+ }
+ }
+ }
+}
+
+const MeshExtract extract_fdots_nor = {
+ .init = extract_fdots_nor_init,
+ .finish = extract_fdots_nor_finish,
+ .data_type = MR_DATA_POLY_NOR,
+ .data_size = 0,
+ .use_threading = false,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_nor)};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Face-dots High Quality Normal and edit flag
+ * \{ */
+static void extract_fdots_nor_hq_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *UNUSED(tls_data))
+{
+ GPUVertBuf *vbo = buf;
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ GPU_vertformat_attr_add(&format, "norAndFlag", GPU_COMP_I16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
+ }
+
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, mr->poly_len);
+}
+
+static void extract_fdots_nor_hq_finish(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *UNUSED(data))
+{
+ GPUVertBuf *vbo = buf;
+ static float invalid_normal[3] = {0.0f, 0.0f, 0.0f};
+ short *nor = (short *)GPU_vertbuf_get_data(vbo);
+ BMFace *efa;
+
+ /* Quicker than doing it for each loop. */
+ if (mr->extract_type == MR_EXTRACT_BMESH) {
+ for (int f = 0; f < mr->poly_len; f++) {
+ efa = BM_face_at_index(mr->bm, f);
+ const bool is_face_hidden = BM_elem_flag_test(efa, BM_ELEM_HIDDEN);
+ if (is_face_hidden || (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex &&
+ mr->p_origindex[f] == ORIGINDEX_NONE)) {
+ normal_float_to_short_v3(&nor[f * 4], invalid_normal);
+ nor[f * 4 + 3] = NOR_AND_FLAG_HIDDEN;
+ }
+ else {
+ normal_float_to_short_v3(&nor[f * 4], bm_face_no_get(mr, efa));
+ /* Select / Active Flag. */
+ nor[f * 4 + 3] = (BM_elem_flag_test(efa, BM_ELEM_SELECT) ?
+ ((efa == mr->efa_act) ? NOR_AND_FLAG_ACTIVE : NOR_AND_FLAG_SELECT) :
+ NOR_AND_FLAG_DEFAULT);
+ }
+ }
+ }
+ else {
+ for (int f = 0; f < mr->poly_len; f++) {
+ efa = bm_original_face_get(mr, f);
+ const bool is_face_hidden = efa && BM_elem_flag_test(efa, BM_ELEM_HIDDEN);
+ if (is_face_hidden || (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex &&
+ mr->p_origindex[f] == ORIGINDEX_NONE)) {
+ normal_float_to_short_v3(&nor[f * 4], invalid_normal);
+ nor[f * 4 + 3] = NOR_AND_FLAG_HIDDEN;
+ }
+ else {
+ normal_float_to_short_v3(&nor[f * 4], bm_face_no_get(mr, efa));
+ /* Select / Active Flag. */
+ nor[f * 4 + 3] = (BM_elem_flag_test(efa, BM_ELEM_SELECT) ?
+ ((efa == mr->efa_act) ? NOR_AND_FLAG_ACTIVE : NOR_AND_FLAG_SELECT) :
+ NOR_AND_FLAG_DEFAULT);
+ }
+ }
+ }
+}
+
+const MeshExtract extract_fdots_nor_hq = {
+ .init = extract_fdots_nor_hq_init,
+ .finish = extract_fdots_nor_hq_finish,
+ .data_type = MR_DATA_POLY_NOR,
+ .data_size = 0,
+ .use_threading = false,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_nor)};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Face-dots UV
+ * \{ */
+
+typedef struct MeshExtract_FdotUV_Data {
+ float (*vbo_data)[2];
+ MLoopUV *uv_data;
+ int cd_ofs;
+} MeshExtract_FdotUV_Data;
+
+static void extract_fdots_uv_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *tls_data)
+{
+ GPUVertBuf *vbo = buf;
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ GPU_vertformat_attr_add(&format, "u", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ GPU_vertformat_alias_add(&format, "au");
+ GPU_vertformat_alias_add(&format, "pos");
+ }
+
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, mr->poly_len);
+
+ if (!mr->use_subsurf_fdots) {
+ /* Clear so we can accumulate on it. */
+ memset(GPU_vertbuf_get_data(vbo), 0x0, mr->poly_len * GPU_vertbuf_get_format(vbo)->stride);
+ }
+
+ MeshExtract_FdotUV_Data *data = tls_data;
+ data->vbo_data = (float(*)[2])GPU_vertbuf_get_data(vbo);
+
+ if (mr->extract_type == MR_EXTRACT_BMESH) {
+ data->cd_ofs = CustomData_get_offset(&mr->bm->ldata, CD_MLOOPUV);
+ }
+ else {
+ data->uv_data = CustomData_get_layer(&mr->me->ldata, CD_MLOOPUV);
+ }
+}
+
+static void extract_fdots_uv_iter_poly_bm(const MeshRenderData *UNUSED(mr),
+ const BMFace *f,
+ const int UNUSED(f_index),
+ void *_data)
+{
+ MeshExtract_FdotUV_Data *data = _data;
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ float w = 1.0f / (float)f->len;
+ const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l_iter, data->cd_ofs);
+ madd_v2_v2fl(data->vbo_data[BM_elem_index_get(f)], luv->uv, w);
+ } while ((l_iter = l_iter->next) != l_first);
+}
+
+static void extract_fdots_uv_iter_poly_mesh(const MeshRenderData *mr,
+ const MPoly *mp,
+ const int mp_index,
+ void *_data)
+{
+ MeshExtract_FdotUV_Data *data = _data;
+ const MLoop *mloop = mr->mloop;
+ const int ml_index_end = mp->loopstart + mp->totloop;
+ for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
+ const MLoop *ml = &mloop[ml_index];
+ if (mr->use_subsurf_fdots) {
+ const MVert *mv = &mr->mvert[ml->v];
+ if (mv->flag & ME_VERT_FACEDOT) {
+ copy_v2_v2(data->vbo_data[mp_index], data->uv_data[ml_index].uv);
+ }
+ }
+ else {
+ float w = 1.0f / (float)mp->totloop;
+ madd_v2_v2fl(data->vbo_data[mp_index], data->uv_data[ml_index].uv, w);
+ }
+ }
+}
+
+const MeshExtract extract_fdots_uv = {
+ .init = extract_fdots_uv_init,
+ .iter_poly_bm = extract_fdots_uv_iter_poly_bm,
+ .iter_poly_mesh = extract_fdots_uv_iter_poly_mesh,
+ .data_type = 0,
+ .data_size = sizeof(MeshExtract_FdotUV_Data),
+ .use_threading = true,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_uv)};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Face-dots Edit UV flag
+ * \{ */
+
+typedef struct MeshExtract_EditUVFdotData_Data {
+ EditLoopData *vbo_data;
+ int cd_ofs;
+} MeshExtract_EditUVFdotData_Data;
+
+static void extract_fdots_edituv_data_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *tls_data)
+{
+ GPUVertBuf *vbo = buf;
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ GPU_vertformat_attr_add(&format, "flag", GPU_COMP_U8, 4, GPU_FETCH_INT);
+ }
+
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, mr->poly_len);
+
+ MeshExtract_EditUVFdotData_Data *data = tls_data;
+ data->vbo_data = (EditLoopData *)GPU_vertbuf_get_data(vbo);
+ data->cd_ofs = CustomData_get_offset(&mr->bm->ldata, CD_MLOOPUV);
+}
+
+static void extract_fdots_edituv_data_iter_poly_bm(const MeshRenderData *mr,
+ const BMFace *f,
+ const int UNUSED(f_index),
+ void *_data)
+{
+ MeshExtract_EditUVFdotData_Data *data = _data;
+ EditLoopData *eldata = &data->vbo_data[BM_elem_index_get(f)];
+ memset(eldata, 0x0, sizeof(*eldata));
+ mesh_render_data_face_flag(mr, f, data->cd_ofs, eldata);
+}
+
+static void extract_fdots_edituv_data_iter_poly_mesh(const MeshRenderData *mr,
+ const MPoly *UNUSED(mp),
+ const int mp_index,
+ void *_data)
+{
+ MeshExtract_EditUVFdotData_Data *data = _data;
+ EditLoopData *eldata = &data->vbo_data[mp_index];
+ memset(eldata, 0x0, sizeof(*eldata));
+ BMFace *efa = bm_original_face_get(mr, mp_index);
+ if (efa) {
+ mesh_render_data_face_flag(mr, efa, data->cd_ofs, eldata);
+ }
+}
+
+const MeshExtract extract_fdots_edituv_data = {
+ .init = extract_fdots_edituv_data_init,
+ .iter_poly_bm = extract_fdots_edituv_data_iter_poly_bm,
+ .iter_poly_mesh = extract_fdots_edituv_data_iter_poly_mesh,
+ .data_type = 0,
+ .data_size = sizeof(MeshExtract_EditUVFdotData_Data),
+ .use_threading = true,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_edituv_data)};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Skin Modifier Roots
+ * \{ */
+
+typedef struct SkinRootData {
+ float size;
+ float local_pos[3];
+} SkinRootData;
+
+static void extract_skin_roots_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *UNUSED(tls_data))
+{
+ GPUVertBuf *vbo = buf;
+ /* Exclusively for edit mode. */
+ BLI_assert(mr->bm);
+
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ GPU_vertformat_attr_add(&format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
+ GPU_vertformat_attr_add(&format, "local_pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+ }
+
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, mr->bm->totvert);
+
+ SkinRootData *vbo_data = (SkinRootData *)GPU_vertbuf_get_data(vbo);
+
+ int root_len = 0;
+ int cd_ofs = CustomData_get_offset(&mr->bm->vdata, CD_MVERT_SKIN);
+
+ BMIter iter;
+ BMVert *eve;
+ BM_ITER_MESH (eve, &iter, mr->bm, BM_VERTS_OF_MESH) {
+ const MVertSkin *vs = BM_ELEM_CD_GET_VOID_P(eve, cd_ofs);
+ if (vs->flag & MVERT_SKIN_ROOT) {
+ vbo_data->size = (vs->radius[0] + vs->radius[1]) * 0.5f;
+ copy_v3_v3(vbo_data->local_pos, bm_vert_co_get(mr, eve));
+ vbo_data++;
+ root_len++;
+ }
+ }
+
+ /* It's really unlikely that all verts will be roots. Resize to avoid losing VRAM. */
+ GPU_vertbuf_data_len_set(vbo, root_len);
+}
+
+const MeshExtract extract_skin_roots = {
+ .init = extract_skin_roots_init,
+ .data_type = 0,
+ .data_size = 0,
+ .use_threading = false,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.skin_roots)};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Selection Index
+ * \{ */
+
+static void extract_select_idx_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *tls_data)
+{
+ GPUVertBuf *vbo = buf;
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ /* TODO rename "color" to something more descriptive. */
+ GPU_vertformat_attr_add(&format, "color", GPU_COMP_U32, 1, GPU_FETCH_INT);
+ }
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len);
+ *(uint32_t **)tls_data = GPU_vertbuf_get_data(vbo);
+}
+
+/* TODO Use #glVertexID to get loop index and use the data structure on the CPU to retrieve the
+ * select element associated with this loop ID. This would remove the need for this separate
+ * index VBO's. We could upload the p/e/v_origindex as a buffer texture and sample it inside the
+ * shader to output original index. */
+
+static void extract_poly_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr),
+ const BMFace *f,
+ const int f_index,
+ void *data)
+{
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ const int l_index = BM_elem_index_get(l_iter);
+ (*(uint32_t **)data)[l_index] = f_index;
+ } while ((l_iter = l_iter->next) != l_first);
+}
+
+static void extract_edge_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr),
+ const BMFace *f,
+ const int UNUSED(f_index),
+ void *data)
+{
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ const int l_index = BM_elem_index_get(l_iter);
+ (*(uint32_t **)data)[l_index] = BM_elem_index_get(l_iter->e);
+ } while ((l_iter = l_iter->next) != l_first);
+}
+
+static void extract_vert_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr),
+ const BMFace *f,
+ const int UNUSED(f_index),
+ void *data)
+{
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ const int l_index = BM_elem_index_get(l_iter);
+ (*(uint32_t **)data)[l_index] = BM_elem_index_get(l_iter->v);
+ } while ((l_iter = l_iter->next) != l_first);
+}
+
+static void extract_edge_idx_iter_ledge_bm(const MeshRenderData *mr,
+ const BMEdge *eed,
+ const int ledge_index,
+ void *data)
+{
+ (*(uint32_t **)data)[mr->loop_len + ledge_index * 2 + 0] = BM_elem_index_get(eed);
+ (*(uint32_t **)data)[mr->loop_len + ledge_index * 2 + 1] = BM_elem_index_get(eed);
+}
+
+static void extract_vert_idx_iter_ledge_bm(const MeshRenderData *mr,
+ const BMEdge *eed,
+ const int ledge_index,
+ void *data)
+{
+ (*(uint32_t **)data)[mr->loop_len + ledge_index * 2 + 0] = BM_elem_index_get(eed->v1);
+ (*(uint32_t **)data)[mr->loop_len + ledge_index * 2 + 1] = BM_elem_index_get(eed->v2);
+}
+
+static void extract_vert_idx_iter_lvert_bm(const MeshRenderData *mr,
+ const BMVert *eve,
+ const int lvert_index,
+ void *data)
+{
+ const int offset = mr->loop_len + (mr->edge_loose_len * 2);
+
+ (*(uint32_t **)data)[offset + lvert_index] = BM_elem_index_get(eve);
+}
+
+static void extract_poly_idx_iter_poly_mesh(const MeshRenderData *mr,
+ const MPoly *mp,
+ const int mp_index,
+ void *data)
+{
+ const int ml_index_end = mp->loopstart + mp->totloop;
+ for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
+ (*(uint32_t **)data)[ml_index] = (mr->p_origindex) ? mr->p_origindex[mp_index] : mp_index;
+ }
+}
+
+static void extract_edge_idx_iter_poly_mesh(const MeshRenderData *mr,
+ const MPoly *mp,
+ const int UNUSED(mp_index),
+ void *data)
+{
+ const MLoop *mloop = mr->mloop;
+ const int ml_index_end = mp->loopstart + mp->totloop;
+ for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
+ const MLoop *ml = &mloop[ml_index];
+ (*(uint32_t **)data)[ml_index] = (mr->e_origindex) ? mr->e_origindex[ml->e] : ml->e;
+ }
+}
+
+static void extract_vert_idx_iter_poly_mesh(const MeshRenderData *mr,
+ const MPoly *mp,
+ const int UNUSED(mp_index),
+ void *data)
+{
+ const MLoop *mloop = mr->mloop;
+ const int ml_index_end = mp->loopstart + mp->totloop;
+ for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
+ const MLoop *ml = &mloop[ml_index];
+ (*(uint32_t **)data)[ml_index] = (mr->v_origindex) ? mr->v_origindex[ml->v] : ml->v;
+ }
+}
+
+static void extract_edge_idx_iter_ledge_mesh(const MeshRenderData *mr,
+ const MEdge *UNUSED(med),
+ const int ledge_index,
+ void *data)
+{
+ const int e_index = mr->ledges[ledge_index];
+ const int e_orig = (mr->e_origindex) ? mr->e_origindex[e_index] : e_index;
+ (*(uint32_t **)data)[mr->loop_len + ledge_index * 2 + 0] = e_orig;
+ (*(uint32_t **)data)[mr->loop_len + ledge_index * 2 + 1] = e_orig;
+}
+
+static void extract_vert_idx_iter_ledge_mesh(const MeshRenderData *mr,
+ const MEdge *med,
+ const int ledge_index,
+ void *data)
+{
+ int v1_orig = (mr->v_origindex) ? mr->v_origindex[med->v1] : med->v1;
+ int v2_orig = (mr->v_origindex) ? mr->v_origindex[med->v2] : med->v2;
+ (*(uint32_t **)data)[mr->loop_len + ledge_index * 2 + 0] = v1_orig;
+ (*(uint32_t **)data)[mr->loop_len + ledge_index * 2 + 1] = v2_orig;
+}
+
+static void extract_vert_idx_iter_lvert_mesh(const MeshRenderData *mr,
+ const MVert *UNUSED(mv),
+ const int lvert_index,
+ void *data)
+{
+ const int offset = mr->loop_len + (mr->edge_loose_len * 2);
+
+ const int v_index = mr->lverts[lvert_index];
+ const int v_orig = (mr->v_origindex) ? mr->v_origindex[v_index] : v_index;
+ (*(uint32_t **)data)[offset + lvert_index] = v_orig;
+}
+
+const MeshExtract extract_poly_idx = {
+ .init = extract_select_idx_init,
+ .iter_poly_bm = extract_poly_idx_iter_poly_bm,
+ .iter_poly_mesh = extract_poly_idx_iter_poly_mesh,
+ .data_type = 0,
+ .data_size = sizeof(uint32_t *),
+ .use_threading = true,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.poly_idx)};
+
+const MeshExtract extract_edge_idx = {
+ .init = extract_select_idx_init,
+ .iter_poly_bm = extract_edge_idx_iter_poly_bm,
+ .iter_poly_mesh = extract_edge_idx_iter_poly_mesh,
+ .iter_ledge_bm = extract_edge_idx_iter_ledge_bm,
+ .iter_ledge_mesh = extract_edge_idx_iter_ledge_mesh,
+ .data_type = 0,
+ .data_size = sizeof(uint32_t *),
+ .use_threading = true,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edge_idx)};
+
+const MeshExtract extract_vert_idx = {
+ .init = extract_select_idx_init,
+ .iter_poly_bm = extract_vert_idx_iter_poly_bm,
+ .iter_poly_mesh = extract_vert_idx_iter_poly_mesh,
+ .iter_ledge_bm = extract_vert_idx_iter_ledge_bm,
+ .iter_ledge_mesh = extract_vert_idx_iter_ledge_mesh,
+ .iter_lvert_bm = extract_vert_idx_iter_lvert_bm,
+ .iter_lvert_mesh = extract_vert_idx_iter_lvert_mesh,
+ .data_type = 0,
+ .data_size = sizeof(uint32_t *),
+ .use_threading = true,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.vert_idx)};
+
+static void extract_fdot_idx_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *tls_data)
+{
+ GPUVertBuf *vbo = buf;
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ /* TODO rename "color" to something more descriptive. */
+ GPU_vertformat_attr_add(&format, "color", GPU_COMP_U32, 1, GPU_FETCH_INT);
+ }
+
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, mr->poly_len);
+ *(uint32_t **)tls_data = GPU_vertbuf_get_data(vbo);
+}
+
+static void extract_fdot_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr),
+ const BMFace *UNUSED(f),
+ const int f_index,
+ void *data)
+{
+ (*(uint32_t **)data)[f_index] = f_index;
+}
+
+static void extract_fdot_idx_iter_poly_mesh(const MeshRenderData *mr,
+ const MPoly *UNUSED(mp),
+ const int mp_index,
+ void *data)
+{
+ if (mr->p_origindex != NULL) {
+ (*(uint32_t **)data)[mp_index] = mr->p_origindex[mp_index];
+ }
+ else {
+ (*(uint32_t **)data)[mp_index] = mp_index;
+ }
+}
+
+const MeshExtract extract_fdot_idx = {
+ .init = extract_fdot_idx_init,
+ .iter_poly_bm = extract_fdot_idx_iter_poly_bm,
+ .iter_poly_mesh = extract_fdot_idx_iter_poly_mesh,
+ .data_type = 0,
+ .data_size = sizeof(uint32_t *),
+ .use_threading = true,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdot_idx)};
diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_private.h b/source/blender/draw/intern/draw_cache_extract_mesh_private.h
new file mode 100644
index 00000000000..2ece0b4f1db
--- /dev/null
+++ b/source/blender/draw/intern/draw_cache_extract_mesh_private.h
@@ -0,0 +1,295 @@
+/*
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2021 by Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup draw
+ *
+ * \brief Extraction of Mesh data into VBO to feed to GPU.
+ */
+
+#pragma once
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+
+#include "BKE_editmesh.h"
+
+#include "draw_cache_extract.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum eMRExtractType {
+ MR_EXTRACT_BMESH,
+ MR_EXTRACT_MAPPED,
+ MR_EXTRACT_MESH,
+} eMRExtractType;
+
+typedef struct MeshRenderData {
+ eMRExtractType extract_type;
+
+ int poly_len, edge_len, vert_len, loop_len;
+ int edge_loose_len;
+ int vert_loose_len;
+ int loop_loose_len;
+ int tri_len;
+ int mat_len;
+
+ bool use_hide;
+ bool use_subsurf_fdots;
+ bool use_final_mesh;
+
+ /** Use for #MeshStatVis calculation which use world-space coords. */
+ float obmat[4][4];
+
+ const ToolSettings *toolsettings;
+ /** Edit Mesh */
+ BMEditMesh *edit_bmesh;
+ BMesh *bm;
+ EditMeshData *edit_data;
+
+ /* For deformed edit-mesh data. */
+ /* Use for #ME_WRAPPER_TYPE_BMESH. */
+ const float (*bm_vert_coords)[3];
+ const float (*bm_vert_normals)[3];
+ const float (*bm_poly_normals)[3];
+ const float (*bm_poly_centers)[3];
+
+ int *v_origindex, *e_origindex, *p_origindex;
+ int crease_ofs;
+ int bweight_ofs;
+ int freestyle_edge_ofs;
+ int freestyle_face_ofs;
+ /** Mesh */
+ Mesh *me;
+ const MVert *mvert;
+ const MEdge *medge;
+ const MLoop *mloop;
+ const MPoly *mpoly;
+ BMVert *eve_act;
+ BMEdge *eed_act;
+ BMFace *efa_act;
+ BMFace *efa_act_uv;
+ /* Data created on-demand (usually not for #BMesh based data). */
+ MLoopTri *mlooptri;
+ float (*loop_normals)[3];
+ float (*poly_normals)[3];
+ int *lverts, *ledges;
+} MeshRenderData;
+
+BLI_INLINE BMFace *bm_original_face_get(const MeshRenderData *mr, int idx)
+{
+ return ((mr->p_origindex != NULL) && (mr->p_origindex[idx] != ORIGINDEX_NONE) && mr->bm) ?
+ BM_face_at_index(mr->bm, mr->p_origindex[idx]) :
+ NULL;
+}
+
+BLI_INLINE BMEdge *bm_original_edge_get(const MeshRenderData *mr, int idx)
+{
+ return ((mr->e_origindex != NULL) && (mr->e_origindex[idx] != ORIGINDEX_NONE) && mr->bm) ?
+ BM_edge_at_index(mr->bm, mr->e_origindex[idx]) :
+ NULL;
+}
+
+BLI_INLINE BMVert *bm_original_vert_get(const MeshRenderData *mr, int idx)
+{
+ return ((mr->v_origindex != NULL) && (mr->v_origindex[idx] != ORIGINDEX_NONE) && mr->bm) ?
+ BM_vert_at_index(mr->bm, mr->v_origindex[idx]) :
+ NULL;
+}
+
+BLI_INLINE const float *bm_vert_co_get(const MeshRenderData *mr, const BMVert *eve)
+{
+ const float(*vert_coords)[3] = mr->bm_vert_coords;
+ if (vert_coords != NULL) {
+ return vert_coords[BM_elem_index_get(eve)];
+ }
+
+ UNUSED_VARS(mr);
+ return eve->co;
+}
+
+BLI_INLINE const float *bm_vert_no_get(const MeshRenderData *mr, const BMVert *eve)
+{
+ const float(*vert_normals)[3] = mr->bm_vert_normals;
+ if (vert_normals != NULL) {
+ return vert_normals[BM_elem_index_get(eve)];
+ }
+
+ UNUSED_VARS(mr);
+ return eve->no;
+}
+
+BLI_INLINE const float *bm_face_no_get(const MeshRenderData *mr, const BMFace *efa)
+{
+ const float(*poly_normals)[3] = mr->bm_poly_normals;
+ if (poly_normals != NULL) {
+ return poly_normals[BM_elem_index_get(efa)];
+ }
+
+ UNUSED_VARS(mr);
+ return efa->no;
+}
+
+/* ---------------------------------------------------------------------- */
+/** \name Mesh Elements Extract Struct
+ * \{ */
+/* TODO(jbakker): move parameters inside a struct. */
+typedef void(ExtractTriBMeshFn)(const MeshRenderData *mr,
+ BMLoop **elt,
+ const int elt_index,
+ void *data);
+typedef void(ExtractTriMeshFn)(const MeshRenderData *mr,
+ const MLoopTri *mlt,
+ const int elt_index,
+ void *data);
+typedef void(ExtractPolyBMeshFn)(const MeshRenderData *mr,
+ const BMFace *f,
+ const int f_index,
+ void *data);
+typedef void(ExtractPolyMeshFn)(const MeshRenderData *mr,
+ const MPoly *mp,
+ const int mp_index,
+ void *data);
+typedef void(ExtractLEdgeBMeshFn)(const MeshRenderData *mr,
+ const BMEdge *eed,
+ const int ledge_index,
+ void *data);
+typedef void(ExtractLEdgeMeshFn)(const MeshRenderData *mr,
+ const MEdge *med,
+ const int ledge_index,
+ void *data);
+typedef void(ExtractLVertBMeshFn)(const MeshRenderData *mr,
+ const BMVert *eve,
+ const int lvert_index,
+ void *data);
+typedef void(ExtractLVertMeshFn)(const MeshRenderData *mr,
+ const MVert *mv,
+ const int lvert_index,
+ void *data);
+typedef void(ExtractInitFn)(const MeshRenderData *mr,
+ struct MeshBatchCache *cache,
+ void *buffer,
+ void *r_data);
+typedef void(ExtractFinishFn)(const MeshRenderData *mr,
+ struct MeshBatchCache *cache,
+ void *buffer,
+ void *data);
+typedef void(ExtractTaskReduceFn)(void *userdata, void *task_userdata);
+
+typedef struct MeshExtract {
+ /** Executed on main thread and return user data for iteration functions. */
+ ExtractInitFn *init;
+ /** Executed on one (or more if use_threading) worker thread(s). */
+ ExtractTriBMeshFn *iter_looptri_bm;
+ ExtractTriMeshFn *iter_looptri_mesh;
+ ExtractPolyBMeshFn *iter_poly_bm;
+ ExtractPolyMeshFn *iter_poly_mesh;
+ ExtractLEdgeBMeshFn *iter_ledge_bm;
+ ExtractLEdgeMeshFn *iter_ledge_mesh;
+ ExtractLVertBMeshFn *iter_lvert_bm;
+ ExtractLVertMeshFn *iter_lvert_mesh;
+ /** Executed on one worker thread after all elements iterations. */
+ ExtractTaskReduceFn *task_reduce;
+ ExtractFinishFn *finish;
+ /** Used to request common data. */
+ eMRDataType data_type;
+ size_t data_size;
+ /** Used to know if the element callbacks are thread-safe and can be parallelized. */
+ bool use_threading;
+ /**
+ * Offset in bytes of the buffer inside a MeshBufferCache instance. Points to a vertex or index
+ * buffer.
+ */
+ size_t mesh_buffer_offset;
+} MeshExtract;
+
+/** \} */
+
+/* draw_cache_extract_mesh_render_data.c */
+MeshRenderData *mesh_render_data_create(Mesh *me,
+ MeshBufferExtractionCache *cache,
+ const bool is_editmode,
+ const bool is_paint_mode,
+ const bool is_mode_active,
+ const float obmat[4][4],
+ const bool do_final,
+ const bool do_uvedit,
+ const ToolSettings *ts,
+ const eMRIterType iter_type);
+void mesh_render_data_free(MeshRenderData *mr);
+void mesh_render_data_update_normals(MeshRenderData *mr, const eMRDataType data_flag);
+void mesh_render_data_update_looptris(MeshRenderData *mr,
+ const eMRIterType iter_type,
+ const eMRDataType data_flag);
+
+/* draw_cache_extract_mesh_extractors.c */
+void *mesh_extract_buffer_get(const MeshExtract *extractor, MeshBufferCache *mbc);
+eMRIterType mesh_extract_iter_type(const MeshExtract *ext);
+const MeshExtract *mesh_extract_override_get(const MeshExtract *extractor,
+ const bool do_hq_normals,
+ const bool do_single_mat);
+
+extern const MeshExtract extract_tris;
+extern const MeshExtract extract_tris_single_mat;
+extern const MeshExtract extract_lines;
+extern const MeshExtract extract_lines_with_lines_loose;
+extern const MeshExtract extract_lines_loose_only;
+extern const MeshExtract extract_points;
+extern const MeshExtract extract_fdots;
+extern const MeshExtract extract_lines_paint_mask;
+extern const MeshExtract extract_lines_adjacency;
+extern const MeshExtract extract_edituv_tris;
+extern const MeshExtract extract_edituv_lines;
+extern const MeshExtract extract_edituv_points;
+extern const MeshExtract extract_edituv_fdots;
+extern const MeshExtract extract_pos_nor;
+extern const MeshExtract extract_pos_nor_hq;
+extern const MeshExtract extract_lnor_hq;
+extern const MeshExtract extract_lnor;
+extern const MeshExtract extract_uv;
+extern const MeshExtract extract_tan;
+extern const MeshExtract extract_tan_hq;
+extern const MeshExtract extract_sculpt_data;
+extern const MeshExtract extract_vcol;
+extern const MeshExtract extract_orco;
+extern const MeshExtract extract_edge_fac;
+extern const MeshExtract extract_weights;
+extern const MeshExtract extract_edit_data;
+extern const MeshExtract extract_edituv_data;
+extern const MeshExtract extract_edituv_stretch_area;
+extern const MeshExtract extract_edituv_stretch_angle;
+extern const MeshExtract extract_mesh_analysis;
+extern const MeshExtract extract_fdots_pos;
+extern const MeshExtract extract_fdots_nor;
+extern const MeshExtract extract_fdots_nor_hq;
+extern const MeshExtract extract_fdots_uv;
+extern const MeshExtract extract_fdots_edituv_data;
+extern const MeshExtract extract_skin_roots;
+extern const MeshExtract extract_poly_idx;
+extern const MeshExtract extract_edge_idx;
+extern const MeshExtract extract_vert_idx;
+extern const MeshExtract extract_fdot_idx;
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c
new file mode 100644
index 00000000000..4741bcb06c9
--- /dev/null
+++ b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c
@@ -0,0 +1,371 @@
+/*
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2021 by Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup draw
+ *
+ * \brief Extraction of Mesh data into VBO to feed to GPU.
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_bitmap.h"
+#include "BLI_math.h"
+
+#include "BKE_editmesh.h"
+#include "BKE_editmesh_cache.h"
+#include "BKE_mesh.h"
+
+#include "GPU_batch.h"
+
+#include "ED_mesh.h"
+
+#include "draw_cache_extract_mesh_private.h"
+
+/* ---------------------------------------------------------------------- */
+/** \name Mesh/BMesh Interface (indirect, partially cached access to complex data).
+ * \{ */
+
+static void mesh_render_data_loose_geom_load(MeshRenderData *mr, MeshBufferExtractionCache *cache)
+{
+ mr->ledges = cache->ledges;
+ mr->lverts = cache->lverts;
+ mr->vert_loose_len = cache->vert_loose_len;
+ mr->edge_loose_len = cache->edge_loose_len;
+
+ mr->loop_loose_len = mr->vert_loose_len + (mr->edge_loose_len * 2);
+}
+
+static void mesh_render_data_loose_geom_ensure(const MeshRenderData *mr,
+ MeshBufferExtractionCache *cache)
+{
+ /* Early exit: Are loose geometry already available. Only checking for loose verts as loose edges
+ * and verts are calculated at the same time.*/
+ if (cache->lverts) {
+ return;
+ }
+
+ cache->vert_loose_len = 0;
+ cache->edge_loose_len = 0;
+
+ if (mr->extract_type != MR_EXTRACT_BMESH) {
+ /* Mesh */
+
+ BLI_bitmap *lvert_map = BLI_BITMAP_NEW(mr->vert_len, __func__);
+
+ cache->ledges = MEM_mallocN(mr->edge_len * sizeof(*cache->ledges), __func__);
+ const MEdge *med = mr->medge;
+ for (int med_index = 0; med_index < mr->edge_len; med_index++, med++) {
+ if (med->flag & ME_LOOSEEDGE) {
+ cache->ledges[cache->edge_loose_len++] = med_index;
+ }
+ /* Tag verts as not loose. */
+ BLI_BITMAP_ENABLE(lvert_map, med->v1);
+ BLI_BITMAP_ENABLE(lvert_map, med->v2);
+ }
+ if (cache->edge_loose_len < mr->edge_len) {
+ cache->ledges = MEM_reallocN(cache->ledges, cache->edge_loose_len * sizeof(*cache->ledges));
+ }
+
+ cache->lverts = MEM_mallocN(mr->vert_len * sizeof(*mr->lverts), __func__);
+ for (int v = 0; v < mr->vert_len; v++) {
+ if (!BLI_BITMAP_TEST(lvert_map, v)) {
+ cache->lverts[cache->vert_loose_len++] = v;
+ }
+ }
+ if (cache->vert_loose_len < mr->vert_len) {
+ cache->lverts = MEM_reallocN(cache->lverts, cache->vert_loose_len * sizeof(*cache->lverts));
+ }
+
+ MEM_freeN(lvert_map);
+ }
+ else {
+ /* #BMesh */
+ BMesh *bm = mr->bm;
+ int elem_id;
+ BMIter iter;
+ BMVert *eve;
+ BMEdge *ede;
+
+ cache->lverts = MEM_mallocN(mr->vert_len * sizeof(*cache->lverts), __func__);
+ BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, elem_id) {
+ if (eve->e == NULL) {
+ cache->lverts[cache->vert_loose_len++] = elem_id;
+ }
+ }
+ if (cache->vert_loose_len < mr->vert_len) {
+ cache->lverts = MEM_reallocN(cache->lverts, cache->vert_loose_len * sizeof(*cache->lverts));
+ }
+
+ cache->ledges = MEM_mallocN(mr->edge_len * sizeof(*cache->ledges), __func__);
+ BM_ITER_MESH_INDEX (ede, &iter, bm, BM_EDGES_OF_MESH, elem_id) {
+ if (ede->l == NULL) {
+ cache->ledges[cache->edge_loose_len++] = elem_id;
+ }
+ }
+ if (cache->edge_loose_len < mr->edge_len) {
+ cache->ledges = MEM_reallocN(cache->ledges, cache->edge_loose_len * sizeof(*cache->ledges));
+ }
+ }
+}
+
+/**
+ * Part of the creation of the #MeshRenderData that happens in a thread.
+ */
+void mesh_render_data_update_looptris(MeshRenderData *mr,
+ const eMRIterType iter_type,
+ const eMRDataType data_flag)
+{
+ Mesh *me = mr->me;
+ if (mr->extract_type != MR_EXTRACT_BMESH) {
+ /* Mesh */
+ if ((iter_type & MR_ITER_LOOPTRI) || (data_flag & MR_DATA_LOOPTRI)) {
+ mr->mlooptri = MEM_mallocN(sizeof(*mr->mlooptri) * mr->tri_len, "MR_DATATYPE_LOOPTRI");
+ BKE_mesh_recalc_looptri(
+ me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, mr->mlooptri);
+ }
+ }
+ else {
+ /* #BMesh */
+ if ((iter_type & MR_ITER_LOOPTRI) || (data_flag & MR_DATA_LOOPTRI)) {
+ /* Edit mode ensures this is valid, no need to calculate. */
+ BLI_assert((mr->bm->totloop == 0) || (mr->edit_bmesh->looptris != NULL));
+ }
+ }
+}
+
+void mesh_render_data_update_normals(MeshRenderData *mr, const eMRDataType data_flag)
+{
+ Mesh *me = mr->me;
+ const bool is_auto_smooth = (me->flag & ME_AUTOSMOOTH) != 0;
+ const float split_angle = is_auto_smooth ? me->smoothresh : (float)M_PI;
+
+ if (mr->extract_type != MR_EXTRACT_BMESH) {
+ /* Mesh */
+ if (data_flag & (MR_DATA_POLY_NOR | MR_DATA_LOOP_NOR | MR_DATA_TAN_LOOP_NOR)) {
+ mr->poly_normals = MEM_mallocN(sizeof(*mr->poly_normals) * mr->poly_len, __func__);
+ BKE_mesh_calc_normals_poly((MVert *)mr->mvert,
+ NULL,
+ mr->vert_len,
+ mr->mloop,
+ mr->mpoly,
+ mr->loop_len,
+ mr->poly_len,
+ mr->poly_normals,
+ true);
+ }
+ if (((data_flag & MR_DATA_LOOP_NOR) && is_auto_smooth) || (data_flag & MR_DATA_TAN_LOOP_NOR)) {
+ mr->loop_normals = MEM_mallocN(sizeof(*mr->loop_normals) * mr->loop_len, __func__);
+ short(*clnors)[2] = CustomData_get_layer(&mr->me->ldata, CD_CUSTOMLOOPNORMAL);
+ BKE_mesh_normals_loop_split(mr->me->mvert,
+ mr->vert_len,
+ mr->me->medge,
+ mr->edge_len,
+ mr->me->mloop,
+ mr->loop_normals,
+ mr->loop_len,
+ mr->me->mpoly,
+ mr->poly_normals,
+ mr->poly_len,
+ is_auto_smooth,
+ split_angle,
+ NULL,
+ clnors,
+ NULL);
+ }
+ }
+ else {
+ /* #BMesh */
+ if (data_flag & MR_DATA_POLY_NOR) {
+ /* Use #BMFace.no instead. */
+ }
+ if (((data_flag & MR_DATA_LOOP_NOR) && is_auto_smooth) || (data_flag & MR_DATA_TAN_LOOP_NOR)) {
+
+ const float(*vert_coords)[3] = NULL;
+ const float(*vert_normals)[3] = NULL;
+ const float(*poly_normals)[3] = NULL;
+
+ if (mr->edit_data && mr->edit_data->vertexCos) {
+ vert_coords = mr->bm_vert_coords;
+ vert_normals = mr->bm_vert_normals;
+ poly_normals = mr->bm_poly_normals;
+ }
+
+ mr->loop_normals = MEM_mallocN(sizeof(*mr->loop_normals) * mr->loop_len, __func__);
+ const int clnors_offset = CustomData_get_offset(&mr->bm->ldata, CD_CUSTOMLOOPNORMAL);
+ BM_loops_calc_normal_vcos(mr->bm,
+ vert_coords,
+ vert_normals,
+ poly_normals,
+ is_auto_smooth,
+ split_angle,
+ mr->loop_normals,
+ NULL,
+ NULL,
+ clnors_offset,
+ false);
+ }
+ }
+}
+
+/**
+ * \param is_mode_active: When true, use the modifiers from the edit-data,
+ * otherwise don't use modifiers as they are not from this object.
+ */
+MeshRenderData *mesh_render_data_create(Mesh *me,
+ MeshBufferExtractionCache *cache,
+ const bool is_editmode,
+ const bool is_paint_mode,
+ const bool is_mode_active,
+ const float obmat[4][4],
+ const bool do_final,
+ const bool do_uvedit,
+ const ToolSettings *ts,
+ const eMRIterType iter_type)
+{
+ MeshRenderData *mr = MEM_callocN(sizeof(*mr), __func__);
+ mr->toolsettings = ts;
+ mr->mat_len = mesh_render_mat_len_get(me);
+
+ copy_m4_m4(mr->obmat, obmat);
+
+ if (is_editmode) {
+ BLI_assert(me->edit_mesh->mesh_eval_cage && me->edit_mesh->mesh_eval_final);
+ mr->bm = me->edit_mesh->bm;
+ mr->edit_bmesh = me->edit_mesh;
+ mr->me = (do_final) ? me->edit_mesh->mesh_eval_final : me->edit_mesh->mesh_eval_cage;
+ mr->edit_data = is_mode_active ? mr->me->runtime.edit_data : NULL;
+
+ if (mr->edit_data) {
+ EditMeshData *emd = mr->edit_data;
+ if (emd->vertexCos) {
+ BKE_editmesh_cache_ensure_vert_normals(mr->edit_bmesh, emd);
+ BKE_editmesh_cache_ensure_poly_normals(mr->edit_bmesh, emd);
+ }
+
+ mr->bm_vert_coords = mr->edit_data->vertexCos;
+ mr->bm_vert_normals = mr->edit_data->vertexNos;
+ mr->bm_poly_normals = mr->edit_data->polyNos;
+ mr->bm_poly_centers = mr->edit_data->polyCos;
+ }
+
+ bool has_mdata = is_mode_active && (mr->me->runtime.wrapper_type == ME_WRAPPER_TYPE_MDATA);
+ bool use_mapped = is_mode_active &&
+ (has_mdata && !do_uvedit && mr->me && !mr->me->runtime.is_original);
+
+ int bm_ensure_types = BM_VERT | BM_EDGE | BM_LOOP | BM_FACE;
+
+ BM_mesh_elem_index_ensure(mr->bm, bm_ensure_types);
+ BM_mesh_elem_table_ensure(mr->bm, bm_ensure_types & ~BM_LOOP);
+
+ mr->efa_act_uv = EDBM_uv_active_face_get(mr->edit_bmesh, false, false);
+ mr->efa_act = BM_mesh_active_face_get(mr->bm, false, true);
+ mr->eed_act = BM_mesh_active_edge_get(mr->bm);
+ mr->eve_act = BM_mesh_active_vert_get(mr->bm);
+
+ mr->crease_ofs = CustomData_get_offset(&mr->bm->edata, CD_CREASE);
+ mr->bweight_ofs = CustomData_get_offset(&mr->bm->edata, CD_BWEIGHT);
+#ifdef WITH_FREESTYLE
+ mr->freestyle_edge_ofs = CustomData_get_offset(&mr->bm->edata, CD_FREESTYLE_EDGE);
+ mr->freestyle_face_ofs = CustomData_get_offset(&mr->bm->pdata, CD_FREESTYLE_FACE);
+#endif
+
+ if (use_mapped) {
+ mr->v_origindex = CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX);
+ mr->e_origindex = CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX);
+ mr->p_origindex = CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX);
+
+ use_mapped = (mr->v_origindex || mr->e_origindex || mr->p_origindex);
+ }
+
+ mr->extract_type = use_mapped ? MR_EXTRACT_MAPPED : MR_EXTRACT_BMESH;
+
+ /* Seems like the mesh_eval_final do not have the right origin indices.
+ * Force not mapped in this case. */
+ if (has_mdata && do_final && me->edit_mesh->mesh_eval_final != me->edit_mesh->mesh_eval_cage) {
+ // mr->edit_bmesh = NULL;
+ mr->extract_type = MR_EXTRACT_MESH;
+ }
+ }
+ else {
+ mr->me = me;
+ mr->edit_bmesh = NULL;
+
+ bool use_mapped = is_paint_mode && mr->me && !mr->me->runtime.is_original;
+ if (use_mapped) {
+ mr->v_origindex = CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX);
+ mr->e_origindex = CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX);
+ mr->p_origindex = CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX);
+
+ use_mapped = (mr->v_origindex || mr->e_origindex || mr->p_origindex);
+ }
+
+ mr->extract_type = use_mapped ? MR_EXTRACT_MAPPED : MR_EXTRACT_MESH;
+ }
+
+ if (mr->extract_type != MR_EXTRACT_BMESH) {
+ /* Mesh */
+ mr->vert_len = mr->me->totvert;
+ mr->edge_len = mr->me->totedge;
+ mr->loop_len = mr->me->totloop;
+ mr->poly_len = mr->me->totpoly;
+ mr->tri_len = poly_to_tri_count(mr->poly_len, mr->loop_len);
+
+ mr->mvert = CustomData_get_layer(&mr->me->vdata, CD_MVERT);
+ mr->medge = CustomData_get_layer(&mr->me->edata, CD_MEDGE);
+ mr->mloop = CustomData_get_layer(&mr->me->ldata, CD_MLOOP);
+ mr->mpoly = CustomData_get_layer(&mr->me->pdata, CD_MPOLY);
+
+ mr->v_origindex = CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX);
+ mr->e_origindex = CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX);
+ mr->p_origindex = CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX);
+ }
+ else {
+ /* #BMesh */
+ BMesh *bm = mr->bm;
+
+ mr->vert_len = bm->totvert;
+ mr->edge_len = bm->totedge;
+ mr->loop_len = bm->totloop;
+ mr->poly_len = bm->totface;
+ mr->tri_len = poly_to_tri_count(mr->poly_len, mr->loop_len);
+ }
+
+ if (iter_type & (MR_ITER_LEDGE | MR_ITER_LVERT)) {
+ mesh_render_data_loose_geom_ensure(mr, cache);
+ mesh_render_data_loose_geom_load(mr, cache);
+ }
+
+ return mr;
+}
+
+void mesh_render_data_free(MeshRenderData *mr)
+{
+ MEM_SAFE_FREE(mr->mlooptri);
+ MEM_SAFE_FREE(mr->poly_normals);
+ MEM_SAFE_FREE(mr->loop_normals);
+
+ /* Loose geometry are owned by MeshBufferExtractionCache. */
+ mr->ledges = NULL;
+ mr->lverts = NULL;
+
+ MEM_freeN(mr);
+}
+
+/** \} */ \ No newline at end of file
diff --git a/source/blender/draw/intern/draw_cache_impl.h b/source/blender/draw/intern/draw_cache_impl.h
index 7b97ce43558..5743f39f7da 100644
--- a/source/blender/draw/intern/draw_cache_impl.h
+++ b/source/blender/draw/intern/draw_cache_impl.h
@@ -43,6 +43,10 @@ struct bGPdata;
#include "BKE_mesh_types.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* Expose via BKE callbacks */
void DRW_mball_batch_cache_dirty_tag(struct MetaBall *mb, int mode);
void DRW_mball_batch_cache_validate(struct MetaBall *mb);
@@ -262,3 +266,6 @@ struct GPUBatch *DRW_particles_batch_cache_get_edit_inner_points(struct Object *
struct GPUBatch *DRW_particles_batch_cache_get_edit_tip_points(struct Object *object,
struct ParticleSystem *psys,
struct PTCacheEdit *edit);
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/draw/intern/draw_cache_impl_curve.c b/source/blender/draw/intern/draw_cache_impl_curve.cc
index e9558fb320c..9ca452cdacc 100644
--- a/source/blender/draw/intern/draw_cache_impl_curve.c
+++ b/source/blender/draw/intern/draw_cache_impl_curve.cc
@@ -25,8 +25,11 @@
#include "MEM_guardedalloc.h"
+#include "BLI_array.hh"
+#include "BLI_float3.hh"
#include "BLI_listbase.h"
#include "BLI_math_vector.h"
+#include "BLI_span.hh"
#include "BLI_utildefines.h"
#include "DNA_curve_types.h"
@@ -34,6 +37,8 @@
#include "BKE_curve.h"
#include "BKE_displist.h"
#include "BKE_font.h"
+#include "BKE_geometry_set.hh"
+#include "BKE_spline.hh"
#include "GPU_batch.h"
#include "GPU_capabilities.h"
@@ -48,6 +53,11 @@
#include "draw_cache_impl.h" /* own include */
+using blender::Array;
+using blender::float3;
+using blender::IndexRange;
+using blender::Span;
+
/* See: edit_curve_point_vert.glsl for duplicate includes. */
#define SELECT 1
#define ACTIVE_NURB (1 << 2)
@@ -139,12 +149,28 @@ static void curve_render_wire_verts_edges_len_get(const CurveCache *ob_curve_cac
}
}
+static void curve_eval_render_wire_verts_edges_len_get(const CurveEval &curve_eval,
+ int *r_curve_len,
+ int *r_vert_len,
+ int *r_edge_len)
+{
+ Span<SplinePtr> splines = curve_eval.splines();
+ *r_curve_len = splines.size();
+ *r_vert_len = 0;
+ *r_edge_len = 0;
+ for (const SplinePtr &spline : splines) {
+ *r_vert_len += spline->evaluated_points_size();
+ *r_edge_len += spline->evaluated_edges_size();
+ }
+}
+
static int curve_render_normal_len_get(const ListBase *lb, const CurveCache *ob_curve_cache)
{
int normal_len = 0;
const BevList *bl;
const Nurb *nu;
- for (bl = ob_curve_cache->bev.first, nu = lb->first; nu && bl; bl = bl->next, nu = nu->next) {
+ for (bl = (const BevList *)ob_curve_cache->bev.first, nu = (const Nurb *)lb->first; nu && bl;
+ bl = bl->next, nu = nu->next) {
int nr = bl->nr;
int skip = nu->resolu / 16;
#if 0
@@ -163,7 +189,7 @@ static int curve_render_normal_len_get(const ListBase *lb, const CurveCache *ob_
/* ---------------------------------------------------------------------- */
/* Curve Interface, indirect, partially cached access to complex data. */
-typedef struct CurveRenderData {
+struct CurveRenderData {
int types;
struct {
@@ -191,6 +217,9 @@ typedef struct CurveRenderData {
/* borrow from 'Object' */
CurveCache *ob_curve_cache;
+ /* Owned by the evaluated object's geometry set (#geometry_set_eval). */
+ const CurveEval *curve_eval;
+
/* borrow from 'Curve' */
ListBase *nurbs;
@@ -198,7 +227,7 @@ typedef struct CurveRenderData {
int actnu;
/* edit, index in active nurb (BPoint or BezTriple) */
int actvert;
-} CurveRenderData;
+};
enum {
/* Wire center-line */
@@ -220,7 +249,7 @@ static CurveRenderData *curve_render_data_create(Curve *cu,
CurveCache *ob_curve_cache,
const int types)
{
- CurveRenderData *rdata = MEM_callocN(sizeof(*rdata), __func__);
+ CurveRenderData *rdata = (CurveRenderData *)MEM_callocN(sizeof(*rdata), __func__);
rdata->types = types;
ListBase *nurbs;
@@ -229,11 +258,21 @@ static CurveRenderData *curve_render_data_create(Curve *cu,
rdata->ob_curve_cache = ob_curve_cache;
+ rdata->curve_eval = cu->curve_eval;
+
if (types & CU_DATATYPE_WIRE) {
- curve_render_wire_verts_edges_len_get(rdata->ob_curve_cache,
- &rdata->wire.curve_len,
- &rdata->wire.vert_len,
- &rdata->wire.edge_len);
+ if (rdata->curve_eval != nullptr) {
+ curve_eval_render_wire_verts_edges_len_get(*rdata->curve_eval,
+ &rdata->wire.curve_len,
+ &rdata->wire.vert_len,
+ &rdata->wire.edge_len);
+ }
+ else {
+ curve_render_wire_verts_edges_len_get(rdata->ob_curve_cache,
+ &rdata->wire.curve_len,
+ &rdata->wire.vert_len,
+ &rdata->wire.edge_len);
+ }
}
if (cu->editnurb) {
@@ -314,7 +353,7 @@ static void curve_cd_calc_used_gpu_layers(CustomDataMask *cd_layers,
{
for (int i = 0; i < gpumat_array_len; i++) {
struct GPUMaterial *gpumat = gpumat_array[i];
- if (gpumat == NULL) {
+ if (gpumat == nullptr) {
continue;
}
@@ -354,7 +393,7 @@ static void curve_cd_calc_used_gpu_layers(CustomDataMask *cd_layers,
/* ---------------------------------------------------------------------- */
/* Curve GPUBatch Cache */
-typedef struct CurveBatchCache {
+struct CurveBatchCache {
struct {
GPUVertBuf *pos_nor;
GPUVertBuf *edge_fac;
@@ -406,15 +445,15 @@ typedef struct CurveBatchCache {
/* Valid only if edge_detection is up to date. */
bool is_manifold;
-} CurveBatchCache;
+};
/* GPUBatch cache management. */
static bool curve_batch_cache_valid(Curve *cu)
{
- CurveBatchCache *cache = cu->batch_cache;
+ CurveBatchCache *cache = (CurveBatchCache *)cu->batch_cache;
- if (cache == NULL) {
+ if (cache == nullptr) {
return false;
}
@@ -426,7 +465,7 @@ static bool curve_batch_cache_valid(Curve *cu)
return false;
}
- if (cache->is_editmode != ((cu->editnurb != NULL) || (cu->editfont != NULL))) {
+ if (cache->is_editmode != ((cu->editnurb != nullptr) || (cu->editfont != nullptr))) {
return false;
}
@@ -441,10 +480,11 @@ static bool curve_batch_cache_valid(Curve *cu)
static void curve_batch_cache_init(Curve *cu)
{
- CurveBatchCache *cache = cu->batch_cache;
+ CurveBatchCache *cache = (CurveBatchCache *)cu->batch_cache;
if (!cache) {
- cache = cu->batch_cache = MEM_callocN(sizeof(*cache), __func__);
+ cache = (CurveBatchCache *)MEM_callocN(sizeof(*cache), __func__);
+ cu->batch_cache = cache;
}
else {
memset(cache, 0, sizeof(*cache));
@@ -463,11 +503,12 @@ static void curve_batch_cache_init(Curve *cu)
cache->cd_used = 0;
cache->mat_len = DRW_curve_material_count_get(cu);
- cache->surf_per_mat_tris = MEM_callocN(sizeof(*cache->surf_per_mat_tris) * cache->mat_len,
- __func__);
- cache->surf_per_mat = MEM_callocN(sizeof(*cache->surf_per_mat) * cache->mat_len, __func__);
+ cache->surf_per_mat_tris = (GPUIndexBuf **)MEM_callocN(
+ sizeof(*cache->surf_per_mat_tris) * cache->mat_len, __func__);
+ cache->surf_per_mat = (GPUBatch **)MEM_callocN(sizeof(*cache->surf_per_mat) * cache->mat_len,
+ __func__);
- cache->is_editmode = (cu->editnurb != NULL) || (cu->editfont != NULL);
+ cache->is_editmode = (cu->editnurb != nullptr) || (cu->editfont != nullptr);
cache->is_dirty = false;
}
@@ -482,13 +523,13 @@ void DRW_curve_batch_cache_validate(Curve *cu)
static CurveBatchCache *curve_batch_cache_get(Curve *cu)
{
- return cu->batch_cache;
+ return (CurveBatchCache *)cu->batch_cache;
}
void DRW_curve_batch_cache_dirty_tag(Curve *cu, int mode)
{
- CurveBatchCache *cache = cu->batch_cache;
- if (cache == NULL) {
+ CurveBatchCache *cache = (CurveBatchCache *)cu->batch_cache;
+ if (cache == nullptr) {
return;
}
switch (mode) {
@@ -508,7 +549,7 @@ void DRW_curve_batch_cache_dirty_tag(Curve *cu, int mode)
static void curve_batch_cache_clear(Curve *cu)
{
- CurveBatchCache *cache = cu->batch_cache;
+ CurveBatchCache *cache = (CurveBatchCache *)cu->batch_cache;
if (!cache) {
return;
}
@@ -553,8 +594,6 @@ void DRW_curve_batch_cache_free(Curve *cu)
/* GPUBatch cache usage. */
static void curve_create_curves_pos(CurveRenderData *rdata, GPUVertBuf *vbo_curves_pos)
{
- BLI_assert(rdata->ob_curve_cache != NULL);
-
static GPUVertFormat format = {0};
static struct {
uint pos;
@@ -567,30 +606,46 @@ static void curve_create_curves_pos(CurveRenderData *rdata, GPUVertBuf *vbo_curv
GPU_vertbuf_init_with_format(vbo_curves_pos, &format);
GPU_vertbuf_data_alloc(vbo_curves_pos, vert_len);
- int v_idx = 0;
- LISTBASE_FOREACH (const BevList *, bl, &rdata->ob_curve_cache->bev) {
- if (bl->nr <= 0) {
- continue;
- }
- const int i_end = v_idx + bl->nr;
- for (const BevPoint *bevp = bl->bevpoints; v_idx < i_end; v_idx++, bevp++) {
- GPU_vertbuf_attr_set(vbo_curves_pos, attr_id.pos, v_idx, bevp->vec);
+ if (rdata->curve_eval != nullptr) {
+ const CurveEval &curve_eval = *rdata->curve_eval;
+ Span<SplinePtr> splines = curve_eval.splines();
+ Array<int> offsets = curve_eval.evaluated_point_offsets();
+ BLI_assert(offsets.last() == vert_len);
+
+ for (const int i_spline : splines.index_range()) {
+ Span<float3> positions = splines[i_spline]->evaluated_positions();
+ for (const int i_point : positions.index_range()) {
+ GPU_vertbuf_attr_set(
+ vbo_curves_pos, attr_id.pos, offsets[i_spline] + i_point, positions[i_point]);
+ }
}
}
- LISTBASE_FOREACH (const DispList *, dl, &rdata->ob_curve_cache->disp) {
- if (ELEM(dl->type, DL_SEGM, DL_POLY)) {
- for (int i = 0; i < dl->nr; v_idx++, i++) {
- GPU_vertbuf_attr_set(vbo_curves_pos, attr_id.pos, v_idx, &((float(*)[3])dl->verts)[i]);
+ else {
+ BLI_assert(rdata->ob_curve_cache != nullptr);
+
+ int v_idx = 0;
+ LISTBASE_FOREACH (const BevList *, bl, &rdata->ob_curve_cache->bev) {
+ if (bl->nr <= 0) {
+ continue;
+ }
+ const int i_end = v_idx + bl->nr;
+ for (const BevPoint *bevp = bl->bevpoints; v_idx < i_end; v_idx++, bevp++) {
+ GPU_vertbuf_attr_set(vbo_curves_pos, attr_id.pos, v_idx, bevp->vec);
+ }
+ }
+ LISTBASE_FOREACH (const DispList *, dl, &rdata->ob_curve_cache->disp) {
+ if (ELEM(dl->type, DL_SEGM, DL_POLY)) {
+ for (int i = 0; i < dl->nr; v_idx++, i++) {
+ GPU_vertbuf_attr_set(vbo_curves_pos, attr_id.pos, v_idx, &((float(*)[3])dl->verts)[i]);
+ }
}
}
+ BLI_assert(v_idx == vert_len);
}
- BLI_assert(v_idx == vert_len);
}
static void curve_create_curves_lines(CurveRenderData *rdata, GPUIndexBuf *ibo_curve_lines)
{
- BLI_assert(rdata->ob_curve_cache != NULL);
-
const int vert_len = curve_render_data_wire_verts_len_get(rdata);
const int edge_len = curve_render_data_wire_edges_len_get(rdata);
const int curve_len = curve_render_data_wire_curve_len_get(rdata);
@@ -600,34 +655,56 @@ static void curve_create_curves_lines(CurveRenderData *rdata, GPUIndexBuf *ibo_c
GPUIndexBufBuilder elb;
GPU_indexbuf_init_ex(&elb, GPU_PRIM_LINE_STRIP, index_len, vert_len);
- int v_idx = 0;
- LISTBASE_FOREACH (const BevList *, bl, &rdata->ob_curve_cache->bev) {
- if (bl->nr <= 0) {
- continue;
- }
- const bool is_cyclic = bl->poly != -1;
- if (is_cyclic) {
- GPU_indexbuf_add_generic_vert(&elb, v_idx + (bl->nr - 1));
- }
- for (int i = 0; i < bl->nr; i++) {
- GPU_indexbuf_add_generic_vert(&elb, v_idx + i);
+ if (rdata->curve_eval != nullptr) {
+ const CurveEval &curve_eval = *rdata->curve_eval;
+ Span<SplinePtr> splines = curve_eval.splines();
+ Array<int> offsets = curve_eval.evaluated_point_offsets();
+ BLI_assert(offsets.last() == vert_len);
+
+ for (const int i_spline : splines.index_range()) {
+ const int eval_size = splines[i_spline]->evaluated_points_size();
+ if (splines[i_spline]->is_cyclic()) {
+ GPU_indexbuf_add_generic_vert(&elb, offsets[i_spline] + eval_size - 1);
+ }
+ for (const int i_point : IndexRange(eval_size)) {
+ GPU_indexbuf_add_generic_vert(&elb, offsets[i_spline] + i_point);
+ }
+ GPU_indexbuf_add_primitive_restart(&elb);
}
- GPU_indexbuf_add_primitive_restart(&elb);
- v_idx += bl->nr;
}
- LISTBASE_FOREACH (const DispList *, dl, &rdata->ob_curve_cache->disp) {
- if (ELEM(dl->type, DL_SEGM, DL_POLY)) {
- const bool is_cyclic = dl->type == DL_POLY;
+ else {
+ BLI_assert(rdata->ob_curve_cache != nullptr);
+
+ int v_idx = 0;
+ LISTBASE_FOREACH (const BevList *, bl, &rdata->ob_curve_cache->bev) {
+ if (bl->nr <= 0) {
+ continue;
+ }
+ const bool is_cyclic = bl->poly != -1;
if (is_cyclic) {
- GPU_indexbuf_add_generic_vert(&elb, v_idx + (dl->nr - 1));
+ GPU_indexbuf_add_generic_vert(&elb, v_idx + (bl->nr - 1));
}
- for (int i = 0; i < dl->nr; i++) {
+ for (int i = 0; i < bl->nr; i++) {
GPU_indexbuf_add_generic_vert(&elb, v_idx + i);
}
GPU_indexbuf_add_primitive_restart(&elb);
- v_idx += dl->nr;
+ v_idx += bl->nr;
+ }
+ LISTBASE_FOREACH (const DispList *, dl, &rdata->ob_curve_cache->disp) {
+ if (ELEM(dl->type, DL_SEGM, DL_POLY)) {
+ const bool is_cyclic = dl->type == DL_POLY;
+ if (is_cyclic) {
+ GPU_indexbuf_add_generic_vert(&elb, v_idx + (dl->nr - 1));
+ }
+ for (int i = 0; i < dl->nr; i++) {
+ GPU_indexbuf_add_generic_vert(&elb, v_idx + i);
+ }
+ GPU_indexbuf_add_primitive_restart(&elb);
+ v_idx += dl->nr;
+ }
}
}
+
GPU_indexbuf_build_in_place(&elb, ibo_curve_lines);
}
@@ -677,7 +754,9 @@ static void curve_create_edit_curves_nor(CurveRenderData *rdata,
const uint tan_id = do_hq_normals ? attr_id.tan_hq : attr_id.tan;
const uint rad_id = do_hq_normals ? attr_id.rad_hq : attr_id.rad;
- for (bl = rdata->ob_curve_cache->bev.first, nu = rdata->nurbs->first; nu && bl;
+ for (bl = (const BevList *)rdata->ob_curve_cache->bev.first,
+ nu = (const Nurb *)rdata->nurbs->first;
+ nu && bl;
bl = bl->next, nu = nu->next) {
const BevPoint *bevp = bl->bevpoints;
int nr = bl->nr;
@@ -764,6 +843,9 @@ static void curve_create_edit_data_and_handles(CurveRenderData *rdata,
int edges_len_capacity = curve_render_data_overlay_edges_len_get(rdata) * 2;
int vbo_len_used = 0;
+#define DRW_TEST_ASSIGN_VBO(v) (v = (DRW_vbo_requested(v) ? (v) : nullptr))
+#define DRW_TEST_ASSIGN_IBO(v) (v = (DRW_ibo_requested(v) ? (v) : nullptr))
+
if (DRW_TEST_ASSIGN_VBO(vbo_pos)) {
GPU_vertbuf_init_with_format(vbo_pos, &format_pos);
GPU_vertbuf_data_alloc(vbo_pos, verts_len_capacity);
@@ -773,8 +855,8 @@ static void curve_create_edit_data_and_handles(CurveRenderData *rdata,
GPU_vertbuf_data_alloc(vbo_data, verts_len_capacity);
}
- GPUIndexBufBuilder elb_verts, *elbp_verts = NULL;
- GPUIndexBufBuilder elb_lines, *elbp_lines = NULL;
+ GPUIndexBufBuilder elb_verts, *elbp_verts = nullptr;
+ GPUIndexBufBuilder elb_lines, *elbp_lines = nullptr;
if (DRW_TEST_ASSIGN_IBO(ibo_edit_verts_points)) {
elbp_verts = &elb_verts;
GPU_indexbuf_init(elbp_verts, GPU_PRIM_POINTS, verts_len_capacity, verts_len_capacity);
@@ -784,14 +866,17 @@ static void curve_create_edit_data_and_handles(CurveRenderData *rdata,
GPU_indexbuf_init(elbp_lines, GPU_PRIM_LINES, edges_len_capacity, verts_len_capacity);
}
+#undef DRW_TEST_ASSIGN_VBO
+#undef DRW_TEST_ASSIGN_IBO
+
int nu_id = 0;
- for (Nurb *nu = rdata->nurbs->first; nu; nu = nu->next, nu_id++) {
+ for (Nurb *nu = (Nurb *)rdata->nurbs->first; nu; nu = nu->next, nu_id++) {
const BezTriple *bezt = nu->bezt;
const BPoint *bp = nu->bp;
if (bezt) {
for (int a = 0; a < nu->pntsu; a++, bezt++) {
- if (bezt->hide == true) {
+ if (bezt->hide != 0) {
continue;
}
const bool handle_selected = BEZT_ISSEL_ANY(bezt);
@@ -826,7 +911,7 @@ static void curve_create_edit_data_and_handles(CurveRenderData *rdata,
else if (bp) {
int pt_len = nu->pntsu * nu->pntsv;
for (int a = 0; a < pt_len; a++, bp++, vbo_len_used += 1) {
- if (bp->hide == true) {
+ if (bp->hide != 0) {
continue;
}
int u = (a % nu->pntsu);
@@ -837,8 +922,8 @@ static void curve_create_edit_data_and_handles(CurveRenderData *rdata,
GPU_indexbuf_add_point_vert(elbp_verts, vbo_len_used);
}
if (elbp_lines) {
- const BPoint *bp_next_u = (u < (nu->pntsu - 1)) ? &nu->bp[a + 1] : NULL;
- const BPoint *bp_next_v = (v < (nu->pntsv - 1)) ? &nu->bp[a + nu->pntsu] : NULL;
+ const BPoint *bp_next_u = (u < (nu->pntsu - 1)) ? &nu->bp[a + 1] : nullptr;
+ const BPoint *bp_next_v = (v < (nu->pntsv - 1)) ? &nu->bp[a + nu->pntsu] : nullptr;
if (bp_next_u && (bp_next_u->hide == false)) {
GPU_indexbuf_add_line_verts(elbp_lines, vbo_len_used, vbo_len_used + 1);
}
@@ -858,17 +943,17 @@ static void curve_create_edit_data_and_handles(CurveRenderData *rdata,
}
/* Resize & Finish */
- if (elbp_verts != NULL) {
+ if (elbp_verts != nullptr) {
GPU_indexbuf_build_in_place(elbp_verts, ibo_edit_verts_points);
}
- if (elbp_lines != NULL) {
+ if (elbp_lines != nullptr) {
GPU_indexbuf_build_in_place(elbp_lines, ibo_edit_lines);
}
if (vbo_len_used != verts_len_capacity) {
- if (vbo_pos != NULL) {
+ if (vbo_pos != nullptr) {
GPU_vertbuf_data_resize(vbo_pos, vbo_len_used);
}
- if (vbo_data != NULL) {
+ if (vbo_data != nullptr) {
GPU_vertbuf_data_resize(vbo_data, vbo_len_used);
}
}
@@ -932,7 +1017,7 @@ GPUVertBuf *DRW_curve_batch_cache_pos_vertbuf_get(struct Curve *cu)
/* Request surface to trigger the vbo filling. Otherwise it may do nothing. */
DRW_batch_request(&cache->batch.surfaces);
- DRW_vbo_request(NULL, &cache->ordered.loop_pos_nor);
+ DRW_vbo_request(nullptr, &cache->ordered.loop_pos_nor);
return cache->ordered.loop_pos_nor;
}
@@ -968,7 +1053,7 @@ void DRW_curve_batch_cache_create_requested(Object *ob, const struct Scene *scen
{
BLI_assert(ELEM(ob->type, OB_CURVE, OB_SURF, OB_FONT));
- Curve *cu = ob->data;
+ Curve *cu = (Curve *)ob->data;
CurveBatchCache *cache = curve_batch_cache_get(cu);
/* Verify that all surface batches have needed attribute layers. */
@@ -1065,8 +1150,11 @@ void DRW_curve_batch_cache_create_requested(Object *ob, const struct Scene *scen
CurveRenderData *rdata = curve_render_data_create(cu, ob->runtime.curve_cache, mr_flag);
- /* DispLists */
- ListBase *lb = &rdata->ob_curve_cache->disp;
+ /* The object's curve cache can be empty (in one case because we use #CurveEval's cache instead),
+ * If so, point to an empty DispList list to avoid the need to check for null in the following
+ * functions. */
+ ListBase empty_lb = {nullptr, nullptr};
+ ListBase *lb = rdata->ob_curve_cache == nullptr ? &empty_lb : &rdata->ob_curve_cache->disp;
/* Generate VBOs */
if (DRW_vbo_requested(cache->ordered.pos_nor)) {
@@ -1118,7 +1206,7 @@ void DRW_curve_batch_cache_create_requested(Object *ob, const struct Scene *scen
#ifdef DEBUG
/* Make sure all requested batches have been setup. */
for (int i = 0; i < sizeof(cache->batch) / sizeof(void *); i++) {
- BLI_assert(!DRW_batch_requested(((GPUBatch **)&cache->batch)[i], 0));
+ BLI_assert(!DRW_batch_requested(((GPUBatch **)&cache->batch)[i], (GPUPrimType)0));
}
#endif
}
diff --git a/source/blender/draw/intern/draw_cache_impl_displist.c b/source/blender/draw/intern/draw_cache_impl_displist.c
index d606f70db9e..b59c4db8383 100644
--- a/source/blender/draw/intern/draw_cache_impl_displist.c
+++ b/source/blender/draw/intern/draw_cache_impl_displist.c
@@ -469,7 +469,7 @@ static void displist_surf_fnors_ensure(const DispList *dl, float (**fnors)[3])
{
int u_len = dl->nr - ((dl->flag & DL_CYCL_U) ? 0 : 1);
int v_len = dl->parts - ((dl->flag & DL_CYCL_V) ? 0 : 1);
- const float(*verts)[3] = (float(*)[3])dl->verts;
+ const float(*verts)[3] = (const float(*)[3])dl->verts;
float(*nor_flat)[3] = MEM_mallocN(sizeof(float[3]) * u_len * v_len, __func__);
*fnors = nor_flat;
@@ -532,6 +532,8 @@ void DRW_displist_vertbuf_create_loop_pos_and_nor_and_uv_and_tan(ListBase *lb,
GPUVertBufRaw uv_step = {0};
GPUVertBufRaw tan_step = {0};
+#define DRW_TEST_ASSIGN_VBO(v) (v = (DRW_vbo_requested(v) ? (v) : NULL))
+
if (DRW_TEST_ASSIGN_VBO(vbo_pos_nor)) {
GPU_vertbuf_init_with_format(vbo_pos_nor,
do_hq_normals ? &format_pos_nor_hq : &format_pos_nor);
@@ -550,13 +552,15 @@ void DRW_displist_vertbuf_create_loop_pos_and_nor_and_uv_and_tan(ListBase *lb,
GPU_vertbuf_attr_get_raw_data(vbo_tan, tan_id, &tan_step);
}
+#undef DRW_TEST_ASSIGN_VBO
+
BKE_displist_normals_add(lb);
LISTBASE_FOREACH (const DispList *, dl, lb) {
const bool is_smooth = (dl->rt & CU_SMOOTH) != 0;
if (ELEM(dl->type, DL_INDEX3, DL_INDEX4, DL_SURF)) {
- const float(*verts)[3] = (float(*)[3])dl->verts;
- const float(*nors)[3] = (float(*)[3])dl->nors;
+ const float(*verts)[3] = (const float(*)[3])dl->verts;
+ const float(*nors)[3] = (const float(*)[3])dl->nors;
const int *idx = dl->index;
float uv[4][2];
diff --git a/source/blender/draw/intern/draw_cache_impl_gpencil.c b/source/blender/draw/intern/draw_cache_impl_gpencil.c
index c07271a0d33..bea9ba1122b 100644
--- a/source/blender/draw/intern/draw_cache_impl_gpencil.c
+++ b/source/blender/draw/intern/draw_cache_impl_gpencil.c
@@ -348,7 +348,14 @@ static void gpencil_buffer_add_stroke(gpStrokeVert *verts,
}
/* Draw line to first point to complete the loop for cyclic strokes. */
if (is_cyclic) {
- gpencil_buffer_add_point(verts, cols, gps, &pts[0], v++, false);
+ gpencil_buffer_add_point(verts, cols, gps, &pts[0], v, false);
+ /* UV factor needs to be adjusted for the last point to not be equal to the UV factor of the
+ * first point. It should be the factor of the last point plus the distance from the last point
+ * to the first.
+ */
+ gpStrokeVert *vert = &verts[v];
+ vert->u_stroke = verts[v - 1].u_stroke + len_v3v3(&pts[pts_len - 1].x, &pts[0].x);
+ v++;
}
/* Last adjacency point (not drawn). */
adj_idx = (is_cyclic) ? 1 : max_ii(0, pts_len - 2);
@@ -398,7 +405,7 @@ static void gpencil_batches_ensure(Object *ob, GpencilBatchCache *cache, int cfr
if (cache->vbo == NULL) {
/* Should be discarded together. */
BLI_assert(cache->vbo == NULL && cache->ibo == NULL);
- BLI_assert(cache->stroke_batch == NULL && cache->stroke_batch == NULL);
+ BLI_assert(cache->fill_batch == NULL && cache->stroke_batch == NULL);
/* TODO/PERF: Could be changed to only do it if needed.
* For now it's simpler to assume we always need it
* since multiple viewport could or could not need it.
diff --git a/source/blender/draw/intern/draw_cache_impl_hair.c b/source/blender/draw/intern/draw_cache_impl_hair.c
index fd28ac00186..6424b21666d 100644
--- a/source/blender/draw/intern/draw_cache_impl_hair.c
+++ b/source/blender/draw/intern/draw_cache_impl_hair.c
@@ -243,7 +243,8 @@ static void hair_batch_cache_ensure_procedural_final_points(ParticleHairCache *c
GPUVertFormat format = {0};
GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
- cache->final[subdiv].proc_buf = GPU_vertbuf_create_with_format(&format);
+ cache->final[subdiv].proc_buf = GPU_vertbuf_create_with_format_ex(&format,
+ GPU_USAGE_DEVICE_ONLY);
/* Create a destination buffer for the transform feedback. Sized appropriately */
/* Those are points! not line segments. */
diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c
index 04bfb667d24..3cc71e47f28 100644
--- a/source/blender/draw/intern/draw_cache_impl_mesh.c
+++ b/source/blender/draw/intern/draw_cache_impl_mesh.c
@@ -397,7 +397,7 @@ static void drw_mesh_weight_state_extract(Object *ob,
wstate->flags |= DRW_MESH_WEIGHT_STATE_MULTIPAINT |
(ts->auto_normalize ? DRW_MESH_WEIGHT_STATE_AUTO_NORMALIZE : 0);
- if (me->editflag & ME_EDIT_VERTEX_GROUPS_X_SYMMETRY) {
+ if (ME_USING_MIRROR_X_VERTEX_GROUPS(me)) {
BKE_object_defgroup_mirror_selection(ob,
wstate->defgroup_len,
wstate->defgroup_sel,
@@ -557,12 +557,18 @@ static void mesh_batch_cache_discard_surface_batches(MeshBatchCache *cache)
static void mesh_batch_cache_discard_shaded_tri(MeshBatchCache *cache)
{
FOREACH_MESH_BUFFER_CACHE (cache, mbufcache) {
- GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.pos_nor);
GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.uv);
GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.tan);
GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.vcol);
GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.orco);
}
+ /* Discard batches using vbo.uv. */
+ GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_faces);
+ GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_faces_stretch_area);
+ GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_faces_stretch_angle);
+ GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_edges);
+ GPU_BATCH_DISCARD_SAFE(cache->batch.edituv_verts);
+
mesh_batch_cache_discard_surface_batches(cache);
mesh_cd_layers_type_clear(&cache->cd_used);
}
@@ -659,8 +665,17 @@ void DRW_mesh_batch_cache_dirty_tag(Mesh *me, eMeshBatchDirtyMode mode)
GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.lnor);
}
GPU_BATCH_DISCARD_SAFE(cache->batch.surface);
+ /* Discard batches using vbo.pos_nor. */
GPU_BATCH_DISCARD_SAFE(cache->batch.wire_loops);
GPU_BATCH_DISCARD_SAFE(cache->batch.wire_edges);
+ GPU_BATCH_DISCARD_SAFE(cache->batch.all_verts);
+ GPU_BATCH_DISCARD_SAFE(cache->batch.all_edges);
+ GPU_BATCH_DISCARD_SAFE(cache->batch.loose_edges);
+ GPU_BATCH_DISCARD_SAFE(cache->batch.edge_detection);
+ GPU_BATCH_DISCARD_SAFE(cache->batch.surface_weights);
+ GPU_BATCH_DISCARD_SAFE(cache->batch.edit_mesh_analysis);
+ /* Discard batches using vbo.lnor. */
+ GPU_BATCH_DISCARD_SAFE(cache->batch.edit_lnor);
mesh_batch_cache_discard_surface_batches(cache);
cache->batch_ready &= ~(MBC_SURFACE | MBC_WIRE_EDGES | MBC_WIRE_LOOPS);
break;
@@ -692,6 +707,26 @@ void DRW_mesh_batch_cache_dirty_tag(Mesh *me, eMeshBatchDirtyMode mode)
}
}
+static void mesh_buffer_cache_clear(MeshBufferCache *mbufcache)
+{
+ GPUVertBuf **vbos = (GPUVertBuf **)&mbufcache->vbo;
+ GPUIndexBuf **ibos = (GPUIndexBuf **)&mbufcache->ibo;
+ for (int i = 0; i < sizeof(mbufcache->vbo) / sizeof(void *); i++) {
+ GPU_VERTBUF_DISCARD_SAFE(vbos[i]);
+ }
+ for (int i = 0; i < sizeof(mbufcache->ibo) / sizeof(void *); i++) {
+ GPU_INDEXBUF_DISCARD_SAFE(ibos[i]);
+ }
+}
+
+static void mesh_buffer_extraction_cache_clear(MeshBufferExtractionCache *extraction_cache)
+{
+ MEM_SAFE_FREE(extraction_cache->lverts);
+ MEM_SAFE_FREE(extraction_cache->ledges);
+ extraction_cache->edge_loose_len = 0;
+ extraction_cache->vert_loose_len = 0;
+}
+
static void mesh_batch_cache_clear(Mesh *me)
{
MeshBatchCache *cache = me->runtime.batch_cache;
@@ -699,16 +734,13 @@ static void mesh_batch_cache_clear(Mesh *me)
return;
}
FOREACH_MESH_BUFFER_CACHE (cache, mbufcache) {
- GPUVertBuf **vbos = (GPUVertBuf **)&mbufcache->vbo;
- GPUIndexBuf **ibos = (GPUIndexBuf **)&mbufcache->ibo;
- for (int i = 0; i < sizeof(mbufcache->vbo) / sizeof(void *); i++) {
- GPU_VERTBUF_DISCARD_SAFE(vbos[i]);
- }
- for (int i = 0; i < sizeof(mbufcache->ibo) / sizeof(void *); i++) {
- GPU_INDEXBUF_DISCARD_SAFE(ibos[i]);
- }
+ mesh_buffer_cache_clear(mbufcache);
}
+ mesh_buffer_extraction_cache_clear(&cache->final_extraction_cache);
+ mesh_buffer_extraction_cache_clear(&cache->cage_extraction_cache);
+ mesh_buffer_extraction_cache_clear(&cache->uv_cage_extraction_cache);
+
for (int i = 0; i < cache->mat_len; i++) {
GPU_INDEXBUF_DISCARD_SAFE(cache->final.tris_per_mat[i]);
}
@@ -1145,25 +1177,25 @@ static void drw_mesh_batch_cache_check_available(struct TaskGraph *task_graph, M
* some issues (See T77867 where we needed to disable this function in order to debug what was
* happening in release builds). */
BLI_task_graph_work_and_wait(task_graph);
- for (int i = 0; i < sizeof(cache->batch) / sizeof(void *); i++) {
+ for (int i = 0; i < MBC_BATCH_LEN; i++) {
BLI_assert(!DRW_batch_requested(((GPUBatch **)&cache->batch)[i], 0));
}
- for (int i = 0; i < sizeof(cache->final.vbo) / sizeof(void *); i++) {
+ for (int i = 0; i < MBC_VBO_LEN; i++) {
BLI_assert(!DRW_vbo_requested(((GPUVertBuf **)&cache->final.vbo)[i]));
}
- for (int i = 0; i < sizeof(cache->final.ibo) / sizeof(void *); i++) {
+ for (int i = 0; i < MBC_IBO_LEN; i++) {
BLI_assert(!DRW_ibo_requested(((GPUIndexBuf **)&cache->final.ibo)[i]));
}
- for (int i = 0; i < sizeof(cache->cage.vbo) / sizeof(void *); i++) {
+ for (int i = 0; i < MBC_VBO_LEN; i++) {
BLI_assert(!DRW_vbo_requested(((GPUVertBuf **)&cache->cage.vbo)[i]));
}
- for (int i = 0; i < sizeof(cache->cage.ibo) / sizeof(void *); i++) {
+ for (int i = 0; i < MBC_IBO_LEN; i++) {
BLI_assert(!DRW_ibo_requested(((GPUIndexBuf **)&cache->cage.ibo)[i]));
}
- for (int i = 0; i < sizeof(cache->uv_cage.vbo) / sizeof(void *); i++) {
+ for (int i = 0; i < MBC_VBO_LEN; i++) {
BLI_assert(!DRW_vbo_requested(((GPUVertBuf **)&cache->uv_cage.vbo)[i]));
}
- for (int i = 0; i < sizeof(cache->uv_cage.ibo) / sizeof(void *); i++) {
+ for (int i = 0; i < MBC_IBO_LEN; i++) {
BLI_assert(!DRW_ibo_requested(((GPUIndexBuf **)&cache->uv_cage.ibo)[i]));
}
}
@@ -1527,7 +1559,8 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
if (do_uvcage) {
mesh_buffer_cache_create_requested(task_graph,
cache,
- cache->uv_cage,
+ &cache->uv_cage,
+ &cache->uv_cage_extraction_cache,
me,
is_editmode,
is_paint_mode,
@@ -1536,7 +1569,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
false,
true,
false,
- &cache->cd_used,
scene,
ts,
true);
@@ -1545,7 +1577,8 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
if (do_cage) {
mesh_buffer_cache_create_requested(task_graph,
cache,
- cache->cage,
+ &cache->cage,
+ &cache->cage_extraction_cache,
me,
is_editmode,
is_paint_mode,
@@ -1554,7 +1587,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
false,
false,
use_subsurf_fdots,
- &cache->cd_used,
scene,
ts,
true);
@@ -1562,7 +1594,8 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
mesh_buffer_cache_create_requested(task_graph,
cache,
- cache->final,
+ &cache->final,
+ &cache->final_extraction_cache,
me,
is_editmode,
is_paint_mode,
@@ -1571,7 +1604,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
true,
false,
use_subsurf_fdots,
- &cache->cd_used,
scene,
ts,
use_hide);
diff --git a/source/blender/draw/intern/draw_cache_impl_volume.c b/source/blender/draw/intern/draw_cache_impl_volume.c
index 7244dfd12c3..a91a1391c31 100644
--- a/source/blender/draw/intern/draw_cache_impl_volume.c
+++ b/source/blender/draw/intern/draw_cache_impl_volume.c
@@ -152,7 +152,7 @@ typedef struct VolumeWireframeUserData {
} VolumeWireframeUserData;
static void drw_volume_wireframe_cb(
- void *userdata, float (*verts)[3], int (*edges)[2], int totvert, int totedge)
+ void *userdata, const float (*verts)[3], const int (*edges)[2], int totvert, int totedge)
{
VolumeWireframeUserData *data = userdata;
Scene *scene = data->scene;
@@ -225,7 +225,7 @@ GPUBatch *DRW_volume_batch_cache_get_wireframes_face(Volume *volume)
VolumeBatchCache *cache = volume_batch_cache_get(volume);
if (cache->face_wire.batch == NULL) {
- VolumeGrid *volume_grid = BKE_volume_grid_active_get(volume);
+ const VolumeGrid *volume_grid = BKE_volume_grid_active_get_for_read(volume);
if (volume_grid == NULL) {
return NULL;
}
@@ -274,7 +274,7 @@ GPUBatch *DRW_volume_batch_cache_get_selection_surface(Volume *volume)
{
VolumeBatchCache *cache = volume_batch_cache_get(volume);
if (cache->selection_surface == NULL) {
- VolumeGrid *volume_grid = BKE_volume_grid_active_get(volume);
+ const VolumeGrid *volume_grid = BKE_volume_grid_active_get_for_read(volume);
if (volume_grid == NULL) {
return NULL;
}
@@ -284,8 +284,8 @@ GPUBatch *DRW_volume_batch_cache_get_selection_surface(Volume *volume)
return cache->selection_surface;
}
-static DRWVolumeGrid *volume_grid_cache_get(Volume *volume,
- VolumeGrid *grid,
+static DRWVolumeGrid *volume_grid_cache_get(const Volume *volume,
+ const VolumeGrid *grid,
VolumeBatchCache *cache)
{
const char *name = BKE_volume_grid_name(grid);
@@ -351,7 +351,7 @@ static DRWVolumeGrid *volume_grid_cache_get(Volume *volume,
return cache_grid;
}
-DRWVolumeGrid *DRW_volume_batch_cache_get_grid(Volume *volume, VolumeGrid *volume_grid)
+DRWVolumeGrid *DRW_volume_batch_cache_get_grid(Volume *volume, const VolumeGrid *volume_grid)
{
VolumeBatchCache *cache = volume_batch_cache_get(volume);
DRWVolumeGrid *grid = volume_grid_cache_get(volume, volume_grid, cache);
diff --git a/source/blender/draw/intern/draw_cache_inline.h b/source/blender/draw/intern/draw_cache_inline.h
index ebe97b4e7c4..6e537a3bffa 100644
--- a/source/blender/draw/intern/draw_cache_inline.h
+++ b/source/blender/draw/intern/draw_cache_inline.h
@@ -40,10 +40,6 @@
(flag |= DRW_ibo_requested(ibo) ? (value) : 0)
#endif
-/* Test and assign NULL if test fails */
-#define DRW_TEST_ASSIGN_VBO(v) (v = (DRW_vbo_requested(v) ? (v) : NULL))
-#define DRW_TEST_ASSIGN_IBO(v) (v = (DRW_ibo_requested(v) ? (v) : NULL))
-
BLI_INLINE GPUBatch *DRW_batch_request(GPUBatch **batch)
{
/* XXX TODO(fclem): We are writing to batch cache here. Need to make this thread safe. */
@@ -53,13 +49,13 @@ BLI_INLINE GPUBatch *DRW_batch_request(GPUBatch **batch)
return *batch;
}
-BLI_INLINE bool DRW_batch_requested(GPUBatch *batch, int prim_type)
+BLI_INLINE bool DRW_batch_requested(GPUBatch *batch, GPUPrimType prim_type)
{
/* Batch has been requested if it has been created but not initialized. */
if (batch != NULL && batch->verts[0] == NULL) {
/* HACK. We init without a valid VBO and let the first vbo binding
* fill verts[0]. */
- GPU_batch_init_ex(batch, prim_type, (GPUVertBuf *)1, NULL, 0);
+ GPU_batch_init_ex(batch, prim_type, (GPUVertBuf *)1, NULL, (eGPUBatchFlag)0);
batch->verts[0] = NULL;
return true;
}
diff --git a/source/blender/draw/intern/draw_hair.c b/source/blender/draw/intern/draw_hair.c
index bca227a24e2..585e171adc5 100644
--- a/source/blender/draw/intern/draw_hair.c
+++ b/source/blender/draw/intern/draw_hair.c
@@ -36,15 +36,28 @@
#include "BKE_duplilist.h"
#include "GPU_batch.h"
+#include "GPU_capabilities.h"
+#include "GPU_compute.h"
#include "GPU_shader.h"
+#include "GPU_texture.h"
#include "GPU_vertex_buffer.h"
#include "draw_hair_private.h"
#ifndef __APPLE__
# define USE_TRANSFORM_FEEDBACK
+# define USE_COMPUTE_SHADERS
#endif
+BLI_INLINE bool drw_hair_use_compute_shaders(void)
+{
+#ifdef USE_COMPUTE_SHADERS
+ return GPU_compute_shader_support();
+#else
+ return false;
+#endif
+}
+
typedef enum ParticleRefineShader {
PART_REFINE_CATMULL_ROM = 0,
PART_REFINE_MAX_SHADER,
@@ -71,38 +84,89 @@ static DRWPass *g_tf_pass; /* XXX can be a problem with multiple DRWManager in t
extern char datatoc_common_hair_lib_glsl[];
extern char datatoc_common_hair_refine_vert_glsl[];
+extern char datatoc_common_hair_refine_comp_glsl[];
extern char datatoc_gpu_shader_3D_smooth_color_frag_glsl[];
-static GPUShader *hair_refine_shader_get(ParticleRefineShader sh)
+/* TODO(jbakker): move shader creation to `draw_shaders` and add test cases. */
+/* TODO(jbakker): replace defines with `constexpr` to check compilation on all OS's.
+ * Currently the `__APPLE__` code-path does not compile on other platforms and vice versa. */
+#ifdef USE_COMPUTE_SHADERS
+static GPUShader *hair_refine_shader_compute_create(ParticleRefineShader UNUSED(refinement))
{
- if (g_refine_shaders[sh]) {
- return g_refine_shaders[sh];
- }
-
- char *vert_with_lib = BLI_string_joinN(datatoc_common_hair_lib_glsl,
- datatoc_common_hair_refine_vert_glsl);
+ GPUShader *sh = NULL;
+ sh = GPU_shader_create_compute(datatoc_common_hair_refine_comp_glsl,
+ datatoc_common_hair_lib_glsl,
+ "#define HAIR_PHASE_SUBDIV\n",
+ __func__);
+ return sh;
+}
+#endif
#ifdef USE_TRANSFORM_FEEDBACK
+static GPUShader *hair_refine_shader_transform_feedback_create(
+ ParticleRefineShader UNUSED(refinement))
+{
+ GPUShader *sh = NULL;
+
+ char *shader_src = BLI_string_joinN(datatoc_common_hair_lib_glsl,
+ datatoc_common_hair_refine_vert_glsl);
const char *var_names[1] = {"finalColor"};
- g_refine_shaders[sh] = DRW_shader_create_with_transform_feedback(
- vert_with_lib, NULL, "#define HAIR_PHASE_SUBDIV\n", GPU_SHADER_TFB_POINTS, var_names, 1);
-#else
- g_refine_shaders[sh] = DRW_shader_create(vert_with_lib,
- NULL,
- datatoc_gpu_shader_3D_smooth_color_frag_glsl,
- "#define blender_srgb_to_framebuffer_space(a) a\n"
- "#define HAIR_PHASE_SUBDIV\n"
- "#define TF_WORKAROUND\n");
+ sh = DRW_shader_create_with_transform_feedback(
+ shader_src, NULL, "#define HAIR_PHASE_SUBDIV\n", GPU_SHADER_TFB_POINTS, var_names, 1);
+ MEM_freeN(shader_src);
+
+ return sh;
+}
#endif
- MEM_freeN(vert_with_lib);
+static GPUShader *hair_refine_shader_transform_feedback_workaround_create(
+ ParticleRefineShader UNUSED(refinement))
+{
+ GPUShader *sh = NULL;
+
+ char *shader_src = BLI_string_joinN(datatoc_common_hair_lib_glsl,
+ datatoc_common_hair_refine_vert_glsl);
+ sh = DRW_shader_create(shader_src,
+ NULL,
+ datatoc_gpu_shader_3D_smooth_color_frag_glsl,
+ "#define blender_srgb_to_framebuffer_space(a) a\n"
+ "#define HAIR_PHASE_SUBDIV\n"
+ "#define TF_WORKAROUND\n");
+ MEM_freeN(shader_src);
+
+ return sh;
+}
+
+static GPUShader *hair_refine_shader_get(ParticleRefineShader refinement)
+{
+ if (g_refine_shaders[refinement]) {
+ return g_refine_shaders[refinement];
+ }
+
+#ifdef USE_COMPUTE_SHADERS
+ if (drw_hair_use_compute_shaders()) {
+ g_refine_shaders[refinement] = hair_refine_shader_compute_create(refinement);
+ if (g_refine_shaders[refinement]) {
+ return g_refine_shaders[refinement];
+ }
+ }
+#endif
+
+#ifdef USE_TRANSFORM_FEEDBACK
+ g_refine_shaders[refinement] = hair_refine_shader_transform_feedback_create(refinement);
+ if (g_refine_shaders[refinement]) {
+ return g_refine_shaders[refinement];
+ }
+#endif
- return g_refine_shaders[sh];
+ g_refine_shaders[refinement] = hair_refine_shader_transform_feedback_workaround_create(
+ refinement);
+ return g_refine_shaders[refinement];
}
void DRW_hair_init(void)
{
-#ifdef USE_TRANSFORM_FEEDBACK
+#if defined(USE_TRANSFORM_FEEDBACK) || defined(USE_COMPUTE_SHADERS)
g_tf_pass = DRW_pass_create("Update Hair Pass", 0);
#else
g_tf_pass = DRW_pass_create("Update Hair Pass", DRW_STATE_WRITE_COLOR);
@@ -125,6 +189,67 @@ void DRW_hair_init(void)
}
}
+static void drw_hair_particle_cache_shgrp_attach_resources(DRWShadingGroup *shgrp,
+ ParticleHairCache *cache,
+ const int subdiv)
+{
+ DRW_shgroup_uniform_texture(shgrp, "hairPointBuffer", cache->point_tex);
+ DRW_shgroup_uniform_texture(shgrp, "hairStrandBuffer", cache->strand_tex);
+ DRW_shgroup_uniform_texture(shgrp, "hairStrandSegBuffer", cache->strand_seg_tex);
+ DRW_shgroup_uniform_int(shgrp, "hairStrandsRes", &cache->final[subdiv].strands_res, 1);
+}
+
+static void drw_hair_particle_cache_update_compute(ParticleHairCache *cache, const int subdiv)
+{
+ const int strands_len = cache->strands_len;
+ const int final_points_len = cache->final[subdiv].strands_res * strands_len;
+ if (final_points_len > 0) {
+ GPUShader *shader = hair_refine_shader_get(PART_REFINE_CATMULL_ROM);
+ DRWShadingGroup *shgrp = DRW_shgroup_create(shader, g_tf_pass);
+ drw_hair_particle_cache_shgrp_attach_resources(shgrp, cache, subdiv);
+ DRW_shgroup_vertex_buffer(shgrp, "hairPointOutputBuffer", cache->final[subdiv].proc_buf);
+
+ const int max_strands_per_call = GPU_max_work_group_count(0);
+ int strands_start = 0;
+ while (strands_start < strands_len) {
+ int batch_strands_len = MIN2(strands_len - strands_start, max_strands_per_call);
+ DRWShadingGroup *subgroup = DRW_shgroup_create_sub(shgrp);
+ DRW_shgroup_uniform_int_copy(subgroup, "hairStrandOffset", strands_start);
+ DRW_shgroup_call_compute(subgroup, batch_strands_len, cache->final[subdiv].strands_res, 1);
+ strands_start += batch_strands_len;
+ }
+ }
+}
+
+static void drw_hair_particle_cache_update_transform_feedback(ParticleHairCache *cache,
+ const int subdiv)
+{
+ const int final_points_len = cache->final[subdiv].strands_res * cache->strands_len;
+ if (final_points_len > 0) {
+ GPUShader *tf_shader = hair_refine_shader_get(PART_REFINE_CATMULL_ROM);
+
+#ifdef USE_TRANSFORM_FEEDBACK
+ DRWShadingGroup *tf_shgrp = DRW_shgroup_transform_feedback_create(
+ tf_shader, g_tf_pass, cache->final[subdiv].proc_buf);
+#else
+ DRWShadingGroup *tf_shgrp = DRW_shgroup_create(tf_shader, g_tf_pass);
+
+ ParticleRefineCall *pr_call = MEM_mallocN(sizeof(*pr_call), __func__);
+ pr_call->next = g_tf_calls;
+ pr_call->vbo = cache->final[subdiv].proc_buf;
+ pr_call->shgrp = tf_shgrp;
+ pr_call->vert_len = final_points_len;
+ g_tf_calls = pr_call;
+ DRW_shgroup_uniform_int(tf_shgrp, "targetHeight", &g_tf_target_height, 1);
+ DRW_shgroup_uniform_int(tf_shgrp, "targetWidth", &g_tf_target_width, 1);
+ DRW_shgroup_uniform_int(tf_shgrp, "idOffset", &g_tf_id_offset, 1);
+#endif
+
+ drw_hair_particle_cache_shgrp_attach_resources(tf_shgrp, cache, subdiv);
+ DRW_shgroup_call_procedural_points(tf_shgrp, NULL, final_points_len);
+ }
+}
+
static ParticleHairCache *drw_hair_particle_cache_get(
Object *object, ParticleSystem *psys, ModifierData *md, int subdiv, int thickness_res)
{
@@ -140,32 +265,11 @@ static ParticleHairCache *drw_hair_particle_cache_get(
}
if (update) {
- int final_points_len = cache->final[subdiv].strands_res * cache->strands_len;
- if (final_points_len > 0) {
- GPUShader *tf_shader = hair_refine_shader_get(PART_REFINE_CATMULL_ROM);
-
-#ifdef USE_TRANSFORM_FEEDBACK
- DRWShadingGroup *tf_shgrp = DRW_shgroup_transform_feedback_create(
- tf_shader, g_tf_pass, cache->final[subdiv].proc_buf);
-#else
- DRWShadingGroup *tf_shgrp = DRW_shgroup_create(tf_shader, g_tf_pass);
-
- ParticleRefineCall *pr_call = MEM_mallocN(sizeof(*pr_call), __func__);
- pr_call->next = g_tf_calls;
- pr_call->vbo = cache->final[subdiv].proc_buf;
- pr_call->shgrp = tf_shgrp;
- pr_call->vert_len = final_points_len;
- g_tf_calls = pr_call;
- DRW_shgroup_uniform_int(tf_shgrp, "targetHeight", &g_tf_target_height, 1);
- DRW_shgroup_uniform_int(tf_shgrp, "targetWidth", &g_tf_target_width, 1);
- DRW_shgroup_uniform_int(tf_shgrp, "idOffset", &g_tf_id_offset, 1);
-#endif
-
- DRW_shgroup_uniform_texture(tf_shgrp, "hairPointBuffer", cache->point_tex);
- DRW_shgroup_uniform_texture(tf_shgrp, "hairStrandBuffer", cache->strand_tex);
- DRW_shgroup_uniform_texture(tf_shgrp, "hairStrandSegBuffer", cache->strand_seg_tex);
- DRW_shgroup_uniform_int(tf_shgrp, "hairStrandsRes", &cache->final[subdiv].strands_res, 1);
- DRW_shgroup_call_procedural_points(tf_shgrp, NULL, final_points_len);
+ if (drw_hair_use_compute_shaders()) {
+ drw_hair_particle_cache_update_compute(cache, subdiv);
+ }
+ else {
+ drw_hair_particle_cache_update_transform_feedback(cache, subdiv);
}
}
return cache;
@@ -367,9 +471,11 @@ void DRW_hair_update(void)
MEM_freeN(data);
GPU_framebuffer_free(fb);
#else
- /* TODO(fclem): replace by compute shader. */
- /* Just render using transform feedback. */
+ /* Just render the pass when using compute shaders or transform feedback. */
DRW_draw_pass(g_tf_pass);
+ if (drw_hair_use_compute_shaders()) {
+ GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE);
+ }
#endif
}
diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c
index a088c27d3f3..37f6bbf52b5 100644
--- a/source/blender/draw/intern/draw_manager.c
+++ b/source/blender/draw/intern/draw_manager.c
@@ -1473,6 +1473,14 @@ void DRW_draw_callbacks_post_scene(void)
GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
}
+ else {
+ if (v3d && ((v3d->flag2 & V3D_SHOW_ANNOTATION) != 0)) {
+ GPU_depth_test(GPU_DEPTH_NONE);
+ /* XXX: as scene->gpd is not copied for COW yet */
+ ED_annotation_draw_view3d(DEG_get_input_scene(depsgraph), depsgraph, v3d, region, true);
+ GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
+ }
+ }
}
struct DRWTextStore *DRW_text_cache_ensure(void)
@@ -2601,8 +2609,7 @@ static void drw_draw_depth_loop_impl(struct Depsgraph *depsgraph,
void DRW_draw_depth_loop(struct Depsgraph *depsgraph,
ARegion *region,
View3D *v3d,
- GPUViewport *viewport,
- bool use_opengl_context)
+ GPUViewport *viewport)
{
/* Reset before using it. */
drw_state_prepare_clean_for_draw(&DST);
@@ -2618,7 +2625,7 @@ void DRW_draw_depth_loop(struct Depsgraph *depsgraph,
}
}
- drw_draw_depth_loop_impl(depsgraph, region, v3d, viewport, use_opengl_context);
+ drw_draw_depth_loop_impl(depsgraph, region, v3d, viewport, false);
}
/**
@@ -2634,7 +2641,7 @@ void DRW_draw_depth_loop_gpencil(struct Depsgraph *depsgraph,
use_drw_engine(&draw_engine_gpencil_type);
- drw_draw_depth_loop_impl(depsgraph, region, v3d, viewport, true);
+ drw_draw_depth_loop_impl(depsgraph, region, v3d, viewport, false);
}
void DRW_draw_select_id(Depsgraph *depsgraph, ARegion *region, View3D *v3d, const rcti *rect)
@@ -2725,7 +2732,6 @@ void DRW_draw_depth_object(
{
RegionView3D *rv3d = region->regiondata;
- DRW_opengl_context_enable();
GPU_matrix_projection_set(rv3d->winmat);
GPU_matrix_set(rv3d->viewmat);
GPU_matrix_mul(object->obmat);
@@ -2784,7 +2790,6 @@ void DRW_draw_depth_object(
GPU_matrix_set(rv3d->viewmat);
GPU_depth_test(GPU_DEPTH_NONE);
GPU_framebuffer_restore();
- DRW_opengl_context_disable();
}
/** \} */
diff --git a/source/blender/draw/intern/draw_manager.h b/source/blender/draw/intern/draw_manager.h
index 84bc0327aa2..d4e22c83798 100644
--- a/source/blender/draw/intern/draw_manager.h
+++ b/source/blender/draw/intern/draw_manager.h
@@ -187,6 +187,10 @@ typedef enum {
DRW_CMD_DRAW_INSTANCE = 2,
DRW_CMD_DRAW_INSTANCE_RANGE = 3,
DRW_CMD_DRAW_PROCEDURAL = 4,
+
+ /* Compute Commands. */
+ DRW_CMD_COMPUTE = 8,
+
/* Other Commands */
DRW_CMD_CLEAR = 12,
DRW_CMD_DRWSTATE = 13,
@@ -224,6 +228,12 @@ typedef struct DRWCommandDrawInstanceRange {
uint inst_count;
} DRWCommandDrawInstanceRange;
+typedef struct DRWCommandCompute {
+ int groups_x_len;
+ int groups_y_len;
+ int groups_z_len;
+} DRWCommandCompute;
+
typedef struct DRWCommandDrawProcedural {
GPUBatch *batch;
DRWResourceHandle handle;
@@ -260,6 +270,7 @@ typedef union DRWCommand {
DRWCommandDrawInstance instance;
DRWCommandDrawInstanceRange instance_range;
DRWCommandDrawProcedural procedural;
+ DRWCommandCompute compute;
DRWCommandSetMutableState state;
DRWCommandSetStencil stencil;
DRWCommandSetSelectID select_id;
@@ -274,6 +285,7 @@ struct DRWCallBuffer {
};
/** Used by #DRWUniform.type */
+/* TODO(jbakker): rename to DRW_RESOURCE/DRWResourceType. */
typedef enum {
DRW_UNIFORM_INT = 0,
DRW_UNIFORM_INT_COPY,
@@ -286,6 +298,7 @@ typedef enum {
DRW_UNIFORM_BLOCK,
DRW_UNIFORM_BLOCK_REF,
DRW_UNIFORM_TFEEDBACK_TARGET,
+ DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE,
/** Per drawcall uniforms/UBO */
DRW_UNIFORM_BLOCK_OBMATS,
DRW_UNIFORM_BLOCK_OBINFOS,
diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c
index 6bdc5305fed..3b852e7f8c8 100644
--- a/source/blender/draw/intern/draw_manager_data.c
+++ b/source/blender/draw/intern/draw_manager_data.c
@@ -47,6 +47,7 @@
#endif
#include "GPU_buffers.h"
+#include "GPU_capabilities.h"
#include "GPU_material.h"
#include "GPU_uniform_buffer.h"
@@ -446,6 +447,19 @@ void DRW_shgroup_uniform_vec4_array_copy(DRWShadingGroup *shgroup,
}
}
+void DRW_shgroup_vertex_buffer(DRWShadingGroup *shgroup,
+ const char *name,
+ GPUVertBuf *vertex_buffer)
+{
+ int location = GPU_shader_get_ssbo(shgroup->shader, name);
+ if (location == -1) {
+ BLI_assert(false && "Unable to locate binding of shader storage buffer objects.");
+ return;
+ }
+ drw_shgroup_uniform_create_ex(
+ shgroup, location, DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE, vertex_buffer, 0, 0, 1);
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -700,6 +714,17 @@ static void drw_command_draw_intance_range(
cmd->inst_count = count;
}
+static void drw_command_compute(DRWShadingGroup *shgroup,
+ int groups_x_len,
+ int groups_y_len,
+ int groups_z_len)
+{
+ DRWCommandCompute *cmd = drw_command_create(shgroup, DRW_CMD_COMPUTE);
+ cmd->groups_x_len = groups_x_len;
+ cmd->groups_y_len = groups_y_len;
+ cmd->groups_z_len = groups_z_len;
+}
+
static void drw_command_draw_procedural(DRWShadingGroup *shgroup,
GPUBatch *batch,
DRWResourceHandle handle,
@@ -815,6 +840,17 @@ void DRW_shgroup_call_instance_range(
drw_command_draw_intance_range(shgroup, geom, handle, i_sta, i_ct);
}
+void DRW_shgroup_call_compute(DRWShadingGroup *shgroup,
+ int groups_x_len,
+ int groups_y_len,
+ int groups_z_len)
+{
+ BLI_assert(groups_x_len > 0 && groups_y_len > 0 && groups_z_len > 0);
+ BLI_assert(GPU_compute_shader_support());
+
+ drw_command_compute(shgroup, groups_x_len, groups_y_len, groups_z_len);
+}
+
static void drw_shgroup_call_procedural_add_ex(DRWShadingGroup *shgroup,
GPUBatch *geom,
Object *ob,
diff --git a/source/blender/draw/intern/draw_manager_exec.c b/source/blender/draw/intern/draw_manager_exec.c
index 4c8fcb0e016..f29caebeb84 100644
--- a/source/blender/draw/intern/draw_manager_exec.c
+++ b/source/blender/draw/intern/draw_manager_exec.c
@@ -29,6 +29,7 @@
#include "BKE_global.h"
+#include "GPU_compute.h"
#include "GPU_platform.h"
#include "GPU_shader.h"
#include "GPU_state.h"
@@ -672,6 +673,9 @@ static void draw_update_uniforms(DRWShadingGroup *shgroup,
*use_tfeedback = GPU_shader_transform_feedback_enable(shgroup->shader,
((GPUVertBuf *)uni->pvalue));
break;
+ case DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE:
+ GPU_vertbuf_bind_as_ssbo((GPUVertBuf *)uni->pvalue, uni->location);
+ break;
/* Legacy/Fallback support. */
case DRW_UNIFORM_BASE_INSTANCE:
state->baseinst_loc = uni->location;
@@ -1050,6 +1054,12 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state)
cmd->instance_range.inst_count,
false);
break;
+ case DRW_CMD_COMPUTE:
+ GPU_compute_dispatch(shgroup->shader,
+ cmd->compute.groups_x_len,
+ cmd->compute.groups_y_len,
+ cmd->compute.groups_z_len);
+ break;
}
}
diff --git a/source/blender/draw/intern/draw_manager_profiling.c b/source/blender/draw/intern/draw_manager_profiling.c
index 9bfc8d98fe4..783ec1b1d7d 100644
--- a/source/blender/draw/intern/draw_manager_profiling.c
+++ b/source/blender/draw/intern/draw_manager_profiling.c
@@ -41,7 +41,7 @@
#define MAX_TIMER_NAME 32
#define MAX_NESTED_TIMER 8
-#define CHUNK_SIZE 8
+#define MIM_RANGE_LEN 8
#define GPU_TIMER_FALLOFF 0.1
typedef struct DRWTimer {
@@ -82,7 +82,7 @@ void DRW_stats_begin(void)
if (DTP.is_recording && DTP.timers == NULL) {
DTP.chunk_count = 1;
- DTP.timer_count = DTP.chunk_count * CHUNK_SIZE;
+ DTP.timer_count = DTP.chunk_count * MIM_RANGE_LEN;
DTP.timers = MEM_callocN(sizeof(DRWTimer) * DTP.timer_count, "DRWTimer stack");
}
else if (!DTP.is_recording && DTP.timers != NULL) {
@@ -99,7 +99,7 @@ static DRWTimer *drw_stats_timer_get(void)
if (UNLIKELY(DTP.timer_increment >= DTP.timer_count)) {
/* Resize the stack. */
DTP.chunk_count++;
- DTP.timer_count = DTP.chunk_count * CHUNK_SIZE;
+ DTP.timer_count = DTP.chunk_count * MIM_RANGE_LEN;
DTP.timers = MEM_recallocN(DTP.timers, sizeof(DRWTimer) * DTP.timer_count);
}
diff --git a/source/blender/draw/intern/draw_manager_shader.c b/source/blender/draw/intern/draw_manager_shader.c
index c93cbf16a30..83d0030f89b 100644
--- a/source/blender/draw/intern/draw_manager_shader.c
+++ b/source/blender/draw/intern/draw_manager_shader.c
@@ -396,6 +396,7 @@ GPUShader *DRW_shader_create_with_transform_feedback(const char *vert,
datatoc_gpu_shader_depth_only_frag_glsl,
geom,
NULL,
+ NULL,
defines,
prim_type,
varying_names,
@@ -606,10 +607,10 @@ static uint32_t drw_shader_dependencies_get(const DRWShaderLibrary *lib, const c
haystack += 16;
int dep = drw_shader_library_search(lib, haystack);
if (dep == -1) {
- char dbg_name[32];
+ char dbg_name[33];
int i = 0;
- while ((haystack[0] != ')') && (i < 31)) {
- dbg_name[i] = haystack[0];
+ while ((*haystack != ')') && (i < (sizeof(dbg_name) - 2))) {
+ dbg_name[i] = *haystack;
haystack++;
i++;
}
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc
new file mode 100644
index 00000000000..565f8834ab1
--- /dev/null
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc
@@ -0,0 +1,392 @@
+/*
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2021 by Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup draw
+ */
+
+#include "draw_cache_extract_mesh_private.h"
+
+#include "BLI_vector.hh"
+
+#include "MEM_guardedalloc.h"
+
+namespace blender::draw {
+/* ---------------------------------------------------------------------- */
+/** \name Extract Edit UV Triangles Indices
+ * \{ */
+
+struct MeshExtract_EditUvElem_Data {
+ GPUIndexBufBuilder elb;
+ bool sync_selection;
+};
+
+static void extract_edituv_tris_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *UNUSED(ibo),
+ void *tls_data)
+{
+ MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(tls_data);
+ GPU_indexbuf_init(&data->elb, GPU_PRIM_TRIS, mr->tri_len, mr->loop_len);
+ data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0;
+}
+
+BLI_INLINE void edituv_tri_add(
+ MeshExtract_EditUvElem_Data *data, bool hidden, bool selected, int v1, int v2, int v3)
+{
+ if (!hidden && (data->sync_selection || selected)) {
+ GPU_indexbuf_add_tri_verts(&data->elb, v1, v2, v3);
+ }
+}
+
+static void extract_edituv_tris_iter_looptri_bm(const MeshRenderData *UNUSED(mr),
+ BMLoop **elt,
+ const int UNUSED(elt_index),
+ void *_data)
+{
+ MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data);
+ edituv_tri_add(data,
+ BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN),
+ BM_elem_flag_test(elt[0]->f, BM_ELEM_SELECT),
+ BM_elem_index_get(elt[0]),
+ BM_elem_index_get(elt[1]),
+ BM_elem_index_get(elt[2]));
+}
+
+static void extract_edituv_tris_iter_looptri_mesh(const MeshRenderData *mr,
+ const MLoopTri *mlt,
+ const int UNUSED(elt_index),
+ void *_data)
+{
+ MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data);
+ const MPoly *mp = &mr->mpoly[mlt->poly];
+ edituv_tri_add(data,
+ (mp->flag & ME_HIDE) != 0,
+ (mp->flag & ME_FACE_SEL) != 0,
+ mlt->tri[0],
+ mlt->tri[1],
+ mlt->tri[2]);
+}
+
+static void extract_edituv_tris_finish(const MeshRenderData *UNUSED(mr),
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *_data)
+{
+ MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data);
+ GPUIndexBuf *ibo = static_cast<GPUIndexBuf *>(buf);
+ GPU_indexbuf_build_in_place(&data->elb, ibo);
+}
+
+constexpr MeshExtract create_extractor_edituv_tris()
+{
+ MeshExtract extractor = {0};
+ extractor.init = extract_edituv_tris_init;
+ extractor.iter_looptri_bm = extract_edituv_tris_iter_looptri_bm;
+ extractor.iter_looptri_mesh = extract_edituv_tris_iter_looptri_mesh;
+ extractor.finish = extract_edituv_tris_finish;
+ extractor.data_type = MR_DATA_NONE;
+ extractor.data_size = sizeof(MeshExtract_EditUvElem_Data);
+ extractor.use_threading = false;
+ extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_tris);
+ return extractor;
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Edit UV Line Indices around faces
+ * \{ */
+
+static void extract_edituv_lines_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *UNUSED(ibo),
+ void *tls_data)
+{
+ MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(tls_data);
+ GPU_indexbuf_init(&data->elb, GPU_PRIM_LINES, mr->loop_len, mr->loop_len);
+ data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0;
+}
+
+BLI_INLINE void edituv_edge_add(
+ MeshExtract_EditUvElem_Data *data, bool hidden, bool selected, int v1, int v2)
+{
+ if (!hidden && (data->sync_selection || selected)) {
+ GPU_indexbuf_add_line_verts(&data->elb, v1, v2);
+ }
+}
+
+static void extract_edituv_lines_iter_poly_bm(const MeshRenderData *UNUSED(mr),
+ const BMFace *f,
+ const int UNUSED(f_index),
+ void *_data)
+{
+ MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data);
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ const int l_index = BM_elem_index_get(l_iter);
+
+ edituv_edge_add(data,
+ BM_elem_flag_test_bool(f, BM_ELEM_HIDDEN),
+ BM_elem_flag_test_bool(f, BM_ELEM_SELECT),
+ l_index,
+ BM_elem_index_get(l_iter->next));
+ } while ((l_iter = l_iter->next) != l_first);
+}
+
+static void extract_edituv_lines_iter_poly_mesh(const MeshRenderData *mr,
+ const MPoly *mp,
+ const int UNUSED(mp_index),
+ void *_data)
+{
+ MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data);
+ const MLoop *mloop = mr->mloop;
+ const int ml_index_end = mp->loopstart + mp->totloop;
+ for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
+ const MLoop *ml = &mloop[ml_index];
+
+ const int ml_index_last = mp->totloop + mp->loopstart - 1;
+ const int ml_index_next = (ml_index == ml_index_last) ? mp->loopstart : (ml_index + 1);
+ const bool real_edge = (mr->e_origindex == nullptr ||
+ mr->e_origindex[ml->e] != ORIGINDEX_NONE);
+ edituv_edge_add(data,
+ (mp->flag & ME_HIDE) != 0 || !real_edge,
+ (mp->flag & ME_FACE_SEL) != 0,
+ ml_index,
+ ml_index_next);
+ }
+}
+
+static void extract_edituv_lines_finish(const MeshRenderData *UNUSED(mr),
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *_data)
+{
+ MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data);
+ GPUIndexBuf *ibo = static_cast<GPUIndexBuf *>(buf);
+ GPU_indexbuf_build_in_place(&data->elb, ibo);
+}
+
+constexpr MeshExtract create_extractor_edituv_lines()
+{
+ MeshExtract extractor = {0};
+ extractor.init = extract_edituv_lines_init;
+ extractor.iter_poly_bm = extract_edituv_lines_iter_poly_bm;
+ extractor.iter_poly_mesh = extract_edituv_lines_iter_poly_mesh;
+ extractor.finish = extract_edituv_lines_finish;
+ extractor.data_type = MR_DATA_NONE;
+ extractor.data_size = sizeof(MeshExtract_EditUvElem_Data);
+ extractor.use_threading = false;
+ extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_lines);
+ return extractor;
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Edit UV Points Indices
+ * \{ */
+
+static void extract_edituv_points_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *UNUSED(ibo),
+ void *tls_data)
+{
+ MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(tls_data);
+ GPU_indexbuf_init(&data->elb, GPU_PRIM_POINTS, mr->loop_len, mr->loop_len);
+ data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0;
+}
+
+BLI_INLINE void edituv_point_add(MeshExtract_EditUvElem_Data *data,
+ bool hidden,
+ bool selected,
+ int v1)
+{
+ if (!hidden && (data->sync_selection || selected)) {
+ GPU_indexbuf_add_point_vert(&data->elb, v1);
+ }
+}
+
+static void extract_edituv_points_iter_poly_bm(const MeshRenderData *UNUSED(mr),
+ const BMFace *f,
+ const int UNUSED(f_index),
+ void *_data)
+{
+ MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data);
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ const int l_index = BM_elem_index_get(l_iter);
+
+ edituv_point_add(
+ data, BM_elem_flag_test(f, BM_ELEM_HIDDEN), BM_elem_flag_test(f, BM_ELEM_SELECT), l_index);
+ } while ((l_iter = l_iter->next) != l_first);
+}
+
+static void extract_edituv_points_iter_poly_mesh(const MeshRenderData *mr,
+ const MPoly *mp,
+ const int UNUSED(mp_index),
+ void *_data)
+{
+ MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data);
+ const MLoop *mloop = mr->mloop;
+ const int ml_index_end = mp->loopstart + mp->totloop;
+ for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
+ const MLoop *ml = &mloop[ml_index];
+
+ const bool real_vert = (mr->extract_type == MR_EXTRACT_MAPPED && (mr->v_origindex) &&
+ mr->v_origindex[ml->v] != ORIGINDEX_NONE);
+ edituv_point_add(
+ data, ((mp->flag & ME_HIDE) != 0) || !real_vert, (mp->flag & ME_FACE_SEL) != 0, ml_index);
+ }
+}
+
+static void extract_edituv_points_finish(const MeshRenderData *UNUSED(mr),
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *_data)
+{
+ MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data);
+ GPUIndexBuf *ibo = static_cast<GPUIndexBuf *>(buf);
+ GPU_indexbuf_build_in_place(&data->elb, ibo);
+}
+
+constexpr MeshExtract create_extractor_edituv_points()
+{
+ MeshExtract extractor = {0};
+ extractor.init = extract_edituv_points_init;
+ extractor.iter_poly_bm = extract_edituv_points_iter_poly_bm;
+ extractor.iter_poly_mesh = extract_edituv_points_iter_poly_mesh;
+ extractor.finish = extract_edituv_points_finish;
+ extractor.data_type = MR_DATA_NONE;
+ extractor.data_size = sizeof(MeshExtract_EditUvElem_Data);
+ extractor.use_threading = false;
+ extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_points);
+ return extractor;
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Edit UV Face-dots Indices
+ * \{ */
+
+static void extract_edituv_fdots_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *UNUSED(ibo),
+ void *tls_data)
+{
+ MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(tls_data);
+ GPU_indexbuf_init(&data->elb, GPU_PRIM_POINTS, mr->poly_len, mr->poly_len);
+ data->sync_selection = (mr->toolsettings->uv_flag & UV_SYNC_SELECTION) != 0;
+}
+
+BLI_INLINE void edituv_facedot_add(MeshExtract_EditUvElem_Data *data,
+ bool hidden,
+ bool selected,
+ int face_index)
+{
+ if (!hidden && (data->sync_selection || selected)) {
+ GPU_indexbuf_set_point_vert(&data->elb, face_index, face_index);
+ }
+ else {
+ GPU_indexbuf_set_point_restart(&data->elb, face_index);
+ }
+}
+
+static void extract_edituv_fdots_iter_poly_bm(const MeshRenderData *UNUSED(mr),
+ const BMFace *f,
+ const int f_index,
+ void *_data)
+{
+ MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data);
+ edituv_facedot_add(data,
+ BM_elem_flag_test_bool(f, BM_ELEM_HIDDEN),
+ BM_elem_flag_test_bool(f, BM_ELEM_SELECT),
+ f_index);
+}
+
+static void extract_edituv_fdots_iter_poly_mesh(const MeshRenderData *mr,
+ const MPoly *mp,
+ const int mp_index,
+ void *_data)
+{
+ MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data);
+ if (mr->use_subsurf_fdots) {
+ /* Check #ME_VERT_FACEDOT. */
+ const MLoop *mloop = mr->mloop;
+ const int ml_index_end = mp->loopstart + mp->totloop;
+ for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
+ const MLoop *ml = &mloop[ml_index];
+
+ const bool real_fdot = (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex &&
+ mr->p_origindex[mp_index] != ORIGINDEX_NONE);
+ const bool subd_fdot = (!mr->use_subsurf_fdots ||
+ (mr->mvert[ml->v].flag & ME_VERT_FACEDOT) != 0);
+ edituv_facedot_add(data,
+ ((mp->flag & ME_HIDE) != 0) || !real_fdot || !subd_fdot,
+ (mp->flag & ME_FACE_SEL) != 0,
+ mp_index);
+ }
+ }
+ else {
+ const bool real_fdot = (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex &&
+ mr->p_origindex[mp_index] != ORIGINDEX_NONE);
+ edituv_facedot_add(
+ data, ((mp->flag & ME_HIDE) != 0) || !real_fdot, (mp->flag & ME_FACE_SEL) != 0, mp_index);
+ }
+}
+
+static void extract_edituv_fdots_finish(const MeshRenderData *UNUSED(mr),
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *_data)
+{
+ MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data);
+ GPUIndexBuf *ibo = static_cast<GPUIndexBuf *>(buf);
+ GPU_indexbuf_build_in_place(&data->elb, ibo);
+}
+
+constexpr MeshExtract create_extractor_edituv_fdots()
+{
+ MeshExtract extractor = {0};
+ extractor.init = extract_edituv_fdots_init;
+ extractor.iter_poly_bm = extract_edituv_fdots_iter_poly_bm;
+ extractor.iter_poly_mesh = extract_edituv_fdots_iter_poly_mesh;
+ extractor.finish = extract_edituv_fdots_finish;
+ extractor.data_type = MR_DATA_NONE;
+ extractor.data_size = sizeof(MeshExtract_EditUvElem_Data);
+ extractor.use_threading = false;
+ extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_fdots);
+ return extractor;
+}
+
+/** \} */
+
+} // namespace blender::draw
+
+extern "C" {
+const MeshExtract extract_edituv_tris = blender::draw::create_extractor_edituv_tris();
+const MeshExtract extract_edituv_lines = blender::draw::create_extractor_edituv_lines();
+const MeshExtract extract_edituv_points = blender::draw::create_extractor_edituv_points();
+const MeshExtract extract_edituv_fdots = blender::draw::create_extractor_edituv_fdots();
+}
+
+/** \} */
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc
new file mode 100644
index 00000000000..1d1a000061d
--- /dev/null
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2021 by Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup draw
+ */
+
+#include "draw_cache_extract_mesh_private.h"
+
+#include "BLI_vector.hh"
+
+#include "MEM_guardedalloc.h"
+
+namespace blender::draw {
+/* ---------------------------------------------------------------------- */
+/** \name Extract Face-dots Indices
+ * \{ */
+
+static void extract_fdots_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *UNUSED(buf),
+ void *tls_data)
+{
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(tls_data);
+ GPU_indexbuf_init(elb, GPU_PRIM_POINTS, mr->poly_len, mr->poly_len);
+}
+
+static void extract_fdots_iter_poly_bm(const MeshRenderData *UNUSED(mr),
+ const BMFace *f,
+ const int f_index,
+ void *_userdata)
+{
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_userdata);
+ if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
+ GPU_indexbuf_set_point_vert(elb, f_index, f_index);
+ }
+ else {
+ GPU_indexbuf_set_point_restart(elb, f_index);
+ }
+}
+
+static void extract_fdots_iter_poly_mesh(const MeshRenderData *mr,
+ const MPoly *mp,
+ const int mp_index,
+ void *_userdata)
+{
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_userdata);
+ if (mr->use_subsurf_fdots) {
+ /* Check #ME_VERT_FACEDOT. */
+ const MLoop *mloop = mr->mloop;
+ const int ml_index_end = mp->loopstart + mp->totloop;
+ for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
+ const MLoop *ml = &mloop[ml_index];
+ const MVert *mv = &mr->mvert[ml->v];
+ if ((mv->flag & ME_VERT_FACEDOT) && !(mr->use_hide && (mp->flag & ME_HIDE))) {
+ GPU_indexbuf_set_point_vert(elb, mp_index, mp_index);
+ return;
+ }
+ }
+ GPU_indexbuf_set_point_restart(elb, mp_index);
+ }
+ else {
+ if (!(mr->use_hide && (mp->flag & ME_HIDE))) {
+ GPU_indexbuf_set_point_vert(elb, mp_index, mp_index);
+ }
+ else {
+ GPU_indexbuf_set_point_restart(elb, mp_index);
+ }
+ }
+}
+
+static void extract_fdots_finish(const MeshRenderData *UNUSED(mr),
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *_userdata)
+{
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_userdata);
+ GPUIndexBuf *ibo = static_cast<GPUIndexBuf *>(buf);
+ GPU_indexbuf_build_in_place(elb, ibo);
+}
+
+constexpr MeshExtract create_extractor_fdots()
+{
+ MeshExtract extractor = {0};
+ extractor.init = extract_fdots_init;
+ extractor.iter_poly_bm = extract_fdots_iter_poly_bm;
+ extractor.iter_poly_mesh = extract_fdots_iter_poly_mesh;
+ extractor.finish = extract_fdots_finish;
+ extractor.data_type = MR_DATA_NONE;
+ extractor.data_size = sizeof(GPUIndexBufBuilder);
+ extractor.use_threading = false;
+ extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.fdots);
+ return extractor;
+}
+
+/** \} */
+} // namespace blender::draw
+
+extern "C" {
+const MeshExtract extract_fdots = blender::draw::create_extractor_fdots();
+}
+
+/** \} */
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc
new file mode 100644
index 00000000000..64aaed6600f
--- /dev/null
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc
@@ -0,0 +1,256 @@
+/*
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2021 by Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup draw
+ */
+
+#include "draw_cache_extract_mesh_private.h"
+
+#include "MEM_guardedalloc.h"
+
+namespace blender::draw {
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Edges Indices
+ * \{ */
+
+static void extract_lines_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *UNUSED(buf),
+ void *tls_data)
+{
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(tls_data);
+ /* Put loose edges at the end. */
+ GPU_indexbuf_init(
+ elb, GPU_PRIM_LINES, mr->edge_len + mr->edge_loose_len, mr->loop_len + mr->loop_loose_len);
+}
+
+static void extract_lines_iter_poly_bm(const MeshRenderData *UNUSED(mr),
+ const BMFace *f,
+ const int UNUSED(f_index),
+ void *data)
+{
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(data);
+ BMLoop *l_iter, *l_first;
+ /* Use #BMLoop.prev to match mesh order (to avoid minor differences in data extraction). */
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f)->prev;
+ do {
+ if (!BM_elem_flag_test(l_iter->e, BM_ELEM_HIDDEN)) {
+ GPU_indexbuf_set_line_verts(elb,
+ BM_elem_index_get(l_iter->e),
+ BM_elem_index_get(l_iter),
+ BM_elem_index_get(l_iter->next));
+ }
+ else {
+ GPU_indexbuf_set_line_restart(elb, BM_elem_index_get(l_iter->e));
+ }
+ } while ((l_iter = l_iter->next) != l_first);
+}
+
+static void extract_lines_iter_poly_mesh(const MeshRenderData *mr,
+ const MPoly *mp,
+ const int UNUSED(mp_index),
+ void *data)
+{
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(data);
+ /* Using poly & loop iterator would complicate accessing the adjacent loop. */
+ const MLoop *mloop = mr->mloop;
+ const MEdge *medge = mr->medge;
+ if (mr->use_hide || (mr->extract_type == MR_EXTRACT_MAPPED) || (mr->e_origindex != nullptr)) {
+ const int ml_index_last = mp->loopstart + (mp->totloop - 1);
+ int ml_index = ml_index_last, ml_index_next = mp->loopstart;
+ do {
+ const MLoop *ml = &mloop[ml_index];
+ const MEdge *med = &medge[ml->e];
+ if (!((mr->use_hide && (med->flag & ME_HIDE)) ||
+ ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) &&
+ (mr->e_origindex[ml->e] == ORIGINDEX_NONE)))) {
+ GPU_indexbuf_set_line_verts(elb, ml->e, ml_index, ml_index_next);
+ }
+ else {
+ GPU_indexbuf_set_line_restart(elb, ml->e);
+ }
+ } while ((ml_index = ml_index_next++) != ml_index_last);
+ }
+ else {
+ const int ml_index_last = mp->loopstart + (mp->totloop - 1);
+ int ml_index = ml_index_last, ml_index_next = mp->loopstart;
+ do {
+ const MLoop *ml = &mloop[ml_index];
+ GPU_indexbuf_set_line_verts(elb, ml->e, ml_index, ml_index_next);
+ } while ((ml_index = ml_index_next++) != ml_index_last);
+ }
+}
+
+static void extract_lines_iter_ledge_bm(const MeshRenderData *mr,
+ const BMEdge *eed,
+ const int ledge_index,
+ void *data)
+{
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(data);
+ const int l_index_offset = mr->edge_len + ledge_index;
+ if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) {
+ const int l_index = mr->loop_len + ledge_index * 2;
+ GPU_indexbuf_set_line_verts(elb, l_index_offset, l_index, l_index + 1);
+ }
+ else {
+ GPU_indexbuf_set_line_restart(elb, l_index_offset);
+ }
+ /* Don't render the edge twice. */
+ GPU_indexbuf_set_line_restart(elb, BM_elem_index_get(eed));
+}
+
+static void extract_lines_iter_ledge_mesh(const MeshRenderData *mr,
+ const MEdge *med,
+ const int ledge_index,
+ void *data)
+{
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(data);
+ const int l_index_offset = mr->edge_len + ledge_index;
+ const int e_index = mr->ledges[ledge_index];
+ if (!((mr->use_hide && (med->flag & ME_HIDE)) ||
+ ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) &&
+ (mr->e_origindex[e_index] == ORIGINDEX_NONE)))) {
+ const int l_index = mr->loop_len + ledge_index * 2;
+ GPU_indexbuf_set_line_verts(elb, l_index_offset, l_index, l_index + 1);
+ }
+ else {
+ GPU_indexbuf_set_line_restart(elb, l_index_offset);
+ }
+ /* Don't render the edge twice. */
+ GPU_indexbuf_set_line_restart(elb, e_index);
+}
+
+static void extract_lines_task_reduce(void *_userdata_to, void *_userdata_from)
+{
+ GPUIndexBufBuilder *elb_to = static_cast<GPUIndexBufBuilder *>(_userdata_to);
+ GPUIndexBufBuilder *elb_from = static_cast<GPUIndexBufBuilder *>(_userdata_from);
+ GPU_indexbuf_join(elb_to, elb_from);
+}
+
+static void extract_lines_finish(const MeshRenderData *UNUSED(mr),
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *data)
+{
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(data);
+ GPUIndexBuf *ibo = static_cast<GPUIndexBuf *>(buf);
+ GPU_indexbuf_build_in_place(elb, ibo);
+}
+
+constexpr MeshExtract create_extractor_lines()
+{
+ MeshExtract extractor = {0};
+ extractor.init = extract_lines_init;
+ extractor.iter_poly_bm = extract_lines_iter_poly_bm;
+ extractor.iter_poly_mesh = extract_lines_iter_poly_mesh;
+ extractor.iter_ledge_bm = extract_lines_iter_ledge_bm;
+ extractor.iter_ledge_mesh = extract_lines_iter_ledge_mesh;
+ extractor.task_reduce = extract_lines_task_reduce;
+ extractor.finish = extract_lines_finish;
+ extractor.data_type = MR_DATA_NONE;
+ extractor.data_size = sizeof(GPUIndexBufBuilder);
+ extractor.use_threading = true;
+ extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines);
+ return extractor;
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Lines and Loose Edges Sub Buffer
+ * \{ */
+
+static void extract_lines_loose_subbuffer(const MeshRenderData *mr, struct MeshBatchCache *cache)
+{
+ BLI_assert(cache->final.ibo.lines);
+ /* Multiply by 2 because these are edges indices. */
+ const int start = mr->edge_len * 2;
+ const int len = mr->edge_loose_len * 2;
+ GPU_indexbuf_create_subrange_in_place(
+ cache->final.ibo.lines_loose, cache->final.ibo.lines, start, len);
+ cache->no_loose_wire = (len == 0);
+}
+
+static void extract_lines_with_lines_loose_finish(const MeshRenderData *mr,
+ struct MeshBatchCache *cache,
+ void *buf,
+ void *data)
+{
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(data);
+ GPUIndexBuf *ibo = static_cast<GPUIndexBuf *>(buf);
+ GPU_indexbuf_build_in_place(elb, ibo);
+ extract_lines_loose_subbuffer(mr, cache);
+}
+
+constexpr MeshExtract create_extractor_lines_with_lines_loose()
+{
+ MeshExtract extractor = {0};
+ extractor.init = extract_lines_init;
+ extractor.iter_poly_bm = extract_lines_iter_poly_bm;
+ extractor.iter_poly_mesh = extract_lines_iter_poly_mesh;
+ extractor.iter_ledge_bm = extract_lines_iter_ledge_bm;
+ extractor.iter_ledge_mesh = extract_lines_iter_ledge_mesh;
+ extractor.task_reduce = extract_lines_task_reduce;
+ extractor.finish = extract_lines_with_lines_loose_finish;
+ extractor.data_type = MR_DATA_NONE;
+ extractor.data_size = sizeof(GPUIndexBufBuilder);
+ extractor.use_threading = true;
+ extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines);
+ return extractor;
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Loose Edges Sub Buffer
+ * \{ */
+
+static void extract_lines_loose_only_init(const MeshRenderData *mr,
+ struct MeshBatchCache *cache,
+ void *buf,
+ void *UNUSED(tls_data))
+{
+ BLI_assert(buf == cache->final.ibo.lines_loose);
+ UNUSED_VARS_NDEBUG(buf);
+ extract_lines_loose_subbuffer(mr, cache);
+}
+
+constexpr MeshExtract create_extractor_lines_loose_only()
+{
+ MeshExtract extractor = {0};
+ extractor.init = extract_lines_loose_only_init;
+ extractor.data_type = MR_DATA_NONE;
+ extractor.data_size = 0;
+ extractor.use_threading = false;
+ extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines_loose);
+ return extractor;
+}
+
+/** \} */
+
+} // namespace blender::draw
+
+extern "C" {
+const MeshExtract extract_lines = blender::draw::create_extractor_lines();
+const MeshExtract extract_lines_with_lines_loose =
+ blender::draw::create_extractor_lines_with_lines_loose();
+const MeshExtract extract_lines_loose_only = blender::draw::create_extractor_lines_loose_only();
+}
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc
new file mode 100644
index 00000000000..cace18b0c08
--- /dev/null
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc
@@ -0,0 +1,198 @@
+/*
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2021 by Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup draw
+ */
+
+#include "draw_cache_extract_mesh_private.h"
+
+#include "BLI_edgehash.h"
+#include "BLI_vector.hh"
+
+#include "MEM_guardedalloc.h"
+
+namespace blender::draw {
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Line Adjacency Indices
+ * \{ */
+
+#define NO_EDGE INT_MAX
+
+struct MeshExtract_LineAdjacency_Data {
+ GPUIndexBufBuilder elb;
+ EdgeHash *eh;
+ bool is_manifold;
+ /* Array to convert vert index to any loop index of this vert. */
+ uint *vert_to_loop;
+};
+
+static void extract_lines_adjacency_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *UNUSED(buf),
+ void *tls_data)
+{
+ /* Similar to poly_to_tri_count().
+ * There is always (loop + triangle - 1) edges inside a polygon.
+ * Accumulate for all polys and you get : */
+ uint tess_edge_len = mr->loop_len + mr->tri_len - mr->poly_len;
+
+ MeshExtract_LineAdjacency_Data *data = static_cast<MeshExtract_LineAdjacency_Data *>(tls_data);
+ data->vert_to_loop = static_cast<uint *>(MEM_callocN(sizeof(uint) * mr->vert_len, __func__));
+
+ GPU_indexbuf_init(&data->elb, GPU_PRIM_LINES_ADJ, tess_edge_len, mr->loop_len);
+ data->eh = BLI_edgehash_new_ex(__func__, tess_edge_len);
+ data->is_manifold = true;
+}
+
+BLI_INLINE void lines_adjacency_triangle(
+ uint v1, uint v2, uint v3, uint l1, uint l2, uint l3, MeshExtract_LineAdjacency_Data *data)
+{
+ GPUIndexBufBuilder *elb = &data->elb;
+ /* Iterate around the triangle's edges. */
+ for (int e = 0; e < 3; e++) {
+ SHIFT3(uint, v3, v2, v1);
+ SHIFT3(uint, l3, l2, l1);
+
+ bool inv_indices = (v2 > v3);
+ void **pval;
+ bool value_is_init = BLI_edgehash_ensure_p(data->eh, v2, v3, &pval);
+ int v_data = POINTER_AS_INT(*pval);
+ if (!value_is_init || v_data == NO_EDGE) {
+ /* Save the winding order inside the sign bit. Because the
+ * Edge-hash sort the keys and we need to compare winding later. */
+ int value = (int)l1 + 1; /* 0 cannot be signed so add one. */
+ *pval = POINTER_FROM_INT((inv_indices) ? -value : value);
+ /* Store loop indices for remaining non-manifold edges. */
+ data->vert_to_loop[v2] = l2;
+ data->vert_to_loop[v3] = l3;
+ }
+ else {
+ /* HACK Tag as not used. Prevent overhead of BLI_edgehash_remove. */
+ *pval = POINTER_FROM_INT(NO_EDGE);
+ bool inv_opposite = (v_data < 0);
+ uint l_opposite = (uint)abs(v_data) - 1;
+ /* TODO Make this part thread-safe. */
+ if (inv_opposite == inv_indices) {
+ /* Don't share edge if triangles have non matching winding. */
+ GPU_indexbuf_add_line_adj_verts(elb, l1, l2, l3, l1);
+ GPU_indexbuf_add_line_adj_verts(elb, l_opposite, l2, l3, l_opposite);
+ data->is_manifold = false;
+ }
+ else {
+ GPU_indexbuf_add_line_adj_verts(elb, l1, l2, l3, l_opposite);
+ }
+ }
+ }
+}
+
+static void extract_lines_adjacency_iter_looptri_bm(const MeshRenderData *UNUSED(mr),
+ BMLoop **elt,
+ const int UNUSED(elt_index),
+ void *_data)
+{
+ MeshExtract_LineAdjacency_Data *data = static_cast<MeshExtract_LineAdjacency_Data *>(_data);
+ if (!BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN)) {
+ lines_adjacency_triangle(BM_elem_index_get(elt[0]->v),
+ BM_elem_index_get(elt[1]->v),
+ BM_elem_index_get(elt[2]->v),
+ BM_elem_index_get(elt[0]),
+ BM_elem_index_get(elt[1]),
+ BM_elem_index_get(elt[2]),
+ data);
+ }
+}
+
+static void extract_lines_adjacency_iter_looptri_mesh(const MeshRenderData *mr,
+ const MLoopTri *mlt,
+ const int UNUSED(elt_index),
+ void *_data)
+{
+ MeshExtract_LineAdjacency_Data *data = static_cast<MeshExtract_LineAdjacency_Data *>(_data);
+ const MPoly *mp = &mr->mpoly[mlt->poly];
+ if (!(mr->use_hide && (mp->flag & ME_HIDE))) {
+ lines_adjacency_triangle(mr->mloop[mlt->tri[0]].v,
+ mr->mloop[mlt->tri[1]].v,
+ mr->mloop[mlt->tri[2]].v,
+ mlt->tri[0],
+ mlt->tri[1],
+ mlt->tri[2],
+ data);
+ }
+}
+
+static void extract_lines_adjacency_finish(const MeshRenderData *UNUSED(mr),
+ struct MeshBatchCache *cache,
+ void *buf,
+ void *_data)
+{
+ GPUIndexBuf *ibo = static_cast<GPUIndexBuf *>(buf);
+ MeshExtract_LineAdjacency_Data *data = static_cast<MeshExtract_LineAdjacency_Data *>(_data);
+ /* Create edges for remaining non manifold edges. */
+ EdgeHashIterator *ehi = BLI_edgehashIterator_new(data->eh);
+ for (; !BLI_edgehashIterator_isDone(ehi); BLI_edgehashIterator_step(ehi)) {
+ uint v2, v3, l1, l2, l3;
+ int v_data = POINTER_AS_INT(BLI_edgehashIterator_getValue(ehi));
+ if (v_data != NO_EDGE) {
+ BLI_edgehashIterator_getKey(ehi, &v2, &v3);
+ l1 = (uint)abs(v_data) - 1;
+ if (v_data < 0) { /* inv_opposite */
+ SWAP(uint, v2, v3);
+ }
+ l2 = data->vert_to_loop[v2];
+ l3 = data->vert_to_loop[v3];
+ GPU_indexbuf_add_line_adj_verts(&data->elb, l1, l2, l3, l1);
+ data->is_manifold = false;
+ }
+ }
+ BLI_edgehashIterator_free(ehi);
+ BLI_edgehash_free(data->eh, nullptr);
+
+ cache->is_manifold = data->is_manifold;
+
+ GPU_indexbuf_build_in_place(&data->elb, ibo);
+ MEM_freeN(data->vert_to_loop);
+}
+
+#undef NO_EDGE
+
+/** \} */
+
+constexpr MeshExtract create_extractor_lines_adjacency()
+{
+ MeshExtract extractor = {0};
+ extractor.init = extract_lines_adjacency_init;
+ extractor.iter_looptri_bm = extract_lines_adjacency_iter_looptri_bm;
+ extractor.iter_looptri_mesh = extract_lines_adjacency_iter_looptri_mesh;
+ extractor.finish = extract_lines_adjacency_finish;
+ extractor.data_type = MR_DATA_NONE;
+ extractor.data_size = sizeof(MeshExtract_LineAdjacency_Data);
+ extractor.use_threading = false;
+ extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines_adjacency);
+ return extractor;
+}
+
+} // namespace blender::draw
+
+extern "C" {
+const MeshExtract extract_lines_adjacency = blender::draw::create_extractor_lines_adjacency();
+}
+
+/** \} */
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc
new file mode 100644
index 00000000000..a142692ab4e
--- /dev/null
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc
@@ -0,0 +1,127 @@
+/*
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2021 by Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup draw
+ */
+
+#include "draw_cache_extract_mesh_private.h"
+
+#include "BLI_bitmap.h"
+#include "BLI_vector.hh"
+#include "atomic_ops.h"
+
+#include "MEM_guardedalloc.h"
+
+namespace blender::draw {
+/* ---------------------------------------------------------------------- */
+/** \name Extract Paint Mask Line Indices
+ * \{ */
+
+struct MeshExtract_LinePaintMask_Data {
+ GPUIndexBufBuilder elb;
+ /** One bit per edge set if face is selected. */
+ BLI_bitmap *select_map;
+};
+
+static void extract_lines_paint_mask_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *UNUSED(ibo),
+ void *tls_data)
+{
+ MeshExtract_LinePaintMask_Data *data = static_cast<MeshExtract_LinePaintMask_Data *>(tls_data);
+ data->select_map = BLI_BITMAP_NEW(mr->edge_len, __func__);
+ GPU_indexbuf_init(&data->elb, GPU_PRIM_LINES, mr->edge_len, mr->loop_len);
+}
+
+static void extract_lines_paint_mask_iter_poly_mesh(const MeshRenderData *mr,
+ const MPoly *mp,
+ const int UNUSED(mp_index),
+ void *_data)
+{
+ MeshExtract_LinePaintMask_Data *data = static_cast<MeshExtract_LinePaintMask_Data *>(_data);
+ const MLoop *mloop = mr->mloop;
+ const int ml_index_end = mp->loopstart + mp->totloop;
+ for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
+ const MLoop *ml = &mloop[ml_index];
+
+ const int e_index = ml->e;
+ const MEdge *me = &mr->medge[e_index];
+ if (!((mr->use_hide && (me->flag & ME_HIDE)) ||
+ ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) &&
+ (mr->e_origindex[e_index] == ORIGINDEX_NONE)))) {
+
+ const int ml_index_last = mp->totloop + mp->loopstart - 1;
+ const int ml_index_other = (ml_index == ml_index_last) ? mp->loopstart : (ml_index + 1);
+ if (mp->flag & ME_FACE_SEL) {
+ if (BLI_BITMAP_TEST_AND_SET_ATOMIC(data->select_map, e_index)) {
+ /* Hide edge as it has more than 2 selected loop. */
+ GPU_indexbuf_set_line_restart(&data->elb, e_index);
+ }
+ else {
+ /* First selected loop. Set edge visible, overwriting any unselected loop. */
+ GPU_indexbuf_set_line_verts(&data->elb, e_index, ml_index, ml_index_other);
+ }
+ }
+ else {
+ /* Set these unselected loop only if this edge has no other selected loop. */
+ if (!BLI_BITMAP_TEST(data->select_map, e_index)) {
+ GPU_indexbuf_set_line_verts(&data->elb, e_index, ml_index, ml_index_other);
+ }
+ }
+ }
+ else {
+ GPU_indexbuf_set_line_restart(&data->elb, e_index);
+ }
+ }
+}
+
+static void extract_lines_paint_mask_finish(const MeshRenderData *UNUSED(mr),
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *_data)
+{
+ MeshExtract_LinePaintMask_Data *data = static_cast<MeshExtract_LinePaintMask_Data *>(_data);
+ GPUIndexBuf *ibo = static_cast<GPUIndexBuf *>(buf);
+ GPU_indexbuf_build_in_place(&data->elb, ibo);
+ MEM_freeN(data->select_map);
+}
+
+/** \} */
+
+constexpr MeshExtract create_extractor_lines_paint_mask()
+{
+ MeshExtract extractor = {0};
+ extractor.init = extract_lines_paint_mask_init;
+ extractor.iter_poly_mesh = extract_lines_paint_mask_iter_poly_mesh;
+ extractor.finish = extract_lines_paint_mask_finish;
+ extractor.data_type = MR_DATA_NONE;
+ extractor.data_size = sizeof(MeshExtract_LinePaintMask_Data);
+ extractor.use_threading = false;
+ extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines_paint_mask);
+ return extractor;
+}
+
+} // namespace blender::draw
+
+extern "C" {
+const MeshExtract extract_lines_paint_mask = blender::draw::create_extractor_lines_paint_mask();
+}
+
+/** \} */
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc
new file mode 100644
index 00000000000..1e4e76ba7c5
--- /dev/null
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc
@@ -0,0 +1,182 @@
+/*
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2021 by Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup draw
+ */
+
+#include "draw_cache_extract_mesh_private.h"
+
+#include "BLI_vector.hh"
+
+#include "MEM_guardedalloc.h"
+
+namespace blender::draw {
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Point Indices
+ * \{ */
+static void extract_points_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *UNUSED(buf),
+ void *tls_data)
+{
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(tls_data);
+ GPU_indexbuf_init(elb, GPU_PRIM_POINTS, mr->vert_len, mr->loop_len + mr->loop_loose_len);
+}
+
+BLI_INLINE void vert_set_bm(GPUIndexBufBuilder *elb, const BMVert *eve, int l_index)
+{
+ const int v_index = BM_elem_index_get(eve);
+ if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
+ GPU_indexbuf_set_point_vert(elb, v_index, l_index);
+ }
+ else {
+ GPU_indexbuf_set_point_restart(elb, v_index);
+ }
+}
+
+BLI_INLINE void vert_set_mesh(GPUIndexBufBuilder *elb,
+ const MeshRenderData *mr,
+ const int v_index,
+ const int l_index)
+{
+ const MVert *mv = &mr->mvert[v_index];
+ if (!((mr->use_hide && (mv->flag & ME_HIDE)) ||
+ ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->v_origindex) &&
+ (mr->v_origindex[v_index] == ORIGINDEX_NONE)))) {
+ GPU_indexbuf_set_point_vert(elb, v_index, l_index);
+ }
+ else {
+ GPU_indexbuf_set_point_restart(elb, v_index);
+ }
+}
+
+static void extract_points_iter_poly_bm(const MeshRenderData *UNUSED(mr),
+ const BMFace *f,
+ const int UNUSED(f_index),
+ void *_userdata)
+{
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_userdata);
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ const int l_index = BM_elem_index_get(l_iter);
+
+ vert_set_bm(elb, l_iter->v, l_index);
+ } while ((l_iter = l_iter->next) != l_first);
+}
+
+static void extract_points_iter_poly_mesh(const MeshRenderData *mr,
+ const MPoly *mp,
+ const int UNUSED(mp_index),
+ void *_userdata)
+{
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_userdata);
+ const MLoop *mloop = mr->mloop;
+ const int ml_index_end = mp->loopstart + mp->totloop;
+ for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
+ const MLoop *ml = &mloop[ml_index];
+ vert_set_mesh(elb, mr, ml->v, ml_index);
+ }
+}
+
+static void extract_points_iter_ledge_bm(const MeshRenderData *mr,
+ const BMEdge *eed,
+ const int ledge_index,
+ void *_userdata)
+{
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_userdata);
+ vert_set_bm(elb, eed->v1, mr->loop_len + (ledge_index * 2));
+ vert_set_bm(elb, eed->v2, mr->loop_len + (ledge_index * 2) + 1);
+}
+
+static void extract_points_iter_ledge_mesh(const MeshRenderData *mr,
+ const MEdge *med,
+ const int ledge_index,
+ void *_userdata)
+{
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_userdata);
+ vert_set_mesh(elb, mr, med->v1, mr->loop_len + (ledge_index * 2));
+ vert_set_mesh(elb, mr, med->v2, mr->loop_len + (ledge_index * 2) + 1);
+}
+
+static void extract_points_iter_lvert_bm(const MeshRenderData *mr,
+ const BMVert *eve,
+ const int lvert_index,
+ void *_userdata)
+{
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_userdata);
+ const int offset = mr->loop_len + (mr->edge_loose_len * 2);
+ vert_set_bm(elb, eve, offset + lvert_index);
+}
+
+static void extract_points_iter_lvert_mesh(const MeshRenderData *mr,
+ const MVert *UNUSED(mv),
+ const int lvert_index,
+ void *_userdata)
+{
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_userdata);
+ const int offset = mr->loop_len + (mr->edge_loose_len * 2);
+ vert_set_mesh(elb, mr, mr->lverts[lvert_index], offset + lvert_index);
+}
+
+static void extract_points_task_reduce(void *_userdata_to, void *_userdata_from)
+{
+ GPUIndexBufBuilder *elb_to = static_cast<GPUIndexBufBuilder *>(_userdata_to);
+ GPUIndexBufBuilder *elb_from = static_cast<GPUIndexBufBuilder *>(_userdata_from);
+ GPU_indexbuf_join(elb_to, elb_from);
+}
+
+static void extract_points_finish(const MeshRenderData *UNUSED(mr),
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf,
+ void *_userdata)
+{
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_userdata);
+ GPUIndexBuf *ibo = static_cast<GPUIndexBuf *>(buf);
+ GPU_indexbuf_build_in_place(elb, ibo);
+}
+
+constexpr MeshExtract create_extractor_points()
+{
+ MeshExtract extractor = {0};
+ extractor.init = extract_points_init;
+ extractor.iter_poly_bm = extract_points_iter_poly_bm;
+ extractor.iter_poly_mesh = extract_points_iter_poly_mesh;
+ extractor.iter_ledge_bm = extract_points_iter_ledge_bm;
+ extractor.iter_ledge_mesh = extract_points_iter_ledge_mesh;
+ extractor.iter_lvert_bm = extract_points_iter_lvert_bm;
+ extractor.iter_lvert_mesh = extract_points_iter_lvert_mesh;
+ extractor.task_reduce = extract_points_task_reduce;
+ extractor.finish = extract_points_finish;
+ extractor.use_threading = true;
+ extractor.data_type = MR_DATA_NONE;
+ extractor.data_size = sizeof(GPUIndexBufBuilder);
+ extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.points);
+ return extractor;
+}
+
+} // namespace blender::draw
+
+extern "C" {
+const MeshExtract extract_points = blender::draw::create_extractor_points();
+}
+
+/** \} */
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc
new file mode 100644
index 00000000000..70b46481b51
--- /dev/null
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc
@@ -0,0 +1,269 @@
+/*
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2021 by Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup draw
+ */
+
+#include "draw_cache_extract_mesh_private.h"
+
+#include "MEM_guardedalloc.h"
+
+namespace blender::draw {
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Triangles Indices (multi material)
+ * \{ */
+
+struct MeshExtract_Tri_Data {
+ GPUIndexBufBuilder elb;
+ int *tri_mat_start;
+ int *tri_mat_end;
+};
+
+static void extract_tris_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *UNUSED(ibo),
+ void *tls_data)
+{
+ MeshExtract_Tri_Data *data = static_cast<MeshExtract_Tri_Data *>(tls_data);
+
+ size_t mat_tri_idx_size = sizeof(int) * mr->mat_len;
+ data->tri_mat_start = static_cast<int *>(MEM_callocN(mat_tri_idx_size, __func__));
+ data->tri_mat_end = static_cast<int *>(MEM_callocN(mat_tri_idx_size, __func__));
+
+ int *mat_tri_len = data->tri_mat_start;
+ /* Count how many triangle for each material. */
+ if (mr->extract_type == MR_EXTRACT_BMESH) {
+ BMIter iter;
+ BMFace *efa;
+ BM_ITER_MESH (efa, &iter, mr->bm, BM_FACES_OF_MESH) {
+ if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
+ int mat = min_ii(efa->mat_nr, mr->mat_len - 1);
+ mat_tri_len[mat] += efa->len - 2;
+ }
+ }
+ }
+ else {
+ const MPoly *mp = mr->mpoly;
+ for (int mp_index = 0; mp_index < mr->poly_len; mp_index++, mp++) {
+ if (!(mr->use_hide && (mp->flag & ME_HIDE))) {
+ int mat = min_ii(mp->mat_nr, mr->mat_len - 1);
+ mat_tri_len[mat] += mp->totloop - 2;
+ }
+ }
+ }
+ /* Accumulate triangle lengths per material to have correct offsets. */
+ int ofs = mat_tri_len[0];
+ mat_tri_len[0] = 0;
+ for (int i = 1; i < mr->mat_len; i++) {
+ int tmp = mat_tri_len[i];
+ mat_tri_len[i] = ofs;
+ ofs += tmp;
+ }
+
+ memcpy(data->tri_mat_end, mat_tri_len, mat_tri_idx_size);
+
+ int visible_tri_tot = ofs;
+ GPU_indexbuf_init(&data->elb, GPU_PRIM_TRIS, visible_tri_tot, mr->loop_len);
+}
+
+static void extract_tris_iter_looptri_bm(const MeshRenderData *mr,
+ BMLoop **elt,
+ const int UNUSED(elt_index),
+ void *_data)
+{
+ MeshExtract_Tri_Data *data = static_cast<MeshExtract_Tri_Data *>(_data);
+ const int mat_last = mr->mat_len - 1;
+
+ if (!BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN)) {
+ int *mat_tri_ofs = data->tri_mat_end;
+ const int mat = min_ii(elt[0]->f->mat_nr, mat_last);
+ GPU_indexbuf_set_tri_verts(&data->elb,
+ mat_tri_ofs[mat]++,
+ BM_elem_index_get(elt[0]),
+ BM_elem_index_get(elt[1]),
+ BM_elem_index_get(elt[2]));
+ }
+}
+
+static void extract_tris_iter_looptri_mesh(const MeshRenderData *mr,
+ const MLoopTri *mlt,
+ const int UNUSED(elt_index),
+ void *_data)
+{
+ MeshExtract_Tri_Data *data = static_cast<MeshExtract_Tri_Data *>(_data);
+ const int mat_last = mr->mat_len - 1;
+ const MPoly *mp = &mr->mpoly[mlt->poly];
+ if (!(mr->use_hide && (mp->flag & ME_HIDE))) {
+ int *mat_tri_ofs = data->tri_mat_end;
+ const int mat = min_ii(mp->mat_nr, mat_last);
+ GPU_indexbuf_set_tri_verts(
+ &data->elb, mat_tri_ofs[mat]++, mlt->tri[0], mlt->tri[1], mlt->tri[2]);
+ }
+}
+
+static void extract_tris_finish(const MeshRenderData *mr,
+ struct MeshBatchCache *cache,
+ void *buf,
+ void *_data)
+{
+ GPUIndexBuf *ibo = static_cast<GPUIndexBuf *>(buf);
+ MeshExtract_Tri_Data *data = static_cast<MeshExtract_Tri_Data *>(_data);
+ GPU_indexbuf_build_in_place(&data->elb, ibo);
+
+ /* Create ibo sub-ranges. Always do this to avoid error when the standard surface batch
+ * is created before the surfaces-per-material. */
+ if (mr->use_final_mesh && cache->final.tris_per_mat) {
+ MeshBufferCache *mbc_final = &cache->final;
+ for (int i = 0; i < mr->mat_len; i++) {
+ /* These IBOs have not been queried yet but we create them just in case they are needed
+ * later since they are not tracked by mesh_buffer_cache_create_requested(). */
+ if (mbc_final->tris_per_mat[i] == nullptr) {
+ mbc_final->tris_per_mat[i] = GPU_indexbuf_calloc();
+ }
+ /* Multiply by 3 because these are triangle indices. */
+ const int mat_start = data->tri_mat_start[i];
+ const int mat_end = data->tri_mat_end[i];
+ const int start = mat_start * 3;
+ const int len = (mat_end - mat_start) * 3;
+ GPU_indexbuf_create_subrange_in_place(mbc_final->tris_per_mat[i], ibo, start, len);
+ }
+ }
+ MEM_freeN(data->tri_mat_start);
+ MEM_freeN(data->tri_mat_end);
+}
+
+constexpr MeshExtract create_extractor_tris()
+{
+ MeshExtract extractor = {0};
+ extractor.init = extract_tris_init;
+ extractor.iter_looptri_bm = extract_tris_iter_looptri_bm;
+ extractor.iter_looptri_mesh = extract_tris_iter_looptri_mesh;
+ extractor.finish = extract_tris_finish;
+ extractor.data_type = MR_DATA_NONE;
+ extractor.data_size = sizeof(MeshExtract_Tri_Data);
+ extractor.use_threading = false;
+ extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.tris);
+ return extractor;
+}
+
+/** \} */
+
+/** \name Extract Triangles Indices (single material)
+ * \{ */
+
+static void extract_tris_single_mat_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *UNUSED(ibo),
+ void *tls_data)
+{
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(tls_data);
+ GPU_indexbuf_init(elb, GPU_PRIM_TRIS, mr->tri_len, mr->loop_len);
+}
+
+static void extract_tris_single_mat_iter_looptri_bm(const MeshRenderData *UNUSED(mr),
+ BMLoop **elt,
+ const int elt_index,
+ void *_data)
+{
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_data);
+ if (!BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN)) {
+ GPU_indexbuf_set_tri_verts(elb,
+ elt_index,
+ BM_elem_index_get(elt[0]),
+ BM_elem_index_get(elt[1]),
+ BM_elem_index_get(elt[2]));
+ }
+ else {
+ GPU_indexbuf_set_tri_restart(elb, elt_index);
+ }
+}
+
+static void extract_tris_single_mat_iter_looptri_mesh(const MeshRenderData *mr,
+ const MLoopTri *mlt,
+ const int mlt_index,
+ void *_data)
+{
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_data);
+ const MPoly *mp = &mr->mpoly[mlt->poly];
+ if (!(mr->use_hide && (mp->flag & ME_HIDE))) {
+ GPU_indexbuf_set_tri_verts(elb, mlt_index, mlt->tri[0], mlt->tri[1], mlt->tri[2]);
+ }
+ else {
+ GPU_indexbuf_set_tri_restart(elb, mlt_index);
+ }
+}
+
+static void extract_tris_single_mat_task_reduce(void *_userdata_to, void *_userdata_from)
+{
+ GPUIndexBufBuilder *elb_to = static_cast<GPUIndexBufBuilder *>(_userdata_to);
+ GPUIndexBufBuilder *elb_from = static_cast<GPUIndexBufBuilder *>(_userdata_from);
+ GPU_indexbuf_join(elb_to, elb_from);
+}
+
+static void extract_tris_single_mat_finish(const MeshRenderData *mr,
+ struct MeshBatchCache *cache,
+ void *buf,
+ void *_data)
+{
+ GPUIndexBuf *ibo = static_cast<GPUIndexBuf *>(buf);
+ GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_data);
+ GPU_indexbuf_build_in_place(elb, ibo);
+
+ /* Create ibo sub-ranges. Always do this to avoid error when the standard surface batch
+ * is created before the surfaces-per-material. */
+ if (mr->use_final_mesh && cache->final.tris_per_mat) {
+ MeshBufferCache *mbc = &cache->final;
+ for (int i = 0; i < mr->mat_len; i++) {
+ /* These IBOs have not been queried yet but we create them just in case they are needed
+ * later since they are not tracked by mesh_buffer_cache_create_requested(). */
+ if (mbc->tris_per_mat[i] == NULL) {
+ mbc->tris_per_mat[i] = GPU_indexbuf_calloc();
+ }
+ /* Multiply by 3 because these are triangle indices. */
+ const int len = mr->tri_len * 3;
+ GPU_indexbuf_create_subrange_in_place(mbc->tris_per_mat[i], ibo, 0, len);
+ }
+ }
+}
+
+constexpr MeshExtract create_extractor_tris_single_mat()
+{
+ MeshExtract extractor = {0};
+ extractor.init = extract_tris_single_mat_init;
+ extractor.iter_looptri_bm = extract_tris_single_mat_iter_looptri_bm;
+ extractor.iter_looptri_mesh = extract_tris_single_mat_iter_looptri_mesh;
+ extractor.task_reduce = extract_tris_single_mat_task_reduce;
+ extractor.finish = extract_tris_single_mat_finish;
+ extractor.data_type = MR_DATA_NONE;
+ extractor.data_size = sizeof(GPUIndexBufBuilder);
+ extractor.use_threading = true;
+ extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.tris);
+ return extractor;
+}
+
+/** \} */
+
+} // namespace blender::draw
+
+extern "C" {
+const MeshExtract extract_tris = blender::draw::create_extractor_tris();
+const MeshExtract extract_tris_single_mat = blender::draw::create_extractor_tris_single_mat();
+}
diff --git a/source/blender/draw/intern/shaders/common_hair_lib.glsl b/source/blender/draw/intern/shaders/common_hair_lib.glsl
index 8684d82f228..02c335ddae2 100644
--- a/source/blender/draw/intern/shaders/common_hair_lib.glsl
+++ b/source/blender/draw/intern/shaders/common_hair_lib.glsl
@@ -28,6 +28,9 @@ uniform bool hairCloseTip = true;
uniform vec4 hairDupliMatrix[4];
+/* Strand batch offset when used in compute shaders. */
+uniform int hairStrandOffset = 0;
+
/* -- Per control points -- */
uniform samplerBuffer hairPointBuffer; /* RGBA32F */
#define point_position xyz
@@ -43,13 +46,37 @@ uniform usamplerBuffer hairStrandSegBuffer; /* R16UI */
/* -- Subdivision stage -- */
/**
- * We use a transform feedback to preprocess the strands and add more subdivision to it.
- * For the moment these are simple smooth interpolation but one could hope to see the full
+ * We use a transform feedback or compute shader to preprocess the strands and add more subdivision
+ * to it. For the moment these are simple smooth interpolation but one could hope to see the full
* children particle modifiers being evaluated at this stage.
*
* If no more subdivision is needed, we can skip this step.
*/
+#ifdef GPU_VERTEX_SHADER
+float hair_get_local_time()
+{
+ return float(gl_VertexID % hairStrandsRes) / float(hairStrandsRes - 1);
+}
+
+int hair_get_id()
+{
+ return gl_VertexID / hairStrandsRes;
+}
+#endif
+
+#ifdef GPU_COMPUTE_SHADER
+float hair_get_local_time()
+{
+ return float(gl_GlobalInvocationID.y) / float(hairStrandsRes - 1);
+}
+
+int hair_get_id()
+{
+ return int(gl_GlobalInvocationID.x) + hairStrandOffset;
+}
+#endif
+
#ifdef HAIR_PHASE_SUBDIV
int hair_get_base_id(float local_time, int strand_segments, out float interp_time)
{
@@ -64,9 +91,9 @@ int hair_get_base_id(float local_time, int strand_segments, out float interp_tim
void hair_get_interp_attrs(
out vec4 data0, out vec4 data1, out vec4 data2, out vec4 data3, out float interp_time)
{
- float local_time = float(gl_VertexID % hairStrandsRes) / float(hairStrandsRes - 1);
+ float local_time = hair_get_local_time();
- int hair_id = gl_VertexID / hairStrandsRes;
+ int hair_id = hair_get_id();
int strand_offset = int(texelFetch(hairStrandBuffer, hair_id).x);
int strand_segments = int(texelFetch(hairStrandSegBuffer, hair_id).x);
@@ -96,6 +123,7 @@ void hair_get_interp_attrs(
*/
#if !defined(HAIR_PHASE_SUBDIV) && defined(GPU_VERTEX_SHADER)
+
int hair_get_strand_id(void)
{
return gl_VertexID / (hairStrandsRes * hairThicknessRes);
@@ -227,3 +255,45 @@ vec2 hair_resolve_barycentric(vec2 vert_barycentric)
return vec2(1.0 - vert_barycentric.x, 0.0);
}
}
+
+/* Hair interpolation functions. */
+vec4 hair_get_weights_cardinal(float t)
+{
+ float t2 = t * t;
+ float t3 = t2 * t;
+#if defined(CARDINAL)
+ float fc = 0.71;
+#else /* defined(CATMULL_ROM) */
+ float fc = 0.5;
+#endif
+
+ vec4 weights;
+ /* GLSL Optimized version of key_curve_position_weights() */
+ float fct = t * fc;
+ float fct2 = t2 * fc;
+ float fct3 = t3 * fc;
+ weights.x = (fct2 * 2.0 - fct3) - fct;
+ weights.y = (t3 * 2.0 - fct3) + (-t2 * 3.0 + fct2) + 1.0;
+ weights.z = (-t3 * 2.0 + fct3) + (t2 * 3.0 - (2.0 * fct2)) + fct;
+ weights.w = fct3 - fct2;
+ return weights;
+}
+
+/* TODO(fclem): This one is buggy, find why. (it's not the optimization!!) */
+vec4 hair_get_weights_bspline(float t)
+{
+ float t2 = t * t;
+ float t3 = t2 * t;
+
+ vec4 weights;
+ /* GLSL Optimized version of key_curve_position_weights() */
+ weights.xz = vec2(-0.16666666, -0.5) * t3 + (0.5 * t2 + 0.5 * vec2(-t, t) + 0.16666666);
+ weights.y = (0.5 * t3 - t2 + 0.66666666);
+ weights.w = (0.16666666 * t3);
+ return weights;
+}
+
+vec4 hair_interp_data(vec4 v0, vec4 v1, vec4 v2, vec4 v3, vec4 w)
+{
+ return v0 * w.x + v1 * w.y + v2 * w.z + v3 * w.w;
+}
diff --git a/source/blender/draw/intern/shaders/common_hair_refine_comp.glsl b/source/blender/draw/intern/shaders/common_hair_refine_comp.glsl
new file mode 100644
index 00000000000..4dcde4b0245
--- /dev/null
+++ b/source/blender/draw/intern/shaders/common_hair_refine_comp.glsl
@@ -0,0 +1,24 @@
+
+/*
+ * To be compiled with common_hair_lib.glsl.
+ */
+
+layout(local_size_x = 1, local_size_y = 1) in;
+layout(std430, binding = 0) writeonly buffer hairPointOutputBuffer
+{
+ vec4 posTime[];
+}
+out_vertbuf;
+
+void main(void)
+{
+ float interp_time;
+ vec4 data0, data1, data2, data3;
+ hair_get_interp_attrs(data0, data1, data2, data3, interp_time);
+
+ vec4 weights = hair_get_weights_cardinal(interp_time);
+ vec4 result = hair_interp_data(data0, data1, data2, data3, weights);
+
+ uint index = uint(hair_get_id() * hairStrandsRes) + gl_GlobalInvocationID.y;
+ out_vertbuf.posTime[index] = result;
+}
diff --git a/source/blender/draw/intern/shaders/common_hair_refine_vert.glsl b/source/blender/draw/intern/shaders/common_hair_refine_vert.glsl
index 3f5e3f8226f..371d43827b9 100644
--- a/source/blender/draw/intern/shaders/common_hair_refine_vert.glsl
+++ b/source/blender/draw/intern/shaders/common_hair_refine_vert.glsl
@@ -3,47 +3,6 @@
out vec4 finalColor;
-vec4 get_weights_cardinal(float t)
-{
- float t2 = t * t;
- float t3 = t2 * t;
-#if defined(CARDINAL)
- float fc = 0.71;
-#else /* defined(CATMULL_ROM) */
- float fc = 0.5;
-#endif
-
- vec4 weights;
- /* GLSL Optimized version of key_curve_position_weights() */
- float fct = t * fc;
- float fct2 = t2 * fc;
- float fct3 = t3 * fc;
- weights.x = (fct2 * 2.0 - fct3) - fct;
- weights.y = (t3 * 2.0 - fct3) + (-t2 * 3.0 + fct2) + 1.0;
- weights.z = (-t3 * 2.0 + fct3) + (t2 * 3.0 - (2.0 * fct2)) + fct;
- weights.w = fct3 - fct2;
- return weights;
-}
-
-/* TODO(fclem): This one is buggy, find why. (it's not the optimization!!) */
-vec4 get_weights_bspline(float t)
-{
- float t2 = t * t;
- float t3 = t2 * t;
-
- vec4 weights;
- /* GLSL Optimized version of key_curve_position_weights() */
- weights.xz = vec2(-0.16666666, -0.5) * t3 + (0.5 * t2 + 0.5 * vec2(-t, t) + 0.16666666);
- weights.y = (0.5 * t3 - t2 + 0.66666666);
- weights.w = (0.16666666 * t3);
- return weights;
-}
-
-vec4 interp_data(vec4 v0, vec4 v1, vec4 v2, vec4 v3, vec4 w)
-{
- return v0 * w.x + v1 * w.y + v2 * w.z + v3 * w.w;
-}
-
#ifdef TF_WORKAROUND
uniform int targetWidth;
uniform int targetHeight;
@@ -56,8 +15,8 @@ void main(void)
vec4 data0, data1, data2, data3;
hair_get_interp_attrs(data0, data1, data2, data3, interp_time);
- vec4 weights = get_weights_cardinal(interp_time);
- finalColor = interp_data(data0, data1, data2, data3, weights);
+ vec4 weights = hair_get_weights_cardinal(interp_time);
+ finalColor = hair_interp_data(data0, data1, data2, data3, weights);
#ifdef TF_WORKAROUND
int id = gl_VertexID - idOffset;
diff --git a/source/blender/draw/intern/shaders/common_math_lib.glsl b/source/blender/draw/intern/shaders/common_math_lib.glsl
index 0344b977139..479f9cd1827 100644
--- a/source/blender/draw/intern/shaders/common_math_lib.glsl
+++ b/source/blender/draw/intern/shaders/common_math_lib.glsl
@@ -86,11 +86,18 @@ float safe_rcp(float a) { return (a != 0.0) ? (1.0 / a) : 0.0; }
vec2 safe_rcp(vec2 a) { return mix(vec2(0.0), (1.0 / a), notEqual(a, vec2(0.0))); }
vec4 safe_rcp(vec4 a) { return mix(vec4(0.0), (1.0 / a), notEqual(a, vec4(0.0))); }
+float safe_sqrt(float a) { return sqrt(max(a, 0.0)); }
+
float sqr(float a) { return a * a; }
vec2 sqr(vec2 a) { return a * a; }
vec3 sqr(vec3 a) { return a * a; }
vec4 sqr(vec4 a) { return a * a; }
+/* Use manual powers for fixed powers. pow() can have unpredicatble results on some implementations.
+ * (see T87369, T87541) */
+float pow6(float x) { return sqr(sqr(x) * x); }
+float pow8(float x) { return sqr(sqr(sqr(x))); }
+
float len_squared(vec3 a) { return dot(a, a); }
float len_squared(vec2 a) { return dot(a, a); }
diff --git a/source/blender/draw/intern/shaders/common_pointcloud_lib.glsl b/source/blender/draw/intern/shaders/common_pointcloud_lib.glsl
index f007a8c2322..74b989441a2 100644
--- a/source/blender/draw/intern/shaders/common_pointcloud_lib.glsl
+++ b/source/blender/draw/intern/shaders/common_pointcloud_lib.glsl
@@ -12,7 +12,7 @@ in vec3 nor;
mat3 pointcloud_get_facing_matrix(vec3 p)
{
mat3 facing_mat;
- facing_mat[2] = normalize(ViewMatrixInverse[3].xyz - p);
+ facing_mat[2] = cameraVec(p);
facing_mat[1] = normalize(cross(ViewMatrixInverse[0].xyz, facing_mat[2]));
facing_mat[0] = cross(facing_mat[1], facing_mat[2]);
return facing_mat;
diff --git a/source/blender/draw/tests/draw_testing.cc b/source/blender/draw/tests/draw_testing.cc
new file mode 100644
index 00000000000..0104437e921
--- /dev/null
+++ b/source/blender/draw/tests/draw_testing.cc
@@ -0,0 +1,18 @@
+/* Apache License, Version 2.0 */
+
+#include "draw_testing.hh"
+
+#include "GPU_shader.h"
+
+#include "draw_manager_testing.h"
+
+namespace blender::draw {
+
+/* Base class for draw test cases. It will setup and tear down the GPU part around each test. */
+void DrawTest::SetUp()
+{
+ GPUTest::SetUp();
+ DRW_draw_state_init_gtests(GPU_SHADER_CFG_DEFAULT);
+}
+
+} // namespace blender::draw
diff --git a/source/blender/draw/tests/draw_testing.hh b/source/blender/draw/tests/draw_testing.hh
new file mode 100644
index 00000000000..ec0b15b611e
--- /dev/null
+++ b/source/blender/draw/tests/draw_testing.hh
@@ -0,0 +1,13 @@
+/* Apache License, Version 2.0 */
+
+#include "gpu_testing.hh"
+
+namespace blender::draw {
+
+/* Base class for draw test cases. It will setup and tear down the GPU part around each test. */
+class DrawTest : public blender::gpu::GPUTest {
+ public:
+ void SetUp() override;
+};
+
+} // namespace blender::draw
diff --git a/source/blender/draw/tests/shaders_test.cc b/source/blender/draw/tests/shaders_test.cc
index 96d544fd855..c96f22859ca 100644
--- a/source/blender/draw/tests/shaders_test.cc
+++ b/source/blender/draw/tests/shaders_test.cc
@@ -2,12 +2,12 @@
#include "testing/testing.h"
+#include "draw_testing.hh"
#include "intern/draw_manager_testing.h"
#include "GPU_context.h"
#include "GPU_init_exit.h"
#include "GPU_shader.h"
-#include "gpu_testing.hh"
#include "engines/eevee/eevee_private.h"
#include "engines/gpencil/gpencil_engine.h"
@@ -17,19 +17,9 @@
namespace blender::draw {
-/* Base class for draw test cases. It will setup and tear down the GPU part around each test. */
-class DrawTest : public blender::gpu::GPUTest {
- void SetUp() override
- {
- GPUTest::SetUp();
- DRW_draw_state_init_gtests(GPU_SHADER_CFG_DEFAULT);
- }
-};
-
TEST_F(DrawTest, workbench_glsl_shaders)
{
workbench_shader_library_ensure();
- DRW_draw_state_init_gtests(GPU_SHADER_CFG_DEFAULT);
const int MAX_WPD = 6;
WORKBENCH_PrivateData wpds[MAX_WPD];
diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c
index 2bfa417eb78..0b587191807 100644
--- a/source/blender/editors/animation/anim_channels_defines.c
+++ b/source/blender/editors/animation/anim_channels_defines.c
@@ -4666,7 +4666,7 @@ static void achannel_setting_flush_widget_cb(bContext *C, void *ale_npoin, void
}
/* UI updates */
- WM_event_add_notifier(C, NC_GPENCIL | ND_DATA, NULL);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
}
/* Tag for full animation update, so that the settings will have an effect. */
@@ -4674,7 +4674,7 @@ static void achannel_setting_flush_widget_cb(bContext *C, void *ale_npoin, void
DEG_id_tag_update(ale_setting->id, ID_RECALC_ANIMATION);
}
if (ale_setting->adt && ale_setting->adt->action) {
- /* Action is it's own datablock, so has to be tagged specifically. */
+ /* Action is its own datablock, so has to be tagged specifically. */
DEG_id_tag_update(&ale_setting->adt->action->id, ID_RECALC_ANIMATION);
}
@@ -5462,7 +5462,6 @@ void ANIM_channel_draw_widgets(const bContext *C,
prop = RNA_struct_find_property(&ptr, "use_mask_layer");
gp_rna_path = RNA_path_from_ID_to_property(&ptr, prop);
if (RNA_path_resolve_property(&id_ptr, gp_rna_path, &ptr, &prop)) {
- icon = ICON_LAYER_ACTIVE;
if (gpl->flag & GP_LAYER_USE_MASK) {
icon = ICON_MOD_MASK;
}
diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c
index 64082b08da9..6c6fab13b7a 100644
--- a/source/blender/editors/animation/anim_channels_edit.c
+++ b/source/blender/editors/animation/anim_channels_edit.c
@@ -1349,10 +1349,12 @@ static void join_groups_action_temp(bAction *act)
/* BLI_movelisttolist() doesn't touch first->prev and last->next pointers in its "dst" list.
* Ensure that after the reshuffling the list is properly terminated. */
- FCurve *act_fcurves_first = act->curves.first;
- act_fcurves_first->prev = NULL;
- FCurve *act_fcurves_last = act->curves.last;
- act_fcurves_last->next = NULL;
+ if (!BLI_listbase_is_empty(&act->curves)) {
+ FCurve *act_fcurves_first = act->curves.first;
+ act_fcurves_first->prev = NULL;
+ FCurve *act_fcurves_last = act->curves.last;
+ act_fcurves_last->next = NULL;
+ }
}
/* Change the order of anim-channels within action
diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c
index 4cc0413be5b..f229d48b4eb 100644
--- a/source/blender/editors/animation/keyframing.c
+++ b/source/blender/editors/animation/keyframing.c
@@ -483,7 +483,8 @@ int insert_bezt_fcurve(FCurve *fcu, const BezTriple *bezt, eInsertKeyFlags flag)
return i;
}
-/** Update the FCurve to allow insertion of `bezt` without modifying the curve shape.
+/**
+ * Update the FCurve to allow insertion of `bezt` without modifying the curve shape.
*
* Checks whether it is necessary to apply Bezier subdivision due to involvement of non-auto
* handles. If necessary, changes `bezt` handles from Auto to Aligned.
@@ -3034,19 +3035,9 @@ bool ED_autokeyframe_pchan(
ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA);
BLI_freelistN(&dsources);
- /* clear any unkeyed tags */
- if (pchan->bone) {
- pchan->bone->flag &= ~BONE_UNKEYED;
- }
-
return true;
}
- /* add unkeyed tags */
- if (pchan->bone) {
- pchan->bone->flag |= BONE_UNKEYED;
- }
-
return false;
}
diff --git a/source/blender/editors/armature/CMakeLists.txt b/source/blender/editors/armature/CMakeLists.txt
index 98c050950be..0030e78002b 100644
--- a/source/blender/editors/armature/CMakeLists.txt
+++ b/source/blender/editors/armature/CMakeLists.txt
@@ -17,6 +17,7 @@
set(INC
../include
+ ../../blenfont
../../blenkernel
../../blenlib
../../blentranslation
diff --git a/source/blender/editors/armature/armature_add.c b/source/blender/editors/armature/armature_add.c
index 68fff1091af..3902f6613a1 100644
--- a/source/blender/editors/armature/armature_add.c
+++ b/source/blender/editors/armature/armature_add.c
@@ -339,7 +339,7 @@ void postEditBoneDuplicate(struct ListBase *editbones, Object *ob)
}
BKE_pose_channels_hash_free(ob->pose);
- BKE_pose_channels_hash_make(ob->pose);
+ BKE_pose_channels_hash_ensure(ob->pose);
GHash *name_map = BLI_ghash_str_new(__func__);
@@ -390,7 +390,7 @@ static void updateDuplicateSubtarget(EditBone *dup_bone,
bConstraint *curcon;
ListBase *conlist;
- if ((pchan = BKE_pose_channel_verify(ob->pose, dup_bone->name))) {
+ if ((pchan = BKE_pose_channel_ensure(ob->pose, dup_bone->name))) {
if ((conlist = &pchan->constraints)) {
for (curcon = conlist->first; curcon; curcon = curcon->next) {
/* does this constraint have a subtarget in
@@ -825,7 +825,7 @@ static void updateDuplicateConstraintSettings(EditBone *dup_bone, EditBone *orig
bConstraint *curcon;
ListBase *conlist;
- if ((pchan = BKE_pose_channel_verify(ob->pose, dup_bone->name)) == NULL ||
+ if ((pchan = BKE_pose_channel_ensure(ob->pose, dup_bone->name)) == NULL ||
(conlist = &pchan->constraints) == NULL) {
return;
}
@@ -855,7 +855,7 @@ static void updateDuplicateCustomBoneShapes(bContext *C, EditBone *dup_bone, Obj
return;
}
bPoseChannel *pchan;
- pchan = BKE_pose_channel_verify(ob->pose, dup_bone->name);
+ pchan = BKE_pose_channel_ensure(ob->pose, dup_bone->name);
if (pchan->custom != NULL) {
Main *bmain = CTX_data_main(C);
@@ -885,12 +885,12 @@ static void copy_pchan(EditBone *src_bone, EditBone *dst_bone, Object *src_ob, O
if (src_ob->pose) {
bPoseChannel *chanold, *channew;
- chanold = BKE_pose_channel_verify(src_ob->pose, src_bone->name);
+ chanold = BKE_pose_channel_ensure(src_ob->pose, src_bone->name);
if (chanold) {
/* WARNING: this creates a new posechannel, but there will not be an attached bone
* yet as the new bones created here are still 'EditBones' not 'Bones'.
*/
- channew = BKE_pose_channel_verify(dst_ob->pose, dst_bone->name);
+ channew = BKE_pose_channel_ensure(dst_ob->pose, dst_bone->name);
if (channew) {
BKE_pose_channel_copy_data(channew, chanold);
@@ -1193,7 +1193,7 @@ static int armature_symmetrize_exec(bContext *C, wmOperator *op)
* is synchronized. */
bPoseChannel *pchan;
/* Make sure we clean up the old data before overwriting it */
- pchan = BKE_pose_channel_verify(obedit->pose, ebone_iter->temp.ebone->name);
+ pchan = BKE_pose_channel_ensure(obedit->pose, ebone_iter->temp.ebone->name);
BKE_pose_channel_free(pchan);
/* Sync pchan data */
copy_pchan(ebone_iter, ebone_iter->temp.ebone, obedit, obedit);
diff --git a/source/blender/editors/armature/armature_naming.c b/source/blender/editors/armature/armature_naming.c
index 8bcaf72f678..70154695dcd 100644
--- a/source/blender/editors/armature/armature_naming.c
+++ b/source/blender/editors/armature/armature_naming.c
@@ -525,8 +525,8 @@ void ARMATURE_OT_flip_names(wmOperatorType *ot)
"do_strip_numbers",
false,
"Strip Numbers",
- "Try to remove right-most dot-number from flipped names "
- "(WARNING: may result in incoherent naming in some cases)");
+ "Try to remove right-most dot-number from flipped names.\n"
+ "Warning: May result in incoherent naming in some cases");
}
/** \} */
diff --git a/source/blender/editors/armature/armature_select.c b/source/blender/editors/armature/armature_select.c
index 226253cc063..65f30c3729f 100644
--- a/source/blender/editors/armature/armature_select.c
+++ b/source/blender/editors/armature/armature_select.c
@@ -961,131 +961,145 @@ bool ED_armature_edit_deselect_all_visible_multi(bContext *C)
/** \name Select Cursor Pick API
* \{ */
-/* context: editmode armature in view3d */
-bool ED_armature_edit_select_pick(
- bContext *C, const int mval[2], bool extend, bool deselect, bool toggle)
+bool ED_armature_edit_select_pick_bone(bContext *C,
+ Base *basact,
+ EditBone *ebone,
+ const int selmask,
+ const bool extend,
+ const bool deselect,
+ const bool toggle)
{
- Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
- ViewContext vc;
- EditBone *nearBone = NULL;
- int selmask;
- Base *basact = NULL;
+ if (!ebone) {
+ return false;
+ }
- ED_view3d_viewcontext_init(C, &vc, depsgraph);
- vc.mval[0] = mval[0];
- vc.mval[1] = mval[1];
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+ View3D *v3d = CTX_wm_view3d(C);
- nearBone = get_nearest_editbonepoint(&vc, true, true, &basact, &selmask);
- if (nearBone) {
- ED_view3d_viewcontext_init_object(&vc, basact->object);
- bArmature *arm = vc.obedit->data;
+ BLI_assert(BKE_object_is_in_editmode(basact->object));
+ bArmature *arm = basact->object->data;
- if (!EBONE_SELECTABLE(arm, nearBone)) {
- return false;
- }
+ if (!EBONE_SELECTABLE(arm, ebone)) {
+ return false;
+ }
- if (!extend && !deselect && !toggle) {
- uint bases_len = 0;
- Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data(
- vc.view_layer, vc.v3d, &bases_len);
- ED_armature_edit_deselect_all_multi_ex(bases, bases_len);
- MEM_freeN(bases);
- }
+ if (!extend && !deselect && !toggle) {
+ uint bases_len = 0;
+ Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data(
+ view_layer, v3d, &bases_len);
+ ED_armature_edit_deselect_all_multi_ex(bases, bases_len);
+ MEM_freeN(bases);
+ }
- /* by definition the non-root connected bones have no root point drawn,
- * so a root selection needs to be delivered to the parent tip */
+ /* By definition the non-root connected bones have no root point drawn,
+ * so a root selection needs to be delivered to the parent tip. */
- if (selmask & BONE_SELECTED) {
- if (nearBone->parent && (nearBone->flag & BONE_CONNECTED)) {
- /* click in a chain */
- if (extend) {
- /* select this bone */
- nearBone->flag |= BONE_TIPSEL;
- nearBone->parent->flag |= BONE_TIPSEL;
- }
- else if (deselect) {
- /* deselect this bone */
- nearBone->flag &= ~(BONE_TIPSEL | BONE_SELECTED);
- /* only deselect parent tip if it is not selected */
- if (!(nearBone->parent->flag & BONE_SELECTED)) {
- nearBone->parent->flag &= ~BONE_TIPSEL;
- }
+ if (selmask & BONE_SELECTED) {
+ if (ebone->parent && (ebone->flag & BONE_CONNECTED)) {
+ /* Bone is in a chain. */
+ if (extend) {
+ /* Select this bone. */
+ ebone->flag |= BONE_TIPSEL;
+ ebone->parent->flag |= BONE_TIPSEL;
+ }
+ else if (deselect) {
+ /* Deselect this bone. */
+ ebone->flag &= ~(BONE_TIPSEL | BONE_SELECTED);
+ /* Only deselect parent tip if it is not selected. */
+ if (!(ebone->parent->flag & BONE_SELECTED)) {
+ ebone->parent->flag &= ~BONE_TIPSEL;
}
- else if (toggle) {
- /* hold shift inverts this bone's selection */
- if (nearBone->flag & BONE_SELECTED) {
- /* deselect this bone */
- nearBone->flag &= ~(BONE_TIPSEL | BONE_SELECTED);
- /* only deselect parent tip if it is not selected */
- if (!(nearBone->parent->flag & BONE_SELECTED)) {
- nearBone->parent->flag &= ~BONE_TIPSEL;
- }
- }
- else {
- /* select this bone */
- nearBone->flag |= BONE_TIPSEL;
- nearBone->parent->flag |= BONE_TIPSEL;
+ }
+ else if (toggle) {
+ /* Toggle inverts this bone's selection. */
+ if (ebone->flag & BONE_SELECTED) {
+ /* Deselect this bone. */
+ ebone->flag &= ~(BONE_TIPSEL | BONE_SELECTED);
+ /* Only deselect parent tip if it is not selected. */
+ if (!(ebone->parent->flag & BONE_SELECTED)) {
+ ebone->parent->flag &= ~BONE_TIPSEL;
}
}
else {
- /* select this bone */
- nearBone->flag |= BONE_TIPSEL;
- nearBone->parent->flag |= BONE_TIPSEL;
+ /* Select this bone. */
+ ebone->flag |= BONE_TIPSEL;
+ ebone->parent->flag |= BONE_TIPSEL;
}
}
else {
- if (extend) {
- nearBone->flag |= (BONE_TIPSEL | BONE_ROOTSEL);
- }
- else if (deselect) {
- nearBone->flag &= ~(BONE_TIPSEL | BONE_ROOTSEL);
- }
- else if (toggle) {
- /* hold shift inverts this bone's selection */
- if (nearBone->flag & BONE_SELECTED) {
- nearBone->flag &= ~(BONE_TIPSEL | BONE_ROOTSEL);
- }
- else {
- nearBone->flag |= (BONE_TIPSEL | BONE_ROOTSEL);
- }
- }
- else {
- nearBone->flag |= (BONE_TIPSEL | BONE_ROOTSEL);
- }
+ /* Select this bone. */
+ ebone->flag |= BONE_TIPSEL;
+ ebone->parent->flag |= BONE_TIPSEL;
}
}
else {
if (extend) {
- nearBone->flag |= selmask;
+ ebone->flag |= (BONE_TIPSEL | BONE_ROOTSEL);
}
else if (deselect) {
- nearBone->flag &= ~selmask;
+ ebone->flag &= ~(BONE_TIPSEL | BONE_ROOTSEL);
}
- else if (toggle && (nearBone->flag & selmask)) {
- nearBone->flag &= ~selmask;
+ else if (toggle) {
+ /* Toggle inverts this bone's selection. */
+ if (ebone->flag & BONE_SELECTED) {
+ ebone->flag &= ~(BONE_TIPSEL | BONE_ROOTSEL);
+ }
+ else {
+ ebone->flag |= (BONE_TIPSEL | BONE_ROOTSEL);
+ }
}
else {
- nearBone->flag |= selmask;
+ ebone->flag |= (BONE_TIPSEL | BONE_ROOTSEL);
}
}
-
- ED_armature_edit_sync_selection(arm->edbo);
-
- /* then now check for active status */
- if (ED_armature_ebone_selectflag_get(nearBone)) {
- arm->act_edbone = nearBone;
+ }
+ else {
+ if (extend) {
+ ebone->flag |= selmask;
}
-
- if (vc.view_layer->basact != basact) {
- ED_object_base_activate(C, basact);
+ else if (deselect) {
+ ebone->flag &= ~selmask;
+ }
+ else if (toggle && (ebone->flag & selmask)) {
+ ebone->flag &= ~selmask;
}
+ else {
+ ebone->flag |= selmask;
+ }
+ }
- WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, basact->object);
- DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE);
- return true;
+ ED_armature_edit_sync_selection(arm->edbo);
+
+ /* Then now check for active status. */
+ if (ED_armature_ebone_selectflag_get(ebone)) {
+ arm->act_edbone = ebone;
+ }
+
+ if (view_layer->basact != basact) {
+ ED_object_base_activate(C, basact);
}
- return false;
+ WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, basact->object);
+ DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE);
+ return true;
+}
+
+/* context: editmode armature in view3d */
+bool ED_armature_edit_select_pick(
+ bContext *C, const int mval[2], bool extend, bool deselect, bool toggle)
+{
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ ViewContext vc;
+ EditBone *nearBone = NULL;
+ int selmask;
+ Base *basact = NULL;
+
+ ED_view3d_viewcontext_init(C, &vc, depsgraph);
+ vc.mval[0] = mval[0];
+ vc.mval[1] = mval[1];
+
+ nearBone = get_nearest_editbonepoint(&vc, true, true, &basact, &selmask);
+ return ED_armature_edit_select_pick_bone(C, basact, nearBone, selmask, extend, deselect, toggle);
}
/** \} */
@@ -2127,7 +2141,7 @@ static int armature_select_mirror_exec(bContext *C, wmOperator *op)
void ARMATURE_OT_select_mirror(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Flip Active/Selected Bone";
+ ot->name = "Select Mirror";
ot->idname = "ARMATURE_OT_select_mirror";
ot->description = "Mirror the bone selection";
diff --git a/source/blender/editors/armature/pose_edit.c b/source/blender/editors/armature/pose_edit.c
index e65871c0896..f5daa427149 100644
--- a/source/blender/editors/armature/pose_edit.c
+++ b/source/blender/editors/armature/pose_edit.c
@@ -593,8 +593,8 @@ void POSE_OT_flip_names(wmOperatorType *ot)
"do_strip_numbers",
false,
"Strip Numbers",
- "Try to remove right-most dot-number from flipped names "
- "(WARNING: may result in incoherent naming in some cases)");
+ "Try to remove right-most dot-number from flipped names.\n"
+ "Warning: May result in incoherent naming in some cases");
}
/* ------------------ */
diff --git a/source/blender/editors/armature/pose_lib.c b/source/blender/editors/armature/pose_lib.c
index dd90f9f2cc3..21fcbf8886b 100644
--- a/source/blender/editors/armature/pose_lib.c
+++ b/source/blender/editors/armature/pose_lib.c
@@ -1108,17 +1108,6 @@ static void poselib_keytag_pose(bContext *C, Scene *scene, tPoseLib_PreviewData
if (autokey) {
/* Add data-source override for the PoseChannel, to be used later. */
ANIM_relative_keyingset_add_source(&dsources, &pld->ob->id, &RNA_PoseBone, pchan);
-
- /* clear any unkeyed tags */
- if (pchan->bone) {
- pchan->bone->flag &= ~BONE_UNKEYED;
- }
- }
- else {
- /* add unkeyed tags */
- if (pchan->bone) {
- pchan->bone->flag |= BONE_UNKEYED;
- }
}
}
}
diff --git a/source/blender/editors/armature/pose_select.c b/source/blender/editors/armature/pose_select.c
index a3f97000509..8fc06a5f962 100644
--- a/source/blender/editors/armature/pose_select.c
+++ b/source/blender/editors/armature/pose_select.c
@@ -138,6 +138,106 @@ void ED_pose_bone_select(Object *ob, bPoseChannel *pchan, bool select)
}
}
+void ED_armature_pose_select_pick_bone(ViewLayer *view_layer,
+ View3D *v3d,
+ Object *ob,
+ Bone *bone,
+ const bool extend,
+ const bool deselect,
+ const bool toggle)
+{
+ if (!ob || !ob->pose) {
+ return;
+ }
+
+ Object *ob_act = OBACT(view_layer);
+ BLI_assert(OBEDIT_FROM_VIEW_LAYER(view_layer) == NULL);
+
+ /* If the bone cannot be affected, don't do anything. */
+ if (bone == NULL || (bone->flag & BONE_UNSELECTABLE)) {
+ return;
+ }
+ bArmature *arm = ob->data;
+
+ /* Since we do unified select, we don't shift+select a bone if the
+ * armature object was not active yet.
+ * Note, special exception for armature mode so we can do multi-select
+ * we could check for multi-select explicitly but think its fine to
+ * always give predictable behavior in weight paint mode - campbell */
+ if ((ob_act == NULL) || ((ob_act != ob) && (ob_act->mode & OB_MODE_ALL_WEIGHT_PAINT) == 0)) {
+ /* When we are entering into posemode via toggle-select,
+ * from another active object - always select the bone. */
+ if (!extend && !deselect && toggle) {
+ /* Re-select the bone again later in this function. */
+ bone->flag &= ~BONE_SELECTED;
+ }
+ }
+
+ if (!extend && !deselect && !toggle) {
+ {
+ /* Don't use 'BKE_object_pose_base_array_get_unique'
+ * because we may be selecting from object mode. */
+ FOREACH_VISIBLE_BASE_BEGIN (view_layer, v3d, base_iter) {
+ Object *ob_iter = base_iter->object;
+ if ((ob_iter->type == OB_ARMATURE) && (ob_iter->mode & OB_MODE_POSE)) {
+ if (ED_pose_deselect_all(ob_iter, SEL_DESELECT, true)) {
+ ED_pose_bone_select_tag_update(ob_iter);
+ }
+ }
+ }
+ FOREACH_VISIBLE_BASE_END;
+ }
+ bone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
+ arm->act_bone = bone;
+ }
+ else {
+ if (extend) {
+ bone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
+ arm->act_bone = bone;
+ }
+ else if (deselect) {
+ bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
+ }
+ else if (toggle) {
+ if (bone->flag & BONE_SELECTED) {
+ /* If not active, we make it active. */
+ if (bone != arm->act_bone) {
+ arm->act_bone = bone;
+ }
+ else {
+ bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
+ }
+ }
+ else {
+ bone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
+ arm->act_bone = bone;
+ }
+ }
+ }
+
+ if (ob_act) {
+ /* In weightpaint we select the associated vertex group too. */
+ if (ob_act->mode & OB_MODE_ALL_WEIGHT_PAINT) {
+ if (bone == arm->act_bone) {
+ ED_vgroup_select_by_name(ob_act, bone->name);
+ DEG_id_tag_update(&ob_act->id, ID_RECALC_GEOMETRY);
+ }
+ }
+ /* If there are some dependencies for visualizing armature state
+ * (e.g. Mask Modifier in 'Armature' mode), force update.
+ */
+ else if (arm->flag & ARM_HAS_VIZ_DEPS) {
+ /* NOTE: ob not ob_act here is intentional - it's the source of the
+ * bones being selected [T37247]
+ */
+ DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
+ }
+
+ /* Tag armature for copy-on-write update (since act_bone is in armature not object). */
+ DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE);
+ }
+}
+
/**
* Called for mode-less pose selection.
* assumes the active object is still on old situation.
@@ -159,96 +259,12 @@ bool ED_armature_pose_select_pick_with_buffer(ViewLayer *view_layer,
return 0;
}
- Object *ob_act = OBACT(view_layer);
- BLI_assert(OBEDIT_FROM_VIEW_LAYER(view_layer) == NULL);
-
/* Callers happen to already get the active base */
Base *base_dummy = NULL;
nearBone = ED_armature_pick_bone_from_selectbuffer(
&base, 1, buffer, hits, 1, do_nearest, &base_dummy);
- /* if the bone cannot be affected, don't do anything */
- if ((nearBone) && !(nearBone->flag & BONE_UNSELECTABLE)) {
- bArmature *arm = ob->data;
-
- /* since we do unified select, we don't shift+select a bone if the
- * armature object was not active yet.
- * note, special exception for armature mode so we can do multi-select
- * we could check for multi-select explicitly but think its fine to
- * always give predictable behavior in weight paint mode - campbell */
- if ((ob_act == NULL) || ((ob_act != ob) && (ob_act->mode & OB_MODE_ALL_WEIGHT_PAINT) == 0)) {
- /* when we are entering into posemode via toggle-select,
- * from another active object - always select the bone. */
- if (!extend && !deselect && toggle) {
- /* re-select below */
- nearBone->flag &= ~BONE_SELECTED;
- }
- }
-
- if (!extend && !deselect && !toggle) {
- {
- /* Don't use 'BKE_object_pose_base_array_get_unique'
- * because we may be selecting from object mode. */
- FOREACH_VISIBLE_BASE_BEGIN (view_layer, v3d, base_iter) {
- Object *ob_iter = base_iter->object;
- if ((ob_iter->type == OB_ARMATURE) && (ob_iter->mode & OB_MODE_POSE)) {
- if (ED_pose_deselect_all(ob_iter, SEL_DESELECT, true)) {
- ED_pose_bone_select_tag_update(ob_iter);
- }
- }
- }
- FOREACH_VISIBLE_BASE_END;
- }
- nearBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
- arm->act_bone = nearBone;
- }
- else {
- if (extend) {
- nearBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
- arm->act_bone = nearBone;
- }
- else if (deselect) {
- nearBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
- }
- else if (toggle) {
- if (nearBone->flag & BONE_SELECTED) {
- /* if not active, we make it active */
- if (nearBone != arm->act_bone) {
- arm->act_bone = nearBone;
- }
- else {
- nearBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
- }
- }
- else {
- nearBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
- arm->act_bone = nearBone;
- }
- }
- }
-
- if (ob_act) {
- /* in weightpaint we select the associated vertex group too */
- if (ob_act->mode & OB_MODE_ALL_WEIGHT_PAINT) {
- if (nearBone == arm->act_bone) {
- ED_vgroup_select_by_name(ob_act, nearBone->name);
- DEG_id_tag_update(&ob_act->id, ID_RECALC_GEOMETRY);
- }
- }
- /* if there are some dependencies for visualizing armature state
- * (e.g. Mask Modifier in 'Armature' mode), force update
- */
- else if (arm->flag & ARM_HAS_VIZ_DEPS) {
- /* NOTE: ob not ob_act here is intentional - it's the source of the
- * bones being selected [T37247]
- */
- DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
- }
-
- /* tag armature for copy-on-write update (since act_bone is in armature not object) */
- DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE);
- }
- }
+ ED_armature_pose_select_pick_bone(view_layer, v3d, ob, nearBone, extend, deselect, toggle);
return nearBone != NULL;
}
@@ -1268,7 +1284,7 @@ static int pose_select_mirror_exec(bContext *C, wmOperator *op)
void POSE_OT_select_mirror(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Flip Active/Selected Bone";
+ ot->name = "Select Mirror";
ot->idname = "POSE_OT_select_mirror";
ot->description = "Mirror the bone selection";
diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c
index 93d36abe792..d32faf9a9ea 100644
--- a/source/blender/editors/armature/pose_slide.c
+++ b/source/blender/editors/armature/pose_slide.c
@@ -19,6 +19,28 @@
/** \file
* \ingroup edarmature
+ *
+ * Pose 'Sliding' Tools
+ * ====================
+ *
+ * - Push & Relax, Breakdowner
+
+ * These tools provide the animator with various capabilities
+ * for interactively controlling the spacing of poses, but also
+ * for 'pushing' and/or 'relaxing' extremes as they see fit.
+ *
+ * - Propagate
+
+ * This tool copies elements of the selected pose to successive
+ * keyframes, allowing the animator to go back and modify the poses
+ * for some "static" pose controls, without having to repeatedly
+ * doing a "next paste" dance.
+ *
+ * - Pose Sculpting (TODO)
+
+ * This is yet to be implemented, but the idea here is to use
+ * sculpting techniques to make it easier to pose rigs by allowing
+ * rigs to be manipulated using a familiar paint-based interface.
*/
#include "MEM_guardedalloc.h"
@@ -33,6 +55,7 @@
#include "DNA_armature_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
+#include "DNA_vec_types.h"
#include "BKE_fcurve.h"
#include "BKE_nla.h"
@@ -41,6 +64,7 @@
#include "BKE_layer.h"
#include "BKE_object.h"
#include "BKE_report.h"
+#include "BKE_screen.h"
#include "BKE_unit.h"
#include "RNA_access.h"
@@ -50,45 +74,71 @@
#include "WM_types.h"
#include "UI_interface.h"
+#include "UI_resources.h"
#include "ED_armature.h"
#include "ED_keyframes_draw.h"
#include "ED_markers.h"
#include "ED_numinput.h"
#include "ED_screen.h"
+#include "ED_space_api.h"
+
+#include "GPU_immediate.h"
+#include "GPU_immediate_util.h"
+#include "GPU_matrix.h"
+#include "GPU_state.h"
#include "armature_intern.h"
-/* **************************************************** */
-/* == POSE 'SLIDING' TOOLS ==
- *
- * A) Push & Relax, Breakdowner
- * These tools provide the animator with various capabilities
- * for interactively controlling the spacing of poses, but also
- * for 'pushing' and/or 'relaxing' extremes as they see fit.
- *
- * B) Propagate
- * This tool copies elements of the selected pose to successive
- * keyframes, allowing the animator to go back and modify the poses
- * for some "static" pose controls, without having to repeatedly
- * doing a "next paste" dance.
- *
- * C) Pose Sculpting
- * This is yet to be implemented, but the idea here is to use
- * sculpting techniques to make it easier to pose rigs by allowing
- * rigs to be manipulated using a familiar paint-based interface.
- */
+#include "BLF_api.h"
+
+/* Pixel distance from 0% to 100%. */
+#define SLIDE_PIXEL_DISTANCE (300 * U.pixelsize)
+#define OVERSHOOT_RANGE_DELTA 0.2f
+
/* **************************************************** */
/* A) Push & Relax, Breakdowner */
-/* Temporary data shared between these operators */
+/** Axis Locks. */
+typedef enum ePoseSlide_AxisLock {
+ PS_LOCK_X = (1 << 0),
+ PS_LOCK_Y = (1 << 1),
+ PS_LOCK_Z = (1 << 2),
+} ePoseSlide_AxisLock;
+
+/** Pose Sliding Modes. */
+typedef enum ePoseSlide_Modes {
+ /** Exaggerate the pose. */
+ POSESLIDE_PUSH = 0,
+ /** soften the pose. */
+ POSESLIDE_RELAX,
+ /** Slide between the endpoint poses, finding a 'soft' spot. */
+ POSESLIDE_BREAKDOWN,
+ POSESLIDE_PUSH_REST,
+ POSESLIDE_RELAX_REST,
+} ePoseSlide_Modes;
+
+/** Transforms/Channels to Affect. */
+typedef enum ePoseSlide_Channels {
+ PS_TFM_ALL = 0, /* All transforms and properties */
+
+ PS_TFM_LOC, /* Loc/Rot/Scale */
+ PS_TFM_ROT,
+ PS_TFM_SIZE,
+
+ PS_TFM_BBONE_SHAPE, /* Bendy Bones */
+
+ PS_TFM_PROPS, /* Custom Properties */
+} ePoseSlide_Channels;
+
+/** Temporary data shared between these operators. */
typedef struct tPoseSlideOp {
/** current scene */
Scene *scene;
/** area that we're operating in (needed for modal()) */
ScrArea *area;
- /** region that we're operating in (needed for modal()) */
- ARegion *region;
+ /** Header of the region used for drawing the slider. */
+ ARegion *region_header;
/** len of the PoseSlideObject array. */
uint objects_len;
@@ -105,55 +155,57 @@ typedef struct tPoseSlideOp {
/** frame after current frame (blend-to) - global time */
int nextFrame;
- /** sliding mode (ePoseSlide_Modes) */
- short mode;
+ /** Sliding Mode. */
+ ePoseSlide_Modes mode;
/** unused for now, but can later get used for storing runtime settings.... */
short flag;
- /** which transforms/channels are affected (ePoseSlide_Channels) */
- short channels;
- /** axis-limits for transforms (ePoseSlide_AxisLock) */
- short axislock;
+ /* Store overlay settings when invoking the operator. Bones will be temporarily hidden. */
+ int overlay_flag;
+
+ /** Which transforms/channels are affected. */
+ ePoseSlide_Channels channels;
+ /** Axis-limits for transforms. */
+ ePoseSlide_AxisLock axislock;
- /** 0-1 value for determining the influence of whatever is relevant */
- float percentage;
+ /** Allow overshoot or clamp between 0% and 100%. */
+ bool overshoot;
- /** numeric input */
+ /** Reduces factor delta from mouse movement. */
+ bool precision;
+
+ /** Move factor in 10% steps. */
+ bool increments;
+
+ /** Draw callback handler. */
+ void *draw_handle;
+
+ /** Accumulative, unclamped and unrounded factor. */
+ float raw_factor;
+
+ /** 0-1 value for determining the influence of whatever is relevant. */
+ float factor;
+
+ /** Last cursor position in screen space used for mouse movement delta calculation. */
+ int last_cursor_x;
+
+ /** Numeric input. */
NumInput num;
struct tPoseSlideObject *ob_data_array;
} tPoseSlideOp;
typedef struct tPoseSlideObject {
- Object *ob; /* active object that Pose Info comes from */
- float prevFrameF; /* prevFrame, but in local action time (for F-Curve lookups to work) */
- float nextFrameF; /* nextFrame, but in local action time (for F-Curve lookups to work) */
+ /** Active object that Pose Info comes from. */
+ Object *ob;
+ /** `prevFrame`, but in local action time (for F-Curve look-ups to work). */
+ float prevFrameF;
+ /** `nextFrame`, but in local action time (for F-Curve look-ups to work). */
+ float nextFrameF;
bool valid;
} tPoseSlideObject;
-/* Pose Sliding Modes */
-typedef enum ePoseSlide_Modes {
- POSESLIDE_PUSH = 0, /* exaggerate the pose... */
- POSESLIDE_RELAX, /* soften the pose... */
- POSESLIDE_BREAKDOWN, /* slide between the endpoint poses, finding a 'soft' spot */
- POSESLIDE_PUSH_REST,
- POSESLIDE_RELAX_REST,
-} ePoseSlide_Modes;
-
-/* Transforms/Channels to Affect */
-typedef enum ePoseSlide_Channels {
- PS_TFM_ALL = 0, /* All transforms and properties */
-
- PS_TFM_LOC, /* Loc/Rot/Scale */
- PS_TFM_ROT,
- PS_TFM_SIZE,
-
- PS_TFM_BBONE_SHAPE, /* Bendy Bones */
-
- PS_TFM_PROPS, /* Custom Properties */
-} ePoseSlide_Channels;
-
-/* Property enum for ePoseSlide_Channels */
+/** Property enum for #ePoseSlide_Channels. */
static const EnumPropertyItem prop_channels_types[] = {
{PS_TFM_ALL,
"ALL",
@@ -168,13 +220,6 @@ static const EnumPropertyItem prop_channels_types[] = {
{0, NULL, 0, NULL, NULL},
};
-/* Axis Locks */
-typedef enum ePoseSlide_AxisLock {
- PS_LOCK_X = (1 << 0),
- PS_LOCK_Y = (1 << 1),
- PS_LOCK_Z = (1 << 2),
-} ePoseSlide_AxisLock;
-
/* Property enum for ePoseSlide_AxisLock */
static const EnumPropertyItem prop_axis_lock_types[] = {
{0, "FREE", 0, "Free", "All axes are affected"},
@@ -187,33 +232,284 @@ static const EnumPropertyItem prop_axis_lock_types[] = {
/* ------------------------------------ */
-/* operator init */
+static void draw_overshoot_triangle(const uint8_t color[4],
+ const bool facing_right,
+ const float x,
+ const float y)
+{
+ const uint shdr_pos_2d = GPU_vertformat_attr_add(
+ immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ GPU_blend(GPU_BLEND_ALPHA);
+ GPU_polygon_smooth(true);
+ immUniformColor3ubvAlpha(color, 225);
+ const float triangle_side_length = facing_right ? 6 * U.pixelsize : -6 * U.pixelsize;
+ const float triangle_offset = facing_right ? 2 * U.pixelsize : -2 * U.pixelsize;
+
+ immBegin(GPU_PRIM_TRIS, 3);
+ immVertex2f(shdr_pos_2d, x + triangle_offset + triangle_side_length, y);
+ immVertex2f(shdr_pos_2d, x + triangle_offset, y + triangle_side_length / 2);
+ immVertex2f(shdr_pos_2d, x + triangle_offset, y - triangle_side_length / 2);
+ immEnd();
+
+ GPU_polygon_smooth(false);
+ GPU_blend(GPU_BLEND_NONE);
+ immUnbindProgram();
+}
+
+static void draw_ticks(const float start_factor,
+ const float end_factor,
+ const float line_start[2],
+ const float base_tick_height,
+ const float line_width,
+ const uint8_t color_overshoot[4],
+ const uint8_t color_line[4])
+{
+ /* Use factor represented as 0-100 int to avoid floating point precision problems. */
+ const int tick_increment = 10;
+
+ /* Round initial_tick_factor up to the next tick_increment. */
+ int tick_percentage = ceil((start_factor * 100) / tick_increment) * tick_increment;
+
+ while (tick_percentage <= (int)(end_factor * 100)) {
+ float tick_height;
+ /* Different ticks have different heights. Multiples of 100% are the tallest, 50% is a bit
+ * smaller and the rest is the minimum size. */
+ if (tick_percentage % 100 == 0) {
+ tick_height = base_tick_height;
+ }
+ else if (tick_percentage % 50 == 0) {
+ tick_height = base_tick_height * 0.8;
+ }
+ else {
+ tick_height = base_tick_height * 0.5;
+ }
+
+ const float x = line_start[0] +
+ (((float)tick_percentage / 100) - start_factor) * SLIDE_PIXEL_DISTANCE;
+ const rctf tick_rect = {
+ .xmin = x - (line_width / 2),
+ .xmax = x + (line_width / 2),
+ .ymin = line_start[1] - (tick_height / 2),
+ .ymax = line_start[1] + (tick_height / 2),
+ };
+
+ if (tick_percentage < 0 || tick_percentage > 100) {
+ UI_draw_roundbox_3ub_alpha(&tick_rect, true, 1, color_overshoot, 255);
+ }
+ else {
+ UI_draw_roundbox_3ub_alpha(&tick_rect, true, 1, color_line, 255);
+ }
+ tick_percentage += tick_increment;
+ }
+}
+
+static void draw_main_line(const rctf *main_line_rect,
+ const float factor,
+ const bool overshoot,
+ const uint8_t color_overshoot[4],
+ const uint8_t color_line[4])
+{
+ if (overshoot) {
+ /* In overshoot mode, draw the 0-100% range differently to provide a visual reference. */
+ const float line_zero_percent = main_line_rect->xmin -
+ ((factor - 0.5f - OVERSHOOT_RANGE_DELTA) *
+ SLIDE_PIXEL_DISTANCE);
+
+ const float clamped_line_zero_percent = clamp_f(
+ line_zero_percent, main_line_rect->xmin, main_line_rect->xmax);
+ const float clamped_line_hundred_percent = clamp_f(
+ line_zero_percent + SLIDE_PIXEL_DISTANCE, main_line_rect->xmin, main_line_rect->xmax);
+
+ const rctf left_overshoot_line_rect = {
+ .xmin = main_line_rect->xmin,
+ .xmax = clamped_line_zero_percent,
+ .ymin = main_line_rect->ymin,
+ .ymax = main_line_rect->ymax,
+ };
+ const rctf right_overshoot_line_rect = {
+ .xmin = clamped_line_hundred_percent,
+ .xmax = main_line_rect->xmax,
+ .ymin = main_line_rect->ymin,
+ .ymax = main_line_rect->ymax,
+ };
+ UI_draw_roundbox_3ub_alpha(&left_overshoot_line_rect, true, 0, color_overshoot, 255);
+ UI_draw_roundbox_3ub_alpha(&right_overshoot_line_rect, true, 0, color_overshoot, 255);
+
+ const rctf non_overshoot_line_rect = {
+ .xmin = clamped_line_zero_percent,
+ .xmax = clamped_line_hundred_percent,
+ .ymin = main_line_rect->ymin,
+ .ymax = main_line_rect->ymax,
+ };
+ UI_draw_roundbox_3ub_alpha(&non_overshoot_line_rect, true, 0, color_line, 255);
+ }
+ else {
+ UI_draw_roundbox_3ub_alpha(main_line_rect, true, 0, color_line, 255);
+ }
+}
+
+static void draw_backdrop(const int fontid,
+ const rctf *main_line_rect,
+ const float color_bg[4],
+ const short region_y_size,
+ const float base_tick_height)
+{
+ float string_pixel_size[2];
+ const char *percentage_string_placeholder = "000%%";
+ BLF_width_and_height(fontid,
+ percentage_string_placeholder,
+ sizeof(percentage_string_placeholder),
+ &string_pixel_size[0],
+ &string_pixel_size[1]);
+ const float pad[2] = {(region_y_size - base_tick_height) / 2, 2.0f * U.pixelsize};
+ const rctf backdrop_rect = {
+ .xmin = main_line_rect->xmin - string_pixel_size[0] - pad[0],
+ .xmax = main_line_rect->xmax + pad[0],
+ .ymin = pad[1],
+ .ymax = region_y_size - pad[1],
+ };
+ UI_draw_roundbox_aa(&backdrop_rect, true, 4.0f, color_bg);
+}
+
+/**
+ * Draw an on screen Slider for a Pose Slide Operator.
+ */
+static void pose_slide_draw_2d_slider(const struct bContext *UNUSED(C), ARegion *region, void *arg)
+{
+ tPoseSlideOp *pso = arg;
+
+ /* Only draw in region from which the Operator was started. */
+ if (region != pso->region_header) {
+ return;
+ }
+
+ uint8_t color_text[4];
+ uint8_t color_line[4];
+ uint8_t color_handle[4];
+ uint8_t color_overshoot[4];
+ float color_bg[4];
+
+ /* Get theme colors. */
+ UI_GetThemeColor4ubv(TH_TEXT, color_text);
+ UI_GetThemeColor4ubv(TH_TEXT, color_line);
+ UI_GetThemeColor4ubv(TH_TEXT, color_overshoot);
+ UI_GetThemeColor4ubv(TH_ACTIVE, color_handle);
+ UI_GetThemeColor3fv(TH_BACK, color_bg);
+
+ color_bg[3] = 0.5f;
+ color_overshoot[0] = color_overshoot[0] * 0.7;
+ color_overshoot[1] = color_overshoot[1] * 0.7;
+ color_overshoot[2] = color_overshoot[2] * 0.7;
+
+ /* Get the default font. */
+ const uiStyle *style = UI_style_get();
+ const uiFontStyle *fstyle = &style->widget;
+ const int fontid = fstyle->uifont_id;
+ BLF_color3ubv(fontid, color_text);
+ BLF_rotation(fontid, 0.0f);
+
+ const float line_width = 1.5 * U.pixelsize;
+ const float base_tick_height = 12.0 * U.pixelsize;
+ const float line_y = region->winy / 2;
+
+ rctf main_line_rect = {
+ .xmin = (region->winx / 2) - (SLIDE_PIXEL_DISTANCE / 2),
+ .xmax = (region->winx / 2) + (SLIDE_PIXEL_DISTANCE / 2),
+ .ymin = line_y - line_width / 2,
+ .ymax = line_y + line_width / 2,
+ };
+ float line_start_factor = 0;
+ int handle_pos_x = main_line_rect.xmin + SLIDE_PIXEL_DISTANCE * pso->factor;
+
+ if (pso->overshoot) {
+ main_line_rect.xmin = main_line_rect.xmin - SLIDE_PIXEL_DISTANCE * OVERSHOOT_RANGE_DELTA;
+ main_line_rect.xmax = main_line_rect.xmax + SLIDE_PIXEL_DISTANCE * OVERSHOOT_RANGE_DELTA;
+ line_start_factor = pso->factor - 0.5f - OVERSHOOT_RANGE_DELTA;
+ handle_pos_x = region->winx / 2;
+ }
+
+ draw_backdrop(fontid, &main_line_rect, color_bg, pso->region_header->winy, base_tick_height);
+
+ draw_main_line(&main_line_rect, pso->factor, pso->overshoot, color_overshoot, color_line);
+
+ const float factor_range = pso->overshoot ? 1 + OVERSHOOT_RANGE_DELTA * 2 : 1;
+ const float line_start_position[2] = {main_line_rect.xmin, line_y};
+ draw_ticks(line_start_factor,
+ line_start_factor + factor_range,
+ line_start_position,
+ base_tick_height,
+ line_width,
+ color_overshoot,
+ color_line);
+
+ /* Draw triangles at the ends of the line in overshoot mode to indicate direction of 0-100%
+ * range.*/
+ if (pso->overshoot) {
+ if (pso->factor > 1 + OVERSHOOT_RANGE_DELTA + 0.5) {
+ draw_overshoot_triangle(color_line, false, main_line_rect.xmin, line_y);
+ }
+ if (pso->factor < 0 - OVERSHOOT_RANGE_DELTA - 0.5) {
+ draw_overshoot_triangle(color_line, true, main_line_rect.xmax, line_y);
+ }
+ }
+
+ char percentage_string[256];
+
+ /* Draw handle indicating current factor. */
+ const rctf handle_rect = {
+ .xmin = handle_pos_x - (line_width),
+ .xmax = handle_pos_x + (line_width),
+ .ymin = line_y - (base_tick_height / 2),
+ .ymax = line_y + (base_tick_height / 2),
+ };
+
+ UI_draw_roundbox_3ub_alpha(&handle_rect, true, 1, color_handle, 255);
+ BLI_snprintf(percentage_string, sizeof(percentage_string), "%.0f%%", pso->factor * 100);
+
+ /* Draw percentage string. */
+ float percentage_string_pixel_size[2];
+ BLF_width_and_height(fontid,
+ percentage_string,
+ sizeof(percentage_string),
+ &percentage_string_pixel_size[0],
+ &percentage_string_pixel_size[1]);
+
+ BLF_position(fontid,
+ main_line_rect.xmin - 24.0 * U.pixelsize - percentage_string_pixel_size[0] / 2,
+ (region->winy / 2) - percentage_string_pixel_size[1] / 2,
+ 0.0f);
+ BLF_draw(fontid, percentage_string, sizeof(percentage_string));
+}
+
+/** Operator custom-data initialization. */
static int pose_slide_init(bContext *C, wmOperator *op, ePoseSlide_Modes mode)
{
tPoseSlideOp *pso;
- /* init slide-op data */
+ /* Init slide-op data. */
pso = op->customdata = MEM_callocN(sizeof(tPoseSlideOp), "tPoseSlideOp");
- /* get info from context */
+ /* Get info from context. */
pso->scene = CTX_data_scene(C);
- pso->area = CTX_wm_area(C); /* only really needed when doing modal() */
- pso->region = CTX_wm_region(C); /* only really needed when doing modal() */
+ pso->area = CTX_wm_area(C); /* Only really needed when doing modal(). */
+ pso->region_header = CTX_wm_region(C); /* Only really needed when doing modal(). */
pso->cframe = pso->scene->r.cfra;
pso->mode = mode;
- /* set range info from property values - these may get overridden for the invoke() */
- pso->percentage = RNA_float_get(op->ptr, "percentage");
+ /* Set range info from property values - these may get overridden for the invoke(). */
+ pso->factor = RNA_float_get(op->ptr, "factor");
+ pso->raw_factor = pso->factor;
pso->prevFrame = RNA_int_get(op->ptr, "prev_frame");
pso->nextFrame = RNA_int_get(op->ptr, "next_frame");
- /* get the set of properties/axes that can be operated on */
+ /* Get the set of properties/axes that can be operated on. */
pso->channels = RNA_enum_get(op->ptr, "channels");
pso->axislock = RNA_enum_get(op->ptr, "axis_lock");
- /* for each Pose-Channel which gets affected, get the F-Curves for that channel
- * and set the relevant transform flags... */
+ /* For each Pose-Channel which gets affected, get the F-Curves for that channel
+ * and set the relevant transform flags. */
poseAnim_mapping_get(C, &pso->pfLinks);
Object **objects = BKE_view_layer_array_from_objects_in_mode_unique_data(
@@ -233,65 +529,80 @@ static int pose_slide_init(bContext *C, wmOperator *op, ePoseSlide_Modes mode)
ob_data->ob = ob_iter;
ob_data->valid = true;
- /* apply NLA mapping corrections so the frame lookups work */
+ /* Apply NLA mapping corrections so the frame look-ups work. */
ob_data->prevFrameF = BKE_nla_tweakedit_remap(
ob_data->ob->adt, pso->prevFrame, NLATIME_CONVERT_UNMAP);
ob_data->nextFrameF = BKE_nla_tweakedit_remap(
ob_data->ob->adt, pso->nextFrame, NLATIME_CONVERT_UNMAP);
- /* set depsgraph flags */
- /* make sure the lock is set OK, unlock can be accidentally saved? */
+ /* Set depsgraph flags. */
+ /* Make sure the lock is set OK, unlock can be accidentally saved? */
ob_data->ob->pose->flag |= POSE_LOCKED;
ob_data->ob->pose->flag &= ~POSE_DO_UNLOCK;
}
MEM_freeN(objects);
- /* do basic initialize of RB-BST used for finding keyframes, but leave the filling of it up
- * to the caller of this (usually only invoke() will do it, to make things more efficient).
- */
+ /* Do basic initialize of RB-BST used for finding keyframes, but leave the filling of it up
+ * to the caller of this (usually only invoke() will do it, to make things more efficient). */
BLI_dlrbTree_init(&pso->keys);
/* Initialize numeric input. */
initNumInput(&pso->num);
- pso->num.idx_max = 0; /* one axis */
+ pso->num.idx_max = 0; /* One axis. */
pso->num.val_flag[0] |= NUM_NO_NEGATIVE;
- pso->num.unit_type[0] = B_UNIT_NONE; /* percentages don't have any units... */
+ pso->num.unit_type[0] = B_UNIT_NONE; /* Percentages don't have any units. */
+
+ /* Register UI drawing callback. */
+ ARegion *region_header = BKE_area_find_region_type(pso->area, RGN_TYPE_HEADER);
+ if (region_header != NULL) {
+ pso->region_header = region_header;
+ pso->draw_handle = ED_region_draw_cb_activate(
+ region_header->type, pose_slide_draw_2d_slider, pso, REGION_DRAW_POST_PIXEL);
+ }
- /* return status is whether we've got all the data we were requested to get */
+ /* Return status is whether we've got all the data we were requested to get. */
return 1;
}
-/* exiting the operator - free data */
+/**
+ * Exiting the operator (free data).
+ */
static void pose_slide_exit(wmOperator *op)
{
tPoseSlideOp *pso = op->customdata;
- /* if data exists, clear its data and exit */
- if (pso) {
- /* free the temp pchan links and their data */
- poseAnim_mapping_free(&pso->pfLinks);
+ /* Hide Bone Overlay. */
+ View3D *v3d = pso->area->spacedata.first;
+ v3d->overlay.flag = pso->overlay_flag;
- /* free RB-BST for keyframes (if it contained data) */
- BLI_dlrbTree_free(&pso->keys);
+ /* Remove UI drawing callback. */
+ ED_region_draw_cb_exit(pso->region_header->type, pso->draw_handle);
- if (pso->ob_data_array != NULL) {
- MEM_freeN(pso->ob_data_array);
- }
+ /* Free the temp pchan links and their data. */
+ poseAnim_mapping_free(&pso->pfLinks);
+
+ /* Free RB-BST for keyframes (if it contained data). */
+ BLI_dlrbTree_free(&pso->keys);
- /* free data itself */
- MEM_freeN(pso);
+ if (pso->ob_data_array != NULL) {
+ MEM_freeN(pso->ob_data_array);
}
- /* cleanup */
+ /* Free data itself. */
+ MEM_freeN(pso);
+
+ /* Cleanup. */
op->customdata = NULL;
}
/* ------------------------------------ */
-/* helper for apply() / reset() - refresh the data */
+/**
+ * Helper for apply() / reset() - refresh the data.
+ */
static void pose_slide_refresh(bContext *C, tPoseSlideOp *pso)
{
- /* wrapper around the generic version, allowing us to add some custom stuff later still */
+ /* Wrapper around the generic version, allowing us to add some custom stuff later still. */
for (uint ob_index = 0; ob_index < pso->objects_len; ob_index++) {
tPoseSlideObject *ob_data = &pso->ob_data_array[ob_index];
if (ob_data->valid) {
@@ -323,7 +634,9 @@ static bool pose_frame_range_from_object_get(tPoseSlideOp *pso,
return false;
}
-/* helper for apply() - perform sliding for some value */
+/**
+ * Helper for apply() - perform sliding for some value.
+ */
static void pose_slide_apply_val(tPoseSlideOp *pso, FCurve *fcu, Object *ob, float *val)
{
float prevFrameF, nextFrameF;
@@ -333,17 +646,17 @@ static void pose_slide_apply_val(tPoseSlideOp *pso, FCurve *fcu, Object *ob, flo
pose_frame_range_from_object_get(pso, ob, &prevFrameF, &nextFrameF);
- /* get keyframe values for endpoint poses to blend with */
- /* previous/start */
+ /* Get keyframe values for endpoint poses to blend with. */
+ /* Previous/start. */
sVal = evaluate_fcurve(fcu, prevFrameF);
- /* next/end */
+ /* Next/end. */
eVal = evaluate_fcurve(fcu, nextFrameF);
- /* calculate the relative weights of the endpoints */
+ /* Calculate the relative weights of the endpoints. */
if (pso->mode == POSESLIDE_BREAKDOWN) {
- /* get weights from the percentage control */
- w1 = pso->percentage; /* this must come second */
- w2 = 1.0f - w1; /* this must come first */
+ /* Get weights from the factor control. */
+ w1 = pso->factor; /* This must come second. */
+ w2 = 1.0f - w1; /* This must come first. */
}
else {
/* - these weights are derived from the relative distance of these
@@ -366,30 +679,37 @@ static void pose_slide_apply_val(tPoseSlideOp *pso, FCurve *fcu, Object *ob, flo
* the value the current frame is closer to.
*/
switch (pso->mode) {
- case POSESLIDE_PUSH: /* make the current pose more pronounced */
+ case POSESLIDE_PUSH: /* Make the current pose more pronounced. */
{
/* Slide the pose away from the breakdown pose in the timeline */
- (*val) -= ((sVal * w2) + (eVal * w1) - (*val)) * pso->percentage;
+ (*val) -= ((sVal * w2) + (eVal * w1) - (*val)) * pso->factor;
break;
}
- case POSESLIDE_RELAX: /* make the current pose more like its surrounding ones */
+ case POSESLIDE_RELAX: /* Make the current pose more like its surrounding ones. */
{
/* Slide the pose towards the breakdown pose in the timeline */
- (*val) += ((sVal * w2) + (eVal * w1) - (*val)) * pso->percentage;
+ (*val) += ((sVal * w2) + (eVal * w1) - (*val)) * pso->factor;
break;
}
- case POSESLIDE_BREAKDOWN: /* make the current pose slide around between the endpoints */
+ case POSESLIDE_BREAKDOWN: /* Make the current pose slide around between the endpoints. */
{
/* Perform simple linear interpolation -
- * coefficient for start must come from pso->percentage. */
+ * coefficient for start must come from pso->factor. */
/* TODO: make this use some kind of spline interpolation instead? */
(*val) = ((sVal * w2) + (eVal * w1));
break;
}
+ /* Those are handled in pose_slide_rest_pose_apply. */
+ case POSESLIDE_PUSH_REST:
+ case POSESLIDE_RELAX_REST: {
+ break;
+ }
}
}
-/* helper for apply() - perform sliding for some 3-element vector */
+/**
+ * Helper for apply() - perform sliding for some 3-element vector.
+ */
static void pose_slide_apply_vec3(tPoseSlideOp *pso,
tPChanFCurveLink *pfl,
float vec[3],
@@ -398,30 +718,32 @@ static void pose_slide_apply_vec3(tPoseSlideOp *pso,
LinkData *ld = NULL;
char *path = NULL;
- /* get the path to use... */
+ /* Get the path to use. */
path = BLI_sprintfN("%s.%s", pfl->pchan_path, propName);
- /* using this path, find each matching F-Curve for the variables we're interested in */
+ /* Using this path, find each matching F-Curve for the variables we're interested in. */
while ((ld = poseAnim_mapping_getNextFCurve(&pfl->fcurves, ld, path))) {
FCurve *fcu = (FCurve *)ld->data;
const int idx = fcu->array_index;
const int lock = pso->axislock;
- /* check if this F-Curve is ok given the current axis locks */
+ /* Check if this F-Curve is ok given the current axis locks. */
BLI_assert(fcu->array_index < 3);
if ((lock == 0) || ((lock & PS_LOCK_X) && (idx == 0)) || ((lock & PS_LOCK_Y) && (idx == 1)) ||
((lock & PS_LOCK_Z) && (idx == 2))) {
- /* just work on these channels one by one... there's no interaction between values */
+ /* Just work on these channels one by one... there's no interaction between values. */
pose_slide_apply_val(pso, fcu, pfl->ob, &vec[fcu->array_index]);
}
}
- /* free the temp path we got */
+ /* Free the temp path we got. */
MEM_freeN(path);
}
-/* helper for apply() - perform sliding for custom properties or bbone properties */
+/**
+ * Helper for apply() - perform sliding for custom properties or bbone properties.
+ */
static void pose_slide_apply_props(tPoseSlideOp *pso,
tPChanFCurveLink *pfl,
const char prop_prefix[])
@@ -430,7 +752,7 @@ static void pose_slide_apply_props(tPoseSlideOp *pso,
LinkData *ld;
int len = strlen(pfl->pchan_path);
- /* setup pointer RNA for resolving paths */
+ /* Setup pointer RNA for resolving paths. */
RNA_pointer_create(NULL, &RNA_PoseBone, pfl->pchan, &ptr);
/* - custom properties are just denoted using ["..."][etc.] after the end of the base path,
@@ -446,22 +768,21 @@ static void pose_slide_apply_props(tPoseSlideOp *pso,
continue;
}
- /* do we have a match?
- * - bPtr is the RNA Path with the standard part chopped off
- * - pPtr is the chunk of the path which is left over
+ /* Do we have a match?
+ * - bPtr is the RNA Path with the standard part chopped off.
+ * - pPtr is the chunk of the path which is left over.
*/
bPtr = strstr(fcu->rna_path, pfl->pchan_path) + len;
pPtr = strstr(bPtr, prop_prefix);
if (pPtr) {
- /* use RNA to try and get a handle on this property, then, assuming that it is just
- * numerical, try and grab the value as a float for temp editing before setting back
- */
+ /* Use RNA to try and get a handle on this property, then, assuming that it is just
+ * numerical, try and grab the value as a float for temp editing before setting back. */
PropertyRNA *prop = RNA_struct_find_property(&ptr, pPtr);
if (prop) {
switch (RNA_property_type(prop)) {
- /* continuous values that can be smoothly interpolated... */
+ /* Continuous values that can be smoothly interpolated. */
case PROP_FLOAT: {
float tval = RNA_property_float_get(&ptr, prop);
pose_slide_apply_val(pso, fcu, pfl->ob, &tval);
@@ -475,7 +796,7 @@ static void pose_slide_apply_props(tPoseSlideOp *pso,
break;
}
- /* values which can only take discrete values */
+ /* Values which can only take discrete values. */
case PROP_BOOLEAN: {
float tval = (float)RNA_property_boolean_get(&ptr, prop);
pose_slide_apply_val(pso, fcu, pfl->ob, &tval);
@@ -484,14 +805,13 @@ static void pose_slide_apply_props(tPoseSlideOp *pso,
break;
}
case PROP_ENUM: {
- /* don't handle this case - these don't usually represent interchangeable
- * set of values which should be interpolated between
- */
+ /* Don't handle this case - these don't usually represent interchangeable
+ * set of values which should be interpolated between. */
break;
}
default:
- /* cannot handle */
+ /* Cannot handle. */
// printf("Cannot Pose Slide non-numerical property\n");
break;
}
@@ -500,7 +820,9 @@ static void pose_slide_apply_props(tPoseSlideOp *pso,
}
}
-/* helper for apply() - perform sliding for quaternion rotations (using quat blending) */
+/**
+ * Helper for apply() - perform sliding for quaternion rotations (using quat blending).
+ */
static void pose_slide_apply_quat(tPoseSlideOp *pso, tPChanFCurveLink *pfl)
{
FCurve *fcu_w = NULL, *fcu_x = NULL, *fcu_y = NULL, *fcu_z = NULL;
@@ -515,17 +837,17 @@ static void pose_slide_apply_quat(tPoseSlideOp *pso, tPChanFCurveLink *pfl)
return;
}
- /* get the path to use - this should be quaternion rotations only (needs care) */
+ /* Get the path to use - this should be quaternion rotations only (needs care). */
path = BLI_sprintfN("%s.%s", pfl->pchan_path, "rotation_quaternion");
- /* get the current frame number */
+ /* Get the current frame number. */
cframe = (float)pso->cframe;
- /* using this path, find each matching F-Curve for the variables we're interested in */
+ /* Using this path, find each matching F-Curve for the variables we're interested in. */
while ((ld = poseAnim_mapping_getNextFCurve(&pfl->fcurves, ld, path))) {
FCurve *fcu = (FCurve *)ld->data;
- /* assign this F-Curve to one of the relevant pointers... */
+ /* Assign this F-Curve to one of the relevant pointers. */
switch (fcu->array_index) {
case 3: /* z */
fcu_z = fcu;
@@ -542,14 +864,14 @@ static void pose_slide_apply_quat(tPoseSlideOp *pso, tPChanFCurveLink *pfl)
}
}
- /* only if all channels exist, proceed */
+ /* Only if all channels exist, proceed. */
if (fcu_w && fcu_x && fcu_y && fcu_z) {
float quat_final[4];
- /* perform blending */
+ /* Perform blending. */
if (pso->mode == POSESLIDE_BREAKDOWN) {
/* Just perform the interpolation between quat_prev and
- * quat_next using pso->percentage as a guide. */
+ * quat_next using pso->factor as a guide. */
float quat_prev[4];
float quat_next[4];
@@ -566,7 +888,7 @@ static void pose_slide_apply_quat(tPoseSlideOp *pso, tPChanFCurveLink *pfl)
normalize_qt(quat_prev);
normalize_qt(quat_next);
- interp_qt_qtqt(quat_final, quat_prev, quat_next, pso->percentage);
+ interp_qt_qtqt(quat_final, quat_prev, quat_next, pso->factor);
}
else {
/* POSESLIDE_PUSH and POSESLIDE_RELAX. */
@@ -584,11 +906,11 @@ static void pose_slide_apply_quat(tPoseSlideOp *pso, tPChanFCurveLink *pfl)
normalize_qt(quat_curr);
if (pso->mode == POSESLIDE_PUSH) {
- interp_qt_qtqt(quat_final, quat_breakdown, quat_curr, 1.0f + pso->percentage);
+ interp_qt_qtqt(quat_final, quat_breakdown, quat_curr, 1.0f + pso->factor);
}
else {
BLI_assert(pso->mode == POSESLIDE_RELAX);
- interp_qt_qtqt(quat_final, quat_curr, quat_breakdown, pso->percentage);
+ interp_qt_qtqt(quat_final, quat_curr, quat_breakdown, pso->factor);
}
}
@@ -596,24 +918,24 @@ static void pose_slide_apply_quat(tPoseSlideOp *pso, tPChanFCurveLink *pfl)
quat_to_compatible_quat(pchan->quat, quat_final, pchan->quat);
}
- /* free the path now */
+ /* Free the path now. */
MEM_freeN(path);
}
static void pose_slide_rest_pose_apply_vec3(tPoseSlideOp *pso, float vec[3], float default_value)
{
- /* We only slide to the rest pose. So only use the default rest pose value */
+ /* We only slide to the rest pose. So only use the default rest pose value. */
const int lock = pso->axislock;
for (int idx = 0; idx < 3; idx++) {
if ((lock == 0) || ((lock & PS_LOCK_X) && (idx == 0)) || ((lock & PS_LOCK_Y) && (idx == 1)) ||
((lock & PS_LOCK_Z) && (idx == 2))) {
float diff_val = default_value - vec[idx];
if (pso->mode == POSESLIDE_RELAX_REST) {
- vec[idx] += pso->percentage * diff_val;
+ vec[idx] += pso->factor * diff_val;
}
else {
/* Push */
- vec[idx] -= pso->percentage * diff_val;
+ vec[idx] -= pso->factor * diff_val;
}
}
}
@@ -621,7 +943,7 @@ static void pose_slide_rest_pose_apply_vec3(tPoseSlideOp *pso, float vec[3], flo
static void pose_slide_rest_pose_apply_other_rot(tPoseSlideOp *pso, float vec[4], bool quat)
{
- /* We only slide to the rest pose. So only use the default rest pose value */
+ /* We only slide to the rest pose. So only use the default rest pose value. */
float default_values[] = {1.0f, 0.0f, 0.0f, 0.0f};
if (!quat) {
/* Axis Angle */
@@ -631,56 +953,58 @@ static void pose_slide_rest_pose_apply_other_rot(tPoseSlideOp *pso, float vec[4]
for (int idx = 0; idx < 4; idx++) {
float diff_val = default_values[idx] - vec[idx];
if (pso->mode == POSESLIDE_RELAX_REST) {
- vec[idx] += pso->percentage * diff_val;
+ vec[idx] += pso->factor * diff_val;
}
else {
/* Push */
- vec[idx] -= pso->percentage * diff_val;
+ vec[idx] -= pso->factor * diff_val;
}
}
}
-/* apply() - perform the pose sliding between the current pose and the rest pose */
+/**
+ * apply() - perform the pose sliding between the current pose and the rest pose.
+ */
static void pose_slide_rest_pose_apply(bContext *C, tPoseSlideOp *pso)
{
tPChanFCurveLink *pfl;
- /* for each link, handle each set of transforms */
+ /* For each link, handle each set of transforms. */
for (pfl = pso->pfLinks.first; pfl; pfl = pfl->next) {
- /* valid transforms for each PoseChannel should have been noted already
- * - sliding the pose should be a straightforward exercise for location+rotation,
+ /* Valid transforms for each #bPoseChannel should have been noted already.
+ * - Sliding the pose should be a straightforward exercise for location+rotation,
* but rotations get more complicated since we may want to use quaternion blending
- * for quaternions instead...
+ * for quaternions instead.
*/
bPoseChannel *pchan = pfl->pchan;
if (ELEM(pso->channels, PS_TFM_ALL, PS_TFM_LOC) && (pchan->flag & POSE_LOC)) {
- /* calculate these for the 'location' vector, and use location curves */
+ /* Calculate these for the 'location' vector, and use location curves. */
pose_slide_rest_pose_apply_vec3(pso, pchan->loc, 0.0f);
}
if (ELEM(pso->channels, PS_TFM_ALL, PS_TFM_SIZE) && (pchan->flag & POSE_SIZE)) {
- /* calculate these for the 'scale' vector, and use scale curves */
+ /* Calculate these for the 'scale' vector, and use scale curves. */
pose_slide_rest_pose_apply_vec3(pso, pchan->size, 1.0f);
}
if (ELEM(pso->channels, PS_TFM_ALL, PS_TFM_ROT) && (pchan->flag & POSE_ROT)) {
- /* everything depends on the rotation mode */
+ /* Everything depends on the rotation mode. */
if (pchan->rotmode > 0) {
- /* eulers - so calculate these for the 'eul' vector, and use euler_rotation curves */
+ /* Eulers - so calculate these for the 'eul' vector, and use euler_rotation curves. */
pose_slide_rest_pose_apply_vec3(pso, pchan->eul, 0.0f);
}
else if (pchan->rotmode == ROT_MODE_AXISANGLE) {
pose_slide_rest_pose_apply_other_rot(pso, pchan->quat, false);
}
else {
- /* quaternions - use quaternion blending */
+ /* Quaternions - use quaternion blending. */
pose_slide_rest_pose_apply_other_rot(pso, pchan->quat, true);
}
}
if (ELEM(pso->channels, PS_TFM_ALL, PS_TFM_BBONE_SHAPE) && (pchan->flag & POSE_BBONE_SHAPE)) {
- /* bbone properties - they all start a "bbone_" prefix */
+ /* Bbone properties - they all start a "bbone_" prefix. */
/* TODO Not implemented */
// pose_slide_apply_props(pso, pfl, "bbone_");
}
@@ -693,18 +1017,20 @@ static void pose_slide_rest_pose_apply(bContext *C, tPoseSlideOp *pso)
}
}
- /* depsgraph updates + redraws */
+ /* Depsgraph updates + redraws. */
pose_slide_refresh(C, pso);
}
-/* apply() - perform the pose sliding based on weighting various poses */
+/**
+ * apply() - perform the pose sliding based on weighting various poses.
+ */
static void pose_slide_apply(bContext *C, tPoseSlideOp *pso)
{
tPChanFCurveLink *pfl;
/* Sanitize the frame ranges. */
if (pso->prevFrame == pso->nextFrame) {
- /* move out one step either side */
+ /* Move out one step either side. */
pso->prevFrame--;
pso->nextFrame++;
@@ -715,7 +1041,7 @@ static void pose_slide_apply(bContext *C, tPoseSlideOp *pso)
continue;
}
- /* apply NLA mapping corrections so the frame lookups work */
+ /* Apply NLA mapping corrections so the frame look-ups work. */
ob_data->prevFrameF = BKE_nla_tweakedit_remap(
ob_data->ob->adt, pso->prevFrame, NLATIME_CONVERT_UNMAP);
ob_data->nextFrameF = BKE_nla_tweakedit_remap(
@@ -723,9 +1049,9 @@ static void pose_slide_apply(bContext *C, tPoseSlideOp *pso)
}
}
- /* for each link, handle each set of transforms */
+ /* For each link, handle each set of transforms. */
for (pfl = pso->pfLinks.first; pfl; pfl = pfl->next) {
- /* valid transforms for each PoseChannel should have been noted already
+ /* Valid transforms for each #bPoseChannel should have been noted already
* - sliding the pose should be a straightforward exercise for location+rotation,
* but rotations get more complicated since we may want to use quaternion blending
* for quaternions instead...
@@ -733,32 +1059,32 @@ static void pose_slide_apply(bContext *C, tPoseSlideOp *pso)
bPoseChannel *pchan = pfl->pchan;
if (ELEM(pso->channels, PS_TFM_ALL, PS_TFM_LOC) && (pchan->flag & POSE_LOC)) {
- /* calculate these for the 'location' vector, and use location curves */
+ /* Calculate these for the 'location' vector, and use location curves. */
pose_slide_apply_vec3(pso, pfl, pchan->loc, "location");
}
if (ELEM(pso->channels, PS_TFM_ALL, PS_TFM_SIZE) && (pchan->flag & POSE_SIZE)) {
- /* calculate these for the 'scale' vector, and use scale curves */
+ /* Calculate these for the 'scale' vector, and use scale curves. */
pose_slide_apply_vec3(pso, pfl, pchan->size, "scale");
}
if (ELEM(pso->channels, PS_TFM_ALL, PS_TFM_ROT) && (pchan->flag & POSE_ROT)) {
- /* everything depends on the rotation mode */
+ /* Everything depends on the rotation mode. */
if (pchan->rotmode > 0) {
- /* eulers - so calculate these for the 'eul' vector, and use euler_rotation curves */
+ /* Eulers - so calculate these for the 'eul' vector, and use euler_rotation curves. */
pose_slide_apply_vec3(pso, pfl, pchan->eul, "rotation_euler");
}
else if (pchan->rotmode == ROT_MODE_AXISANGLE) {
/* TODO: need to figure out how to do this! */
}
else {
- /* quaternions - use quaternion blending */
+ /* Quaternions - use quaternion blending. */
pose_slide_apply_quat(pso, pfl);
}
}
if (ELEM(pso->channels, PS_TFM_ALL, PS_TFM_BBONE_SHAPE) && (pchan->flag & POSE_BBONE_SHAPE)) {
- /* bbone properties - they all start a "bbone_" prefix */
+ /* Bbone properties - they all start a "bbone_" prefix. */
pose_slide_apply_props(pso, pfl, "bbone_");
}
@@ -769,34 +1095,45 @@ static void pose_slide_apply(bContext *C, tPoseSlideOp *pso)
}
}
- /* depsgraph updates + redraws */
+ /* Depsgraph updates + redraws. */
pose_slide_refresh(C, pso);
}
-/* perform auto-key-framing after changes were made + confirmed */
+/**
+ * Perform auto-key-framing after changes were made + confirmed.
+ */
static void pose_slide_autoKeyframe(bContext *C, tPoseSlideOp *pso)
{
- /* wrapper around the generic call */
+ /* Wrapper around the generic call. */
poseAnim_mapping_autoKeyframe(C, pso->scene, &pso->pfLinks, (float)pso->cframe);
}
-/* reset changes made to current pose */
+/**
+ * Reset changes made to current pose.
+ */
static void pose_slide_reset(tPoseSlideOp *pso)
{
- /* wrapper around the generic call, so that custom stuff can be added later */
+ /* Wrapper around the generic call, so that custom stuff can be added later. */
poseAnim_mapping_reset(&pso->pfLinks);
}
/* ------------------------------------ */
-/* draw percentage indicator in header */
-/* TODO: Include hints about locks here... */
-static void pose_slide_draw_status(tPoseSlideOp *pso)
+/**
+ * Draw percentage indicator in status-bar.
+ *
+ * TODO: Include hints about locks here.
+ */
+static void pose_slide_draw_status(bContext *C, tPoseSlideOp *pso)
{
char status_str[UI_MAX_DRAW_STR];
char limits_str[UI_MAX_DRAW_STR];
char axis_str[50];
char mode_str[32];
+ char overshoot_str[50];
+ char precision_str[50];
+ char increments_str[50];
+ char bone_vis_str[50];
switch (pso->mode) {
case POSESLIDE_PUSH:
@@ -810,25 +1147,25 @@ static void pose_slide_draw_status(tPoseSlideOp *pso)
break;
default:
- /* unknown */
+ /* Unknown. */
strcpy(mode_str, TIP_("Sliding-Tool"));
break;
}
switch (pso->axislock) {
case PS_LOCK_X:
- BLI_strncpy(axis_str, TIP_("[X]/Y/Z axis only (X to clear)"), sizeof(axis_str));
+ STRNCPY(axis_str, TIP_("[X]/Y/Z axis only (X to clear)"));
break;
case PS_LOCK_Y:
- BLI_strncpy(axis_str, TIP_("X/[Y]/Z axis only (Y to clear)"), sizeof(axis_str));
+ STRNCPY(axis_str, TIP_("X/[Y]/Z axis only (Y to clear)"));
break;
case PS_LOCK_Z:
- BLI_strncpy(axis_str, TIP_("X/Y/[Z] axis only (Z to clear)"), sizeof(axis_str));
+ STRNCPY(axis_str, TIP_("X/Y/[Z] axis only (Z to clear)"));
break;
default:
if (ELEM(pso->channels, PS_TFM_LOC, PS_TFM_ROT, PS_TFM_SIZE)) {
- BLI_strncpy(axis_str, TIP_("X/Y/Z = Axis Constraint"), sizeof(axis_str));
+ STRNCPY(axis_str, TIP_("X/Y/Z = Axis Constraint"));
}
else {
axis_str[0] = '\0';
@@ -856,93 +1193,116 @@ static void pose_slide_draw_status(tPoseSlideOp *pso)
axis_str);
break;
case PS_TFM_BBONE_SHAPE:
- BLI_strncpy(limits_str,
- TIP_("G/R/S/[B]/C - Bendy Bone properties only (B to clear) | %s"),
- sizeof(limits_str));
+ STRNCPY(limits_str, TIP_("G/R/S/[B]/C - Bendy Bone properties only (B to clear) | %s"));
break;
case PS_TFM_PROPS:
- BLI_strncpy(limits_str,
- TIP_("G/R/S/B/[C] - Custom Properties only (C to clear) | %s"),
- sizeof(limits_str));
+ STRNCPY(limits_str, TIP_("G/R/S/B/[C] - Custom Properties only (C to clear) | %s"));
break;
default:
- BLI_strncpy(
- limits_str, TIP_("G/R/S/B/C - Limit to Transform/Property Set"), sizeof(limits_str));
+ STRNCPY(limits_str, TIP_("G/R/S/B/C - Limit to Transform/Property Set"));
break;
}
+ if (pso->overshoot) {
+ STRNCPY(overshoot_str, TIP_("[E] - Disable overshoot"));
+ }
+ else {
+ STRNCPY(overshoot_str, TIP_("[E] - Enable overshoot"));
+ }
+
+ if (pso->precision) {
+ STRNCPY(precision_str, TIP_("[Shift] - Precision active"));
+ }
+ else {
+ STRNCPY(precision_str, TIP_("Shift - Hold for precision"));
+ }
+
+ if (pso->increments) {
+ STRNCPY(increments_str, TIP_("[Ctrl] - Increments active"));
+ }
+ else {
+ STRNCPY(increments_str, TIP_("Ctrl - Hold for 10% increments"));
+ }
+
+ STRNCPY(bone_vis_str, TIP_("[H] - Toggle bone visibility"));
+
if (hasNumInput(&pso->num)) {
Scene *scene = pso->scene;
- char str_ofs[NUM_STR_REP_LEN];
+ char str_offs[NUM_STR_REP_LEN];
- outputNumInput(&pso->num, str_ofs, &scene->unit);
+ outputNumInput(&pso->num, str_offs, &scene->unit);
- BLI_snprintf(
- status_str, sizeof(status_str), "%s: %s | %s", mode_str, str_ofs, limits_str);
+ BLI_snprintf(status_str, sizeof(status_str), "%s: %s | %s", mode_str, str_offs, limits_str);
}
else {
BLI_snprintf(status_str,
sizeof(status_str),
- "%s: %d %% | %s",
+ "%s: %s | %s | %s | %s | %s",
mode_str,
- (int)(pso->percentage * 100.0f),
- limits_str);
+ limits_str,
+ overshoot_str,
+ precision_str,
+ increments_str,
+ bone_vis_str);
}
- ED_area_status_text(pso->area, status_str);
+ ED_workspace_status_text(C, status_str);
+ ED_area_status_text(pso->area, "");
}
-/* common code for invoke() methods */
+/**
+ * Common code for invoke() methods.
+ */
static int pose_slide_invoke_common(bContext *C, wmOperator *op, tPoseSlideOp *pso)
{
tPChanFCurveLink *pfl;
wmWindow *win = CTX_wm_window(C);
- /* for each link, add all its keyframes to the search tree */
+ /* For each link, add all its keyframes to the search tree. */
for (pfl = pso->pfLinks.first; pfl; pfl = pfl->next) {
LinkData *ld;
- /* do this for each F-Curve */
+ /* Do this for each F-Curve. */
for (ld = pfl->fcurves.first; ld; ld = ld->next) {
FCurve *fcu = (FCurve *)ld->data;
fcurve_to_keylist(pfl->ob->adt, fcu, &pso->keys, 0);
}
}
- /* cancel if no keyframes found... */
+ /* Cancel if no keyframes found. */
if (pso->keys.root) {
ActKeyColumn *ak;
float cframe = (float)pso->cframe;
- /* firstly, check if the current frame is a keyframe... */
+ /* Firstly, check if the current frame is a keyframe. */
ak = (ActKeyColumn *)BLI_dlrbTree_search_exact(&pso->keys, compare_ak_cfraPtr, &cframe);
if (ak == NULL) {
- /* current frame is not a keyframe, so search */
+ /* Current frame is not a keyframe, so search. */
ActKeyColumn *pk = (ActKeyColumn *)BLI_dlrbTree_search_prev(
&pso->keys, compare_ak_cfraPtr, &cframe);
ActKeyColumn *nk = (ActKeyColumn *)BLI_dlrbTree_search_next(
&pso->keys, compare_ak_cfraPtr, &cframe);
- /* new set the frames */
- /* prev frame */
+ /* New set the frames. */
+ /* Prev frame. */
pso->prevFrame = (pk) ? (pk->cfra) : (pso->cframe - 1);
RNA_int_set(op->ptr, "prev_frame", pso->prevFrame);
- /* next frame */
+ /* Next frame. */
pso->nextFrame = (nk) ? (nk->cfra) : (pso->cframe + 1);
RNA_int_set(op->ptr, "next_frame", pso->nextFrame);
}
else {
- /* current frame itself is a keyframe, so just take keyframes on either side */
- /* prev frame */
+ /* Current frame itself is a keyframe, so just take keyframes on either side. */
+ /* Prev frame. */
pso->prevFrame = (ak->prev) ? (ak->prev->cfra) : (pso->cframe - 1);
RNA_int_set(op->ptr, "prev_frame", pso->prevFrame);
- /* next frame */
+ /* Next frame. */
pso->nextFrame = (ak->next) ? (ak->next->cfra) : (pso->cframe + 1);
RNA_int_set(op->ptr, "next_frame", pso->nextFrame);
}
- /* apply NLA mapping corrections so the frame lookups work */
+ /* Apply NLA mapping corrections so the frame look-ups work. */
for (uint ob_index = 0; ob_index < pso->objects_len; ob_index++) {
tPoseSlideObject *ob_data = &pso->ob_data_array[ob_index];
if (ob_data->valid) {
@@ -959,8 +1319,8 @@ static int pose_slide_invoke_common(bContext *C, wmOperator *op, tPoseSlideOp *p
return OPERATOR_CANCELLED;
}
- /* initial apply for operator... */
- /* TODO: need to calculate percentage for initial round too... */
+ /* Initial apply for operator. */
+ /* TODO: need to calculate factor for initial round too. */
if (!ELEM(pso->mode, POSESLIDE_PUSH_REST, POSESLIDE_RELAX_REST)) {
pose_slide_apply(C, pso);
}
@@ -968,32 +1328,52 @@ static int pose_slide_invoke_common(bContext *C, wmOperator *op, tPoseSlideOp *p
pose_slide_rest_pose_apply(C, pso);
}
- /* depsgraph updates + redraws */
+ /* Depsgraph updates + redraws. */
pose_slide_refresh(C, pso);
- /* set cursor to indicate modal */
+ /* Set cursor to indicate modal. */
WM_cursor_modal_set(win, WM_CURSOR_EW_SCROLL);
- /* header print */
- pose_slide_draw_status(pso);
+ /* Header print. */
+ pose_slide_draw_status(C, pso);
- /* add a modal handler for this operator */
+ /* Add a modal handler for this operator. */
WM_event_add_modal_handler(C, op);
+
+ /* Hide Bone Overlay. */
+ View3D *v3d = pso->area->spacedata.first;
+ pso->overlay_flag = v3d->overlay.flag;
+ v3d->overlay.flag |= V3D_OVERLAY_HIDE_BONES;
+
return OPERATOR_RUNNING_MODAL;
}
-/* calculate percentage based on position of mouse (we only use x-axis for now.
- * since this is more convenient for users to do), and store new percentage value
+/**
+ * Calculate factor based on mouse movement, clamp or round to increments if
+ * enabled by the user. Store the new factor value.
*/
-static void pose_slide_mouse_update_percentage(tPoseSlideOp *pso,
- wmOperator *op,
- const wmEvent *event)
+static void pose_slide_mouse_update_factor(tPoseSlideOp *pso, wmOperator *op, const wmEvent *event)
{
- pso->percentage = (event->x - pso->region->winrct.xmin) / ((float)pso->region->winx);
- RNA_float_set(op->ptr, "percentage", pso->percentage);
+ const float factor_delta = (event->x - pso->last_cursor_x) / ((float)(SLIDE_PIXEL_DISTANCE));
+ /* Reduced factor delta in precision mode (shift held). */
+ pso->raw_factor += pso->precision ? (factor_delta / 8) : factor_delta;
+ pso->factor = pso->raw_factor;
+ pso->last_cursor_x = event->x;
+
+ if (!pso->overshoot) {
+ pso->factor = clamp_f(pso->factor, 0, 1);
+ }
+
+ if (pso->increments) {
+ pso->factor = round(pso->factor * 10) / 10;
+ }
+
+ RNA_float_set(op->ptr, "factor", pso->factor);
}
-/* handle an event to toggle channels mode */
+/**
+ * Handle an event to toggle channels mode.
+ */
static void pose_slide_toggle_channels_mode(wmOperator *op,
tPoseSlideOp *pso,
ePoseSlide_Channels channel)
@@ -1014,7 +1394,9 @@ static void pose_slide_toggle_channels_mode(wmOperator *op,
RNA_enum_set(op->ptr, "axis_lock", pso->axislock);
}
-/* handle an event to toggle axis locks - returns whether any change in state is needed */
+/**
+ * Handle an event to toggle axis locks - returns whether any change in state is needed.
+ */
static bool pose_slide_toggle_axis_locks(wmOperator *op,
tPoseSlideOp *pso,
ePoseSlide_AxisLock axis)
@@ -1041,7 +1423,9 @@ static bool pose_slide_toggle_axis_locks(wmOperator *op,
return true;
}
-/* common code for modal() */
+/**
+ * Operator `modal()` callback.
+ */
static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
tPoseSlideOp *pso = op->customdata;
@@ -1051,55 +1435,60 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event)
const bool has_numinput = hasNumInput(&pso->num);
switch (event->type) {
- case LEFTMOUSE: /* confirm */
+ case LEFTMOUSE: /* Confirm. */
case EVT_RETKEY:
case EVT_PADENTER: {
if (event->val == KM_PRESS) {
- /* return to normal cursor and header status */
+ /* Return to normal cursor and header status. */
+ ED_workspace_status_text(C, NULL);
ED_area_status_text(pso->area, NULL);
WM_cursor_modal_restore(win);
- /* insert keyframes as required... */
+ /* Depsgraph updates + redraws. Redraw needed to remove UI. */
+ pose_slide_refresh(C, pso);
+
+ /* Insert keyframes as required. */
pose_slide_autoKeyframe(C, pso);
pose_slide_exit(op);
- /* done! */
+ /* Done! */
return OPERATOR_FINISHED;
}
break;
}
- case EVT_ESCKEY: /* cancel */
+ case EVT_ESCKEY: /* Cancel. */
case RIGHTMOUSE: {
if (event->val == KM_PRESS) {
- /* return to normal cursor and header status */
+ /* Return to normal cursor and header status. */
+ ED_workspace_status_text(C, NULL);
ED_area_status_text(pso->area, NULL);
WM_cursor_modal_restore(win);
- /* reset transforms back to original state */
+ /* Reset transforms back to original state. */
pose_slide_reset(pso);
- /* depsgraph updates + redraws */
+ /* Depsgraph updates + redraws.*/
pose_slide_refresh(C, pso);
- /* clean up temp data */
+ /* Clean up temp data. */
pose_slide_exit(op);
- /* canceled! */
+ /* Canceled! */
return OPERATOR_CANCELLED;
}
break;
}
- /* Percentage Change... */
- case MOUSEMOVE: /* calculate new position */
+ /* Factor Change... */
+ case MOUSEMOVE: /* Calculate new position. */
{
- /* only handle mousemove if not doing numinput */
+ /* Only handle mouse-move if not doing numinput. */
if (has_numinput == false) {
- /* update percentage based on position of mouse */
- pose_slide_mouse_update_percentage(pso, op, event);
+ /* Update factor based on position of mouse. */
+ pose_slide_mouse_update_factor(pso, op, event);
- /* update pose to reflect the new values (see below) */
+ /* Update pose to reflect the new values (see below). */
do_pose_update = true;
}
break;
@@ -1111,12 +1500,12 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event)
/* Grab percentage from numeric input, and store this new value for redo
* NOTE: users see ints, while internally we use a 0-1 float
*/
- value = pso->percentage * 100.0f;
+ value = pso->factor * 100.0f;
applyNumInput(&pso->num, &value);
- pso->percentage = value / 100.0f;
- CLAMP(pso->percentage, 0.0f, 1.0f);
- RNA_float_set(op->ptr, "percentage", pso->percentage);
+ pso->factor = value / 100.0f;
+ CLAMP(pso->factor, 0.0f, 1.0f);
+ RNA_float_set(op->ptr, "factor", pso->factor);
/* Update pose to reflect the new values (see below) */
do_pose_update = true;
@@ -1178,13 +1567,61 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event)
break;
}
+ /* Overshoot. */
+ case EVT_EKEY: {
+ pso->overshoot = !pso->overshoot;
+ do_pose_update = true;
+ break;
+ }
+
+ /* Precision mode. */
+ case EVT_LEFTSHIFTKEY:
+ case EVT_RIGHTSHIFTKEY: {
+ pso->precision = true;
+ do_pose_update = true;
+ break;
+ }
+
+ /* Increments mode. */
+ case EVT_LEFTCTRLKEY:
+ case EVT_RIGHTCTRLKEY: {
+ pso->increments = true;
+ do_pose_update = true;
+ break;
+ }
+
+ /* Toggle Bone visibility. */
+ case EVT_HKEY: {
+ View3D *v3d = pso->area->spacedata.first;
+ v3d->overlay.flag ^= V3D_OVERLAY_HIDE_BONES;
+ }
+
default: /* Some other unhandled key... */
break;
}
}
+ /* Precision and stepping only active while button is held. */
+ else if (event->val == KM_RELEASE) {
+ switch (event->type) {
+ case EVT_LEFTSHIFTKEY:
+ case EVT_RIGHTSHIFTKEY: {
+ pso->precision = false;
+ do_pose_update = true;
+ break;
+ }
+ case EVT_LEFTCTRLKEY:
+ case EVT_RIGHTCTRLKEY: {
+ pso->increments = false;
+ do_pose_update = true;
+ break;
+ }
+ default:
+ break;
+ }
+ }
else {
- /* unhandled event - maybe it was some view manipulation? */
- /* allow to pass through */
+ /* Unhandled event - maybe it was some view manipulation? */
+ /* Allow to pass through. */
return OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH;
}
}
@@ -1193,13 +1630,13 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event)
/* Perform pose updates - in response to some user action
* (e.g. pressing a key or moving the mouse). */
if (do_pose_update) {
- /* update percentage indicator in header */
- pose_slide_draw_status(pso);
+ /* Update percentage indicator in header. */
+ pose_slide_draw_status(C, pso);
- /* reset transforms (to avoid accumulation errors) */
+ /* Reset transforms (to avoid accumulation errors). */
pose_slide_reset(pso);
- /* apply... */
+ /* Apply. */
if (!ELEM(pso->mode, POSESLIDE_PUSH_REST, POSESLIDE_RELAX_REST)) {
pose_slide_apply(C, pso);
}
@@ -1208,21 +1645,25 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event)
}
}
- /* still running... */
+ /* Still running. */
return OPERATOR_RUNNING_MODAL;
}
-/* common code for cancel() */
+/**
+ * Common code for cancel()
+ */
static void pose_slide_cancel(bContext *UNUSED(C), wmOperator *op)
{
- /* cleanup and done */
+ /* Cleanup and done. */
pose_slide_exit(op);
}
-/* common code for exec() methods */
+/**
+ * Common code for exec() methods.
+ */
static int pose_slide_exec_common(bContext *C, wmOperator *op, tPoseSlideOp *pso)
{
- /* settings should have been set up ok for applying, so just apply! */
+ /* Settings should have been set up ok for applying, so just apply! */
if (!ELEM(pso->mode, POSESLIDE_PUSH_REST, POSESLIDE_RELAX_REST)) {
pose_slide_apply(C, pso);
}
@@ -1230,10 +1671,10 @@ static int pose_slide_exec_common(bContext *C, wmOperator *op, tPoseSlideOp *pso
pose_slide_rest_pose_apply(C, pso);
}
- /* insert keyframes if needed */
+ /* Insert keyframes if needed. */
pose_slide_autoKeyframe(C, pso);
- /* cleanup and done */
+ /* Cleanup and done. */
pose_slide_exit(op);
return OPERATOR_FINISHED;
@@ -1297,12 +1738,14 @@ static void pose_slide_opdef_properties(wmOperatorType *ot)
/* ------------------------------------ */
-/* invoke() - for 'push from breakdown' mode */
+/**
+ * Operator `invoke()` callback for 'push from breakdown' mode.
+ */
static int pose_slide_push_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
tPoseSlideOp *pso;
- /* initialize data */
+ /* Initialize data. */
if (pose_slide_init(C, op, POSESLIDE_PUSH) == 0) {
pose_slide_exit(op);
return OPERATOR_CANCELLED;
@@ -1310,19 +1753,23 @@ static int pose_slide_push_invoke(bContext *C, wmOperator *op, const wmEvent *ev
pso = op->customdata;
- /* Initialize percentage so that it won't pop on first mouse move. */
- pose_slide_mouse_update_percentage(pso, op, event);
+ pso->last_cursor_x = event->x;
- /* do common setup work */
+ /* Initialize factor so that it won't pop on first mouse move. */
+ pose_slide_mouse_update_factor(pso, op, event);
+
+ /* Do common setup work. */
return pose_slide_invoke_common(C, op, pso);
}
-/* exec() - for push */
+/**
+ * Operator `exec()` callback - for push.
+ */
static int pose_slide_push_exec(bContext *C, wmOperator *op)
{
tPoseSlideOp *pso;
- /* initialize data (from RNA-props) */
+ /* Initialize data (from RNA-props). */
if (pose_slide_init(C, op, POSESLIDE_PUSH) == 0) {
pose_slide_exit(op);
return OPERATOR_CANCELLED;
@@ -1330,7 +1777,7 @@ static int pose_slide_push_exec(bContext *C, wmOperator *op)
pso = op->customdata;
- /* do common exec work */
+ /* Do common exec work. */
return pose_slide_exec_common(C, op, pso);
}
@@ -1349,7 +1796,7 @@ void POSE_OT_push(wmOperatorType *ot)
ot->poll = ED_operator_posemode;
/* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_X;
/* Properties */
pose_slide_opdef_properties(ot);
@@ -1357,12 +1804,14 @@ void POSE_OT_push(wmOperatorType *ot)
/* ........................ */
-/* invoke() - for 'relax to breakdown' mode */
+/**
+ * Invoke callback - for 'relax to breakdown' mode.
+ */
static int pose_slide_relax_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
tPoseSlideOp *pso;
- /* initialize data */
+ /* Initialize data. */
if (pose_slide_init(C, op, POSESLIDE_RELAX) == 0) {
pose_slide_exit(op);
return OPERATOR_CANCELLED;
@@ -1370,19 +1819,23 @@ static int pose_slide_relax_invoke(bContext *C, wmOperator *op, const wmEvent *e
pso = op->customdata;
- /* Initialize percentage so that it won't pop on first mouse move. */
- pose_slide_mouse_update_percentage(pso, op, event);
+ pso->last_cursor_x = event->x;
- /* do common setup work */
+ /* Initialize factor so that it won't pop on first mouse move. */
+ pose_slide_mouse_update_factor(pso, op, event);
+
+ /* Do common setup work. */
return pose_slide_invoke_common(C, op, pso);
}
-/* exec() - for relax */
+/**
+ * Operator exec() - for relax.
+ */
static int pose_slide_relax_exec(bContext *C, wmOperator *op)
{
tPoseSlideOp *pso;
- /* initialize data (from RNA-props) */
+ /* Initialize data (from RNA-props). */
if (pose_slide_init(C, op, POSESLIDE_RELAX) == 0) {
pose_slide_exit(op);
return OPERATOR_CANCELLED;
@@ -1390,7 +1843,7 @@ static int pose_slide_relax_exec(bContext *C, wmOperator *op)
pso = op->customdata;
- /* do common exec work */
+ /* Do common exec work. */
return pose_slide_exec_common(C, op, pso);
}
@@ -1409,19 +1862,21 @@ void POSE_OT_relax(wmOperatorType *ot)
ot->poll = ED_operator_posemode;
/* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_X;
/* Properties */
pose_slide_opdef_properties(ot);
}
/* ........................ */
-/* invoke() - for 'push from rest pose' mode */
+/**
+ * Operator `invoke()` - for 'push from rest pose' mode.
+ */
static int pose_slide_push_rest_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
tPoseSlideOp *pso;
- /* initialize data */
+ /* Initialize data. */
if (pose_slide_init(C, op, POSESLIDE_PUSH_REST) == 0) {
pose_slide_exit(op);
return OPERATOR_CANCELLED;
@@ -1429,19 +1884,23 @@ static int pose_slide_push_rest_invoke(bContext *C, wmOperator *op, const wmEven
pso = op->customdata;
- /* Initialize percentage so that it won't pop on first mouse move. */
- pose_slide_mouse_update_percentage(pso, op, event);
+ pso->last_cursor_x = event->x;
+
+ /* Initialize factor so that it won't pop on first mouse move. */
+ pose_slide_mouse_update_factor(pso, op, event);
/* do common setup work */
return pose_slide_invoke_common(C, op, pso);
}
-/* exec() - for push */
+/**
+ * Operator `exec()` - for push.
+ */
static int pose_slide_push_rest_exec(bContext *C, wmOperator *op)
{
tPoseSlideOp *pso;
- /* initialize data (from RNA-props) */
+ /* Initialize data (from RNA-props). */
if (pose_slide_init(C, op, POSESLIDE_PUSH_REST) == 0) {
pose_slide_exit(op);
return OPERATOR_CANCELLED;
@@ -1449,7 +1908,7 @@ static int pose_slide_push_rest_exec(bContext *C, wmOperator *op)
pso = op->customdata;
- /* do common exec work */
+ /* Do common exec work. */
return pose_slide_exec_common(C, op, pso);
}
@@ -1468,7 +1927,7 @@ void POSE_OT_push_rest(wmOperatorType *ot)
ot->poll = ED_operator_posemode;
/* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_X;
/* Properties */
pose_slide_opdef_properties(ot);
@@ -1476,12 +1935,14 @@ void POSE_OT_push_rest(wmOperatorType *ot)
/* ........................ */
-/* invoke() - for 'relax' mode */
+/**
+ * Operator `invoke()` - for 'relax' mode.
+ */
static int pose_slide_relax_rest_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
tPoseSlideOp *pso;
- /* initialize data */
+ /* Initialize data. */
if (pose_slide_init(C, op, POSESLIDE_RELAX_REST) == 0) {
pose_slide_exit(op);
return OPERATOR_CANCELLED;
@@ -1489,19 +1950,23 @@ static int pose_slide_relax_rest_invoke(bContext *C, wmOperator *op, const wmEve
pso = op->customdata;
- /* Initialize percentage so that it won't pop on first mouse move. */
- pose_slide_mouse_update_percentage(pso, op, event);
+ pso->last_cursor_x = event->x;
- /* do common setup work */
+ /* Initialize factor so that it won't pop on first mouse move. */
+ pose_slide_mouse_update_factor(pso, op, event);
+
+ /* Do common setup work. */
return pose_slide_invoke_common(C, op, pso);
}
-/* exec() - for relax */
+/**
+ * Operator `exec()` - for relax.
+ */
static int pose_slide_relax_rest_exec(bContext *C, wmOperator *op)
{
tPoseSlideOp *pso;
- /* initialize data (from RNA-props) */
+ /* Initialize data (from RNA-props). */
if (pose_slide_init(C, op, POSESLIDE_RELAX_REST) == 0) {
pose_slide_exit(op);
return OPERATOR_CANCELLED;
@@ -1509,7 +1974,7 @@ static int pose_slide_relax_rest_exec(bContext *C, wmOperator *op)
pso = op->customdata;
- /* do common exec work */
+ /* Do common exec work. */
return pose_slide_exec_common(C, op, pso);
}
@@ -1528,7 +1993,7 @@ void POSE_OT_relax_rest(wmOperatorType *ot)
ot->poll = ED_operator_posemode;
/* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_X;
/* Properties */
pose_slide_opdef_properties(ot);
@@ -1536,12 +2001,14 @@ void POSE_OT_relax_rest(wmOperatorType *ot)
/* ........................ */
-/* invoke() - for 'breakdown' mode */
+/**
+ * Operator `invoke()` - for 'breakdown' mode.
+ */
static int pose_slide_breakdown_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
tPoseSlideOp *pso;
- /* initialize data */
+ /* Initialize data. */
if (pose_slide_init(C, op, POSESLIDE_BREAKDOWN) == 0) {
pose_slide_exit(op);
return OPERATOR_CANCELLED;
@@ -1549,19 +2016,23 @@ static int pose_slide_breakdown_invoke(bContext *C, wmOperator *op, const wmEven
pso = op->customdata;
- /* Initialize percentage so that it won't pop on first mouse move. */
- pose_slide_mouse_update_percentage(pso, op, event);
+ pso->last_cursor_x = event->x;
- /* do common setup work */
+ /* Initialize factor so that it won't pop on first mouse move. */
+ pose_slide_mouse_update_factor(pso, op, event);
+
+ /* Do common setup work. */
return pose_slide_invoke_common(C, op, pso);
}
-/* exec() - for breakdown */
+/**
+ * Operator exec() - for breakdown.
+ */
static int pose_slide_breakdown_exec(bContext *C, wmOperator *op)
{
tPoseSlideOp *pso;
- /* initialize data (from RNA-props) */
+ /* Initialize data (from RNA-props). */
if (pose_slide_init(C, op, POSESLIDE_BREAKDOWN) == 0) {
pose_slide_exit(op);
return OPERATOR_CANCELLED;
@@ -1569,7 +2040,7 @@ static int pose_slide_breakdown_exec(bContext *C, wmOperator *op)
pso = op->customdata;
- /* do common exec work */
+ /* Do common exec work. */
return pose_slide_exec_common(C, op, pso);
}
@@ -1588,7 +2059,7 @@ void POSE_OT_breakdown(wmOperatorType *ot)
ot->poll = ED_operator_posemode;
/* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_X;
/* Properties */
pose_slide_opdef_properties(ot);
@@ -1599,36 +2070,39 @@ void POSE_OT_breakdown(wmOperatorType *ot)
/* "termination conditions" - i.e. when we stop */
typedef enum ePosePropagate_Termination {
- /* stop after the current hold ends */
+ /** Stop after the current hold ends. */
POSE_PROPAGATE_SMART_HOLDS = 0,
- /* only do on the last keyframe */
+ /** Only do on the last keyframe. */
POSE_PROPAGATE_LAST_KEY,
- /* stop after the next keyframe */
+ /** Stop after the next keyframe. */
POSE_PROPAGATE_NEXT_KEY,
- /* stop after the specified frame */
+ /** Stop after the specified frame. */
POSE_PROPAGATE_BEFORE_FRAME,
- /* stop when we run out of keyframes */
+ /** Stop when we run out of keyframes. */
POSE_PROPAGATE_BEFORE_END,
- /* only do on keyframes that are selected */
+ /** Only do on keyframes that are selected. */
POSE_PROPAGATE_SELECTED_KEYS,
- /* only do on the frames where markers are selected */
+ /** Only do on the frames where markers are selected. */
POSE_PROPAGATE_SELECTED_MARKERS,
} ePosePropagate_Termination;
-/* Termination data needed for some modes -
- * assumes only one of these entries will be needed at a time. */
+/**
+ * Termination data needed for some modes -
+ * assumes only one of these entries will be needed at a time.
+ */
typedef union tPosePropagate_ModeData {
- /* smart holds + before frame: frame number to stop on */
+ /** Smart holds + before frame: frame number to stop on. */
float end_frame;
- /* selected markers: listbase for CfraElem's marking these frames */
+ /** Selected markers: listbase for CfraElem's marking these frames. */
ListBase sel_markers;
} tPosePropagate_ModeData;
/* --------------------------------- */
-/* get frame on which the "hold" for the bone ends
+/**
+ * Get frame on which the "hold" for the bone ends.
* XXX: this may not really work that well if a bone moves on some channels and not others
* if this happens to be a major issue, scrap this, and just make this happen
* independently per F-Curve
@@ -1642,7 +2116,7 @@ static float pose_propagate_get_boneHoldEndFrame(tPChanFCurveLink *pfl, float st
LinkData *ld;
float endFrame = startFrame;
- /* set up optimized data-structures for searching for relevant keyframes + holds */
+ /* Set up optimized data-structures for searching for relevant keyframes + holds. */
BLI_dlrbTree_init(&keys);
for (ld = pfl->fcurves.first; ld; ld = ld->next) {
@@ -1650,7 +2124,7 @@ static float pose_propagate_get_boneHoldEndFrame(tPChanFCurveLink *pfl, float st
fcurve_to_keylist(adt, fcu, &keys, 0);
}
- /* find the long keyframe (i.e. hold), and hence obtain the endFrame value
+ /* Find the long keyframe (i.e. hold), and hence obtain the endFrame value
* - the best case would be one that starts on the frame itself
*/
ActKeyColumn *ab = (ActKeyColumn *)BLI_dlrbTree_search_exact(
@@ -1664,67 +2138,68 @@ static float pose_propagate_get_boneHoldEndFrame(tPChanFCurveLink *pfl, float st
* otherwise forget it, as we'd be overwriting some valid data.
*/
if (ab == NULL) {
- /* we've got case 1, so try the one after */
+ /* We've got case 1, so try the one after. */
ab = (ActKeyColumn *)BLI_dlrbTree_search_next(&keys, compare_ak_cfraPtr, &startFrame);
if ((actkeyblock_get_valid_hold(ab) & ACTKEYBLOCK_FLAG_STATIC_HOLD) == 0) {
- /* try the block before this frame then as last resort */
+ /* Try the block before this frame then as last resort. */
ab = (ActKeyColumn *)BLI_dlrbTree_search_prev(&keys, compare_ak_cfraPtr, &startFrame);
}
}
- /* whatever happens, stop searching now... */
+ /* Whatever happens, stop searching now.... */
if ((actkeyblock_get_valid_hold(ab) & ACTKEYBLOCK_FLAG_STATIC_HOLD) == 0) {
- /* restrict range to just the frame itself
- * i.e. everything is in motion, so no holds to safely overwrite
- */
+ /* Restrict range to just the frame itself
+ * i.e. everything is in motion, so no holds to safely overwrite. */
ab = NULL;
}
- /* check if we can go any further than we've already gone */
+ /* Check if we can go any further than we've already gone. */
if (ab) {
- /* go to next if it is also valid and meets "extension" criteria */
+ /* Go to next if it is also valid and meets "extension" criteria. */
while (ab->next) {
ActKeyColumn *abn = ab->next;
- /* must be valid */
+ /* Must be valid. */
if ((actkeyblock_get_valid_hold(abn) & ACTKEYBLOCK_FLAG_STATIC_HOLD) == 0) {
break;
}
- /* should have the same number of curves */
+ /* Should have the same number of curves. */
if (ab->totblock != abn->totblock) {
break;
}
- /* we can extend the bounds to the end of this "next" block now */
+ /* We can extend the bounds to the end of this "next" block now. */
ab = abn;
}
- /* end frame can now take the value of the end of the block */
+ /* End frame can now take the value of the end of the block. */
endFrame = ab->next->cfra;
}
- /* free temp memory */
+ /* Free temp memory. */
BLI_dlrbTree_free(&keys);
- /* return the end frame we've found */
+ /* Return the end frame we've found. */
return endFrame;
}
-/* get reference value from F-Curve using RNA */
+/**
+ * Get reference value from F-Curve using RNA.
+ */
static bool pose_propagate_get_refVal(Object *ob, FCurve *fcu, float *value)
{
PointerRNA id_ptr, ptr;
PropertyRNA *prop;
bool found = false;
- /* base pointer is always the object -> id_ptr */
+ /* Base pointer is always the `object -> id_ptr`. */
RNA_id_pointer_create(&ob->id, &id_ptr);
- /* resolve the property... */
+ /* Resolve the property. */
if (RNA_path_resolve_property(&id_ptr, fcu->rna_path, &ptr, &prop)) {
if (RNA_property_array_check(prop)) {
- /* array */
+ /* Array. */
if (fcu->array_index < RNA_property_array_length(&ptr, prop)) {
found = true;
switch (RNA_property_type(prop)) {
@@ -1744,7 +2219,7 @@ static bool pose_propagate_get_refVal(Object *ob, FCurve *fcu, float *value)
}
}
else {
- /* not an array */
+ /* Not an array. */
found = true;
switch (RNA_property_type(prop)) {
case PROP_BOOLEAN:
@@ -1769,7 +2244,9 @@ static bool pose_propagate_get_refVal(Object *ob, FCurve *fcu, float *value)
return found;
}
-/* propagate just works along each F-Curve in turn */
+/**
+ * Propagate just works along each F-Curve in turn.
+ */
static void pose_propagate_fcurve(
wmOperator *op, Object *ob, FCurve *fcu, float startFrame, tPosePropagate_ModeData modeData)
{
@@ -1778,31 +2255,31 @@ static void pose_propagate_fcurve(
BezTriple *bezt;
float refVal = 0.0f;
bool keyExists;
- int i, match;
+ int i;
bool first = true;
- /* skip if no keyframes to edit */
+ /* Skip if no keyframes to edit. */
if ((fcu->bezt == NULL) || (fcu->totvert < 2)) {
return;
}
- /* find the reference value from bones directly, which means that the user
+ /* Find the reference value from bones directly, which means that the user
* doesn't need to firstly keyframe the pose (though this doesn't mean that
- * they can't either)
- */
+ * they can't either). */
if (!pose_propagate_get_refVal(ob, fcu, &refVal)) {
return;
}
- /* find the first keyframe to start propagating from
+ /* Find the first keyframe to start propagating from:
* - if there's a keyframe on the current frame, we probably want to save this value there too
- * since it may be as of yet unkeyed
+ * since it may be as of yet un-keyed
* - if starting before the starting frame, don't touch the key, as it may have had some valid
* values
* - if only doing selected keyframes, start from the first one
*/
if (mode != POSE_PROPAGATE_SELECTED_KEYS) {
- match = BKE_fcurve_bezt_binarysearch_index(fcu->bezt, startFrame, fcu->totvert, &keyExists);
+ const int match = BKE_fcurve_bezt_binarysearch_index(
+ fcu->bezt, startFrame, fcu->totvert, &keyExists);
if (fcu->bezt[match].vec[1][0] < startFrame) {
i = match + 1;
@@ -1812,58 +2289,58 @@ static void pose_propagate_fcurve(
}
}
else {
- /* selected - start from first keyframe */
+ /* Selected - start from first keyframe. */
i = 0;
}
for (bezt = &fcu->bezt[i]; i < fcu->totvert; i++, bezt++) {
- /* additional termination conditions based on the operator 'mode' property go here... */
+ /* Additional termination conditions based on the operator 'mode' property go here. */
if (ELEM(mode, POSE_PROPAGATE_BEFORE_FRAME, POSE_PROPAGATE_SMART_HOLDS)) {
- /* stop if keyframe is outside the accepted range */
+ /* Stop if keyframe is outside the accepted range. */
if (bezt->vec[1][0] > modeData.end_frame) {
break;
}
}
else if (mode == POSE_PROPAGATE_NEXT_KEY) {
- /* stop after the first keyframe has been processed */
+ /* Stop after the first keyframe has been processed. */
if (first == false) {
break;
}
}
else if (mode == POSE_PROPAGATE_LAST_KEY) {
- /* only affect this frame if it will be the last one */
+ /* Only affect this frame if it will be the last one. */
if (i != (fcu->totvert - 1)) {
continue;
}
}
else if (mode == POSE_PROPAGATE_SELECTED_MARKERS) {
- /* only allow if there's a marker on this frame */
+ /* Only allow if there's a marker on this frame. */
CfraElem *ce = NULL;
- /* stop on matching marker if there is one */
+ /* Stop on matching marker if there is one. */
for (ce = modeData.sel_markers.first; ce; ce = ce->next) {
if (ce->cfra == round_fl_to_int(bezt->vec[1][0])) {
break;
}
}
- /* skip this keyframe if no marker */
+ /* Skip this keyframe if no marker. */
if (ce == NULL) {
continue;
}
}
else if (mode == POSE_PROPAGATE_SELECTED_KEYS) {
- /* only allow if this keyframe is already selected - skip otherwise */
+ /* Only allow if this keyframe is already selected - skip otherwise. */
if (BEZT_ISSEL_ANY(bezt) == 0) {
continue;
}
}
- /* just flatten handles, since values will now be the same either side... */
+ /* Just flatten handles, since values will now be the same either side. */
/* TODO: perhaps a fade-out modulation of the value is required here (optional once again)? */
bezt->vec[0][1] = bezt->vec[1][1] = bezt->vec[2][1] = refVal;
- /* select keyframe to indicate that it's been changed */
+ /* Select keyframe to indicate that it's been changed. */
bezt->f2 |= SELECT;
first = false;
}
@@ -1883,7 +2360,7 @@ static int pose_propagate_exec(bContext *C, wmOperator *op)
tPosePropagate_ModeData modeData;
const int mode = RNA_enum_get(op->ptr, "mode");
- /* isolate F-Curves related to the selected bones */
+ /* Isolate F-Curves related to the selected bones. */
poseAnim_mapping_get(C, &pflinks);
if (BLI_listbase_is_empty(&pflinks)) {
@@ -1894,42 +2371,41 @@ static int pose_propagate_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- /* mode-specific data preprocessing (requiring no access to curves) */
+ /* Mode-specific data preprocessing (requiring no access to curves). */
if (mode == POSE_PROPAGATE_SELECTED_MARKERS) {
- /* get a list of selected markers */
+ /* Get a list of selected markers. */
ED_markers_make_cfra_list(&scene->markers, &modeData.sel_markers, SELECT);
}
else {
- /* assume everything else wants endFrame */
+ /* Assume everything else wants endFrame. */
modeData.end_frame = RNA_float_get(op->ptr, "end_frame");
}
- /* for each bone, perform the copying required */
+ /* For each bone, perform the copying required. */
for (pfl = pflinks.first; pfl; pfl = pfl->next) {
LinkData *ld;
- /* mode-specific data preprocessing (requiring access to all curves) */
+ /* Mode-specific data preprocessing (requiring access to all curves). */
if (mode == POSE_PROPAGATE_SMART_HOLDS) {
- /* we store in endFrame the end frame of the "long keyframe" (i.e. a held value) starting
- * from the keyframe that occurs after the current frame
- */
+ /* We store in endFrame the end frame of the "long keyframe" (i.e. a held value) starting
+ * from the keyframe that occurs after the current frame. */
modeData.end_frame = pose_propagate_get_boneHoldEndFrame(pfl, (float)CFRA);
}
- /* go through propagating pose to keyframes, curve by curve */
+ /* Go through propagating pose to keyframes, curve by curve. */
for (ld = pfl->fcurves.first; ld; ld = ld->next) {
pose_propagate_fcurve(op, pfl->ob, (FCurve *)ld->data, (float)CFRA, modeData);
}
}
- /* free temp data */
+ /* Free temp data. */
poseAnim_mapping_free(&pflinks);
if (mode == POSE_PROPAGATE_SELECTED_MARKERS) {
BLI_freelistN(&modeData.sel_markers);
}
- /* updates + notifiers */
+ /* Updates + notifiers. */
FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob) {
poseAnim_mapping_refresh(C, scene, ob);
}
diff --git a/source/blender/editors/armature/pose_transform.c b/source/blender/editors/armature/pose_transform.c
index 8d1c196e9c2..43ab20eb71c 100644
--- a/source/blender/editors/armature/pose_transform.c
+++ b/source/blender/editors/armature/pose_transform.c
@@ -172,9 +172,6 @@ static void applyarmature_transfer_properties(EditBone *curbone,
unit_qt(pchan->quat);
unit_axis_angle(pchan->rotAxis, &pchan->rotAngle);
pchan->size[0] = pchan->size[1] = pchan->size[2] = 1.0f;
-
- /* Set anim lock. */
- curbone->flag |= BONE_UNKEYED;
}
/* Adjust the current edit position of the bone using the pose space matrix. */
@@ -1200,10 +1197,6 @@ static int pose_clear_transform_generic_exec(bContext *C,
/* do auto-keyframing as appropriate */
if (autokeyframe_cfra_can_key(scene, &ob_iter->id)) {
- /* clear any unkeyed tags */
- if (pchan->bone) {
- pchan->bone->flag &= ~BONE_UNKEYED;
- }
/* tag for autokeying later */
ANIM_relative_keyingset_add_source(&dsources, &ob_iter->id, &RNA_PoseBone, pchan);
@@ -1212,12 +1205,6 @@ static int pose_clear_transform_generic_exec(bContext *C,
clear_func(ob_iter->pose, pchan_eval);
#endif
}
- else {
- /* add unkeyed tags */
- if (pchan->bone) {
- pchan->bone->flag |= BONE_UNKEYED;
- }
- }
}
FOREACH_PCHAN_SELECTED_IN_OBJECT_END;
diff --git a/source/blender/editors/armature/pose_utils.c b/source/blender/editors/armature/pose_utils.c
index c75e9c9ef69..75348c2b196 100644
--- a/source/blender/editors/armature/pose_utils.c
+++ b/source/blender/editors/armature/pose_utils.c
@@ -313,11 +313,6 @@ void poseAnim_mapping_autoKeyframe(bContext *C, Scene *scene, ListBase *pfLinks,
/* Add data-source override for the PoseChannel, to be used later. */
ANIM_relative_keyingset_add_source(&dsources, &pfl->ob->id, &RNA_PoseBone, pchan);
-
- /* clear any unkeyed tags */
- if (pchan->bone) {
- pchan->bone->flag &= ~BONE_UNKEYED;
- }
}
/* insert keyframes for all relevant bones in one go */
diff --git a/source/blender/editors/curve/curve_intern.h b/source/blender/editors/curve/curve_intern.h
index 704e4b740de..8ecf41162e9 100644
--- a/source/blender/editors/curve/curve_intern.h
+++ b/source/blender/editors/curve/curve_intern.h
@@ -142,7 +142,10 @@ struct GHash *ED_curve_keyindex_hash_duplicate(struct GHash *keyindex);
void ED_curve_keyindex_update_nurb(struct EditNurb *editnurb, struct Nurb *nu, struct Nurb *newnu);
/* helper functions */
-void ed_editnurb_translate_flag(struct ListBase *editnurb, uint8_t flag, const float vec[3]);
+void ed_editnurb_translate_flag(struct ListBase *editnurb,
+ uint8_t flag,
+ const float vec[3],
+ bool is_2d);
bool ed_editnurb_extrude_flag(struct EditNurb *editnurb, const uint8_t flag);
bool ed_editnurb_spin(float viewmat[4][4],
struct View3D *v3d,
diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c
index 4816a432376..2999ac784ba 100644
--- a/source/blender/editors/curve/editcurve.c
+++ b/source/blender/editors/curve/editcurve.c
@@ -1314,7 +1314,6 @@ void ED_curve_editnurb_make(Object *obedit)
LISTBASE_FOREACH (Nurb *, nu, &cu->nurb) {
Nurb *newnu = BKE_nurb_duplicate(nu);
- BKE_nurb_test_2d(newnu); /* after join, or any other creation of curve */
BLI_addtail(&editnurb->nurbs, newnu);
}
@@ -1706,7 +1705,7 @@ static void rotateflagNurb(ListBase *editnurb,
}
}
-void ed_editnurb_translate_flag(ListBase *editnurb, uint8_t flag, const float vec[3])
+void ed_editnurb_translate_flag(ListBase *editnurb, uint8_t flag, const float vec[3], bool is_2d)
{
/* all verts with ('flag' & flag) translate */
BezTriple *bezt;
@@ -1741,7 +1740,9 @@ void ed_editnurb_translate_flag(ListBase *editnurb, uint8_t flag, const float ve
}
}
- BKE_nurb_test_2d(nu);
+ if (is_2d) {
+ BKE_nurb_project_2d(nu);
+ }
}
}
@@ -5401,7 +5402,7 @@ static int ed_editcurve_addvert(Curve *cu,
mul_v3_fl(center, 1.0f / (float)verts_len);
sub_v3_v3v3(ofs, location_init, center);
- if ((cu->flag & CU_3D) == 0) {
+ if (CU_IS_2D(cu)) {
ofs[2] = 0.0f;
}
@@ -5439,7 +5440,7 @@ static int ed_editcurve_addvert(Curve *cu,
copy_v3_v3(location, location_init);
- if ((cu->flag & CU_3D) == 0) {
+ if (CU_IS_2D(cu)) {
location[2] = 0.0f;
}
@@ -5455,10 +5456,6 @@ static int ed_editcurve_addvert(Curve *cu,
nurb_new->orderu = 4;
nurb_new->flag |= CU_SMOOTH;
BKE_nurb_bezierPoints_add(nurb_new, 1);
-
- if ((cu->flag & CU_3D) == 0) {
- nurb_new->flag |= CU_2D;
- }
}
else {
/* Copy the active nurb settings. */
@@ -5579,7 +5576,7 @@ static int add_vertex_invoke(bContext *C, wmOperator *op, const wmEvent *event)
SCE_SNAP_MODE_FACE,
&(const struct SnapObjectParams){
.snap_select = (vc.obedit != NULL) ? SNAP_NOT_ACTIVE : SNAP_ALL,
- .use_object_edit_cage = false,
+ .edit_mode_type = SNAP_GEOM_FINAL,
},
mval,
NULL,
@@ -5590,7 +5587,7 @@ static int add_vertex_invoke(bContext *C, wmOperator *op, const wmEvent *event)
ED_transform_snap_object_context_destroy(snap_context);
}
- if ((cu->flag & CU_3D) == 0) {
+ if (CU_IS_2D(cu)) {
const float eps = 1e-6f;
/* get the view vector to 'location' */
@@ -6649,7 +6646,7 @@ void CURVE_OT_dissolve_verts(wmOperatorType *ot)
static bool nurb_bezt_flag_any(const Nurb *nu, const char flag_test)
{
- BezTriple *bezt = nu->bezt;
+ const BezTriple *bezt;
int i;
for (i = nu->pntsu, bezt = nu->bezt; i--; bezt++) {
@@ -6922,9 +6919,9 @@ int ED_curve_join_objects_exec(bContext *C, wmOperator *op)
cu = ob_active->data;
BLI_movelisttolist(&cu->nurb, &tempbase);
- if (ob_active->type == OB_CURVE) {
+ if (ob_active->type == OB_CURVE && CU_IS_2D(cu)) {
/* Account for mixed 2D/3D curves when joining */
- BKE_curve_curve_dimension_update(cu);
+ BKE_curve_dimension_update(cu);
}
DEG_relations_tag_update(bmain); /* because we removed object(s), call before editmode! */
diff --git a/source/blender/editors/curve/editcurve_add.c b/source/blender/editors/curve/editcurve_add.c
index 5b66d473466..065763764c1 100644
--- a/source/blender/editors/curve/editcurve_add.c
+++ b/source/blender/editors/curve/editcurve_add.c
@@ -380,10 +380,10 @@ Nurb *ED_curve_add_nurbs_primitive(
mul_mat3_m4_v3(mat, vec);
- ed_editnurb_translate_flag(editnurb, SELECT, vec);
+ ed_editnurb_translate_flag(editnurb, SELECT, vec, CU_IS_2D(cu));
ed_editnurb_extrude_flag(cu->editnurb, SELECT);
mul_v3_fl(vec, -2.0f);
- ed_editnurb_translate_flag(editnurb, SELECT, vec);
+ ed_editnurb_translate_flag(editnurb, SELECT, vec, CU_IS_2D(cu));
BLI_remlink(editnurb, nu);
@@ -492,15 +492,13 @@ Nurb *ED_curve_add_nurbs_primitive(
BLI_assert(nu != NULL);
if (nu) { /* should always be set */
- if ((obedit->type != OB_SURF) && ((cu->flag & CU_3D) == 0)) {
- nu->flag |= CU_2D;
- }
-
nu->flag |= CU_SMOOTH;
cu->actnu = BLI_listbase_count(editnurb);
cu->actvert = CU_ACT_NONE;
- BKE_nurb_test_2d(nu);
+ if (CU_IS_2D(cu)) {
+ BKE_nurb_project_2d(nu);
+ }
}
return nu;
diff --git a/source/blender/editors/curve/editcurve_paint.c b/source/blender/editors/curve/editcurve_paint.c
index 48a36ff276d..03c120df28b 100644
--- a/source/blender/editors/curve/editcurve_paint.c
+++ b/source/blender/editors/curve/editcurve_paint.c
@@ -206,9 +206,11 @@ static bool stroke_elem_project(const struct CurveDrawData *cdd,
else {
const ViewDepths *depths = rv3d->depths;
if (depths && ((uint)mval_i[0] < depths->w) && ((uint)mval_i[1] < depths->h)) {
- const double depth = (double)ED_view3d_depth_read_cached(&cdd->vc, mval_i);
+ float depth_fl = 1.0f;
+ ED_view3d_depth_read_cached(depths, mval_i, 0, &depth_fl);
+ const double depth = (double)depth_fl;
if ((depth > depths->depth_range[0]) && (depth < depths->depth_range[1])) {
- if (ED_view3d_depth_unproject(region, mval_i, depth, r_location_world)) {
+ if (ED_view3d_depth_unproject_v3(region, mval_i, depth, r_location_world)) {
is_location_world_set = true;
if (r_normal_world) {
zero_v3(r_normal_world);
@@ -385,7 +387,6 @@ static void curve_draw_stroke_3d(const struct bContext *UNUSED(C),
GPU_matrix_translate_3f(selem->location_local[0] - location_prev[0],
selem->location_local[1] - location_prev[1],
selem->location_local[2] - location_prev[2]);
- location_prev = selem->location_local;
const float radius = stroke_elem_radius(cdd, selem);
@@ -1072,7 +1073,7 @@ static int curve_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event)
const float *plane_no = NULL;
const float *plane_co = NULL;
- if ((cu->flag & CU_3D) == 0) {
+ if (CU_IS_2D(cu)) {
/* 2D overrides other options */
plane_co = obedit->obmat[3];
plane_no = obedit->obmat[2];
@@ -1083,13 +1084,8 @@ static int curve_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event)
/* needed or else the draw matrix can be incorrect */
view3d_operator_needs_opengl(C);
- ED_view3d_autodist_init(cdd->vc.depsgraph, cdd->vc.region, cdd->vc.v3d, 0);
-
- if (cdd->vc.rv3d->depths) {
- cdd->vc.rv3d->depths->damaged = true;
- }
-
- ED_view3d_depth_update(cdd->vc.region);
+ ED_view3d_depth_override(
+ cdd->vc.depsgraph, cdd->vc.region, cdd->vc.v3d, NULL, V3D_DEPTH_NO_GPENCIL, true);
if (cdd->vc.rv3d->depths != NULL) {
cdd->project.use_depth = true;
diff --git a/source/blender/editors/curve/editcurve_select.c b/source/blender/editors/curve/editcurve_select.c
index e3fc8b73172..90cefef38ee 100644
--- a/source/blender/editors/curve/editcurve_select.c
+++ b/source/blender/editors/curve/editcurve_select.c
@@ -1990,7 +1990,7 @@ static int edcu_shortest_path_pick_invoke(bContext *C, wmOperator *op, const wmE
ED_object_base_activate(C, basact);
}
- DEG_id_tag_update(obedit->data, ID_RECALC_SELECT);
+ DEG_id_tag_update(obedit->data, ID_RECALC_SELECT | ID_RECALC_COPY_ON_WRITE);
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
return OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/curve/editfont.c b/source/blender/editors/curve/editfont.c
index 80771df3572..95970cff4ef 100644
--- a/source/blender/editors/curve/editfont.c
+++ b/source/blender/editors/curve/editfont.c
@@ -1573,7 +1573,8 @@ static int delete_exec(bContext *C, wmOperator *op)
else if (*sel >= range[1]) {
*sel -= len_remove;
}
- else if (*sel < range[1]) {
+ else {
+ BLI_assert(*sel < range[1]);
/* pass */
*sel = range[0];
}
diff --git a/source/blender/editors/geometry/geometry_attributes.c b/source/blender/editors/geometry/geometry_attributes.c
index 12f6bb90677..b2ecee90a57 100644
--- a/source/blender/editors/geometry/geometry_attributes.c
+++ b/source/blender/editors/geometry/geometry_attributes.c
@@ -52,12 +52,16 @@ static const EnumPropertyItem *geometry_attribute_domain_itemf(bContext *C,
PropertyRNA *UNUSED(prop),
bool *r_free)
{
+ if (C == NULL) {
+ return DummyRNA_NULL_items;
+ }
+
Object *ob = ED_object_context(C);
- if (ob != NULL) {
- return rna_enum_attribute_domain_itemf(ob->data, r_free);
+ if (ob == NULL) {
+ return DummyRNA_NULL_items;
}
- return DummyRNA_NULL_items;
+ return rna_enum_attribute_domain_itemf(ob->data, r_free);
}
static int geometry_attribute_add_exec(bContext *C, wmOperator *op)
diff --git a/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c
index f4c4e4eb2ac..d99ce25451c 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c
@@ -320,14 +320,44 @@ static int gizmo_button2d_cursor_get(wmGizmo *gz)
return WM_CURSOR_DEFAULT;
}
-static void gizmo_button2d_bounds(bContext *C, wmGizmo *gz, rcti *r_bounding_box)
+static bool gizmo_button2d_bounds(bContext *C, wmGizmo *gz, rcti *r_bounding_box)
{
ScrArea *area = CTX_wm_area(C);
float rad = CIRCLE_RESOLUTION * U.dpi_fac / 2.0f;
- r_bounding_box->xmin = gz->matrix_basis[3][0] + area->totrct.xmin - rad;
- r_bounding_box->ymin = gz->matrix_basis[3][1] + area->totrct.ymin - rad;
- r_bounding_box->xmax = r_bounding_box->xmin + rad;
- r_bounding_box->ymax = r_bounding_box->ymin + rad;
+ const float *co = NULL;
+ float matrix_final[4][4];
+ float co_proj[3];
+ WM_gizmo_calc_matrix_final(gz, matrix_final);
+
+ if (gz->parent_gzgroup->type->flag & WM_GIZMOGROUPTYPE_3D) {
+ ARegion *region = CTX_wm_region(C);
+ if (ED_view3d_project_float_global(region, matrix_final[3], co_proj, V3D_PROJ_TEST_NOP) ==
+ V3D_PROJ_RET_OK) {
+ float matrix_final_no_offset[4][4];
+ const RegionView3D *rv3d = region->regiondata;
+ WM_gizmo_calc_matrix_final_no_offset(gz, matrix_final_no_offset);
+ const float factor = ED_view3d_pixel_size_no_ui_scale(rv3d, matrix_final_no_offset[3]) /
+ ED_view3d_pixel_size_no_ui_scale(rv3d, matrix_final[3]);
+ /* It's possible (although unlikely) `matrix_final_no_offset` is behind the view.
+ * `matrix_final` has already been projected so both can't be negative. */
+ if (factor > 0.0f) {
+ rad *= factor;
+ }
+ co = co_proj;
+ }
+ }
+ else {
+ co = matrix_final[3];
+ }
+
+ if (co != NULL) {
+ r_bounding_box->xmin = co[0] + area->totrct.xmin - rad;
+ r_bounding_box->ymin = co[1] + area->totrct.ymin - rad;
+ r_bounding_box->xmax = r_bounding_box->xmin + rad;
+ r_bounding_box->ymax = r_bounding_box->ymin + rad;
+ return true;
+ }
+ return false;
}
static void gizmo_button2d_free(wmGizmo *gz)
diff --git a/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c
index 364444f99ae..68322ed56af 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c
@@ -292,7 +292,7 @@ static int gizmo_move_modal(bContext *C,
(SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE),
&(const struct SnapObjectParams){
.snap_select = SNAP_ALL,
- .use_object_edit_cage = true,
+ .edit_mode_type = SNAP_GEOM_EDIT,
.use_occlusion_test = true,
},
mval_fl,
diff --git a/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c
index b8ee1722cb3..b2d3a2e1576 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c
@@ -27,11 +27,14 @@
* \brief Snap gizmo which exposes the location, normal and index in the props.
*/
+#include "BLI_listbase.h"
#include "BLI_math.h"
#include "DNA_scene_types.h"
#include "BKE_context.h"
+#include "BKE_global.h"
+#include "BKE_main.h"
#include "GPU_immediate.h"
#include "GPU_state.h"
@@ -57,11 +60,6 @@
typedef struct SnapGizmo3D {
wmGizmo gizmo;
- PropertyRNA *prop_prevpoint;
- PropertyRNA *prop_location;
- PropertyRNA *prop_normal;
- PropertyRNA *prop_elem_index;
- PropertyRNA *prop_snap_force;
/* We could have other snap contexts, for now only support 3D view. */
SnapObjectContext *snap_context_v3d;
@@ -70,7 +68,9 @@ typedef struct SnapGizmo3D {
struct {
int x;
int y;
+#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK
short shift, ctrl, alt, oskey;
+#endif
} last_eventstate;
#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK
@@ -78,8 +78,18 @@ typedef struct SnapGizmo3D {
int snap_on;
bool invert_snap;
#endif
- int use_snap_override;
+
+ /* Setup. */
+ eSnapGizmo flag;
+ float *prevpoint;
+ float prevpoint_stack[3];
+ short snap_elem_force;
+
+ /* Return values. */
short snap_elem;
+ float loc[3];
+ float nor[3];
+ int elem_index[3];
/** Enabled when snap is activated, even if it didn't find anything. */
bool is_enabled;
@@ -91,28 +101,30 @@ static bool eventstate_has_changed(SnapGizmo3D *snap_gizmo, const wmWindowManage
if (wm && wm->winactive) {
const wmEvent *event = wm->winactive->eventstate;
if ((event->x != snap_gizmo->last_eventstate.x) ||
- (event->y != snap_gizmo->last_eventstate.y) ||
- (event->ctrl != snap_gizmo->last_eventstate.ctrl) ||
- (event->shift != snap_gizmo->last_eventstate.shift) ||
- (event->alt != snap_gizmo->last_eventstate.alt) ||
- (event->oskey != snap_gizmo->last_eventstate.oskey)) {
+ (event->y != snap_gizmo->last_eventstate.y)) {
return true;
}
+#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK
+ if (!(snap_gizmo->flag & ED_SNAPGIZMO_TOGGLE_ALWAYS_TRUE)) {
+ if ((event->ctrl != snap_gizmo->last_eventstate.ctrl) ||
+ (event->shift != snap_gizmo->last_eventstate.shift) ||
+ (event->alt != snap_gizmo->last_eventstate.alt) ||
+ (event->oskey != snap_gizmo->last_eventstate.oskey)) {
+ return true;
+ }
+ }
+#endif
}
return false;
}
/* Copies the current eventstate. */
-static void eventstate_save(SnapGizmo3D *snap_gizmo, const wmWindowManager *wm)
+static void eventstate_save_xy(SnapGizmo3D *snap_gizmo, const wmWindowManager *wm)
{
if (wm && wm->winactive) {
const wmEvent *event = wm->winactive->eventstate;
snap_gizmo->last_eventstate.x = event->x;
snap_gizmo->last_eventstate.y = event->y;
- snap_gizmo->last_eventstate.ctrl = event->ctrl;
- snap_gizmo->last_eventstate.shift = event->shift;
- snap_gizmo->last_eventstate.alt = event->alt;
- snap_gizmo->last_eventstate.oskey = event->oskey;
}
}
@@ -132,11 +144,12 @@ static bool invert_snap(SnapGizmo3D *snap_gizmo, const wmWindowManager *wm)
return snap_gizmo->invert_snap;
}
- if (snap_gizmo->keymap == NULL) {
- /* Lazy initialization. */
- snap_gizmo->keymap = WM_modalkeymap_find(wm->defaultconf, "Generic Gizmo Tweak Modal Map");
- RNA_enum_value_from_id(snap_gizmo->keymap->modal_items, "SNAP_ON", &snap_gizmo->snap_on);
- }
+ /* Save new eventstate. */
+ snap_gizmo->last_eventstate.ctrl = event->ctrl;
+ snap_gizmo->last_eventstate.shift = event->shift;
+ snap_gizmo->last_eventstate.alt = event->alt;
+ snap_gizmo->last_eventstate.oskey = event->oskey;
+
const int snap_on = snap_gizmo->snap_on;
wmKeyMap *keymap = WM_keymap_active(wm, snap_gizmo->keymap);
@@ -158,6 +171,19 @@ static bool invert_snap(SnapGizmo3D *snap_gizmo, const wmWindowManager *wm)
}
#endif
+static short snap_gizmo_snap_elements(SnapGizmo3D *snap_gizmo)
+{
+ int snap_elements = snap_gizmo->snap_elem_force;
+
+ wmGizmoProperty *gz_prop = WM_gizmo_target_property_find(&snap_gizmo->gizmo, "snap_elements");
+ if (gz_prop->prop) {
+ snap_elements |= RNA_property_enum_get(&gz_prop->ptr, gz_prop->prop);
+ }
+ snap_elements &= (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE |
+ SCE_SNAP_MODE_EDGE_MIDPOINT | SCE_SNAP_MODE_EDGE_PERPENDICULAR);
+ return (ushort)snap_elements;
+}
+
/* -------------------------------------------------------------------- */
/** \name ED_gizmo_library specific API
* \{ */
@@ -265,26 +291,32 @@ SnapObjectContext *ED_gizmotypes_snap_3d_context_ensure(Scene *scene,
return snap_gizmo->snap_context_v3d;
}
-bool ED_gizmotypes_snap_3d_invert_snap_get(struct wmGizmo *gz)
+void ED_gizmotypes_snap_3d_flag_set(struct wmGizmo *gz, eSnapGizmo flag)
{
-#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK
SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz;
- return snap_gizmo->invert_snap;
-#else
- return false;
-#endif
+ snap_gizmo->flag |= flag;
}
-void ED_gizmotypes_snap_3d_toggle_set(wmGizmo *gz, bool enable)
+void ED_gizmotypes_snap_3d_flag_clear(struct wmGizmo *gz, eSnapGizmo flag)
{
SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz;
- snap_gizmo->use_snap_override = (int)enable;
+ snap_gizmo->flag &= ~flag;
}
-void ED_gizmotypes_snap_3d_toggle_clear(wmGizmo *gz)
+bool ED_gizmotypes_snap_3d_flag_test(struct wmGizmo *gz, eSnapGizmo flag)
{
SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz;
- snap_gizmo->use_snap_override = -1;
+ return (snap_gizmo->flag & flag) != 0;
+}
+
+bool ED_gizmotypes_snap_3d_invert_snap_get(struct wmGizmo *gz)
+{
+#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK
+ SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz;
+ return snap_gizmo->invert_snap;
+#else
+ return false;
+#endif
}
bool ED_gizmotypes_snap_3d_is_enabled(wmGizmo *gz)
@@ -298,29 +330,17 @@ short ED_gizmotypes_snap_3d_update(wmGizmo *gz,
const ARegion *region,
const View3D *v3d,
const wmWindowManager *wm,
- const float mval_fl[2],
- float r_loc[3],
- float r_nor[3])
+ const float mval_fl[2])
{
SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz;
snap_gizmo->is_enabled = false;
- if (snap_gizmo->use_snap_override != -1) {
- if (snap_gizmo->use_snap_override == false) {
- snap_gizmo->snap_elem = 0;
- return 0;
- }
- }
-
-#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK
- snap_gizmo->invert_snap = invert_snap(snap_gizmo, wm);
-#endif
-
- eventstate_save(snap_gizmo, wm);
Scene *scene = DEG_get_input_scene(depsgraph);
#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK
- if (snap_gizmo->use_snap_override == -1) {
+ if (!(snap_gizmo->flag & ED_SNAPGIZMO_TOGGLE_ALWAYS_TRUE)) {
+ snap_gizmo->invert_snap = invert_snap(snap_gizmo, wm);
+
const ToolSettings *ts = scene->toolsettings;
if (snap_gizmo->invert_snap != !(ts->snap_flag & SCE_SNAP)) {
snap_gizmo->snap_elem = 0;
@@ -328,6 +348,7 @@ short ED_gizmotypes_snap_3d_update(wmGizmo *gz,
}
}
#endif
+ eventstate_save_xy(snap_gizmo, wm);
snap_gizmo->is_enabled = true;
@@ -336,43 +357,50 @@ short ED_gizmotypes_snap_3d_update(wmGizmo *gz,
int snap_elem_index[3] = {-1, -1, -1};
int index = -1;
- wmGizmoProperty *gz_prop = WM_gizmo_target_property_find(gz, "snap_elements");
- int snap_elements = RNA_property_enum_get(&gz_prop->ptr, gz_prop->prop);
- if (gz_prop->prop != snap_gizmo->prop_snap_force) {
- int snap_elements_force = RNA_property_enum_get(gz->ptr, snap_gizmo->prop_snap_force);
- snap_elements |= snap_elements_force;
- }
- snap_elements &= (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE |
- SCE_SNAP_MODE_EDGE_MIDPOINT | SCE_SNAP_MODE_EDGE_PERPENDICULAR);
+ ushort snap_elements = snap_gizmo_snap_elements(snap_gizmo);
if (snap_elements) {
float prev_co[3] = {0.0f};
- if (RNA_property_is_set(gz->ptr, snap_gizmo->prop_prevpoint)) {
- RNA_property_float_get_array(gz->ptr, snap_gizmo->prop_prevpoint, prev_co);
+ if (snap_gizmo->prevpoint) {
+ copy_v3_v3(prev_co, snap_gizmo->prevpoint);
}
else {
snap_elements &= ~SCE_SNAP_MODE_EDGE_PERPENDICULAR;
}
+ eSnapSelect snap_select = (snap_gizmo->flag & ED_SNAPGIZMO_SNAP_ONLY_ACTIVE) ?
+ SNAP_ONLY_ACTIVE :
+ SNAP_ALL;
+
+ eSnapEditType edit_mode_type = (snap_gizmo->flag & ED_SNAPGIZMO_SNAP_EDIT_GEOM_FINAL) ?
+ SNAP_GEOM_FINAL :
+ (snap_gizmo->flag & ED_SNAPGIZMO_SNAP_EDIT_GEOM_CAGE) ?
+ SNAP_GEOM_CAGE :
+ SNAP_GEOM_EDIT;
+
+ bool use_occlusion_test = (snap_gizmo->flag & ED_SNAPGIZMO_OCCLUSION_ALWAYS_TRUE) ? false :
+ true;
+
float dist_px = 12.0f * U.pixelsize;
ED_gizmotypes_snap_3d_context_ensure(scene, region, v3d, gz);
- snap_elem = ED_transform_snap_object_project_view3d_ex(snap_gizmo->snap_context_v3d,
- depsgraph,
- snap_elements,
- &(const struct SnapObjectParams){
- .snap_select = SNAP_ALL,
- .use_object_edit_cage = true,
- .use_occlusion_test = true,
- },
- mval_fl,
- prev_co,
- &dist_px,
- co,
- no,
- &index,
- NULL,
- NULL);
+ snap_elem = ED_transform_snap_object_project_view3d_ex(
+ snap_gizmo->snap_context_v3d,
+ depsgraph,
+ snap_elements,
+ &(const struct SnapObjectParams){
+ .snap_select = snap_select,
+ .edit_mode_type = edit_mode_type,
+ .use_occlusion_test = use_occlusion_test,
+ },
+ mval_fl,
+ prev_co,
+ &dist_px,
+ co,
+ no,
+ &index,
+ NULL,
+ NULL);
}
if (snap_elem == 0) {
@@ -392,45 +420,166 @@ short ED_gizmotypes_snap_3d_update(wmGizmo *gz,
}
snap_gizmo->snap_elem = snap_elem;
- RNA_property_float_set_array(gz->ptr, snap_gizmo->prop_location, co);
- RNA_property_float_set_array(gz->ptr, snap_gizmo->prop_normal, no);
- RNA_property_int_set_array(gz->ptr, snap_gizmo->prop_elem_index, snap_elem_index);
+ copy_v3_v3(snap_gizmo->loc, co);
+ copy_v3_v3(snap_gizmo->nor, no);
+ copy_v3_v3_int(snap_gizmo->elem_index, snap_elem_index);
+
+ return snap_elem;
+}
+void ED_gizmotypes_snap_3d_data_get(
+ wmGizmo *gz, float r_loc[3], float r_nor[3], int r_elem_index[3], int *r_snap_elem)
+{
+ SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz;
+ BLI_assert(snap_gizmo->is_enabled);
if (r_loc) {
- copy_v3_v3(r_loc, co);
+ copy_v3_v3(r_loc, snap_gizmo->loc);
}
-
if (r_nor) {
- copy_v3_v3(r_nor, no);
+ copy_v3_v3(r_nor, snap_gizmo->nor);
+ }
+ if (r_elem_index) {
+ copy_v3_v3_int(r_elem_index, snap_gizmo->elem_index);
+ }
+ if (r_snap_elem) {
+ *r_snap_elem = snap_gizmo->snap_elem;
}
-
- return snap_elem;
}
/** \} */
/* -------------------------------------------------------------------- */
-/** \name GIZMO_GT_snap_3d
+/** \name RNA callbacks
* \{ */
-static void snap_gizmo_setup(wmGizmo *gz)
+/* Based on 'rna_GizmoProperties_find_operator'. */
+static struct SnapGizmo3D *gizmo_snap_rna_find_operator(PointerRNA *ptr)
{
- SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz;
+ IDProperty *properties = ptr->data;
+ for (bScreen *screen = G_MAIN->screens.first; screen; screen = screen->id.next) {
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
+ if (area->spacetype != SPACE_VIEW3D) {
+ continue;
+ }
+ LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
+ if (region->regiontype == RGN_TYPE_WINDOW && region->gizmo_map) {
+ wmGizmoMap *gzmap = region->gizmo_map;
+ LISTBASE_FOREACH (wmGizmoGroup *, gzgroup, WM_gizmomap_group_list(gzmap)) {
+ LISTBASE_FOREACH (wmGizmo *, gz, &gzgroup->gizmos) {
+ if (gz->properties == properties) {
+ return (SnapGizmo3D *)gz;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return NULL;
+}
+
+static int gizmo_snap_rna_snap_elements_force_get_fn(struct PointerRNA *ptr,
+ struct PropertyRNA *UNUSED(prop))
+{
+ SnapGizmo3D *snap_gizmo = gizmo_snap_rna_find_operator(ptr);
+ if (snap_gizmo) {
+ return snap_gizmo->snap_elem_force;
+ }
+ return 0;
+}
+
+static void gizmo_snap_rna_snap_elements_force_set_fn(struct PointerRNA *ptr,
+ struct PropertyRNA *UNUSED(prop),
+ int value)
+{
+ SnapGizmo3D *snap_gizmo = gizmo_snap_rna_find_operator(ptr);
+ if (snap_gizmo) {
+ snap_gizmo->snap_elem_force = (short)value;
+ }
+}
+
+static void gizmo_snap_rna_prevpoint_get_fn(struct PointerRNA *ptr,
+ struct PropertyRNA *UNUSED(prop),
+ float *values)
+{
+ SnapGizmo3D *snap_gizmo = gizmo_snap_rna_find_operator(ptr);
+ if (snap_gizmo) {
+ copy_v3_v3(values, snap_gizmo->prevpoint_stack);
+ }
+}
- /* For quick access to the props. */
- snap_gizmo->prop_prevpoint = RNA_struct_find_property(gz->ptr, "prev_point");
- snap_gizmo->prop_location = RNA_struct_find_property(gz->ptr, "location");
- snap_gizmo->prop_normal = RNA_struct_find_property(gz->ptr, "normal");
- snap_gizmo->prop_elem_index = RNA_struct_find_property(gz->ptr, "snap_elem_index");
- snap_gizmo->prop_snap_force = RNA_struct_find_property(gz->ptr, "snap_elements_force");
+static void gizmo_snap_rna_prevpoint_set_fn(struct PointerRNA *ptr,
+ struct PropertyRNA *UNUSED(prop),
+ const float *values)
+{
+ SnapGizmo3D *snap_gizmo = gizmo_snap_rna_find_operator(ptr);
+ if (snap_gizmo) {
+ if (values) {
+ copy_v3_v3(snap_gizmo->prevpoint_stack, values);
+ snap_gizmo->prevpoint = snap_gizmo->prevpoint_stack;
+ }
+ else {
+ snap_gizmo->prevpoint = NULL;
+ }
+ }
+}
- snap_gizmo->use_snap_override = -1;
+static void gizmo_snap_rna_location_get_fn(struct PointerRNA *ptr,
+ struct PropertyRNA *UNUSED(prop),
+ float *values)
+{
+ SnapGizmo3D *snap_gizmo = gizmo_snap_rna_find_operator(ptr);
+ if (snap_gizmo) {
+ copy_v3_v3(values, snap_gizmo->loc);
+ }
+}
- /* Prop fallback. */
- WM_gizmo_target_property_def_rna(gz, "snap_elements", gz->ptr, "snap_elements_force", -1);
+static void gizmo_snap_rna_location_set_fn(struct PointerRNA *ptr,
+ struct PropertyRNA *UNUSED(prop),
+ const float *values)
+{
+ SnapGizmo3D *snap_gizmo = gizmo_snap_rna_find_operator(ptr);
+ if (snap_gizmo) {
+ copy_v3_v3(snap_gizmo->loc, values);
+ }
+}
- /* Flags. */
+static void gizmo_snap_rna_normal_get_fn(struct PointerRNA *ptr,
+ struct PropertyRNA *UNUSED(prop),
+ float *values)
+{
+ SnapGizmo3D *snap_gizmo = gizmo_snap_rna_find_operator(ptr);
+ if (snap_gizmo) {
+ copy_v3_v3(values, snap_gizmo->nor);
+ }
+}
+
+static void gizmo_snap_rna_snap_elem_index_get_fn(struct PointerRNA *ptr,
+ struct PropertyRNA *UNUSED(prop),
+ int *values)
+{
+ SnapGizmo3D *snap_gizmo = gizmo_snap_rna_find_operator(ptr);
+ if (snap_gizmo) {
+ copy_v3_v3_int(values, snap_gizmo->elem_index);
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name GIZMO_GT_snap_3d
+ * \{ */
+
+static void snap_gizmo_setup(wmGizmo *gz)
+{
gz->flag |= WM_GIZMO_NO_TOOLTIP;
+
+#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK
+ SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz;
+ snap_gizmo->keymap = WM_modalkeymap_find(gz->parent_gzgroup->type->keyconf,
+ "Generic Gizmo Tweak Modal Map");
+ RNA_enum_value_from_id(snap_gizmo->keymap->modal_items, "SNAP_ON", &snap_gizmo->snap_on);
+#endif
}
static void snap_gizmo_draw(const bContext *C, wmGizmo *gz)
@@ -455,42 +604,50 @@ static void snap_gizmo_draw(const bContext *C, wmGizmo *gz)
return;
}
- float location[3], prev_point_stack[3], *prev_point = NULL;
uchar color_line[4], color_point[4];
-
- RNA_property_float_get_array(gz->ptr, snap_gizmo->prop_location, location);
-
UI_GetThemeColor3ubv(TH_TRANSFORM, color_line);
color_line[3] = 128;
rgba_float_to_uchar(color_point, gz->color);
- if (RNA_property_is_set(gz->ptr, snap_gizmo->prop_prevpoint)) {
- RNA_property_float_get_array(gz->ptr, snap_gizmo->prop_prevpoint, prev_point_stack);
- prev_point = prev_point_stack;
- }
-
GPU_line_smooth(false);
GPU_line_width(1.0f);
+
+ const float *prev_point = (snap_gizmo_snap_elements(snap_gizmo) &
+ SCE_SNAP_MODE_EDGE_PERPENDICULAR) ?
+ snap_gizmo->prevpoint :
+ NULL;
+
ED_gizmotypes_snap_3d_draw_util(
- rv3d, prev_point, location, NULL, color_line, color_point, snap_gizmo->snap_elem);
+ rv3d, prev_point, snap_gizmo->loc, NULL, color_line, color_point, snap_gizmo->snap_elem);
}
static int snap_gizmo_test_select(bContext *C, wmGizmo *gz, const int mval[2])
{
SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz;
wmWindowManager *wm = CTX_wm_manager(C);
+ ARegion *region = CTX_wm_region(C);
+
+ /* FIXME: this hack is to ignore drag events, otherwise drag events
+ * cause momentary snap gizmo re-positioning at the drag-start location, see: T87511. */
+ if (wm && wm->winactive) {
+ const wmEvent *event = wm->winactive->eventstate;
+ int mval_compare[2] = {event->x - region->winrct.xmin, event->y - region->winrct.ymin};
+ if (!equals_v2v2_int(mval_compare, mval)) {
+ return snap_gizmo->snap_elem ? 0 : -1;
+ }
+ }
+
if (!eventstate_has_changed(snap_gizmo, wm)) {
/* Performance, do not update. */
return snap_gizmo->snap_elem ? 0 : -1;
}
- ARegion *region = CTX_wm_region(C);
View3D *v3d = CTX_wm_view3d(C);
const float mval_fl[2] = {UNPACK2(mval)};
short snap_elem = ED_gizmotypes_snap_3d_update(
- gz, CTX_data_ensure_evaluated_depsgraph(C), region, v3d, wm, mval_fl, NULL, NULL);
+ gz, CTX_data_ensure_evaluated_depsgraph(C), region, v3d, wm, mval_fl);
if (snap_elem) {
ED_region_tag_redraw_editor_overlays(region);
@@ -553,57 +710,74 @@ static void GIZMO_GT_snap_3d(wmGizmoType *gzt)
}
/* Setup. */
- RNA_def_enum_flag(gzt->srna,
- "snap_elements_force",
- rna_enum_snap_element_items,
- SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE,
- "Snap Elements",
- "");
-
- RNA_def_float_vector(gzt->srna,
- "prev_point",
- 3,
- NULL,
- FLT_MIN,
- FLT_MAX,
- "Previous Point",
- "Point that defines the location of the perpendicular snap",
- FLT_MIN,
- FLT_MAX);
+ PropertyRNA *prop;
+ prop = RNA_def_enum_flag(gzt->srna,
+ "snap_elements_force",
+ rna_enum_snap_element_items,
+ SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE,
+ "Snap Elements",
+ "");
+
+ RNA_def_property_enum_funcs_runtime(prop,
+ gizmo_snap_rna_snap_elements_force_get_fn,
+ gizmo_snap_rna_snap_elements_force_set_fn,
+ NULL);
+
+ prop = RNA_def_float_array(gzt->srna,
+ "prev_point",
+ 3,
+ NULL,
+ FLT_MIN,
+ FLT_MAX,
+ "Previous Point",
+ "Point that defines the location of the perpendicular snap",
+ FLT_MIN,
+ FLT_MAX);
+
+ RNA_def_property_float_array_funcs_runtime(
+ prop, gizmo_snap_rna_prevpoint_get_fn, gizmo_snap_rna_prevpoint_set_fn, NULL);
/* Returns. */
- RNA_def_float_vector(gzt->srna,
- "location",
- 3,
- NULL,
- FLT_MIN,
- FLT_MAX,
- "Location",
- "Snap Point Location",
- FLT_MIN,
- FLT_MAX);
-
- RNA_def_float_vector(gzt->srna,
- "normal",
- 3,
- NULL,
- FLT_MIN,
- FLT_MAX,
- "Normal",
- "Snap Point Normal",
- FLT_MIN,
- FLT_MAX);
-
- RNA_def_int_vector(gzt->srna,
- "snap_elem_index",
- 3,
- NULL,
- INT_MIN,
- INT_MAX,
- "Snap Element",
- "Array index of face, edge and vert snapped",
- INT_MIN,
- INT_MAX);
+ prop = RNA_def_float_translation(gzt->srna,
+ "location",
+ 3,
+ NULL,
+ FLT_MIN,
+ FLT_MAX,
+ "Location",
+ "Snap Point Location",
+ FLT_MIN,
+ FLT_MAX);
+
+ RNA_def_property_float_array_funcs_runtime(
+ prop, gizmo_snap_rna_location_get_fn, gizmo_snap_rna_location_set_fn, NULL);
+
+ prop = RNA_def_float_vector_xyz(gzt->srna,
+ "normal",
+ 3,
+ NULL,
+ FLT_MIN,
+ FLT_MAX,
+ "Normal",
+ "Snap Point Normal",
+ FLT_MIN,
+ FLT_MAX);
+
+ RNA_def_property_float_array_funcs_runtime(prop, gizmo_snap_rna_normal_get_fn, NULL, NULL);
+
+ prop = RNA_def_int_vector(gzt->srna,
+ "snap_elem_index",
+ 3,
+ NULL,
+ INT_MIN,
+ INT_MAX,
+ "Snap Element",
+ "Array index of face, edge and vert snapped",
+ INT_MIN,
+ INT_MAX);
+
+ RNA_def_property_int_array_funcs_runtime(
+ prop, gizmo_snap_rna_snap_elem_index_get_fn, NULL, NULL);
/* Read/Write. */
WM_gizmotype_target_property_def(gzt, "snap_elements", PROP_ENUM, 1);
diff --git a/source/blender/editors/gpencil/CMakeLists.txt b/source/blender/editors/gpencil/CMakeLists.txt
index 47ae90acb74..bff7310e9f7 100644
--- a/source/blender/editors/gpencil/CMakeLists.txt
+++ b/source/blender/editors/gpencil/CMakeLists.txt
@@ -36,10 +36,12 @@ set(SRC
annotate_paint.c
drawgpencil.c
editaction_gpencil.c
+ gpencil_add_blank.c
gpencil_add_lineart.c
gpencil_add_monkey.c
gpencil_add_stroke.c
gpencil_armature.c
+ gpencil_bake_animation.c
gpencil_convert.c
gpencil_data.c
gpencil_edit.c
diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c
index e9817f82090..c155587e95a 100644
--- a/source/blender/editors/gpencil/annotate_paint.c
+++ b/source/blender/editors/gpencil/annotate_paint.c
@@ -660,10 +660,14 @@ static short annotation_stroke_addpoint(tGPsdata *p,
View3D *v3d = p->area->spacedata.first;
view3d_region_operator_needs_opengl(p->win, p->region);
- ED_view3d_autodist_init(p->depsgraph,
- p->region,
- v3d,
- (ts->annotate_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 1 : 0);
+ ED_view3d_depth_override(p->depsgraph,
+ p->region,
+ v3d,
+ NULL,
+ (ts->annotate_v3d_align & GP_PROJECT_DEPTH_STROKE) ?
+ V3D_DEPTH_GPENCIL_ONLY :
+ V3D_DEPTH_NO_GPENCIL,
+ false);
}
/* convert screen-coordinates to appropriate coordinates (and store them) */
@@ -1222,7 +1226,7 @@ static void annotation_stroke_doeraser(tGPsdata *p)
if (p->flags & GP_PAINTFLAG_V3D_ERASER_DEPTH) {
View3D *v3d = p->area->spacedata.first;
view3d_region_operator_needs_opengl(p->win, p->region);
- ED_view3d_autodist_init(p->depsgraph, p->region, v3d, 0);
+ ED_view3d_depth_override(p->depsgraph, p->region, v3d, NULL, V3D_DEPTH_NO_GPENCIL, false);
}
}
@@ -1544,7 +1548,7 @@ static void annotation_paint_initstroke(tGPsdata *p,
if (p->gpl == NULL) {
/* tag for annotations */
p->gpd->flag |= GP_DATA_ANNOTATIONS;
- p->gpl = BKE_gpencil_layer_addnew(p->gpd, DATA_("Note"), true);
+ p->gpl = BKE_gpencil_layer_addnew(p->gpd, DATA_("Note"), true, false);
if (p->custom_color[3]) {
copy_v3_v3(p->gpl->color, p->custom_color);
@@ -1695,8 +1699,14 @@ static void annotation_paint_strokeend(tGPsdata *p)
/* need to restore the original projection settings before packing up */
view3d_region_operator_needs_opengl(p->win, p->region);
- ED_view3d_autodist_init(
- p->depsgraph, p->region, v3d, (ts->annotate_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 1 : 0);
+ ED_view3d_depth_override(p->depsgraph,
+ p->region,
+ v3d,
+ NULL,
+ (ts->annotate_v3d_align & GP_PROJECT_DEPTH_STROKE) ?
+ V3D_DEPTH_GPENCIL_ONLY :
+ V3D_DEPTH_NO_GPENCIL,
+ false);
}
/* check if doing eraser or not */
diff --git a/source/blender/editors/gpencil/gpencil_add_blank.c b/source/blender/editors/gpencil/gpencil_add_blank.c
new file mode 100644
index 00000000000..3aa16e54597
--- /dev/null
+++ b/source/blender/editors/gpencil/gpencil_add_blank.c
@@ -0,0 +1,101 @@
+/*
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2017 Blender Foundation
+ * This is a new part of Blender
+ */
+
+/** \file
+ * \ingroup edgpencil
+ */
+
+#include "BLI_math.h"
+#include "BLI_utildefines.h"
+
+#include "DNA_gpencil_types.h"
+#include "DNA_material_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+
+#include "BKE_context.h"
+#include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
+#include "BKE_main.h"
+#include "BKE_material.h"
+
+#include "DEG_depsgraph.h"
+
+#include "ED_gpencil.h"
+
+/* Definition of the most important info from a color */
+typedef struct ColorTemplate {
+ const char *name;
+ float line[4];
+ float fill[4];
+} ColorTemplate;
+
+/* Add color an ensure duplications (matched by name) */
+static int gpencil_stroke_material(Main *bmain, Object *ob, const ColorTemplate *pct)
+{
+ int index;
+ Material *ma = BKE_gpencil_object_material_ensure_by_name(bmain, ob, pct->name, &index);
+
+ copy_v4_v4(ma->gp_style->stroke_rgba, pct->line);
+ srgb_to_linearrgb_v4(ma->gp_style->stroke_rgba, ma->gp_style->stroke_rgba);
+
+ copy_v4_v4(ma->gp_style->fill_rgba, pct->fill);
+ srgb_to_linearrgb_v4(ma->gp_style->fill_rgba, ma->gp_style->fill_rgba);
+
+ return index;
+}
+
+/* ***************************************************************** */
+/* Stroke Geometry */
+
+/* ***************************************************************** */
+/* Color Data */
+
+static const ColorTemplate gp_stroke_material_black = {
+ "Black",
+ {0.0f, 0.0f, 0.0f, 1.0f},
+ {0.0f, 0.0f, 0.0f, 0.0f},
+};
+
+/* ***************************************************************** */
+/* Blank API */
+
+/* Add a Simple empty object with one layer and one color. */
+void ED_gpencil_create_blank(bContext *C, Object *ob, float UNUSED(mat[4][4]))
+{
+ Main *bmain = CTX_data_main(C);
+ Scene *scene = CTX_data_scene(C);
+ bGPdata *gpd = (bGPdata *)ob->data;
+
+ /* create colors */
+ int color_black = gpencil_stroke_material(bmain, ob, &gp_stroke_material_black);
+
+ /* set first color as active and in brushes */
+ ob->actcol = color_black + 1;
+
+ /* layers */
+ bGPDlayer *layer = BKE_gpencil_layer_addnew(gpd, "GP_Layer", true, false);
+
+ /* frames */
+ BKE_gpencil_frame_addnew(layer, CFRA);
+
+ /* update depsgraph */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ gpd->flag |= GP_DATA_CACHE_IS_DIRTY;
+}
diff --git a/source/blender/editors/gpencil/gpencil_add_lineart.c b/source/blender/editors/gpencil/gpencil_add_lineart.c
index 6b28c6ec13e..ac0da0ad1db 100644
--- a/source/blender/editors/gpencil/gpencil_add_lineart.c
+++ b/source/blender/editors/gpencil/gpencil_add_lineart.c
@@ -96,7 +96,7 @@ void ED_gpencil_create_lineart(bContext *C, Object *ob)
ob->actcol = color_black + 1;
/* layers */
- bGPDlayer *lines = BKE_gpencil_layer_addnew(gpd, "Lines", true);
+ bGPDlayer *lines = BKE_gpencil_layer_addnew(gpd, "Lines", true, false);
/* frames */
BKE_gpencil_frame_addnew(lines, 0);
diff --git a/source/blender/editors/gpencil/gpencil_add_monkey.c b/source/blender/editors/gpencil/gpencil_add_monkey.c
index 4497d963c6d..d8734c4ae6b 100644
--- a/source/blender/editors/gpencil/gpencil_add_monkey.c
+++ b/source/blender/editors/gpencil/gpencil_add_monkey.c
@@ -837,8 +837,8 @@ void ED_gpencil_create_monkey(bContext *C, Object *ob, float mat[4][4])
/* layers */
/* NOTE: For now, we just add new layers, to make it easier to separate out old/new instances */
- bGPDlayer *Fills = BKE_gpencil_layer_addnew(gpd, "Fills", false);
- bGPDlayer *Lines = BKE_gpencil_layer_addnew(gpd, "Lines", true);
+ bGPDlayer *Fills = BKE_gpencil_layer_addnew(gpd, "Fills", false, false);
+ bGPDlayer *Lines = BKE_gpencil_layer_addnew(gpd, "Lines", true, false);
/* frames */
/* NOTE: No need to check for existing, as this will take care of it for us */
diff --git a/source/blender/editors/gpencil/gpencil_add_stroke.c b/source/blender/editors/gpencil/gpencil_add_stroke.c
index 26237636526..e95496b51ee 100644
--- a/source/blender/editors/gpencil/gpencil_add_stroke.c
+++ b/source/blender/editors/gpencil/gpencil_add_stroke.c
@@ -225,8 +225,8 @@ void ED_gpencil_create_stroke(bContext *C, Object *ob, float mat[4][4])
ob->actcol = color_black + 1;
/* layers */
- bGPDlayer *colors = BKE_gpencil_layer_addnew(gpd, "Colors", false);
- bGPDlayer *lines = BKE_gpencil_layer_addnew(gpd, "Lines", true);
+ bGPDlayer *colors = BKE_gpencil_layer_addnew(gpd, "Colors", false, false);
+ bGPDlayer *lines = BKE_gpencil_layer_addnew(gpd, "Lines", true, false);
/* frames */
bGPDframe *frame_color = BKE_gpencil_frame_addnew(colors, CFRA);
diff --git a/source/blender/editors/gpencil/gpencil_bake_animation.c b/source/blender/editors/gpencil/gpencil_bake_animation.c
new file mode 100644
index 00000000000..30ebc9189c5
--- /dev/null
+++ b/source/blender/editors/gpencil/gpencil_bake_animation.c
@@ -0,0 +1,448 @@
+/*
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2021 Blender Foundation
+ * This is a new part of Blender
+ */
+
+/** \file
+ * \ingroup edgpencil
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_ghash.h"
+#include "BLI_math.h"
+
+#include "DNA_anim_types.h"
+#include "DNA_gpencil_types.h"
+#include "DNA_material_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
+
+#include "BKE_anim_data.h"
+#include "BKE_context.h"
+#include "BKE_duplilist.h"
+#include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
+#include "BKE_layer.h"
+#include "BKE_main.h"
+#include "BKE_material.h"
+#include "BKE_object.h"
+#include "BKE_report.h"
+#include "BKE_scene.h"
+
+#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_query.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "ED_gpencil.h"
+#include "ED_transform_snap_object_context.h"
+
+#include "gpencil_intern.h"
+
+const EnumPropertyItem rna_gpencil_reproject_type_items[] = {
+ {GP_REPROJECT_KEEP, "KEEP", 0, "No Reproject", ""},
+ {GP_REPROJECT_FRONT, "FRONT", 0, "Front", "Reproject the strokes using the X-Z plane"},
+ {GP_REPROJECT_SIDE, "SIDE", 0, "Side", "Reproject the strokes using the Y-Z plane"},
+ {GP_REPROJECT_TOP, "TOP", 0, "Top", "Reproject the strokes using the X-Y plane"},
+ {GP_REPROJECT_VIEW,
+ "VIEW",
+ 0,
+ "View",
+ "Reproject the strokes to end up on the same plane, as if drawn from the current "
+ "viewpoint "
+ "using 'Cursor' Stroke Placement"},
+ {GP_REPROJECT_CURSOR,
+ "CURSOR",
+ 0,
+ "Cursor",
+ "Reproject the strokes using the orientation of 3D cursor"},
+ {0, NULL, 0, NULL, NULL},
+};
+
+/* Check frame_end is always > start frame! */
+static void gpencil_bake_set_frame_end(struct Main *UNUSED(main),
+ struct Scene *UNUSED(scene),
+ struct PointerRNA *ptr)
+{
+ int frame_start = RNA_int_get(ptr, "frame_start");
+ int frame_end = RNA_int_get(ptr, "frame_end");
+
+ if (frame_end <= frame_start) {
+ RNA_int_set(ptr, "frame_end", frame_start + 1);
+ }
+}
+
+/* Extract mesh animation to Grease Pencil. */
+static bool gpencil_bake_grease_pencil_animation_poll(bContext *C)
+{
+ Object *obact = CTX_data_active_object(C);
+ if (CTX_data_mode_enum(C) != CTX_MODE_OBJECT) {
+ return false;
+ }
+
+ /* Check if grease pencil or empty for dupli groups. */
+ if ((obact == NULL) || ((obact->type != OB_GPENCIL) && (obact->type != OB_EMPTY))) {
+ return false;
+ }
+
+ /* Only if the current view is 3D View. */
+ ScrArea *area = CTX_wm_area(C);
+ return (area && area->spacetype);
+}
+
+typedef struct GpBakeOb {
+ struct GpBakeOb *next, *prev;
+ Object *ob;
+} GpBakeOb;
+
+/* Get list of keyframes used by selected objects. */
+static void animdata_keyframe_list_get(ListBase *ob_list,
+ const bool only_selected,
+ GHash *r_keyframes)
+{
+ /* Loop all objects to get the list of keyframes used. */
+ LISTBASE_FOREACH (GpBakeOb *, elem, ob_list) {
+ Object *ob = elem->ob;
+ AnimData *adt = BKE_animdata_from_id(&ob->id);
+ if ((adt == NULL) || (adt->action == NULL)) {
+ continue;
+ }
+ LISTBASE_FOREACH (FCurve *, fcurve, &adt->action->curves) {
+ int i;
+ BezTriple *bezt;
+ for (i = 0, bezt = fcurve->bezt; i < fcurve->totvert; i++, bezt++) {
+ /* Keyframe number is x value of point. */
+ if ((bezt->f2 & SELECT) || (!only_selected)) {
+ /* Insert only one key for each keyframe number. */
+ int key = (int)bezt->vec[1][0];
+ if (!BLI_ghash_haskey(r_keyframes, POINTER_FROM_INT(key))) {
+ BLI_ghash_insert(r_keyframes, POINTER_FROM_INT(key), POINTER_FROM_INT(key));
+ }
+ }
+ }
+ }
+ }
+}
+
+static void gpencil_bake_duplilist(Depsgraph *depsgraph, Scene *scene, Object *ob, ListBase *list)
+{
+ GpBakeOb *elem = NULL;
+ ListBase *lb;
+ DupliObject *dob;
+ lb = object_duplilist(depsgraph, scene, ob);
+ for (dob = lb->first; dob; dob = dob->next) {
+ if (dob->ob->type != OB_GPENCIL) {
+ continue;
+ }
+
+ elem = MEM_callocN(sizeof(GpBakeOb), __func__);
+ elem->ob = dob->ob;
+ BLI_addtail(list, elem);
+ }
+
+ free_object_duplilist(lb);
+}
+
+static void gpencil_bake_ob_list(bContext *C, Depsgraph *depsgraph, Scene *scene, ListBase *list)
+{
+ GpBakeOb *elem = NULL;
+
+ /* Add active object. In some files this could not be in selected array. */
+ Object *obact = CTX_data_active_object(C);
+
+ if (obact->type == OB_GPENCIL) {
+ elem = MEM_callocN(sizeof(GpBakeOb), __func__);
+ elem->ob = obact;
+ BLI_addtail(list, elem);
+ }
+ /* Add duplilist. */
+ else if (obact->type == OB_EMPTY) {
+ gpencil_bake_duplilist(depsgraph, scene, obact, list);
+ }
+
+ /* Add other selected objects. */
+ CTX_DATA_BEGIN (C, Object *, ob, selected_objects) {
+ if (ob == obact) {
+ continue;
+ }
+ /* Add selected objects.*/
+ if (ob->type == OB_GPENCIL) {
+ elem = MEM_callocN(sizeof(GpBakeOb), __func__);
+ elem->ob = ob;
+ BLI_addtail(list, elem);
+ }
+
+ /* Add duplilist. */
+ if (ob->type == OB_EMPTY) {
+ gpencil_bake_duplilist(depsgraph, scene, ob, list);
+ }
+ }
+ CTX_DATA_END;
+}
+
+static void gpencil_bake_free_ob_list(ListBase *list)
+{
+ LISTBASE_FOREACH_MUTABLE (GpBakeOb *, elem, list) {
+ MEM_SAFE_FREE(elem);
+ }
+}
+
+static int gpencil_bake_grease_pencil_animation_exec(bContext *C, wmOperator *op)
+{
+ Main *bmain = CTX_data_main(C);
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ Scene *scene = CTX_data_scene(C);
+ ARegion *region = CTX_wm_region(C);
+ View3D *v3d = CTX_wm_view3d(C);
+
+ ListBase ob_selected_list = {NULL, NULL};
+ gpencil_bake_ob_list(C, depsgraph, scene, &ob_selected_list);
+
+ /* Grab all relevant settings. */
+ const int step = RNA_int_get(op->ptr, "step");
+
+ const int frame_start = (scene->r.sfra > RNA_int_get(op->ptr, "frame_start")) ?
+ scene->r.sfra :
+ RNA_int_get(op->ptr, "frame_start");
+
+ const int frame_end = (scene->r.efra < RNA_int_get(op->ptr, "frame_end")) ?
+ scene->r.efra :
+ RNA_int_get(op->ptr, "frame_end");
+
+ const bool only_selected = RNA_boolean_get(op->ptr, "only_selected");
+ const int frame_offset = RNA_int_get(op->ptr, "frame_target") - frame_start;
+ const int project_type = RNA_enum_get(op->ptr, "project_type");
+
+ /* Create a new grease pencil object. */
+ Object *ob_gpencil = NULL;
+ ushort local_view_bits = (v3d && v3d->localvd) ? v3d->local_view_uuid : 0;
+ ob_gpencil = ED_gpencil_add_object(C, scene->cursor.location, local_view_bits);
+ float invmat[4][4];
+ invert_m4_m4(invmat, ob_gpencil->obmat);
+
+ bGPdata *gpd_dst = (bGPdata *)ob_gpencil->data;
+ gpd_dst->draw_mode = GP_DRAWMODE_2D;
+
+ /* Set cursor to indicate working. */
+ WM_cursor_wait(true);
+
+ GP_SpaceConversion gsc = {NULL};
+ SnapObjectContext *sctx = NULL;
+ if (project_type != GP_REPROJECT_KEEP) {
+ /* Init space conversion stuff. */
+ gpencil_point_conversion_init(C, &gsc);
+ /* Move the grease pencil object to conversion data. */
+ gsc.ob = ob_gpencil;
+
+ /* Init snap context for geometry projection. */
+ sctx = ED_transform_snap_object_context_create_view3d(scene, 0, region, CTX_wm_view3d(C));
+ }
+
+ /* Loop all frame range. */
+ int oldframe = (int)DEG_get_ctime(depsgraph);
+ int key = -1;
+
+ /* Get list of keyframes. */
+ GHash *keyframe_list = BLI_ghash_int_new(__func__);
+ if (only_selected) {
+ animdata_keyframe_list_get(&ob_selected_list, only_selected, keyframe_list);
+ }
+
+ for (int i = frame_start; i < frame_end + 1; i++) {
+ key++;
+ /* Jump if not step limit but include last frame always. */
+ if ((key % step != 0) && (i != frame_end)) {
+ continue;
+ }
+
+ /* Check if frame is in the list of frames to be exported. */
+ if ((only_selected) && (!BLI_ghash_haskey(keyframe_list, POINTER_FROM_INT(i)))) {
+ continue;
+ }
+
+ /* Move scene to new frame. */
+ CFRA = i;
+ BKE_scene_graph_update_for_newframe(depsgraph);
+
+ /* Loop all objects in the list. */
+ LISTBASE_FOREACH (GpBakeOb *, elem, &ob_selected_list) {
+ Object *ob_eval = (Object *)DEG_get_evaluated_object(depsgraph, elem->ob);
+ bGPdata *gpd_src = ob_eval->data;
+
+ LISTBASE_FOREACH (bGPDlayer *, gpl_src, &gpd_src->layers) {
+ /* Create destination layer. */
+ char *layer_name;
+ layer_name = BLI_sprintfN("%s_%s", elem->ob->id.name + 2, gpl_src->info);
+ bGPDlayer *gpl_dst = BKE_gpencil_layer_named_get(gpd_dst, layer_name);
+ if (gpl_dst == NULL) {
+ gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, layer_name, true, false);
+ }
+ MEM_freeN(layer_name);
+
+ /* Layer Transform matrix. */
+ float matrix[4][4];
+ BKE_gpencil_layer_transform_matrix_get(depsgraph, elem->ob, gpl_src, matrix);
+
+ /* Duplicate frame. */
+ bGPDframe *gpf_src = BKE_gpencil_layer_frame_get(gpl_src, CFRA, GP_GETFRAME_USE_PREV);
+ if (gpf_src == NULL) {
+ continue;
+ }
+ bGPDframe *gpf_dst = BKE_gpencil_frame_duplicate(gpf_src, true);
+ gpf_dst->framenum = CFRA + frame_offset;
+ gpf_dst->flag &= ~GP_FRAME_SELECT;
+ BLI_addtail(&gpl_dst->frames, gpf_dst);
+
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf_dst->strokes) {
+ /* Create material of the stroke. */
+ Material *ma_src = BKE_object_material_get(elem->ob, gps->mat_nr + 1);
+ bool found = false;
+ for (int index = 0; index < ob_gpencil->totcol; index++) {
+ Material *ma_dst = BKE_object_material_get(ob_gpencil, index + 1);
+ if (ma_src == ma_dst) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ BKE_object_material_slot_add(bmain, ob_gpencil);
+ BKE_object_material_assign(
+ bmain, ob_gpencil, ma_src, ob_gpencil->totcol, BKE_MAT_ASSIGN_USERPREF);
+ }
+
+ /* Set new material index. */
+ gps->mat_nr = BKE_gpencil_object_material_index_get(ob_gpencil, ma_src);
+
+ /* Update point location to new object space. */
+ for (int j = 0; j < gps->totpoints; j++) {
+ bGPDspoint *pt = &gps->points[j];
+ mul_m4_v3(matrix, &pt->x);
+ mul_m4_v3(invmat, &pt->x);
+ }
+
+ /* Reproject stroke. */
+ if (project_type != GP_REPROJECT_KEEP) {
+ ED_gpencil_stroke_reproject(
+ depsgraph, &gsc, sctx, gpl_dst, gpf_dst, gps, project_type, false);
+ }
+ else {
+ BKE_gpencil_stroke_geometry_update(gpd_dst, gps);
+ }
+ }
+ }
+ }
+ }
+ /* Return scene frame state and DB to original state. */
+ CFRA = oldframe;
+ BKE_scene_graph_update_for_newframe(depsgraph);
+
+ /* Free memory. */
+ gpencil_bake_free_ob_list(&ob_selected_list);
+ if (sctx != NULL) {
+ ED_transform_snap_object_context_destroy(sctx);
+ }
+ /* Free temp hash table. */
+ if (keyframe_list != NULL) {
+ BLI_ghash_free(keyframe_list, NULL, NULL);
+ }
+
+ /* Notifiers. */
+ DEG_relations_tag_update(bmain);
+ DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
+ DEG_id_tag_update(&gpd_dst->id, ID_RECALC_COPY_ON_WRITE);
+ WM_event_add_notifier(C, NC_OBJECT | NA_ADDED, NULL);
+ WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene);
+
+ /* Reset cursor. */
+ WM_cursor_wait(false);
+
+ /* done */
+ return OPERATOR_FINISHED;
+}
+
+static int gpencil_bake_grease_pencil_animation_invoke(bContext *C,
+ wmOperator *op,
+ const wmEvent *UNUSED(event))
+{
+ PropertyRNA *prop;
+ Scene *scene = CTX_data_scene(C);
+
+ prop = RNA_struct_find_property(op->ptr, "frame_start");
+ if (!RNA_property_is_set(op->ptr, prop)) {
+ const int frame_start = RNA_property_int_get(op->ptr, prop);
+ if (frame_start < scene->r.sfra) {
+ RNA_property_int_set(op->ptr, prop, scene->r.sfra);
+ }
+ }
+
+ prop = RNA_struct_find_property(op->ptr, "frame_end");
+ if (!RNA_property_is_set(op->ptr, prop)) {
+ const int frame_end = RNA_property_int_get(op->ptr, prop);
+ if (frame_end > scene->r.efra) {
+ RNA_property_int_set(op->ptr, prop, scene->r.efra);
+ }
+ }
+
+ /* Show popup dialog to allow editing. */
+ return WM_operator_props_dialog_popup(C, op, 250);
+}
+
+void GPENCIL_OT_bake_grease_pencil_animation(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+
+ /* identifiers */
+ ot->name = "Bake Object Transform to Grease Pencil";
+ ot->idname = "GPENCIL_OT_bake_grease_pencil_animation";
+ ot->description = "Bake grease pencil object transform to grease pencil keyframes";
+
+ /* callbacks */
+ ot->invoke = gpencil_bake_grease_pencil_animation_invoke;
+ ot->exec = gpencil_bake_grease_pencil_animation_exec;
+ ot->poll = gpencil_bake_grease_pencil_animation_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ prop = RNA_def_int(
+ ot->srna, "frame_start", 1, 1, 100000, "Start Frame", "The start frame", 1, 100000);
+
+ prop = RNA_def_int(
+ ot->srna, "frame_end", 250, 1, 100000, "End Frame", "The end frame of animation", 1, 100000);
+ RNA_def_property_update_runtime(prop, gpencil_bake_set_frame_end);
+
+ prop = RNA_def_int(ot->srna, "step", 1, 1, 100, "Step", "Step between generated frames", 1, 100);
+
+ RNA_def_boolean(
+ ot->srna, "only_selected", 0, "Only Selected Keyframes", "Convert only selected keyframes");
+ RNA_def_int(
+ ot->srna, "frame_target", 1, 1, 100000, "Target Frame", "Destination frame", 1, 100000);
+
+ RNA_def_enum(ot->srna,
+ "project_type",
+ rna_gpencil_reproject_type_items,
+ GP_REPROJECT_KEEP,
+ "Projection Type",
+ "");
+}
diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c
index ac75ae44c8a..8ab413e907c 100644
--- a/source/blender/editors/gpencil/gpencil_convert.c
+++ b/source/blender/editors/gpencil/gpencil_convert.c
@@ -1857,7 +1857,7 @@ static int image_to_gpencil_exec(bContext *C, wmOperator *op)
/* Add layer and frame. */
bGPdata *gpd = (bGPdata *)ob->data;
- bGPDlayer *gpl = BKE_gpencil_layer_addnew(gpd, "Image Layer", true);
+ bGPDlayer *gpl = BKE_gpencil_layer_addnew(gpd, "Image Layer", true, false);
bGPDframe *gpf = BKE_gpencil_frame_addnew(gpl, CFRA);
done = BKE_gpencil_from_image(sima, gpd, gpf, size, is_mask);
diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c
index fd2758c8a08..d9a807d17ab 100644
--- a/source/blender/editors/gpencil/gpencil_data.c
+++ b/source/blender/editors/gpencil/gpencil_data.c
@@ -129,7 +129,7 @@ static int gpencil_data_add_exec(bContext *C, wmOperator *op)
gpd->flag |= GP_DATA_ANNOTATIONS;
/* add new layer (i.e. a "note") */
- BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("Note"), true);
+ BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("Note"), true, false);
/* notifiers */
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
@@ -231,7 +231,7 @@ static int gpencil_layer_add_exec(bContext *C, wmOperator *op)
/* mark as annotation */
(*gpd_ptr)->flag |= GP_DATA_ANNOTATIONS;
- BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("Note"), true);
+ BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("Note"), true, false);
gpd = *gpd_ptr;
}
else {
@@ -239,7 +239,7 @@ static int gpencil_layer_add_exec(bContext *C, wmOperator *op)
Object *ob = CTX_data_active_object(C);
if ((ob != NULL) && (ob->type == OB_GPENCIL)) {
gpd = (bGPdata *)ob->data;
- bGPDlayer *gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true);
+ bGPDlayer *gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true, false);
/* Add a new frame to make it visible in Dopesheet. */
if (gpl != NULL) {
gpl->actframe = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_ADD_NEW);
@@ -524,7 +524,6 @@ enum {
static bool gpencil_layer_duplicate_object_poll(bContext *C)
{
- ViewLayer *view_layer = CTX_data_view_layer(C);
Object *ob = CTX_data_active_object(C);
if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
return false;
@@ -537,90 +536,75 @@ static bool gpencil_layer_duplicate_object_poll(bContext *C)
return false;
}
- /* check there are more grease pencil objects */
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
- if ((base->object != ob) && (base->object->type == OB_GPENCIL)) {
- return true;
- }
- }
-
- return false;
+ return true;
}
static int gpencil_layer_duplicate_object_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
- Scene *scene = CTX_data_scene(C);
- char name[MAX_ID_NAME - 2];
- RNA_string_get(op->ptr, "object", name);
-
- if (name[0] == '\0') {
- return OPERATOR_CANCELLED;
- }
-
- Object *ob_dst = (Object *)BKE_scene_object_find_by_name(scene, name);
-
- int mode = RNA_enum_get(op->ptr, "mode");
+ const bool only_active = RNA_boolean_get(op->ptr, "only_active");
+ const int mode = RNA_enum_get(op->ptr, "mode");
Object *ob_src = CTX_data_active_object(C);
bGPdata *gpd_src = (bGPdata *)ob_src->data;
- bGPDlayer *gpl_src = BKE_gpencil_layer_active_get(gpd_src);
+ bGPDlayer *gpl_active = BKE_gpencil_layer_active_get(gpd_src);
- /* Sanity checks. */
- if (ELEM(NULL, gpd_src, gpl_src, ob_dst)) {
- return OPERATOR_CANCELLED;
- }
- /* Cannot copy itself and check destination type. */
- if ((ob_src == ob_dst) || (ob_dst->type != OB_GPENCIL)) {
- return OPERATOR_CANCELLED;
- }
-
- bGPdata *gpd_dst = (bGPdata *)ob_dst->data;
-
- /* Create new layer. */
- bGPDlayer *gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, gpl_src->info, true);
- /* Need to copy some variables (not all). */
- gpl_dst->onion_flag = gpl_src->onion_flag;
- gpl_dst->thickness = gpl_src->thickness;
- gpl_dst->line_change = gpl_src->line_change;
- copy_v4_v4(gpl_dst->tintcolor, gpl_src->tintcolor);
- gpl_dst->opacity = gpl_src->opacity;
-
- /* Create all frames. */
- LISTBASE_FOREACH (bGPDframe *, gpf_src, &gpl_src->frames) {
-
- if ((mode == GP_LAYER_COPY_OBJECT_ACT_FRAME) && (gpf_src != gpl_src->actframe)) {
+ CTX_DATA_BEGIN (C, Object *, ob, selected_objects) {
+ if ((ob == ob_src) || (ob->type != OB_GPENCIL)) {
continue;
}
+ bGPdata *gpd_dst = (bGPdata *)ob->data;
+ LISTBASE_FOREACH_BACKWARD (bGPDlayer *, gpl_src, &gpd_src->layers) {
+ if ((only_active) && (gpl_src != gpl_active)) {
+ continue;
+ }
+ /* Create new layer (adding at head of the list). */
+ bGPDlayer *gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, gpl_src->info, true, true);
+ /* Need to copy some variables (not all). */
+ gpl_dst->onion_flag = gpl_src->onion_flag;
+ gpl_dst->thickness = gpl_src->thickness;
+ gpl_dst->line_change = gpl_src->line_change;
+ copy_v4_v4(gpl_dst->tintcolor, gpl_src->tintcolor);
+ gpl_dst->opacity = gpl_src->opacity;
+
+ /* Create all frames. */
+ LISTBASE_FOREACH (bGPDframe *, gpf_src, &gpl_src->frames) {
+
+ if ((mode == GP_LAYER_COPY_OBJECT_ACT_FRAME) && (gpf_src != gpl_src->actframe)) {
+ continue;
+ }
- /* Create new frame. */
- bGPDframe *gpf_dst = BKE_gpencil_frame_addnew(gpl_dst, gpf_src->framenum);
+ /* Create new frame. */
+ bGPDframe *gpf_dst = BKE_gpencil_frame_addnew(gpl_dst, gpf_src->framenum);
- /* Copy strokes. */
- LISTBASE_FOREACH (bGPDstroke *, gps_src, &gpf_src->strokes) {
+ /* Copy strokes. */
+ LISTBASE_FOREACH (bGPDstroke *, gps_src, &gpf_src->strokes) {
- /* Make copy of source stroke. */
- bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps_src, true, true);
+ /* Make copy of source stroke. */
+ bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps_src, true, true);
- /* Check if material is in destination object,
- * otherwise add the slot with the material. */
- Material *ma_src = BKE_object_material_get(ob_src, gps_src->mat_nr + 1);
- if (ma_src != NULL) {
- int idx = BKE_gpencil_object_material_ensure(bmain, ob_dst, ma_src);
+ /* Check if material is in destination object,
+ * otherwise add the slot with the material. */
+ Material *ma_src = BKE_object_material_get(ob_src, gps_src->mat_nr + 1);
+ if (ma_src != NULL) {
+ int idx = BKE_gpencil_object_material_ensure(bmain, ob, ma_src);
- /* Reassign the stroke material to the right slot in destination object. */
- gps_dst->mat_nr = idx;
- }
+ /* Reassign the stroke material to the right slot in destination object. */
+ gps_dst->mat_nr = idx;
+ }
- /* Add new stroke to frame. */
- BLI_addtail(&gpf_dst->strokes, gps_dst);
+ /* Add new stroke to frame. */
+ BLI_addtail(&gpf_dst->strokes, gps_dst);
+ }
+ }
}
+ /* notifiers */
+ DEG_id_tag_update(&gpd_dst->id,
+ ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
+ DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE);
}
+ CTX_DATA_END;
- /* notifiers */
- DEG_id_tag_update(&gpd_dst->id,
- ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
- DEG_id_tag_update(&ob_dst->id, ID_RECALC_COPY_ON_WRITE);
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
return OPERATOR_FINISHED;
@@ -628,6 +612,8 @@ static int gpencil_layer_duplicate_object_exec(bContext *C, wmOperator *op)
void GPENCIL_OT_layer_duplicate_object(wmOperatorType *ot)
{
+ PropertyRNA *prop;
+
static const EnumPropertyItem copy_mode[] = {
{GP_LAYER_COPY_OBJECT_ALL_FRAME, "ALL", 0, "All Frames", ""},
{GP_LAYER_COPY_OBJECT_ACT_FRAME, "ACTIVE", 0, "Active Frame", ""},
@@ -637,7 +623,7 @@ void GPENCIL_OT_layer_duplicate_object(wmOperatorType *ot)
/* identifiers */
ot->name = "Duplicate Layer to New Object";
ot->idname = "GPENCIL_OT_layer_duplicate_object";
- ot->description = "Make a copy of the active Grease Pencil layer to new object";
+ ot->description = "Make a copy of the active Grease Pencil layer to selected object";
/* callbacks */
ot->exec = gpencil_layer_duplicate_object_exec;
@@ -646,11 +632,14 @@ void GPENCIL_OT_layer_duplicate_object(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
- ot->prop = RNA_def_string(
- ot->srna, "object", NULL, MAX_ID_NAME - 2, "Object", "Name of the destination object");
- RNA_def_property_flag(ot->prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+ ot->prop = RNA_def_enum(ot->srna, "mode", copy_mode, GP_LAYER_COPY_OBJECT_ALL_FRAME, "Mode", "");
- RNA_def_enum(ot->srna, "mode", copy_mode, GP_LAYER_COPY_OBJECT_ALL_FRAME, "Mode", "");
+ prop = RNA_def_boolean(ot->srna,
+ "only_active",
+ true,
+ "Only Active",
+ "Copy only active Layer, uncheck to append all layers");
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
}
/* ********************* Duplicate Frame ************************** */
@@ -1443,7 +1432,7 @@ static int gpencil_layer_change_exec(bContext *C, wmOperator *op)
/* Get layer or create new one */
if (layer_num == -1) {
/* Create layer */
- gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true);
+ gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true, false);
}
else {
/* Try to get layer */
@@ -1555,6 +1544,7 @@ static int gpencil_stroke_arrange_exec(bContext *C, wmOperator *op)
const int direction = RNA_enum_get(op->ptr, "direction");
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
+ bGPDstroke *gps_target = NULL;
bool changed = false;
CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
@@ -1569,7 +1559,6 @@ static int gpencil_stroke_arrange_exec(bContext *C, wmOperator *op)
if (gpf == NULL) {
continue;
}
- bool gpf_lock = false;
/* verify if any selected stroke is in the extreme of the stack and select to move */
for (gps = gpf->strokes.first; gps; gps = gps->next) {
/* only if selected */
@@ -1582,18 +1571,19 @@ static int gpencil_stroke_arrange_exec(bContext *C, wmOperator *op)
if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) {
continue;
}
+ bool gpf_lock = false;
/* some stroke is already at front*/
if (ELEM(direction, GP_STROKE_MOVE_TOP, GP_STROKE_MOVE_UP)) {
if (gps == gpf->strokes.last) {
gpf_lock = true;
- continue;
+ gps_target = gps;
}
}
/* Some stroke is already at bottom. */
if (ELEM(direction, GP_STROKE_MOVE_BOTTOM, GP_STROKE_MOVE_DOWN)) {
if (gps == gpf->strokes.first) {
gpf_lock = true;
- continue;
+ gps_target = gps;
}
}
/* add to list (if not locked) */
@@ -1602,47 +1592,74 @@ static int gpencil_stroke_arrange_exec(bContext *C, wmOperator *op)
}
}
}
+
+ const int target_index = (gps_target) ? BLI_findindex(&gpf->strokes, gps_target) : -1;
+ int prev_index = target_index;
/* Now do the movement of the stroke */
- if (!gpf_lock) {
- switch (direction) {
- /* Bring to Front */
- case GP_STROKE_MOVE_TOP:
- LISTBASE_FOREACH (LinkData *, link, &selected) {
- gps = link->data;
- BLI_remlink(&gpf->strokes, gps);
+ switch (direction) {
+ /* Bring to Front */
+ case GP_STROKE_MOVE_TOP:
+ LISTBASE_FOREACH (LinkData *, link, &selected) {
+ gps = link->data;
+ BLI_remlink(&gpf->strokes, gps);
+ if (gps_target) {
+ BLI_insertlinkbefore(&gpf->strokes, gps_target, gps);
+ }
+ else {
BLI_addtail(&gpf->strokes, gps);
- changed = true;
}
- break;
- /* Bring Forward */
- case GP_STROKE_MOVE_UP:
- LISTBASE_FOREACH_BACKWARD (LinkData *, link, &selected) {
- gps = link->data;
- BLI_listbase_link_move(&gpf->strokes, gps, 1);
- changed = true;
+ changed = true;
+ }
+ break;
+ /* Bring Forward */
+ case GP_STROKE_MOVE_UP:
+ LISTBASE_FOREACH_BACKWARD (LinkData *, link, &selected) {
+ gps = link->data;
+ if (gps_target) {
+ int gps_index = BLI_findindex(&gpf->strokes, gps);
+ if (gps_index + 1 >= prev_index) {
+ prev_index = gps_index;
+ continue;
+ }
+ prev_index = gps_index;
+ }
+ BLI_listbase_link_move(&gpf->strokes, gps, 1);
+ changed = true;
+ }
+ break;
+ /* Send Backward */
+ case GP_STROKE_MOVE_DOWN:
+ LISTBASE_FOREACH (LinkData *, link, &selected) {
+ gps = link->data;
+ if (gps_target) {
+ int gps_index = BLI_findindex(&gpf->strokes, gps);
+ if (gps_index - 1 <= prev_index) {
+ prev_index = gps_index;
+ continue;
+ }
+ prev_index = gps_index;
}
- break;
- /* Send Backward */
- case GP_STROKE_MOVE_DOWN:
- LISTBASE_FOREACH (LinkData *, link, &selected) {
- gps = link->data;
- BLI_listbase_link_move(&gpf->strokes, gps, -1);
- changed = true;
+ BLI_listbase_link_move(&gpf->strokes, gps, -1);
+ changed = true;
+ }
+ break;
+ /* Send to Back */
+ case GP_STROKE_MOVE_BOTTOM:
+ LISTBASE_FOREACH_BACKWARD (LinkData *, link, &selected) {
+ gps = link->data;
+ BLI_remlink(&gpf->strokes, gps);
+ if (gps_target) {
+ BLI_insertlinkafter(&gpf->strokes, gps_target, gps);
}
- break;
- /* Send to Back */
- case GP_STROKE_MOVE_BOTTOM:
- LISTBASE_FOREACH_BACKWARD (LinkData *, link, &selected) {
- gps = link->data;
- BLI_remlink(&gpf->strokes, gps);
+ else {
BLI_addhead(&gpf->strokes, gps);
- changed = true;
}
- break;
- default:
- BLI_assert(0);
- break;
- }
+ changed = true;
+ }
+ break;
+ default:
+ BLI_assert(0);
+ break;
}
BLI_freelistN(&selected);
}
@@ -3561,6 +3578,79 @@ void GPENCIL_OT_set_active_material(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
+/* ********************* Append Materials in a new object ************************** */
+static bool gpencil_materials_copy_to_object_poll(bContext *C)
+{
+ Object *ob = CTX_data_active_object(C);
+ if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
+ return false;
+ }
+ short *totcolp = BKE_object_material_len_p(ob);
+ if (*totcolp == 0) {
+ return false;
+ }
+
+ return true;
+}
+
+static int gpencil_materials_copy_to_object_exec(bContext *C, wmOperator *op)
+{
+ Main *bmain = CTX_data_main(C);
+ const bool only_active = RNA_boolean_get(op->ptr, "only_active");
+ Object *ob_src = CTX_data_active_object(C);
+ Material *ma_active = BKE_gpencil_material(ob_src, ob_src->actcol);
+
+ CTX_DATA_BEGIN (C, Object *, ob, selected_objects) {
+ if ((ob == ob_src) || (ob->type != OB_GPENCIL)) {
+ continue;
+ }
+ /* Duplicate materials. */
+ for (int i = 0; i < ob_src->totcol; i++) {
+ Material *ma_src = BKE_object_material_get(ob_src, i + 1);
+ if (only_active && ma_src != ma_active) {
+ continue;
+ }
+
+ if (ma_src != NULL) {
+ BKE_gpencil_object_material_ensure(bmain, ob, ma_src);
+ }
+ }
+
+ /* notifiers */
+ DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE);
+ }
+ CTX_DATA_END;
+
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_materials_copy_to_object(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+
+ /* identifiers */
+ ot->name = "Copy Materials to Selected Object";
+ ot->idname = "GPENCIL_OT_materials_copy_to_object";
+ ot->description = "Append Materials of the active Grease Pencil to other object";
+
+ /* callbacks */
+ ot->exec = gpencil_materials_copy_to_object_exec;
+ ot->poll = gpencil_materials_copy_to_object_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ prop = RNA_def_boolean(ot->srna,
+ "only_active",
+ true,
+ "Only Active",
+ "Append only active material, uncheck to append all materials");
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+}
+
/* Parent GPencil object to Lattice */
bool ED_gpencil_add_lattice_modifier(const bContext *C,
ReportList *reports,
@@ -3717,3 +3807,51 @@ void GPENCIL_OT_layer_mask_remove(wmOperatorType *ot)
ot->exec = gpencil_layer_mask_remove_exec;
ot->poll = gpencil_active_layer_poll;
}
+
+static int gpencil_layer_mask_move_exec(bContext *C, wmOperator *op)
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd);
+ const int direction = RNA_enum_get(op->ptr, "type");
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd, gpl)) {
+ return OPERATOR_CANCELLED;
+ }
+ if (gpl->act_mask > 0) {
+ bGPDlayer_Mask *mask = BLI_findlink(&gpl->mask_layers, gpl->act_mask - 1);
+ if (mask != NULL) {
+ BLI_assert(ELEM(direction, -1, 0, 1)); /* we use value below */
+ if (BLI_listbase_link_move(&gpl->mask_layers, mask, direction)) {
+ gpl->act_mask += direction;
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+ }
+ }
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_layer_mask_move(wmOperatorType *ot)
+{
+ static const EnumPropertyItem slot_move[] = {
+ {GP_LAYER_MOVE_UP, "UP", 0, "Up", ""},
+ {GP_LAYER_MOVE_DOWN, "DOWN", 0, "Down", ""},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ /* identifiers */
+ ot->name = "Move Grease Pencil Layer Mask";
+ ot->idname = "GPENCIL_OT_layer_mask_move";
+ ot->description = "Move the active Grease Pencil mask layer up/down in the list";
+
+ /* api callbacks */
+ ot->exec = gpencil_layer_mask_move_exec;
+ ot->poll = gpencil_active_layer_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ ot->prop = RNA_def_enum(ot->srna, "type", slot_move, 0, "Type", "");
+}
diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c
index 4bbd475dd2c..f29f5187015 100644
--- a/source/blender/editors/gpencil/gpencil_edit.c
+++ b/source/blender/editors/gpencil/gpencil_edit.c
@@ -1677,7 +1677,7 @@ static int gpencil_strokes_paste_exec(bContext *C, wmOperator *op)
if (gpl == NULL) {
/* no active layer - let's just create one */
- gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true);
+ gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true, false);
}
else if ((BKE_gpencil_layer_is_editable(gpl) == false) && (type == GP_COPY_TO_ACTIVE)) {
BKE_report(
@@ -1818,18 +1818,13 @@ static int gpencil_move_to_layer_exec(bContext *C, wmOperator *op)
{
Object *ob = CTX_data_active_object(C);
bGPdata *gpd = (bGPdata *)ob->data;
- Scene *scene = CTX_data_scene(C);
bGPDlayer *target_layer = NULL;
ListBase strokes = {NULL, NULL};
int layer_num = RNA_int_get(op->ptr, "layer");
const bool use_autolock = (bool)(gpd->flag & GP_DATA_AUTOLOCK_LAYERS);
+ const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
- if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) {
- BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition");
- return OPERATOR_CANCELLED;
- }
-
- /* if autolock enabled, disabled now */
+ /* If autolock enabled, disabled now. */
if (use_autolock) {
gpd->flag &= ~GP_DATA_AUTOLOCK_LAYERS;
}
@@ -1840,7 +1835,7 @@ static int gpencil_move_to_layer_exec(bContext *C, wmOperator *op)
}
else {
/* Create a new layer. */
- target_layer = BKE_gpencil_layer_addnew(gpd, "GP_Layer", true);
+ target_layer = BKE_gpencil_layer_addnew(gpd, "GP_Layer", true, false);
}
if (target_layer == NULL) {
@@ -1852,53 +1847,59 @@ static int gpencil_move_to_layer_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- /* Extract all strokes to move to this layer
- * NOTE: We need to do this in a two-pass system to avoid conflicts with strokes
- * getting repeatedly moved
- */
- CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
- bGPDframe *gpf = gpl->actframe;
-
- /* skip if no frame with strokes, or if this is the layer we're moving strokes to */
- if ((gpl == target_layer) || (gpf == NULL)) {
+ /* Extract all strokes to move to this layer. */
+ CTX_DATA_BEGIN (C, bGPDlayer *, gpl_src, editable_gpencil_layers) {
+ /* Skip if this is the layer we're moving strokes to. */
+ if (gpl_src == target_layer) {
continue;
}
+ bGPDframe *init_gpf = (is_multiedit) ? gpl_src->frames.first : gpl_src->actframe;
+ for (bGPDframe *gpf_src = init_gpf; gpf_src; gpf_src = gpf_src->next) {
+ if ((gpf_src == gpl_src->actframe) ||
+ ((gpf_src->flag & GP_FRAME_SELECT) && (is_multiedit))) {
+ if (gpf_src == NULL) {
+ continue;
+ }
- /* make copies of selected strokes, and deselect these once we're done */
- LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
+ bGPDstroke *gpsn = NULL;
+ BLI_listbase_clear(&strokes);
+ for (bGPDstroke *gps = gpf_src->strokes.first; gps; gps = gpsn) {
+ gpsn = gps->next;
+ /* Skip strokes that are invalid for current view. */
+ if (ED_gpencil_stroke_can_use(C, gps) == false) {
+ continue;
+ }
+ /* Check if the color is editable. */
+ if (ED_gpencil_stroke_material_editable(ob, gpl_src, gps) == false) {
+ continue;
+ }
- /* skip strokes that are invalid for current view */
- if (ED_gpencil_stroke_can_use(C, gps) == false) {
- continue;
- }
+ if (gps->flag & GP_STROKE_SELECT) {
+ BLI_remlink(&gpf_src->strokes, gps);
+ BLI_addtail(&strokes, gps);
+ }
+ }
+ /* Paste them all in one go. */
+ if (strokes.first) {
+ bGPDframe *gpf_dst = BKE_gpencil_layer_frame_get(
+ target_layer, gpf_src->framenum, GP_GETFRAME_ADD_NEW);
- /* Check if the color is editable. */
- if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) {
- continue;
+ BLI_movelisttolist(&gpf_dst->strokes, &strokes);
+ BLI_assert((strokes.first == strokes.last) && (strokes.first == NULL));
+ }
}
-
- /* TODO: Don't just move entire strokes - instead, only copy the selected portions... */
- if (gps->flag & GP_STROKE_SELECT) {
- BLI_remlink(&gpf->strokes, gps);
- BLI_addtail(&strokes, gps);
+ /* If not multi-edit, exit loop. */
+ if (!is_multiedit) {
+ break;
}
}
-
- /* if new layer and autolock, lock old layer */
+ /* If new layer and autolock, lock old layer. */
if ((layer_num == -1) && (use_autolock)) {
- gpl->flag |= GP_LAYER_LOCKED;
+ gpl_src->flag |= GP_LAYER_LOCKED;
}
}
CTX_DATA_END;
- /* Paste them all in one go */
- if (strokes.first) {
- bGPDframe *gpf = BKE_gpencil_layer_frame_get(target_layer, CFRA, GP_GETFRAME_ADD_NEW);
-
- BLI_movelisttolist(&gpf->strokes, &strokes);
- BLI_assert((strokes.first == strokes.last) && (strokes.first == NULL));
- }
-
/* back autolock status */
if (use_autolock) {
gpd->flag |= GP_DATA_AUTOLOCK_LAYERS;
@@ -2778,7 +2779,7 @@ void GPENCIL_OT_dissolve(wmOperatorType *ot)
/* Poll callback for snap operators */
/* NOTE: For now, we only allow these in the 3D view, as other editors do not
- * define a cursor or gridstep which can be used
+ * define a cursor or grid-step which can be used.
*/
static bool gpencil_snap_poll(bContext *C)
{
@@ -3442,7 +3443,7 @@ void GPENCIL_OT_stroke_caps_set(wmOperatorType *ot)
{GP_STROKE_CAPS_TOGGLE_BOTH, "TOGGLE", 0, "Both", ""},
{GP_STROKE_CAPS_TOGGLE_START, "START", 0, "Start", ""},
{GP_STROKE_CAPS_TOGGLE_END, "END", 0, "End", ""},
- {GP_STROKE_CAPS_TOGGLE_DEFAULT, "TOGGLE", 0, "Default", "Set as default rounded"},
+ {GP_STROKE_CAPS_TOGGLE_DEFAULT, "DEFAULT", 0, "Default", "Set as default rounded"},
{0, NULL, 0, NULL, NULL},
};
@@ -3762,6 +3763,7 @@ static int gpencil_strokes_reproject_exec(bContext *C, wmOperator *op)
const eGP_ReprojectModes mode = RNA_enum_get(op->ptr, "type");
const bool keep_original = RNA_boolean_get(op->ptr, "keep_original");
const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
+ const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
/* Init snap context for geometry projection. */
SnapObjectContext *sctx = NULL;
@@ -3774,36 +3776,55 @@ static int gpencil_strokes_reproject_exec(bContext *C, wmOperator *op)
int cfra_prv = INT_MIN;
/* Go through each editable + selected stroke, adjusting each of its points one by one... */
- GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
- bool curve_select = false;
- if (is_curve_edit && gps->editcurve != NULL) {
- curve_select = gps->editcurve->flag & GP_CURVE_SELECT;
- }
+ CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
+ bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
- if (gps->flag & GP_STROKE_SELECT || curve_select) {
+ for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
+ if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
+ if (gpf == NULL) {
+ continue;
+ }
+ for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps) == false) {
+ continue;
+ }
+ bool curve_select = false;
+ if (is_curve_edit && gps->editcurve != NULL) {
+ curve_select = gps->editcurve->flag & GP_CURVE_SELECT;
+ }
- /* update frame to get the new location of objects */
- if ((mode == GP_REPROJECT_SURFACE) && (cfra_prv != gpf_->framenum)) {
- cfra_prv = gpf_->framenum;
- CFRA = gpf_->framenum;
- BKE_scene_graph_update_for_newframe(depsgraph);
- }
+ if (gps->flag & GP_STROKE_SELECT || curve_select) {
- ED_gpencil_stroke_reproject(depsgraph, &gsc, sctx, gpl, gpf_, gps, mode, keep_original);
+ /* update frame to get the new location of objects */
+ if ((mode == GP_REPROJECT_SURFACE) && (cfra_prv != gpf->framenum)) {
+ cfra_prv = gpf->framenum;
+ CFRA = gpf->framenum;
+ BKE_scene_graph_update_for_newframe(depsgraph);
+ }
- if (is_curve_edit && gps->editcurve != NULL) {
- BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps);
- /* Update the selection from the stroke to the curve. */
- BKE_gpencil_editcurve_stroke_sync_selection(gpd, gps, gps->editcurve);
+ ED_gpencil_stroke_reproject(depsgraph, &gsc, sctx, gpl, gpf, gps, mode, keep_original);
- gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
- BKE_gpencil_stroke_geometry_update(gpd, gps);
- }
+ if (is_curve_edit && gps->editcurve != NULL) {
+ BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps);
+ /* Update the selection from the stroke to the curve. */
+ BKE_gpencil_editcurve_stroke_sync_selection(gpd, gps, gps->editcurve);
- changed = true;
+ gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
+ }
+
+ changed = true;
+ }
+ }
+ }
+ /* If not multi-edit, exit loop. */
+ if (!is_multiedit) {
+ break;
+ }
}
}
- GP_EDITABLE_STROKES_END(gpstroke_iter);
+ CTX_DATA_END;
/* return frame state and DB to original state */
CFRA = oldframe;
@@ -3832,7 +3853,8 @@ void GPENCIL_OT_reproject(wmOperatorType *ot)
"VIEW",
0,
"View",
- "Reproject the strokes to end up on the same plane, as if drawn from the current viewpoint "
+ "Reproject the strokes to end up on the same plane, as if drawn from the current "
+ "viewpoint "
"using 'Cursor' Stroke Placement"},
{GP_REPROJECT_SURFACE,
"SURFACE",
@@ -3851,7 +3873,8 @@ void GPENCIL_OT_reproject(wmOperatorType *ot)
ot->name = "Reproject Strokes";
ot->idname = "GPENCIL_OT_reproject";
ot->description =
- "Reproject the selected strokes from the current viewpoint as if they had been newly drawn "
+ "Reproject the selected strokes from the current viewpoint as if they had been newly "
+ "drawn "
"(e.g. to fix problems from accidental 3D cursor movement or accidental viewport changes, "
"or for matching deforming geometry)";
@@ -3951,7 +3974,7 @@ static void gpencil_smooth_stroke(bContext *C, wmOperator *op)
}
if (smooth_thickness) {
/* thickness need to repeat process several times */
- for (int r2 = 0; r2 < r * 20; r2++) {
+ for (int r2 = 0; r2 < 20; r2++) {
BKE_gpencil_stroke_smooth_thickness(gps, i, factor);
}
}
@@ -3981,6 +4004,11 @@ static int gpencil_count_subdivision_cuts(bGPDstroke *gps)
}
}
+ if ((gps->flag & GP_STROKE_CYCLIC) && (gps->points[0].flag & GP_SPOINT_SELECT) &&
+ (gps->points[gps->totpoints - 1].flag & GP_SPOINT_SELECT)) {
+ totnewpoints++;
+ }
+
return totnewpoints;
}
@@ -4079,6 +4107,47 @@ static void gpencil_stroke_subdivide(bGPDstroke *gps, const int cuts)
}
}
}
+
+ /* Subdivide between last and first point. */
+ if (gps->flag & GP_STROKE_CYCLIC) {
+ bGPDspoint *pt = &temp_points[oldtotpoints - 1];
+ bGPDspoint *next = &temp_points[0];
+ if ((pt->flag & GP_SPOINT_SELECT) && (next->flag & GP_SPOINT_SELECT)) {
+ bGPDspoint *pt_final = &gps->points[i2];
+ if (gps->dvert != NULL) {
+ dvert_final = &gps->dvert[i2];
+ }
+ /* Interpolate all values */
+ interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f);
+ pt_final->pressure = interpf(pt->pressure, next->pressure, 0.5f);
+ pt_final->strength = interpf(pt->strength, next->strength, 0.5f);
+ CLAMP(pt_final->strength, GPENCIL_STRENGTH_MIN, 1.0f);
+ interp_v4_v4v4(pt_final->vert_color, pt->vert_color, next->vert_color, 0.5f);
+ pt_final->time = interpf(pt->time, next->time, 0.5f);
+ pt_final->flag |= GP_SPOINT_SELECT;
+
+ /* interpolate weights */
+ if (gps->dvert != NULL) {
+ dvert = &temp_dverts[oldtotpoints - 1];
+ dvert_next = &temp_dverts[0];
+ dvert_final = &gps->dvert[i2];
+
+ dvert_final->totweight = dvert->totweight;
+ dvert_final->dw = MEM_dupallocN(dvert->dw);
+
+ /* interpolate weight values */
+ for (int d = 0; d < dvert->totweight; d++) {
+ MDeformWeight *dw_a = &dvert->dw[d];
+ if (dvert_next->totweight > d) {
+ MDeformWeight *dw_b = &dvert_next->dw[d];
+ MDeformWeight *dw_final = &dvert_final->dw[d];
+ dw_final->weight = interpf(dw_a->weight, dw_b->weight, 0.5f);
+ }
+ }
+ }
+ }
+ }
+
/* free temp memory */
MEM_SAFE_FREE(temp_points);
MEM_SAFE_FREE(temp_dverts);
@@ -4162,7 +4231,8 @@ void GPENCIL_OT_stroke_subdivide(wmOperatorType *ot)
ot->name = "Subdivide Stroke";
ot->idname = "GPENCIL_OT_stroke_subdivide";
ot->description =
- "Subdivide between continuous selected points of the stroke adding a point half way between "
+ "Subdivide between continuous selected points of the stroke adding a point half way "
+ "between "
"them";
/* api callbacks */
@@ -4474,6 +4544,9 @@ static int gpencil_stroke_separate_exec(bContext *C, wmOperator *op)
eGP_SeparateModes mode = RNA_enum_get(op->ptr, "mode");
+ const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd_src);
+ const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd_src);
+
/* sanity checks */
if (ELEM(NULL, gpd_src)) {
return OPERATOR_CANCELLED;
@@ -4484,8 +4557,22 @@ static int gpencil_stroke_separate_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd_src);
- const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd_src);
+ /* Cancel if nothing selected. */
+ if (ELEM(mode, GP_SEPARATE_POINT, GP_SEPARATE_STROKE)) {
+ bool has_selected = false;
+ CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
+ if (ED_gpencil_layer_has_selected_stroke(gpl, is_multiedit)) {
+ has_selected = true;
+ break;
+ }
+ }
+ CTX_DATA_END;
+
+ if (!has_selected) {
+ BKE_report(op->reports, RPT_ERROR, "Nothing selected");
+ return OPERATOR_CANCELLED;
+ }
+ }
/* Create a new object. */
/* Take into account user preferences for duplicating actions. */
@@ -4530,7 +4617,7 @@ static int gpencil_stroke_separate_exec(bContext *C, wmOperator *op)
if (gps->flag & GP_STROKE_SELECT) {
/* add layer if not created before */
if (gpl_dst == NULL) {
- gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, gpl->info, false);
+ gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, gpl->info, false, false);
}
/* add frame if not created before */
@@ -4979,17 +5066,27 @@ static int gpencil_cutter_lasso_select(bContext *C,
/* init space conversion stuff */
gpencil_point_conversion_init(C, &gsc);
- /* deselect all strokes first */
- CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
- int i;
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- pt->flag &= ~GP_SPOINT_SELECT;
- }
+ /* Deselect all strokes. */
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+ bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
+ for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ if (gps->flag & GP_STROKE_SELECT) {
+ int i;
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ pt->flag &= ~GP_SPOINT_SELECT;
+ }
- gps->flag &= ~GP_STROKE_SELECT;
- BKE_gpencil_stroke_select_index_reset(gps);
+ gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_reset(gps);
+ }
+ }
+ /* if not multiedit, exit loop. */
+ if (!is_multiedit) {
+ break;
+ }
+ }
}
- CTX_DATA_END;
/* Select points */
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
@@ -5250,3 +5347,181 @@ void GPENCIL_OT_stroke_merge_by_distance(wmOperatorType *ot)
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Stroke Normalize Operator
+ * \{ */
+
+typedef enum eGP_NormalizeMode {
+ GP_NORMALIZE_THICKNESS = 0,
+ GP_NORMALIZE_OPACITY,
+} eGP_NormalizeMode;
+
+static bool gpencil_stroke_normalize_poll(bContext *C)
+{
+ Object *ob = CTX_data_active_object(C);
+ if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
+ return false;
+ }
+ bGPdata *gpd = (bGPdata *)ob->data;
+ if (gpd == NULL) {
+ return false;
+ }
+
+ bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd);
+
+ return ((gpl != NULL) && (ob->mode == OB_MODE_EDIT_GPENCIL));
+}
+
+static void gpencil_stroke_normalize_ui(bContext *UNUSED(C), wmOperator *op)
+{
+ uiLayout *layout = op->layout;
+ uiLayout *row;
+
+ const eGP_NormalizeMode mode = RNA_enum_get(op->ptr, "mode");
+
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+ row = uiLayoutRow(layout, true);
+ uiItemR(row, op->ptr, "mode", 0, NULL, ICON_NONE);
+
+ if (mode == GP_NORMALIZE_THICKNESS) {
+ row = uiLayoutRow(layout, true);
+ uiItemR(row, op->ptr, "value", 0, NULL, ICON_NONE);
+ }
+ else if (mode == GP_NORMALIZE_OPACITY) {
+ row = uiLayoutRow(layout, true);
+ uiItemR(row, op->ptr, "factor", 0, NULL, ICON_NONE);
+ }
+}
+
+static int gpencil_stroke_normalize_exec(bContext *C, wmOperator *op)
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+
+ /* Sanity checks. */
+ if (ELEM(NULL, gpd)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ const eGP_NormalizeMode mode = RNA_enum_get(op->ptr, "mode");
+ const int value = RNA_int_get(op->ptr, "value");
+ const float factor = RNA_float_get(op->ptr, "factor");
+
+ /* Go through each editable + selected stroke. */
+ const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
+ const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
+
+ CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
+ bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
+
+ for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
+ if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
+
+ if (gpf == NULL) {
+ continue;
+ }
+
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+
+ /* Skip strokes that are invalid for current view. */
+ if (ED_gpencil_stroke_can_use(C, gps) == false) {
+ continue;
+ }
+
+ bool selected = (is_curve_edit) ? gps->editcurve->flag |= GP_CURVE_SELECT :
+ (gps->flag & GP_STROKE_SELECT);
+ if (!selected) {
+ continue;
+ }
+
+ float stroke_thickness_inv = 1.0f / max_ii(gps->thickness, 1);
+ /* Fill opacity need to be managed before. */
+ if (mode == GP_NORMALIZE_OPACITY) {
+ gps->fill_opacity_fac = factor;
+ CLAMP(gps->fill_opacity_fac, 0.0f, 1.0f);
+ }
+
+ /* Loop all Polyline points. */
+ if (!is_curve_edit) {
+ for (int i = 0; i < gps->totpoints; i++) {
+ bGPDspoint *pt = &gps->points[i];
+ if (mode == GP_NORMALIZE_THICKNESS) {
+ pt->pressure = max_ff((float)value * stroke_thickness_inv, 0.0f);
+ }
+ else if (mode == GP_NORMALIZE_OPACITY) {
+ pt->strength = factor;
+ CLAMP(pt->strength, 0.0f, 1.0f);
+ }
+ }
+ }
+ else {
+ /* Loop all Bezier points. */
+ for (int i = 0; i < gps->editcurve->tot_curve_points; i++) {
+ bGPDcurve_point *gpc_pt = &gps->editcurve->curve_points[i];
+ if (mode == GP_NORMALIZE_THICKNESS) {
+ gpc_pt->pressure = max_ff((float)value * stroke_thickness_inv, 0.0f);
+ }
+ else if (mode == GP_NORMALIZE_OPACITY) {
+ gpc_pt->strength = factor;
+ CLAMP(gpc_pt->strength, 0.0f, 1.0f);
+ }
+ }
+
+ gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
+ }
+ }
+ /* If not multiedit, exit loop. */
+ if (!is_multiedit) {
+ break;
+ }
+ }
+ }
+ }
+ CTX_DATA_END;
+
+ /* notifiers */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_stroke_normalize(wmOperatorType *ot)
+{
+ static const EnumPropertyItem prop_gpencil_normalize_modes[] = {
+ {GP_NORMALIZE_THICKNESS,
+ "THICKNESS",
+ 0,
+ "Thickness",
+ "Normalizes the stroke thickness by making all points use the same thickness value"},
+ {GP_NORMALIZE_OPACITY,
+ "OPACITY",
+ 0,
+ "Opacity",
+ "Normalizes the stroke opacity by making all points use the same opacity value"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ /* identifiers */
+ ot->name = "Normalize Stroke";
+ ot->idname = "GPENCIL_OT_stroke_normalize";
+ ot->description = "Normalize stroke attributes";
+
+ /* api callbacks */
+ ot->exec = gpencil_stroke_normalize_exec;
+ ot->poll = gpencil_stroke_normalize_poll;
+ ot->ui = gpencil_stroke_normalize_ui;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* props */
+ ot->prop = RNA_def_enum(
+ ot->srna, "mode", prop_gpencil_normalize_modes, 0, "Mode", "Attribute to be normalized");
+ RNA_def_float(ot->srna, "factor", 1.0f, 0.0f, 1.0f, "Factor", "", 0.0f, 1.0f);
+ RNA_def_int(ot->srna, "value", 10, 0, 1000, "Value", "Value", 0, 1000);
+}
+
+/** \} */
diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c
index 4749f40fac5..f74e211dd65 100644
--- a/source/blender/editors/gpencil/gpencil_fill.c
+++ b/source/blender/editors/gpencil/gpencil_fill.c
@@ -1029,9 +1029,15 @@ static void gpencil_invert_image(tGPDfill *tgpf)
/* Red->Green */
else if (color[0] == 1.0f) {
set_pixel(ibuf, v, fill_col[1]);
- /* Add thickness of 2 pixels to avoid too thin lines. */
- int offset = (v % ibuf->x < center) ? 1 : -1;
- set_pixel(ibuf, v + offset, fill_col[1]);
+ /* Add thickness of 2 pixels to avoid too thin lines, but avoid extremes of the pixel line.
+ */
+ int row = v / ibuf->x;
+ int lowpix = row * ibuf->x;
+ int highpix = lowpix + ibuf->x - 1;
+ if ((v > lowpix) && (v < highpix)) {
+ int offset = (v % ibuf->x < center) ? 1 : -1;
+ set_pixel(ibuf, v + offset, fill_col[1]);
+ }
}
else {
/* Set to Transparent. */
@@ -1241,6 +1247,7 @@ static bool dilate_shape(ImBuf *ibuf)
static void gpencil_get_outline_points(tGPDfill *tgpf, const bool dilate)
{
ImBuf *ibuf;
+ Brush *brush = tgpf->brush;
float rgba[4];
void *lock;
int v[2];
@@ -1273,7 +1280,9 @@ static void gpencil_get_outline_points(tGPDfill *tgpf, const bool dilate)
/* Dilate. */
if (dilate) {
- dilate_shape(ibuf);
+ for (int i = 0; i < brush->gpencil_settings->dilate_pixels; i++) {
+ dilate_shape(ibuf);
+ }
}
for (int idx = imagesize - 1; idx != 0; idx--) {
@@ -1363,7 +1372,8 @@ static void gpencil_get_depth_array(tGPDfill *tgpf)
if (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_VIEW) {
/* need to restore the original projection settings before packing up */
view3d_region_operator_needs_opengl(tgpf->win, tgpf->region);
- ED_view3d_autodist_init(tgpf->depsgraph, tgpf->region, tgpf->v3d, 0);
+ ED_view3d_depth_override(
+ tgpf->depsgraph, tgpf->region, tgpf->v3d, NULL, V3D_DEPTH_NO_GPENCIL, false);
/* Since strokes are so fine, when using their depth we need a margin
* otherwise they might get missed. */
@@ -1679,7 +1689,7 @@ static tGPDfill *gpencil_session_init_fill(bContext *C, wmOperator *op)
tgpf->gpd = gpd;
tgpf->gpl = BKE_gpencil_layer_active_get(gpd);
if (tgpf->gpl == NULL) {
- tgpf->gpl = BKE_gpencil_layer_addnew(tgpf->gpd, DATA_("GP_Layer"), true);
+ tgpf->gpl = BKE_gpencil_layer_addnew(tgpf->gpd, DATA_("GP_Layer"), true, false);
}
tgpf->lock_axis = ts->gp_sculpt.lock_axis;
diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h
index c6f74c39beb..276e8c81e0f 100644
--- a/source/blender/editors/gpencil/gpencil_intern.h
+++ b/source/blender/editors/gpencil/gpencil_intern.h
@@ -421,6 +421,7 @@ void GPENCIL_OT_layer_duplicate_object(struct wmOperatorType *ot);
void GPENCIL_OT_layer_mask_add(struct wmOperatorType *ot);
void GPENCIL_OT_layer_mask_remove(struct wmOperatorType *ot);
+void GPENCIL_OT_layer_mask_move(struct wmOperatorType *ot);
void GPENCIL_OT_hide(struct wmOperatorType *ot);
void GPENCIL_OT_reveal(struct wmOperatorType *ot);
@@ -443,6 +444,7 @@ void GPENCIL_OT_frame_clean_duplicate(struct wmOperatorType *ot);
void GPENCIL_OT_convert(struct wmOperatorType *ot);
void GPENCIL_OT_bake_mesh_animation(struct wmOperatorType *ot);
+void GPENCIL_OT_bake_grease_pencil_animation(struct wmOperatorType *ot);
void GPENCIL_OT_image_to_grease_pencil(struct wmOperatorType *ot);
void GPENCIL_OT_trace_image(struct wmOperatorType *ot);
@@ -486,6 +488,7 @@ void GPENCIL_OT_stroke_trim(struct wmOperatorType *ot);
void GPENCIL_OT_stroke_merge_by_distance(struct wmOperatorType *ot);
void GPENCIL_OT_stroke_merge_material(struct wmOperatorType *ot);
void GPENCIL_OT_stroke_reset_vertex_color(struct wmOperatorType *ot);
+void GPENCIL_OT_stroke_normalize(struct wmOperatorType *ot);
void GPENCIL_OT_material_to_vertex_color(struct wmOperatorType *ot);
void GPENCIL_OT_extract_palette_vertex(struct wmOperatorType *ot);
@@ -537,6 +540,7 @@ void GPENCIL_OT_material_lock_unused(struct wmOperatorType *ot);
void GPENCIL_OT_material_select(struct wmOperatorType *ot);
void GPENCIL_OT_material_set(struct wmOperatorType *ot);
void GPENCIL_OT_set_active_material(struct wmOperatorType *ot);
+void GPENCIL_OT_materials_copy_to_object(struct wmOperatorType *ot);
/* convert old 2.7 files to 2.8 */
void GPENCIL_OT_convert_old_files(struct wmOperatorType *ot);
@@ -748,4 +752,7 @@ struct GP_EditableStrokes_Iter {
} \
(void)0
+/* Reused items for bake operators. */
+extern const EnumPropertyItem rna_gpencil_reproject_type_items[];
+
/* ****************************************************** */
diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c
index bfa1ee6bcaf..0062e363cdf 100644
--- a/source/blender/editors/gpencil/gpencil_interpolate.c
+++ b/source/blender/editors/gpencil/gpencil_interpolate.c
@@ -84,7 +84,8 @@ typedef struct tGPDinterpolate_layer {
/** interpolate factor */
float factor;
- /* Hash tablets to create temp relationship between strokes. */
+ /* List of strokes and Hash tablets to create temp relationship between strokes. */
+ struct ListBase selected_strokes;
struct GHash *used_strokes;
struct GHash *pair_strokes;
@@ -282,6 +283,7 @@ static void gpencil_stroke_pair_table(bContext *C,
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
/* Create hash tablets with relationship between strokes. */
+ BLI_listbase_clear(&tgpil->selected_strokes);
tgpil->used_strokes = BLI_ghash_ptr_new(__func__);
tgpil->pair_strokes = BLI_ghash_ptr_new(__func__);
@@ -315,7 +317,8 @@ static void gpencil_stroke_pair_table(bContext *C,
if (ELEM(NULL, gps_from, gps_to)) {
continue;
}
- /* Insert the pair entry in the hash table. */
+ /* Insert the pair entry in the hash table and the list of strokes to keep order. */
+ BLI_addtail(&tgpil->selected_strokes, BLI_genericNodeN(gps_from));
BLI_ghash_insert(tgpil->pair_strokes, gps_from, gps_to);
}
}
@@ -405,10 +408,13 @@ static void gpencil_interpolate_update_strokes(bContext *C, tGPDinterpolate *tgp
/* Clear previous interpolations. */
gpencil_interpolate_free_tagged_strokes(tgpil->interFrame);
- GHashIterator gh_iter;
- GHASH_ITER (gh_iter, tgpil->pair_strokes) {
- bGPDstroke *gps_from = (bGPDstroke *)BLI_ghashIterator_getKey(&gh_iter);
- bGPDstroke *gps_to = (bGPDstroke *)BLI_ghashIterator_getValue(&gh_iter);
+ LISTBASE_FOREACH (LinkData *, link, &tgpil->selected_strokes) {
+ bGPDstroke *gps_from = link->data;
+ if (!BLI_ghash_haskey(tgpil->pair_strokes, gps_from)) {
+ continue;
+ }
+ bGPDstroke *gps_to = (bGPDstroke *)BLI_ghash_lookup(tgpil->pair_strokes, gps_from);
+
/* Create new stroke. */
bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true, true);
new_stroke->flag |= GP_STROKE_TAG;
@@ -527,10 +533,12 @@ static void gpencil_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi)
gpencil_stroke_pair_table(C, tgpi, tgpil);
/* Create new strokes data with interpolated points reading original stroke. */
- GHashIterator gh_iter;
- GHASH_ITER (gh_iter, tgpil->pair_strokes) {
- bGPDstroke *gps_from = (bGPDstroke *)BLI_ghashIterator_getKey(&gh_iter);
- bGPDstroke *gps_to = (bGPDstroke *)BLI_ghashIterator_getValue(&gh_iter);
+ LISTBASE_FOREACH (LinkData *, link, &tgpil->selected_strokes) {
+ bGPDstroke *gps_from = link->data;
+ if (!BLI_ghash_haskey(tgpil->pair_strokes, gps_from)) {
+ continue;
+ }
+ bGPDstroke *gps_to = (bGPDstroke *)BLI_ghash_lookup(tgpil->pair_strokes, gps_from);
/* If destination stroke is smaller, resize new_stroke to size of gps_to stroke. */
if (gps_from->totpoints > gps_to->totpoints) {
@@ -658,6 +666,9 @@ static void gpencil_interpolate_exit(bContext *C, wmOperator *op)
MEM_SAFE_FREE(tgpil->nextFrame);
MEM_SAFE_FREE(tgpil->interFrame);
+ /* Free list of strokes. */
+ BLI_freelistN(&tgpil->selected_strokes);
+
/* Free Hash tablets. */
if (tgpil->used_strokes != NULL) {
BLI_ghash_free(tgpil->used_strokes, NULL, NULL);
@@ -1292,9 +1303,9 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op)
bGPDframe *nextFrame = BKE_gpencil_frame_duplicate(gpf_next, true);
/* Create a table with source and target pair of strokes. */
+ ListBase selected_strokes = {NULL};
GHash *used_strokes = BLI_ghash_ptr_new(__func__);
GHash *pair_strokes = BLI_ghash_ptr_new(__func__);
-
LISTBASE_FOREACH (bGPDstroke *, gps_from, &prevFrame->strokes) {
bGPDstroke *gps_to = NULL;
/* Only selected. */
@@ -1342,7 +1353,9 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op)
}
}
- /* Insert the pair entry in the hash table. */
+ /* Insert the pair entry in the hash table and in the list of strokes to keep same order.
+ */
+ BLI_addtail(&selected_strokes, BLI_genericNodeN(gps_from));
BLI_ghash_insert(pair_strokes, gps_from, gps_to);
}
@@ -1369,11 +1382,12 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op)
}
/* Apply the factor to all pair of strokes. */
- GHashIterator gh_iter;
- GHASH_ITER (gh_iter, pair_strokes) {
- bGPDstroke *gps_from = (bGPDstroke *)BLI_ghashIterator_getKey(&gh_iter);
- bGPDstroke *gps_to = (bGPDstroke *)BLI_ghashIterator_getValue(&gh_iter);
-
+ LISTBASE_FOREACH (LinkData *, link, &selected_strokes) {
+ bGPDstroke *gps_from = link->data;
+ if (!BLI_ghash_haskey(pair_strokes, gps_from)) {
+ continue;
+ }
+ bGPDstroke *gps_to = (bGPDstroke *)BLI_ghash_lookup(pair_strokes, gps_from);
/* Create new stroke. */
bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true, true);
new_stroke->flag |= GP_STROKE_TAG;
@@ -1394,6 +1408,8 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op)
}
}
+ BLI_freelistN(&selected_strokes);
+
/* Free Hash tablets. */
if (used_strokes != NULL) {
BLI_ghash_free(used_strokes, NULL, NULL);
@@ -1419,34 +1435,31 @@ static void gpencil_interpolate_seq_ui(bContext *C, wmOperator *op)
{
uiLayout *layout = op->layout;
uiLayout *col, *row;
- PointerRNA ptr;
-
- RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
const eGP_Interpolate_Type type = RNA_enum_get(op->ptr, "type");
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
row = uiLayoutRow(layout, true);
- uiItemR(row, &ptr, "step", 0, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "step", 0, NULL, ICON_NONE);
row = uiLayoutRow(layout, true);
- uiItemR(row, &ptr, "layers", 0, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "layers", 0, NULL, ICON_NONE);
if (CTX_data_mode_enum(C) == CTX_MODE_EDIT_GPENCIL) {
row = uiLayoutRow(layout, true);
- uiItemR(row, &ptr, "interpolate_selected_only", 0, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "interpolate_selected_only", 0, NULL, ICON_NONE);
}
row = uiLayoutRow(layout, true);
- uiItemR(row, &ptr, "flip", 0, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "flip", 0, NULL, ICON_NONE);
col = uiLayoutColumn(layout, true);
- uiItemR(col, &ptr, "smooth_factor", 0, NULL, ICON_NONE);
- uiItemR(col, &ptr, "smooth_steps", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "smooth_factor", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "smooth_steps", 0, NULL, ICON_NONE);
row = uiLayoutRow(layout, true);
- uiItemR(row, &ptr, "type", 0, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "type", 0, NULL, ICON_NONE);
if (type == GP_IPO_CURVEMAP) {
/* Get an RNA pointer to ToolSettings to give to the custom curve. */
@@ -1460,16 +1473,16 @@ static void gpencil_interpolate_seq_ui(bContext *C, wmOperator *op)
}
else if (type != GP_IPO_LINEAR) {
row = uiLayoutRow(layout, false);
- uiItemR(row, &ptr, "easing", 0, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "easing", 0, NULL, ICON_NONE);
if (type == GP_IPO_BACK) {
row = uiLayoutRow(layout, false);
- uiItemR(row, &ptr, "back", 0, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "back", 0, NULL, ICON_NONE);
}
else if (type == GP_IPO_ELASTIC) {
row = uiLayoutRow(layout, false);
- uiItemR(row, &ptr, "amplitude", 0, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "amplitude", 0, NULL, ICON_NONE);
row = uiLayoutRow(layout, false);
- uiItemR(row, &ptr, "period", 0, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "period", 0, NULL, ICON_NONE);
}
}
}
@@ -1690,7 +1703,7 @@ static bool gpencil_interpolate_reverse_poll(bContext *C)
if (area == NULL) {
return false;
}
- if ((area->spacetype != SPACE_VIEW3D) && (area->spacetype != SPACE_ACTION)) {
+ if (!ELEM(area->spacetype, SPACE_VIEW3D, SPACE_ACTION)) {
return false;
}
diff --git a/source/blender/editors/gpencil/gpencil_mesh.c b/source/blender/editors/gpencil/gpencil_mesh.c
index b7ed77801c0..55468dffab0 100644
--- a/source/blender/editors/gpencil/gpencil_mesh.c
+++ b/source/blender/editors/gpencil/gpencil_mesh.c
@@ -402,25 +402,6 @@ static int gpencil_bake_mesh_animation_invoke(bContext *C,
void GPENCIL_OT_bake_mesh_animation(wmOperatorType *ot)
{
- static const EnumPropertyItem reproject_type[] = {
- {GP_REPROJECT_KEEP, "KEEP", 0, "No Reproject", ""},
- {GP_REPROJECT_FRONT, "FRONT", 0, "Front", "Reproject the strokes using the X-Z plane"},
- {GP_REPROJECT_SIDE, "SIDE", 0, "Side", "Reproject the strokes using the Y-Z plane"},
- {GP_REPROJECT_TOP, "TOP", 0, "Top", "Reproject the strokes using the X-Y plane"},
- {GP_REPROJECT_VIEW,
- "VIEW",
- 0,
- "View",
- "Reproject the strokes to end up on the same plane, as if drawn from the current viewpoint "
- "using 'Cursor' Stroke Placement"},
- {GP_REPROJECT_CURSOR,
- "CURSOR",
- 0,
- "Cursor",
- "Reproject the strokes using the orientation of 3D cursor"},
- {0, NULL, 0, NULL, NULL},
- };
-
static const EnumPropertyItem target_object_modes[] = {
{GP_TARGET_OB_NEW, "NEW", 0, "New Object", ""},
{GP_TARGET_OB_SELECTED, "SELECTED", 0, "Selected Object", ""},
@@ -491,5 +472,10 @@ void GPENCIL_OT_bake_mesh_animation(wmOperatorType *ot)
RNA_def_int(
ot->srna, "frame_target", 1, 1, 100000, "Target Frame", "Destination frame", 1, 100000);
- RNA_def_enum(ot->srna, "project_type", reproject_type, GP_REPROJECT_VIEW, "Projection Type", "");
+ RNA_def_enum(ot->srna,
+ "project_type",
+ rna_gpencil_reproject_type_items,
+ GP_REPROJECT_VIEW,
+ "Projection Type",
+ "");
}
diff --git a/source/blender/editors/gpencil/gpencil_ops.c b/source/blender/editors/gpencil/gpencil_ops.c
index 1a6cb5670c4..35640cf3b66 100644
--- a/source/blender/editors/gpencil/gpencil_ops.c
+++ b/source/blender/editors/gpencil/gpencil_ops.c
@@ -601,6 +601,7 @@ void ED_operatortypes_gpencil(void)
WM_operatortype_append(GPENCIL_OT_layer_mask_add);
WM_operatortype_append(GPENCIL_OT_layer_mask_remove);
+ WM_operatortype_append(GPENCIL_OT_layer_mask_move);
WM_operatortype_append(GPENCIL_OT_hide);
WM_operatortype_append(GPENCIL_OT_reveal);
@@ -621,6 +622,7 @@ void ED_operatortypes_gpencil(void)
WM_operatortype_append(GPENCIL_OT_convert);
WM_operatortype_append(GPENCIL_OT_bake_mesh_animation);
+ WM_operatortype_append(GPENCIL_OT_bake_grease_pencil_animation);
WM_operatortype_append(GPENCIL_OT_image_to_grease_pencil);
#ifdef WITH_POTRACE
@@ -647,9 +649,11 @@ void ED_operatortypes_gpencil(void)
WM_operatortype_append(GPENCIL_OT_stroke_merge_by_distance);
WM_operatortype_append(GPENCIL_OT_stroke_merge_material);
WM_operatortype_append(GPENCIL_OT_stroke_reset_vertex_color);
+ WM_operatortype_append(GPENCIL_OT_stroke_normalize);
WM_operatortype_append(GPENCIL_OT_material_to_vertex_color);
WM_operatortype_append(GPENCIL_OT_extract_palette_vertex);
+ WM_operatortype_append(GPENCIL_OT_materials_copy_to_object);
WM_operatortype_append(GPENCIL_OT_transform_fill);
WM_operatortype_append(GPENCIL_OT_reset_transform_fill);
diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c
index 1217a3a7e8f..e40748e5f6e 100644
--- a/source/blender/editors/gpencil/gpencil_paint.c
+++ b/source/blender/editors/gpencil/gpencil_paint.c
@@ -1332,7 +1332,7 @@ static float view3d_point_depth(const RegionView3D *rv3d, const float co[3])
/* only erase stroke points that are visible */
static bool gpencil_stroke_eraser_is_occluded(
- tGPsdata *p, bGPDlayer *gpl, const bGPDspoint *pt, const int x, const int y)
+ tGPsdata *p, bGPDlayer *gpl, bGPDspoint *pt, const int x, const int y)
{
Object *obact = (Object *)p->ownerPtr.data;
Brush *brush = p->brush;
@@ -1364,7 +1364,11 @@ static bool gpencil_stroke_eraser_is_occluded(
mul_v3_m4v3(fpt, diff_mat, &pt->x);
const float depth_pt = view3d_point_depth(rv3d, fpt);
+ /* Checked occlusion flag. */
+ pt->flag |= GP_SPOINT_TEMP_TAG;
if (depth_pt > depth_mval) {
+ /* Is occluded. */
+ pt->flag |= GP_SPOINT_TEMP_TAG2;
return true;
}
}
@@ -1540,6 +1544,10 @@ static void gpencil_stroke_eraser_dostroke(tGPsdata *p,
for (i = 0; i < gps->totpoints; i++) {
bGPDspoint *pt = &gps->points[i];
pt->flag &= ~GP_SPOINT_TAG;
+ /* Occlusion already checked. */
+ pt->flag &= ~GP_SPOINT_TEMP_TAG;
+ /* Point is occluded. */
+ pt->flag &= ~GP_SPOINT_TEMP_TAG2;
}
/* First Pass: Loop over the points in the stroke
@@ -1585,9 +1593,23 @@ static void gpencil_stroke_eraser_dostroke(tGPsdata *p,
* - this assumes that linewidth is irrelevant
*/
if (gpencil_stroke_inside_circle(mval, radius, pc0[0], pc0[1], pc2[0], pc2[1])) {
- if ((gpencil_stroke_eraser_is_occluded(p, gpl, pt0, pc0[0], pc0[1]) == false) ||
- (gpencil_stroke_eraser_is_occluded(p, gpl, pt1, pc1[0], pc1[1]) == false) ||
- (gpencil_stroke_eraser_is_occluded(p, gpl, pt2, pc2[0], pc2[1]) == false)) {
+
+ bool is_occluded_pt0, is_occluded_pt1, is_occluded_pt2 = true;
+ is_occluded_pt0 = (pt0 && ((pt0->flag & GP_SPOINT_TEMP_TAG) != 0)) ?
+ ((pt0->flag & GP_SPOINT_TEMP_TAG2) != 0) :
+ gpencil_stroke_eraser_is_occluded(p, gpl, pt0, pc0[0], pc0[1]);
+ if (is_occluded_pt0) {
+ is_occluded_pt1 = ((pt1->flag & GP_SPOINT_TEMP_TAG) != 0) ?
+ ((pt1->flag & GP_SPOINT_TEMP_TAG2) != 0) :
+ gpencil_stroke_eraser_is_occluded(p, gpl, pt1, pc1[0], pc1[1]);
+ if (is_occluded_pt1) {
+ is_occluded_pt2 = ((pt2->flag & GP_SPOINT_TEMP_TAG) != 0) ?
+ ((pt2->flag & GP_SPOINT_TEMP_TAG2) != 0) :
+ gpencil_stroke_eraser_is_occluded(p, gpl, pt2, pc2[0], pc2[1]);
+ }
+ }
+
+ if (!is_occluded_pt0 || !is_occluded_pt1 || !is_occluded_pt2) {
/* Point is affected: */
/* Adjust thickness
* - Influence of eraser falls off with distance from the middle of the eraser
@@ -1722,7 +1744,7 @@ static void gpencil_stroke_doeraser(tGPsdata *p)
if ((gp_settings != NULL) && (gp_settings->flag & GP_BRUSH_OCCLUDE_ERASER)) {
View3D *v3d = p->area->spacedata.first;
view3d_region_operator_needs_opengl(p->win, p->region);
- ED_view3d_autodist_init(p->depsgraph, p->region, v3d, 0);
+ ED_view3d_depth_override(p->depsgraph, p->region, v3d, NULL, V3D_DEPTH_NO_GPENCIL, false);
}
}
@@ -2120,7 +2142,7 @@ static void gpencil_paint_initstroke(tGPsdata *p,
/* get active layer (or add a new one if non-existent) */
p->gpl = BKE_gpencil_layer_active_get(p->gpd);
if (p->gpl == NULL) {
- p->gpl = BKE_gpencil_layer_addnew(p->gpd, DATA_("GP_Layer"), true);
+ p->gpl = BKE_gpencil_layer_addnew(p->gpd, DATA_("GP_Layer"), true, false);
changed = true;
if (p->custom_color[3]) {
copy_v3_v3(p->gpl->color, p->custom_color);
@@ -2305,8 +2327,14 @@ static void gpencil_paint_strokeend(tGPsdata *p)
/* need to restore the original projection settings before packing up */
view3d_region_operator_needs_opengl(p->win, p->region);
- ED_view3d_autodist_init(
- p->depsgraph, p->region, v3d, (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 1 : 0);
+ ED_view3d_depth_override(p->depsgraph,
+ p->region,
+ v3d,
+ NULL,
+ (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ?
+ V3D_DEPTH_GPENCIL_ONLY :
+ V3D_DEPTH_NO_GPENCIL,
+ false);
}
/* check if doing eraser or not */
@@ -3259,7 +3287,7 @@ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event
gpencil_guide_event_handling(C, op, event, p);
}
- if (ob && (ob->type == OB_GPENCIL) && ((p->gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0)) {
+ if ((ob->type == OB_GPENCIL) && ((p->gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0)) {
/* FIXME: use the mode switching operator, this misses notifiers, messages. */
/* Just set paintmode flag... */
p->gpd->flag |= GP_DATA_STROKE_PAINTMODE;
@@ -3671,14 +3699,6 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
* is essential for ensuring that they can quickly return to that view
*/
}
- else if ((event->type == EVT_BKEY) && (event->val == KM_RELEASE)) {
- /* Add Blank Frame
- * - Since this operator is non-modal, we can just call it here, and keep going...
- * - This operator is especially useful when animating
- */
- WM_operator_name_call(C, "GPENCIL_OT_blank_frame_add", WM_OP_EXEC_DEFAULT, NULL);
- estate = OPERATOR_RUNNING_MODAL;
- }
else if ((!ELEM(p->paintmode, GP_PAINTMODE_ERASER, GP_PAINTMODE_SET_CP))) {
gpencil_guide_event_handling(C, op, event, p);
estate = OPERATOR_RUNNING_MODAL;
@@ -3691,7 +3711,7 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
/* Exit painting mode (and/or end current stroke).
*
*/
- if (ELEM(event->type, EVT_RETKEY, EVT_PADENTER, EVT_ESCKEY, EVT_SPACEKEY, EVT_EKEY)) {
+ if (ELEM(event->type, EVT_RETKEY, EVT_PADENTER, EVT_ESCKEY, EVT_SPACEKEY)) {
p->status = GP_STATUS_DONE;
estate = OPERATOR_FINISHED;
diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c
index dfff0ce639e..5f02bbf0a77 100644
--- a/source/blender/editors/gpencil/gpencil_primitive.c
+++ b/source/blender/editors/gpencil/gpencil_primitive.c
@@ -314,7 +314,7 @@ static void gpencil_primitive_set_initdata(bContext *C, tGPDprimitive *tgpi)
/* if layer doesn't exist, create a new one */
if (gpl == NULL) {
- gpl = BKE_gpencil_layer_addnew(tgpi->gpd, DATA_("Primitives"), true);
+ gpl = BKE_gpencil_layer_addnew(tgpi->gpd, DATA_("Primitives"), true, false);
}
tgpi->gpl = gpl;
@@ -785,10 +785,14 @@ static void gpencil_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi)
/* need to restore the original projection settings before packing up */
view3d_region_operator_needs_opengl(tgpi->win, tgpi->region);
- ED_view3d_autodist_init(tgpi->depsgraph,
- tgpi->region,
- tgpi->v3d,
- (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 1 : 0);
+ ED_view3d_depth_override(tgpi->depsgraph,
+ tgpi->region,
+ tgpi->v3d,
+ NULL,
+ (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ?
+ V3D_DEPTH_GPENCIL_ONLY :
+ V3D_DEPTH_NO_GPENCIL,
+ false);
depth_arr = MEM_mallocN(sizeof(float) * gps->totpoints, "depth_points");
tGPspoint *ptc = &points2D[0];
@@ -1532,24 +1536,22 @@ static void gpencil_primitive_strength(tGPDprimitive *tgpi, bool reset)
Brush *brush = tgpi->brush;
BrushGpencilSettings *brush_settings = brush->gpencil_settings;
- if (brush) {
- if (reset) {
- brush_settings->draw_strength = tgpi->brush_strength;
- tgpi->brush_strength = 0.0f;
- }
- else {
- if (tgpi->brush_strength == 0.0f) {
- tgpi->brush_strength = brush_settings->draw_strength;
- }
- float move[2];
- sub_v2_v2v2(move, tgpi->mval, tgpi->mvalo);
- float adjust = (move[1] > 0.0f) ? 0.01f : -0.01f;
- brush_settings->draw_strength += adjust * fabsf(len_manhattan_v2(move));
+ if (reset) {
+ brush_settings->draw_strength = tgpi->brush_strength;
+ tgpi->brush_strength = 0.0f;
+ }
+ else {
+ if (tgpi->brush_strength == 0.0f) {
+ tgpi->brush_strength = brush_settings->draw_strength;
}
-
- /* limit low limit because below 0.2f the stroke is invisible */
- CLAMP(brush_settings->draw_strength, 0.2f, 1.0f);
+ float move[2];
+ sub_v2_v2v2(move, tgpi->mval, tgpi->mvalo);
+ float adjust = (move[1] > 0.0f) ? 0.01f : -0.01f;
+ brush_settings->draw_strength += adjust * fabsf(len_manhattan_v2(move));
}
+
+ /* limit low limit because below 0.2f the stroke is invisible */
+ CLAMP(brush_settings->draw_strength, 0.2f, 1.0f);
}
/* brush size */
diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c
index aab08e9c8c4..e1776988186 100644
--- a/source/blender/editors/gpencil/gpencil_select.c
+++ b/source/blender/editors/gpencil/gpencil_select.c
@@ -505,7 +505,7 @@ void GPENCIL_OT_select_alternate(wmOperatorType *ot)
/* properties */
RNA_def_boolean(ot->srna,
"unselect_ends",
- true,
+ false,
"Unselect Ends",
"Do not select the first and last point of the stroke");
}
diff --git a/source/blender/editors/gpencil/gpencil_trace_ops.c b/source/blender/editors/gpencil/gpencil_trace_ops.c
index d2e5fa3db32..cd75f9313ad 100644
--- a/source/blender/editors/gpencil/gpencil_trace_ops.c
+++ b/source/blender/editors/gpencil/gpencil_trace_ops.c
@@ -207,7 +207,7 @@ static void trace_initialize_job_data(TraceJob *trace_job)
trace_job->gpd = (bGPdata *)trace_job->ob_gpencil->data;
trace_job->gpl = BKE_gpencil_layer_active_get(trace_job->gpd);
if (trace_job->gpl == NULL) {
- trace_job->gpl = BKE_gpencil_layer_addnew(trace_job->gpd, DATA_("Trace"), true);
+ trace_job->gpl = BKE_gpencil_layer_addnew(trace_job->gpd, DATA_("Trace"), true, false);
}
}
diff --git a/source/blender/editors/gpencil/gpencil_trace_utils.c b/source/blender/editors/gpencil/gpencil_trace_utils.c
index ada777d43f3..12c38fb2744 100644
--- a/source/blender/editors/gpencil/gpencil_trace_utils.c
+++ b/source/blender/editors/gpencil/gpencil_trace_utils.c
@@ -281,7 +281,6 @@ void ED_gpencil_trace_data_to_strokes(Main *bmain,
mat_mask_idx = ob->totcol - 1;
}
- potrace_path_t *path = st->plist;
int n, *tag;
potrace_dpoint_t(*c)[3];
@@ -289,7 +288,7 @@ void ED_gpencil_trace_data_to_strokes(Main *bmain,
* good results using the Potrace data. */
const float scalef = 0.008f * scale;
/* Draw each curve. */
- path = st->plist;
+ potrace_path_t *path = st->plist;
while (path != NULL) {
n = path->curve.n;
tag = path->curve.tag;
@@ -308,9 +307,16 @@ void ED_gpencil_trace_data_to_strokes(Main *bmain,
if (gps->totpoints == 0) {
add_point(gps, scalef, offset, c[n - 1][2].x, c[n - 1][2].y);
}
+ else {
+ add_point(gps, scalef, offset, last[0], last[1]);
+ }
+
add_point(gps, scalef, offset, c[i][1].x, c[i][1].y);
add_point(gps, scalef, offset, c[i][2].x, c[i][2].y);
+
+ last[0] = c[i][2].x;
+ last[1] = c[i][2].y;
break;
}
case POTRACE_CURVETO: {
diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c
index 574670de7ca..c9ef340b9d3 100644
--- a/source/blender/editors/gpencil/gpencil_utils.c
+++ b/source/blender/editors/gpencil/gpencil_utils.c
@@ -543,6 +543,38 @@ bool gpencil_stroke_inside_circle(const float mval[2], int rad, int x0, int y0,
}
/* ******************************************************** */
+/* Selection Validity Testing */
+
+bool ED_gpencil_frame_has_selected_stroke(const bGPDframe *gpf)
+{
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ if (gps->flag & GP_STROKE_SELECT) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool ED_gpencil_layer_has_selected_stroke(const bGPDlayer *gpl, const bool is_multiedit)
+{
+ bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
+ for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
+ if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
+ if (ED_gpencil_frame_has_selected_stroke(gpf)) {
+ return true;
+ }
+ }
+ /* If not multiedit, exit loop. */
+ if (!is_multiedit) {
+ break;
+ }
+ }
+
+ return false;
+}
+
+/* ******************************************************** */
/* Stroke Validity Testing */
/* Check whether given stroke can be edited given the supplied context */
@@ -648,7 +680,7 @@ void gpencil_point_conversion_init(bContext *C, GP_SpaceConversion *r_gsc)
view3d_operator_needs_opengl(C);
view3d_region_operator_needs_opengl(win, region);
- ED_view3d_autodist_init(depsgraph, region, v3d, 0);
+ ED_view3d_depth_override(depsgraph, region, v3d, NULL, V3D_DEPTH_NO_GPENCIL, false);
/* for camera view set the subrect */
if (rv3d->persp == RV3D_CAMOB) {
@@ -1253,7 +1285,11 @@ void ED_gpencil_stroke_reproject(Depsgraph *depsgraph,
float location[3] = {0.0f, 0.0f, 0.0f};
float normal[3] = {0.0f, 0.0f, 0.0f};
- ED_view3d_win_to_ray(region, xy, &ray_start[0], &ray_normal[0]);
+ BLI_assert(gps->flag & GP_STROKE_3DSPACE);
+ BLI_assert(gsc->area && gsc->area->spacetype == SPACE_VIEW3D);
+ const View3D *v3d = gsc->area->spacedata.first;
+ ED_view3d_win_to_ray_clipped(
+ depsgraph, region, v3d, xy, &ray_start[0], &ray_normal[0], true);
if (ED_transform_snap_object_project_ray(sctx,
depsgraph,
&(const struct SnapObjectParams){
diff --git a/source/blender/editors/include/ED_armature.h b/source/blender/editors/include/ED_armature.h
index 0c4576096fb..85563b76f38 100644
--- a/source/blender/editors/include/ED_armature.h
+++ b/source/blender/editors/include/ED_armature.h
@@ -125,6 +125,13 @@ bool ED_armature_edit_deselect_all_visible(struct Object *obedit);
bool ED_armature_edit_deselect_all_multi_ex(struct Base **bases, uint bases_len);
bool ED_armature_edit_deselect_all_visible_multi_ex(struct Base **bases, uint bases_len);
bool ED_armature_edit_deselect_all_visible_multi(struct bContext *C);
+bool ED_armature_edit_select_pick_bone(struct bContext *C,
+ struct Base *basact,
+ struct EditBone *ebone,
+ int selmask,
+ bool extend,
+ bool deselect,
+ bool toggle);
bool ED_armature_edit_select_pick(
struct bContext *C, const int mval[2], bool extend, bool deselect, bool toggle);
bool ED_armature_edit_select_op_from_tagged(struct bArmature *arm, const int sel_op);
@@ -201,6 +208,13 @@ void ED_pose_recalculate_paths(struct bContext *C,
ePosePathCalcRange range);
/* pose_select.c */
+void ED_armature_pose_select_pick_bone(struct ViewLayer *view_layer,
+ struct View3D *v3d,
+ struct Object *ob,
+ struct Bone *bone,
+ bool extend,
+ bool deselect,
+ bool toggle);
bool ED_armature_pose_select_pick_with_buffer(struct ViewLayer *view_layer,
struct View3D *v3d,
struct Base *base,
diff --git a/source/blender/editors/include/ED_fileselect.h b/source/blender/editors/include/ED_fileselect.h
index 983ae94b637..8118e3c6c69 100644
--- a/source/blender/editors/include/ED_fileselect.h
+++ b/source/blender/editors/include/ED_fileselect.h
@@ -110,7 +110,7 @@ struct FileAssetSelectParams *ED_fileselect_get_asset_params(const struct SpaceF
void ED_fileselect_set_params_from_userdef(struct SpaceFile *sfile);
void ED_fileselect_params_to_userdef(struct SpaceFile *sfile,
- const int temp_win_size[],
+ const int temp_win_size[2],
const bool is_maximized);
void ED_fileselect_init_layout(struct SpaceFile *sfile, struct ARegion *region);
diff --git a/source/blender/editors/include/ED_gizmo_library.h b/source/blender/editors/include/ED_gizmo_library.h
index dfc8cfea5ce..571519e52f7 100644
--- a/source/blender/editors/include/ED_gizmo_library.h
+++ b/source/blender/editors/include/ED_gizmo_library.h
@@ -261,9 +261,20 @@ struct SnapObjectContext *ED_gizmotypes_snap_3d_context_ensure(struct Scene *sce
const struct View3D *v3d,
struct wmGizmo *gz);
+typedef enum {
+ ED_SNAPGIZMO_TOGGLE_ALWAYS_TRUE = 1 << 0,
+ ED_SNAPGIZMO_OCCLUSION_ALWAYS_TRUE = 1 << 1,
+ ED_SNAPGIZMO_OCCLUSION_ALWAYS_FALSE = 1 << 2, /* TODO. */
+ ED_SNAPGIZMO_SNAP_ONLY_ACTIVE = 1 << 3,
+ ED_SNAPGIZMO_SNAP_EDIT_GEOM_FINAL = 1 << 4,
+ ED_SNAPGIZMO_SNAP_EDIT_GEOM_CAGE = 1 << 5,
+} eSnapGizmo;
+
+void ED_gizmotypes_snap_3d_flag_set(struct wmGizmo *gz, eSnapGizmo flag);
+void ED_gizmotypes_snap_3d_flag_clear(struct wmGizmo *gz, eSnapGizmo flag);
+bool ED_gizmotypes_snap_3d_flag_test(struct wmGizmo *gz, eSnapGizmo flag);
+
bool ED_gizmotypes_snap_3d_invert_snap_get(struct wmGizmo *gz);
-void ED_gizmotypes_snap_3d_toggle_set(struct wmGizmo *gz, bool enable);
-void ED_gizmotypes_snap_3d_toggle_clear(struct wmGizmo *gz);
bool ED_gizmotypes_snap_3d_is_enabled(struct wmGizmo *gz);
short ED_gizmotypes_snap_3d_update(struct wmGizmo *gz,
@@ -271,9 +282,9 @@ short ED_gizmotypes_snap_3d_update(struct wmGizmo *gz,
const struct ARegion *region,
const struct View3D *v3d,
const struct wmWindowManager *wm,
- const float mval_fl[2],
- float r_loc[3],
- float r_nor[3]);
+ const float mval_fl[2]);
+void ED_gizmotypes_snap_3d_data_get(
+ struct wmGizmo *gz, float r_loc[3], float r_nor[3], int r_elem_index[3], int *r_snap_elem);
#ifdef __cplusplus
}
diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h
index e9ac21f60cf..bad080e1609 100644
--- a/source/blender/editors/include/ED_gpencil.h
+++ b/source/blender/editors/include/ED_gpencil.h
@@ -144,6 +144,8 @@ bool ED_gpencil_data_owner_is_annotation(struct PointerRNA *owner_ptr);
bool ED_gpencil_has_keyframe_v3d(struct Scene *scene, struct Object *ob, int cfra);
/* ----------- Stroke Editing Utilities ---------------- */
+bool ED_gpencil_frame_has_selected_stroke(const struct bGPDframe *gpf);
+bool ED_gpencil_layer_has_selected_stroke(const struct bGPDlayer *gpl, const bool is_multiedit);
bool ED_gpencil_stroke_can_use_direct(const struct ScrArea *area, const struct bGPDstroke *gps);
bool ED_gpencil_stroke_can_use(const struct bContext *C, const struct bGPDstroke *gps);
@@ -249,6 +251,7 @@ void ED_gpencil_brush_draw_eraser(struct Brush *brush, int x, int y);
/* ----------- Add Primitive Utilities -------------- */
+void ED_gpencil_create_blank(struct bContext *C, struct Object *ob, float mat[4][4]);
void ED_gpencil_create_monkey(struct bContext *C, struct Object *ob, float mat[4][4]);
void ED_gpencil_create_stroke(struct bContext *C, struct Object *ob, float mat[4][4]);
void ED_gpencil_create_lineart(struct bContext *C, struct Object *ob);
diff --git a/source/blender/editors/include/ED_keyframing.h b/source/blender/editors/include/ED_keyframing.h
index 12d6f1fce54..179c9d5b30d 100644
--- a/source/blender/editors/include/ED_keyframing.h
+++ b/source/blender/editors/include/ED_keyframing.h
@@ -511,6 +511,7 @@ bool ED_autokeyframe_property(struct bContext *C,
#define ANIM_KS_ROTATION_ID "Rotation"
#define ANIM_KS_SCALING_ID "Scaling"
#define ANIM_KS_LOC_ROT_SCALE_ID "LocRotScale"
+#define ANIM_KS_LOC_ROT_SCALE_CPROP_ID "LocRotScaleCProp"
#define ANIM_KS_AVAILABLE_ID "Available"
#define ANIM_KS_WHOLE_CHARACTER_ID "WholeCharacter"
#define ANIM_KS_WHOLE_CHARACTER_SELECTED_ID "WholeCharacterSelected"
diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h
index 85e7a491feb..b8e9f6e8871 100644
--- a/source/blender/editors/include/ED_mesh.h
+++ b/source/blender/editors/include/ED_mesh.h
@@ -164,16 +164,16 @@ void EDBM_select_mirrored(struct BMEditMesh *em,
int *r_totfail);
struct BMVert *EDBM_vert_find_nearest_ex(struct ViewContext *vc,
- float *r_dist,
+ float *dist_px_manhattan_p,
const bool use_select_bias,
bool use_cycle,
struct Base **bases,
uint bases_len,
uint *r_base_index);
-struct BMVert *EDBM_vert_find_nearest(struct ViewContext *vc, float *r_dist);
+struct BMVert *EDBM_vert_find_nearest(struct ViewContext *vc, float *dist_px_manhattan_p);
struct BMEdge *EDBM_edge_find_nearest_ex(struct ViewContext *vc,
- float *r_dist,
+ float *dist_px_manhattan,
float *r_dist_center,
const bool use_select_bias,
bool use_cycle,
@@ -181,18 +181,19 @@ struct BMEdge *EDBM_edge_find_nearest_ex(struct ViewContext *vc,
struct Base **bases,
uint bases_len,
uint *r_base_index);
-struct BMEdge *EDBM_edge_find_nearest(struct ViewContext *vc, float *r_dist);
+struct BMEdge *EDBM_edge_find_nearest(struct ViewContext *vc, float *dist_px_manhattan_p);
struct BMFace *EDBM_face_find_nearest_ex(struct ViewContext *vc,
- float *r_dist,
+ float *dist_px_manhattan,
float *r_dist_center,
+ const bool use_zbuf_single_px,
const bool use_select_bias,
bool use_cycle,
struct BMFace **r_efa_zbuf,
struct Base **bases,
uint bases_len,
uint *r_base_index);
-struct BMFace *EDBM_face_find_nearest(struct ViewContext *vc, float *r_dist);
+struct BMFace *EDBM_face_find_nearest(struct ViewContext *vc, float *dist_px_manhattan_p);
bool EDBM_unified_findnearest(struct ViewContext *vc,
struct Base **bases,
diff --git a/source/blender/editors/include/ED_numinput.h b/source/blender/editors/include/ED_numinput.h
index 50f1ce1efe2..d5685788ce1 100644
--- a/source/blender/editors/include/ED_numinput.h
+++ b/source/blender/editors/include/ED_numinput.h
@@ -107,8 +107,9 @@ bool user_string_to_number(bContext *C,
const char *str,
const struct UnitSettings *unit,
int type,
- const char *error_prefix,
- double *r_value);
+ double *r_value,
+ const bool use_single_line_error,
+ char **r_error);
/** \} */
diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h
index 0767ce21382..1738c383328 100644
--- a/source/blender/editors/include/ED_object.h
+++ b/source/blender/editors/include/ED_object.h
@@ -178,6 +178,9 @@ void ED_object_base_active_refresh(struct Main *bmain,
struct Scene *scene,
struct ViewLayer *view_layer);
void ED_object_base_free_and_unlink(struct Main *bmain, struct Scene *scene, struct Object *ob);
+void ED_object_base_free_and_unlink_no_indirect_check(struct Main *bmain,
+ struct Scene *scene,
+ struct Object *ob);
bool ED_object_base_deselect_all_ex(struct ViewLayer *view_layer,
struct View3D *v3d,
int action,
@@ -207,6 +210,8 @@ bool ED_object_editmode_exit_ex(struct Main *bmain,
int flag);
bool ED_object_editmode_exit(struct bContext *C, int flag);
+bool ED_object_editmode_free_ex(struct Main *bmain, struct Object *obedit);
+
bool ED_object_editmode_exit_multi_ex(struct Main *bmain,
struct Scene *scene,
struct ViewLayer *view_layer,
@@ -242,6 +247,7 @@ void ED_object_texture_paint_mode_enter(struct bContext *C);
void ED_object_texture_paint_mode_exit_ex(struct Main *bmain, struct Scene *scene, Object *ob);
void ED_object_texture_paint_mode_exit(struct bContext *C);
+bool ED_object_particle_edit_mode_supported(const Object *ob);
void ED_object_particle_edit_mode_enter_ex(struct Depsgraph *depsgraph,
struct Scene *scene,
Object *ob);
@@ -289,12 +295,12 @@ void ED_object_add_mesh_props(struct wmOperatorType *ot);
bool ED_object_add_generic_get_opts(struct bContext *C,
struct wmOperator *op,
const char view_align_axis,
- float loc[3],
- float rot[3],
- float scale[3],
- bool *enter_editmode,
- unsigned short *local_view_bits,
- bool *is_view_aligned);
+ float r_loc[3],
+ float r_rot[3],
+ float r_scale[3],
+ bool *r_enter_editmode,
+ unsigned short *r_local_view_bits,
+ bool *r_is_view_aligned);
struct Object *ED_object_add_type_with_obdata(struct bContext *C,
const int type,
diff --git a/source/blender/editors/include/ED_render.h b/source/blender/editors/include/ED_render.h
index ed35b9138f3..0fb06639dbf 100644
--- a/source/blender/editors/include/ED_render.h
+++ b/source/blender/editors/include/ED_render.h
@@ -91,8 +91,7 @@ void ED_preview_shader_job(const struct bContext *C,
int sizex,
int sizey,
int method);
-void ED_preview_icon_render(struct Main *bmain,
- struct Depsgraph *depsgraph,
+void ED_preview_icon_render(const struct bContext *C,
struct Scene *scene,
struct ID *id,
unsigned int *rect,
diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h
index b3205acb8ee..bdd7ec571dc 100644
--- a/source/blender/editors/include/ED_screen.h
+++ b/source/blender/editors/include/ED_screen.h
@@ -162,7 +162,7 @@ void ED_area_tag_redraw_no_rebuild(ScrArea *area);
void ED_area_tag_redraw_regiontype(ScrArea *area, int type);
void ED_area_tag_refresh(ScrArea *area);
void ED_area_do_refresh(struct bContext *C, ScrArea *area);
-struct AZone *ED_area_azones_update(ScrArea *area, const int mouse_xy[]);
+struct AZone *ED_area_azones_update(ScrArea *area, const int mouse_xy[2]);
void ED_area_status_text(ScrArea *area, const char *str);
void ED_area_newspace(struct bContext *C, ScrArea *area, int type, const bool skip_region_exit);
void ED_area_prevspace(struct bContext *C, ScrArea *area);
@@ -200,8 +200,6 @@ ScrArea *ED_screen_areas_iter_next(const bScreen *screen, const ScrArea *area);
/* screens */
void ED_screens_init(struct Main *bmain, struct wmWindowManager *wm);
void ED_screen_draw_edges(struct wmWindow *win);
-void ED_screen_draw_join_shape(struct ScrArea *sa1, struct ScrArea *sa2);
-void ED_screen_draw_split_preview(struct ScrArea *area, const int dir, const float fac);
void ED_screen_refresh(struct wmWindowManager *wm, struct wmWindow *win);
void ED_screen_ensure_updated(struct wmWindowManager *wm,
struct wmWindow *win,
@@ -304,6 +302,7 @@ void ED_operatortypes_workspace(void);
/* operators; context poll callbacks */
bool ED_operator_screenactive(struct bContext *C);
+bool ED_operator_screenactive_nobackground(struct bContext *C);
bool ED_operator_screen_mainwinactive(struct bContext *C);
bool ED_operator_areaactive(struct bContext *C);
bool ED_operator_regionactive(struct bContext *C);
@@ -449,10 +448,10 @@ enum {
};
/* SCREEN_OT_space_context_cycle direction */
-enum {
+typedef enum eScreenCycle {
SPACE_CONTEXT_CYCLE_PREV,
SPACE_CONTEXT_CYCLE_NEXT,
-};
+} eScreenCycle;
#ifdef __cplusplus
}
diff --git a/source/blender/editors/include/ED_sequencer.h b/source/blender/editors/include/ED_sequencer.h
index 11eff2d583b..ae76f0b6eaf 100644
--- a/source/blender/editors/include/ED_sequencer.h
+++ b/source/blender/editors/include/ED_sequencer.h
@@ -42,6 +42,7 @@ bool ED_space_sequencer_maskedit_poll(struct bContext *C);
bool ED_space_sequencer_check_show_imbuf(struct SpaceSeq *sseq);
bool ED_space_sequencer_check_show_strip(struct SpaceSeq *sseq);
+bool ED_space_sequencer_has_visible_animation_on_strip(const struct Scene *scene);
void ED_operatormacros_sequencer(void);
diff --git a/source/blender/editors/include/ED_spreadsheet.h b/source/blender/editors/include/ED_spreadsheet.h
new file mode 100644
index 00000000000..3a07b1b9d4b
--- /dev/null
+++ b/source/blender/editors/include/ED_spreadsheet.h
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+struct SpreadsheetContext;
+struct SpaceSpreadsheet;
+struct SpaceNode;
+struct ID;
+struct bNode;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct SpreadsheetContext *ED_spreadsheet_context_new(int type);
+void ED_spreadsheet_context_free(struct SpreadsheetContext *context);
+void ED_spreadsheet_context_path_clear(struct SpaceSpreadsheet *sspreadsheet);
+void ED_spreadsheet_context_path_update_tag(struct SpaceSpreadsheet *sspreadsheet);
+uint64_t ED_spreadsheet_context_path_hash(struct SpaceSpreadsheet *sspreadsheet);
+
+struct ID *ED_spreadsheet_get_current_id(struct SpaceSpreadsheet *sspreadsheet);
+
+void ED_spreadsheet_set_geometry_node_context(struct SpaceSpreadsheet *sspreadsheet,
+ struct SpaceNode *snode,
+ struct bNode *node);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/editors/include/ED_text.h b/source/blender/editors/include/ED_text.h
index 6742561735e..2284c82b3d5 100644
--- a/source/blender/editors/include/ED_text.h
+++ b/source/blender/editors/include/ED_text.h
@@ -34,6 +34,8 @@ struct UndoStep;
struct UndoType;
struct bContext;
+void ED_text_scroll_to_cursor(struct SpaceText *st, struct ARegion *region, bool center);
+
bool ED_text_region_location_from_cursor(struct SpaceText *st,
struct ARegion *region,
const int cursor_co[2],
diff --git a/source/blender/editors/include/ED_transform.h b/source/blender/editors/include/ED_transform.h
index 8f1be847e2b..cb6fb0dba60 100644
--- a/source/blender/editors/include/ED_transform.h
+++ b/source/blender/editors/include/ED_transform.h
@@ -151,8 +151,7 @@ short ED_transform_calc_orientation_from_type_ex(const struct bContext *C,
struct RegionView3D *rv3d,
struct Object *ob,
struct Object *obedit,
- const short orientation_type,
- int orientation_index_custom,
+ const short orientation_index,
const int pivot_point);
/* transform gizmos */
@@ -186,8 +185,7 @@ struct TransformCalcParams {
uint use_only_center : 1;
uint use_local_axis : 1;
/* Use 'Scene.orientation_type' when zero, otherwise subtract one and use. */
- ushort orientation_type;
- ushort orientation_index_custom;
+ ushort orientation_index;
};
int ED_transform_calc_gizmo_stats(const struct bContext *C,
const struct TransformCalcParams *params,
diff --git a/source/blender/editors/include/ED_transform_snap_object_context.h b/source/blender/editors/include/ED_transform_snap_object_context.h
index b7174964ef6..42e73bbf744 100644
--- a/source/blender/editors/include/ED_transform_snap_object_context.h
+++ b/source/blender/editors/include/ED_transform_snap_object_context.h
@@ -39,12 +39,19 @@ struct View3D;
/* ED_transform_snap_object_*** API */
-typedef enum eSnapSelect {
+typedef enum {
SNAP_ALL = 0,
SNAP_NOT_SELECTED = 1,
SNAP_NOT_ACTIVE = 2,
+ SNAP_ONLY_ACTIVE = 3,
} eSnapSelect;
+typedef enum {
+ SNAP_GEOM_FINAL = 0,
+ SNAP_GEOM_CAGE = 1,
+ SNAP_GEOM_EDIT = 2, /* Bmesh for mesh-type. */
+} eSnapEditType;
+
/** used for storing multiple hits */
struct SnapObjectHitDepth {
struct SnapObjectHitDepth *next, *prev;
@@ -54,7 +61,7 @@ struct SnapObjectHitDepth {
float no[3];
int index;
- struct Object *ob;
+ struct Object *ob_eval;
float obmat[4][4];
/* needed to tell which ray-cast this was part of,
@@ -64,10 +71,10 @@ struct SnapObjectHitDepth {
/** parameters that define which objects will be used to snap. */
struct SnapObjectParams {
- /* special context sensitive handling for the active or selected object */
+ /* Special context sensitive handling for the active or selected object. */
char snap_select;
- /* use editmode cage */
- unsigned int use_object_edit_cage : 1;
+ /* Geometry for snapping in edit mode. */
+ char edit_mode_type;
/* snap to the closest element, use when using more than one snap type */
unsigned int use_occlusion_test : 1;
/* exclude back facing geometry from snapping */
diff --git a/source/blender/editors/include/ED_uvedit.h b/source/blender/editors/include/ED_uvedit.h
index 4de97411059..ea3d921f2c5 100644
--- a/source/blender/editors/include/ED_uvedit.h
+++ b/source/blender/editors/include/ED_uvedit.h
@@ -42,6 +42,7 @@ struct SpaceImage;
struct ToolSettings;
struct ViewLayer;
struct bNode;
+struct bNodeTree;
struct wmKeyConfig;
/* uvedit_ops.c */
diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h
index 3b8e062ffec..52d69d12253 100644
--- a/source/blender/editors/include/ED_view3d.h
+++ b/source/blender/editors/include/ED_view3d.h
@@ -144,15 +144,28 @@ bool ED_view3d_camera_to_view_selected(struct Main *bmain,
void ED_view3d_lastview_store(struct RegionView3D *rv3d);
/* Depth buffer */
-void ED_view3d_depth_update(struct ARegion *region);
-float ED_view3d_depth_read_cached(const struct ViewContext *vc, const int mval[2]);
+typedef enum {
+ V3D_DEPTH_NO_GPENCIL = 0,
+ V3D_DEPTH_GPENCIL_ONLY,
+ V3D_DEPTH_OBJECT_ONLY,
+} eV3DDepthOverrideMode;
+void ED_view3d_depth_override(struct Depsgraph *depsgraph,
+ struct ARegion *region,
+ struct View3D *v3d,
+ struct Object *obact,
+ eV3DDepthOverrideMode mode,
+ bool update_cache);
+bool ED_view3d_depth_read_cached(const ViewDepths *vd,
+ const int mval[2],
+ int margin,
+ float *r_depth);
bool ED_view3d_depth_read_cached_normal(const ViewContext *vc,
const int mval[2],
float r_normal[3]);
-bool ED_view3d_depth_unproject(const struct ARegion *region,
- const int mval[2],
- const double depth,
- float r_location_world[3]);
+bool ED_view3d_depth_unproject_v3(const struct ARegion *region,
+ const int mval[2],
+ const double depth,
+ float r_location_world[3]);
void ED_view3d_depth_tag_update(struct RegionView3D *rv3d);
/* Projection */
@@ -397,8 +410,13 @@ void ED_view3d_ob_project_mat_get_from_obmat(const struct RegionView3D *rv3d,
const float obmat[4][4],
float r_pmat[4][4]);
-void ED_view3d_project(const struct ARegion *region, const float world[3], float r_region_co[3]);
-bool ED_view3d_unproject(
+void ED_view3d_project_v3(const struct ARegion *region,
+ const float world[3],
+ float r_region_co[3]);
+void ED_view3d_project_v2(const struct ARegion *region,
+ const float world[3],
+ float r_region_co[2]);
+bool ED_view3d_unproject_v3(
const struct ARegion *region, float regionx, float regiony, float regionz, float world[3]);
/* end */
@@ -441,7 +459,7 @@ bool ED_view3d_calc_render_border(const struct Scene *scene,
struct ARegion *region,
struct rcti *rect);
-void ED_view3d_clipping_calc_from_boundbox(float clip[6][4],
+void ED_view3d_clipping_calc_from_boundbox(float clip[4][4],
const struct BoundBox *clipbb,
const bool is_flip);
void ED_view3d_clipping_calc(struct BoundBox *bb,
@@ -481,11 +499,6 @@ bool ED_view3d_autodist(struct Depsgraph *depsgraph,
const bool alphaoverride,
const float fallback_depth_pt[3]);
-/* Only draw so #ED_view3d_autodist_simple can be called many times after. */
-void ED_view3d_autodist_init(struct Depsgraph *depsgraph,
- struct ARegion *region,
- struct View3D *v3d,
- int mode);
bool ED_view3d_autodist_simple(struct ARegion *region,
const int mval[2],
float mouse_worldloc[3],
@@ -682,7 +695,7 @@ float ED_view3d_grid_scale(const struct Scene *scene,
void ED_view3d_grid_steps(const struct Scene *scene,
struct View3D *v3d,
struct RegionView3D *rv3d,
- float *r_grid_steps);
+ float r_grid_steps[8]);
float ED_view3d_grid_view_scale(struct Scene *scene,
struct View3D *v3d,
struct ARegion *region,
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index dfe0898a85b..1d335c500ca 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -186,17 +186,17 @@ enum {
UI_RETURN_POPUP_OK = 1 << 5,
};
-/* but->flag - general state flags. */
+/** #uiBut.flag general state flags. */
enum {
- /** Warning, the first 6 flags are internal. */
- UI_BUT_ICON_SUBMENU = 1 << 6,
- UI_BUT_ICON_PREVIEW = 1 << 7,
+ /* WARNING: the first 7 flags are internal (see #UI_SELECT definition). */
+ UI_BUT_ICON_SUBMENU = 1 << 7,
+ UI_BUT_ICON_PREVIEW = 1 << 8,
- UI_BUT_NODE_LINK = 1 << 8,
- UI_BUT_NODE_ACTIVE = 1 << 9,
- UI_BUT_DRAG_LOCK = 1 << 10,
+ UI_BUT_NODE_LINK = 1 << 9,
+ UI_BUT_NODE_ACTIVE = 1 << 10,
+ UI_BUT_DRAG_LOCK = 1 << 11,
/** Grayed out and un-editable. */
- UI_BUT_DISABLED = 1 << 11,
+ UI_BUT_DISABLED = 1 << 12,
UI_BUT_ANIMATED = 1 << 13,
UI_BUT_ANIMATED_KEY = 1 << 14,
@@ -723,6 +723,7 @@ void UI_but_drag_set_asset(uiBut *but,
const char *name,
const char *path,
int id_type,
+ int import_type, /* eFileAssetImportType */
int icon,
struct ImBuf *imb,
float scale);
@@ -1600,6 +1601,7 @@ void UI_but_func_search_set(uiBut *but,
uiButSearchCreateFn search_create_fn,
uiButSearchUpdateFn search_update_fn,
void *arg,
+ const bool free_arg,
uiButSearchArgFreeFn search_arg_free_fn,
uiButHandleFunc search_exec_fn,
void *active);
@@ -1728,7 +1730,7 @@ struct Panel *UI_panel_add_instanced(const struct bContext *C,
struct PointerRNA *custom_data);
void UI_panels_free_instanced(const struct bContext *C, struct ARegion *region);
-#define INSTANCED_PANEL_UNIQUE_STR_LEN 4
+#define INSTANCED_PANEL_UNIQUE_STR_LEN 16
void UI_list_panel_unique_str(struct Panel *panel, char *r_name);
typedef void (*uiListPanelIDFromDataFunc)(void *data_link, char *r_idname);
@@ -2136,7 +2138,7 @@ void uiTemplateComponentMenu(uiLayout *layout,
struct PointerRNA *ptr,
const char *propname,
const char *name);
-void uiTemplateNodeSocket(uiLayout *layout, struct bContext *C, float *color);
+void uiTemplateNodeSocket(uiLayout *layout, struct bContext *C, float color[4]);
void uiTemplateCacheFile(uiLayout *layout,
const struct bContext *C,
struct PointerRNA *ptr,
@@ -2404,9 +2406,12 @@ void uiItemS_ex(uiLayout *layout, float factor);
void uiItemSpacer(uiLayout *layout);
void uiItemPopoverPanel_ptr(
- uiLayout *layout, struct bContext *C, struct PanelType *pt, const char *name, int icon);
-void uiItemPopoverPanel(
- uiLayout *layout, struct bContext *C, const char *panel_type, const char *name, int icon);
+ uiLayout *layout, const struct bContext *C, struct PanelType *pt, const char *name, int icon);
+void uiItemPopoverPanel(uiLayout *layout,
+ const struct bContext *C,
+ const char *panel_type,
+ const char *name,
+ int icon);
void uiItemPopoverPanelFromGroup(uiLayout *layout,
struct bContext *C,
int space_id,
diff --git a/source/blender/editors/include/UI_resources.h b/source/blender/editors/include/UI_resources.h
index 1820c2f133c..c99c7f681b3 100644
--- a/source/blender/editors/include/UI_resources.h
+++ b/source/blender/editors/include/UI_resources.h
@@ -451,7 +451,7 @@ int UI_ThemeMenuShadowWidth(void);
/* only for buttons in theme editor! */
const unsigned char *UI_ThemeGetColorPtr(struct bTheme *btheme, int spacetype, int colorid);
-void UI_make_axis_color(const unsigned char *src_col, unsigned char *dst_col, const char axis);
+void UI_make_axis_color(const unsigned char src_col[3], unsigned char dst_col[3], const char axis);
#ifdef __cplusplus
}
diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c
index 279239fcc65..26ea5f8e24a 100644
--- a/source/blender/editors/interface/interface.c
+++ b/source/blender/editors/interface/interface.c
@@ -52,6 +52,7 @@
#include "BKE_context.h"
#include "BKE_idprop.h"
#include "BKE_main.h"
+#include "BKE_report.h"
#include "BKE_scene.h"
#include "BKE_screen.h"
#include "BKE_unit.h"
@@ -1038,7 +1039,6 @@ static bool ui_but_is_rna_undo(const uiBut *but)
if (ID_CHECK_UNDO(id) == false) {
return false;
}
- return true;
}
if (but->rnapoin.type && !RNA_struct_undo_check(but->rnapoin.type)) {
return false;
@@ -1817,9 +1817,9 @@ static void ui_but_validate(const uiBut *but)
#endif
/**
- * Check if the operator \a ot poll is successfull with the context given by \a but (optionally).
+ * Check if the operator \a ot poll is successful with the context given by \a but (optionally).
* \param but: The button that might store context. Can be NULL for convenience (e.g. if there is
- * no button to take context from, but we still want to poll the operator).
+ * no button to take context from, but we still want to poll the operator).
*/
bool ui_but_context_poll_operator(bContext *C, wmOperatorType *ot, const uiBut *but)
{
@@ -2327,6 +2327,14 @@ bool ui_but_is_float(const uiBut *but)
return false;
}
+PropertyScaleType ui_but_scale_type(const uiBut *but)
+{
+ if (but->rnaprop) {
+ return RNA_property_ui_scale(but->rnaprop);
+ }
+ return PROP_SCALE_LINEAR;
+}
+
bool ui_but_is_bool(const uiBut *but)
{
if (ELEM(but->type,
@@ -2913,7 +2921,14 @@ char *ui_but_string_get_dynamic(uiBut *but, int *r_str_size)
static bool ui_number_from_string_units(
bContext *C, const char *str, const int unit_type, const UnitSettings *unit, double *r_value)
{
- return user_string_to_number(C, str, unit, unit_type, UI_NUMBER_EVAL_ERROR_PREFIX, r_value);
+ char *error = NULL;
+ const bool ok = user_string_to_number(C, str, unit, unit_type, r_value, true, &error);
+ if (error) {
+ ReportList *reports = CTX_wm_reports(C);
+ BKE_reportf(reports, RPT_ERROR, "%s: %s", UI_NUMBER_EVAL_ERROR_PREFIX, error);
+ MEM_freeN(error);
+ }
+ return ok;
}
static bool ui_number_from_string_units_with_but(bContext *C,
@@ -2930,7 +2945,11 @@ static bool ui_number_from_string(bContext *C, const char *str, double *r_value)
{
bool ok;
#ifdef WITH_PYTHON
- ok = BPY_run_string_as_number(C, NULL, str, UI_NUMBER_EVAL_ERROR_PREFIX, r_value);
+ struct BPy_RunErrInfo err_info = {
+ .reports = CTX_wm_reports(C),
+ .report_prefix = UI_NUMBER_EVAL_ERROR_PREFIX,
+ };
+ ok = BPY_run_string_as_number(C, NULL, str, &err_info, r_value);
#else
UNUSED_VARS(C);
*r_value = atof(str);
@@ -3188,19 +3207,17 @@ void ui_but_range_set_hard(uiBut *but)
const PropertyType type = RNA_property_type(but->rnaprop);
- /* clamp button range to something reasonable in case
- * we get -inf/inf from RNA properties */
if (type == PROP_INT) {
int imin, imax;
RNA_property_int_range(&but->rnapoin, but->rnaprop, &imin, &imax);
- but->hardmin = (imin == INT_MIN) ? -1e4 : imin;
- but->hardmax = (imin == INT_MAX) ? 1e4 : imax;
+ but->hardmin = imin;
+ but->hardmax = imax;
}
else if (type == PROP_FLOAT) {
float fmin, fmax;
RNA_property_float_range(&but->rnapoin, but->rnaprop, &fmin, &fmax);
- but->hardmin = (fmin == -FLT_MAX) ? (float)-1e4 : fmin;
- but->hardmax = (fmax == FLT_MAX) ? (float)1e4 : fmax;
+ but->hardmin = fmin;
+ but->hardmax = fmax;
}
}
@@ -6127,6 +6144,7 @@ void UI_but_drag_set_asset(uiBut *but,
const char *name,
const char *path,
int id_type,
+ int import_type,
int icon,
struct ImBuf *imb,
float scale)
@@ -6136,6 +6154,7 @@ void UI_but_drag_set_asset(uiBut *but,
BLI_strncpy(asset_drag->name, name, sizeof(asset_drag->name));
asset_drag->path = path;
asset_drag->id_type = id_type;
+ asset_drag->import_type = import_type;
but->dragtype = WM_DRAG_ASSET;
ui_def_but_icon(but, icon, 0); /* no flag UI_HAS_ICON, so icon doesn't draw in button */
@@ -6591,6 +6610,8 @@ uiBut *uiDefSearchBut(uiBlock *block,
* \param search_create_fn: Function to create the menu.
* \param search_update_fn: Function to refresh search content after the search text has changed.
* \param arg: user value.
+ * \param free_arg: Set to true if the argument is newly allocated memory for every redraw and
+ * should be freed when the button is destroyed.
* \param search_arg_free_fn: When non-null, use this function to free \a arg.
* \param search_exec_fn: Function that executes the action, gets \a arg as the first argument.
* The second argument as the active item-pointer
@@ -6601,6 +6622,7 @@ void UI_but_func_search_set(uiBut *but,
uiButSearchCreateFn search_create_fn,
uiButSearchUpdateFn search_update_fn,
void *arg,
+ const bool free_arg,
uiButSearchArgFreeFn search_arg_free_fn,
uiButHandleFunc search_exec_fn,
void *active)
@@ -6636,11 +6658,17 @@ void UI_but_func_search_set(uiBut *but,
}
#endif
/* Handling will pass the active item as arg2 later, so keep it NULL here. */
- UI_but_func_set(but, search_exec_fn, search_but->arg, NULL);
+ if (free_arg) {
+ UI_but_funcN_set(but, search_exec_fn, search_but->arg, NULL);
+ }
+ else {
+ UI_but_func_set(but, search_exec_fn, search_but->arg, NULL);
+ }
}
- /* search buttons show red-alert if item doesn't exist, not for menus */
- if (0 == (but->block->flag & UI_BLOCK_LOOP)) {
+ /* search buttons show red-alert if item doesn't exist, not for menus. Don't do this for
+ * buttons where any result is valid anyway, since any string will be valid anyway. */
+ if (0 == (but->block->flag & UI_BLOCK_LOOP) && !search_but->results_are_suggestions) {
/* skip empty buttons, not all buttons need input, we only show invalid */
if (but->drawstr[0]) {
ui_but_search_refresh(search_but);
@@ -6780,6 +6808,7 @@ uiBut *uiDefSearchButO_ptr(uiBlock *block,
ui_searchbox_create_generic,
operator_enum_search_update_fn,
but,
+ false,
NULL,
operator_enum_search_exec_fn,
NULL);
diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c
index 91c19ff2850..b142e383df0 100644
--- a/source/blender/editors/interface/interface_context_menu.c
+++ b/source/blender/editors/interface/interface_context_menu.c
@@ -399,7 +399,7 @@ static void ui_but_user_menu_add(bContext *C, uiBut *but, bUserMenu *um)
"'%s').label",
idname);
char *expr_result = NULL;
- if (BPY_run_string_as_string(C, expr_imports, expr, __func__, &expr_result)) {
+ if (BPY_run_string_as_string(C, expr_imports, expr, NULL, &expr_result)) {
STRNCPY(drawstr, expr_result);
MEM_freeN(expr_result);
}
@@ -542,9 +542,7 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but)
const PropertyType type = RNA_property_type(prop);
const PropertySubType subtype = RNA_property_subtype(prop);
bool is_anim = RNA_property_animateable(ptr, prop);
- const bool is_editable = RNA_property_editable(ptr, prop);
const bool is_idprop = RNA_property_is_idprop(prop);
- const bool is_set = RNA_property_is_set(ptr, prop);
/* second slower test,
* saved people finding keyframe items in menus when its not possible */
@@ -893,12 +891,6 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but)
"all",
1);
}
- if (is_editable /*&& is_idprop*/ && is_set) {
- uiItemO(layout,
- CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Unset"),
- ICON_NONE,
- "UI_OT_unset_property_button");
- }
if (is_idprop && !is_array && ELEM(type, PROP_INT, PROP_FLOAT)) {
uiItemO(layout,
diff --git a/source/blender/editors/interface/interface_eyedropper.c b/source/blender/editors/interface/interface_eyedropper.c
index 178f663ff58..b52bfc81b7a 100644
--- a/source/blender/editors/interface/interface_eyedropper.c
+++ b/source/blender/editors/interface/interface_eyedropper.c
@@ -130,14 +130,19 @@ void eyedropper_draw_cursor_text_region(const struct bContext *C,
const char *name)
{
wmWindow *win = CTX_wm_window(C);
- const int x = win->eventstate->x - region->winrct.xmin;
- const int y = win->eventstate->y - region->winrct.ymin;
+ const int x = win->eventstate->x;
+ const int y = win->eventstate->y;
if ((name[0] == '\0') || (BLI_rcti_isect_pt(&region->winrct, x, y) == false)) {
return;
}
- eyedropper_draw_cursor_text_ex(x, y, name);
+ const int mval[2] = {
+ x - region->winrct.xmin,
+ y - region->winrct.ymin,
+ };
+
+ eyedropper_draw_cursor_text_ex(mval[0], mval[1], name);
}
/**
diff --git a/source/blender/editors/interface/interface_eyedropper_color.c b/source/blender/editors/interface/interface_eyedropper_color.c
index 4ae6f66281f..ba72cecc514 100644
--- a/source/blender/editors/interface/interface_eyedropper_color.c
+++ b/source/blender/editors/interface/interface_eyedropper_color.c
@@ -116,14 +116,12 @@ static bool eyedropper_init(bContext *C, wmOperator *op)
float col[4];
RNA_property_float_get_array(&eye->ptr, eye->prop, col);
- if (ELEM(eye->ptr.type, &RNA_CompositorNodeCryptomatteV2, &RNA_CompositorNodeCryptomatte)) {
+ if (eye->ptr.type == &RNA_CompositorNodeCryptomatteV2) {
eye->crypto_node = (bNode *)eye->ptr.data;
- eye->cryptomatte_session = ntreeCompositCryptomatteSession(eye->crypto_node);
+ eye->cryptomatte_session = ntreeCompositCryptomatteSession(CTX_data_scene(C),
+ eye->crypto_node);
eye->draw_handle_sample_text = WM_draw_cb_activate(CTX_wm_window(C), eyedropper_draw_cb, eye);
}
- else {
- eye->crypto_node = NULL;
- }
if (prop_subtype != PROP_COLOR) {
Scene *scene = CTX_data_scene(C);
@@ -202,6 +200,57 @@ static bool eyedropper_cryptomatte_sample_renderlayer_fl(RenderLayer *render_lay
return false;
}
+static bool eyedropper_cryptomatte_sample_render_fl(const bNode *node,
+ const char *prefix,
+ const float fpos[2],
+ float r_col[3])
+{
+ bool success = false;
+ Scene *scene = (Scene *)node->id;
+ BLI_assert(GS(scene->id.name) == ID_SCE);
+ Render *re = RE_GetSceneRender(scene);
+
+ if (re) {
+ RenderResult *rr = RE_AcquireResultRead(re);
+ if (rr) {
+ LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) {
+ RenderLayer *render_layer = RE_GetRenderLayer(rr, view_layer->name);
+ success = eyedropper_cryptomatte_sample_renderlayer_fl(render_layer, prefix, fpos, r_col);
+ if (success) {
+ break;
+ }
+ }
+ }
+ RE_ReleaseResult(re);
+ }
+ return success;
+}
+
+static bool eyedropper_cryptomatte_sample_image_fl(const bNode *node,
+ NodeCryptomatte *crypto,
+ const char *prefix,
+ const float fpos[2],
+ float r_col[3])
+{
+ bool success = false;
+ Image *image = (Image *)node->id;
+ BLI_assert((image == NULL) || (GS(image->id.name) == ID_IM));
+ ImageUser *iuser = &crypto->iuser;
+
+ if (image && image->type == IMA_TYPE_MULTILAYER) {
+ ImBuf *ibuf = BKE_image_acquire_ibuf(image, iuser, NULL);
+ if (image->rr) {
+ LISTBASE_FOREACH (RenderLayer *, render_layer, &image->rr->layers) {
+ success = eyedropper_cryptomatte_sample_renderlayer_fl(render_layer, prefix, fpos, r_col);
+ if (success) {
+ break;
+ }
+ }
+ }
+ BKE_image_release_ibuf(image, ibuf, NULL);
+ }
+ return success;
+}
static bool eyedropper_cryptomatte_sample_fl(
bContext *C, Eyedropper *eye, int mx, int my, float r_col[3])
@@ -258,53 +307,19 @@ static bool eyedropper_cryptomatte_sample_fl(
return false;
}
- bool success = false;
/* TODO(jbakker): Migrate this file to cc and use std::string as return param. */
char prefix[MAX_NAME + 1];
- ntreeCompositCryptomatteLayerPrefix(node, prefix, sizeof(prefix) - 1);
+ const Scene *scene = CTX_data_scene(C);
+ ntreeCompositCryptomatteLayerPrefix(scene, node, prefix, sizeof(prefix) - 1);
prefix[MAX_NAME] = '\0';
if (node->custom1 == CMP_CRYPTOMATTE_SRC_RENDER) {
- Scene *scene = (Scene *)node->id;
- BLI_assert(GS(scene->id.name) == ID_SCE);
- Render *re = RE_GetSceneRender(scene);
-
- if (re) {
- RenderResult *rr = RE_AcquireResultRead(re);
- if (rr) {
- LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) {
- RenderLayer *render_layer = RE_GetRenderLayer(rr, view_layer->name);
- success = eyedropper_cryptomatte_sample_renderlayer_fl(
- render_layer, prefix, fpos, r_col);
- if (success) {
- break;
- }
- }
- }
- RE_ReleaseResult(re);
- }
+ return eyedropper_cryptomatte_sample_render_fl(node, prefix, fpos, r_col);
}
- else if (node->custom1 == CMP_CRYPTOMATTE_SRC_IMAGE) {
- Image *image = (Image *)node->id;
- BLI_assert(GS(image->id.name) == ID_IM);
- ImageUser *iuser = &crypto->iuser;
-
- if (image && image->type == IMA_TYPE_MULTILAYER) {
- ImBuf *ibuf = BKE_image_acquire_ibuf(image, iuser, NULL);
- if (image->rr) {
- LISTBASE_FOREACH (RenderLayer *, render_layer, &image->rr->layers) {
- success = eyedropper_cryptomatte_sample_renderlayer_fl(
- render_layer, prefix, fpos, r_col);
- if (success) {
- break;
- }
- }
- }
- BKE_image_release_ibuf(image, ibuf, NULL);
- }
+ if (node->custom1 == CMP_CRYPTOMATTE_SRC_IMAGE) {
+ return eyedropper_cryptomatte_sample_image_fl(node, crypto, prefix, fpos, r_col);
}
-
- return success;
+ return false;
}
/**
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index a5a5a69728e..5f98a501bec 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -126,6 +126,24 @@
#define UI_MAX_PASSWORD_STR 128
/**
+ * This is a lower limit on the soft minimum of the range.
+ * Usually the derived lower limit from the visible precision is higher,
+ * so this number is the backup minimum.
+ *
+ * Logarithmic scale does not work with a minimum value of zero,
+ * but we want to support it anyway. It is set to 0.5e... for
+ * correct rounding since when the tweaked value is lower than
+ * the log minimum (lower limit), it will snap to 0.
+ */
+#define UI_PROP_SCALE_LOG_MIN 0.5e-8f
+/**
+ * This constant defines an offset for the precision change in
+ * snap rounding, when going to higher values. It is set to
+ * `0.5 - log10(3) = 0.03` to make the switch at `0.3` values.
+ */
+#define UI_PROP_SCALE_LOG_SNAP_OFFSET 0.03f
+
+/**
* When #USER_CONTINUOUS_MOUSE is disabled or tablet input is used,
* Use this as a maximum soft range for mapping cursor motion to the value.
* Otherwise min/max of #FLT_MAX, #INT_MAX cause small adjustments to jump to large numbers.
@@ -477,6 +495,7 @@ static bool ui_do_but_extra_operator_icon(bContext *C,
static void ui_do_but_extra_operator_icons_mousemove(uiBut *but,
uiHandleButtonData *data,
const wmEvent *event);
+static void ui_numedit_begin_set_values(uiBut *but, uiHandleButtonData *data);
#ifdef USE_DRAG_MULTINUM
static void ui_multibut_restore(bContext *C, uiHandleButtonData *data, uiBlock *block);
@@ -1114,6 +1133,13 @@ static void ui_apply_but_TAB(bContext *C, uiBut *but, uiHandleButtonData *data)
static void ui_apply_but_NUM(bContext *C, uiBut *but, uiHandleButtonData *data)
{
if (data->str) {
+ double value;
+ /* Check if the string value is a number and cancel if it's equal to the startvalue. */
+ if (ui_but_string_eval_number(C, but, data->str, &value) && (value == data->startvalue)) {
+ data->cancel = true;
+ return;
+ }
+
if (ui_but_string_set(C, but, data->str)) {
data->value = ui_but_value_get(but);
}
@@ -2320,16 +2346,16 @@ static int get_but_property_array_length(uiBut *but)
}
static void ui_but_set_float_array(
- bContext *C, uiBut *but, uiHandleButtonData *data, float *values, int array_length)
+ bContext *C, uiBut *but, uiHandleButtonData *data, const float *values, const int values_len)
{
button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
- for (int i = 0; i < array_length; i++) {
+ for (int i = 0; i < values_len; i++) {
RNA_property_float_set_index(&but->rnapoin, but->rnaprop, i, values[i]);
}
if (data) {
if (but->type == UI_BTYPE_UNITVEC) {
- BLI_assert(array_length == 3);
+ BLI_assert(values_len == 3);
copy_v3_v3(data->vec, values);
}
else {
@@ -2340,56 +2366,39 @@ static void ui_but_set_float_array(
button_activate_state(C, but, BUTTON_STATE_EXIT);
}
-static void float_array_to_string(float *values,
- int array_length,
+static void float_array_to_string(const float *values,
+ const int values_len,
char *output,
int output_len_max)
{
- /* to avoid buffer overflow attacks; numbers are quite arbitrary */
- BLI_assert(output_len_max > 15);
- output_len_max -= 10;
-
- int current_index = 0;
- output[current_index] = '[';
- current_index++;
-
- for (int i = 0; i < array_length; i++) {
- int length = BLI_snprintf(
- output + current_index, output_len_max - current_index, "%f", values[i]);
- current_index += length;
-
- if (i < array_length - 1) {
- if (current_index < output_len_max) {
- output[current_index + 0] = ',';
- output[current_index + 1] = ' ';
- current_index += 2;
- }
- }
+ const int values_end = values_len - 1;
+ int ofs = 0;
+ output[ofs++] = '[';
+ for (int i = 0; i < values_len; i++) {
+ ofs += BLI_snprintf_rlen(
+ output + ofs, output_len_max - ofs, (i != values_end) ? "%f, " : "%f]", values[i]);
}
-
- output[current_index + 0] = ']';
- output[current_index + 1] = '\0';
}
static void ui_but_copy_numeric_array(uiBut *but, char *output, int output_len_max)
{
- const int array_length = get_but_property_array_length(but);
- float *values = alloca(array_length * sizeof(float));
+ const int values_len = get_but_property_array_length(but);
+ float *values = alloca(values_len * sizeof(float));
RNA_property_float_get_array(&but->rnapoin, but->rnaprop, values);
- float_array_to_string(values, array_length, output, output_len_max);
+ float_array_to_string(values, values_len, output, output_len_max);
}
-static bool parse_float_array(char *text, float *values, int expected_length)
+static bool parse_float_array(char *text, float *values, int values_len_expected)
{
/* can parse max 4 floats for now */
- BLI_assert(0 <= expected_length && expected_length <= 4);
+ BLI_assert(0 <= values_len_expected && values_len_expected <= 4);
float v[5];
- const int actual_length = sscanf(
+ const int values_len_actual = sscanf(
text, "[%f, %f, %f, %f, %f]", &v[0], &v[1], &v[2], &v[3], &v[4]);
- if (actual_length == expected_length) {
- memcpy(values, v, sizeof(float) * expected_length);
+ if (values_len_actual == values_len_expected) {
+ memcpy(values, v, sizeof(float) * values_len_expected);
return true;
}
return false;
@@ -2400,16 +2409,16 @@ static void ui_but_paste_numeric_array(bContext *C,
uiHandleButtonData *data,
char *buf_paste)
{
- const int array_length = get_but_property_array_length(but);
- if (array_length > 4) {
+ const int values_len = get_but_property_array_length(but);
+ if (values_len > 4) {
/* not supported for now */
return;
}
- float *values = alloca(sizeof(float) * array_length);
+ float *values = alloca(sizeof(float) * values_len);
- if (parse_float_array(buf_paste, values, array_length)) {
- ui_but_set_float_array(C, but, data, values, array_length);
+ if (parse_float_array(buf_paste, values, values_len)) {
+ ui_but_set_float_array(C, but, data, values, values_len);
}
else {
WM_report(RPT_ERROR, "Expected an array of numbers: [n, n, ...]");
@@ -3354,6 +3363,8 @@ static void ui_textedit_begin(bContext *C, uiBut *but, uiHandleButtonData *data)
if (is_num_but) {
BLI_assert(data->is_str_dynamic == false);
ui_but_convert_to_unit_alt_name(but, data->str, data->maxlen);
+
+ ui_numedit_begin_set_values(but, data);
}
/* won't change from now on */
@@ -3890,6 +3901,14 @@ static void ui_do_but_textedit_select(
/** \name Button Number Editing (various types)
* \{ */
+static void ui_numedit_begin_set_values(uiBut *but, uiHandleButtonData *data)
+{
+ data->startvalue = ui_but_value_get(but);
+ data->origvalue = data->startvalue;
+ data->value = data->origvalue;
+ but->editval = &data->value;
+}
+
static void ui_numedit_begin(uiBut *but, uiHandleButtonData *data)
{
if (but->type == UI_BTYPE_CURVE) {
@@ -3915,19 +3934,21 @@ static void ui_numedit_begin(uiBut *but, uiHandleButtonData *data)
but->editvec = data->vec;
}
else {
- float softrange, softmin, softmax;
+ ui_numedit_begin_set_values(but, data);
- data->startvalue = ui_but_value_get(but);
- data->origvalue = data->startvalue;
- data->value = data->origvalue;
- but->editval = &data->value;
+ float softmin = but->softmin;
+ float softmax = but->softmax;
+ float softrange = softmax - softmin;
+ const PropertyScaleType scale_type = ui_but_scale_type(but);
- softmin = but->softmin;
- softmax = but->softmax;
- softrange = softmax - softmin;
+ float log_min = (scale_type == PROP_SCALE_LOG) ? max_ff(softmin, UI_PROP_SCALE_LOG_MIN) : 0.0f;
if ((but->type == UI_BTYPE_NUM) && (ui_but_is_cursor_warp(but) == false)) {
uiButNumber *number_but = (uiButNumber *)but;
+
+ if (scale_type == PROP_SCALE_LOG) {
+ log_min = max_ff(log_min, powf(10, -number_but->precision) * 0.5f);
+ }
/* Use a minimum so we have a predictable range,
* otherwise some float buttons get a large range. */
const float value_step_float_min = 0.1f;
@@ -3976,7 +3997,31 @@ static void ui_numedit_begin(uiBut *but, uiHandleButtonData *data)
}
}
- data->dragfstart = (softrange == 0.0f) ? 0.0f : ((float)data->value - softmin) / softrange;
+ if (softrange == 0.0f) {
+ data->dragfstart = 0.0f;
+ }
+ else {
+ switch (scale_type) {
+ case PROP_SCALE_LINEAR: {
+ data->dragfstart = ((float)data->value - softmin) / softrange;
+ break;
+ }
+ case PROP_SCALE_LOG: {
+ BLI_assert(log_min != 0.0f);
+ const float base = softmax / log_min;
+ data->dragfstart = logf((float)data->value / log_min) / logf(base);
+ break;
+ }
+ case PROP_SCALE_CUBIC: {
+ const float cubic_min = cube_f(softmin);
+ const float cubic_max = cube_f(softmax);
+ const float cubic_range = cubic_max - cubic_min;
+ const float f = ((float)data->value - softmin) * cubic_range / softrange + cubic_min;
+ data->dragfstart = (cbrtf(f) - softmin) / softrange;
+ break;
+ }
+ }
+ }
data->dragf = data->dragfstart;
data->drag_map_soft_min = softmin;
@@ -4694,6 +4739,7 @@ static float ui_numedit_apply_snapf(
/* pass */
}
else {
+ const PropertyScaleType scale_type = ui_but_scale_type(but);
float softrange = softmax - softmin;
float fac = 1.0f;
@@ -4731,31 +4777,30 @@ static float ui_numedit_apply_snapf(
}
}
- if (snap == SNAP_ON) {
- if (softrange < 2.10f) {
- tempf = roundf(tempf * 10.0f) * 0.1f;
- }
- else if (softrange < 21.0f) {
- tempf = roundf(tempf);
- }
- else {
- tempf = roundf(tempf * 0.1f) * 10.0f;
- }
- }
- else if (snap == SNAP_ON_SMALL) {
- if (softrange < 2.10f) {
- tempf = roundf(tempf * 100.0f) * 0.01f;
- }
- else if (softrange < 21.0f) {
- tempf = roundf(tempf * 10.0f) * 0.1f;
+ BLI_assert(ELEM(snap, SNAP_ON, SNAP_ON_SMALL));
+ switch (scale_type) {
+ case PROP_SCALE_LINEAR:
+ case PROP_SCALE_CUBIC: {
+ const float snap_fac = (snap == SNAP_ON_SMALL ? 0.1f : 1.0f);
+ if (softrange < 2.10f) {
+ tempf = roundf(tempf * 10.0f / snap_fac) * 0.1f * snap_fac;
+ }
+ else if (softrange < 21.0f) {
+ tempf = roundf(tempf / snap_fac) * snap_fac;
+ }
+ else {
+ tempf = roundf(tempf * 0.1f / snap_fac) * 10.0f * snap_fac;
+ }
+ break;
}
- else {
- tempf = roundf(tempf);
+ case PROP_SCALE_LOG: {
+ const float snap_fac = powf(10.0f,
+ roundf(log10f(tempf) + UI_PROP_SCALE_LOG_SNAP_OFFSET) -
+ (snap == SNAP_ON_SMALL ? 2.0f : 1.0f));
+ tempf = roundf(tempf / snap_fac) * snap_fac;
+ break;
}
}
- else {
- BLI_assert(0);
- }
if (fac != 1.0f) {
tempf *= fac;
@@ -4800,6 +4845,7 @@ static bool ui_numedit_but_NUM(uiButNumber *number_but,
int lvalue, temp;
bool changed = false;
const bool is_float = ui_but_is_float(but);
+ const PropertyScaleType scale_type = ui_but_scale_type(but);
/* prevent unwanted drag adjustments, test motion so modifier keys refresh. */
if ((is_motion || data->draglock) && (ui_but_dragedit_update_mval(data, mx) == false)) {
@@ -4811,21 +4857,74 @@ static bool ui_numedit_but_NUM(uiButNumber *number_but,
const float softmax = but->softmax;
const float softrange = softmax - softmin;
+ const float log_min = (scale_type == PROP_SCALE_LOG) ?
+ max_ff(max_ff(softmin, UI_PROP_SCALE_LOG_MIN),
+ powf(10, -number_but->precision) * 0.5f) :
+ 0;
+
/* Mouse location isn't screen clamped to the screen so use a linear mapping
* 2px == 1-int, or 1px == 1-ClickStep */
if (is_float) {
fac *= 0.01f * number_but->step_size;
- tempf = (float)data->startvalue + ((float)(mx - data->dragstartx) * fac);
+ switch (scale_type) {
+ case PROP_SCALE_LINEAR: {
+ tempf = (float)data->startvalue + (float)(mx - data->dragstartx) * fac;
+ break;
+ }
+ case PROP_SCALE_LOG: {
+ const float startvalue = max_ff((float)data->startvalue, log_min);
+ tempf = expf((float)(mx - data->dragstartx) * fac) * startvalue;
+ if (tempf <= log_min) {
+ tempf = 0.0f;
+ }
+ break;
+ }
+ case PROP_SCALE_CUBIC: {
+ tempf = cbrtf((float)data->startvalue) + (float)(mx - data->dragstartx) * fac;
+ tempf *= tempf * tempf;
+ break;
+ }
+ }
+
tempf = ui_numedit_apply_snapf(but, tempf, softmin, softmax, snap);
#if 1 /* fake moving the click start, nicer for dragging back after passing the limit */
- if (tempf < softmin) {
- data->dragstartx -= (softmin - tempf) / fac;
- tempf = softmin;
- }
- else if (tempf > softmax) {
- data->dragstartx += (tempf - softmax) / fac;
- tempf = softmax;
+ switch (scale_type) {
+ case PROP_SCALE_LINEAR: {
+ if (tempf < softmin) {
+ data->dragstartx -= (softmin - tempf) / fac;
+ tempf = softmin;
+ }
+ else if (tempf > softmax) {
+ data->dragstartx -= (softmax - tempf) / fac;
+ tempf = softmax;
+ }
+ break;
+ }
+ case PROP_SCALE_LOG: {
+ if (tempf < log_min) {
+ data->dragstartx -= logf(log_min / (float)data->startvalue) / fac -
+ (float)(mx - data->dragstartx);
+ tempf = softmin;
+ }
+ else if (tempf > softmax) {
+ data->dragstartx -= logf(softmax / (float)data->startvalue) / fac -
+ (float)(mx - data->dragstartx);
+ tempf = softmax;
+ }
+ break;
+ }
+ case PROP_SCALE_CUBIC: {
+ if (tempf < softmin) {
+ data->dragstartx = mx - (int)((cbrtf(softmin) - cbrtf((float)data->startvalue)) / fac);
+ tempf = softmin;
+ }
+ else if (tempf > softmax) {
+ data->dragstartx = mx - (int)((cbrtf(softmax) - cbrtf((float)data->startvalue)) / fac);
+ tempf = softmax;
+ }
+ break;
+ }
}
#else
CLAMP(tempf, softmin, softmax);
@@ -4932,7 +5031,31 @@ static bool ui_numedit_but_NUM(uiButNumber *number_but,
}
data->draglastx = mx;
- tempf = (softmin + data->dragf * softrange);
+
+ switch (scale_type) {
+ case PROP_SCALE_LINEAR: {
+ tempf = (softmin + data->dragf * softrange);
+ break;
+ }
+ case PROP_SCALE_LOG: {
+ const float log_min = max_ff(max_ff(softmin, UI_PROP_SCALE_LOG_MIN),
+ powf(10.0f, -number_but->precision) * 0.5f);
+ const float base = softmax / log_min;
+ tempf = powf(base, data->dragf) * log_min;
+ if (tempf <= log_min) {
+ tempf = 0.0f;
+ }
+ break;
+ }
+ case PROP_SCALE_CUBIC: {
+ tempf = (softmin + data->dragf * softrange);
+ tempf *= tempf * tempf;
+ float cubic_min = softmin * softmin * softmin;
+ float cubic_max = softmax * softmax * softmax;
+ tempf = (tempf - cubic_min) / (cubic_max - cubic_min) * softrange + softmin;
+ break;
+ }
+ }
if (!is_float) {
temp = round_fl_to_int(tempf);
@@ -5179,9 +5302,19 @@ static int ui_do_but_NUM(
else {
/* Float Value. */
if (but->drawflag & (UI_BUT_ACTIVE_LEFT | UI_BUT_ACTIVE_RIGHT)) {
+ const PropertyScaleType scale_type = ui_but_scale_type(but);
+
button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
- const double value_step = (double)number_but->step_size * UI_PRECISION_FLOAT_SCALE;
+ double value_step;
+ if (scale_type == PROP_SCALE_LOG) {
+ value_step = powf(10.0f,
+ (roundf(log10f(data->value) + UI_PROP_SCALE_LOG_SNAP_OFFSET) - 1.0f) +
+ log10f(number_but->step_size));
+ }
+ else {
+ value_step = (double)number_but->step_size * UI_PRECISION_FLOAT_SCALE;
+ }
BLI_assert(value_step > 0.0f);
const double value_test = (but->drawflag & UI_BUT_ACTIVE_LEFT) ?
(double)max_ff(but->softmin,
@@ -5229,6 +5362,8 @@ static bool ui_numedit_but_SLI(uiBut *but,
return changed;
}
+ const PropertyScaleType scale_type = ui_but_scale_type(but);
+
softmin = but->softmin;
softmax = but->softmax;
softrange = softmax - softmin;
@@ -5270,7 +5405,24 @@ static bool ui_numedit_but_SLI(uiBut *but,
#endif
/* done correcting mouse */
- tempf = softmin + f * softrange;
+ switch (scale_type) {
+ case PROP_SCALE_LINEAR: {
+ tempf = softmin + f * softrange;
+ break;
+ }
+ case PROP_SCALE_LOG: {
+ tempf = powf(softmax / softmin, f) * softmin;
+ break;
+ }
+ case PROP_SCALE_CUBIC: {
+ const float cubicmin = cube_f(softmin);
+ const float cubicmax = cube_f(softmax);
+ const float cubicrange = cubicmax - cubicmin;
+ tempf = cube_f(softmin + f * softrange);
+ tempf = (tempf - cubicmin) / cubicrange * softrange + softmin;
+ break;
+ }
+ }
temp = round_fl_to_int(tempf);
if (snap) {
@@ -5464,6 +5616,8 @@ static int ui_do_but_SLI(
if (click) {
if (click == 2) {
+ const PropertyScaleType scale_type = ui_but_scale_type(but);
+
/* nudge slider to the left or right */
float f, tempf, softmin, softmax, softrange;
int temp;
@@ -5488,14 +5642,20 @@ static int ui_do_but_SLI(
f = (float)(mx - but->rect.xmin) / (BLI_rctf_size_x(&but->rect));
}
- f = softmin + f * softrange;
+ if (scale_type == PROP_SCALE_LOG) {
+ f = powf(softmax / softmin, f) * softmin;
+ }
+ else {
+ f = softmin + f * softrange;
+ }
if (!ui_but_is_float(but)) {
+ int value_step = 1;
if (f < temp) {
- temp--;
+ temp -= value_step;
}
else {
- temp++;
+ temp += value_step;
}
if (temp >= softmin && temp <= softmax) {
@@ -5506,14 +5666,23 @@ static int ui_do_but_SLI(
}
}
else {
- if (f < tempf) {
- tempf -= 0.01f;
- }
- else {
- tempf += 0.01f;
- }
-
if (tempf >= softmin && tempf <= softmax) {
+ float value_step;
+ if (scale_type == PROP_SCALE_LOG) {
+ value_step = powf(10.0f, roundf(log10f(tempf) + UI_PROP_SCALE_LOG_SNAP_OFFSET) - 1.0f);
+ }
+ else {
+ value_step = 0.01f;
+ }
+
+ if (f < tempf) {
+ tempf -= value_step;
+ }
+ else {
+ tempf += value_step;
+ }
+
+ CLAMP(tempf, softmin, softmax);
data->value = tempf;
}
else {
diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c
index c16c3d2c49a..4defbed940e 100644
--- a/source/blender/editors/interface/interface_icons.c
+++ b/source/blender/editors/interface/interface_icons.c
@@ -457,13 +457,15 @@ DEF_ICON_VECTOR_COLORSET_DRAW_NTH(20, 19)
# undef DEF_ICON_VECTOR_COLORSET_DRAW_NTH
static void vicon_collection_color_draw(
- short color_tag, int x, int y, int UNUSED(w), int UNUSED(h), float UNUSED(alpha))
+ short color_tag, int x, int y, int w, int UNUSED(h), float UNUSED(alpha))
{
bTheme *btheme = UI_GetTheme();
const ThemeCollectionColor *collection_color = &btheme->collection_color[color_tag];
+ const float aspect = (float)ICON_DEFAULT_WIDTH / (float)w;
+
UI_icon_draw_ex(
- x, y, ICON_OUTLINER_COLLECTION, U.inv_dpi_fac, 1.0f, 0.0f, collection_color->color, true);
+ x, y, ICON_OUTLINER_COLLECTION, aspect, 1.0f, 0.0f, collection_color->color, true);
}
# define DEF_ICON_COLLECTION_COLOR_DRAW(index, color) \
@@ -1426,13 +1428,7 @@ static void icon_set_image(const bContext *C,
scene = CTX_data_scene(C);
}
/* Immediate version */
- ED_preview_icon_render(CTX_data_main(C),
- CTX_data_ensure_evaluated_depsgraph(C),
- scene,
- id,
- prv_img->rect[size],
- prv_img->w[size],
- prv_img->h[size]);
+ ED_preview_icon_render(C, scene, id, prv_img->rect[size], prv_img->w[size], prv_img->h[size]);
}
}
diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h
index 4c96512b4f3..23856c41ceb 100644
--- a/source/blender/editors/interface/interface_intern.h
+++ b/source/blender/editors/interface/interface_intern.h
@@ -62,7 +62,7 @@ struct wmTimer;
#define UI_MENU_PADDING (int)(0.2f * UI_UNIT_Y)
#define UI_MENU_WIDTH_MIN (UI_UNIT_Y * 9)
-/* some extra padding added to menus containing submenu icons */
+/** Some extra padding added to menus containing sub-menu icons. */
#define UI_MENU_SUBMENU_PADDING (6 * UI_DPI_FAC)
/* menu scrolling */
@@ -74,28 +74,31 @@ struct wmTimer;
#define UI_PANEL_MINX 100
#define UI_PANEL_MINY 70
-/* popover width (multiplied by 'U.widget_unit') */
+/** Popover width (multiplied by #U.widget_unit) */
#define UI_POPOVER_WIDTH_UNITS 10
-/* uiBut->flag */
+/** #uiBut.flag */
enum {
- UI_SELECT = (1 << 0), /* use when the button is pressed */
- UI_SCROLLED = (1 << 1), /* temp hidden, scrolled away */
+ /** Use when the button is pressed. */
+ UI_SELECT = (1 << 0),
+ /** Temporarily hidden (scrolled out of the view). */
+ UI_SCROLLED = (1 << 1),
UI_ACTIVE = (1 << 2),
UI_HAS_ICON = (1 << 3),
UI_HIDDEN = (1 << 4),
- UI_SELECT_DRAW = (1 << 5), /* Display selected, doesn't impact interaction. */
+ /** Display selected, doesn't impact interaction. */
+ UI_SELECT_DRAW = (1 << 5),
/** Property search filter is active and the button does not match. */
- UI_SEARCH_FILTER_NO_MATCH = (1 << 12),
- /* warn: rest of uiBut->flag in UI_interface.h */
+ UI_SEARCH_FILTER_NO_MATCH = (1 << 6),
+ /* WARNING: rest of #uiBut.flag in UI_interface.h */
};
-/* uiBut->dragflag */
+/** #uiBut.dragflag */
enum {
UI_BUT_DRAGPOIN_FREE = (1 << 0),
};
-/* but->pie_dir */
+/** #uiBut.pie_dir */
typedef enum RadialDirection {
UI_RADIAL_NONE = -1,
UI_RADIAL_N = 0,
@@ -126,13 +129,13 @@ extern const short ui_radial_dir_to_angle[8];
#define UI_BITBUT_ROW(min, max) \
(((max) >= 31 ? 0xFFFFFFFF : (1 << ((max) + 1)) - 1) - ((min) ? ((1 << (min)) - 1) : 0))
-/* split numbuts by ':' and align l/r */
+/** Split number-buttons by ':' and align left/right. */
#define USE_NUMBUTS_LR_ALIGN
-/* Use new 'align' computation code. */
+/** Use new 'align' computation code. */
#define USE_UIBUT_SPATIAL_ALIGN
-/* PieMenuData->flags */
+/** #PieMenuData.flags */
enum {
/** pie menu item collision is detected at 90 degrees */
UI_PIE_DEGREES_RANGE_LARGE = (1 << 0),
@@ -152,13 +155,13 @@ enum {
#define PIE_CLICK_THRESHOLD_SQ 50.0f
-/* max amount of items a radial menu (pie menu) can contain */
+/** The maximum number of items a radial menu (pie menu) can contain. */
#define PIE_MAX_ITEMS 8
struct uiBut {
struct uiBut *next, *prev;
- /* Pointer back to the layout item holding this button. */
+ /** Pointer back to the layout item holding this button. */
uiLayout *layout;
int flag, drawflag;
eButType type;
@@ -235,10 +238,10 @@ struct uiBut {
short modifier_key;
short iconadd;
- /* UI_BTYPE_BLOCK data */
+ /** #UI_BTYPE_BLOCK data */
uiBlockCreateFunc block_create_func;
- /* UI_BTYPE_PULLDOWN/UI_BTYPE_MENU data */
+ /** #UI_BTYPE_PULLDOWN / #UI_BTYPE_MENU data */
uiMenuCreateFunc menu_create_func;
uiMenuStepFunc menu_step_func;
@@ -252,9 +255,11 @@ struct uiBut {
struct wmOperatorType *optype;
struct PointerRNA *opptr;
short opcontext;
- uchar menu_key; /* 'a'-'z', always lower case */
- ListBase extra_op_icons; /* uiButExtraOpIcon */
+ /** When non-zero, this is the key used to activate a menu items (`a-z` always lower case). */
+ uchar menu_key;
+
+ ListBase extra_op_icons; /** #uiButExtraOpIcon */
/* Draggable data, type is WM_DRAG_... */
char dragtype;
@@ -263,10 +268,10 @@ struct uiBut {
struct ImBuf *imb;
float imb_scale;
- /* active button data */
+ /** Active button data (set when the user is hovering or interacting with a button). */
struct uiHandleButtonData *active;
- /* Custom button data. */
+ /** Custom button data (borrowed, not owned). */
void *custom_data;
char *editstr;
@@ -429,7 +434,7 @@ struct PieMenuData {
float alphafac;
};
-/* uiBlock.content_hints */
+/** #uiBlock.content_hints */
enum eBlockContentHints {
/** In a menu block, if there is a single sub-menu button, we add some
* padding to the right to put nicely aligned triangle icons there. */
@@ -463,7 +468,8 @@ struct uiBlock {
struct Panel *panel;
uiBlock *oldblock;
- ListBase butstore; /* UI_butstore_* runtime function */
+ /** Used for `UI_butstore_*` runtime function. */
+ ListBase butstore;
ListBase button_groups; /* #uiButtonGroup. */
@@ -479,7 +485,8 @@ struct uiBlock {
rctf rect;
float aspect;
- uint puphash; /* popup menu hash for memory */
+ /** Unique hash used to implement popup menu memory. */
+ uint puphash;
uiButHandleFunc func;
void *func_arg1;
@@ -494,10 +501,10 @@ struct uiBlock {
uiBlockHandleFunc handle_func;
void *handle_func_arg;
- /* custom extra handling */
+ /** Custom extra event handling. */
int (*block_event_func)(const struct bContext *C, struct uiBlock *, const struct wmEvent *);
- /* extra draw function for custom blocks */
+ /** Custom extra draw function for custom blocks. */
void (*drawextra)(const struct bContext *C, void *idv, void *arg1, void *arg2, rcti *rect);
void *drawextra_arg1;
void *drawextra_arg2;
@@ -507,7 +514,7 @@ struct uiBlock {
/** Hints about the buttons of this block. Used to avoid iterating over
* buttons to find out if some criteria is met by any. Instead, check this
* criteria when adding the button and set a flag here if it's met. */
- short content_hints; /* eBlockContentHints */
+ short content_hints; /* #eBlockContentHints */
char direction;
/** UI_BLOCK_THEME_STYLE_* */
@@ -521,11 +528,11 @@ struct uiBlock {
const char *lockstr;
bool lock;
- /** to keep blocks while drawing and free them afterwards */
+ /** To keep blocks while drawing and free them afterwards. */
bool active;
- /** to avoid tooltip after click */
+ /** To avoid tool-tip after click. */
bool tooltipdisabled;
- /** UI_block_end done? */
+ /** True when #UI_block_end has been called. */
bool endblock;
/** for doing delayed */
@@ -535,12 +542,12 @@ struct uiBlock {
/** for doing delayed */
int bounds, minbounds;
- /** pull-downs, to detect outside, can differ per case how it is created. */
+ /** Pull-downs, to detect outside, can differ per case how it is created. */
rctf safety;
- /** uiSafetyRct list */
+ /** #uiSafetyRct list */
ListBase saferct;
- uiPopupBlockHandle *handle; /* handle */
+ uiPopupBlockHandle *handle;
/** use so presets can find the operator,
* across menus and from nested popups which fail for operator context. */
@@ -555,10 +562,12 @@ struct uiBlock {
/** \note only accessed by color picker templates. */
ColorPickerData color_pickers;
- bool is_color_gamma_picker; /* Block for color picker with gamma baked in. */
+ /** Block for color picker with gamma baked in. */
+ bool is_color_gamma_picker;
- /** display device name used to display this block,
- * used by color widgets to transform colors from/to scene linear
+ /**
+ * Display device name used to display this block,
+ * used by color widgets to transform colors from/to scene linear.
*/
char display_device[64];
@@ -651,6 +660,7 @@ bool ui_but_context_poll_operator(struct bContext *C, struct wmOperatorType *ot,
extern void ui_but_update(uiBut *but);
extern void ui_but_update_edited(uiBut *but);
+extern PropertyScaleType ui_but_scale_type(const uiBut *but) ATTR_WARN_UNUSED_RESULT;
extern bool ui_but_is_float(const uiBut *but) ATTR_WARN_UNUSED_RESULT;
extern bool ui_but_is_bool(const uiBut *but) ATTR_WARN_UNUSED_RESULT;
extern bool ui_but_is_unit(const uiBut *but) ATTR_WARN_UNUSED_RESULT;
@@ -671,9 +681,9 @@ void ui_block_cm_to_display_space_v3(uiBlock *block, float pixel[3]);
/* interface_regions.c */
struct uiKeyNavLock {
- /* Set when we're using key-input. */
+ /** Set when we're using keyboard-input. */
bool is_keynav;
- /* only used to check if we've moved the cursor */
+ /** Only used to check if we've moved the cursor. */
int event_xy[2];
};
@@ -689,7 +699,7 @@ struct uiPopupBlockCreate {
int event_xy[2];
- /* when popup is initialized from a button */
+ /** Set when popup is initialized from a button. */
struct ARegion *butregion;
uiBut *but;
};
@@ -698,7 +708,7 @@ struct uiPopupBlockHandle {
/* internal */
struct ARegion *region;
- /* use only for 'UI_BLOCK_MOVEMOUSE_QUIT' popups */
+ /** Use only for #UI_BLOCK_MOVEMOUSE_QUIT popups. */
float towards_xy[2];
double towardstime;
bool dotowards;
@@ -708,9 +718,9 @@ struct uiPopupBlockHandle {
void (*cancel_func)(struct bContext *C, void *arg);
void *popup_arg;
- /* store data for refreshing popups */
+ /** Store data for refreshing popups. */
struct uiPopupBlockCreate popup_create_vars;
- /* true if we can re-create the popup using 'popup_create_vars' */
+ /** True if we can re-create the popup using #uiPopupBlockHandle.popup_create_vars. */
bool can_refresh;
bool refresh;
@@ -730,7 +740,7 @@ struct uiPopupBlockHandle {
int retvalue;
float retvec[4];
- /* menu direction */
+ /** Menu direction. */
int direction;
/* Previous values so we don't resize or reposition on refresh. */
@@ -924,9 +934,7 @@ extern void ui_but_execute_end(struct bContext *C,
void *active_back);
extern void ui_but_active_free(const struct bContext *C, uiBut *but);
extern int ui_but_menu_direction(uiBut *but);
-extern void ui_but_text_password_hide(char password_str[UI_MAX_DRAW_STR],
- uiBut *but,
- const bool restore);
+extern void ui_but_text_password_hide(char password_str[128], uiBut *but, const bool restore);
extern uiBut *ui_but_find_select_in_enum(uiBut *but, int direction);
bool ui_but_is_editing(const uiBut *but);
float ui_block_calc_pie_segment(struct uiBlock *block, const float event_xy[2]);
diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c
index 4430d00f2e3..c04432a2912 100644
--- a/source/blender/editors/interface/interface_layout.c
+++ b/source/blender/editors/interface/interface_layout.c
@@ -2718,6 +2718,7 @@ uiBut *ui_but_add_search(
ui_searchbox_create_generic,
ui_rna_collection_search_update_fn,
coll_search,
+ false,
ui_rna_collection_search_arg_free_fn,
NULL,
NULL);
@@ -3037,7 +3038,7 @@ void uiItemDecoratorR(uiLayout *layout, PointerRNA *ptr, const char *propname, i
/* popover */
void uiItemPopoverPanel_ptr(
- uiLayout *layout, bContext *C, PanelType *pt, const char *name, int icon)
+ uiLayout *layout, const bContext *C, PanelType *pt, const char *name, int icon)
{
if (!name) {
name = CTX_IFACE_(pt->translation_context, pt->label);
@@ -3066,7 +3067,7 @@ void uiItemPopoverPanel_ptr(
}
void uiItemPopoverPanel(
- uiLayout *layout, bContext *C, const char *panel_type, const char *name, int icon)
+ uiLayout *layout, const bContext *C, const char *panel_type, const char *name, int icon)
{
PanelType *pt = WM_paneltype_find(panel_type, true);
if (pt == NULL) {
@@ -4041,12 +4042,11 @@ static void ui_litem_layout_column_flow(uiLayout *litem)
int emy = 0;
int miny = 0;
- int w = litem->w - (flow->totcol - 1) * style->columnspace;
emh = toth / flow->totcol;
/* create column per column */
col = 0;
- w = (litem->w - (flow->totcol - 1) * style->columnspace) / flow->totcol;
+ int w = (litem->w - (flow->totcol - 1) * style->columnspace) / flow->totcol;
LISTBASE_FOREACH (uiItem *, item, &litem->items) {
ui_item_size(item, &itemw, &itemh);
diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c
index 540e98f542e..0cf3ad59903 100644
--- a/source/blender/editors/interface/interface_ops.c
+++ b/source/blender/editors/interface/interface_ops.c
@@ -72,6 +72,7 @@
#include "BKE_main.h"
#include "BLI_ghash.h"
#include "ED_screen.h"
+#include "ED_text.h"
/* -------------------------------------------------------------------- */
/** \name Copy Data Path Operator
@@ -1336,18 +1337,23 @@ static int editsource_text_edit(bContext *C,
return OPERATOR_CANCELLED;
}
+ txt_move_toline(text, line - 1, false);
+
/* naughty!, find text area to set, not good behavior
* but since this is a dev tool lets allow it - campbell */
ScrArea *area = BKE_screen_find_big_area(CTX_wm_screen(C), SPACE_TEXT, 0);
if (area) {
SpaceText *st = area->spacedata.first;
+ ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW);
st->text = text;
+ if (region) {
+ ED_text_scroll_to_cursor(st, region, true);
+ }
}
else {
BKE_reportf(op->reports, RPT_INFO, "See '%s' in the text editor", text->id.name + 2);
}
- txt_move_toline(text, line - 1, false);
WM_event_add_notifier(C, NC_TEXT | ND_CURSOR, text);
return OPERATOR_FINISHED;
@@ -1374,7 +1380,9 @@ static int editsource_exec(bContext *C, wmOperator *op)
/* redraw and get active button python info */
ED_region_do_layout(C, region);
+ WM_draw_region_viewport_bind(region);
ED_region_do_draw(C, region);
+ WM_draw_region_viewport_unbind(region);
region->do_draw = false;
for (BLI_ghashIterator_init(&ghi, ui_editsource_info->hash);
@@ -1487,117 +1495,115 @@ static void edittranslation_find_po_file(const char *root,
static int edittranslation_exec(bContext *C, wmOperator *op)
{
uiBut *but = UI_context_active_but_get(C);
- int ret = OPERATOR_CANCELLED;
-
- if (but) {
- wmOperatorType *ot;
- PointerRNA ptr;
- char popath[FILE_MAX];
- const char *root = U.i18ndir;
- const char *uilng = BLT_lang_get();
-
- uiStringInfo but_label = {BUT_GET_LABEL, NULL};
- uiStringInfo rna_label = {BUT_GET_RNA_LABEL, NULL};
- uiStringInfo enum_label = {BUT_GET_RNAENUM_LABEL, NULL};
- uiStringInfo but_tip = {BUT_GET_TIP, NULL};
- uiStringInfo rna_tip = {BUT_GET_RNA_TIP, NULL};
- uiStringInfo enum_tip = {BUT_GET_RNAENUM_TIP, NULL};
- uiStringInfo rna_struct = {BUT_GET_RNASTRUCT_IDENTIFIER, NULL};
- uiStringInfo rna_prop = {BUT_GET_RNAPROP_IDENTIFIER, NULL};
- uiStringInfo rna_enum = {BUT_GET_RNAENUM_IDENTIFIER, NULL};
- uiStringInfo rna_ctxt = {BUT_GET_RNA_LABEL_CONTEXT, NULL};
-
- if (!BLI_is_dir(root)) {
- BKE_report(op->reports,
- RPT_ERROR,
- "Please set your Preferences' 'Translation Branches "
- "Directory' path to a valid directory");
- return OPERATOR_CANCELLED;
- }
- ot = WM_operatortype_find(EDTSRC_I18N_OP_NAME, 0);
- if (ot == NULL) {
- BKE_reportf(op->reports,
- RPT_ERROR,
- "Could not find operator '%s'! Please enable ui_translate add-on "
- "in the User Preferences",
- EDTSRC_I18N_OP_NAME);
- return OPERATOR_CANCELLED;
- }
- /* Try to find a valid po file for current language... */
- edittranslation_find_po_file(root, uilng, popath, FILE_MAX);
- /* printf("po path: %s\n", popath); */
- if (popath[0] == '\0') {
- BKE_reportf(
- op->reports, RPT_ERROR, "No valid po found for language '%s' under %s", uilng, root);
- return OPERATOR_CANCELLED;
- }
-
- UI_but_string_info_get(C,
- but,
- &but_label,
- &rna_label,
- &enum_label,
- &but_tip,
- &rna_tip,
- &enum_tip,
- &rna_struct,
- &rna_prop,
- &rna_enum,
- &rna_ctxt,
- NULL);
-
- WM_operator_properties_create_ptr(&ptr, ot);
- RNA_string_set(&ptr, "lang", uilng);
- RNA_string_set(&ptr, "po_file", popath);
- RNA_string_set(&ptr, "but_label", but_label.strinfo);
- RNA_string_set(&ptr, "rna_label", rna_label.strinfo);
- RNA_string_set(&ptr, "enum_label", enum_label.strinfo);
- RNA_string_set(&ptr, "but_tip", but_tip.strinfo);
- RNA_string_set(&ptr, "rna_tip", rna_tip.strinfo);
- RNA_string_set(&ptr, "enum_tip", enum_tip.strinfo);
- RNA_string_set(&ptr, "rna_struct", rna_struct.strinfo);
- RNA_string_set(&ptr, "rna_prop", rna_prop.strinfo);
- RNA_string_set(&ptr, "rna_enum", rna_enum.strinfo);
- RNA_string_set(&ptr, "rna_ctxt", rna_ctxt.strinfo);
- ret = WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr);
-
- /* Clean up */
- if (but_label.strinfo) {
- MEM_freeN(but_label.strinfo);
- }
- if (rna_label.strinfo) {
- MEM_freeN(rna_label.strinfo);
- }
- if (enum_label.strinfo) {
- MEM_freeN(enum_label.strinfo);
- }
- if (but_tip.strinfo) {
- MEM_freeN(but_tip.strinfo);
- }
- if (rna_tip.strinfo) {
- MEM_freeN(rna_tip.strinfo);
- }
- if (enum_tip.strinfo) {
- MEM_freeN(enum_tip.strinfo);
- }
- if (rna_struct.strinfo) {
- MEM_freeN(rna_struct.strinfo);
- }
- if (rna_prop.strinfo) {
- MEM_freeN(rna_prop.strinfo);
- }
- if (rna_enum.strinfo) {
- MEM_freeN(rna_enum.strinfo);
- }
- if (rna_ctxt.strinfo) {
- MEM_freeN(rna_ctxt.strinfo);
- }
+ if (but == NULL) {
+ BKE_report(op->reports, RPT_ERROR, "Active button not found");
+ return OPERATOR_CANCELLED;
+ }
- return ret;
+ wmOperatorType *ot;
+ PointerRNA ptr;
+ char popath[FILE_MAX];
+ const char *root = U.i18ndir;
+ const char *uilng = BLT_lang_get();
+
+ uiStringInfo but_label = {BUT_GET_LABEL, NULL};
+ uiStringInfo rna_label = {BUT_GET_RNA_LABEL, NULL};
+ uiStringInfo enum_label = {BUT_GET_RNAENUM_LABEL, NULL};
+ uiStringInfo but_tip = {BUT_GET_TIP, NULL};
+ uiStringInfo rna_tip = {BUT_GET_RNA_TIP, NULL};
+ uiStringInfo enum_tip = {BUT_GET_RNAENUM_TIP, NULL};
+ uiStringInfo rna_struct = {BUT_GET_RNASTRUCT_IDENTIFIER, NULL};
+ uiStringInfo rna_prop = {BUT_GET_RNAPROP_IDENTIFIER, NULL};
+ uiStringInfo rna_enum = {BUT_GET_RNAENUM_IDENTIFIER, NULL};
+ uiStringInfo rna_ctxt = {BUT_GET_RNA_LABEL_CONTEXT, NULL};
+
+ if (!BLI_is_dir(root)) {
+ BKE_report(op->reports,
+ RPT_ERROR,
+ "Please set your Preferences' 'Translation Branches "
+ "Directory' path to a valid directory");
+ return OPERATOR_CANCELLED;
+ }
+ ot = WM_operatortype_find(EDTSRC_I18N_OP_NAME, 0);
+ if (ot == NULL) {
+ BKE_reportf(op->reports,
+ RPT_ERROR,
+ "Could not find operator '%s'! Please enable ui_translate add-on "
+ "in the User Preferences",
+ EDTSRC_I18N_OP_NAME);
+ return OPERATOR_CANCELLED;
+ }
+ /* Try to find a valid po file for current language... */
+ edittranslation_find_po_file(root, uilng, popath, FILE_MAX);
+ /* printf("po path: %s\n", popath); */
+ if (popath[0] == '\0') {
+ BKE_reportf(
+ op->reports, RPT_ERROR, "No valid po found for language '%s' under %s", uilng, root);
+ return OPERATOR_CANCELLED;
}
- BKE_report(op->reports, RPT_ERROR, "Active button not found");
- return OPERATOR_CANCELLED;
+ UI_but_string_info_get(C,
+ but,
+ &but_label,
+ &rna_label,
+ &enum_label,
+ &but_tip,
+ &rna_tip,
+ &enum_tip,
+ &rna_struct,
+ &rna_prop,
+ &rna_enum,
+ &rna_ctxt,
+ NULL);
+
+ WM_operator_properties_create_ptr(&ptr, ot);
+ RNA_string_set(&ptr, "lang", uilng);
+ RNA_string_set(&ptr, "po_file", popath);
+ RNA_string_set(&ptr, "but_label", but_label.strinfo);
+ RNA_string_set(&ptr, "rna_label", rna_label.strinfo);
+ RNA_string_set(&ptr, "enum_label", enum_label.strinfo);
+ RNA_string_set(&ptr, "but_tip", but_tip.strinfo);
+ RNA_string_set(&ptr, "rna_tip", rna_tip.strinfo);
+ RNA_string_set(&ptr, "enum_tip", enum_tip.strinfo);
+ RNA_string_set(&ptr, "rna_struct", rna_struct.strinfo);
+ RNA_string_set(&ptr, "rna_prop", rna_prop.strinfo);
+ RNA_string_set(&ptr, "rna_enum", rna_enum.strinfo);
+ RNA_string_set(&ptr, "rna_ctxt", rna_ctxt.strinfo);
+ const int ret = WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr);
+
+ /* Clean up */
+ if (but_label.strinfo) {
+ MEM_freeN(but_label.strinfo);
+ }
+ if (rna_label.strinfo) {
+ MEM_freeN(rna_label.strinfo);
+ }
+ if (enum_label.strinfo) {
+ MEM_freeN(enum_label.strinfo);
+ }
+ if (but_tip.strinfo) {
+ MEM_freeN(but_tip.strinfo);
+ }
+ if (rna_tip.strinfo) {
+ MEM_freeN(rna_tip.strinfo);
+ }
+ if (enum_tip.strinfo) {
+ MEM_freeN(enum_tip.strinfo);
+ }
+ if (rna_struct.strinfo) {
+ MEM_freeN(rna_struct.strinfo);
+ }
+ if (rna_prop.strinfo) {
+ MEM_freeN(rna_prop.strinfo);
+ }
+ if (rna_enum.strinfo) {
+ MEM_freeN(rna_enum.strinfo);
+ }
+ if (rna_ctxt.strinfo) {
+ MEM_freeN(rna_ctxt.strinfo);
+ }
+
+ return ret;
}
static void UI_OT_edittranslation_init(wmOperatorType *ot)
diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c
index 7343417137a..6505a7cd76a 100644
--- a/source/blender/editors/interface/interface_panel.c
+++ b/source/blender/editors/interface/interface_panel.c
@@ -437,15 +437,21 @@ static void reorder_instanced_panel_list(bContext *C, ARegion *region, Panel *dr
/* Find how many instanced panels with this context string. */
int list_panels_len = 0;
+ int start_index = -1;
LISTBASE_FOREACH (const Panel *, panel, &region->panels) {
if (panel->type) {
if (panel->type->flag & PANEL_TYPE_INSTANCED) {
if (panel_type_context_poll(region, panel->type, context)) {
+ if (panel == drag_panel) {
+ BLI_assert(start_index == -1); /* This panel should only appear once. */
+ start_index = list_panels_len;
+ }
list_panels_len++;
}
}
}
}
+ BLI_assert(start_index != -1); /* The drag panel should definitely be in the list. */
/* Sort the matching instanced panels by their display order. */
PanelSort *panel_sort = MEM_callocN(list_panels_len * sizeof(*panel_sort), __func__);
@@ -472,6 +478,11 @@ static void reorder_instanced_panel_list(bContext *C, ARegion *region, Panel *dr
MEM_freeN(panel_sort);
+ if (move_to_index == start_index) {
+ /* In this case, the reorder was not changed, so don't do any updates or call the callback. */
+ return;
+ }
+
/* Set the bit to tell the interface to instanced the list. */
drag_panel->flag |= PNL_INSTANCED_LIST_ORDER_CHANGED;
@@ -2529,9 +2540,8 @@ PointerRNA *UI_region_panel_custom_data_under_cursor(const bContext *C, const wm
{
ARegion *region = CTX_wm_region(C);
- Panel *panel = NULL;
LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
- panel = block->panel;
+ Panel *panel = block->panel;
if (panel == NULL) {
continue;
}
@@ -2541,15 +2551,11 @@ PointerRNA *UI_region_panel_custom_data_under_cursor(const bContext *C, const wm
ui_window_to_block(region, block, &mx, &my);
const int mouse_state = ui_panel_mouse_state_get(block, panel, mx, my);
if (ELEM(mouse_state, PANEL_MOUSE_INSIDE_CONTENT, PANEL_MOUSE_INSIDE_HEADER)) {
- break;
+ return UI_panel_custom_data_get(panel);
}
}
- if (panel == NULL) {
- return NULL;
- }
-
- return UI_panel_custom_data_get(panel);
+ return NULL;
}
/** \} */
diff --git a/source/blender/editors/interface/interface_region_menu_popup.c b/source/blender/editors/interface/interface_region_menu_popup.c
index f234f0fbbf5..58a74a3473e 100644
--- a/source/blender/editors/interface/interface_region_menu_popup.c
+++ b/source/blender/editors/interface/interface_region_menu_popup.c
@@ -86,6 +86,17 @@ int ui_but_menu_step(uiBut *but, int direction)
return 0;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Popup Menu Memory
+ *
+ * Support menu-memory, a feature that positions the cursor
+ * over the previously used menu item.
+ *
+ * \note This is stored for each unique menu title.
+ * \{ */
+
static uint ui_popup_string_hash(const char *str, const bool use_sep)
{
/* sometimes button contains hotkey, sometimes not, strip for proper compare */
@@ -392,7 +403,7 @@ uiPopupMenu *UI_popup_menu_begin_ex(bContext *C,
pup->layout = UI_block_layout(
pup->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_MENU, 0, 0, 200, 0, UI_MENU_PADDING, style);
- /* note, this intentionally differs from the menu & submenu default because many operators
+ /* note, this intentionally differs from the menu & sub-menu default because many operators
* use popups like this to select one of their options -
* where having invoke doesn't make sense */
uiLayoutSetOperatorContext(pup->layout, WM_OP_EXEC_REGION_WIN);
diff --git a/source/blender/editors/interface/interface_region_search.c b/source/blender/editors/interface/interface_region_search.c
index 12044863b8c..987cde61f97 100644
--- a/source/blender/editors/interface/interface_region_search.c
+++ b/source/blender/editors/interface/interface_region_search.c
@@ -677,8 +677,7 @@ static void ui_searchbox_region_draw_cb(const bContext *C, ARegion *region)
}
/* The previous menu item draws the active selection. */
- ui_draw_menu_item(
- &data->fstyle, &rect, name_sep, icon, state & ~UI_ACTIVE, separator_type, NULL);
+ ui_draw_menu_item(&data->fstyle, &rect, name_sep, icon, state, separator_type, NULL);
}
}
/* indicate more */
diff --git a/source/blender/editors/interface/interface_region_tooltip.c b/source/blender/editors/interface/interface_region_tooltip.c
index 244cf7622e0..accfb78ab94 100644
--- a/source/blender/editors/interface/interface_region_tooltip.c
+++ b/source/blender/editors/interface/interface_region_tooltip.c
@@ -428,7 +428,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
if (has_valid_context == false) {
expr_result = BLI_strdup(has_valid_context_error);
}
- else if (BPY_run_string_as_string(C, expr_imports, expr, __func__, &expr_result)) {
+ else if (BPY_run_string_as_string(C, expr_imports, expr, NULL, &expr_result)) {
if (STREQ(expr_result, "")) {
MEM_freeN(expr_result);
expr_result = NULL;
@@ -485,7 +485,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
if (has_valid_context == false) {
expr_result = BLI_strdup(has_valid_context_error);
}
- else if (BPY_run_string_as_string(C, expr_imports, expr, __func__, &expr_result)) {
+ else if (BPY_run_string_as_string(C, expr_imports, expr, NULL, &expr_result)) {
if (STREQ(expr_result, ".")) {
MEM_freeN(expr_result);
expr_result = NULL;
@@ -589,7 +589,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
if (has_valid_context == false) {
shortcut = BLI_strdup(has_valid_context_error);
}
- else if (BPY_run_string_as_intptr(C, expr_imports, expr, __func__, &expr_result)) {
+ else if (BPY_run_string_as_intptr(C, expr_imports, expr, NULL, &expr_result)) {
if (expr_result != 0) {
wmKeyMap *keymap = (wmKeyMap *)expr_result;
LISTBASE_FOREACH (wmKeyMapItem *, kmi, &keymap->items) {
@@ -654,7 +654,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
/* pass */
}
else if (BPY_run_string_as_string_and_size(
- C, expr_imports, expr, __func__, &expr_result, &expr_result_len)) {
+ C, expr_imports, expr, NULL, &expr_result, &expr_result_len)) {
/* pass. */
}
}
@@ -731,7 +731,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
if (has_valid_context == false) {
/* pass */
}
- else if (BPY_run_string_as_intptr(C, expr_imports, expr, __func__, &expr_result)) {
+ else if (BPY_run_string_as_intptr(C, expr_imports, expr, NULL, &expr_result)) {
if (expr_result != 0) {
{
uiTooltipField *field = text_field_add(data,
@@ -947,12 +947,13 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but)
/* button is disabled, we may be able to tell user why */
if (but->flag & UI_BUT_DISABLED) {
const char *disabled_msg = NULL;
+ bool disabled_msg_free = false;
/* if operator poll check failed, it can give pretty precise info why */
if (but->optype) {
- CTX_wm_operator_poll_msg_set(C, NULL);
+ CTX_wm_operator_poll_msg_clear(C);
WM_operator_poll_context(C, but->optype, but->opcontext);
- disabled_msg = CTX_wm_operator_poll_msg_get(C);
+ disabled_msg = CTX_wm_operator_poll_msg_get(C, &disabled_msg_free);
}
/* alternatively, buttons can store some reasoning too */
else if (but->disabled_info) {
@@ -967,6 +968,9 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but)
});
field->text = BLI_sprintfN(TIP_("Disabled: %s"), disabled_msg);
}
+ if (disabled_msg_free) {
+ MEM_freeN((void *)disabled_msg);
+ }
}
if ((U.flag & USER_TOOLTIPS_PYTHON) && !but->optype && rna_struct.strinfo) {
@@ -1469,9 +1473,10 @@ ARegion *UI_tooltip_create_from_gizmo(bContext *C, wmGizmo *gz)
*/
if (gz->type->screen_bounds_get) {
rcti bounds;
- gz->type->screen_bounds_get(C, gz, &bounds);
- init_position[0] = bounds.xmin;
- init_position[1] = bounds.ymin;
+ if (gz->type->screen_bounds_get(C, gz, &bounds)) {
+ init_position[0] = bounds.xmin;
+ init_position[1] = bounds.ymin;
+ }
}
return ui_tooltip_create_with_data(C, data, init_position, NULL, aspect);
diff --git a/source/blender/editors/interface/interface_template_search_menu.c b/source/blender/editors/interface/interface_template_search_menu.c
index ff42d434f29..91ad6619889 100644
--- a/source/blender/editors/interface/interface_template_search_menu.c
+++ b/source/blender/editors/interface/interface_template_search_menu.c
@@ -510,11 +510,19 @@ static struct MenuSearch_Data *menu_items_from_ui_create(
const char *global_menu_prefix = NULL;
if (include_all_areas) {
+ bScreen *screen = WM_window_get_active_screen(win);
+
/* First create arrays for ui_type. */
PropertyRNA *prop_ui_type = NULL;
{
+ /* This must be a valid pointer, with only it's type checked. */
+ ScrArea area_dummy = {
+ /* Anything besides #SPACE_EMPTY is fine,
+ * as this value is only included in the enum when set. */
+ .spacetype = SPACE_TOPBAR,
+ };
PointerRNA ptr;
- RNA_pointer_create(NULL, &RNA_Area, NULL, &ptr);
+ RNA_pointer_create(&screen->id, &RNA_Area, &area_dummy, &ptr);
prop_ui_type = RNA_struct_find_property(&ptr, "ui_type");
RNA_property_enum_items(C,
&ptr,
@@ -529,7 +537,6 @@ static struct MenuSearch_Data *menu_items_from_ui_create(
}
}
- bScreen *screen = WM_window_get_active_screen(win);
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW);
if (region != NULL) {
@@ -1141,6 +1148,7 @@ void UI_but_func_menu_search(uiBut *but)
ui_searchbox_create_menu,
menu_search_update_fn,
data,
+ false,
menu_search_arg_free_fn,
menu_search_exec_fn,
NULL);
diff --git a/source/blender/editors/interface/interface_template_search_operator.c b/source/blender/editors/interface/interface_template_search_operator.c
index 2c83f184ff0..2b765a1a2f5 100644
--- a/source/blender/editors/interface/interface_template_search_operator.c
+++ b/source/blender/editors/interface/interface_template_search_operator.c
@@ -121,6 +121,7 @@ void UI_but_func_operator_search(uiBut *but)
operator_search_update_fn,
NULL,
false,
+ NULL,
operator_search_exec_fn,
NULL);
}
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index 2d1663a3ecd..3990ad68c4d 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -309,6 +309,7 @@ static uiBlock *template_common_search_menu(const bContext *C,
ui_searchbox_create_generic,
search_update_fn,
search_arg,
+ false,
NULL,
search_exec_fn,
active_item);
@@ -652,7 +653,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
/* Only remap that specific ID usage to overriding local data-block. */
ID *override_id = BKE_lib_override_library_create_from_id(bmain, id, false);
if (override_id != NULL) {
- BKE_main_id_clear_newpoins(bmain);
+ BKE_main_id_newptr_and_tag_clear(bmain);
if (GS(override_id->name) == ID_OB) {
Scene *scene = CTX_data_scene(C);
@@ -671,7 +672,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
}
else {
if (BKE_lib_id_make_local(bmain, id, false, 0)) {
- BKE_main_id_clear_newpoins(bmain);
+ BKE_main_id_newptr_and_tag_clear(bmain);
/* reassign to get get proper updates/notifiers */
idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop);
@@ -1077,7 +1078,7 @@ static void template_ID(const bContext *C,
char numstr[32];
short numstr_len;
- numstr_len = BLI_snprintf(numstr, sizeof(numstr), "%d", ID_REAL_USERS(id));
+ numstr_len = BLI_snprintf_rlen(numstr, sizeof(numstr), "%d", ID_REAL_USERS(id));
but = uiDefBut(
block,
@@ -1109,7 +1110,7 @@ static void template_ID(const bContext *C,
UI_but_flag_enable(but, UI_BUT_REDALERT);
}
- if (id->lib == NULL && !(ELEM(GS(id->name), ID_GR, ID_SCE, ID_SCR, ID_TXT, ID_OB, ID_WS)) &&
+ if (id->lib == NULL && !(ELEM(GS(id->name), ID_GR, ID_SCE, ID_SCR, ID_OB, ID_WS)) &&
(hide_buttons == false)) {
uiDefIconButR(block,
UI_BTYPE_ICON_TOGGLE,
@@ -6829,6 +6830,7 @@ void uiTemplateReportsBanner(uiLayout *layout, bContext *C)
uiLayout *ui_abs = uiLayoutAbsolute(layout, false);
uiBlock *block = uiLayoutGetBlock(ui_abs);
+ eUIEmbossType previous_emboss = UI_block_emboss_get(block);
UI_fontstyle_set(&style->widgetlabel);
int width = BLF_width(style->widgetlabel.uifont_id, report->message, report->len);
@@ -6903,6 +6905,8 @@ void uiTemplateReportsBanner(uiLayout *layout, bContext *C)
width + UI_UNIT_X,
UI_UNIT_Y,
"Show in Info Log");
+
+ UI_block_emboss_set(block, previous_emboss);
}
void uiTemplateInputStatus(uiLayout *layout, struct bContext *C)
@@ -7191,7 +7195,7 @@ void uiTemplateComponentMenu(uiLayout *layout,
/** \name Node Socket Icon Template
* \{ */
-void uiTemplateNodeSocket(uiLayout *layout, bContext *UNUSED(C), float *color)
+void uiTemplateNodeSocket(uiLayout *layout, bContext *UNUSED(C), float color[4])
{
uiBlock *block = uiLayoutGetBlock(layout);
UI_block_align_begin(block);
diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c
index 0c6be7b1196..fe6a8b0d1a6 100644
--- a/source/blender/editors/interface/interface_widgets.c
+++ b/source/blender/editors/interface/interface_widgets.c
@@ -1225,12 +1225,6 @@ static bool draw_widgetbase_batch_skip_draw_cache(void)
return true;
}
- /* There are also reports that some AMD and Mesa driver configuration suffer from the
- * same issue, T78803. */
- if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_UNIX, GPU_DRIVER_OPENSOURCE)) {
- return true;
- }
-
return false;
}
@@ -1936,19 +1930,27 @@ static void widget_draw_text_ime_underline(const uiFontStyle *fstyle,
}
#endif /* WITH_INPUT_IME */
-static bool widget_draw_text_underline_calc_center_x(const char *UNUSED(str),
+struct UnderlineData {
+ size_t str_offset; /* The string offset of the underlined character. */
+ int width_px; /* The underline width in pixels. */
+ int r_offset_px[2]; /* Write the X,Y offset here. */
+};
+
+static bool widget_draw_text_underline_calc_position(const char *UNUSED(str),
const size_t str_step_ofs,
const rcti *glyph_step_bounds,
const int UNUSED(glyph_advance_x),
const rctf *glyph_bounds,
- const int glyph_bearing[2],
+ const int UNUSED(glyph_bearing[2]),
void *user_data)
{
- /* The index of the character to get, set to the x-position. */
- int *ul_data = user_data;
- if (ul_data[0] == (int)str_step_ofs) {
- ul_data[1] = glyph_step_bounds->xmin + glyph_bearing[0] +
- (BLI_rctf_size_x(glyph_bounds) / 2.0f);
+ struct UnderlineData *ul_data = user_data;
+ if (ul_data->str_offset == str_step_ofs) {
+ /* Full width of this glyph including both bearings. */
+ const float width = glyph_bounds->xmin + BLI_rctf_size_x(glyph_bounds) + glyph_bounds->xmin;
+ ul_data->r_offset_px[0] = glyph_step_bounds->xmin + ((width - ul_data->width_px) * 0.5f);
+ /* Two line-widths below the lower glyph bounds. */
+ ul_data->r_offset_px[1] = glyph_bounds->ymin - U.pixelsize - U.pixelsize;
/* Early exit. */
return false;
}
@@ -2204,23 +2206,30 @@ static void widget_draw_text(const uiFontStyle *fstyle,
BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
}
- int ul_data[2] = {
- ul_index, /* Character index to test. */
- 0, /* Write the x-offset here. */
+ int ul_width = round_fl_to_int(BLF_width(fstyle->uifont_id, "_", 2));
+ int ul_height = max_ii(fstyle->points * U.dpi_fac * 0.1f, U.pixelsize);
+
+ struct UnderlineData ul_data = {
+ .str_offset = ul_index,
+ .width_px = ul_width,
};
+
BLF_boundbox_foreach_glyph(fstyle->uifont_id,
drawstr_ofs,
ul_index + 1,
- widget_draw_text_underline_calc_center_x,
- ul_data);
- ul_data[1] -= BLF_width(fstyle->uifont_id, "_", 2) / 2.0f;
-
- BLF_position(fstyle->uifont_id,
- rect->xmin + font_xofs + ul_data[1],
- rect->ymin + font_yofs,
- 0.0f);
- BLF_color4ubv(fstyle->uifont_id, wcol->text);
- BLF_draw(fstyle->uifont_id, "_", 2);
+ widget_draw_text_underline_calc_position,
+ &ul_data);
+
+ GPU_blend(GPU_BLEND_ALPHA);
+ const uint pos = GPU_vertformat_attr_add(
+ immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
+ immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immUniformColor4ubv(wcol->text);
+ const int pos_x = rect->xmin + font_xofs + ul_data.r_offset_px[0];
+ const int pos_y = rect->ymin + font_yofs + ul_data.r_offset_px[1];
+ immRecti(pos, pos_x, pos_y, pos_x + ul_width, pos_y - ul_height);
+ immUnbindProgram();
+ GPU_blend(GPU_BLEND_NONE);
if (fstyle->kerning == 1) {
BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
@@ -2422,11 +2431,12 @@ static void widget_draw_text_icon(const uiFontStyle *fstyle,
but->block->aspect = aspect_orig;
#endif
- rect->xmin += icon_size + icon_padding;
+ rect->xmin += round_fl_to_int(icon_size + icon_padding);
}
if (!no_text_padding) {
- const int text_padding = (UI_TEXT_MARGIN_X * U.widget_unit) / but->block->aspect;
+ const int text_padding = round_fl_to_int((UI_TEXT_MARGIN_X * U.widget_unit) /
+ but->block->aspect);
if (but->editstr) {
rect->xmin += text_padding;
}
@@ -3757,12 +3767,35 @@ static void widget_numslider(
float factor, factor_ui;
float factor_discard = 1.0f; /* No discard. */
const float value = (float)ui_but_value_get(but);
-
- if (but->rnaprop && (RNA_property_subtype(but->rnaprop) == PROP_PERCENTAGE)) {
- factor = value / but->softmax;
- }
- else {
- factor = (value - but->softmin) / (but->softmax - but->softmin);
+ const float softmin = but->softmin;
+ const float softmax = but->softmax;
+ const float softrange = softmax - softmin;
+ const PropertyScaleType scale_type = ui_but_scale_type(but);
+
+ switch (scale_type) {
+ case PROP_SCALE_LINEAR: {
+ if (but->rnaprop && (RNA_property_subtype(but->rnaprop) == PROP_PERCENTAGE)) {
+ factor = value / softmax;
+ }
+ else {
+ factor = (value - softmin) / softrange;
+ }
+ break;
+ }
+ case PROP_SCALE_LOG: {
+ const float logmin = fmaxf(softmin, 0.5e-8f);
+ const float base = softmax / logmin;
+ factor = logf(value / logmin) / logf(base);
+ break;
+ }
+ case PROP_SCALE_CUBIC: {
+ const float cubicmin = cube_f(softmin);
+ const float cubicmax = cube_f(softmax);
+ const float cubicrange = cubicmax - cubicmin;
+ const float f = (value - softmin) * cubicrange / softrange + cubicmin;
+ factor = (cbrtf(f) - softmin) / softrange;
+ break;
+ }
}
const float width = (float)BLI_rcti_size_x(rect);
diff --git a/source/blender/editors/interface/view2d_ops.c b/source/blender/editors/interface/view2d_ops.c
index 7453cd17868..40c510af7e5 100644
--- a/source/blender/editors/interface/view2d_ops.c
+++ b/source/blender/editors/interface/view2d_ops.c
@@ -757,7 +757,7 @@ typedef struct v2dViewZoomData {
} v2dViewZoomData;
/**
- * Clamp by convention rather then locking flags,
+ * Clamp by convention rather than locking flags,
* for ndof and +/- keys
*/
static void view_zoom_axis_lock_defaults(bContext *C, bool r_do_zoom_xy[2])
diff --git a/source/blender/editors/io/CMakeLists.txt b/source/blender/editors/io/CMakeLists.txt
index d45c7ca9b75..44b5f85050f 100644
--- a/source/blender/editors/io/CMakeLists.txt
+++ b/source/blender/editors/io/CMakeLists.txt
@@ -40,8 +40,8 @@ set(SRC
io_alembic.c
io_cache.c
io_collada.c
- io_gpencil_import.c
io_gpencil_export.c
+ io_gpencil_import.c
io_gpencil_utils.c
io_ops.c
io_usd.c
diff --git a/source/blender/editors/io/io_alembic.c b/source/blender/editors/io/io_alembic.c
index a66f53ea839..28838d677f0 100644
--- a/source/blender/editors/io/io_alembic.c
+++ b/source/blender/editors/io/io_alembic.c
@@ -121,6 +121,7 @@ static int wm_alembic_export_exec(bContext *C, wmOperator *op)
.uvs = RNA_boolean_get(op->ptr, "uvs"),
.normals = RNA_boolean_get(op->ptr, "normals"),
.vcolors = RNA_boolean_get(op->ptr, "vcolors"),
+ .orcos = RNA_boolean_get(op->ptr, "orcos"),
.apply_subdiv = RNA_boolean_get(op->ptr, "apply_subdiv"),
.curves_as_mesh = RNA_boolean_get(op->ptr, "curves_as_mesh"),
.flatten_hierarchy = RNA_boolean_get(op->ptr, "flatten"),
@@ -210,6 +211,7 @@ static void ui_alembic_export_settings(uiLayout *layout, PointerRNA *imfptr)
uiItemR(col, imfptr, "normals", 0, NULL, ICON_NONE);
uiItemR(col, imfptr, "vcolors", 0, NULL, ICON_NONE);
+ uiItemR(col, imfptr, "orcos", 0, NULL, ICON_NONE);
uiItemR(col, imfptr, "face_sets", 0, NULL, ICON_NONE);
uiItemR(col, imfptr, "curves_as_mesh", 0, NULL, ICON_NONE);
@@ -240,21 +242,17 @@ static void ui_alembic_export_settings(uiLayout *layout, PointerRNA *imfptr)
static void wm_alembic_export_draw(bContext *C, wmOperator *op)
{
- PointerRNA ptr;
-
- RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
-
/* Conveniently set start and end frame to match the scene's frame range. */
Scene *scene = CTX_data_scene(C);
- if (scene != NULL && RNA_boolean_get(&ptr, "init_scene_frame_range")) {
- RNA_int_set(&ptr, "start", SFRA);
- RNA_int_set(&ptr, "end", EFRA);
+ if (scene != NULL && RNA_boolean_get(op->ptr, "init_scene_frame_range")) {
+ RNA_int_set(op->ptr, "start", SFRA);
+ RNA_int_set(op->ptr, "end", EFRA);
- RNA_boolean_set(&ptr, "init_scene_frame_range", false);
+ RNA_boolean_set(op->ptr, "init_scene_frame_range", false);
}
- ui_alembic_export_settings(op->layout, &ptr);
+ ui_alembic_export_settings(op->layout, op->ptr);
}
static bool wm_alembic_export_check(bContext *UNUSED(C), wmOperator *op)
@@ -382,6 +380,12 @@ void WM_OT_alembic_export(wmOperatorType *ot)
RNA_def_boolean(ot->srna, "vcolors", 0, "Vertex Colors", "Export vertex colors");
+ RNA_def_boolean(ot->srna,
+ "orcos",
+ true,
+ "Generated Coordinates",
+ "Export undeformed mesh vertex coordinates");
+
RNA_def_boolean(
ot->srna, "face_sets", 0, "Face Sets", "Export per face shading group assignments");
@@ -595,10 +599,7 @@ static void ui_alembic_import_settings(uiLayout *layout, PointerRNA *imfptr)
static void wm_alembic_import_draw(bContext *UNUSED(C), wmOperator *op)
{
- PointerRNA ptr;
-
- RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
- ui_alembic_import_settings(op->layout, &ptr);
+ ui_alembic_import_settings(op->layout, op->ptr);
}
/* op->invoke, opens fileselect if path property not set, otherwise executes */
diff --git a/source/blender/editors/io/io_collada.c b/source/blender/editors/io/io_collada.c
index 2bf975cc4f5..859c12d7e52 100644
--- a/source/blender/editors/io/io_collada.c
+++ b/source/blender/editors/io/io_collada.c
@@ -402,10 +402,7 @@ static void uiCollada_exportSettings(uiLayout *layout, PointerRNA *imfptr)
static void wm_collada_export_draw(bContext *UNUSED(C), wmOperator *op)
{
- PointerRNA ptr;
-
- RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
- uiCollada_exportSettings(op->layout, &ptr);
+ uiCollada_exportSettings(op->layout, op->ptr);
}
static bool wm_collada_export_check(bContext *UNUSED(C), wmOperator *op)
@@ -799,10 +796,7 @@ static void uiCollada_importSettings(uiLayout *layout, PointerRNA *imfptr)
static void wm_collada_import_draw(bContext *UNUSED(C), wmOperator *op)
{
- PointerRNA ptr;
-
- RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
- uiCollada_importSettings(op->layout, &ptr);
+ uiCollada_importSettings(op->layout, op->ptr);
}
void WM_OT_collada_import(wmOperatorType *ot)
diff --git a/source/blender/editors/io/io_gpencil.h b/source/blender/editors/io/io_gpencil.h
index 98cb8b13310..b347be00412 100644
--- a/source/blender/editors/io/io_gpencil.h
+++ b/source/blender/editors/io/io_gpencil.h
@@ -27,7 +27,6 @@
struct ARegion;
struct bContext;
struct View3D;
-struct wmOperator;
struct wmOperatorType;
void WM_OT_gpencil_import_svg(struct wmOperatorType *ot);
diff --git a/source/blender/editors/io/io_gpencil_export.c b/source/blender/editors/io/io_gpencil_export.c
index 98e30f51116..b49be324372 100644
--- a/source/blender/editors/io/io_gpencil_export.c
+++ b/source/blender/editors/io/io_gpencil_export.c
@@ -50,6 +50,7 @@
#include "gpencil_io.h"
+#if defined(WITH_PUGIXML) || defined(WITH_HARU)
/* Definition of enum elements to export. */
/* Common props for exporting. */
static void gpencil_export_common_props_definition(wmOperatorType *ot)
@@ -102,6 +103,7 @@ static void set_export_filepath(bContext *C, wmOperator *op, const char *extensi
RNA_string_set(op->ptr, "filepath", filepath);
}
}
+#endif
/* <-------- SVG single frame export. --------> */
#ifdef WITH_PUGIXML
@@ -215,11 +217,7 @@ static void ui_gpencil_export_svg_settings(uiLayout *layout, PointerRNA *imfptr)
static void wm_gpencil_export_svg_draw(bContext *UNUSED(C), wmOperator *op)
{
- PointerRNA ptr;
-
- RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
-
- ui_gpencil_export_svg_settings(op->layout, &ptr);
+ ui_gpencil_export_svg_settings(op->layout, op->ptr);
}
static bool wm_gpencil_export_svg_poll(bContext *C)
@@ -377,11 +375,7 @@ static void ui_gpencil_export_pdf_settings(uiLayout *layout, PointerRNA *imfptr)
static void wm_gpencil_export_pdf_draw(bContext *UNUSED(C), wmOperator *op)
{
- PointerRNA ptr;
-
- RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
-
- ui_gpencil_export_pdf_settings(op->layout, &ptr);
+ ui_gpencil_export_pdf_settings(op->layout, op->ptr);
}
static bool wm_gpencil_export_pdf_poll(bContext *C)
diff --git a/source/blender/editors/io/io_gpencil_import.c b/source/blender/editors/io/io_gpencil_import.c
index 9768da85940..a9911f1cef2 100644
--- a/source/blender/editors/io/io_gpencil_import.c
+++ b/source/blender/editors/io/io_gpencil_import.c
@@ -138,10 +138,7 @@ static void ui_gpencil_import_svg_settings(uiLayout *layout, PointerRNA *imfptr)
static void wm_gpencil_import_svg_draw(bContext *UNUSED(C), wmOperator *op)
{
- PointerRNA ptr;
- RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
-
- ui_gpencil_import_svg_settings(op->layout, &ptr);
+ ui_gpencil_import_svg_settings(op->layout, op->ptr);
}
static bool wm_gpencil_import_svg_poll(bContext *C)
diff --git a/source/blender/editors/mask/mask_add.c b/source/blender/editors/mask/mask_add.c
index 1226cc57359..36edbbe31d6 100644
--- a/source/blender/editors/mask/mask_add.c
+++ b/source/blender/editors/mask/mask_add.c
@@ -26,6 +26,7 @@
#include "BLI_math.h"
#include "BKE_context.h"
+#include "BKE_curve.h"
#include "BKE_mask.h"
#include "DEG_depsgraph.h"
@@ -692,6 +693,33 @@ void MASK_OT_add_feather_vertex(wmOperatorType *ot)
/******************** common primitive functions *********************/
+static BezTriple *points_to_bezier(const float (*points)[2],
+ const int num_points,
+ const char handle_type,
+ const float scale,
+ const float location[2])
+{
+ BezTriple *bezier_points = MEM_calloc_arrayN(num_points, sizeof(BezTriple), __func__);
+ for (int i = 0; i < num_points; i++) {
+ copy_v2_v2(bezier_points[i].vec[1], points[i]);
+ mul_v2_fl(bezier_points[i].vec[1], scale);
+ add_v2_v2(bezier_points[i].vec[1], location);
+
+ bezier_points[i].h1 = handle_type;
+ bezier_points[i].h2 = handle_type;
+ }
+
+ for (int i = 0; i < num_points; i++) {
+ BKE_nurb_handle_calc(&bezier_points[i],
+ &bezier_points[(i - 1 + num_points) % num_points],
+ &bezier_points[(i + 1) % num_points],
+ false,
+ false);
+ }
+
+ return bezier_points;
+}
+
static int create_primitive_from_points(
bContext *C, wmOperator *op, const float (*points)[2], int num_points, char handle_type)
{
@@ -734,25 +762,25 @@ static int create_primitive_from_points(
const int spline_index = BKE_mask_layer_shape_spline_to_index(mask_layer, new_spline);
+ BezTriple *bezier_points = points_to_bezier(points, num_points, handle_type, scale, location);
+
for (int i = 0; i < num_points; i++) {
new_spline->tot_point = i + 1;
MaskSplinePoint *new_point = &new_spline->points[i];
BKE_mask_parent_init(&new_point->parent);
- copy_v2_v2(new_point->bezt.vec[1], points[i]);
- mul_v2_fl(new_point->bezt.vec[1], scale);
- add_v2_v2(new_point->bezt.vec[1], location);
+ new_point->bezt = bezier_points[i];
- new_point->bezt.h1 = handle_type;
- new_point->bezt.h2 = handle_type;
BKE_mask_point_select_set(new_point, true);
if (mask_layer->splines_shapes.first) {
- BKE_mask_layer_shape_changed_add(mask_layer, spline_index + i, true, true);
+ BKE_mask_layer_shape_changed_add(mask_layer, spline_index + i, true, false);
}
}
+ MEM_freeN(bezier_points);
+
if (added_mask) {
WM_event_add_notifier(C, NC_MASK | NA_ADDED, NULL);
}
diff --git a/source/blender/editors/mask/mask_shapekey.c b/source/blender/editors/mask/mask_shapekey.c
index 1eb6613d8fe..81bf66da72c 100644
--- a/source/blender/editors/mask/mask_shapekey.c
+++ b/source/blender/editors/mask/mask_shapekey.c
@@ -220,8 +220,8 @@ void MASK_OT_shape_key_feather_reset(wmOperatorType *ot)
}
/*
- * - loop over selected shapekeys.
- * - find firstsel/lastsel pairs.
+ * - loop over selected shape-keys.
+ * - find first-selected/last-selected pairs.
* - move these into a temp list.
* - re-key all the original shapes.
* - copy unselected values back from the original.
diff --git a/source/blender/editors/mesh/editmesh_bevel.c b/source/blender/editors/mesh/editmesh_bevel.c
index 340a7ae92ff..43492cd57af 100644
--- a/source/blender/editors/mesh/editmesh_bevel.c
+++ b/source/blender/editors/mesh/editmesh_bevel.c
@@ -912,74 +912,72 @@ static void edbm_bevel_ui(bContext *C, wmOperator *op)
{
uiLayout *layout = op->layout;
uiLayout *col, *row;
- PointerRNA ptr, toolsettings_ptr;
+ PointerRNA toolsettings_ptr;
- RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
-
- int profile_type = RNA_enum_get(&ptr, "profile_type");
- int offset_type = RNA_enum_get(&ptr, "offset_type");
- bool affect_type = RNA_enum_get(&ptr, "affect");
+ int profile_type = RNA_enum_get(op->ptr, "profile_type");
+ int offset_type = RNA_enum_get(op->ptr, "offset_type");
+ bool affect_type = RNA_enum_get(op->ptr, "affect");
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
row = uiLayoutRow(layout, false);
- uiItemR(row, &ptr, "affect", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "affect", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
uiItemS(layout);
- uiItemR(layout, &ptr, "offset_type", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "offset_type", 0, NULL, ICON_NONE);
if (offset_type == BEVEL_AMT_PERCENT) {
- uiItemR(layout, &ptr, "offset_pct", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "offset_pct", 0, NULL, ICON_NONE);
}
else {
- uiItemR(layout, &ptr, "offset", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "offset", 0, NULL, ICON_NONE);
}
- uiItemR(layout, &ptr, "segments", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "segments", 0, NULL, ICON_NONE);
if (ELEM(profile_type, BEVEL_PROFILE_SUPERELLIPSE, BEVEL_PROFILE_CUSTOM)) {
uiItemR(layout,
- &ptr,
+ op->ptr,
"profile",
UI_ITEM_R_SLIDER,
(profile_type == BEVEL_PROFILE_SUPERELLIPSE) ? IFACE_("Shape") : IFACE_("Miter Shape"),
ICON_NONE);
}
- uiItemR(layout, &ptr, "material", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "material", 0, NULL, ICON_NONE);
col = uiLayoutColumn(layout, true);
- uiItemR(col, &ptr, "harden_normals", 0, NULL, ICON_NONE);
- uiItemR(col, &ptr, "clamp_overlap", 0, NULL, ICON_NONE);
- uiItemR(col, &ptr, "loop_slide", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "harden_normals", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "clamp_overlap", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "loop_slide", 0, NULL, ICON_NONE);
col = uiLayoutColumnWithHeading(layout, true, IFACE_("Mark"));
uiLayoutSetActive(col, affect_type == BEVEL_AFFECT_EDGES);
- uiItemR(col, &ptr, "mark_seam", 0, IFACE_("Seams"), ICON_NONE);
- uiItemR(col, &ptr, "mark_sharp", 0, IFACE_("Sharp"), ICON_NONE);
+ uiItemR(col, op->ptr, "mark_seam", 0, IFACE_("Seams"), ICON_NONE);
+ uiItemR(col, op->ptr, "mark_sharp", 0, IFACE_("Sharp"), ICON_NONE);
uiItemS(layout);
col = uiLayoutColumn(layout, false);
uiLayoutSetActive(col, affect_type == BEVEL_AFFECT_EDGES);
- uiItemR(col, &ptr, "miter_outer", 0, IFACE_("Miter Outer"), ICON_NONE);
- uiItemR(col, &ptr, "miter_inner", 0, IFACE_("Inner"), ICON_NONE);
- if (RNA_enum_get(&ptr, "miter_inner") == BEVEL_MITER_ARC) {
- uiItemR(col, &ptr, "spread", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "miter_outer", 0, IFACE_("Miter Outer"), ICON_NONE);
+ uiItemR(col, op->ptr, "miter_inner", 0, IFACE_("Inner"), ICON_NONE);
+ if (RNA_enum_get(op->ptr, "miter_inner") == BEVEL_MITER_ARC) {
+ uiItemR(col, op->ptr, "spread", 0, NULL, ICON_NONE);
}
uiItemS(layout);
col = uiLayoutColumn(layout, false);
uiLayoutSetActive(col, affect_type == BEVEL_AFFECT_EDGES);
- uiItemR(col, &ptr, "vmesh_method", 0, IFACE_("Intersection Type"), ICON_NONE);
+ uiItemR(col, op->ptr, "vmesh_method", 0, IFACE_("Intersection Type"), ICON_NONE);
- uiItemR(layout, &ptr, "face_strength_mode", 0, IFACE_("Face Strength"), ICON_NONE);
+ uiItemR(layout, op->ptr, "face_strength_mode", 0, IFACE_("Face Strength"), ICON_NONE);
uiItemS(layout);
row = uiLayoutRow(layout, false);
- uiItemR(row, &ptr, "profile_type", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "profile_type", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
if (profile_type == BEVEL_PROFILE_CUSTOM) {
/* Get an RNA pointer to ToolSettings to give to the curve profile template code. */
Scene *scene = CTX_data_scene(C);
diff --git a/source/blender/editors/mesh/editmesh_bisect.c b/source/blender/editors/mesh/editmesh_bisect.c
index bc8c456889d..ea35d5a9e26 100644
--- a/source/blender/editors/mesh/editmesh_bisect.c
+++ b/source/blender/editors/mesh/editmesh_bisect.c
@@ -444,15 +444,17 @@ void MESH_OT_bisect(struct wmOperatorType *ot)
RNA_def_boolean(
ot->srna, "clear_outer", false, "Clear Outer", "Remove geometry in front of the plane");
- RNA_def_float(ot->srna,
- "threshold",
- 0.0001,
- 0.0,
- 10.0,
- "Axis Threshold",
- "Preserves the existing geometry along the cut plane",
- 0.00001,
- 0.1);
+ prop = RNA_def_float(ot->srna,
+ "threshold",
+ 0.0001,
+ 0.0,
+ 10.0,
+ "Axis Threshold",
+ "Preserves the existing geometry along the cut plane",
+ 0.00001,
+ 0.1);
+ /* Without higher precision, the default value displays as zero. */
+ RNA_def_property_ui_range(prop, 0.0, 10.0, 0.01, 5);
WM_operator_properties_gesture_straightline(ot, WM_CURSOR_EDIT);
diff --git a/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c b/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c
index feb6b5aaca9..38d530ba911 100644
--- a/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c
+++ b/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c
@@ -468,6 +468,7 @@ void MESH_GGT_spin(struct wmGizmoGroupType *gzgt)
gzgt->poll = ED_gizmo_poll_or_unlink_delayed_from_tool;
gzgt->setup = gizmo_mesh_spin_init_setup;
+ gzgt->setup_keymap = WM_gizmogroup_setup_keymap_generic_maybe_drag;
gzgt->refresh = gizmo_mesh_spin_init_refresh;
gzgt->message_subscribe = gizmo_mesh_spin_init_message_subscribe;
gzgt->draw_prepare = gizmo_mesh_spin_init_draw_prepare;
diff --git a/source/blender/editors/mesh/editmesh_intersect.c b/source/blender/editors/mesh/editmesh_intersect.c
index 0e3cc22d358..d1f228e951a 100644
--- a/source/blender/editors/mesh/editmesh_intersect.c
+++ b/source/blender/editors/mesh/editmesh_intersect.c
@@ -255,27 +255,24 @@ static void edbm_intersect_ui(bContext *UNUSED(C), wmOperator *op)
{
uiLayout *layout = op->layout;
uiLayout *row;
- PointerRNA ptr;
- RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
-
- bool use_exact = RNA_enum_get(&ptr, "solver") == ISECT_SOLVER_EXACT;
+ bool use_exact = RNA_enum_get(op->ptr, "solver") == ISECT_SOLVER_EXACT;
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
row = uiLayoutRow(layout, false);
- uiItemR(row, &ptr, "mode", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "mode", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
uiItemS(layout);
row = uiLayoutRow(layout, false);
- uiItemR(row, &ptr, "separate_mode", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "separate_mode", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
uiItemS(layout);
row = uiLayoutRow(layout, false);
- uiItemR(row, &ptr, "solver", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "solver", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
uiItemS(layout);
if (!use_exact) {
- uiItemR(layout, &ptr, "threshold", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "threshold", 0, NULL, ICON_NONE);
}
}
@@ -421,27 +418,24 @@ static void edbm_intersect_boolean_ui(bContext *UNUSED(C), wmOperator *op)
{
uiLayout *layout = op->layout;
uiLayout *row;
- PointerRNA ptr;
-
- RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
- bool use_exact = RNA_enum_get(&ptr, "solver") == ISECT_SOLVER_EXACT;
+ bool use_exact = RNA_enum_get(op->ptr, "solver") == ISECT_SOLVER_EXACT;
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
row = uiLayoutRow(layout, false);
- uiItemR(row, &ptr, "operation", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "operation", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
uiItemS(layout);
row = uiLayoutRow(layout, false);
- uiItemR(row, &ptr, "solver", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "solver", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
uiItemS(layout);
- uiItemR(layout, &ptr, "use_swap", 0, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "use_self", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "use_swap", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "use_self", 0, NULL, ICON_NONE);
if (!use_exact) {
- uiItemR(layout, &ptr, "threshold", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "threshold", 0, NULL, ICON_NONE);
}
}
diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c
index df0ca6e3cae..ae824cad50b 100644
--- a/source/blender/editors/mesh/editmesh_knife.c
+++ b/source/blender/editors/mesh/editmesh_knife.c
@@ -74,7 +74,7 @@
/* detect isolated holes and fill them */
#define USE_NET_ISLAND_CONNECT
-#define KMAXDIST 10 /* max mouse distance from edge before not detecting it */
+#define KMAXDIST (10 * U.dpi_fac) /* max mouse distance from edge before not detecting it */
/* WARNING: knife float precision is fragile:
* be careful before making changes here see: (T43229, T42864, T42459, T41164).
@@ -146,6 +146,8 @@ typedef struct KnifePosData {
KnifeVert *vert;
KnifeEdge *edge;
BMFace *bmface;
+
+ /** When true, the cursor isn't over a face. */
bool is_space;
float mval[2]; /* mouse screen position (may be non-integral if snapped to something) */
@@ -196,10 +198,19 @@ typedef struct KnifeTool_OpData {
KnifePosData prev; /* last added cut (a line draws from the cursor to this) */
KnifePosData init; /* the first point in the cut-list, used for closing the loop */
- int totkedge, totkvert;
+ /** Number of knife edges `kedges`. */
+ int totkedge;
+ /** Number of knife vertices, `kverts`. */
+ int totkvert;
BLI_mempool *refs;
+ /**
+ * Use this instead of #Object.imat since it's calculated using #invert_m4_m4_safe_ortho
+ * to support objects with zero scale on a single axis.
+ */
+ float ob_imat[4][4];
+
float projmat[4][4];
float projmat_inv[4][4];
/* vector along view z axis (object space, normalized) */
@@ -243,8 +254,8 @@ enum {
KNF_MODAL_MIDPOINT_ON,
KNF_MODAL_MIDPOINT_OFF,
KNF_MODAL_NEW_CUT,
- KNF_MODEL_IGNORE_SNAP_ON,
- KNF_MODEL_IGNORE_SNAP_OFF,
+ KNF_MODAL_IGNORE_SNAP_ON,
+ KNF_MODAL_IGNORE_SNAP_OFF,
KNF_MODAL_ADD_CUT,
KNF_MODAL_ANGLE_SNAP_TOGGLE,
KNF_MODAL_CUT_THROUGH_TOGGLE,
@@ -252,19 +263,263 @@ enum {
KNF_MODAL_ADD_CUT_CLOSED,
};
-static ListBase *knife_get_face_kedges(KnifeTool_OpData *kcd, BMFace *f);
+/* -------------------------------------------------------------------- */
+/** \name Drawing
+ * \{ */
-static void knife_input_ray_segment(KnifeTool_OpData *kcd,
- const float mval[2],
- const float ofs,
- float r_origin[3],
- float r_origin_ofs[3]);
+static void knifetool_draw_angle_snapping(const KnifeTool_OpData *kcd)
+{
+ float v1[3], v2[3];
+ float planes[4][4];
+
+ planes_from_projmat(
+ (const float(*)[4])kcd->projmat, planes[2], planes[0], planes[3], planes[1], NULL, NULL);
+
+ /* ray-cast all planes */
+ {
+ float ray_dir[3];
+ float ray_hit_best[2][3] = {{UNPACK3(kcd->prev.cage)}, {UNPACK3(kcd->curr.cage)}};
+ float lambda_best[2] = {-FLT_MAX, FLT_MAX};
+ int i;
+
+ /* we (sometimes) need the lines to be at the same depth before projecting */
+#if 0
+ sub_v3_v3v3(ray_dir, kcd->curr.cage, kcd->prev.cage);
+#else
+ {
+ float curr_cage_adjust[3];
+ float co_depth[3];
-static bool knife_verts_edge_in_face(KnifeVert *v1, KnifeVert *v2, BMFace *f);
+ copy_v3_v3(co_depth, kcd->prev.cage);
+ mul_m4_v3(kcd->ob->obmat, co_depth);
+ ED_view3d_win_to_3d(kcd->vc.v3d, kcd->region, co_depth, kcd->curr.mval, curr_cage_adjust);
+ mul_m4_v3(kcd->ob_imat, curr_cage_adjust);
+
+ sub_v3_v3v3(ray_dir, curr_cage_adjust, kcd->prev.cage);
+ }
+#endif
-static void knifetool_free_bmbvh(KnifeTool_OpData *kcd);
+ for (i = 0; i < 4; i++) {
+ float ray_hit[3];
+ float lambda_test;
+ if (isect_ray_plane_v3(kcd->prev.cage, ray_dir, planes[i], &lambda_test, false)) {
+ madd_v3_v3v3fl(ray_hit, kcd->prev.cage, ray_dir, lambda_test);
+ if (lambda_test < 0.0f) {
+ if (lambda_test > lambda_best[0]) {
+ copy_v3_v3(ray_hit_best[0], ray_hit);
+ lambda_best[0] = lambda_test;
+ }
+ }
+ else {
+ if (lambda_test < lambda_best[1]) {
+ copy_v3_v3(ray_hit_best[1], ray_hit);
+ lambda_best[1] = lambda_test;
+ }
+ }
+ }
+ }
+
+ copy_v3_v3(v1, ray_hit_best[0]);
+ copy_v3_v3(v2, ray_hit_best[1]);
+ }
+
+ uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
+ immUniformThemeColor3(TH_TRANSFORM);
+ GPU_line_width(2.0);
+
+ immBegin(GPU_PRIM_LINES, 2);
+ immVertex3fv(pos, v1);
+ immVertex3fv(pos, v2);
+ immEnd();
+
+ immUnbindProgram();
+}
+
+/* modal loop selection drawing callback */
+static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), void *arg)
+{
+ const KnifeTool_OpData *kcd = arg;
+ GPU_depth_test(GPU_DEPTH_NONE);
+
+ GPU_matrix_push_projection();
+ GPU_polygon_offset(1.0f, 1.0f);
+
+ GPU_matrix_push();
+ GPU_matrix_mul(kcd->ob->obmat);
+
+ if (kcd->mode == MODE_DRAGGING && kcd->is_angle_snapping) {
+ knifetool_draw_angle_snapping(kcd);
+ }
+
+ GPUVertFormat *format = immVertexFormat();
+ uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
+
+ if (kcd->mode == MODE_DRAGGING) {
+ immUniformColor3ubv(kcd->colors.line);
+ GPU_line_width(2.0);
+
+ immBegin(GPU_PRIM_LINES, 2);
+ immVertex3fv(pos, kcd->prev.cage);
+ immVertex3fv(pos, kcd->curr.cage);
+ immEnd();
+ }
+
+ if (kcd->prev.vert) {
+ immUniformColor3ubv(kcd->colors.point);
+ GPU_point_size(11 * UI_DPI_FAC);
-static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event);
+ immBegin(GPU_PRIM_POINTS, 1);
+ immVertex3fv(pos, kcd->prev.cage);
+ immEnd();
+ }
+
+ if (kcd->prev.bmface || kcd->prev.edge) {
+ immUniformColor3ubv(kcd->colors.curpoint);
+ GPU_point_size(9 * UI_DPI_FAC);
+
+ immBegin(GPU_PRIM_POINTS, 1);
+ immVertex3fv(pos, kcd->prev.cage);
+ immEnd();
+ }
+
+ if (kcd->curr.vert) {
+ immUniformColor3ubv(kcd->colors.point);
+ GPU_point_size(11 * UI_DPI_FAC);
+
+ immBegin(GPU_PRIM_POINTS, 1);
+ immVertex3fv(pos, kcd->curr.cage);
+ immEnd();
+ }
+ else if (kcd->curr.edge) {
+ immUniformColor3ubv(kcd->colors.edge);
+ GPU_line_width(2.0);
+
+ immBegin(GPU_PRIM_LINES, 2);
+ immVertex3fv(pos, kcd->curr.edge->v1->cageco);
+ immVertex3fv(pos, kcd->curr.edge->v2->cageco);
+ immEnd();
+ }
+
+ if (kcd->curr.bmface || kcd->curr.edge) {
+ immUniformColor3ubv(kcd->colors.curpoint);
+ GPU_point_size(9 * UI_DPI_FAC);
+
+ immBegin(GPU_PRIM_POINTS, 1);
+ immVertex3fv(pos, kcd->curr.cage);
+ immEnd();
+ }
+
+ if (kcd->totlinehit > 0) {
+ KnifeLineHit *lh;
+ int i, snapped_verts_count, other_verts_count;
+ float fcol[4];
+
+ GPU_blend(GPU_BLEND_ALPHA);
+
+ GPUVertBuf *vert = GPU_vertbuf_create_with_format(format);
+ GPU_vertbuf_data_alloc(vert, kcd->totlinehit);
+
+ lh = kcd->linehits;
+ for (i = 0, snapped_verts_count = 0, other_verts_count = 0; i < kcd->totlinehit; i++, lh++) {
+ if (lh->v) {
+ GPU_vertbuf_attr_set(vert, pos, snapped_verts_count++, lh->cagehit);
+ }
+ else {
+ GPU_vertbuf_attr_set(vert, pos, kcd->totlinehit - 1 - other_verts_count++, lh->cagehit);
+ }
+ }
+
+ GPUBatch *batch = GPU_batch_create_ex(GPU_PRIM_POINTS, vert, NULL, GPU_BATCH_OWNS_VBO);
+ GPU_batch_program_set_builtin(batch, GPU_SHADER_3D_UNIFORM_COLOR);
+
+ /* draw any snapped verts first */
+ rgba_uchar_to_float(fcol, kcd->colors.point_a);
+ GPU_batch_uniform_4fv(batch, "color", fcol);
+ GPU_point_size(11 * UI_DPI_FAC);
+ if (snapped_verts_count > 0) {
+ GPU_batch_draw_range(batch, 0, snapped_verts_count);
+ }
+
+ /* now draw the rest */
+ rgba_uchar_to_float(fcol, kcd->colors.curpoint_a);
+ GPU_batch_uniform_4fv(batch, "color", fcol);
+ GPU_point_size(7 * UI_DPI_FAC);
+ if (other_verts_count > 0) {
+ GPU_batch_draw_range(batch, snapped_verts_count, other_verts_count);
+ }
+
+ GPU_batch_discard(batch);
+
+ GPU_blend(GPU_BLEND_NONE);
+ }
+
+ if (kcd->totkedge > 0) {
+ BLI_mempool_iter iter;
+ KnifeEdge *kfe;
+
+ immUniformColor3ubv(kcd->colors.line);
+ GPU_line_width(1.0);
+
+ GPUBatch *batch = immBeginBatchAtMost(GPU_PRIM_LINES, BLI_mempool_len(kcd->kedges) * 2);
+
+ BLI_mempool_iternew(kcd->kedges, &iter);
+ for (kfe = BLI_mempool_iterstep(&iter); kfe; kfe = BLI_mempool_iterstep(&iter)) {
+ if (!kfe->is_cut) {
+ continue;
+ }
+
+ immVertex3fv(pos, kfe->v1->cageco);
+ immVertex3fv(pos, kfe->v2->cageco);
+ }
+
+ immEnd();
+
+ GPU_batch_draw(batch);
+ GPU_batch_discard(batch);
+ }
+
+ if (kcd->totkvert > 0) {
+ BLI_mempool_iter iter;
+ KnifeVert *kfv;
+
+ immUniformColor3ubv(kcd->colors.point);
+ GPU_point_size(5.0 * UI_DPI_FAC);
+
+ GPUBatch *batch = immBeginBatchAtMost(GPU_PRIM_POINTS, BLI_mempool_len(kcd->kverts));
+
+ BLI_mempool_iternew(kcd->kverts, &iter);
+ for (kfv = BLI_mempool_iterstep(&iter); kfv; kfv = BLI_mempool_iterstep(&iter)) {
+ if (!kfv->is_cut) {
+ continue;
+ }
+
+ immVertex3fv(pos, kfv->cageco);
+ }
+
+ immEnd();
+
+ GPU_batch_draw(batch);
+ GPU_batch_discard(batch);
+ }
+
+ immUnbindProgram();
+
+ GPU_matrix_pop();
+ GPU_matrix_pop_projection();
+
+ /* Reset default */
+ GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Header
+ * \{ */
static void knife_update_header(bContext *C, wmOperator *op, KnifeTool_OpData *kcd)
{
@@ -292,7 +547,7 @@ static void knife_update_header(bContext *C, wmOperator *op, KnifeTool_OpData *k
WM_MODALKEY(KNF_MODAL_NEW_CUT),
WM_MODALKEY(KNF_MODAL_MIDPOINT_ON),
WM_bool_as_string(kcd->snap_midpoints),
- WM_MODALKEY(KNF_MODEL_IGNORE_SNAP_ON),
+ WM_MODALKEY(KNF_MODAL_IGNORE_SNAP_ON),
WM_bool_as_string(kcd->ignore_edge_snapping),
WM_MODALKEY(KNF_MODAL_ANGLE_SNAP_TOGGLE),
WM_bool_as_string(kcd->angle_snapping),
@@ -305,51 +560,160 @@ static void knife_update_header(bContext *C, wmOperator *op, KnifeTool_OpData *k
ED_workspace_status_text(C, header);
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Geometry Utils
+ * \{ */
+
static void knife_project_v2(const KnifeTool_OpData *kcd, const float co[3], float sco[2])
{
ED_view3d_project_float_v2_m4(kcd->region, co, sco, (float(*)[4])kcd->projmat);
}
-/* use when lambda is in screen-space */
-static void knife_interp_v3_v3v3(const KnifeTool_OpData *kcd,
- float r_co[3],
- const float v1[3],
- const float v2[3],
- float lambda_ss)
+static void knife_input_ray_segment(KnifeTool_OpData *kcd,
+ const float mval[2],
+ const float ofs,
+ float r_origin[3],
+ float r_origin_ofs[3])
{
- if (kcd->is_ortho) {
- interp_v3_v3v3(r_co, v1, v2, lambda_ss);
+ /* unproject to find view ray */
+ ED_view3d_unproject_v3(kcd->vc.region, mval[0], mval[1], 0.0f, r_origin);
+ ED_view3d_unproject_v3(kcd->vc.region, mval[0], mval[1], ofs, r_origin_ofs);
+
+ /* transform into object space */
+ mul_m4_v3(kcd->ob_imat, r_origin);
+ mul_m4_v3(kcd->ob_imat, r_origin_ofs);
+}
+
+static bool knife_verts_edge_in_face(KnifeVert *v1, KnifeVert *v2, BMFace *f)
+{
+ bool v1_inside, v2_inside;
+ bool v1_inface, v2_inface;
+ BMLoop *l1, *l2;
+
+ if (!f || !v1 || !v2) {
+ return false;
}
- else {
- /* transform into screen-space, interp, then transform back */
- float v1_ss[3], v2_ss[3];
- mul_v3_project_m4_v3(v1_ss, (float(*)[4])kcd->projmat, v1);
- mul_v3_project_m4_v3(v2_ss, (float(*)[4])kcd->projmat, v2);
+ l1 = v1->v ? BM_face_vert_share_loop(f, v1->v) : NULL;
+ l2 = v2->v ? BM_face_vert_share_loop(f, v2->v) : NULL;
- interp_v3_v3v3(r_co, v1_ss, v2_ss, lambda_ss);
+ if ((l1 && l2) && BM_loop_is_adjacent(l1, l2)) {
+ /* boundary-case, always false to avoid edge-in-face checks below */
+ return false;
+ }
- mul_project_m4_v3((float(*)[4])kcd->projmat_inv, r_co);
+ /* find out if v1 and v2, if set, are part of the face */
+ v1_inface = (l1 != NULL);
+ v2_inface = (l2 != NULL);
+
+ /* BM_face_point_inside_test uses best-axis projection so this isn't most accurate test... */
+ v1_inside = v1_inface ? false : BM_face_point_inside_test(f, v1->co);
+ v2_inside = v2_inface ? false : BM_face_point_inside_test(f, v2->co);
+ if ((v1_inface && v2_inside) || (v2_inface && v1_inside) || (v1_inside && v2_inside)) {
+ return true;
}
+
+ if (v1_inface && v2_inface) {
+ float mid[3];
+ /* Can have case where v1 and v2 are on shared chain between two faces.
+ * BM_face_splits_check_legal does visibility and self-intersection tests,
+ * but it is expensive and maybe a bit buggy, so use a simple
+ * "is the midpoint in the face" test */
+ mid_v3_v3v3(mid, v1->co, v2->co);
+ return BM_face_point_inside_test(f, mid);
+ }
+ return false;
}
-static void knife_pos_data_clear(KnifePosData *kpd)
+static void knife_recalc_projmat(KnifeTool_OpData *kcd)
{
- zero_v3(kpd->co);
- zero_v3(kpd->cage);
- kpd->vert = NULL;
- kpd->edge = NULL;
- kpd->bmface = NULL;
- zero_v2(kpd->mval);
+ ED_view3d_ob_project_mat_get(kcd->region->regiondata, kcd->ob, kcd->projmat);
+ invert_m4_m4(kcd->projmat_inv, kcd->projmat);
+
+ invert_m4_m4_safe_ortho(kcd->ob_imat, kcd->ob->obmat);
+ mul_v3_mat3_m4v3(kcd->proj_zaxis, kcd->ob_imat, kcd->vc.rv3d->viewinv[2]);
+ normalize_v3(kcd->proj_zaxis);
+
+ kcd->is_ortho = ED_view3d_clip_range_get(
+ kcd->vc.depsgraph, kcd->vc.v3d, kcd->vc.rv3d, &kcd->clipsta, &kcd->clipend, true);
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Knife Element Utils
+ *
+ * Currently only used in #knife_find_line_hits.
+ * \{ */
+
+static BMElem *bm_elem_from_knife_vert(KnifeVert *kfv, KnifeEdge **r_kfe)
+{
+ BMElem *ele_test;
+ KnifeEdge *kfe = NULL;
+
+ /* vert? */
+ ele_test = (BMElem *)kfv->v;
+
+ if (r_kfe || ele_test == NULL) {
+ if (kfv->v == NULL) {
+ Ref *ref;
+ for (ref = kfv->edges.first; ref; ref = ref->next) {
+ kfe = ref->ref;
+ if (kfe->e) {
+ if (r_kfe) {
+ *r_kfe = kfe;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ /* edge? */
+ if (ele_test == NULL) {
+ if (kfe) {
+ ele_test = (BMElem *)kfe->e;
+ }
+ }
+
+ /* face? */
+ if (ele_test == NULL) {
+ if (BLI_listbase_is_single(&kfe->faces)) {
+ ele_test = ((Ref *)kfe->faces.first)->ref;
+ }
+ }
+
+ return ele_test;
+}
+
+static BMElem *bm_elem_from_knife_edge(KnifeEdge *kfe)
+{
+ BMElem *ele_test;
+
+ ele_test = (BMElem *)kfe->e;
+
+ if (ele_test == NULL) {
+ ele_test = (BMElem *)kfe->basef;
+ }
+
+ return ele_test;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Knife Element List Utils
+ * \{ */
+
static ListBase *knife_empty_list(KnifeTool_OpData *kcd)
{
- ListBase *lst;
+ ListBase *list;
- lst = BLI_memarena_alloc(kcd->arena, sizeof(ListBase));
- BLI_listbase_clear(lst);
- return lst;
+ list = BLI_memarena_alloc(kcd->arena, sizeof(ListBase));
+ BLI_listbase_clear(list);
+ return list;
}
static void knife_append_list(KnifeTool_OpData *kcd, ListBase *lst, void *elem)
@@ -381,12 +745,6 @@ static void knife_append_list_no_dup(KnifeTool_OpData *kcd, ListBase *lst, void
}
}
-static KnifeEdge *new_knife_edge(KnifeTool_OpData *kcd)
-{
- kcd->totkedge++;
- return BLI_mempool_calloc(kcd->kedges);
-}
-
static void knife_add_to_vert_edges(KnifeTool_OpData *kcd, KnifeEdge *kfe)
{
knife_append_list(kcd, &kfe->v1->edges, kfe);
@@ -420,6 +778,12 @@ static BMFace *knife_find_common_face(ListBase *faces1, ListBase *faces2)
return NULL;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Knife Element Creation
+ * \{ */
+
static KnifeVert *new_knife_vert(KnifeTool_OpData *kcd, const float co[3], const float cageco[3])
{
KnifeVert *kfv = BLI_mempool_calloc(kcd->kverts);
@@ -432,6 +796,12 @@ static KnifeVert *new_knife_vert(KnifeTool_OpData *kcd, const float co[3], const
return kfv;
}
+static KnifeEdge *new_knife_edge(KnifeTool_OpData *kcd)
+{
+ kcd->totkedge++;
+ return BLI_mempool_calloc(kcd->kedges);
+}
+
/* get a KnifeVert wrapper for an existing BMVert */
static KnifeVert *get_bm_knife_vert(KnifeTool_OpData *kcd, BMVert *v)
{
@@ -484,92 +854,24 @@ static KnifeEdge *get_bm_knife_edge(KnifeTool_OpData *kcd, BMEdge *e)
return kfe;
}
-/* Record the index in kcd->em->looptris of first looptri triple for a given face,
- * given an index for some triple in that array.
- * This assumes that all of the triangles for a given face are contiguous
- * in that array (as they are by the current tessellation routines).
- * Actually store index + 1 in the hash, because 0 looks like "no entry"
- * to hash lookup routine; will reverse this in the get routine.
- * Doing this lazily rather than all at once for all faces.
- */
-static void set_lowest_face_tri(KnifeTool_OpData *kcd, BMFace *f, int index)
-{
- int i;
-
- if (BLI_ghash_lookup(kcd->facetrimap, f)) {
- return;
- }
-
- BLI_assert(index >= 0 && index < kcd->em->tottri);
- BLI_assert(kcd->em->looptris[index][0]->f == f);
- for (i = index - 1; i >= 0; i--) {
- if (kcd->em->looptris[i][0]->f != f) {
- i++;
- break;
- }
- }
- if (i == -1) {
- i++;
- }
-
- BLI_ghash_insert(kcd->facetrimap, f, POINTER_FROM_INT(i + 1));
-}
-
-/* This should only be called for faces that have had a lowest face tri set by previous function */
-static int get_lowest_face_tri(KnifeTool_OpData *kcd, BMFace *f)
-{
- int ans;
-
- ans = POINTER_AS_INT(BLI_ghash_lookup(kcd->facetrimap, f));
- BLI_assert(ans != 0);
- return ans - 1;
-}
-
-/* User has just clicked for first time or first time after a restart (E key).
- * Copy the current position data into prev. */
-static void knife_start_cut(KnifeTool_OpData *kcd)
-{
- kcd->prev = kcd->curr;
- kcd->curr.is_space = 0; /*TODO: why do we do this? */
-
- if (kcd->prev.vert == NULL && kcd->prev.edge == NULL) {
- float origin[3], origin_ofs[3];
- float ofs_local[3];
-
- negate_v3_v3(ofs_local, kcd->vc.rv3d->ofs);
- invert_m4_m4(kcd->ob->imat, kcd->ob->obmat);
- mul_m4_v3(kcd->ob->imat, ofs_local);
-
- knife_input_ray_segment(kcd, kcd->curr.mval, 1.0f, origin, origin_ofs);
-
- if (!isect_line_plane_v3(kcd->prev.cage, origin, origin_ofs, ofs_local, kcd->proj_zaxis)) {
- zero_v3(kcd->prev.cage);
- }
-
- copy_v3_v3(kcd->prev.co, kcd->prev.cage); /*TODO: do we need this? */
- copy_v3_v3(kcd->curr.cage, kcd->prev.cage);
- copy_v3_v3(kcd->curr.co, kcd->prev.co);
- }
-}
-
static ListBase *knife_get_face_kedges(KnifeTool_OpData *kcd, BMFace *f)
{
- ListBase *lst = BLI_ghash_lookup(kcd->kedgefacemap, f);
+ ListBase *list = BLI_ghash_lookup(kcd->kedgefacemap, f);
- if (!lst) {
+ if (!list) {
BMIter bmiter;
BMEdge *e;
- lst = knife_empty_list(kcd);
+ list = knife_empty_list(kcd);
BM_ITER_ELEM (e, &bmiter, f, BM_EDGES_OF_FACE) {
- knife_append_list(kcd, lst, get_bm_knife_edge(kcd, e));
+ knife_append_list(kcd, list, get_bm_knife_edge(kcd, e));
}
- BLI_ghash_insert(kcd->kedgefacemap, f, lst);
+ BLI_ghash_insert(kcd->kedgefacemap, f, list);
}
- return lst;
+ return list;
}
static void knife_edge_append_face(KnifeTool_OpData *kcd, KnifeEdge *kfe, BMFace *f)
@@ -625,6 +927,38 @@ static KnifeVert *knife_split_edge(KnifeTool_OpData *kcd,
return newkfe->v2;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Cut/Hit Utils
+ * \{ */
+
+/* User has just clicked for first time or first time after a restart (E key).
+ * Copy the current position data into prev. */
+static void knife_start_cut(KnifeTool_OpData *kcd)
+{
+ kcd->prev = kcd->curr;
+ kcd->curr.is_space = 0; /*TODO: why do we do this? */
+
+ if (kcd->prev.vert == NULL && kcd->prev.edge == NULL) {
+ float origin[3], origin_ofs[3];
+ float ofs_local[3];
+
+ negate_v3_v3(ofs_local, kcd->vc.rv3d->ofs);
+ mul_m4_v3(kcd->ob_imat, ofs_local);
+
+ knife_input_ray_segment(kcd, kcd->curr.mval, 1.0f, origin, origin_ofs);
+
+ if (!isect_line_plane_v3(kcd->prev.cage, origin, origin_ofs, ofs_local, kcd->proj_zaxis)) {
+ zero_v3(kcd->prev.cage);
+ }
+
+ copy_v3_v3(kcd->prev.co, kcd->prev.cage); /*TODO: do we need this? */
+ copy_v3_v3(kcd->curr.cage, kcd->prev.cage);
+ copy_v3_v3(kcd->curr.co, kcd->prev.co);
+ }
+}
+
static void linehit_to_knifepos(KnifePosData *kpos, KnifeLineHit *lh)
{
kpos->bmface = lh->f;
@@ -750,13 +1084,13 @@ static void add_hit_to_facehits(KnifeTool_OpData *kcd,
BMFace *f,
KnifeLineHit *hit)
{
- ListBase *lst = BLI_ghash_lookup(facehits, f);
+ ListBase *list = BLI_ghash_lookup(facehits, f);
- if (!lst) {
- lst = knife_empty_list(kcd);
- BLI_ghash_insert(facehits, f, lst);
+ if (!list) {
+ list = knife_empty_list(kcd);
+ BLI_ghash_insert(facehits, f, list);
}
- knife_append_list_no_dup(kcd, lst, hit);
+ knife_append_list_no_dup(kcd, list, hit);
}
/**
@@ -879,6 +1213,228 @@ static void knife_cut_face(KnifeTool_OpData *kcd, BMFace *f, ListBase *hits)
}
}
+static void knife_make_face_cuts(KnifeTool_OpData *kcd, BMFace *f, ListBase *kfedges)
+{
+ BMesh *bm = kcd->em->bm;
+ KnifeEdge *kfe;
+ Ref *ref;
+ int edge_array_len = BLI_listbase_count(kfedges);
+ int i;
+
+ BMEdge **edge_array = BLI_array_alloca(edge_array, edge_array_len);
+
+ /* point to knife edges we've created edges in, edge_array aligned */
+ KnifeEdge **kfe_array = BLI_array_alloca(kfe_array, edge_array_len);
+
+ BLI_assert(BLI_gset_len(kcd->edgenet.edge_visit) == 0);
+
+ i = 0;
+ for (ref = kfedges->first; ref; ref = ref->next) {
+ bool is_new_edge = false;
+ kfe = ref->ref;
+
+ if (kfe->e == NULL) {
+ if (kfe->v1->v && kfe->v2->v) {
+ kfe->e = BM_edge_exists(kfe->v1->v, kfe->v2->v);
+ }
+ }
+
+ if (kfe->e) {
+ if (BM_edge_in_face(kfe->e, f)) {
+ /* shouldn't happen, but in this case - just ignore */
+ continue;
+ }
+ }
+ else {
+ if (kfe->v1->v == NULL) {
+ kfe->v1->v = BM_vert_create(bm, kfe->v1->co, NULL, 0);
+ }
+ if (kfe->v2->v == NULL) {
+ kfe->v2->v = BM_vert_create(bm, kfe->v2->co, NULL, 0);
+ }
+ BLI_assert(kfe->e == NULL);
+ kfe->e = BM_edge_create(bm, kfe->v1->v, kfe->v2->v, NULL, 0);
+ if (kfe->e) {
+ if (kcd->select_result || BM_elem_flag_test(f, BM_ELEM_SELECT)) {
+ BM_edge_select_set(bm, kfe->e, true);
+ }
+ is_new_edge = true;
+ }
+ }
+
+ BLI_assert(kfe->e);
+
+ if (BLI_gset_add(kcd->edgenet.edge_visit, kfe->e)) {
+ kfe_array[i] = is_new_edge ? kfe : 0;
+ edge_array[i] = kfe->e;
+ i += 1;
+ }
+ }
+
+ if (i) {
+ const int edge_array_len_orig = i;
+ edge_array_len = i;
+
+#ifdef USE_NET_ISLAND_CONNECT
+ uint edge_array_holes_len;
+ BMEdge **edge_array_holes;
+ if (BM_face_split_edgenet_connect_islands(bm,
+ f,
+ edge_array,
+ edge_array_len,
+ true,
+ kcd->edgenet.arena,
+ &edge_array_holes,
+ &edge_array_holes_len)) {
+ if (BM_elem_flag_test(f, BM_ELEM_SELECT)) {
+ for (i = edge_array_len; i < edge_array_holes_len; i++) {
+ BM_edge_select_set(bm, edge_array_holes[i], true);
+ }
+ }
+
+ edge_array_len = edge_array_holes_len;
+ edge_array = edge_array_holes; /* owned by the arena */
+ }
+#endif
+
+ {
+ BMFace **face_arr = NULL;
+ int face_arr_len;
+
+ BM_face_split_edgenet(bm, f, edge_array, edge_array_len, &face_arr, &face_arr_len);
+
+ if (face_arr) {
+ MEM_freeN(face_arr);
+ }
+ }
+
+ /* remove dangling edges, not essential - but nice for users */
+ for (i = 0; i < edge_array_len_orig; i++) {
+ if (kfe_array[i]) {
+ if (BM_edge_is_wire(kfe_array[i]->e)) {
+ BM_edge_kill(bm, kfe_array[i]->e);
+ kfe_array[i]->e = NULL;
+ }
+ }
+ }
+
+#ifdef USE_NET_ISLAND_CONNECT
+ BLI_memarena_clear(kcd->edgenet.arena);
+#endif
+ }
+
+ BLI_gset_clear(kcd->edgenet.edge_visit, NULL);
+}
+
+static int sort_verts_by_dist_cb(void *co_p, const void *cur_a_p, const void *cur_b_p)
+{
+ const KnifeVert *cur_a = ((const Ref *)cur_a_p)->ref;
+ const KnifeVert *cur_b = ((const Ref *)cur_b_p)->ref;
+ const float *co = co_p;
+ const float a_sq = len_squared_v3v3(co, cur_a->co);
+ const float b_sq = len_squared_v3v3(co, cur_b->co);
+
+ if (a_sq < b_sq) {
+ return -1;
+ }
+ if (a_sq > b_sq) {
+ return 1;
+ }
+ return 0;
+}
+
+/* Use the network of KnifeEdges and KnifeVerts accumulated to make real BMVerts and BMEdedges */
+static void knife_make_cuts(KnifeTool_OpData *kcd)
+{
+ BMesh *bm = kcd->em->bm;
+ KnifeEdge *kfe;
+ KnifeVert *kfv;
+ BMFace *f;
+ BMEdge *e, *enew;
+ ListBase *list;
+ Ref *ref;
+ float pct;
+ SmallHashIter hiter;
+ BLI_mempool_iter iter;
+ SmallHash fhash_, *fhash = &fhash_;
+ SmallHash ehash_, *ehash = &ehash_;
+
+ BLI_smallhash_init(fhash);
+ BLI_smallhash_init(ehash);
+
+ /* put list of cutting edges for a face into fhash, keyed by face */
+ BLI_mempool_iternew(kcd->kedges, &iter);
+ for (kfe = BLI_mempool_iterstep(&iter); kfe; kfe = BLI_mempool_iterstep(&iter)) {
+
+ /* select edges that lie directly on the cut */
+ if (kcd->select_result) {
+ if (kfe->e && kfe->is_cut) {
+ BM_edge_select_set(bm, kfe->e, true);
+ }
+ }
+
+ f = kfe->basef;
+ if (!f || kfe->e) {
+ continue;
+ }
+ list = BLI_smallhash_lookup(fhash, (uintptr_t)f);
+ if (!list) {
+ list = knife_empty_list(kcd);
+ BLI_smallhash_insert(fhash, (uintptr_t)f, list);
+ }
+ knife_append_list(kcd, list, kfe);
+ }
+
+ /* put list of splitting vertices for an edge into ehash, keyed by edge */
+ BLI_mempool_iternew(kcd->kverts, &iter);
+ for (kfv = BLI_mempool_iterstep(&iter); kfv; kfv = BLI_mempool_iterstep(&iter)) {
+ if (kfv->v) {
+ continue; /* already have a BMVert */
+ }
+ for (ref = kfv->edges.first; ref; ref = ref->next) {
+ kfe = ref->ref;
+ e = kfe->e;
+ if (!e) {
+ continue;
+ }
+ list = BLI_smallhash_lookup(ehash, (uintptr_t)e);
+ if (!list) {
+ list = knife_empty_list(kcd);
+ BLI_smallhash_insert(ehash, (uintptr_t)e, list);
+ }
+ /* there can be more than one kfe in kfv's list with same e */
+ if (!find_ref(list, kfv)) {
+ knife_append_list(kcd, list, kfv);
+ }
+ }
+ }
+
+ /* split bmesh edges where needed */
+ for (list = BLI_smallhash_iternew(ehash, &hiter, (uintptr_t *)&e); list;
+ list = BLI_smallhash_iternext(&hiter, (uintptr_t *)&e)) {
+ BLI_listbase_sort_r(list, sort_verts_by_dist_cb, e->v1->co);
+
+ for (ref = list->first; ref; ref = ref->next) {
+ kfv = ref->ref;
+ pct = line_point_factor_v3(kfv->co, e->v1->co, e->v2->co);
+ kfv->v = BM_edge_split(bm, e, e->v1, &enew, pct);
+ }
+ }
+
+ if (kcd->only_select) {
+ EDBM_flag_disable_all(kcd->em, BM_ELEM_SELECT);
+ }
+
+ /* do cuts for each face */
+ for (list = BLI_smallhash_iternew(fhash, &hiter, (uintptr_t *)&f); list;
+ list = BLI_smallhash_iternext(&hiter, (uintptr_t *)&f)) {
+ knife_make_face_cuts(kcd, f, list);
+ }
+
+ BLI_smallhash_release(fhash);
+ BLI_smallhash_release(ehash);
+}
+
/* User has just left-clicked after the first time.
* Add all knife cuts implied by line from prev to curr.
* If that line crossed edges then kcd->linehits will be non-NULL.
@@ -891,7 +1447,7 @@ static void knife_add_cut(KnifeTool_OpData *kcd)
BMFace *f;
Ref *r;
GHashIterator giter;
- ListBase *lst;
+ ListBase *list;
prepare_linehits_for_cut(kcd);
if (kcd->totlinehit == 0) {
@@ -926,8 +1482,8 @@ static void knife_add_cut(KnifeTool_OpData *kcd)
* the v and the kfe or f fields will be non-NULL. */
GHASH_ITER (giter, facehits) {
f = (BMFace *)BLI_ghashIterator_getKey(&giter);
- lst = (ListBase *)BLI_ghashIterator_getValue(&giter);
- knife_cut_face(kcd, f, lst);
+ list = (ListBase *)BLI_ghashIterator_getValue(&giter);
+ knife_cut_face(kcd, f, list);
}
/* set up for next cut */
@@ -960,267 +1516,51 @@ static void knife_finish_cut(KnifeTool_OpData *kcd)
}
}
-static void knifetool_draw_angle_snapping(const KnifeTool_OpData *kcd)
-{
- float v1[3], v2[3];
- float planes[4][4];
-
- planes_from_projmat(
- (const float(*)[4])kcd->projmat, planes[2], planes[0], planes[3], planes[1], NULL, NULL);
-
- /* ray-cast all planes */
- {
- float ray_dir[3];
- float ray_hit_best[2][3] = {{UNPACK3(kcd->prev.cage)}, {UNPACK3(kcd->curr.cage)}};
- float lambda_best[2] = {-FLT_MAX, FLT_MAX};
- int i;
-
- /* we (sometimes) need the lines to be at the same depth before projecting */
-#if 0
- sub_v3_v3v3(ray_dir, kcd->curr.cage, kcd->prev.cage);
-#else
- {
- float curr_cage_adjust[3];
- float co_depth[3];
-
- copy_v3_v3(co_depth, kcd->prev.cage);
- mul_m4_v3(kcd->ob->obmat, co_depth);
- ED_view3d_win_to_3d(kcd->vc.v3d, kcd->region, co_depth, kcd->curr.mval, curr_cage_adjust);
- mul_m4_v3(kcd->ob->imat, curr_cage_adjust);
-
- sub_v3_v3v3(ray_dir, curr_cage_adjust, kcd->prev.cage);
- }
-#endif
-
- for (i = 0; i < 4; i++) {
- float ray_hit[3];
- float lambda_test;
- if (isect_ray_plane_v3(kcd->prev.cage, ray_dir, planes[i], &lambda_test, false)) {
- madd_v3_v3v3fl(ray_hit, kcd->prev.cage, ray_dir, lambda_test);
- if (lambda_test < 0.0f) {
- if (lambda_test > lambda_best[0]) {
- copy_v3_v3(ray_hit_best[0], ray_hit);
- lambda_best[0] = lambda_test;
- }
- }
- else {
- if (lambda_test < lambda_best[1]) {
- copy_v3_v3(ray_hit_best[1], ray_hit);
- lambda_best[1] = lambda_test;
- }
- }
- }
- }
-
- copy_v3_v3(v1, ray_hit_best[0]);
- copy_v3_v3(v2, ray_hit_best[1]);
- }
-
- uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
-
- immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
- immUniformThemeColor3(TH_TRANSFORM);
- GPU_line_width(2.0);
-
- immBegin(GPU_PRIM_LINES, 2);
- immVertex3fv(pos, v1);
- immVertex3fv(pos, v2);
- immEnd();
+/** \} */
- immUnbindProgram();
-}
-
-static void knife_init_colors(KnifeColors *colors)
-{
- /* possible BMESH_TODO: add explicit themes or calculate these by
- * figuring out contrasting colors with grid / edges / verts
- * a la UI_make_axis_color */
- UI_GetThemeColorType3ubv(TH_NURB_VLINE, SPACE_VIEW3D, colors->line);
- UI_GetThemeColorType3ubv(TH_NURB_ULINE, SPACE_VIEW3D, colors->edge);
- UI_GetThemeColorType3ubv(TH_HANDLE_SEL_VECT, SPACE_VIEW3D, colors->curpoint);
- UI_GetThemeColorType3ubv(TH_HANDLE_SEL_VECT, SPACE_VIEW3D, colors->curpoint_a);
- colors->curpoint_a[3] = 102;
- UI_GetThemeColorType3ubv(TH_ACTIVE_SPLINE, SPACE_VIEW3D, colors->point);
- UI_GetThemeColorType3ubv(TH_ACTIVE_SPLINE, SPACE_VIEW3D, colors->point_a);
- colors->point_a[3] = 102;
-}
+/* -------------------------------------------------------------------- */
+/** \name Screen Line Hits (#knife_find_line_hits)
+ * \{ */
-/* modal loop selection drawing callback */
-static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), void *arg)
+/* Record the index in kcd->em->looptris of first looptri triple for a given face,
+ * given an index for some triple in that array.
+ * This assumes that all of the triangles for a given face are contiguous
+ * in that array (as they are by the current tessellation routines).
+ * Actually store index + 1 in the hash, because 0 looks like "no entry"
+ * to hash lookup routine; will reverse this in the get routine.
+ * Doing this lazily rather than all at once for all faces.
+ */
+static void set_lowest_face_tri(KnifeTool_OpData *kcd, BMFace *f, int index)
{
- const KnifeTool_OpData *kcd = arg;
- GPU_depth_test(GPU_DEPTH_NONE);
-
- GPU_matrix_push_projection();
- GPU_polygon_offset(1.0f, 1.0f);
-
- GPU_matrix_push();
- GPU_matrix_mul(kcd->ob->obmat);
-
- if (kcd->mode == MODE_DRAGGING && kcd->is_angle_snapping) {
- knifetool_draw_angle_snapping(kcd);
- }
-
- GPUVertFormat *format = immVertexFormat();
- uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
-
- immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
-
- if (kcd->mode == MODE_DRAGGING) {
- immUniformColor3ubv(kcd->colors.line);
- GPU_line_width(2.0);
-
- immBegin(GPU_PRIM_LINES, 2);
- immVertex3fv(pos, kcd->prev.cage);
- immVertex3fv(pos, kcd->curr.cage);
- immEnd();
- }
-
- if (kcd->prev.vert) {
- immUniformColor3ubv(kcd->colors.point);
- GPU_point_size(11);
-
- immBegin(GPU_PRIM_POINTS, 1);
- immVertex3fv(pos, kcd->prev.cage);
- immEnd();
- }
-
- if (kcd->prev.bmface) {
- immUniformColor3ubv(kcd->colors.curpoint);
- GPU_point_size(9);
-
- immBegin(GPU_PRIM_POINTS, 1);
- immVertex3fv(pos, kcd->prev.cage);
- immEnd();
- }
-
- if (kcd->curr.edge) {
- immUniformColor3ubv(kcd->colors.edge);
- GPU_line_width(2.0);
-
- immBegin(GPU_PRIM_LINES, 2);
- immVertex3fv(pos, kcd->curr.edge->v1->cageco);
- immVertex3fv(pos, kcd->curr.edge->v2->cageco);
- immEnd();
- }
- else if (kcd->curr.vert) {
- immUniformColor3ubv(kcd->colors.point);
- GPU_point_size(11);
-
- immBegin(GPU_PRIM_POINTS, 1);
- immVertex3fv(pos, kcd->curr.cage);
- immEnd();
- }
-
- if (kcd->curr.bmface) {
- immUniformColor3ubv(kcd->colors.curpoint);
- GPU_point_size(9);
-
- immBegin(GPU_PRIM_POINTS, 1);
- immVertex3fv(pos, kcd->curr.cage);
- immEnd();
- }
-
- if (kcd->totlinehit > 0) {
- KnifeLineHit *lh;
- int i, snapped_verts_count, other_verts_count;
- float fcol[4];
-
- GPU_blend(GPU_BLEND_ALPHA);
-
- GPUVertBuf *vert = GPU_vertbuf_create_with_format(format);
- GPU_vertbuf_data_alloc(vert, kcd->totlinehit);
-
- lh = kcd->linehits;
- for (i = 0, snapped_verts_count = 0, other_verts_count = 0; i < kcd->totlinehit; i++, lh++) {
- if (lh->v) {
- GPU_vertbuf_attr_set(vert, pos, snapped_verts_count++, lh->cagehit);
- }
- else {
- GPU_vertbuf_attr_set(vert, pos, kcd->totlinehit - 1 - other_verts_count++, lh->cagehit);
- }
- }
-
- GPUBatch *batch = GPU_batch_create_ex(GPU_PRIM_POINTS, vert, NULL, GPU_BATCH_OWNS_VBO);
- GPU_batch_program_set_builtin(batch, GPU_SHADER_3D_UNIFORM_COLOR);
-
- /* draw any snapped verts first */
- rgba_uchar_to_float(fcol, kcd->colors.point_a);
- GPU_batch_uniform_4fv(batch, "color", fcol);
- GPU_point_size(11);
- if (snapped_verts_count > 0) {
- GPU_batch_draw_range(batch, 0, snapped_verts_count);
- }
-
- /* now draw the rest */
- rgba_uchar_to_float(fcol, kcd->colors.curpoint_a);
- GPU_batch_uniform_4fv(batch, "color", fcol);
- GPU_point_size(7);
- if (other_verts_count > 0) {
- GPU_batch_draw_range(batch, snapped_verts_count, other_verts_count);
- }
-
- GPU_batch_discard(batch);
+ int i;
- GPU_blend(GPU_BLEND_NONE);
+ if (BLI_ghash_lookup(kcd->facetrimap, f)) {
+ return;
}
- if (kcd->totkedge > 0) {
- BLI_mempool_iter iter;
- KnifeEdge *kfe;
-
- immUniformColor3ubv(kcd->colors.line);
- GPU_line_width(1.0);
-
- GPUBatch *batch = immBeginBatchAtMost(GPU_PRIM_LINES, BLI_mempool_len(kcd->kedges) * 2);
-
- BLI_mempool_iternew(kcd->kedges, &iter);
- for (kfe = BLI_mempool_iterstep(&iter); kfe; kfe = BLI_mempool_iterstep(&iter)) {
- if (!kfe->is_cut) {
- continue;
- }
-
- immVertex3fv(pos, kfe->v1->cageco);
- immVertex3fv(pos, kfe->v2->cageco);
+ BLI_assert(index >= 0 && index < kcd->em->tottri);
+ BLI_assert(kcd->em->looptris[index][0]->f == f);
+ for (i = index - 1; i >= 0; i--) {
+ if (kcd->em->looptris[i][0]->f != f) {
+ i++;
+ break;
}
-
- immEnd();
-
- GPU_batch_draw(batch);
- GPU_batch_discard(batch);
}
-
- if (kcd->totkvert > 0) {
- BLI_mempool_iter iter;
- KnifeVert *kfv;
-
- immUniformColor3ubv(kcd->colors.point);
- GPU_point_size(5.0);
-
- GPUBatch *batch = immBeginBatchAtMost(GPU_PRIM_POINTS, BLI_mempool_len(kcd->kverts));
-
- BLI_mempool_iternew(kcd->kverts, &iter);
- for (kfv = BLI_mempool_iterstep(&iter); kfv; kfv = BLI_mempool_iterstep(&iter)) {
- if (!kfv->is_cut) {
- continue;
- }
-
- immVertex3fv(pos, kfv->cageco);
- }
-
- immEnd();
-
- GPU_batch_draw(batch);
- GPU_batch_discard(batch);
+ if (i == -1) {
+ i++;
}
- immUnbindProgram();
+ BLI_ghash_insert(kcd->facetrimap, f, POINTER_FROM_INT(i + 1));
+}
- GPU_matrix_pop();
- GPU_matrix_pop_projection();
+/* This should only be called for faces that have had a lowest face tri set by previous function */
+static int get_lowest_face_tri(KnifeTool_OpData *kcd, BMFace *f)
+{
+ int ans;
- /* Reset default */
- GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
+ ans = POINTER_AS_INT(BLI_ghash_lookup(kcd->facetrimap, f));
+ BLI_assert(ans != 0);
+ return ans - 1;
}
/**
@@ -1246,7 +1586,7 @@ static bool knife_ray_intersect_face(KnifeTool_OpData *kcd,
float se1[2], se2[2];
float d, lambda;
BMLoop **tri;
- ListBase *lst;
+ ListBase *list;
Ref *ref;
KnifeEdge *kfe;
@@ -1281,8 +1621,8 @@ static bool knife_ray_intersect_face(KnifeTool_OpData *kcd,
}
interp_v3_v3v3v3_uv(hit_cageco, lv1, lv2, lv3, ray_tri_uv);
/* Now check that far enough away from verts and edges */
- lst = knife_get_face_kedges(kcd, f);
- for (ref = lst->first; ref; ref = ref->next) {
+ list = knife_get_face_kedges(kcd, f);
+ for (ref = list->first; ref; ref = ref->next) {
kfe = ref->ref;
knife_project_v2(kcd, kfe->v1->cageco, se1);
knife_project_v2(kcd, kfe->v2->cageco, se2);
@@ -1323,59 +1663,6 @@ static void calc_ortho_extent(KnifeTool_OpData *kcd)
mid_v3_v3v3(kcd->ortho_extent_center, min, max);
}
-static BMElem *bm_elem_from_knife_vert(KnifeVert *kfv, KnifeEdge **r_kfe)
-{
- BMElem *ele_test;
- KnifeEdge *kfe = NULL;
-
- /* vert? */
- ele_test = (BMElem *)kfv->v;
-
- if (r_kfe || ele_test == NULL) {
- if (kfv->v == NULL) {
- Ref *ref;
- for (ref = kfv->edges.first; ref; ref = ref->next) {
- kfe = ref->ref;
- if (kfe->e) {
- if (r_kfe) {
- *r_kfe = kfe;
- }
- break;
- }
- }
- }
- }
-
- /* edge? */
- if (ele_test == NULL) {
- if (kfe) {
- ele_test = (BMElem *)kfe->e;
- }
- }
-
- /* face? */
- if (ele_test == NULL) {
- if (BLI_listbase_is_single(&kfe->faces)) {
- ele_test = ((Ref *)kfe->faces.first)->ref;
- }
- }
-
- return ele_test;
-}
-
-static BMElem *bm_elem_from_knife_edge(KnifeEdge *kfe)
-{
- BMElem *ele_test;
-
- ele_test = (BMElem *)kfe->e;
-
- if (ele_test == NULL) {
- ele_test = (BMElem *)kfe->basef;
- }
-
- return ele_test;
-}
-
/* Do edges e1 and e2 go between exactly the same coordinates? */
static bool coinciding_edges(BMEdge *e1, BMEdge *e2)
{
@@ -1458,9 +1745,9 @@ static bool point_is_visible(KnifeTool_OpData *kcd,
float view[3], p_ofs[3];
/* TODO: I think there's a simpler way to get the required raycast ray */
- ED_view3d_unproject(kcd->vc.region, s[0], s[1], 0.0f, view);
+ ED_view3d_unproject_v3(kcd->vc.region, s[0], s[1], 0.0f, view);
- mul_m4_v3(kcd->ob->imat, view);
+ mul_m4_v3(kcd->ob_imat, view);
/* make p_ofs a little towards view, so ray doesn't hit p's face. */
sub_v3_v3(view, p);
@@ -1546,7 +1833,7 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd)
BMFace *f;
KnifeEdge *kfe;
KnifeVert *v;
- ListBase *lst;
+ ListBase *list;
Ref *ref;
KnifeLineHit *linehits = NULL;
BLI_array_declare(linehits);
@@ -1591,10 +1878,10 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd)
ED_view3d_win_to_segment_clipped(kcd->vc.depsgraph, kcd->region, kcd->vc.v3d, s1, v1, v3, true);
ED_view3d_win_to_segment_clipped(kcd->vc.depsgraph, kcd->region, kcd->vc.v3d, s2, v2, v4, true);
- mul_m4_v3(kcd->ob->imat, v1);
- mul_m4_v3(kcd->ob->imat, v2);
- mul_m4_v3(kcd->ob->imat, v3);
- mul_m4_v3(kcd->ob->imat, v4);
+ mul_m4_v3(kcd->ob_imat, v1);
+ mul_m4_v3(kcd->ob_imat, v2);
+ mul_m4_v3(kcd->ob_imat, v3);
+ mul_m4_v3(kcd->ob_imat, v4);
/* Numeric error, 'v1' -> 'v2', 'v2' -> 'v4'
* can end up being ~2000 units apart with an orthogonal perspective.
@@ -1650,8 +1937,8 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd)
/* don't care what the value is except that it is non-NULL, for iterator */
BLI_smallhash_insert(&faces, (uintptr_t)f, f);
- lst = knife_get_face_kedges(kcd, f);
- for (ref = lst->first; ref; ref = ref->next) {
+ list = knife_get_face_kedges(kcd, f);
+ for (ref = list->first; ref; ref = ref->next) {
kfe = ref->ref;
if (BLI_smallhash_haskey(&kfes, (uintptr_t)kfe)) {
continue;
@@ -1853,28 +2140,31 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd)
BLI_smallhash_release(&faces);
BLI_smallhash_release(&kfes);
BLI_smallhash_release(&kfvs);
- if (results) {
- MEM_freeN(results);
- }
+ MEM_freeN(results);
}
-static void knife_input_ray_segment(KnifeTool_OpData *kcd,
- const float mval[2],
- const float ofs,
- float r_origin[3],
- float r_origin_ofs[3])
-{
- /* unproject to find view ray */
- ED_view3d_unproject(kcd->vc.region, mval[0], mval[1], 0.0f, r_origin);
- ED_view3d_unproject(kcd->vc.region, mval[0], mval[1], ofs, r_origin_ofs);
+/** \} */
- /* transform into object space */
- invert_m4_m4(kcd->ob->imat, kcd->ob->obmat);
+/* -------------------------------------------------------------------- */
+/** \name KnifePosData Utils
+ * \{ */
- mul_m4_v3(kcd->ob->imat, r_origin);
- mul_m4_v3(kcd->ob->imat, r_origin_ofs);
+static void knife_pos_data_clear(KnifePosData *kpd)
+{
+ zero_v3(kpd->co);
+ zero_v3(kpd->cage);
+ kpd->vert = NULL;
+ kpd->edge = NULL;
+ kpd->bmface = NULL;
+ zero_v2(kpd->mval);
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Snapping (#knife_snap_update_from_mval)
+ * \{ */
+
static BMFace *knife_find_closest_face(KnifeTool_OpData *kcd,
float co[3],
float cageco[3],
@@ -1917,6 +2207,8 @@ static BMFace *knife_find_closest_face(KnifeTool_OpData *kcd,
* of a true coordinate on the face.
* This just puts a point 1.0f in front of the view. */
add_v3_v3v3(co, origin, ray);
+ /* Use this value for the cage location too as it's used to find near edges/vertices. */
+ copy_v3_v3(cageco, co);
}
}
@@ -1926,64 +2218,119 @@ static BMFace *knife_find_closest_face(KnifeTool_OpData *kcd,
/**
* Find the 2d screen space density of vertices within a radius.
* Used to scale snapping distance for picking edges/verts.
+ *
+ * Arguments `f` and `cageco` should be the result of a call to #knife_find_closest_face.
*/
-static int knife_sample_screen_density(KnifeTool_OpData *kcd, const float radius)
+static int knife_sample_screen_density_from_closest_face(KnifeTool_OpData *kcd,
+ const float radius,
+ BMFace *f,
+ const float cageco[3])
{
- BMFace *f;
- bool is_space;
- float co[3], cageco[3], sco[2];
-
- BLI_assert(kcd->is_interactive == true);
-
- f = knife_find_closest_face(kcd, co, cageco, &is_space);
-
- if (f && !is_space) {
- const float radius_sq = radius * radius;
- ListBase *lst;
- Ref *ref;
- float dis_sq;
- int c = 0;
+ const float radius_sq = radius * radius;
+ ListBase *list;
+ Ref *ref;
+ float sco[2];
+ float dis_sq;
+ int c = 0;
- knife_project_v2(kcd, cageco, sco);
+ knife_project_v2(kcd, cageco, sco);
- lst = knife_get_face_kedges(kcd, f);
- for (ref = lst->first; ref; ref = ref->next) {
- KnifeEdge *kfe = ref->ref;
- int i;
+ list = knife_get_face_kedges(kcd, f);
+ for (ref = list->first; ref; ref = ref->next) {
+ KnifeEdge *kfe = ref->ref;
+ int i;
- for (i = 0; i < 2; i++) {
- KnifeVert *kfv = i ? kfe->v2 : kfe->v1;
- float kfv_sco[2];
+ for (i = 0; i < 2; i++) {
+ KnifeVert *kfv = i ? kfe->v2 : kfe->v1;
+ float kfv_sco[2];
- knife_project_v2(kcd, kfv->cageco, kfv_sco);
+ knife_project_v2(kcd, kfv->cageco, kfv_sco);
- dis_sq = len_squared_v2v2(kfv_sco, sco);
- if (dis_sq < radius_sq) {
- if (RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d)) {
- if (ED_view3d_clipping_test(kcd->vc.rv3d, kfv->cageco, true) == 0) {
- c++;
- }
- }
- else {
+ dis_sq = len_squared_v2v2(kfv_sco, sco);
+ if (dis_sq < radius_sq) {
+ if (RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d)) {
+ if (ED_view3d_clipping_test(kcd->vc.rv3d, kfv->cageco, true) == 0) {
c++;
}
}
+ else {
+ c++;
+ }
}
}
-
- return c;
}
- return 0;
+ return c;
}
-/* returns snapping distance for edges/verts, scaled by the density of the
- * surrounding mesh (in screen space)*/
+/**
+ * \return the snapping distance for edges/verts, scaled by the density of the
+ * surrounding mesh (in screen space).
+ *
+ * \note Face values in `kcd->curr` must be up to date.
+ */
static float knife_snap_size(KnifeTool_OpData *kcd, float maxsize)
{
- float density = (float)knife_sample_screen_density(kcd, maxsize * 2.0f);
+ BLI_assert(kcd->is_interactive == true);
+ int density = 0;
+
+ if (!kcd->curr.is_space) {
+ density = (float)knife_sample_screen_density_from_closest_face(
+ kcd, maxsize * 2.0f, kcd->curr.bmface, kcd->curr.cage);
+ }
+
+ return density ? min_ff(maxsize / ((float)density * 0.5f), maxsize) : maxsize;
+}
+
+/* Snap to edge in a specified angle.
+ * Returns 'lambda' calculated (in screen-space). */
+static bool knife_snap_edge_in_angle(KnifeTool_OpData *kcd,
+ const float sco[3],
+ const float kfv1_sco[2],
+ const float kfv2_sco[2],
+ float *r_dist_sq,
+ float *r_lambda)
+{
+ /* if snapping, check we're in bounds */
+ float sco_snap[2];
+ isect_line_line_v2_point(kfv1_sco, kfv2_sco, kcd->prev.mval, kcd->curr.mval, sco_snap);
+ float lambda = line_point_factor_v2(sco_snap, kfv1_sco, kfv2_sco);
+
+ /* be strict about angle-snapping within edge */
+ if ((lambda < 0.0f - KNIFE_FLT_EPSBIG) || (lambda > 1.0f + KNIFE_FLT_EPSBIG)) {
+ return false;
+ }
- return min_ff(maxsize / (density * 0.5f), maxsize);
+ float dis_sq = len_squared_v2v2(sco, sco_snap);
+ if (dis_sq < *r_dist_sq) {
+ *r_dist_sq = dis_sq;
+ *r_lambda = lambda;
+ return true;
+ }
+ return false;
+}
+
+/* use when lambda is in screen-space */
+static void knife_interp_v3_v3v3(const KnifeTool_OpData *kcd,
+ float r_co[3],
+ const float v1[3],
+ const float v2[3],
+ float lambda_ss)
+{
+ if (kcd->is_ortho) {
+ interp_v3_v3v3(r_co, v1, v2, lambda_ss);
+ }
+ else {
+ /* transform into screen-space, interp, then transform back */
+ float v1_ss[3], v2_ss[3];
+
+ mul_v3_project_m4_v3(v1_ss, (float(*)[4])kcd->projmat, v1);
+ mul_v3_project_m4_v3(v2_ss, (float(*)[4])kcd->projmat, v2);
+
+ interp_v3_v3v3(r_co, v1_ss, v2_ss, lambda_ss);
+
+ mul_project_m4_v3((float(*)[4])kcd->projmat_inv, r_co);
+ }
}
/* p is closest point on edge to the mouse cursor */
@@ -2009,15 +2356,15 @@ static KnifeEdge *knife_find_closest_edge_of_face(KnifeTool_OpData *kcd,
const float maxdist_sq = maxdist * maxdist;
KnifeEdge *cure = NULL;
float cur_cagep[3];
- ListBase *lst;
+ ListBase *list;
Ref *ref;
- float dis_sq, curdis_sq = FLT_MAX;
+ float dis_sq, curdis_sq = maxdist_sq;
knife_project_v2(kcd, cagep, sco);
/* look through all edges associated with this face */
- lst = knife_get_face_kedges(kcd, f);
- for (ref = lst->first; ref; ref = ref->next) {
+ list = knife_get_face_kedges(kcd, f);
+ for (ref = list->first; ref; ref = ref->next) {
KnifeEdge *kfe = ref->ref;
float kfv1_sco[2], kfv2_sco[2], test_cagep[3];
float lambda;
@@ -2028,27 +2375,14 @@ static KnifeEdge *knife_find_closest_edge_of_face(KnifeTool_OpData *kcd,
/* check if we're close enough and calculate 'lambda' */
if (kcd->is_angle_snapping) {
- /* if snapping, check we're in bounds */
- float sco_snap[2];
- isect_line_line_v2_point(kfv1_sco, kfv2_sco, kcd->prev.mval, kcd->curr.mval, sco_snap);
- lambda = line_point_factor_v2(sco_snap, kfv1_sco, kfv2_sco);
-
- /* be strict about angle-snapping within edge */
- if ((lambda < 0.0f - KNIFE_FLT_EPSBIG) || (lambda > 1.0f + KNIFE_FLT_EPSBIG)) {
- continue;
- }
-
- dis_sq = len_squared_v2v2(sco, sco_snap);
- if (dis_sq < curdis_sq && dis_sq < maxdist_sq) {
- /* we already have 'lambda' */
- }
- else {
+ dis_sq = curdis_sq;
+ if (!knife_snap_edge_in_angle(kcd, sco, kfv1_sco, kfv2_sco, &dis_sq, &lambda)) {
continue;
}
}
else {
dis_sq = dist_squared_to_line_segment_v2(sco, kfv1_sco, kfv2_sco);
- if (dis_sq < curdis_sq && dis_sq < maxdist_sq) {
+ if (dis_sq < curdis_sq) {
lambda = line_point_factor_v2(sco, kfv1_sco, kfv2_sco);
}
else {
@@ -2071,36 +2405,31 @@ static KnifeEdge *knife_find_closest_edge_of_face(KnifeTool_OpData *kcd,
copy_v3_v3(cur_cagep, test_cagep);
}
- if (cure) {
- if (!kcd->ignore_edge_snapping || !(cure->e)) {
- KnifeVert *edgesnap = NULL;
+ if (cure && !kcd->ignore_edge_snapping) {
+ KnifeVert *edgesnap = NULL;
- if (kcd->snap_midpoints) {
- mid_v3_v3v3(p, cure->v1->co, cure->v2->co);
- mid_v3_v3v3(cagep, cure->v1->cageco, cure->v2->cageco);
- }
- else {
- float lambda = line_point_factor_v3(cur_cagep, cure->v1->cageco, cure->v2->cageco);
- copy_v3_v3(cagep, cur_cagep);
- interp_v3_v3v3(p, cure->v1->co, cure->v2->co, lambda);
- }
-
- /* update mouse coordinates to the snapped-to edge's screen coordinates
- * this is important for angle snap, which uses the previous mouse position */
- edgesnap = new_knife_vert(kcd, p, cagep);
- knife_project_v2(kcd, edgesnap->cageco, kcd->curr.mval);
+ if (kcd->snap_midpoints) {
+ mid_v3_v3v3(p, cure->v1->co, cure->v2->co);
+ mid_v3_v3v3(cagep, cure->v1->cageco, cure->v2->cageco);
}
else {
- return NULL;
+ float lambda = line_point_factor_v3(cur_cagep, cure->v1->cageco, cure->v2->cageco);
+ copy_v3_v3(cagep, cur_cagep);
+ interp_v3_v3v3(p, cure->v1->co, cure->v2->co, lambda);
}
+
+ /* update mouse coordinates to the snapped-to edge's screen coordinates
+ * this is important for angle snap, which uses the previous mouse position */
+ edgesnap = new_knife_vert(kcd, p, cagep);
+ knife_project_v2(kcd, edgesnap->cageco, kcd->curr.mval);
}
return cure;
}
/* find a vertex near the mouse cursor, if it exists */
-static KnifeVert *knife_find_closest_vert_of_face(KnifeTool_OpData *kcd,
- BMFace *f,
+static KnifeVert *knife_find_closest_vert_of_edge(KnifeTool_OpData *kcd,
+ KnifeEdge *kfe,
float p[3],
float cagep[3])
{
@@ -2118,60 +2447,48 @@ static KnifeVert *knife_find_closest_vert_of_face(KnifeTool_OpData *kcd,
}
const float maxdist_sq = maxdist * maxdist;
- ListBase *lst;
- Ref *ref;
KnifeVert *curv = NULL;
float cur_kfv_sco[2];
float dis_sq, curdis_sq = FLT_MAX;
knife_project_v2(kcd, cagep, sco);
- lst = knife_get_face_kedges(kcd, f);
- for (ref = lst->first; ref; ref = ref->next) {
- KnifeEdge *kfe = ref->ref;
- int i;
-
- for (i = 0; i < 2; i++) {
- KnifeVert *kfv = i ? kfe->v2 : kfe->v1;
- float kfv_sco[2];
+ for (int i = 0; i < 2; i++) {
+ KnifeVert *kfv = i ? kfe->v2 : kfe->v1;
+ float kfv_sco[2];
- knife_project_v2(kcd, kfv->cageco, kfv_sco);
+ knife_project_v2(kcd, kfv->cageco, kfv_sco);
- /* be strict about angle snapping, the vertex needs to be very close to the angle,
- * or we ignore */
- if (kcd->is_angle_snapping) {
- if (dist_squared_to_line_segment_v2(kfv_sco, kcd->prev.mval, kcd->curr.mval) >
- KNIFE_FLT_EPSBIG) {
- continue;
- }
+ /* be strict about angle snapping, the vertex needs to be very close to the angle,
+ * or we ignore */
+ if (kcd->is_angle_snapping) {
+ if (dist_squared_to_line_segment_v2(kfv_sco, kcd->prev.mval, kcd->curr.mval) >
+ KNIFE_FLT_EPSBIG) {
+ continue;
}
+ }
- dis_sq = len_squared_v2v2(kfv_sco, sco);
- if (dis_sq < curdis_sq && dis_sq < maxdist_sq) {
- if (!RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d) ||
- !ED_view3d_clipping_test(kcd->vc.rv3d, kfv->cageco, true)) {
- curv = kfv;
- curdis_sq = dis_sq;
- copy_v2_v2(cur_kfv_sco, kfv_sco);
- }
+ dis_sq = len_squared_v2v2(kfv_sco, sco);
+ if (dis_sq < curdis_sq && dis_sq < maxdist_sq) {
+ if (!RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d) ||
+ !ED_view3d_clipping_test(kcd->vc.rv3d, kfv->cageco, true)) {
+ curv = kfv;
+ curdis_sq = dis_sq;
+ copy_v2_v2(cur_kfv_sco, kfv_sco);
}
}
}
- if (!kcd->ignore_vert_snapping || !(curv && curv->v)) {
- if (curv) {
- copy_v3_v3(p, curv->co);
- copy_v3_v3(cagep, curv->cageco);
-
- /* update mouse coordinates to the snapped-to vertex's screen coordinates
- * this is important for angle snap, which uses the previous mouse position */
- copy_v2_v2(kcd->curr.mval, cur_kfv_sco);
- }
+ if (curv && !kcd->ignore_vert_snapping) {
+ copy_v3_v3(p, curv->co);
+ copy_v3_v3(cagep, curv->cageco);
- return curv;
+ /* update mouse coordinates to the snapped-to vertex's screen coordinates
+ * this is important for angle snap, which uses the previous mouse position */
+ copy_v2_v2(kcd->curr.mval, cur_kfv_sco);
}
- return NULL;
+ return curv;
}
/**
@@ -2215,7 +2532,15 @@ static bool knife_snap_angle(KnifeTool_OpData *kcd)
return true;
}
-static void knife_snap_update_from_mval(KnifeTool_OpData *kcd, const float mval[2])
+/**
+ * \return true when `kcd->curr.co` & `kcd->curr.cage` are set.
+ *
+ * In this case `is_space` is nearly always false.
+ * There are some situations when vertex or edge can be snapped to, when `is_space` is true.
+ * In this case the selection-buffer is used to select the face,
+ * then the closest `vert` or `edge` is set, and those will enable `is_co_set`.
+ */
+static bool knife_snap_update_from_mval(KnifeTool_OpData *kcd, const float mval[2])
{
knife_pos_data_clear(&kcd->curr);
copy_v2_v2(kcd->curr.mval, mval);
@@ -2230,344 +2555,143 @@ static void knife_snap_update_from_mval(KnifeTool_OpData *kcd, const float mval[
kcd->is_angle_snapping = false;
}
- kcd->curr.bmface = knife_find_closest_face(
- kcd, kcd->curr.co, kcd->curr.cage, &kcd->curr.is_space);
-
- if (kcd->curr.bmface) {
- kcd->curr.vert = knife_find_closest_vert_of_face(
- kcd, kcd->curr.bmface, kcd->curr.co, kcd->curr.cage);
+ {
+ kcd->curr.bmface = knife_find_closest_face(
+ kcd, kcd->curr.co, kcd->curr.cage, &kcd->curr.is_space);
- if (!kcd->curr.vert &&
- /* no edge snapping while dragging (edges are too sticky when cuts are immediate) */
- !kcd->is_drag_hold) {
+ if (kcd->curr.bmface) {
kcd->curr.edge = knife_find_closest_edge_of_face(
kcd, kcd->curr.bmface, kcd->curr.co, kcd->curr.cage);
}
- }
-}
-/* update active knife edge/vert pointers */
-static int knife_update_active(KnifeTool_OpData *kcd)
-{
- knife_snap_update_from_mval(kcd, kcd->mval);
+ if (kcd->curr.edge) {
+ kcd->curr.vert = knife_find_closest_vert_of_edge(
+ kcd, kcd->curr.edge, kcd->curr.co, kcd->curr.cage);
- /* if no hits are found this would normally default to (0, 0, 0) so instead
- * get a point at the mouse ray closest to the previous point.
- * Note that drawing lines in `free-space` isn't properly supported
- * but there's no guarantee (0, 0, 0) has any geometry either - campbell */
- if (kcd->curr.vert == NULL && kcd->curr.edge == NULL && kcd->curr.bmface == NULL) {
- float origin[3];
- float origin_ofs[3];
-
- knife_input_ray_segment(kcd, kcd->curr.mval, 1.0f, origin, origin_ofs);
-
- if (!isect_line_plane_v3(
- kcd->curr.cage, origin, origin_ofs, kcd->prev.cage, kcd->proj_zaxis)) {
- copy_v3_v3(kcd->curr.cage, kcd->prev.cage);
-
- /* should never fail! */
- BLI_assert(0);
+ if (kcd->ignore_edge_snapping) {
+ kcd->curr.edge = NULL;
+ }
}
}
- if (kcd->mode == MODE_DRAGGING) {
- knife_find_line_hits(kcd);
- }
- return 1;
+ return kcd->curr.vert || kcd->curr.edge || (kcd->curr.bmface && !kcd->curr.is_space);
}
-static int sort_verts_by_dist_cb(void *co_p, const void *cur_a_p, const void *cur_b_p)
-{
- const KnifeVert *cur_a = ((const Ref *)cur_a_p)->ref;
- const KnifeVert *cur_b = ((const Ref *)cur_b_p)->ref;
- const float *co = co_p;
- const float a_sq = len_squared_v3v3(co, cur_a->co);
- const float b_sq = len_squared_v3v3(co, cur_b->co);
+/** \} */
- if (a_sq < b_sq) {
- return -1;
- }
- if (a_sq > b_sq) {
- return 1;
- }
- return 0;
-}
+/* -------------------------------------------------------------------- */
+/** \name #KnifeTool_OpData (#op->customdata) Init and Free
+ * \{ */
-static bool knife_verts_edge_in_face(KnifeVert *v1, KnifeVert *v2, BMFace *f)
+static void knifetool_init_bmbvh(KnifeTool_OpData *kcd)
{
- bool v1_inside, v2_inside;
- bool v1_inface, v2_inface;
- BMLoop *l1, *l2;
-
- if (!f || !v1 || !v2) {
- return false;
- }
+ BM_mesh_elem_index_ensure(kcd->em->bm, BM_VERT);
- l1 = v1->v ? BM_face_vert_share_loop(f, v1->v) : NULL;
- l2 = v2->v ? BM_face_vert_share_loop(f, v2->v) : NULL;
+ Scene *scene_eval = (Scene *)DEG_get_evaluated_id(kcd->vc.depsgraph, &kcd->scene->id);
+ Object *obedit_eval = (Object *)DEG_get_evaluated_id(kcd->vc.depsgraph, &kcd->ob->id);
+ BMEditMesh *em_eval = BKE_editmesh_from_object(obedit_eval);
- if ((l1 && l2) && BM_loop_is_adjacent(l1, l2)) {
- /* boundary-case, always false to avoid edge-in-face checks below */
- return false;
- }
+ kcd->cagecos = (const float(*)[3])BKE_editmesh_vert_coords_alloc(
+ kcd->vc.depsgraph, em_eval, scene_eval, obedit_eval, NULL);
- /* find out if v1 and v2, if set, are part of the face */
- v1_inface = (l1 != NULL);
- v2_inface = (l2 != NULL);
+ kcd->bmbvh = BKE_bmbvh_new_from_editmesh(
+ kcd->em,
+ BMBVH_RETURN_ORIG |
+ ((kcd->only_select && kcd->cut_through) ? BMBVH_RESPECT_SELECT : BMBVH_RESPECT_HIDDEN),
+ kcd->cagecos,
+ false);
+}
- /* BM_face_point_inside_test uses best-axis projection so this isn't most accurate test... */
- v1_inside = v1_inface ? false : BM_face_point_inside_test(f, v1->co);
- v2_inside = v2_inface ? false : BM_face_point_inside_test(f, v2->co);
- if ((v1_inface && v2_inside) || (v2_inface && v1_inside) || (v1_inside && v2_inside)) {
- return true;
+static void knifetool_free_bmbvh(KnifeTool_OpData *kcd)
+{
+ if (kcd->bmbvh) {
+ BKE_bmbvh_free(kcd->bmbvh);
+ kcd->bmbvh = NULL;
}
- if (v1_inface && v2_inface) {
- float mid[3];
- /* Can have case where v1 and v2 are on shared chain between two faces.
- * BM_face_splits_check_legal does visibility and self-intersection tests,
- * but it is expensive and maybe a bit buggy, so use a simple
- * "is the midpoint in the face" test */
- mid_v3_v3v3(mid, v1->co, v2->co);
- return BM_face_point_inside_test(f, mid);
+ if (kcd->cagecos) {
+ MEM_freeN((void *)kcd->cagecos);
+ kcd->cagecos = NULL;
}
- return false;
}
-static void knife_make_face_cuts(KnifeTool_OpData *kcd, BMFace *f, ListBase *kfedges)
+static void knife_init_colors(KnifeColors *colors)
{
- BMesh *bm = kcd->em->bm;
- KnifeEdge *kfe;
- Ref *ref;
- int edge_array_len = BLI_listbase_count(kfedges);
- int i;
-
- BMEdge **edge_array = BLI_array_alloca(edge_array, edge_array_len);
-
- /* point to knife edges we've created edges in, edge_array aligned */
- KnifeEdge **kfe_array = BLI_array_alloca(kfe_array, edge_array_len);
-
- BLI_assert(BLI_gset_len(kcd->edgenet.edge_visit) == 0);
-
- i = 0;
- for (ref = kfedges->first; ref; ref = ref->next) {
- bool is_new_edge = false;
- kfe = ref->ref;
-
- if (kfe->e == NULL) {
- if (kfe->v1->v && kfe->v2->v) {
- kfe->e = BM_edge_exists(kfe->v1->v, kfe->v2->v);
- }
- }
-
- if (kfe->e) {
- if (BM_edge_in_face(kfe->e, f)) {
- /* shouldn't happen, but in this case - just ignore */
- continue;
- }
- }
- else {
- if (kfe->v1->v == NULL) {
- kfe->v1->v = BM_vert_create(bm, kfe->v1->co, NULL, 0);
- }
- if (kfe->v2->v == NULL) {
- kfe->v2->v = BM_vert_create(bm, kfe->v2->co, NULL, 0);
- }
- BLI_assert(kfe->e == NULL);
- kfe->e = BM_edge_create(bm, kfe->v1->v, kfe->v2->v, NULL, 0);
- if (kfe->e) {
- if (kcd->select_result || BM_elem_flag_test(f, BM_ELEM_SELECT)) {
- BM_edge_select_set(bm, kfe->e, true);
- }
- is_new_edge = true;
- }
- }
-
- BLI_assert(kfe->e);
-
- if (BLI_gset_add(kcd->edgenet.edge_visit, kfe->e)) {
- kfe_array[i] = is_new_edge ? kfe : 0;
- edge_array[i] = kfe->e;
- i += 1;
- }
- }
+ /* possible BMESH_TODO: add explicit themes or calculate these by
+ * figuring out contrasting colors with grid / edges / verts
+ * a la UI_make_axis_color */
+ UI_GetThemeColorType3ubv(TH_NURB_VLINE, SPACE_VIEW3D, colors->line);
+ UI_GetThemeColorType3ubv(TH_NURB_ULINE, SPACE_VIEW3D, colors->edge);
+ UI_GetThemeColorType3ubv(TH_HANDLE_SEL_VECT, SPACE_VIEW3D, colors->curpoint);
+ UI_GetThemeColorType3ubv(TH_HANDLE_SEL_VECT, SPACE_VIEW3D, colors->curpoint_a);
+ colors->curpoint_a[3] = 102;
+ UI_GetThemeColorType3ubv(TH_ACTIVE_SPLINE, SPACE_VIEW3D, colors->point);
+ UI_GetThemeColorType3ubv(TH_ACTIVE_SPLINE, SPACE_VIEW3D, colors->point_a);
+ colors->point_a[3] = 102;
+}
- if (i) {
- const int edge_array_len_orig = i;
- edge_array_len = i;
+/* called when modal loop selection gets set up... */
+static void knifetool_init(bContext *C,
+ KnifeTool_OpData *kcd,
+ const bool only_select,
+ const bool cut_through,
+ const bool is_interactive)
+{
+ Scene *scene = CTX_data_scene(C);
+ Object *obedit = CTX_data_edit_object(C);
-#ifdef USE_NET_ISLAND_CONNECT
- uint edge_array_holes_len;
- BMEdge **edge_array_holes;
- if (BM_face_split_edgenet_connect_islands(bm,
- f,
- edge_array,
- edge_array_len,
- true,
- kcd->edgenet.arena,
- &edge_array_holes,
- &edge_array_holes_len)) {
- if (BM_elem_flag_test(f, BM_ELEM_SELECT)) {
- for (i = edge_array_len; i < edge_array_holes_len; i++) {
- BM_edge_select_set(bm, edge_array_holes[i], true);
- }
- }
+ /* assign the drawing handle for drawing preview line... */
+ kcd->scene = scene;
+ kcd->ob = obedit;
+ kcd->region = CTX_wm_region(C);
- edge_array_len = edge_array_holes_len;
- edge_array = edge_array_holes; /* owned by the arena */
- }
-#endif
+ invert_m4_m4_safe_ortho(kcd->ob_imat, kcd->ob->obmat);
- {
- BMFace **face_arr = NULL;
- int face_arr_len;
+ em_setup_viewcontext(C, &kcd->vc);
- BM_face_split_edgenet(bm, f, edge_array, edge_array_len, &face_arr, &face_arr_len);
+ kcd->em = BKE_editmesh_from_object(kcd->ob);
- if (face_arr) {
- MEM_freeN(face_arr);
- }
- }
+ /* cut all the way through the mesh if use_occlude_geometry button not pushed */
+ kcd->is_interactive = is_interactive;
+ kcd->cut_through = cut_through;
+ kcd->only_select = only_select;
- /* remove dangling edges, not essential - but nice for users */
- for (i = 0; i < edge_array_len_orig; i++) {
- if (kfe_array[i]) {
- if (BM_edge_is_wire(kfe_array[i]->e)) {
- BM_edge_kill(bm, kfe_array[i]->e);
- kfe_array[i]->e = NULL;
- }
- }
- }
+ knifetool_init_bmbvh(kcd);
+ kcd->arena = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 15), "knife");
#ifdef USE_NET_ISLAND_CONNECT
- BLI_memarena_clear(kcd->edgenet.arena);
+ kcd->edgenet.arena = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 15), __func__);
#endif
- }
-
- BLI_gset_clear(kcd->edgenet.edge_visit, NULL);
-}
-
-/* Use the network of KnifeEdges and KnifeVerts accumulated to make real BMVerts and BMEdedges */
-static void knife_make_cuts(KnifeTool_OpData *kcd)
-{
- BMesh *bm = kcd->em->bm;
- KnifeEdge *kfe;
- KnifeVert *kfv;
- BMFace *f;
- BMEdge *e, *enew;
- ListBase *lst;
- Ref *ref;
- float pct;
- SmallHashIter hiter;
- BLI_mempool_iter iter;
- SmallHash fhash_, *fhash = &fhash_;
- SmallHash ehash_, *ehash = &ehash_;
+ kcd->edgenet.edge_visit = BLI_gset_ptr_new(__func__);
- BLI_smallhash_init(fhash);
- BLI_smallhash_init(ehash);
+ kcd->vthresh = KMAXDIST - 1;
+ kcd->ethresh = KMAXDIST;
- /* put list of cutting edges for a face into fhash, keyed by face */
- BLI_mempool_iternew(kcd->kedges, &iter);
- for (kfe = BLI_mempool_iterstep(&iter); kfe; kfe = BLI_mempool_iterstep(&iter)) {
+ knife_recalc_projmat(kcd);
- /* select edges that lie directly on the cut */
- if (kcd->select_result) {
- if (kfe->e && kfe->is_cut) {
- BM_edge_select_set(bm, kfe->e, true);
- }
- }
+ ED_region_tag_redraw(kcd->region);
- f = kfe->basef;
- if (!f || kfe->e) {
- continue;
- }
- lst = BLI_smallhash_lookup(fhash, (uintptr_t)f);
- if (!lst) {
- lst = knife_empty_list(kcd);
- BLI_smallhash_insert(fhash, (uintptr_t)f, lst);
- }
- knife_append_list(kcd, lst, kfe);
- }
+ kcd->refs = BLI_mempool_create(sizeof(Ref), 0, 2048, 0);
+ kcd->kverts = BLI_mempool_create(sizeof(KnifeVert), 0, 512, BLI_MEMPOOL_ALLOW_ITER);
+ kcd->kedges = BLI_mempool_create(sizeof(KnifeEdge), 0, 512, BLI_MEMPOOL_ALLOW_ITER);
- /* put list of splitting vertices for an edge into ehash, keyed by edge */
- BLI_mempool_iternew(kcd->kverts, &iter);
- for (kfv = BLI_mempool_iterstep(&iter); kfv; kfv = BLI_mempool_iterstep(&iter)) {
- if (kfv->v) {
- continue; /* already have a BMVert */
- }
- for (ref = kfv->edges.first; ref; ref = ref->next) {
- kfe = ref->ref;
- e = kfe->e;
- if (!e) {
- continue;
- }
- lst = BLI_smallhash_lookup(ehash, (uintptr_t)e);
- if (!lst) {
- lst = knife_empty_list(kcd);
- BLI_smallhash_insert(ehash, (uintptr_t)e, lst);
- }
- /* there can be more than one kfe in kfv's list with same e */
- if (!find_ref(lst, kfv)) {
- knife_append_list(kcd, lst, kfv);
- }
- }
- }
+ kcd->origedgemap = BLI_ghash_ptr_new("knife origedgemap");
+ kcd->origvertmap = BLI_ghash_ptr_new("knife origvertmap");
+ kcd->kedgefacemap = BLI_ghash_ptr_new("knife kedgefacemap");
+ kcd->facetrimap = BLI_ghash_ptr_new("knife facetrimap");
- /* split bmesh edges where needed */
- for (lst = BLI_smallhash_iternew(ehash, &hiter, (uintptr_t *)&e); lst;
- lst = BLI_smallhash_iternext(&hiter, (uintptr_t *)&e)) {
- BLI_listbase_sort_r(lst, sort_verts_by_dist_cb, e->v1->co);
+ /* can't usefully select resulting edges in face mode */
+ kcd->select_result = (kcd->em->selectmode != SCE_SELECT_FACE);
- for (ref = lst->first; ref; ref = ref->next) {
- kfv = ref->ref;
- pct = line_point_factor_v3(kfv->co, e->v1->co, e->v2->co);
- kfv->v = BM_edge_split(bm, e, e->v1, &enew, pct);
- }
- }
+ knife_pos_data_clear(&kcd->curr);
+ knife_pos_data_clear(&kcd->prev);
- if (kcd->only_select) {
- EDBM_flag_disable_all(kcd->em, BM_ELEM_SELECT);
- }
+ if (is_interactive) {
+ kcd->draw_handle = ED_region_draw_cb_activate(
+ kcd->region->type, knifetool_draw, kcd, REGION_DRAW_POST_VIEW);
- /* do cuts for each face */
- for (lst = BLI_smallhash_iternew(fhash, &hiter, (uintptr_t *)&f); lst;
- lst = BLI_smallhash_iternext(&hiter, (uintptr_t *)&f)) {
- knife_make_face_cuts(kcd, f, lst);
+ knife_init_colors(&kcd->colors);
}
-
- BLI_smallhash_release(fhash);
- BLI_smallhash_release(ehash);
-}
-
-/* called on tool confirmation */
-static void knifetool_finish_ex(KnifeTool_OpData *kcd)
-{
- knife_make_cuts(kcd);
-
- EDBM_selectmode_flush(kcd->em);
- EDBM_mesh_normals_update(kcd->em);
- EDBM_update_generic(kcd->ob->data, true, true);
-
- /* Re-tessellating makes this invalid, don't use again by accident. */
- knifetool_free_bmbvh(kcd);
-}
-static void knifetool_finish(wmOperator *op)
-{
- KnifeTool_OpData *kcd = op->customdata;
- knifetool_finish_ex(kcd);
-}
-
-static void knife_recalc_projmat(KnifeTool_OpData *kcd)
-{
- invert_m4_m4(kcd->ob->imat, kcd->ob->obmat);
- ED_view3d_ob_project_mat_get(kcd->region->regiondata, kcd->ob, kcd->projmat);
- invert_m4_m4(kcd->projmat_inv, kcd->projmat);
-
- mul_v3_mat3_m4v3(kcd->proj_zaxis, kcd->ob->imat, kcd->vc.rv3d->viewinv[2]);
- normalize_v3(kcd->proj_zaxis);
-
- kcd->is_ortho = ED_view3d_clip_range_get(
- kcd->vc.depsgraph, kcd->vc.v3d, kcd->vc.rv3d, &kcd->clipsta, &kcd->clipend, true);
}
/* called when modal loop selection is done... */
@@ -2619,6 +2743,40 @@ static void knifetool_exit(bContext *C, wmOperator *op)
op->customdata = NULL;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Mouse-Moving Event Updates
+ * \{ */
+
+/* update active knife edge/vert pointers */
+static int knife_update_active(KnifeTool_OpData *kcd)
+{
+ /* if no hits are found this would normally default to (0, 0, 0) so instead
+ * get a point at the mouse ray closest to the previous point.
+ * Note that drawing lines in `free-space` isn't properly supported
+ * but there's no guarantee (0, 0, 0) has any geometry either - campbell */
+ if (!knife_snap_update_from_mval(kcd, kcd->mval)) {
+ float origin[3];
+ float origin_ofs[3];
+
+ knife_input_ray_segment(kcd, kcd->curr.mval, 1.0f, origin, origin_ofs);
+
+ if (!isect_line_plane_v3(
+ kcd->curr.cage, origin, origin_ofs, kcd->prev.cage, kcd->proj_zaxis)) {
+ copy_v3_v3(kcd->curr.cage, kcd->prev.cage);
+
+ /* should never fail! */
+ BLI_assert(0);
+ }
+ }
+
+ if (kcd->mode == MODE_DRAGGING) {
+ knife_find_line_hits(kcd);
+ }
+ return 1;
+}
+
static void knifetool_update_mval(KnifeTool_OpData *kcd, const float mval[2])
{
knife_recalc_projmat(kcd);
@@ -2635,99 +2793,36 @@ static void knifetool_update_mval_i(KnifeTool_OpData *kcd, const int mval_i[2])
knifetool_update_mval(kcd, mval);
}
-static void knifetool_init_bmbvh(KnifeTool_OpData *kcd)
-{
- BM_mesh_elem_index_ensure(kcd->em->bm, BM_VERT);
+/** \} */
- Scene *scene_eval = (Scene *)DEG_get_evaluated_id(kcd->vc.depsgraph, &kcd->scene->id);
- Object *obedit_eval = (Object *)DEG_get_evaluated_id(kcd->vc.depsgraph, &kcd->ob->id);
- BMEditMesh *em_eval = BKE_editmesh_from_object(obedit_eval);
-
- kcd->cagecos = (const float(*)[3])BKE_editmesh_vert_coords_alloc(
- kcd->vc.depsgraph, em_eval, scene_eval, obedit_eval, NULL);
-
- kcd->bmbvh = BKE_bmbvh_new_from_editmesh(
- kcd->em,
- BMBVH_RETURN_ORIG |
- ((kcd->only_select && kcd->cut_through) ? BMBVH_RESPECT_SELECT : BMBVH_RESPECT_HIDDEN),
- kcd->cagecos,
- false);
-}
+/* -------------------------------------------------------------------- */
+/** \name Finalization
+ * \{ */
-static void knifetool_free_bmbvh(KnifeTool_OpData *kcd)
+/* called on tool confirmation */
+static void knifetool_finish_ex(KnifeTool_OpData *kcd)
{
- if (kcd->bmbvh) {
- BKE_bmbvh_free(kcd->bmbvh);
- kcd->bmbvh = NULL;
- }
+ knife_make_cuts(kcd);
- if (kcd->cagecos) {
- MEM_freeN((void *)kcd->cagecos);
- kcd->cagecos = NULL;
- }
+ EDBM_selectmode_flush(kcd->em);
+ EDBM_mesh_normals_update(kcd->em);
+ EDBM_update_generic(kcd->ob->data, true, true);
+
+ /* Re-tessellating makes this invalid, don't use again by accident. */
+ knifetool_free_bmbvh(kcd);
}
-/* called when modal loop selection gets set up... */
-static void knifetool_init(bContext *C,
- KnifeTool_OpData *kcd,
- const bool only_select,
- const bool cut_through,
- const bool is_interactive)
+static void knifetool_finish(wmOperator *op)
{
- Scene *scene = CTX_data_scene(C);
- Object *obedit = CTX_data_edit_object(C);
-
- /* assign the drawing handle for drawing preview line... */
- kcd->scene = scene;
- kcd->ob = obedit;
- kcd->region = CTX_wm_region(C);
-
- em_setup_viewcontext(C, &kcd->vc);
-
- kcd->em = BKE_editmesh_from_object(kcd->ob);
-
- /* cut all the way through the mesh if use_occlude_geometry button not pushed */
- kcd->is_interactive = is_interactive;
- kcd->cut_through = cut_through;
- kcd->only_select = only_select;
-
- knifetool_init_bmbvh(kcd);
-
- kcd->arena = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 15), "knife");
-#ifdef USE_NET_ISLAND_CONNECT
- kcd->edgenet.arena = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 15), __func__);
-#endif
- kcd->edgenet.edge_visit = BLI_gset_ptr_new(__func__);
-
- kcd->vthresh = KMAXDIST - 1;
- kcd->ethresh = KMAXDIST;
-
- knife_recalc_projmat(kcd);
-
- ED_region_tag_redraw(kcd->region);
-
- kcd->refs = BLI_mempool_create(sizeof(Ref), 0, 2048, 0);
- kcd->kverts = BLI_mempool_create(sizeof(KnifeVert), 0, 512, BLI_MEMPOOL_ALLOW_ITER);
- kcd->kedges = BLI_mempool_create(sizeof(KnifeEdge), 0, 512, BLI_MEMPOOL_ALLOW_ITER);
-
- kcd->origedgemap = BLI_ghash_ptr_new("knife origedgemap");
- kcd->origvertmap = BLI_ghash_ptr_new("knife origvertmap");
- kcd->kedgefacemap = BLI_ghash_ptr_new("knife kedgefacemap");
- kcd->facetrimap = BLI_ghash_ptr_new("knife facetrimap");
-
- /* can't usefully select resulting edges in face mode */
- kcd->select_result = (kcd->em->selectmode != SCE_SELECT_FACE);
-
- knife_pos_data_clear(&kcd->curr);
- knife_pos_data_clear(&kcd->prev);
+ KnifeTool_OpData *kcd = op->customdata;
+ knifetool_finish_ex(kcd);
+}
- if (is_interactive) {
- kcd->draw_handle = ED_region_draw_cb_activate(
- kcd->region->type, knifetool_draw, kcd, REGION_DRAW_POST_VIEW);
+/** \} */
- knife_init_colors(&kcd->colors);
- }
-}
+/* -------------------------------------------------------------------- */
+/** \name Operator (#MESH_OT_knife_tool)
+ * \{ */
static void knifetool_cancel(bContext *C, wmOperator *op)
{
@@ -2735,53 +2830,6 @@ static void knifetool_cancel(bContext *C, wmOperator *op)
knifetool_exit(C, op);
}
-static int knifetool_invoke(bContext *C, wmOperator *op, const wmEvent *event)
-{
- const bool only_select = RNA_boolean_get(op->ptr, "only_selected");
- const bool cut_through = !RNA_boolean_get(op->ptr, "use_occlude_geometry");
- const bool wait_for_input = RNA_boolean_get(op->ptr, "wait_for_input");
-
- KnifeTool_OpData *kcd;
-
- if (only_select) {
- Object *obedit = CTX_data_edit_object(C);
- BMEditMesh *em = BKE_editmesh_from_object(obedit);
- if (em->bm->totfacesel == 0) {
- BKE_report(op->reports, RPT_ERROR, "Selected faces required");
- return OPERATOR_CANCELLED;
- }
- }
-
- /* alloc new customdata */
- kcd = op->customdata = MEM_callocN(sizeof(KnifeTool_OpData), __func__);
-
- knifetool_init(C, kcd, only_select, cut_through, true);
-
- op->flag |= OP_IS_MODAL_CURSOR_REGION;
-
- /* add a modal handler for this operator - handles loop selection */
- WM_cursor_modal_set(CTX_wm_window(C), WM_CURSOR_KNIFE);
- WM_event_add_modal_handler(C, op);
-
- knifetool_update_mval_i(kcd, event->mval);
-
- if (wait_for_input == false) {
- /* Avoid copy-paste logic. */
- wmEvent event_modal = {
- .prevval = KM_NOTHING,
- .type = EVT_MODAL_MAP,
- .val = KNF_MODAL_ADD_CUT,
- };
- int ret = knifetool_modal(C, op, &event_modal);
- BLI_assert(ret == OPERATOR_RUNNING_MODAL);
- UNUSED_VARS_NDEBUG(ret);
- }
-
- knife_update_header(C, op, kcd);
-
- return OPERATOR_RUNNING_MODAL;
-}
-
wmKeyMap *knifetool_modal_keymap(wmKeyConfig *keyconf)
{
static const EnumPropertyItem modal_items[] = {
@@ -2789,8 +2837,8 @@ wmKeyMap *knifetool_modal_keymap(wmKeyConfig *keyconf)
{KNF_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
{KNF_MODAL_MIDPOINT_ON, "SNAP_MIDPOINTS_ON", 0, "Snap to Midpoints On", ""},
{KNF_MODAL_MIDPOINT_OFF, "SNAP_MIDPOINTS_OFF", 0, "Snap to Midpoints Off", ""},
- {KNF_MODEL_IGNORE_SNAP_ON, "IGNORE_SNAP_ON", 0, "Ignore Snapping On", ""},
- {KNF_MODEL_IGNORE_SNAP_OFF, "IGNORE_SNAP_OFF", 0, "Ignore Snapping Off", ""},
+ {KNF_MODAL_IGNORE_SNAP_ON, "IGNORE_SNAP_ON", 0, "Ignore Snapping On", ""},
+ {KNF_MODAL_IGNORE_SNAP_OFF, "IGNORE_SNAP_OFF", 0, "Ignore Snapping Off", ""},
{KNF_MODAL_ANGLE_SNAP_TOGGLE, "ANGLE_SNAP_TOGGLE", 0, "Toggle Angle Snapping", ""},
{KNF_MODAL_CUT_THROUGH_TOGGLE, "CUT_THROUGH_TOGGLE", 0, "Toggle Cut Through", ""},
{KNF_MODAL_NEW_CUT, "NEW_CUT", 0, "End Current Cut", ""},
@@ -2873,13 +2921,13 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event)
ED_region_tag_redraw(kcd->region);
do_refresh = true;
break;
- case KNF_MODEL_IGNORE_SNAP_ON:
+ case KNF_MODAL_IGNORE_SNAP_ON:
ED_region_tag_redraw(kcd->region);
kcd->ignore_vert_snapping = kcd->ignore_edge_snapping = true;
knife_update_header(C, op, kcd);
do_refresh = true;
break;
- case KNF_MODEL_IGNORE_SNAP_OFF:
+ case KNF_MODAL_IGNORE_SNAP_OFF:
ED_region_tag_redraw(kcd->region);
kcd->ignore_vert_snapping = kcd->ignore_edge_snapping = false;
knife_update_header(C, op, kcd);
@@ -2917,10 +2965,13 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event)
/* freehand drawing is incompatible with cut-through */
if (kcd->cut_through == false) {
kcd->is_drag_hold = true;
+ /* No edge snapping while dragging (edges are too sticky when cuts are immediate). */
+ kcd->ignore_edge_snapping = true;
}
}
else {
kcd->is_drag_hold = false;
+ kcd->ignore_edge_snapping = false;
/* needed because the last face 'hit' is ignored when dragging */
knifetool_update_mval(kcd, kcd->curr.mval);
@@ -3006,6 +3057,53 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event)
return OPERATOR_RUNNING_MODAL;
}
+static int knifetool_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ const bool only_select = RNA_boolean_get(op->ptr, "only_selected");
+ const bool cut_through = !RNA_boolean_get(op->ptr, "use_occlude_geometry");
+ const bool wait_for_input = RNA_boolean_get(op->ptr, "wait_for_input");
+
+ KnifeTool_OpData *kcd;
+
+ if (only_select) {
+ Object *obedit = CTX_data_edit_object(C);
+ BMEditMesh *em = BKE_editmesh_from_object(obedit);
+ if (em->bm->totfacesel == 0) {
+ BKE_report(op->reports, RPT_ERROR, "Selected faces required");
+ return OPERATOR_CANCELLED;
+ }
+ }
+
+ /* alloc new customdata */
+ kcd = op->customdata = MEM_callocN(sizeof(KnifeTool_OpData), __func__);
+
+ knifetool_init(C, kcd, only_select, cut_through, true);
+
+ op->flag |= OP_IS_MODAL_CURSOR_REGION;
+
+ /* add a modal handler for this operator - handles loop selection */
+ WM_cursor_modal_set(CTX_wm_window(C), WM_CURSOR_KNIFE);
+ WM_event_add_modal_handler(C, op);
+
+ knifetool_update_mval_i(kcd, event->mval);
+
+ if (wait_for_input == false) {
+ /* Avoid copy-paste logic. */
+ wmEvent event_modal = {
+ .prevval = KM_NOTHING,
+ .type = EVT_MODAL_MAP,
+ .val = KNF_MODAL_ADD_CUT,
+ };
+ int ret = knifetool_modal(C, op, &event_modal);
+ BLI_assert(ret == OPERATOR_RUNNING_MODAL);
+ UNUSED_VARS_NDEBUG(ret);
+ }
+
+ knife_update_header(C, op, kcd);
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
void MESH_OT_knife_tool(wmOperatorType *ot)
{
/* description */
@@ -3035,9 +3133,13 @@ void MESH_OT_knife_tool(wmOperatorType *ot)
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
}
+/** \} */
+
/* -------------------------------------------------------------------- */
-/* Knife tool as a utility function
- * that can be used for internal slicing operations */
+/** \name Knife tool as a utility function
+ *
+ * Can be used for internal slicing operations.
+ * \{ */
static bool edbm_mesh_knife_point_isect(LinkNode *polys, const float cent_ss[2])
{
@@ -3212,3 +3314,5 @@ void EDBM_mesh_knife(bContext *C, LinkNode *polys, bool use_tag, bool cut_throug
kcd = NULL;
}
}
+
+/** \} */
diff --git a/source/blender/editors/mesh/editmesh_path.c b/source/blender/editors/mesh/editmesh_path.c
index 2cb8da37260..b7f671a4157 100644
--- a/source/blender/editors/mesh/editmesh_path.c
+++ b/source/blender/editors/mesh/editmesh_path.c
@@ -669,18 +669,17 @@ static int edbm_shortest_path_pick_invoke(bContext *C, wmOperator *op, const wmE
return edbm_shortest_path_pick_exec(C, op);
}
- Base *basact = NULL;
BMVert *eve = NULL;
BMEdge *eed = NULL;
BMFace *efa = NULL;
ViewContext vc;
- BMEditMesh *em;
bool track_active = true;
em_setup_viewcontext(C, &vc);
copy_v2_v2_int(vc.mval, event->mval);
- em = vc.em;
+ Base *basact = BASACT(vc.view_layer);
+ BMEditMesh *em = vc.em;
view3d_operator_needs_opengl(C);
diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c
index 35608a4abde..6cb103460f6 100644
--- a/source/blender/editors/mesh/editmesh_select.c
+++ b/source/blender/editors/mesh/editmesh_select.c
@@ -268,7 +268,8 @@ static void findnearestvert__doClosest(void *userData,
/**
* Nearest vertex under the cursor.
*
- * \param r_dist: (in/out), minimal distance to the nearest and at the end, actual distance
+ * \param dist_px_manhattan_p: (in/out), minimal distance to the nearest and at the end,
+ * actual distance.
* \param use_select_bias:
* - When true, selected vertices are given a 5 pixel bias
* to make them further than unselect verts.
@@ -276,7 +277,7 @@ static void findnearestvert__doClosest(void *userData,
* \param use_cycle: Cycle over elements within #FIND_NEAR_CYCLE_THRESHOLD_MIN in order of index.
*/
BMVert *EDBM_vert_find_nearest_ex(ViewContext *vc,
- float *r_dist,
+ float *dist_px_manhattan_p,
const bool use_select_bias,
bool use_cycle,
Base **bases,
@@ -286,7 +287,8 @@ BMVert *EDBM_vert_find_nearest_ex(ViewContext *vc,
uint base_index = 0;
if (!XRAY_FLAG_ENABLED(vc->v3d)) {
- uint dist_px = (uint)ED_view3d_backbuf_sample_size_clamp(vc->region, *r_dist);
+ uint dist_px_manhattan_test = (uint)ED_view3d_backbuf_sample_size_clamp(vc->region,
+ *dist_px_manhattan_p);
uint index;
BMVert *eve;
@@ -295,7 +297,7 @@ BMVert *EDBM_vert_find_nearest_ex(ViewContext *vc,
DRW_select_buffer_context_create(bases, bases_len, SCE_SELECT_VERTEX);
index = DRW_select_buffer_find_nearest_to_point(
- vc->depsgraph, vc->region, vc->v3d, vc->mval, 1, UINT_MAX, &dist_px);
+ vc->depsgraph, vc->region, vc->v3d, vc->mval, 1, UINT_MAX, &dist_px_manhattan_test);
if (index) {
eve = (BMVert *)edbm_select_id_bm_elem_get(bases, index, &base_index);
@@ -306,11 +308,11 @@ BMVert *EDBM_vert_find_nearest_ex(ViewContext *vc,
}
if (eve) {
- if (dist_px < *r_dist) {
+ if (dist_px_manhattan_test < *dist_px_manhattan_p) {
if (r_base_index) {
*r_base_index = base_index;
}
- *r_dist = dist_px;
+ *dist_px_manhattan_p = dist_px_manhattan_test;
return eve;
}
}
@@ -348,18 +350,19 @@ BMVert *EDBM_vert_find_nearest_ex(ViewContext *vc,
data.cycle_index_prev = 0;
}
- data.hit.dist = data.hit_cycle.dist = data.hit.dist_bias = data.hit_cycle.dist_bias = *r_dist;
+ data.hit.dist = data.hit_cycle.dist = data.hit.dist_bias = data.hit_cycle.dist_bias =
+ *dist_px_manhattan_p;
ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d);
mesh_foreachScreenVert(vc, findnearestvert__doClosest, &data, clip_flag);
hit = (data.use_cycle && data.hit_cycle.vert) ? &data.hit_cycle : &data.hit;
- if (hit->dist < *r_dist) {
+ if (hit->dist < *dist_px_manhattan_p) {
if (r_base_index) {
*r_base_index = base_index;
}
- *r_dist = hit->dist;
+ *dist_px_manhattan_p = hit->dist;
prev_select_bm = vc->em->bm;
}
}
@@ -375,10 +378,10 @@ BMVert *EDBM_vert_find_nearest_ex(ViewContext *vc,
return hit->vert;
}
-BMVert *EDBM_vert_find_nearest(ViewContext *vc, float *r_dist)
+BMVert *EDBM_vert_find_nearest(ViewContext *vc, float *dist_px_manhattan_p)
{
Base *base = BKE_view_layer_base_find(vc->view_layer, vc->obact);
- return EDBM_vert_find_nearest_ex(vc, r_dist, false, false, &base, 1, NULL);
+ return EDBM_vert_find_nearest_ex(vc, dist_px_manhattan_p, false, false, &base, 1, NULL);
}
/* find the distance to the edge we already have */
@@ -417,7 +420,7 @@ struct NearestEdgeUserData_Hit {
/* edges only, un-biased manhattan distance to which ever edge we pick
* (not used for choosing) */
- float dist_center;
+ float dist_center_px_manhattan;
};
struct NearestEdgeUserData {
@@ -477,7 +480,7 @@ static void find_nearest_edge__doClosest(
data->hit.edge = eed;
mid_v2_v2v2(screen_co_mid, screen_co_a, screen_co_b);
- data->hit.dist_center = len_manhattan_v2v2(data->mval_fl, screen_co_mid);
+ data->hit.dist_center_px_manhattan = len_manhattan_v2v2(data->mval_fl, screen_co_mid);
}
if (data->use_cycle) {
@@ -491,14 +494,14 @@ static void find_nearest_edge__doClosest(
data->hit_cycle.edge = eed;
mid_v2_v2v2(screen_co_mid, screen_co_a, screen_co_b);
- data->hit_cycle.dist_center = len_manhattan_v2v2(data->mval_fl, screen_co_mid);
+ data->hit_cycle.dist_center_px_manhattan = len_manhattan_v2v2(data->mval_fl, screen_co_mid);
}
}
}
BMEdge *EDBM_edge_find_nearest_ex(ViewContext *vc,
- float *r_dist,
- float *r_dist_center,
+ float *dist_px_manhattan_p,
+ float *r_dist_center_px_manhattan,
const bool use_select_bias,
bool use_cycle,
BMEdge **r_eed_zbuf,
@@ -509,7 +512,8 @@ BMEdge *EDBM_edge_find_nearest_ex(ViewContext *vc,
uint base_index = 0;
if (!XRAY_FLAG_ENABLED(vc->v3d)) {
- uint dist_px = (uint)ED_view3d_backbuf_sample_size_clamp(vc->region, *r_dist);
+ uint dist_px_manhattan_test = (uint)ED_view3d_backbuf_sample_size_clamp(vc->region,
+ *dist_px_manhattan_p);
uint index;
BMEdge *eed;
@@ -518,7 +522,7 @@ BMEdge *EDBM_edge_find_nearest_ex(ViewContext *vc,
DRW_select_buffer_context_create(bases, bases_len, SCE_SELECT_EDGE);
index = DRW_select_buffer_find_nearest_to_point(
- vc->depsgraph, vc->region, vc->v3d, vc->mval, 1, UINT_MAX, &dist_px);
+ vc->depsgraph, vc->region, vc->v3d, vc->mval, 1, UINT_MAX, &dist_px_manhattan_test);
if (index) {
eed = (BMEdge *)edbm_select_id_bm_elem_get(bases, index, &base_index);
@@ -533,7 +537,7 @@ BMEdge *EDBM_edge_find_nearest_ex(ViewContext *vc,
}
/* exception for faces (verts don't need this) */
- if (r_dist_center && eed) {
+ if (r_dist_center_px_manhattan && eed) {
struct NearestEdgeUserData_ZBuf data;
data.mval_fl[0] = vc->mval[0];
@@ -546,16 +550,16 @@ BMEdge *EDBM_edge_find_nearest_ex(ViewContext *vc,
mesh_foreachScreenEdge(
vc, find_nearest_edge_center__doZBuf, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
- *r_dist_center = data.dist;
+ *r_dist_center_px_manhattan = data.dist;
}
/* end exception */
if (eed) {
- if (dist_px < *r_dist) {
+ if (dist_px_manhattan_test < *dist_px_manhattan_p) {
if (r_base_index) {
*r_base_index = base_index;
}
- *r_dist = dist_px;
+ *dist_px_manhattan_p = dist_px_manhattan_test;
return eed;
}
}
@@ -593,18 +597,19 @@ BMEdge *EDBM_edge_find_nearest_ex(ViewContext *vc,
data.cycle_index_prev = 0;
}
- data.hit.dist = data.hit_cycle.dist = data.hit.dist_bias = data.hit_cycle.dist_bias = *r_dist;
+ data.hit.dist = data.hit_cycle.dist = data.hit.dist_bias = data.hit_cycle.dist_bias =
+ *dist_px_manhattan_p;
ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d);
mesh_foreachScreenEdge(vc, find_nearest_edge__doClosest, &data, clip_flag);
hit = (data.use_cycle && data.hit_cycle.edge) ? &data.hit_cycle : &data.hit;
- if (hit->dist < *r_dist) {
+ if (hit->dist < *dist_px_manhattan_p) {
if (r_base_index) {
*r_base_index = base_index;
}
- *r_dist = hit->dist;
+ *dist_px_manhattan_p = hit->dist;
prev_select_bm = vc->em->bm;
}
}
@@ -613,8 +618,8 @@ BMEdge *EDBM_edge_find_nearest_ex(ViewContext *vc,
return NULL;
}
- if (r_dist_center) {
- *r_dist_center = hit->dist_center;
+ if (r_dist_center_px_manhattan) {
+ *r_dist_center_px_manhattan = hit->dist_center_px_manhattan;
}
prev_select.index = hit->index;
@@ -624,16 +629,17 @@ BMEdge *EDBM_edge_find_nearest_ex(ViewContext *vc,
return hit->edge;
}
-BMEdge *EDBM_edge_find_nearest(ViewContext *vc, float *r_dist)
+BMEdge *EDBM_edge_find_nearest(ViewContext *vc, float *dist_px_manhattan_p)
{
Base *base = BKE_view_layer_base_find(vc->view_layer, vc->obact);
- return EDBM_edge_find_nearest_ex(vc, r_dist, NULL, false, false, NULL, &base, 1, NULL);
+ return EDBM_edge_find_nearest_ex(
+ vc, dist_px_manhattan_p, NULL, false, false, NULL, &base, 1, NULL);
}
/* find the distance to the face we already have */
struct NearestFaceUserData_ZBuf {
float mval_fl[2];
- float dist;
+ float dist_px_manhattan;
const BMFace *face_test;
};
@@ -647,8 +653,8 @@ static void find_nearest_face_center__doZBuf(void *userData,
if (efa == data->face_test) {
const float dist_test = len_manhattan_v2v2(data->mval_fl, screen_co);
- if (dist_test < data->dist) {
- data->dist = dist_test;
+ if (dist_test < data->dist_px_manhattan) {
+ data->dist_px_manhattan = dist_test;
}
}
}
@@ -702,9 +708,17 @@ static void findnearestface__doClosest(void *userData,
}
}
+/**
+ * \param use_zbuf_single_px: Special case, when using the back-buffer selection,
+ * only use the pixel at `vc->mval` instead of using `dist_px_manhattan_p` to search over a larger
+ * region. This is needed because historically selection worked this way for a long time, however
+ * it's reasonable that some callers might want to expand the region too. So add an argument to do
+ * this,
+ */
BMFace *EDBM_face_find_nearest_ex(ViewContext *vc,
- float *r_dist,
+ float *dist_px_manhattan_p,
float *r_dist_center,
+ const bool use_zbuf_single_px,
const bool use_select_bias,
bool use_cycle,
BMFace **r_efa_zbuf,
@@ -715,14 +729,28 @@ BMFace *EDBM_face_find_nearest_ex(ViewContext *vc,
uint base_index = 0;
if (!XRAY_FLAG_ENABLED(vc->v3d)) {
- float dist_test = 0.0f;
+ float dist_test;
uint index;
BMFace *efa;
{
+ uint dist_px_manhattan_test = 0;
+ if (*dist_px_manhattan_p != 0.0f && (use_zbuf_single_px == false)) {
+ dist_px_manhattan_test = (uint)ED_view3d_backbuf_sample_size_clamp(vc->region,
+ *dist_px_manhattan_p);
+ }
+
DRW_select_buffer_context_create(bases, bases_len, SCE_SELECT_FACE);
- index = DRW_select_buffer_sample_point(vc->depsgraph, vc->region, vc->v3d, vc->mval);
+ if (dist_px_manhattan_test == 0) {
+ index = DRW_select_buffer_sample_point(vc->depsgraph, vc->region, vc->v3d, vc->mval);
+ dist_test = 0.0f;
+ }
+ else {
+ index = DRW_select_buffer_find_nearest_to_point(
+ vc->depsgraph, vc->region, vc->v3d, vc->mval, 1, UINT_MAX, &dist_px_manhattan_test);
+ dist_test = dist_px_manhattan_test;
+ }
if (index) {
efa = (BMFace *)edbm_select_id_bm_elem_get(bases, index, &base_index);
@@ -742,7 +770,7 @@ BMFace *EDBM_face_find_nearest_ex(ViewContext *vc,
data.mval_fl[0] = vc->mval[0];
data.mval_fl[1] = vc->mval[1];
- data.dist = FLT_MAX;
+ data.dist_px_manhattan = FLT_MAX;
data.face_test = efa;
ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d);
@@ -750,16 +778,16 @@ BMFace *EDBM_face_find_nearest_ex(ViewContext *vc,
mesh_foreachScreenFace(
vc, find_nearest_face_center__doZBuf, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
- *r_dist_center = data.dist;
+ *r_dist_center = data.dist_px_manhattan;
}
/* end exception */
if (efa) {
- if (dist_test < *r_dist) {
+ if (dist_test < *dist_px_manhattan_p) {
if (r_base_index) {
*r_base_index = base_index;
}
- *r_dist = dist_test;
+ *dist_px_manhattan_p = dist_test;
return efa;
}
}
@@ -795,18 +823,19 @@ BMFace *EDBM_face_find_nearest_ex(ViewContext *vc,
data.cycle_index_prev = 0;
}
- data.hit.dist = data.hit_cycle.dist = data.hit.dist_bias = data.hit_cycle.dist_bias = *r_dist;
+ data.hit.dist = data.hit_cycle.dist = data.hit.dist_bias = data.hit_cycle.dist_bias =
+ *dist_px_manhattan_p;
ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d);
mesh_foreachScreenFace(vc, findnearestface__doClosest, &data, clip_flag);
hit = (data.use_cycle && data.hit_cycle.face) ? &data.hit_cycle : &data.hit;
- if (hit->dist < *r_dist) {
+ if (hit->dist < *dist_px_manhattan_p) {
if (r_base_index) {
*r_base_index = base_index;
}
- *r_dist = hit->dist;
+ *dist_px_manhattan_p = hit->dist;
prev_select_bm = vc->em->bm;
}
}
@@ -826,10 +855,11 @@ BMFace *EDBM_face_find_nearest_ex(ViewContext *vc,
return hit->face;
}
-BMFace *EDBM_face_find_nearest(ViewContext *vc, float *r_dist)
+BMFace *EDBM_face_find_nearest(ViewContext *vc, float *dist_px_manhattan_p)
{
Base *base = BKE_view_layer_base_find(vc->view_layer, vc->obact);
- return EDBM_face_find_nearest_ex(vc, r_dist, NULL, false, false, NULL, &base, 1, NULL);
+ return EDBM_face_find_nearest_ex(
+ vc, dist_px_manhattan_p, NULL, false, false, false, NULL, &base, 1, NULL);
}
#undef FIND_NEAR_SELECT_BIAS
@@ -883,7 +913,7 @@ static bool unified_findnearest(ViewContext *vc,
uint base_index = 0;
BMFace *efa_zbuf = NULL;
BMFace *efa_test = EDBM_face_find_nearest_ex(
- vc, &dist, dist_center_p, true, use_cycle, &efa_zbuf, bases, bases_len, &base_index);
+ vc, &dist, dist_center_p, true, true, use_cycle, &efa_zbuf, bases, bases_len, &base_index);
if (efa_test && dist_center_p) {
dist = min_ff(dist_margin, dist_center);
@@ -2517,6 +2547,7 @@ bool EDBM_selectmode_set_multi(bContext *C, const short selectmode)
changed = true;
}
}
+ MEM_freeN(objects);
if (changed) {
WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, NULL);
@@ -4602,35 +4633,56 @@ static int edbm_select_random_exec(bContext *C, wmOperator *op)
seed_iter += BLI_ghashutil_strhash_p(obedit->id.name);
}
- RNG *rng = BLI_rng_new_srandom(seed_iter);
-
if (em->selectmode & SCE_SELECT_VERTEX) {
+ int elem_map_len = 0;
+ BMVert **elem_map = MEM_mallocN(sizeof(*elem_map) * em->bm->totvert, __func__);
BMVert *eve;
BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
- if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN) && BLI_rng_get_float(rng) < randfac) {
- BM_vert_select_set(em->bm, eve, select);
+ if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
+ elem_map[elem_map_len++] = eve;
}
}
+
+ BLI_array_randomize(elem_map, sizeof(*elem_map), elem_map_len, seed);
+ const int count_select = elem_map_len * randfac;
+ for (int i = 0; i < count_select; i++) {
+ BM_vert_select_set(em->bm, elem_map[i], select);
+ }
+ MEM_freeN(elem_map);
}
else if (em->selectmode & SCE_SELECT_EDGE) {
+ int elem_map_len = 0;
+ BMEdge **elem_map = MEM_mallocN(sizeof(*elem_map) * em->bm->totedge, __func__);
BMEdge *eed;
BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
- if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN) && BLI_rng_get_float(rng) < randfac) {
- BM_edge_select_set(em->bm, eed, select);
+ if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) {
+ elem_map[elem_map_len++] = eed;
}
}
+ BLI_array_randomize(elem_map, sizeof(*elem_map), elem_map_len, seed);
+ const int count_select = elem_map_len * randfac;
+ for (int i = 0; i < count_select; i++) {
+ BM_edge_select_set(em->bm, elem_map[i], select);
+ }
+ MEM_freeN(elem_map);
}
else {
+ int elem_map_len = 0;
+ BMFace **elem_map = MEM_mallocN(sizeof(*elem_map) * em->bm->totface, __func__);
BMFace *efa;
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
- if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN) && BLI_rng_get_float(rng) < randfac) {
- BM_face_select_set(em->bm, efa, select);
+ if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
+ elem_map[elem_map_len++] = efa;
}
}
+ BLI_array_randomize(elem_map, sizeof(*elem_map), elem_map_len, seed);
+ const int count_select = elem_map_len * randfac;
+ for (int i = 0; i < count_select; i++) {
+ BM_face_select_set(em->bm, elem_map[i], select);
+ }
+ MEM_freeN(elem_map);
}
- BLI_rng_free(rng);
-
if (select) {
/* was EDBM_select_flush, but it over select in edge/face mode */
EDBM_selectmode_flush(em);
@@ -4795,15 +4847,8 @@ static int edbm_select_axis_exec(bContext *C, wmOperator *op)
float axis_mat[3][3];
/* 3D view variables may be NULL, (no need to check in poll function). */
- ED_transform_calc_orientation_from_type_ex(C,
- axis_mat,
- scene,
- CTX_wm_region_view3d(C),
- obedit,
- obedit,
- orientation,
- 0,
- V3D_AROUND_ACTIVE);
+ ED_transform_calc_orientation_from_type_ex(
+ C, axis_mat, scene, CTX_wm_region_view3d(C), obedit, obedit, orientation, V3D_AROUND_ACTIVE);
const float *axis_vector = axis_mat[axis];
diff --git a/source/blender/editors/mesh/editmesh_select_similar.c b/source/blender/editors/mesh/editmesh_select_similar.c
index f9651454dee..f3c0da67ecc 100644
--- a/source/blender/editors/mesh/editmesh_select_similar.c
+++ b/source/blender/editors/mesh/editmesh_select_similar.c
@@ -1325,7 +1325,9 @@ void MESH_OT_select_similar(wmOperatorType *ot)
RNA_def_enum(ot->srna, "compare", prop_similar_compare_types, SIM_CMP_EQ, "Compare", "");
- RNA_def_float(ot->srna, "threshold", 0.0f, 0.0f, 1.0f, "Threshold", "", 0.0f, 1.0f);
+ prop = RNA_def_float(ot->srna, "threshold", 0.0f, 0.0f, 1.0f, "Threshold", "", 0.0f, 1.0f);
+ /* Very small values are needed sometimes, similar area of small faces for e.g: see T87823 */
+ RNA_def_property_ui_range(prop, 0.0, 1.0, 0.01, 5);
}
/** \} */
diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c
index ade21f58232..bb332a4094c 100644
--- a/source/blender/editors/mesh/editmesh_tools.c
+++ b/source/blender/editors/mesh/editmesh_tools.c
@@ -3694,20 +3694,18 @@ static const EnumPropertyItem *shape_itemf(bContext *C,
static void edbm_blend_from_shape_ui(bContext *C, wmOperator *op)
{
uiLayout *layout = op->layout;
- PointerRNA ptr;
Object *obedit = CTX_data_edit_object(C);
Mesh *me = obedit->data;
PointerRNA ptr_key;
- RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
RNA_id_pointer_create((ID *)me->key, &ptr_key);
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
- uiItemPointerR(layout, &ptr, "shape", &ptr_key, "key_blocks", NULL, ICON_SHAPEKEY_DATA);
- uiItemR(layout, &ptr, "blend", 0, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "add", 0, NULL, ICON_NONE);
+ uiItemPointerR(layout, op->ptr, "shape", &ptr_key, "key_blocks", NULL, ICON_SHAPEKEY_DATA);
+ uiItemR(layout, op->ptr, "blend", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "add", 0, NULL, ICON_NONE);
}
void MESH_OT_blend_from_shape(wmOperatorType *ot)
@@ -5614,25 +5612,22 @@ static bool edbm_decimate_check(bContext *UNUSED(C), wmOperator *UNUSED(op))
static void edbm_decimate_ui(bContext *UNUSED(C), wmOperator *op)
{
uiLayout *layout = op->layout, *row, *col, *sub;
- PointerRNA ptr;
-
- RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
uiLayoutSetPropSep(layout, true);
- uiItemR(layout, &ptr, "ratio", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "ratio", 0, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "use_vertex_group", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "use_vertex_group", 0, NULL, ICON_NONE);
col = uiLayoutColumn(layout, false);
- uiLayoutSetActive(col, RNA_boolean_get(&ptr, "use_vertex_group"));
- uiItemR(col, &ptr, "vertex_group_factor", 0, NULL, ICON_NONE);
- uiItemR(col, &ptr, "invert_vertex_group", 0, NULL, ICON_NONE);
+ uiLayoutSetActive(col, RNA_boolean_get(op->ptr, "use_vertex_group"));
+ uiItemR(col, op->ptr, "vertex_group_factor", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "invert_vertex_group", 0, NULL, ICON_NONE);
row = uiLayoutRowWithHeading(layout, true, IFACE_("Symmetry"));
- uiItemR(row, &ptr, "use_symmetry", 0, "", ICON_NONE);
+ uiItemR(row, op->ptr, "use_symmetry", 0, "", ICON_NONE);
sub = uiLayoutRow(row, true);
- uiLayoutSetActive(sub, RNA_boolean_get(&ptr, "use_symmetry"));
- uiItemR(sub, &ptr, "symmetry_axis", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiLayoutSetActive(sub, RNA_boolean_get(op->ptr, "use_symmetry"));
+ uiItemR(sub, op->ptr, "symmetry_axis", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
}
void MESH_OT_decimate(wmOperatorType *ot)
@@ -6886,8 +6881,8 @@ void MESH_OT_sort_elements(wmOperatorType *ot)
"SELECTED",
0,
"Selected",
- "Move all selected elements in first places, preserving their relative order "
- "(WARNING: this will affect unselected elements' indices as well!)"},
+ "Move all selected elements in first places, preserving their relative order.\n"
+ "Warning: This will affect unselected elements' indices as well"},
{SRT_RANDOMIZE, "RANDOMIZE", 0, "Randomize", "Randomize order of selected elements"},
{SRT_REVERSE, "REVERSE", 0, "Reverse", "Reverse current order of selected elements"},
{0, NULL, 0, NULL, NULL},
diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c
index 79385e28aa9..274f4cdbb6c 100644
--- a/source/blender/editors/mesh/editmesh_undo.c
+++ b/source/blender/editors/mesh/editmesh_undo.c
@@ -93,6 +93,12 @@ typedef struct BArrayCustomData {
#endif
typedef struct UndoMesh {
+ /**
+ * This undo-meshes in `um_arraystore.local_links`.
+ * Not to be confused with the next and previous undo steps.
+ */
+ struct UndoMesh *local_next, *local_prev;
+
Mesh me;
int selectmode;
@@ -128,7 +134,10 @@ static struct {
struct BArrayStore_AtSize bs_stride;
int users;
- /* We could have the undo API pass in the previous state, for now store a local list */
+ /**
+ * A list of #UndoMesh items ordered from oldest to newest
+ * used to access previous undo data for a mesh.
+ */
ListBase local_links;
# ifdef USE_ARRAY_STORE_THREAD
@@ -520,11 +529,63 @@ static void um_arraystore_free(UndoMesh *um)
/** \} */
+/* -------------------------------------------------------------------- */
+/** \name Array Store Utilities
+ * \{ */
+
+/**
+ * Create an array of #UndoMesh from `objects`.
+ *
+ * where each element in the resulting array is the most recently created
+ * undo-mesh for the object's mesh.
+ * When no undo-mesh can be found that array index is NULL.
+ *
+ * This is used for de-duplicating memory between undo steps,
+ * failure to find the undo step will store a full duplicate in memory.
+ * define `DEBUG_PRINT` to check memory is de-duplicating as expected.
+ */
+static UndoMesh **mesh_undostep_reference_elems_from_objects(Object **object, int object_len)
+{
+ /* Map: `Mesh.id.session_uuid` -> `UndoMesh`. */
+ GHash *uuid_map = BLI_ghash_ptr_new_ex(__func__, object_len);
+ UndoMesh **um_references = MEM_callocN(sizeof(UndoMesh *) * object_len, __func__);
+ for (int i = 0; i < object_len; i++) {
+ const Mesh *me = object[i]->data;
+ BLI_ghash_insert(uuid_map, POINTER_FROM_INT(me->id.session_uuid), &um_references[i]);
+ }
+ int uuid_map_len = object_len;
+
+ /* Loop backwards over all previous mesh undo data until either:
+ * - All elements have been found (where `um_references` we'll have every element set).
+ * - There are no undo steps left to look for. */
+ UndoMesh *um_iter = um_arraystore.local_links.last;
+ while (um_iter && (uuid_map_len != 0)) {
+ UndoMesh **um_p;
+ if ((um_p = BLI_ghash_popkey(uuid_map, POINTER_FROM_INT(um_iter->me.id.session_uuid), NULL))) {
+ *um_p = um_iter;
+ uuid_map_len--;
+ }
+ um_iter = um_iter->local_prev;
+ }
+ BLI_assert(uuid_map_len == BLI_ghash_len(uuid_map));
+ BLI_ghash_free(uuid_map, NULL, NULL);
+ if (uuid_map_len == object_len) {
+ MEM_freeN(um_references);
+ um_references = NULL;
+ }
+ return um_references;
+}
+
+/** \} */
+
#endif /* USE_ARRAY_STORE */
/* for callbacks */
/* undo simply makes copies of a bmesh */
-static void *undomesh_from_editmesh(UndoMesh *um, BMEditMesh *em, Key *key)
+/**
+ * \param um_ref: The reference to use for de-duplicating memory between undo-steps.
+ */
+static void *undomesh_from_editmesh(UndoMesh *um, BMEditMesh *em, Key *key, UndoMesh *um_ref)
{
BLI_assert(BLI_array_is_zeroed(um, 1));
#ifdef USE_ARRAY_STORE_THREAD
@@ -560,18 +621,12 @@ static void *undomesh_from_editmesh(UndoMesh *um, BMEditMesh *em, Key *key)
#ifdef USE_ARRAY_STORE
{
- /* We could be more clever here,
- * the previous undo state may be from a separate mesh. */
- const UndoMesh *um_ref = um_arraystore.local_links.last ?
- ((LinkData *)um_arraystore.local_links.last)->data :
- NULL;
-
/* Add ourselves. */
- BLI_addtail(&um_arraystore.local_links, BLI_genericNodeN(um));
+ BLI_addtail(&um_arraystore.local_links, um);
# ifdef USE_ARRAY_STORE_THREAD
if (um_arraystore.task_pool == NULL) {
- um_arraystore.task_pool = BLI_task_pool_create_background(NULL, TASK_PRIORITY_LOW);
+ um_arraystore.task_pool = BLI_task_pool_create_background(NULL, TASK_PRIORITY_LOW, true);
}
struct UMArrayData *um_data = MEM_mallocN(sizeof(*um_data), __func__);
@@ -583,6 +638,8 @@ static void *undomesh_from_editmesh(UndoMesh *um, BMEditMesh *em, Key *key)
um_arraystore_compact_with_info(um, um_ref);
# endif
}
+#else
+ UNUSED_VARS(um_ref);
#endif
return um;
@@ -682,11 +739,9 @@ static void undomesh_free_data(UndoMesh *um)
/* we need to expand so any allocations in custom-data are freed with the mesh */
um_arraystore_expand(um);
- {
- LinkData *link = BLI_findptr(&um_arraystore.local_links, um, offsetof(LinkData, data));
- BLI_remlink(&um_arraystore.local_links, link);
- MEM_freeN(link);
- }
+ BLI_assert(BLI_findindex(&um_arraystore.local_links, um) != -1);
+ BLI_remlink(&um_arraystore.local_links, um);
+
um_arraystore_free(um);
#endif
@@ -720,7 +775,6 @@ static Object *editmesh_object_from_context(bContext *C)
* \{ */
typedef struct MeshUndoStep_Elem {
- struct MeshUndoStep_Elem *next, *prev;
UndoRefID_Object obedit_ref;
UndoMesh data;
} MeshUndoStep_Elem;
@@ -749,6 +803,12 @@ static bool mesh_undosys_step_encode(struct bContext *C, struct Main *bmain, Und
us->elems = MEM_callocN(sizeof(*us->elems) * objects_len, __func__);
us->elems_len = objects_len;
+ UndoMesh **um_references = NULL;
+
+#ifdef USE_ARRAY_STORE
+ um_references = mesh_undostep_reference_elems_from_objects(objects, objects_len);
+#endif
+
for (uint i = 0; i < objects_len; i++) {
Object *ob = objects[i];
MeshUndoStep_Elem *elem = &us->elems[i];
@@ -756,12 +816,22 @@ static bool mesh_undosys_step_encode(struct bContext *C, struct Main *bmain, Und
elem->obedit_ref.ptr = ob;
Mesh *me = elem->obedit_ref.ptr->data;
BMEditMesh *em = me->edit_mesh;
- undomesh_from_editmesh(&elem->data, me->edit_mesh, me->key);
+ undomesh_from_editmesh(
+ &elem->data, me->edit_mesh, me->key, um_references ? um_references[i] : NULL);
em->needs_flush_to_id = 1;
us->step.data_size += elem->data.undo_size;
+
+#ifdef USE_ARRAY_STORE
+ /** As this is only data storage it is safe to set the session ID here. */
+ elem->data.me.id.session_uuid = me->id.session_uuid;
+#endif
}
MEM_freeN(objects);
+ if (um_references != NULL) {
+ MEM_freeN(um_references);
+ }
+
bmain->is_memfile_undo_flush_needed = true;
return true;
diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c
index 94f386e08d5..19c9909039c 100644
--- a/source/blender/editors/mesh/editmesh_utils.c
+++ b/source/blender/editors/mesh/editmesh_utils.c
@@ -785,7 +785,7 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm,
l = v->l;
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
uv = luv->uv;
- uv_vert_sel = luv->flag & MLOOPUV_VERTSEL;
+ uv_vert_sel = uvedit_uv_select_test(scene, l, cd_loop_uv_offset);
lastv = NULL;
iterv = vlist;
@@ -796,7 +796,7 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm,
l = iterv->l;
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
uv2 = luv->uv;
- uv2_vert_sel = luv->flag & MLOOPUV_VERTSEL;
+ uv2_vert_sel = uvedit_uv_select_test(scene, l, cd_loop_uv_offset);
/* Check if the uv loops share the same selection state (if not, they are not connected as
* they have been ripped or other edit commands have separated them). */
@@ -1721,7 +1721,7 @@ void EDBM_project_snap_verts(
SCE_SNAP_MODE_FACE,
&(const struct SnapObjectParams){
.snap_select = SNAP_NOT_ACTIVE,
- .use_object_edit_cage = false,
+ .edit_mode_type = SNAP_GEOM_FINAL,
.use_occlusion_test = true,
},
mval,
diff --git a/source/blender/editors/mesh/meshtools.c b/source/blender/editors/mesh/meshtools.c
index 3450d61337c..f306612f295 100644
--- a/source/blender/editors/mesh/meshtools.c
+++ b/source/blender/editors/mesh/meshtools.c
@@ -149,7 +149,7 @@ static void join_mesh_single(Depsgraph *depsgraph,
mul_m4_m4m4(cmat, imat, ob_src->obmat);
/* transform vertex coordinates into new space */
- for (a = 0, mvert = *mvert_pp; a < me->totvert; a++, mvert++) {
+ for (a = 0; a < me->totvert; a++, mvert++) {
mul_m4_v3(cmat, mvert->co);
}
diff --git a/source/blender/editors/object/CMakeLists.txt b/source/blender/editors/object/CMakeLists.txt
index 77b5379ddd4..4338b043fc9 100644
--- a/source/blender/editors/object/CMakeLists.txt
+++ b/source/blender/editors/object/CMakeLists.txt
@@ -87,7 +87,7 @@ if(WITH_INTERNATIONAL)
endif()
if(WITH_EXPERIMENTAL_FEATURES)
- add_definitions(-DWITH_GEOMETRY_NODES)
+ add_definitions(-DWITH_SIMULATION_DATABLOCK)
add_definitions(-DWITH_POINT_CLOUD)
add_definitions(-DWITH_HAIR_NODES)
endif()
diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c
index 50dc1af5ca8..aefcf68390e 100644
--- a/source/blender/editors/object/object_add.c
+++ b/source/blender/editors/object/object_add.c
@@ -117,6 +117,7 @@
#include "ED_physics.h"
#include "ED_render.h"
#include "ED_screen.h"
+#include "ED_select_utils.h"
#include "ED_transform.h"
#include "ED_view3d.h"
@@ -455,51 +456,53 @@ void ED_object_add_mesh_props(wmOperatorType *ot)
bool ED_object_add_generic_get_opts(bContext *C,
wmOperator *op,
const char view_align_axis,
- float loc[3],
- float rot[3],
- float scale[3],
- bool *enter_editmode,
- ushort *local_view_bits,
- bool *is_view_aligned)
-{
- PropertyRNA *prop;
-
- /* Switch to Edit mode? optional prop */
- if ((prop = RNA_struct_find_property(op->ptr, "enter_editmode"))) {
+ float r_loc[3],
+ float r_rot[3],
+ float r_scale[3],
+ bool *r_enter_editmode,
+ ushort *r_local_view_bits,
+ bool *r_is_view_aligned)
+{
+ /* Edit Mode! (optional) */
+ {
bool _enter_editmode;
- if (!enter_editmode) {
- enter_editmode = &_enter_editmode;
+ if (!r_enter_editmode) {
+ r_enter_editmode = &_enter_editmode;
}
+ /* Only to ensure the value is _always_ set.
+ * Typically the property will exist when the argument is non-NULL. */
+ *r_enter_editmode = false;
- if (RNA_property_is_set(op->ptr, prop) && enter_editmode) {
- *enter_editmode = RNA_property_boolean_get(op->ptr, prop);
- }
- else {
- *enter_editmode = (U.flag & USER_ADD_EDITMODE) != 0;
- RNA_property_boolean_set(op->ptr, prop, *enter_editmode);
+ PropertyRNA *prop = RNA_struct_find_property(op->ptr, "enter_editmode");
+ if (prop != NULL) {
+ if (RNA_property_is_set(op->ptr, prop) && r_enter_editmode) {
+ *r_enter_editmode = RNA_property_boolean_get(op->ptr, prop);
+ }
+ else {
+ *r_enter_editmode = (U.flag & USER_ADD_EDITMODE) != 0;
+ RNA_property_boolean_set(op->ptr, prop, *r_enter_editmode);
+ }
}
}
- if (local_view_bits) {
+ if (r_local_view_bits) {
View3D *v3d = CTX_wm_view3d(C);
- if (v3d && v3d->localvd) {
- *local_view_bits = v3d->local_view_uuid;
- }
+ *r_local_view_bits = (v3d && v3d->localvd) ? v3d->local_view_uuid : 0;
}
/* Location! */
{
float _loc[3];
- if (!loc) {
- loc = _loc;
+ if (!r_loc) {
+ r_loc = _loc;
}
if (RNA_struct_property_is_set(op->ptr, "location")) {
- RNA_float_get_array(op->ptr, "location", loc);
+ RNA_float_get_array(op->ptr, "location", r_loc);
}
else {
- ED_object_location_from_view(C, loc);
- RNA_float_set_array(op->ptr, "location", loc);
+ ED_object_location_from_view(C, r_loc);
+ RNA_float_set_array(op->ptr, "location", r_loc);
}
}
@@ -507,33 +510,33 @@ bool ED_object_add_generic_get_opts(bContext *C,
{
bool _is_view_aligned;
float _rot[3];
- if (!is_view_aligned) {
- is_view_aligned = &_is_view_aligned;
+ if (!r_is_view_aligned) {
+ r_is_view_aligned = &_is_view_aligned;
}
- if (!rot) {
- rot = _rot;
+ if (!r_rot) {
+ r_rot = _rot;
}
if (RNA_struct_property_is_set(op->ptr, "rotation")) {
/* If rotation is set, always use it. Alignment (and corresponding user preference)
* can be ignored since this is in world space anyways.
* To not confuse (e.g. on redo), don't set it to #ALIGN_WORLD in the op UI though. */
- *is_view_aligned = false;
- RNA_float_get_array(op->ptr, "rotation", rot);
+ *r_is_view_aligned = false;
+ RNA_float_get_array(op->ptr, "rotation", r_rot);
}
else {
int alignment = ALIGN_WORLD;
- prop = RNA_struct_find_property(op->ptr, "align");
+ PropertyRNA *prop = RNA_struct_find_property(op->ptr, "align");
if (RNA_property_is_set(op->ptr, prop)) {
/* If alignment is set, always use it. */
- *is_view_aligned = alignment == ALIGN_VIEW;
+ *r_is_view_aligned = alignment == ALIGN_VIEW;
alignment = RNA_property_enum_get(op->ptr, prop);
}
else {
/* If alignment is not set, use User Preferences. */
- *is_view_aligned = (U.flag & USER_ADD_VIEWALIGNED) != 0;
- if (*is_view_aligned) {
+ *r_is_view_aligned = (U.flag & USER_ADD_VIEWALIGNED) != 0;
+ if (*r_is_view_aligned) {
RNA_property_enum_set(op->ptr, prop, ALIGN_VIEW);
alignment = ALIGN_VIEW;
}
@@ -548,18 +551,18 @@ bool ED_object_add_generic_get_opts(bContext *C,
}
switch (alignment) {
case ALIGN_WORLD:
- RNA_float_get_array(op->ptr, "rotation", rot);
+ RNA_float_get_array(op->ptr, "rotation", r_rot);
break;
case ALIGN_VIEW:
- ED_object_rotation_from_view(C, rot, view_align_axis);
- RNA_float_set_array(op->ptr, "rotation", rot);
+ ED_object_rotation_from_view(C, r_rot, view_align_axis);
+ RNA_float_set_array(op->ptr, "rotation", r_rot);
break;
case ALIGN_CURSOR: {
const Scene *scene = CTX_data_scene(C);
float tmat[3][3];
BKE_scene_cursor_rot_to_mat3(&scene->cursor, tmat);
- mat3_normalized_to_eul(rot, tmat);
- RNA_float_set_array(op->ptr, "rotation", rot);
+ mat3_normalized_to_eul(r_rot, tmat);
+ RNA_float_set_array(op->ptr, "rotation", r_rot);
break;
}
}
@@ -569,19 +572,21 @@ bool ED_object_add_generic_get_opts(bContext *C,
/* Scale! */
{
float _scale[3];
- if (!scale) {
- scale = _scale;
+ if (!r_scale) {
+ r_scale = _scale;
}
/* For now this is optional, we can make it always use. */
- copy_v3_fl(scale, 1.0f);
- if ((prop = RNA_struct_find_property(op->ptr, "scale"))) {
+ copy_v3_fl(r_scale, 1.0f);
+
+ PropertyRNA *prop = RNA_struct_find_property(op->ptr, "scale");
+ if (prop != NULL) {
if (RNA_property_is_set(op->ptr, prop)) {
- RNA_property_float_get_array(op->ptr, prop, scale);
+ RNA_property_float_get_array(op->ptr, prop, r_scale);
}
else {
- copy_v3_fl(scale, 1.0f);
- RNA_property_float_set_array(op->ptr, prop, scale);
+ copy_v3_fl(r_scale, 1.0f);
+ RNA_property_float_set_array(op->ptr, prop, r_scale);
}
}
}
@@ -861,8 +866,9 @@ static int effector_add_exec(bContext *C, wmOperator *op)
float mat[4][4];
ED_object_new_primitive_matrix(C, ob, loc, rot, mat);
+ mul_mat3_m4_fl(mat, dia);
BLI_addtail(&cu->editnurb->nurbs,
- ED_curve_add_nurbs_primitive(C, ob, mat, CU_NURBS | CU_PRIM_PATH, dia));
+ ED_curve_add_nurbs_primitive(C, ob, mat, CU_NURBS | CU_PRIM_PATH, 1));
if (!enter_editmode) {
ED_object_editmode_exit_ex(bmain, scene, ob, EM_FREEDATA);
}
@@ -1311,6 +1317,9 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op)
bGPdata *gpd = (ob && (ob->type == OB_GPENCIL)) ? ob->data : NULL;
const int type = RNA_enum_get(op->ptr, "type");
+ const bool use_in_front = RNA_boolean_get(op->ptr, "use_in_front");
+ const bool use_lights = RNA_boolean_get(op->ptr, "use_lights");
+ const int stroke_depth_order = RNA_enum_get(op->ptr, "stroke_depth_order");
ushort local_view_bits;
float loc[3], rot[3];
@@ -1321,12 +1330,14 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op)
if (!ED_object_add_generic_get_opts(C, op, 'Y', loc, rot, NULL, NULL, &local_view_bits, NULL)) {
return OPERATOR_CANCELLED;
}
- /* add new object if not currently editing a GP object,
- * or if "empty" was chosen (i.e. user wants a blank GP canvas)
- */
- if ((gpd == NULL) || (GPENCIL_ANY_MODE(gpd) == false) || (type == GP_EMPTY)) {
+ /* Add new object if not currently editing a GP object. */
+ if ((gpd == NULL) || (GPENCIL_ANY_MODE(gpd) == false)) {
const char *ob_name = NULL;
switch (type) {
+ case GP_EMPTY: {
+ ob_name = "GPencil";
+ break;
+ }
case GP_MONKEY: {
ob_name = "Suzanne";
break;
@@ -1336,6 +1347,7 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op)
break;
}
case GP_LRT_OBJECT:
+ case GP_LRT_SCENE:
case GP_LRT_COLLECTION: {
ob_name = "Line Art";
break;
@@ -1356,6 +1368,13 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op)
/* create relevant geometry */
switch (type) {
+ case GP_EMPTY: {
+ float mat[4][4];
+
+ ED_object_new_primitive_matrix(C, ob, loc, rot, mat);
+ ED_gpencil_create_blank(C, ob, mat);
+ break;
+ }
case GP_STROKE: {
float radius = RNA_float_get(op->ptr, "radius");
float mat[4][4];
@@ -1416,14 +1435,29 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op)
/* Only created one layer and one material. */
strcpy(md->target_layer, ((bGPDlayer *)gpd->layers.first)->info);
md->target_material = BKE_gpencil_material(ob, 1);
+ if (md->target_material) {
+ id_us_plus(&md->target_material->id);
+ }
+
+ if (use_lights) {
+ ob->dtx |= OB_USE_GPENCIL_LIGHTS;
+ }
+ else {
+ ob->dtx &= ~OB_USE_GPENCIL_LIGHTS;
+ }
/* Stroke object is drawn in front of meshes by default. */
- ob->dtx |= OB_DRAW_IN_FRONT;
- }
- case GP_EMPTY:
- /* do nothing */
- break;
+ if (use_in_front) {
+ ob->dtx |= OB_DRAW_IN_FRONT;
+ }
+ else {
+ if (stroke_depth_order == GP_DRAWMODE_3D) {
+ gpd->draw_mode = GP_DRAWMODE_3D;
+ }
+ }
+ break;
+ }
default:
BKE_report(op->reports, RPT_WARNING, "Not implemented");
break;
@@ -1440,6 +1474,39 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
+static void object_add_ui(bContext *UNUSED(C), wmOperator *op)
+{
+ uiLayout *layout = op->layout;
+
+ uiLayoutSetPropSep(layout, true);
+
+ uiItemR(layout, op->ptr, "radius", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "align", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "location", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "rotation", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "type", 0, NULL, ICON_NONE);
+
+ int type = RNA_enum_get(op->ptr, "type");
+ if (type == GP_LRT_COLLECTION || type == GP_LRT_OBJECT || type == GP_LRT_SCENE) {
+ uiItemR(layout, op->ptr, "use_lights", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "use_in_front", 0, NULL, ICON_NONE);
+ bool in_front = RNA_boolean_get(op->ptr, "use_in_front");
+ uiLayout *row = uiLayoutRow(layout, false);
+ uiLayoutSetActive(row, !in_front);
+ uiItemR(row, op->ptr, "stroke_depth_order", 0, NULL, ICON_NONE);
+ }
+}
+
+static EnumPropertyItem rna_enum_gpencil_add_stroke_depth_order_items[] = {
+ {GP_DRAWMODE_2D,
+ "2D",
+ 0,
+ "2D Layers",
+ "Display strokes using grease pencil layers to define order"},
+ {GP_DRAWMODE_3D, "3D", 0, "3D Location", "Display strokes using real 3D position in 3D space"},
+ {0, NULL, 0, NULL, NULL},
+};
+
void OBJECT_OT_gpencil_add(wmOperatorType *ot)
{
/* identifiers */
@@ -1455,11 +1522,28 @@ void OBJECT_OT_gpencil_add(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+ /* ui */
+ ot->ui = object_add_ui;
+
/* properties */
ED_object_add_unit_props_radius(ot);
ED_object_add_generic_props(ot, false);
ot->prop = RNA_def_enum(ot->srna, "type", rna_enum_object_gpencil_type_items, 0, "Type", "");
+ RNA_def_boolean(ot->srna,
+ "use_in_front",
+ false,
+ "In Front",
+ "Show line art grease pencil in front of everything");
+ RNA_def_boolean(
+ ot->srna, "use_lights", false, "Use Lights", "Use lights for this grease pencil object");
+ RNA_def_enum(
+ ot->srna,
+ "stroke_depth_order",
+ rna_enum_gpencil_add_stroke_depth_order_items,
+ GP_DRAWMODE_3D,
+ "Stroke Depth Order",
+ "Defines how the strokes are ordered in 3D space for objects not displayed 'In Front'");
}
/** \} */
@@ -1896,8 +1980,8 @@ void OBJECT_OT_pointcloud_add(wmOperatorType *ot)
/* note: now unlinks constraints as well */
void ED_object_base_free_and_unlink(Main *bmain, Scene *scene, Object *ob)
{
- if (BKE_library_ID_is_indirectly_used(bmain, ob) && ID_REAL_USERS(ob) <= 1 &&
- ID_EXTRA_USERS(ob) == 0) {
+ if (ID_REAL_USERS(ob) <= 1 && ID_EXTRA_USERS(ob) == 0 &&
+ BKE_library_ID_is_indirectly_used(bmain, ob)) {
/* We cannot delete indirectly used object... */
printf(
"WARNING, undeletable object '%s', should have been caught before reaching this "
@@ -1911,6 +1995,17 @@ void ED_object_base_free_and_unlink(Main *bmain, Scene *scene, Object *ob)
BKE_scene_collections_object_remove(bmain, scene, ob, true);
}
+/**
+ * Remove base from a specific scene.
+ * `ob` must not be indirectly used.
+ */
+void ED_object_base_free_and_unlink_no_indirect_check(Main *bmain, Scene *scene, Object *ob)
+{
+ BLI_assert(!BKE_library_ID_is_indirectly_used(bmain, ob));
+ DEG_id_tag_update_ex(bmain, &ob->id, ID_RECALC_BASE_FLAGS);
+ BKE_scene_collections_object_remove(bmain, scene, ob, true);
+}
+
static int object_delete_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
@@ -1918,13 +2013,15 @@ static int object_delete_exec(bContext *C, wmOperator *op)
wmWindowManager *wm = CTX_wm_manager(C);
const bool use_global = RNA_boolean_get(op->ptr, "use_global");
uint changed_count = 0;
+ uint tagged_count = 0;
if (CTX_data_edit_object(C)) {
return OPERATOR_CANCELLED;
}
+ BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
+
CTX_DATA_BEGIN (C, Object *, ob, selected_objects) {
- const bool is_indirectly_used = BKE_library_ID_is_indirectly_used(bmain, ob);
if (ob->id.tag & LIB_TAG_INDIRECT) {
/* Can this case ever happen? */
BKE_reportf(op->reports,
@@ -1933,7 +2030,9 @@ static int object_delete_exec(bContext *C, wmOperator *op)
ob->id.name + 2);
continue;
}
- if (is_indirectly_used && ID_REAL_USERS(ob) <= 1 && ID_EXTRA_USERS(ob) == 0) {
+
+ if (ID_REAL_USERS(ob) <= 1 && ID_EXTRA_USERS(ob) == 0 &&
+ BKE_library_ID_is_indirectly_used(bmain, ob)) {
BKE_reportf(op->reports,
RPT_WARNING,
"Cannot delete object '%s' from scene '%s', indirectly used objects need at "
@@ -1949,63 +2048,41 @@ static int object_delete_exec(bContext *C, wmOperator *op)
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
}
- /* This is sort of a quick hack to address T51243 -
- * Proper thing to do here would be to nuke most of all this custom scene/object/base handling,
- * and use generic lib remap/query for that.
- * But this is for later (aka 2.8, once layers & co are settled and working).
- */
- if (use_global && ob->id.lib == NULL) {
- /* We want to nuke the object, let's nuke it the easy way (not for linked data though)... */
- BKE_id_delete(bmain, &ob->id);
- changed_count += 1;
- continue;
+ /* Use multi tagged delete if `use_global=True`, or the object is used only in one scene. */
+ if (use_global || ID_REAL_USERS(ob) <= 1) {
+ ob->id.tag |= LIB_TAG_DOIT;
+ tagged_count += 1;
}
+ else {
+ /* Object is used in multiple scenes. Delete the object from the current scene only. */
+ ED_object_base_free_and_unlink_no_indirect_check(bmain, scene, ob);
+ changed_count += 1;
- /* remove from Grease Pencil parent */
- /* XXX This is likely not correct?
- * Will also remove parent from grease pencil from other scenes,
- * even when use_global is false... */
- for (bGPdata *gpd = bmain->gpencils.first; gpd; gpd = gpd->id.next) {
- LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
- if (gpl->parent != NULL) {
- if (gpl->parent == ob) {
- gpl->parent = NULL;
- }
- }
- }
- }
-
- /* remove from current scene only */
- ED_object_base_free_and_unlink(bmain, scene, ob);
- changed_count += 1;
-
- if (use_global) {
- Scene *scene_iter;
- for (scene_iter = bmain->scenes.first; scene_iter; scene_iter = scene_iter->id.next) {
- if (scene_iter != scene && !ID_IS_LINKED(scene_iter)) {
- if (is_indirectly_used && ID_REAL_USERS(ob) <= 1 && ID_EXTRA_USERS(ob) == 0) {
- BKE_reportf(op->reports,
- RPT_WARNING,
- "Cannot delete object '%s' from scene '%s', indirectly used objects need "
- "at least one user",
- ob->id.name + 2,
- scene_iter->id.name + 2);
- break;
+ /* FIXME: this will also remove parent from grease pencil from other scenes. */
+ /* Remove from Grease Pencil parent */
+ for (bGPdata *gpd = bmain->gpencils.first; gpd; gpd = gpd->id.next) {
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+ if (gpl->parent != NULL) {
+ if (gpl->parent == ob) {
+ gpl->parent = NULL;
+ }
}
- ED_object_base_free_and_unlink(bmain, scene_iter, ob);
}
}
}
- /* end global */
}
CTX_DATA_END;
- BKE_reportf(op->reports, RPT_INFO, "Deleted %u object(s)", changed_count);
-
- if (changed_count == 0) {
+ if ((changed_count + tagged_count) == 0) {
return OPERATOR_CANCELLED;
}
+ if (tagged_count > 0) {
+ BKE_id_multi_tagged_delete(bmain);
+ }
+
+ BKE_reportf(op->reports, RPT_INFO, "Deleted %u object(s)", (changed_count + tagged_count));
+
/* delete has to handle all open scenes */
BKE_main_id_tag_listbase(&bmain->scenes, LIB_TAG_DOIT, true);
LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
@@ -2077,8 +2154,7 @@ static void copy_object_set_idnew(bContext *C)
FOREACH_MAIN_ID_END;
#endif
- BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false);
- BKE_main_id_clear_newpoins(bmain);
+ BKE_main_id_newptr_and_tag_clear(bmain);
}
/** \} */
@@ -2399,7 +2475,7 @@ static void make_object_duplilist_real(bContext *C,
free_object_duplilist(lb_duplis);
- BKE_main_id_clear_newpoins(bmain);
+ BKE_main_id_newptr_and_tag_clear(bmain);
base->object->transflag &= ~OB_DUPLI;
DEG_id_tag_update(&base->object->id, ID_RECALC_COPY_ON_WRITE);
@@ -2414,7 +2490,7 @@ static int object_duplicates_make_real_exec(bContext *C, wmOperator *op)
const bool use_base_parent = RNA_boolean_get(op->ptr, "use_base_parent");
const bool use_hierarchy = RNA_boolean_get(op->ptr, "use_hierarchy");
- BKE_main_id_clear_newpoins(bmain);
+ BKE_main_id_newptr_and_tag_clear(bmain);
CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases) {
make_object_duplilist_real(C, depsgraph, scene, base, use_base_parent, use_hierarchy);
@@ -2626,6 +2702,7 @@ static int object_convert_exec(bContext *C, wmOperator *op)
int a, mballConverted = 0;
bool gpencilConverted = false;
+ bool gpencilCurveConverted = false;
/* don't forget multiple users! */
@@ -2838,6 +2915,7 @@ static int object_convert_exec(bContext *C, wmOperator *op)
me_eval = BKE_mesh_copy_for_eval(me_eval, false);
/* Full (edge-angle based) draw calculation should ideally be performed. */
BKE_mesh_edges_set_draw_render(me_eval);
+ BKE_object_material_from_eval_data(bmain, newob, &me_eval->id);
BKE_mesh_nomain_to_mesh(me_eval, newob->data, newob, &CD_MASK_MESH, true);
BKE_object_free_modifiers(newob, 0); /* after derivedmesh calls! */
}
@@ -2901,7 +2979,7 @@ static int object_convert_exec(bContext *C, wmOperator *op)
}
cu->flag &= ~CU_3D;
- BKE_curve_curve_dimension_update(cu);
+ BKE_curve_dimension_update(cu);
if (target == OB_MESH) {
/* No assumption should be made that the resulting objects is a mesh, as conversion can
@@ -2910,6 +2988,16 @@ static int object_convert_exec(bContext *C, wmOperator *op)
/* meshes doesn't use displist */
BKE_object_free_curve_cache(newob);
}
+ else if (target == OB_GPENCIL) {
+ ushort local_view_bits = (v3d && v3d->localvd) ? v3d->local_view_uuid : 0;
+ Object *ob_gpencil = ED_gpencil_add_object(C, newob->loc, local_view_bits);
+ copy_v3_v3(ob_gpencil->rot, newob->rot);
+ copy_v3_v3(ob_gpencil->scale, newob->scale);
+ BKE_gpencil_convert_curve(bmain, scene, ob_gpencil, newob, false, 1.0f, 0.0f);
+ gpencilConverted = true;
+ gpencilCurveConverted = true;
+ basen = NULL;
+ }
}
else if (ELEM(ob->type, OB_CURVE, OB_SURF)) {
ob->flag |= OB_DONE;
@@ -3091,6 +3179,17 @@ static int object_convert_exec(bContext *C, wmOperator *op)
FOREACH_SCENE_OBJECT_END;
}
}
+ else {
+ /* Remove Text curves converted to Grease Pencil object to avoid duplicated curves. */
+ if (gpencilCurveConverted) {
+ FOREACH_SCENE_OBJECT_BEGIN (scene, ob_delete) {
+ if (ELEM(ob_delete->type, OB_CURVE) && (ob_delete->flag & OB_DONE)) {
+ ED_object_base_free_and_unlink(bmain, scene, ob_delete);
+ }
+ }
+ FOREACH_SCENE_OBJECT_END;
+ }
+ }
// XXX ED_object_editmode_enter(C, 0);
// XXX exit_editmode(C, EM_FREEDATA|); /* freedata, but no undo */
@@ -3117,20 +3216,18 @@ static int object_convert_exec(bContext *C, wmOperator *op)
static void object_convert_ui(bContext *UNUSED(C), wmOperator *op)
{
uiLayout *layout = op->layout;
- PointerRNA ptr;
uiLayoutSetPropSep(layout, true);
- RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
- uiItemR(layout, &ptr, "target", 0, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "keep_original", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "target", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "keep_original", 0, NULL, ICON_NONE);
- if (RNA_enum_get(&ptr, "target") == OB_GPENCIL) {
- uiItemR(layout, &ptr, "thickness", 0, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "angle", 0, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "offset", 0, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "seams", 0, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "faces", 0, NULL, ICON_NONE);
+ if (RNA_enum_get(op->ptr, "target") == OB_GPENCIL) {
+ uiItemR(layout, op->ptr, "thickness", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "angle", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "offset", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "seams", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "faces", 0, NULL, ICON_NONE);
}
}
@@ -3278,7 +3375,7 @@ Base *ED_object_add_duplicate(
DEG_id_tag_update_ex(bmain, (ID *)ob->data, ID_RECALC_EDITORS);
}
- BKE_main_id_clear_newpoins(bmain);
+ BKE_main_id_newptr_and_tag_clear(bmain);
return basen;
}
@@ -3294,8 +3391,7 @@ static int duplicate_exec(bContext *C, wmOperator *op)
/* We need to handle that here ourselves, because we may duplicate several objects, in which case
* we also want to remap pointers between those... */
- BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false);
- BKE_main_id_clear_newpoins(bmain);
+ BKE_main_id_newptr_and_tag_clear(bmain);
CTX_DATA_BEGIN (C, Base *, base, selected_bases) {
Base *basen = object_add_duplicate_internal(
@@ -3372,6 +3468,19 @@ void OBJECT_OT_duplicate(wmOperatorType *ot)
* Use for drag & drop.
* \{ */
+static Base *object_add_ensure_in_view_layer(Main *bmain, ViewLayer *view_layer, Object *ob)
+{
+ Base *base = BKE_view_layer_base_find(view_layer, ob);
+
+ if (!base) {
+ LayerCollection *layer_collection = BKE_layer_collection_get_active(view_layer);
+ BKE_collection_object_add(bmain, layer_collection->collection, ob);
+ base = BKE_view_layer_base_find(view_layer, ob);
+ }
+
+ return base;
+}
+
static int object_add_named_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
@@ -3379,7 +3488,8 @@ static int object_add_named_exec(bContext *C, wmOperator *op)
ViewLayer *view_layer = CTX_data_view_layer(C);
Base *basen;
Object *ob;
- const bool linked = RNA_boolean_get(op->ptr, "linked");
+ const bool duplicate = RNA_boolean_get(op->ptr, "duplicate");
+ const bool linked = duplicate && RNA_boolean_get(op->ptr, "linked");
const eDupli_ID_Flags dupflag = (linked) ? 0 : (eDupli_ID_Flags)U.dupflag;
char name[MAX_ID_NAME - 2];
@@ -3393,10 +3503,30 @@ static int object_add_named_exec(bContext *C, wmOperator *op)
}
/* prepare dupli */
- basen = object_add_duplicate_internal(bmain, scene, view_layer, ob, dupflag, 0);
+ if (duplicate) {
+ basen = object_add_duplicate_internal(
+ bmain,
+ scene,
+ view_layer,
+ ob,
+ dupflag,
+ /* Sub-process flag because the new-ID remapping (#BKE_libblock_relink_to_newid()) in this
+ * function will only work if the object is already linked in the view layer, which is not
+ * the case here. So we have to do the new-ID relinking ourselves
+ * (#copy_object_set_idnew()).
+ */
+ LIB_ID_DUPLICATE_IS_SUBPROCESS);
+ }
+ else {
+ /* basen is actually not a new base in this case. */
+ basen = object_add_ensure_in_view_layer(bmain, view_layer, ob);
+ }
if (basen == NULL) {
- BKE_report(op->reports, RPT_ERROR, "Object could not be duplicated");
+ BKE_report(op->reports,
+ RPT_ERROR,
+ duplicate ? "Object could not be duplicated" :
+ "Object could not be linked to the view layer");
return OPERATOR_CANCELLED;
}
@@ -3410,7 +3540,7 @@ static int object_add_named_exec(bContext *C, wmOperator *op)
/* object_add_duplicate_internal() doesn't deselect other objects, unlike object_add_common() or
* BKE_view_layer_base_deselect_all(). */
- ED_object_base_deselect_all(view_layer, NULL, BA_DESELECT);
+ ED_object_base_deselect_all(view_layer, NULL, SEL_DESELECT);
ED_object_base_select(basen, BA_SELECT);
ED_object_base_activate(C, basen);
@@ -3443,11 +3573,24 @@ void OBJECT_OT_add_named(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+ PropertyRNA *prop;
+
+ prop = RNA_def_boolean(
+ ot->srna,
+ "duplicate",
+ true,
+ "Duplicate",
+ "Create a duplicate of the object. If not set, only ensures the object is linked into the "
+ "active view layer, positions and selects/activates it (deselecting others)");
+ RNA_def_property_flag(prop, PROP_HIDDEN);
+
RNA_def_boolean(ot->srna,
"linked",
- 0,
+ false,
"Linked",
- "Duplicate object but not object data, linking to the original data");
+ "Duplicate object but not object data, linking to the original data (ignored if "
+ "'duplicate' is false)");
+
RNA_def_string(ot->srna, "name", NULL, MAX_ID_NAME - 2, "Name", "Object name to add");
object_add_drop_xy_props(ot);
diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c
index f651f5bc3fd..3370476d466 100644
--- a/source/blender/editors/object/object_bake_api.c
+++ b/source/blender/editors/object/object_bake_api.c
@@ -669,9 +669,11 @@ static void bake_targets_clear(Main *bmain, const bool is_tangent)
}
/* create new mesh with edit mode changes and modifiers applied */
-static Mesh *bake_mesh_new_from_object(Object *object)
+static Mesh *bake_mesh_new_from_object(Depsgraph *depsgraph,
+ Object *object,
+ const bool preserve_origindex)
{
- Mesh *me = BKE_mesh_new_from_object(NULL, object, false);
+ Mesh *me = BKE_mesh_new_from_object(depsgraph, object, false, preserve_origindex);
if (me->flag & ME_AUTOSMOOTH) {
BKE_mesh_split_faces(me, true);
@@ -961,10 +963,39 @@ static bool bake_targets_init_vertex_colors(BakeTargets *targets, Object *ob, Re
return true;
}
+static int find_original_loop(const Mesh *me_orig,
+ const int *vert_origindex,
+ const int *poly_origindex,
+ const int poly_eval,
+ const int vert_eval)
+{
+ /* Get original vertex and polygon index. There is currently no loop mapping
+ * in modifier stack evaluation. */
+ const int vert_orig = vert_origindex[vert_eval];
+ const int poly_orig = poly_origindex[poly_eval];
+
+ if (vert_orig == ORIGINDEX_NONE || poly_orig == ORIGINDEX_NONE) {
+ return ORIGINDEX_NONE;
+ }
+
+ /* Find matching loop with original vertex in original polygon. */
+ MPoly *mpoly_orig = me_orig->mpoly + poly_orig;
+ MLoop *mloop_orig = me_orig->mloop + mpoly_orig->loopstart;
+ for (int j = 0; j < mpoly_orig->totloop; ++j, ++mloop_orig) {
+ if (mloop_orig->v == vert_orig) {
+ return mpoly_orig->loopstart + j;
+ }
+ }
+
+ return ORIGINDEX_NONE;
+}
+
static void bake_targets_populate_pixels_vertex_colors(BakeTargets *targets,
- Mesh *me,
+ Object *ob,
+ Mesh *me_eval,
BakePixel *pixel_array)
{
+ Mesh *me = ob->data;
const int num_pixels = targets->num_pixels;
/* Initialize blank pixels. */
@@ -983,16 +1014,31 @@ static void bake_targets_populate_pixels_vertex_colors(BakeTargets *targets,
}
/* Populate through adjacent triangles, first triangle wins. */
- const int tottri = poly_to_tri_count(me->totpoly, me->totloop);
+ const int tottri = poly_to_tri_count(me_eval->totpoly, me_eval->totloop);
MLoopTri *looptri = MEM_mallocN(sizeof(*looptri) * tottri, __func__);
- BKE_mesh_recalc_looptri(me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, looptri);
+ BKE_mesh_recalc_looptri(
+ me_eval->mloop, me_eval->mpoly, me_eval->mvert, me_eval->totloop, me_eval->totpoly, looptri);
+
+ /* For mapping back to original mesh in case there are modifiers. */
+ const int *vert_origindex = CustomData_get_layer(&me_eval->vdata, CD_ORIGINDEX);
+ const int *poly_origindex = CustomData_get_layer(&me_eval->pdata, CD_ORIGINDEX);
for (int i = 0; i < tottri; i++) {
const MLoopTri *lt = &looptri[i];
for (int j = 0; j < 3; j++) {
- const unsigned int l = lt->tri[j];
+ unsigned int l = lt->tri[j];
+ unsigned int v = me_eval->mloop[l].v;
+
+ /* Map back to original loop if there are modifiers. */
+ if (vert_origindex != NULL && poly_origindex != NULL) {
+ l = find_original_loop(me, vert_origindex, poly_origindex, lt->poly, v);
+ if (l == ORIGINDEX_NONE || l >= me->totloop) {
+ continue;
+ }
+ }
+
BakePixel *pixel = &pixel_array[l];
if (pixel->primitive_id != -1) {
@@ -1004,7 +1050,7 @@ static void bake_targets_populate_pixels_vertex_colors(BakeTargets *targets,
/* Seed is the vertex, so that sampling noise is coherent for the same
* vertex, but different corners can still have different normals,
* materials and UVs. */
- pixel->seed = me->mloop[l].v;
+ pixel->seed = v;
/* Barycentric coordinates, nudged a bit to avoid precision issues that
* may happen when exactly at the vertex coordinate. */
@@ -1043,7 +1089,7 @@ static void bake_result_add_to_rgba(float rgba[4], const float *result, const in
}
}
-static bool bake_targets_output_vertex_colors(BakeTargets *targets, Object *ob, Mesh *me_split)
+static bool bake_targets_output_vertex_colors(BakeTargets *targets, Object *ob)
{
Mesh *me = ob->data;
MPropCol *mcol = CustomData_get_layer(&me->vdata, CD_PROP_COLOR);
@@ -1052,11 +1098,6 @@ static bool bake_targets_output_vertex_colors(BakeTargets *targets, Object *ob,
const int num_channels = targets->num_channels;
const float *result = targets->result;
- /* We bake using a mesh with additional vertices for split normals, but the
- * number of loops must match to be able to transfer the vertex colors. */
- BLI_assert(me->totloop == me_split->totloop);
- UNUSED_VARS_NDEBUG(me_split);
-
if (mcol_valid) {
const int totvert = me->totvert;
const int totloop = me->totloop;
@@ -1111,16 +1152,17 @@ static bool bake_targets_output_vertex_colors(BakeTargets *targets, Object *ob,
static bool bake_targets_init(const BakeAPIRender *bkr,
BakeTargets *targets,
Object *ob,
+ Object *ob_eval,
ReportList *reports)
{
if (bkr->target == R_BAKE_TARGET_IMAGE_TEXTURES) {
if (bkr->save_mode == R_BAKE_SAVE_INTERNAL) {
- if (!bake_targets_init_internal(bkr, targets, ob, reports)) {
+ if (!bake_targets_init_internal(bkr, targets, ob_eval, reports)) {
return false;
}
}
else if (bkr->save_mode == R_BAKE_SAVE_EXTERNAL) {
- if (!bake_targets_init_external(bkr, targets, ob, reports)) {
+ if (!bake_targets_init_external(bkr, targets, ob_eval, reports)) {
return false;
}
}
@@ -1145,14 +1187,15 @@ static bool bake_targets_init(const BakeAPIRender *bkr,
static void bake_targets_populate_pixels(const BakeAPIRender *bkr,
BakeTargets *targets,
- Mesh *me,
+ Object *ob,
+ Mesh *me_eval,
BakePixel *pixel_array)
{
if (bkr->target == R_BAKE_TARGET_VERTEX_COLORS) {
- bake_targets_populate_pixels_vertex_colors(targets, me, pixel_array);
+ bake_targets_populate_pixels_vertex_colors(targets, ob, me_eval, pixel_array);
}
else {
- RE_bake_pixels_populate(me, pixel_array, targets->num_pixels, targets, bkr->uv_layer);
+ RE_bake_pixels_populate(me_eval, pixel_array, targets->num_pixels, targets, bkr->uv_layer);
}
}
@@ -1160,7 +1203,7 @@ static bool bake_targets_output(const BakeAPIRender *bkr,
BakeTargets *targets,
Object *ob,
Object *ob_eval,
- Mesh *me,
+ Mesh *me_eval,
BakePixel *pixel_array,
ReportList *reports)
{
@@ -1169,11 +1212,12 @@ static bool bake_targets_output(const BakeAPIRender *bkr,
return bake_targets_output_internal(bkr, targets, ob, pixel_array, reports);
}
if (bkr->save_mode == R_BAKE_SAVE_EXTERNAL) {
- return bake_targets_output_external(bkr, targets, ob, ob_eval, me, pixel_array, reports);
+ return bake_targets_output_external(
+ bkr, targets, ob, ob_eval, me_eval, pixel_array, reports);
}
}
else if (bkr->target == R_BAKE_TARGET_VERTEX_COLORS) {
- return bake_targets_output_vertex_colors(targets, ob, me);
+ return bake_targets_output_vertex_colors(targets, ob);
}
return false;
@@ -1213,8 +1257,8 @@ static int bake(const BakeAPIRender *bkr,
BakeHighPolyData *highpoly = NULL;
int tot_highpoly = 0;
- Mesh *me_low = NULL;
- Mesh *me_cage = NULL;
+ Mesh *me_low_eval = NULL;
+ Mesh *me_cage_eval = NULL;
MultiresModifierData *mmd_low = NULL;
int mmd_flags_low = 0;
@@ -1224,6 +1268,8 @@ static int bake(const BakeAPIRender *bkr,
BakeTargets targets = {NULL};
+ const bool preserve_origindex = (bkr->target == R_BAKE_TARGET_VERTEX_COLORS);
+
RE_bake_engine_set_engine_parameters(re, bmain, scene);
if (!RE_bake_has_engine(re)) {
@@ -1287,10 +1333,10 @@ static int bake(const BakeAPIRender *bkr,
ob_low_eval = DEG_get_evaluated_object(depsgraph, ob_low);
/* get the mesh as it arrives in the renderer */
- me_low = bake_mesh_new_from_object(ob_low_eval);
+ me_low_eval = bake_mesh_new_from_object(depsgraph, ob_low_eval, preserve_origindex);
/* Initialize bake targets. */
- if (!bake_targets_init(bkr, &targets, ob_low_eval, reports)) {
+ if (!bake_targets_init(bkr, &targets, ob_low, ob_low_eval, reports)) {
goto cleanup;
}
@@ -1298,7 +1344,7 @@ static int bake(const BakeAPIRender *bkr,
* it is populated later with the cage mesh (smoothed version of the mesh). */
pixel_array_low = MEM_mallocN(sizeof(BakePixel) * targets.num_pixels, "bake pixels low poly");
if ((bkr->is_selected_to_active && (ob_cage == NULL) && bkr->is_cage) == false) {
- bake_targets_populate_pixels(bkr, &targets, me_low, pixel_array_low);
+ bake_targets_populate_pixels(bkr, &targets, ob_low, me_low_eval, pixel_array_low);
}
if (bkr->is_selected_to_active) {
@@ -1307,8 +1353,9 @@ static int bake(const BakeAPIRender *bkr,
/* prepare cage mesh */
if (ob_cage) {
- me_cage = bake_mesh_new_from_object(ob_cage_eval);
- if ((me_low->totpoly != me_cage->totpoly) || (me_low->totloop != me_cage->totloop)) {
+ me_cage_eval = bake_mesh_new_from_object(depsgraph, ob_cage_eval, preserve_origindex);
+ if ((me_low_eval->totpoly != me_cage_eval->totpoly) ||
+ (me_low_eval->totloop != me_cage_eval->totloop)) {
BKE_report(reports,
RPT_ERROR,
"Invalid cage object, the cage mesh must have the same number "
@@ -1348,8 +1395,8 @@ static int bake(const BakeAPIRender *bkr,
BKE_object_handle_data_update(depsgraph, scene, ob_low_eval);
}
- me_cage = BKE_mesh_new_from_object(NULL, ob_low_eval, false);
- bake_targets_populate_pixels(bkr, &targets, me_cage, pixel_array_low);
+ me_cage_eval = BKE_mesh_new_from_object(NULL, ob_low_eval, false, preserve_origindex);
+ bake_targets_populate_pixels(bkr, &targets, ob_low, me_cage_eval, pixel_array_low);
}
highpoly = MEM_callocN(sizeof(BakeHighPolyData) * tot_highpoly, "bake high poly objects");
@@ -1367,7 +1414,7 @@ static int bake(const BakeAPIRender *bkr,
highpoly[i].ob_eval = DEG_get_evaluated_object(depsgraph, ob_iter);
highpoly[i].ob_eval->restrictflag &= ~OB_RESTRICT_RENDER;
highpoly[i].ob_eval->base_flag |= (BASE_VISIBLE_DEPSGRAPH | BASE_ENABLED_RENDER);
- highpoly[i].me = BKE_mesh_new_from_object(NULL, highpoly[i].ob_eval, false);
+ highpoly[i].me = BKE_mesh_new_from_object(NULL, highpoly[i].ob_eval, false, false);
/* Low-poly to high-poly transformation matrix. */
copy_m4_m4(highpoly[i].obmat, highpoly[i].ob->obmat);
@@ -1391,7 +1438,7 @@ static int bake(const BakeAPIRender *bkr,
pixel_array_high = MEM_mallocN(sizeof(BakePixel) * targets.num_pixels,
"bake pixels high poly");
- if (!RE_bake_pixels_populate_from_objects(me_low,
+ if (!RE_bake_pixels_populate_from_objects(me_low_eval,
pixel_array_low,
pixel_array_high,
highpoly,
@@ -1402,7 +1449,7 @@ static int bake(const BakeAPIRender *bkr,
bkr->max_ray_distance,
ob_low_eval->obmat,
(ob_cage ? ob_cage->obmat : ob_low_eval->obmat),
- me_cage)) {
+ me_cage_eval)) {
BKE_report(reports, RPT_ERROR, "Error handling selected objects");
goto cleanup;
}
@@ -1478,7 +1525,7 @@ static int bake(const BakeAPIRender *bkr,
targets.num_pixels,
targets.num_channels,
targets.result,
- me_low,
+ me_low_eval,
bkr->normal_swizzle,
ob_low_eval->obmat);
}
@@ -1497,8 +1544,8 @@ static int bake(const BakeAPIRender *bkr,
}
/* Evaluate modifiers again. */
- me_nores = BKE_mesh_new_from_object(NULL, ob_low_eval, false);
- bake_targets_populate_pixels(bkr, &targets, me_nores, pixel_array_low);
+ me_nores = BKE_mesh_new_from_object(NULL, ob_low_eval, false, false);
+ bake_targets_populate_pixels(bkr, &targets, ob_low, me_nores, pixel_array_low);
RE_bake_normal_world_to_tangent(pixel_array_low,
targets.num_pixels,
@@ -1527,7 +1574,7 @@ static int bake(const BakeAPIRender *bkr,
else {
/* save the results */
if (bake_targets_output(
- bkr, &targets, ob_low, ob_low_eval, me_low, pixel_array_low, reports)) {
+ bkr, &targets, ob_low, ob_low_eval, me_low_eval, pixel_array_low, reports)) {
op_result = OPERATOR_FINISHED;
}
else {
@@ -1562,12 +1609,12 @@ cleanup:
bake_targets_free(&targets);
- if (me_low != NULL) {
- BKE_id_free(NULL, &me_low->id);
+ if (me_low_eval != NULL) {
+ BKE_id_free(NULL, &me_low_eval->id);
}
- if (me_cage != NULL) {
- BKE_id_free(NULL, &me_cage->id);
+ if (me_cage_eval != NULL) {
+ BKE_id_free(NULL, &me_cage_eval->id);
}
DEG_graph_free(depsgraph);
diff --git a/source/blender/editors/object/object_data_transfer.c b/source/blender/editors/object/object_data_transfer.c
index 72ef72403cf..22c9d669ff3 100644
--- a/source/blender/editors/object/object_data_transfer.c
+++ b/source/blender/editors/object/object_data_transfer.c
@@ -684,7 +684,8 @@ void OBJECT_OT_data_transfer(wmOperatorType *ot)
false,
"Auto Transform",
"Automatically compute transformation to get the best possible match between source and "
- "destination meshes (WARNING: results will never be as good as manual matching of objects)");
+ "destination meshes.\n"
+ "Warning: Results will never be as good as manual matching of objects");
RNA_def_boolean(ot->srna,
"use_object_transform",
true,
diff --git a/source/blender/editors/object/object_data_transform.c b/source/blender/editors/object/object_data_transform.c
index 72dde7b734b..6e3a5e715f6 100644
--- a/source/blender/editors/object/object_data_transform.c
+++ b/source/blender/editors/object/object_data_transform.c
@@ -593,13 +593,14 @@ void ED_object_data_xform_by_mat4(struct XFormObjectData *xod_base, const float
if (xod_base->is_edit_mode) {
EditNurb *editnurb = cu->editnurb;
nurb = &editnurb->nurbs;
- BKE_curve_nurbs_vert_coords_apply_with_mat4(&editnurb->nurbs, xod->elem_array, mat, true);
+ BKE_curve_nurbs_vert_coords_apply_with_mat4(
+ &editnurb->nurbs, xod->elem_array, mat, CU_IS_2D(cu));
/* Always operate on all keys for the moment. */
// key_index = editnurb->shapenr - 1;
}
else {
nurb = &cu->nurb;
- BKE_curve_nurbs_vert_coords_apply_with_mat4(&cu->nurb, xod->elem_array, mat, true);
+ BKE_curve_nurbs_vert_coords_apply_with_mat4(&cu->nurb, xod->elem_array, mat, CU_IS_2D(cu));
}
if ((key != NULL) && (xod->key_data != NULL)) {
@@ -694,12 +695,12 @@ void ED_object_data_xform_restore(struct XFormObjectData *xod_base)
struct XFormObjectData_Curve *xod = (struct XFormObjectData_Curve *)xod_base;
if (xod_base->is_edit_mode) {
EditNurb *editnurb = cu->editnurb;
- BKE_curve_nurbs_vert_coords_apply(&editnurb->nurbs, xod->elem_array, true);
+ BKE_curve_nurbs_vert_coords_apply(&editnurb->nurbs, xod->elem_array, CU_IS_2D(cu));
/* Always operate on all keys for the moment. */
// key_index = editnurb->shapenr - 1;
}
else {
- BKE_curve_nurbs_vert_coords_apply(&cu->nurb, xod->elem_array, true);
+ BKE_curve_nurbs_vert_coords_apply(&cu->nurb, xod->elem_array, CU_IS_2D(cu));
}
if ((key != NULL) && (xod->key_data != NULL)) {
diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c
index c774bc9f9cc..5be572baec5 100644
--- a/source/blender/editors/object/object_edit.c
+++ b/source/blender/editors/object/object_edit.c
@@ -55,6 +55,7 @@
#include "IMB_imbuf_types.h"
#include "BKE_anim_visualization.h"
+#include "BKE_armature.h"
#include "BKE_collection.h"
#include "BKE_constraint.h"
#include "BKE_context.h"
@@ -520,11 +521,18 @@ static bool mesh_needs_keyindex(Main *bmain, const Mesh *me)
}
/**
- * Load EditMode data back into the object,
- * optionally freeing the editmode data.
+ * Load edit-mode data back into the object.
+ *
+ * \param load_data: Flush the edit-mode data back to the object.
+ * \param free_data: Free the edit-mode data.
*/
-static bool ED_object_editmode_load_ex(Main *bmain, Object *obedit, const bool freedata)
+static bool ED_object_editmode_load_free_ex(Main *bmain,
+ Object *obedit,
+ const bool load_data,
+ const bool free_data)
{
+ BLI_assert(load_data || free_data);
+
if (obedit == NULL) {
return false;
}
@@ -544,9 +552,11 @@ static bool ED_object_editmode_load_ex(Main *bmain, Object *obedit, const bool f
return false;
}
- EDBM_mesh_load_ex(bmain, obedit, freedata);
+ if (load_data) {
+ EDBM_mesh_load_ex(bmain, obedit, free_data);
+ }
- if (freedata) {
+ if (free_data) {
EDBM_mesh_free(me->edit_mesh);
MEM_freeN(me->edit_mesh);
me->edit_mesh = NULL;
@@ -562,9 +572,21 @@ static bool ED_object_editmode_load_ex(Main *bmain, Object *obedit, const bool f
if (arm->edbo == NULL) {
return false;
}
- ED_armature_from_edit(bmain, obedit->data);
- if (freedata) {
+
+ if (load_data) {
+ ED_armature_from_edit(bmain, obedit->data);
+ }
+
+ if (free_data) {
ED_armature_edit_free(obedit->data);
+
+ if (load_data == false) {
+ /* Don't keep unused pose channels created by duplicating bones
+ * which may have been deleted/undone, see: T87631. */
+ if (obedit->pose != NULL) {
+ BKE_pose_channels_clear_with_null_bone(obedit->pose, true);
+ }
+ }
}
/* TODO(sergey): Pose channels might have been changed, so need
* to inform dependency graph about this. But is it really the
@@ -577,8 +599,12 @@ static bool ED_object_editmode_load_ex(Main *bmain, Object *obedit, const bool f
if (cu->editnurb == NULL) {
return false;
}
- ED_curve_editnurb_load(bmain, obedit);
- if (freedata) {
+
+ if (load_data) {
+ ED_curve_editnurb_load(bmain, obedit);
+ }
+
+ if (free_data) {
ED_curve_editnurb_free(obedit);
}
}
@@ -587,8 +613,12 @@ static bool ED_object_editmode_load_ex(Main *bmain, Object *obedit, const bool f
if (cu->editfont == NULL) {
return false;
}
- ED_curve_editfont_load(obedit);
- if (freedata) {
+
+ if (load_data) {
+ ED_curve_editfont_load(obedit);
+ }
+
+ if (free_data) {
ED_curve_editfont_free(obedit);
}
}
@@ -597,8 +627,12 @@ static bool ED_object_editmode_load_ex(Main *bmain, Object *obedit, const bool f
if (lt->editlatt == NULL) {
return false;
}
- BKE_editlattice_load(obedit);
- if (freedata) {
+
+ if (load_data) {
+ BKE_editlattice_load(obedit);
+ }
+
+ if (free_data) {
BKE_editlattice_free(obedit);
}
}
@@ -607,8 +641,12 @@ static bool ED_object_editmode_load_ex(Main *bmain, Object *obedit, const bool f
if (mb->editelems == NULL) {
return false;
}
- ED_mball_editmball_load(obedit);
- if (freedata) {
+
+ if (load_data) {
+ ED_mball_editmball_load(obedit);
+ }
+
+ if (free_data) {
ED_mball_editmball_free(obedit);
}
}
@@ -616,9 +654,11 @@ static bool ED_object_editmode_load_ex(Main *bmain, Object *obedit, const bool f
return false;
}
- char *needs_flush_ptr = BKE_object_data_editmode_flush_ptr_get(obedit->data);
- if (needs_flush_ptr) {
- *needs_flush_ptr = false;
+ if (load_data) {
+ char *needs_flush_ptr = BKE_object_data_editmode_flush_ptr_get(obedit->data);
+ if (needs_flush_ptr) {
+ *needs_flush_ptr = false;
+ }
}
return true;
@@ -626,7 +666,7 @@ static bool ED_object_editmode_load_ex(Main *bmain, Object *obedit, const bool f
bool ED_object_editmode_load(Main *bmain, Object *obedit)
{
- return ED_object_editmode_load_ex(bmain, obedit, false);
+ return ED_object_editmode_load_free_ex(bmain, obedit, true, false);
}
/**
@@ -635,9 +675,9 @@ bool ED_object_editmode_load(Main *bmain, Object *obedit)
*/
bool ED_object_editmode_exit_ex(Main *bmain, Scene *scene, Object *obedit, int flag)
{
- const bool freedata = (flag & EM_FREEDATA) != 0;
+ const bool free_data = (flag & EM_FREEDATA) != 0;
- if (ED_object_editmode_load_ex(bmain, obedit, freedata) == false) {
+ if (ED_object_editmode_load_free_ex(bmain, obedit, true, free_data) == false) {
/* in rare cases (background mode) its possible active object
* is flagged for editmode, without 'obedit' being set T35489. */
if (UNLIKELY(obedit && obedit->mode & OB_MODE_EDIT)) {
@@ -648,8 +688,8 @@ bool ED_object_editmode_exit_ex(Main *bmain, Scene *scene, Object *obedit, int f
return true;
}
- /* freedata only 0 now on file saves and render */
- if (freedata) {
+ /* `free_data` only false now on file saves and render. */
+ if (free_data) {
/* flag object caches as outdated */
ListBase pidlist;
BKE_ptcache_ids_from_object(&pidlist, obedit, scene, 0);
@@ -683,6 +723,16 @@ bool ED_object_editmode_exit(bContext *C, int flag)
return ED_object_editmode_exit_ex(bmain, scene, obedit, flag);
}
+/**
+ * Support freeing edit-mode data without flushing it back to the object.
+ *
+ * \return true if data was freed.
+ */
+bool ED_object_editmode_free_ex(Main *bmain, Object *obedit)
+{
+ return ED_object_editmode_load_free_ex(bmain, obedit, false, true);
+}
+
bool ED_object_editmode_exit_multi_ex(Main *bmain, Scene *scene, ViewLayer *view_layer, int flag)
{
Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
@@ -718,26 +768,22 @@ bool ED_object_editmode_enter_ex(Main *bmain, Scene *scene, Object *ob, int flag
return false;
}
+ /* This checks actual `ob->data`, for cases when other scenes have it in edit-mode context.
+ * Currently multiple objects sharing a mesh being in edit-mode at once isn't supported,
+ * see: T86767. */
+ if (BKE_object_is_in_editmode(ob)) {
+ return true;
+ }
+
if (BKE_object_obdata_is_libdata(ob)) {
/* Ideally the caller should check this. */
CLOG_WARN(&LOG, "Unable to enter edit-mode on library data for object '%s'", ob->id.name + 2);
return false;
}
- if ((ob->mode & OB_MODE_EDIT) == 0) {
- ob->restore_mode = ob->mode;
+ ob->restore_mode = ob->mode;
- ob->mode = OB_MODE_EDIT;
- }
-
- /* This checks actual `object->data`,
- * for cases when other scenes have it in edit-mode context.
- *
- * It's important to run this after setting the object's mode (above), since in rare cases
- * the object may have the edit-data but not it's object-mode set. See T85974. */
- if (BKE_object_is_in_editmode(ob)) {
- return true;
- }
+ ob->mode = OB_MODE_EDIT;
if (ob->type == OB_MESH) {
ok = true;
@@ -1544,30 +1590,10 @@ static const EnumPropertyItem *object_mode_set_itemsf(bContext *C,
return rna_enum_object_mode_items;
}
- Object *ob = CTX_data_active_object(C);
+ const Object *ob = CTX_data_active_object(C);
if (ob) {
- const bool use_mode_particle_edit = (BLI_listbase_is_empty(&ob->particlesystem) == false) ||
- (ob->soft != NULL) ||
- (BKE_modifiers_findby_type(ob, eModifierType_Cloth) !=
- NULL);
while (input->identifier) {
- if ((input->value == OB_MODE_EDIT && OB_TYPE_SUPPORT_EDITMODE(ob->type)) ||
- (input->value == OB_MODE_POSE && (ob->type == OB_ARMATURE)) ||
- (input->value == OB_MODE_PARTICLE_EDIT && use_mode_particle_edit) ||
- (ELEM(input->value,
- OB_MODE_SCULPT,
- OB_MODE_VERTEX_PAINT,
- OB_MODE_WEIGHT_PAINT,
- OB_MODE_TEXTURE_PAINT) &&
- (ob->type == OB_MESH)) ||
- (ELEM(input->value,
- OB_MODE_EDIT_GPENCIL,
- OB_MODE_PAINT_GPENCIL,
- OB_MODE_SCULPT_GPENCIL,
- OB_MODE_WEIGHT_GPENCIL,
- OB_MODE_VERTEX_GPENCIL) &&
- (ob->type == OB_GPENCIL)) ||
- (input->value == OB_MODE_OBJECT)) {
+ if (ED_object_mode_compat_test(ob, input->value)) {
RNA_enum_item_add(&item, &totitem, input);
}
input++;
diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h
index 00426e96a81..5bf04e195fe 100644
--- a/source/blender/editors/object/object_intern.h
+++ b/source/blender/editors/object/object_intern.h
@@ -90,7 +90,7 @@ void OBJECT_OT_forcefield_toggle(struct wmOperatorType *ot);
void OBJECT_OT_move_to_collection(struct wmOperatorType *ot);
void OBJECT_OT_link_to_collection(struct wmOperatorType *ot);
-void OBJECT_OT_switch_object(struct wmOperatorType *ot);
+void OBJECT_OT_transfer_mode(struct wmOperatorType *ot);
/* object_select.c */
void OBJECT_OT_select_all(struct wmOperatorType *ot);
@@ -157,10 +157,6 @@ bool edit_modifier_poll_generic(struct bContext *C,
const bool is_liboverride_allowed);
void edit_modifier_properties(struct wmOperatorType *ot);
bool edit_modifier_invoke_properties(struct bContext *C, struct wmOperator *op);
-bool edit_modifier_invoke_properties_with_hover_no_active(struct bContext *C,
- struct wmOperator *op,
- const struct wmEvent *event,
- int *r_retval);
struct ModifierData *edit_modifier_property_get(struct wmOperator *op,
struct Object *ob,
diff --git a/source/blender/editors/object/object_modes.c b/source/blender/editors/object/object_modes.c
index 99103cf10c7..ed06cd2a217 100644
--- a/source/blender/editors/object/object_modes.c
+++ b/source/blender/editors/object/object_modes.c
@@ -30,6 +30,10 @@
#include "BLI_math.h"
#include "BLI_utildefines.h"
+#include "PIL_time.h"
+
+#include "BLT_translation.h"
+
#include "BKE_context.h"
#include "BKE_gpencil_modifier.h"
#include "BKE_layer.h"
@@ -39,11 +43,13 @@
#include "BKE_paint.h"
#include "BKE_report.h"
#include "BKE_scene.h"
+#include "BKE_screen.h"
#include "WM_api.h"
#include "WM_types.h"
#include "RNA_access.h"
+#include "RNA_define.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
@@ -111,43 +117,46 @@ static const char *object_mode_op_string(eObjectMode mode)
*/
bool ED_object_mode_compat_test(const Object *ob, eObjectMode mode)
{
- if (ob) {
- if (mode == OB_MODE_OBJECT) {
- return true;
- }
+ if (mode == OB_MODE_OBJECT) {
+ return true;
+ }
- switch (ob->type) {
- case OB_MESH:
- if (mode & (OB_MODE_EDIT | OB_MODE_SCULPT | OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT |
- OB_MODE_TEXTURE_PAINT | OB_MODE_PARTICLE_EDIT)) {
- return true;
- }
- break;
- case OB_CURVE:
- case OB_SURF:
- case OB_FONT:
- case OB_MBALL:
- if (mode & OB_MODE_EDIT) {
- return true;
- }
- break;
- case OB_LATTICE:
- if (mode & (OB_MODE_EDIT | OB_MODE_WEIGHT_PAINT)) {
- return true;
- }
- break;
- case OB_ARMATURE:
- if (mode & (OB_MODE_EDIT | OB_MODE_POSE)) {
- return true;
- }
- break;
- case OB_GPENCIL:
- if (mode & (OB_MODE_EDIT | OB_MODE_EDIT_GPENCIL | OB_MODE_PAINT_GPENCIL |
- OB_MODE_SCULPT_GPENCIL | OB_MODE_WEIGHT_GPENCIL | OB_MODE_VERTEX_GPENCIL)) {
+ switch (ob->type) {
+ case OB_MESH:
+ if (mode & (OB_MODE_EDIT | OB_MODE_SCULPT | OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT |
+ OB_MODE_TEXTURE_PAINT)) {
+ return true;
+ }
+ if (mode & OB_MODE_PARTICLE_EDIT) {
+ if (ED_object_particle_edit_mode_supported(ob)) {
return true;
}
- break;
- }
+ }
+ break;
+ case OB_CURVE:
+ case OB_SURF:
+ case OB_FONT:
+ case OB_MBALL:
+ if (mode & OB_MODE_EDIT) {
+ return true;
+ }
+ break;
+ case OB_LATTICE:
+ if (mode & (OB_MODE_EDIT | OB_MODE_WEIGHT_PAINT)) {
+ return true;
+ }
+ break;
+ case OB_ARMATURE:
+ if (mode & (OB_MODE_EDIT | OB_MODE_POSE)) {
+ return true;
+ }
+ break;
+ case OB_GPENCIL:
+ if (mode & (OB_MODE_EDIT_GPENCIL | OB_MODE_PAINT_GPENCIL | OB_MODE_SCULPT_GPENCIL |
+ OB_MODE_WEIGHT_GPENCIL | OB_MODE_VERTEX_GPENCIL)) {
+ return true;
+ }
+ break;
}
return false;
@@ -400,51 +409,66 @@ bool ED_object_mode_generic_has_data(struct Depsgraph *depsgraph, struct Object
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Switch Object
+/** \name Transfer Mode
*
* Enters the same mode of the current active object in another object,
* leaving the mode of the current object.
* \{ */
-static bool object_switch_object_poll(bContext *C)
+static bool object_transfer_mode_poll(bContext *C)
{
-
- if (!U.experimental.use_switch_object_operator) {
- return false;
- }
-
if (!CTX_wm_region_view3d(C)) {
return false;
}
const Object *ob = CTX_data_active_object(C);
- return ob && (ob->mode & (OB_MODE_EDIT | OB_MODE_SCULPT));
+ return ob && (ob->mode != OB_MODE_OBJECT);
}
-static int object_switch_object_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+/* Update the viewport rotation origin to the mouse cursor. */
+static void object_transfer_mode_reposition_view_pivot(bContext *C, const int mval[2])
{
ARegion *region = CTX_wm_region(C);
Scene *scene = CTX_data_scene(C);
- ViewLayer *view_layer = CTX_data_view_layer(C);
- Base *base_dst = ED_view3d_give_base_under_cursor(C, event->mval);
+ float global_loc[3];
+ if (!ED_view3d_autodist_simple(region, mval, global_loc, 0, NULL)) {
+ return;
+ }
+ UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings;
+ copy_v3_v3(ups->average_stroke_accum, global_loc);
+ ups->average_stroke_counter = 1;
+ ups->last_stroke_valid = true;
+}
+
+static void object_overlay_mode_transfer_animation_start(bContext *C, Object *ob_dst)
+{
+ Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
+ Object *ob_dst_eval = DEG_get_evaluated_object(depsgraph, ob_dst);
+ ob_dst_eval->runtime.overlay_mode_transfer_start_time = PIL_check_seconds_timer();
+}
+
+static bool object_transfer_mode_to_base(bContext *C, wmOperator *op, Base *base_dst)
+{
+ Scene *scene = CTX_data_scene(C);
+ ViewLayer *view_layer = CTX_data_view_layer(C);
if (base_dst == NULL) {
- return OPERATOR_CANCELLED;
+ return false;
}
Object *ob_dst = base_dst->object;
Object *ob_src = CTX_data_active_object(C);
if (ob_dst == ob_src) {
- return OPERATOR_CANCELLED;
+ return false;
}
const eObjectMode last_mode = (eObjectMode)ob_src->mode;
if (!ED_object_mode_compat_test(ob_dst, last_mode)) {
- return OPERATOR_CANCELLED;
+ return false;
}
- int retval = OPERATOR_CANCELLED;
+ bool mode_transfered = false;
ED_undo_group_begin(C);
@@ -460,42 +484,108 @@ static int object_switch_object_invoke(bContext *C, wmOperator *op, const wmEven
ob_dst_orig = DEG_get_original_object(ob_dst);
ED_object_mode_set_ex(C, last_mode, true, op->reports);
- /* Update the viewport rotation origin to the mouse cursor. */
- if (last_mode & OB_MODE_ALL_PAINT) {
- float global_loc[3];
- if (ED_view3d_autodist_simple(region, event->mval, global_loc, 0, NULL)) {
- UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings;
- copy_v3_v3(ups->average_stroke_accum, global_loc);
- ups->average_stroke_counter = 1;
- ups->last_stroke_valid = true;
- }
+ if (RNA_boolean_get(op->ptr, "use_flash_on_transfer")) {
+ object_overlay_mode_transfer_animation_start(C, ob_dst);
}
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
WM_toolsystem_update_from_context_view3d(C);
- retval = OPERATOR_FINISHED;
+ mode_transfered = true;
}
ED_undo_group_end(C);
+ return mode_transfered;
+}
+
+static int object_transfer_mode_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ switch (event->type) {
+ case LEFTMOUSE:
+ if (event->val == KM_PRESS) {
+ WM_cursor_modal_restore(CTX_wm_window(C));
+ ED_workspace_status_text(C, NULL);
+
+ /* This ensures that the click was done in an viewport region. */
+ bScreen *screen = CTX_wm_screen(C);
+ ARegion *region = BKE_screen_find_main_region_at_xy(
+ screen, SPACE_VIEW3D, event->x, event->y);
+ if (!region) {
+ return OPERATOR_CANCELLED;
+ }
+
+ const int mval[2] = {event->x - region->winrct.xmin, event->y - region->winrct.ymin};
+ Base *base_dst = ED_view3d_give_base_under_cursor(C, mval);
+ const bool mode_transfered = object_transfer_mode_to_base(C, op, base_dst);
+ if (!mode_transfered) {
+ return OPERATOR_CANCELLED;
+ }
- return retval;
+ return OPERATOR_FINISHED;
+ }
+ break;
+ case RIGHTMOUSE: {
+ WM_cursor_modal_restore(CTX_wm_window(C));
+ ED_workspace_status_text(C, NULL);
+ return OPERATOR_CANCELLED;
+ }
+ }
+ return OPERATOR_RUNNING_MODAL;
}
-void OBJECT_OT_switch_object(wmOperatorType *ot)
+static int object_transfer_mode_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ const bool use_eyedropper = RNA_boolean_get(op->ptr, "use_eyedropper");
+ if (use_eyedropper) {
+ ED_workspace_status_text(C, TIP_("Click in the viewport to select an object"));
+ WM_cursor_modal_set(CTX_wm_window(C), WM_CURSOR_EYEDROPPER);
+ WM_event_add_modal_handler(C, op);
+ return OPERATOR_RUNNING_MODAL;
+ }
+
+ Object *ob_src = CTX_data_active_object(C);
+ const eObjectMode src_mode = (eObjectMode)ob_src->mode;
+
+ Base *base_dst = ED_view3d_give_base_under_cursor(C, event->mval);
+ const bool mode_transfered = object_transfer_mode_to_base(C, op, base_dst);
+ if (!mode_transfered) {
+ return OPERATOR_CANCELLED;
+ }
+
+ if (src_mode & OB_MODE_ALL_PAINT) {
+ object_transfer_mode_reposition_view_pivot(C, event->mval);
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void OBJECT_OT_transfer_mode(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Switch Object";
- ot->idname = "OBJECT_OT_switch_object";
+ ot->name = "Transfer Mode";
+ ot->idname = "OBJECT_OT_transfer_mode";
ot->description =
"Switches the active object and assigns the same mode to a new one under the mouse cursor, "
"leaving the active mode in the current one";
/* api callbacks */
- ot->invoke = object_switch_object_invoke;
- ot->poll = object_switch_object_poll;
+ ot->invoke = object_transfer_mode_invoke;
+ ot->modal = object_transfer_mode_modal;
+ ot->poll = object_transfer_mode_poll;
/* Undo push is handled by the operator. */
ot->flag = OPTYPE_REGISTER;
+
+ RNA_def_boolean(ot->srna,
+ "use_eyedropper",
+ false,
+ "Use Eyedropper",
+ "Pick the object to switch to using an eyedropper");
+
+ RNA_def_boolean(ot->srna,
+ "use_flash_on_transfer",
+ true,
+ "Flash On Transfer",
+ "Flash the target object when transfering the mode");
}
/** \} */
diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c
index 7673649c261..e14e5cbd44b 100644
--- a/source/blender/editors/object/object_modifier.c
+++ b/source/blender/editors/object/object_modifier.c
@@ -63,6 +63,7 @@
#include "BKE_lattice.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
+#include "BKE_material.h"
#include "BKE_mesh.h"
#include "BKE_mesh_mapping.h"
#include "BKE_mesh_runtime.h"
@@ -210,7 +211,7 @@ ModifierData *ED_object_modifier_add(
/* special cases */
if (type == eModifierType_Softbody) {
if (!ob->soft) {
- ob->soft = sbNew(scene);
+ ob->soft = sbNew();
ob->softflag |= OB_SB_GOAL | OB_SB_EDGES;
}
}
@@ -772,6 +773,8 @@ static bool modifier_apply_obdata(
return false;
}
+ Main *bmain = DEG_get_bmain(depsgraph);
+ BKE_object_material_from_eval_data(bmain, ob, &mesh_applied->id);
BKE_mesh_nomain_to_mesh(mesh_applied, me, ob, &CD_MASK_MESH, true);
if (md_eval->type == eModifierType_Multires) {
@@ -1115,6 +1118,47 @@ bool edit_modifier_invoke_properties(bContext *C, wmOperator *op)
return false;
}
+/**
+ * If the "modifier" property is not set, fill the modifier property with the name of the modifier
+ * with a UI panel below the mouse cursor, unless a specific modifier is set with a context
+ * pointer. Used in order to apply modifier operators on hover over their panels.
+ */
+static bool edit_modifier_invoke_properties_with_hover(bContext *C,
+ wmOperator *op,
+ const wmEvent *event,
+ int *r_retval)
+{
+ if (RNA_struct_property_is_set(op->ptr, "modifier")) {
+ return true;
+ }
+
+ /* Note that the context pointer is *not* the active modifier, it is set in UI layouts. */
+ PointerRNA ctx_ptr = CTX_data_pointer_get_type(C, "modifier", &RNA_Modifier);
+ if (ctx_ptr.data != NULL) {
+ ModifierData *md = ctx_ptr.data;
+ RNA_string_set(op->ptr, "modifier", md->name);
+ return true;
+ }
+
+ PointerRNA *panel_ptr = UI_region_panel_custom_data_under_cursor(C, event);
+ if (panel_ptr == NULL || RNA_pointer_is_null(panel_ptr)) {
+ *r_retval = OPERATOR_CANCELLED;
+ return false;
+ }
+
+ if (!RNA_struct_is_a(panel_ptr->type, &RNA_Modifier)) {
+ /* Work around multiple operators using the same shortcut. The operators for the other
+ * stacks in the property editor use the same key, and will not run after these return
+ * OPERATOR_CANCELLED. */
+ *r_retval = (OPERATOR_PASS_THROUGH | OPERATOR_CANCELLED);
+ return false;
+ }
+
+ const ModifierData *md = panel_ptr->data;
+ RNA_string_set(op->ptr, "modifier", md->name);
+ return true;
+}
+
ModifierData *edit_modifier_property_get(wmOperator *op, Object *ob, int type)
{
char modifier_name[MAX_NAME];
@@ -1174,14 +1218,13 @@ static int modifier_remove_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
-static int modifier_remove_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+static int modifier_remove_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
- if (edit_modifier_invoke_properties(C, op)) {
+ int retval;
+ if (edit_modifier_invoke_properties_with_hover(C, op, event, &retval)) {
return modifier_remove_exec(C, op);
}
-
- /* Work around multiple operators using the same shortcut. */
- return (OPERATOR_PASS_THROUGH | OPERATOR_CANCELLED);
+ return retval;
}
void OBJECT_OT_modifier_remove(wmOperatorType *ot)
@@ -1221,13 +1264,13 @@ static int modifier_move_up_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
-static int modifier_move_up_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+static int modifier_move_up_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
- if (edit_modifier_invoke_properties(C, op)) {
+ int retval;
+ if (edit_modifier_invoke_properties_with_hover(C, op, event, &retval)) {
return modifier_move_up_exec(C, op);
}
- /* Work around multiple operators using the same shortcut. */
- return (OPERATOR_PASS_THROUGH | OPERATOR_CANCELLED);
+ return retval;
}
void OBJECT_OT_modifier_move_up(wmOperatorType *ot)
@@ -1266,13 +1309,13 @@ static int modifier_move_down_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
-static int modifier_move_down_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+static int modifier_move_down_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
- if (edit_modifier_invoke_properties(C, op)) {
+ int retval;
+ if (edit_modifier_invoke_properties_with_hover(C, op, event, &retval)) {
return modifier_move_down_exec(C, op);
}
- /* Work around multiple operators using the same shortcut. */
- return (OPERATOR_PASS_THROUGH | OPERATOR_CANCELLED);
+ return retval;
}
void OBJECT_OT_modifier_move_down(wmOperatorType *ot)
@@ -1309,12 +1352,13 @@ static int modifier_move_to_index_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
-static int modifier_move_to_index_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+static int modifier_move_to_index_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
- if (edit_modifier_invoke_properties(C, op)) {
+ int retval;
+ if (edit_modifier_invoke_properties_with_hover(C, op, event, &retval)) {
return modifier_move_to_index_exec(C, op);
}
- return OPERATOR_CANCELLED;
+ return retval;
}
void OBJECT_OT_modifier_move_to_index(wmOperatorType *ot)
@@ -1421,13 +1465,13 @@ static int modifier_apply_exec(bContext *C, wmOperator *op)
return modifier_apply_exec_ex(C, op, MODIFIER_APPLY_DATA, false);
}
-static int modifier_apply_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+static int modifier_apply_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
- if (edit_modifier_invoke_properties(C, op)) {
+ int retval;
+ if (edit_modifier_invoke_properties_with_hover(C, op, event, &retval)) {
return modifier_apply_exec(C, op);
}
- /* Work around multiple operators using the same shortcut. */
- return (OPERATOR_PASS_THROUGH | OPERATOR_CANCELLED);
+ return retval;
}
void OBJECT_OT_modifier_apply(wmOperatorType *ot)
@@ -1465,15 +1509,13 @@ static int modifier_apply_as_shapekey_exec(bContext *C, wmOperator *op)
return modifier_apply_exec_ex(C, op, MODIFIER_APPLY_SHAPE, keep);
}
-static int modifier_apply_as_shapekey_invoke(bContext *C,
- wmOperator *op,
- const wmEvent *UNUSED(event))
+static int modifier_apply_as_shapekey_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
- if (edit_modifier_invoke_properties(C, op)) {
+ int retval;
+ if (edit_modifier_invoke_properties_with_hover(C, op, event, &retval)) {
return modifier_apply_as_shapekey_exec(C, op);
}
- /* Work around multiple operators using the same shortcut. */
- return (OPERATOR_PASS_THROUGH | OPERATOR_CANCELLED);
+ return retval;
}
static char *modifier_apply_as_shapekey_get_description(struct bContext *UNUSED(C),
@@ -1579,13 +1621,13 @@ static int modifier_copy_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
-static int modifier_copy_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+static int modifier_copy_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
- if (edit_modifier_invoke_properties(C, op)) {
+ int retval;
+ if (edit_modifier_invoke_properties_with_hover(C, op, event, &retval)) {
return modifier_copy_exec(C, op);
}
- /* Work around multiple operators using the same shortcut. */
- return (OPERATOR_PASS_THROUGH | OPERATOR_CANCELLED);
+ return retval;
}
void OBJECT_OT_modifier_copy(wmOperatorType *ot)
@@ -1622,54 +1664,12 @@ static int modifier_set_active_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
-/**
- * Get the modifier below the mouse cursor modifier without checking the context pointer.
- * Used in order to set the active modifier on mouse click. If this checked the context
- * pointer then it would always set the active modifier to the already active modifier.
- *
- * \param event: If this isn't NULL, the operator will also look for panels underneath
- * the cursor with custom-data set to a modifier.
- * \param r_retval: This should be used if #event is used in order to return
- * #OPERATOR_PASS_THROUGH to check other operators with the same key set.
- */
-bool edit_modifier_invoke_properties_with_hover_no_active(bContext *C,
- wmOperator *op,
- const wmEvent *event,
- int *r_retval)
-{
- if (RNA_struct_property_is_set(op->ptr, "modifier")) {
- return true;
- }
-
- PointerRNA *panel_ptr = UI_region_panel_custom_data_under_cursor(C, event);
-
- if (!(panel_ptr == NULL || RNA_pointer_is_null(panel_ptr))) {
- if (RNA_struct_is_a(panel_ptr->type, &RNA_Modifier)) {
- ModifierData *md = panel_ptr->data;
- RNA_string_set(op->ptr, "modifier", md->name);
- return true;
- }
- BLI_assert(r_retval != NULL); /* We need the return value in this case. */
- if (r_retval != NULL) {
- *r_retval = (OPERATOR_PASS_THROUGH | OPERATOR_CANCELLED);
- }
- return false;
- }
-
- if (r_retval != NULL) {
- *r_retval = OPERATOR_CANCELLED;
- }
-
- return false;
-}
-
static int modifier_set_active_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
int retval;
- if (edit_modifier_invoke_properties_with_hover_no_active(C, op, event, &retval)) {
+ if (edit_modifier_invoke_properties_with_hover(C, op, event, &retval)) {
return modifier_set_active_exec(C, op);
}
-
return retval;
}
@@ -1756,15 +1756,13 @@ static int modifier_copy_to_selected_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
-static int modifier_copy_to_selected_invoke(bContext *C,
- wmOperator *op,
- const wmEvent *UNUSED(event))
+static int modifier_copy_to_selected_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
- if (edit_modifier_invoke_properties(C, op)) {
+ int retval;
+ if (edit_modifier_invoke_properties_with_hover(C, op, event, &retval)) {
return modifier_copy_to_selected_exec(C, op);
}
- /* Work around multiple operators using the same shortcut. */
- return (OPERATOR_PASS_THROUGH | OPERATOR_CANCELLED);
+ return retval;
}
static bool modifier_copy_to_selected_poll(bContext *C)
diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c
index 71bea6bf9e3..b50fc3c88fd 100644
--- a/source/blender/editors/object/object_ops.c
+++ b/source/blender/editors/object/object_ops.c
@@ -65,7 +65,7 @@ void ED_operatortypes_object(void)
WM_operatortype_append(OBJECT_OT_paths_range_update);
WM_operatortype_append(OBJECT_OT_forcefield_toggle);
- WM_operatortype_append(OBJECT_OT_switch_object);
+ WM_operatortype_append(OBJECT_OT_transfer_mode);
WM_operatortype_append(OBJECT_OT_parent_set);
WM_operatortype_append(OBJECT_OT_parent_no_inverse_set);
diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c
index 0aa739c2fc8..f3433833b5f 100644
--- a/source/blender/editors/object/object_relations.c
+++ b/source/blender/editors/object/object_relations.c
@@ -801,7 +801,7 @@ bool ED_object_parent_set(ReportList *reports,
* so we check this by assuming that the parent is selected too.
*/
/* XXX currently this should only happen for meshes, curves, surfaces,
- * and lattices - this stuff isn't available for metas yet */
+ * and lattices - this stuff isn't available for meta-balls yet. */
if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_LATTICE)) {
ModifierData *md;
@@ -812,7 +812,8 @@ bool ED_object_parent_set(ReportList *reports,
if (md) {
((CurveModifierData *)md)->object = par;
}
- if (par->runtime.curve_cache && par->runtime.curve_cache->path == NULL) {
+ if (par->runtime.curve_cache &&
+ par->runtime.curve_cache->anim_path_accum_length == NULL) {
DEG_id_tag_update(&par->id, ID_RECALC_GEOMETRY);
}
}
@@ -1943,7 +1944,7 @@ void ED_object_single_user(Main *bmain, Scene *scene, Object *ob)
ob->flag |= OB_DONE;
single_object_users(bmain, scene, NULL, OB_DONE, false);
- BKE_main_id_clear_newpoins(bmain);
+ BKE_main_id_newptr_and_tag_clear(bmain);
}
static void single_obdata_users(
@@ -2453,7 +2454,7 @@ static int make_override_library_exec(bContext *C, wmOperator *op)
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
const bool success = BKE_lib_override_library_create(
- bmain, scene, view_layer, id_root, &obact->id);
+ bmain, scene, view_layer, id_root, &obact->id, NULL);
/* Remove the instance empty from this scene, the items now have an overridden collection
* instead. */
@@ -2569,14 +2570,14 @@ static int convert_proxy_to_override_exec(bContext *C, wmOperator *op)
/* Remove the instance empty from this scene, the items now have an overridden collection
* instead. */
- if (success && is_override_instancing_object) {
+ if (is_override_instancing_object) {
ED_object_base_free_and_unlink(bmain, scene, ob_proxy_group);
}
DEG_id_tag_update(&CTX_data_scene(C)->id, ID_RECALC_BASE_FLAGS | ID_RECALC_COPY_ON_WRITE);
WM_event_add_notifier(C, NC_WINDOW, NULL);
- return success ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
+ return OPERATOR_FINISHED;
}
void OBJECT_OT_convert_proxy_to_override(wmOperatorType *ot)
@@ -2643,7 +2644,7 @@ static int make_single_user_exec(bContext *C, wmOperator *op)
single_object_action_users(bmain, scene, view_layer, v3d, flag);
}
- BKE_main_id_clear_newpoins(bmain);
+ BKE_main_id_newptr_and_tag_clear(bmain);
WM_event_add_notifier(C, NC_WINDOW, NULL);
diff --git a/source/blender/editors/object/object_remesh.c b/source/blender/editors/object/object_remesh.c
index 9ef2cce875f..1ff576504ce 100644
--- a/source/blender/editors/object/object_remesh.c
+++ b/source/blender/editors/object/object_remesh.c
@@ -525,7 +525,7 @@ static int voxel_size_edit_invoke(bContext *C, wmOperator *op, const wmEvent *ev
float d_a[3], d_b[3];
float d_a_proj[2], d_b_proj[2];
- float preview_plane_proj[4][3];
+ float preview_plane_proj[4][2];
const float y_axis_proj[2] = {0.0f, 1.0f};
mid_v3_v3v3(text_pos, cd->preview_plane[0], cd->preview_plane[2]);
@@ -534,7 +534,7 @@ static int voxel_size_edit_invoke(bContext *C, wmOperator *op, const wmEvent *ev
for (int i = 0; i < 4; i++) {
float preview_plane_world_space[3];
mul_v3_m4v3(preview_plane_world_space, active_object->obmat, cd->preview_plane[i]);
- ED_view3d_project(region, preview_plane_world_space, preview_plane_proj[i]);
+ ED_view3d_project_v2(region, preview_plane_world_space, preview_plane_proj[i]);
}
/* Get the initial X and Y axis of the basis from the edges of the Bounding Box face. */
diff --git a/source/blender/editors/object/object_transform.c b/source/blender/editors/object/object_transform.c
index 13c0740bce6..b9a3bc87e19 100644
--- a/source/blender/editors/object/object_transform.c
+++ b/source/blender/editors/object/object_transform.c
@@ -1654,7 +1654,7 @@ static void object_transform_axis_target_calc_depth_init(struct XFormAxisData *x
if (center_tot) {
mul_v3_fl(center, 1.0f / center_tot);
float center_proj[3];
- ED_view3d_project(xfd->vc.region, center, center_proj);
+ ED_view3d_project_v3(xfd->vc.region, center, center_proj);
xfd->prev.depth = center_proj[2];
xfd->prev.is_depth_valid = true;
}
@@ -1782,12 +1782,7 @@ static int object_transform_axis_target_invoke(bContext *C, wmOperator *op, cons
vc.v3d->flag2 |= V3D_HIDE_OVERLAYS;
#endif
- ED_view3d_autodist_init(vc.depsgraph, vc.region, vc.v3d, 0);
-
- if (vc.rv3d->depths != NULL) {
- vc.rv3d->depths->damaged = true;
- }
- ED_view3d_depth_update(vc.region);
+ ED_view3d_depth_override(vc.depsgraph, vc.region, vc.v3d, NULL, V3D_DEPTH_NO_GPENCIL, true);
#ifdef USE_RENDER_OVERRIDE
vc.v3d->flag2 = flag2_prev;
@@ -1870,30 +1865,32 @@ static int object_transform_axis_target_modal(bContext *C, wmOperator *op, const
if (event->type == MOUSEMOVE || is_translate_init) {
const ViewDepths *depths = xfd->vc.rv3d->depths;
if (depths && ((uint)event->mval[0] < depths->w) && ((uint)event->mval[1] < depths->h)) {
- double depth = (double)ED_view3d_depth_read_cached(&xfd->vc, event->mval);
+ float depth_fl = 1.0f;
+ ED_view3d_depth_read_cached(depths, event->mval, 0, &depth_fl);
float location_world[3];
- if (depth == 1.0f) {
+ if (depth_fl == 1.0f) {
if (xfd->prev.is_depth_valid) {
- depth = (double)xfd->prev.depth;
+ depth_fl = xfd->prev.depth;
}
}
#ifdef USE_FAKE_DEPTH_INIT
/* First time only. */
- if (depth == 1.0f) {
+ if (depth_fl == 1.0f) {
if (xfd->prev.is_depth_valid == false) {
object_transform_axis_target_calc_depth_init(xfd, event->mval);
if (xfd->prev.is_depth_valid) {
- depth = (double)xfd->prev.depth;
+ depth_fl = xfd->prev.depth;
}
}
}
#endif
+ double depth = (double)depth_fl;
if ((depth > depths->depth_range[0]) && (depth < depths->depth_range[1])) {
- xfd->prev.depth = depth;
+ xfd->prev.depth = depth_fl;
xfd->prev.is_depth_valid = true;
- if (ED_view3d_depth_unproject(region, event->mval, depth, location_world)) {
+ if (ED_view3d_depth_unproject_v3(region, event->mval, depth, location_world)) {
if (is_translate) {
float normal[3];
diff --git a/source/blender/editors/object/object_vgroup.c b/source/blender/editors/object/object_vgroup.c
index 2296e158cc8..3f40d637188 100644
--- a/source/blender/editors/object/object_vgroup.c
+++ b/source/blender/editors/object/object_vgroup.c
@@ -1751,7 +1751,7 @@ static bool *vgroup_selected_get(Object *ob)
/* Mirror the selection if X Mirror is enabled. */
Mesh *me = BKE_mesh_from_object(ob);
- if (me && (me->editflag & ME_EDIT_VERTEX_GROUPS_X_SYMMETRY) != 0) {
+ if (me && ME_USING_MIRROR_X_VERTEX_GROUPS(me)) {
BKE_object_defgroup_mirror_selection(ob, defbase_tot, mask, mask, &sel_count);
}
}
@@ -3985,6 +3985,10 @@ static const EnumPropertyItem *vgroup_itemf(bContext *C,
PropertyRNA *UNUSED(prop),
bool *r_free)
{
+ if (C == NULL) {
+ return DummyRNA_NULL_items;
+ }
+
Object *ob = ED_object_context(C);
EnumPropertyItem tmp = {0, "", 0, "", ""};
EnumPropertyItem *item = NULL;
diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c
index 0ec8238f741..97994b65f40 100644
--- a/source/blender/editors/physics/particle_edit.c
+++ b/source/blender/editors/physics/particle_edit.c
@@ -524,15 +524,13 @@ static void PE_set_view3d_data(bContext *C, PEData *data)
ED_view3d_viewcontext_init(C, &data->vc, data->depsgraph);
if (!XRAY_ENABLED(data->vc.v3d)) {
- if (data->vc.v3d->flag & V3D_INVALID_BACKBUF) {
- /* needed or else the draw matrix can be incorrect */
- view3d_operator_needs_opengl(C);
-
- ED_view3d_backbuf_depth_validate(&data->vc);
- /* we may need to force an update here by setting the rv3d as dirty
- * for now it seems ok, but take care!:
- * rv3d->depths->dirty = 1; */
- ED_view3d_depth_update(data->vc.region);
+ if (!(data->vc.v3d->runtime.flag & V3D_RUNTIME_DEPTHBUF_OVERRIDDEN)) {
+ ED_view3d_depth_override(data->depsgraph,
+ data->vc.region,
+ data->vc.v3d,
+ data->vc.obact,
+ V3D_DEPTH_OBJECT_ONLY,
+ true);
}
}
}
@@ -611,7 +609,7 @@ static bool key_test_depth(const PEData *data, const float co[3], const int scre
}
float win[3];
- ED_view3d_project(data->vc.region, co, win);
+ ED_view3d_project_v3(data->vc.region, co, win);
if (win[2] - 0.00001f > depth) {
return 0;
@@ -5377,8 +5375,7 @@ static bool particle_edit_toggle_poll(bContext *C)
return 0;
}
- return (ob->particlesystem.first || BKE_modifiers_findby_type(ob, eModifierType_Cloth) ||
- BKE_modifiers_findby_type(ob, eModifierType_Softbody));
+ return ED_object_particle_edit_mode_supported(ob);
}
static void free_all_psys_edit(Object *object)
@@ -5393,6 +5390,12 @@ static void free_all_psys_edit(Object *object)
}
}
+bool ED_object_particle_edit_mode_supported(const Object *ob)
+{
+ return (ob->particlesystem.first || BKE_modifiers_findby_type(ob, eModifierType_Cloth) ||
+ BKE_modifiers_findby_type(ob, eModifierType_Softbody));
+}
+
void ED_object_particle_edit_mode_enter_ex(Depsgraph *depsgraph, Scene *scene, Object *ob)
{
/* Needed so #ParticleSystemModifierData.mesh_final is set. */
diff --git a/source/blender/editors/physics/rigidbody_object.c b/source/blender/editors/physics/rigidbody_object.c
index 81a8b57776b..fca3b5817b0 100644
--- a/source/blender/editors/physics/rigidbody_object.c
+++ b/source/blender/editors/physics/rigidbody_object.c
@@ -520,6 +520,26 @@ static int rigidbody_objects_calc_mass_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
+static bool mass_calculate_poll_property(const bContext *UNUSED(C),
+ wmOperator *op,
+ const PropertyRNA *prop)
+{
+ const char *prop_id = RNA_property_identifier(prop);
+
+ /* Disable density input when not using the 'Custom' preset. */
+ if (STREQ(prop_id, "density")) {
+ int material = RNA_enum_get(op->ptr, "material");
+ if (material >= 0) {
+ RNA_def_property_clear_flag((PropertyRNA *)prop, PROP_EDITABLE);
+ }
+ else {
+ RNA_def_property_flag((PropertyRNA *)prop, PROP_EDITABLE);
+ }
+ }
+
+ return true;
+}
+
void RIGIDBODY_OT_mass_calculate(wmOperatorType *ot)
{
PropertyRNA *prop;
@@ -533,6 +553,7 @@ void RIGIDBODY_OT_mass_calculate(wmOperatorType *ot)
ot->invoke = WM_menu_invoke; /* XXX */
ot->exec = rigidbody_objects_calc_mass_exec;
ot->poll = ED_operator_rigidbody_active_poll;
+ ot->poll_property = mass_calculate_poll_property;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -554,7 +575,7 @@ void RIGIDBODY_OT_mass_calculate(wmOperatorType *ot)
FLT_MIN,
FLT_MAX,
"Density",
- "Custom density value (kg/m^3) to use instead of material preset",
+ "Density value (kg/m^3), allows custom value if the 'Custom' preset is used",
1.0f,
2500.0f);
}
diff --git a/source/blender/editors/render/render_internal.c b/source/blender/editors/render/render_internal.c
index 0b5a8db0115..0bec509cd7e 100644
--- a/source/blender/editors/render/render_internal.c
+++ b/source/blender/editors/render/render_internal.c
@@ -574,9 +574,12 @@ static void image_rect_update(void *rjv, RenderResult *rr, volatile rcti *renrec
return;
}
if (rj->image_outdated) {
- /* update entire render */
+ /* Free all render buffer caches when switching slots, with lock to ensure main
+ * thread is not drawing the buffer at the same time. */
rj->image_outdated = false;
- BKE_image_signal(rj->main, ima, NULL, IMA_SIGNAL_COLORMANAGE);
+ ibuf = BKE_image_acquire_ibuf(ima, &rj->iuser, &lock);
+ BKE_image_free_buffers(ima);
+ BKE_image_release_ibuf(ima, ibuf, lock);
*(rj->do_update) = true;
return;
}
diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c
index 48f937fb4ec..cfc07de3f6c 100644
--- a/source/blender/editors/render/render_opengl.c
+++ b/source/blender/editors/render/render_opengl.c
@@ -859,7 +859,7 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op)
oglrender->task_pool = BLI_task_pool_create_background_serial(oglrender, TASK_PRIORITY_LOW);
}
else {
- oglrender->task_pool = BLI_task_pool_create(oglrender, TASK_PRIORITY_LOW);
+ oglrender->task_pool = BLI_task_pool_create(oglrender, TASK_PRIORITY_LOW, TASK_ISOLATION_ON);
}
oglrender->pool_ok = true;
BLI_spin_init(&oglrender->reports_lock);
diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c
index 1b7209b164b..0b0009c2a2f 100644
--- a/source/blender/editors/render/render_preview.c
+++ b/source/blender/editors/render/render_preview.c
@@ -70,6 +70,7 @@
#include "BKE_node.h"
#include "BKE_object.h"
#include "BKE_scene.h"
+#include "BKE_screen.h"
#include "BKE_texture.h"
#include "BKE_world.h"
@@ -610,9 +611,7 @@ static bool ed_preview_draw_rect(ScrArea *area, int split, int first, rcti *rect
float fy = rect->ymin;
/* material preview only needs monoscopy (view 0) */
- if (re) {
- RE_AcquiredResultGet32(re, &rres, (uint *)rect_byte, 0);
- }
+ RE_AcquiredResultGet32(re, &rres, (uint *)rect_byte, 0);
IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR);
immDrawPixelsTex(
@@ -692,8 +691,9 @@ struct ObjectPreviewData {
int sizey;
};
-static Object *object_preview_camera_create(
- Main *preview_main, ViewLayer *view_layer, Object *preview_object, int sizex, int sizey)
+static Object *object_preview_camera_create(Main *preview_main,
+ ViewLayer *view_layer,
+ Object *preview_object)
{
Object *camera = BKE_object_add(preview_main, view_layer, OB_CAMERA, "Preview Camera");
@@ -701,18 +701,17 @@ static Object *object_preview_camera_create(
float dummyscale[3];
mat4_to_loc_rot_size(camera->loc, rotmat, dummyscale, preview_object->obmat);
- /* Camera is Y up, so needs additional 90deg rotation around X to match object's Z up. */
+ /* Camera is Y up, so needs additional rotations to obliquely face the front. */
float drotmat[3][3];
- axis_angle_to_mat3_single(drotmat, 'X', M_PI_2);
+ const float eul[3] = {M_PI * 0.4f, 0.0f, M_PI * 0.1f};
+ eul_to_mat3(drotmat, eul);
mul_m3_m3_post(rotmat, drotmat);
camera->rotmode = ROT_MODE_QUAT;
mat3_to_quat(camera->quat, rotmat);
- /* shader_preview_render() does this too. */
- if (sizex > sizey) {
- ((Camera *)camera->data)->lens *= (float)sizey / (float)sizex;
- }
+ /* Nice focal length for close portraiture. */
+ ((Camera *)camera->data)->lens = 85;
return camera;
}
@@ -730,11 +729,8 @@ static Scene *object_preview_scene_create(const struct ObjectPreviewData *previe
BKE_collection_object_add(preview_data->pr_main, scene->master_collection, preview_data->object);
- Object *camera_object = object_preview_camera_create(preview_data->pr_main,
- view_layer,
- preview_data->object,
- preview_data->sizex,
- preview_data->sizey);
+ Object *camera_object = object_preview_camera_create(
+ preview_data->pr_main, view_layer, preview_data->object);
scene->camera = camera_object;
scene->r.xsch = preview_data->sizex;
@@ -779,16 +775,21 @@ static void object_preview_render(IconPreview *preview, IconPreviewSize *preview
U.pixelsize = 2.0f;
+ View3DShading shading;
+ BKE_screen_view3d_shading_init(&shading);
+ /* Enable shadows, makes it a bit easier to see the shape. */
+ shading.flag |= V3D_SHADING_SHADOW;
+
ImBuf *ibuf = ED_view3d_draw_offscreen_imbuf_simple(
depsgraph,
DEG_get_evaluated_scene(depsgraph),
- NULL,
- OB_SOLID,
+ &shading,
+ OB_TEXTURE,
DEG_get_evaluated_object(depsgraph, scene->camera),
preview_sized->sizex,
preview_sized->sizey,
IB_rect,
- V3D_OFSDRAW_NONE,
+ V3D_OFSDRAW_OVERRIDE_SCENE_SETTINGS,
R_ALPHAPREMUL,
NULL,
NULL,
@@ -1608,7 +1609,7 @@ static void icon_preview_free(void *customdata)
}
void ED_preview_icon_render(
- Main *bmain, Depsgraph *depsgraph, Scene *scene, ID *id, uint *rect, int sizex, int sizey)
+ const bContext *C, Scene *scene, ID *id, uint *rect, int sizex, int sizey)
{
IconPreview ip = {NULL};
short stop = false, update = false;
@@ -1616,9 +1617,9 @@ void ED_preview_icon_render(
ED_preview_ensure_dbase();
- ip.bmain = bmain;
+ ip.bmain = CTX_data_main(C);
ip.scene = scene;
- ip.depsgraph = depsgraph;
+ ip.depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
ip.owner = BKE_previewimg_id_ensure(id);
ip.id = id;
/* Control isn't given back to the caller until the preview is done. So we don't need to copy
diff --git a/source/blender/editors/render/render_update.c b/source/blender/editors/render/render_update.c
index 4ed1cbd60a5..3e8a1bda2f0 100644
--- a/source/blender/editors/render/render_update.c
+++ b/source/blender/editors/render/render_update.c
@@ -188,7 +188,7 @@ void ED_render_engine_changed(Main *bmain, const bool update_scene_data)
ED_render_engine_area_exit(bmain, area);
}
}
- RE_FreePersistentData();
+ RE_FreePersistentData(NULL);
/* Inform all render engines and draw managers. */
DEGEditorUpdateContext update_ctx = {NULL};
update_ctx.bmain = bmain;
diff --git a/source/blender/editors/render/render_view.c b/source/blender/editors/render/render_view.c
index 465438f814a..eb4a040e891 100644
--- a/source/blender/editors/render/render_view.c
+++ b/source/blender/editors/render/render_view.c
@@ -164,6 +164,7 @@ ScrArea *render_view_open(bContext *C, int mx, int my, ReportList *reports)
sizex,
sizey,
SPACE_IMAGE,
+ true,
false,
true,
WIN_ALIGN_LOCATION_CENTER) == NULL) {
diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c
index bd2b1c4c553..2814a4c9351 100644
--- a/source/blender/editors/screen/area.c
+++ b/source/blender/editors/screen/area.c
@@ -1172,12 +1172,12 @@ static void region_azones_add(const bScreen *screen, ScrArea *area, ARegion *reg
}
/* dir is direction to check, not the splitting edge direction! */
-static int rct_fits(const rcti *rect, char dir, int size)
+static int rct_fits(const rcti *rect, const eScreenAxis dir_axis, int size)
{
- if (dir == 'h') {
+ if (dir_axis == SCREEN_AXIS_H) {
return BLI_rcti_size_x(rect) + 1 - size;
}
- /* 'v' */
+ /* Vertical. */
return BLI_rcti_size_y(rect) + 1 - size;
}
@@ -1398,7 +1398,8 @@ static void region_rect_recursive(
region->flag |= RGN_FLAG_TOO_SMALL;
}
}
- else if (rct_fits(remainder, 'v', 1) < 0 || rct_fits(remainder, 'h', 1) < 0) {
+ else if (rct_fits(remainder, SCREEN_AXIS_V, 1) < 0 ||
+ rct_fits(remainder, SCREEN_AXIS_H, 1) < 0) {
/* remainder is too small for any usage */
region->flag |= RGN_FLAG_TOO_SMALL;
}
@@ -1410,11 +1411,11 @@ static void region_rect_recursive(
else if (ELEM(alignment, RGN_ALIGN_TOP, RGN_ALIGN_BOTTOM)) {
rcti *winrct = (region->overlap) ? overlap_remainder : remainder;
- if ((prefsizey == 0) || (rct_fits(winrct, 'v', prefsizey) < 0)) {
+ if ((prefsizey == 0) || (rct_fits(winrct, SCREEN_AXIS_V, prefsizey) < 0)) {
region->flag |= RGN_FLAG_TOO_SMALL;
}
else {
- int fac = rct_fits(winrct, 'v', prefsizey);
+ int fac = rct_fits(winrct, SCREEN_AXIS_V, prefsizey);
if (fac < 0) {
prefsizey += fac;
@@ -1436,11 +1437,11 @@ static void region_rect_recursive(
else if (ELEM(alignment, RGN_ALIGN_LEFT, RGN_ALIGN_RIGHT)) {
rcti *winrct = (region->overlap) ? overlap_remainder : remainder;
- if ((prefsizex == 0) || (rct_fits(winrct, 'h', prefsizex) < 0)) {
+ if ((prefsizex == 0) || (rct_fits(winrct, SCREEN_AXIS_H, prefsizex) < 0)) {
region->flag |= RGN_FLAG_TOO_SMALL;
}
else {
- int fac = rct_fits(winrct, 'h', prefsizex);
+ int fac = rct_fits(winrct, SCREEN_AXIS_H, prefsizex);
if (fac < 0) {
prefsizex += fac;
@@ -1464,7 +1465,7 @@ static void region_rect_recursive(
region->winrct = *remainder;
if (alignment == RGN_ALIGN_HSPLIT) {
- if (rct_fits(remainder, 'h', prefsizex) > 4) {
+ if (rct_fits(remainder, SCREEN_AXIS_H, prefsizex) > 4) {
region->winrct.xmax = BLI_rcti_cent_x(remainder);
remainder->xmin = region->winrct.xmax + 1;
}
@@ -1473,7 +1474,7 @@ static void region_rect_recursive(
}
}
else {
- if (rct_fits(remainder, 'v', prefsizey) > 4) {
+ if (rct_fits(remainder, SCREEN_AXIS_V, prefsizey) > 4) {
region->winrct.ymax = BLI_rcti_cent_y(remainder);
remainder->ymin = region->winrct.ymax + 1;
}
@@ -1526,8 +1527,8 @@ static void region_rect_recursive(
BLI_rcti_init(remainder, 0, 0, 0, 0);
}
- /* Fix any negative dimensions. This can happen when a quad split 3d view gets to small. (see
- * T72200). */
+ /* Fix any negative dimensions. This can happen when a quad split 3d view gets too small.
+ * (see T72200). */
BLI_rcti_sanitize(&region->winrct);
quad++;
diff --git a/source/blender/editors/screen/screen_draw.c b/source/blender/editors/screen/screen_draw.c
index 2ba7ef8f972..2c45524ef94 100644
--- a/source/blender/editors/screen/screen_draw.c
+++ b/source/blender/editors/screen/screen_draw.c
@@ -33,184 +33,11 @@
#include "WM_api.h"
+#include "UI_interface.h"
#include "UI_resources.h"
#include "screen_intern.h"
-/**
- * Draw horizontal shape visualizing future joining
- * (left as well right direction of future joining).
- */
-static void draw_horizontal_join_shape(ScrArea *area, char dir, uint pos)
-{
- const float width = screen_geom_area_width(area) - 1;
- const float height = screen_geom_area_height(area) - 1;
-
- float w, h;
- if (height < width) {
- h = height / 8;
- w = height / 4;
- }
- else {
- h = width / 8;
- w = width / 4;
- }
-
- vec2f points[10];
- points[0].x = area->v1->vec.x;
- points[0].y = area->v1->vec.y + height / 2;
-
- points[1].x = area->v1->vec.x;
- points[1].y = area->v1->vec.y;
-
- points[2].x = area->v4->vec.x - w;
- points[2].y = area->v4->vec.y;
-
- points[3].x = area->v4->vec.x - w;
- points[3].y = area->v4->vec.y + height / 2 - 2 * h;
-
- points[4].x = area->v4->vec.x - 2 * w;
- points[4].y = area->v4->vec.y + height / 2;
-
- points[5].x = area->v4->vec.x - w;
- points[5].y = area->v4->vec.y + height / 2 + 2 * h;
-
- points[6].x = area->v3->vec.x - w;
- points[6].y = area->v3->vec.y;
-
- points[7].x = area->v2->vec.x;
- points[7].y = area->v2->vec.y;
-
- points[8].x = area->v4->vec.x;
- points[8].y = area->v4->vec.y + height / 2 - h;
-
- points[9].x = area->v4->vec.x;
- points[9].y = area->v4->vec.y + height / 2 + h;
-
- if (dir == 'l') {
- /* when direction is left, then we flip direction of arrow */
- float cx = area->v1->vec.x + width;
- for (int i = 0; i < 10; i++) {
- points[i].x -= cx;
- points[i].x = -points[i].x;
- points[i].x += area->v1->vec.x;
- }
- }
-
- immBegin(GPU_PRIM_TRI_FAN, 5);
-
- for (int i = 0; i < 5; i++) {
- immVertex2f(pos, points[i].x, points[i].y);
- }
-
- immEnd();
-
- immBegin(GPU_PRIM_TRI_FAN, 5);
-
- for (int i = 4; i < 8; i++) {
- immVertex2f(pos, points[i].x, points[i].y);
- }
-
- immVertex2f(pos, points[0].x, points[0].y);
- immEnd();
-
- immRectf(pos, points[2].x, points[2].y, points[8].x, points[8].y);
- immRectf(pos, points[6].x, points[6].y, points[9].x, points[9].y);
-}
-
-/**
- * Draw vertical shape visualizing future joining (up/down direction).
- */
-static void draw_vertical_join_shape(ScrArea *area, char dir, uint pos)
-{
- const float width = screen_geom_area_width(area) - 1;
- const float height = screen_geom_area_height(area) - 1;
-
- float w, h;
- if (height < width) {
- h = height / 4;
- w = height / 8;
- }
- else {
- h = width / 4;
- w = width / 8;
- }
-
- vec2f points[10];
- points[0].x = area->v1->vec.x + width / 2;
- points[0].y = area->v3->vec.y;
-
- points[1].x = area->v2->vec.x;
- points[1].y = area->v2->vec.y;
-
- points[2].x = area->v1->vec.x;
- points[2].y = area->v1->vec.y + h;
-
- points[3].x = area->v1->vec.x + width / 2 - 2 * w;
- points[3].y = area->v1->vec.y + h;
-
- points[4].x = area->v1->vec.x + width / 2;
- points[4].y = area->v1->vec.y + 2 * h;
-
- points[5].x = area->v1->vec.x + width / 2 + 2 * w;
- points[5].y = area->v1->vec.y + h;
-
- points[6].x = area->v4->vec.x;
- points[6].y = area->v4->vec.y + h;
-
- points[7].x = area->v3->vec.x;
- points[7].y = area->v3->vec.y;
-
- points[8].x = area->v1->vec.x + width / 2 - w;
- points[8].y = area->v1->vec.y;
-
- points[9].x = area->v1->vec.x + width / 2 + w;
- points[9].y = area->v1->vec.y;
-
- if (dir == 'u') {
- /* when direction is up, then we flip direction of arrow */
- float cy = area->v1->vec.y + height;
- for (int i = 0; i < 10; i++) {
- points[i].y -= cy;
- points[i].y = -points[i].y;
- points[i].y += area->v1->vec.y;
- }
- }
-
- immBegin(GPU_PRIM_TRI_FAN, 5);
-
- for (int i = 0; i < 5; i++) {
- immVertex2f(pos, points[i].x, points[i].y);
- }
-
- immEnd();
-
- immBegin(GPU_PRIM_TRI_FAN, 5);
-
- for (int i = 4; i < 8; i++) {
- immVertex2f(pos, points[i].x, points[i].y);
- }
-
- immVertex2f(pos, points[0].x, points[0].y);
- immEnd();
-
- immRectf(pos, points[2].x, points[2].y, points[8].x, points[8].y);
- immRectf(pos, points[6].x, points[6].y, points[9].x, points[9].y);
-}
-
-/**
- * Draw join shape due to direction of joining.
- */
-static void draw_join_shape(ScrArea *area, char dir, uint pos)
-{
- if (ELEM(dir, 'u', 'd')) {
- draw_vertical_join_shape(area, dir, pos);
- }
- else {
- draw_horizontal_join_shape(area, dir, pos);
- }
-}
-
#define CORNER_RESOLUTION 3
static void do_vert_pair(GPUVertBuf *vbo, uint pos, uint *vidx, int corner, int i)
@@ -290,28 +117,6 @@ static GPUBatch *batch_screen_edges_get(int *corner_len)
#undef CORNER_RESOLUTION
-/**
- * Draw screen area darker with arrow (visualization of future joining).
- */
-static void scrarea_draw_shape_dark(ScrArea *area, char dir, uint pos)
-{
- GPU_blend(GPU_BLEND_ALPHA);
- immUniformColor4ub(0, 0, 0, 50);
-
- draw_join_shape(area, dir, pos);
-}
-
-/**
- * Draw screen area lighter with arrow shape ("eraser" of previous dark shape).
- */
-static void scrarea_draw_shape_light(ScrArea *area, char UNUSED(dir), uint pos)
-{
- GPU_blend(GPU_BLEND_ALPHA);
- immUniformColor4ub(255, 255, 255, 25);
-
- immRectf(pos, area->v1->vec.x, area->v1->vec.y, area->v3->vec.x, area->v3->vec.y);
-}
-
static void drawscredge_area_draw(
int sizex, int sizey, short x1, short y1, short x2, short y2, float edge_thickness)
{
@@ -427,53 +232,97 @@ void ED_screen_draw_edges(wmWindow *win)
}
/**
- * The blended join arrows.
+ * Visual indication of the two areas involved in a proposed join.
*
* \param sa1: Area from which the resultant originates.
* \param sa2: Target area that will be replaced.
*/
-void ED_screen_draw_join_shape(ScrArea *sa1, ScrArea *sa2)
+void screen_draw_join_highlight(ScrArea *sa1, ScrArea *sa2)
{
- uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ const eScreenDir dir = area_getorientation(sa1, sa2);
+ if (dir == SCREEN_DIR_NONE) {
+ return;
+ }
+
+ /* Rect of the combined areas.*/
+ const bool vertical = SCREEN_DIR_IS_VERTICAL(dir);
+ const rctf combined = {
+ .xmin = vertical ? MAX2(sa1->totrct.xmin, sa2->totrct.xmin) :
+ MIN2(sa1->totrct.xmin, sa2->totrct.xmin),
+ .xmax = vertical ? MIN2(sa1->totrct.xmax, sa2->totrct.xmax) :
+ MAX2(sa1->totrct.xmax, sa2->totrct.xmax),
+ .ymin = vertical ? MIN2(sa1->totrct.ymin, sa2->totrct.ymin) :
+ MAX2(sa1->totrct.ymin, sa2->totrct.ymin),
+ .ymax = vertical ? MAX2(sa1->totrct.ymax, sa2->totrct.ymax) :
+ MIN2(sa1->totrct.ymax, sa2->totrct.ymax),
+ };
+
+ uint pos_id = GPU_vertformat_attr_add(
+ immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ GPU_blend(GPU_BLEND_ALPHA);
- GPU_line_width(1);
-
- /* blended join arrow */
- int dir = area_getorientation(sa1, sa2);
- int dira = -1;
- if (dir != -1) {
- switch (dir) {
- case 0: /* W */
- dir = 'r';
- dira = 'l';
- break;
- case 1: /* N */
- dir = 'd';
- dira = 'u';
- break;
- case 2: /* E */
- dir = 'l';
- dira = 'r';
- break;
- case 3: /* S */
- dir = 'u';
- dira = 'd';
- break;
+ /* Highlight source (sa1) within combined area. */
+ immUniformColor4fv((const float[4]){1.0f, 1.0f, 1.0f, 0.10f});
+ immRectf(pos_id,
+ MAX2(sa1->totrct.xmin, combined.xmin),
+ MAX2(sa1->totrct.ymin, combined.ymin),
+ MIN2(sa1->totrct.xmax, combined.xmax),
+ MIN2(sa1->totrct.ymax, combined.ymax));
+
+ /* Highlight destination (sa2) within combined area. */
+ immUniformColor4fv((const float[4]){0.0f, 0.0f, 0.0f, 0.25f});
+ immRectf(pos_id,
+ MAX2(sa2->totrct.xmin, combined.xmin),
+ MAX2(sa2->totrct.ymin, combined.ymin),
+ MIN2(sa2->totrct.xmax, combined.xmax),
+ MIN2(sa2->totrct.ymax, combined.ymax));
+
+ int offset1;
+ int offset2;
+ area_getoffsets(sa1, sa2, dir, &offset1, &offset2);
+ if (offset1 < 0 || offset2 > 0) {
+ /* Show partial areas that will be closed. */
+ immUniformColor4fv((const float[4]){0.0f, 0.0f, 0.0f, 0.8f});
+ if (vertical) {
+ if (sa1->totrct.xmin < combined.xmin) {
+ immRectf(pos_id, sa1->totrct.xmin, sa1->totrct.ymin, combined.xmin, sa1->totrct.ymax);
+ }
+ if (sa2->totrct.xmin < combined.xmin) {
+ immRectf(pos_id, sa2->totrct.xmin, sa2->totrct.ymin, combined.xmin, sa2->totrct.ymax);
+ }
+ if (sa1->totrct.xmax > combined.xmax) {
+ immRectf(pos_id, combined.xmax, sa1->totrct.ymin, sa1->totrct.xmax, sa1->totrct.ymax);
+ }
+ if (sa2->totrct.xmax > combined.xmax) {
+ immRectf(pos_id, combined.xmax, sa2->totrct.ymin, sa2->totrct.xmax, sa2->totrct.ymax);
+ }
+ }
+ else {
+ if (sa1->totrct.ymin < combined.ymin) {
+ immRectf(pos_id, sa1->totrct.xmin, combined.ymin, sa1->totrct.xmax, sa1->totrct.ymin);
+ }
+ if (sa2->totrct.ymin < combined.ymin) {
+ immRectf(pos_id, sa2->totrct.xmin, combined.ymin, sa2->totrct.xmax, sa2->totrct.ymin);
+ }
+ if (sa1->totrct.ymax > combined.ymax) {
+ immRectf(pos_id, sa1->totrct.xmin, sa1->totrct.ymax, sa1->totrct.xmax, combined.ymax);
+ }
+ if (sa2->totrct.ymax > combined.ymax) {
+ immRectf(pos_id, sa2->totrct.xmin, sa2->totrct.ymax, sa2->totrct.xmax, combined.ymax);
+ }
}
-
- GPU_blend(GPU_BLEND_ALPHA);
-
- scrarea_draw_shape_dark(sa2, dir, pos);
- scrarea_draw_shape_light(sa1, dira, pos);
-
- GPU_blend(GPU_BLEND_NONE);
}
immUnbindProgram();
+ GPU_blend(GPU_BLEND_NONE);
+
+ /* Outline the combined area. */
+ UI_draw_roundbox_corner_set(UI_CNR_ALL);
+ UI_draw_roundbox_4fv(&combined, false, 7 * U.pixelsize, (float[4]){1.0f, 1.0f, 1.0f, 0.8f});
}
-void ED_screen_draw_split_preview(ScrArea *area, const int dir, const float fac)
+void screen_draw_split_preview(ScrArea *area, const eScreenAxis dir_axis, const float fac)
{
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
@@ -485,7 +334,7 @@ void ED_screen_draw_split_preview(ScrArea *area, const int dir, const float fac)
immBegin(GPU_PRIM_LINES, 2);
- if (dir == 'h') {
+ if (dir_axis == SCREEN_AXIS_H) {
const float y = (1 - fac) * area->totrct.ymin + fac * area->totrct.ymax;
immVertex2f(pos, area->totrct.xmin, y);
@@ -503,7 +352,7 @@ void ED_screen_draw_split_preview(ScrArea *area, const int dir, const float fac)
immEnd();
}
else {
- BLI_assert(dir == 'v');
+ BLI_assert(dir_axis == SCREEN_AXIS_V);
const float x = (1 - fac) * area->totrct.xmin + fac * area->totrct.xmax;
immVertex2f(pos, x, area->totrct.ymin);
diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c
index 7ad8eada3b9..6cb184a3394 100644
--- a/source/blender/editors/screen/screen_edit.c
+++ b/source/blender/editors/screen/screen_edit.c
@@ -104,8 +104,12 @@ static void screen_delarea(bContext *C, bScreen *screen, ScrArea *area)
MEM_freeN(area);
}
-ScrArea *area_split(
- const wmWindow *win, bScreen *screen, ScrArea *area, char dir, float fac, int merge)
+ScrArea *area_split(const wmWindow *win,
+ bScreen *screen,
+ ScrArea *area,
+ const eScreenAxis dir_axis,
+ const float fac,
+ const bool merge)
{
ScrArea *newa = NULL;
@@ -116,7 +120,7 @@ ScrArea *area_split(
rcti window_rect;
WM_window_rect_calc(win, &window_rect);
- short split = screen_geom_find_area_split_point(area, &window_rect, dir, fac);
+ short split = screen_geom_find_area_split_point(area, &window_rect, dir_axis, fac);
if (split == 0) {
return NULL;
}
@@ -125,7 +129,7 @@ ScrArea *area_split(
* normally it shouldn't matter which is used since the copy should match the original
* however with viewport rendering and python console this isn't the case. - campbell */
- if (dir == 'h') {
+ if (dir_axis == SCREEN_AXIS_H) {
/* new vertices */
ScrVert *sv1 = screen_geom_vertex_add(screen, area->v1->vec.x, split);
ScrVert *sv2 = screen_geom_vertex_add(screen, area->v4->vec.x, split);
@@ -279,47 +283,44 @@ void screen_new_activate_prepare(const wmWindow *win, bScreen *screen_new)
screen_new->do_draw = true;
}
-/* with area as center, sb is located at: 0=W, 1=N, 2=E, 3=S */
-/* -1 = not valid check */
-/* used with join operator */
-int area_getorientation(ScrArea *area, ScrArea *sb)
+/**
+ * with `sa_a` as center, `sa_b` is located at: 0=W, 1=N, 2=E, 3=S
+ * -1 = not valid check.
+ * used with join operator.
+ */
+eScreenDir area_getorientation(ScrArea *sa_a, ScrArea *sa_b)
{
- if (area == NULL || sb == NULL) {
- return -1;
+ if (sa_a == NULL || sa_b == NULL || sa_a == sa_b) {
+ return SCREEN_DIR_NONE;
}
- ScrVert *saBL = area->v1;
- ScrVert *saTL = area->v2;
- ScrVert *saTR = area->v3;
- ScrVert *saBR = area->v4;
+ const vec2s *sa_bl = &sa_a->v1->vec;
+ const vec2s *sa_tl = &sa_a->v2->vec;
+ const vec2s *sa_tr = &sa_a->v3->vec;
+ const vec2s *sa_br = &sa_a->v4->vec;
- ScrVert *sbBL = sb->v1;
- ScrVert *sbTL = sb->v2;
- ScrVert *sbTR = sb->v3;
- ScrVert *sbBR = sb->v4;
+ const vec2s *sb_bl = &sa_b->v1->vec;
+ const vec2s *sb_tl = &sa_b->v2->vec;
+ const vec2s *sb_tr = &sa_b->v3->vec;
+ const vec2s *sb_br = &sa_b->v4->vec;
- if (saBL->vec.x == sbBR->vec.x && saTL->vec.x == sbTR->vec.x) { /* area to right of sb = W */
- if ((abs(saBL->vec.y - sbBR->vec.y) <= AREAJOINTOLERANCE) &&
- (abs(saTL->vec.y - sbTR->vec.y) <= AREAJOINTOLERANCE)) {
+ if (sa_bl->x == sb_br->x && sa_tl->x == sb_tr->x) { /* sa_a to right of sa_b = W */
+ if ((MIN2(sa_tl->y, sb_tr->y) - MAX2(sa_bl->y, sb_br->y)) > AREAJOINTOLERANCEY) {
return 0;
}
}
- else if (saTL->vec.y == sbBL->vec.y &&
- saTR->vec.y == sbBR->vec.y) { /* area to bottom of sb = N */
- if ((abs(saTL->vec.x - sbBL->vec.x) <= AREAJOINTOLERANCE) &&
- (abs(saTR->vec.x - sbBR->vec.x) <= AREAJOINTOLERANCE)) {
+ else if (sa_tl->y == sb_bl->y && sa_tr->y == sb_br->y) { /* sa_a to bottom of sa_b = N */
+ if ((MIN2(sa_tr->x, sb_br->x) - MAX2(sa_tl->x, sb_bl->x)) > AREAJOINTOLERANCEX) {
return 1;
}
}
- else if (saTR->vec.x == sbTL->vec.x && saBR->vec.x == sbBL->vec.x) { /* area to left of sb = E */
- if ((abs(saTR->vec.y - sbTL->vec.y) <= AREAJOINTOLERANCE) &&
- (abs(saBR->vec.y - sbBL->vec.y) <= AREAJOINTOLERANCE)) {
+ else if (sa_tr->x == sb_tl->x && sa_br->x == sb_bl->x) { /* sa_a to left of sa_b = E */
+ if ((MIN2(sa_tr->y, sb_tl->y) - MAX2(sa_br->y, sb_bl->y)) > AREAJOINTOLERANCEY) {
return 2;
}
}
- else if (saBL->vec.y == sbTL->vec.y && saBR->vec.y == sbTR->vec.y) { /* area on top of sb = S*/
- if ((abs(saBL->vec.x - sbTL->vec.x) <= AREAJOINTOLERANCE) &&
- (abs(saBR->vec.x - sbTR->vec.x) <= AREAJOINTOLERANCE)) {
+ else if (sa_bl->y == sb_tl->y && sa_br->y == sb_tr->y) { /* sa_a on top of sa_b = S */
+ if ((MIN2(sa_br->x, sb_tr->x) - MAX2(sa_bl->x, sb_tl->x)) > AREAJOINTOLERANCEX) {
return 3;
}
}
@@ -327,6 +328,39 @@ int area_getorientation(ScrArea *area, ScrArea *sb)
return -1;
}
+/**
+ * Get alignment offset of adjacent areas. 'dir' value is like #area_getorientation().
+ */
+void area_getoffsets(
+ ScrArea *sa_a, ScrArea *sa_b, const eScreenDir dir, int *r_offset1, int *r_offset2)
+{
+ if (sa_a == NULL || sa_b == NULL) {
+ *r_offset1 = INT_MAX;
+ *r_offset2 = INT_MAX;
+ }
+ else if (dir == SCREEN_DIR_W) { /* West: sa on right and sa_b to the left. */
+ *r_offset1 = sa_b->v3->vec.y - sa_a->v2->vec.y;
+ *r_offset2 = sa_b->v4->vec.y - sa_a->v1->vec.y;
+ }
+ else if (dir == SCREEN_DIR_N) { /* North: sa below and sa_b above. */
+ *r_offset1 = sa_a->v2->vec.x - sa_b->v1->vec.x;
+ *r_offset2 = sa_a->v3->vec.x - sa_b->v4->vec.x;
+ }
+ else if (dir == SCREEN_DIR_E) { /* East: sa on left and sa_b to the right. */
+ *r_offset1 = sa_b->v2->vec.y - sa_a->v3->vec.y;
+ *r_offset2 = sa_b->v1->vec.y - sa_a->v4->vec.y;
+ }
+ else if (dir == SCREEN_DIR_S) { /* South: sa above and sa_b below. */
+ *r_offset1 = sa_a->v1->vec.x - sa_b->v2->vec.x;
+ *r_offset2 = sa_a->v4->vec.x - sa_b->v3->vec.x;
+ }
+ else {
+ BLI_assert(dir == SCREEN_DIR_NONE);
+ *r_offset1 = INT_MAX;
+ *r_offset2 = INT_MAX;
+ }
+}
+
/* Screen verts with horizontal position equal to from_x are moved to to_x. */
static void screen_verts_halign(const wmWindow *win,
const bScreen *screen,
@@ -358,11 +392,11 @@ static void screen_verts_valign(const wmWindow *win,
/* Adjust all screen edges to allow joining two areas. 'dir' value is like area_getorientation().
*/
static void screen_areas_align(
- bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2, const int dir)
+ bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2, const eScreenDir dir)
{
wmWindow *win = CTX_wm_window(C);
- if (ELEM(dir, 0, 2)) {
+ if (SCREEN_DIR_IS_HORIZONTAL(dir)) {
/* horizontal join, use average for new top and bottom. */
int top = (sa1->v2->vec.y + sa2->v2->vec.y) / 2;
int bottom = (sa1->v4->vec.y + sa2->v4->vec.y) / 2;
@@ -390,41 +424,47 @@ static void screen_areas_align(
}
}
-/* Helper function to join 2 areas, it has a return value, 0=failed 1=success
- * used by the split, join operators
- */
-int screen_area_join(bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2)
+/* Simple join of two areas without any splitting. Will return false if not possible. */
+static bool screen_area_join_aligned(bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2)
{
- int dir = area_getorientation(sa1, sa2);
+ const eScreenDir dir = area_getorientation(sa1, sa2);
+ if (dir == SCREEN_DIR_NONE) {
+ return false;
+ }
- if (dir == -1) {
- return 0;
+ int offset1;
+ int offset2;
+ area_getoffsets(sa1, sa2, dir, &offset1, &offset2);
+
+ int tolerance = SCREEN_DIR_IS_HORIZONTAL(dir) ? AREAJOINTOLERANCEY : AREAJOINTOLERANCEX;
+ if ((abs(offset1) >= tolerance) || (abs(offset2) >= tolerance)) {
+ return false;
}
- /* Align areas if they are not. Do sanity checking before getting here. */
+ /* Align areas if they are not. */
screen_areas_align(C, screen, sa1, sa2, dir);
- if (dir == 0) { /* sa1 to right of sa2 = W */
- sa1->v1 = sa2->v1; /* BL */
- sa1->v2 = sa2->v2; /* TL */
+ if (dir == SCREEN_DIR_W) { /* sa1 to right of sa2 = West. */
+ sa1->v1 = sa2->v1; /* BL */
+ sa1->v2 = sa2->v2; /* TL */
screen_geom_edge_add(screen, sa1->v2, sa1->v3);
screen_geom_edge_add(screen, sa1->v1, sa1->v4);
}
- else if (dir == 1) { /* sa1 to bottom of sa2 = N */
- sa1->v2 = sa2->v2; /* TL */
- sa1->v3 = sa2->v3; /* TR */
+ else if (dir == SCREEN_DIR_N) { /* sa1 to bottom of sa2 = North. */
+ sa1->v2 = sa2->v2; /* TL */
+ sa1->v3 = sa2->v3; /* TR */
screen_geom_edge_add(screen, sa1->v1, sa1->v2);
screen_geom_edge_add(screen, sa1->v3, sa1->v4);
}
- else if (dir == 2) { /* sa1 to left of sa2 = E */
- sa1->v3 = sa2->v3; /* TR */
- sa1->v4 = sa2->v4; /* BR */
+ else if (dir == SCREEN_DIR_E) { /* sa1 to left of sa2 = East. */
+ sa1->v3 = sa2->v3; /* TR */
+ sa1->v4 = sa2->v4; /* BR */
screen_geom_edge_add(screen, sa1->v2, sa1->v3);
screen_geom_edge_add(screen, sa1->v1, sa1->v4);
}
- else if (dir == 3) { /* sa1 on top of sa2 = S */
- sa1->v1 = sa2->v1; /* BL */
- sa1->v4 = sa2->v4; /* BR */
+ else if (dir == SCREEN_DIR_S) { /* sa1 on top of sa2 = South. */
+ sa1->v1 = sa2->v1; /* BL */
+ sa1->v4 = sa2->v4; /* BR */
screen_geom_edge_add(screen, sa1->v1, sa1->v2);
screen_geom_edge_add(screen, sa1->v3, sa1->v4);
}
@@ -434,7 +474,104 @@ int screen_area_join(bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2)
/* Update preview thumbnail */
BKE_icon_changed(screen->id.icon_id);
- return 1;
+ return true;
+}
+
+/* Slice off and return new area. "Reverse" gives right/bottom, rather than left/top. */
+static ScrArea *screen_area_trim(
+ bContext *C, bScreen *screen, ScrArea **area, int size, eScreenDir dir, bool reverse)
+{
+ const bool vertical = SCREEN_DIR_IS_VERTICAL(dir);
+ if (abs(size) < (vertical ? AREAJOINTOLERANCEX : AREAJOINTOLERANCEY)) {
+ return NULL;
+ }
+
+ /* Measurement with ScrVerts because winx and winy might not be correct at this time. */
+ float fac = abs(size) / (float)(vertical ? ((*area)->v3->vec.x - (*area)->v1->vec.x) :
+ ((*area)->v3->vec.y - (*area)->v1->vec.y));
+ fac = (reverse == vertical) ? 1.0f - fac : fac;
+ ScrArea *newsa = area_split(
+ CTX_wm_window(C), screen, *area, vertical ? SCREEN_AXIS_V : SCREEN_AXIS_H, fac, true);
+
+ /* area_split always returns smallest of the two areas, so might have to swap. */
+ if (((fac > 0.5f) == vertical) != reverse) {
+ ScrArea *temp = *area;
+ *area = newsa;
+ newsa = temp;
+ }
+
+ return newsa;
+}
+
+/* Join any two neighboring areas. Might create new areas, kept if over min_remainder. */
+static bool screen_area_join_ex(
+ bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2, bool close_all_remainders)
+{
+ const eScreenDir dir = area_getorientation(sa1, sa2);
+ if (dir == SCREEN_DIR_NONE) {
+ return false;
+ }
+
+ int offset1;
+ int offset2;
+ area_getoffsets(sa1, sa2, dir, &offset1, &offset2);
+
+ /* Split Left/Top into new area if overhanging. */
+ ScrArea *side1 = screen_area_trim(C, screen, (offset1 > 0) ? &sa2 : &sa1, offset1, dir, false);
+
+ /* Split Right/Bottom into new area if overhanging. */
+ ScrArea *side2 = screen_area_trim(C, screen, (offset2 > 0) ? &sa1 : &sa2, offset2, dir, true);
+
+ /* The two areas now line up, so join them. */
+ screen_area_join_aligned(C, screen, sa1, sa2);
+
+ if (close_all_remainders || offset1 < 0 || offset2 > 0) {
+ /* Close both if trimming `sa1`. */
+ screen_area_close(C, screen, side1);
+ screen_area_close(C, screen, side2);
+ }
+
+ BKE_icon_changed(screen->id.icon_id);
+ return true;
+}
+
+/* Join any two neighboring areas. Might involve complex changes. */
+int screen_area_join(bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2)
+{
+ return screen_area_join_ex(C, screen, sa1, sa2, false);
+}
+
+/* Close a screen area, allowing most-aligned neighbor to take its place. */
+bool screen_area_close(struct bContext *C, bScreen *screen, ScrArea *area)
+{
+ if (area == NULL) {
+ return false;
+ }
+
+ ScrArea *sa2 = NULL;
+ float best_alignment = 0.0f;
+
+ LISTBASE_FOREACH (ScrArea *, neighbor, &screen->areabase) {
+ const eScreenDir dir = area_getorientation(area, neighbor);
+ /* Must at least partially share an edge and not be a global area. */
+ if ((dir != SCREEN_DIR_NONE) && (neighbor->global == NULL)) {
+ /* Winx/Winy might not be updated yet, so get lengths from verts. */
+ const bool vertical = SCREEN_DIR_IS_VERTICAL(dir);
+ const int area_length = vertical ? (area->v3->vec.x - area->v1->vec.x) :
+ (area->v3->vec.y - area->v1->vec.y);
+ const int ar_length = vertical ? (neighbor->v3->vec.x - neighbor->v1->vec.x) :
+ (neighbor->v3->vec.y - neighbor->v1->vec.y);
+ /* Calculate the ratio of the lengths of the shared edges. */
+ float alignment = MIN2(area_length, ar_length) / (float)MAX2(area_length, ar_length);
+ if (alignment > best_alignment) {
+ best_alignment = alignment;
+ sa2 = neighbor;
+ }
+ }
+ }
+
+ /* Join from neighbor into this area to close it. */
+ return screen_area_join_ex(C, screen, sa2, area, true);
}
/* ****************** EXPORTED API TO OTHER MODULES *************************** */
@@ -1448,6 +1585,7 @@ ScrArea *ED_screen_temp_space_open(bContext *C,
sizex,
sizey,
(int)space_type,
+ false,
dialog,
true,
WIN_ALIGN_LOCATION_CENTER)) {
diff --git a/source/blender/editors/screen/screen_geometry.c b/source/blender/editors/screen/screen_geometry.c
index ac159f4d633..51edad0332b 100644
--- a/source/blender/editors/screen/screen_geometry.c
+++ b/source/blender/editors/screen/screen_geometry.c
@@ -304,7 +304,7 @@ void screen_geom_vertices_scale(const wmWindow *win, bScreen *screen)
*/
short screen_geom_find_area_split_point(const ScrArea *area,
const rcti *window_rect,
- char dir,
+ const eScreenAxis dir_axis,
float fac)
{
const int cur_area_width = screen_geom_area_width(area);
@@ -313,17 +313,21 @@ short screen_geom_find_area_split_point(const ScrArea *area,
const short area_min_y = ED_area_headersize();
/* area big enough? */
- if ((dir == 'v') && (cur_area_width <= 2 * area_min_x)) {
- return 0;
+ if (dir_axis == SCREEN_AXIS_V) {
+ if (cur_area_width <= 2 * area_min_x) {
+ return 0;
+ }
}
- if ((dir == 'h') && (cur_area_height <= 2 * area_min_y)) {
- return 0;
+ else if (dir_axis == SCREEN_AXIS_H) {
+ if (cur_area_height <= 2 * area_min_y) {
+ return 0;
+ }
}
/* to be sure */
CLAMP(fac, 0.0f, 1.0f);
- if (dir == 'h') {
+ if (dir_axis == SCREEN_AXIS_H) {
short y = area->v1->vec.y + round_fl_to_short(fac * cur_area_height);
int area_min = area_min_y;
@@ -373,13 +377,13 @@ void screen_geom_select_connected_edge(const wmWindow *win, ScrEdge *edge)
{
bScreen *screen = WM_window_get_active_screen(win);
- /* 'dir' is the direction of EDGE */
- char dir;
+ /* 'dir_axis' is the direction of EDGE */
+ eScreenAxis dir_axis;
if (edge->v1->vec.x == edge->v2->vec.x) {
- dir = 'v';
+ dir_axis = SCREEN_AXIS_V;
}
else {
- dir = 'h';
+ dir_axis = SCREEN_AXIS_H;
}
ED_screen_verts_iter(win, screen, sv)
@@ -396,13 +400,13 @@ void screen_geom_select_connected_edge(const wmWindow *win, ScrEdge *edge)
oneselected = false;
LISTBASE_FOREACH (ScrEdge *, se, &screen->edgebase) {
if (se->v1->flag + se->v2->flag == 1) {
- if (dir == 'h') {
+ if (dir_axis == SCREEN_AXIS_H) {
if (se->v1->vec.y == se->v2->vec.y) {
se->v1->flag = se->v2->flag = 1;
oneselected = true;
}
}
- if (dir == 'v') {
+ else if (dir_axis == SCREEN_AXIS_V) {
if (se->v1->vec.x == se->v2->vec.x) {
se->v1->flag = se->v2->flag = 1;
oneselected = true;
diff --git a/source/blender/editors/screen/screen_intern.h b/source/blender/editors/screen/screen_intern.h
index c51ff559786..683f2844371 100644
--- a/source/blender/editors/screen/screen_intern.h
+++ b/source/blender/editors/screen/screen_intern.h
@@ -29,12 +29,37 @@ struct bContextDataResult;
/* internal exports only */
+typedef enum eScreenDir {
+ /** This can mean unset, unknown or invalid. */
+ SCREEN_DIR_NONE = -1,
+ /** West/Left. */
+ SCREEN_DIR_W = 0,
+ /** North/Up. */
+ SCREEN_DIR_N = 1,
+ /** East/Right. */
+ SCREEN_DIR_E = 2,
+ /** South/Down. */
+ SCREEN_DIR_S = 3,
+} eScreenDir;
+
+#define SCREEN_DIR_IS_VERTICAL(dir) (ELEM(dir, SCREEN_DIR_N, SCREEN_DIR_S))
+#define SCREEN_DIR_IS_HORIZONTAL(dir) (ELEM(dir, SCREEN_DIR_W, SCREEN_DIR_E))
+
+typedef enum eScreenAxis {
+ /** Horizontal. */
+ SCREEN_AXIS_H = 'h',
+ /** Vertical. */
+ SCREEN_AXIS_V = 'v',
+} eScreenAxis;
+
#define AZONESPOTW UI_HEADER_OFFSET /* width of corner #AZone - max */
#define AZONESPOTH (0.6f * U.widget_unit) /* height of corner #AZone */
#define AZONEFADEIN (5.0f * U.widget_unit) /* when #AZone is totally visible */
#define AZONEFADEOUT (6.5f * U.widget_unit) /* when we start seeing the #AZone */
-#define AREAJOINTOLERANCE (1.0f * U.widget_unit) /* Edges must be close to allow joining. */
+/* Edges must be within these to allow joining. */
+#define AREAJOINTOLERANCEX (AREAMINX * U.dpi_fac)
+#define AREAJOINTOLERANCEY (HEADERY * U.dpi_fac)
/* Expanded interaction influence of area borders. */
#define BORDERPADDING (U.dpi_fac + U.pixelsize)
@@ -44,6 +69,10 @@ void ED_area_data_copy(ScrArea *area_dst, ScrArea *area_src, const bool do_free)
void ED_area_data_swap(ScrArea *area_dst, ScrArea *area_src);
void region_toggle_hidden(struct bContext *C, ARegion *region, const bool do_fade);
+/* screen_draw.c */
+void screen_draw_join_highlight(struct ScrArea *sa1, struct ScrArea *sa2);
+void screen_draw_split_preview(struct ScrArea *area, const eScreenAxis dir_axis, const float fac);
+
/* screen_edit.c */
bScreen *screen_add(struct Main *bmain, const char *name, const rcti *rect);
void screen_data_copy(bScreen *to, bScreen *from);
@@ -54,11 +83,17 @@ void screen_change_prepare(bScreen *screen_old,
struct Main *bmain,
struct bContext *C,
wmWindow *win);
-ScrArea *area_split(
- const wmWindow *win, bScreen *screen, ScrArea *area, char dir, float fac, int merge);
+ScrArea *area_split(const wmWindow *win,
+ bScreen *screen,
+ ScrArea *area,
+ const eScreenAxis dir_axis,
+ const float fac,
+ const bool merge);
int screen_area_join(struct bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2);
-int area_getorientation(ScrArea *area, ScrArea *sb);
-
+eScreenDir area_getorientation(ScrArea *sa_a, ScrArea *sa_b);
+void area_getoffsets(
+ ScrArea *sa_a, ScrArea *sa_b, const eScreenDir dir, int *r_offset1, int *r_offset2);
+bool screen_area_close(struct bContext *C, bScreen *screen, ScrArea *area);
struct AZone *ED_area_actionzone_find_xy(ScrArea *area, const int xy[2]);
/* screen_geometry.c */
@@ -80,7 +115,7 @@ ScrEdge *screen_geom_find_active_scredge(const wmWindow *win,
void screen_geom_vertices_scale(const wmWindow *win, bScreen *screen);
short screen_geom_find_area_split_point(const ScrArea *area,
const rcti *window_rect,
- char dir,
+ const eScreenAxis dir_axis,
float fac);
void screen_geom_select_connected_edge(const wmWindow *win, ScrEdge *edge);
diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c
index 797c3bcf132..373bde99e24 100644
--- a/source/blender/editors/screen/screen_ops.c
+++ b/source/blender/editors/screen/screen_ops.c
@@ -141,6 +141,14 @@ bool ED_operator_screenactive(bContext *C)
return true;
}
+bool ED_operator_screenactive_nobackground(bContext *C)
+{
+ if (G.background) {
+ return false;
+ }
+ return ED_operator_screenactive(C);
+}
+
/* XXX added this to prevent anim state to change during renders */
static bool ED_operator_screenactive_norender(bContext *C)
{
@@ -686,7 +694,9 @@ static bool screen_active_editable(bContext *C)
typedef struct sActionzoneData {
ScrArea *sa1, *sa2;
AZone *az;
- int x, y, gesture_dir, modifier;
+ int x, y;
+ eScreenDir gesture_dir;
+ int modifier;
} sActionzoneData;
/* quick poll to save operators to be created and handled */
@@ -1037,16 +1047,16 @@ static int actionzone_modal(bContext *C, wmOperator *op, const wmEvent *event)
/* Calculate gesture cardinal direction. */
if (delta_y > abs(delta_x)) {
- sad->gesture_dir = 'n';
+ sad->gesture_dir = SCREEN_DIR_N;
}
else if (delta_x >= abs(delta_y)) {
- sad->gesture_dir = 'e';
+ sad->gesture_dir = SCREEN_DIR_E;
}
else if (delta_y < -abs(delta_x)) {
- sad->gesture_dir = 's';
+ sad->gesture_dir = SCREEN_DIR_S;
}
else {
- sad->gesture_dir = 'w';
+ sad->gesture_dir = SCREEN_DIR_W;
}
bool is_gesture;
@@ -1063,22 +1073,24 @@ static int actionzone_modal(bContext *C, wmOperator *op, const wmEvent *event)
/* Are we still in same area? */
if (BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, event->x, event->y) == sad->sa1) {
/* Same area, so possible split. */
- WM_cursor_set(
- win, (ELEM(sad->gesture_dir, 'n', 's')) ? WM_CURSOR_H_SPLIT : WM_CURSOR_V_SPLIT);
+ WM_cursor_set(win,
+ SCREEN_DIR_IS_VERTICAL(sad->gesture_dir) ? WM_CURSOR_H_SPLIT :
+ WM_CURSOR_V_SPLIT);
is_gesture = (delta_max > split_threshold);
}
else {
/* Different area, so possible join. */
- if (sad->gesture_dir == 'n') {
+ if (sad->gesture_dir == SCREEN_DIR_N) {
WM_cursor_set(win, WM_CURSOR_N_ARROW);
}
- else if (sad->gesture_dir == 's') {
+ else if (sad->gesture_dir == SCREEN_DIR_S) {
WM_cursor_set(win, WM_CURSOR_S_ARROW);
}
- else if (sad->gesture_dir == 'e') {
+ else if (sad->gesture_dir == SCREEN_DIR_E) {
WM_cursor_set(win, WM_CURSOR_E_ARROW);
}
else {
+ BLI_assert(sad->gesture_dir == SCREEN_DIR_W);
WM_cursor_set(win, WM_CURSOR_W_ARROW);
}
is_gesture = (delta_max > join_threshold);
@@ -1350,6 +1362,7 @@ static int area_dupli_invoke(bContext *C, wmOperator *op, const wmEvent *event)
area->winx,
area->winy,
SPACE_EMPTY,
+ false,
true,
false,
WIN_ALIGN_ABSOLUTE);
@@ -1387,6 +1400,58 @@ static void SCREEN_OT_area_dupli(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Area Close Operator
+ *
+ * Close selected area, replace by expanding a neighbor
+ * \{ */
+
+/* operator callback */
+static int area_close_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *UNUSED(event))
+{
+ ScrArea *area = CTX_wm_area(C);
+ if ((area != NULL) && screen_area_close(C, CTX_wm_screen(C), area)) {
+ WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
+ return OPERATOR_FINISHED;
+ }
+ return OPERATOR_CANCELLED;
+}
+
+static bool area_close_poll(bContext *C)
+{
+ if (!ED_operator_areaactive(C)) {
+ return false;
+ }
+
+ ScrArea *area = CTX_wm_area(C);
+
+ if (ED_area_is_global(area)) {
+ return false;
+ }
+
+ bScreen *screen = CTX_wm_screen(C);
+
+ /* Can this area join with ANY other area? */
+ LISTBASE_FOREACH (ScrArea *, ar, &screen->areabase) {
+ if (area_getorientation(ar, area) != -1) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static void SCREEN_OT_area_close(wmOperatorType *ot)
+{
+ ot->name = "Close Area";
+ ot->description = "Close selected area";
+ ot->idname = "SCREEN_OT_area_close";
+ ot->invoke = area_close_invoke;
+ ot->poll = area_close_poll;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Move Area Edge Operator
* \{ */
@@ -1420,7 +1485,7 @@ static void SCREEN_OT_area_dupli(wmOperatorType *ot)
typedef struct sAreaMoveData {
int bigger, smaller, origval, step;
- char dir;
+ eScreenAxis dir_axis;
enum AreaMoveSnapType {
/* Snapping disabled */
SNAP_NONE = 0,
@@ -1439,7 +1504,7 @@ typedef struct sAreaMoveData {
* need window bounds in order to get correct limits */
static void area_move_set_limits(wmWindow *win,
bScreen *screen,
- int dir,
+ const eScreenAxis dir_axis,
int *bigger,
int *smaller,
bool *use_bigger_smaller_snap)
@@ -1492,7 +1557,7 @@ static void area_move_set_limits(wmWindow *win,
WM_window_rect_calc(win, &window_rect);
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
- if (dir == 'h') {
+ if (dir_axis == SCREEN_AXIS_H) {
int areamin = ED_area_headersize();
if (area->v1->vec.y > window_rect.ymin) {
@@ -1555,8 +1620,8 @@ static bool area_move_init(bContext *C, wmOperator *op)
sAreaMoveData *md = MEM_callocN(sizeof(sAreaMoveData), "sAreaMoveData");
op->customdata = md;
- md->dir = screen_geom_edge_is_horizontal(actedge) ? 'h' : 'v';
- if (md->dir == 'h') {
+ md->dir_axis = screen_geom_edge_is_horizontal(actedge) ? SCREEN_AXIS_H : SCREEN_AXIS_V;
+ if (md->dir_axis == SCREEN_AXIS_H) {
md->origval = actedge->v1->vec.y;
}
else {
@@ -1571,7 +1636,8 @@ static bool area_move_init(bContext *C, wmOperator *op)
}
bool use_bigger_smaller_snap = false;
- area_move_set_limits(win, screen, md->dir, &md->bigger, &md->smaller, &use_bigger_smaller_snap);
+ area_move_set_limits(
+ win, screen, md->dir_axis, &md->bigger, &md->smaller, &use_bigger_smaller_snap);
md->snap_type = use_bigger_smaller_snap ? SNAP_BIGGER_SMALLER_ONLY : SNAP_AREAGRID;
@@ -1582,7 +1648,7 @@ static int area_snap_calc_location(const bScreen *screen,
const enum AreaMoveSnapType snap_type,
const int delta,
const int origval,
- const int dir,
+ const eScreenAxis dir_axis,
const int bigger,
const int smaller)
{
@@ -1607,7 +1673,7 @@ static int area_snap_calc_location(const bScreen *screen,
break;
case SNAP_FRACTION_AND_ADJACENT: {
- const int axis = (dir == 'v') ? 0 : 1;
+ const int axis = (dir_axis == SCREEN_AXIS_V) ? 0 : 1;
int snap_dist_best = INT_MAX;
{
const float div_array[] = {
@@ -1675,7 +1741,7 @@ static int area_snap_calc_location(const bScreen *screen,
static void area_move_apply_do(const bContext *C,
int delta,
const int origval,
- const int dir,
+ const eScreenAxis dir_axis,
const int bigger,
const int smaller,
const enum AreaMoveSnapType snap_type)
@@ -1693,11 +1759,12 @@ static void area_move_apply_do(const bContext *C,
final_loc = origval + delta;
}
else {
- final_loc = area_snap_calc_location(screen, snap_type, delta, origval, dir, bigger, smaller);
+ final_loc = area_snap_calc_location(
+ screen, snap_type, delta, origval, dir_axis, bigger, smaller);
}
BLI_assert(final_loc != -1);
- short axis = (dir == 'v') ? 0 : 1;
+ short axis = (dir_axis == SCREEN_AXIS_V) ? 0 : 1;
ED_screen_verts_iter(win, screen, v1)
{
@@ -1753,7 +1820,7 @@ static void area_move_apply(bContext *C, wmOperator *op)
sAreaMoveData *md = op->customdata;
int delta = RNA_int_get(op->ptr, "delta");
- area_move_apply_do(C, delta, md->origval, md->dir, md->bigger, md->smaller, md->snap_type);
+ area_move_apply_do(C, delta, md->origval, md->dir_axis, md->bigger, md->smaller, md->snap_type);
}
static void area_move_exit(bContext *C, wmOperator *op)
@@ -1818,7 +1885,7 @@ static int area_move_modal(bContext *C, wmOperator *op, const wmEvent *event)
int x = RNA_int_get(op->ptr, "x");
int y = RNA_int_get(op->ptr, "y");
- int delta = (md->dir == 'v') ? event->x - x : event->y - y;
+ const int delta = (md->dir_axis == SCREEN_AXIS_V) ? event->x - x : event->y - y;
RNA_int_set(op->ptr, "delta", delta);
area_move_apply(C, op);
@@ -1884,7 +1951,7 @@ static void SCREEN_OT_area_move(wmOperatorType *ot)
/*
* operator state vars:
* fac spit point
- * dir direction 'v' or 'h'
+ * dir direction #SCREEN_AXIS_V or #SCREEN_AXIS_H
*
* operator customdata:
* area pointer to (active) area
@@ -1921,7 +1988,7 @@ typedef struct sAreaSplitData {
int delta; /* delta move edge */
int origmin, origsize; /* to calculate fac, for property storage */
int previewmode; /* draw previewline, then split */
- void *draw_callback; /* call `ED_screen_draw_split_preview` */
+ void *draw_callback; /* call `screen_draw_split_preview` */
bool do_snap;
ScrEdge *nedge; /* new edge */
@@ -1936,10 +2003,10 @@ static void area_split_draw_cb(const struct wmWindow *UNUSED(win), void *userdat
sAreaSplitData *sd = op->customdata;
if (sd->sarea) {
- int dir = RNA_enum_get(op->ptr, "direction");
+ const eScreenAxis dir_axis = RNA_enum_get(op->ptr, "direction");
float fac = RNA_float_get(op->ptr, "factor");
- ED_screen_draw_split_preview(sd->sarea, dir, fac);
+ screen_draw_split_preview(sd->sarea, dir_axis, fac);
}
}
@@ -1966,14 +2033,18 @@ static bool area_split_init(bContext *C, wmOperator *op)
}
/* required properties */
- int dir = RNA_enum_get(op->ptr, "direction");
+ const eScreenAxis dir_axis = RNA_enum_get(op->ptr, "direction");
/* minimal size */
- if (dir == 'v' && area->winx < 2 * AREAMINX) {
- return false;
+ if (dir_axis == SCREEN_AXIS_V) {
+ if (area->winx < 2 * AREAMINX) {
+ return false;
+ }
}
- if (dir == 'h' && area->winy < 2 * ED_area_headersize()) {
- return false;
+ else {
+ if (area->winy < 2 * ED_area_headersize()) {
+ return false;
+ }
}
/* custom data */
@@ -1981,7 +2052,7 @@ static bool area_split_init(bContext *C, wmOperator *op)
op->customdata = sd;
sd->sarea = area;
- if (dir == 'v') {
+ if (dir_axis == SCREEN_AXIS_V) {
sd->origmin = area->v1->vec.x;
sd->origsize = area->v4->vec.x - sd->origmin;
}
@@ -2030,9 +2101,9 @@ static bool area_split_apply(bContext *C, wmOperator *op)
sAreaSplitData *sd = (sAreaSplitData *)op->customdata;
float fac = RNA_float_get(op->ptr, "factor");
- int dir = RNA_enum_get(op->ptr, "direction");
+ const eScreenAxis dir_axis = RNA_enum_get(op->ptr, "direction");
- sd->narea = area_split(win, screen, sd->sarea, dir, fac, 0); /* 0 = no merge */
+ sd->narea = area_split(win, screen, sd->sarea, dir_axis, fac, false); /* false = no merge */
if (sd->narea == NULL) {
return false;
@@ -2049,7 +2120,7 @@ static bool area_split_apply(bContext *C, wmOperator *op)
sd->nedge->v1->editflag = 1;
sd->nedge->v2->editflag = 1;
- if (dir == 'h') {
+ if (dir_axis == SCREEN_AXIS_H) {
sd->origval = sd->nedge->v1->vec.y;
}
else {
@@ -2098,8 +2169,8 @@ static void area_split_exit(bContext *C, wmOperator *op)
static void area_split_preview_update_cursor(bContext *C, wmOperator *op)
{
wmWindow *win = CTX_wm_window(C);
- int dir = RNA_enum_get(op->ptr, "direction");
- WM_cursor_set(win, dir == 'h' ? WM_CURSOR_H_SPLIT : WM_CURSOR_V_SPLIT);
+ const eScreenAxis dir_axis = RNA_enum_get(op->ptr, "direction");
+ WM_cursor_set(win, (dir_axis == SCREEN_AXIS_H) ? WM_CURSOR_H_SPLIT : WM_CURSOR_V_SPLIT);
}
/* UI callback, adds new handler */
@@ -2115,7 +2186,7 @@ static int area_split_invoke(bContext *C, wmOperator *op, const wmEvent *event)
PropertyRNA *prop_factor = RNA_struct_find_property(op->ptr, "factor");
PropertyRNA *prop_cursor = RNA_struct_find_property(op->ptr, "cursor");
- int dir;
+ eScreenAxis dir_axis;
if (event->type == EVT_ACTIONZONE_AREA) {
sActionzoneData *sad = event->customdata;
@@ -2143,12 +2214,12 @@ static int area_split_invoke(bContext *C, wmOperator *op, const wmEvent *event)
float factor;
/* Prepare operator state vars. */
- if (ELEM(sad->gesture_dir, 'n', 's')) {
- dir = 'h';
+ if (SCREEN_DIR_IS_VERTICAL(sad->gesture_dir)) {
+ dir_axis = SCREEN_AXIS_H;
factor = factor_h;
}
else {
- dir = 'v';
+ dir_axis = SCREEN_AXIS_V;
factor = factor_v;
}
@@ -2158,7 +2229,7 @@ static int area_split_invoke(bContext *C, wmOperator *op, const wmEvent *event)
RNA_property_float_set(op->ptr, prop_factor, factor);
- RNA_property_enum_set(op->ptr, prop_dir, dir);
+ RNA_property_enum_set(op->ptr, prop_dir, dir_axis);
/* general init, also non-UI case, adds customdata, sets area and defaults */
if (!area_split_init(C, op)) {
@@ -2170,8 +2241,8 @@ static int area_split_invoke(bContext *C, wmOperator *op, const wmEvent *event)
if (area == NULL) {
return OPERATOR_CANCELLED;
}
- dir = RNA_property_enum_get(op->ptr, prop_dir);
- if (dir == 'h') {
+ dir_axis = RNA_property_enum_get(op->ptr, prop_dir);
+ if (dir_axis == SCREEN_AXIS_H) {
RNA_property_float_set(
op->ptr, prop_factor, ((float)(event->x - area->v1->vec.x)) / (float)area->winx);
}
@@ -2204,9 +2275,9 @@ static int area_split_invoke(bContext *C, wmOperator *op, const wmEvent *event)
return OPERATOR_CANCELLED;
}
- dir = screen_geom_edge_is_horizontal(actedge) ? 'v' : 'h';
+ dir_axis = screen_geom_edge_is_horizontal(actedge) ? SCREEN_AXIS_V : SCREEN_AXIS_H;
- RNA_property_enum_set(op->ptr, prop_dir, dir);
+ RNA_property_enum_set(op->ptr, prop_dir, dir_axis);
/* special case, adds customdata, sets defaults */
if (!area_split_menu_init(C, op)) {
@@ -2219,7 +2290,7 @@ static int area_split_invoke(bContext *C, wmOperator *op, const wmEvent *event)
if (event->type == EVT_ACTIONZONE_AREA) {
/* do the split */
if (area_split_apply(C, op)) {
- area_move_set_limits(win, screen, dir, &sd->bigger, &sd->smaller, NULL);
+ area_move_set_limits(win, screen, dir_axis, &sd->bigger, &sd->smaller, NULL);
/* add temp handler for edge move or cancel */
G.moving |= G_TRANSFORM_WM;
@@ -2307,8 +2378,9 @@ static int area_split_modal(bContext *C, wmOperator *op, const wmEvent *event)
else {
if (event->val == KM_PRESS) {
if (sd->sarea) {
- int dir = RNA_property_enum_get(op->ptr, prop_dir);
- RNA_property_enum_set(op->ptr, prop_dir, (dir == 'v') ? 'h' : 'v');
+ const eScreenAxis dir_axis = RNA_property_enum_get(op->ptr, prop_dir);
+ RNA_property_enum_set(
+ op->ptr, prop_dir, (dir_axis == SCREEN_AXIS_V) ? SCREEN_AXIS_H : SCREEN_AXIS_V);
area_split_preview_update_cursor(C, op);
update_factor = true;
}
@@ -2329,9 +2401,9 @@ static int area_split_modal(bContext *C, wmOperator *op, const wmEvent *event)
}
if (update_factor) {
- const int dir = RNA_property_enum_get(op->ptr, prop_dir);
+ const eScreenAxis dir_axis = RNA_property_enum_get(op->ptr, prop_dir);
- sd->delta = (dir == 'v') ? event->x - sd->origval : event->y - sd->origval;
+ sd->delta = (dir_axis == SCREEN_AXIS_V) ? event->x - sd->origval : event->y - sd->origval;
if (sd->previewmode == 0) {
if (sd->do_snap) {
@@ -2339,12 +2411,12 @@ static int area_split_modal(bContext *C, wmOperator *op, const wmEvent *event)
SNAP_FRACTION_AND_ADJACENT,
sd->delta,
sd->origval,
- dir,
+ dir_axis,
sd->bigger,
sd->smaller);
sd->delta = snap_loc - sd->origval;
}
- area_move_apply_do(C, sd->delta, sd->origval, dir, sd->bigger, sd->smaller, SNAP_NONE);
+ area_move_apply_do(C, sd->delta, sd->origval, dir_axis, sd->bigger, sd->smaller, SNAP_NONE);
}
else {
if (sd->sarea) {
@@ -2355,7 +2427,7 @@ static int area_split_modal(bContext *C, wmOperator *op, const wmEvent *event)
if (sd->sarea) {
ScrArea *area = sd->sarea;
- if (dir == 'v') {
+ if (dir_axis == SCREEN_AXIS_V) {
sd->origmin = area->v1->vec.x;
sd->origsize = area->v4->vec.x - sd->origmin;
}
@@ -2371,7 +2443,7 @@ static int area_split_modal(bContext *C, wmOperator *op, const wmEvent *event)
SNAP_FRACTION_AND_ADJACENT,
sd->delta,
sd->origval,
- dir,
+ dir_axis,
sd->origmin + sd->origsize,
-sd->origmin);
@@ -2393,8 +2465,8 @@ static int area_split_modal(bContext *C, wmOperator *op, const wmEvent *event)
}
static const EnumPropertyItem prop_direction_items[] = {
- {'h', "HORIZONTAL", 0, "Horizontal", ""},
- {'v', "VERTICAL", 0, "Vertical", ""},
+ {SCREEN_AXIS_H, "HORIZONTAL", 0, "Horizontal", ""},
+ {SCREEN_AXIS_V, "VERTICAL", 0, "Vertical", ""},
{0, NULL, 0, NULL, NULL},
};
@@ -2415,7 +2487,7 @@ static void SCREEN_OT_area_split(wmOperatorType *ot)
ot->flag = OPTYPE_BLOCKING | OPTYPE_INTERNAL;
/* rna */
- RNA_def_enum(ot->srna, "direction", prop_direction_items, 'h', "Direction", "");
+ RNA_def_enum(ot->srna, "direction", prop_direction_items, SCREEN_AXIS_H, "Direction", "");
RNA_def_float(ot->srna, "factor", 0.5f, 0.0, 1.0, "Factor", "", 0.0, 1.0);
RNA_def_int_vector(
ot->srna, "cursor", 2, NULL, INT_MIN, INT_MAX, "Cursor", "", INT_MIN, INT_MAX);
@@ -3210,9 +3282,10 @@ static void SCREEN_OT_screen_full_area(wmOperatorType *ot)
*/
typedef struct sAreaJoinData {
- ScrArea *sa1; /* first area to be considered */
- ScrArea *sa2; /* second area to be considered */
- void *draw_callback; /* call `ED_screen_draw_join_shape` */
+ ScrArea *sa1; /* Potential source area (kept). */
+ ScrArea *sa2; /* Potential target area (removed or reduced). */
+ eScreenDir dir; /* Direction of potential join. */
+ void *draw_callback; /* call #screen_draw_join_highlight */
} sAreaJoinData;
@@ -3221,8 +3294,8 @@ static void area_join_draw_cb(const struct wmWindow *UNUSED(win), void *userdata
const wmOperator *op = userdata;
sAreaJoinData *sd = op->customdata;
- if (sd->sa1 && sd->sa2) {
- ED_screen_draw_join_shape(sd->sa1, sd->sa2);
+ if (sd->sa1 && sd->sa2 && (sd->dir != SCREEN_DIR_NONE)) {
+ screen_draw_join_highlight(sd->sa1, sd->sa2);
}
}
@@ -3244,6 +3317,7 @@ static bool area_join_init(bContext *C, wmOperator *op, ScrArea *sa1, ScrArea *s
jd->sa1 = sa1;
jd->sa2 = sa2;
+ jd->dir = SCREEN_DIR_NONE;
op->customdata = jd;
@@ -3256,7 +3330,7 @@ static bool area_join_init(bContext *C, wmOperator *op, ScrArea *sa1, ScrArea *s
static bool area_join_apply(bContext *C, wmOperator *op)
{
sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
- if (!jd) {
+ if (!jd || (jd->dir == SCREEN_DIR_NONE)) {
return false;
}
@@ -3358,61 +3432,30 @@ static int area_join_modal(bContext *C, wmOperator *op, const wmEvent *event)
case MOUSEMOVE: {
ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, event->x, event->y);
- int dir = -1;
+ jd->dir = area_getorientation(jd->sa1, jd->sa2);
- if (area) {
- if (jd->sa1 != area) {
- dir = area_getorientation(jd->sa1, area);
- if (dir != -1) {
- jd->sa2 = area;
- }
- else {
- /* we are not bordering on the previously selected area
- * we check if area has common border with the one marked for removal
- * in this case we can swap areas.
- */
- dir = area_getorientation(area, jd->sa2);
- if (dir != -1) {
- jd->sa1 = jd->sa2;
- jd->sa2 = area;
- }
- else {
- jd->sa2 = NULL;
- }
- }
- WM_event_add_notifier(C, NC_WINDOW, NULL);
- }
- else {
- /* we are back in the area previously selected for keeping
- * we swap the areas if possible to allow user to choose */
- if (jd->sa2 != NULL) {
- jd->sa1 = jd->sa2;
- jd->sa2 = area;
- dir = area_getorientation(jd->sa1, jd->sa2);
- if (dir == -1) {
- printf("oops, didn't expect that!\n");
- }
- }
- else {
- dir = area_getorientation(jd->sa1, area);
- if (dir != -1) {
- jd->sa2 = area;
- }
- }
- WM_event_add_notifier(C, NC_WINDOW, NULL);
- }
+ if (area == jd->sa1) {
+ /* Hovering current source, so change direction. */
+ jd->sa1 = jd->sa2;
+ jd->sa2 = area;
+ jd->dir = area_getorientation(jd->sa1, jd->sa2);
}
+ else if (area != jd->sa2) {
+ jd->dir = SCREEN_DIR_NONE;
+ }
+
+ WM_event_add_notifier(C, NC_WINDOW, NULL);
- if (dir == 1) {
+ if (jd->dir == SCREEN_DIR_N) {
WM_cursor_set(win, WM_CURSOR_N_ARROW);
}
- else if (dir == 3) {
+ else if (jd->dir == SCREEN_DIR_S) {
WM_cursor_set(win, WM_CURSOR_S_ARROW);
}
- else if (dir == 2) {
+ else if (jd->dir == SCREEN_DIR_E) {
WM_cursor_set(win, WM_CURSOR_E_ARROW);
}
- else if (dir == 0) {
+ else if (jd->dir == SCREEN_DIR_W) {
WM_cursor_set(win, WM_CURSOR_W_ARROW);
}
else {
@@ -3423,6 +3466,10 @@ static int area_join_modal(bContext *C, wmOperator *op, const wmEvent *event)
}
case LEFTMOUSE:
if (event->val == KM_RELEASE) {
+ if (jd->dir == SCREEN_DIR_NONE) {
+ area_join_cancel(C, op);
+ return OPERATOR_CANCELLED;
+ }
ED_area_tag_redraw(jd->sa1);
ED_area_tag_redraw(jd->sa2);
@@ -3493,7 +3540,7 @@ static int screen_area_options_invoke(bContext *C, wmOperator *op, const wmEvent
&ptr);
/* store initial mouse cursor position. */
RNA_int_set_array(&ptr, "cursor", &event->x);
- RNA_enum_set(&ptr, "direction", 'v');
+ RNA_enum_set(&ptr, "direction", SCREEN_AXIS_V);
/* Horizontal Split */
uiItemFullO(layout,
@@ -3506,7 +3553,7 @@ static int screen_area_options_invoke(bContext *C, wmOperator *op, const wmEvent
&ptr);
/* store initial mouse cursor position. */
RNA_int_set_array(&ptr, "cursor", &event->x);
- RNA_enum_set(&ptr, "direction", 'h');
+ RNA_enum_set(&ptr, "direction", SCREEN_AXIS_H);
if (sa1 && sa2) {
uiItemS(layout);
@@ -4069,6 +4116,69 @@ static void SCREEN_OT_header_toggle_menus(wmOperatorType *ot)
/** \name Region Context Menu Operator (Header/Footer/Navbar)
* \{ */
+static void screen_area_menu_items(ScrArea *area, uiLayout *layout)
+{
+ if (ED_area_is_global(area)) {
+ return;
+ }
+
+ PointerRNA ptr;
+
+ /* Mouse position as if in middle of area. */
+ const int loc[2] = {BLI_rcti_cent_x(&area->totrct), BLI_rcti_cent_y(&area->totrct)};
+
+ /* Vertical Split */
+ uiItemFullO(layout,
+ "SCREEN_OT_area_split",
+ IFACE_("Vertical Split"),
+ ICON_NONE,
+ NULL,
+ WM_OP_INVOKE_DEFAULT,
+ 0,
+ &ptr);
+
+ RNA_int_set_array(&ptr, "cursor", loc);
+ RNA_enum_set(&ptr, "direction", SCREEN_AXIS_V);
+
+ /* Horizontal Split */
+ uiItemFullO(layout,
+ "SCREEN_OT_area_split",
+ IFACE_("Horizontal Split"),
+ ICON_NONE,
+ NULL,
+ WM_OP_INVOKE_DEFAULT,
+ 0,
+ &ptr);
+
+ RNA_int_set_array(&ptr, "cursor", &loc[0]);
+ RNA_enum_set(&ptr, "direction", SCREEN_AXIS_H);
+
+ uiItemS(layout);
+
+ if (area->spacetype != SPACE_FILE) {
+ uiItemO(layout,
+ area->full ? IFACE_("Restore Areas") : IFACE_("Maximize Area"),
+ ICON_NONE,
+ "SCREEN_OT_screen_full_area");
+
+ if (!area->full) {
+ uiItemFullO(layout,
+ "SCREEN_OT_screen_full_area",
+ IFACE_("Full Screen Area"),
+ ICON_NONE,
+ NULL,
+ WM_OP_INVOKE_DEFAULT,
+ 0,
+ &ptr);
+ RNA_boolean_set(&ptr, "use_hide_panels", true);
+ }
+ }
+
+ uiItemO(layout, NULL, ICON_NONE, "SCREEN_OT_area_dupli");
+ uiItemS(layout);
+ uiItemO(layout, NULL, ICON_NONE, "SCREEN_OT_area_close");
+}
+
void ED_screens_header_tools_menu_create(bContext *C, uiLayout *layout, void *UNUSED(arg))
{
ScrArea *area = CTX_wm_area(C);
@@ -4102,17 +4212,9 @@ void ED_screens_header_tools_menu_create(bContext *C, uiLayout *layout, void *UN
if (!ELEM(area->spacetype, SPACE_TOPBAR)) {
uiItemS(layout);
-
uiItemO(layout, but_flip_str, ICON_NONE, "SCREEN_OT_region_flip");
- }
-
- /* File browser should be fullscreen all the time, top-bar should
- * never be. But other regions can be maximized/restored. */
- if (!ELEM(area->spacetype, SPACE_FILE, SPACE_TOPBAR)) {
uiItemS(layout);
-
- const char *but_str = area->full ? IFACE_("Tile Area") : IFACE_("Maximize Area");
- uiItemO(layout, but_str, ICON_NONE, "SCREEN_OT_screen_full_area");
+ screen_area_menu_items(area, layout);
}
}
@@ -4134,14 +4236,8 @@ void ED_screens_footer_tools_menu_create(bContext *C, uiLayout *layout, void *UN
uiItemO(layout, but_flip_str, ICON_NONE, "SCREEN_OT_region_flip");
- /* File browser should be fullscreen all the time, top-bar should
- * never be. But other regions can be maximized/restored... */
- if (!ELEM(area->spacetype, SPACE_FILE, SPACE_TOPBAR)) {
- uiItemS(layout);
-
- const char *but_str = area->full ? IFACE_("Tile Area") : IFACE_("Maximize Area");
- uiItemO(layout, but_str, ICON_NONE, "SCREEN_OT_screen_full_area");
- }
+ uiItemS(layout);
+ screen_area_menu_items(area, layout);
}
void ED_screens_navigation_bar_tools_menu_create(bContext *C, uiLayout *layout, void *UNUSED(arg))
@@ -4351,9 +4447,16 @@ static void screen_animation_region_tag_redraw(ScrArea *area,
/* No need to do a full redraw as the current frame indicator is only updated.
* We do need to redraw when this area is in full screen as no other areas
* will be tagged for redrawing. */
- if ((region->regiontype == RGN_TYPE_WINDOW) &&
- (ELEM(area->spacetype, SPACE_GRAPH, SPACE_NLA, SPACE_ACTION)) && !area->full) {
- return;
+ if (region->regiontype == RGN_TYPE_WINDOW && !area->full) {
+ if (ELEM(area->spacetype, SPACE_GRAPH, SPACE_NLA, SPACE_ACTION)) {
+ return;
+ }
+
+ if (area->spacetype == SPACE_SEQ) {
+ if (!ED_space_sequencer_has_visible_animation_on_strip(scene)) {
+ return;
+ }
+ }
}
ED_region_tag_redraw(region);
}
@@ -4853,6 +4956,7 @@ static int userpref_show_exec(bContext *C, wmOperator *op)
sizey,
SPACE_USERPREF,
false,
+ false,
true,
WIN_ALIGN_LOCATION_CENTER) != NULL) {
/* The header only contains the editor switcher and looks empty.
@@ -4863,6 +4967,11 @@ static int userpref_show_exec(bContext *C, wmOperator *op)
region->flag |= RGN_FLAG_HIDDEN;
ED_region_visibility_change_update(C, area, region);
+ /* And also show the region with "Load & Save" buttons. */
+ region = BKE_area_find_region_type(area, RGN_TYPE_EXECUTE);
+ region->flag &= ~RGN_FLAG_HIDDEN;
+ ED_region_visibility_change_update(C, area, region);
+
return OPERATOR_FINISHED;
}
BKE_report(op->reports, RPT_ERROR, "Failed to open window!");
@@ -4878,7 +4987,7 @@ static void SCREEN_OT_userpref_show(struct wmOperatorType *ot)
/* api callbacks */
ot->exec = userpref_show_exec;
- ot->poll = ED_operator_screenactive;
+ ot->poll = ED_operator_screenactive_nobackground; /* Not in background as this opens a window. */
}
/** \} */
@@ -4914,6 +5023,7 @@ static int drivers_editor_show_exec(bContext *C, wmOperator *op)
sizey,
SPACE_GRAPH,
false,
+ false,
true,
WIN_ALIGN_LOCATION_CENTER) != NULL) {
ED_drivers_editor_init(C, CTX_wm_area(C));
@@ -4955,7 +5065,7 @@ static void SCREEN_OT_drivers_editor_show(struct wmOperatorType *ot)
/* api callbacks */
ot->exec = drivers_editor_show_exec;
- ot->poll = ED_operator_screenactive;
+ ot->poll = ED_operator_screenactive_nobackground; /* Not in background as this opens a window. */
}
/** \} */
@@ -4982,6 +5092,7 @@ static int info_log_show_exec(bContext *C, wmOperator *op)
sizey,
SPACE_INFO,
false,
+ false,
true,
WIN_ALIGN_LOCATION_CENTER) != NULL) {
return OPERATOR_FINISHED;
@@ -4999,7 +5110,7 @@ static void SCREEN_OT_info_log_show(struct wmOperatorType *ot)
/* api callbacks */
ot->exec = info_log_show_exec;
- ot->poll = ED_operator_screenactive;
+ ot->poll = ED_operator_screenactive_nobackground;
}
/** \} */
@@ -5331,7 +5442,7 @@ static void context_cycle_prop_get(bScreen *screen,
static int space_context_cycle_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
- const int direction = RNA_enum_get(op->ptr, "direction");
+ const eScreenCycle direction = RNA_enum_get(op->ptr, "direction");
PointerRNA ptr;
PropertyRNA *prop;
@@ -5380,7 +5491,7 @@ static int space_workspace_cycle_invoke(bContext *C, wmOperator *op, const wmEve
}
Main *bmain = CTX_data_main(C);
- const int direction = RNA_enum_get(op->ptr, "direction");
+ const eScreenCycle direction = RNA_enum_get(op->ptr, "direction");
WorkSpace *workspace_src = WM_window_get_active_workspace(win);
WorkSpace *workspace_dst = NULL;
@@ -5456,6 +5567,7 @@ void ED_operatortypes_screen(void)
WM_operatortype_append(SCREEN_OT_area_move);
WM_operatortype_append(SCREEN_OT_area_split);
WM_operatortype_append(SCREEN_OT_area_join);
+ WM_operatortype_append(SCREEN_OT_area_close);
WM_operatortype_append(SCREEN_OT_area_options);
WM_operatortype_append(SCREEN_OT_area_dupli);
WM_operatortype_append(SCREEN_OT_area_swap);
diff --git a/source/blender/editors/screen/screendump.c b/source/blender/editors/screen/screendump.c
index ff0dab7f1c7..6df96b1e30f 100644
--- a/source/blender/editors/screen/screendump.c
+++ b/source/blender/editors/screen/screendump.c
@@ -213,9 +213,8 @@ static void screenshot_draw(bContext *UNUSED(C), wmOperator *op)
uiTemplateImageSettings(layout, &ptr, false);
/* main draw call */
- RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
uiDefAutoButsRNA(
- layout, &ptr, screenshot_draw_check_prop, NULL, NULL, UI_BUT_LABEL_ALIGN_NONE, false);
+ layout, op->ptr, screenshot_draw_check_prop, NULL, NULL, UI_BUT_LABEL_ALIGN_NONE, false);
}
static bool screenshot_poll(bContext *C)
diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c
index b44f2f66d92..7e111905883 100644
--- a/source/blender/editors/sculpt_paint/paint_cursor.c
+++ b/source/blender/editors/sculpt_paint/paint_cursor.c
@@ -545,6 +545,7 @@ static bool paint_draw_tex_overlay(UnifiedPaintSettings *ups,
int x,
int y,
float zoom,
+ const ePaintMode mode,
bool col,
bool primary)
{
@@ -556,6 +557,13 @@ static bool paint_draw_tex_overlay(UnifiedPaintSettings *ups,
(brush->overlay_flags & BRUSH_OVERLAY_SECONDARY) != 0);
int overlay_alpha = (primary) ? brush->texture_overlay_alpha : brush->mask_overlay_alpha;
+ if (mode == PAINT_MODE_TEXTURE_3D) {
+ if (primary && brush->imagepaint_tool != PAINT_TOOL_DRAW) {
+ /* All non-draw tools don't use the primary texture (clone, smear, soften.. etc). */
+ return false;
+ }
+ }
+
if (!(mtex->tex) ||
!((mtex->brush_map_mode == MTEX_MAP_MODE_STENCIL) ||
(valid && ELEM(mtex->brush_map_mode, MTEX_MAP_MODE_VIEW, MTEX_MAP_MODE_TILED)))) {
@@ -785,10 +793,11 @@ static bool paint_draw_alpha_overlay(UnifiedPaintSettings *ups,
/* Colored overlay should be drawn separately. */
if (col) {
if (!(flags & PAINT_OVERLAY_OVERRIDE_PRIMARY)) {
- alpha_overlay_active = paint_draw_tex_overlay(ups, brush, vc, x, y, zoom, true, true);
+ alpha_overlay_active = paint_draw_tex_overlay(ups, brush, vc, x, y, zoom, mode, true, true);
}
if (!(flags & PAINT_OVERLAY_OVERRIDE_SECONDARY)) {
- alpha_overlay_active = paint_draw_tex_overlay(ups, brush, vc, x, y, zoom, false, false);
+ alpha_overlay_active = paint_draw_tex_overlay(
+ ups, brush, vc, x, y, zoom, mode, false, false);
}
if (!(flags & PAINT_OVERLAY_OVERRIDE_CURSOR)) {
alpha_overlay_active = paint_draw_cursor_overlay(ups, brush, vc, x, y, zoom);
@@ -796,7 +805,7 @@ static bool paint_draw_alpha_overlay(UnifiedPaintSettings *ups,
}
else {
if (!(flags & PAINT_OVERLAY_OVERRIDE_PRIMARY) && (mode != PAINT_MODE_WEIGHT)) {
- alpha_overlay_active = paint_draw_tex_overlay(ups, brush, vc, x, y, zoom, false, true);
+ alpha_overlay_active = paint_draw_tex_overlay(ups, brush, vc, x, y, zoom, mode, false, true);
}
if (!(flags & PAINT_OVERLAY_OVERRIDE_CURSOR)) {
alpha_overlay_active = paint_draw_cursor_overlay(ups, brush, vc, x, y, zoom);
@@ -1030,7 +1039,7 @@ static void cursor_draw_point_screen_space(const uint gpuattr,
float translation_vertex_cursor[3], location[3];
copy_v3_v3(location, true_location);
mul_m4_v3(obmat, location);
- ED_view3d_project(region, location, translation_vertex_cursor);
+ ED_view3d_project_v3(region, location, translation_vertex_cursor);
/* Do not draw points behind the view. Z [near, far] is mapped to [-1, 1]. */
if (translation_vertex_cursor[2] <= 1.0f) {
imm_draw_circle_fill_3d(
@@ -1315,6 +1324,13 @@ static bool paint_cursor_context_init(bContext *C,
copy_v3_fl(pcontext->outline_col, 0.8f);
}
+ const bool is_brush_tool = PAINT_brush_tool_poll(C);
+ if (!is_brush_tool) {
+ /* Use a default color for tools that are not brushes. */
+ pcontext->outline_alpha = 0.8f;
+ copy_v3_fl(pcontext->outline_col, 0.8f);
+ }
+
pcontext->is_stroke_active = pcontext->ups->stroke_active;
return true;
@@ -1601,9 +1617,11 @@ static void paint_cursor_draw_3d_view_brush_cursor_inactive(PaintCursorContext *
pcontext->radius);
}
+ const bool is_brush_tool = PAINT_brush_tool_poll(pcontext->C);
+
/* Pose brush updates and rotation origins. */
- if (brush->sculpt_tool == SCULPT_TOOL_POSE) {
+ if (is_brush_tool && brush->sculpt_tool == SCULPT_TOOL_POSE) {
/* Just after switching to the Pose Brush, the active vertex can be the same and the
* cursor won't be tagged to update, so always initialize the preview chain if it is
* null before drawing it. */
@@ -1636,7 +1654,7 @@ static void paint_cursor_draw_3d_view_brush_cursor_inactive(PaintCursorContext *
2);
}
- if (brush->sculpt_tool == SCULPT_TOOL_BOUNDARY) {
+ if (is_brush_tool && brush->sculpt_tool == SCULPT_TOOL_BOUNDARY) {
paint_cursor_preview_boundary_data_update(pcontext, update_previews);
paint_cursor_preview_boundary_data_pivot_draw(pcontext);
}
@@ -1657,17 +1675,18 @@ static void paint_cursor_draw_3d_view_brush_cursor_inactive(PaintCursorContext *
GPU_matrix_mul(pcontext->vc.obact->obmat);
/* Drawing Cursor overlays in 3D object space. */
- if (brush->sculpt_tool == SCULPT_TOOL_GRAB && (brush->flag & BRUSH_GRAB_ACTIVE_VERTEX)) {
+ if (is_brush_tool && brush->sculpt_tool == SCULPT_TOOL_GRAB &&
+ (brush->flag & BRUSH_GRAB_ACTIVE_VERTEX)) {
SCULPT_geometry_preview_lines_update(pcontext->C, pcontext->ss, pcontext->radius);
sculpt_geometry_preview_lines_draw(
pcontext->pos, pcontext->brush, pcontext->is_multires, pcontext->ss);
}
- if (brush->sculpt_tool == SCULPT_TOOL_POSE) {
+ if (is_brush_tool && brush->sculpt_tool == SCULPT_TOOL_POSE) {
paint_cursor_pose_brush_segments_draw(pcontext);
}
- if (brush->sculpt_tool == SCULPT_TOOL_BOUNDARY) {
+ if (is_brush_tool && brush->sculpt_tool == SCULPT_TOOL_BOUNDARY) {
SCULPT_boundary_edges_preview_draw(
pcontext->pos, pcontext->ss, pcontext->outline_col, pcontext->outline_alpha);
SCULPT_boundary_pivot_line_preview_draw(pcontext->pos, pcontext->ss);
@@ -1683,7 +1702,7 @@ static void paint_cursor_draw_3d_view_brush_cursor_inactive(PaintCursorContext *
paint_cursor_draw_main_inactive_cursor(pcontext);
/* Cloth brush local simulation areas. */
- if (brush->sculpt_tool == SCULPT_TOOL_CLOTH &&
+ if (is_brush_tool && brush->sculpt_tool == SCULPT_TOOL_CLOTH &&
brush->cloth_simulation_area_type != BRUSH_CLOTH_SIMULATION_AREA_GLOBAL) {
const float white[3] = {1.0f, 1.0f, 1.0f};
const float zero_v[3] = {0.0f};
@@ -1695,7 +1714,7 @@ static void paint_cursor_draw_3d_view_brush_cursor_inactive(PaintCursorContext *
}
/* Layer brush height. */
- if (brush->sculpt_tool == SCULPT_TOOL_LAYER) {
+ if (is_brush_tool && brush->sculpt_tool == SCULPT_TOOL_LAYER) {
SCULPT_layer_brush_height_preview_draw(pcontext->pos,
brush,
pcontext->radius,
diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c
index 530689ce049..41db79bc134 100644
--- a/source/blender/editors/sculpt_paint/paint_image_proj.c
+++ b/source/blender/editors/sculpt_paint/paint_image_proj.c
@@ -300,7 +300,7 @@ typedef struct ProjPaintState {
/** Calculated from screenMin & screenMax. */
float screen_width;
float screen_height;
- /** from the carea or from the projection render. */
+ /** From the area or from the projection render. */
int winx, winy;
/* options for projection painting */
@@ -5571,7 +5571,7 @@ static bool project_paint_op(void *state, const float lastpos[2], const float po
}
if (ps->thread_tot > 1) {
- task_pool = BLI_task_pool_create_suspended(NULL, TASK_PRIORITY_HIGH);
+ task_pool = BLI_task_pool_create_suspended(NULL, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON);
}
image_pool = BKE_image_pool_new();
diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h
index 3ca0d853d6a..7341d984c91 100644
--- a/source/blender/editors/sculpt_paint/paint_intern.h
+++ b/source/blender/editors/sculpt_paint/paint_intern.h
@@ -87,7 +87,7 @@ struct ViewContext *paint_stroke_view_context(struct PaintStroke *stroke);
void *paint_stroke_mode_data(struct PaintStroke *stroke);
float paint_stroke_distance_get(struct PaintStroke *stroke);
void paint_stroke_set_mode_data(struct PaintStroke *stroke, void *mode_data);
-bool paint_poll(struct bContext *C);
+bool PAINT_brush_tool_poll(struct bContext *C);
void paint_cursor_start(struct Paint *p, bool (*poll)(struct bContext *C));
void paint_cursor_delete_textures(void);
@@ -304,7 +304,7 @@ bool paint_curve_poll(struct bContext *C);
bool facemask_paint_poll(struct bContext *C);
void flip_v3_v3(float out[3], const float in[3], const enum ePaintSymmetryFlags symm);
-void flip_qt_qt(float out[3], const float in[3], const enum ePaintSymmetryFlags symm);
+void flip_qt_qt(float out[4], const float in[4], const enum ePaintSymmetryFlags symm);
/* stroke operator */
typedef enum BrushStrokeMode {
diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c
index 7671f69ee05..da34723eed4 100644
--- a/source/blender/editors/sculpt_paint/paint_mask.c
+++ b/source/blender/editors/sculpt_paint/paint_mask.c
@@ -1239,10 +1239,9 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext)
}));
const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop);
- int tottri;
BMLoop *(*looptris)[3];
looptris = MEM_malloc_arrayN(looptris_tot, sizeof(*looptris), __func__);
- BM_mesh_calc_tessellation_beauty(bm, looptris, &tottri);
+ BM_mesh_calc_tessellation_beauty(bm, looptris);
BMIter iter;
int i;
@@ -1290,7 +1289,7 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext)
break;
}
BM_mesh_boolean(
- bm, looptris, tottri, bm_face_isect_pair, NULL, 2, true, true, false, boolean_mode);
+ bm, looptris, looptris_tot, bm_face_isect_pair, NULL, 2, true, true, false, boolean_mode);
}
MEM_freeN(looptris);
@@ -1650,7 +1649,7 @@ void PAINT_OT_mask_lasso_gesture(wmOperatorType *ot)
ot->modal = WM_gesture_lasso_modal;
ot->exec = paint_mask_gesture_lasso_exec;
- ot->poll = SCULPT_mode_poll;
+ ot->poll = SCULPT_mode_poll_view3d;
ot->flag = OPTYPE_REGISTER;
@@ -1671,7 +1670,7 @@ void PAINT_OT_mask_box_gesture(wmOperatorType *ot)
ot->modal = WM_gesture_box_modal;
ot->exec = paint_mask_gesture_box_exec;
- ot->poll = SCULPT_mode_poll;
+ ot->poll = SCULPT_mode_poll_view3d;
ot->flag = OPTYPE_REGISTER;
@@ -1692,7 +1691,7 @@ void PAINT_OT_mask_line_gesture(wmOperatorType *ot)
ot->modal = WM_gesture_straightline_oneshot_modal;
ot->exec = paint_mask_gesture_line_exec;
- ot->poll = SCULPT_mode_poll;
+ ot->poll = SCULPT_mode_poll_view3d;
ot->flag = OPTYPE_REGISTER;
@@ -1713,6 +1712,8 @@ void SCULPT_OT_face_set_lasso_gesture(wmOperatorType *ot)
ot->modal = WM_gesture_lasso_modal;
ot->exec = face_set_gesture_lasso_exec;
+ ot->poll = SCULPT_mode_poll_view3d;
+
/* Properties. */
WM_operator_properties_gesture_lasso(ot);
sculpt_gesture_operator_properties(ot);
@@ -1728,7 +1729,7 @@ void SCULPT_OT_face_set_box_gesture(wmOperatorType *ot)
ot->modal = WM_gesture_box_modal;
ot->exec = face_set_gesture_box_exec;
- ot->poll = SCULPT_mode_poll;
+ ot->poll = SCULPT_mode_poll_view3d;
ot->flag = OPTYPE_REGISTER;
@@ -1747,7 +1748,7 @@ void SCULPT_OT_trim_lasso_gesture(wmOperatorType *ot)
ot->modal = WM_gesture_lasso_modal;
ot->exec = sculpt_trim_gesture_lasso_exec;
- ot->poll = SCULPT_mode_poll;
+ ot->poll = SCULPT_mode_poll_view3d;
ot->flag = OPTYPE_REGISTER;
@@ -1768,7 +1769,7 @@ void SCULPT_OT_trim_box_gesture(wmOperatorType *ot)
ot->modal = WM_gesture_box_modal;
ot->exec = sculpt_trim_gesture_box_exec;
- ot->poll = SCULPT_mode_poll;
+ ot->poll = SCULPT_mode_poll_view3d;
ot->flag = OPTYPE_REGISTER;
@@ -1789,7 +1790,7 @@ void SCULPT_OT_project_line_gesture(wmOperatorType *ot)
ot->modal = WM_gesture_straightline_oneshot_modal;
ot->exec = project_gesture_line_exec;
- ot->poll = SCULPT_mode_poll;
+ ot->poll = SCULPT_mode_poll_view3d;
ot->flag = OPTYPE_REGISTER;
diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c
index e1dc8fa30b9..fed89e02e8f 100644
--- a/source/blender/editors/sculpt_paint/paint_ops.c
+++ b/source/blender/editors/sculpt_paint/paint_ops.c
@@ -139,13 +139,14 @@ static int brush_scale_size_exec(bContext *C, wmOperator *op)
Scene *scene = CTX_data_scene(C);
Paint *paint = BKE_paint_get_active_from_context(C);
Brush *brush = BKE_paint_brush(paint);
+ const bool is_gpencil = (brush && brush->gpencil_settings != NULL);
// Object *ob = CTX_data_active_object(C);
float scalar = RNA_float_get(op->ptr, "scalar");
if (brush) {
/* pixel radius */
{
- const int old_size = BKE_brush_size_get(scene, brush);
+ const int old_size = (!is_gpencil) ? BKE_brush_size_get(scene, brush) : brush->size;
int size = (int)(scalar * old_size);
if (abs(old_size - size) < U.pixelsize) {
@@ -156,6 +157,12 @@ static int brush_scale_size_exec(bContext *C, wmOperator *op)
size -= U.pixelsize;
}
}
+ /* Grease Pencil does not use unified size. */
+ if (is_gpencil) {
+ brush->size = max_ii(size, 1);
+ WM_main_add_notifier(NC_BRUSH | NA_EDITED, brush);
+ return OPERATOR_FINISHED;
+ }
BKE_brush_size_set(scene, brush, size);
}
diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c
index 57b1102219e..59a5ad63f0e 100644
--- a/source/blender/editors/sculpt_paint/paint_stroke.c
+++ b/source/blender/editors/sculpt_paint/paint_stroke.c
@@ -846,7 +846,7 @@ static int paint_space_stroke(bContext *C,
while (length > 0.0f) {
float spacing = paint_space_stroke_spacing_variable(
C, scene, stroke, pressure, dpressure, length);
- float mouse[3];
+ float mouse[2];
if (length >= spacing) {
if (use_scene_spacing) {
@@ -856,7 +856,7 @@ static int paint_space_stroke(bContext *C,
add_v3_v3v3(final_world_space_position,
stroke->last_world_space_position,
final_world_space_position);
- ED_view3d_project(region, final_world_space_position, mouse);
+ ED_view3d_project_v2(region, final_world_space_position, mouse);
}
else {
mouse[0] = stroke->last_mouse_position[0] + dmouse[0] * spacing;
@@ -1184,14 +1184,43 @@ static void paint_line_strokes_spacing(bContext *C,
const float new_pos[2])
{
UnifiedPaintSettings *ups = stroke->ups;
+ Paint *paint = BKE_paint_get_active_from_context(C);
+ Brush *brush = BKE_paint_brush(paint);
+ ePaintMode mode = BKE_paintmode_get_active_from_context(C);
+ ARegion *region = CTX_wm_region(C);
+
+ const bool use_scene_spacing = paint_stroke_use_scene_spacing(brush, mode);
float mouse[2], dmouse[2];
float length;
+ float d_world_space_position[3] = {0.0f};
+ float world_space_position_old[3], world_space_position_new[3];
- sub_v2_v2v2(dmouse, new_pos, old_pos);
copy_v2_v2(stroke->last_mouse_position, old_pos);
- length = normalize_v2(dmouse);
+ if (use_scene_spacing) {
+ bool hit_old = SCULPT_stroke_get_location(C, world_space_position_old, old_pos);
+ bool hit_new = SCULPT_stroke_get_location(C, world_space_position_new, new_pos);
+ mul_m4_v3(stroke->vc.obact->obmat, world_space_position_old);
+ mul_m4_v3(stroke->vc.obact->obmat, world_space_position_new);
+ if (hit_old && hit_new && stroke->stroke_over_mesh) {
+ sub_v3_v3v3(d_world_space_position, world_space_position_new, world_space_position_old);
+ length = len_v3(d_world_space_position);
+ stroke->stroke_over_mesh = true;
+ }
+ else {
+ length = 0.0f;
+ zero_v3(d_world_space_position);
+ stroke->stroke_over_mesh = hit_new;
+ if (stroke->stroke_over_mesh) {
+ copy_v3_v3(stroke->last_world_space_position, world_space_position_old);
+ }
+ }
+ }
+ else {
+ sub_v2_v2v2(dmouse, new_pos, old_pos);
+ length = normalize_v2(dmouse);
+ }
BLI_assert(length >= 0.0f);
@@ -1205,8 +1234,18 @@ static void paint_line_strokes_spacing(bContext *C,
*length_residue = 0.0;
if (length >= spacing) {
- mouse[0] = stroke->last_mouse_position[0] + dmouse[0] * spacing_final;
- mouse[1] = stroke->last_mouse_position[1] + dmouse[1] * spacing_final;
+ if (use_scene_spacing) {
+ float final_world_space_position[3];
+ normalize_v3(d_world_space_position);
+ mul_v3_v3fl(final_world_space_position, d_world_space_position, spacing_final);
+ add_v3_v3v3(
+ final_world_space_position, world_space_position_old, final_world_space_position);
+ ED_view3d_project_v2(region, final_world_space_position, mouse);
+ }
+ else {
+ mouse[0] = stroke->last_mouse_position[0] + dmouse[0] * spacing_final;
+ mouse[1] = stroke->last_mouse_position[1] + dmouse[1] * spacing_final;
+ }
ups->overlap_factor = paint_stroke_integrate_overlap(stroke->brush, 1.0);
@@ -1302,6 +1341,13 @@ static bool paint_stroke_curve_end(bContext *C, wmOperator *op, PaintStroke *str
if (!stroke->stroke_started) {
stroke->last_pressure = 1.0;
copy_v2_v2(stroke->last_mouse_position, data + 2 * j);
+
+ if (paint_stroke_use_scene_spacing(br, BKE_paintmode_get_active_from_context(C))) {
+ stroke->stroke_over_mesh = SCULPT_stroke_get_location(
+ C, stroke->last_world_space_position, data + 2 * j);
+ mul_m4_v3(stroke->vc.obact->obmat, stroke->last_world_space_position);
+ }
+
stroke->stroke_started = stroke->test_start(C, op, stroke->last_mouse_position);
if (stroke->stroke_started) {
@@ -1416,7 +1462,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
if (paint_supports_smooth_stroke(br, mode)) {
stroke->stroke_cursor = WM_paint_cursor_activate(
- SPACE_TYPE_ANY, RGN_TYPE_ANY, paint_poll, paint_draw_smooth_cursor, stroke);
+ SPACE_TYPE_ANY, RGN_TYPE_ANY, PAINT_brush_tool_poll, paint_draw_smooth_cursor, stroke);
}
stroke->stroke_init = true;
@@ -1443,7 +1489,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
if (br->flag & BRUSH_LINE) {
stroke->stroke_cursor = WM_paint_cursor_activate(
- SPACE_TYPE_ANY, RGN_TYPE_ANY, paint_poll, paint_draw_line_cursor, stroke);
+ SPACE_TYPE_ANY, RGN_TYPE_ANY, PAINT_brush_tool_poll, paint_draw_line_cursor, stroke);
}
first_dab = true;
@@ -1613,7 +1659,7 @@ void paint_stroke_set_mode_data(PaintStroke *stroke, void *mode_data)
stroke->mode_data = mode_data;
}
-bool paint_poll(bContext *C)
+bool PAINT_brush_tool_poll(bContext *C)
{
Paint *p = BKE_paint_get_active_from_context(C);
Object *ob = CTX_data_active_object(C);
diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c
index fc52f6fea7c..daccc6f228a 100644
--- a/source/blender/editors/sculpt_paint/paint_vertex.c
+++ b/source/blender/editors/sculpt_paint/paint_vertex.c
@@ -769,8 +769,8 @@ static void do_weight_paint_vertex_single(
MDeformVert *dv_mirr;
MDeformWeight *dw_mirr;
- /* from now on we can check if mirrors enabled if this var is -1 and not bother with the flag */
- if (me->editflag & ME_EDIT_VERTEX_GROUPS_X_SYMMETRY) {
+ /* Check if we should mirror vertex groups (X-axis). */
+ if (ME_USING_MIRROR_X_VERTEX_GROUPS(me)) {
index_mirr = mesh_get_x_mirror_vert(ob, NULL, index, topology);
vgroup_mirr = wpi->mirror.index;
@@ -979,8 +979,8 @@ static void do_weight_paint_vertex_multi(
float curw, curw_real, oldw, neww, change, curw_mirr, change_mirr;
float dw_rel_free, dw_rel_locked;
- /* from now on we can check if mirrors enabled if this var is -1 and not bother with the flag */
- if (me->editflag & ME_EDIT_VERTEX_GROUPS_X_SYMMETRY) {
+ /* Check if we should mirror vertex groups (X-axis). */
+ if (ME_USING_MIRROR_X_VERTEX_GROUPS(me)) {
index_mirr = mesh_get_x_mirror_vert(ob, NULL, index, topology);
if (!ELEM(index_mirr, -1, index)) {
@@ -1629,7 +1629,7 @@ static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo
int i;
bDeformGroup *dg;
- if (me->editflag & ME_EDIT_VERTEX_GROUPS_X_SYMMETRY) {
+ if (ME_USING_MIRROR_X_VERTEX_GROUPS(me)) {
BKE_object_defgroup_mirror_selection(
ob, defbase_tot, defbase_sel, defbase_sel, &defbase_tot_sel);
}
@@ -2191,7 +2191,7 @@ static void wpaint_paint_leaves(bContext *C,
/* NOTE: current mirroring code cannot be run in parallel */
TaskParallelSettings settings;
- const bool use_threading = ((me->editflag & ME_EDIT_VERTEX_GROUPS_X_SYMMETRY) == 0);
+ const bool use_threading = !ME_USING_MIRROR_X_VERTEX_GROUPS(me);
BKE_pbvh_parallel_range_settings(&settings, use_threading, totnode);
switch ((eBrushWeightPaintTool)brush->weightpaint_tool) {
@@ -2322,6 +2322,13 @@ static void wpaint_do_symmetrical_brush_actions(
cache->symmetry = symm;
+ if (me->editflag & ME_EDIT_MIRROR_VERTEX_GROUPS) {
+ /* We don't do any symmetry strokes when mirroring vertex groups. */
+ copy_v3_v3(cache->true_last_location, cache->true_location);
+ cache->is_last_valid = true;
+ return;
+ }
+
/* symm is a bit combination of XYZ - 1 is mirror
* X; 2 is Y; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */
for (i = 1; i <= symm; i++) {
diff --git a/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c b/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c
index 8277b485578..0fafd3589fe 100644
--- a/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c
+++ b/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c
@@ -235,7 +235,7 @@ static int weight_sample_invoke(bContext *C, wmOperator *op, const wmEvent *even
vc.obact, defbase_tot, &defbase_tot_sel);
if (defbase_tot_sel > 1) {
- if (me->editflag & ME_EDIT_VERTEX_GROUPS_X_SYMMETRY) {
+ if (ME_USING_MIRROR_X_VERTEX_GROUPS(me)) {
BKE_object_defgroup_mirror_selection(
vc.obact, defbase_tot, defbase_sel, defbase_sel, &defbase_tot_sel);
}
@@ -461,7 +461,7 @@ static bool weight_paint_set(Object *ob, float paintweight)
vgroup_active = ob->actdef - 1;
/* if mirror painting, find the other group */
- if (me->editflag & ME_EDIT_VERTEX_GROUPS_X_SYMMETRY) {
+ if (ME_USING_MIRROR_X_VERTEX_GROUPS(me)) {
vgroup_mirror = ED_wpaint_mirror_vgroup_ensure(ob, vgroup_active);
}
@@ -489,7 +489,8 @@ static bool weight_paint_set(Object *ob, float paintweight)
dw_prev->weight = dw->weight; /* set the undo weight */
dw->weight = paintweight;
- if (me->editflag & ME_EDIT_VERTEX_GROUPS_X_SYMMETRY) { /* x mirror painting */
+ if (me->symmetry & ME_SYMMETRY_X) {
+ /* x mirror painting */
int j = mesh_get_x_mirror_vert(ob, NULL, vidx, topology);
if (j >= 0) {
/* copy, not paint again */
diff --git a/source/blender/editors/sculpt_paint/paint_vertex_weight_utils.c b/source/blender/editors/sculpt_paint/paint_vertex_weight_utils.c
index a8ba87ac483..d6a118bbd59 100644
--- a/source/blender/editors/sculpt_paint/paint_vertex_weight_utils.c
+++ b/source/blender/editors/sculpt_paint/paint_vertex_weight_utils.c
@@ -118,7 +118,7 @@ bool ED_wpaint_ensure_data(bContext *C,
}
if (flag & WPAINT_ENSURE_MIRROR) {
- if (me->editflag & ME_EDIT_VERTEX_GROUPS_X_SYMMETRY) {
+ if (ME_USING_MIRROR_X_VERTEX_GROUPS(me)) {
int mirror = ED_wpaint_mirror_vgroup_ensure(ob, ob->actdef - 1);
if (vgroup_index) {
vgroup_index->mirror = mirror;
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index 964e5bdaa90..d6d54a1985d 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -780,6 +780,10 @@ static void sculpt_vertex_neighbors_get_faces(SculptSession *ss,
iter->neighbors = iter->neighbors_fixed;
for (int i = 0; i < ss->pmap[index].count; i++) {
+ if (ss->face_sets[vert_map->indices[i]] < 0) {
+ /* Skip connectivity from hidden faces. */
+ continue;
+ }
const MPoly *p = &ss->mpoly[vert_map->indices[i]];
uint f_adj_v[2];
if (poly_get_adj_loops_from_vert(p, ss->mloop, index, f_adj_v) != -1) {
@@ -1850,7 +1854,7 @@ static void flip_v3(float v[3], const ePaintSymmetryFlags symm)
flip_v3_v3(v, v, symm);
}
-static void flip_qt(float quat[3], const ePaintSymmetryFlags symm)
+static void flip_qt(float quat[4], const ePaintSymmetryFlags symm)
{
flip_qt_qt(quat, quat, symm);
}
@@ -4192,7 +4196,7 @@ void SCULPT_flip_v3_by_symm_area(float v[3],
}
}
-void SCULPT_flip_quat_by_symm_area(float quat[3],
+void SCULPT_flip_quat_by_symm_area(float quat[4],
const ePaintSymmetryFlags symm,
const ePaintSymmetryAreas symmarea,
const float pivot[3])
@@ -6602,7 +6606,7 @@ bool SCULPT_poll_view3d(bContext *C)
bool SCULPT_poll(bContext *C)
{
- return SCULPT_mode_poll(C) && paint_poll(C);
+ return SCULPT_mode_poll(C) && PAINT_brush_tool_poll(C);
}
static const char *sculpt_tool_name(Sculpt *sd)
diff --git a/source/blender/editors/sculpt_paint/sculpt_expand.c b/source/blender/editors/sculpt_paint/sculpt_expand.c
index 8b8ed42a694..40874375772 100644
--- a/source/blender/editors/sculpt_paint/sculpt_expand.c
+++ b/source/blender/editors/sculpt_paint/sculpt_expand.c
@@ -273,7 +273,7 @@ static bool sculpt_expand_state_get(SculptSession *ss, ExpandCache *expand_cache
*/
static bool sculpt_expand_face_state_get(SculptSession *ss, ExpandCache *expand_cache, const int f)
{
- if (ss->face_sets[f] <= 0) {
+ if (expand_cache->original_face_sets[f] <= 0) {
return false;
}
@@ -1398,6 +1398,13 @@ static void sculpt_expand_face_sets_restore(SculptSession *ss, ExpandCache *expa
{
const int totfaces = ss->totfaces;
for (int i = 0; i < totfaces; i++) {
+ if (expand_cache->original_face_sets[i] <= 0) {
+ /* Do not modify hidden Face Sets, even when restoring the IDs state. */
+ continue;
+ }
+ if (!sculpt_expand_is_face_in_active_component(ss, expand_cache, i)) {
+ continue;
+ }
ss->face_sets[i] = expand_cache->initial_face_sets[i];
}
}
@@ -1892,13 +1899,22 @@ static int sculpt_expand_modal(bContext *C, wmOperator *op, const wmEvent *event
* The faces that were using the `delete_id` Face Set are filled
* using the content from their neighbors.
*/
-static void sculpt_expand_delete_face_set_id(
- int *r_face_sets, Mesh *mesh, MeshElemMap *pmap, const int totface, const int delete_id)
+static void sculpt_expand_delete_face_set_id(int *r_face_sets,
+ SculptSession *ss,
+ ExpandCache *expand_cache,
+ Mesh *mesh,
+ const int delete_id)
{
+ const int totface = ss->totfaces;
+ MeshElemMap *pmap = ss->pmap;
+
/* Check that all the face sets IDs in the mesh are not equal to `delete_id`
* before attempting to delete it. */
bool all_same_id = true;
for (int i = 0; i < totface; i++) {
+ if (!sculpt_expand_is_face_in_active_component(ss, expand_cache, i)) {
+ continue;
+ }
if (r_face_sets[i] != delete_id) {
all_same_id = false;
break;
@@ -1921,6 +1937,7 @@ static void sculpt_expand_delete_face_set_id(
}
while (BLI_LINKSTACK_SIZE(queue)) {
+ bool any_updated = false;
while (BLI_LINKSTACK_SIZE(queue)) {
const int f_index = POINTER_AS_INT(BLI_LINKSTACK_POP(queue));
int other_id = delete_id;
@@ -1931,6 +1948,10 @@ static void sculpt_expand_delete_face_set_id(
for (int i = 0; i < vert_map->count; i++) {
const int neighbor_face_index = vert_map->indices[i];
+ if (expand_cache->original_face_sets[neighbor_face_index] <= 0) {
+ /* Skip picking IDs from hidden Face Sets. */
+ continue;
+ }
if (r_face_sets[neighbor_face_index] != delete_id) {
other_id = r_face_sets[neighbor_face_index];
}
@@ -1938,18 +1959,36 @@ static void sculpt_expand_delete_face_set_id(
}
if (other_id != delete_id) {
+ any_updated = true;
r_face_sets[f_index] = other_id;
}
else {
BLI_LINKSTACK_PUSH(queue_next, POINTER_FROM_INT(f_index));
}
}
+ if (!any_updated) {
+ /* No Face Sets where updated in this iteration, which means that no more content to keep
+ * filling the polys of the deleted Face Set was found. Break to avoid entering an infinite
+ * loop trying to search for those polys again. */
+ break;
+ }
BLI_LINKSTACK_SWAP(queue, queue_next);
}
BLI_LINKSTACK_FREE(queue);
BLI_LINKSTACK_FREE(queue_next);
+
+ /* Ensure that the visibility state of the modified Face Sets is the same as the original ones.
+ */
+ for (int i = 0; i < totface; i++) {
+ if (expand_cache->original_face_sets[i] >= 0) {
+ r_face_sets[i] = abs(r_face_sets[i]);
+ }
+ else {
+ r_face_sets[i] = -abs(r_face_sets[i]);
+ }
+ }
}
static void sculpt_expand_cache_initial_config_set(bContext *C,
@@ -2070,9 +2109,9 @@ static int sculpt_expand_invoke(bContext *C, wmOperator *op, const wmEvent *even
if (ss->expand_cache->modify_active_face_set) {
sculpt_expand_delete_face_set_id(ss->expand_cache->initial_face_sets,
+ ss,
+ ss->expand_cache,
ob->data,
- ss->pmap,
- ss->totfaces,
ss->expand_cache->next_face_set);
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.c b/source/blender/editors/sculpt_paint/sculpt_face_set.c
index 17c4beab086..bdbdb75732a 100644
--- a/source/blender/editors/sculpt_paint/sculpt_face_set.c
+++ b/source/blender/editors/sculpt_paint/sculpt_face_set.c
@@ -949,10 +949,6 @@ static int sculpt_face_sets_change_visibility_exec(bContext *C, wmOperator *op)
MEM_SAFE_FREE(nodes);
- if (BKE_pbvh_type(pbvh) == PBVH_FACES) {
- BKE_mesh_flush_hidden_from_verts(ob->data);
- }
-
SCULPT_tag_update_overlays(C);
return OPERATOR_FINISHED;
diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h
index 087cb6dd94a..e2ee4c9fed3 100644
--- a/source/blender/editors/sculpt_paint/sculpt_intern.h
+++ b/source/blender/editors/sculpt_paint/sculpt_intern.h
@@ -278,7 +278,7 @@ void SCULPT_flip_v3_by_symm_area(float v[3],
const ePaintSymmetryFlags symm,
const ePaintSymmetryAreas symmarea,
const float pivot[3]);
-void SCULPT_flip_quat_by_symm_area(float quat[3],
+void SCULPT_flip_quat_by_symm_area(float quat[4],
const ePaintSymmetryFlags symm,
const ePaintSymmetryAreas symmarea,
const float pivot[3]);
diff --git a/source/blender/editors/sculpt_paint/sculpt_pose.c b/source/blender/editors/sculpt_paint/sculpt_pose.c
index 4d2a1bf13dc..587ce346428 100644
--- a/source/blender/editors/sculpt_paint/sculpt_pose.c
+++ b/source/blender/editors/sculpt_paint/sculpt_pose.c
@@ -197,8 +197,9 @@ static void do_pose_brush_task_cb_ex(void *__restrict userdata,
mul_v3_fl(disp, segments[ik].weights[vd.index]);
/* Apply the vertex mask to the displacement. */
- float mask = vd.mask ? *vd.mask : 0.0f;
- mul_v3_fl(disp, 1.0f - mask);
+ const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f;
+ const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index);
+ mul_v3_fl(disp, mask * automask);
/* Accumulate the displacement. */
add_v3_v3(total_disp, disp);
diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c
index b6205db6f45..71166b7c20c 100644
--- a/source/blender/editors/sculpt_paint/sculpt_undo.c
+++ b/source/blender/editors/sculpt_paint/sculpt_undo.c
@@ -1189,9 +1189,7 @@ static SculptUndoNode *sculpt_undo_geometry_push(Object *object, SculptUndoType
static SculptUndoNode *sculpt_undo_face_sets_push(Object *ob, SculptUndoType type)
{
UndoSculpt *usculpt = sculpt_undo_get_nodes();
- SculptUndoNode *unode = usculpt->nodes.first;
-
- unode = MEM_callocN(sizeof(*unode), __func__);
+ SculptUndoNode *unode = MEM_callocN(sizeof(*unode), __func__);
BLI_strncpy(unode->idname, ob->id.name, sizeof(unode->idname));
unode->type = type;
diff --git a/source/blender/editors/sound/sound_ops.c b/source/blender/editors/sound/sound_ops.c
index 85616f6356d..fe0a53ae964 100644
--- a/source/blender/editors/sound/sound_ops.c
+++ b/source/blender/editors/sound/sound_ops.c
@@ -52,6 +52,7 @@
#include "RNA_enum_types.h"
#include "SEQ_iterator.h"
+#include "SEQ_utils.h"
#include "UI_interface.h"
@@ -258,7 +259,7 @@ static void sound_update_animation_flags(Scene *scene)
scene->id.tag |= LIB_TAG_DOIT;
SEQ_ALL_BEGIN (scene->ed, seq) {
- SEQ_iterator_recursive_apply(seq, sound_update_animation_flags_fn, scene);
+ SEQ_recursive_apply(seq, sound_update_animation_flags_fn, scene);
}
SEQ_ALL_END;
diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c
index 5c061518570..fea62f0d9c2 100644
--- a/source/blender/editors/space_action/action_edit.c
+++ b/source/blender/editors/space_action/action_edit.c
@@ -648,6 +648,19 @@ static int actkeys_paste_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
+static char *actkeys_paste_description(bContext *UNUSED(C),
+ wmOperatorType *UNUSED(op),
+ PointerRNA *ptr)
+{
+ /* Custom description if the 'flipped' option is used. */
+ if (RNA_boolean_get(ptr, "flipped")) {
+ return BLI_strdup("Paste keyframes from mirrored bones if they exist");
+ }
+
+ /* Use the default description in the other cases. */
+ return NULL;
+}
+
void ACTION_OT_paste(wmOperatorType *ot)
{
PropertyRNA *prop;
@@ -660,6 +673,7 @@ void ACTION_OT_paste(wmOperatorType *ot)
/* api callbacks */
// ot->invoke = WM_operator_props_popup; // better wait for action redo panel
+ ot->get_description = actkeys_paste_description;
ot->exec = actkeys_paste_exec;
ot->poll = ED_operator_action_active;
diff --git a/source/blender/editors/space_action/space_action.c b/source/blender/editors/space_action/space_action.c
index 722005235d3..f6af2f79890 100644
--- a/source/blender/editors/space_action/space_action.c
+++ b/source/blender/editors/space_action/space_action.c
@@ -301,6 +301,11 @@ static void action_header_region_init(wmWindowManager *UNUSED(wm), ARegion *regi
static void action_header_region_draw(const bContext *C, ARegion *region)
{
+ /* The anim context is not actually used, but this makes sure the action being displayed is up to
+ * date. */
+ bAnimContext ac;
+ ANIM_animdata_get_context(C, &ac);
+
ED_region_header(C, region);
}
diff --git a/source/blender/editors/space_buttons/CMakeLists.txt b/source/blender/editors/space_buttons/CMakeLists.txt
index c71e5e49d8d..b5f6874fcfc 100644
--- a/source/blender/editors/space_buttons/CMakeLists.txt
+++ b/source/blender/editors/space_buttons/CMakeLists.txt
@@ -50,7 +50,7 @@ if(WITH_FREESTYLE)
endif()
if(WITH_EXPERIMENTAL_FEATURES)
- add_definitions(-DWITH_GEOMETRY_NODES)
+ add_definitions(-DWITH_SIMULATION_DATABLOCK)
add_definitions(-DWITH_POINT_CLOUD)
add_definitions(-DWITH_HAIR_NODES)
endif()
diff --git a/source/blender/editors/space_buttons/buttons_context.c b/source/blender/editors/space_buttons/buttons_context.c
index c42e2531f25..aeb2c04656e 100644
--- a/source/blender/editors/space_buttons/buttons_context.c
+++ b/source/blender/editors/space_buttons/buttons_context.c
@@ -975,23 +975,13 @@ int /*eContextResult*/ buttons_context(const bContext *C,
if (matnr < 0) {
matnr = 0;
}
- CTX_data_pointer_set(result, &ob->id, &RNA_MaterialSlot, &ob->mat[matnr]);
+ /* Keep aligned with rna_Object_material_slots_get. */
+ CTX_data_pointer_set(result, &ob->id, &RNA_MaterialSlot, POINTER_FROM_INT(matnr + 1));
}
}
return CTX_RESULT_OK;
}
- if (CTX_data_equals(member, "modifier")) {
- PointerRNA *ptr = get_pointer_type(path, &RNA_Modifier);
-
- if (ptr != NULL && !RNA_pointer_is_null(ptr)) {
- Object *ob = (Object *)ptr->owner_id;
- ModifierData *md = ptr->data;
- CTX_data_pointer_set(result, &ob->id, &RNA_Modifier, md);
- return CTX_RESULT_OK;
- }
- return CTX_RESULT_NO_DATA;
- }
if (CTX_data_equals(member, "texture_user")) {
ButsContextTexture *ct = sbuts->texuser;
diff --git a/source/blender/editors/space_buttons/buttons_intern.h b/source/blender/editors/space_buttons/buttons_intern.h
index 74e7bc11c26..7564fa4b930 100644
--- a/source/blender/editors/space_buttons/buttons_intern.h
+++ b/source/blender/editors/space_buttons/buttons_intern.h
@@ -35,6 +35,7 @@ struct bContext;
struct bContextDataResult;
struct bNode;
struct bNodeTree;
+struct bNodeSocket;
struct wmOperatorType;
struct SpaceProperties_Runtime {
@@ -66,6 +67,7 @@ typedef struct ButsTextureUser {
struct bNodeTree *ntree;
struct bNode *node;
+ struct bNodeSocket *socket;
const char *category;
int icon;
diff --git a/source/blender/editors/space_buttons/buttons_texture.c b/source/blender/editors/space_buttons/buttons_texture.c
index 43128ed00fa..97e3cb750c1 100644
--- a/source/blender/editors/space_buttons/buttons_texture.c
+++ b/source/blender/editors/space_buttons/buttons_texture.c
@@ -75,15 +75,16 @@ static SpaceProperties *find_space_properties(const bContext *C);
/************************* Texture User **************************/
-static void buttons_texture_user_node_property_add(ListBase *users,
- ID *id,
- PointerRNA ptr,
- PropertyRNA *prop,
- bNodeTree *ntree,
- bNode *node,
- const char *category,
- int icon,
- const char *name)
+static void buttons_texture_user_socket_property_add(ListBase *users,
+ ID *id,
+ PointerRNA ptr,
+ PropertyRNA *prop,
+ bNodeTree *ntree,
+ bNode *node,
+ bNodeSocket *socket,
+ const char *category,
+ int icon,
+ const char *name)
{
ButsTextureUser *user = MEM_callocN(sizeof(ButsTextureUser), "ButsTextureUser");
@@ -92,6 +93,7 @@ static void buttons_texture_user_node_property_add(ListBase *users,
user->prop = prop;
user->ntree = ntree;
user->node = node;
+ user->socket = socket;
user->category = category;
user->icon = icon;
user->name = name;
@@ -181,25 +183,29 @@ static void buttons_texture_modifier_geonodes_users_add(Object *ob,
/* Recurse into the node group */
buttons_texture_modifier_geonodes_users_add(ob, nmd, (bNodeTree *)node->id, users);
}
- else if (node->type == GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE) {
- RNA_pointer_create(&node_tree->id, &RNA_Node, node, &ptr);
- prop = RNA_struct_find_property(&ptr, "texture");
- if (prop == NULL) {
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
+ if (socket->flag & SOCK_UNAVAIL) {
+ continue;
+ }
+ if (socket->type != SOCK_TEXTURE) {
continue;
}
+ RNA_pointer_create(&node_tree->id, &RNA_NodeSocket, socket, &ptr);
+ prop = RNA_struct_find_property(&ptr, "default_value");
PointerRNA texptr = RNA_property_pointer_get(&ptr, prop);
Tex *tex = (RNA_struct_is_a(texptr.type, &RNA_Texture)) ? (Tex *)texptr.data : NULL;
if (tex != NULL) {
- buttons_texture_user_node_property_add(users,
- &ob->id,
- ptr,
- prop,
- node_tree,
- node,
- N_("Geometry Nodes"),
- RNA_struct_ui_icon(ptr.type),
- nmd->modifier.name);
+ buttons_texture_user_socket_property_add(users,
+ &ob->id,
+ ptr,
+ prop,
+ node_tree,
+ node,
+ socket,
+ N_("Geometry Nodes"),
+ RNA_struct_ui_icon(ptr.type),
+ nmd->modifier.name);
}
}
}
diff --git a/source/blender/editors/space_clip/clip_buttons.c b/source/blender/editors/space_clip/clip_buttons.c
index d555238e949..7379891543b 100644
--- a/source/blender/editors/space_clip/clip_buttons.c
+++ b/source/blender/editors/space_clip/clip_buttons.c
@@ -809,12 +809,12 @@ void uiTemplateMovieclipInformation(uiLayout *layout,
char str[1024];
size_t ofs = 0;
- ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, TIP_("%d x %d"), width, height);
+ ofs += BLI_snprintf_rlen(str + ofs, sizeof(str) - ofs, TIP_("%d x %d"), width, height);
if (ibuf) {
if (ibuf->rect_float) {
if (ibuf->channels != 4) {
- ofs += BLI_snprintf(
+ ofs += BLI_snprintf_rlen(
str + ofs, sizeof(str) - ofs, TIP_(", %d float channel(s)"), ibuf->channels);
}
else if (ibuf->planes == R_IMF_PLANES_RGBA) {
@@ -837,7 +837,7 @@ void uiTemplateMovieclipInformation(uiLayout *layout,
short frs_sec;
float frs_sec_base;
if (IMB_anim_get_fps(clip->anim, &frs_sec, &frs_sec_base, true)) {
- ofs += BLI_snprintf(
+ ofs += BLI_snprintf_rlen(
str + ofs, sizeof(str) - ofs, TIP_(", %.2f fps"), (float)frs_sec / frs_sec_base);
}
}
diff --git a/source/blender/editors/space_clip/clip_editor.c b/source/blender/editors/space_clip/clip_editor.c
index 2da13646a8b..9aa6a993c13 100644
--- a/source/blender/editors/space_clip/clip_editor.c
+++ b/source/blender/editors/space_clip/clip_editor.c
@@ -904,7 +904,7 @@ static void start_prefetch_threads(MovieClip *clip,
queue.do_update = do_update;
queue.progress = progress;
- TaskPool *task_pool = BLI_task_pool_create(&queue, TASK_PRIORITY_LOW);
+ TaskPool *task_pool = BLI_task_pool_create(&queue, TASK_PRIORITY_LOW, TASK_ISOLATION_ON);
for (int i = 0; i < tot_thread; i++) {
BLI_task_pool_push(task_pool, prefetch_task_func, clip, false, NULL);
}
diff --git a/source/blender/editors/space_clip/clip_ops.c b/source/blender/editors/space_clip/clip_ops.c
index 0c54a042a1a..369a6e7c944 100644
--- a/source/blender/editors/space_clip/clip_ops.c
+++ b/source/blender/editors/space_clip/clip_ops.c
@@ -1235,10 +1235,8 @@ static void do_movie_proxy(void *pjv,
float *progress)
{
ProxyJob *pj = pjv;
- Scene *scene = pj->scene;
MovieClip *clip = pj->clip;
struct MovieDistortion *distortion = NULL;
- int cfra, sfra = SFRA, efra = EFRA;
if (pj->index_context) {
IMB_anim_index_rebuild(pj->index_context, stop, do_update, progress);
@@ -1252,8 +1250,8 @@ static void do_movie_proxy(void *pjv,
return;
}
- sfra = 1;
- efra = clip->len;
+ const int sfra = 1;
+ const int efra = clip->len;
if (build_undistort_count) {
int threads = BLI_system_thread_count();
@@ -1265,7 +1263,7 @@ static void do_movie_proxy(void *pjv,
BKE_tracking_distortion_set_threads(distortion, threads);
}
- for (cfra = sfra; cfra <= efra; cfra++) {
+ for (int cfra = sfra; cfra <= efra; cfra++) {
BKE_movieclip_build_proxy_frame(
clip, pj->clip_flag, distortion, cfra, build_undistort_sizes, build_undistort_count, 1);
@@ -1431,7 +1429,7 @@ static void do_sequence_proxy(void *pjv,
queue.do_update = do_update;
queue.progress = progress;
- TaskPool *task_pool = BLI_task_pool_create(&queue, TASK_PRIORITY_LOW);
+ TaskPool *task_pool = BLI_task_pool_create(&queue, TASK_PRIORITY_LOW, TASK_ISOLATION_ON);
handles = MEM_callocN(sizeof(ProxyThread) * tot_thread, "proxy threaded handles");
for (int i = 0; i < tot_thread; i++) {
ProxyThread *handle = &handles[i];
diff --git a/source/blender/editors/space_clip/tracking_ops_track.c b/source/blender/editors/space_clip/tracking_ops_track.c
index 9882304d97d..0a99d1020f6 100644
--- a/source/blender/editors/space_clip/tracking_ops_track.c
+++ b/source/blender/editors/space_clip/tracking_ops_track.c
@@ -24,8 +24,11 @@
#include "MEM_guardedalloc.h"
#include "BLI_math.h"
+#include "BLI_string.h"
#include "BLI_utildefines.h"
+#include "BLT_translation.h"
+
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_main.h"
@@ -398,6 +401,28 @@ static int track_markers_modal(bContext *C, wmOperator *UNUSED(op), const wmEven
return OPERATOR_PASS_THROUGH;
}
+static char *track_markers_desc(bContext *UNUSED(C), wmOperatorType *UNUSED(op), PointerRNA *ptr)
+{
+ const bool backwards = RNA_boolean_get(ptr, "backwards");
+ const bool sequence = RNA_boolean_get(ptr, "sequence");
+
+ if (backwards && sequence) {
+ return BLI_strdup(TIP_("Track the selected markers backward for the entire clip"));
+ }
+ if (backwards && !sequence) {
+ return BLI_strdup(TIP_("Track the selected markers backward by one frame"));
+ }
+ if (!backwards && sequence) {
+ return BLI_strdup(TIP_("Track the selected markers forward for the entire clip"));
+ }
+ if (!backwards && !sequence) {
+ return BLI_strdup(TIP_("Track the selected markers forward by one frame"));
+ }
+
+ /* Use default description. */
+ return NULL;
+}
+
void CLIP_OT_track_markers(wmOperatorType *ot)
{
/* identifiers */
@@ -410,6 +435,7 @@ void CLIP_OT_track_markers(wmOperatorType *ot)
ot->invoke = track_markers_invoke;
ot->modal = track_markers_modal;
ot->poll = ED_space_clip_tracking_poll;
+ ot->get_description = track_markers_desc;
/* flags */
ot->flag = OPTYPE_UNDO;
diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c
index c1dcf2e56d3..17d029f7541 100644
--- a/source/blender/editors/space_file/file_draw.c
+++ b/source/blender/editors/space_file/file_draw.c
@@ -142,7 +142,8 @@ static void draw_tile(int sx, int sy, int width, int height, int colorid, int sh
color);
}
-static void file_draw_icon(uiBlock *block,
+static void file_draw_icon(const SpaceFile *sfile,
+ uiBlock *block,
const FileDirEntry *file,
const char *path,
int sx,
@@ -177,10 +178,14 @@ static void file_draw_icon(uiBlock *block,
ImBuf *preview_image = filelist_file_getimage(file);
char blend_path[FILE_MAX_LIBEXTRA];
if (BLO_library_path_explode(path, blend_path, NULL, NULL)) {
+ const FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile);
+ BLI_assert(asset_params != NULL);
+
UI_but_drag_set_asset(but,
file->name,
BLI_strdup(blend_path),
file->blentype,
+ asset_params->import_type,
icon,
preview_image,
UI_DPI_FAC);
@@ -299,7 +304,8 @@ void file_calc_previews(const bContext *C, ARegion *region)
UI_view2d_totRect_set(v2d, sfile->layout->width, sfile->layout->height);
}
-static void file_draw_preview(uiBlock *block,
+static void file_draw_preview(const SpaceFile *sfile,
+ uiBlock *block,
const FileDirEntry *file,
const char *path,
int sx,
@@ -323,6 +329,7 @@ static void file_draw_preview(uiBlock *block,
int ex, ey;
bool show_outline = !is_icon &&
(file->typeflag & (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_BLENDER));
+ const bool is_offline = (file->attributes & FILE_ATTR_OFFLINE);
BLI_assert(imb != NULL);
@@ -419,14 +426,14 @@ static void file_draw_preview(uiBlock *block,
icon_x, icon_y, icon, icon_aspect / U.dpi_fac, icon_opacity, 0.0f, icon_color, false);
}
- if (is_link) {
- /* Arrow icon to indicate it is a shortcut, link, or alias. */
+ if (is_link || is_offline) {
+ /* Icon at bottom to indicate it is a shortcut, link, alias, or offline. */
float icon_x, icon_y;
icon_x = xco + (2.0f * UI_DPI_FAC);
icon_y = yco + (2.0f * UI_DPI_FAC);
- const int arrow = ICON_LOOP_FORWARDS;
+ const int arrow = is_link ? ICON_LOOP_FORWARDS : ICON_URL;
if (!is_icon) {
- /* Arrow at very bottom-left if preview style. */
+ /* At very bottom-left if preview style. */
const uchar dark[4] = {0, 0, 0, 255};
const uchar light[4] = {255, 255, 255, 255};
UI_icon_draw_ex(icon_x + 1, icon_y - 1, arrow, 1.0f / U.dpi_fac, 0.2f, 0.0f, dark, false);
@@ -483,9 +490,19 @@ static void file_draw_preview(uiBlock *block,
/* path is no more static, cannot give it directly to but... */
else if (file->typeflag & FILE_TYPE_ASSET) {
char blend_path[FILE_MAX_LIBEXTRA];
+
if (BLO_library_path_explode(path, blend_path, NULL, NULL)) {
- UI_but_drag_set_asset(
- but, file->name, BLI_strdup(blend_path), file->blentype, icon, imb, scale);
+ const FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile);
+ BLI_assert(asset_params != NULL);
+
+ UI_but_drag_set_asset(but,
+ file->name,
+ BLI_strdup(blend_path),
+ file->blentype,
+ asset_params->import_type,
+ icon,
+ imb,
+ scale);
}
}
else {
@@ -924,7 +941,8 @@ void file_draw_list(const bContext *C, ARegion *region)
is_icon = 1;
}
- file_draw_preview(block,
+ file_draw_preview(sfile,
+ block,
file,
path,
sx,
@@ -939,7 +957,8 @@ void file_draw_list(const bContext *C, ARegion *region)
is_link);
}
else {
- file_draw_icon(block,
+ file_draw_icon(sfile,
+ block,
file,
path,
sx,
diff --git a/source/blender/editors/space_file/file_intern.h b/source/blender/editors/space_file/file_intern.h
index 309b280177c..f1d0197b9ae 100644
--- a/source/blender/editors/space_file/file_intern.h
+++ b/source/blender/editors/space_file/file_intern.h
@@ -63,6 +63,7 @@ void FILE_OT_bookmark_move(struct wmOperatorType *ot);
void FILE_OT_reset_recent(wmOperatorType *ot);
void FILE_OT_hidedot(struct wmOperatorType *ot);
void FILE_OT_execute(struct wmOperatorType *ot);
+void FILE_OT_mouse_execute(struct wmOperatorType *ot);
void FILE_OT_cancel(struct wmOperatorType *ot);
void FILE_OT_parent(struct wmOperatorType *ot);
void FILE_OT_directory_new(struct wmOperatorType *ot);
diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c
index 856bd5b1bc3..7c14f4659eb 100644
--- a/source/blender/editors/space_file/file_ops.c
+++ b/source/blender/editors/space_file/file_ops.c
@@ -536,6 +536,14 @@ void FILE_OT_select_box(wmOperatorType *ot)
/** \name Select Pick Operator
* \{ */
+static rcti file_select_mval_to_select_rect(const int mval[2])
+{
+ rcti rect;
+ rect.xmin = rect.xmax = mval[0];
+ rect.ymin = rect.ymax = mval[1];
+ return rect;
+}
+
static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
ARegion *region = CTX_wm_region(C);
@@ -551,8 +559,7 @@ static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
return OPERATOR_CANCELLED;
}
- rect.xmin = rect.xmax = event->mval[0];
- rect.ymin = rect.ymax = event->mval[1];
+ rect = file_select_mval_to_select_rect(event->mval);
if (!ED_fileselect_layout_is_inside_pt(sfile->layout, &region->v2d, rect.xmin, rect.ymin)) {
return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
@@ -1711,14 +1718,14 @@ bool file_draw_check_exists(SpaceFile *sfile)
/** \name Execute File Window Operator
* \{ */
-static int file_exec(bContext *C, wmOperator *exec_op)
+/**
+ * Execute the active file, as set in the file select params.
+ */
+static bool file_execute(bContext *C, SpaceFile *sfile)
{
Main *bmain = CTX_data_main(C);
- wmWindowManager *wm = CTX_wm_manager(C);
- SpaceFile *sfile = CTX_wm_space_file(C);
FileSelectParams *params = ED_fileselect_get_active_params(sfile);
- struct FileDirEntry *file = filelist_file(sfile->files, params->active_file);
- char filepath[FILE_MAX];
+ FileDirEntry *file = filelist_file(sfile->files, params->active_file);
if (file && file->redirection_path) {
/* redirection_path is an absolute path that takes precedence
@@ -1737,7 +1744,7 @@ static int file_exec(bContext *C, wmOperator *exec_op)
/* directory change */
if (file && (file->typeflag & FILE_TYPE_DIR)) {
if (!file->relpath) {
- return OPERATOR_CANCELLED;
+ return false;
}
if (FILENAME_IS_PARENT(file->relpath)) {
@@ -1753,22 +1760,7 @@ static int file_exec(bContext *C, wmOperator *exec_op)
/* opening file - sends events now, so things get handled on windowqueue level */
else if (sfile->op) {
wmOperator *op = sfile->op;
-
- /* When used as a macro, for double-click, to prevent closing when double-clicking on item. */
- if (RNA_boolean_get(exec_op->ptr, "need_active")) {
- const int numfiles = filelist_files_ensure(sfile->files);
- int i, active = 0;
-
- for (i = 0; i < numfiles; i++) {
- if (filelist_entry_select_index_get(sfile->files, i, CHECK_ALL)) {
- active = 1;
- break;
- }
- }
- if (active == 0) {
- return OPERATOR_CANCELLED;
- }
- }
+ char filepath[FILE_MAX];
sfile->op = NULL;
@@ -1788,50 +1780,94 @@ static int file_exec(bContext *C, wmOperator *exec_op)
BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL),
BLENDER_BOOKMARK_FILE);
fsmenu_write_file(ED_fsmenu_get(), filepath);
- WM_event_fileselect_event(wm, op, EVT_FILESELECT_EXEC);
+ WM_event_fileselect_event(CTX_wm_manager(C), op, EVT_FILESELECT_EXEC);
}
- return OPERATOR_FINISHED;
+ return true;
}
-static int file_exec_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+static int file_exec(bContext *C, wmOperator *UNUSED(op))
{
- ARegion *region = CTX_wm_region(C);
SpaceFile *sfile = CTX_wm_space_file(C);
- if (!ED_fileselect_layout_is_inside_pt(
- sfile->layout, &region->v2d, event->mval[0], event->mval[1])) {
- return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
+ if (!file_execute(C, sfile)) {
+ return OPERATOR_CANCELLED;
}
- return file_exec(C, op);
+ return OPERATOR_FINISHED;
}
void FILE_OT_execute(struct wmOperatorType *ot)
{
- PropertyRNA *prop;
-
/* identifiers */
ot->name = "Execute File Window";
ot->description = "Execute selected file";
ot->idname = "FILE_OT_execute";
/* api callbacks */
- ot->invoke = file_exec_invoke;
ot->exec = file_exec;
/* Important since handler is on window level.
*
* Avoid using #file_operator_poll since this is also used for entering directories
* which is used even when the file manager doesn't have an operator. */
ot->poll = ED_operator_file_active;
+}
- /* properties */
- prop = RNA_def_boolean(ot->srna,
- "need_active",
- 0,
- "Need Active",
- "Only execute if there's an active selected file in the file list");
- RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+/**
+ * \returns false if the mouse doesn't hover a selectable item.
+ */
+static bool file_ensure_hovered_is_active(bContext *C, const wmEvent *event)
+{
+ rcti rect = file_select_mval_to_select_rect(event->mval);
+ if (file_select(C, &rect, FILE_SEL_ADD, false, false) == FILE_SELECT_NOTHING) {
+ return false;
+ }
+
+ return true;
+}
+
+static int file_execute_mouse_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
+{
+ ARegion *region = CTX_wm_region(C);
+ SpaceFile *sfile = CTX_wm_space_file(C);
+
+ if (!ED_fileselect_layout_is_inside_pt(
+ sfile->layout, &region->v2d, event->mval[0], event->mval[1])) {
+ return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
+ }
+
+ /* Note that this isn't needed practically, because the keymap already activates the hovered item
+ * on mouse-press. This execute operator is called afterwards on the double-click event then.
+ * However relying on this would be fragile and could break with keymap changes, so better to
+ * have this mouse-execute operator that makes sure once more that the hovered file is active. */
+ if (!file_ensure_hovered_is_active(C, event)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ if (!file_execute(C, sfile)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+/**
+ * Variation of #FILE_OT_execute that accounts for some mouse specific handling. Otherwise calls
+ * the same logic.
+ */
+void FILE_OT_mouse_execute(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Execute File";
+ ot->description =
+ "Perform the current execute action for the file under the cursor (e.g. open the file)";
+ ot->idname = "FILE_OT_mouse_execute";
+
+ /* api callbacks */
+ ot->invoke = file_execute_mouse_invoke;
+ ot->poll = ED_operator_file_active;
+
+ ot->flag = OPTYPE_INTERNAL;
}
/** \} */
@@ -2227,23 +2263,24 @@ void FILE_OT_filepath_drop(wmOperatorType *ot)
* \{ */
/**
- * Create a new, non-existing folder name, returns 1 if successful, 0 if name couldn't be created.
+ * Create a new, non-existing folder name, returns true if successful,
+ * false if name couldn't be created.
* The actual name is returned in 'name', 'folder' contains the complete path,
* including the new folder name.
*/
-static int new_folder_path(const char *parent, char *folder, char *name)
+static bool new_folder_path(const char *parent, char folder[FILE_MAX], char name[FILE_MAXFILE])
{
int i = 1;
int len = 0;
BLI_strncpy(name, "New Folder", FILE_MAXFILE);
- BLI_join_dirfile(folder, FILE_MAX, parent, name); /* XXX, not real length */
+ BLI_join_dirfile(folder, FILE_MAX, parent, name);
/* check whether folder with the name already exists, in this case
* add number to the name. Check length of generated name to avoid
* crazy case of huge number of folders each named 'New Folder (x)' */
while (BLI_exists(folder) && (len < FILE_MAXFILE)) {
len = BLI_snprintf(name, FILE_MAXFILE, "New Folder(%d)", i);
- BLI_join_dirfile(folder, FILE_MAX, parent, name); /* XXX, not real length */
+ BLI_join_dirfile(folder, FILE_MAX, parent, name);
i++;
}
diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c
index 4c9f80bfa64..8ea44e5c3ee 100644
--- a/source/blender/editors/space_file/filelist.c
+++ b/source/blender/editors/space_file/filelist.c
@@ -1553,7 +1553,8 @@ static void filelist_cache_preview_freef(TaskPool *__restrict UNUSED(pool), void
static void filelist_cache_preview_ensure_running(FileListEntryCache *cache)
{
if (!cache->previews_pool) {
- cache->previews_pool = BLI_task_pool_create_background(cache, TASK_PRIORITY_LOW);
+ cache->previews_pool = BLI_task_pool_create_background(
+ cache, TASK_PRIORITY_LOW, TASK_ISOLATION_ON);
cache->previews_done = BLI_thread_queue_init();
IMB_thumb_locks_acquire();
@@ -1601,37 +1602,51 @@ static void filelist_cache_previews_push(FileList *filelist, FileDirEntry *entry
BLI_assert(cache->flags & FLC_PREVIEWS_ACTIVE);
- if (!entry->preview_icon_id && !(entry->flags & FILE_ENTRY_INVALID_PREVIEW) &&
- (entry->typeflag & (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_FTFONT |
- FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP | FILE_TYPE_BLENDERLIB))) {
- FileListEntryPreview *preview = MEM_mallocN(sizeof(*preview), __func__);
- FileListInternEntry *intern_entry = filelist->filelist_intern.filtered[index];
+ if (!entry->preview_icon_id && (entry->attributes & FILE_ATTR_OFFLINE)) {
+ entry->flags |= FILE_ENTRY_INVALID_PREVIEW;
+ return;
+ }
- if (entry->redirection_path) {
- BLI_strncpy(preview->path, entry->redirection_path, FILE_MAXDIR);
- }
- else {
- BLI_join_dirfile(
- preview->path, sizeof(preview->path), filelist->filelist.root, entry->relpath);
- }
+ if (entry->preview_icon_id) {
+ return;
+ }
- preview->index = index;
- preview->flags = entry->typeflag;
- preview->in_memory_preview = intern_entry->local_data.preview_image;
- preview->icon_id = 0;
- // printf("%s: %d - %s - %p\n", __func__, preview->index, preview->path, preview->img);
+ if (entry->flags & FILE_ENTRY_INVALID_PREVIEW) {
+ return;
+ }
+
+ if (!(entry->typeflag & (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_FTFONT |
+ FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP | FILE_TYPE_BLENDERLIB))) {
+ return;
+ }
- filelist_cache_preview_ensure_running(cache);
+ FileListEntryPreview *preview = MEM_mallocN(sizeof(*preview), __func__);
+ FileListInternEntry *intern_entry = filelist->filelist_intern.filtered[index];
- FileListEntryPreviewTaskData *preview_taskdata = MEM_mallocN(sizeof(*preview_taskdata),
- __func__);
- preview_taskdata->preview = preview;
- BLI_task_pool_push(cache->previews_pool,
- filelist_cache_preview_runf,
- preview_taskdata,
- true,
- filelist_cache_preview_freef);
+ if (entry->redirection_path) {
+ BLI_strncpy(preview->path, entry->redirection_path, FILE_MAXDIR);
+ }
+ else {
+ BLI_join_dirfile(
+ preview->path, sizeof(preview->path), filelist->filelist.root, entry->relpath);
}
+
+ preview->index = index;
+ preview->flags = entry->typeflag;
+ preview->in_memory_preview = intern_entry->local_data.preview_image;
+ preview->icon_id = 0;
+ // printf("%s: %d - %s - %p\n", __func__, preview->index, preview->path, preview->img);
+
+ filelist_cache_preview_ensure_running(cache);
+
+ FileListEntryPreviewTaskData *preview_taskdata = MEM_mallocN(sizeof(*preview_taskdata),
+ __func__);
+ preview_taskdata->preview = preview;
+ BLI_task_pool_push(cache->previews_pool,
+ filelist_cache_preview_runf,
+ preview_taskdata,
+ true,
+ filelist_cache_preview_freef);
}
static void filelist_cache_init(FileListEntryCache *cache, size_t cache_size)
@@ -2360,17 +2375,19 @@ bool filelist_file_cache_block(struct FileList *filelist, const int index)
// printf("Re-queueing previews...\n");
- /* Note we try to preview first images around given index - i.e. assumed visible ones. */
if (cache->flags & FLC_PREVIEWS_ACTIVE) {
- for (i = 0; ((index + i) < end_index) || ((index - i) >= start_index); i++) {
- if ((index - i) >= start_index) {
- const int idx = (cache->block_cursor + (index - start_index) - i) % cache_size;
- filelist_cache_previews_push(filelist, cache->block_entries[idx], index - i);
- }
- if ((index + i) < end_index) {
- const int idx = (cache->block_cursor + (index - start_index) + i) % cache_size;
- filelist_cache_previews_push(filelist, cache->block_entries[idx], index + i);
- }
+ /* Note we try to preview first images around given index - i.e. assumed visible ones. */
+ int block_index = cache->block_cursor + (index - start_index);
+ int offs_max = max_ii(end_index - index, index - start_index);
+ for (i = 0; i <= offs_max; i++) {
+ int offs = i;
+ do {
+ int offs_idx = index + offs;
+ if (start_index <= offs_idx && offs_idx < end_index) {
+ int offs_block_idx = (block_index + offs) % (int)cache_size;
+ filelist_cache_previews_push(filelist, cache->block_entries[offs_block_idx], offs_idx);
+ }
+ } while ((offs = -offs) < 0); /* Switch between negative and positive offset. */
}
}
@@ -3017,8 +3034,7 @@ static void filelist_readjob_main_recursive(Main *bmain, FileList *filelist)
}
}
- /* XXX TODO: if databrowse F4 or append/link
- * filelist->flags & FLF_HIDE_PARENT has to be set */
+ /* XXX TODO: if data-browse or append/link #FLF_HIDE_PARENT has to be set. */
if (!(filelist->filter_data.flags & FLF_HIDE_PARENT)) {
filelist->filelist.nbr_entries++;
}
diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c
index 038b9c11bca..8e3fc36aa71 100644
--- a/source/blender/editors/space_file/filesel.c
+++ b/source/blender/editors/space_file/filesel.c
@@ -120,6 +120,7 @@ static void fileselect_ensure_updated_asset_params(SpaceFile *sfile)
asset_params->base_params.details_flags = U_default.file_space_data.details_flags;
asset_params->asset_library.type = FILE_ASSET_LIBRARY_LOCAL;
asset_params->asset_library.custom_library_index = -1;
+ asset_params->import_type = FILE_ASSET_IMPORT_APPEND;
}
FileSelectParams *base_params = &asset_params->base_params;
diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c
index 993b1d9b69c..12bc0a68ca6 100644
--- a/source/blender/editors/space_file/space_file.c
+++ b/source/blender/editors/space_file/space_file.c
@@ -467,6 +467,15 @@ static void file_listener(const wmSpaceTypeListenerParams *params)
break;
}
break;
+ case NC_ID: {
+ switch (wmn->action) {
+ case NA_RENAME:
+ /* Force list to update sorting (with a full reset for now). */
+ file_reset_filelist_showing_main_data(area, sfile);
+ break;
+ }
+ break;
+ }
case NC_ASSET: {
switch (wmn->action) {
case NA_SELECTED:
@@ -654,6 +663,7 @@ static void file_operatortypes(void)
WM_operatortype_append(FILE_OT_highlight);
WM_operatortype_append(FILE_OT_sort_column_ui_context);
WM_operatortype_append(FILE_OT_execute);
+ WM_operatortype_append(FILE_OT_mouse_execute);
WM_operatortype_append(FILE_OT_cancel);
WM_operatortype_append(FILE_OT_parent);
WM_operatortype_append(FILE_OT_previous);
diff --git a/source/blender/editors/space_graph/graph_edit.c b/source/blender/editors/space_graph/graph_edit.c
index 31f606e515d..a2e812a4ed1 100644
--- a/source/blender/editors/space_graph/graph_edit.c
+++ b/source/blender/editors/space_graph/graph_edit.c
@@ -578,6 +578,19 @@ static int graphkeys_paste_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
+static char *graphkeys_paste_description(bContext *UNUSED(C),
+ wmOperatorType *UNUSED(op),
+ PointerRNA *ptr)
+{
+ /* Custom description if the 'flipped' option is used. */
+ if (RNA_boolean_get(ptr, "flipped")) {
+ return BLI_strdup("Paste keyframes from mirrored bones if they exist");
+ }
+
+ /* Use the default description in the other cases. */
+ return NULL;
+}
+
void GRAPH_OT_paste(wmOperatorType *ot)
{
PropertyRNA *prop;
@@ -592,6 +605,7 @@ void GRAPH_OT_paste(wmOperatorType *ot)
/* API callbacks */
// ot->invoke = WM_operator_props_popup; /* better wait for graph redo panel */
+ ot->get_description = graphkeys_paste_description;
ot->exec = graphkeys_paste_exec;
ot->poll = graphop_editable_keyframes_poll;
diff --git a/source/blender/editors/space_graph/graph_select.c b/source/blender/editors/space_graph/graph_select.c
index 4ab4ef518fb..068ee177d59 100644
--- a/source/blender/editors/space_graph/graph_select.c
+++ b/source/blender/editors/space_graph/graph_select.c
@@ -512,16 +512,19 @@ static rctf initialize_box_select_coords(const bAnimContext *ac, const rctf *rec
return rectf;
}
-static ListBase initialize_box_select_anim_data(const SpaceGraph *sipo, bAnimContext *ac)
+static int initialize_animdata_selection_filter(const SpaceGraph *sipo)
{
- ListBase anim_data = {NULL, NULL};
-
int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
if (sipo->flag & SIPO_SELCUVERTSONLY) {
filter |= ANIMFILTER_FOREDIT | ANIMFILTER_SELEDIT;
}
- ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
+ return filter;
+}
+static ListBase initialize_box_select_anim_data(const int filter, bAnimContext *ac)
+{
+ ListBase anim_data = {NULL, NULL};
+ ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
return anim_data;
}
@@ -573,8 +576,11 @@ static void initialize_box_select_key_editing_data(const SpaceGraph *sipo,
* which means that they may be inadvertently moved as well. However, incl_handles overrides
* this, and allow handles to be considered independently too.
* Also, for convenience, handles should get same status as keyframe (if it was within bounds).
+ *
+ * This function returns true if there was any change in the selection of a key (selecting or
+ * deselecting any key returns true, otherwise it returns false).
*/
-static void box_select_graphkeys(bAnimContext *ac,
+static bool box_select_graphkeys(bAnimContext *ac,
const rctf *rectf_view,
short mode,
short selectmode,
@@ -583,7 +589,8 @@ static void box_select_graphkeys(bAnimContext *ac,
{
const rctf rectf = initialize_box_select_coords(ac, rectf_view);
SpaceGraph *sipo = (SpaceGraph *)ac->sl;
- ListBase anim_data = initialize_box_select_anim_data(sipo, ac);
+ const int filter = initialize_animdata_selection_filter(sipo);
+ ListBase anim_data = initialize_box_select_anim_data(filter, ac);
rctf scaled_rectf;
KeyframeEditData ked;
int mapping_flag;
@@ -597,6 +604,9 @@ static void box_select_graphkeys(bAnimContext *ac,
/* Try selecting the keyframes. */
bAnimListElem *ale = NULL;
+ /* This variable will be set to true if any key is selected or deselected. */
+ bool any_key_selection_changed = false;
+
/* First loop over data, doing box select. try selecting keys only. */
for (ale = anim_data.first; ale; ale = ale->next) {
AnimData *adt = ANIM_nla_mapping_get(ac, ale);
@@ -634,7 +644,7 @@ static void box_select_graphkeys(bAnimContext *ac,
if (ANIM_fcurve_keyframes_loop(&ked, fcu, NULL, ok_cb, NULL)) {
/* select keyframes that are in the appropriate places */
ANIM_fcurve_keyframes_loop(&ked, fcu, ok_cb, select_cb, NULL);
-
+ any_key_selection_changed = true;
/* Only change selection of channel when the visibility of keyframes
* doesn't depend on this. */
if ((sipo->flag & SIPO_SELCUVERTSONLY) == 0) {
@@ -653,6 +663,125 @@ static void box_select_graphkeys(bAnimContext *ac,
/* Cleanup. */
ANIM_animdata_freelist(&anim_data);
+
+ return any_key_selection_changed;
+}
+
+/* This function is used to set all the keyframes of a given curve as selectable
+ * by the "select_cb" function inside of "box_select_graphcurves".
+ */
+static short ok_bezier_always_ok(KeyframeEditData *UNUSED(ked), BezTriple *UNUSED(bezt))
+{
+ return KEYFRAME_OK_KEY | KEYFRAME_OK_H1 | KEYFRAME_OK_H2;
+}
+
+/* Checks whether the given rectangle intersects the given fcurve's calculated curve (i.e. not
+ * only keyframes, but also all the interpolated values). This is done by sampling the curve at
+ * different points between the xmin and the xmax of the rectangle.
+ */
+static bool rectf_curve_intersection(
+ const float offset, const float unit_scale, const rctf *rectf, AnimData *adt, FCurve *fcu)
+{
+ /* 30 sampling points. This worked well in tests. */
+ const float num_steps = 30.0f;
+ const float step = (rectf->xmax - rectf->xmin) / num_steps;
+
+ /* Remap the range at which to evaluate the fcurves. This enables us to avoid remapping
+ * the keys themselves. */
+ const float mapped_max = BKE_nla_tweakedit_remap(adt, rectf->xmax, NLATIME_CONVERT_UNMAP);
+ const float mapped_min = BKE_nla_tweakedit_remap(adt, rectf->xmin, NLATIME_CONVERT_UNMAP);
+ const float eval_step = (mapped_max - mapped_min) / num_steps;
+
+ float x = rectf->xmin;
+ float eval_x = mapped_min;
+ /* Sample points on the given fcurve in the interval defined by the
+ * mapped_min and mapped_max of the selected rectangle.
+ * For each point, check if it is inside of the selection box. If it is, then select
+ * all the keyframes of the curve, the curve, and stop the loop.
+ */
+ while (x < rectf->xmax) {
+ const float fcurve_y = (evaluate_fcurve(fcu, eval_x) + offset) * unit_scale;
+ /* Since rectf->xmin <= x < rectf->xmax is always true, there is no need to keep comparing the
+ * X-coordinate to the rectangle in every iteration. Therefore we do the comparisons manually
+ * instead of using BLI_rctf_isect_pt_v(rectf, current_point).
+ */
+ if (rectf->ymin <= fcurve_y && fcurve_y <= rectf->ymax) {
+ return true;
+ }
+ x += step;
+ eval_x += eval_step;
+ }
+ return false;
+}
+
+/* Perform a box selection of the curves themselves. This means this function tries
+ * to select a curve by sampling it at various points instead of trying to select the
+ * keyframes directly.
+ * The selection actions done to a curve are actually done on all the keyframes of the curve.
+ * Note: This function is only called if no keyframe is in the selection area.
+ */
+static void box_select_graphcurves(bAnimContext *ac,
+ const rctf *rectf_view,
+ const short mode,
+ const short selectmode,
+ const bool incl_handles,
+ void *data)
+{
+ const SpaceGraph *sipo = (SpaceGraph *)ac->sl;
+ const int filter = initialize_animdata_selection_filter(sipo);
+ ListBase anim_data = initialize_box_select_anim_data(filter, ac);
+ rctf scaled_rectf;
+ KeyframeEditData ked;
+ int mapping_flag;
+ initialize_box_select_key_editing_data(
+ sipo, incl_handles, mode, ac, data, &scaled_rectf, &ked, &mapping_flag);
+
+ FCurve *last_selected_curve = NULL;
+
+ /* Go through all the curves and try selecting them. This function is only called
+ * if no keyframe is in the selection area, so we only have to check if the curve
+ * intersects the area in order to check if the selection/deselection must happen.
+ */
+
+ LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
+ AnimData *adt = ANIM_nla_mapping_get(ac, ale);
+ FCurve *fcu = (FCurve *)ale->key_data;
+ float offset;
+ const float unit_scale = ANIM_unit_mapping_get_factor(
+ ac->scene, ale->id, fcu, mapping_flag, &offset);
+
+ const rctf rectf = initialize_box_select_coords(ac, rectf_view);
+
+ /* scaled_rectf is declared at the top of the block because it is required by the
+ * initialize_box_select_key_editing_data function (which does
+ * data_xxx->rectf_scaled = scaled_rectf). The below assignment therefore modifies the
+ * data we use to iterate over the curves (ked).
+ */
+ scaled_rectf.xmin = rectf.xmin;
+ scaled_rectf.xmax = rectf.xmax;
+ scaled_rectf.ymin = rectf.ymin / unit_scale - offset;
+ scaled_rectf.ymax = rectf.ymax / unit_scale - offset;
+
+ const KeyframeEditFunc select_cb = ANIM_editkeyframes_select(selectmode);
+ if (rectf_curve_intersection(offset, unit_scale, &rectf, adt, fcu)) {
+ if ((selectmode & SELECT_ADD) || (selectmode & SELECT_REPLACE)) {
+ fcu->flag |= FCURVE_SELECTED;
+ last_selected_curve = fcu;
+ }
+ else {
+ fcu->flag &= ~FCURVE_SELECTED;
+ }
+ ANIM_fcurve_keyframes_loop(&ked, fcu, ok_bezier_always_ok, select_cb, NULL);
+ }
+ }
+
+ /* Make sure that one of the selected curves is active in the end. */
+ if (last_selected_curve != NULL) {
+ ANIM_set_active_channel(
+ ac, ac->data, ac->datatype, filter, last_selected_curve, ANIMTYPE_FCURVE);
+ }
+
+ ANIM_animdata_freelist(&anim_data);
}
/* ------------------- */
@@ -726,7 +855,12 @@ static int graphkeys_box_select_exec(bContext *C, wmOperator *op)
BLI_rctf_rcti_copy(&rect_fl, &rect);
/* Apply box_select action. */
- box_select_graphkeys(&ac, &rect_fl, mode, selectmode, incl_handles, NULL);
+ const bool any_key_selection_changed = box_select_graphkeys(
+ &ac, &rect_fl, mode, selectmode, incl_handles, NULL);
+ const bool use_curve_selection = RNA_boolean_get(op->ptr, "use_curve_selection");
+ if (use_curve_selection && !any_key_selection_changed) {
+ box_select_graphcurves(&ac, &rect_fl, mode, selectmode, incl_handles, NULL);
+ }
/* Send notifier that keyframe selection has changed. */
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
@@ -767,6 +901,14 @@ void GRAPH_OT_select_box(wmOperatorType *ot)
ot->srna, "tweak", 0, "Tweak", "Operator has been activated using a tweak event");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+ prop = RNA_def_boolean(
+ ot->srna,
+ "use_curve_selection",
+ 1,
+ "Select Curves",
+ "Allow selecting all the keyframes of a curve by selecting the calculated fcurve");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+
WM_operator_properties_gesture_box(ot);
WM_operator_properties_select_operation_simple(ot);
}
@@ -815,7 +957,13 @@ static int graphkeys_lassoselect_exec(bContext *C, wmOperator *op)
BLI_rctf_rcti_copy(&rect_fl, &rect);
/* Apply box_select action. */
- box_select_graphkeys(&ac, &rect_fl, BEZT_OK_REGION_LASSO, selectmode, incl_handles, &data_lasso);
+ const bool any_key_selection_changed = box_select_graphkeys(
+ &ac, &rect_fl, BEZT_OK_REGION_LASSO, selectmode, incl_handles, &data_lasso);
+ const bool use_curve_selection = RNA_boolean_get(op->ptr, "use_curve_selection");
+ if (use_curve_selection && !any_key_selection_changed) {
+ box_select_graphcurves(
+ &ac, &rect_fl, BEZT_OK_REGION_LASSO, selectmode, incl_handles, &data_lasso);
+ }
MEM_freeN((void *)data_lasso.mcoords);
@@ -845,6 +993,13 @@ void GRAPH_OT_select_lasso(wmOperatorType *ot)
/* Properties. */
WM_operator_properties_gesture_lasso(ot);
WM_operator_properties_select_operation_simple(ot);
+ PropertyRNA *prop = RNA_def_boolean(
+ ot->srna,
+ "use_curve_selection",
+ 1,
+ "Select Curves",
+ "Allow selecting all the keyframes of a curve by selecting the curve itself");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
/* ------------------- */
@@ -894,7 +1049,12 @@ static int graph_circle_select_exec(bContext *C, wmOperator *op)
}
/* Apply box_select action. */
- box_select_graphkeys(&ac, &rect_fl, BEZT_OK_REGION_CIRCLE, selectmode, incl_handles, &data);
+ const bool any_key_selection_changed = box_select_graphkeys(
+ &ac, &rect_fl, BEZT_OK_REGION_CIRCLE, selectmode, incl_handles, &data);
+ const bool use_curve_selection = RNA_boolean_get(op->ptr, "use_curve_selection");
+ if (use_curve_selection && !any_key_selection_changed) {
+ box_select_graphcurves(&ac, &rect_fl, BEZT_OK_REGION_CIRCLE, selectmode, incl_handles, &data);
+ }
/* Send notifier that keyframe selection has changed. */
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
@@ -920,6 +1080,13 @@ void GRAPH_OT_select_circle(wmOperatorType *ot)
/* properties */
WM_operator_properties_gesture_circle(ot);
WM_operator_properties_select_operation_simple(ot);
+ PropertyRNA *prop = RNA_def_boolean(
+ ot->srna,
+ "use_curve_selection",
+ 1,
+ "Select Curves",
+ "Allow selecting all the keyframes of a curve by selecting the curve itself");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
/* ******************** Column Select Operator **************************** */
@@ -1638,7 +1805,6 @@ static int graphkeys_mselect_column(bAnimContext *ac,
KeyframeEditFunc select_cb, ok_cb;
KeyframeEditData ked;
tNearestVertInfo *nvi;
- float selx = (float)ac->scene->r.cfra;
/* find the beztriple that we're selecting, and the handle that was clicked on */
nvi = find_nearest_fcurve_vert(ac, mval);
@@ -1650,7 +1816,7 @@ static int graphkeys_mselect_column(bAnimContext *ac,
/* get frame number on which elements should be selected */
/* TODO: should we restrict to integer frames only? */
- selx = nvi->frame;
+ const float selx = nvi->frame;
if (select_mode != SELECT_REPLACE) {
/* Doesn't need to deselect anything -> Pass. */
diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c
index 6fb64de7e85..d909bfd1864 100644
--- a/source/blender/editors/space_image/image_buttons.c
+++ b/source/blender/editors/space_image/image_buttons.c
@@ -1218,11 +1218,12 @@ void uiTemplateImageInfo(uiLayout *layout, bContext *C, Image *ima, ImageUser *i
const int len = MAX_IMAGE_INFO_LEN;
int ofs = 0;
- ofs += BLI_snprintf(str + ofs, len - ofs, TIP_("%d x %d, "), ibuf->x, ibuf->y);
+ ofs += BLI_snprintf_rlen(str + ofs, len - ofs, TIP_("%d x %d, "), ibuf->x, ibuf->y);
if (ibuf->rect_float) {
if (ibuf->channels != 4) {
- ofs += BLI_snprintf(str + ofs, len - ofs, TIP_("%d float channel(s)"), ibuf->channels);
+ ofs += BLI_snprintf_rlen(
+ str + ofs, len - ofs, TIP_("%d float channel(s)"), ibuf->channels);
}
else if (ibuf->planes == R_IMF_PLANES_RGBA) {
ofs += BLI_strncpy_rlen(str + ofs, TIP_(" RGBA float"), len - ofs);
diff --git a/source/blender/editors/space_image/image_draw.c b/source/blender/editors/space_image/image_draw.c
index 2be6d31369c..dc693b25107 100644
--- a/source/blender/editors/space_image/image_draw.c
+++ b/source/blender/editors/space_image/image_draw.c
@@ -83,7 +83,6 @@ static void draw_render_info(
const bContext *C, Scene *scene, Image *ima, ARegion *region, float zoomx, float zoomy)
{
Render *re = RE_GetSceneRender(scene);
- RenderData *rd = RE_engine_get_render_data(re);
Scene *stats_scene = ED_render_job_get_scene(C);
if (stats_scene == NULL) {
stats_scene = CTX_data_scene(C);
@@ -112,6 +111,7 @@ static void draw_render_info(
GPU_matrix_translate_2f(x, y);
GPU_matrix_scale_2f(zoomx, zoomy);
+ RenderData *rd = RE_engine_get_render_data(re);
if (rd->mode & R_BORDER) {
/* TODO: round or floor instead of casting to int */
GPU_matrix_translate_2f((int)(-rd->border.xmin * rd->xsch * rd->size * 0.01f),
diff --git a/source/blender/editors/space_image/image_intern.h b/source/blender/editors/space_image/image_intern.h
index 0044c6072a4..d302f099772 100644
--- a/source/blender/editors/space_image/image_intern.h
+++ b/source/blender/editors/space_image/image_intern.h
@@ -50,6 +50,7 @@ void IMAGE_OT_view_all(struct wmOperatorType *ot);
void IMAGE_OT_view_pan(struct wmOperatorType *ot);
void IMAGE_OT_view_selected(struct wmOperatorType *ot);
void IMAGE_OT_view_center_cursor(struct wmOperatorType *ot);
+void IMAGE_OT_view_cursor_center(struct wmOperatorType *ot);
void IMAGE_OT_view_zoom(struct wmOperatorType *ot);
void IMAGE_OT_view_zoom_in(struct wmOperatorType *ot);
void IMAGE_OT_view_zoom_out(struct wmOperatorType *ot);
diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c
index 72405a51aca..0c34c0cc756 100644
--- a/source/blender/editors/space_image/image_ops.c
+++ b/source/blender/editors/space_image/image_ops.c
@@ -244,6 +244,70 @@ static bool image_not_packed_poll(bContext *C)
return (ima && BLI_listbase_is_empty(&ima->packedfiles));
}
+static void image_view_all(struct SpaceImage *sima, struct ARegion *region, struct wmOperator *op)
+{
+ float aspx, aspy, zoomx, zoomy, w, h;
+ int width, height;
+ const bool fit_view = RNA_boolean_get(op->ptr, "fit_view");
+
+ ED_space_image_get_size(sima, &width, &height);
+ ED_space_image_get_aspect(sima, &aspx, &aspy);
+
+ w = width * aspx;
+ h = height * aspy;
+
+ float xof = 0.0f, yof = 0.0f;
+ if ((sima->image == NULL) || (sima->image->source == IMA_SRC_TILED)) {
+ /* Extend the shown area to cover all UDIM tiles. */
+ int x_tiles, y_tiles;
+ if (sima->image == NULL) {
+ x_tiles = sima->tile_grid_shape[0];
+ y_tiles = sima->tile_grid_shape[1];
+ }
+ else {
+ x_tiles = y_tiles = 1;
+ LISTBASE_FOREACH (ImageTile *, tile, &sima->image->tiles) {
+ int tile_x = (tile->tile_number - 1001) % 10;
+ int tile_y = (tile->tile_number - 1001) / 10;
+ x_tiles = max_ii(x_tiles, tile_x + 1);
+ y_tiles = max_ii(y_tiles, tile_y + 1);
+ }
+ }
+ xof = 0.5f * (x_tiles - 1.0f) * w;
+ yof = 0.5f * (y_tiles - 1.0f) * h;
+ w *= x_tiles;
+ h *= y_tiles;
+ }
+
+ /* check if the image will fit in the image with (zoom == 1) */
+ width = BLI_rcti_size_x(&region->winrct) + 1;
+ height = BLI_rcti_size_y(&region->winrct) + 1;
+
+ if (fit_view) {
+ const int margin = 5; /* margin from border */
+
+ zoomx = (float)width / (w + 2 * margin);
+ zoomy = (float)height / (h + 2 * margin);
+
+ sima_zoom_set(sima, region, min_ff(zoomx, zoomy), NULL, false);
+ }
+ else {
+ if ((w >= width || h >= height) && (width > 0 && height > 0)) {
+ zoomx = (float)width / w;
+ zoomy = (float)height / h;
+
+ /* find the zoom value that will fit the image in the image space */
+ sima_zoom_set(sima, region, 1.0f / power_of_2(1.0f / min_ff(zoomx, zoomy)), NULL, false);
+ }
+ else {
+ sima_zoom_set(sima, region, 1.0f, NULL, false);
+ }
+ }
+
+ sima->xof = xof;
+ sima->yof = yof;
+}
+
bool space_image_main_region_poll(bContext *C)
{
SpaceImage *sima = CTX_wm_space_image(C);
@@ -736,70 +800,12 @@ static int image_view_all_exec(bContext *C, wmOperator *op)
{
SpaceImage *sima;
ARegion *region;
- float aspx, aspy, zoomx, zoomy, w, h;
- int width, height;
- const bool fit_view = RNA_boolean_get(op->ptr, "fit_view");
/* retrieve state */
sima = CTX_wm_space_image(C);
region = CTX_wm_region(C);
- ED_space_image_get_size(sima, &width, &height);
- ED_space_image_get_aspect(sima, &aspx, &aspy);
-
- w = width * aspx;
- h = height * aspy;
-
- float xof = 0.0f, yof = 0.0f;
- if ((sima->image == NULL) || (sima->image->source == IMA_SRC_TILED)) {
- /* Extend the shown area to cover all UDIM tiles. */
- int x_tiles, y_tiles;
- if (sima->image == NULL) {
- x_tiles = sima->tile_grid_shape[0];
- y_tiles = sima->tile_grid_shape[1];
- }
- else {
- x_tiles = y_tiles = 1;
- LISTBASE_FOREACH (ImageTile *, tile, &sima->image->tiles) {
- int tile_x = (tile->tile_number - 1001) % 10;
- int tile_y = (tile->tile_number - 1001) / 10;
- x_tiles = max_ii(x_tiles, tile_x + 1);
- y_tiles = max_ii(y_tiles, tile_y + 1);
- }
- }
- xof = 0.5f * (x_tiles - 1.0f) * w;
- yof = 0.5f * (y_tiles - 1.0f) * h;
- w *= x_tiles;
- h *= y_tiles;
- }
-
- /* check if the image will fit in the image with (zoom == 1) */
- width = BLI_rcti_size_x(&region->winrct) + 1;
- height = BLI_rcti_size_y(&region->winrct) + 1;
-
- if (fit_view) {
- const int margin = 5; /* margin from border */
-
- zoomx = (float)width / (w + 2 * margin);
- zoomy = (float)height / (h + 2 * margin);
-
- sima_zoom_set(sima, region, min_ff(zoomx, zoomy), NULL, false);
- }
- else {
- if ((w >= width || h >= height) && (width > 0 && height > 0)) {
- zoomx = (float)width / w;
- zoomy = (float)height / h;
-
- /* find the zoom value that will fit the image in the image space */
- sima_zoom_set(sima, region, 1.0f / power_of_2(1.0f / min_ff(zoomx, zoomy)), NULL, false);
- }
- else {
- sima_zoom_set(sima, region, 1.0f, NULL, false);
- }
- }
-
- sima->xof = xof;
- sima->yof = yof;
+ image_view_all(sima, region, op);
ED_region_tag_redraw(region);
@@ -830,6 +836,49 @@ void IMAGE_OT_view_all(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Cursor To Center View Operator
+ * \{ */
+
+static int view_cursor_center_exec(bContext *C, wmOperator *op)
+{
+ SpaceImage *sima;
+ ARegion *region;
+
+ sima = CTX_wm_space_image(C);
+ region = CTX_wm_region(C);
+
+ image_view_all(sima, region, op);
+
+ sima->cursor[0] = 0.5f;
+ sima->cursor[1] = 0.5f;
+
+ /* Needed for updating the cursor. */
+ WM_event_add_notifier(C, NC_SPACE | ND_SPACE_IMAGE, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void IMAGE_OT_view_cursor_center(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+
+ /* identifiers */
+ ot->name = "Cursor To Center View";
+ ot->description = "Set 2D Cursor To Center View location";
+ ot->idname = "IMAGE_OT_view_cursor_center";
+
+ /* api callbacks */
+ ot->exec = view_cursor_center_exec;
+ ot->poll = ED_space_image_cursor_poll;
+
+ /* properties */
+ prop = RNA_def_boolean(ot->srna, "fit_view", 0, "Fit View", "Fit frame to the viewport");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Center View To Cursor Operator
* \{ */
@@ -1262,7 +1311,6 @@ static int image_open_exec(bContext *C, wmOperator *op)
Scene *scene = CTX_data_scene(C);
Object *obedit = CTX_data_edit_object(C);
ImageUser *iuser = NULL;
- ImageOpenData *iod = op->customdata;
Image *ima = NULL;
int frame_seq_len = 0;
int frame_ofs = 1;
@@ -1296,7 +1344,7 @@ static int image_open_exec(bContext *C, wmOperator *op)
}
/* hook into UI */
- iod = op->customdata;
+ ImageOpenData *iod = op->customdata;
if (iod->pprop.prop) {
/* when creating new ID blocks, use is already 1, but RNA
@@ -1435,12 +1483,11 @@ static void image_open_draw(bContext *UNUSED(C), wmOperator *op)
uiLayout *layout = op->layout;
ImageOpenData *iod = op->customdata;
ImageFormatData *imf = &iod->im_format;
- PointerRNA imf_ptr, ptr;
+ PointerRNA imf_ptr;
/* main draw call */
- RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
uiDefAutoButsRNA(
- layout, &ptr, image_open_draw_check_prop, NULL, NULL, UI_BUT_LABEL_ALIGN_NONE, false);
+ layout, op->ptr, image_open_draw_check_prop, NULL, NULL, UI_BUT_LABEL_ALIGN_NONE, false);
/* image template */
RNA_pointer_create(NULL, &RNA_ImageFormatSettings, imf, &imf_ptr);
@@ -1955,7 +2002,7 @@ static void image_save_as_draw(bContext *UNUSED(C), wmOperator *op)
{
uiLayout *layout = op->layout;
ImageSaveData *isd = op->customdata;
- PointerRNA imf_ptr, ptr;
+ PointerRNA imf_ptr;
const bool is_multiview = RNA_boolean_get(op->ptr, "show_multiview");
/* image template */
@@ -1963,9 +2010,8 @@ static void image_save_as_draw(bContext *UNUSED(C), wmOperator *op)
uiTemplateImageSettings(layout, &imf_ptr, false);
/* main draw call */
- RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
uiDefAutoButsRNA(
- layout, &ptr, image_save_as_draw_check_prop, NULL, NULL, UI_BUT_LABEL_ALIGN_NONE, false);
+ layout, op->ptr, image_save_as_draw_check_prop, NULL, NULL, UI_BUT_LABEL_ALIGN_NONE, false);
/* multiview template */
if (is_multiview) {
@@ -2567,33 +2613,30 @@ static void image_new_draw(bContext *UNUSED(C), wmOperator *op)
{
uiLayout *col;
uiLayout *layout = op->layout;
- PointerRNA ptr;
#if 0
Scene *scene = CTX_data_scene(C);
const bool is_multiview = (scene->r.scemode & R_MULTIVIEW) != 0;
#endif
- RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
-
/* copy of WM_operator_props_dialog_popup() layout */
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
col = uiLayoutColumn(layout, false);
- uiItemR(col, &ptr, "name", 0, NULL, ICON_NONE);
- uiItemR(col, &ptr, "width", 0, NULL, ICON_NONE);
- uiItemR(col, &ptr, "height", 0, NULL, ICON_NONE);
- uiItemR(col, &ptr, "color", 0, NULL, ICON_NONE);
- uiItemR(col, &ptr, "alpha", 0, NULL, ICON_NONE);
- uiItemR(col, &ptr, "generated_type", 0, NULL, ICON_NONE);
- uiItemR(col, &ptr, "float", 0, NULL, ICON_NONE);
- uiItemR(col, &ptr, "tiled", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "name", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "width", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "height", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "color", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "alpha", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "generated_type", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "float", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "tiled", 0, NULL, ICON_NONE);
#if 0
if (is_multiview) {
uiItemL(col[0], "", ICON_NONE);
- uiItemR(col[1], &ptr, "use_stereo_3d", 0, NULL, ICON_NONE);
+ uiItemR(col[1], op->ptr, "use_stereo_3d", 0, NULL, ICON_NONE);
}
#endif
}
@@ -3943,21 +3986,18 @@ static void tile_add_draw(bContext *UNUSED(C), wmOperator *op)
{
uiLayout *col;
uiLayout *layout = op->layout;
- PointerRNA ptr;
-
- RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
col = uiLayoutColumn(layout, false);
- uiItemR(col, &ptr, "number", 0, NULL, ICON_NONE);
- uiItemR(col, &ptr, "count", 0, NULL, ICON_NONE);
- uiItemR(col, &ptr, "label", 0, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "fill", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "number", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "count", 0, NULL, ICON_NONE);
+ uiItemR(col, op->ptr, "label", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "fill", 0, NULL, ICON_NONE);
- if (RNA_boolean_get(&ptr, "fill")) {
- draw_fill_tile(&ptr, layout);
+ if (RNA_boolean_get(op->ptr, "fill")) {
+ draw_fill_tile(op->ptr, layout);
}
}
@@ -4077,10 +4117,7 @@ static int tile_fill_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(e
static void tile_fill_draw(bContext *UNUSED(C), wmOperator *op)
{
- PointerRNA ptr;
- RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
-
- draw_fill_tile(&ptr, op->layout);
+ draw_fill_tile(op->ptr, op->layout);
}
void IMAGE_OT_tile_fill(wmOperatorType *ot)
diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c
index c51d2f25efd..5a03b4f6ef0 100644
--- a/source/blender/editors/space_image/space_image.c
+++ b/source/blender/editors/space_image/space_image.c
@@ -200,6 +200,7 @@ static void image_operatortypes(void)
WM_operatortype_append(IMAGE_OT_view_pan);
WM_operatortype_append(IMAGE_OT_view_selected);
WM_operatortype_append(IMAGE_OT_view_center_cursor);
+ WM_operatortype_append(IMAGE_OT_view_cursor_center);
WM_operatortype_append(IMAGE_OT_view_zoom);
WM_operatortype_append(IMAGE_OT_view_zoom_in);
WM_operatortype_append(IMAGE_OT_view_zoom_out);
diff --git a/source/blender/editors/space_info/info_draw.c b/source/blender/editors/space_info/info_draw.c
index be3b60d581b..9524c2a1d8a 100644
--- a/source/blender/editors/space_info/info_draw.c
+++ b/source/blender/editors/space_info/info_draw.c
@@ -134,14 +134,12 @@ static void report_textview_end(TextViewContext *UNUSED(tvc))
static int report_textview_step(TextViewContext *tvc)
{
/* simple case, but no newline support */
- const Report *report = tvc->iter;
-
if (tvc->iter_char_begin <= 0) {
tvc->iter = (void *)((Link *)tvc->iter)->prev;
if (tvc->iter && report_textview_skip__internal(tvc)) {
tvc->iter_tmp++;
- report = tvc->iter;
+ const Report *report = tvc->iter;
tvc->iter_char_end = report->len; /* reset start */
report_textview_init__internal(tvc);
diff --git a/source/blender/editors/space_info/info_ops.c b/source/blender/editors/space_info/info_ops.c
index 0583628be43..aaf9852e212 100644
--- a/source/blender/editors/space_info/info_ops.c
+++ b/source/blender/editors/space_info/info_ops.c
@@ -56,7 +56,9 @@
#include "info_intern.h"
-/********************* pack blend file libraries operator *********************/
+/* -------------------------------------------------------------------- */
+/** \name Pack Blend File Libraries Operator
+ * \{ */
static int pack_libraries_exec(bContext *C, wmOperator *op)
{
@@ -70,9 +72,11 @@ static int pack_libraries_exec(bContext *C, wmOperator *op)
void FILE_OT_pack_libraries(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Pack Blender Libraries";
+ ot->name = "Pack Linked Libraries";
ot->idname = "FILE_OT_pack_libraries";
- ot->description = "Pack all used Blender library files into the current .blend";
+ ot->description =
+ "Store all data-blocks linked from other .blend files in the current .blend file. "
+ "Library references are preserved so the linked data-blocks can be unpacked again";
/* api callbacks */
ot->exec = pack_libraries_exec;
@@ -90,18 +94,24 @@ static int unpack_libraries_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Unpack Blend File Libraries Operator
+ * \{ */
+
static int unpack_libraries_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
return WM_operator_confirm_message(
- C, op, "Unpack Blender Libraries - creates directories, all new paths should work");
+ C, op, "Unpack Linked Libraries - creates directories, all new paths should work");
}
void FILE_OT_unpack_libraries(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Unpack Blender Libraries";
+ ot->name = "Unpack Linked Libraries";
ot->idname = "FILE_OT_unpack_libraries";
- ot->description = "Unpack all used Blender library files from this .blend file";
+ ot->description = "Restore all packed linked data-blocks to their original locations";
/* api callbacks */
ot->invoke = unpack_libraries_invoke;
@@ -111,7 +121,11 @@ void FILE_OT_unpack_libraries(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/********************* toggle auto-pack operator *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Toggle Auto-Pack Operator
+ * \{ */
static int autopack_toggle_exec(bContext *C, wmOperator *op)
{
@@ -131,7 +145,7 @@ static int autopack_toggle_exec(bContext *C, wmOperator *op)
void FILE_OT_autopack_toggle(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Automatically Pack Into .blend";
+ ot->name = "Automatically Pack Resources";
ot->idname = "FILE_OT_autopack_toggle";
ot->description = "Automatically pack all external files into the .blend file";
@@ -142,7 +156,11 @@ void FILE_OT_autopack_toggle(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/********************* pack all operator *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Pack All Operator
+ * \{ */
static int pack_all_exec(bContext *C, wmOperator *op)
{
@@ -176,9 +194,9 @@ static int pack_all_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(ev
void FILE_OT_pack_all(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Pack All Into .blend";
+ ot->name = "Pack Resources";
ot->idname = "FILE_OT_pack_all";
- ot->description = "Pack all used external files into the .blend";
+ ot->description = "Pack all used external files into this .blend";
/* api callbacks */
ot->exec = pack_all_exec;
@@ -188,7 +206,11 @@ void FILE_OT_pack_all(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/********************* unpack all operator *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Unpack All Operator
+ * \{ */
static const EnumPropertyItem unpack_all_method_items[] = {
{PF_USE_LOCAL, "USE_LOCAL", 0, "Use files in current directory (create when necessary)", ""},
@@ -263,7 +285,7 @@ static int unpack_all_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(
void FILE_OT_unpack_all(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Unpack All Into Files";
+ ot->name = "Unpack Resources";
ot->idname = "FILE_OT_unpack_all";
ot->description = "Unpack all files packed into this .blend to external ones";
@@ -279,7 +301,11 @@ void FILE_OT_unpack_all(wmOperatorType *ot)
ot->srna, "method", unpack_all_method_items, PF_USE_LOCAL, "Method", "How to unpack");
}
-/********************* unpack single item operator *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Unpack Single Item Operator
+ * \{ */
static const EnumPropertyItem unpack_item_method_items[] = {
{PF_USE_LOCAL, "USE_LOCAL", 0, "Use file from current directory (create when necessary)", ""},
@@ -373,7 +399,11 @@ void FILE_OT_unpack_item(wmOperatorType *ot)
INT_MAX);
}
-/********************* make paths relative operator *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Make Paths Relative Operator
+ * \{ */
static int make_paths_relative_exec(bContext *C, wmOperator *op)
{
@@ -395,7 +425,7 @@ static int make_paths_relative_exec(bContext *C, wmOperator *op)
void FILE_OT_make_paths_relative(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Make All Paths Relative";
+ ot->name = "Make Paths Relative";
ot->idname = "FILE_OT_make_paths_relative";
ot->description = "Make all paths to external files relative to current .blend";
@@ -406,7 +436,11 @@ void FILE_OT_make_paths_relative(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/********************* make paths absolute operator *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Make Paths Absolute Operator
+ * \{ */
static int make_paths_absolute_exec(bContext *C, wmOperator *op)
{
@@ -428,7 +462,7 @@ static int make_paths_absolute_exec(bContext *C, wmOperator *op)
void FILE_OT_make_paths_absolute(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Make All Paths Absolute";
+ ot->name = "Make Paths Absolute";
ot->idname = "FILE_OT_make_paths_absolute";
ot->description = "Make all paths to external files absolute";
@@ -439,7 +473,11 @@ void FILE_OT_make_paths_absolute(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/********************* report missing files operator *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Report Missing Files Operator
+ * \{ */
static int report_missing_files_exec(bContext *C, wmOperator *op)
{
@@ -465,7 +503,11 @@ void FILE_OT_report_missing_files(wmOperatorType *ot)
ot->flag = 0; /* only reports so no need to undo/register */
}
-/********************* find missing files operator *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Find Missing Files Operator
+ * \{ */
static int find_missing_files_exec(bContext *C, wmOperator *op)
{
@@ -516,7 +558,11 @@ void FILE_OT_find_missing_files(wmOperatorType *ot)
FILE_SORT_DEFAULT);
}
-/********************* report box operator *********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Report Box Operator
+ * \{ */
/* Hard to decide whether to keep this as an operator,
* or turn it into a hardcoded ui control feature,
@@ -621,3 +667,5 @@ void INFO_OT_reports_display_update(wmOperatorType *ot)
}
/* report operators */
+
+/** \} */
diff --git a/source/blender/editors/space_info/info_stats.c b/source/blender/editors/space_info/info_stats.c
index ffac5c982d6..ebdf32a1cdb 100644
--- a/source/blender/editors/space_info/info_stats.c
+++ b/source/blender/editors/space_info/info_stats.c
@@ -505,14 +505,14 @@ static void get_stats_string(
LayerCollection *layer_collection = view_layer->active_collection;
if (object_mode == OB_MODE_OBJECT) {
- *ofs += BLI_snprintf(info + *ofs,
- len - *ofs,
- "%s | ",
- BKE_collection_ui_name_get(layer_collection->collection));
+ *ofs += BLI_snprintf_rlen(info + *ofs,
+ len - *ofs,
+ "%s | ",
+ BKE_collection_ui_name_get(layer_collection->collection));
}
if (ob) {
- *ofs += BLI_snprintf(info + *ofs, len - *ofs, "%s | ", ob->id.name + 2);
+ *ofs += BLI_snprintf_rlen(info + *ofs, len - *ofs, "%s | ", ob->id.name + 2);
}
if (obedit) {
@@ -521,72 +521,72 @@ static void get_stats_string(
}
if (obedit->type == OB_MESH) {
- *ofs += BLI_snprintf(info + *ofs,
- len - *ofs,
- TIP_("Verts:%s/%s | Edges:%s/%s | Faces:%s/%s | Tris:%s"),
- stats_fmt->totvertsel,
- stats_fmt->totvert,
- stats_fmt->totedgesel,
- stats_fmt->totedge,
- stats_fmt->totfacesel,
- stats_fmt->totface,
- stats_fmt->tottri);
+ *ofs += BLI_snprintf_rlen(info + *ofs,
+ len - *ofs,
+ TIP_("Verts:%s/%s | Edges:%s/%s | Faces:%s/%s | Tris:%s"),
+ stats_fmt->totvertsel,
+ stats_fmt->totvert,
+ stats_fmt->totedgesel,
+ stats_fmt->totedge,
+ stats_fmt->totfacesel,
+ stats_fmt->totface,
+ stats_fmt->tottri);
}
else if (obedit->type == OB_ARMATURE) {
- *ofs += BLI_snprintf(info + *ofs,
- len - *ofs,
- TIP_("Joints:%s/%s | Bones:%s/%s"),
- stats_fmt->totvertsel,
- stats_fmt->totvert,
- stats_fmt->totbonesel,
- stats_fmt->totbone);
+ *ofs += BLI_snprintf_rlen(info + *ofs,
+ len - *ofs,
+ TIP_("Joints:%s/%s | Bones:%s/%s"),
+ stats_fmt->totvertsel,
+ stats_fmt->totvert,
+ stats_fmt->totbonesel,
+ stats_fmt->totbone);
}
else {
- *ofs += BLI_snprintf(
+ *ofs += BLI_snprintf_rlen(
info + *ofs, len - *ofs, TIP_("Verts:%s/%s"), stats_fmt->totvertsel, stats_fmt->totvert);
}
}
else if (ob && (object_mode & OB_MODE_POSE)) {
- *ofs += BLI_snprintf(
+ *ofs += BLI_snprintf_rlen(
info + *ofs, len - *ofs, TIP_("Bones:%s/%s"), stats_fmt->totbonesel, stats_fmt->totbone);
}
else if ((ob) && (ob->type == OB_GPENCIL)) {
- *ofs += BLI_snprintf(info + *ofs,
- len - *ofs,
- TIP_("Layers:%s | Frames:%s | Strokes:%s | Points:%s"),
- stats_fmt->totgplayer,
- stats_fmt->totgpframe,
- stats_fmt->totgpstroke,
- stats_fmt->totgppoint);
+ *ofs += BLI_snprintf_rlen(info + *ofs,
+ len - *ofs,
+ TIP_("Layers:%s | Frames:%s | Strokes:%s | Points:%s"),
+ stats_fmt->totgplayer,
+ stats_fmt->totgpframe,
+ stats_fmt->totgpstroke,
+ stats_fmt->totgppoint);
}
else if (ob && (object_mode & OB_MODE_SCULPT)) {
if (stats_is_object_dynamic_topology_sculpt(ob)) {
- *ofs += BLI_snprintf(info + *ofs,
- len - *ofs,
- TIP_("Verts:%s | Tris:%s"),
- stats_fmt->totvert,
- stats_fmt->tottri);
+ *ofs += BLI_snprintf_rlen(info + *ofs,
+ len - *ofs,
+ TIP_("Verts:%s | Tris:%s"),
+ stats_fmt->totvert,
+ stats_fmt->tottri);
}
else {
- *ofs += BLI_snprintf(info + *ofs,
- len - *ofs,
- TIP_("Verts:%s/%s | Faces:%s/%s"),
- stats_fmt->totvertsculpt,
- stats_fmt->totvert,
- stats_fmt->totfacesculpt,
- stats_fmt->totface);
+ *ofs += BLI_snprintf_rlen(info + *ofs,
+ len - *ofs,
+ TIP_("Verts:%s/%s | Faces:%s/%s"),
+ stats_fmt->totvertsculpt,
+ stats_fmt->totvert,
+ stats_fmt->totfacesculpt,
+ stats_fmt->totface);
}
}
else {
- *ofs += BLI_snprintf(info + *ofs,
- len - *ofs,
- TIP_("Verts:%s | Faces:%s | Tris:%s"),
- stats_fmt->totvert,
- stats_fmt->totface,
- stats_fmt->tottri);
+ *ofs += BLI_snprintf_rlen(info + *ofs,
+ len - *ofs,
+ TIP_("Verts:%s | Faces:%s | Tris:%s"),
+ stats_fmt->totvert,
+ stats_fmt->totface,
+ stats_fmt->tottri);
}
- *ofs += BLI_snprintf(
+ *ofs += BLI_snprintf_rlen(
info + *ofs, len - *ofs, TIP_(" | Objects:%s/%s"), stats_fmt->totobjsel, stats_fmt->totobj);
}
@@ -613,11 +613,11 @@ static const char *info_statusbar_string(Main *bmain,
/* Memory status. */
if (statusbar_flag & STATUSBAR_SHOW_MEMORY) {
if (info[0]) {
- ofs += BLI_snprintf(info + ofs, len - ofs, " | ");
+ ofs += BLI_snprintf_rlen(info + ofs, len - ofs, " | ");
}
uintptr_t mem_in_use = MEM_get_memory_in_use();
BLI_str_format_byte_unit(formatted_mem, mem_in_use, false);
- ofs += BLI_snprintf(info + ofs, len, TIP_("Memory: %s"), formatted_mem);
+ ofs += BLI_snprintf_rlen(info + ofs, len, TIP_("Memory: %s"), formatted_mem);
}
/* GPU VRAM status. */
@@ -627,27 +627,27 @@ static const char *info_statusbar_string(Main *bmain,
float gpu_total_gb = gpu_tot_mem_kb / 1048576.0f;
float gpu_free_gb = gpu_free_mem_kb / 1048576.0f;
if (info[0]) {
- ofs += BLI_snprintf(info + ofs, len - ofs, " | ");
+ ofs += BLI_snprintf_rlen(info + ofs, len - ofs, " | ");
}
if (gpu_free_mem_kb && gpu_tot_mem_kb) {
- ofs += BLI_snprintf(info + ofs,
- len - ofs,
- TIP_("VRAM: %.1f/%.1f GiB"),
- gpu_total_gb - gpu_free_gb,
- gpu_total_gb);
+ ofs += BLI_snprintf_rlen(info + ofs,
+ len - ofs,
+ TIP_("VRAM: %.1f/%.1f GiB"),
+ gpu_total_gb - gpu_free_gb,
+ gpu_total_gb);
}
else {
/* Can only show amount of GPU VRAM available. */
- ofs += BLI_snprintf(info + ofs, len - ofs, TIP_("VRAM: %.1f GiB Free"), gpu_free_gb);
+ ofs += BLI_snprintf_rlen(info + ofs, len - ofs, TIP_("VRAM: %.1f GiB Free"), gpu_free_gb);
}
}
/* Blender version. */
if (statusbar_flag & STATUSBAR_SHOW_VERSION) {
if (info[0]) {
- ofs += BLI_snprintf(info + ofs, len - ofs, " | ");
+ ofs += BLI_snprintf_rlen(info + ofs, len - ofs, " | ");
}
- ofs += BLI_snprintf(info + ofs, len - ofs, TIP_("%s"), BKE_blender_version_string());
+ ofs += BLI_snprintf_rlen(info + ofs, len - ofs, TIP_("%s"), BKE_blender_version_string());
}
return info;
@@ -693,11 +693,12 @@ void ED_info_draw_stats(
Object *ob = OBACT(view_layer);
Object *obedit = OBEDIT_FROM_OBACT(ob);
eObjectMode object_mode = ob ? ob->mode : OB_MODE_OBJECT;
- const int font_id = BLF_default();
+ const int font_id = BLF_set_default();
UI_FontThemeColor(font_id, TH_TEXT_HI);
BLF_enable(font_id, BLF_SHADOW);
- BLF_shadow(font_id, 5, (const float[4]){0.0f, 0.0f, 0.0f, 1.0f});
+ const float shadow_color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
+ BLF_shadow(font_id, 5, shadow_color);
BLF_shadow_offset(font_id, 1, -1);
/* Translated labels for each stat row. */
diff --git a/source/blender/editors/space_info/space_info.c b/source/blender/editors/space_info/space_info.c
index dfc0abee704..e56bb44b1e6 100644
--- a/source/blender/editors/space_info/space_info.c
+++ b/source/blender/editors/space_info/space_info.c
@@ -255,11 +255,10 @@ static void info_header_region_message_subscribe(const wmRegionMessageSubscribeP
struct wmMsgBus *mbus = params->message_bus;
ARegion *region = params->region;
- wmMsgSubscribeValue msg_sub_value_region_tag_redraw = {
- .owner = region,
- .user_data = region,
- .notify = ED_region_do_msg_notify_tag_redraw,
- };
+ wmMsgSubscribeValue msg_sub_value_region_tag_redraw = {NULL};
+ msg_sub_value_region_tag_redraw.owner = region;
+ msg_sub_value_region_tag_redraw.user_data = region;
+ msg_sub_value_region_tag_redraw.notify = ED_region_do_msg_notify_tag_redraw;
WM_msg_subscribe_rna_anon_prop(mbus, Window, view_layer, &msg_sub_value_region_tag_redraw);
WM_msg_subscribe_rna_anon_prop(mbus, ViewLayer, name, &msg_sub_value_region_tag_redraw);
diff --git a/source/blender/editors/space_nla/nla_draw.c b/source/blender/editors/space_nla/nla_draw.c
index eea81e425c2..7d4011e0812 100644
--- a/source/blender/editors/space_nla/nla_draw.c
+++ b/source/blender/editors/space_nla/nla_draw.c
@@ -266,7 +266,7 @@ static void nla_strip_get_color_inside(AnimData *adt, NlaStrip *strip, float col
}
else if (strip->type == NLASTRIP_TYPE_META) {
/* Meta Clip */
- /* TODO: should temporary metas get different colors too? */
+ /* TODO: should temporary meta-strips get different colors too? */
if (strip->flag & NLASTRIP_FLAG_SELECT) {
/* selected - use a bold purple color */
UI_GetThemeColor3fv(TH_NLA_META_SEL, color);
@@ -408,8 +408,10 @@ static uint nla_draw_use_dashed_outlines(const float color[4], bool muted)
return shdr_pos;
}
-/** This check only accounts for the track's disabled flag and whether the strip is being tweaked.
- * It does not account for muting or soloing. */
+/**
+ * This check only accounts for the track's disabled flag and whether the strip is being tweaked.
+ * It does not account for muting or soloing.
+ */
static bool is_nlastrip_enabled(AnimData *adt, NlaTrack *nlt, NlaStrip *strip)
{
/** This shouldn't happen. If passed NULL, then just treat strip as enabled. */
diff --git a/source/blender/editors/space_node/CMakeLists.txt b/source/blender/editors/space_node/CMakeLists.txt
index bc043a4e665..6e234c5b2ce 100644
--- a/source/blender/editors/space_node/CMakeLists.txt
+++ b/source/blender/editors/space_node/CMakeLists.txt
@@ -37,20 +37,20 @@ set(INC
set(SRC
- drawnode.c
- node_add.c
+ drawnode.cc
+ node_add.cc
node_buttons.c
node_draw.cc
- node_edit.c
+ node_edit.cc
node_geometry_attribute_search.cc
node_gizmo.c
- node_group.c
+ node_group.cc
node_ops.c
- node_relationships.c
- node_select.c
- node_templates.c
- node_toolbar.c
- node_view.c
+ node_relationships.cc
+ node_select.cc
+ node_templates.cc
+ node_toolbar.cc
+ node_view.cc
space_node.c
node_intern.h
diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.cc
index 6864d34885a..1d4c3b67387 100644
--- a/source/blender/editors/space_node/drawnode.c
+++ b/source/blender/editors/space_node/drawnode.cc
@@ -95,9 +95,9 @@ static void node_socket_button_label(bContext *UNUSED(C),
static void node_buts_value(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- bNode *node = ptr->data;
+ bNode *node = (bNode *)ptr->data;
/* first output stores value */
- bNodeSocket *output = node->outputs.first;
+ bNodeSocket *output = (bNodeSocket *)node->outputs.first;
PointerRNA sockptr;
RNA_pointer_create(ptr->owner_id, &RNA_NodeSocket, output, &sockptr);
@@ -106,15 +106,15 @@ static void node_buts_value(uiLayout *layout, bContext *UNUSED(C), PointerRNA *p
static void node_buts_rgb(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- bNode *node = ptr->data;
+ bNode *node = (bNode *)ptr->data;
/* first output stores value */
- bNodeSocket *output = node->outputs.first;
+ bNodeSocket *output = (bNodeSocket *)node->outputs.first;
PointerRNA sockptr;
uiLayout *col;
RNA_pointer_create(ptr->owner_id, &RNA_NodeSocket, output, &sockptr);
col = uiLayoutColumn(layout, false);
- uiTemplateColorPicker(col, &sockptr, "default_value", 1, 0, 0, 0);
+ uiTemplateColorPicker(col, &sockptr, "default_value", true, false, false, false);
uiItemR(col, &sockptr, "default_value", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE);
}
@@ -129,14 +129,14 @@ static void node_buts_mix_rgb(uiLayout *layout, bContext *UNUSED(C), PointerRNA
uiItemR(row, ptr, "use_alpha", DEFAULT_FLAGS, "", ICON_IMAGE_RGB_ALPHA);
}
- uiItemR(col, ptr, "use_clamp", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "use_clamp", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_buts_time(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
#if 0
/* XXX no context access here .. */
- bNode *node = ptr->data;
+ bNode *node = (bNode*)ptr->data;
CurveMapping *cumap = node->storage;
if (cumap) {
@@ -156,7 +156,7 @@ static void node_buts_time(uiLayout *layout, bContext *UNUSED(C), PointerRNA *pt
static void node_buts_colorramp(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- uiTemplateColorRamp(layout, ptr, "color_ramp", 0);
+ uiTemplateColorRamp(layout, ptr, "color_ramp", false);
}
static void node_buts_curvevec(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -179,8 +179,8 @@ void ED_node_sample_set(const float col[4])
static void node_buts_curvecol(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- bNode *node = ptr->data;
- CurveMapping *cumap = node->storage;
+ bNode *node = (bNode *)ptr->data;
+ CurveMapping *cumap = (CurveMapping *)node->storage;
if (_sample_col[0] != SAMPLE_FLT_ISNONE) {
cumap->flag |= CUMA_DRAW_SAMPLE;
@@ -198,9 +198,9 @@ static void node_buts_curvecol(uiLayout *layout, bContext *UNUSED(C), PointerRNA
static void node_buts_normal(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- bNode *node = ptr->data;
+ bNode *node = (bNode *)ptr->data;
/* first output stores normal */
- bNodeSocket *output = node->outputs.first;
+ bNodeSocket *output = (bNodeSocket *)node->outputs.first;
PointerRNA sockptr;
RNA_pointer_create(ptr->owner_id, &RNA_NodeSocket, output, &sockptr);
@@ -209,7 +209,7 @@ static void node_buts_normal(uiLayout *layout, bContext *UNUSED(C), PointerRNA *
static void node_buts_texture(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- bNode *node = ptr->data;
+ bNode *node = (bNode *)ptr->data;
short multi = (node->id && ((Tex *)node->id)->use_nodes && (node->type != CMP_NODE_TEXTURE) &&
(node->type != TEX_NODE_TEXTURE));
@@ -233,14 +233,14 @@ static void node_shader_buts_map_range(uiLayout *layout, bContext *UNUSED(C), Po
if (!ELEM(RNA_enum_get(ptr, "interpolation_type"),
NODE_MAP_RANGE_SMOOTHSTEP,
NODE_MAP_RANGE_SMOOTHERSTEP)) {
- uiItemR(layout, ptr, "clamp", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "clamp", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
}
static void node_buts_math(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "operation", DEFAULT_FLAGS, "", ICON_NONE);
- uiItemR(layout, ptr, "use_clamp", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "use_clamp", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static int node_resize_area_default(bNode *node, int x, int y)
@@ -274,7 +274,7 @@ static int node_resize_area_default(bNode *node, int x, int y)
static void node_draw_buttons_group(uiLayout *layout, bContext *C, PointerRNA *ptr)
{
uiTemplateIDBrowse(
- layout, C, ptr, "node_tree", NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL, NULL);
+ layout, C, ptr, "node_tree", nullptr, nullptr, nullptr, UI_TEMPLATE_ID_FILTER_ALL, nullptr);
}
/* XXX Does a bounding box update by iterating over all children.
@@ -311,7 +311,7 @@ static void node_draw_frame_prepare(const bContext *UNUSED(C), bNodeTree *ntree,
/* first child initializes frame */
if (bbinit) {
- bbinit = 0;
+ bbinit = false;
rect = noderect;
data->flag &= ~NODE_FRAME_RESIZEABLE;
}
@@ -418,9 +418,9 @@ static void node_draw_frame(const bContext *C,
{
/* skip if out of view */
- if (BLI_rctf_isect(&node->totr, &region->v2d.cur, NULL) == false) {
+ if (BLI_rctf_isect(&node->totr, &region->v2d.cur, nullptr) == false) {
UI_block_end(C, node->block);
- node->block = NULL;
+ node->block = nullptr;
return;
}
@@ -456,11 +456,13 @@ static void node_draw_frame(const bContext *C,
}
/* label */
- node_draw_frame_label(ntree, node, snode->runtime->aspect);
+ if (node->label[0] != '\0') {
+ node_draw_frame_label(ntree, node, snode->runtime->aspect);
+ }
UI_block_end(C, node->block);
UI_block_draw(C, node->block);
- node->block = NULL;
+ node->block = nullptr;
}
static int node_resize_area_frame(bNode *node, int x, int y)
@@ -495,7 +497,7 @@ static void node_buts_frame_ex(uiLayout *layout, bContext *UNUSED(C), PointerRNA
{
uiItemR(layout, ptr, "label_size", DEFAULT_FLAGS, IFACE_("Label Size"), ICON_NONE);
uiItemR(layout, ptr, "shrink", DEFAULT_FLAGS, IFACE_("Shrink"), ICON_NONE);
- uiItemR(layout, ptr, "text", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "text", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
#define NODE_REROUTE_SIZE 8.0f
@@ -509,11 +511,11 @@ static void node_draw_reroute_prepare(const bContext *UNUSED(C),
node_to_view(node, 0.0f, 0.0f, &locx, &locy);
/* reroute node has exactly one input and one output, both in the same place */
- bNodeSocket *nsock = node->outputs.first;
+ bNodeSocket *nsock = (bNodeSocket *)node->outputs.first;
nsock->locx = locx;
nsock->locy = locy;
- nsock = node->inputs.first;
+ nsock = (bNodeSocket *)node->inputs.first;
nsock->locx = locx;
nsock->locy = locy;
@@ -539,7 +541,7 @@ static void node_draw_reroute(const bContext *C,
if (node->totr.xmax < region->v2d.cur.xmin || node->totr.xmin > region->v2d.cur.xmax ||
node->totr.ymax < region->v2d.cur.ymin || node->totr.ymin > region->v2d.cur.ymax) {
UI_block_end(C, node->block);
- node->block = NULL;
+ node->block = nullptr;
return;
}
@@ -584,12 +586,12 @@ static void node_draw_reroute(const bContext *C,
(int)(rct->ymax),
(short)512,
(short)NODE_DY,
- NULL,
+ nullptr,
0,
0,
0,
0,
- NULL);
+ nullptr);
}
/* only draw input socket. as they all are placed on the same position.
@@ -599,7 +601,7 @@ static void node_draw_reroute(const bContext *C,
UI_block_end(C, node->block);
UI_block_draw(C, node->block);
- node->block = NULL;
+ node->block = nullptr;
}
/* Special tweak area for reroute node.
@@ -610,7 +612,7 @@ static int node_tweak_area_reroute(bNode *node, int x, int y)
/* square of tweak radius */
const float tweak_radius_sq = square_f(24.0f);
- bNodeSocket *sock = node->inputs.first;
+ bNodeSocket *sock = (bNodeSocket *)node->inputs.first;
float dx = sock->locx - x;
float dy = sock->locy - y;
return (dx * dx + dy * dy <= tweak_radius_sq);
@@ -660,28 +662,28 @@ static void node_buts_image_user(uiLayout *layout,
/* don't use iuser->framenr directly
* because it may not be updated if auto-refresh is off */
Scene *scene = CTX_data_scene(C);
- ImageUser *iuser = iuserptr->data;
+ ImageUser *iuser = (ImageUser *)iuserptr->data;
/* Image *ima = imaptr->data; */ /* UNUSED */
char numstr[32];
- const int framenr = BKE_image_user_frame_get(iuser, CFRA, NULL);
+ const int framenr = BKE_image_user_frame_get(iuser, CFRA, nullptr);
BLI_snprintf(numstr, sizeof(numstr), IFACE_("Frame: %d"), framenr);
uiItemL(layout, numstr, ICON_NONE);
}
if (ELEM(source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE)) {
col = uiLayoutColumn(layout, true);
- uiItemR(col, ptr, "frame_duration", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(col, ptr, "frame_start", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(col, ptr, "frame_offset", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(col, ptr, "use_cyclic", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(col, ptr, "use_auto_refresh", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "frame_duration", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "frame_start", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "frame_offset", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "use_cyclic", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "use_auto_refresh", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
if (show_layer_selection && RNA_enum_get(imaptr, "type") == IMA_TYPE_MULTILAYER &&
RNA_boolean_get(ptr, "has_layers")) {
col = uiLayoutColumn(layout, false);
- uiItemR(col, ptr, "layer", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "layer", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
if (show_color_management) {
@@ -691,7 +693,7 @@ static void node_buts_image_user(uiLayout *layout,
uiItemR(split, &colorspace_settings_ptr, "name", DEFAULT_FLAGS, "", ICON_NONE);
/* Avoid losing changes image is painted. */
- if (BKE_image_is_dirty(imaptr->data)) {
+ if (BKE_image_is_dirty((Image *)imaptr->data)) {
uiLayoutSetEnabled(split, false);
}
}
@@ -699,13 +701,13 @@ static void node_buts_image_user(uiLayout *layout,
static void node_shader_buts_mapping(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- uiItemR(layout, ptr, "vector_type", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "vector_type", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_shader_buts_vector_rotate(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- uiItemR(layout, ptr, "rotation_type", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "invert", DEFAULT_FLAGS, NULL, 0);
+ uiItemR(layout, ptr, "rotation_type", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "invert", DEFAULT_FLAGS, nullptr, 0);
}
static void node_shader_buts_vect_math(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -715,7 +717,7 @@ static void node_shader_buts_vect_math(uiLayout *layout, bContext *UNUSED(C), Po
static void node_shader_buts_vect_transform(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- uiItemR(layout, ptr, "vector_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "vector_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
uiItemR(layout, ptr, "convert_from", DEFAULT_FLAGS, "", ICON_NONE);
uiItemR(layout, ptr, "convert_to", DEFAULT_FLAGS, "", ICON_NONE);
}
@@ -728,7 +730,7 @@ static void node_shader_buts_attribute(uiLayout *layout, bContext *UNUSED(C), Po
static void node_shader_buts_wireframe(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- uiItemR(layout, ptr, "use_pixel_size", DEFAULT_FLAGS, NULL, 0);
+ uiItemR(layout, ptr, "use_pixel_size", DEFAULT_FLAGS, nullptr, 0);
}
static void node_shader_buts_tex_image(uiLayout *layout, bContext *C, PointerRNA *ptr)
@@ -743,10 +745,10 @@ static void node_shader_buts_tex_image(uiLayout *layout, bContext *C, PointerRNA
"image",
"IMAGE_OT_new",
"IMAGE_OT_open",
- NULL,
+ nullptr,
UI_TEMPLATE_ID_FILTER_ALL,
false,
- NULL);
+ nullptr);
uiItemR(layout, ptr, "interpolation", DEFAULT_FLAGS, "", ICON_NONE);
uiItemR(layout, ptr, "projection", DEFAULT_FLAGS, "", ICON_NONE);
@@ -765,7 +767,7 @@ static void node_shader_buts_tex_image(uiLayout *layout, bContext *C, PointerRNA
static void node_shader_buts_tex_image_ex(uiLayout *layout, bContext *C, PointerRNA *ptr)
{
PointerRNA iuserptr = RNA_pointer_get(ptr, "image_user");
- uiTemplateImage(layout, C, ptr, "image", &iuserptr, 0, 0);
+ uiTemplateImage(layout, C, ptr, "image", &iuserptr, false, false);
}
static void node_shader_buts_tex_environment(uiLayout *layout, bContext *C, PointerRNA *ptr)
@@ -780,10 +782,10 @@ static void node_shader_buts_tex_environment(uiLayout *layout, bContext *C, Poin
"image",
"IMAGE_OT_new",
"IMAGE_OT_open",
- NULL,
+ nullptr,
UI_TEMPLATE_ID_FILTER_ALL,
false,
- NULL);
+ nullptr);
uiItemR(layout, ptr, "interpolation", DEFAULT_FLAGS, "", ICON_NONE);
uiItemR(layout, ptr, "projection", DEFAULT_FLAGS, "", ICON_NONE);
@@ -794,7 +796,7 @@ static void node_shader_buts_tex_environment(uiLayout *layout, bContext *C, Poin
static void node_shader_buts_tex_environment_ex(uiLayout *layout, bContext *C, PointerRNA *ptr)
{
PointerRNA iuserptr = RNA_pointer_get(ptr, "image_user");
- uiTemplateImage(layout, C, ptr, "image", &iuserptr, 0, 0);
+ uiTemplateImage(layout, C, ptr, "image", &iuserptr, false, false);
uiItemR(layout, ptr, "interpolation", DEFAULT_FLAGS, IFACE_("Interpolation"), ICON_NONE);
uiItemR(layout, ptr, "projection", DEFAULT_FLAGS, IFACE_("Projection"), ICON_NONE);
@@ -806,33 +808,33 @@ static void node_shader_buts_tex_sky(uiLayout *layout, bContext *UNUSED(C), Poin
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);
+ uiItemR(layout, ptr, "turbidity", DEFAULT_FLAGS, nullptr, 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);
+ uiItemR(layout, ptr, "turbidity", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "ground_albedo", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
if (RNA_enum_get(ptr, "sky_type") == SHD_SKY_NISHITA) {
- uiItemR(layout, ptr, "sun_disc", DEFAULT_FLAGS, NULL, 0);
+ uiItemR(layout, ptr, "sun_disc", DEFAULT_FLAGS, nullptr, 0);
uiLayout *col;
if (RNA_boolean_get(ptr, "sun_disc")) {
col = uiLayoutColumn(layout, true);
- uiItemR(col, ptr, "sun_size", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(col, ptr, "sun_intensity", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "sun_size", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "sun_intensity", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
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(col, ptr, "sun_elevation", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "sun_rotation", DEFAULT_FLAGS, nullptr, ICON_NONE);
- uiItemR(layout, ptr, "altitude", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "altitude", DEFAULT_FLAGS, nullptr, 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);
+ uiItemR(col, ptr, "air_density", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "dust_density", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "ozone_density", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
}
@@ -843,7 +845,7 @@ static void node_shader_buts_tex_gradient(uiLayout *layout, bContext *UNUSED(C),
static void node_shader_buts_tex_magic(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- uiItemR(layout, ptr, "turbulence_depth", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "turbulence_depth", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_shader_buts_tex_brick(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -899,32 +901,33 @@ static void node_shader_buts_tex_pointdensity(uiLayout *layout,
bContext *UNUSED(C),
PointerRNA *ptr)
{
- bNode *node = ptr->data;
- NodeShaderTexPointDensity *shader_point_density = node->storage;
+ bNode *node = (bNode *)ptr->data;
+ NodeShaderTexPointDensity *shader_point_density = (NodeShaderTexPointDensity *)node->storage;
Object *ob = (Object *)node->id;
PointerRNA ob_ptr, obdata_ptr;
RNA_id_pointer_create((ID *)ob, &ob_ptr);
- RNA_id_pointer_create(ob ? (ID *)ob->data : NULL, &obdata_ptr);
+ RNA_id_pointer_create(ob ? (ID *)ob->data : nullptr, &obdata_ptr);
- uiItemR(layout, ptr, "point_source", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
- uiItemR(layout, ptr, "object", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "point_source", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "object", DEFAULT_FLAGS, nullptr, ICON_NONE);
if (node->id && shader_point_density->point_source == SHD_POINTDENSITY_SOURCE_PSYS) {
PointerRNA dataptr;
RNA_id_pointer_create((ID *)node->id, &dataptr);
- uiItemPointerR(layout, ptr, "particle_system", &dataptr, "particle_systems", NULL, ICON_NONE);
+ uiItemPointerR(
+ layout, ptr, "particle_system", &dataptr, "particle_systems", nullptr, ICON_NONE);
}
- uiItemR(layout, ptr, "space", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "radius", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "interpolation", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "resolution", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "space", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "radius", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "interpolation", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "resolution", DEFAULT_FLAGS, nullptr, ICON_NONE);
if (shader_point_density->point_source == SHD_POINTDENSITY_SOURCE_PSYS) {
- uiItemR(layout, ptr, "particle_color_source", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "particle_color_source", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
else {
- uiItemR(layout, ptr, "vertex_color_source", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "vertex_color_source", DEFAULT_FLAGS, nullptr, ICON_NONE);
if (shader_point_density->ob_color_source == SHD_POINTDENSITY_COLOR_VERTWEIGHT) {
if (ob_ptr.data) {
uiItemPointerR(
@@ -942,18 +945,18 @@ static void node_shader_buts_tex_pointdensity(uiLayout *layout,
static void node_shader_buts_tex_coord(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- uiItemR(layout, ptr, "object", DEFAULT_FLAGS, NULL, 0);
- uiItemR(layout, ptr, "from_instancer", DEFAULT_FLAGS, NULL, 0);
+ uiItemR(layout, ptr, "object", DEFAULT_FLAGS, nullptr, 0);
+ uiItemR(layout, ptr, "from_instancer", DEFAULT_FLAGS, nullptr, 0);
}
static void node_shader_buts_bump(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- uiItemR(layout, ptr, "invert", DEFAULT_FLAGS, NULL, 0);
+ uiItemR(layout, ptr, "invert", DEFAULT_FLAGS, nullptr, 0);
}
static void node_shader_buts_uvmap(uiLayout *layout, bContext *C, PointerRNA *ptr)
{
- uiItemR(layout, ptr, "from_instancer", DEFAULT_FLAGS, NULL, 0);
+ uiItemR(layout, ptr, "from_instancer", DEFAULT_FLAGS, nullptr, 0);
if (!RNA_boolean_get(ptr, "from_instancer")) {
PointerRNA obptr = CTX_data_pointer_get(C, "active_object");
@@ -987,7 +990,7 @@ static void node_shader_buts_vertex_color(uiLayout *layout, bContext *C, Pointer
static void node_shader_buts_uvalongstroke(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- uiItemR(layout, ptr, "use_tips", DEFAULT_FLAGS, NULL, 0);
+ uiItemR(layout, ptr, "use_tips", DEFAULT_FLAGS, nullptr, 0);
}
static void node_shader_buts_normal_map(uiLayout *layout, bContext *C, PointerRNA *ptr)
@@ -1034,7 +1037,7 @@ static void node_shader_buts_tangent(uiLayout *layout, bContext *C, PointerRNA *
}
}
else {
- uiItemR(row, ptr, "axis", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, 0);
+ uiItemR(row, ptr, "axis", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, 0);
}
}
@@ -1081,7 +1084,7 @@ static void node_shader_buts_ies(uiLayout *layout, bContext *UNUSED(C), PointerR
uiLayout *row;
row = uiLayoutRow(layout, false);
- uiItemR(row, ptr, "mode", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, ptr, "mode", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
row = uiLayoutRow(layout, true);
@@ -1098,7 +1101,7 @@ static void node_shader_buts_script(uiLayout *layout, bContext *UNUSED(C), Point
uiLayout *row;
row = uiLayoutRow(layout, false);
- uiItemR(row, ptr, "mode", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, ptr, "mode", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
row = uiLayoutRow(layout, true);
@@ -1120,7 +1123,7 @@ static void node_shader_buts_script_ex(uiLayout *layout, bContext *C, PointerRNA
#if 0 /* not implemented yet */
if (RNA_enum_get(ptr, "mode") == NODE_SCRIPT_EXTERNAL) {
- uiItemR(layout, ptr, "use_auto_update", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "use_auto_update", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
#endif
}
@@ -1137,21 +1140,21 @@ static void node_buts_output_linestyle(uiLayout *layout, bContext *UNUSED(C), Po
col = uiLayoutColumn(layout, false);
row = uiLayoutRow(col, true);
uiItemR(row, ptr, "blend_type", DEFAULT_FLAGS, "", ICON_NONE);
- uiItemR(col, ptr, "use_clamp", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "use_clamp", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_shader_buts_bevel(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- uiItemR(layout, ptr, "samples", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "samples", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_shader_buts_ambient_occlusion(uiLayout *layout,
bContext *UNUSED(C),
PointerRNA *ptr)
{
- uiItemR(layout, ptr, "samples", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "inside", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "only_local", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "samples", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "inside", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "only_local", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_shader_buts_white_noise(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -1161,7 +1164,7 @@ static void node_shader_buts_white_noise(uiLayout *layout, bContext *UNUSED(C),
static void node_shader_buts_output_aov(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- uiItemR(layout, ptr, "name", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "name", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
/* only once called */
@@ -1346,17 +1349,17 @@ static void node_buts_image_views(uiLayout *layout,
if (RNA_boolean_get(ptr, "has_views")) {
if (RNA_enum_get(ptr, "view") == 0) {
- uiItemR(col, ptr, "view", DEFAULT_FLAGS, NULL, ICON_CAMERA_STEREO);
+ uiItemR(col, ptr, "view", DEFAULT_FLAGS, nullptr, ICON_CAMERA_STEREO);
}
else {
- uiItemR(col, ptr, "view", DEFAULT_FLAGS, NULL, ICON_SCENE);
+ uiItemR(col, ptr, "view", DEFAULT_FLAGS, nullptr, ICON_SCENE);
}
}
}
static void node_composit_buts_image(uiLayout *layout, bContext *C, PointerRNA *ptr)
{
- bNode *node = ptr->data;
+ bNode *node = (bNode *)ptr->data;
PointerRNA iuserptr;
RNA_pointer_create(ptr->owner_id, &RNA_ImageUser, node->storage, &iuserptr);
@@ -1367,10 +1370,10 @@ static void node_composit_buts_image(uiLayout *layout, bContext *C, PointerRNA *
"image",
"IMAGE_OT_new",
"IMAGE_OT_open",
- NULL,
+ nullptr,
UI_TEMPLATE_ID_FILTER_ALL,
false,
- NULL);
+ nullptr);
if (!node->id) {
return;
}
@@ -1384,20 +1387,29 @@ static void node_composit_buts_image(uiLayout *layout, bContext *C, PointerRNA *
static void node_composit_buts_image_ex(uiLayout *layout, bContext *C, PointerRNA *ptr)
{
- bNode *node = ptr->data;
+ bNode *node = (bNode *)ptr->data;
PointerRNA iuserptr;
RNA_pointer_create(ptr->owner_id, &RNA_ImageUser, node->storage, &iuserptr);
uiLayoutSetContextPointer(layout, "image_user", &iuserptr);
- uiTemplateImage(layout, C, ptr, "image", &iuserptr, 0, 1);
+ uiTemplateImage(layout, C, ptr, "image", &iuserptr, false, true);
}
static void node_composit_buts_viewlayers(uiLayout *layout, bContext *C, PointerRNA *ptr)
{
- bNode *node = ptr->data;
+ bNode *node = (bNode *)ptr->data;
uiLayout *col, *row;
- uiTemplateID(layout, C, ptr, "scene", NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL);
+ uiTemplateID(layout,
+ C,
+ ptr,
+ "scene",
+ nullptr,
+ nullptr,
+ nullptr,
+ UI_TEMPLATE_ID_FILTER_ALL,
+ false,
+ nullptr);
if (!node->id) {
return;
@@ -1421,7 +1433,7 @@ static void node_composit_buts_viewlayers(uiLayout *layout, bContext *C, Pointer
PointerRNA op_ptr;
uiItemFullO(
- row, "RENDER_OT_render", "", ICON_RENDER_STILL, NULL, WM_OP_INVOKE_DEFAULT, 0, &op_ptr);
+ row, "RENDER_OT_render", "", ICON_RENDER_STILL, nullptr, WM_OP_INVOKE_DEFAULT, 0, &op_ptr);
RNA_string_set(&op_ptr, "layer", layer_name);
RNA_string_set(&op_ptr, "scene", scene_name);
}
@@ -1436,19 +1448,19 @@ static void node_composit_buts_blur(uiLayout *layout, bContext *UNUSED(C), Point
uiItemR(col, ptr, "filter_type", DEFAULT_FLAGS, "", ICON_NONE);
if (filter != R_FILTER_FAST_GAUSS) {
- uiItemR(col, ptr, "use_variable_size", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "use_variable_size", DEFAULT_FLAGS, nullptr, ICON_NONE);
if (!reference) {
- uiItemR(col, ptr, "use_bokeh", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "use_bokeh", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
- uiItemR(col, ptr, "use_gamma_correction", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "use_gamma_correction", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
- uiItemR(col, ptr, "use_relative", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "use_relative", DEFAULT_FLAGS, nullptr, ICON_NONE);
if (RNA_boolean_get(ptr, "use_relative")) {
uiItemL(col, IFACE_("Aspect Correction"), ICON_NONE);
row = uiLayoutRow(layout, true);
- uiItemR(row, ptr, "aspect_correction", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, ptr, "aspect_correction", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
col = uiLayoutColumn(layout, true);
uiItemR(col, ptr, "factor_x", DEFAULT_FLAGS, IFACE_("X"), ICON_NONE);
@@ -1459,15 +1471,15 @@ static void node_composit_buts_blur(uiLayout *layout, bContext *UNUSED(C), Point
uiItemR(col, ptr, "size_x", DEFAULT_FLAGS, IFACE_("X"), ICON_NONE);
uiItemR(col, ptr, "size_y", DEFAULT_FLAGS, IFACE_("Y"), ICON_NONE);
}
- uiItemR(col, ptr, "use_extended_bounds", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "use_extended_bounds", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_dblur(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiLayout *col;
- uiItemR(layout, ptr, "iterations", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "use_wrap", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "iterations", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "use_wrap", DEFAULT_FLAGS, nullptr, ICON_NONE);
col = uiLayoutColumn(layout, true);
uiItemL(col, IFACE_("Center:"), ICON_NONE);
@@ -1477,13 +1489,13 @@ static void node_composit_buts_dblur(uiLayout *layout, bContext *UNUSED(C), Poin
uiItemS(layout);
col = uiLayoutColumn(layout, true);
- uiItemR(col, ptr, "distance", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(col, ptr, "angle", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "distance", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "angle", DEFAULT_FLAGS, nullptr, ICON_NONE);
uiItemS(layout);
- uiItemR(layout, ptr, "spin", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "zoom", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "spin", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "zoom", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_bilateralblur(uiLayout *layout,
@@ -1493,9 +1505,9 @@ static void node_composit_buts_bilateralblur(uiLayout *layout,
uiLayout *col;
col = uiLayoutColumn(layout, true);
- uiItemR(col, ptr, "iterations", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(col, ptr, "sigma_color", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(col, ptr, "sigma_space", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "iterations", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "sigma_color", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "sigma_space", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_defocus(uiLayout *layout, bContext *C, PointerRNA *ptr)
@@ -1505,27 +1517,47 @@ static void node_composit_buts_defocus(uiLayout *layout, bContext *C, PointerRNA
col = uiLayoutColumn(layout, false);
uiItemL(col, IFACE_("Bokeh Type:"), ICON_NONE);
uiItemR(col, ptr, "bokeh", DEFAULT_FLAGS, "", ICON_NONE);
- uiItemR(col, ptr, "angle", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "angle", DEFAULT_FLAGS, nullptr, ICON_NONE);
- uiItemR(layout, ptr, "use_gamma_correction", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "use_gamma_correction", DEFAULT_FLAGS, nullptr, ICON_NONE);
col = uiLayoutColumn(layout, false);
uiLayoutSetActive(col, RNA_boolean_get(ptr, "use_zbuffer") == true);
- uiItemR(col, ptr, "f_stop", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "f_stop", DEFAULT_FLAGS, nullptr, ICON_NONE);
- uiItemR(layout, ptr, "blur_max", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "threshold", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "blur_max", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "threshold", DEFAULT_FLAGS, nullptr, ICON_NONE);
col = uiLayoutColumn(layout, false);
- uiItemR(col, ptr, "use_preview", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "use_preview", DEFAULT_FLAGS, nullptr, ICON_NONE);
- uiTemplateID(layout, C, ptr, "scene", NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL);
+ uiTemplateID(layout,
+ C,
+ ptr,
+ "scene",
+ nullptr,
+ nullptr,
+ nullptr,
+ UI_TEMPLATE_ID_FILTER_ALL,
+ false,
+ nullptr);
col = uiLayoutColumn(layout, false);
- uiItemR(col, ptr, "use_zbuffer", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "use_zbuffer", DEFAULT_FLAGS, nullptr, ICON_NONE);
sub = uiLayoutColumn(col, false);
uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_zbuffer") == false);
- uiItemR(sub, ptr, "z_scale", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(sub, ptr, "z_scale", DEFAULT_FLAGS, nullptr, ICON_NONE);
+}
+
+static void node_composit_buts_antialiasing(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiLayout *col;
+
+ col = uiLayoutColumn(layout, false);
+
+ uiItemR(col, ptr, "threshold", 0, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "contrast_limit", 0, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "corner_rounding", 0, nullptr, ICON_NONE);
}
/* qdn: glare node */
@@ -1535,29 +1567,30 @@ static void node_composit_buts_glare(uiLayout *layout, bContext *UNUSED(C), Poin
uiItemR(layout, ptr, "quality", DEFAULT_FLAGS, "", ICON_NONE);
if (RNA_enum_get(ptr, "glare_type") != 1) {
- uiItemR(layout, ptr, "iterations", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "iterations", DEFAULT_FLAGS, nullptr, ICON_NONE);
if (RNA_enum_get(ptr, "glare_type") != 0) {
- uiItemR(layout, ptr, "color_modulation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+ uiItemR(
+ layout, ptr, "color_modulation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
}
- uiItemR(layout, ptr, "mix", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "threshold", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "mix", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "threshold", DEFAULT_FLAGS, nullptr, ICON_NONE);
if (RNA_enum_get(ptr, "glare_type") == 2) {
- uiItemR(layout, ptr, "streaks", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "angle_offset", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "streaks", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "angle_offset", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
if (RNA_enum_get(ptr, "glare_type") == 0 || RNA_enum_get(ptr, "glare_type") == 2) {
- uiItemR(layout, ptr, "fade", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "fade", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
if (RNA_enum_get(ptr, "glare_type") == 0) {
- uiItemR(layout, ptr, "use_rotate_45", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "use_rotate_45", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
}
if (RNA_enum_get(ptr, "glare_type") == 1) {
- uiItemR(layout, ptr, "size", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "size", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
}
@@ -1568,15 +1601,15 @@ static void node_composit_buts_tonemap(uiLayout *layout, bContext *UNUSED(C), Po
col = uiLayoutColumn(layout, false);
uiItemR(col, ptr, "tonemap_type", DEFAULT_FLAGS, "", ICON_NONE);
if (RNA_enum_get(ptr, "tonemap_type") == 0) {
- uiItemR(col, ptr, "key", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(col, ptr, "offset", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(col, ptr, "gamma", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "key", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "offset", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "gamma", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
else {
- uiItemR(col, ptr, "intensity", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(col, ptr, "contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(col, ptr, "adaptation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(col, ptr, "correction", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+ uiItemR(col, ptr, "intensity", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "adaptation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "correction", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
}
@@ -1585,12 +1618,12 @@ static void node_composit_buts_lensdist(uiLayout *layout, bContext *UNUSED(C), P
uiLayout *col;
col = uiLayoutColumn(layout, false);
- uiItemR(col, ptr, "use_projector", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "use_projector", DEFAULT_FLAGS, nullptr, ICON_NONE);
col = uiLayoutColumn(col, false);
uiLayoutSetActive(col, RNA_boolean_get(ptr, "use_projector") == false);
- uiItemR(col, ptr, "use_jitter", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(col, ptr, "use_fit", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "use_jitter", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "use_fit", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_vecblur(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -1598,7 +1631,7 @@ static void node_composit_buts_vecblur(uiLayout *layout, bContext *UNUSED(C), Po
uiLayout *col;
col = uiLayoutColumn(layout, false);
- uiItemR(col, ptr, "samples", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "samples", DEFAULT_FLAGS, nullptr, ICON_NONE);
uiItemR(col, ptr, "factor", DEFAULT_FLAGS, IFACE_("Blur"), ICON_NONE);
col = uiLayoutColumn(layout, true);
@@ -1606,7 +1639,7 @@ static void node_composit_buts_vecblur(uiLayout *layout, bContext *UNUSED(C), Po
uiItemR(col, ptr, "speed_min", DEFAULT_FLAGS, IFACE_("Min"), ICON_NONE);
uiItemR(col, ptr, "speed_max", DEFAULT_FLAGS, IFACE_("Max"), ICON_NONE);
- uiItemR(layout, ptr, "use_curved", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "use_curved", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_filter(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -1623,8 +1656,8 @@ static void node_composit_buts_crop(uiLayout *layout, bContext *UNUSED(C), Point
{
uiLayout *col;
- uiItemR(layout, ptr, "use_crop_size", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "relative", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "use_crop_size", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "relative", DEFAULT_FLAGS, nullptr, ICON_NONE);
col = uiLayoutColumn(layout, true);
if (RNA_boolean_get(ptr, "relative")) {
@@ -1647,8 +1680,8 @@ static void node_composit_buts_splitviewer(uiLayout *layout, bContext *UNUSED(C)
col = uiLayoutColumn(layout, false);
row = uiLayoutRow(col, false);
- uiItemR(row, ptr, "axis", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
- uiItemR(col, ptr, "factor", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(row, ptr, "axis", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "factor", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_double_edge_mask(uiLayout *layout,
@@ -1670,7 +1703,7 @@ static void node_composit_buts_map_range(uiLayout *layout, bContext *UNUSED(C),
uiLayout *col;
col = uiLayoutColumn(layout, true);
- uiItemR(col, ptr, "use_clamp", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "use_clamp", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_map_value(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -1678,17 +1711,17 @@ static void node_composit_buts_map_value(uiLayout *layout, bContext *UNUSED(C),
uiLayout *sub, *col;
col = uiLayoutColumn(layout, true);
- uiItemR(col, ptr, "offset", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(col, ptr, "size", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "offset", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "size", DEFAULT_FLAGS, nullptr, ICON_NONE);
col = uiLayoutColumn(layout, true);
- uiItemR(col, ptr, "use_min", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "use_min", DEFAULT_FLAGS, nullptr, ICON_NONE);
sub = uiLayoutColumn(col, false);
uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_min"));
uiItemR(sub, ptr, "min", DEFAULT_FLAGS, "", ICON_NONE);
col = uiLayoutColumn(layout, true);
- uiItemR(col, ptr, "use_max", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "use_max", DEFAULT_FLAGS, nullptr, ICON_NONE);
sub = uiLayoutColumn(col, false);
uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_max"));
uiItemR(sub, ptr, "max", DEFAULT_FLAGS, "", ICON_NONE);
@@ -1699,8 +1732,8 @@ static void node_composit_buts_alphaover(uiLayout *layout, bContext *UNUSED(C),
uiLayout *col;
col = uiLayoutColumn(layout, true);
- uiItemR(col, ptr, "use_premultiply", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(col, ptr, "premul", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "use_premultiply", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "premul", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_zcombine(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -1708,27 +1741,27 @@ static void node_composit_buts_zcombine(uiLayout *layout, bContext *UNUSED(C), P
uiLayout *col;
col = uiLayoutColumn(layout, true);
- uiItemR(col, ptr, "use_alpha", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(col, ptr, "use_antialias_z", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "use_alpha", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "use_antialias_z", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_dilateerode(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- uiItemR(layout, ptr, "mode", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "distance", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "mode", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "distance", DEFAULT_FLAGS, nullptr, ICON_NONE);
switch (RNA_enum_get(ptr, "mode")) {
case CMP_NODE_DILATEERODE_DISTANCE_THRESH:
- uiItemR(layout, ptr, "edge", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "edge", DEFAULT_FLAGS, nullptr, ICON_NONE);
break;
case CMP_NODE_DILATEERODE_DISTANCE_FEATHER:
- uiItemR(layout, ptr, "falloff", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "falloff", DEFAULT_FLAGS, nullptr, ICON_NONE);
break;
}
}
static void node_composit_buts_inpaint(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- uiItemR(layout, ptr, "distance", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "distance", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_despeckle(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -1736,8 +1769,8 @@ static void node_composit_buts_despeckle(uiLayout *layout, bContext *UNUSED(C),
uiLayout *col;
col = uiLayoutColumn(layout, false);
- uiItemR(col, ptr, "threshold", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(col, ptr, "threshold_neighbor", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "threshold", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "threshold_neighbor", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_diff_matte(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -1745,8 +1778,8 @@ static void node_composit_buts_diff_matte(uiLayout *layout, bContext *UNUSED(C),
uiLayout *col;
col = uiLayoutColumn(layout, true);
- uiItemR(col, ptr, "tolerance", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(col, ptr, "falloff", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+ uiItemR(col, ptr, "tolerance", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "falloff", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
static void node_composit_buts_distance_matte(uiLayout *layout,
@@ -1759,10 +1792,10 @@ static void node_composit_buts_distance_matte(uiLayout *layout,
uiItemL(layout, IFACE_("Color Space:"), ICON_NONE);
row = uiLayoutRow(layout, false);
- uiItemR(row, ptr, "channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, ptr, "channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
- uiItemR(col, ptr, "tolerance", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(col, ptr, "falloff", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+ uiItemR(col, ptr, "tolerance", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "falloff", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
static void node_composit_buts_color_spill(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -1771,23 +1804,23 @@ static void node_composit_buts_color_spill(uiLayout *layout, bContext *UNUSED(C)
uiItemL(layout, IFACE_("Despill Channel:"), ICON_NONE);
row = uiLayoutRow(layout, false);
- uiItemR(row, ptr, "channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, ptr, "channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
col = uiLayoutColumn(layout, false);
- uiItemR(col, ptr, "limit_method", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "limit_method", DEFAULT_FLAGS, nullptr, ICON_NONE);
if (RNA_enum_get(ptr, "limit_method") == 0) {
uiItemL(col, IFACE_("Limiting Channel:"), ICON_NONE);
row = uiLayoutRow(col, false);
- uiItemR(row, ptr, "limit_channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, ptr, "limit_channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
}
- uiItemR(col, ptr, "ratio", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(col, ptr, "use_unspill", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "ratio", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "use_unspill", DEFAULT_FLAGS, nullptr, ICON_NONE);
if (RNA_boolean_get(ptr, "use_unspill") == true) {
- uiItemR(col, ptr, "unspill_red", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(col, ptr, "unspill_green", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(col, ptr, "unspill_blue", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+ uiItemR(col, ptr, "unspill_red", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "unspill_green", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "unspill_blue", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
}
@@ -1796,13 +1829,13 @@ static void node_composit_buts_chroma_matte(uiLayout *layout, bContext *UNUSED(C
uiLayout *col;
col = uiLayoutColumn(layout, false);
- uiItemR(col, ptr, "tolerance", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(col, ptr, "threshold", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "tolerance", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "threshold", DEFAULT_FLAGS, nullptr, ICON_NONE);
col = uiLayoutColumn(layout, true);
- /*uiItemR(col, ptr, "lift", UI_ITEM_R_SLIDER, NULL, ICON_NONE); Removed for now */
- uiItemR(col, ptr, "gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- /*uiItemR(col, ptr, "shadow_adjust", UI_ITEM_R_SLIDER, NULL, ICON_NONE); Removed for now*/
+ /*uiItemR(col, ptr, "lift", UI_ITEM_R_SLIDER, nullptr, ICON_NONE); Removed for now */
+ uiItemR(col, ptr, "gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ /*uiItemR(col, ptr, "shadow_adjust", UI_ITEM_R_SLIDER, nullptr, ICON_NONE); Removed for now*/
}
static void node_composit_buts_color_matte(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -1810,9 +1843,9 @@ static void node_composit_buts_color_matte(uiLayout *layout, bContext *UNUSED(C)
uiLayout *col;
col = uiLayoutColumn(layout, true);
- uiItemR(col, ptr, "color_hue", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(col, ptr, "color_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(col, ptr, "color_value", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+ uiItemR(col, ptr, "color_hue", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "color_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "color_value", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
static void node_composit_buts_channel_matte(uiLayout *layout,
@@ -1823,24 +1856,24 @@ static void node_composit_buts_channel_matte(uiLayout *layout,
uiItemL(layout, IFACE_("Color Space:"), ICON_NONE);
row = uiLayoutRow(layout, false);
- uiItemR(row, ptr, "color_space", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, ptr, "color_space", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
col = uiLayoutColumn(layout, false);
uiItemL(col, IFACE_("Key Channel:"), ICON_NONE);
row = uiLayoutRow(col, false);
- uiItemR(row, ptr, "matte_channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, ptr, "matte_channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
col = uiLayoutColumn(layout, false);
- uiItemR(col, ptr, "limit_method", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "limit_method", DEFAULT_FLAGS, nullptr, ICON_NONE);
if (RNA_enum_get(ptr, "limit_method") == 0) {
uiItemL(col, IFACE_("Limiting Channel:"), ICON_NONE);
row = uiLayoutRow(col, false);
- uiItemR(row, ptr, "limit_channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, ptr, "limit_channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
}
- uiItemR(col, ptr, "limit_max", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(col, ptr, "limit_min", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+ uiItemR(col, ptr, "limit_max", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "limit_min", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
static void node_composit_buts_luma_matte(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -1848,19 +1881,19 @@ static void node_composit_buts_luma_matte(uiLayout *layout, bContext *UNUSED(C),
uiLayout *col;
col = uiLayoutColumn(layout, true);
- uiItemR(col, ptr, "limit_max", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(col, ptr, "limit_min", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+ uiItemR(col, ptr, "limit_max", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "limit_min", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
static void node_composit_buts_map_uv(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- uiItemR(layout, ptr, "alpha", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "alpha", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_id_mask(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- uiItemR(layout, ptr, "index", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "use_antialiasing", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "index", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "use_antialiasing", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_file_output(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -1892,7 +1925,7 @@ static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, Poi
/* disable stereo output for multilayer, too much work for something that no one will use */
/* if someone asks for that we can implement it */
if (is_multiview) {
- uiTemplateImageFormatViews(layout, &imfptr, NULL);
+ uiTemplateImageFormatViews(layout, &imfptr, nullptr);
}
uiItemS(layout);
@@ -1913,7 +1946,7 @@ static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, Poi
"layer_slots",
ptr,
"active_input_index",
- NULL,
+ nullptr,
0,
0,
0,
@@ -1932,7 +1965,7 @@ static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, Poi
"file_slots",
ptr,
"active_input_index",
- NULL,
+ nullptr,
0,
0,
0,
@@ -1948,9 +1981,9 @@ static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, Poi
col = uiLayoutColumn(row, true);
wmOperatorType *ot = WM_operatortype_find("NODE_OT_output_file_move_active_socket", false);
- uiItemFullO_ptr(col, ot, "", ICON_TRIA_UP, NULL, WM_OP_INVOKE_DEFAULT, 0, &op_ptr);
+ uiItemFullO_ptr(col, ot, "", ICON_TRIA_UP, nullptr, WM_OP_INVOKE_DEFAULT, 0, &op_ptr);
RNA_enum_set(&op_ptr, "direction", 1);
- uiItemFullO_ptr(col, ot, "", ICON_TRIA_DOWN, NULL, WM_OP_INVOKE_DEFAULT, 0, &op_ptr);
+ uiItemFullO_ptr(col, ot, "", ICON_TRIA_DOWN, nullptr, WM_OP_INVOKE_DEFAULT, 0, &op_ptr);
RNA_enum_set(&op_ptr, "direction", 2);
if (active_input_ptr.data) {
@@ -1964,10 +1997,10 @@ static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, Poi
"NODE_OT_output_file_remove_active_socket",
"",
ICON_X,
- NULL,
+ nullptr,
WM_OP_EXEC_DEFAULT,
UI_ITEM_R_ICON_ONLY,
- NULL);
+ nullptr);
}
else {
col = uiLayoutColumn(layout, true);
@@ -1979,23 +2012,23 @@ static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, Poi
"NODE_OT_output_file_remove_active_socket",
"",
ICON_X,
- NULL,
+ nullptr,
WM_OP_EXEC_DEFAULT,
UI_ITEM_R_ICON_ONLY,
- NULL);
+ nullptr);
/* format details for individual files */
imfptr = RNA_pointer_get(&active_input_ptr, "format");
col = uiLayoutColumn(layout, true);
uiItemL(col, IFACE_("Format:"), ICON_NONE);
- uiItemR(col, &active_input_ptr, "use_node_format", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, &active_input_ptr, "use_node_format", DEFAULT_FLAGS, nullptr, ICON_NONE);
const bool is_socket_exr = RNA_enum_get(&imfptr, "file_format") == R_IMF_IMTYPE_OPENEXR;
const bool use_node_format = RNA_boolean_get(&active_input_ptr, "use_node_format");
if ((!is_exr && use_node_format) || (!is_socket_exr && !use_node_format)) {
- uiItemR(col, &active_input_ptr, "save_as_render", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, &active_input_ptr, "save_as_render", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
col = uiLayoutColumn(layout, false);
@@ -2003,7 +2036,7 @@ static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, Poi
uiTemplateImageSettings(col, &imfptr, false);
if (is_multiview) {
- uiTemplateImageFormatViews(layout, &imfptr, NULL);
+ uiTemplateImageFormatViews(layout, &imfptr, nullptr);
}
}
}
@@ -2015,7 +2048,7 @@ static void node_composit_buts_scale(uiLayout *layout, bContext *UNUSED(C), Poin
if (RNA_enum_get(ptr, "space") == CMP_SCALE_RENDERPERCENT) {
uiLayout *row;
- uiItemR(layout, ptr, "frame_method", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "frame_method", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
row = uiLayoutRow(layout, true);
uiItemR(row, ptr, "offset_x", DEFAULT_FLAGS, "X", ICON_NONE);
uiItemR(row, ptr, "offset_y", DEFAULT_FLAGS, "Y", ICON_NONE);
@@ -2032,8 +2065,8 @@ static void node_composit_buts_invert(uiLayout *layout, bContext *UNUSED(C), Poi
uiLayout *col;
col = uiLayoutColumn(layout, false);
- uiItemR(col, ptr, "invert_rgb", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(col, ptr, "invert_alpha", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "invert_rgb", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "invert_alpha", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_premulkey(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -2043,86 +2076,86 @@ static void node_composit_buts_premulkey(uiLayout *layout, bContext *UNUSED(C),
static void node_composit_buts_view_levels(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- uiItemR(layout, ptr, "channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
}
static void node_composit_buts_colorbalance(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiLayout *split, *col, *row;
- uiItemR(layout, ptr, "correction_method", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "correction_method", DEFAULT_FLAGS, nullptr, ICON_NONE);
if (RNA_enum_get(ptr, "correction_method") == 0) {
split = uiLayoutSplit(layout, 0.0f, false);
col = uiLayoutColumn(split, false);
- uiTemplateColorPicker(col, ptr, "lift", 1, 1, 0, 1);
+ uiTemplateColorPicker(col, ptr, "lift", true, true, false, true);
row = uiLayoutRow(col, false);
- uiItemR(row, ptr, "lift", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(row, ptr, "lift", DEFAULT_FLAGS, nullptr, ICON_NONE);
col = uiLayoutColumn(split, false);
- uiTemplateColorPicker(col, ptr, "gamma", 1, 1, 1, 1);
+ uiTemplateColorPicker(col, ptr, "gamma", true, true, true, true);
row = uiLayoutRow(col, false);
- uiItemR(row, ptr, "gamma", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(row, ptr, "gamma", DEFAULT_FLAGS, nullptr, ICON_NONE);
col = uiLayoutColumn(split, false);
- uiTemplateColorPicker(col, ptr, "gain", 1, 1, 1, 1);
+ uiTemplateColorPicker(col, ptr, "gain", true, true, true, true);
row = uiLayoutRow(col, false);
- uiItemR(row, ptr, "gain", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(row, ptr, "gain", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
else {
split = uiLayoutSplit(layout, 0.0f, false);
col = uiLayoutColumn(split, false);
- uiTemplateColorPicker(col, ptr, "offset", 1, 1, 0, 1);
+ uiTemplateColorPicker(col, ptr, "offset", true, true, false, true);
row = uiLayoutRow(col, false);
- uiItemR(row, ptr, "offset", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(col, ptr, "offset_basis", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(row, ptr, "offset", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "offset_basis", DEFAULT_FLAGS, nullptr, ICON_NONE);
col = uiLayoutColumn(split, false);
- uiTemplateColorPicker(col, ptr, "power", 1, 1, 0, 1);
+ uiTemplateColorPicker(col, ptr, "power", true, true, false, true);
row = uiLayoutRow(col, false);
- uiItemR(row, ptr, "power", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(row, ptr, "power", DEFAULT_FLAGS, nullptr, ICON_NONE);
col = uiLayoutColumn(split, false);
- uiTemplateColorPicker(col, ptr, "slope", 1, 1, 0, 1);
+ uiTemplateColorPicker(col, ptr, "slope", true, true, false, true);
row = uiLayoutRow(col, false);
- uiItemR(row, ptr, "slope", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(row, ptr, "slope", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
}
static void node_composit_buts_colorbalance_ex(uiLayout *layout,
bContext *UNUSED(C),
PointerRNA *ptr)
{
- uiItemR(layout, ptr, "correction_method", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "correction_method", DEFAULT_FLAGS, nullptr, ICON_NONE);
if (RNA_enum_get(ptr, "correction_method") == 0) {
- uiTemplateColorPicker(layout, ptr, "lift", 1, 1, 0, 1);
- uiItemR(layout, ptr, "lift", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiTemplateColorPicker(layout, ptr, "lift", true, true, false, true);
+ uiItemR(layout, ptr, "lift", DEFAULT_FLAGS, nullptr, ICON_NONE);
- uiTemplateColorPicker(layout, ptr, "gamma", 1, 1, 1, 1);
- uiItemR(layout, ptr, "gamma", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiTemplateColorPicker(layout, ptr, "gamma", true, true, true, true);
+ uiItemR(layout, ptr, "gamma", DEFAULT_FLAGS, nullptr, ICON_NONE);
- uiTemplateColorPicker(layout, ptr, "gain", 1, 1, 1, 1);
- uiItemR(layout, ptr, "gain", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiTemplateColorPicker(layout, ptr, "gain", true, true, true, true);
+ uiItemR(layout, ptr, "gain", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
else {
- uiTemplateColorPicker(layout, ptr, "offset", 1, 1, 0, 1);
- uiItemR(layout, ptr, "offset", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiTemplateColorPicker(layout, ptr, "offset", true, true, false, true);
+ uiItemR(layout, ptr, "offset", DEFAULT_FLAGS, nullptr, ICON_NONE);
- uiTemplateColorPicker(layout, ptr, "power", 1, 1, 0, 1);
- uiItemR(layout, ptr, "power", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiTemplateColorPicker(layout, ptr, "power", true, true, false, true);
+ uiItemR(layout, ptr, "power", DEFAULT_FLAGS, nullptr, ICON_NONE);
- uiTemplateColorPicker(layout, ptr, "slope", 1, 1, 0, 1);
- uiItemR(layout, ptr, "slope", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiTemplateColorPicker(layout, ptr, "slope", true, true, false, true);
+ uiItemR(layout, ptr, "slope", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
}
static void node_composit_buts_huecorrect(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- bNode *node = ptr->data;
- CurveMapping *cumap = node->storage;
+ bNode *node = (bNode *)ptr->data;
+ CurveMapping *cumap = (CurveMapping *)node->storage;
if (_sample_col[0] != SAMPLE_FLT_ISNONE) {
cumap->flag |= CUMA_DRAW_SAMPLE;
@@ -2142,17 +2175,33 @@ static void node_composit_buts_ycc(uiLayout *layout, bContext *UNUSED(C), Pointe
static void node_composit_buts_movieclip(uiLayout *layout, bContext *C, PointerRNA *ptr)
{
- uiTemplateID(
- layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL);
+ uiTemplateID(layout,
+ C,
+ ptr,
+ "clip",
+ nullptr,
+ "CLIP_OT_open",
+ nullptr,
+ UI_TEMPLATE_ID_FILTER_ALL,
+ false,
+ nullptr);
}
static void node_composit_buts_movieclip_ex(uiLayout *layout, bContext *C, PointerRNA *ptr)
{
- bNode *node = ptr->data;
+ bNode *node = (bNode *)ptr->data;
PointerRNA clipptr;
- uiTemplateID(
- layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL);
+ uiTemplateID(layout,
+ C,
+ ptr,
+ "clip",
+ nullptr,
+ "CLIP_OT_open",
+ nullptr,
+ UI_TEMPLATE_ID_FILTER_ALL,
+ false,
+ nullptr);
if (!node->id) {
return;
@@ -2165,23 +2214,31 @@ static void node_composit_buts_movieclip_ex(uiLayout *layout, bContext *C, Point
static void node_composit_buts_stabilize2d(uiLayout *layout, bContext *C, PointerRNA *ptr)
{
- bNode *node = ptr->data;
+ bNode *node = (bNode *)ptr->data;
- uiTemplateID(
- layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL);
+ uiTemplateID(layout,
+ C,
+ ptr,
+ "clip",
+ nullptr,
+ "CLIP_OT_open",
+ nullptr,
+ UI_TEMPLATE_ID_FILTER_ALL,
+ false,
+ nullptr);
if (!node->id) {
return;
}
uiItemR(layout, ptr, "filter_type", DEFAULT_FLAGS, "", ICON_NONE);
- uiItemR(layout, ptr, "invert", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "invert", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_translate(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- uiItemR(layout, ptr, "use_relative", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "wrap_axis", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "use_relative", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "wrap_axis", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_transform(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -2191,10 +2248,18 @@ static void node_composit_buts_transform(uiLayout *layout, bContext *UNUSED(C),
static void node_composit_buts_moviedistortion(uiLayout *layout, bContext *C, PointerRNA *ptr)
{
- bNode *node = ptr->data;
+ bNode *node = (bNode *)ptr->data;
- uiTemplateID(
- layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL);
+ uiTemplateID(layout,
+ C,
+ ptr,
+ "clip",
+ nullptr,
+ "CLIP_OT_open",
+ nullptr,
+ UI_TEMPLATE_ID_FILTER_ALL,
+ false,
+ nullptr);
if (!node->id) {
return;
@@ -2210,9 +2275,9 @@ static void node_composit_buts_colorcorrection(uiLayout *layout,
uiLayout *row;
row = uiLayoutRow(layout, false);
- uiItemR(row, ptr, "red", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(row, ptr, "green", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(row, ptr, "blue", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(row, ptr, "red", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "green", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "blue", DEFAULT_FLAGS, nullptr, ICON_NONE);
row = uiLayoutRow(layout, false);
uiItemL(row, "", ICON_NONE);
@@ -2255,8 +2320,8 @@ static void node_composit_buts_colorcorrection(uiLayout *layout,
uiItemR(row, ptr, "shadows_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE);
row = uiLayoutRow(layout, false);
- uiItemR(row, ptr, "midtones_start", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(row, ptr, "midtones_end", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+ uiItemR(row, ptr, "midtones_start", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "midtones_end", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
static void node_composit_buts_colorcorrection_ex(uiLayout *layout,
@@ -2266,53 +2331,53 @@ static void node_composit_buts_colorcorrection_ex(uiLayout *layout,
uiLayout *row;
row = uiLayoutRow(layout, false);
- uiItemR(row, ptr, "red", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(row, ptr, "green", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(row, ptr, "blue", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(row, ptr, "red", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "green", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "blue", DEFAULT_FLAGS, nullptr, ICON_NONE);
row = layout;
uiItemL(row, IFACE_("Saturation"), ICON_NONE);
- uiItemR(row, ptr, "master_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(row, ptr, "highlights_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(row, ptr, "midtones_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(row, ptr, "shadows_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+ uiItemR(row, ptr, "master_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "highlights_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "midtones_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "shadows_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
uiItemL(row, IFACE_("Contrast"), ICON_NONE);
- uiItemR(row, ptr, "master_contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(row, ptr, "highlights_contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(row, ptr, "midtones_contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(row, ptr, "shadows_contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+ uiItemR(row, ptr, "master_contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "highlights_contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "midtones_contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "shadows_contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
uiItemL(row, IFACE_("Gamma"), ICON_NONE);
- uiItemR(row, ptr, "master_gamma", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(row, ptr, "highlights_gamma", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(row, ptr, "midtones_gamma", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(row, ptr, "shadows_gamma", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+ uiItemR(row, ptr, "master_gamma", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "highlights_gamma", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "midtones_gamma", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "shadows_gamma", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
uiItemL(row, IFACE_("Gain"), ICON_NONE);
- uiItemR(row, ptr, "master_gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(row, ptr, "highlights_gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(row, ptr, "midtones_gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(row, ptr, "shadows_gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+ uiItemR(row, ptr, "master_gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "highlights_gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "midtones_gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "shadows_gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
uiItemL(row, IFACE_("Lift"), ICON_NONE);
- uiItemR(row, ptr, "master_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(row, ptr, "highlights_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(row, ptr, "midtones_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(row, ptr, "shadows_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+ uiItemR(row, ptr, "master_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "highlights_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "midtones_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "shadows_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
row = uiLayoutRow(layout, false);
- uiItemR(row, ptr, "midtones_start", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(row, ptr, "midtones_end", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(row, ptr, "midtones_start", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "midtones_end", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_set_alpha(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- uiItemR(layout, ptr, "mode", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "mode", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_switch(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- uiItemR(layout, ptr, "check", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "check", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_switch_view_ex(uiLayout *layout,
@@ -2323,10 +2388,10 @@ static void node_composit_buts_switch_view_ex(uiLayout *layout,
"NODE_OT_switch_view_update",
"Update Views",
ICON_FILE_REFRESH,
- NULL,
+ nullptr,
WM_OP_INVOKE_DEFAULT,
0,
- NULL);
+ nullptr);
}
static void node_composit_buts_boxmask(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -2334,32 +2399,32 @@ static void node_composit_buts_boxmask(uiLayout *layout, bContext *UNUSED(C), Po
uiLayout *row;
row = uiLayoutRow(layout, true);
- uiItemR(row, ptr, "x", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(row, ptr, "y", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(row, ptr, "x", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "y", DEFAULT_FLAGS, nullptr, ICON_NONE);
row = uiLayoutRow(layout, true);
- uiItemR(row, ptr, "width", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(row, ptr, "height", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+ uiItemR(row, ptr, "width", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "height", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
- uiItemR(layout, ptr, "rotation", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "mask_type", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "rotation", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "mask_type", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_bokehimage(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- uiItemR(layout, ptr, "flaps", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "angle", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "rounding", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(layout, ptr, "catadioptric", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(layout, ptr, "shift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "flaps", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "angle", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "rounding", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "catadioptric", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "shift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
static void node_composit_buts_bokehblur(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- uiItemR(layout, ptr, "use_variable_size", DEFAULT_FLAGS, NULL, ICON_NONE);
- // uiItemR(layout, ptr, "f_stop", DEFAULT_FLAGS, NULL, ICON_NONE); /* UNUSED */
- uiItemR(layout, ptr, "blur_max", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "use_extended_bounds", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "use_variable_size", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ // uiItemR(layout, ptr, "f_stop", DEFAULT_FLAGS, nullptr, ICON_NONE); /* UNUSED */
+ uiItemR(layout, ptr, "blur_max", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "use_extended_bounds", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_backdrop_viewer(
@@ -2394,7 +2459,7 @@ static void node_composit_backdrop_viewer(
static void node_composit_backdrop_boxmask(
SpaceNode *snode, ImBuf *backdrop, bNode *node, int x, int y)
{
- NodeBoxMask *boxmask = node->storage;
+ NodeBoxMask *boxmask = (NodeBoxMask *)node->storage;
const float backdropWidth = backdrop->x;
const float backdropHeight = backdrop->y;
const float aspect = backdropWidth / backdropHeight;
@@ -2439,7 +2504,7 @@ static void node_composit_backdrop_boxmask(
static void node_composit_backdrop_ellipsemask(
SpaceNode *snode, ImBuf *backdrop, bNode *node, int x, int y)
{
- NodeEllipseMask *ellipsemask = node->storage;
+ NodeEllipseMask *ellipsemask = (NodeEllipseMask *)node->storage;
const float backdropWidth = backdrop->x;
const float backdropHeight = backdrop->y;
const float aspect = backdropWidth / backdropHeight;
@@ -2485,65 +2550,83 @@ static void node_composit_buts_ellipsemask(uiLayout *layout, bContext *UNUSED(C)
{
uiLayout *row;
row = uiLayoutRow(layout, true);
- uiItemR(row, ptr, "x", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(row, ptr, "y", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(row, ptr, "x", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "y", DEFAULT_FLAGS, nullptr, ICON_NONE);
row = uiLayoutRow(layout, true);
- uiItemR(row, ptr, "width", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
- uiItemR(row, ptr, "height", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+ uiItemR(row, ptr, "width", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
+ uiItemR(row, ptr, "height", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
- uiItemR(layout, ptr, "rotation", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "mask_type", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "rotation", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "mask_type", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_composite(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- uiItemR(layout, ptr, "use_alpha", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "use_alpha", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_viewer(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- uiItemR(layout, ptr, "use_alpha", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "use_alpha", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_viewer_ex(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiLayout *col;
- uiItemR(layout, ptr, "use_alpha", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "tile_order", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "use_alpha", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "tile_order", DEFAULT_FLAGS, nullptr, ICON_NONE);
if (RNA_enum_get(ptr, "tile_order") == 0) {
col = uiLayoutColumn(layout, true);
- uiItemR(col, ptr, "center_x", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(col, ptr, "center_y", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "center_x", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(col, ptr, "center_y", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
}
static void node_composit_buts_mask(uiLayout *layout, bContext *C, PointerRNA *ptr)
{
- bNode *node = ptr->data;
+ bNode *node = (bNode *)ptr->data;
- uiTemplateID(layout, C, ptr, "mask", NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL);
- uiItemR(layout, ptr, "use_feather", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiTemplateID(layout,
+ C,
+ ptr,
+ "mask",
+ nullptr,
+ nullptr,
+ nullptr,
+ UI_TEMPLATE_ID_FILTER_ALL,
+ false,
+ nullptr);
+ uiItemR(layout, ptr, "use_feather", DEFAULT_FLAGS, nullptr, ICON_NONE);
uiItemR(layout, ptr, "size_source", DEFAULT_FLAGS, "", ICON_NONE);
if (node->custom1 & (CMP_NODEFLAG_MASK_FIXED | CMP_NODEFLAG_MASK_FIXED_SCENE)) {
- uiItemR(layout, ptr, "size_x", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "size_y", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "size_x", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "size_y", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
- uiItemR(layout, ptr, "use_motion_blur", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "use_motion_blur", DEFAULT_FLAGS, nullptr, ICON_NONE);
if (node->custom1 & CMP_NODEFLAG_MASK_MOTION_BLUR) {
- uiItemR(layout, ptr, "motion_blur_samples", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "motion_blur_shutter", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "motion_blur_samples", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "motion_blur_shutter", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
}
static void node_composit_buts_keyingscreen(uiLayout *layout, bContext *C, PointerRNA *ptr)
{
- bNode *node = ptr->data;
+ bNode *node = (bNode *)ptr->data;
- uiTemplateID(layout, C, ptr, "clip", NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL);
+ uiTemplateID(layout,
+ C,
+ ptr,
+ "clip",
+ nullptr,
+ nullptr,
+ nullptr,
+ UI_TEMPLATE_ID_FILTER_ALL,
+ false,
+ nullptr);
if (node->id) {
MovieClip *clip = (MovieClip *)node->id;
@@ -2559,28 +2642,36 @@ static void node_composit_buts_keyingscreen(uiLayout *layout, bContext *C, Point
static void node_composit_buts_keying(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
- /* bNode *node = ptr->data; */ /* UNUSED */
+ /* bNode *node = (bNode*)ptr->data; */ /* UNUSED */
- uiItemR(layout, ptr, "blur_pre", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "screen_balance", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "despill_factor", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "despill_balance", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "edge_kernel_radius", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "edge_kernel_tolerance", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "clip_black", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "clip_white", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "dilate_distance", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "feather_falloff", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "feather_distance", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "blur_post", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "blur_pre", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "screen_balance", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "despill_factor", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "despill_balance", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "edge_kernel_radius", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "edge_kernel_tolerance", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "clip_black", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "clip_white", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "dilate_distance", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "feather_falloff", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "feather_distance", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "blur_post", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_trackpos(uiLayout *layout, bContext *C, PointerRNA *ptr)
{
- bNode *node = ptr->data;
+ bNode *node = (bNode *)ptr->data;
- uiTemplateID(
- layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL);
+ uiTemplateID(layout,
+ C,
+ ptr,
+ "clip",
+ nullptr,
+ "CLIP_OT_open",
+ nullptr,
+ UI_TEMPLATE_ID_FILTER_ALL,
+ false,
+ nullptr);
if (node->id) {
MovieClip *clip = (MovieClip *)node->id;
@@ -2588,7 +2679,7 @@ static void node_composit_buts_trackpos(uiLayout *layout, bContext *C, PointerRN
MovieTrackingObject *object;
uiLayout *col;
PointerRNA tracking_ptr;
- NodeTrackPosData *data = node->storage;
+ NodeTrackPosData *data = (NodeTrackPosData *)node->storage;
RNA_pointer_create(&clip->id, &RNA_MovieTracking, tracking, &tracking_ptr);
@@ -2607,21 +2698,29 @@ static void node_composit_buts_trackpos(uiLayout *layout, bContext *C, PointerRN
uiItemR(layout, ptr, "track_name", DEFAULT_FLAGS, "", ICON_ANIM_DATA);
}
- uiItemR(layout, ptr, "position", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "position", DEFAULT_FLAGS, nullptr, ICON_NONE);
if (ELEM(node->custom1, CMP_TRACKPOS_RELATIVE_FRAME, CMP_TRACKPOS_ABSOLUTE_FRAME)) {
- uiItemR(layout, ptr, "frame_relative", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "frame_relative", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
}
}
static void node_composit_buts_planetrackdeform(uiLayout *layout, bContext *C, PointerRNA *ptr)
{
- bNode *node = ptr->data;
- NodePlaneTrackDeformData *data = node->storage;
+ bNode *node = (bNode *)ptr->data;
+ NodePlaneTrackDeformData *data = (NodePlaneTrackDeformData *)node->storage;
- uiTemplateID(
- layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL);
+ uiTemplateID(layout,
+ C,
+ ptr,
+ "clip",
+ nullptr,
+ "CLIP_OT_open",
+ nullptr,
+ UI_TEMPLATE_ID_FILTER_ALL,
+ false,
+ nullptr);
if (node->id) {
MovieClip *clip = (MovieClip *)node->id;
@@ -2649,10 +2748,10 @@ static void node_composit_buts_planetrackdeform(uiLayout *layout, bContext *C, P
}
}
- uiItemR(layout, ptr, "use_motion_blur", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "use_motion_blur", DEFAULT_FLAGS, nullptr, ICON_NONE);
if (data->flag & CMP_NODEFLAG_PLANETRACKDEFORM_MOTION_BLUR) {
- uiItemR(layout, ptr, "motion_blur_samples", DEFAULT_FLAGS, NULL, ICON_NONE);
- uiItemR(layout, ptr, "motion_blur_shutter", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "motion_blur_samples", DEFAULT_FLAGS, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "motion_blur_shutter", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
}
@@ -2665,7 +2764,7 @@ static void node_composit_buts_cornerpin(uiLayout *UNUSED(layout),
static void node_composit_buts_sunbeams(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "source", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, "", ICON_NONE);
- uiItemR(layout, ptr, "ray_length", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "ray_length", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
static void node_composit_buts_cryptomatte_legacy(uiLayout *layout,
@@ -2693,18 +2792,35 @@ static void node_composit_buts_cryptomatte_legacy_ex(uiLayout *layout,
static void node_composit_buts_cryptomatte(uiLayout *layout, bContext *C, PointerRNA *ptr)
{
- bNode *node = ptr->data;
+ bNode *node = (bNode *)ptr->data;
uiLayout *row = uiLayoutRow(layout, true);
- uiItemR(row, ptr, "source", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, ptr, "source", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
uiLayout *col = uiLayoutColumn(layout, false);
if (node->custom1 == CMP_CRYPTOMATTE_SRC_RENDER) {
- uiTemplateID(col, C, ptr, "scene", NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL);
+ uiTemplateID(col,
+ C,
+ ptr,
+ "scene",
+ nullptr,
+ nullptr,
+ nullptr,
+ UI_TEMPLATE_ID_FILTER_ALL,
+ false,
+ nullptr);
}
else {
- uiTemplateID(
- col, C, ptr, "image", NULL, "IMAGE_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL);
+ uiTemplateID(col,
+ C,
+ ptr,
+ "image",
+ nullptr,
+ "IMAGE_OT_open",
+ nullptr,
+ UI_TEMPLATE_ID_FILTER_ALL,
+ false,
+ nullptr);
NodeCryptomatte *crypto = (NodeCryptomatte *)node->storage;
PointerRNA imaptr = RNA_pointer_get(ptr, "image");
@@ -2730,7 +2846,7 @@ static void node_composit_buts_brightcontrast(uiLayout *layout,
bContext *UNUSED(C),
PointerRNA *ptr)
{
- uiItemR(layout, ptr, "use_premultiply", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "use_premultiply", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
static void node_composit_buts_denoise(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -2738,12 +2854,15 @@ static void node_composit_buts_denoise(uiLayout *layout, bContext *UNUSED(C), Po
#ifndef WITH_OPENIMAGEDENOISE
uiItemL(layout, IFACE_("Disabled, built without OpenImageDenoise"), ICON_ERROR);
#else
+ /* Always supported through Accelerate framework BNNS on macOS. */
+# ifndef __APPLE__
if (!BLI_cpu_support_sse41()) {
uiItemL(layout, IFACE_("Disabled, CPU with SSE4.1 is required"), ICON_ERROR);
}
+# endif
#endif
- uiItemR(layout, ptr, "use_hdr", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "use_hdr", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
/* only once called */
@@ -2799,6 +2918,9 @@ static void node_composit_set_butfunc(bNodeType *ntype)
case CMP_NODE_DEFOCUS:
ntype->draw_buttons = node_composit_buts_defocus;
break;
+ case CMP_NODE_ANTIALIASING:
+ ntype->draw_buttons = node_composit_buts_antialiasing;
+ break;
case CMP_NODE_GLARE:
ntype->draw_buttons = node_composit_buts_glare;
break;
@@ -3010,7 +3132,7 @@ static void node_texture_buts_bricks(uiLayout *layout, bContext *UNUSED(C), Poin
static void node_texture_buts_proc(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
PointerRNA tex_ptr;
- bNode *node = ptr->data;
+ bNode *node = (bNode *)ptr->data;
ID *id = ptr->owner_id;
Tex *tex = (Tex *)node->storage;
uiLayout *col, *row;
@@ -3023,29 +3145,31 @@ static void node_texture_buts_proc(uiLayout *layout, bContext *UNUSED(C), Pointe
case TEX_BLEND:
uiItemR(col, &tex_ptr, "progression", DEFAULT_FLAGS, "", ICON_NONE);
row = uiLayoutRow(col, false);
- uiItemR(row, &tex_ptr, "use_flip_axis", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(
+ row, &tex_ptr, "use_flip_axis", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
break;
case TEX_MARBLE:
row = uiLayoutRow(col, false);
- uiItemR(row, &tex_ptr, "marble_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, &tex_ptr, "marble_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
row = uiLayoutRow(col, false);
- uiItemR(row, &tex_ptr, "noise_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, &tex_ptr, "noise_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
row = uiLayoutRow(col, false);
uiItemR(row, &tex_ptr, "noise_basis", DEFAULT_FLAGS, "", ICON_NONE);
row = uiLayoutRow(col, false);
- uiItemR(row, &tex_ptr, "noise_basis_2", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(
+ row, &tex_ptr, "noise_basis_2", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
break;
case TEX_MAGIC:
- uiItemR(col, &tex_ptr, "noise_depth", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, &tex_ptr, "noise_depth", DEFAULT_FLAGS, nullptr, ICON_NONE);
break;
case TEX_STUCCI:
row = uiLayoutRow(col, false);
- uiItemR(row, &tex_ptr, "stucci_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, &tex_ptr, "stucci_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
row = uiLayoutRow(col, false);
- uiItemR(row, &tex_ptr, "noise_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, &tex_ptr, "noise_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
uiItemR(col, &tex_ptr, "noise_basis", DEFAULT_FLAGS, "", ICON_NONE);
break;
@@ -3053,18 +3177,19 @@ static void node_texture_buts_proc(uiLayout *layout, bContext *UNUSED(C), Pointe
uiItemR(col, &tex_ptr, "noise_basis", DEFAULT_FLAGS, "", ICON_NONE);
uiItemR(col, &tex_ptr, "wood_type", DEFAULT_FLAGS, "", ICON_NONE);
row = uiLayoutRow(col, false);
- uiItemR(row, &tex_ptr, "noise_basis_2", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(
+ row, &tex_ptr, "noise_basis_2", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
row = uiLayoutRow(col, false);
uiLayoutSetActive(row, !(ELEM(tex->stype, TEX_BAND, TEX_RING)));
- uiItemR(row, &tex_ptr, "noise_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, &tex_ptr, "noise_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
break;
case TEX_CLOUDS:
uiItemR(col, &tex_ptr, "noise_basis", DEFAULT_FLAGS, "", ICON_NONE);
row = uiLayoutRow(col, false);
- uiItemR(row, &tex_ptr, "cloud_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, &tex_ptr, "cloud_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
row = uiLayoutRow(col, false);
- uiItemR(row, &tex_ptr, "noise_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(row, &tex_ptr, "noise_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
uiItemR(col,
&tex_ptr,
"noise_depth",
@@ -3085,7 +3210,7 @@ static void node_texture_buts_proc(uiLayout *layout, bContext *UNUSED(C), Pointe
case TEX_VORONOI:
uiItemR(col, &tex_ptr, "distance_metric", DEFAULT_FLAGS, "", ICON_NONE);
if (tex->vn_distm == TEX_MINKOVSKY) {
- uiItemR(col, &tex_ptr, "minkovsky_exponent", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, &tex_ptr, "minkovsky_exponent", DEFAULT_FLAGS, nullptr, ICON_NONE);
}
uiItemR(col, &tex_ptr, "color_mode", DEFAULT_FLAGS, "", ICON_NONE);
break;
@@ -3100,19 +3225,19 @@ static void node_texture_buts_image(uiLayout *layout, bContext *C, PointerRNA *p
"image",
"IMAGE_OT_new",
"IMAGE_OT_open",
- NULL,
+ nullptr,
UI_TEMPLATE_ID_FILTER_ALL,
false,
- NULL);
+ nullptr);
}
static void node_texture_buts_image_ex(uiLayout *layout, bContext *C, PointerRNA *ptr)
{
- bNode *node = ptr->data;
+ bNode *node = (bNode *)ptr->data;
PointerRNA iuserptr;
RNA_pointer_create(ptr->owner_id, &RNA_ImageUser, node->storage, &iuserptr);
- uiTemplateImage(layout, C, ptr, "image", &iuserptr, 0, 0);
+ uiTemplateImage(layout, C, ptr, "image", &iuserptr, false, false);
}
static void node_texture_buts_output(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
@@ -3169,12 +3294,16 @@ static void node_texture_set_butfunc(bNodeType *ntype)
}
}
-/* ****** init draw callbacks for all tree types, only called in usiblender.c, once ************ */
+/* -------------------------------------------------------------------- */
+/** \name Init Draw Callbacks For All Tree Types
+ *
+ * Only called on node initialization, once.
+ * \{ */
static void node_property_update_default(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr)
{
bNodeTree *ntree = (bNodeTree *)ptr->owner_id;
- bNode *node = ptr->data;
+ bNode *node = (bNode *)ptr->data;
ED_node_tag_update_nodetree(bmain, ntree, node);
}
@@ -3184,7 +3313,7 @@ static void node_socket_template_properties_update(bNodeType *ntype, bNodeSocket
PropertyRNA *prop = RNA_struct_type_find_property(srna, stemp->identifier);
if (prop) {
- RNA_def_property_update_runtime(prop, node_property_update_default);
+ RNA_def_property_update_runtime(prop, (const void *)node_property_update_default);
}
}
@@ -3241,6 +3370,8 @@ static void node_socket_undefined_interface_draw_color(bContext *UNUSED(C),
r_color[3] = 1.0f;
}
+/** \} */
+
void ED_node_init_butfuncs(void)
{
/* Fallback types for undefined tree, nodes, sockets
@@ -3252,8 +3383,8 @@ void ED_node_init_butfuncs(void)
NodeTypeUndefined.draw_nodetype_prepare = node_update_default;
NodeTypeUndefined.select_area_func = node_select_area_default;
NodeTypeUndefined.tweak_area_func = node_tweak_area_default;
- NodeTypeUndefined.draw_buttons = NULL;
- NodeTypeUndefined.draw_buttons_ex = NULL;
+ NodeTypeUndefined.draw_buttons = nullptr;
+ NodeTypeUndefined.draw_buttons_ex = nullptr;
NodeTypeUndefined.resize_area_func = node_resize_area_default;
NodeSocketTypeUndefined.draw = node_socket_undefined_draw;
@@ -3314,12 +3445,14 @@ static const float std_node_socket_colors[][4] = {
{0.39, 0.78, 0.39, 1.0}, /* SOCK_SHADER */
{0.80, 0.65, 0.84, 1.0}, /* SOCK_BOOLEAN */
{0.0, 0.0, 0.0, 1.0}, /*__SOCK_MESH (deprecated) */
- {0.25, 0.75, 0.26, 1.0}, /* SOCK_INT */
+ {0.35, 0.55, 0.36, 1.0}, /* SOCK_INT */
{0.44, 0.70, 1.00, 1.0}, /* SOCK_STRING */
{0.93, 0.62, 0.36, 1.0}, /* SOCK_OBJECT */
- {0.89, 0.76, 0.43, 1.0}, /* SOCK_IMAGE */
+ {0.39, 0.22, 0.39, 1.0}, /* SOCK_IMAGE */
{0.00, 0.84, 0.64, 1.0}, /* SOCK_GEOMETRY */
{0.96, 0.96, 0.96, 1.0}, /* SOCK_COLLECTION */
+ {0.62, 0.31, 0.64, 1.0}, /* SOCK_TEXTURE */
+ {0.92, 0.46, 0.51, 1.0}, /* SOCK_MATERIAL */
};
/* common color callbacks for standard types */
@@ -3328,7 +3461,7 @@ static void std_node_socket_draw_color(bContext *UNUSED(C),
PointerRNA *UNUSED(node_ptr),
float *r_color)
{
- bNodeSocket *sock = ptr->data;
+ bNodeSocket *sock = (bNodeSocket *)ptr->data;
int type = sock->typeinfo->type;
copy_v4_v4(r_color, std_node_socket_colors[type]);
}
@@ -3336,7 +3469,7 @@ static void std_node_socket_interface_draw_color(bContext *UNUSED(C),
PointerRNA *ptr,
float *r_color)
{
- bNodeSocket *sock = ptr->data;
+ bNodeSocket *sock = (bNodeSocket *)ptr->data;
int type = sock->typeinfo->type;
copy_v4_v4(r_color, std_node_socket_colors[type]);
}
@@ -3349,7 +3482,7 @@ static void node_file_output_socket_draw(bContext *C,
PointerRNA *node_ptr)
{
bNodeTree *ntree = (bNodeTree *)ptr->owner_id;
- bNodeSocket *sock = ptr->data;
+ bNodeSocket *sock = (bNodeSocket *)ptr->data;
uiLayout *row;
PointerRNA inputptr;
@@ -3359,13 +3492,13 @@ static void node_file_output_socket_draw(bContext *C,
int imtype = RNA_enum_get(&imfptr, "file_format");
if (imtype == R_IMF_IMTYPE_MULTILAYER) {
- NodeImageMultiFileSocket *input = sock->storage;
+ NodeImageMultiFileSocket *input = (NodeImageMultiFileSocket *)sock->storage;
RNA_pointer_create(&ntree->id, &RNA_NodeOutputFileSlotLayer, input, &inputptr);
uiItemL(row, input->layer, ICON_NONE);
}
else {
- NodeImageMultiFileSocket *input = sock->storage;
+ NodeImageMultiFileSocket *input = (NodeImageMultiFileSocket *)sock->storage;
uiBlock *block;
RNA_pointer_create(&ntree->id, &RNA_NodeOutputFileSlotFile, input, &inputptr);
@@ -3392,8 +3525,8 @@ static void node_file_output_socket_draw(bContext *C,
static void std_node_socket_draw(
bContext *C, uiLayout *layout, PointerRNA *ptr, PointerRNA *node_ptr, const char *text)
{
- bNode *node = node_ptr->data;
- bNodeSocket *sock = ptr->data;
+ bNode *node = (bNode *)node_ptr->data;
+ bNodeSocket *sock = (bNodeSocket *)ptr->data;
int type = sock->typeinfo->type;
/*int subtype = sock->typeinfo->subtype;*/
@@ -3408,6 +3541,8 @@ static void std_node_socket_draw(
return;
}
+ text = (sock->flag & SOCK_HIDE_LABEL) ? "" : text;
+
switch (type) {
case SOCK_FLOAT:
case SOCK_INT:
@@ -3440,7 +3575,7 @@ static void std_node_socket_draw(
const bNodeTree *node_tree = (const bNodeTree *)node_ptr->owner_id;
if (node_tree->type == NTREE_GEOMETRY) {
- node_geometry_add_attribute_search_button(node_tree, node, ptr, row);
+ node_geometry_add_attribute_search_button(C, node_tree, node, ptr, row);
}
else {
uiItemR(row, ptr, "default_value", DEFAULT_FLAGS, "", 0);
@@ -3460,6 +3595,15 @@ static void std_node_socket_draw(
uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, text, 0);
break;
}
+ case SOCK_TEXTURE: {
+ uiTemplateID(
+ layout, C, ptr, "default_value", "texture.new", nullptr, nullptr, 0, ICON_NONE, nullptr);
+ break;
+ }
+ case SOCK_MATERIAL: {
+ uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, text, 0);
+ break;
+ }
default:
node_socket_button_label(C, layout, ptr, node_ptr, text);
break;
@@ -3468,7 +3612,7 @@ static void std_node_socket_draw(
static void std_node_socket_interface_draw(bContext *UNUSED(C), uiLayout *layout, PointerRNA *ptr)
{
- bNodeSocket *sock = ptr->data;
+ bNodeSocket *sock = (bNodeSocket *)ptr->data;
int type = sock->typeinfo->type;
uiLayout *col = uiLayoutColumn(layout, false);
@@ -3503,7 +3647,7 @@ static void std_node_socket_interface_draw(bContext *UNUSED(C), uiLayout *layout
}
}
- uiItemR(layout, ptr, "hide_value", DEFAULT_FLAGS, NULL, 0);
+ uiItemR(layout, ptr, "hide_value", DEFAULT_FLAGS, nullptr, 0);
}
void ED_init_standard_node_socket_type(bNodeSocketType *stype)
@@ -3570,7 +3714,7 @@ void draw_nodespace_back_pix(const bContext *C,
void *lock;
Image *ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node");
- ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock);
+ ImBuf *ibuf = BKE_image_acquire_ibuf(ima, nullptr, &lock);
if (ibuf) {
/* somehow the offset has to be calculated inverse */
wmOrtho2_region_pixelspace(region);
@@ -3579,7 +3723,7 @@ void draw_nodespace_back_pix(const bContext *C,
/** \note draw selected info on backdrop */
if (snode->edittree) {
- bNode *node = snode->edittree->nodes.first;
+ bNode *node = (bNode *)snode->edittree->nodes.first;
rctf *viewer_border = &snode->nodetree->viewer_border;
while (node) {
if (node->flag & NODE_SELECT) {
@@ -3616,7 +3760,7 @@ void draw_nodespace_back_pix(const bContext *C,
GPU_matrix_pop();
}
-/* return quadratic beziers points for a given nodelink and clip if v2d is not NULL. */
+/* return quadratic beziers points for a given nodelink and clip if v2d is not nullptr. */
bool node_link_bezier_handles(const View2D *v2d,
const SpaceNode *snode,
const bNodeLink *link,
@@ -3646,7 +3790,7 @@ bool node_link_bezier_handles(const View2D *v2d,
fromreroute = (link->fromnode && link->fromnode->type == NODE_REROUTE);
}
else {
- if (snode == NULL) {
+ if (snode == nullptr) {
return false;
}
copy_v2_v2(vec[0], cursor);
@@ -3665,7 +3809,7 @@ bool node_link_bezier_handles(const View2D *v2d,
toreroute = (link->tonode && link->tonode->type == NODE_REROUTE);
}
else {
- if (snode == NULL) {
+ if (snode == nullptr) {
return false;
}
copy_v2_v2(vec[3], cursor);
@@ -3725,7 +3869,7 @@ bool node_link_bezier_handles(const View2D *v2d,
return true;
}
-/* if v2d not NULL, it clips and returns 0 if not visible */
+/* if v2d not nullptr, it clips and returns 0 if not visible */
bool node_link_bezier_points(const View2D *v2d,
const SpaceNode *snode,
const bNodeLink *link,
@@ -3758,6 +3902,7 @@ static float arrow_expand_axis[3][2] = {{0.7071f, 0.7071f}, {M_SQRT2, 0.0f}, {0.
static float mute_verts[3][2] = {{0.7071f, 1.0f}, {0.7071f, 0.0f}, {0.7071f, -1.0f}};
static float mute_expand_axis[3][2] = {{1.0f, 0.0f}, {1.0f, 0.0f}, {1.0f, -0.0f}};
+/* Is zero initialized because it is static data. */
static struct {
GPUBatch *batch; /* for batching line together */
GPUBatch *batch_single; /* for single line */
@@ -3768,9 +3913,9 @@ static struct {
GPUVertBufRaw colid_step, muted_step;
uint count;
bool enabled;
-} g_batch_link = {0};
+} g_batch_link;
-static void nodelink_batch_reset(void)
+static void nodelink_batch_reset()
{
GPU_vertbuf_attr_get_raw_data(g_batch_link.inst_vbo, g_batch_link.p0_id, &g_batch_link.p0_step);
GPU_vertbuf_attr_get_raw_data(g_batch_link.inst_vbo, g_batch_link.p1_id, &g_batch_link.p1_step);
@@ -3797,7 +3942,7 @@ static void set_nodelink_vertex(GPUVertBuf *vbo,
GPU_vertbuf_attr_set(vbo, exp_id, v, exp);
}
-static void nodelink_batch_init(void)
+static void nodelink_batch_init()
{
GPUVertFormat format = {0};
uint uv_id = GPU_vertformat_attr_add(&format, "uv", GPU_COMP_U8, 2, GPU_FETCH_INT_TO_FLOAT_UNIT);
@@ -3876,10 +4021,11 @@ static void nodelink_batch_init(void)
}
}
- g_batch_link.batch = GPU_batch_create_ex(GPU_PRIM_TRI_STRIP, vbo, NULL, GPU_BATCH_OWNS_VBO);
+ g_batch_link.batch = GPU_batch_create_ex(GPU_PRIM_TRI_STRIP, vbo, nullptr, GPU_BATCH_OWNS_VBO);
gpu_batch_presets_register(g_batch_link.batch);
- g_batch_link.batch_single = GPU_batch_create_ex(GPU_PRIM_TRI_STRIP, vbo, NULL, 0);
+ g_batch_link.batch_single = GPU_batch_create_ex(
+ GPU_PRIM_TRI_STRIP, vbo, nullptr, GPU_BATCH_INVALID);
gpu_batch_presets_register(g_batch_link.batch_single);
/* Instances data */
@@ -3979,16 +4125,16 @@ static void nodelink_batch_add_link(const SpaceNode *snode,
BLI_assert(ELEM(th_col3, TH_WIRE, TH_REDALERT, -1));
g_batch_link.count++;
- copy_v2_v2(GPU_vertbuf_raw_step(&g_batch_link.p0_step), p0);
- copy_v2_v2(GPU_vertbuf_raw_step(&g_batch_link.p1_step), p1);
- copy_v2_v2(GPU_vertbuf_raw_step(&g_batch_link.p2_step), p2);
- copy_v2_v2(GPU_vertbuf_raw_step(&g_batch_link.p3_step), p3);
- char *colid = GPU_vertbuf_raw_step(&g_batch_link.colid_step);
+ copy_v2_v2((float *)GPU_vertbuf_raw_step(&g_batch_link.p0_step), p0);
+ copy_v2_v2((float *)GPU_vertbuf_raw_step(&g_batch_link.p1_step), p1);
+ copy_v2_v2((float *)GPU_vertbuf_raw_step(&g_batch_link.p2_step), p2);
+ copy_v2_v2((float *)GPU_vertbuf_raw_step(&g_batch_link.p3_step), p3);
+ char *colid = (char *)GPU_vertbuf_raw_step(&g_batch_link.colid_step);
colid[0] = nodelink_get_color_id(th_col1);
colid[1] = nodelink_get_color_id(th_col2);
colid[2] = nodelink_get_color_id(th_col3);
colid[3] = drawarrow;
- char *muted = GPU_vertbuf_raw_step(&g_batch_link.muted_step);
+ char *muted = (char *)GPU_vertbuf_raw_step(&g_batch_link.muted_step);
muted[0] = drawmuted;
if (g_batch_link.count == NODELINK_GROUP_SIZE) {
@@ -4010,7 +4156,7 @@ void node_draw_link_bezier(const View2D *v2d,
int drawarrow = ((link->tonode && (link->tonode->type == NODE_REROUTE)) &&
(link->fromnode && (link->fromnode->type == NODE_REROUTE)));
int drawmuted = (link->flag & NODE_LINK_MUTED);
- if (g_batch_link.batch == NULL) {
+ if (g_batch_link.batch == nullptr) {
nodelink_batch_init();
}
@@ -4052,7 +4198,7 @@ void node_draw_link(View2D *v2d, SpaceNode *snode, bNodeLink *link)
{
int th_col1 = TH_WIRE_INNER, th_col2 = TH_WIRE_INNER, th_col3 = TH_WIRE;
- if (link->fromsock == NULL && link->tosock == NULL) {
+ if (link->fromsock == nullptr && link->tosock == nullptr) {
return;
}
diff --git a/source/blender/editors/space_node/node_add.c b/source/blender/editors/space_node/node_add.cc
index c4fe9e9e531..6143af8ed70 100644
--- a/source/blender/editors/space_node/node_add.c
+++ b/source/blender/editors/space_node/node_add.cc
@@ -41,6 +41,8 @@
#include "BKE_scene.h"
#include "BKE_texture.h"
+#include "DEG_depsgraph_build.h"
+
#include "ED_node.h" /* own include */
#include "ED_render.h"
#include "ED_screen.h"
@@ -63,13 +65,13 @@
/**
* XXX Does some additional initialization on top of #nodeAddNode
* Can be used with both custom and static nodes,
- * if `idname == NULL` the static int type will be used instead.
+ * if `idname == nullptr` the static int type will be used instead.
*/
bNode *node_add_node(const bContext *C, const char *idname, int type, float locx, float locy)
{
SpaceNode *snode = CTX_wm_space_node(C);
Main *bmain = CTX_data_main(C);
- bNode *node = NULL;
+ bNode *node = nullptr;
node_deselect_all(snode);
@@ -88,7 +90,7 @@ bNode *node_add_node(const bContext *C, const char *idname, int type, float locx
nodeSetSelected(node, true);
ntreeUpdateTree(bmain, snode->edittree);
- ED_node_set_active(bmain, snode->edittree, node, NULL);
+ ED_node_set_active(bmain, snode->edittree, node, nullptr);
snode_update(snode, node);
@@ -112,7 +114,7 @@ static bool add_reroute_intersect_check(bNodeLink *link,
{
float coord_array[NODE_LINK_RESOL + 1][2];
- if (node_link_bezier_points(NULL, NULL, link, coord_array, NODE_LINK_RESOL)) {
+ if (node_link_bezier_points(nullptr, nullptr, link, coord_array, NODE_LINK_RESOL)) {
for (int i = 0; i < tot - 1; i++) {
for (int b = 0; b < NODE_LINK_RESOL; b++) {
if (isect_seg_seg_v2_point(
@@ -125,13 +127,13 @@ static bool add_reroute_intersect_check(bNodeLink *link,
return false;
}
-typedef struct bNodeSocketLink {
+struct bNodeSocketLink {
struct bNodeSocketLink *next, *prev;
struct bNodeSocket *sock;
struct bNodeLink *link;
float point[2];
-} bNodeSocketLink;
+};
static bNodeSocketLink *add_reroute_insert_socket_link(ListBase *lb,
bNodeSocket *sock,
@@ -140,12 +142,12 @@ static bNodeSocketLink *add_reroute_insert_socket_link(ListBase *lb,
{
bNodeSocketLink *socklink, *prev;
- socklink = MEM_callocN(sizeof(bNodeSocketLink), "socket link");
+ socklink = (bNodeSocketLink *)MEM_callocN(sizeof(bNodeSocketLink), "socket link");
socklink->sock = sock;
socklink->link = link;
copy_v2_v2(socklink->point, point);
- for (prev = lb->last; prev; prev = prev->prev) {
+ for (prev = (bNodeSocketLink *)lb->last; prev; prev = prev->prev) {
if (prev->sock == sock) {
break;
}
@@ -160,7 +162,7 @@ static bNodeSocketLink *add_reroute_do_socket_section(bContext *C,
{
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *ntree = snode->edittree;
- bNode *reroute_node = NULL;
+ bNode *reroute_node = nullptr;
bNodeSocket *cursock = socklink->sock;
float insert_point[2];
int num_links;
@@ -182,12 +184,12 @@ static bNodeSocketLink *add_reroute_do_socket_section(bContext *C,
socklink->link->fromnode,
socklink->link->fromsock,
reroute_node,
- reroute_node->inputs.first);
+ (bNodeSocket *)reroute_node->inputs.first);
}
else {
nodeAddLink(ntree,
reroute_node,
- reroute_node->outputs.first,
+ (bNodeSocket *)reroute_node->outputs.first,
socklink->link->tonode,
socklink->link->tosock);
}
@@ -196,11 +198,11 @@ static bNodeSocketLink *add_reroute_do_socket_section(bContext *C,
/* insert the reroute node into the link */
if (in_out == SOCK_OUT) {
socklink->link->fromnode = reroute_node;
- socklink->link->fromsock = reroute_node->outputs.first;
+ socklink->link->fromsock = (bNodeSocket *)reroute_node->outputs.first;
}
else {
socklink->link->tonode = reroute_node;
- socklink->link->tosock = reroute_node->inputs.first;
+ socklink->link->tosock = (bNodeSocket *)reroute_node->inputs.first;
}
add_v2_v2(insert_point, socklink->point);
@@ -257,7 +259,7 @@ static int add_reroute_exec(bContext *C, wmOperator *op)
BLI_listbase_clear(&output_links);
BLI_listbase_clear(&input_links);
- for (link = ntree->links.first; link; link = link->next) {
+ for (link = (bNodeLink *)ntree->links.first; link; link = link->next) {
if (nodeLinkIsHidden(link)) {
continue;
}
@@ -273,11 +275,11 @@ static int add_reroute_exec(bContext *C, wmOperator *op)
/* Create reroute nodes for intersected links.
* Only one reroute if links share the same input/output socket.
*/
- socklink = output_links.first;
+ socklink = (bNodeSocketLink *)output_links.first;
while (socklink) {
socklink = add_reroute_do_socket_section(C, socklink, SOCK_OUT);
}
- socklink = input_links.first;
+ socklink = (bNodeSocketLink *)input_links.first;
while (socklink) {
socklink = add_reroute_do_socket_section(C, socklink, SOCK_IN);
}
@@ -315,7 +317,7 @@ void NODE_OT_add_reroute(wmOperatorType *ot)
/* properties */
PropertyRNA *prop;
prop = RNA_def_collection_runtime(ot->srna, "path", &RNA_OperatorMousePath, "Path", "");
- RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+ RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE));
/* internal */
RNA_def_int(ot->srna, "cursor", WM_CURSOR_CROSS, 0, INT_MAX, "Cursor", "", 0, INT_MAX);
}
@@ -335,10 +337,28 @@ static bNodeTree *node_add_group_get_and_poll_group_node_tree(Main *bmain,
bNodeTree *node_group = (bNodeTree *)BKE_libblock_find_name(bmain, ID_NT, name);
if (!node_group) {
- return NULL;
- }
- if ((node_group->type != ntree->type) || !nodeGroupPoll(ntree, node_group)) {
- return NULL;
+ return nullptr;
+ }
+
+ const char *disabled_hint = nullptr;
+ if ((node_group->type != ntree->type) || !nodeGroupPoll(ntree, node_group, &disabled_hint)) {
+ if (disabled_hint) {
+ BKE_reportf(op->reports,
+ RPT_ERROR,
+ "Can not add node group '%s' to '%s':\n %s",
+ node_group->id.name + 2,
+ ntree->id.name + 2,
+ disabled_hint);
+ }
+ else {
+ BKE_reportf(op->reports,
+ RPT_ERROR,
+ "Can not add node group '%s' to '%s'",
+ node_group->id.name + 2,
+ ntree->id.name + 2);
+ }
+
+ return nullptr;
}
return node_group;
@@ -430,7 +450,7 @@ static Object *node_add_object_get_and_poll_object_node_tree(Main *bmain, wmOper
Object *object = (Object *)BKE_libblock_find_name(bmain, ID_OB, name);
if (!object) {
- return NULL;
+ return nullptr;
}
return object;
@@ -450,7 +470,7 @@ static int node_add_object_exec(bContext *C, wmOperator *op)
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
bNode *object_node = node_add_node(
- C, NULL, GEO_NODE_OBJECT_INFO, snode->runtime->cursor[0], snode->runtime->cursor[1]);
+ C, nullptr, GEO_NODE_OBJECT_INFO, snode->runtime->cursor[0], snode->runtime->cursor[1]);
if (!object_node) {
BKE_report(op->reports, RPT_WARNING, "Could not add node object");
return OPERATOR_CANCELLED;
@@ -462,7 +482,7 @@ static int node_add_object_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- bNodeSocketValueObject *socket_data = sock->default_value;
+ bNodeSocketValueObject *socket_data = (bNodeSocketValueObject *)sock->default_value;
socket_data->value = object;
id_us_plus(&object->id);
@@ -473,6 +493,7 @@ static int node_add_object_exec(bContext *C, wmOperator *op)
snode_dag_update(C, snode);
ED_node_tag_update_nodetree(bmain, ntree, object_node);
+ DEG_relations_tag_update(bmain);
return OPERATOR_FINISHED;
}
@@ -533,7 +554,7 @@ static Tex *node_add_texture_get_and_poll_texture_node_tree(Main *bmain, wmOpera
Tex *texture = (Tex *)BKE_libblock_find_name(bmain, ID_TE, name);
if (!texture) {
- return NULL;
+ return nullptr;
}
return texture;
@@ -553,7 +574,7 @@ static int node_add_texture_exec(bContext *C, wmOperator *op)
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
bNode *texture_node = node_add_node(C,
- NULL,
+ nullptr,
GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE,
snode->runtime->cursor[0],
snode->runtime->cursor[1]);
@@ -570,6 +591,7 @@ static int node_add_texture_exec(bContext *C, wmOperator *op)
snode_notify(C, snode);
snode_dag_update(C, snode);
+ DEG_relations_tag_update(bmain);
ED_node_tag_update_nodetree(bmain, ntree, texture_node);
@@ -634,7 +656,7 @@ static Collection *node_add_collection_get_and_poll_collection_node_tree(Main *b
Collection *collection = (Collection *)BKE_libblock_find_name(bmain, ID_GR, name);
if (!collection) {
- return NULL;
+ return nullptr;
}
return collection;
@@ -654,7 +676,7 @@ static int node_add_collection_exec(bContext *C, wmOperator *op)
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
bNode *collection_node = node_add_node(
- C, NULL, GEO_NODE_COLLECTION_INFO, snode->runtime->cursor[0], snode->runtime->cursor[1]);
+ C, nullptr, GEO_NODE_COLLECTION_INFO, snode->runtime->cursor[0], snode->runtime->cursor[1]);
if (!collection_node) {
BKE_report(op->reports, RPT_WARNING, "Could not add node collection");
return OPERATOR_CANCELLED;
@@ -666,7 +688,7 @@ static int node_add_collection_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- bNodeSocketValueCollection *socket_data = sock->default_value;
+ bNodeSocketValueCollection *socket_data = (bNodeSocketValueCollection *)sock->default_value;
socket_data->value = collection;
id_us_plus(&collection->id);
@@ -675,6 +697,7 @@ static int node_add_collection_exec(bContext *C, wmOperator *op)
snode_notify(C, snode);
snode_dag_update(C, snode);
+ DEG_relations_tag_update(bmain);
ED_node_tag_update_nodetree(bmain, ntree, collection_node);
@@ -767,7 +790,7 @@ static int node_add_file_exec(bContext *C, wmOperator *op)
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
- node = node_add_node(C, NULL, type, snode->runtime->cursor[0], snode->runtime->cursor[1]);
+ node = node_add_node(C, nullptr, type, snode->runtime->cursor[0], snode->runtime->cursor[1]);
if (!node) {
BKE_report(op->reports, RPT_WARNING, "Could not add an image node");
@@ -780,12 +803,13 @@ static int node_add_file_exec(bContext *C, wmOperator *op)
* to get proper image source.
*/
if (RNA_struct_property_is_set(op->ptr, "filepath")) {
- BKE_image_signal(bmain, ima, NULL, IMA_SIGNAL_RELOAD);
+ BKE_image_signal(bmain, ima, nullptr, IMA_SIGNAL_RELOAD);
WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima);
}
snode_notify(C, snode);
snode_dag_update(C, snode);
+ DEG_relations_tag_update(bmain);
return OPERATOR_FINISHED;
}
@@ -855,7 +879,7 @@ static int node_add_mask_exec(bContext *C, wmOperator *op)
Main *bmain = CTX_data_main(C);
SpaceNode *snode = CTX_wm_space_node(C);
bNode *node;
- ID *mask = NULL;
+ ID *mask = nullptr;
/* check input variables */
char name[MAX_ID_NAME - 2];
@@ -869,7 +893,7 @@ static int node_add_mask_exec(bContext *C, wmOperator *op)
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
node = node_add_node(
- C, NULL, CMP_NODE_MASK, snode->runtime->cursor[0], snode->runtime->cursor[1]);
+ C, nullptr, CMP_NODE_MASK, snode->runtime->cursor[0], snode->runtime->cursor[1]);
if (!node) {
BKE_report(op->reports, RPT_WARNING, "Could not add a mask node");
@@ -881,6 +905,7 @@ static int node_add_mask_exec(bContext *C, wmOperator *op)
snode_notify(C, snode);
snode_dag_update(C, snode);
+ DEG_relations_tag_update(bmain);
return OPERATOR_FINISHED;
}
@@ -955,7 +980,7 @@ static int new_node_tree_exec(bContext *C, wmOperator *op)
id_us_min(&ntree->id);
RNA_id_pointer_create(&ntree->id, &idptr);
- RNA_property_pointer_set(&ptr, prop, idptr, NULL);
+ RNA_property_pointer_set(&ptr, prop, idptr, nullptr);
RNA_property_update(C, &ptr, prop);
}
else if (snode) {
@@ -972,7 +997,7 @@ static const EnumPropertyItem *new_node_tree_type_itemf(bContext *UNUSED(C),
PropertyRNA *UNUSED(prop),
bool *r_free)
{
- return rna_node_tree_type_itemf(NULL, NULL, r_free);
+ return rna_node_tree_type_itemf(nullptr, nullptr, r_free);
}
void NODE_OT_new_node_tree(wmOperatorType *ot)
diff --git a/source/blender/editors/space_node/node_buttons.c b/source/blender/editors/space_node/node_buttons.c
index eb89658857b..336b0c46a81 100644
--- a/source/blender/editors/space_node/node_buttons.c
+++ b/source/blender/editors/space_node/node_buttons.c
@@ -157,6 +157,11 @@ static void draw_socket_list(const bContext *C,
RNA_pointer_create((ID *)ntree, &RNA_NodeSocketInterface, socket, &socket_ptr);
uiItemR(layout, &socket_ptr, "name", 0, NULL, ICON_NONE);
+ /* Display descriptions only for Geometry Nodes, since it's only used in the modifier panel. */
+ if (ntree->type == NTREE_GEOMETRY) {
+ uiItemR(layout, &socket_ptr, "description", 0, NULL, ICON_NONE);
+ }
+
if (socket->typeinfo->interface_draw) {
socket->typeinfo->interface_draw((bContext *)C, layout, &socket_ptr);
}
diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc
index f64ce771b25..1dc8e1412af 100644
--- a/source/blender/editors/space_node/node_draw.cc
+++ b/source/blender/editors/space_node/node_draw.cc
@@ -82,6 +82,7 @@
# include "COM_compositor.h"
#endif
+using blender::Map;
using blender::Set;
using blender::Span;
using blender::Vector;
@@ -1404,6 +1405,28 @@ static void node_draw_basis(const bContext *C,
"");
UI_block_emboss_set(node->block, UI_EMBOSS);
}
+ if (ntree->type == NTREE_GEOMETRY) {
+ /* Active preview toggle. */
+ iconofs -= iconbutw;
+ UI_block_emboss_set(node->block, UI_EMBOSS_NONE);
+ int icon = (node->flag & NODE_ACTIVE_PREVIEW) ? ICON_RESTRICT_VIEW_OFF : ICON_RESTRICT_VIEW_ON;
+ uiBut *but = uiDefIconBut(node->block,
+ UI_BTYPE_BUT_TOGGLE,
+ 0,
+ icon,
+ iconofs,
+ rct->ymax - NODE_DY,
+ iconbutw,
+ UI_UNIT_Y,
+ nullptr,
+ 0,
+ 0,
+ 0,
+ 0,
+ "Show this node's geometry output in the spreadsheet");
+ UI_but_func_set(but, node_toggle_button_cb, node, (void *)"NODE_OT_active_preview_toggle");
+ UI_block_emboss_set(node->block, UI_EMBOSS);
+ }
node_add_error_message_button(C, *ntree, *node, *rct, iconofs);
@@ -1745,26 +1768,27 @@ static void node_update(const bContext *C, bNodeTree *ntree, bNode *node)
static void count_mutli_input_socket_links(bNodeTree *ntree, SpaceNode *snode)
{
+ Map<bNodeSocket *, int> counts;
+ LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
+ if (link->tosock->flag & SOCK_MULTI_INPUT) {
+ int &count = counts.lookup_or_add(link->tosock, 0);
+ count++;
+ }
+ }
+ /* Count temporary links going into this socket. */
+ LISTBASE_FOREACH (bNodeLinkDrag *, nldrag, &snode->runtime->linkdrag) {
+ LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) {
+ bNodeLink *link = (bNodeLink *)linkdata->data;
+ if (link->tosock && (link->tosock->flag & SOCK_MULTI_INPUT)) {
+ int &count = counts.lookup_or_add(link->tosock, 0);
+ count++;
+ }
+ }
+ }
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
- LISTBASE_FOREACH (struct bNodeSocket *, socket, &node->inputs) {
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
if (socket->flag & SOCK_MULTI_INPUT) {
- Set<bNodeSocket *> visited_from_sockets;
- socket->total_inputs = 0;
- LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
- if (link->tosock == socket) {
- visited_from_sockets.add(link->fromsock);
- }
- }
- /* Count temporary links going into this socket. */
- LISTBASE_FOREACH (bNodeLinkDrag *, nldrag, &snode->runtime->linkdrag) {
- LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) {
- bNodeLink *link = (bNodeLink *)linkdata->data;
- if (link->tosock == socket) {
- visited_from_sockets.add(link->fromsock);
- }
- }
- }
- socket->total_inputs = visited_from_sockets.size();
+ socket->total_inputs = counts.lookup_default(socket, 0);
}
}
}
@@ -1959,8 +1983,8 @@ void node_draw_space(const bContext *C, ARegion *region)
ID *name_id = (path->nodetree && path->nodetree != snode->nodetree) ? &path->nodetree->id :
snode->id;
- if (name_id && UNLIKELY(!STREQ(path->node_name, name_id->name + 2))) {
- BLI_strncpy(path->node_name, name_id->name + 2, sizeof(path->node_name));
+ if (name_id && UNLIKELY(!STREQ(path->display_name, name_id->name + 2))) {
+ BLI_strncpy(path->display_name, name_id->name + 2, sizeof(path->display_name));
}
/* Current View2D center, will be set temporarily for parent node trees. */
diff --git a/source/blender/editors/space_node/node_edit.c b/source/blender/editors/space_node/node_edit.cc
index b72a6503749..d86b069aac3 100644
--- a/source/blender/editors/space_node/node_edit.c
+++ b/source/blender/editors/space_node/node_edit.cc
@@ -41,6 +41,7 @@
#include "BKE_node.h"
#include "BKE_report.h"
#include "BKE_scene.h"
+#include "BKE_workspace.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_build.h"
@@ -82,7 +83,7 @@ enum {
COM_RECALC_VIEWER = 2,
};
-typedef struct CompoJob {
+struct CompoJob {
/* Input parameters. */
Main *bmain;
Scene *scene;
@@ -96,7 +97,7 @@ typedef struct CompoJob {
const short *stop;
short *do_update;
float *progress;
-} CompoJob;
+};
float node_socket_calculate_height(const bNodeSocket *socket)
{
@@ -149,7 +150,7 @@ static int compo_get_recalc_flags(const bContext *C)
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
if (area->spacetype == SPACE_IMAGE) {
- SpaceImage *sima = area->spacedata.first;
+ SpaceImage *sima = (SpaceImage *)area->spacedata.first;
if (sima->image) {
if (sima->image->type == IMA_TYPE_R_RESULT) {
recalc_flags |= COM_RECALC_COMPOSITE;
@@ -160,7 +161,7 @@ static int compo_get_recalc_flags(const bContext *C)
}
}
else if (area->spacetype == SPACE_NODE) {
- SpaceNode *snode = area->spacedata.first;
+ SpaceNode *snode = (SpaceNode *)area->spacedata.first;
if (snode->flag & SNODE_BACKDRAW) {
recalc_flags |= COM_RECALC_VIEWER;
}
@@ -174,7 +175,7 @@ static int compo_get_recalc_flags(const bContext *C)
/* called by compo, only to check job 'stop' value */
static int compo_breakjob(void *cjv)
{
- CompoJob *cj = cjv;
+ CompoJob *cj = (CompoJob *)cjv;
/* without G.is_break 'ESC' wont quit - which annoys users */
return (*(cj->stop)
@@ -187,7 +188,7 @@ static int compo_breakjob(void *cjv)
/* called by compo, wmJob sends notifier */
static void compo_statsdrawjob(void *cjv, const char *UNUSED(str))
{
- CompoJob *cj = cjv;
+ CompoJob *cj = (CompoJob *)cjv;
*(cj->do_update) = true;
}
@@ -195,19 +196,19 @@ static void compo_statsdrawjob(void *cjv, const char *UNUSED(str))
/* called by compo, wmJob sends notifier */
static void compo_redrawjob(void *cjv)
{
- CompoJob *cj = cjv;
+ CompoJob *cj = (CompoJob *)cjv;
*(cj->do_update) = true;
}
static void compo_freejob(void *cjv)
{
- CompoJob *cj = cjv;
+ CompoJob *cj = (CompoJob *)cjv;
if (cj->localtree) {
ntreeLocalMerge(cj->bmain, cj->localtree, cj->ntree);
}
- if (cj->compositor_depsgraph != NULL) {
+ if (cj->compositor_depsgraph != nullptr) {
DEG_graph_free(cj->compositor_depsgraph);
}
MEM_freeN(cj);
@@ -217,7 +218,7 @@ static void compo_freejob(void *cjv)
* sliding buttons doesn't frustrate */
static void compo_initjob(void *cjv)
{
- CompoJob *cj = cjv;
+ CompoJob *cj = (CompoJob *)cjv;
Main *bmain = cj->bmain;
Scene *scene = cj->scene;
ViewLayer *view_layer = cj->view_layer;
@@ -242,12 +243,12 @@ static void compo_initjob(void *cjv)
/* called before redraw notifiers, it moves finished previews over */
static void compo_updatejob(void *UNUSED(cjv))
{
- WM_main_add_notifier(NC_SCENE | ND_COMPO_RESULT, NULL);
+ WM_main_add_notifier(NC_SCENE | ND_COMPO_RESULT, nullptr);
}
static void compo_progressjob(void *cjv, float progress)
{
- CompoJob *cj = cjv;
+ CompoJob *cj = (CompoJob *)cjv;
*(cj->progress) = progress;
}
@@ -260,7 +261,7 @@ static void compo_startjob(void *cjv,
short *do_update,
float *progress)
{
- CompoJob *cj = cjv;
+ CompoJob *cj = (CompoJob *)cjv;
bNodeTree *ntree = cj->localtree;
Scene *scene = cj->scene;
@@ -310,9 +311,9 @@ static void compo_startjob(void *cjv,
}
}
- ntree->test_break = NULL;
- ntree->stats_draw = NULL;
- ntree->progress = NULL;
+ ntree->test_break = nullptr;
+ ntree->stats_draw = nullptr;
+ ntree->progress = nullptr;
}
/**
@@ -346,7 +347,7 @@ void ED_node_composite_job(const bContext *C, struct bNodeTree *nodetree, Scene
"Compositing",
WM_JOB_EXCL_RENDER | WM_JOB_PROGRESS,
WM_JOB_TYPE_COMPOSITE);
- CompoJob *cj = MEM_callocN(sizeof(CompoJob), "compo job");
+ CompoJob *cj = (CompoJob *)MEM_callocN(sizeof(CompoJob), "compo job");
/* customdata for preview thread */
cj->bmain = bmain;
@@ -358,7 +359,7 @@ void ED_node_composite_job(const bContext *C, struct bNodeTree *nodetree, Scene
/* setup job */
WM_jobs_customdata_set(wm_job, cj, compo_freejob);
WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_COMPO_RESULT, NC_SCENE | ND_COMPO_RESULT);
- WM_jobs_callbacks(wm_job, compo_startjob, compo_initjob, compo_updatejob, NULL);
+ WM_jobs_callbacks(wm_job, compo_startjob, compo_initjob, compo_updatejob, nullptr);
WM_jobs_start(CTX_wm_manager(C), wm_job);
}
@@ -411,7 +412,7 @@ void snode_notify(bContext *C, SpaceNode *snode)
{
ID *id = snode->id;
- WM_event_add_notifier(C, NC_NODE | NA_EDITED, NULL);
+ WM_event_add_notifier(C, NC_NODE | NA_EDITED, nullptr);
if (ED_node_is_shader(snode)) {
if (GS(id->name) == ID_MA) {
@@ -489,15 +490,15 @@ void ED_node_shader_default(const bContext *C, ID *id)
}
else if (ELEM(GS(id->name), ID_WO, ID_LA)) {
/* Emission */
- bNodeTree *ntree = ntreeAddTree(NULL, "Shader Nodetree", ntreeType_Shader->idname);
+ bNodeTree *ntree = ntreeAddTree(nullptr, "Shader Nodetree", ntreeType_Shader->idname);
bNode *shader, *output;
if (GS(id->name) == ID_WO) {
World *world = (World *)id;
world->nodetree = ntree;
- shader = nodeAddStaticNode(NULL, ntree, SH_NODE_BACKGROUND);
- output = nodeAddStaticNode(NULL, ntree, SH_NODE_OUTPUT_WORLD);
+ shader = nodeAddStaticNode(nullptr, ntree, SH_NODE_BACKGROUND);
+ output = nodeAddStaticNode(nullptr, ntree, SH_NODE_OUTPUT_WORLD);
nodeAddLink(ntree,
shader,
nodeFindSocket(shader, SOCK_OUT, "Background"),
@@ -511,8 +512,8 @@ void ED_node_shader_default(const bContext *C, ID *id)
Light *light = (Light *)id;
light->nodetree = ntree;
- shader = nodeAddStaticNode(NULL, ntree, SH_NODE_EMISSION);
- output = nodeAddStaticNode(NULL, ntree, SH_NODE_OUTPUT_LIGHT);
+ shader = nodeAddStaticNode(nullptr, ntree, SH_NODE_EMISSION);
+ output = nodeAddStaticNode(nullptr, ntree, SH_NODE_OUTPUT_LIGHT);
nodeAddLink(ntree,
shader,
nodeFindSocket(shader, SOCK_OUT, "Emission"),
@@ -545,7 +546,7 @@ void ED_node_composit_default(const bContext *C, struct Scene *sce)
return;
}
- sce->nodetree = ntreeAddTree(NULL, "Compositing Nodetree", ntreeType_Composite->idname);
+ sce->nodetree = ntreeAddTree(nullptr, "Compositing Nodetree", ntreeType_Composite->idname);
sce->nodetree->chunksize = 256;
sce->nodetree->edit_quality = NTREE_QUALITY_HIGH;
@@ -561,8 +562,8 @@ void ED_node_composit_default(const bContext *C, struct Scene *sce)
nodeSetActive(sce->nodetree, in);
/* links from color to color */
- bNodeSocket *fromsock = in->outputs.first;
- bNodeSocket *tosock = out->inputs.first;
+ bNodeSocket *fromsock = (bNodeSocket *)in->outputs.first;
+ bNodeSocket *tosock = (bNodeSocket *)out->inputs.first;
nodeAddLink(sce->nodetree, in, fromsock, out, tosock);
ntreeUpdateTree(CTX_data_main(C), sce->nodetree);
@@ -580,7 +581,7 @@ void ED_node_texture_default(const bContext *C, Tex *tex)
return;
}
- tex->nodetree = ntreeAddTree(NULL, "Texture Nodetree", ntreeType_Texture->idname);
+ tex->nodetree = ntreeAddTree(nullptr, "Texture Nodetree", ntreeType_Texture->idname);
bNode *out = nodeAddStaticNode(C, tex->nodetree, TEX_NODE_OUTPUT);
out->locx = 300.0f;
@@ -591,8 +592,8 @@ void ED_node_texture_default(const bContext *C, Tex *tex)
in->locy = 300.0f;
nodeSetActive(tex->nodetree, in);
- bNodeSocket *fromsock = in->outputs.first;
- bNodeSocket *tosock = out->inputs.first;
+ bNodeSocket *fromsock = (bNodeSocket *)in->outputs.first;
+ bNodeSocket *tosock = (bNodeSocket *)out->inputs.first;
nodeAddLink(tex->nodetree, in, fromsock, out, tosock);
ntreeUpdateTree(CTX_data_main(C), tex->nodetree);
@@ -617,24 +618,24 @@ void snode_set_context(const bContext *C)
if (snode->nodetree && !STREQ(snode->nodetree->idname, snode->tree_idname)) {
/* current tree does not match selected type, clear tree path */
- ntree = NULL;
- id = NULL;
- from = NULL;
+ ntree = nullptr;
+ id = nullptr;
+ from = nullptr;
}
- if (!(snode->flag & SNODE_PIN) || ntree == NULL) {
+ if (!(snode->flag & SNODE_PIN) || ntree == nullptr) {
if (treetype->get_from_context) {
/* reset and update from context */
- ntree = NULL;
- id = NULL;
- from = NULL;
+ ntree = nullptr;
+ id = nullptr;
+ from = nullptr;
treetype->get_from_context(C, treetype, &ntree, &id, &from);
}
}
if (snode->nodetree != ntree || snode->id != id || snode->from != from ||
- (snode->treepath.last == NULL && ntree)) {
+ (snode->treepath.last == nullptr && ntree)) {
ED_node_tree_start(snode, ntree, id, from);
}
}
@@ -647,7 +648,7 @@ void snode_update(SpaceNode *snode, bNode *node)
*/
/* update all edited group nodes */
- bNodeTreePath *path = snode->treepath.last;
+ bNodeTreePath *path = (bNodeTreePath *)snode->treepath.last;
if (path) {
bNodeTree *ngroup = path->nodetree;
for (path = path->prev; path; path = path->prev) {
@@ -684,7 +685,7 @@ void ED_node_set_active(Main *bmain, bNodeTree *ntree, bNode *node, bool *r_acti
node->flag |= NODE_DO_OUTPUT;
if (!was_output) {
- do_update = 1;
+ do_update = true;
}
}
@@ -733,7 +734,7 @@ void ED_node_set_active(Main *bmain, bNodeTree *ntree, bNode *node, bool *r_acti
*r_active_texture_changed = true;
}
ED_node_tag_update_nodetree(bmain, ntree, node);
- WM_main_add_notifier(NC_IMAGE, NULL);
+ WM_main_add_notifier(NC_IMAGE, nullptr);
}
WM_main_add_notifier(NC_MATERIAL | ND_NODES, node->id);
@@ -752,7 +753,7 @@ void ED_node_set_active(Main *bmain, bNodeTree *ntree, bNode *node, bool *r_acti
ED_node_tag_update_nodetree(bmain, ntree, node);
}
- /* addnode() doesn't link this yet... */
+ /* Adding a node doesn't link this yet. */
node->id = (ID *)BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node");
}
else if (node->type == CMP_NODE_COMPOSITE) {
@@ -805,7 +806,7 @@ static bool edit_node_poll(bContext *C)
static void edit_node_properties(wmOperatorType *ot)
{
/* XXX could node be a context pointer? */
- RNA_def_string(ot->srna, "node", NULL, MAX_NAME, "Node", "");
+ RNA_def_string(ot->srna, "node", nullptr, MAX_NAME, "Node", "");
RNA_def_int(ot->srna, "socket", 0, 0, MAX_SOCKET, "Socket", "", 0, MAX_SOCKET);
RNA_def_enum(ot->srna, "in_out", rna_enum_node_socket_in_out_items, SOCK_IN, "Socket Side", "");
}
@@ -837,7 +838,7 @@ static void edit_node_properties_get(
wmOperator *op, bNodeTree *ntree, bNode **r_node, bNodeSocket **r_sock, int *r_in_out)
{
bNode *node;
- bNodeSocket *sock = NULL;
+ bNodeSocket *sock = nullptr;
char nodename[MAX_NAME];
int sockindex;
int in_out;
@@ -875,29 +876,30 @@ static void edit_node_properties_get(
static bNode *visible_node(SpaceNode *snode, const rctf *rct)
{
LISTBASE_FOREACH_BACKWARD (bNode *, node, &snode->edittree->nodes) {
- if (BLI_rctf_isect(&node->totr, rct, NULL)) {
+ if (BLI_rctf_isect(&node->totr, rct, nullptr)) {
return node;
}
}
- return NULL;
+ return nullptr;
}
/* ********************** size widget operator ******************** */
-typedef struct NodeSizeWidget {
+struct NodeSizeWidget {
float mxstart, mystart;
float oldlocx, oldlocy;
float oldoffsetx, oldoffsety;
float oldwidth, oldheight;
int directions;
-} NodeSizeWidget;
+};
static void node_resize_init(
bContext *C, wmOperator *op, const wmEvent *UNUSED(event), bNode *node, int dir)
{
SpaceNode *snode = CTX_wm_space_node(C);
- NodeSizeWidget *nsw = MEM_callocN(sizeof(NodeSizeWidget), "size widget op data");
+ NodeSizeWidget *nsw = (NodeSizeWidget *)MEM_callocN(sizeof(NodeSizeWidget),
+ "size widget op data");
op->customdata = nsw;
nsw->mxstart = snode->runtime->cursor[0] * UI_DPI_FAC;
@@ -925,7 +927,7 @@ static void node_resize_exit(bContext *C, wmOperator *op, bool cancel)
if (cancel) {
SpaceNode *snode = CTX_wm_space_node(C);
bNode *node = nodeGetActive(snode->edittree);
- NodeSizeWidget *nsw = op->customdata;
+ NodeSizeWidget *nsw = (NodeSizeWidget *)op->customdata;
node->locx = nsw->oldlocx;
node->locy = nsw->oldlocy;
@@ -936,7 +938,7 @@ static void node_resize_exit(bContext *C, wmOperator *op, bool cancel)
}
MEM_freeN(op->customdata);
- op->customdata = NULL;
+ op->customdata = nullptr;
}
static int node_resize_modal(bContext *C, wmOperator *op, const wmEvent *event)
@@ -944,7 +946,7 @@ static int node_resize_modal(bContext *C, wmOperator *op, const wmEvent *event)
SpaceNode *snode = CTX_wm_space_node(C);
ARegion *region = CTX_wm_region(C);
bNode *node = nodeGetActive(snode->edittree);
- NodeSizeWidget *nsw = op->customdata;
+ NodeSizeWidget *nsw = (NodeSizeWidget *)op->customdata;
switch (event->type) {
case MOUSEMOVE: {
@@ -1111,7 +1113,7 @@ void node_set_hidden_sockets(SpaceNode *snode, bNode *node, int set)
else {
/* hide unused sockets */
LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
- if (sock->link == NULL) {
+ if (sock->link == nullptr) {
sock->flag |= SOCK_HIDDEN;
}
}
@@ -1127,12 +1129,17 @@ void node_set_hidden_sockets(SpaceNode *snode, bNode *node, int set)
static bool cursor_isect_multi_input_socket(const float cursor[2], const bNodeSocket *socket)
{
const float node_socket_height = node_socket_calculate_height(socket);
- const rctf multi_socket_rect = {
- .xmin = socket->locx - NODE_SOCKSIZE * 4,
- .xmax = socket->locx + NODE_SOCKSIZE,
- .ymin = socket->locy - node_socket_height * 0.5 - NODE_SOCKSIZE * 2.0f,
- .ymax = socket->locy + node_socket_height * 0.5 + NODE_SOCKSIZE * 2.0f,
- };
+ rctf multi_socket_rect;
+ /*.xmax = socket->locx + NODE_SOCKSIZE * 5.5f
+ * would be the same behavior as for regular sockets.
+ * But keep it smaller because for multi-input socket you
+ * sometimes want to drag the link to the other side, if you may
+ * accidentally pick the wrong link otherwise. */
+ BLI_rctf_init(&multi_socket_rect,
+ socket->locx - NODE_SOCKSIZE * 4.0f,
+ socket->locx + NODE_SOCKSIZE * 2.0f,
+ socket->locy - node_socket_height,
+ socket->locy + node_socket_height);
if (BLI_rctf_isect_pt(&multi_socket_rect, cursor[0], cursor[1])) {
return true;
}
@@ -1141,12 +1148,12 @@ static bool cursor_isect_multi_input_socket(const float cursor[2], const bNodeSo
/* type is SOCK_IN and/or SOCK_OUT */
int node_find_indicated_socket(
- SpaceNode *snode, bNode **nodep, bNodeSocket **sockp, float cursor[2], int in_out)
+ SpaceNode *snode, bNode **nodep, bNodeSocket **sockp, const float cursor[2], int in_out)
{
rctf rect;
- *nodep = NULL;
- *sockp = NULL;
+ *nodep = nullptr;
+ *sockp = nullptr;
/* check if we click in a socket */
LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) {
@@ -1238,7 +1245,7 @@ static int node_duplicate_exec(bContext *C, wmOperator *op)
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
- bNode *lastnode = ntree->nodes.last;
+ bNode *lastnode = (bNode *)ntree->nodes.last;
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->flag & SELECT) {
BKE_node_copy_store_new_pointers(ntree, node, LIB_ID_COPY_DEFAULT);
@@ -1256,14 +1263,14 @@ static int node_duplicate_exec(bContext *C, wmOperator *op)
/* copy links between selected nodes
* NB: this depends on correct node->new_node and sock->new_sock pointers from above copy!
*/
- bNodeLink *lastlink = ntree->links.last;
+ bNodeLink *lastlink = (bNodeLink *)ntree->links.last;
LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
/* This creates new links between copied nodes.
- * If keep_inputs is set, also copies input links from unselected (when fromnode==NULL)!
+ * If keep_inputs is set, also copies input links from unselected (when fromnode==nullptr)!
*/
if (link->tonode && (link->tonode->flag & NODE_SELECT) &&
(keep_inputs || (link->fromnode && (link->fromnode->flag & NODE_SELECT)))) {
- bNodeLink *newlink = MEM_callocN(sizeof(bNodeLink), "bNodeLink");
+ bNodeLink *newlink = (bNodeLink *)MEM_callocN(sizeof(bNodeLink), "bNodeLink");
newlink->flag = link->flag;
newlink->tonode = link->tonode->new_node;
newlink->tosock = link->tosock->new_sock;
@@ -1311,6 +1318,7 @@ static int node_duplicate_exec(bContext *C, wmOperator *op)
nodeSetSelected(node, false);
node->flag &= ~(NODE_ACTIVE | NODE_ACTIVE_TEXTURE);
nodeSetSelected(newnode, true);
+ newnode->flag &= ~NODE_ACTIVE_PREVIEW;
do_tag_update |= (do_tag_update || node_connected_to_output(bmain, ntree, newnode));
}
@@ -1346,7 +1354,7 @@ void NODE_OT_duplicate(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
RNA_def_boolean(
- ot->srna, "keep_inputs", 0, "Keep Inputs", "Keep the input links to duplicated nodes");
+ ot->srna, "keep_inputs", false, "Keep Inputs", "Keep the input links to duplicated nodes");
}
bool ED_node_select_check(ListBase *lb)
@@ -1407,7 +1415,7 @@ static int node_read_viewlayers_exec(bContext *C, wmOperator *UNUSED(op))
if ((node->type == CMP_NODE_R_LAYERS) ||
(node->type == CMP_NODE_CRYPTOMATTE && node->custom1 == CMP_CRYPTOMATTE_SRC_RENDER)) {
ID *id = node->id;
- if (id == NULL) {
+ if (id == nullptr) {
continue;
}
if (id->tag & LIB_TAG_DOIT) {
@@ -1446,7 +1454,7 @@ int node_render_changed_exec(bContext *C, wmOperator *UNUSED(op))
/* This is actually a test whether scene is used by the compositor or not.
* All the nodes are using same render result, so there is no need to do
* anything smart about check how exactly scene is used. */
- bNode *node = NULL;
+ bNode *node = nullptr;
LISTBASE_FOREACH (bNode *, node_iter, &sce->nodetree->nodes) {
if (node_iter->id == (ID *)sce) {
node = node_iter;
@@ -1455,7 +1463,7 @@ int node_render_changed_exec(bContext *C, wmOperator *UNUSED(op))
}
if (node) {
- ViewLayer *view_layer = BLI_findlink(&sce->view_layers, node->custom1);
+ ViewLayer *view_layer = (ViewLayer *)BLI_findlink(&sce->view_layers, node->custom1);
if (view_layer) {
PointerRNA op_ptr;
@@ -1464,7 +1472,7 @@ int node_render_changed_exec(bContext *C, wmOperator *UNUSED(op))
RNA_string_set(&op_ptr, "layer", view_layer->name);
RNA_string_set(&op_ptr, "scene", sce->id.name + 2);
- /* to keep keypositions */
+ /* To keep keyframe positions. */
sce->r.scemode |= R_NO_FRAME_UPDATE;
WM_operator_name_call(C, "RENDER_OT_render", WM_OP_INVOKE_DEFAULT, &op_ptr);
@@ -1547,13 +1555,13 @@ static int node_hide_toggle_exec(bContext *C, wmOperator *UNUSED(op))
SpaceNode *snode = CTX_wm_space_node(C);
/* sanity checking (poll callback checks this already) */
- if ((snode == NULL) || (snode->edittree == NULL)) {
+ if ((snode == nullptr) || (snode->edittree == nullptr)) {
return OPERATOR_CANCELLED;
}
node_flag_toggle_exec(snode, NODE_HIDDEN);
- WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL);
+ WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr);
return OPERATOR_FINISHED;
}
@@ -1578,7 +1586,7 @@ static int node_preview_toggle_exec(bContext *C, wmOperator *UNUSED(op))
SpaceNode *snode = CTX_wm_space_node(C);
/* sanity checking (poll callback checks this already) */
- if ((snode == NULL) || (snode->edittree == NULL)) {
+ if ((snode == nullptr) || (snode->edittree == nullptr)) {
return OPERATOR_CANCELLED;
}
@@ -1611,13 +1619,13 @@ static int node_options_toggle_exec(bContext *C, wmOperator *UNUSED(op))
SpaceNode *snode = CTX_wm_space_node(C);
/* sanity checking (poll callback checks this already) */
- if ((snode == NULL) || (snode->edittree == NULL)) {
+ if ((snode == nullptr) || (snode->edittree == nullptr)) {
return OPERATOR_CANCELLED;
}
node_flag_toggle_exec(snode, NODE_OPTIONS);
- WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL);
+ WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr);
return OPERATOR_FINISHED;
}
@@ -1642,7 +1650,7 @@ static int node_socket_toggle_exec(bContext *C, wmOperator *UNUSED(op))
SpaceNode *snode = CTX_wm_space_node(C);
/* sanity checking (poll callback checks this already) */
- if ((snode == NULL) || (snode->edittree == NULL)) {
+ if ((snode == nullptr) || (snode->edittree == nullptr)) {
return OPERATOR_CANCELLED;
}
@@ -1667,7 +1675,7 @@ static int node_socket_toggle_exec(bContext *C, wmOperator *UNUSED(op))
ntreeUpdateTree(CTX_data_main(C), snode->edittree);
- WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL);
+ WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr);
return OPERATOR_FINISHED;
}
@@ -1706,8 +1714,6 @@ static int node_mute_exec(bContext *C, wmOperator *UNUSED(op))
}
}
- do_tag_update |= ED_node_is_geometry(snode);
-
snode_notify(C, snode);
if (do_tag_update) {
snode_dag_update(C, snode);
@@ -1748,8 +1754,6 @@ static int node_delete_exec(bContext *C, wmOperator *UNUSED(op))
}
}
- do_tag_update |= ED_node_is_geometry(snode);
-
ntreeUpdateTree(CTX_data_main(C), snode->edittree);
snode_notify(C, snode);
@@ -1867,12 +1871,12 @@ static int node_output_file_add_socket_exec(bContext *C, wmOperator *op)
Scene *scene = CTX_data_scene(C);
SpaceNode *snode = CTX_wm_space_node(C);
PointerRNA ptr = CTX_data_pointer_get(C, "node");
- bNodeTree *ntree = NULL;
- bNode *node = NULL;
+ bNodeTree *ntree = nullptr;
+ bNode *node = nullptr;
char file_path[MAX_NAME];
if (ptr.data) {
- node = ptr.data;
+ node = (bNode *)ptr.data;
ntree = (bNodeTree *)ptr.owner_id;
}
else if (snode && snode->edittree) {
@@ -1916,11 +1920,11 @@ static int node_output_file_remove_active_socket_exec(bContext *C, wmOperator *U
{
SpaceNode *snode = CTX_wm_space_node(C);
PointerRNA ptr = CTX_data_pointer_get(C, "node");
- bNodeTree *ntree = NULL;
- bNode *node = NULL;
+ bNodeTree *ntree = nullptr;
+ bNode *node = nullptr;
if (ptr.data) {
- node = ptr.data;
+ node = (bNode *)ptr.data;
ntree = (bNodeTree *)ptr.owner_id;
}
else if (snode && snode->edittree) {
@@ -1962,10 +1966,10 @@ static int node_output_file_move_active_socket_exec(bContext *C, wmOperator *op)
{
SpaceNode *snode = CTX_wm_space_node(C);
PointerRNA ptr = CTX_data_pointer_get(C, "node");
- bNode *node = NULL;
+ bNode *node = nullptr;
if (ptr.data) {
- node = ptr.data;
+ node = (bNode *)ptr.data;
}
else if (snode && snode->edittree) {
node = nodeGetActive(snode->edittree);
@@ -1975,9 +1979,9 @@ static int node_output_file_move_active_socket_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- NodeImageMultiFile *nimf = node->storage;
+ NodeImageMultiFile *nimf = (NodeImageMultiFile *)node->storage;
- bNodeSocket *sock = BLI_findlink(&node->inputs, nimf->active_input);
+ bNodeSocket *sock = (bNodeSocket *)BLI_findlink(&node->inputs, nimf->active_input);
if (!sock) {
return OPERATOR_CANCELLED;
}
@@ -2011,7 +2015,7 @@ static int node_output_file_move_active_socket_exec(bContext *C, wmOperator *op)
void NODE_OT_output_file_move_active_socket(wmOperatorType *ot)
{
static const EnumPropertyItem direction_items[] = {
- {1, "UP", 0, "Up", ""}, {2, "DOWN", 0, "Down", ""}, {0, NULL, 0, NULL, NULL}};
+ {1, "UP", 0, "Up", ""}, {2, "DOWN", 0, "Down", ""}, {0, nullptr, 0, nullptr, nullptr}};
/* identifiers */
ot->name = "Move File Node Socket";
@@ -2056,7 +2060,7 @@ static int node_copy_color_exec(bContext *C, wmOperator *UNUSED(op))
}
ED_node_sort(ntree);
- WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL);
+ WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr);
return OPERATOR_FINISHED;
}
@@ -2094,7 +2098,7 @@ static int node_clipboard_copy_exec(bContext *C, wmOperator *UNUSED(op))
/* No ID refcounting, this node is virtual,
* detached from any actual Blender data currently. */
bNode *new_node = BKE_node_copy_store_new_pointers(
- NULL, node, LIB_ID_CREATE_NO_USER_REFCOUNT | LIB_ID_CREATE_NO_MAIN);
+ nullptr, node, LIB_ID_CREATE_NO_USER_REFCOUNT | LIB_ID_CREATE_NO_MAIN);
BKE_node_clipboard_add_node(new_node);
}
}
@@ -2124,7 +2128,7 @@ static int node_clipboard_copy_exec(bContext *C, wmOperator *UNUSED(op))
/* This creates new links between copied nodes. */
if (link->tonode && (link->tonode->flag & NODE_SELECT) && link->fromnode &&
(link->fromnode->flag & NODE_SELECT)) {
- bNodeLink *newlink = MEM_callocN(sizeof(bNodeLink), "bNodeLink");
+ bNodeLink *newlink = (bNodeLink *)MEM_callocN(sizeof(bNodeLink), "bNodeLink");
newlink->flag = link->flag;
newlink->tonode = link->tonode->new_node;
newlink->tosock = link->tosock->new_sock;
@@ -2185,13 +2189,25 @@ static int node_clipboard_paste_exec(bContext *C, wmOperator *op)
/* make sure all clipboard nodes would be valid in the target tree */
bool all_nodes_valid = true;
LISTBASE_FOREACH (bNode *, node, clipboard_nodes_lb) {
- if (!node->typeinfo->poll_instance || !node->typeinfo->poll_instance(node, ntree)) {
+ const char *disabled_hint = nullptr;
+ if (!node->typeinfo->poll_instance ||
+ !node->typeinfo->poll_instance(node, ntree, &disabled_hint)) {
all_nodes_valid = false;
- BKE_reportf(op->reports,
- RPT_ERROR,
- "Cannot add node %s into node tree %s",
- node->name,
- ntree->id.name + 2);
+ if (disabled_hint) {
+ BKE_reportf(op->reports,
+ RPT_ERROR,
+ "Cannot add node %s into node tree %s:\n %s",
+ node->name,
+ ntree->id.name + 2,
+ disabled_hint);
+ }
+ else {
+ BKE_reportf(op->reports,
+ RPT_ERROR,
+ "Cannot add node %s into node tree %s",
+ node->name,
+ ntree->id.name + 2);
+ }
}
}
if (!all_nodes_valid) {
@@ -2271,7 +2287,7 @@ static bNodeSocket *ntree_get_active_interface_socket(ListBase *lb)
return socket;
}
}
- return NULL;
+ return nullptr;
}
static int ntree_socket_add_exec(bContext *C, wmOperator *op)
@@ -2282,7 +2298,7 @@ static int ntree_socket_add_exec(bContext *C, wmOperator *op)
PointerRNA ntree_ptr;
RNA_id_pointer_create((ID *)ntree, &ntree_ptr);
- const eNodeSocketInOut in_out = RNA_enum_get(op->ptr, "in_out");
+ const eNodeSocketInOut in_out = (eNodeSocketInOut)RNA_enum_get(op->ptr, "in_out");
ListBase *sockets = (in_out == SOCK_IN) ? &ntree->inputs : &ntree->outputs;
const char *default_name = (in_out == SOCK_IN) ? "Input" : "Output";
@@ -2313,7 +2329,7 @@ static int ntree_socket_add_exec(bContext *C, wmOperator *op)
snode_notify(C, snode);
snode_dag_update(C, snode);
- WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL);
+ WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr);
return OPERATOR_FINISHED;
}
@@ -2341,11 +2357,11 @@ static int ntree_socket_remove_exec(bContext *C, wmOperator *op)
{
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *ntree = snode->edittree;
- const eNodeSocketInOut in_out = RNA_enum_get(op->ptr, "in_out");
+ const eNodeSocketInOut in_out = (eNodeSocketInOut)RNA_enum_get(op->ptr, "in_out");
bNodeSocket *iosock = ntree_get_active_interface_socket(in_out == SOCK_IN ? &ntree->inputs :
&ntree->outputs);
- if (iosock == NULL) {
+ if (iosock == nullptr) {
return OPERATOR_CANCELLED;
}
@@ -2363,7 +2379,7 @@ static int ntree_socket_remove_exec(bContext *C, wmOperator *op)
snode_notify(C, snode);
snode_dag_update(C, snode);
- WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL);
+ WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr);
return OPERATOR_FINISHED;
}
@@ -2389,7 +2405,7 @@ void NODE_OT_tree_socket_remove(wmOperatorType *ot)
static const EnumPropertyItem move_direction_items[] = {
{1, "UP", 0, "Up", ""},
{2, "DOWN", 0, "Down", ""},
- {0, NULL, 0, NULL, NULL},
+ {0, nullptr, 0, nullptr, nullptr},
};
static int ntree_socket_move_exec(bContext *C, wmOperator *op)
@@ -2398,12 +2414,12 @@ static int ntree_socket_move_exec(bContext *C, wmOperator *op)
bNodeTree *ntree = snode->edittree;
int direction = RNA_enum_get(op->ptr, "direction");
- const eNodeSocketInOut in_out = RNA_enum_get(op->ptr, "in_out");
+ const eNodeSocketInOut in_out = (eNodeSocketInOut)RNA_enum_get(op->ptr, "in_out");
ListBase *sockets = in_out == SOCK_IN ? &ntree->inputs : &ntree->outputs;
bNodeSocket *iosock = ntree_get_active_interface_socket(sockets);
- if (iosock == NULL) {
+ if (iosock == nullptr) {
return OPERATOR_CANCELLED;
}
@@ -2438,7 +2454,7 @@ static int ntree_socket_move_exec(bContext *C, wmOperator *op)
snode_notify(C, snode);
snode_dag_update(C, snode);
- WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL);
+ WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr);
return OPERATOR_FINISHED;
}
@@ -2471,18 +2487,18 @@ static bool node_shader_script_update_poll(bContext *C)
/* test if we have a render engine that supports shaders scripts */
if (!(type && type->update_script_node)) {
- return 0;
+ return false;
}
/* see if we have a shader script node in context */
- bNode *node = CTX_data_pointer_get_type(C, "node", &RNA_ShaderNodeScript).data;
+ bNode *node = (bNode *)CTX_data_pointer_get_type(C, "node", &RNA_ShaderNodeScript).data;
if (!node && snode && snode->edittree) {
node = nodeGetActive(snode->edittree);
}
if (node && node->type == SH_NODE_SCRIPT) {
- NodeShaderScript *nss = node->storage;
+ NodeShaderScript *nss = (NodeShaderScript *)node->storage;
if (node->id || nss->filepath[0]) {
return ED_operator_node_editable(C);
@@ -2490,14 +2506,14 @@ static bool node_shader_script_update_poll(bContext *C)
}
/* see if we have a text datablock in context */
- Text *text = CTX_data_pointer_get_type(C, "edit_text", &RNA_Text).data;
+ Text *text = (Text *)CTX_data_pointer_get_type(C, "edit_text", &RNA_Text).data;
if (text) {
- return 1;
+ return true;
}
/* we don't check if text datablock is actually in use, too slow for poll */
- return 0;
+ return false;
}
/* recursively check for script nodes in groups using this text and update */
@@ -2541,11 +2557,11 @@ static int node_shader_script_update_exec(bContext *C, wmOperator *op)
engine->reports = op->reports;
/* get node */
- bNodeTree *ntree_base = NULL;
- bNode *node = NULL;
+ bNodeTree *ntree_base = nullptr;
+ bNode *node = nullptr;
if (nodeptr.data) {
ntree_base = (bNodeTree *)nodeptr.owner_id;
- node = nodeptr.data;
+ node = (bNode *)nodeptr.data;
}
else if (snode && snode->edittree) {
ntree_base = snode->edittree;
@@ -2560,7 +2576,7 @@ static int node_shader_script_update_exec(bContext *C, wmOperator *op)
}
else {
/* update all nodes using text datablock */
- Text *text = CTX_data_pointer_get_type(C, "edit_text", &RNA_Text).data;
+ Text *text = (Text *)CTX_data_pointer_get_type(C, "edit_text", &RNA_Text).data;
if (text) {
/* clear flags for recursion check */
@@ -2632,7 +2648,7 @@ static int viewer_border_exec(bContext *C, wmOperator *op)
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
Image *ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node");
- ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock);
+ ImBuf *ibuf = BKE_image_acquire_ibuf(ima, nullptr, &lock);
if (ibuf) {
ARegion *region = CTX_wm_region(C);
@@ -2668,7 +2684,7 @@ static int viewer_border_exec(bContext *C, wmOperator *op)
}
snode_notify(C, snode);
- WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL);
+ WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr);
}
else {
btree->flag &= ~NTREE_VIEWER_BORDER;
@@ -2708,7 +2724,7 @@ static int clear_viewer_border_exec(bContext *C, wmOperator *UNUSED(op))
btree->flag &= ~NTREE_VIEWER_BORDER;
snode_notify(C, snode);
- WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL);
+ WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr);
return OPERATOR_FINISHED;
}
@@ -2734,11 +2750,11 @@ static int node_cryptomatte_add_socket_exec(bContext *C, wmOperator *UNUSED(op))
{
SpaceNode *snode = CTX_wm_space_node(C);
PointerRNA ptr = CTX_data_pointer_get(C, "node");
- bNodeTree *ntree = NULL;
- bNode *node = NULL;
+ bNodeTree *ntree = nullptr;
+ bNode *node = nullptr;
if (ptr.data) {
- node = ptr.data;
+ node = (bNode *)ptr.data;
ntree = (bNodeTree *)ptr.owner_id;
}
else if (snode && snode->edittree) {
@@ -2778,11 +2794,11 @@ static int node_cryptomatte_remove_socket_exec(bContext *C, wmOperator *UNUSED(o
{
SpaceNode *snode = CTX_wm_space_node(C);
PointerRNA ptr = CTX_data_pointer_get(C, "node");
- bNodeTree *ntree = NULL;
- bNode *node = NULL;
+ bNodeTree *ntree = nullptr;
+ bNode *node = nullptr;
if (ptr.data) {
- node = ptr.data;
+ node = (bNode *)ptr.data;
ntree = (bNodeTree *)ptr.owner_id;
}
else if (snode && snode->edittree) {
diff --git a/source/blender/editors/space_node/node_geometry_attribute_search.cc b/source/blender/editors/space_node/node_geometry_attribute_search.cc
index 6d0cd254505..94080a7b616 100644
--- a/source/blender/editors/space_node/node_geometry_attribute_search.cc
+++ b/source/blender/editors/space_node/node_geometry_attribute_search.cc
@@ -30,6 +30,13 @@
#include "BKE_node_ui_storage.hh"
#include "BKE_object.h"
+#include "RNA_access.h"
+#include "RNA_enum_types.h"
+
+#include "ED_undo.h"
+
+#include "BLT_translation.h"
+
#include "UI_interface.h"
#include "UI_resources.h"
@@ -37,44 +44,71 @@
using blender::IndexRange;
using blender::Map;
-using blender::MultiValueMap;
using blender::Set;
using blender::StringRef;
struct AttributeSearchData {
- const bNodeTree &node_tree;
- const bNode &node;
+ AvailableAttributeInfo &dummy_info_for_search;
+ const NodeUIStorage &ui_storage;
+ bNodeSocket &socket;
+};
- uiBut *search_button;
+/* This class must not have a destructor, since it is used by buttons and freed with #MEM_freeN. */
+BLI_STATIC_ASSERT(std::is_trivially_destructible_v<AttributeSearchData>, "");
- /* Used to keep track of a button pointer over multiple redraws. Since the UI code
- * may reallocate the button, without this we might end up with a dangling pointer. */
- uiButStore *button_store;
- uiBlock *button_store_block;
-};
+static StringRef attribute_data_type_string(const CustomDataType type)
+{
+ const char *name = nullptr;
+ RNA_enum_name_from_value(rna_enum_attribute_type_items, type, &name);
+ return StringRef(IFACE_(name));
+}
+
+static StringRef attribute_domain_string(const AttributeDomain domain)
+{
+ const char *name = nullptr;
+ RNA_enum_name_from_value(rna_enum_attribute_domain_items, domain, &name);
+ return StringRef(IFACE_(name));
+}
+
+/* Unicode arrow. */
+#define MENU_SEP "\xe2\x96\xb6"
+
+static bool attribute_search_item_add(uiSearchItems *items, const AvailableAttributeInfo &item)
+{
+ const StringRef data_type_name = attribute_data_type_string(item.data_type);
+ const StringRef domain_name = attribute_domain_string(item.domain);
+ std::string search_item_text = domain_name + " " + MENU_SEP + item.name + UI_SEP_CHAR +
+ data_type_name;
+
+ return UI_search_item_add(
+ items, search_item_text.c_str(), (void *)&item, ICON_NONE, UI_BUT_HAS_SEP_CHAR, 0);
+}
-static void attribute_search_update_fn(
- const bContext *C, void *arg, const char *str, uiSearchItems *items, const bool is_first)
+static void attribute_search_update_fn(const bContext *UNUSED(C),
+ void *arg,
+ const char *str,
+ uiSearchItems *items,
+ const bool is_first)
{
AttributeSearchData *data = static_cast<AttributeSearchData *>(arg);
- const NodeUIStorage *ui_storage = BKE_node_tree_ui_storage_get_from_context(
- C, data->node_tree, data->node);
- if (ui_storage == nullptr) {
- return;
- }
- const MultiValueMap<std::string, AvailableAttributeInfo> &attribute_hints =
- ui_storage->attribute_hints;
+ const Set<AvailableAttributeInfo> &attribute_hints = data->ui_storage.attribute_hints;
- if (str[0] != '\0' && attribute_hints.lookup_as(StringRef(str)).is_empty()) {
- /* Any string may be valid, so add the current search string with the hints. */
- UI_search_item_add(items, str, (void *)str, ICON_ADD, 0, 0);
+ /* Any string may be valid, so add the current search string along with the hints. */
+ if (str[0] != '\0') {
+ /* Note that the attribute domain and data type are dummies, since
+ * #AvailableAttributeInfo equality is only based on the string. */
+ if (!attribute_hints.contains(AvailableAttributeInfo{str, ATTR_DOMAIN_AUTO, CD_PROP_BOOL})) {
+ data->dummy_info_for_search.name = std::string(str);
+ UI_search_item_add(items, str, &data->dummy_info_for_search, ICON_ADD, 0, 0);
+ }
}
if (str[0] == '\0' && !is_first) {
/* Allow clearing the text field when the string is empty, but not on the first pass,
* or opening an attribute field for the first time would show this search item. */
- UI_search_item_add(items, str, (void *)str, ICON_X, 0, 0);
+ data->dummy_info_for_search.name = std::string(str);
+ UI_search_item_add(items, str, &data->dummy_info_for_search, ICON_X, 0, 0);
}
/* Don't filter when the menu is first opened, but still run the search
@@ -82,16 +116,16 @@ static void attribute_search_update_fn(
const char *string = is_first ? "" : str;
StringSearch *search = BLI_string_search_new();
- for (const std::string &attribute_name : attribute_hints.keys()) {
- BLI_string_search_add(search, attribute_name.c_str(), (void *)&attribute_name);
+ for (const AvailableAttributeInfo &item : attribute_hints) {
+ BLI_string_search_add(search, item.name.c_str(), (void *)&item);
}
- std::string **filtered_items;
+ AvailableAttributeInfo **filtered_items;
const int filtered_amount = BLI_string_search_query(search, string, (void ***)&filtered_items);
for (const int i : IndexRange(filtered_amount)) {
- std::string *item = filtered_items[i];
- if (!UI_search_item_add(items, item->c_str(), item, ICON_NONE, 0, 0)) {
+ const AvailableAttributeInfo *item = filtered_items[i];
+ if (!attribute_search_item_add(items, *item)) {
break;
}
}
@@ -100,19 +134,34 @@ static void attribute_search_update_fn(
BLI_string_search_free(search);
}
-static void attribute_search_free_fn(void *arg)
+static void attribute_search_exec_fn(bContext *C, void *data_v, void *item_v)
{
- AttributeSearchData *data = static_cast<AttributeSearchData *>(arg);
+ AttributeSearchData *data = static_cast<AttributeSearchData *>(data_v);
+ AvailableAttributeInfo *item = static_cast<AvailableAttributeInfo *>(item_v);
- UI_butstore_free(data->button_store_block, data->button_store);
- delete data;
+ bNodeSocket &socket = data->socket;
+ bNodeSocketValueString *value = static_cast<bNodeSocketValueString *>(socket.default_value);
+ BLI_strncpy(value->value, item->name.c_str(), MAX_NAME);
+
+ ED_undo_push(C, "Assign Attribute Name");
}
-void node_geometry_add_attribute_search_button(const bNodeTree *node_tree,
+void node_geometry_add_attribute_search_button(const bContext *C,
+ const bNodeTree *node_tree,
const bNode *node,
PointerRNA *socket_ptr,
uiLayout *layout)
{
+ const NodeUIStorage *ui_storage = BKE_node_tree_ui_storage_get_from_context(
+ C, *node_tree, *node);
+
+ if (ui_storage == nullptr) {
+ uiItemR(layout, socket_ptr, "default_value", 0, "", 0);
+ return;
+ }
+
+ const NodeTreeUIStorage *tree_ui_storage = node_tree->ui_storage;
+
uiBlock *block = uiLayoutGetBlock(layout);
uiBut *but = uiDefIconTextButR(block,
UI_BTYPE_SEARCH_MENU,
@@ -132,22 +181,19 @@ void node_geometry_add_attribute_search_button(const bNodeTree *node_tree,
0.0f,
"");
- AttributeSearchData *data = new AttributeSearchData{
- *node_tree,
- *node,
- but,
- UI_butstore_create(block),
- block,
- };
-
- UI_butstore_register(data->button_store, &data->search_button);
+ AttributeSearchData *data = OBJECT_GUARDED_NEW(AttributeSearchData,
+ {tree_ui_storage->dummy_info_for_search,
+ *ui_storage,
+ *static_cast<bNodeSocket *>(socket_ptr->data)});
UI_but_func_search_set_results_are_suggestions(but, true);
+ UI_but_func_search_set_sep_string(but, MENU_SEP);
UI_but_func_search_set(but,
nullptr,
attribute_search_update_fn,
static_cast<void *>(data),
- attribute_search_free_fn,
+ true,
nullptr,
+ attribute_search_exec_fn,
nullptr);
}
diff --git a/source/blender/editors/space_node/node_gizmo.c b/source/blender/editors/space_node/node_gizmo.c
index 28d7e1b8d04..8547c825230 100644
--- a/source/blender/editors/space_node/node_gizmo.c
+++ b/source/blender/editors/space_node/node_gizmo.c
@@ -97,7 +97,6 @@ static void gizmo_node_backdrop_prop_matrix_set(const wmGizmo *UNUSED(gz),
BLI_assert(gz_prop->type->array_length == 16);
SpaceNode *snode = gz_prop->custom_func.user_data;
snode->zoom = matrix[0][0];
- snode->zoom = matrix[1][1];
snode->xof = matrix[3][0];
snode->yof = matrix[3][1];
}
diff --git a/source/blender/editors/space_node/node_group.c b/source/blender/editors/space_node/node_group.cc
index e1de4bfc21e..851d386ad0d 100644
--- a/source/blender/editors/space_node/node_group.c
+++ b/source/blender/editors/space_node/node_group.cc
@@ -21,7 +21,7 @@
* \ingroup spnode
*/
-#include <stdlib.h>
+#include <cstdlib>
#include "MEM_guardedalloc.h"
@@ -70,9 +70,8 @@ static bool node_group_operator_active_poll(bContext *C)
SpaceNode *snode = CTX_wm_space_node(C);
/* Group operators only defined for standard node tree types.
- * Disabled otherwise to allow pynodes define their own operators
- * with same keymap.
- */
+ * Disabled otherwise to allow python-nodes define their own operators
+ * with same key-map. */
if (STR_ELEM(snode->tree_idname,
"ShaderNodeTree",
"CompositorNodeTree",
@@ -90,9 +89,8 @@ static bool node_group_operator_editable(bContext *C)
SpaceNode *snode = CTX_wm_space_node(C);
/* Group operators only defined for standard node tree types.
- * Disabled otherwise to allow pynodes define their own operators
- * with same keymap.
- */
+ * Disabled otherwise to allow python-nodes define their own operators
+ * with same key-map. */
if (ED_node_is_shader(snode) || ED_node_is_compositor(snode) || ED_node_is_texture(snode) ||
ED_node_is_geometry(snode)) {
return true;
@@ -135,7 +133,7 @@ static bNode *node_group_get_active(bContext *C, const char *node_idname)
if (node && STREQ(node->idname, node_idname)) {
return node;
}
- return NULL;
+ return nullptr;
}
/** \} */
@@ -165,7 +163,7 @@ static int node_group_edit_exec(bContext *C, wmOperator *op)
ED_node_tree_pop(snode);
}
- WM_event_add_notifier(C, NC_SCENE | ND_NODES, NULL);
+ WM_event_add_notifier(C, NC_SCENE | ND_NODES, nullptr);
return OPERATOR_FINISHED;
}
@@ -200,7 +198,8 @@ void NODE_OT_group_edit(wmOperatorType *ot)
static AnimationBasePathChange *animation_basepath_change_new(const char *src_basepath,
const char *dst_basepath)
{
- AnimationBasePathChange *basepath_change = MEM_callocN(sizeof(*basepath_change), AT);
+ AnimationBasePathChange *basepath_change = (AnimationBasePathChange *)MEM_callocN(
+ sizeof(*basepath_change), AT);
basepath_change->src_basepath = src_basepath;
basepath_change->dst_basepath = dst_basepath;
return basepath_change;
@@ -218,13 +217,13 @@ static void animation_basepath_change_free(AnimationBasePathChange *basepath_cha
/* returns 1 if its OK */
static int node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode)
{
- /* clear new pointers, set in copytree */
+ /* Clear new pointers, set in #ntreeCopyTree_ex_new_pointers. */
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
- node->new_node = NULL;
+ node->new_node = nullptr;
}
- ListBase anim_basepaths = {NULL, NULL};
- LinkNode *nodes_delayed_free = NULL;
+ ListBase anim_basepaths = {nullptr, nullptr};
+ LinkNode *nodes_delayed_free = nullptr;
bNodeTree *ngroup = (bNodeTree *)gnode->id;
/* wgroup is a temporary copy of the NodeTree we're merging in
@@ -247,7 +246,7 @@ static int node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode)
/* keep track of this node's RNA "base" path (the part of the path identifying the node)
* if the old nodetree has animation data which potentially covers this node
*/
- const char *old_animation_basepath = NULL;
+ const char *old_animation_basepath = nullptr;
if (wgroup->adt) {
PointerRNA ptr;
RNA_pointer_create(&wgroup->id, &RNA_Node, node, &ptr);
@@ -277,7 +276,7 @@ static int node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode)
node->flag |= NODE_SELECT;
}
- bNodeLink *glinks_first = ntree->links.last;
+ bNodeLink *glinks_first = (bNodeLink *)ntree->links.last;
/* Add internal links to the ntree */
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &wgroup->links) {
@@ -285,10 +284,10 @@ static int node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode)
BLI_addtail(&ntree->links, link);
}
- bNodeLink *glinks_last = ntree->links.last;
+ bNodeLink *glinks_last = (bNodeLink *)ntree->links.last;
/* and copy across the animation,
- * note that the animation data's action can be NULL here */
+ * note that the animation data's action can be nullptr here */
if (wgroup->adt) {
bAction *waction;
@@ -307,7 +306,7 @@ static int node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode)
/* free temp action too */
if (waction) {
BKE_id_free(bmain, waction);
- wgroup->adt->action = NULL;
+ wgroup->adt->action = nullptr;
}
}
@@ -317,14 +316,14 @@ static int node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode)
/* restore external links to and from the gnode */
/* input links */
- if (glinks_first != NULL) {
+ if (glinks_first != nullptr) {
for (bNodeLink *link = glinks_first->next; link != glinks_last->next; link = link->next) {
if (link->fromnode->type == NODE_GROUP_INPUT) {
const char *identifier = link->fromsock->identifier;
int num_external_links = 0;
/* find external links to this input */
- for (bNodeLink *tlink = ntree->links.first; tlink != glinks_first->next;
+ for (bNodeLink *tlink = (bNodeLink *)ntree->links.first; tlink != glinks_first->next;
tlink = tlink->next) {
if (tlink->tonode == gnode && STREQ(tlink->tosock->identifier, identifier)) {
nodeAddLink(ntree, tlink->fromnode, tlink->fromsock, link->tonode, link->tosock);
@@ -348,10 +347,11 @@ static int node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode)
}
/* Also iterate over new links to cover passthrough links. */
- glinks_last = ntree->links.last;
+ glinks_last = (bNodeLink *)ntree->links.last;
/* output links */
- for (bNodeLink *link = ntree->links.first; link != glinks_first->next; link = link->next) {
+ for (bNodeLink *link = (bNodeLink *)ntree->links.first; link != glinks_first->next;
+ link = link->next) {
if (link->fromnode == gnode) {
const char *identifier = link->fromsock->identifier;
int num_internal_links = 0;
@@ -384,7 +384,7 @@ static int node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode)
}
while (nodes_delayed_free) {
- bNode *node = BLI_linklist_pop(&nodes_delayed_free);
+ bNode *node = (bNode *)BLI_linklist_pop(&nodes_delayed_free);
nodeRemoveNode(bmain, ntree, node, false);
}
@@ -455,10 +455,10 @@ static int node_group_separate_selected(
/* clear new pointers, set in BKE_node_copy_ex(). */
LISTBASE_FOREACH (bNode *, node, &ngroup->nodes) {
- node->new_node = NULL;
+ node->new_node = nullptr;
}
- ListBase anim_basepaths = {NULL, NULL};
+ ListBase anim_basepaths = {nullptr, nullptr};
/* add selected nodes into the ntree */
LISTBASE_FOREACH_MUTABLE (bNode *, node, &ngroup->nodes) {
@@ -543,7 +543,7 @@ static int node_group_separate_selected(
}
/* and copy across the animation,
- * note that the animation data's action can be NULL here */
+ * note that the animation data's action can be nullptr here */
if (ngroup->adt) {
/* now perform the moving */
BKE_animdata_transfer_by_basepath(bmain, &ngroup->id, &ntree->id, &anim_basepaths);
@@ -562,16 +562,16 @@ static int node_group_separate_selected(
return 1;
}
-typedef enum eNodeGroupSeparateType {
+enum eNodeGroupSeparateType {
NODE_GS_COPY,
NODE_GS_MOVE,
-} eNodeGroupSeparateType;
+};
/* Operator Property */
static const EnumPropertyItem node_group_separate_types[] = {
{NODE_GS_COPY, "COPY", 0, "Copy", "Copy to parent node tree, keep group intact"},
{NODE_GS_MOVE, "MOVE", 0, "Move", "Move to parent node tree, remove from group"},
- {0, NULL, 0, NULL, NULL},
+ {0, nullptr, 0, nullptr, nullptr},
};
static int node_group_separate_exec(bContext *C, wmOperator *op)
@@ -628,8 +628,8 @@ static int node_group_separate_invoke(bContext *C,
uiLayout *layout = UI_popup_menu_layout(pup);
uiLayoutSetOperatorContext(layout, WM_OP_EXEC_DEFAULT);
- uiItemEnumO(layout, "NODE_OT_group_separate", NULL, 0, "type", NODE_GS_COPY);
- uiItemEnumO(layout, "NODE_OT_group_separate", NULL, 0, "type", NODE_GS_MOVE);
+ uiItemEnumO(layout, "NODE_OT_group_separate", nullptr, 0, "type", NODE_GS_COPY);
+ uiItemEnumO(layout, "NODE_OT_group_separate", nullptr, 0, "type", NODE_GS_MOVE);
UI_popup_menu_end(C, pup);
@@ -674,13 +674,24 @@ static bool node_group_make_test_selected(bNodeTree *ntree,
int ok = true;
/* make a local pseudo node tree to pass to the node poll functions */
- bNodeTree *ngroup = ntreeAddTree(NULL, "Pseudo Node Group", ntree_idname);
+ bNodeTree *ngroup = ntreeAddTree(nullptr, "Pseudo Node Group", ntree_idname);
/* check poll functions for selected nodes */
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node_group_make_use_node(node, gnode)) {
- if (node->typeinfo->poll_instance && !node->typeinfo->poll_instance(node, ngroup)) {
- BKE_reportf(reports, RPT_WARNING, "Can not add node '%s' in a group", node->name);
+ const char *disabled_hint = nullptr;
+ if (node->typeinfo->poll_instance &&
+ !node->typeinfo->poll_instance(node, ngroup, &disabled_hint)) {
+ if (disabled_hint) {
+ BKE_reportf(reports,
+ RPT_WARNING,
+ "Can not add node '%s' in a group:\n %s",
+ node->name,
+ disabled_hint);
+ }
+ else {
+ BKE_reportf(reports, RPT_WARNING, "Can not add node '%s' in a group", node->name);
+ }
ok = false;
break;
}
@@ -770,7 +781,7 @@ static void node_group_make_insert_selected(const bContext *C, bNodeTree *ntree,
expose_visible = true;
}
- ListBase anim_basepaths = {NULL, NULL};
+ ListBase anim_basepaths = {nullptr, nullptr};
/* move nodes over */
LISTBASE_FOREACH_MUTABLE (bNode *, node, &ntree->nodes) {
@@ -984,10 +995,10 @@ static bNode *node_group_make_from_selected(const bContext *C,
Main *bmain = CTX_data_main(C);
float min[2], max[2];
- const int totselect = node_get_selected_minmax(ntree, NULL, min, max, false);
+ const int totselect = node_get_selected_minmax(ntree, nullptr, min, max, false);
/* don't make empty group */
if (totselect == 0) {
- return NULL;
+ return nullptr;
}
/* new nodetree */
@@ -1018,7 +1029,7 @@ static int node_group_make_exec(bContext *C, wmOperator *op)
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
- if (!node_group_make_test_selected(ntree, NULL, ntree_idname, op->reports)) {
+ if (!node_group_make_test_selected(ntree, nullptr, ntree_idname, op->reports)) {
return OPERATOR_CANCELLED;
}
@@ -1031,7 +1042,7 @@ static int node_group_make_exec(bContext *C, wmOperator *op)
if (ngroup) {
ED_node_tree_push(snode, ngroup, gnode);
LISTBASE_FOREACH (bNode *, node, &ngroup->nodes) {
- sort_multi_input_socket_links(snode, node, NULL, NULL);
+ sort_multi_input_socket_links(snode, node, nullptr, nullptr);
}
ntreeUpdateTree(bmain, ngroup);
}
diff --git a/source/blender/editors/space_node/node_intern.h b/source/blender/editors/space_node/node_intern.h
index 21a36ff9683..2fcc59cde0b 100644
--- a/source/blender/editors/space_node/node_intern.h
+++ b/source/blender/editors/space_node/node_intern.h
@@ -58,9 +58,11 @@ typedef struct bNodeLinkDrag {
bool from_multi_input_socket;
int in_out;
- /** Temporarily stores the last picked link from multi input socket operator. */
+ /** Temporarily stores the last picked link from multi-input socket operator. */
struct bNodeLink *last_picked_multi_input_socket_link;
+ /** Temporarily stores the last hovered socket for multi-input socket operator.
+ * Store it to recalculate sorting after it is no longer hovered. */
struct bNode *last_node_hovered_while_dragging_a_link;
} bNodeLinkDrag;
@@ -260,7 +262,7 @@ int node_render_changed_exec(bContext *, struct wmOperator *);
int node_find_indicated_socket(struct SpaceNode *snode,
struct bNode **nodep,
struct bNodeSocket **sockp,
- float cursor[2],
+ const float cursor[2],
int in_out);
void NODE_OT_duplicate(struct wmOperatorType *ot);
@@ -273,6 +275,7 @@ void NODE_OT_hide_toggle(struct wmOperatorType *ot);
void NODE_OT_hide_socket_toggle(struct wmOperatorType *ot);
void NODE_OT_preview_toggle(struct wmOperatorType *ot);
void NODE_OT_options_toggle(struct wmOperatorType *ot);
+void NODE_OT_active_preview_toggle(struct wmOperatorType *ot);
void NODE_OT_node_copy_color(struct wmOperatorType *ot);
void NODE_OT_read_viewlayers(struct wmOperatorType *ot);
@@ -307,7 +310,8 @@ void NODE_OT_cryptomatte_layer_add(struct wmOperatorType *ot);
void NODE_OT_cryptomatte_layer_remove(struct wmOperatorType *ot);
/* node_geometry_attribute_search.cc */
-void node_geometry_add_attribute_search_button(const struct bNodeTree *node_tree,
+void node_geometry_add_attribute_search_button(const struct bContext *C,
+ const struct bNodeTree *node_tree,
const struct bNode *node,
struct PointerRNA *socket_ptr,
struct uiLayout *layout);
diff --git a/source/blender/editors/space_node/node_relationships.c b/source/blender/editors/space_node/node_relationships.cc
index 4b2290c094b..57dc0b6fef2 100644
--- a/source/blender/editors/space_node/node_relationships.c
+++ b/source/blender/editors/space_node/node_relationships.cc
@@ -55,12 +55,14 @@
#include "node_intern.h" /* own include */
-/* ****************** Relations helpers *********************** */
+/* -------------------------------------------------------------------- */
+/** \name Relations Helpers
+ * \{ */
static bool ntree_has_drivers(bNodeTree *ntree)
{
const AnimData *adt = BKE_animdata_from_id(&ntree->id);
- if (adt == NULL) {
+ if (adt == nullptr) {
return false;
}
return !BLI_listbase_is_empty(&adt->drivers);
@@ -102,7 +104,7 @@ static bool node_group_has_output_dfs(bNode *node)
return false;
}
ntree->id.tag |= LIB_TAG_DOIT;
- for (bNode *current_node = ntree->nodes.first; current_node != NULL;
+ for (bNode *current_node = (bNode *)ntree->nodes.first; current_node != nullptr;
current_node = current_node->next) {
if (current_node->type == NODE_GROUP) {
if (current_node->id && node_group_has_output_dfs(current_node)) {
@@ -120,7 +122,7 @@ static bool node_group_has_output(Main *bmain, bNode *node)
{
BLI_assert(ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP));
bNodeTree *ntree = (bNodeTree *)node->id;
- if (ntree == NULL) {
+ if (ntree == nullptr) {
return false;
}
BKE_main_id_tag_listbase(&bmain->nodetrees, LIB_TAG_DOIT, false);
@@ -145,7 +147,7 @@ bool node_connected_to_output(Main *bmain, bNodeTree *ntree, bNode *node)
* is connected to and so eventually.
*/
if (ELEM(current_node->type, NODE_GROUP, NODE_CUSTOM_GROUP)) {
- if (current_node->id != NULL && ntree_has_drivers((bNodeTree *)current_node->id)) {
+ if (current_node->id != nullptr && ntree_has_drivers((bNodeTree *)current_node->id)) {
return true;
}
if (ntree_check_nodes_connected(ntree, node, current_node) &&
@@ -162,14 +164,18 @@ bool node_connected_to_output(Main *bmain, bNodeTree *ntree, bNode *node)
return false;
}
-/* ****************** Add *********************** */
+/** \} */
-typedef struct bNodeListItem {
+/* -------------------------------------------------------------------- */
+/** \name Add Node
+ * \{ */
+
+struct bNodeListItem {
struct bNodeListItem *next, *prev;
struct bNode *node;
-} bNodeListItem;
+};
-typedef struct NodeInsertOfsData {
+struct NodeInsertOfsData {
bNodeTree *ntree;
bNode *insert; /* inserted node */
bNode *prev, *next; /* prev/next node in the chain */
@@ -178,7 +184,7 @@ typedef struct NodeInsertOfsData {
wmTimer *anim_timer;
float offset_x; /* offset to apply to node chain */
-} NodeInsertOfsData;
+};
static void clear_picking_highlight(ListBase *links)
{
@@ -189,8 +195,8 @@ static void clear_picking_highlight(ListBase *links)
static LinkData *create_drag_link(Main *bmain, SpaceNode *snode, bNode *node, bNodeSocket *sock)
{
- LinkData *linkdata = MEM_callocN(sizeof(LinkData), "drag link op link data");
- bNodeLink *oplink = MEM_callocN(sizeof(bNodeLink), "drag link op link");
+ LinkData *linkdata = (LinkData *)MEM_callocN(sizeof(LinkData), "drag link op link data");
+ bNodeLink *oplink = (bNodeLink *)MEM_callocN(sizeof(bNodeLink), "drag link op link");
linkdata->data = oplink;
if (sock->in_out == SOCK_OUT) {
oplink->fromnode = node;
@@ -224,6 +230,12 @@ static void pick_link(const bContext *C,
BLI_addtail(&nldrag->links, linkdata);
nodeRemLink(snode->edittree, link_to_pick);
+
+ BLI_assert(nldrag->last_node_hovered_while_dragging_a_link != nullptr);
+
+ sort_multi_input_socket_links(
+ snode, nldrag->last_node_hovered_while_dragging_a_link, nullptr, nullptr);
+
/* Send changed event to original link->tonode. */
if (node) {
snode_update(snode, node);
@@ -245,22 +257,12 @@ static void pick_input_link_by_link_intersect(const bContext *C,
bNodeSocket *socket;
node_find_indicated_socket(snode, &node, &socket, drag_start, SOCK_IN);
- const float trigger_drag_distance = 25.0f;
- const float cursor_link_touch_distance = 25.0f;
-
- const float socket_height = node_socket_calculate_height(socket);
-
- float cursor_to_socket_relative[2];
- float socket_position[2] = {socket->locx, socket->locy};
- sub_v2_v2v2(cursor_to_socket_relative, cursor, socket_position);
- float distance_from_socket_v2[2] = {
- max_ff(0, fabs(cursor_to_socket_relative[0]) - NODE_SOCKSIZE * 0.5),
- max_ff(0, fabs(cursor_to_socket_relative[1]) - socket_height)};
- const float distance_from_socket = len_v2(distance_from_socket_v2);
+ /* Distance to test overlapping of cursor on link. */
+ const float cursor_link_touch_distance = 12.5f * UI_DPI_FAC;
const int resolution = NODE_LINK_RESOL;
- bNodeLink *link_to_pick = NULL;
+ bNodeLink *link_to_pick = nullptr;
clear_picking_highlight(&snode->edittree->links);
LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) {
if (link->tosock == socket) {
@@ -301,7 +303,7 @@ static void pick_input_link_by_link_intersect(const bContext *C,
link_to_pick->flag |= NODE_LINK_TEMP_HIGHLIGHT;
ED_area_tag_redraw(CTX_wm_area(C));
- if (distance_from_socket > trigger_drag_distance) {
+ if (!node_find_indicated_socket(snode, &node, &socket, cursor, SOCK_IN)) {
pick_link(C, op, nldrag, snode, node, link_to_pick);
}
}
@@ -309,8 +311,8 @@ static void pick_input_link_by_link_intersect(const bContext *C,
static int sort_nodes_locx(const void *a, const void *b)
{
- const bNodeListItem *nli1 = a;
- const bNodeListItem *nli2 = b;
+ const bNodeListItem *nli1 = (const bNodeListItem *)a;
+ const bNodeListItem *nli2 = (const bNodeListItem *)b;
const bNode *node1 = nli1->node;
const bNode *node2 = nli2->node;
@@ -323,14 +325,14 @@ static int sort_nodes_locx(const void *a, const void *b)
static bool socket_is_available(bNodeTree *UNUSED(ntree), bNodeSocket *sock, const bool allow_used)
{
if (nodeSocketIsHidden(sock)) {
- return 0;
+ return false;
}
if (!allow_used && (sock->flag & SOCK_IN_USE)) {
- return 0;
+ return false;
}
- return 1;
+ return true;
}
static bNodeSocket *best_socket_output(bNodeTree *ntree,
@@ -378,10 +380,10 @@ static bNodeSocket *best_socket_output(bNodeTree *ntree,
/* Always allow linking to an reroute node. The socket type of the reroute sockets might change
* after the link has been created. */
if (node->type == NODE_REROUTE) {
- return node->outputs.first;
+ return (bNodeSocket *)node->outputs.first;
}
- return NULL;
+ return nullptr;
}
/* this is a bit complicated, but designed to prioritize finding
@@ -413,7 +415,7 @@ static bNodeSocket *best_socket_input(bNodeTree *ntree, bNode *node, int num, in
}
}
- return NULL;
+ return nullptr;
}
static bool snode_autoconnect_input(SpaceNode *snode,
@@ -434,10 +436,10 @@ static bool snode_autoconnect_input(SpaceNode *snode,
return true;
}
-typedef struct LinkAndPosition {
+struct LinkAndPosition {
struct bNodeLink *link;
float multi_socket_position[2];
-} LinkAndPosition;
+};
static int compare_link_by_y_position(const void *a, const void *b)
{
@@ -461,14 +463,14 @@ void sort_multi_input_socket_links(SpaceNode *snode,
}
/* The total is calculated in #node_update_nodetree, which runs before this draw step. */
int total_inputs = socket->total_inputs + 1;
- struct LinkAndPosition **input_links = MEM_malloc_arrayN(
+ struct LinkAndPosition **input_links = (LinkAndPosition **)MEM_malloc_arrayN(
total_inputs, sizeof(LinkAndPosition *), __func__);
int index = 0;
LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) {
if (link->tosock == socket) {
- struct LinkAndPosition *link_and_position = MEM_callocN(sizeof(struct LinkAndPosition),
- __func__);
+ struct LinkAndPosition *link_and_position = (LinkAndPosition *)MEM_callocN(
+ sizeof(struct LinkAndPosition), __func__);
link_and_position->link = link;
node_link_calculate_multi_input_position(link->tosock->locx,
link->tosock->locy,
@@ -481,7 +483,8 @@ void sort_multi_input_socket_links(SpaceNode *snode,
}
if (drag_link) {
- LinkAndPosition *link_and_position = MEM_callocN(sizeof(LinkAndPosition), __func__);
+ LinkAndPosition *link_and_position = (LinkAndPosition *)MEM_callocN(sizeof(LinkAndPosition),
+ __func__);
link_and_position->link = drag_link;
copy_v2_v2(link_and_position->multi_socket_position, cursor);
input_links[index] = link_and_position;
@@ -509,11 +512,12 @@ static void snode_autoconnect(Main *bmain,
const bool replace)
{
bNodeTree *ntree = snode->edittree;
- ListBase *nodelist = MEM_callocN(sizeof(ListBase), "items_list");
+ ListBase *nodelist = (ListBase *)MEM_callocN(sizeof(ListBase), "items_list");
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->flag & NODE_SELECT) {
- bNodeListItem *nli = MEM_mallocN(sizeof(bNodeListItem), "temporary node list item");
+ bNodeListItem *nli = (bNodeListItem *)MEM_mallocN(sizeof(bNodeListItem),
+ "temporary node list item");
nli->node = node;
BLI_addtail(nodelist, nli);
}
@@ -526,7 +530,7 @@ static void snode_autoconnect(Main *bmain,
LISTBASE_FOREACH (bNodeListItem *, nli, nodelist) {
bool has_selected_inputs = false;
- if (nli->next == NULL) {
+ if (nli->next == nullptr) {
break;
}
@@ -540,7 +544,7 @@ static void snode_autoconnect(Main *bmain,
/* if there are selected sockets, connect those */
LISTBASE_FOREACH (bNodeSocket *, sock_to, &node_to->inputs) {
if (sock_to->flag & SELECT) {
- has_selected_inputs = 1;
+ has_selected_inputs = true;
if (!socket_is_available(ntree, sock_to, replace)) {
continue;
@@ -592,14 +596,18 @@ static void snode_autoconnect(Main *bmain,
MEM_freeN(nodelist);
}
-/* *************************** link viewer op ******************** */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Link Viewer Operator
+ * \{ */
static int node_link_viewer(const bContext *C, bNode *tonode)
{
SpaceNode *snode = CTX_wm_space_node(C);
/* context check */
- if (tonode == NULL || BLI_listbase_is_empty(&tonode->outputs)) {
+ if (tonode == nullptr || BLI_listbase_is_empty(&tonode->outputs)) {
return OPERATOR_CANCELLED;
}
if (ELEM(tonode->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) {
@@ -607,7 +615,7 @@ static int node_link_viewer(const bContext *C, bNode *tonode)
}
/* get viewer */
- bNode *viewer_node = NULL;
+ bNode *viewer_node = nullptr;
LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) {
if (ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) {
if (node->flag & NODE_DO_OUTPUT) {
@@ -617,7 +625,7 @@ static int node_link_viewer(const bContext *C, bNode *tonode)
}
}
/* no viewer, we make one active */
- if (viewer_node == NULL) {
+ if (viewer_node == nullptr) {
LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) {
if (ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) {
node->flag |= NODE_DO_OUTPUT;
@@ -627,14 +635,14 @@ static int node_link_viewer(const bContext *C, bNode *tonode)
}
}
- bNodeSocket *sock = NULL;
- bNodeLink *link = NULL;
+ bNodeSocket *sock = nullptr;
+ bNodeLink *link = nullptr;
/* try to find an already connected socket to cycle to the next */
if (viewer_node) {
- link = NULL;
+ link = nullptr;
- for (link = snode->edittree->links.first; link; link = link->next) {
+ for (link = (bNodeLink *)snode->edittree->links.first; link; link = link->next) {
if (link->tonode == viewer_node && link->fromnode == tonode) {
if (link->tosock == viewer_node->inputs.first) {
break;
@@ -667,7 +675,7 @@ static int node_link_viewer(const bContext *C, bNode *tonode)
/* find a socket starting from the first socket */
if (!sock) {
- for (sock = tonode->outputs.first; sock; sock = sock->next) {
+ for (sock = (bNodeSocket *)tonode->outputs.first; sock; sock = sock->next) {
if (!nodeSocketIsHidden(sock)) {
break;
}
@@ -678,24 +686,25 @@ static int node_link_viewer(const bContext *C, bNode *tonode)
/* add a new viewer if none exists yet */
if (!viewer_node) {
/* XXX location is a quick hack, just place it next to the linked socket */
- viewer_node = node_add_node(C, NULL, CMP_NODE_VIEWER, sock->locx + 100, sock->locy);
+ viewer_node = node_add_node(C, nullptr, CMP_NODE_VIEWER, sock->locx + 100, sock->locy);
if (!viewer_node) {
return OPERATOR_CANCELLED;
}
- link = NULL;
+ link = nullptr;
}
else {
/* get link to viewer */
- for (link = snode->edittree->links.first; link; link = link->next) {
+ for (link = (bNodeLink *)snode->edittree->links.first; link; link = link->next) {
if (link->tonode == viewer_node && link->tosock == viewer_node->inputs.first) {
break;
}
}
}
- if (link == NULL) {
- nodeAddLink(snode->edittree, tonode, sock, viewer_node, viewer_node->inputs.first);
+ if (link == nullptr) {
+ nodeAddLink(
+ snode->edittree, tonode, sock, viewer_node, (bNodeSocket *)viewer_node->inputs.first);
}
else {
link->fromnode = tonode;
@@ -745,7 +754,11 @@ void NODE_OT_link_viewer(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/* *************************** add link op ******************** */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Add Link Operator
+ * \{ */
static void node_link_update_header(bContext *C, bNodeLinkDrag *UNUSED(nldrag))
{
@@ -755,14 +768,11 @@ static void node_link_update_header(bContext *C, bNodeLinkDrag *UNUSED(nldrag))
ED_workspace_status_text(C, header);
}
-static int node_count_links(bNodeTree *ntree, bNodeSocket *sock)
+static int node_count_links(const bNodeTree *ntree, const bNodeSocket *socket)
{
int count = 0;
LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
- if (link->fromsock == sock) {
- count++;
- }
- if (link->tosock == sock) {
+ if (ELEM(socket, link->fromsock, link->tosock)) {
count++;
}
}
@@ -786,7 +796,7 @@ static void node_remove_extra_links(SpaceNode *snode, bNodeLink *link)
if (tlink && tlink->fromsock == from) {
if (from_count > from_link_limit) {
nodeRemLink(ntree, tlink);
- tlink = NULL;
+ tlink = nullptr;
from_count--;
}
}
@@ -794,13 +804,13 @@ static void node_remove_extra_links(SpaceNode *snode, bNodeLink *link)
if (tlink && tlink->tosock == to) {
if (to_count > to_link_limit) {
nodeRemLink(ntree, tlink);
- tlink = NULL;
+ tlink = nullptr;
to_count--;
}
else if (tlink->fromsock == from) {
/* Also remove link if it comes from the same output. */
nodeRemLink(ntree, tlink);
- tlink = NULL;
+ tlink = nullptr;
to_count--;
from_count--;
}
@@ -813,13 +823,13 @@ static void node_link_exit(bContext *C, wmOperator *op, bool apply_links)
Main *bmain = CTX_data_main(C);
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *ntree = snode->edittree;
- bNodeLinkDrag *nldrag = op->customdata;
+ bNodeLinkDrag *nldrag = (bNodeLinkDrag *)op->customdata;
bool do_tag_update = false;
/* avoid updates while applying links */
ntree->is_updating = true;
LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) {
- bNodeLink *link = linkdata->data;
+ bNodeLink *link = (bNodeLink *)linkdata->data;
/* See note below, but basically TEST flag means that the link
* was connected to output (or to a node which affects the
@@ -859,8 +869,6 @@ static void node_link_exit(bContext *C, wmOperator *op, bool apply_links)
}
ntree->is_updating = false;
- do_tag_update |= ED_node_is_geometry(snode);
-
ntreeUpdateTree(bmain, ntree);
snode_notify(C, snode);
if (do_tag_update) {
@@ -876,14 +884,14 @@ static void node_link_exit(bContext *C, wmOperator *op, bool apply_links)
static void node_link_find_socket(bContext *C, wmOperator *op, float cursor[2])
{
SpaceNode *snode = CTX_wm_space_node(C);
- bNodeLinkDrag *nldrag = op->customdata;
+ bNodeLinkDrag *nldrag = (bNodeLinkDrag *)op->customdata;
if (nldrag->in_out == SOCK_OUT) {
bNode *tnode;
- bNodeSocket *tsock = NULL;
+ bNodeSocket *tsock = nullptr;
if (node_find_indicated_socket(snode, &tnode, &tsock, cursor, SOCK_IN)) {
LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) {
- bNodeLink *link = linkdata->data;
+ bNodeLink *link = (bNodeLink *)linkdata->data;
/* skip if socket is on the same node as the fromsock */
if (tnode && link->fromnode == tnode) {
@@ -891,7 +899,7 @@ static void node_link_find_socket(bContext *C, wmOperator *op, float cursor[2])
}
/* Skip if tsock is already linked with this output. */
- bNodeLink *existing_link_connected_to_fromsock = NULL;
+ bNodeLink *existing_link_connected_to_fromsock = nullptr;
LISTBASE_FOREACH (bNodeLink *, existing_link, &snode->edittree->links) {
if (existing_link->fromsock == link->fromsock && existing_link->tosock == tsock) {
existing_link_connected_to_fromsock = existing_link;
@@ -908,27 +916,29 @@ static void node_link_find_socket(bContext *C, wmOperator *op, float cursor[2])
existing_link_connected_to_fromsock->multi_input_socket_index;
continue;
}
- sort_multi_input_socket_links(snode, tnode, link, cursor);
+ if (link->tosock && link->tosock->flag & SOCK_MULTI_INPUT) {
+ sort_multi_input_socket_links(snode, tnode, link, cursor);
+ }
}
}
else {
LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) {
- bNodeLink *link = linkdata->data;
+ bNodeLink *link = (bNodeLink *)linkdata->data;
if (nldrag->last_node_hovered_while_dragging_a_link) {
sort_multi_input_socket_links(
- snode, nldrag->last_node_hovered_while_dragging_a_link, NULL, cursor);
+ snode, nldrag->last_node_hovered_while_dragging_a_link, nullptr, cursor);
}
- link->tonode = NULL;
- link->tosock = NULL;
+ link->tonode = nullptr;
+ link->tosock = nullptr;
}
}
}
else {
bNode *tnode;
- bNodeSocket *tsock = NULL;
+ bNodeSocket *tsock = nullptr;
if (node_find_indicated_socket(snode, &tnode, &tsock, cursor, SOCK_OUT)) {
LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) {
- bNodeLink *link = linkdata->data;
+ bNodeLink *link = (bNodeLink *)linkdata->data;
/* skip if this is already the target socket */
if (link->fromsock == tsock) {
@@ -946,10 +956,10 @@ static void node_link_find_socket(bContext *C, wmOperator *op, float cursor[2])
}
else {
LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) {
- bNodeLink *link = linkdata->data;
+ bNodeLink *link = (bNodeLink *)linkdata->data;
- link->fromnode = NULL;
- link->fromsock = NULL;
+ link->fromnode = nullptr;
+ link->fromsock = nullptr;
}
}
}
@@ -959,7 +969,7 @@ static void node_link_find_socket(bContext *C, wmOperator *op, float cursor[2])
/* in_out = starting socket */
static int node_link_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
- bNodeLinkDrag *nldrag = op->customdata;
+ bNodeLinkDrag *nldrag = (bNodeLinkDrag *)op->customdata;
ARegion *region = CTX_wm_region(C);
float cursor[2];
@@ -984,7 +994,7 @@ static int node_link_modal(bContext *C, wmOperator *op, const wmEvent *event)
if (event->val == KM_RELEASE) {
node_link_exit(C, op, true);
- ED_workspace_status_text(C, NULL);
+ ED_workspace_status_text(C, nullptr);
ED_region_tag_redraw(region);
SpaceNode *snode = CTX_wm_space_node(C);
clear_picking_highlight(&snode->edittree->links);
@@ -1000,13 +1010,13 @@ static int node_link_modal(bContext *C, wmOperator *op, const wmEvent *event)
/* return 1 when socket clicked */
static bNodeLinkDrag *node_link_init(Main *bmain, SpaceNode *snode, float cursor[2], bool detach)
{
- bNodeLinkDrag *nldrag = NULL;
+ bNodeLinkDrag *nldrag = nullptr;
/* output indicated? */
bNode *node;
bNodeSocket *sock;
if (node_find_indicated_socket(snode, &node, &sock, cursor, SOCK_OUT)) {
- nldrag = MEM_callocN(sizeof(bNodeLinkDrag), "drag link op customdata");
+ nldrag = (bNodeLinkDrag *)MEM_callocN(sizeof(bNodeLinkDrag), "drag link op customdata");
const int num_links = nodeCountSocketLinks(snode->edittree, sock);
int link_limit = nodeSocketLinkLimit(sock);
@@ -1016,11 +1026,11 @@ static bNodeLinkDrag *node_link_init(Main *bmain, SpaceNode *snode, float cursor
/* detach current links and store them in the operator data */
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &snode->edittree->links) {
if (link->fromsock == sock) {
- LinkData *linkdata = MEM_callocN(sizeof(LinkData), "drag link op link data");
- bNodeLink *oplink = MEM_callocN(sizeof(bNodeLink), "drag link op link");
+ LinkData *linkdata = (LinkData *)MEM_callocN(sizeof(LinkData), "drag link op link data");
+ bNodeLink *oplink = (bNodeLink *)MEM_callocN(sizeof(bNodeLink), "drag link op link");
linkdata->data = oplink;
*oplink = *link;
- oplink->next = oplink->prev = NULL;
+ oplink->next = oplink->prev = nullptr;
oplink->flag |= NODE_LINK_VALID;
/* The link could be disconnected and in that case we
@@ -1050,7 +1060,8 @@ static bNodeLinkDrag *node_link_init(Main *bmain, SpaceNode *snode, float cursor
}
/* or an input? */
else if (node_find_indicated_socket(snode, &node, &sock, cursor, SOCK_IN)) {
- nldrag = MEM_callocN(sizeof(bNodeLinkDrag), "drag link op customdata");
+ nldrag = (bNodeLinkDrag *)MEM_callocN(sizeof(bNodeLinkDrag), "drag link op customdata");
+ nldrag->last_node_hovered_while_dragging_a_link = node;
const int num_links = nodeCountSocketLinks(snode->edittree, sock);
if (num_links > 0) {
@@ -1067,12 +1078,12 @@ static bNodeLinkDrag *node_link_init(Main *bmain, SpaceNode *snode, float cursor
}
}
- if (link_to_pick != NULL && !nldrag->from_multi_input_socket) {
- LinkData *linkdata = MEM_callocN(sizeof(LinkData), "drag link op link data");
- bNodeLink *oplink = MEM_callocN(sizeof(bNodeLink), "drag link op link");
+ if (link_to_pick != nullptr && !nldrag->from_multi_input_socket) {
+ LinkData *linkdata = (LinkData *)MEM_callocN(sizeof(LinkData), "drag link op link data");
+ bNodeLink *oplink = (bNodeLink *)MEM_callocN(sizeof(bNodeLink), "drag link op link");
linkdata->data = oplink;
*oplink = *link_to_pick;
- oplink->next = oplink->prev = NULL;
+ oplink->next = oplink->prev = nullptr;
oplink->flag |= NODE_LINK_VALID;
oplink->flag &= ~NODE_LINK_TEST;
if (node_connected_to_output(bmain, snode->edittree, link_to_pick->tonode)) {
@@ -1133,7 +1144,7 @@ static int node_link_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void node_link_cancel(bContext *C, wmOperator *op)
{
SpaceNode *snode = CTX_wm_space_node(C);
- bNodeLinkDrag *nldrag = op->customdata;
+ bNodeLinkDrag *nldrag = (bNodeLinkDrag *)op->customdata;
BLI_remlink(&snode->runtime->linkdrag, nldrag);
@@ -1173,7 +1184,7 @@ void NODE_OT_link(wmOperatorType *ot)
RNA_def_float_array(ot->srna,
"drag_start",
2,
- 0,
+ nullptr,
-UI_PRECISION_FLOAT_MAX,
UI_PRECISION_FLOAT_MAX,
"Drag Start",
@@ -1184,7 +1195,11 @@ void NODE_OT_link(wmOperatorType *ot)
RNA_def_property_flag(prop, PROP_HIDDEN);
}
-/* ********************** Make Link operator ***************** */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Make Link Operator
+ * \{ */
/* makes a link between selected output and input sockets */
static int node_make_link_exec(bContext *C, wmOperator *op)
@@ -1195,11 +1210,11 @@ static int node_make_link_exec(bContext *C, wmOperator *op)
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
- snode_autoconnect(bmain, snode, 1, replace);
+ snode_autoconnect(bmain, snode, true, replace);
/* deselect sockets after linking */
- node_deselect_all_input_sockets(snode, 0);
- node_deselect_all_output_sockets(snode, 0);
+ node_deselect_all_input_sockets(snode, false);
+ node_deselect_all_output_sockets(snode, false);
ntreeUpdateTree(CTX_data_main(C), snode->edittree);
snode_notify(C, snode);
@@ -1224,27 +1239,37 @@ void NODE_OT_link_make(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
RNA_def_boolean(
- ot->srna, "replace", 0, "Replace", "Replace socket connections with the new links");
+ ot->srna, "replace", false, "Replace", "Replace socket connections with the new links");
}
-/* ********************** Node Link Intersect ***************** */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Node Link Intersect
+ * \{ */
+
static bool node_links_intersect(bNodeLink *link, const float mcoords[][2], int tot)
{
float coord_array[NODE_LINK_RESOL + 1][2];
- if (node_link_bezier_points(NULL, NULL, link, coord_array, NODE_LINK_RESOL)) {
+ if (node_link_bezier_points(nullptr, nullptr, link, coord_array, NODE_LINK_RESOL)) {
for (int i = 0; i < tot - 1; i++) {
for (int b = 0; b < NODE_LINK_RESOL; b++) {
if (isect_seg_seg_v2(mcoords[i], mcoords[i + 1], coord_array[b], coord_array[b + 1]) > 0) {
- return 1;
+ return true;
}
}
}
}
- return 0;
+ return false;
}
-/* ********************** Cut Link operator ***************** */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Cut Link Operator
+ * \{ */
+
static int cut_links_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
@@ -1291,12 +1316,10 @@ static int cut_links_exec(bContext *C, wmOperator *op)
snode_update(snode, link->tonode);
bNode *to_node = link->tonode;
nodeRemLink(snode->edittree, link);
- sort_multi_input_socket_links(snode, to_node, NULL, NULL);
+ sort_multi_input_socket_links(snode, to_node, nullptr, nullptr);
}
}
- do_tag_update |= ED_node_is_geometry(snode);
-
if (found) {
ntreeUpdateTree(CTX_data_main(C), snode->edittree);
snode_notify(C, snode);
@@ -1332,13 +1355,17 @@ void NODE_OT_links_cut(wmOperatorType *ot)
/* properties */
PropertyRNA *prop;
prop = RNA_def_collection_runtime(ot->srna, "path", &RNA_OperatorMousePath, "Path", "");
- RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+ RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE));
/* internal */
RNA_def_int(ot->srna, "cursor", WM_CURSOR_KNIFE, 0, INT_MAX, "Cursor", "", 0, INT_MAX);
}
-/* ********************** Mute links operator ***************** */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Mute Links Operator
+ * \{ */
static int mute_links_exec(bContext *C, wmOperator *op)
{
@@ -1403,8 +1430,6 @@ static int mute_links_exec(bContext *C, wmOperator *op)
link->flag &= ~NODE_LINK_TEST;
}
- do_tag_update |= ED_node_is_geometry(snode);
-
ntreeUpdateTree(CTX_data_main(C), snode->edittree);
snode_notify(C, snode);
if (do_tag_update) {
@@ -1436,13 +1461,17 @@ void NODE_OT_links_mute(wmOperatorType *ot)
/* properties */
PropertyRNA *prop;
prop = RNA_def_collection_runtime(ot->srna, "path", &RNA_OperatorMousePath, "Path", "");
- RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+ RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE));
/* internal */
RNA_def_int(ot->srna, "cursor", WM_CURSOR_MUTE, 0, INT_MAX, "Cursor", "", 0, INT_MAX);
}
-/* ********************** Detach links operator ***************** */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Detach Links Operator
+ * \{ */
static int detach_links_exec(bContext *C, wmOperator *UNUSED(op))
{
@@ -1479,7 +1508,11 @@ void NODE_OT_links_detach(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/* ****************** Set Parent ******************* */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Set Parent Operator
+ * \{ */
static int node_parent_set_exec(bContext *C, wmOperator *UNUSED(op))
{
@@ -1501,7 +1534,7 @@ static int node_parent_set_exec(bContext *C, wmOperator *UNUSED(op))
}
ED_node_sort(ntree);
- WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL);
+ WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr);
return OPERATOR_FINISHED;
}
@@ -1521,7 +1554,11 @@ void NODE_OT_parent_set(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/* ****************** Join Nodes ******************* */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Join Nodes Operator
+ * \{ */
/* tags for depth-first search */
#define NODE_JOIN_DONE 1
@@ -1573,7 +1610,7 @@ static int node_join_exec(bContext *C, wmOperator *UNUSED(op))
}
}
- bNode *frame = node_add_node(C, NULL, NODE_FRAME, 0.0f, 0.0f);
+ bNode *frame = node_add_node(C, nullptr, NODE_FRAME, 0.0f, 0.0f);
/* reset tags */
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
@@ -1594,7 +1631,7 @@ static int node_join_exec(bContext *C, wmOperator *UNUSED(op))
}
ED_node_sort(ntree);
- WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL);
+ WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr);
return OPERATOR_FINISHED;
}
@@ -1614,7 +1651,11 @@ void NODE_OT_join(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/* ****************** Attach ******************* */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Attach Operator
+ * \{ */
static bNode *node_find_frame_to_attach(ARegion *region,
const bNodeTree *ntree,
@@ -1634,7 +1675,7 @@ static bNode *node_find_frame_to_attach(ARegion *region,
}
}
- return NULL;
+ return nullptr;
}
static int node_attach_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
@@ -1647,7 +1688,7 @@ static int node_attach_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent
if (frame) {
LISTBASE_FOREACH_BACKWARD (bNode *, node, &ntree->nodes) {
if (node->flag & NODE_SELECT) {
- if (node->parent == NULL) {
+ if (node->parent == nullptr) {
/* disallow moving a parent into its child */
if (nodeAttachNodeCheck(frame, node) == false) {
/* attach all unparented nodes */
@@ -1676,7 +1717,7 @@ static int node_attach_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent
}
ED_node_sort(ntree);
- WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL);
+ WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr);
return OPERATOR_FINISHED;
}
@@ -1697,7 +1738,11 @@ void NODE_OT_attach(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/* ****************** Detach ******************* */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Detach Operator
+ * \{ */
/* tags for depth-first search */
#define NODE_DETACH_DONE 1
@@ -1748,7 +1793,7 @@ static int node_detach_exec(bContext *C, wmOperator *UNUSED(op))
}
ED_node_sort(ntree);
- WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL);
+ WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr);
return OPERATOR_FINISHED;
}
@@ -1768,7 +1813,11 @@ void NODE_OT_detach(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/* ********************* automatic node insert on dragging ******************* */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Automatic Node Insert on Dragging
+ * \{ */
/* prevent duplicate testing code below */
static bool ed_node_link_conditions(ScrArea *area,
@@ -1776,13 +1825,13 @@ static bool ed_node_link_conditions(ScrArea *area,
SpaceNode **r_snode,
bNode **r_select)
{
- SpaceNode *snode = area ? area->spacedata.first : NULL;
+ SpaceNode *snode = area ? (SpaceNode *)area->spacedata.first : nullptr;
*r_snode = snode;
- *r_select = NULL;
+ *r_select = nullptr;
/* no unlucky accidents */
- if (area == NULL || area->spacetype != SPACE_NODE) {
+ if (area == nullptr || area->spacetype != SPACE_NODE) {
return false;
}
@@ -1792,8 +1841,8 @@ static bool ed_node_link_conditions(ScrArea *area,
}
bNode *node;
- bNode *select = NULL;
- for (node = snode->edittree->nodes.first; node; node = node->next) {
+ bNode *select = nullptr;
+ for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) {
if (node->flag & SELECT) {
if (select) {
break;
@@ -1802,7 +1851,7 @@ static bool ed_node_link_conditions(ScrArea *area,
}
}
/* only one selected */
- if (node || select == NULL) {
+ if (node || select == nullptr) {
return false;
}
@@ -1845,7 +1894,7 @@ void ED_node_link_intersect_test(ScrArea *area, int test)
}
/* find link to select/highlight */
- bNodeLink *selink = NULL;
+ bNodeLink *selink = nullptr;
float dist_best = FLT_MAX;
LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) {
float coord_array[NODE_LINK_RESOL + 1][2];
@@ -1854,7 +1903,7 @@ void ED_node_link_intersect_test(ScrArea *area, int test)
continue;
}
- if (node_link_bezier_points(NULL, NULL, link, coord_array, NODE_LINK_RESOL)) {
+ if (node_link_bezier_points(nullptr, nullptr, link, coord_array, NODE_LINK_RESOL)) {
float dist = FLT_MAX;
/* loop over link coords to find shortest dist to
@@ -1886,35 +1935,76 @@ void ED_node_link_intersect_test(ScrArea *area, int test)
}
}
-/* assumes sockets in list */
-static bNodeSocket *socket_best_match(ListBase *sockets)
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Node Insert Offset Operator
+ * \{ */
+
+static int get_main_socket_priority(const bNodeSocket *socket)
{
- /* find type range */
- int maxtype = 0;
+ switch ((eNodeSocketDatatype)socket->type) {
+ case __SOCK_MESH:
+ case SOCK_CUSTOM:
+ return -1;
+ case SOCK_BOOLEAN:
+ return 0;
+ case SOCK_INT:
+ return 1;
+ case SOCK_FLOAT:
+ return 2;
+ case SOCK_VECTOR:
+ return 3;
+ case SOCK_RGBA:
+ return 4;
+ case SOCK_STRING:
+ case SOCK_SHADER:
+ case SOCK_OBJECT:
+ case SOCK_IMAGE:
+ case SOCK_GEOMETRY:
+ case SOCK_COLLECTION:
+ case SOCK_TEXTURE:
+ case SOCK_MATERIAL:
+ return 5;
+ }
+ return -1;
+}
+
+/** Get the "main" socket of a socket list using a heuristic based on socket types. */
+static bNodeSocket *get_main_socket(ListBase *sockets)
+{
+ /* find priority range */
+ int maxpriority = -1;
LISTBASE_FOREACH (bNodeSocket *, sock, sockets) {
- maxtype = max_ii(sock->type, maxtype);
+ if (sock->flag & SOCK_UNAVAIL) {
+ continue;
+ }
+ maxpriority = max_ii(get_main_socket_priority(sock), maxpriority);
}
- /* try all types, starting from 'highest' (i.e. colors, vectors, values) */
- for (int type = maxtype; type >= 0; type--) {
+ /* try all priorities, starting from 'highest' */
+ for (int priority = maxpriority; priority >= 0; priority--) {
LISTBASE_FOREACH (bNodeSocket *, sock, sockets) {
- if (!nodeSocketIsHidden(sock) && type == sock->type) {
+ if (!nodeSocketIsHidden(sock) && priority == get_main_socket_priority(sock)) {
return sock;
}
}
}
- /* no visible sockets, unhide first of highest type */
- for (int type = maxtype; type >= 0; type--) {
+ /* no visible sockets, unhide first of highest priority */
+ for (int priority = maxpriority; priority >= 0; priority--) {
LISTBASE_FOREACH (bNodeSocket *, sock, sockets) {
- if (type == sock->type) {
+ if (sock->flag & SOCK_UNAVAIL) {
+ continue;
+ }
+ if (priority == get_main_socket_priority(sock)) {
sock->flag &= ~SOCK_HIDDEN;
return sock;
}
}
}
- return NULL;
+ return nullptr;
}
static bool node_parents_offset_flag_enable_cb(bNode *parent, void *UNUSED(userdata))
@@ -1952,7 +2042,7 @@ static void node_parent_offset_apply(NodeInsertOfsData *data, bNode *parent, con
#define NODE_INSOFS_ANIM_DURATION 0.25f
/**
- * Callback that applies NodeInsertOfsData.offset_x to a node or its parent, similar
+ * Callback that applies #NodeInsertOfsData.offset_x to a node or its parent, similar
* to node_link_insert_offset_output_chain_cb below, but with slightly different logic
*/
static bool node_link_insert_offset_frame_chain_cb(bNode *fromnode,
@@ -1960,7 +2050,7 @@ static bool node_link_insert_offset_frame_chain_cb(bNode *fromnode,
void *userdata,
const bool reversed)
{
- NodeInsertOfsData *data = userdata;
+ NodeInsertOfsData *data = (NodeInsertOfsData *)userdata;
bNode *ofs_node = reversed ? fromnode : tonode;
if (ofs_node->parent && ofs_node->parent != data->insert_parent) {
@@ -1997,7 +2087,7 @@ static bool node_link_insert_offset_chain_cb(bNode *fromnode,
void *userdata,
const bool reversed)
{
- NodeInsertOfsData *data = userdata;
+ NodeInsertOfsData *data = (NodeInsertOfsData *)userdata;
bNode *ofs_node = reversed ? fromnode : tonode;
if (data->insert_parent) {
@@ -2010,7 +2100,7 @@ static bool node_link_insert_offset_chain_cb(bNode *fromnode,
}
if (nodeIsChildOf(data->insert_parent, ofs_node) == false) {
- data->insert_parent = NULL;
+ data->insert_parent = nullptr;
}
}
else if (ofs_node->parent) {
@@ -2061,7 +2151,7 @@ static void node_link_insert_offset_ntree(NodeInsertOfsData *iofsd,
rctf totr_frame;
/* check nodes front to back */
- for (frame = ntree->nodes.last; frame; frame = frame->prev) {
+ for (frame = (bNode *)ntree->nodes.last; frame; frame = frame->prev) {
/* skip selected, those are the nodes we want to attach */
if ((frame->type != NODE_FRAME) || (frame->flag & NODE_SELECT)) {
continue;
@@ -2127,7 +2217,7 @@ static void node_link_insert_offset_ntree(NodeInsertOfsData *iofsd,
iofsd->offset_x = margin;
/* flag all parents of insert as offset to prevent them from being offset */
- nodeParentsIter(insert, node_parents_offset_flag_enable_cb, NULL);
+ nodeParentsIter(insert, node_parents_offset_flag_enable_cb, nullptr);
/* iterate over entire chain and apply offsets */
nodeChainIter(ntree,
right_alignment ? next : prev,
@@ -2148,7 +2238,8 @@ static int node_insert_offset_modal(bContext *C, wmOperator *UNUSED(op), const w
NodeInsertOfsData *iofsd = snode->runtime->iofsd;
bool redraw = false;
- if (!snode || event->type != TIMER || iofsd == NULL || iofsd->anim_timer != event->customdata) {
+ if (!snode || event->type != TIMER || iofsd == nullptr ||
+ iofsd->anim_timer != event->customdata) {
return OPERATOR_PASS_THROUGH;
}
@@ -2178,13 +2269,13 @@ static int node_insert_offset_modal(bContext *C, wmOperator *UNUSED(op), const w
/* end timer + free insert offset data */
if (duration > NODE_INSOFS_ANIM_DURATION) {
- WM_event_remove_timer(CTX_wm_manager(C), NULL, iofsd->anim_timer);
+ WM_event_remove_timer(CTX_wm_manager(C), nullptr, iofsd->anim_timer);
LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) {
node->anim_init_locx = node->anim_ofsx = 0.0f;
}
- snode->runtime->iofsd = NULL;
+ snode->runtime->iofsd = nullptr;
MEM_freeN(iofsd);
return (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH);
@@ -2234,6 +2325,12 @@ void NODE_OT_insert_offset(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Note Link Insert
+ * \{ */
+
/* assumes link with NODE_LINKFLAG_HILITE set */
void ED_node_link_insert(Main *bmain, ScrArea *area)
{
@@ -2245,15 +2342,15 @@ void ED_node_link_insert(Main *bmain, ScrArea *area)
/* get the link */
bNodeLink *link;
- for (link = snode->edittree->links.first; link; link = link->next) {
+ for (link = (bNodeLink *)snode->edittree->links.first; link; link = link->next) {
if (link->flag & NODE_LINKFLAG_HILITE) {
break;
}
}
if (link) {
- bNodeSocket *best_input = socket_best_match(&select->inputs);
- bNodeSocket *best_output = socket_best_match(&select->outputs);
+ bNodeSocket *best_input = get_main_socket(&select->inputs);
+ bNodeSocket *best_output = get_main_socket(&select->outputs);
if (best_input && best_output) {
bNode *node = link->tonode;
@@ -2264,11 +2361,17 @@ void ED_node_link_insert(Main *bmain, ScrArea *area)
node_remove_extra_links(snode, link);
link->flag &= ~NODE_LINKFLAG_HILITE;
- nodeAddLink(snode->edittree, select, best_output, node, sockto);
+ bNodeLink *new_link = nodeAddLink(snode->edittree, select, best_output, node, sockto);
+
+ /* Copy the socket index for the new link, and reset it for the old link. This way the
+ * relative order of links is preserved, and the links get drawn in the right place. */
+ new_link->multi_input_socket_index = link->multi_input_socket_index;
+ link->multi_input_socket_index = 0;
/* set up insert offset data, it needs stuff from here */
if ((snode->flag & SNODE_SKIP_INSOFFSET) == 0) {
- NodeInsertOfsData *iofsd = MEM_callocN(sizeof(NodeInsertOfsData), __func__);
+ NodeInsertOfsData *iofsd = (NodeInsertOfsData *)MEM_callocN(sizeof(NodeInsertOfsData),
+ __func__);
iofsd->insert = select;
iofsd->prev = link->fromnode;
@@ -2281,8 +2384,8 @@ void ED_node_link_insert(Main *bmain, ScrArea *area)
snode_update(snode, select);
ED_node_tag_update_id((ID *)snode->edittree);
ED_node_tag_update_id(snode->id);
-
- sort_multi_input_socket_links(snode, node, NULL, NULL);
}
}
}
+
+/** \} */
diff --git a/source/blender/editors/space_node/node_select.c b/source/blender/editors/space_node/node_select.cc
index 1da79671c8e..41820cd813c 100644
--- a/source/blender/editors/space_node/node_select.c
+++ b/source/blender/editors/space_node/node_select.cc
@@ -21,7 +21,8 @@
* \ingroup spnode
*/
-#include <stdlib.h>
+#include <array>
+#include <cstdlib>
#include "DNA_node_types.h"
#include "DNA_windowmanager_types.h"
@@ -81,7 +82,7 @@ static bool has_workbench_in_texture_color(const wmWindowManager *wm,
const bScreen *screen = BKE_workspace_active_screen_get(win->workspace_hook);
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
if (area->spacetype == SPACE_VIEW3D) {
- const View3D *v3d = area->spacedata.first;
+ const View3D *v3d = (const View3D *)area->spacedata.first;
if (ED_view3d_has_workbench_in_texture_color(scene, ob, v3d)) {
return true;
@@ -100,28 +101,28 @@ static bNode *node_under_mouse_select(bNodeTree *ntree, int mx, int my)
{
bNode *node;
- for (node = ntree->nodes.last; node; node = node->prev) {
+ for (node = (bNode *)ntree->nodes.last; node; node = node->prev) {
if (node->typeinfo->select_area_func) {
if (node->typeinfo->select_area_func(node, mx, my)) {
return node;
}
}
}
- return NULL;
+ return nullptr;
}
static bNode *node_under_mouse_tweak(bNodeTree *ntree, int mx, int my)
{
bNode *node;
- for (node = ntree->nodes.last; node; node = node->prev) {
+ for (node = (bNode *)ntree->nodes.last; node; node = node->prev) {
if (node->typeinfo->tweak_area_func) {
if (node->typeinfo->tweak_area_func(node, mx, my)) {
return node;
}
}
}
- return NULL;
+ return nullptr;
}
static bool is_position_over_node_or_socket(SpaceNode *snode, float mouse[2])
@@ -168,18 +169,18 @@ void node_socket_deselect(bNode *node, bNodeSocket *sock, const bool deselect_no
sock->flag &= ~SELECT;
if (node && deselect_node) {
- bool sel = 0;
+ bool sel = false;
/* if no selected sockets remain, also deselect the node */
- for (sock = node->inputs.first; sock; sock = sock->next) {
+ for (sock = (bNodeSocket *)node->inputs.first; sock; sock = sock->next) {
if (sock->flag & SELECT) {
- sel = 1;
+ sel = true;
break;
}
}
- for (sock = node->outputs.first; sock; sock = sock->next) {
+ for (sock = (bNodeSocket *)node->outputs.first; sock; sock = sock->next) {
if (sock->flag & SELECT) {
- sel = 1;
+ sel = true;
break;
}
}
@@ -205,7 +206,7 @@ void node_deselect_all(SpaceNode *snode)
{
bNode *node;
- for (node = snode->edittree->nodes.first; node; node = node->next) {
+ for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) {
nodeSetSelected(node, false);
}
}
@@ -220,16 +221,16 @@ void node_deselect_all_input_sockets(SpaceNode *snode, const bool deselect_nodes
* We can do that more efficiently here.
*/
- for (node = snode->edittree->nodes.first; node; node = node->next) {
+ for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) {
int sel = 0;
- for (sock = node->inputs.first; sock; sock = sock->next) {
+ for (sock = (bNodeSocket *)node->inputs.first; sock; sock = sock->next) {
sock->flag &= ~SELECT;
}
/* if no selected sockets remain, also deselect the node */
if (deselect_nodes) {
- for (sock = node->outputs.first; sock; sock = sock->next) {
+ for (sock = (bNodeSocket *)node->outputs.first; sock; sock = sock->next) {
if (sock->flag & SELECT) {
sel = 1;
break;
@@ -253,18 +254,18 @@ void node_deselect_all_output_sockets(SpaceNode *snode, const bool deselect_node
* We can do that more efficiently here.
*/
- for (node = snode->edittree->nodes.first; node; node = node->next) {
+ for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) {
bool sel = false;
- for (sock = node->outputs.first; sock; sock = sock->next) {
+ for (sock = (bNodeSocket *)node->outputs.first; sock; sock = sock->next) {
sock->flag &= ~SELECT;
}
/* if no selected sockets remain, also deselect the node */
if (deselect_nodes) {
- for (sock = node->inputs.first; sock; sock = sock->next) {
+ for (sock = (bNodeSocket *)node->inputs.first; sock; sock = sock->next) {
if (sock->flag & SELECT) {
- sel = 1;
+ sel = true;
break;
}
}
@@ -289,7 +290,7 @@ static bool node_select_grouped_type(SpaceNode *snode, bNode *node_act)
bNode *node;
bool changed = false;
- for (node = snode->edittree->nodes.first; node; node = node->next) {
+ for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) {
if ((node->flag & SELECT) == 0) {
if (node->type == node_act->type) {
nodeSetSelected(node, true);
@@ -306,7 +307,7 @@ static bool node_select_grouped_color(SpaceNode *snode, bNode *node_act)
bNode *node;
bool changed = false;
- for (node = snode->edittree->nodes.first; node; node = node->next) {
+ for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) {
if ((node->flag & SELECT) == 0) {
if (compare_v3v3(node->color, node_act->color, 0.005f)) {
nodeSetSelected(node, true);
@@ -327,7 +328,7 @@ static bool node_select_grouped_name(SpaceNode *snode, bNode *node_act, const bo
const char *sep, *suf_act, *suf_curr;
pref_len_act = BLI_str_partition_ex_utf8(
- node_act->name, NULL, delims, &sep, &suf_act, from_right);
+ node_act->name, nullptr, delims, &sep, &suf_act, from_right);
/* Note: in case we are searching for suffix, and found none, use whole name as suffix. */
if (from_right && !(sep && suf_act)) {
@@ -335,12 +336,12 @@ static bool node_select_grouped_name(SpaceNode *snode, bNode *node_act, const bo
suf_act = node_act->name;
}
- for (node = snode->edittree->nodes.first; node; node = node->next) {
+ for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) {
if (node->flag & SELECT) {
continue;
}
pref_len_curr = BLI_str_partition_ex_utf8(
- node->name, NULL, delims, &sep, &suf_curr, from_right);
+ node->name, nullptr, delims, &sep, &suf_curr, from_right);
/* Same as with active node name! */
if (from_right && !(sep && suf_curr)) {
@@ -371,7 +372,7 @@ static int node_select_grouped_exec(bContext *C, wmOperator *op)
SpaceNode *snode = CTX_wm_space_node(C);
bNode *node_act = nodeGetActive(snode->edittree);
- if (node_act == NULL) {
+ if (node_act == nullptr) {
return OPERATOR_CANCELLED;
}
@@ -381,7 +382,7 @@ static int node_select_grouped_exec(bContext *C, wmOperator *op)
const int type = RNA_enum_get(op->ptr, "type");
if (!extend) {
- for (node = snode->edittree->nodes.first; node; node = node->next) {
+ for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) {
nodeSetSelected(node, false);
}
}
@@ -406,7 +407,7 @@ static int node_select_grouped_exec(bContext *C, wmOperator *op)
if (changed) {
ED_node_sort(snode->edittree);
- WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
+ WM_event_add_notifier(C, NC_NODE | NA_SELECTED, nullptr);
return OPERATOR_FINISHED;
}
@@ -420,7 +421,7 @@ void NODE_OT_select_grouped(wmOperatorType *ot)
{NODE_SELECT_GROUPED_COLOR, "COLOR", 0, "Color", ""},
{NODE_SELECT_GROUPED_PREFIX, "PREFIX", 0, "Prefix", ""},
{NODE_SELECT_GROUPED_SUFIX, "SUFFIX", 0, "Suffix", ""},
- {0, NULL, 0, NULL, NULL},
+ {0, nullptr, 0, nullptr, nullptr},
};
/* identifiers */
@@ -461,7 +462,7 @@ void node_select_single(bContext *C, bNode *node)
bool active_texture_changed = false;
bNode *tnode;
- for (tnode = snode->edittree->nodes.first; tnode; tnode = tnode->next) {
+ for (tnode = (bNode *)snode->edittree->nodes.first; tnode; tnode = tnode->next) {
if (tnode != node) {
nodeSetSelected(tnode, false);
}
@@ -476,7 +477,7 @@ void node_select_single(bContext *C, bNode *node)
DEG_id_tag_update(&snode->edittree->id, ID_RECALC_COPY_ON_WRITE);
}
- WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
+ WM_event_add_notifier(C, NC_NODE | NA_SELECTED, nullptr);
}
static int node_mouse_select(bContext *C,
@@ -491,7 +492,7 @@ static int node_mouse_select(bContext *C,
const Scene *scene = CTX_data_scene(C);
const wmWindowManager *wm = CTX_wm_manager(C);
bNode *node, *tnode;
- bNodeSocket *sock = NULL;
+ bNodeSocket *sock = nullptr;
bNodeSocket *tsock;
float cursor[2];
int ret_value = OPERATOR_CANCELLED;
@@ -530,7 +531,7 @@ static int node_mouse_select(bContext *C,
/* Only allow one selected output per node, for sensible linking.
* Allow selecting outputs from different nodes though, if extend is true. */
if (node) {
- for (tsock = node->outputs.first; tsock; tsock = tsock->next) {
+ for (tsock = (bNodeSocket *)node->outputs.first; tsock; tsock = tsock->next) {
if (tsock == sock) {
continue;
}
@@ -538,11 +539,11 @@ static int node_mouse_select(bContext *C,
}
}
if (!extend) {
- for (tnode = snode->edittree->nodes.first; tnode; tnode = tnode->next) {
+ for (tnode = (bNode *)snode->edittree->nodes.first; tnode; tnode = tnode->next) {
if (tnode == node) {
continue;
}
- for (tsock = tnode->outputs.first; tsock; tsock = tsock->next) {
+ for (tsock = (bNodeSocket *)tnode->outputs.first; tsock; tsock = tsock->next) {
node_socket_deselect(tnode, tsock, true);
}
}
@@ -558,7 +559,7 @@ static int node_mouse_select(bContext *C,
node = node_under_mouse_select(snode->edittree, (int)cursor[0], (int)cursor[1]);
if (extend) {
- if (node != NULL) {
+ if (node != nullptr) {
/* If node is selected but not active, we want to make it active,
* but not toggle (deselect) it. */
if (!((node->flag & SELECT) && (node->flag & NODE_ACTIVE) == 0)) {
@@ -567,14 +568,22 @@ static int node_mouse_select(bContext *C,
ret_value = OPERATOR_FINISHED;
}
}
- else if (deselect_all && node == NULL) {
- /* Deselect in empty space. */
- for (tnode = snode->edittree->nodes.first; tnode; tnode = tnode->next) {
- nodeSetSelected(tnode, false);
+ else if (deselect_all && node == nullptr) {
+ /* Rather than deselecting others, users may want to drag to box-select (drag from empty
+ * space) or tweak-translate an already selected item. If these cases may apply, delay
+ * deselection. */
+ if (wait_to_deselect_others) {
+ ret_value = OPERATOR_RUNNING_MODAL;
+ }
+ else {
+ /* Deselect in empty space. */
+ for (tnode = (bNode *)snode->edittree->nodes.first; tnode; tnode = tnode->next) {
+ nodeSetSelected(tnode, false);
+ }
+ ret_value = OPERATOR_FINISHED;
}
- ret_value = OPERATOR_FINISHED;
}
- else if (node != NULL) {
+ else if (node != nullptr) {
/* When clicking on an already selected node, we want to wait to deselect
* others and allow the user to start moving the node without that. */
if (wait_to_deselect_others && (node->flag & SELECT)) {
@@ -583,7 +592,7 @@ static int node_mouse_select(bContext *C,
else {
nodeSetSelected(node, true);
- for (tnode = snode->edittree->nodes.first; tnode; tnode = tnode->next) {
+ for (tnode = (bNode *)snode->edittree->nodes.first; tnode; tnode = tnode->next) {
if (tnode != node) {
nodeSetSelected(tnode, false);
}
@@ -597,7 +606,7 @@ static int node_mouse_select(bContext *C,
/* update node order */
if (ret_value != OPERATOR_CANCELLED) {
bool active_texture_changed = false;
- if (node != NULL && ret_value != OPERATOR_RUNNING_MODAL) {
+ if (node != nullptr && ret_value != OPERATOR_RUNNING_MODAL) {
ED_node_set_active(bmain, snode->edittree, node, &active_texture_changed);
}
ED_node_set_active_viewer_key(snode);
@@ -606,7 +615,7 @@ static int node_mouse_select(bContext *C,
DEG_id_tag_update(&snode->edittree->id, ID_RECALC_COPY_ON_WRITE);
}
- WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
+ WM_event_add_notifier(C, NC_NODE | NA_SELECTED, nullptr);
}
return ret_value;
@@ -673,7 +682,7 @@ static int node_box_select_exec(bContext *C, wmOperator *op)
WM_operator_properties_border_to_rctf(op, &rectf);
UI_view2d_region_to_view_rctf(&region->v2d, &rectf, &rectf);
- const eSelectOp sel_op = RNA_enum_get(op->ptr, "mode");
+ const eSelectOp sel_op = (eSelectOp)RNA_enum_get(op->ptr, "mode");
const bool select = (sel_op != SEL_OP_SUB);
if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
ED_node_select_all(&snode->edittree->nodes, SEL_DESELECT);
@@ -685,7 +694,7 @@ static int node_box_select_exec(bContext *C, wmOperator *op)
is_inside = BLI_rctf_inside_rctf(&rectf, &node->totr);
}
else {
- is_inside = BLI_rctf_isect(&rectf, &node->totr, NULL);
+ is_inside = BLI_rctf_isect(&rectf, &node->totr, nullptr);
}
if (is_inside) {
@@ -695,7 +704,7 @@ static int node_box_select_exec(bContext *C, wmOperator *op)
ED_node_sort(snode->edittree);
- WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
+ WM_event_add_notifier(C, NC_NODE | NA_SELECTED, nullptr);
return OPERATOR_FINISHED;
}
@@ -732,7 +741,7 @@ void NODE_OT_select_box(wmOperatorType *ot)
/* properties */
RNA_def_boolean(ot->srna,
"tweak",
- 0,
+ false,
"Tweak",
"Only activate when mouse is not over a node (useful for tweak gesture)");
@@ -758,8 +767,9 @@ static int node_circleselect_exec(bContext *C, wmOperator *op)
float zoom = (float)(BLI_rcti_size_x(&region->winrct)) /
(float)(BLI_rctf_size_x(&region->v2d.cur));
- const eSelectOp sel_op = ED_select_op_modal(RNA_enum_get(op->ptr, "mode"),
- WM_gesture_is_modal_first(op->customdata));
+ const eSelectOp sel_op = ED_select_op_modal(
+ (eSelectOp)RNA_enum_get(op->ptr, "mode"),
+ WM_gesture_is_modal_first((const wmGesture *)op->customdata));
const bool select = (sel_op != SEL_OP_SUB);
if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
ED_node_select_all(&snode->edittree->nodes, SEL_DESELECT);
@@ -772,13 +782,13 @@ static int node_circleselect_exec(bContext *C, wmOperator *op)
UI_view2d_region_to_view(&region->v2d, x, y, &offset[0], &offset[1]);
- for (node = snode->edittree->nodes.first; node; node = node->next) {
+ for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) {
if (BLI_rctf_isect_circle(&node->totr, offset, radius / zoom)) {
nodeSetSelected(node, select);
}
}
- WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
+ WM_event_add_notifier(C, NC_NODE | NA_SELECTED, nullptr);
return OPERATOR_FINISHED;
}
@@ -845,7 +855,7 @@ static bool do_lasso_select_node(bContext *C,
BLI_lasso_boundbox(&rect, mcoords, mcoords_len);
/* do actual selection */
- for (node = snode->edittree->nodes.first; node; node = node->next) {
+ for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) {
if (select && (node->flag & NODE_SELECT)) {
continue;
@@ -865,7 +875,7 @@ static bool do_lasso_select_node(bContext *C,
}
if (changed) {
- WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
+ WM_event_add_notifier(C, NC_NODE | NA_SELECTED, nullptr);
}
return changed;
@@ -877,7 +887,7 @@ static int node_lasso_select_exec(bContext *C, wmOperator *op)
const int(*mcoords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcoords_len);
if (mcoords) {
- const eSelectOp sel_op = RNA_enum_get(op->ptr, "mode");
+ const eSelectOp sel_op = (eSelectOp)RNA_enum_get(op->ptr, "mode");
do_lasso_select_node(C, mcoords, mcoords_len, sel_op);
@@ -908,7 +918,7 @@ void NODE_OT_select_lasso(wmOperatorType *ot)
/* properties */
RNA_def_boolean(ot->srna,
"tweak",
- 0,
+ false,
"Tweak",
"Only activate when mouse is not over a node (useful for tweak gesture)");
@@ -932,7 +942,7 @@ static int node_select_all_exec(bContext *C, wmOperator *op)
ED_node_sort(snode->edittree);
- WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
+ WM_event_add_notifier(C, NC_NODE | NA_SELECTED, nullptr);
return OPERATOR_FINISHED;
}
@@ -965,11 +975,11 @@ static int node_select_linked_to_exec(bContext *C, wmOperator *UNUSED(op))
bNodeLink *link;
bNode *node;
- for (node = snode->edittree->nodes.first; node; node = node->next) {
+ for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) {
node->flag &= ~NODE_TEST;
}
- for (link = snode->edittree->links.first; link; link = link->next) {
+ for (link = (bNodeLink *)snode->edittree->links.first; link; link = link->next) {
if (nodeLinkIsHidden(link)) {
continue;
}
@@ -978,7 +988,7 @@ static int node_select_linked_to_exec(bContext *C, wmOperator *UNUSED(op))
}
}
- for (node = snode->edittree->nodes.first; node; node = node->next) {
+ for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) {
if (node->flag & NODE_TEST) {
nodeSetSelected(node, true);
}
@@ -986,7 +996,7 @@ static int node_select_linked_to_exec(bContext *C, wmOperator *UNUSED(op))
ED_node_sort(snode->edittree);
- WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
+ WM_event_add_notifier(C, NC_NODE | NA_SELECTED, nullptr);
return OPERATOR_FINISHED;
}
@@ -1017,11 +1027,11 @@ static int node_select_linked_from_exec(bContext *C, wmOperator *UNUSED(op))
bNodeLink *link;
bNode *node;
- for (node = snode->edittree->nodes.first; node; node = node->next) {
+ for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) {
node->flag &= ~NODE_TEST;
}
- for (link = snode->edittree->links.first; link; link = link->next) {
+ for (link = (bNodeLink *)snode->edittree->links.first; link; link = link->next) {
if (nodeLinkIsHidden(link)) {
continue;
}
@@ -1030,7 +1040,7 @@ static int node_select_linked_from_exec(bContext *C, wmOperator *UNUSED(op))
}
}
- for (node = snode->edittree->nodes.first; node; node = node->next) {
+ for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) {
if (node->flag & NODE_TEST) {
nodeSetSelected(node, true);
}
@@ -1038,7 +1048,7 @@ static int node_select_linked_from_exec(bContext *C, wmOperator *UNUSED(op))
ED_node_sort(snode->edittree);
- WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL);
+ WM_event_add_notifier(C, NC_NODE | NA_SELECTED, nullptr);
return OPERATOR_FINISHED;
}
@@ -1071,7 +1081,7 @@ static int node_select_same_type_step_exec(bContext *C, wmOperator *op)
bNode *active = nodeGetActive(snode->edittree);
int totnodes;
const bool revert = RNA_boolean_get(op->ptr, "prev");
- const bool same_type = 1;
+ const bool same_type = true;
ntreeGetDependencyList(snode->edittree, &node_array, &totnodes);
@@ -1085,9 +1095,9 @@ static int node_select_same_type_step_exec(bContext *C, wmOperator *op)
}
if (same_type) {
- bNode *node = NULL;
+ bNode *node = nullptr;
- while (node == NULL) {
+ while (node == nullptr) {
if (revert) {
a--;
}
@@ -1104,7 +1114,7 @@ static int node_select_same_type_step_exec(bContext *C, wmOperator *op)
if (node->type == active->type) {
break;
}
- node = NULL;
+ node = nullptr;
}
if (node) {
active = node;
@@ -1160,7 +1170,7 @@ void NODE_OT_select_same_type_step(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
- RNA_def_boolean(ot->srna, "prev", 0, "Previous", "");
+ RNA_def_boolean(ot->srna, "prev", false, "Previous", "");
}
/** \} */
@@ -1215,7 +1225,7 @@ static void node_find_update_fn(const struct bContext *C,
static void node_find_exec_fn(struct bContext *C, void *UNUSED(arg1), void *arg2)
{
SpaceNode *snode = CTX_wm_space_node(C);
- bNode *active = arg2;
+ bNode *active = (bNode *)arg2;
if (active) {
ARegion *region = CTX_wm_region(C);
@@ -1252,7 +1262,8 @@ static uiBlock *node_find_menu(bContext *C, ARegion *region, void *arg_op)
0,
0,
"");
- UI_but_func_search_set(but, NULL, node_find_update_fn, op->type, NULL, node_find_exec_fn, NULL);
+ UI_but_func_search_set(
+ but, nullptr, node_find_update_fn, op->type, false, nullptr, node_find_exec_fn, nullptr);
UI_but_flag_enable(but, UI_BUT_ACTIVATE_ON_INIT);
/* fake button, it holds space for search items */
@@ -1264,22 +1275,23 @@ static uiBlock *node_find_menu(bContext *C, ARegion *region, void *arg_op)
10 - UI_searchbox_size_y(),
UI_searchbox_size_x(),
UI_searchbox_size_y(),
- NULL,
+ nullptr,
0,
0,
0,
0,
- NULL);
+ nullptr);
/* Move it downwards, mouse over button. */
- UI_block_bounds_set_popup(block, 0.3f * U.widget_unit, (const int[2]){0, -UI_UNIT_Y});
+ std::array<int, 2> bounds_offset = {0, -UI_UNIT_Y};
+ UI_block_bounds_set_popup(block, 0.3f * U.widget_unit, bounds_offset.data());
return block;
}
static int node_find_node_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
- UI_popup_block_invoke(C, node_find_menu, op, NULL);
+ UI_popup_block_invoke(C, node_find_menu, op, nullptr);
return OPERATOR_CANCELLED;
}
@@ -1297,7 +1309,7 @@ void NODE_OT_find_node(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
- RNA_def_boolean(ot->srna, "prev", 0, "Previous", "");
+ RNA_def_boolean(ot->srna, "prev", false, "Previous", "");
}
/** \} */
diff --git a/source/blender/editors/space_node/node_templates.c b/source/blender/editors/space_node/node_templates.cc
index 3873985d93a..fae180ca6c5 100644
--- a/source/blender/editors/space_node/node_templates.c
+++ b/source/blender/editors/space_node/node_templates.cc
@@ -18,8 +18,8 @@
* \ingroup edinterface
*/
-#include <stdlib.h>
-#include <string.h>
+#include <cstdlib>
+#include <cstring>
#include "MEM_guardedalloc.h"
@@ -29,6 +29,7 @@
#include "BLI_array.h"
#include "BLI_listbase.h"
#include "BLI_string.h"
+#include "BLI_vector.hh"
#include "BLT_translation.h"
@@ -51,7 +52,7 @@
/************************* Node Socket Manipulation **************************/
/* describes an instance of a node type and a specific socket to link */
-typedef struct NodeLinkItem {
+struct NodeLinkItem {
int socket_index; /* index for linking */
int socket_type; /* socket type for compatibility check */
const char *socket_name; /* ui label of the socket */
@@ -59,7 +60,7 @@ typedef struct NodeLinkItem {
/* extra settings */
bNodeTree *ngroup; /* group node tree */
-} NodeLinkItem;
+};
/* Compare an existing node to a link item to see if it can be reused.
* item must be for the same node type!
@@ -98,7 +99,7 @@ static void node_tag_recursive(bNode *node)
node->flag |= NODE_TEST;
- for (input = node->inputs.first; input; input = input->next) {
+ for (input = (bNodeSocket *)node->inputs.first; input; input = input->next) {
if (input->link) {
node_tag_recursive(input->link->fromnode);
}
@@ -115,7 +116,7 @@ static void node_clear_recursive(bNode *node)
node->flag &= ~NODE_TEST;
- for (input = node->inputs.first; input; input = input->next) {
+ for (input = (bNodeSocket *)node->inputs.first; input; input = input->next) {
if (input->link) {
node_clear_recursive(input->link->fromnode);
}
@@ -132,16 +133,16 @@ static void node_remove_linked(Main *bmain, bNodeTree *ntree, bNode *rem_node)
}
/* tag linked nodes to be removed */
- for (node = ntree->nodes.first; node; node = node->next) {
+ for (node = (bNode *)ntree->nodes.first; node; node = node->next) {
node->flag &= ~NODE_TEST;
}
node_tag_recursive(rem_node);
/* clear tags on nodes that are still used by other nodes */
- for (node = ntree->nodes.first; node; node = node->next) {
+ for (node = (bNode *)ntree->nodes.first; node; node = node->next) {
if (!(node->flag & NODE_TEST)) {
- for (sock = node->inputs.first; sock; sock = sock->next) {
+ for (sock = (bNodeSocket *)node->inputs.first; sock; sock = sock->next) {
if (sock->link && sock->link->fromnode != rem_node) {
node_clear_recursive(sock->link->fromnode);
}
@@ -150,7 +151,7 @@ static void node_remove_linked(Main *bmain, bNodeTree *ntree, bNode *rem_node)
}
/* remove nodes */
- for (node = ntree->nodes.first; node; node = next) {
+ for (node = (bNode *)ntree->nodes.first; node; node = next) {
next = node->next;
if (node->flag & NODE_TEST) {
@@ -205,7 +206,7 @@ static void node_socket_add_replace(const bContext *C,
Main *bmain = CTX_data_main(C);
bNode *node_from;
bNodeSocket *sock_from_tmp;
- bNode *node_prev = NULL;
+ bNode *node_prev = nullptr;
/* unlink existing node */
if (sock_to->link) {
@@ -214,7 +215,7 @@ static void node_socket_add_replace(const bContext *C,
}
/* find existing node that we can use */
- for (node_from = ntree->nodes.first; node_from; node_from = node_from->next) {
+ for (node_from = (bNode *)ntree->nodes.first; node_from; node_from = node_from->next) {
if (node_from->type == type) {
break;
}
@@ -223,7 +224,7 @@ static void node_socket_add_replace(const bContext *C,
if (node_from) {
if (node_from->inputs.first || node_from->typeinfo->draw_buttons ||
node_from->typeinfo->draw_buttons_ex) {
- node_from = NULL;
+ node_from = nullptr;
}
}
@@ -233,7 +234,7 @@ static void node_socket_add_replace(const bContext *C,
}
else if (!node_from) {
node_from = nodeAddStaticNode(C, ntree, type);
- if (node_prev != NULL) {
+ if (node_prev != nullptr) {
/* If we're replacing existing node, use its location. */
node_from->locx = node_prev->locx;
node_from->locy = node_prev->locy;
@@ -241,7 +242,7 @@ static void node_socket_add_replace(const bContext *C,
node_from->offsety = node_prev->offsety;
}
else {
- sock_from_tmp = BLI_findlink(&node_from->outputs, item->socket_index);
+ sock_from_tmp = (bNodeSocket *)BLI_findlink(&node_from->outputs, item->socket_index);
nodePositionRelative(node_from, node_to, sock_from_tmp, sock_to);
}
@@ -251,7 +252,7 @@ static void node_socket_add_replace(const bContext *C,
nodeSetActive(ntree, node_from);
/* add link */
- sock_from_tmp = BLI_findlink(&node_from->outputs, item->socket_index);
+ sock_from_tmp = (bNodeSocket *)BLI_findlink(&node_from->outputs, item->socket_index);
nodeAddLink(ntree, node_from, sock_from_tmp, node_to, sock_to);
sock_to->flag &= ~SOCK_COLLAPSED;
@@ -259,8 +260,10 @@ static void node_socket_add_replace(const bContext *C,
if (node_prev && node_from != node_prev) {
bNodeSocket *sock_prev, *sock_from;
- for (sock_prev = node_prev->inputs.first; sock_prev; sock_prev = sock_prev->next) {
- for (sock_from = node_from->inputs.first; sock_from; sock_from = sock_from->next) {
+ for (sock_prev = (bNodeSocket *)node_prev->inputs.first; sock_prev;
+ sock_prev = sock_prev->next) {
+ for (sock_from = (bNodeSocket *)node_from->inputs.first; sock_from;
+ sock_from = sock_from->next) {
if (nodeCountSocketLinks(ntree, sock_from) >= nodeSocketLinkLimit(sock_from)) {
continue;
}
@@ -282,7 +285,7 @@ static void node_socket_add_replace(const bContext *C,
if (node_from->typeinfo->nclass == NODE_CLASS_TEXTURE &&
node_prev->typeinfo->nclass == NODE_CLASS_TEXTURE &&
/* White noise texture node does not have NodeTexBase. */
- node_from->storage != NULL && node_prev->storage != NULL) {
+ node_from->storage != nullptr && node_prev->storage != nullptr) {
memcpy(node_from->storage, node_prev->storage, sizeof(NodeTexBase));
}
@@ -303,7 +306,7 @@ static void node_socket_add_replace(const bContext *C,
#define UI_NODE_LINK_DISCONNECT -1
#define UI_NODE_LINK_REMOVE -2
-typedef struct NodeLinkArg {
+struct NodeLinkArg {
Main *bmain;
Scene *scene;
bNodeTree *ntree;
@@ -314,7 +317,7 @@ typedef struct NodeLinkArg {
NodeLinkItem item;
uiLayout *layout;
-} NodeLinkArg;
+};
static void ui_node_link_items(NodeLinkArg *arg,
int in_out,
@@ -322,15 +325,18 @@ static void ui_node_link_items(NodeLinkArg *arg,
int *r_totitems)
{
/* XXX this should become a callback for node types! */
- NodeLinkItem *items = NULL;
+ NodeLinkItem *items = nullptr;
int totitems = 0;
if (arg->node_type->type == NODE_GROUP) {
bNodeTree *ngroup;
int i;
- for (ngroup = arg->bmain->nodetrees.first; ngroup; ngroup = ngroup->id.next) {
- if ((ngroup->type != arg->ntree->type) || !nodeGroupPoll(arg->ntree, ngroup)) {
+ for (ngroup = (bNodeTree *)arg->bmain->nodetrees.first; ngroup;
+ ngroup = (bNodeTree *)ngroup->id.next) {
+ const char *disabled_hint;
+ if ((ngroup->type != arg->ntree->type) ||
+ !nodeGroupPoll(arg->ntree, ngroup, &disabled_hint)) {
continue;
}
@@ -339,18 +345,22 @@ static void ui_node_link_items(NodeLinkArg *arg,
}
if (totitems > 0) {
- items = MEM_callocN(sizeof(NodeLinkItem) * totitems, "ui node link items");
+ items = (NodeLinkItem *)MEM_callocN(sizeof(NodeLinkItem) * totitems, "ui node link items");
i = 0;
- for (ngroup = arg->bmain->nodetrees.first; ngroup; ngroup = ngroup->id.next) {
- if ((ngroup->type != arg->ntree->type) || !nodeGroupPoll(arg->ntree, ngroup)) {
+ for (ngroup = (bNodeTree *)arg->bmain->nodetrees.first; ngroup;
+ ngroup = (bNodeTree *)ngroup->id.next) {
+ const char *disabled_hint;
+ if ((ngroup->type != arg->ntree->type) ||
+ !nodeGroupPoll(arg->ntree, ngroup, &disabled_hint)) {
continue;
}
ListBase *lb = (in_out == SOCK_IN ? &ngroup->inputs : &ngroup->outputs);
bNodeSocket *stemp;
int index;
- for (stemp = lb->first, index = 0; stemp; stemp = stemp->next, index++, i++) {
+ for (stemp = (bNodeSocket *)lb->first, index = 0; stemp;
+ stemp = stemp->next, index++, i++) {
NodeLinkItem *item = &items[i];
item->socket_index = index;
@@ -376,7 +386,7 @@ static void ui_node_link_items(NodeLinkArg *arg,
}
if (totitems > 0) {
- items = MEM_callocN(sizeof(NodeLinkItem) * totitems, "ui node link items");
+ items = (NodeLinkItem *)MEM_callocN(sizeof(NodeLinkItem) * totitems, "ui node link items");
i = 0;
for (stemp = socket_templates; stemp && stemp->type != -1; stemp++, i++) {
@@ -470,18 +480,18 @@ static void ui_node_menu_column(NodeLinkArg *arg, int nclass, const char *cname)
bNodeTree *ntree = arg->ntree;
bNodeSocket *sock = arg->sock;
uiLayout *layout = arg->layout;
- uiLayout *column = NULL;
+ uiLayout *column = nullptr;
uiBlock *block = uiLayoutGetBlock(layout);
uiBut *but;
NodeLinkArg *argN;
int first = 1;
/* generate array of node types sorted by UI name */
- bNodeType **sorted_ntypes = NULL;
- BLI_array_declare(sorted_ntypes);
+ blender::Vector<bNodeType *> sorted_ntypes;
NODE_TYPES_BEGIN (ntype) {
- if (!(ntype->poll && ntype->poll(ntype, ntree))) {
+ const char *disabled_hint;
+ if (!(ntype->poll && ntype->poll(ntype, ntree, &disabled_hint))) {
continue;
}
@@ -493,20 +503,20 @@ static void ui_node_menu_column(NodeLinkArg *arg, int nclass, const char *cname)
continue;
}
- BLI_array_append(sorted_ntypes, ntype);
+ sorted_ntypes.append(ntype);
}
NODE_TYPES_END;
qsort(
- sorted_ntypes, BLI_array_len(sorted_ntypes), sizeof(bNodeType *), ui_node_item_name_compare);
+ sorted_ntypes.data(), sorted_ntypes.size(), sizeof(bNodeType *), ui_node_item_name_compare);
/* generate UI */
- for (int j = 0; j < BLI_array_len(sorted_ntypes); j++) {
+ for (int j = 0; j < sorted_ntypes.size(); j++) {
bNodeType *ntype = sorted_ntypes[j];
NodeLinkItem *items;
int totitems;
char name[UI_MAX_NAME_STR];
- const char *cur_node_name = NULL;
+ const char *cur_node_name = nullptr;
int num = 0;
int icon = ICON_NONE;
@@ -526,11 +536,11 @@ static void ui_node_menu_column(NodeLinkArg *arg, int nclass, const char *cname)
}
if (first) {
- column = uiLayoutColumn(layout, 0);
+ column = uiLayoutColumn(layout, false);
UI_block_layout_set_current(block, column);
uiItemL(column, IFACE_(cname), ICON_NODE);
- but = block->buttons.last;
+ but = (uiBut *)block->buttons.last;
first = 0;
}
@@ -548,7 +558,7 @@ static void ui_node_menu_column(NodeLinkArg *arg, int nclass, const char *cname)
0,
UI_UNIT_X * 4,
UI_UNIT_Y,
- NULL,
+ nullptr,
0.0,
0.0,
0.0,
@@ -573,24 +583,22 @@ static void ui_node_menu_column(NodeLinkArg *arg, int nclass, const char *cname)
0,
UI_UNIT_X * 4,
UI_UNIT_Y,
- NULL,
+ nullptr,
0.0,
0.0,
0.0,
0.0,
TIP_("Add node to input"));
- argN = MEM_dupallocN(arg);
+ argN = (NodeLinkArg *)MEM_dupallocN(arg);
argN->item = items[i];
- UI_but_funcN_set(but, ui_node_link, argN, NULL);
+ UI_but_funcN_set(but, ui_node_link, argN, nullptr);
}
if (items) {
MEM_freeN(items);
}
}
-
- BLI_array_free(sorted_ntypes);
}
static void node_menu_column_foreach_cb(void *calldata, int nclass, const char *name)
@@ -630,7 +638,7 @@ static void ui_template_node_link_menu(bContext *C, uiLayout *layout, void *but_
if (sock->link) {
uiItemL(column, IFACE_("Link"), ICON_NONE);
- but = block->buttons.last;
+ but = (uiBut *)block->buttons.last;
but->drawflag = UI_BUT_TEXT_LEFT;
but = uiDefBut(block,
@@ -641,7 +649,7 @@ static void ui_template_node_link_menu(bContext *C, uiLayout *layout, void *but_
0,
UI_UNIT_X * 4,
UI_UNIT_Y,
- NULL,
+ nullptr,
0.0,
0.0,
0.0,
@@ -657,7 +665,7 @@ static void ui_template_node_link_menu(bContext *C, uiLayout *layout, void *but_
0,
UI_UNIT_X * 4,
UI_UNIT_Y,
- NULL,
+ nullptr,
0.0,
0.0,
0.0,
@@ -678,7 +686,7 @@ void uiTemplateNodeLink(
uiBut *but;
float socket_col[4];
- arg = MEM_callocN(sizeof(NodeLinkArg), "NodeLinkArg");
+ arg = (NodeLinkArg *)MEM_callocN(sizeof(NodeLinkArg), "NodeLinkArg");
arg->ntree = ntree;
arg->node = node;
arg->sock = input;
@@ -693,11 +701,11 @@ void uiTemplateNodeLink(
char name[UI_MAX_NAME_STR];
ui_node_sock_name(ntree, input, name);
but = uiDefMenuBut(
- block, ui_template_node_link_menu, NULL, name, 0, 0, UI_UNIT_X * 4, UI_UNIT_Y, "");
+ block, ui_template_node_link_menu, nullptr, name, 0, 0, UI_UNIT_X * 4, UI_UNIT_Y, "");
}
else {
but = uiDefIconMenuBut(
- block, ui_template_node_link_menu, NULL, ICON_NONE, 0, 0, UI_UNIT_X, UI_UNIT_Y, "");
+ block, ui_template_node_link_menu, nullptr, ICON_NONE, 0, 0, UI_UNIT_X, UI_UNIT_Y, "");
}
UI_but_type_set_menu_from_pulldown(but);
@@ -734,7 +742,7 @@ static void ui_node_draw_node(
}
}
- for (input = node->inputs.first; input; input = input->next) {
+ for (input = (bNodeSocket *)node->inputs.first; input; input = input->next) {
ui_node_draw_input(layout, C, ntree, node, input, depth + 1);
}
}
@@ -744,7 +752,7 @@ static void ui_node_draw_input(
{
PointerRNA inputptr, nodeptr;
uiBlock *block = uiLayoutGetBlock(layout);
- uiLayout *row = NULL;
+ uiLayout *row = nullptr;
bNode *lnode;
bool dependency_loop;
@@ -754,11 +762,11 @@ static void ui_node_draw_input(
/* to avoid eternal loops on cyclic dependencies */
node->flag |= NODE_TEST;
- lnode = (input->link) ? input->link->fromnode : NULL;
+ lnode = (input->link) ? input->link->fromnode : nullptr;
dependency_loop = (lnode && (lnode->flag & NODE_TEST));
if (dependency_loop) {
- lnode = NULL;
+ lnode = nullptr;
}
/* socket RNA pointer */
@@ -841,7 +849,7 @@ static void ui_node_draw_input(
case SOCK_STRING: {
const bNodeTree *node_tree = (const bNodeTree *)nodeptr.owner_id;
if (node_tree->type == NTREE_GEOMETRY) {
- node_geometry_add_attribute_search_button(node_tree, node, &inputptr, row);
+ node_geometry_add_attribute_search_button(C, node_tree, node, &inputptr, row);
}
else {
uiItemR(sub, &inputptr, "default_value", 0, "", ICON_NONE);
@@ -857,7 +865,7 @@ static void ui_node_draw_input(
}
if (add_dummy_decorator) {
- uiItemDecoratorR(split_wrapper.decorate_column, NULL, NULL, 0);
+ uiItemDecoratorR(split_wrapper.decorate_column, nullptr, nullptr, 0);
}
/* clear */
@@ -874,7 +882,7 @@ void uiTemplateNodeView(
}
/* clear for cycle check */
- for (tnode = ntree->nodes.first; tnode; tnode = tnode->next) {
+ for (tnode = (bNode *)ntree->nodes.first; tnode; tnode = tnode->next) {
tnode->flag &= ~NODE_TEST;
}
diff --git a/source/blender/editors/space_node/node_toolbar.c b/source/blender/editors/space_node/node_toolbar.cc
index 2e7d6ab6cd5..2e7d6ab6cd5 100644
--- a/source/blender/editors/space_node/node_toolbar.c
+++ b/source/blender/editors/space_node/node_toolbar.cc
diff --git a/source/blender/editors/space_node/node_view.c b/source/blender/editors/space_node/node_view.cc
index 8ecab92aa26..f0db0539c4f 100644
--- a/source/blender/editors/space_node/node_view.c
+++ b/source/blender/editors/space_node/node_view.cc
@@ -76,7 +76,7 @@ int space_node_view_flag(
BLI_rctf_init_minmax(&cur_new);
if (snode->edittree) {
- for (node = snode->edittree->nodes.first; node; node = node->next) {
+ for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) {
if ((node->flag & node_flag) == node_flag) {
BLI_rctf_union(&cur_new, &node->totr);
tot++;
@@ -191,16 +191,16 @@ void NODE_OT_view_selected(wmOperatorType *ot)
/** \name Background Image Operators
* \{ */
-typedef struct NodeViewMove {
+struct NodeViewMove {
int mvalo[2];
int xmin, ymin, xmax, ymax;
-} NodeViewMove;
+};
static int snode_bg_viewmove_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
SpaceNode *snode = CTX_wm_space_node(C);
ARegion *region = CTX_wm_region(C);
- NodeViewMove *nvm = op->customdata;
+ NodeViewMove *nvm = (NodeViewMove *)op->customdata;
switch (event->type) {
case MOUSEMOVE:
@@ -215,8 +215,8 @@ static int snode_bg_viewmove_modal(bContext *C, wmOperator *op, const wmEvent *e
CLAMP(snode->yof, nvm->ymin, nvm->ymax);
ED_region_tag_redraw(region);
- WM_main_add_notifier(NC_NODE | ND_DISPLAY, NULL);
- WM_main_add_notifier(NC_SPACE | ND_SPACE_NODE_VIEW, NULL);
+ WM_main_add_notifier(NC_NODE | ND_DISPLAY, nullptr);
+ WM_main_add_notifier(NC_SPACE | ND_SPACE_NODE_VIEW, nullptr);
break;
@@ -225,7 +225,7 @@ static int snode_bg_viewmove_modal(bContext *C, wmOperator *op, const wmEvent *e
case RIGHTMOUSE:
if (event->val == KM_RELEASE) {
MEM_freeN(nvm);
- op->customdata = NULL;
+ op->customdata = nullptr;
return OPERATOR_FINISHED;
}
break;
@@ -247,14 +247,14 @@ static int snode_bg_viewmove_invoke(bContext *C, wmOperator *op, const wmEvent *
void *lock;
ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node");
- ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock);
+ ibuf = BKE_image_acquire_ibuf(ima, nullptr, &lock);
- if (ibuf == NULL) {
+ if (ibuf == nullptr) {
BKE_image_release_ibuf(ima, ibuf, lock);
return OPERATOR_CANCELLED;
}
- nvm = MEM_callocN(sizeof(NodeViewMove), "NodeViewMove struct");
+ nvm = (NodeViewMove *)MEM_callocN(sizeof(NodeViewMove), "NodeViewMove struct");
op->customdata = nvm;
nvm->mvalo[0] = event->mval[0];
nvm->mvalo[1] = event->mval[1];
@@ -275,7 +275,7 @@ static int snode_bg_viewmove_invoke(bContext *C, wmOperator *op, const wmEvent *
static void snode_bg_viewmove_cancel(bContext *UNUSED(C), wmOperator *op)
{
MEM_freeN(op->customdata);
- op->customdata = NULL;
+ op->customdata = nullptr;
}
void NODE_OT_backimage_move(wmOperatorType *ot)
@@ -309,8 +309,8 @@ static int backimage_zoom_exec(bContext *C, wmOperator *op)
snode->zoom *= fac;
ED_region_tag_redraw(region);
- WM_main_add_notifier(NC_NODE | ND_DISPLAY, NULL);
- WM_main_add_notifier(NC_SPACE | ND_SPACE_NODE_VIEW, NULL);
+ WM_main_add_notifier(NC_NODE | ND_DISPLAY, nullptr);
+ WM_main_add_notifier(NC_SPACE | ND_SPACE_NODE_VIEW, nullptr);
return OPERATOR_FINISHED;
}
@@ -356,9 +356,9 @@ static int backimage_fit_exec(bContext *C, wmOperator *UNUSED(op))
float facx, facy;
ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node");
- ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock);
+ ibuf = BKE_image_acquire_ibuf(ima, nullptr, &lock);
- if ((ibuf == NULL) || (ibuf->x == 0) || (ibuf->y == 0)) {
+ if ((ibuf == nullptr) || (ibuf->x == 0) || (ibuf->y == 0)) {
BKE_image_release_ibuf(ima, ibuf, lock);
return OPERATOR_CANCELLED;
}
@@ -374,8 +374,8 @@ static int backimage_fit_exec(bContext *C, wmOperator *UNUSED(op))
snode->yof = 0;
ED_region_tag_redraw(region);
- WM_main_add_notifier(NC_NODE | ND_DISPLAY, NULL);
- WM_main_add_notifier(NC_SPACE | ND_SPACE_NODE_VIEW, NULL);
+ WM_main_add_notifier(NC_NODE | ND_DISPLAY, nullptr);
+ WM_main_add_notifier(NC_SPACE | ND_SPACE_NODE_VIEW, nullptr);
return OPERATOR_FINISHED;
}
@@ -402,7 +402,7 @@ void NODE_OT_backimage_fit(wmOperatorType *ot)
/** \name Sample Backdrop Operator
* \{ */
-typedef struct ImageSampleInfo {
+struct ImageSampleInfo {
ARegionType *art;
void *draw_handle;
int x, y;
@@ -420,12 +420,12 @@ typedef struct ImageSampleInfo {
int draw;
int color_manage;
-} ImageSampleInfo;
+};
static void sample_draw(const bContext *C, ARegion *region, void *arg_info)
{
Scene *scene = CTX_data_scene(C);
- ImageSampleInfo *info = arg_info;
+ ImageSampleInfo *info = (ImageSampleInfo *)arg_info;
if (info->draw) {
ED_image_draw_info(scene,
@@ -445,7 +445,7 @@ static void sample_draw(const bContext *C, ARegion *region, void *arg_info)
/* Returns mouse position in image space. */
bool ED_space_node_get_position(
- Main *bmain, SpaceNode *snode, struct ARegion *ar, const int mval[2], float fpos[2])
+ Main *bmain, SpaceNode *snode, struct ARegion *region, const int mval[2], float fpos[2])
{
if (!ED_node_is_compositor(snode) || (snode->flag & SNODE_BACKDRAW) == 0) {
return false;
@@ -453,7 +453,7 @@ bool ED_space_node_get_position(
void *lock;
Image *ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node");
- ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock);
+ ImBuf *ibuf = BKE_image_acquire_ibuf(ima, nullptr, &lock);
if (!ibuf) {
BKE_image_release_ibuf(ima, ibuf, lock);
return false;
@@ -462,8 +462,10 @@ bool ED_space_node_get_position(
/* map the mouse coords to the backdrop image space */
float bufx = ibuf->x * snode->zoom;
float bufy = ibuf->y * snode->zoom;
- fpos[0] = (bufx > 0.0f ? ((float)mval[0] - 0.5f * ar->winx - snode->xof) / bufx + 0.5f : 0.0f);
- fpos[1] = (bufy > 0.0f ? ((float)mval[1] - 0.5f * ar->winy - snode->yof) / bufy + 0.5f : 0.0f);
+ fpos[0] = (bufx > 0.0f ? ((float)mval[0] - 0.5f * region->winx - snode->xof) / bufx + 0.5f :
+ 0.0f);
+ fpos[1] = (bufy > 0.0f ? ((float)mval[1] - 0.5f * region->winy - snode->yof) / bufy + 0.5f :
+ 0.0f);
BKE_image_release_ibuf(ima, ibuf, lock);
return true;
@@ -489,7 +491,7 @@ bool ED_space_node_color_sample(
}
ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node");
- ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock);
+ ibuf = BKE_image_acquire_ibuf(ima, nullptr, &lock);
if (!ibuf) {
return false;
}
@@ -532,14 +534,14 @@ static void sample_apply(bContext *C, wmOperator *op, const wmEvent *event)
Main *bmain = CTX_data_main(C);
SpaceNode *snode = CTX_wm_space_node(C);
ARegion *region = CTX_wm_region(C);
- ImageSampleInfo *info = op->customdata;
+ ImageSampleInfo *info = (ImageSampleInfo *)op->customdata;
void *lock;
Image *ima;
ImBuf *ibuf;
float fx, fy, bufx, bufy;
ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node");
- ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock);
+ ibuf = BKE_image_acquire_ibuf(ima, nullptr, &lock);
if (!ibuf) {
info->draw = 0;
return;
@@ -570,8 +572,8 @@ static void sample_apply(bContext *C, wmOperator *op, const wmEvent *event)
info->draw = 1;
info->channels = ibuf->channels;
- info->zp = NULL;
- info->zfp = NULL;
+ info->zp = nullptr;
+ info->zfp = nullptr;
if (ibuf->rect) {
cp = (uchar *)(ibuf->rect + y * ibuf->x + x);
@@ -616,7 +618,7 @@ static void sample_apply(bContext *C, wmOperator *op, const wmEvent *event)
}
else {
info->draw = 0;
- ED_node_sample_set(NULL);
+ ED_node_sample_set(nullptr);
}
BKE_image_release_ibuf(ima, ibuf, lock);
@@ -626,9 +628,9 @@ static void sample_apply(bContext *C, wmOperator *op, const wmEvent *event)
static void sample_exit(bContext *C, wmOperator *op)
{
- ImageSampleInfo *info = op->customdata;
+ ImageSampleInfo *info = (ImageSampleInfo *)op->customdata;
- ED_node_sample_set(NULL);
+ ED_node_sample_set(nullptr);
ED_region_draw_cb_exit(info->art, info->draw_handle);
ED_area_tag_redraw(CTX_wm_area(C));
MEM_freeN(info);
@@ -644,7 +646,7 @@ static int sample_invoke(bContext *C, wmOperator *op, const wmEvent *event)
return OPERATOR_CANCELLED;
}
- info = MEM_callocN(sizeof(ImageSampleInfo), "ImageSampleInfo");
+ info = (ImageSampleInfo *)MEM_callocN(sizeof(ImageSampleInfo), "ImageSampleInfo");
info->art = region->type;
info->draw_handle = ED_region_draw_cb_activate(
region->type, sample_draw, info, REGION_DRAW_POST_PIXEL);
diff --git a/source/blender/editors/space_node/space_node.c b/source/blender/editors/space_node/space_node.c
index 5d14919502e..f20a9409d90 100644
--- a/source/blender/editors/space_node/space_node.c
+++ b/source/blender/editors/space_node/space_node.c
@@ -74,7 +74,7 @@ void ED_node_tree_start(SpaceNode *snode, bNodeTree *ntree, ID *id, ID *from)
copy_v2_v2(path->view_center, ntree->view_center);
if (id) {
- BLI_strncpy(path->node_name, id->name + 2, sizeof(path->node_name));
+ BLI_strncpy(path->display_name, id->name + 2, sizeof(path->display_name));
}
BLI_addtail(&snode->treepath, path);
@@ -111,6 +111,7 @@ void ED_node_tree_push(SpaceNode *snode, bNodeTree *ntree, bNode *gnode)
}
BLI_strncpy(path->node_name, gnode->name, sizeof(path->node_name));
+ BLI_strncpy(path->display_name, gnode->name, sizeof(path->display_name));
}
else {
path->parent_key = NODE_INSTANCE_KEY_BASE;
@@ -175,7 +176,7 @@ int ED_node_tree_path_length(SpaceNode *snode)
int length = 0;
int i = 0;
LISTBASE_FOREACH_INDEX (bNodeTreePath *, path, &snode->treepath, i) {
- length += strlen(path->node_name);
+ length += strlen(path->display_name);
if (i > 0) {
length += 1; /* for separator char */
}
@@ -190,12 +191,12 @@ void ED_node_tree_path_get(SpaceNode *snode, char *value)
value[0] = '\0';
LISTBASE_FOREACH_INDEX (bNodeTreePath *, path, &snode->treepath, i) {
if (i == 0) {
- strcpy(value, path->node_name);
- value += strlen(path->node_name);
+ strcpy(value, path->display_name);
+ value += strlen(path->display_name);
}
else {
- sprintf(value, "/%s", path->node_name);
- value += strlen(path->node_name) + 1;
+ sprintf(value, "/%s", path->display_name);
+ value += strlen(path->display_name) + 1;
}
}
}
@@ -208,10 +209,10 @@ void ED_node_tree_path_get_fixedbuf(SpaceNode *snode, char *value, int max_lengt
int i = 0;
LISTBASE_FOREACH_INDEX (bNodeTreePath *, path, &snode->treepath, i) {
if (i == 0) {
- size = BLI_strncpy_rlen(value, path->node_name, max_length);
+ size = BLI_strncpy_rlen(value, path->display_name, max_length);
}
else {
- size = BLI_snprintf_rlen(value, max_length, "/%s", path->node_name);
+ size = BLI_snprintf_rlen(value, max_length, "/%s", path->display_name);
}
max_length -= size;
if (max_length <= 0) {
diff --git a/source/blender/editors/space_outliner/CMakeLists.txt b/source/blender/editors/space_outliner/CMakeLists.txt
index 4a1e5c1a12c..c31239f0e9c 100644
--- a/source/blender/editors/space_outliner/CMakeLists.txt
+++ b/source/blender/editors/space_outliner/CMakeLists.txt
@@ -51,6 +51,7 @@ set(SRC
tree/tree_display_data.cc
tree/tree_display_libraries.cc
tree/tree_display_orphaned.cc
+ tree/tree_display_override_library.cc
tree/tree_display_scenes.cc
tree/tree_display_sequencer.cc
tree/tree_display_view_layer.cc
diff --git a/source/blender/editors/space_outliner/outliner_dragdrop.c b/source/blender/editors/space_outliner/outliner_dragdrop.c
index b3b36811411..7d889eed612 100644
--- a/source/blender/editors/space_outliner/outliner_dragdrop.c
+++ b/source/blender/editors/space_outliner/outliner_dragdrop.c
@@ -818,12 +818,7 @@ static bool datastack_drop_are_types_valid(StackDropData *drop_data)
switch (drop_data->drag_tselem->type) {
case TSE_MODIFIER_BASE:
case TSE_MODIFIER:
- if (ob_parent->type == OB_GPENCIL) {
- return ob_dst->type == OB_GPENCIL;
- }
- else if (ob_parent->type != OB_GPENCIL) {
- return ob_dst->type != OB_GPENCIL;
- }
+ return (ob_parent->type == OB_GPENCIL) == (ob_dst->type == OB_GPENCIL);
break;
case TSE_CONSTRAINT_BASE:
case TSE_CONSTRAINT:
@@ -1008,7 +1003,7 @@ static void datastack_drop_reorder(bContext *C, ReportList *reports, StackDropDa
drag_te, drop_te, insert_type, &ob->greasepencil_modifiers);
ED_object_gpencil_modifier_move_to_index(reports, ob, drop_data->drag_directdata, index);
}
- else if (ob->type != OB_GPENCIL) {
+ else {
index = outliner_get_insert_index(drag_te, drop_te, insert_type, &ob->modifiers);
ED_object_modifier_move_to_index(reports, ob, drop_data->drag_directdata, index);
}
@@ -1104,8 +1099,6 @@ static bool collection_drop_init(bContext *C,
const wmEvent *event,
CollectionDrop *data)
{
- SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
-
/* Get collection to drop into. */
TreeElementInsertType insert_type;
TreeElement *te = outliner_drop_insert_collection_find(C, event, &insert_type);
@@ -1140,7 +1133,7 @@ static bool collection_drop_init(bContext *C,
/* Get collection to drag out of. */
ID *parent = drag_id->from_parent;
Collection *from_collection = collection_parent_from_ID(parent);
- if (event->ctrl || space_outliner->outlinevis == SO_SCENES) {
+ if (event->ctrl) {
from_collection = NULL;
}
diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c
index 0916e106abf..328a787c768 100644
--- a/source/blender/editors/space_outliner/outliner_draw.c
+++ b/source/blender/editors/space_outliner/outliner_draw.c
@@ -1775,6 +1775,87 @@ static void outliner_draw_userbuts(uiBlock *block,
}
}
+static bool outliner_draw_overrides_buts(uiBlock *block,
+ ARegion *region,
+ SpaceOutliner *space_outliner,
+ ListBase *lb,
+ const bool is_open)
+{
+ bool any_item_has_warnings = false;
+
+ LISTBASE_FOREACH (TreeElement *, te, lb) {
+ bool item_has_warnings = false;
+ const bool do_draw = (te->ys + 2 * UI_UNIT_Y >= region->v2d.cur.ymin &&
+ te->ys <= region->v2d.cur.ymax);
+ int but_flag = UI_BUT_DRAG_LOCK;
+ const char *tip = NULL;
+
+ TreeStoreElem *tselem = TREESTORE(te);
+ switch (tselem->type) {
+ case TSE_LIBRARY_OVERRIDE_BASE: {
+ ID *id = tselem->id;
+
+ if (id->flag & LIB_LIB_OVERRIDE_RESYNC_LEFTOVER) {
+ item_has_warnings = true;
+ if (do_draw) {
+ tip = TIP_(
+ "This override data-block is not needed anymore, but was detected as user-edited");
+ }
+ }
+ else if (ID_IS_OVERRIDE_LIBRARY_REAL(id) && ID_REAL_USERS(id) == 0) {
+ item_has_warnings = true;
+ if (do_draw) {
+ tip = TIP_("This override data-block is unused");
+ }
+ }
+ break;
+ }
+ case TSE_LIBRARY_OVERRIDE: {
+ const bool is_rna_path_valid = (bool)(POINTER_AS_UINT(te->directdata));
+ if (!is_rna_path_valid) {
+ item_has_warnings = true;
+ if (do_draw) {
+ tip = TIP_(
+ "This override property does not exist in current data, it will be removed on "
+ "next .blend file save");
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ const bool any_child_has_warnings = outliner_draw_overrides_buts(
+ block,
+ region,
+ space_outliner,
+ &te->subtree,
+ is_open && TSELEM_OPEN(tselem, space_outliner));
+
+ if (do_draw &&
+ (item_has_warnings || (any_child_has_warnings && !TSELEM_OPEN(tselem, space_outliner)))) {
+ if (tip == NULL) {
+ tip = TIP_("Some sub-items require attention");
+ }
+ uiBut *bt = uiDefIconBlockBut(block,
+ NULL,
+ NULL,
+ 1,
+ ICON_ERROR,
+ (int)(region->v2d.cur.xmax - OL_TOG_USER_BUTS_STATUS),
+ te->ys,
+ UI_UNIT_X,
+ UI_UNIT_Y,
+ tip);
+ UI_but_flag_enable(bt, but_flag);
+ }
+ any_item_has_warnings = any_item_has_warnings || item_has_warnings || any_child_has_warnings;
+ }
+
+ return any_item_has_warnings;
+}
+
static void outliner_draw_rnacols(ARegion *region, int sizex)
{
View2D *v2d = &region->v2d;
@@ -2025,7 +2106,7 @@ static void outliner_draw_mode_column_toggle(uiBlock *block,
tip);
UI_but_func_set(but, outliner_mode_toggle_fn, tselem, NULL);
UI_but_flag_enable(but, UI_BUT_DRAG_LOCK);
- /* Mode toggling handles it's own undo state because undo steps need to be grouped. */
+ /* Mode toggling handles its own undo state because undo steps need to be grouped. */
UI_but_flag_disable(but, UI_BUT_UNDO);
if (ID_IS_LINKED(&ob->id)) {
@@ -2896,7 +2977,19 @@ static void outliner_draw_iconrow(bContext *C,
active = tree_element_type_active_state_get(C, tvc, te, tselem);
}
- if (!ELEM(tselem->type, TSE_SOME_ID, TSE_LAYER_COLLECTION, TSE_R_LAYER, TSE_GP_LAYER)) {
+ if (!ELEM(tselem->type,
+ TSE_ID_BASE,
+ TSE_SOME_ID,
+ TSE_LAYER_COLLECTION,
+ TSE_R_LAYER,
+ TSE_GP_LAYER,
+ TSE_LIBRARY_OVERRIDE_BASE,
+ TSE_LIBRARY_OVERRIDE,
+ TSE_BONE,
+ TSE_EBONE,
+ TSE_POSE_CHANNEL,
+ TSE_POSEGRP,
+ TSE_DEFGROUP)) {
outliner_draw_iconrow_doit(block, te, fstyle, xmax, offsx, ys, alpha_fac, active, 1);
}
else {
@@ -3656,7 +3749,11 @@ void draw_outliner(const bContext *C)
}
/* Sync selection state from view layer. */
- if (!ELEM(space_outliner->outlinevis, SO_LIBRARIES, SO_DATA_API, SO_ID_ORPHANS) &&
+ if (!ELEM(space_outliner->outlinevis,
+ SO_LIBRARIES,
+ SO_OVERRIDES_LIBRARY,
+ SO_DATA_API,
+ SO_ID_ORPHANS) &&
space_outliner->flag & SO_SYNC_SELECT) {
outliner_sync_selection(C, space_outliner);
}
@@ -3703,6 +3800,10 @@ void draw_outliner(const bContext *C)
/* draw user toggle columns */
outliner_draw_userbuts(block, region, space_outliner, &space_outliner->tree);
}
+ else if (space_outliner->outlinevis == SO_OVERRIDES_LIBRARY) {
+ /* Draw overrides status columns. */
+ outliner_draw_overrides_buts(block, region, space_outliner, &space_outliner->tree, true);
+ }
else if (restrict_column_width > 0.0f) {
/* draw restriction columns */
RestrictPropertiesActive props_active;
diff --git a/source/blender/editors/space_outliner/outliner_edit.c b/source/blender/editors/space_outliner/outliner_edit.c
index 681f7fab18a..4a070590d55 100644
--- a/source/blender/editors/space_outliner/outliner_edit.c
+++ b/source/blender/editors/space_outliner/outliner_edit.c
@@ -154,7 +154,9 @@ void OUTLINER_OT_highlight_update(wmOperatorType *ot)
/** \name Toggle Open/Closed Operator
* \{ */
-/* Open or close a tree element, optionally toggling all children recursively */
+/**
+ * Open or close a tree element, optionally toggling all children recursively.
+ */
void outliner_item_openclose(SpaceOutliner *space_outliner,
TreeElement *te,
bool open,
@@ -466,7 +468,7 @@ static void id_delete(bContext *C, ReportList *reports, TreeElement *te, TreeSto
BKE_reportf(reports, RPT_WARNING, "Cannot delete indirectly linked id '%s'", id->name);
return;
}
- if (BKE_library_ID_is_indirectly_used(bmain, id) && ID_REAL_USERS(id) <= 1) {
+ if (ID_REAL_USERS(id) <= 1 && BKE_library_ID_is_indirectly_used(bmain, id)) {
BKE_reportf(reports,
RPT_WARNING,
"Cannot delete id '%s', indirectly used data-blocks need at least one user",
@@ -665,6 +667,10 @@ static const EnumPropertyItem *outliner_id_itemf(bContext *C,
PropertyRNA *UNUSED(prop),
bool *r_free)
{
+ if (C == NULL) {
+ return DummyRNA_NULL_items;
+ }
+
EnumPropertyItem item_tmp = {0}, *item = NULL;
int totitem = 0;
int i = 0;
@@ -1595,8 +1601,10 @@ void OUTLINER_OT_show_one_level(wmOperatorType *ot)
/** \name Show Hierarchy Operator
* \{ */
-/* Helper function for tree_element_shwo_hierarchy() -
- * recursively checks whether subtrees have any objects. */
+/**
+ * Helper function for #tree_element_shwo_hierarchy() -
+ * recursively checks whether subtrees have any objects.
+ */
static int subtree_has_objects(ListBase *lb)
{
LISTBASE_FOREACH (TreeElement *, te, lb) {
diff --git a/source/blender/editors/space_outliner/outliner_intern.h b/source/blender/editors/space_outliner/outliner_intern.h
index f65e273c1b5..fea5ddae16b 100644
--- a/source/blender/editors/space_outliner/outliner_intern.h
+++ b/source/blender/editors/space_outliner/outliner_intern.h
@@ -193,7 +193,7 @@ typedef enum {
/* The outliner display modes that support the filter system.
* Note: keep it synced with space_outliner.py */
#define SUPPORT_FILTER_OUTLINER(space_outliner_) \
- (ELEM((space_outliner_)->outlinevis, SO_VIEW_LAYER))
+ (ELEM((space_outliner_)->outlinevis, SO_VIEW_LAYER, SO_OVERRIDES_LIBRARY))
/* Outliner Searching --
*
diff --git a/source/blender/editors/space_outliner/outliner_sync.c b/source/blender/editors/space_outliner/outliner_sync.c
index 6543a909a41..d78767019b5 100644
--- a/source/blender/editors/space_outliner/outliner_sync.c
+++ b/source/blender/editors/space_outliner/outliner_sync.c
@@ -356,8 +356,11 @@ static void outliner_sync_selection_from_outliner(Scene *scene,
void ED_outliner_select_sync_from_outliner(bContext *C, SpaceOutliner *space_outliner)
{
/* Don't sync if not checked or in certain outliner display modes */
- if (!(space_outliner->flag & SO_SYNC_SELECT) ||
- ELEM(space_outliner->outlinevis, SO_LIBRARIES, SO_DATA_API, SO_ID_ORPHANS)) {
+ if (!(space_outliner->flag & SO_SYNC_SELECT) || ELEM(space_outliner->outlinevis,
+ SO_LIBRARIES,
+ SO_OVERRIDES_LIBRARY,
+ SO_DATA_API,
+ SO_ID_ORPHANS)) {
return;
}
diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c
index dc9205106ab..f809bb13b42 100644
--- a/source/blender/editors/space_outliner/outliner_tools.c
+++ b/source/blender/editors/space_outliner/outliner_tools.c
@@ -598,8 +598,14 @@ static uiBlock *merged_element_search_menu(bContext *C, ARegion *region, void *d
short menu_width = 10 * UI_UNIT_X;
but = uiDefSearchBut(
block, search, 0, ICON_VIEWZOOM, sizeof(search), 10, 10, menu_width, UI_UNIT_Y, 0, 0, "");
- UI_but_func_search_set(
- but, NULL, merged_element_search_update_fn, data, NULL, merged_element_search_exec_fn, NULL);
+ UI_but_func_search_set(but,
+ NULL,
+ merged_element_search_update_fn,
+ data,
+ false,
+ NULL,
+ merged_element_search_exec_fn,
+ NULL);
UI_but_flag_enable(but, UI_BUT_ACTIVATE_ON_INIT);
/* Fake button to hold space for search items */
@@ -699,8 +705,8 @@ static void outliner_object_delete_fn(bContext *C, ReportList *reports, Scene *s
reports, RPT_WARNING, "Cannot delete indirectly linked object '%s'", ob->id.name + 2);
return;
}
- if (BKE_library_ID_is_indirectly_used(bmain, ob) && ID_REAL_USERS(ob) <= 1 &&
- ID_EXTRA_USERS(ob) == 0) {
+ if (ID_REAL_USERS(ob) <= 1 && ID_EXTRA_USERS(ob) == 0 &&
+ BKE_library_ID_is_indirectly_used(bmain, ob)) {
BKE_reportf(reports,
RPT_WARNING,
"Cannot delete object '%s' from scene '%s', indirectly used objects need at "
@@ -734,7 +740,7 @@ static void id_local_fn(bContext *C,
BKE_lib_id_clear_library_data(bmain, tselem->id);
}
else {
- BKE_main_id_clear_newpoins(bmain);
+ BKE_main_id_newptr_and_tag_clear(bmain);
}
}
else if (ID_IS_OVERRIDE_LIBRARY_REAL(tselem->id)) {
@@ -840,13 +846,13 @@ static void id_override_library_create_fn(bContext *C,
te->store_elem->id->tag |= LIB_TAG_DOIT;
}
success = BKE_lib_override_library_create(
- bmain, CTX_data_scene(C), CTX_data_view_layer(C), id_root, id_reference);
+ bmain, CTX_data_scene(C), CTX_data_view_layer(C), id_root, id_reference, NULL);
}
else if (ID_IS_OVERRIDABLE_LIBRARY(id_root)) {
success = BKE_lib_override_library_create_from_id(bmain, id_root, true) != NULL;
/* Cleanup. */
- BKE_main_id_clear_newpoins(bmain);
+ BKE_main_id_newptr_and_tag_clear(bmain);
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
}
@@ -896,7 +902,7 @@ static void id_override_library_reset_fn(bContext *C,
}
static void id_override_library_resync_fn(bContext *C,
- ReportList *UNUSED(reports),
+ ReportList *reports,
Scene *scene,
TreeElement *te,
TreeStoreElem *UNUSED(tsep),
@@ -925,7 +931,7 @@ static void id_override_library_resync_fn(bContext *C,
}
BKE_lib_override_library_resync(
- bmain, scene, CTX_data_view_layer(C), id_root, NULL, do_hierarchy_enforce);
+ bmain, scene, CTX_data_view_layer(C), id_root, NULL, do_hierarchy_enforce, true, reports);
WM_event_add_notifier(C, NC_WINDOW, NULL);
}
@@ -1422,8 +1428,8 @@ static Base *outline_batch_delete_hierarchy(
base->object->id.name + 2);
return base_next;
}
- if (BKE_library_ID_is_indirectly_used(bmain, object) && ID_REAL_USERS(object) <= 1 &&
- ID_EXTRA_USERS(object) == 0) {
+ if (ID_REAL_USERS(object) <= 1 && ID_EXTRA_USERS(object) == 0 &&
+ BKE_library_ID_is_indirectly_used(bmain, object)) {
BKE_reportf(reports,
RPT_WARNING,
"Cannot delete object '%s' from scene '%s', indirectly used objects need at least "
@@ -1873,7 +1879,7 @@ static bool outliner_id_operation_item_poll(bContext *C,
case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY:
case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY_ENFORCE:
case OUTLINER_IDOP_OVERRIDE_LIBRARY_DELETE_HIERARCHY:
- if (ID_IS_OVERRIDE_LIBRARY_REAL(tselem->id)) {
+ if (ID_IS_OVERRIDE_LIBRARY_REAL(tselem->id) && !ID_IS_LINKED(tselem->id)) {
return true;
}
return false;
@@ -2263,7 +2269,8 @@ static const EnumPropertyItem outliner_lib_op_type_items[] = {
"DELETE",
ICON_X,
"Delete",
- "Delete this library and all its item from Blender - WARNING: no undo"},
+ "Delete this library and all its item.\n"
+ "Warning: No undo"},
{OL_LIB_RELOCATE,
"RELOCATE",
0,
diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c
index 573fb492613..90389fc1be2 100644
--- a/source/blender/editors/space_outliner/outliner_tree.c
+++ b/source/blender/editors/space_outliner/outliner_tree.c
@@ -908,6 +908,11 @@ TreeElement *outliner_add_element(SpaceOutliner *space_outliner,
BLI_assert(!"Expected this ID type to be ported to new Outliner tree-element design");
}
}
+ else if (ELEM(type, TSE_LIBRARY_OVERRIDE_BASE, TSE_LIBRARY_OVERRIDE)) {
+ if (!te->type) {
+ BLI_assert(!"Expected override types to be ported to new Outliner tree-element design");
+ }
+ }
else {
/* Other cases must be caught above. */
BLI_assert(TSE_IS_REAL_ID(tselem));
@@ -1335,7 +1340,7 @@ static void outliner_sort(ListBase *lb)
tp->name = te->name;
tp->idcode = te->idcode;
- if ((tselem->type != TSE_SOME_ID) && tselem->type != TSE_DEFGROUP) {
+ if (!ELEM(tselem->type, TSE_SOME_ID, TSE_DEFGROUP)) {
tp->idcode = 0; /* Don't sort this. */
}
if (tselem->type == TSE_ID_BASE) {
@@ -1920,5 +1925,5 @@ void outliner_build_tree(Main *mainvar,
outliner_filter_tree(space_outliner, view_layer);
outliner_restore_scrolling_position(space_outliner, region, &focus);
- BKE_main_id_clear_newpoins(mainvar);
+ BKE_main_id_newptr_and_tag_clear(mainvar);
}
diff --git a/source/blender/editors/space_outliner/outliner_utils.c b/source/blender/editors/space_outliner/outliner_utils.c
index 562457c62e9..5feb157bfc8 100644
--- a/source/blender/editors/space_outliner/outliner_utils.c
+++ b/source/blender/editors/space_outliner/outliner_utils.c
@@ -361,6 +361,7 @@ float outliner_restrict_columns_width(const SpaceOutliner *space_outliner)
case SO_DATA_API:
case SO_SEQUENCE:
case SO_LIBRARIES:
+ case SO_OVERRIDES_LIBRARY:
return 0.0f;
case SO_ID_ORPHANS:
num_columns = 3;
diff --git a/source/blender/editors/space_outliner/space_outliner.c b/source/blender/editors/space_outliner/space_outliner.c
index 419713035b6..728be1ccaaf 100644
--- a/source/blender/editors/space_outliner/space_outliner.c
+++ b/source/blender/editors/space_outliner/space_outliner.c
@@ -155,6 +155,8 @@ static void outliner_main_region_listener(const wmRegionListenerParams *params)
case NC_OBJECT:
switch (wmn->data) {
case ND_TRANSFORM:
+ ED_region_tag_redraw_no_rebuild(region);
+ break;
case ND_BONE_ACTIVE:
case ND_BONE_SELECT:
case ND_DRAW:
@@ -273,7 +275,7 @@ static void outliner_main_region_message_subscribe(const wmRegionMessageSubscrib
.notify = ED_region_do_msg_notify_tag_redraw,
};
- if (ELEM(space_outliner->outlinevis, SO_VIEW_LAYER, SO_SCENES)) {
+ if (ELEM(space_outliner->outlinevis, SO_VIEW_LAYER, SO_SCENES, SO_OVERRIDES_LIBRARY)) {
WM_msg_subscribe_rna_anon_prop(mbus, Window, view_layer, &msg_sub_value_region_tag_redraw);
}
}
diff --git a/source/blender/editors/space_outliner/tree/tree_display.cc b/source/blender/editors/space_outliner/tree/tree_display.cc
index 4395383e838..003afd5bdec 100644
--- a/source/blender/editors/space_outliner/tree/tree_display.cc
+++ b/source/blender/editors/space_outliner/tree/tree_display.cc
@@ -44,6 +44,9 @@ TreeDisplay *outliner_tree_display_create(eSpaceOutliner_Mode mode, SpaceOutline
case SO_ID_ORPHANS:
tree_display = new TreeDisplayIDOrphans(*space_outliner);
break;
+ case SO_OVERRIDES_LIBRARY:
+ tree_display = new TreeDisplayOverrideLibrary(*space_outliner);
+ break;
case SO_VIEW_LAYER:
default:
tree_display = new TreeDisplayViewLayer(*space_outliner);
diff --git a/source/blender/editors/space_outliner/tree/tree_display.hh b/source/blender/editors/space_outliner/tree/tree_display.hh
index b6183050e82..f089a149805 100644
--- a/source/blender/editors/space_outliner/tree/tree_display.hh
+++ b/source/blender/editors/space_outliner/tree/tree_display.hh
@@ -111,6 +111,24 @@ class TreeDisplayLibraries final : public AbstractTreeDisplay {
};
/* -------------------------------------------------------------------- */
+/* Library Overrides Tree-Display. */
+
+/**
+ * \brief Tree-Display for the Library Overrides display mode.
+ */
+class TreeDisplayOverrideLibrary final : public AbstractTreeDisplay {
+ public:
+ TreeDisplayOverrideLibrary(SpaceOutliner &space_outliner);
+
+ ListBase buildTree(const TreeSourceData &source_data) override;
+
+ private:
+ TreeElement *add_library_contents(Main &, ListBase &, Library *) const;
+ bool override_library_id_filter_poll(Library *lib, ID *id) const;
+ short id_filter_get() const;
+};
+
+/* -------------------------------------------------------------------- */
/* Video Sequencer Tree-Display */
enum SequenceAddOp {
diff --git a/source/blender/editors/space_outliner/tree/tree_display_override_library.cc b/source/blender/editors/space_outliner/tree/tree_display_override_library.cc
new file mode 100644
index 00000000000..3059f8bfe0c
--- /dev/null
+++ b/source/blender/editors/space_outliner/tree/tree_display_override_library.cc
@@ -0,0 +1,204 @@
+/*
+ * 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.
+ */
+
+/** \file
+ * \ingroup spoutliner
+ */
+
+#include "BLI_listbase.h"
+#include "BLI_listbase_wrapper.hh"
+
+#include "BKE_collection.h"
+#include "BKE_main.h"
+
+#include "DNA_collection_types.h"
+
+#include "BLT_translation.h"
+
+#include "../outliner_intern.h"
+#include "tree_display.hh"
+
+namespace blender::ed::outliner {
+
+/* Convenience/readability. */
+template<typename T> using List = ListBaseWrapper<T>;
+
+TreeDisplayOverrideLibrary::TreeDisplayOverrideLibrary(SpaceOutliner &space_outliner)
+ : AbstractTreeDisplay(space_outliner)
+{
+}
+
+ListBase TreeDisplayOverrideLibrary::buildTree(const TreeSourceData &source_data)
+{
+ ListBase tree = {nullptr};
+
+ {
+ /* current file first - mainvar provides tselem with unique pointer - not used */
+ TreeElement *ten = add_library_contents(*source_data.bmain, tree, nullptr);
+ TreeStoreElem *tselem;
+
+ if (ten) {
+ tselem = TREESTORE(ten);
+ if (!tselem->used) {
+ tselem->flag &= ~TSE_CLOSED;
+ }
+ }
+ }
+
+ for (ID *id : List<ID>(source_data.bmain->libraries)) {
+ Library *lib = reinterpret_cast<Library *>(id);
+ TreeElement *ten = add_library_contents(*source_data.bmain, tree, lib);
+ /* NULL-check matters, due to filtering there may not be a new element. */
+ if (ten) {
+ lib->id.newid = (ID *)ten;
+ }
+ }
+
+ /* make hierarchy */
+ for (TreeElement *ten : List<TreeElement>(tree)) {
+ if (ten == tree.first) {
+ /* First item is main, skip. */
+ continue;
+ }
+
+ TreeStoreElem *tselem = TREESTORE(ten);
+ Library *lib = (Library *)tselem->id;
+ BLI_assert(!lib || (GS(lib->id.name) == ID_LI));
+ if (!lib || !lib->parent) {
+ continue;
+ }
+
+ TreeElement *parent = (TreeElement *)lib->parent->id.newid;
+
+ if (tselem->id->tag & LIB_TAG_INDIRECT) {
+ /* Only remove from 'first level' if lib is not also directly used. */
+ BLI_remlink(&tree, ten);
+ BLI_addtail(&parent->subtree, ten);
+ ten->parent = parent;
+ }
+ else {
+ /* Else, make a new copy of the libtree for our parent. */
+ TreeElement *dupten = add_library_contents(*source_data.bmain, parent->subtree, lib);
+ if (dupten) {
+ dupten->parent = parent;
+ }
+ }
+ }
+ /* restore newid pointers */
+ for (ID *library_id : List<ID>(source_data.bmain->libraries)) {
+ library_id->newid = nullptr;
+ }
+
+ return tree;
+}
+
+TreeElement *TreeDisplayOverrideLibrary::add_library_contents(Main &mainvar,
+ ListBase &lb,
+ Library *lib) const
+{
+ const short filter_id_type = id_filter_get();
+
+ ListBase *lbarray[INDEX_ID_MAX];
+ int tot;
+ if (filter_id_type) {
+ lbarray[0] = which_libbase(&mainvar, space_outliner_.filter_id_type);
+ tot = 1;
+ }
+ else {
+ tot = set_listbasepointers(&mainvar, lbarray);
+ }
+
+ TreeElement *tenlib = nullptr;
+ for (int a = 0; a < tot; a++) {
+ if (!lbarray[a] || !lbarray[a]->first) {
+ continue;
+ }
+
+ ID *id = nullptr;
+
+ /* check if there's data in current lib */
+ for (ID *id_iter : List<ID>(lbarray[a])) {
+ if (id_iter->lib == lib && ID_IS_OVERRIDE_LIBRARY_REAL(id_iter)) {
+ id = id_iter;
+ break;
+ }
+ }
+
+ if (id != nullptr) {
+ if (!tenlib) {
+ /* Create library tree element on demand, depending if there are any data-blocks. */
+ if (lib) {
+ tenlib = outliner_add_element(&space_outliner_, &lb, lib, nullptr, TSE_SOME_ID, 0);
+ }
+ else {
+ tenlib = outliner_add_element(&space_outliner_, &lb, &mainvar, nullptr, TSE_ID_BASE, 0);
+ tenlib->name = IFACE_("Current File");
+ }
+ }
+
+ /* Create data-block list parent element on demand. */
+ if (id != nullptr) {
+ TreeElement *ten;
+
+ if (filter_id_type) {
+ ten = tenlib;
+ }
+ else {
+ ten = outliner_add_element(
+ &space_outliner_, &tenlib->subtree, lbarray[a], nullptr, TSE_ID_BASE, 0);
+ ten->directdata = lbarray[a];
+ ten->name = outliner_idcode_to_plural(GS(id->name));
+ }
+
+ for (ID *id : List<ID>(lbarray[a])) {
+ if (override_library_id_filter_poll(lib, id)) {
+ TreeElement *override_tree_element = outliner_add_element(
+ &space_outliner_, &ten->subtree, id, ten, TSE_LIBRARY_OVERRIDE_BASE, 0);
+
+ if (BLI_listbase_is_empty(&override_tree_element->subtree)) {
+ outliner_free_tree_element(override_tree_element, &ten->subtree);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return tenlib;
+}
+
+short TreeDisplayOverrideLibrary::id_filter_get() const
+{
+ if (space_outliner_.filter & SO_FILTER_ID_TYPE) {
+ return space_outliner_.filter_id_type;
+ }
+ return 0;
+}
+
+bool TreeDisplayOverrideLibrary::override_library_id_filter_poll(Library *lib, ID *id) const
+{
+ if (id->lib != lib) {
+ return false;
+ }
+
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace blender::ed::outliner
diff --git a/source/blender/editors/space_outliner/tree/tree_element_overrides.cc b/source/blender/editors/space_outliner/tree/tree_element_overrides.cc
index 6b222e877b1..c5d254242c6 100644
--- a/source/blender/editors/space_outliner/tree/tree_element_overrides.cc
+++ b/source/blender/editors/space_outliner/tree/tree_element_overrides.cc
@@ -40,13 +40,24 @@ TreeElementOverridesBase::TreeElementOverridesBase(TreeElement &legacy_te, ID &i
: AbstractTreeElement(legacy_te), id_(id)
{
BLI_assert(legacy_te.store_elem->type == TSE_LIBRARY_OVERRIDE_BASE);
- legacy_te.name = IFACE_("Library Overrides");
+ if (legacy_te.parent != nullptr &&
+ ELEM(legacy_te.parent->store_elem->type, TSE_SOME_ID, TSE_LAYER_COLLECTION))
+
+ {
+ legacy_te.name = IFACE_("Library Overrides");
+ }
+ else {
+ legacy_te.name = id.name + 2;
+ }
}
void TreeElementOverridesBase::expand(SpaceOutliner &space_outliner) const
{
BLI_assert(id_.override_library != nullptr);
+ const bool show_system_overrides = (SUPPORT_FILTER_OUTLINER(&space_outliner) &&
+ (space_outliner.filter & SO_FILTER_SHOW_SYSTEM_OVERRIDES) !=
+ 0);
PointerRNA idpoin;
RNA_id_pointer_create(&id_, &idpoin);
@@ -56,15 +67,26 @@ void TreeElementOverridesBase::expand(SpaceOutliner &space_outliner) const
for (auto *override_prop :
ListBaseWrapper<IDOverrideLibraryProperty>(id_.override_library->properties)) {
- if (!BKE_lib_override_rna_property_find(
- &idpoin, override_prop, &override_rna_ptr, &override_rna_prop)) {
- /* This is fine, override properties list is not always fully up-to-date with current
- * RNA/IDProps etc., this gets cleaned up when re-generating the overrides rules,
- * no error here. */
- continue;
+ const bool is_rna_path_valid = BKE_lib_override_rna_property_find(
+ &idpoin, override_prop, &override_rna_ptr, &override_rna_prop);
+ if (is_rna_path_valid && !show_system_overrides &&
+ ELEM(override_prop->rna_prop_type, PROP_POINTER, PROP_COLLECTION) &&
+ RNA_struct_is_ID(RNA_property_pointer_type(&override_rna_ptr, override_rna_prop))) {
+ bool do_continue = true;
+ for (auto *override_prop_op :
+ ListBaseWrapper<IDOverrideLibraryPropertyOperation>(override_prop->operations)) {
+ if ((override_prop_op->flag & IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE) == 0) {
+ do_continue = false;
+ break;
+ }
+ }
+
+ if (do_continue) {
+ continue;
+ }
}
- TreeElementOverridesData data = {id_, *override_prop};
+ TreeElementOverridesData data = {id_, *override_prop, is_rna_path_valid};
outliner_add_element(
&space_outliner, &legacy_te_.subtree, &data, &legacy_te_, TSE_LIBRARY_OVERRIDE, index++);
}
@@ -79,6 +101,9 @@ TreeElementOverridesProperty::TreeElementOverridesProperty(TreeElement &legacy_t
BLI_assert(legacy_te.store_elem->type == TSE_LIBRARY_OVERRIDE);
legacy_te.name = override_prop_.rna_path;
+ /* Abusing this for now, better way to do it is also pending current refactor of the whole tree
+ * code to use C++. */
+ legacy_te.directdata = POINTER_FROM_UINT(override_data.is_rna_path_valid);
}
} // namespace blender::ed::outliner
diff --git a/source/blender/editors/space_outliner/tree/tree_element_overrides.hh b/source/blender/editors/space_outliner/tree/tree_element_overrides.hh
index b5c772f5b33..c3caab8e268 100644
--- a/source/blender/editors/space_outliner/tree/tree_element_overrides.hh
+++ b/source/blender/editors/space_outliner/tree/tree_element_overrides.hh
@@ -27,6 +27,7 @@ namespace blender::ed::outliner {
struct TreeElementOverridesData {
ID &id;
IDOverrideLibraryProperty &override_property;
+ bool is_rna_path_valid;
};
class TreeElementOverridesBase final : public AbstractTreeElement {
diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c
index a6e7903d1b1..ac31e0e7c37 100644
--- a/source/blender/editors/space_sequencer/sequencer_add.c
+++ b/source/blender/editors/space_sequencer/sequencer_add.c
@@ -95,6 +95,7 @@ typedef struct SequencerAddData {
#define SEQPROP_NOPATHS (1 << 2)
#define SEQPROP_NOCHAN (1 << 3)
#define SEQPROP_FIT_METHOD (1 << 4)
+#define SEQPROP_VIEW_TRANSFORM (1 << 5)
static const EnumPropertyItem scale_fit_methods[] = {
{SEQ_SCALE_TO_FIT, "FIT", 0, "Scale to Fit", "Scale image to fit within the canvas"},
@@ -152,6 +153,14 @@ static void sequencer_generic_props__internal(wmOperatorType *ot, int flag)
"Fit Method",
"Scale fit method");
}
+
+ if (flag & SEQPROP_VIEW_TRANSFORM) {
+ ot->prop = RNA_def_boolean(ot->srna,
+ "set_view_transform",
+ true,
+ "Set View Transform",
+ "Set appropriate view transform based on media colorspace");
+ }
}
static void sequencer_generic_invoke_path__internal(bContext *C,
@@ -228,7 +237,6 @@ static void load_data_init_from_operator(SeqLoadData *load_data, bContext *C, wm
PropertyRNA *prop;
const bool relative = (prop = RNA_struct_find_property(op->ptr, "relative_path")) &&
RNA_property_boolean_get(op->ptr, prop);
- int is_file = -1;
memset(load_data, 0, sizeof(SeqLoadData));
load_data->start_frame = RNA_int_get(op->ptr, "frame_start");
@@ -242,17 +250,26 @@ static void load_data_init_from_operator(SeqLoadData *load_data, bContext *C, wm
}
if ((prop = RNA_struct_find_property(op->ptr, "filepath"))) {
- /* Full path, file is set by the caller. */
RNA_property_string_get(op->ptr, prop, load_data->path);
- is_file = 1;
+ BLI_strncpy(load_data->name, BLI_path_basename(load_data->path), sizeof(load_data->name));
}
else if ((prop = RNA_struct_find_property(op->ptr, "directory"))) {
- /* Full path, file is set by the caller. */
- RNA_property_string_get(op->ptr, prop, load_data->path);
- is_file = 0;
+ char *directory = RNA_string_get_alloc(op->ptr, "directory", NULL, 0);
+
+ if ((prop = RNA_struct_find_property(op->ptr, "files"))) {
+ RNA_PROP_BEGIN (op->ptr, itemptr, prop) {
+ char *filename = RNA_string_get_alloc(&itemptr, "name", NULL, 0);
+ BLI_strncpy(load_data->name, filename, sizeof(load_data->name));
+ BLI_snprintf(load_data->path, sizeof(load_data->path), "%s%s", directory, filename);
+ MEM_freeN(filename);
+ break;
+ }
+ RNA_PROP_END;
+ }
+ MEM_freeN(directory);
}
- if ((is_file != -1) && relative) {
+ if (relative) {
BLI_path_rel(load_data->path, BKE_main_blendfile_path(bmain));
}
@@ -276,17 +293,9 @@ static void load_data_init_from_operator(SeqLoadData *load_data, bContext *C, wm
load_data->flags |= SEQ_LOAD_MOVIE_SYNC_FPS;
}
- if (is_file == 1) {
- BLI_strncpy(load_data->name, BLI_path_basename(load_data->path), sizeof(load_data->name));
- }
- else if ((prop = RNA_struct_find_property(op->ptr, "files"))) {
- RNA_PROP_BEGIN (op->ptr, itemptr, prop) {
- char *name = RNA_string_get_alloc(&itemptr, "name", NULL, 0);
- BLI_strncpy(load_data->name, name, sizeof(load_data->name));
- MEM_freeN(name);
- break;
- }
- RNA_PROP_END;
+ if ((prop = RNA_struct_find_property(op->ptr, "set_view_transform")) &&
+ RNA_property_boolean_get(op->ptr, prop)) {
+ load_data->flags |= SEQ_LOAD_SET_VIEW_TRANSFORM;
}
if ((prop = RNA_struct_find_property(op->ptr, "use_multiview")) &&
@@ -373,8 +382,23 @@ static int sequencer_add_scene_strip_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
+static void sequencer_disable_one_time_properties(bContext *C, wmOperator *op)
+{
+ Editing *ed = SEQ_editing_get(CTX_data_scene(C), false);
+ /* Disable following properties if there are any existing strips, unless overridden by user. */
+ if (ed && ed->seqbasep && ed->seqbasep->first) {
+ if (RNA_struct_find_property(op->ptr, "use_framerate")) {
+ RNA_boolean_set(op->ptr, "use_framerate", false);
+ }
+ if (RNA_struct_find_property(op->ptr, "set_view_transform")) {
+ RNA_boolean_set(op->ptr, "set_view_transform", false);
+ }
+ }
+}
+
static int sequencer_add_scene_strip_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
+ sequencer_disable_one_time_properties(C, op);
if (!RNA_struct_property_is_set(op->ptr, "scene")) {
return WM_enum_search_invoke(C, op, event);
}
@@ -707,13 +731,9 @@ static int sequencer_add_movie_strip_invoke(bContext *C,
{
PropertyRNA *prop;
Scene *scene = CTX_data_scene(C);
- Editing *ed = SEQ_editing_get(scene, false);
- /* Only enable "use_framerate" if there aren't any existing strips, unless overridden by user.
- */
- if (ed && ed->seqbasep && ed->seqbasep->first) {
- RNA_boolean_set(op->ptr, "use_framerate", false);
- }
+ sequencer_disable_one_time_properties(C, op);
+
RNA_enum_set(op->ptr, "fit_method", SEQ_tool_settings_fit_method_get(scene));
/* This is for drag and drop. */
@@ -739,12 +759,11 @@ static void sequencer_add_draw(bContext *UNUSED(C), wmOperator *op)
uiLayout *layout = op->layout;
SequencerAddData *sad = op->customdata;
ImageFormatData *imf = &sad->im_format;
- PointerRNA imf_ptr, ptr;
+ PointerRNA imf_ptr;
/* Main draw call. */
- RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
uiDefAutoButsRNA(
- layout, &ptr, sequencer_add_draw_check_fn, NULL, NULL, UI_BUT_LABEL_ALIGN_NONE, false);
+ layout, op->ptr, sequencer_add_draw_check_fn, NULL, NULL, UI_BUT_LABEL_ALIGN_NONE, false);
/* Image template. */
RNA_pointer_create(NULL, &RNA_ImageFormatSettings, imf, &imf_ptr);
@@ -781,7 +800,8 @@ void SEQUENCER_OT_movie_strip_add(struct wmOperatorType *ot)
WM_FILESEL_SHOW_PROPS | WM_FILESEL_DIRECTORY,
FILE_DEFAULTDISPLAY,
FILE_SORT_DEFAULT);
- sequencer_generic_props__internal(ot, SEQPROP_STARTFRAME | SEQPROP_FIT_METHOD);
+ sequencer_generic_props__internal(
+ ot, SEQPROP_STARTFRAME | SEQPROP_FIT_METHOD | SEQPROP_VIEW_TRANSFORM);
RNA_def_boolean(ot->srna, "sound", true, "Sound", "Load sound with the movie");
RNA_def_boolean(ot->srna,
"use_framerate",
@@ -990,8 +1010,10 @@ static void sequencer_add_image_strip_load_files(
wmOperator *op, Sequence *seq, SeqLoadData *load_data, const int minframe, const int numdigits)
{
const bool use_placeholders = RNA_boolean_get(op->ptr, "use_placeholders");
-
- SEQ_add_image_set_directory(seq, load_data->path);
+ /* size of Strip->dir. */
+ char directory[768];
+ BLI_split_dir_part(load_data->path, directory, sizeof(directory));
+ SEQ_add_image_set_directory(seq, directory);
if (use_placeholders) {
sequencer_image_seq_reserve_frames(
@@ -1057,8 +1079,9 @@ static int sequencer_add_image_strip_invoke(bContext *C,
PropertyRNA *prop;
Scene *scene = CTX_data_scene(C);
- const SequencerToolSettings *tool_settings = scene->toolsettings->sequencer_tool_settings;
- RNA_enum_set(op->ptr, "fit_method", tool_settings->fit_method);
+ sequencer_disable_one_time_properties(C, op);
+
+ RNA_enum_set(op->ptr, "fit_method", SEQ_tool_settings_fit_method_get(scene));
/* Name set already by drag and drop. */
if (RNA_struct_property_is_set(op->ptr, "files") && RNA_collection_length(op->ptr, "files")) {
@@ -1104,8 +1127,8 @@ void SEQUENCER_OT_image_strip_add(struct wmOperatorType *ot)
WM_FILESEL_SHOW_PROPS | WM_FILESEL_DIRECTORY,
FILE_DEFAULTDISPLAY,
FILE_SORT_DEFAULT);
- sequencer_generic_props__internal(ot,
- SEQPROP_STARTFRAME | SEQPROP_ENDFRAME | SEQPROP_FIT_METHOD);
+ sequencer_generic_props__internal(
+ ot, SEQPROP_STARTFRAME | SEQPROP_ENDFRAME | SEQPROP_FIT_METHOD | SEQPROP_VIEW_TRANSFORM);
RNA_def_boolean(ot->srna,
"use_placeholders",
diff --git a/source/blender/editors/space_sequencer/sequencer_buttons.c b/source/blender/editors/space_sequencer/sequencer_buttons.c
index 11614d94862..1e0ecfd890e 100644
--- a/source/blender/editors/space_sequencer/sequencer_buttons.c
+++ b/source/blender/editors/space_sequencer/sequencer_buttons.c
@@ -111,10 +111,10 @@ void sequencer_buttons_register(ARegionType *art)
pt = MEM_callocN(sizeof(PanelType), "spacetype sequencer panel metadata");
strcpy(pt->idname, "SEQUENCER_PT_metadata");
strcpy(pt->label, N_("Metadata"));
+ strcpy(pt->category, "Metadata");
strcpy(pt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA);
pt->poll = metadata_panel_context_poll;
pt->draw = metadata_panel_context_draw;
- pt->flag |= PANEL_TYPE_DEFAULT_CLOSED;
pt->order = 10;
BLI_addtail(&art->paneltypes, pt);
}
diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c
index e4afb27dd2e..5f831cbf535 100644
--- a/source/blender/editors/space_sequencer/sequencer_draw.c
+++ b/source/blender/editors/space_sequencer/sequencer_draw.c
@@ -105,8 +105,6 @@
* it messes up transform. */
#undef SEQ_ALL_BEGIN
#undef SEQ_ALL_END
-#undef SEQ_CURRENT_BEGIN
-#undef SEQ_CURRENT_END
static Sequence *special_seq_update = NULL;
@@ -872,9 +870,9 @@ static void draw_seq_background(Scene *scene,
/* Draw the main strip body. */
if (is_single_image) {
immRectf(pos,
- SEQ_transform_get_left_handle_frame(seq, false),
+ SEQ_transform_get_left_handle_frame(seq),
y1,
- SEQ_transform_get_right_handle_frame(seq, false),
+ SEQ_transform_get_right_handle_frame(seq),
y2);
}
else {
@@ -887,10 +885,12 @@ static void draw_seq_background(Scene *scene,
immUniformColor4ubv(col);
if (seq->startstill) {
- immRectf(pos, seq->startdisp, y1, (float)(seq->start), y2);
+ const float content_start = min_ff(seq->enddisp, seq->start);
+ immRectf(pos, seq->startdisp, y1, content_start, y2);
}
if (seq->endstill) {
- immRectf(pos, (float)(seq->start + seq->len), y1, seq->enddisp, y2);
+ const float content_end = max_ff(seq->startdisp, seq->start + seq->len);
+ immRectf(pos, content_end, y1, seq->enddisp, y2);
}
}
@@ -1107,6 +1107,10 @@ static void draw_seq_strip(const bContext *C,
x2 = (seq->endstill) ? (seq->start + seq->len) : seq->enddisp;
y2 = seq->machine + SEQ_STRIP_OFSTOP;
+ /* Limit body to strip bounds. Meta strip can end up with content outside of strip range. */
+ x1 = min_ff(x1, seq->enddisp);
+ x2 = max_ff(x2, seq->startdisp);
+
float text_margin_y;
bool y_threshold;
if ((sseq->flag & SEQ_SHOW_STRIP_NAME) || (sseq->flag & SEQ_SHOW_STRIP_SOURCE) ||
@@ -1524,10 +1528,10 @@ static void *sequencer_OCIO_transform_ibuf(const bContext *C,
ImBuf *ibuf,
bool *r_glsl_used,
eGPUTextureFormat *r_format,
- eGPUDataFormat *r_data)
+ eGPUDataFormat *r_data,
+ void **r_buffer_cache_handle)
{
void *display_buffer;
- void *cache_handle = NULL;
bool force_fallback = false;
*r_glsl_used = false;
force_fallback |= (ED_draw_imbuf_method(ibuf) != IMAGE_DRAW_METHOD_GLSL);
@@ -1580,13 +1584,10 @@ static void *sequencer_OCIO_transform_ibuf(const bContext *C,
/* There is data to be displayed, but GLSL is not initialized
* properly, in this case we fallback to CPU-based display transform. */
if ((ibuf->rect || ibuf->rect_float) && !*r_glsl_used) {
- display_buffer = IMB_display_buffer_acquire_ctx(C, ibuf, &cache_handle);
+ display_buffer = IMB_display_buffer_acquire_ctx(C, ibuf, r_buffer_cache_handle);
*r_format = GPU_RGBA8;
*r_data = GPU_DATA_UBYTE;
}
- if (cache_handle) {
- IMB_display_buffer_release(cache_handle);
- }
return display_buffer;
}
@@ -1660,6 +1661,7 @@ static void sequencer_draw_display_buffer(const bContext *C,
bool draw_backdrop)
{
void *display_buffer;
+ void *buffer_cache_handle = NULL;
if (sseq->mainb == SEQ_DRAW_IMG_IMBUF && sseq->flag & SEQ_USE_ALPHA) {
GPU_blend(GPU_BLEND_ALPHA);
@@ -1687,7 +1689,8 @@ static void sequencer_draw_display_buffer(const bContext *C,
data = GPU_DATA_UBYTE;
}
else {
- display_buffer = sequencer_OCIO_transform_ibuf(C, ibuf, &glsl_used, &format, &data);
+ display_buffer = sequencer_OCIO_transform_ibuf(
+ C, ibuf, &glsl_used, &format, &data, &buffer_cache_handle);
}
if (draw_backdrop) {
@@ -1747,6 +1750,10 @@ static void sequencer_draw_display_buffer(const bContext *C,
IMB_colormanagement_finish_glsl_draw();
}
+ if (buffer_cache_handle) {
+ IMB_display_buffer_release(buffer_cache_handle);
+ }
+
if (sseq->mainb == SEQ_DRAW_IMG_IMBUF && sseq->flag & SEQ_USE_ALPHA) {
GPU_blend(GPU_BLEND_NONE);
}
@@ -2358,6 +2365,31 @@ static void draw_cache_view(const bContext *C)
}
/* Draw sequencer timeline. */
+static void draw_overlap_frame_indicator(const struct Scene *scene, const View2D *v2d)
+{
+ int overlap_frame = (scene->ed->over_flag & SEQ_EDIT_OVERLAY_ABS) ?
+ scene->ed->over_cfra :
+ scene->r.cfra + scene->ed->over_ofs;
+
+ uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
+ float viewport_size[4];
+ GPU_viewport_size_get_f(viewport_size);
+ immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
+ /* Shader may have color set from past usage - reset it. */
+ immUniform1i("colors_len", 0);
+ immUniform1f("dash_width", 20.0f * U.pixelsize);
+ immUniform1f("dash_factor", 0.5f);
+ immUniformThemeColor(TH_CFRAME);
+
+ immBegin(GPU_PRIM_LINES, 2);
+ immVertex2f(pos, overlap_frame, v2d->cur.ymin);
+ immVertex2f(pos, overlap_frame, v2d->cur.ymax);
+ immEnd();
+
+ immUnbindProgram();
+}
+
void draw_timeline_seq(const bContext *C, ARegion *region)
{
Scene *scene = CTX_data_scene(C);
@@ -2417,31 +2449,6 @@ void draw_timeline_seq(const bContext *C, ARegion *region)
cfra_flag |= DRAWCFRA_UNIT_SECONDS;
}
- /* Draw overlap frame frame indicator. */
- if (scene->ed && scene->ed->over_flag & SEQ_EDIT_OVERLAY_SHOW) {
- int overlap_frame = (scene->ed->over_flag & SEQ_EDIT_OVERLAY_ABS) ?
- scene->ed->over_cfra :
- scene->r.cfra + scene->ed->over_ofs;
-
- uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
- float viewport_size[4];
- GPU_viewport_size_get_f(viewport_size);
- immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
- /* Shader may have color set from past usage - reset it. */
- immUniform1i("colors_len", 0);
- immUniform1f("dash_width", 20.0f * U.pixelsize);
- immUniform1f("dash_factor", 0.5f);
- immUniformThemeColor(TH_CFRAME);
-
- immBegin(GPU_PRIM_LINES, 2);
- immVertex2f(pos, overlap_frame, v2d->cur.ymin);
- immVertex2f(pos, overlap_frame, v2d->cur.ymax);
- immEnd();
-
- immUnbindProgram();
- }
-
UI_view2d_view_orthoSpecial(region, v2d, 1);
int marker_draw_flag = DRAW_MARKERS_MARGIN;
if (sseq->flag & SEQ_SHOW_MARKERS) {
@@ -2449,11 +2456,6 @@ void draw_timeline_seq(const bContext *C, ARegion *region)
}
UI_view2d_view_ortho(v2d);
-
- if (ed) {
- draw_cache_view(C);
- }
-
ANIM_draw_previewrange(C, v2d, 1);
/* Draw registered callbacks. */
@@ -2479,6 +2481,13 @@ void draw_timeline_seq_display(const bContext *C, ARegion *region)
const SpaceSeq *sseq = CTX_wm_space_seq(C);
View2D *v2d = &region->v2d;
+ if (scene->ed && scene->ed->over_flag & SEQ_EDIT_OVERLAY_SHOW) {
+ UI_view2d_view_ortho(v2d);
+ draw_cache_view(C);
+ draw_overlap_frame_indicator(scene, v2d);
+ UI_view2d_view_restore(C);
+ }
+
ED_time_scrub_draw_current_frame(region, scene, !(sseq->flag & SEQ_DRAWFRAMES), true);
UI_view2d_scrollers_draw(v2d, NULL);
}
diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c
index 78d263dffad..1c9b3676b19 100644
--- a/source/blender/editors/space_sequencer/sequencer_edit.c
+++ b/source/blender/editors/space_sequencer/sequencer_edit.c
@@ -34,6 +34,7 @@
#include "BLT_translation.h"
+#include "DNA_anim_types.h"
#include "DNA_scene_types.h"
#include "DNA_sound_types.h"
@@ -138,6 +139,42 @@ bool ED_space_sequencer_check_show_strip(SpaceSeq *sseq)
ELEM(sseq->mainb, SEQ_DRAW_SEQUENCE, SEQ_DRAW_IMG_IMBUF));
}
+static bool sequencer_fcurves_targets_color_strip(const FCurve *fcurve)
+{
+ if (!BLI_str_startswith(fcurve->rna_path, "sequence_editor.sequences_all[\"")) {
+ return false;
+ }
+
+ if (!BLI_str_endswith(fcurve->rna_path, "\"].color")) {
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Check if there is animation attached to a strip, that is shown on the strip in the UI.
+ *
+ * - Colors of color strips are displayed on the strip itself.
+ */
+bool ED_space_sequencer_has_visible_animation_on_strip(const struct Scene *scene)
+{
+ if (!scene->adt) {
+ return false;
+ }
+ if (!scene->adt->action) {
+ return false;
+ }
+
+ LISTBASE_FOREACH (FCurve *, fcurve, &scene->adt->action->curves) {
+ if (sequencer_fcurves_targets_color_strip(fcurve)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -290,7 +327,7 @@ static int sequencer_snap_exec(bContext *C, wmOperator *op)
snap_frame = RNA_int_get(op->ptr, "frame");
- /* Check metas. */
+ /* Check meta-strips. */
for (seq = ed->seqbasep->first; seq; seq = seq->next) {
if (seq->flag & SELECT && !(seq->depth == 0 && seq->flag & SEQ_LOCK) &&
SEQ_transform_sequence_can_be_translated(seq)) {
@@ -312,8 +349,7 @@ static int sequencer_snap_exec(bContext *C, wmOperator *op)
}
}
- /* Test for effects and overlap.
- * Don't use SEQ_CURRENT_BEGIN since that would be recursive. */
+ /* Test for effects and overlap. */
for (seq = ed->seqbasep->first; seq; seq = seq->next) {
if (seq->flag & SELECT && !(seq->depth == 0 && seq->flag & SEQ_LOCK)) {
seq->flag &= ~SEQ_OVERLAP;
@@ -349,7 +385,7 @@ static int sequencer_snap_exec(bContext *C, wmOperator *op)
}
}
- SEQ_sort(scene);
+ SEQ_sort(SEQ_active_seqbase_get(ed));
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
@@ -558,7 +594,7 @@ static bool sequencer_slip_recursively(Scene *scene, SlipData *data, int offset)
seq->endofs = endframe - seq->enddisp;
changed = true;
}
- else if (endframe <= seq->enddisp) {
+ else {
seq->endstill = seq->enddisp - endframe;
seq->endofs = 0;
changed = true;
@@ -569,7 +605,7 @@ static bool sequencer_slip_recursively(Scene *scene, SlipData *data, int offset)
seq->startofs = 0;
changed = true;
}
- else if (seq->start <= seq->startdisp) {
+ else {
seq->startstill = 0;
seq->startofs = seq->startdisp - seq->start;
changed = true;
@@ -1411,28 +1447,25 @@ static int sequencer_split_exec(bContext *C, wmOperator *op)
}
if (changed) { /* Got new strips? */
- Sequence *seq;
if (ignore_selection) {
if (use_cursor_position) {
- SEQ_CURRENT_BEGIN (ed, seq) {
+ LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
if (seq->enddisp == split_frame && seq->machine == split_channel) {
seq_selected = seq->flag & SEQ_ALLSEL;
}
}
- SEQ_CURRENT_END;
if (!seq_selected) {
- SEQ_CURRENT_BEGIN (ed, seq) {
+ LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
if (seq->startdisp == split_frame && seq->machine == split_channel) {
seq->flag &= ~SEQ_ALLSEL;
}
}
- SEQ_CURRENT_END;
}
}
}
else {
if (split_side != SEQ_SIDE_BOTH) {
- SEQ_CURRENT_BEGIN (ed, seq) {
+ LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
if (split_side == SEQ_SIDE_LEFT) {
if (seq->startdisp >= split_frame) {
seq->flag &= ~SEQ_ALLSEL;
@@ -1444,11 +1477,10 @@ static int sequencer_split_exec(bContext *C, wmOperator *op)
}
}
}
- SEQ_CURRENT_END;
}
}
- SEQ_sort(scene);
+ SEQ_sort(SEQ_active_seqbase_get(ed));
}
if (changed) {
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
@@ -1496,19 +1528,16 @@ static void sequencer_split_ui(bContext *UNUSED(C), wmOperator *op)
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
- PointerRNA ptr;
- RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
-
uiLayout *row = uiLayoutRow(layout, false);
- uiItemR(row, &ptr, "type", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "frame", 0, NULL, ICON_NONE);
- uiItemR(layout, &ptr, "side", 0, NULL, ICON_NONE);
+ uiItemR(row, op->ptr, "type", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "frame", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "side", 0, NULL, ICON_NONE);
uiItemS(layout);
- uiItemR(layout, &ptr, "use_cursor_position", 0, NULL, ICON_NONE);
- if (RNA_boolean_get(&ptr, "use_cursor_position")) {
- uiItemR(layout, &ptr, "channel", 0, NULL, ICON_NONE);
+ uiItemR(layout, op->ptr, "use_cursor_position", 0, NULL, ICON_NONE);
+ if (RNA_boolean_get(op->ptr, "use_cursor_position")) {
+ uiItemR(layout, op->ptr, "channel", 0, NULL, ICON_NONE);
}
}
@@ -1585,38 +1614,35 @@ void SEQUENCER_OT_split(struct wmOperatorType *ot)
/** \name Duplicate Strips Operator
* \{ */
-static int apply_unique_name_fn(Sequence *seq, void *arg_pt)
-{
- Scene *scene = (Scene *)arg_pt;
- char name[sizeof(seq->name) - 2];
-
- BLI_strncpy_utf8(name, seq->name + 2, sizeof(name));
- SEQ_sequence_base_unique_name_recursive(&scene->ed->seqbase, seq);
- SEQ_dupe_animdata(scene, name, seq->name + 2);
- return 1;
-}
-
static int sequencer_add_duplicate_exec(bContext *C, wmOperator *UNUSED(op))
{
Scene *scene = CTX_data_scene(C);
Editing *ed = SEQ_editing_get(scene, false);
- ListBase nseqbase = {NULL, NULL};
-
if (ed == NULL) {
return OPERATOR_CANCELLED;
}
- SEQ_sequence_base_dupli_recursive(scene, scene, &nseqbase, ed->seqbasep, SEQ_DUPE_CONTEXT, 0);
+ Sequence *active_seq = SEQ_select_active_get(scene);
+ ListBase duplicated = {NULL, NULL};
- if (nseqbase.first) {
- Sequence *seq = nseqbase.first;
+ SEQ_sequence_base_dupli_recursive(scene, scene, &duplicated, ed->seqbasep, 0, 0);
+ ED_sequencer_deselect_all(scene);
+
+ if (duplicated.first) {
+ Sequence *seq = duplicated.first;
/* Rely on the nseqbase list being added at the end.
* Their UUIDs has been re-generated by the SEQ_sequence_base_dupli_recursive(), */
- BLI_movelisttolist(ed->seqbasep, &nseqbase);
+ BLI_movelisttolist(ed->seqbasep, &duplicated);
+ /* Handle duplicated strips: set active, select, ensure unique name and duplicate animation
+ * data. */
for (; seq; seq = seq->next) {
- SEQ_iterator_recursive_apply(seq, apply_unique_name_fn, scene);
+ if (active_seq != NULL && STREQ(seq->name, active_seq->name)) {
+ SEQ_select_active_set(scene, seq);
+ }
+ seq->flag &= ~(SEQ_LEFTSEL + SEQ_RIGHTSEL + SEQ_LOCK);
+ SEQ_ensure_unique_name(seq, scene);
}
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
@@ -1652,16 +1678,14 @@ static int sequencer_delete_exec(bContext *C, wmOperator *UNUSED(op))
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
Editing *ed = SEQ_editing_get(scene, false);
- Sequence *seq;
SEQ_prefetch_stop(scene);
- SEQ_CURRENT_BEGIN (scene->ed, seq) {
+ LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
if (seq->flag & SELECT) {
SEQ_edit_flag_for_removal(scene, ed->seqbasep, seq);
}
}
- SEQ_CURRENT_END;
SEQ_edit_remove_flagged_sequences(scene, ed->seqbasep);
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
@@ -1789,8 +1813,8 @@ static int sequencer_separate_images_exec(bContext *C, wmOperator *op)
/* if (seq->ipo) id_us_min(&seq->ipo->id); */
/* XXX, remove fcurve and assign to split image strips */
- start_ofs = timeline_frame = SEQ_transform_get_left_handle_frame(seq, false);
- frame_end = SEQ_transform_get_right_handle_frame(seq, false);
+ start_ofs = timeline_frame = SEQ_transform_get_left_handle_frame(seq);
+ frame_end = SEQ_transform_get_right_handle_frame(seq);
while (timeline_frame < frame_end) {
/* New seq. */
@@ -1839,7 +1863,7 @@ static int sequencer_separate_images_exec(bContext *C, wmOperator *op)
}
}
- SEQ_sort(scene);
+ SEQ_sort(SEQ_active_seqbase_get(ed));
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
@@ -1877,13 +1901,13 @@ static int sequencer_meta_toggle_exec(bContext *C, wmOperator *UNUSED(op))
Sequence *active_seq = SEQ_select_active_get(scene);
if (active_seq && active_seq->type == SEQ_TYPE_META && active_seq->flag & SELECT) {
- /* Enter metastrip. */
+ /* Enter meta-strip. */
SEQ_meta_stack_alloc(ed, active_seq);
SEQ_seqbase_active_set(ed, &active_seq->seqbase);
SEQ_select_active_set(scene, NULL);
}
else {
- /* Exit metastrip if possible. */
+ /* Exit meta-strip if possible. */
if (BLI_listbase_is_empty(&ed->metastack)) {
return OPERATOR_CANCELLED;
}
@@ -1905,7 +1929,7 @@ void SEQUENCER_OT_meta_toggle(wmOperatorType *ot)
/* Identifiers. */
ot->name = "Toggle Meta Strip";
ot->idname = "SEQUENCER_OT_meta_toggle";
- ot->description = "Toggle a metastrip (to edit enclosed strips)";
+ ot->description = "Toggle a meta-strip (to edit enclosed strips)";
/* Api callbacks. */
ot->exec = sequencer_meta_toggle_exec;
@@ -1973,7 +1997,7 @@ void SEQUENCER_OT_meta_make(wmOperatorType *ot)
/* Identifiers. */
ot->name = "Make Meta Strip";
ot->idname = "SEQUENCER_OT_meta_make";
- ot->description = "Group selected strips into a metastrip";
+ ot->description = "Group selected strips into a meta-strip";
/* Api callbacks. */
ot->exec = sequencer_meta_make_exec;
@@ -2024,7 +2048,7 @@ static int sequencer_meta_separate_exec(bContext *C, wmOperator *UNUSED(op))
}
}
- SEQ_sort(scene);
+ SEQ_sort(active_seqbase);
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
@@ -2036,7 +2060,7 @@ void SEQUENCER_OT_meta_separate(wmOperatorType *ot)
/* Identifiers. */
ot->name = "UnMeta Strip";
ot->idname = "SEQUENCER_OT_meta_separate";
- ot->description = "Put the contents of a metastrip back in the sequencer";
+ ot->description = "Put the contents of a meta-strip back in the sequencer";
/* Api callbacks. */
ot->exec = sequencer_meta_separate_exec;
@@ -2247,7 +2271,7 @@ static int sequencer_swap_exec(bContext *C, wmOperator *op)
}
}
- SEQ_sort(scene);
+ SEQ_sort(SEQ_active_seqbase_get(ed));
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
@@ -2284,43 +2308,45 @@ void SEQUENCER_OT_swap(wmOperatorType *ot)
static int sequencer_rendersize_exec(bContext *C, wmOperator *UNUSED(op))
{
- int retval = OPERATOR_CANCELLED;
Scene *scene = CTX_data_scene(C);
Sequence *active_seq = SEQ_select_active_get(scene);
StripElem *se = NULL;
- if (active_seq == NULL) {
+ if (active_seq == NULL || active_seq->strip == NULL) {
return OPERATOR_CANCELLED;
}
- if (active_seq->strip) {
- switch (active_seq->type) {
- case SEQ_TYPE_IMAGE:
- se = SEQ_render_give_stripelem(active_seq, scene->r.cfra);
- break;
- case SEQ_TYPE_MOVIE:
- se = active_seq->strip->stripdata;
- break;
- case SEQ_TYPE_SCENE:
- case SEQ_TYPE_META:
- case SEQ_TYPE_SOUND_RAM:
- case SEQ_TYPE_SOUND_HD:
- default:
- break;
- }
+ switch (active_seq->type) {
+ case SEQ_TYPE_IMAGE:
+ se = SEQ_render_give_stripelem(active_seq, scene->r.cfra);
+ break;
+ case SEQ_TYPE_MOVIE:
+ se = active_seq->strip->stripdata;
+ break;
+ default:
+ return OPERATOR_CANCELLED;
}
- if (se) {
- /* Prevent setting the render size if sequence values aren't initialized. */
- if ((se->orig_width > 0) && (se->orig_height > 0)) {
- scene->r.xsch = se->orig_width;
- scene->r.ysch = se->orig_height;
- WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, scene);
- retval = OPERATOR_FINISHED;
- }
+ if (se == NULL) {
+ return OPERATOR_CANCELLED;
+ }
+
+ /* Prevent setting the render size if sequence values aren't initialized. */
+ if (se->orig_width <= 0 || se->orig_height <= 0) {
+ return OPERATOR_CANCELLED;
}
- return retval;
+ scene->r.xsch = se->orig_width;
+ scene->r.ysch = se->orig_height;
+
+ active_seq->strip->transform->scale_x = active_seq->strip->transform->scale_y = 1.0f;
+ active_seq->strip->transform->xofs = active_seq->strip->transform->yofs = 0.0f;
+
+ SEQ_relations_invalidate_cache_preprocessed(scene, active_seq);
+ WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, scene);
+ WM_event_add_notifier(C, NC_SPACE | ND_SPACE_SEQUENCER, NULL);
+
+ return OPERATOR_FINISHED;
}
void SEQUENCER_OT_rendersize(wmOperatorType *ot)
@@ -2418,17 +2444,15 @@ void SEQUENCER_OT_copy(wmOperatorType *ot)
void ED_sequencer_deselect_all(Scene *scene)
{
- Sequence *seq;
Editing *ed = SEQ_editing_get(scene, false);
if (ed == NULL) {
return;
}
- SEQ_CURRENT_BEGIN (ed, seq) {
+ LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
seq->flag &= ~SEQ_ALLSEL;
}
- SEQ_CURRENT_END;
}
static int sequencer_paste_exec(bContext *C, wmOperator *op)
@@ -2475,7 +2499,7 @@ static int sequencer_paste_exec(bContext *C, wmOperator *op)
for (iseq = iseq_first; iseq; iseq = iseq->next) {
/* Make sure, that pasted strips have unique names. */
- SEQ_iterator_recursive_apply(iseq, apply_unique_name_fn, scene);
+ SEQ_ensure_unique_name(iseq, scene);
/* Translate after name has been changed, otherwise this will affect animdata of original
* strip. */
SEQ_transform_translate_sequence(scene, iseq, ofs);
@@ -3074,7 +3098,7 @@ static int sequencer_set_range_to_strips_exec(bContext *C, wmOperator *op)
scene->r.efra = efra;
}
- WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
+ WM_event_add_notifier(C, NC_SCENE | ND_FRAME_RANGE, scene);
return OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/space_sequencer/sequencer_modifier.c b/source/blender/editors/space_sequencer/sequencer_modifier.c
index f11a879912c..9b3ecacceb9 100644
--- a/source/blender/editors/space_sequencer/sequencer_modifier.c
+++ b/source/blender/editors/space_sequencer/sequencer_modifier.c
@@ -230,14 +230,13 @@ static int strip_modifier_copy_exec(bContext *C, wmOperator *op)
Scene *scene = CTX_data_scene(C);
Editing *ed = scene->ed;
Sequence *seq = SEQ_select_active_get(scene);
- Sequence *seq_iter;
const int type = RNA_enum_get(op->ptr, "type");
if (!seq || !seq->modifiers.first) {
return OPERATOR_CANCELLED;
}
- SEQ_CURRENT_BEGIN (ed, seq_iter) {
+ LISTBASE_FOREACH (Sequence *, seq_iter, SEQ_active_seqbase_get(ed)) {
if (seq_iter->flag & SELECT) {
if (seq_iter == seq) {
continue;
@@ -259,7 +258,6 @@ static int strip_modifier_copy_exec(bContext *C, wmOperator *op)
SEQ_modifier_list_copy(seq_iter, seq);
}
}
- SEQ_CURRENT_END;
SEQ_relations_invalidate_cache_preprocessed(scene, seq);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
diff --git a/source/blender/editors/space_sequencer/sequencer_proxy.c b/source/blender/editors/space_sequencer/sequencer_proxy.c
index e44afde371a..2dcc2d389d9 100644
--- a/source/blender/editors/space_sequencer/sequencer_proxy.c
+++ b/source/blender/editors/space_sequencer/sequencer_proxy.c
@@ -58,7 +58,6 @@ static void seq_proxy_build_job(const bContext *C, ReportList *reports)
Scene *scene = CTX_data_scene(C);
Editing *ed = SEQ_editing_get(scene, false);
ScrArea *area = CTX_wm_area(C);
- Sequence *seq;
if (ed == NULL) {
return;
@@ -70,7 +69,7 @@ static void seq_proxy_build_job(const bContext *C, ReportList *reports)
GSet *file_list = BLI_gset_new(BLI_ghashutil_strhash_p, BLI_ghashutil_strcmp, "file list");
bool selected = false; /* Check for no selected strips */
- SEQ_CURRENT_BEGIN (ed, seq) {
+ LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
if (!ELEM(seq->type, SEQ_TYPE_MOVIE, SEQ_TYPE_IMAGE) || (seq->flag & SELECT) == 0) {
continue;
}
@@ -92,7 +91,6 @@ static void seq_proxy_build_job(const bContext *C, ReportList *reports)
BKE_reportf(reports, RPT_WARNING, "Overwrite is not checked for %s, skipping", seq->name);
}
}
- SEQ_CURRENT_END;
BLI_gset_free(file_list, MEM_freeN);
@@ -101,7 +99,7 @@ static void seq_proxy_build_job(const bContext *C, ReportList *reports)
return;
}
- if (selected && !WM_jobs_is_running(wm_job)) {
+ if (!WM_jobs_is_running(wm_job)) {
G.is_break = false;
WM_jobs_start(CTX_wm_manager(C), wm_job);
}
@@ -124,7 +122,6 @@ static int sequencer_rebuild_proxy_exec(bContext *C, wmOperator *UNUSED(op))
struct Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Scene *scene = CTX_data_scene(C);
Editing *ed = SEQ_editing_get(scene, false);
- Sequence *seq;
GSet *file_list;
if (ed == NULL) {
@@ -133,7 +130,7 @@ static int sequencer_rebuild_proxy_exec(bContext *C, wmOperator *UNUSED(op))
file_list = BLI_gset_new(BLI_ghashutil_strhash_p, BLI_ghashutil_strcmp, "file list");
- SEQ_CURRENT_BEGIN (ed, seq) {
+ LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
if ((seq->flag & SELECT)) {
ListBase queue = {NULL, NULL};
LinkData *link;
@@ -150,7 +147,6 @@ static int sequencer_rebuild_proxy_exec(bContext *C, wmOperator *UNUSED(op))
SEQ_relations_free_imbuf(scene, &ed->seqbase, false);
}
}
- SEQ_CURRENT_END;
BLI_gset_free(file_list, MEM_freeN);
@@ -189,7 +185,6 @@ static int sequencer_enable_proxies_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
Editing *ed = SEQ_editing_get(scene, false);
- Sequence *seq;
bool proxy_25 = RNA_boolean_get(op->ptr, "proxy_25");
bool proxy_50 = RNA_boolean_get(op->ptr, "proxy_50");
bool proxy_75 = RNA_boolean_get(op->ptr, "proxy_75");
@@ -201,7 +196,7 @@ static int sequencer_enable_proxies_exec(bContext *C, wmOperator *op)
turnon = false;
}
- SEQ_CURRENT_BEGIN (ed, seq) {
+ LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
if ((seq->flag & SELECT)) {
if (ELEM(seq->type, SEQ_TYPE_MOVIE, SEQ_TYPE_IMAGE)) {
SEQ_proxy_set(seq, turnon);
@@ -246,7 +241,6 @@ static int sequencer_enable_proxies_exec(bContext *C, wmOperator *op)
}
}
}
- SEQ_CURRENT_END;
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c
index 5f0a18fbd0b..7e515271b13 100644
--- a/source/blender/editors/space_sequencer/sequencer_select.c
+++ b/source/blender/editors/space_sequencer/sequencer_select.c
@@ -26,7 +26,6 @@
#include <string.h>
#include "BLI_blenlib.h"
-#include "BLI_ghash.h"
#include "BLI_math.h"
#include "BLI_utildefines.h"
@@ -43,6 +42,7 @@
#include "SEQ_iterator.h"
#include "SEQ_select.h"
#include "SEQ_sequencer.h"
+#include "SEQ_time.h"
#include "SEQ_transform.h"
/* For menu, popup, icons, etc. */
@@ -548,14 +548,14 @@ static int sequencer_select_exec(bContext *C, wmOperator *op)
const float x = UI_view2d_region_to_view_x(v2d, mval[0]);
- SEQ_CURRENT_BEGIN (ed, seq) {
- if (((x < CFRA) && (seq->enddisp <= CFRA)) || ((x >= CFRA) && (seq->startdisp >= CFRA))) {
+ LISTBASE_FOREACH (Sequence *, seq_iter, SEQ_active_seqbase_get(ed)) {
+ if (((x < CFRA) && (seq_iter->enddisp <= CFRA)) ||
+ ((x >= CFRA) && (seq_iter->startdisp >= CFRA))) {
/* Select left or right. */
- seq->flag |= SELECT;
- recurs_sel_seq(seq);
+ seq_iter->flag |= SELECT;
+ recurs_sel_seq(seq_iter);
}
}
- SEQ_CURRENT_END;
{
SpaceSeq *sseq = CTX_wm_space_seq(C);
@@ -808,7 +808,7 @@ static bool select_linked_internal(Scene *scene)
bool changed = false;
LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
- if ((seq->flag & SELECT) != 0) {
+ if ((seq->flag & SELECT) == 0) {
continue;
}
/* Only get unselected neighbors. */
@@ -1170,7 +1170,6 @@ static int sequencer_select_side_of_frame_exec(bContext *C, wmOperator *op)
Editing *ed = SEQ_editing_get(scene, false);
const bool extend = RNA_boolean_get(op->ptr, "extend");
const int side = RNA_enum_get(op->ptr, "side");
- Sequence *seq;
if (ed == NULL) {
return OPERATOR_CANCELLED;
@@ -1179,7 +1178,7 @@ static int sequencer_select_side_of_frame_exec(bContext *C, wmOperator *op)
ED_sequencer_deselect_all(scene);
}
const int timeline_frame = CFRA;
- SEQ_CURRENT_BEGIN (ed, seq) {
+ LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
bool test = false;
switch (side) {
case -1:
@@ -1188,8 +1187,8 @@ static int sequencer_select_side_of_frame_exec(bContext *C, wmOperator *op)
case 1:
test = (timeline_frame <= seq->startdisp);
break;
- case 0:
- test = (timeline_frame <= seq->enddisp) && (timeline_frame >= seq->startdisp);
+ case 2:
+ test = SEQ_time_strip_intersects_frame(seq, timeline_frame);
break;
}
@@ -1198,7 +1197,6 @@ static int sequencer_select_side_of_frame_exec(bContext *C, wmOperator *op)
recurs_sel_seq(seq);
}
}
- SEQ_CURRENT_END;
ED_outliner_select_sync_from_sequence_tag(C);
@@ -1212,6 +1210,7 @@ void SEQUENCER_OT_select_side_of_frame(wmOperatorType *ot)
static const EnumPropertyItem sequencer_select_left_right_types[] = {
{-1, "LEFT", 0, "Left", "Select to the left of the current frame"},
{1, "RIGHT", 0, "Right", "Select to the right of the current frame"},
+ {2, "CURRENT", 0, "Current Frame", "Select intersecting with the current frame"},
{0, NULL, 0, NULL, NULL},
};
@@ -1483,58 +1482,51 @@ static const EnumPropertyItem sequencer_prop_select_grouped_types[] = {
static bool select_grouped_type(Editing *ed, Sequence *actseq, const int channel)
{
- Sequence *seq;
bool changed = false;
- SEQ_CURRENT_BEGIN (ed, seq) {
+ LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
if (SEQ_CHANNEL_CHECK(seq, channel) && seq->type == actseq->type) {
seq->flag |= SELECT;
changed = true;
}
}
- SEQ_CURRENT_END;
return changed;
}
static bool select_grouped_type_basic(Editing *ed, Sequence *actseq, const int channel)
{
- Sequence *seq;
bool changed = false;
const bool is_sound = SEQ_IS_SOUND(actseq);
- SEQ_CURRENT_BEGIN (ed, seq) {
+ LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
if (SEQ_CHANNEL_CHECK(seq, channel) && (is_sound ? SEQ_IS_SOUND(seq) : !SEQ_IS_SOUND(seq))) {
seq->flag |= SELECT;
changed = true;
}
}
- SEQ_CURRENT_END;
return changed;
}
static bool select_grouped_type_effect(Editing *ed, Sequence *actseq, const int channel)
{
- Sequence *seq;
bool changed = false;
const bool is_effect = SEQ_IS_EFFECT(actseq);
- SEQ_CURRENT_BEGIN (ed, seq) {
+ LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
if (SEQ_CHANNEL_CHECK(seq, channel) &&
(is_effect ? SEQ_IS_EFFECT(seq) : !SEQ_IS_EFFECT(seq))) {
seq->flag |= SELECT;
changed = true;
}
}
- SEQ_CURRENT_END;
return changed;
}
static bool select_grouped_data(Editing *ed, Sequence *actseq, const int channel)
{
- Sequence *seq;
bool changed = false;
const char *dir = actseq->strip ? actseq->strip->dir : NULL;
@@ -1543,45 +1535,41 @@ static bool select_grouped_data(Editing *ed, Sequence *actseq, const int channel
}
if (SEQ_HAS_PATH(actseq) && dir) {
- SEQ_CURRENT_BEGIN (ed, seq) {
+ LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
if (SEQ_CHANNEL_CHECK(seq, channel) && SEQ_HAS_PATH(seq) && seq->strip &&
STREQ(seq->strip->dir, dir)) {
seq->flag |= SELECT;
changed = true;
}
}
- SEQ_CURRENT_END;
}
else if (actseq->type == SEQ_TYPE_SCENE) {
Scene *sce = actseq->scene;
- SEQ_CURRENT_BEGIN (ed, seq) {
+ LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
if (SEQ_CHANNEL_CHECK(seq, channel) && seq->type == SEQ_TYPE_SCENE && seq->scene == sce) {
seq->flag |= SELECT;
changed = true;
}
}
- SEQ_CURRENT_END;
}
else if (actseq->type == SEQ_TYPE_MOVIECLIP) {
MovieClip *clip = actseq->clip;
- SEQ_CURRENT_BEGIN (ed, seq) {
+ LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
if (SEQ_CHANNEL_CHECK(seq, channel) && seq->type == SEQ_TYPE_MOVIECLIP &&
seq->clip == clip) {
seq->flag |= SELECT;
changed = true;
}
}
- SEQ_CURRENT_END;
}
else if (actseq->type == SEQ_TYPE_MASK) {
struct Mask *mask = actseq->mask;
- SEQ_CURRENT_BEGIN (ed, seq) {
+ LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
if (SEQ_CHANNEL_CHECK(seq, channel) && seq->type == SEQ_TYPE_MASK && seq->mask == mask) {
seq->flag |= SELECT;
changed = true;
}
}
- SEQ_CURRENT_END;
}
return changed;
@@ -1589,7 +1577,6 @@ static bool select_grouped_data(Editing *ed, Sequence *actseq, const int channel
static bool select_grouped_effect(Editing *ed, Sequence *actseq, const int channel)
{
- Sequence *seq;
bool changed = false;
bool effects[SEQ_TYPE_MAX + 1];
@@ -1597,15 +1584,14 @@ static bool select_grouped_effect(Editing *ed, Sequence *actseq, const int chann
effects[i] = false;
}
- SEQ_CURRENT_BEGIN (ed, seq) {
+ LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
if (SEQ_CHANNEL_CHECK(seq, channel) && (seq->type & SEQ_TYPE_EFFECT) &&
ELEM(actseq, seq->seq1, seq->seq2, seq->seq3)) {
effects[seq->type] = true;
}
}
- SEQ_CURRENT_END;
- SEQ_CURRENT_BEGIN (ed, seq) {
+ LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
if (SEQ_CHANNEL_CHECK(seq, channel) && effects[seq->type]) {
if (seq->seq1) {
seq->seq1->flag |= SELECT;
@@ -1619,86 +1605,65 @@ static bool select_grouped_effect(Editing *ed, Sequence *actseq, const int chann
changed = true;
}
}
- SEQ_CURRENT_END;
return changed;
}
static bool select_grouped_time_overlap(Editing *ed, Sequence *actseq)
{
- Sequence *seq;
bool changed = false;
- SEQ_CURRENT_BEGIN (ed, seq) {
+ LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
if (seq->startdisp < actseq->enddisp && seq->enddisp > actseq->startdisp) {
seq->flag |= SELECT;
changed = true;
}
}
- SEQ_CURRENT_END;
return changed;
}
-static bool select_grouped_effect_link(Editing *ed, Sequence *actseq, const int channel)
+/* Query strips that are in lower channel and intersect in time with seq_reference. */
+static void query_lower_channel_strips(Sequence *seq_reference,
+ ListBase *seqbase,
+ SeqCollection *collection)
{
- Sequence *seq = NULL;
- bool changed = false;
- const bool is_audio = ((actseq->type == SEQ_TYPE_META) || SEQ_IS_SOUND(actseq));
- int startdisp = actseq->startdisp;
- int enddisp = actseq->enddisp;
- int machine = actseq->machine;
- SeqIterator iter;
-
- SEQ_CURRENT_BEGIN (ed, seq) {
- seq->tmp = NULL;
- }
- SEQ_CURRENT_END;
-
- actseq->tmp = POINTER_FROM_INT(true);
-
- for (SEQ_iterator_begin(ed, &iter, true); iter.valid; SEQ_iterator_next(&iter)) {
- seq = iter.seq;
-
- /* Ignore all seqs already selected. */
- /* Ignore all seqs not sharing some time with active one. */
- /* Ignore all seqs of incompatible types (audio vs video). */
- if (!SEQ_CHANNEL_CHECK(seq, channel) || (seq->flag & SELECT) || (seq->startdisp >= enddisp) ||
- (seq->enddisp < startdisp) || (!is_audio && SEQ_IS_SOUND(seq)) ||
- (is_audio && !((seq->type == SEQ_TYPE_META) || SEQ_IS_SOUND(seq)))) {
- continue;
+ LISTBASE_FOREACH (Sequence *, seq_test, seqbase) {
+ if (seq_test->machine > seq_reference->machine) {
+ continue; /* Not lower channel. */
}
+ if (seq_test->enddisp <= seq_reference->startdisp ||
+ seq_test->startdisp >= seq_reference->enddisp) {
+ continue; /* Not intersecting in time. */
+ }
+ SEQ_collection_append_strip(seq_test, collection);
+ }
+}
- /* If the seq is an effect one, we need extra checking. */
- if (SEQ_IS_EFFECT(seq) && ((seq->seq1 && seq->seq1->tmp) || (seq->seq2 && seq->seq2->tmp) ||
- (seq->seq3 && seq->seq3->tmp))) {
- if (startdisp > seq->startdisp) {
- startdisp = seq->startdisp;
- }
- if (enddisp < seq->enddisp) {
- enddisp = seq->enddisp;
- }
- if (machine < seq->machine) {
- machine = seq->machine;
- }
-
- seq->tmp = POINTER_FROM_INT(true);
+/* Select all strips within time range and with lower channel of initial selection. Then select
+ * effect chains of these strips. */
+static bool select_grouped_effect_link(Editing *ed,
+ Sequence *UNUSED(actseq),
+ const int UNUSED(channel))
+{
+ ListBase *seqbase = SEQ_active_seqbase_get(ed);
- seq->flag |= SELECT;
- changed = true;
+ /* Get collection of strips. */
+ SeqCollection *collection = SEQ_query_selected_strips(seqbase);
+ const int selected_strip_count = BLI_gset_len(collection->set);
+ SEQ_collection_expand(seqbase, collection, query_lower_channel_strips);
+ SEQ_collection_expand(seqbase, collection, SEQ_query_strip_effect_chain);
- /* Unfortunately, we must restart checks from the beginning. */
- SEQ_iterator_end(&iter);
- SEQ_iterator_begin(ed, &iter, true);
- }
+ /* Check if other strips will be affected. */
+ const bool changed = BLI_gset_len(collection->set) > selected_strip_count;
- /* Video strips below active one, or any strip for audio (order doesn't matter here). */
- else if (seq->machine < machine || is_audio) {
- seq->flag |= SELECT;
- changed = true;
- }
+ /* Actual logic. */
+ Sequence *seq;
+ SEQ_ITERATOR_FOREACH (seq, collection) {
+ seq->flag |= SELECT;
}
- SEQ_iterator_end(&iter);
+
+ SEQ_collection_free(collection);
return changed;
}
@@ -1711,7 +1676,7 @@ static int sequencer_select_grouped_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
Editing *ed = SEQ_editing_get(scene, false);
- Sequence *seq, *actseq = SEQ_select_active_get(scene);
+ Sequence *actseq = SEQ_select_active_get(scene);
if (actseq == NULL) {
BKE_report(op->reports, RPT_ERROR, "No active sequence!");
@@ -1725,11 +1690,10 @@ static int sequencer_select_grouped_exec(bContext *C, wmOperator *op)
bool changed = false;
if (!extend) {
- SEQ_CURRENT_BEGIN (ed, seq) {
+ LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
seq->flag &= ~SELECT;
changed = true;
}
- SEQ_CURRENT_END;
}
switch (type) {
diff --git a/source/blender/editors/space_spreadsheet/CMakeLists.txt b/source/blender/editors/space_spreadsheet/CMakeLists.txt
index a77e74ffd93..b2a0905d4a2 100644
--- a/source/blender/editors/space_spreadsheet/CMakeLists.txt
+++ b/source/blender/editors/space_spreadsheet/CMakeLists.txt
@@ -17,9 +17,9 @@
set(INC
../include
+ ../../blenfont
../../blenkernel
../../blenlib
- ../../blenfont
../../bmesh
../../depsgraph
../../functions
@@ -33,15 +33,23 @@ set(INC
set(SRC
space_spreadsheet.cc
- spreadsheet_column_layout.cc
+ spreadsheet_context.cc
+ spreadsheet_column.cc
+ spreadsheet_data_source.cc
+ spreadsheet_data_source_geometry.cc
spreadsheet_draw.cc
- spreadsheet_from_geometry.cc
+ spreadsheet_layout.cc
spreadsheet_ops.cc
+ spreadsheet_context.hh
+ spreadsheet_cell_value.hh
+ spreadsheet_column.hh
+ spreadsheet_column_values.hh
+ spreadsheet_data_source.hh
+ spreadsheet_data_source_geometry.hh
spreadsheet_draw.hh
- spreadsheet_column_layout.hh
- spreadsheet_from_geometry.hh
spreadsheet_intern.hh
+ spreadsheet_layout.hh
)
set(LIB
diff --git a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc
index 0f7709b464e..1f0b5d5d13e 100644
--- a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc
+++ b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc
@@ -17,12 +17,12 @@
#include <cstring>
#include "BLI_listbase.h"
-#include "BLI_resource_collector.hh"
#include "BKE_screen.h"
#include "ED_screen.h"
#include "ED_space_api.h"
+#include "ED_spreadsheet.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
@@ -41,12 +41,16 @@
#include "WM_api.h"
#include "WM_types.h"
+#include "BLF_api.h"
+
#include "spreadsheet_intern.hh"
-#include "spreadsheet_column_layout.hh"
-#include "spreadsheet_from_geometry.hh"
+#include "spreadsheet_context.hh"
+#include "spreadsheet_data_source_geometry.hh"
#include "spreadsheet_intern.hh"
+#include "spreadsheet_layout.hh"
+using namespace blender;
using namespace blender::ed::spreadsheet;
static SpaceLink *spreadsheet_create(const ScrArea *UNUSED(area), const Scene *UNUSED(scene))
@@ -85,6 +89,13 @@ static void spreadsheet_free(SpaceLink *sl)
{
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl;
MEM_SAFE_FREE(sspreadsheet->runtime);
+
+ LISTBASE_FOREACH_MUTABLE (SpreadsheetColumn *, column, &sspreadsheet->columns) {
+ spreadsheet_column_free(column);
+ }
+ LISTBASE_FOREACH_MUTABLE (SpreadsheetContext *, context, &sspreadsheet->context_path) {
+ spreadsheet_context_free(context);
+ }
}
static void spreadsheet_init(wmWindowManager *UNUSED(wm), ScrArea *area)
@@ -102,6 +113,18 @@ static SpaceLink *spreadsheet_duplicate(SpaceLink *sl)
SpaceSpreadsheet *sspreadsheet_new = (SpaceSpreadsheet *)MEM_dupallocN(sspreadsheet_old);
sspreadsheet_new->runtime = (SpaceSpreadsheet_Runtime *)MEM_dupallocN(sspreadsheet_old->runtime);
+ BLI_listbase_clear(&sspreadsheet_new->columns);
+ LISTBASE_FOREACH (SpreadsheetColumn *, src_column, &sspreadsheet_old->columns) {
+ SpreadsheetColumn *new_column = spreadsheet_column_copy(src_column);
+ BLI_addtail(&sspreadsheet_new->columns, new_column);
+ }
+
+ BLI_listbase_clear(&sspreadsheet_new->context_path);
+ LISTBASE_FOREACH_MUTABLE (SpreadsheetContext *, src_context, &sspreadsheet_old->context_path) {
+ SpreadsheetContext *new_context = spreadsheet_context_copy(src_context);
+ BLI_addtail(&sspreadsheet_new->context_path, new_context);
+ }
+
return (SpaceLink *)sspreadsheet_new;
}
@@ -109,6 +132,24 @@ static void spreadsheet_keymap(wmKeyConfig *UNUSED(keyconf))
{
}
+static void spreadsheet_id_remap(ScrArea *UNUSED(area), SpaceLink *slink, ID *old_id, ID *new_id)
+{
+ SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)slink;
+ LISTBASE_FOREACH (SpreadsheetContext *, context, &sspreadsheet->context_path) {
+ if (context->type == SPREADSHEET_CONTEXT_OBJECT) {
+ SpreadsheetContextObject *object_context = (SpreadsheetContextObject *)context;
+ if ((ID *)object_context->object == old_id) {
+ if (new_id && GS(new_id->name) == ID_OB) {
+ object_context->object = (Object *)new_id;
+ }
+ else {
+ object_context->object = nullptr;
+ }
+ }
+ }
+ }
+}
+
static void spreadsheet_main_region_init(wmWindowManager *wm, ARegion *region)
{
region->v2d.scroll = V2D_SCROLL_RIGHT | V2D_SCROLL_BOTTOM;
@@ -123,57 +164,212 @@ static void spreadsheet_main_region_init(wmWindowManager *wm, ARegion *region)
WM_event_add_keymap_handler(&region->handlers, keymap);
}
-static ID *get_used_id(const bContext *C)
+ID *ED_spreadsheet_get_current_id(struct SpaceSpreadsheet *sspreadsheet)
+{
+ if (BLI_listbase_is_empty(&sspreadsheet->context_path)) {
+ return nullptr;
+ }
+ SpreadsheetContext *root_context = (SpreadsheetContext *)sspreadsheet->context_path.first;
+ if (root_context->type != SPREADSHEET_CONTEXT_OBJECT) {
+ return nullptr;
+ }
+ SpreadsheetContextObject *object_context = (SpreadsheetContextObject *)root_context;
+ return (ID *)object_context->object;
+}
+
+/* Check if the pinned context still exists. If it doesn't try to find a new context. */
+static void update_pinned_context_path_if_outdated(const bContext *C)
{
SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
- if (sspreadsheet->pinned_id != nullptr) {
- return sspreadsheet->pinned_id;
+
+ /* Currently, this only checks if the object has been deleted. In the future we can have a more
+ * sophisticated check for the entire context (including modifier and nodes). */
+ LISTBASE_FOREACH (SpreadsheetContext *, context, &sspreadsheet->context_path) {
+ if (context->type == SPREADSHEET_CONTEXT_OBJECT) {
+ SpreadsheetContextObject *object_context = (SpreadsheetContextObject *)context;
+ if (object_context->object == nullptr) {
+ ED_spreadsheet_context_path_clear(sspreadsheet);
+ break;
+ }
+ }
+ }
+ if (BLI_listbase_is_empty(&sspreadsheet->context_path)) {
+ Object *active_object = CTX_data_active_object(C);
+ if (active_object != nullptr) {
+ SpreadsheetContext *new_context = spreadsheet_context_new(SPREADSHEET_CONTEXT_OBJECT);
+ ((SpreadsheetContextObject *)new_context)->object = active_object;
+ BLI_addtail(&sspreadsheet->context_path, new_context);
+ }
+ }
+
+ if (BLI_listbase_is_empty(&sspreadsheet->context_path)) {
+ /* Don't pin empty context_path, that could be annoying. */
+ sspreadsheet->flag &= ~SPREADSHEET_FLAG_PINNED;
}
+}
+
+static void update_context_path_from_context(const bContext *C)
+{
+ SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
Object *active_object = CTX_data_active_object(C);
- return (ID *)active_object;
+ if (active_object == nullptr) {
+ ED_spreadsheet_context_path_clear(sspreadsheet);
+ return;
+ }
+ if (!BLI_listbase_is_empty(&sspreadsheet->context_path)) {
+ SpreadsheetContext *root_context = (SpreadsheetContext *)sspreadsheet->context_path.first;
+ if (root_context->type == SPREADSHEET_CONTEXT_OBJECT) {
+ SpreadsheetContextObject *object_context = (SpreadsheetContextObject *)root_context;
+ if (object_context->object != active_object) {
+ ED_spreadsheet_context_path_clear(sspreadsheet);
+ }
+ }
+ }
+ if (BLI_listbase_is_empty(&sspreadsheet->context_path)) {
+ SpreadsheetContext *new_context = spreadsheet_context_new(SPREADSHEET_CONTEXT_OBJECT);
+ ((SpreadsheetContextObject *)new_context)->object = active_object;
+ BLI_addtail(&sspreadsheet->context_path, new_context);
+ }
}
-class FallbackSpreadsheetDrawer : public SpreadsheetDrawer {
-};
+static void update_context_path(const bContext *C)
+{
+ SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
+ if (sspreadsheet->flag & SPREADSHEET_FLAG_PINNED) {
+ update_pinned_context_path_if_outdated(C);
+ }
+ else {
+ update_context_path_from_context(C);
+ }
+}
-static void gather_spreadsheet_columns(const bContext *C,
- SpreadsheetColumnLayout &column_layout,
- blender::ResourceCollector &resources)
+static std::unique_ptr<DataSource> get_data_source(const bContext *C)
{
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
- ID *used_id = get_used_id(C);
+ SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
+ ID *used_id = ED_spreadsheet_get_current_id(sspreadsheet);
if (used_id == nullptr) {
- return;
+ return {};
}
const ID_Type id_type = GS(used_id->name);
if (id_type != ID_OB) {
- return;
+ return {};
}
Object *object_orig = (Object *)used_id;
- if (!ELEM(object_orig->type, OB_MESH, OB_POINTCLOUD)) {
- return;
+ if (!ELEM(object_orig->type, OB_MESH, OB_POINTCLOUD, OB_VOLUME)) {
+ return {};
}
Object *object_eval = DEG_get_evaluated_object(depsgraph, object_orig);
if (object_eval == nullptr) {
- return;
+ return {};
+ }
+
+ return data_source_from_geometry(C, object_eval);
+}
+
+static float get_column_width(const ColumnValues &values)
+{
+ if (values.default_width > 0) {
+ return values.default_width;
}
+ const int fontid = UI_style_get()->widget.uifont_id;
+ BLF_size(fontid, UI_DEFAULT_TEXT_POINTS, U.dpi);
+ const StringRefNull name = values.name();
+ const float name_width = BLF_width(fontid, name.data(), name.size());
+ return std::max<float>(name_width / UI_UNIT_X + 1.0f, 3.0f);
+}
- return spreadsheet_columns_from_geometry(C, object_eval, column_layout, resources);
+static float get_column_width_in_pixels(const ColumnValues &values)
+{
+ return get_column_width(values) * SPREADSHEET_WIDTH_UNIT;
+}
+
+static int get_index_column_width(const int tot_rows)
+{
+ const int fontid = UI_style_get()->widget.uifont_id;
+ BLF_size(fontid, UI_style_get_dpi()->widget.points * U.pixelsize, U.dpi);
+ return std::to_string(std::max(0, tot_rows - 1)).size() * BLF_width(fontid, "0", 1) +
+ UI_UNIT_X * 0.75;
+}
+
+static void update_visible_columns(ListBase &columns, DataSource &data_source)
+{
+ Set<SpreadsheetColumnID> used_ids;
+ LISTBASE_FOREACH_MUTABLE (SpreadsheetColumn *, column, &columns) {
+ std::unique_ptr<ColumnValues> values = data_source.get_column_values(*column->id);
+ /* Remove columns that don't exist anymore. */
+ if (!values) {
+ BLI_remlink(&columns, column);
+ spreadsheet_column_free(column);
+ continue;
+ }
+
+ if (!used_ids.add(*column->id)) {
+ /* Remove duplicate columns for now. */
+ BLI_remlink(&columns, column);
+ spreadsheet_column_free(column);
+ continue;
+ }
+ }
+
+ data_source.foreach_default_column_ids([&](const SpreadsheetColumnID &column_id) {
+ std::unique_ptr<ColumnValues> values = data_source.get_column_values(column_id);
+ if (values) {
+ if (used_ids.add(column_id)) {
+ SpreadsheetColumnID *new_id = spreadsheet_column_id_copy(&column_id);
+ SpreadsheetColumn *new_column = spreadsheet_column_new(new_id);
+ BLI_addtail(&columns, new_column);
+ }
+ }
+ });
}
static void spreadsheet_main_region_draw(const bContext *C, ARegion *region)
{
SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
+ update_context_path(C);
- blender::ResourceCollector resources;
- SpreadsheetColumnLayout column_layout;
- gather_spreadsheet_columns(C, column_layout, resources);
+ std::unique_ptr<DataSource> data_source = get_data_source(C);
+ if (!data_source) {
+ data_source = std::make_unique<DataSource>();
+ }
+
+ update_visible_columns(sspreadsheet->columns, *data_source);
+
+ SpreadsheetLayout spreadsheet_layout;
+ ResourceScope scope;
+
+ LISTBASE_FOREACH (SpreadsheetColumn *, column, &sspreadsheet->columns) {
+ std::unique_ptr<ColumnValues> values_ptr = data_source->get_column_values(*column->id);
+ /* Should have been removed before if it does not exist anymore. */
+ BLI_assert(values_ptr);
+ const ColumnValues *values = scope.add(std::move(values_ptr), __func__);
+ const int width = get_column_width_in_pixels(*values);
+ spreadsheet_layout.columns.append({values, width});
+ }
+
+ const int tot_rows = data_source->tot_rows();
+ spreadsheet_layout.index_column_width = get_index_column_width(tot_rows);
+ spreadsheet_layout.row_indices = IndexRange(tot_rows).as_span();
+
+ if (const GeometryDataSource *geometry_data_source = dynamic_cast<const GeometryDataSource *>(
+ data_source.get())) {
+ Object *object_eval = geometry_data_source->object_eval();
+ Object *object_orig = DEG_get_original_object(object_eval);
+ if (object_orig->type == OB_MESH) {
+ if (object_orig->mode == OB_MODE_EDIT) {
+ if (sspreadsheet->filter_flag & SPREADSHEET_FILTER_SELECTED_ONLY) {
+ spreadsheet_layout.row_indices = geometry_data_source->get_selected_element_indices();
+ }
+ }
+ }
+ }
- sspreadsheet->runtime->visible_rows = column_layout.row_indices.size();
- sspreadsheet->runtime->tot_columns = column_layout.columns.size();
- sspreadsheet->runtime->tot_rows = column_layout.tot_rows;
+ sspreadsheet->runtime->tot_columns = spreadsheet_layout.columns.size();
+ sspreadsheet->runtime->tot_rows = tot_rows;
+ sspreadsheet->runtime->visible_rows = spreadsheet_layout.row_indices.size();
- std::unique_ptr<SpreadsheetDrawer> drawer = spreadsheet_drawer_from_column_layout(column_layout);
+ std::unique_ptr<SpreadsheetDrawer> drawer = spreadsheet_drawer_from_layout(spreadsheet_layout);
draw_spreadsheet_in_region(C, region, *drawer);
/* Tag footer for redraw, because the main region updates data for the footer. */
@@ -208,6 +404,7 @@ static void spreadsheet_main_region_listener(const wmRegionListenerParams *param
}
break;
}
+ case NC_TEXTURE:
case NC_GEOM: {
ED_region_tag_redraw(region);
break;
@@ -222,6 +419,7 @@ static void spreadsheet_header_region_init(wmWindowManager *UNUSED(wm), ARegion
static void spreadsheet_header_region_draw(const bContext *C, ARegion *region)
{
+ update_context_path(C);
ED_region_header(C, region);
}
@@ -327,6 +525,7 @@ void ED_spacetype_spreadsheet(void)
st->duplicate = spreadsheet_duplicate;
st->operatortypes = spreadsheet_operatortypes;
st->keymap = spreadsheet_keymap;
+ st->id_remap = spreadsheet_id_remap;
/* regions: main window */
art = (ARegionType *)MEM_callocN(sizeof(ARegionType), "spacetype spreadsheet region");
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh b/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh
new file mode 100644
index 00000000000..c9b73aabf96
--- /dev/null
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <optional>
+
+#include "BLI_color.hh"
+#include "BLI_float2.hh"
+#include "BLI_float3.hh"
+
+struct Object;
+struct Collection;
+
+namespace blender::ed::spreadsheet {
+
+struct ObjectCellValue {
+ const Object *object;
+};
+
+struct CollectionCellValue {
+ const Collection *collection;
+};
+
+/**
+ * This is a type that can hold the value of a cell in a spreadsheet. This type allows us to
+ * decouple the drawing of individual cells from the code that generates the data to be displayed.
+ */
+class CellValue {
+ public:
+ /* The implementation just uses a bunch of `std::option` for now. Unfortunately, we cannot use
+ * `std::variant` yet, due to missing compiler support. This type can really be optimized more,
+ * but it does not really matter too much currently. */
+
+ std::optional<int> value_int;
+ std::optional<float> value_float;
+ std::optional<bool> value_bool;
+ std::optional<float2> value_float2;
+ std::optional<float3> value_float3;
+ std::optional<ColorGeometry4f> value_color;
+ std::optional<ObjectCellValue> value_object;
+ std::optional<CollectionCellValue> value_collection;
+};
+
+} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_column.cc b/source/blender/editors/space_spreadsheet/spreadsheet_column.cc
new file mode 100644
index 00000000000..de40545fdae
--- /dev/null
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_column.cc
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+#include "DNA_space_types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_hash.hh"
+#include "BLI_string.h"
+#include "BLI_string_ref.hh"
+
+#include "spreadsheet_column.hh"
+
+namespace blender::ed::spreadsheet {
+
+SpreadsheetColumnID *spreadsheet_column_id_new()
+{
+ SpreadsheetColumnID *column_id = (SpreadsheetColumnID *)MEM_callocN(sizeof(SpreadsheetColumnID),
+ __func__);
+ return column_id;
+}
+
+SpreadsheetColumnID *spreadsheet_column_id_copy(const SpreadsheetColumnID *src_column_id)
+{
+ SpreadsheetColumnID *new_column_id = spreadsheet_column_id_new();
+ new_column_id->name = BLI_strdup(src_column_id->name);
+ return new_column_id;
+}
+
+void spreadsheet_column_id_free(SpreadsheetColumnID *column_id)
+{
+ if (column_id->name != nullptr) {
+ MEM_freeN(column_id->name);
+ }
+ MEM_freeN(column_id);
+}
+
+SpreadsheetColumn *spreadsheet_column_new(SpreadsheetColumnID *column_id)
+{
+ SpreadsheetColumn *column = (SpreadsheetColumn *)MEM_callocN(sizeof(SpreadsheetColumn),
+ __func__);
+ column->id = column_id;
+ return column;
+}
+
+SpreadsheetColumn *spreadsheet_column_copy(const SpreadsheetColumn *src_column)
+{
+ SpreadsheetColumnID *new_column_id = spreadsheet_column_id_copy(src_column->id);
+ SpreadsheetColumn *new_column = spreadsheet_column_new(new_column_id);
+ return new_column;
+}
+
+void spreadsheet_column_free(SpreadsheetColumn *column)
+{
+ spreadsheet_column_id_free(column->id);
+ MEM_freeN(column);
+}
+
+} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_column.hh b/source/blender/editors/space_spreadsheet/spreadsheet_column.hh
new file mode 100644
index 00000000000..bb245851d55
--- /dev/null
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_column.hh
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "DNA_space_types.h"
+
+#include "BLI_hash.hh"
+
+namespace blender {
+template<> struct DefaultHash<SpreadsheetColumnID> {
+ uint64_t operator()(const SpreadsheetColumnID &column_id) const
+ {
+ return get_default_hash(StringRef(column_id.name));
+ }
+};
+} // namespace blender
+
+inline bool operator==(const SpreadsheetColumnID &a, const SpreadsheetColumnID &b)
+{
+ using blender::StringRef;
+ return StringRef(a.name) == StringRef(b.name);
+}
+
+namespace blender::ed::spreadsheet {
+
+SpreadsheetColumnID *spreadsheet_column_id_new();
+SpreadsheetColumnID *spreadsheet_column_id_copy(const SpreadsheetColumnID *src_column_id);
+void spreadsheet_column_id_free(SpreadsheetColumnID *column_id);
+
+SpreadsheetColumn *spreadsheet_column_new(SpreadsheetColumnID *column_id);
+SpreadsheetColumn *spreadsheet_column_copy(const SpreadsheetColumn *src_column);
+void spreadsheet_column_free(SpreadsheetColumn *column);
+
+} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_column_layout.cc b/source/blender/editors/space_spreadsheet/spreadsheet_column_layout.cc
deleted file mode 100644
index 46760c0dd4e..00000000000
--- a/source/blender/editors/space_spreadsheet/spreadsheet_column_layout.cc
+++ /dev/null
@@ -1,230 +0,0 @@
-/*
- * 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.
- */
-
-#include <iomanip>
-#include <sstream>
-
-#include "spreadsheet_column_layout.hh"
-
-#include "DNA_userdef_types.h"
-
-#include "UI_interface.h"
-#include "UI_resources.h"
-
-#include "BLF_api.h"
-
-namespace blender::ed::spreadsheet {
-
-class ColumnLayoutDrawer : public SpreadsheetDrawer {
- private:
- const SpreadsheetColumnLayout &column_layout_;
- Vector<int> column_widths_;
-
- public:
- ColumnLayoutDrawer(const SpreadsheetColumnLayout &column_layout) : column_layout_(column_layout)
- {
- tot_columns = column_layout.columns.size();
- tot_rows = column_layout.row_indices.size();
-
- const int fontid = UI_style_get()->widget.uifont_id;
- /* Use a consistent font size for the width calculation. */
- BLF_size(fontid, 11 * U.pixelsize, U.dpi);
-
- /* The width of the index column depends on the maximum row index. */
- left_column_width = std::to_string(std::max(0, column_layout_.tot_rows - 1)).size() *
- BLF_width(fontid, "0", 1) +
- UI_UNIT_X * 0.75;
-
- /* The column widths depend on the column name widths. */
- const int minimum_column_width = 3 * UI_UNIT_X;
- const int header_name_padding = UI_UNIT_X;
- for (const SpreadsheetColumn *column : column_layout_.columns) {
- if (column->default_width == 0.0f) {
- StringRefNull name = column->name();
- const int name_width = BLF_width(fontid, name.data(), name.size());
- const int width = std::max(name_width + header_name_padding, minimum_column_width);
- column_widths_.append(width);
- }
- else {
- column_widths_.append(column->default_width * UI_UNIT_X);
- }
- }
- }
-
- void draw_top_row_cell(int column_index, const CellDrawParams &params) const final
- {
- const StringRefNull name = column_layout_.columns[column_index]->name();
- uiBut *but = uiDefIconTextBut(params.block,
- UI_BTYPE_LABEL,
- 0,
- ICON_NONE,
- name.c_str(),
- params.xmin,
- params.ymin,
- params.width,
- params.height,
- nullptr,
- 0,
- 0,
- 0,
- 0,
- nullptr);
- /* Center-align column headers. */
- UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT);
- UI_but_drawflag_disable(but, UI_BUT_TEXT_RIGHT);
- }
-
- void draw_left_column_cell(int row_index, const CellDrawParams &params) const final
- {
- const int real_index = column_layout_.row_indices[row_index];
- std::string index_str = std::to_string(real_index);
- uiBut *but = uiDefIconTextBut(params.block,
- UI_BTYPE_LABEL,
- 0,
- ICON_NONE,
- index_str.c_str(),
- params.xmin,
- params.ymin,
- params.width,
- params.height,
- nullptr,
- 0,
- 0,
- 0,
- 0,
- nullptr);
- /* Right-align indices. */
- UI_but_drawflag_enable(but, UI_BUT_TEXT_RIGHT);
- UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT);
- }
-
- void draw_content_cell(int row_index, int column_index, const CellDrawParams &params) const final
- {
- const int real_index = column_layout_.row_indices[row_index];
- const SpreadsheetColumn &column = *column_layout_.columns[column_index];
- CellValue cell_value;
- column.get_value(real_index, cell_value);
-
- if (cell_value.value_int.has_value()) {
- const int value = *cell_value.value_int;
- const std::string value_str = std::to_string(value);
- uiDefIconTextBut(params.block,
- UI_BTYPE_LABEL,
- 0,
- ICON_NONE,
- value_str.c_str(),
- params.xmin,
- params.ymin,
- params.width,
- params.height,
- nullptr,
- 0,
- 0,
- 0,
- 0,
- nullptr);
- }
- else if (cell_value.value_float.has_value()) {
- const float value = *cell_value.value_float;
- std::stringstream ss;
- ss << std::fixed << std::setprecision(3) << value;
- const std::string value_str = ss.str();
- uiDefIconTextBut(params.block,
- UI_BTYPE_LABEL,
- 0,
- ICON_NONE,
- value_str.c_str(),
- params.xmin,
- params.ymin,
- params.width,
- params.height,
- nullptr,
- 0,
- 0,
- 0,
- 0,
- nullptr);
- }
- else if (cell_value.value_bool.has_value()) {
- const bool value = *cell_value.value_bool;
- const int icon = value ? ICON_CHECKBOX_HLT : ICON_CHECKBOX_DEHLT;
- uiDefIconTextBut(params.block,
- UI_BTYPE_LABEL,
- 0,
- icon,
- "",
- params.xmin,
- params.ymin,
- params.width,
- params.height,
- nullptr,
- 0,
- 0,
- 0,
- 0,
- nullptr);
- }
- else if (cell_value.value_object.has_value()) {
- const ObjectCellValue value = *cell_value.value_object;
- uiDefIconTextBut(params.block,
- UI_BTYPE_LABEL,
- 0,
- ICON_OBJECT_DATA,
- reinterpret_cast<const ID *const>(value.object)->name + 2,
- params.xmin,
- params.ymin,
- params.width,
- params.height,
- nullptr,
- 0,
- 0,
- 0,
- 0,
- nullptr);
- }
- else if (cell_value.value_collection.has_value()) {
- const CollectionCellValue value = *cell_value.value_collection;
- uiDefIconTextBut(params.block,
- UI_BTYPE_LABEL,
- 0,
- ICON_OUTLINER_COLLECTION,
- reinterpret_cast<const ID *const>(value.collection)->name + 2,
- params.xmin,
- params.ymin,
- params.width,
- params.height,
- nullptr,
- 0,
- 0,
- 0,
- 0,
- nullptr);
- }
- }
-
- int column_width(int column_index) const final
- {
- return column_widths_[column_index];
- }
-};
-
-std::unique_ptr<SpreadsheetDrawer> spreadsheet_drawer_from_column_layout(
- const SpreadsheetColumnLayout &column_layout)
-{
- return std::make_unique<ColumnLayoutDrawer>(column_layout);
-}
-
-} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_column_layout.hh b/source/blender/editors/space_spreadsheet/spreadsheet_column_layout.hh
deleted file mode 100644
index 611337df007..00000000000
--- a/source/blender/editors/space_spreadsheet/spreadsheet_column_layout.hh
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * 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.
- */
-
-#pragma once
-
-#include <optional>
-
-#include "spreadsheet_draw.hh"
-
-struct Object;
-struct Collection;
-
-namespace blender::ed::spreadsheet {
-
-struct ObjectCellValue {
- const Object *object;
-};
-
-struct CollectionCellValue {
- const Collection *collection;
-};
-
-/**
- * This is a small type that can hold the value of a cell in a spreadsheet. This type allows us to
- * decouple the drawing of individual cells from the code that generates the data to be displayed.
- */
-class CellValue {
- public:
- /* The implementation just uses a bunch of `std::option` for now. Unfortunately, we cannot use
- * `std::variant` yet, due to missing compiler support. This type can really be optimized more,
- * but it does not really matter too much currently. */
-
- std::optional<int> value_int;
- std::optional<float> value_float;
- std::optional<bool> value_bool;
- std::optional<ObjectCellValue> value_object;
- std::optional<CollectionCellValue> value_collection;
-};
-
-/**
- * This represents a column in a spreadsheet. It has a name and provides a value for all the cells
- * in the column.
- */
-class SpreadsheetColumn {
- protected:
- std::string name_;
-
- public:
- SpreadsheetColumn(std::string name) : name_(std::move(name))
- {
- }
-
- virtual ~SpreadsheetColumn() = default;
-
- virtual void get_value(int index, CellValue &r_cell_value) const = 0;
-
- StringRefNull name() const
- {
- return name_;
- }
-
- /* The default width of newly created columns, in UI units. */
- float default_width = 0.0f;
-};
-
-/* Utility class for the function below. */
-template<typename GetValueF> class LambdaSpreadsheetColumn : public SpreadsheetColumn {
- private:
- GetValueF get_value_;
-
- public:
- LambdaSpreadsheetColumn(std::string name, GetValueF get_value)
- : SpreadsheetColumn(std::move(name)), get_value_(std::move(get_value))
- {
- }
-
- void get_value(int index, CellValue &r_cell_value) const final
- {
- get_value_(index, r_cell_value);
- }
-};
-
-/* Utility function that simplifies creating a spreadsheet column from a lambda function. */
-template<typename GetValueF>
-std::unique_ptr<SpreadsheetColumn> spreadsheet_column_from_function(std::string name,
- GetValueF get_value)
-{
- return std::make_unique<LambdaSpreadsheetColumn<GetValueF>>(std::move(name),
- std::move(get_value));
-}
-
-/* This contains information required to create a spreadsheet drawer from columns. */
-struct SpreadsheetColumnLayout {
- Vector<const SpreadsheetColumn *> columns;
- Span<int64_t> row_indices;
- int tot_rows = 0;
-};
-
-std::unique_ptr<SpreadsheetDrawer> spreadsheet_drawer_from_column_layout(
- const SpreadsheetColumnLayout &column_layout);
-
-} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh b/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh
new file mode 100644
index 00000000000..373c988a41c
--- /dev/null
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "BLI_string_ref.hh"
+
+#include "spreadsheet_cell_value.hh"
+
+namespace blender::ed::spreadsheet {
+
+/**
+ * This represents a column in a spreadsheet. It has a name and provides a value for all the cells
+ * in the column.
+ */
+class ColumnValues {
+ protected:
+ std::string name_;
+ int size_;
+
+ public:
+ ColumnValues(std::string name, const int size) : name_(std::move(name)), size_(size)
+ {
+ }
+
+ virtual ~ColumnValues() = default;
+
+ virtual void get_value(int index, CellValue &r_cell_value) const = 0;
+
+ StringRefNull name() const
+ {
+ return name_;
+ }
+
+ int size() const
+ {
+ return size_;
+ }
+
+ /* The default width of newly created columns, in UI units. */
+ float default_width = 0.0f;
+};
+
+/* Utility class for the function below. */
+template<typename GetValueF> class LambdaColumnValues : public ColumnValues {
+ private:
+ GetValueF get_value_;
+
+ public:
+ LambdaColumnValues(std::string name, int size, GetValueF get_value)
+ : ColumnValues(std::move(name), size), get_value_(std::move(get_value))
+ {
+ }
+
+ void get_value(int index, CellValue &r_cell_value) const final
+ {
+ get_value_(index, r_cell_value);
+ }
+};
+
+/* Utility function that simplifies creating a spreadsheet column from a lambda function. */
+template<typename GetValueF>
+std::unique_ptr<ColumnValues> column_values_from_function(std::string name,
+ const int size,
+ GetValueF get_value,
+ const float default_width = 0.0f)
+{
+ std::unique_ptr<ColumnValues> column_values = std::make_unique<LambdaColumnValues<GetValueF>>(
+ std::move(name), size, std::move(get_value));
+ column_values->default_width = default_width;
+ return column_values;
+}
+
+static constexpr float default_float_column_width = 3;
+static constexpr float default_float2_column_width = 2 * default_float_column_width;
+static constexpr float default_float3_column_width = 3 * default_float_column_width;
+static constexpr float default_color_column_width = 4 * default_float_column_width;
+
+} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_context.cc b/source/blender/editors/space_spreadsheet/spreadsheet_context.cc
new file mode 100644
index 00000000000..3eb43338908
--- /dev/null
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_context.cc
@@ -0,0 +1,306 @@
+/*
+ * 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.
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_hash.h"
+#include "BLI_hash.hh"
+#include "BLI_hash_mm2a.h"
+#include "BLI_listbase.h"
+#include "BLI_string.h"
+#include "BLI_utildefines.h"
+#include "BLI_vector.hh"
+
+#include "ED_spreadsheet.h"
+
+#include "DEG_depsgraph.h"
+
+#include "BKE_main.h"
+#include "BKE_modifier.h"
+#include "BKE_object.h"
+
+#include "spreadsheet_context.hh"
+
+namespace blender::ed::spreadsheet {
+
+static SpreadsheetContextObject *spreadsheet_context_object_new()
+{
+ SpreadsheetContextObject *context = (SpreadsheetContextObject *)MEM_callocN(
+ sizeof(SpreadsheetContextObject), __func__);
+ context->base.type = SPREADSHEET_CONTEXT_OBJECT;
+ return context;
+}
+
+static SpreadsheetContextObject *spreadsheet_context_object_copy(
+ const SpreadsheetContextObject *src_context)
+{
+ SpreadsheetContextObject *new_context = spreadsheet_context_object_new();
+ new_context->object = src_context->object;
+ return new_context;
+}
+
+static void spreadsheet_context_object_hash(const SpreadsheetContextObject *context,
+ BLI_HashMurmur2A *mm2)
+{
+ BLI_hash_mm2a_add(mm2, (const uchar *)&context->object, sizeof(Object *));
+}
+
+static void spreadsheet_context_object_free(SpreadsheetContextObject *context)
+{
+ MEM_freeN(context);
+}
+
+static SpreadsheetContextModifier *spreadsheet_context_modifier_new()
+{
+ SpreadsheetContextModifier *context = (SpreadsheetContextModifier *)MEM_callocN(
+ sizeof(SpreadsheetContextModifier), __func__);
+ context->base.type = SPREADSHEET_CONTEXT_MODIFIER;
+ return context;
+}
+
+static SpreadsheetContextModifier *spreadsheet_context_modifier_copy(
+ const SpreadsheetContextModifier *src_context)
+{
+ SpreadsheetContextModifier *new_context = spreadsheet_context_modifier_new();
+ if (src_context->modifier_name) {
+ new_context->modifier_name = BLI_strdup(src_context->modifier_name);
+ }
+ return new_context;
+}
+
+static void spreadsheet_context_modifier_hash(const SpreadsheetContextModifier *context,
+ BLI_HashMurmur2A *mm2)
+{
+ if (context->modifier_name) {
+ BLI_hash_mm2a_add(mm2, (const uchar *)context->modifier_name, strlen(context->modifier_name));
+ }
+}
+
+static void spreadsheet_context_modifier_free(SpreadsheetContextModifier *context)
+{
+ if (context->modifier_name) {
+ MEM_freeN(context->modifier_name);
+ }
+ MEM_freeN(context);
+}
+
+static SpreadsheetContextNode *spreadsheet_context_node_new()
+{
+ SpreadsheetContextNode *context = (SpreadsheetContextNode *)MEM_callocN(
+ sizeof(SpreadsheetContextNode), __func__);
+ context->base.type = SPREADSHEET_CONTEXT_NODE;
+ return context;
+}
+
+static SpreadsheetContextNode *spreadsheet_context_node_copy(
+ const SpreadsheetContextNode *src_context)
+{
+ SpreadsheetContextNode *new_context = spreadsheet_context_node_new();
+ if (src_context->node_name) {
+ new_context->node_name = BLI_strdup(src_context->node_name);
+ }
+ return new_context;
+}
+
+static void spreadsheet_context_node_hash(const SpreadsheetContextNode *context,
+ BLI_HashMurmur2A *mm2)
+{
+ if (context->node_name) {
+ BLI_hash_mm2a_add(mm2, (const uchar *)context->node_name, strlen(context->node_name));
+ }
+}
+
+static void spreadsheet_context_node_free(SpreadsheetContextNode *context)
+{
+ if (context->node_name) {
+ MEM_freeN(context->node_name);
+ }
+ MEM_freeN(context);
+}
+
+SpreadsheetContext *spreadsheet_context_new(eSpaceSpreadsheet_ContextType type)
+{
+ switch (type) {
+ case SPREADSHEET_CONTEXT_OBJECT: {
+ return (SpreadsheetContext *)spreadsheet_context_object_new();
+ }
+ case SPREADSHEET_CONTEXT_MODIFIER: {
+ return (SpreadsheetContext *)spreadsheet_context_modifier_new();
+ }
+ case SPREADSHEET_CONTEXT_NODE: {
+ return (SpreadsheetContext *)spreadsheet_context_node_new();
+ }
+ }
+ BLI_assert_unreachable();
+ return nullptr;
+}
+
+SpreadsheetContext *spreadsheet_context_copy(const SpreadsheetContext *old_context)
+{
+ switch (old_context->type) {
+ case SPREADSHEET_CONTEXT_OBJECT: {
+ return (SpreadsheetContext *)spreadsheet_context_object_copy(
+ (const SpreadsheetContextObject *)old_context);
+ }
+ case SPREADSHEET_CONTEXT_MODIFIER: {
+ return (SpreadsheetContext *)spreadsheet_context_modifier_copy(
+ (const SpreadsheetContextModifier *)old_context);
+ }
+ case SPREADSHEET_CONTEXT_NODE: {
+ return (SpreadsheetContext *)spreadsheet_context_node_copy(
+ (const SpreadsheetContextNode *)old_context);
+ }
+ }
+ BLI_assert_unreachable();
+ return nullptr;
+}
+
+static void spreadsheet_context_hash(const SpreadsheetContext *context, BLI_HashMurmur2A *mm2)
+{
+ BLI_hash_mm2a_add_int(mm2, context->type);
+ switch (context->type) {
+ case SPREADSHEET_CONTEXT_OBJECT: {
+ spreadsheet_context_object_hash((const SpreadsheetContextObject *)context, mm2);
+ break;
+ }
+ case SPREADSHEET_CONTEXT_MODIFIER: {
+ spreadsheet_context_modifier_hash((const SpreadsheetContextModifier *)context, mm2);
+ break;
+ }
+ case SPREADSHEET_CONTEXT_NODE: {
+ spreadsheet_context_node_hash((const SpreadsheetContextNode *)context, mm2);
+ break;
+ }
+ }
+}
+
+void spreadsheet_context_free(SpreadsheetContext *context)
+{
+ switch (context->type) {
+ case SPREADSHEET_CONTEXT_OBJECT: {
+ return spreadsheet_context_object_free((SpreadsheetContextObject *)context);
+ }
+ case SPREADSHEET_CONTEXT_MODIFIER: {
+ return spreadsheet_context_modifier_free((SpreadsheetContextModifier *)context);
+ }
+ case SPREADSHEET_CONTEXT_NODE: {
+ return spreadsheet_context_node_free((SpreadsheetContextNode *)context);
+ }
+ }
+ BLI_assert_unreachable();
+}
+
+/**
+ * Tag any data relevant to the spreadsheet's context for recalculation in order to collect
+ * information to display in the editor, which may be cached during evaluation.
+ */
+static void spreadsheet_context_update_tag(SpaceSpreadsheet *sspreadsheet)
+{
+ using namespace blender;
+ Vector<const SpreadsheetContext *> context_path = sspreadsheet->context_path;
+ if (context_path.is_empty()) {
+ return;
+ }
+ if (context_path[0]->type != SPREADSHEET_CONTEXT_OBJECT) {
+ return;
+ }
+ SpreadsheetContextObject *object_context = (SpreadsheetContextObject *)context_path[0];
+ Object *object = object_context->object;
+ if (object == nullptr) {
+ return;
+ }
+ if (context_path.size() == 1) {
+ /* No need to reevaluate, when the final or original object is viewed. */
+ return;
+ }
+
+ DEG_id_tag_update(&object->id, ID_RECALC_GEOMETRY);
+}
+
+} // namespace blender::ed::spreadsheet
+
+SpreadsheetContext *ED_spreadsheet_context_new(int type)
+{
+ return blender::ed::spreadsheet::spreadsheet_context_new((eSpaceSpreadsheet_ContextType)type);
+}
+
+void ED_spreadsheet_context_free(struct SpreadsheetContext *context)
+{
+ blender::ed::spreadsheet::spreadsheet_context_free(context);
+}
+
+void ED_spreadsheet_context_path_clear(struct SpaceSpreadsheet *sspreadsheet)
+{
+ LISTBASE_FOREACH_MUTABLE (SpreadsheetContext *, context, &sspreadsheet->context_path) {
+ ED_spreadsheet_context_free(context);
+ }
+ BLI_listbase_clear(&sspreadsheet->context_path);
+}
+
+void ED_spreadsheet_context_path_update_tag(SpaceSpreadsheet *sspreadsheet)
+{
+ blender::ed::spreadsheet::spreadsheet_context_update_tag(sspreadsheet);
+}
+
+uint64_t ED_spreadsheet_context_path_hash(SpaceSpreadsheet *sspreadsheet)
+{
+ BLI_HashMurmur2A mm2;
+ BLI_hash_mm2a_init(&mm2, 1234);
+ LISTBASE_FOREACH (SpreadsheetContext *, context, &sspreadsheet->context_path) {
+ blender::ed::spreadsheet::spreadsheet_context_hash(context, &mm2);
+ }
+ return BLI_hash_mm2a_end(&mm2);
+}
+
+void ED_spreadsheet_set_geometry_node_context(struct SpaceSpreadsheet *sspreadsheet,
+ struct SpaceNode *snode,
+ struct bNode *node)
+{
+ using namespace blender::ed::spreadsheet;
+ ED_spreadsheet_context_path_clear(sspreadsheet);
+
+ Object *object = (Object *)snode->id;
+ ModifierData *modifier = BKE_object_active_modifier(object);
+
+ {
+ SpreadsheetContextObject *context = spreadsheet_context_object_new();
+ context->object = object;
+ BLI_addtail(&sspreadsheet->context_path, context);
+ }
+ {
+ SpreadsheetContextModifier *context = spreadsheet_context_modifier_new();
+ context->modifier_name = BLI_strdup(modifier->name);
+ BLI_addtail(&sspreadsheet->context_path, context);
+ }
+ {
+ int i;
+ LISTBASE_FOREACH_INDEX (bNodeTreePath *, path, &snode->treepath, i) {
+ if (i == 0) {
+ continue;
+ }
+ SpreadsheetContextNode *context = spreadsheet_context_node_new();
+ context->node_name = BLI_strdup(path->node_name);
+ BLI_addtail(&sspreadsheet->context_path, context);
+ }
+ }
+ {
+ SpreadsheetContextNode *context = spreadsheet_context_node_new();
+ context->node_name = BLI_strdup(node->name);
+ BLI_addtail(&sspreadsheet->context_path, context);
+ }
+
+ sspreadsheet->object_eval_state = SPREADSHEET_OBJECT_EVAL_STATE_EVALUATED;
+}
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_from_geometry.hh b/source/blender/editors/space_spreadsheet/spreadsheet_context.hh
index cef731517b9..d71769e42b3 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_from_geometry.hh
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_context.hh
@@ -16,19 +16,12 @@
#pragma once
-#include "BKE_geometry_set.hh"
-
-#include "BLI_resource_collector.hh"
-
-#include "spreadsheet_column_layout.hh"
-
-struct bContext;
+#include "DNA_space_types.h"
namespace blender::ed::spreadsheet {
-void spreadsheet_columns_from_geometry(const bContext *C,
- Object *object_eval,
- SpreadsheetColumnLayout &column_layout,
- ResourceCollector &resources);
+SpreadsheetContext *spreadsheet_context_new(eSpaceSpreadsheet_ContextType type);
+SpreadsheetContext *spreadsheet_context_copy(const SpreadsheetContext *old_context);
+void spreadsheet_context_free(SpreadsheetContext *context);
} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source.cc
new file mode 100644
index 00000000000..09b8c6b1b54
--- /dev/null
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source.cc
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+#include "spreadsheet_data_source.hh"
+
+namespace blender::ed::spreadsheet {
+
+/* Provide a "key function" for the linker. */
+DataSource::~DataSource() = default;
+
+} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh b/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh
new file mode 100644
index 00000000000..de47109a144
--- /dev/null
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "BLI_function_ref.hh"
+
+#include "spreadsheet_column.hh"
+#include "spreadsheet_column_values.hh"
+
+namespace blender::ed::spreadsheet {
+
+/**
+ * This class is subclassed to implement different data sources for the spreadsheet. A data source
+ * provides the information that should be displayed. It is not concerned with how data is laid
+ * out in the spreadsheet editor exactly.
+ */
+class DataSource {
+ public:
+ virtual ~DataSource();
+
+ /**
+ * Calls the callback with all the column ids that should be displayed as long as the user does
+ * not manually add or remove columns. The column id can be stack allocated. Therefore, the
+ * callback should not keep a reference to it (and copy it instead).
+ */
+ virtual void foreach_default_column_ids(FunctionRef<void(const SpreadsheetColumnID &)> fn) const
+ {
+ UNUSED_VARS(fn);
+ }
+
+ /**
+ * Returns the column values the given column id. If no data exists for this id, null is
+ * returned.
+ */
+ virtual std::unique_ptr<ColumnValues> get_column_values(
+ const SpreadsheetColumnID &column_id) const
+ {
+ UNUSED_VARS(column_id);
+ return {};
+ }
+
+ /**
+ * Returns the number of rows in columns returned by #get_column_values.
+ */
+ virtual int tot_rows() const
+ {
+ return 0;
+ }
+};
+
+} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
new file mode 100644
index 00000000000..452885959f6
--- /dev/null
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
@@ -0,0 +1,445 @@
+/*
+ * 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.
+ */
+
+#include "BKE_context.h"
+#include "BKE_editmesh.h"
+#include "BKE_lib_id.h"
+#include "BKE_mesh.h"
+#include "BKE_mesh_wrapper.h"
+#include "BKE_modifier.h"
+
+#include "DNA_ID.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_space_types.h"
+#include "DNA_userdef_types.h"
+
+#include "DEG_depsgraph_query.h"
+
+#include "ED_spreadsheet.h"
+
+#include "bmesh.h"
+
+#include "spreadsheet_data_source_geometry.hh"
+#include "spreadsheet_intern.hh"
+
+namespace blender::ed::spreadsheet {
+
+void GeometryDataSource::foreach_default_column_ids(
+ FunctionRef<void(const SpreadsheetColumnID &)> fn) const
+{
+ component_->attribute_foreach([&](StringRefNull name, const AttributeMetaData &meta_data) {
+ if (meta_data.domain != domain_) {
+ return true;
+ }
+ SpreadsheetColumnID column_id;
+ column_id.name = (char *)name.c_str();
+ fn(column_id);
+ return true;
+ });
+}
+
+std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
+ const SpreadsheetColumnID &column_id) const
+{
+ std::lock_guard lock{mutex_};
+
+ bke::ReadAttributeLookup attribute = component_->attribute_try_get_for_read(column_id.name);
+ if (!attribute) {
+ return {};
+ }
+ const fn::GVArray *varray = scope_.add(std::move(attribute.varray), __func__);
+ if (attribute.domain != domain_) {
+ return {};
+ }
+ int domain_size = varray->size();
+ const CustomDataType type = bke::cpp_type_to_custom_data_type(varray->type());
+ switch (type) {
+ case CD_PROP_FLOAT:
+ return column_values_from_function(
+ column_id.name, domain_size, [varray](int index, CellValue &r_cell_value) {
+ float value;
+ varray->get(index, &value);
+ r_cell_value.value_float = value;
+ });
+ case CD_PROP_INT32:
+ return column_values_from_function(
+ column_id.name, domain_size, [varray](int index, CellValue &r_cell_value) {
+ int value;
+ varray->get(index, &value);
+ r_cell_value.value_int = value;
+ });
+ case CD_PROP_BOOL:
+ return column_values_from_function(
+ column_id.name, domain_size, [varray](int index, CellValue &r_cell_value) {
+ bool value;
+ varray->get(index, &value);
+ r_cell_value.value_bool = value;
+ });
+ case CD_PROP_FLOAT2: {
+ return column_values_from_function(
+ column_id.name,
+ domain_size,
+ [varray](int index, CellValue &r_cell_value) {
+ float2 value;
+ varray->get(index, &value);
+ r_cell_value.value_float2 = value;
+ },
+ default_float2_column_width);
+ }
+ case CD_PROP_FLOAT3: {
+ return column_values_from_function(
+ column_id.name,
+ domain_size,
+ [varray](int index, CellValue &r_cell_value) {
+ float3 value;
+ varray->get(index, &value);
+ r_cell_value.value_float3 = value;
+ },
+ default_float3_column_width);
+ }
+ case CD_PROP_COLOR: {
+ return column_values_from_function(
+ column_id.name,
+ domain_size,
+ [varray](int index, CellValue &r_cell_value) {
+ ColorGeometry4f value;
+ varray->get(index, &value);
+ r_cell_value.value_color = value;
+ },
+ default_color_column_width);
+ }
+ default:
+ break;
+ }
+ return {};
+}
+
+int GeometryDataSource::tot_rows() const
+{
+ return component_->attribute_domain_size(domain_);
+}
+
+using IsVertexSelectedFn = FunctionRef<bool(int vertex_index)>;
+
+static void get_selected_vertex_indices(const Mesh &mesh,
+ const IsVertexSelectedFn is_vertex_selected_fn,
+ Vector<int64_t> &r_vertex_indices)
+{
+ for (const int i : IndexRange(mesh.totvert)) {
+ if (is_vertex_selected_fn(i)) {
+ r_vertex_indices.append(i);
+ }
+ }
+}
+
+static void get_selected_corner_indices(const Mesh &mesh,
+ const IsVertexSelectedFn is_vertex_selected_fn,
+ Vector<int64_t> &r_corner_indices)
+{
+ for (const int i : IndexRange(mesh.totloop)) {
+ const MLoop &loop = mesh.mloop[i];
+ if (is_vertex_selected_fn(loop.v)) {
+ r_corner_indices.append(i);
+ }
+ }
+}
+
+static void get_selected_face_indices(const Mesh &mesh,
+ const IsVertexSelectedFn is_vertex_selected_fn,
+ Vector<int64_t> &r_face_indices)
+{
+ for (const int poly_index : IndexRange(mesh.totpoly)) {
+ const MPoly &poly = mesh.mpoly[poly_index];
+ bool is_selected = true;
+ for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
+ const MLoop &loop = mesh.mloop[loop_index];
+ if (!is_vertex_selected_fn(loop.v)) {
+ is_selected = false;
+ break;
+ }
+ }
+ if (is_selected) {
+ r_face_indices.append(poly_index);
+ }
+ }
+}
+
+static void get_selected_edge_indices(const Mesh &mesh,
+ const IsVertexSelectedFn is_vertex_selected_fn,
+ Vector<int64_t> &r_edge_indices)
+{
+ for (const int i : IndexRange(mesh.totedge)) {
+ const MEdge &edge = mesh.medge[i];
+ if (is_vertex_selected_fn(edge.v1) && is_vertex_selected_fn(edge.v2)) {
+ r_edge_indices.append(i);
+ }
+ }
+}
+
+static void get_selected_indices_on_domain(const Mesh &mesh,
+ const AttributeDomain domain,
+ const IsVertexSelectedFn is_vertex_selected_fn,
+ Vector<int64_t> &r_indices)
+{
+ switch (domain) {
+ case ATTR_DOMAIN_POINT:
+ return get_selected_vertex_indices(mesh, is_vertex_selected_fn, r_indices);
+ case ATTR_DOMAIN_FACE:
+ return get_selected_face_indices(mesh, is_vertex_selected_fn, r_indices);
+ case ATTR_DOMAIN_CORNER:
+ return get_selected_corner_indices(mesh, is_vertex_selected_fn, r_indices);
+ case ATTR_DOMAIN_EDGE:
+ return get_selected_edge_indices(mesh, is_vertex_selected_fn, r_indices);
+ default:
+ return;
+ }
+}
+
+Span<int64_t> GeometryDataSource::get_selected_element_indices() const
+{
+ std::lock_guard lock{mutex_};
+
+ BLI_assert(object_eval_->mode == OB_MODE_EDIT);
+ BLI_assert(component_->type() == GEO_COMPONENT_TYPE_MESH);
+ Object *object_orig = DEG_get_original_object(object_eval_);
+ Vector<int64_t> &indices = scope_.construct<Vector<int64_t>>(__func__);
+ const MeshComponent *mesh_component = static_cast<const MeshComponent *>(component_);
+ const Mesh *mesh_eval = mesh_component->get_for_read();
+ Mesh *mesh_orig = (Mesh *)object_orig->data;
+ BMesh *bm = mesh_orig->edit_mesh->bm;
+ BM_mesh_elem_table_ensure(bm, BM_VERT);
+
+ int *orig_indices = (int *)CustomData_get_layer(&mesh_eval->vdata, CD_ORIGINDEX);
+ if (orig_indices != nullptr) {
+ /* Use CD_ORIGINDEX layer if it exists. */
+ auto is_vertex_selected = [&](int vertex_index) -> bool {
+ const int i_orig = orig_indices[vertex_index];
+ if (i_orig < 0) {
+ return false;
+ }
+ if (i_orig >= bm->totvert) {
+ return false;
+ }
+ BMVert *vert = bm->vtable[i_orig];
+ return BM_elem_flag_test(vert, BM_ELEM_SELECT);
+ };
+ get_selected_indices_on_domain(*mesh_eval, domain_, is_vertex_selected, indices);
+ }
+ else if (mesh_eval->totvert == bm->totvert) {
+ /* Use a simple heuristic to match original vertices to evaluated ones. */
+ auto is_vertex_selected = [&](int vertex_index) -> bool {
+ BMVert *vert = bm->vtable[vertex_index];
+ return BM_elem_flag_test(vert, BM_ELEM_SELECT);
+ };
+ get_selected_indices_on_domain(*mesh_eval, domain_, is_vertex_selected, indices);
+ }
+
+ return indices;
+}
+
+void InstancesDataSource::foreach_default_column_ids(
+ FunctionRef<void(const SpreadsheetColumnID &)> fn) const
+{
+ if (component_->instances_amount() == 0) {
+ return;
+ }
+
+ SpreadsheetColumnID column_id;
+ column_id.name = (char *)"Name";
+ fn(column_id);
+ for (const char *name : {"Position", "Rotation", "Scale", "ID"}) {
+ column_id.name = (char *)name;
+ fn(column_id);
+ }
+}
+
+std::unique_ptr<ColumnValues> InstancesDataSource::get_column_values(
+ const SpreadsheetColumnID &column_id) const
+{
+ if (component_->instances_amount() == 0) {
+ return {};
+ }
+
+ const int size = this->tot_rows();
+ if (STREQ(column_id.name, "Name")) {
+ Span<int> reference_handles = component_->instance_reference_handles();
+ Span<InstanceReference> references = component_->references();
+ std::unique_ptr<ColumnValues> values = column_values_from_function(
+ "Name", size, [reference_handles, references](int index, CellValue &r_cell_value) {
+ const InstanceReference &reference = references[reference_handles[index]];
+ switch (reference.type()) {
+ case InstanceReference::Type::Object: {
+ Object &object = reference.object();
+ r_cell_value.value_object = ObjectCellValue{&object};
+ break;
+ }
+ case InstanceReference::Type::Collection: {
+ Collection &collection = reference.collection();
+ r_cell_value.value_collection = CollectionCellValue{&collection};
+ break;
+ }
+ case InstanceReference::Type::None: {
+ break;
+ }
+ }
+ });
+ values->default_width = 8.0f;
+ return values;
+ }
+ Span<float4x4> transforms = component_->instance_transforms();
+ if (STREQ(column_id.name, "Position")) {
+ return column_values_from_function(
+ column_id.name,
+ size,
+ [transforms](int index, CellValue &r_cell_value) {
+ r_cell_value.value_float3 = transforms[index].translation();
+ },
+ default_float3_column_width);
+ }
+ if (STREQ(column_id.name, "Rotation")) {
+ return column_values_from_function(
+ column_id.name,
+ size,
+ [transforms](int index, CellValue &r_cell_value) {
+ r_cell_value.value_float3 = transforms[index].to_euler();
+ },
+ default_float3_column_width);
+ }
+ if (STREQ(column_id.name, "Scale")) {
+ return column_values_from_function(
+ column_id.name,
+ size,
+ [transforms](int index, CellValue &r_cell_value) {
+ r_cell_value.value_float3 = transforms[index].scale();
+ },
+ default_float3_column_width);
+ }
+ Span<int> ids = component_->instance_ids();
+ if (STREQ(column_id.name, "ID")) {
+ /* Make the column a bit wider by default, since the IDs tend to be large numbers. */
+ return column_values_from_function(
+ column_id.name,
+ size,
+ [ids](int index, CellValue &r_cell_value) { r_cell_value.value_int = ids[index]; },
+ 5.5f);
+ }
+ return {};
+}
+
+int InstancesDataSource::tot_rows() const
+{
+ return component_->instances_amount();
+}
+
+static GeometrySet get_display_geometry_set(SpaceSpreadsheet *sspreadsheet,
+ Object *object_eval,
+ const GeometryComponentType used_component_type)
+{
+ GeometrySet geometry_set;
+ if (sspreadsheet->object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_ORIGINAL) {
+ Object *object_orig = DEG_get_original_object(object_eval);
+ if (object_orig->type == OB_MESH) {
+ MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
+ if (object_orig->mode == OB_MODE_EDIT) {
+ Mesh *mesh = (Mesh *)object_orig->data;
+ BMEditMesh *em = mesh->edit_mesh;
+ if (em != nullptr) {
+ Mesh *new_mesh = (Mesh *)BKE_id_new_nomain(ID_ME, nullptr);
+ /* This is a potentially heavy operation to do on every redraw. The best solution here is
+ * to display the data directly from the bmesh without a conversion, which can be
+ * implemented a bit later. */
+ BM_mesh_bm_to_me_for_eval(em->bm, new_mesh, nullptr);
+ mesh_component.replace(new_mesh, GeometryOwnershipType::Owned);
+ }
+ }
+ else {
+ Mesh *mesh = (Mesh *)object_orig->data;
+ mesh_component.replace(mesh, GeometryOwnershipType::ReadOnly);
+ }
+ mesh_component.copy_vertex_group_names_from_object(*object_orig);
+ }
+ else if (object_orig->type == OB_POINTCLOUD) {
+ PointCloud *pointcloud = (PointCloud *)object_orig->data;
+ PointCloudComponent &pointcloud_component =
+ geometry_set.get_component_for_write<PointCloudComponent>();
+ pointcloud_component.replace(pointcloud, GeometryOwnershipType::ReadOnly);
+ }
+ }
+ else if (sspreadsheet->object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_EVALUATED) {
+ if (used_component_type == GEO_COMPONENT_TYPE_MESH && object_eval->mode == OB_MODE_EDIT) {
+ Mesh *mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(object_eval, false);
+ if (mesh == nullptr) {
+ return geometry_set;
+ }
+ BKE_mesh_wrapper_ensure_mdata(mesh);
+ MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
+ mesh_component.replace(mesh, GeometryOwnershipType::ReadOnly);
+ mesh_component.copy_vertex_group_names_from_object(*object_eval);
+ }
+ else {
+ if (BLI_listbase_count(&sspreadsheet->context_path) == 1) {
+ /* Use final evaluated object. */
+ if (object_eval->runtime.geometry_set_eval != nullptr) {
+ geometry_set = *object_eval->runtime.geometry_set_eval;
+ }
+ }
+ else {
+ if (object_eval->runtime.geometry_set_previews != nullptr) {
+ GHash *ghash = (GHash *)object_eval->runtime.geometry_set_previews;
+ const uint64_t key = ED_spreadsheet_context_path_hash(sspreadsheet);
+ GeometrySet *geometry_set_preview = (GeometrySet *)BLI_ghash_lookup_default(
+ ghash, POINTER_FROM_UINT(key), nullptr);
+ if (geometry_set_preview != nullptr) {
+ geometry_set = *geometry_set_preview;
+ }
+ }
+ }
+ }
+ }
+ return geometry_set;
+}
+
+static GeometryComponentType get_display_component_type(const bContext *C, Object *object_eval)
+{
+ SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
+ if (sspreadsheet->object_eval_state != SPREADSHEET_OBJECT_EVAL_STATE_ORIGINAL) {
+ return (GeometryComponentType)sspreadsheet->geometry_component_type;
+ }
+ if (object_eval->type == OB_POINTCLOUD) {
+ return GEO_COMPONENT_TYPE_POINT_CLOUD;
+ }
+ return GEO_COMPONENT_TYPE_MESH;
+}
+
+std::unique_ptr<DataSource> data_source_from_geometry(const bContext *C, Object *object_eval)
+{
+ SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
+ const AttributeDomain domain = (AttributeDomain)sspreadsheet->attribute_domain;
+ const GeometryComponentType component_type = get_display_component_type(C, object_eval);
+ GeometrySet geometry_set = get_display_geometry_set(sspreadsheet, object_eval, component_type);
+
+ if (!geometry_set.has(component_type)) {
+ return {};
+ }
+
+ if (component_type == GEO_COMPONENT_TYPE_INSTANCES) {
+ return std::make_unique<InstancesDataSource>(geometry_set);
+ }
+ return std::make_unique<GeometryDataSource>(object_eval, geometry_set, component_type, domain);
+}
+
+} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh
new file mode 100644
index 00000000000..273d39f27bf
--- /dev/null
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <mutex>
+
+#include "BLI_resource_scope.hh"
+
+#include "BKE_geometry_set.hh"
+
+#include "spreadsheet_data_source.hh"
+
+struct bContext;
+
+namespace blender::ed::spreadsheet {
+
+class GeometryDataSource : public DataSource {
+ private:
+ Object *object_eval_;
+ const GeometrySet geometry_set_;
+ const GeometryComponent *component_;
+ AttributeDomain domain_;
+
+ /* Some data is computed on the fly only when it is requested. Computing it does not change the
+ * logical state of this data source. Therefore, the corresponding methods are const and need to
+ * be protected with a mutex. */
+ mutable std::mutex mutex_;
+ mutable ResourceScope scope_;
+
+ public:
+ GeometryDataSource(Object *object_eval,
+ GeometrySet geometry_set,
+ const GeometryComponentType component_type,
+ const AttributeDomain domain)
+ : object_eval_(object_eval),
+ geometry_set_(std::move(geometry_set)),
+ component_(geometry_set_.get_component_for_read(component_type)),
+ domain_(domain)
+ {
+ }
+
+ Object *object_eval() const
+ {
+ return object_eval_;
+ }
+
+ Span<int64_t> get_selected_element_indices() const;
+
+ void foreach_default_column_ids(
+ FunctionRef<void(const SpreadsheetColumnID &)> fn) const override;
+
+ std::unique_ptr<ColumnValues> get_column_values(
+ const SpreadsheetColumnID &column_id) const override;
+
+ int tot_rows() const override;
+};
+
+class InstancesDataSource : public DataSource {
+ const GeometrySet geometry_set_;
+ const InstancesComponent *component_;
+
+ public:
+ InstancesDataSource(GeometrySet geometry_set)
+ : geometry_set_(std::move(geometry_set)),
+ component_(geometry_set_.get_component_for_read<InstancesComponent>())
+ {
+ }
+
+ void foreach_default_column_ids(
+ FunctionRef<void(const SpreadsheetColumnID &)> fn) const override;
+
+ std::unique_ptr<ColumnValues> get_column_values(
+ const SpreadsheetColumnID &column_id) const override;
+
+ int tot_rows() const override;
+};
+
+std::unique_ptr<DataSource> data_source_from_geometry(const bContext *C, Object *object_eval);
+
+} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_draw.cc b/source/blender/editors/space_spreadsheet/spreadsheet_draw.cc
index d6379c740e8..b911c80fa63 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_draw.cc
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_draw.cc
@@ -142,7 +142,9 @@ static void draw_left_column_content(const int scroll_offset_y,
ARegion *region,
const SpreadsheetDrawer &drawer)
{
- GPU_scissor_test(true);
+ int old_scissor[4];
+ GPU_scissor_get(old_scissor);
+
GPU_scissor(0, 0, drawer.left_column_width, region->winy - drawer.top_row_height);
uiBlock *left_column_block = UI_block_begin(C, region, __func__, UI_EMBOSS_NONE);
@@ -165,7 +167,7 @@ static void draw_left_column_content(const int scroll_offset_y,
UI_block_end(C, left_column_block);
UI_block_draw(C, left_column_block);
- GPU_scissor_test(false);
+ GPU_scissor(UNPACK4(old_scissor));
}
static void draw_top_row_content(const bContext *C,
@@ -173,7 +175,9 @@ static void draw_top_row_content(const bContext *C,
const SpreadsheetDrawer &drawer,
const int scroll_offset_x)
{
- GPU_scissor_test(true);
+ int old_scissor[4];
+ GPU_scissor_get(old_scissor);
+
GPU_scissor(drawer.left_column_width + 1,
region->winy - drawer.top_row_height,
region->winx - drawer.left_column_width,
@@ -200,7 +204,7 @@ static void draw_top_row_content(const bContext *C,
UI_block_end(C, first_row_block);
UI_block_draw(C, first_row_block);
- GPU_scissor_test(false);
+ GPU_scissor(UNPACK4(old_scissor));
}
static void draw_cell_contents(const bContext *C,
@@ -209,7 +213,9 @@ static void draw_cell_contents(const bContext *C,
const int scroll_offset_x,
const int scroll_offset_y)
{
- GPU_scissor_test(true);
+ int old_scissor[4];
+ GPU_scissor_get(old_scissor);
+
GPU_scissor(drawer.left_column_width + 1,
0,
region->winx - drawer.left_column_width,
@@ -248,7 +254,7 @@ static void draw_cell_contents(const bContext *C,
UI_block_end(C, cells_block);
UI_block_draw(C, cells_block);
- GPU_scissor_test(false);
+ GPU_scissor(UNPACK4(old_scissor));
}
static void update_view2d_tot_rect(const SpreadsheetDrawer &drawer,
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_draw.hh b/source/blender/editors/space_spreadsheet/spreadsheet_draw.hh
index 6828006f4a1..647587ec8b0 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_draw.hh
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_draw.hh
@@ -19,7 +19,6 @@
#include "BLI_vector.hh"
struct uiBlock;
-struct rcti;
struct bContext;
struct ARegion;
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_from_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_from_geometry.cc
deleted file mode 100644
index 910bc0a34ec..00000000000
--- a/source/blender/editors/space_spreadsheet/spreadsheet_from_geometry.cc
+++ /dev/null
@@ -1,447 +0,0 @@
-/*
- * 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.
- */
-
-#include "BKE_context.h"
-#include "BKE_editmesh.h"
-#include "BKE_lib_id.h"
-#include "BKE_mesh.h"
-#include "BKE_mesh_wrapper.h"
-#include "BKE_modifier.h"
-
-#include "DNA_ID.h"
-#include "DNA_mesh_types.h"
-#include "DNA_meshdata_types.h"
-#include "DNA_space_types.h"
-#include "DNA_userdef_types.h"
-
-#include "DEG_depsgraph_query.h"
-
-#include "bmesh.h"
-
-#include "spreadsheet_from_geometry.hh"
-#include "spreadsheet_intern.hh"
-
-namespace blender::ed::spreadsheet {
-
-using blender::bke::ReadAttribute;
-using blender::bke::ReadAttributePtr;
-
-static void add_columns_for_instances(const InstancesComponent &instances_component,
- SpreadsheetColumnLayout &column_layout,
- ResourceCollector &resources)
-{
- Span<InstancedData> instance_data = instances_component.instanced_data();
- Span<float4x4> transforms = instances_component.transforms();
-
- Vector<std::unique_ptr<SpreadsheetColumn>> &columns =
- resources.construct<Vector<std::unique_ptr<SpreadsheetColumn>>>("columns");
-
- columns.append(spreadsheet_column_from_function(
- "Name", [instance_data](int index, CellValue &r_cell_value) {
- const InstancedData &data = instance_data[index];
- if (data.type == INSTANCE_DATA_TYPE_OBJECT) {
- if (data.data.object != nullptr) {
- r_cell_value.value_object = ObjectCellValue{data.data.object};
- }
- }
- else if (data.type == INSTANCE_DATA_TYPE_COLLECTION) {
- if (data.data.collection != nullptr) {
- r_cell_value.value_collection = CollectionCellValue{data.data.collection};
- }
- }
- }));
-
- columns.last()->default_width = 8.0f;
-
- static std::array<char, 3> axis_char = {'X', 'Y', 'Z'};
- for (const int i : {0, 1, 2}) {
- std::string name = std::string("Position ") + axis_char[i];
- columns.append(spreadsheet_column_from_function(
- name, [transforms, i](int index, CellValue &r_cell_value) {
- r_cell_value.value_float = transforms[index].translation()[i];
- }));
- }
-
- for (const int i : {0, 1, 2}) {
- std::string name = std::string("Rotation ") + axis_char[i];
- columns.append(spreadsheet_column_from_function(
- name, [transforms, i](int index, CellValue &r_cell_value) {
- r_cell_value.value_float = transforms[index].to_euler()[i];
- }));
- }
-
- for (const int i : {0, 1, 2}) {
- std::string name = std::string("Scale ") + axis_char[i];
- columns.append(spreadsheet_column_from_function(
- name, [transforms, i](int index, CellValue &r_cell_value) {
- r_cell_value.value_float = transforms[index].scale()[i];
- }));
- }
-
- for (std::unique_ptr<SpreadsheetColumn> &column : columns) {
- column_layout.columns.append(column.get());
- }
-
- column_layout.row_indices = instance_data.index_range().as_span();
- column_layout.tot_rows = instances_component.instances_amount();
-}
-
-static Vector<std::string> get_sorted_attribute_names_to_display(
- const GeometryComponent &component, const AttributeDomain domain)
-{
- Vector<std::string> attribute_names;
- component.attribute_foreach(
- [&](const StringRef attribute_name, const AttributeMetaData &meta_data) {
- if (meta_data.domain == domain) {
- attribute_names.append(attribute_name);
- }
- return true;
- });
- std::sort(attribute_names.begin(),
- attribute_names.end(),
- [](const std::string &a, const std::string &b) {
- return BLI_strcasecmp_natural(a.c_str(), b.c_str()) < 0;
- });
- return attribute_names;
-}
-
-static void add_columns_for_attribute(const ReadAttribute *attribute,
- const StringRefNull attribute_name,
- Vector<std::unique_ptr<SpreadsheetColumn>> &columns)
-{
- const CustomDataType data_type = attribute->custom_data_type();
- switch (data_type) {
- case CD_PROP_FLOAT: {
- columns.append(spreadsheet_column_from_function(
- attribute_name, [attribute](int index, CellValue &r_cell_value) {
- float value;
- attribute->get(index, &value);
- r_cell_value.value_float = value;
- }));
- break;
- }
- case CD_PROP_FLOAT2: {
- static std::array<char, 2> axis_char = {'X', 'Y'};
- for (const int i : {0, 1}) {
- std::string name = attribute_name + " " + axis_char[i];
- columns.append(spreadsheet_column_from_function(
- name, [attribute, i](int index, CellValue &r_cell_value) {
- float2 value;
- attribute->get(index, &value);
- r_cell_value.value_float = value[i];
- }));
- }
- break;
- }
- case CD_PROP_FLOAT3: {
- static std::array<char, 3> axis_char = {'X', 'Y', 'Z'};
- for (const int i : {0, 1, 2}) {
- std::string name = attribute_name + " " + axis_char[i];
- columns.append(spreadsheet_column_from_function(
- name, [attribute, i](int index, CellValue &r_cell_value) {
- float3 value;
- attribute->get(index, &value);
- r_cell_value.value_float = value[i];
- }));
- }
- break;
- }
- case CD_PROP_COLOR: {
- static std::array<char, 4> axis_char = {'R', 'G', 'B', 'A'};
- for (const int i : {0, 1, 2, 3}) {
- std::string name = attribute_name + " " + axis_char[i];
- columns.append(spreadsheet_column_from_function(
- name, [attribute, i](int index, CellValue &r_cell_value) {
- Color4f value;
- attribute->get(index, &value);
- r_cell_value.value_float = value[i];
- }));
- }
- break;
- }
- case CD_PROP_INT32: {
- columns.append(spreadsheet_column_from_function(
- attribute_name, [attribute](int index, CellValue &r_cell_value) {
- int value;
- attribute->get(index, &value);
- r_cell_value.value_int = value;
- }));
- break;
- }
- case CD_PROP_BOOL: {
- columns.append(spreadsheet_column_from_function(
- attribute_name, [attribute](int index, CellValue &r_cell_value) {
- bool value;
- attribute->get(index, &value);
- r_cell_value.value_bool = value;
- }));
- break;
- }
- default:
- break;
- }
-}
-
-static GeometrySet get_display_geometry_set(SpaceSpreadsheet *sspreadsheet,
- Object *object_eval,
- const GeometryComponentType used_component_type)
-{
- GeometrySet geometry_set;
- if (sspreadsheet->object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_FINAL) {
- if (used_component_type == GEO_COMPONENT_TYPE_MESH && object_eval->mode == OB_MODE_EDIT) {
- Mesh *mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(object_eval, false);
- if (mesh == nullptr) {
- return geometry_set;
- }
- BKE_mesh_wrapper_ensure_mdata(mesh);
- MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
- mesh_component.replace(mesh, GeometryOwnershipType::ReadOnly);
- mesh_component.copy_vertex_group_names_from_object(*object_eval);
- }
- else {
- if (object_eval->runtime.geometry_set_eval != nullptr) {
- /* This does not copy the geometry data itself. */
- geometry_set = *object_eval->runtime.geometry_set_eval;
- }
- }
- }
- else {
- Object *object_orig = DEG_get_original_object(object_eval);
- if (object_orig->type == OB_MESH) {
- MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
- if (object_orig->mode == OB_MODE_EDIT) {
- Mesh *mesh = (Mesh *)object_orig->data;
- BMEditMesh *em = mesh->edit_mesh;
- if (em != nullptr) {
- Mesh *new_mesh = (Mesh *)BKE_id_new_nomain(ID_ME, nullptr);
- /* This is a potentially heavy operation to do on every redraw. The best solution here is
- * to display the data directly from the bmesh without a conversion, which can be
- * implemented a bit later. */
- BM_mesh_bm_to_me_for_eval(em->bm, new_mesh, nullptr);
- mesh_component.replace(new_mesh, GeometryOwnershipType::Owned);
- }
- }
- else {
- Mesh *mesh = (Mesh *)object_orig->data;
- mesh_component.replace(mesh, GeometryOwnershipType::ReadOnly);
- }
- mesh_component.copy_vertex_group_names_from_object(*object_orig);
- }
- else if (object_orig->type == OB_POINTCLOUD) {
- PointCloud *pointcloud = (PointCloud *)object_orig->data;
- PointCloudComponent &pointcloud_component =
- geometry_set.get_component_for_write<PointCloudComponent>();
- pointcloud_component.replace(pointcloud, GeometryOwnershipType::ReadOnly);
- }
- }
- return geometry_set;
-}
-
-using IsVertexSelectedFn = FunctionRef<bool(int vertex_index)>;
-
-static void get_selected_vertex_indices(const Mesh &mesh,
- const IsVertexSelectedFn is_vertex_selected_fn,
- Vector<int64_t> &r_vertex_indices)
-{
- for (const int i : IndexRange(mesh.totvert)) {
- if (is_vertex_selected_fn(i)) {
- r_vertex_indices.append(i);
- }
- }
-}
-
-static void get_selected_corner_indices(const Mesh &mesh,
- const IsVertexSelectedFn is_vertex_selected_fn,
- Vector<int64_t> &r_corner_indices)
-{
- for (const int i : IndexRange(mesh.totloop)) {
- const MLoop &loop = mesh.mloop[i];
- if (is_vertex_selected_fn(loop.v)) {
- r_corner_indices.append(i);
- }
- }
-}
-
-static void get_selected_face_indices(const Mesh &mesh,
- const IsVertexSelectedFn is_vertex_selected_fn,
- Vector<int64_t> &r_face_indices)
-{
- for (const int poly_index : IndexRange(mesh.totpoly)) {
- const MPoly &poly = mesh.mpoly[poly_index];
- bool is_selected = true;
- for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
- const MLoop &loop = mesh.mloop[loop_index];
- if (!is_vertex_selected_fn(loop.v)) {
- is_selected = false;
- break;
- }
- }
- if (is_selected) {
- r_face_indices.append(poly_index);
- }
- }
-}
-
-static void get_selected_edge_indices(const Mesh &mesh,
- const IsVertexSelectedFn is_vertex_selected_fn,
- Vector<int64_t> &r_edge_indices)
-{
- for (const int i : IndexRange(mesh.totedge)) {
- const MEdge &edge = mesh.medge[i];
- if (is_vertex_selected_fn(edge.v1) && is_vertex_selected_fn(edge.v2)) {
- r_edge_indices.append(i);
- }
- }
-}
-
-static void get_selected_indices_on_domain(const Mesh &mesh,
- const AttributeDomain domain,
- const IsVertexSelectedFn is_vertex_selected_fn,
- Vector<int64_t> &r_indices)
-{
- switch (domain) {
- case ATTR_DOMAIN_POINT:
- return get_selected_vertex_indices(mesh, is_vertex_selected_fn, r_indices);
- case ATTR_DOMAIN_FACE:
- return get_selected_face_indices(mesh, is_vertex_selected_fn, r_indices);
- case ATTR_DOMAIN_CORNER:
- return get_selected_corner_indices(mesh, is_vertex_selected_fn, r_indices);
- case ATTR_DOMAIN_EDGE:
- return get_selected_edge_indices(mesh, is_vertex_selected_fn, r_indices);
- default:
- return;
- }
-}
-
-static Span<int64_t> filter_mesh_elements_by_selection(const bContext *C,
- Object *object_eval,
- const MeshComponent *component,
- const AttributeDomain domain,
- ResourceCollector &resources)
-{
- SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
- const bool show_only_selected = sspreadsheet->filter_flag & SPREADSHEET_FILTER_SELECTED_ONLY;
- if (object_eval->mode == OB_MODE_EDIT && show_only_selected) {
- Object *object_orig = DEG_get_original_object(object_eval);
- Vector<int64_t> &visible_rows = resources.construct<Vector<int64_t>>("visible rows");
- const Mesh *mesh_eval = component->get_for_read();
- Mesh *mesh_orig = (Mesh *)object_orig->data;
- BMesh *bm = mesh_orig->edit_mesh->bm;
- BM_mesh_elem_table_ensure(bm, BM_VERT);
-
- int *orig_indices = (int *)CustomData_get_layer(&mesh_eval->vdata, CD_ORIGINDEX);
- if (orig_indices != nullptr) {
- /* Use CD_ORIGINDEX layer if it exists. */
- auto is_vertex_selected = [&](int vertex_index) -> bool {
- const int i_orig = orig_indices[vertex_index];
- if (i_orig < 0) {
- return false;
- }
- if (i_orig >= bm->totvert) {
- return false;
- }
- BMVert *vert = bm->vtable[i_orig];
- return BM_elem_flag_test(vert, BM_ELEM_SELECT);
- };
- get_selected_indices_on_domain(*mesh_eval, domain, is_vertex_selected, visible_rows);
- }
- else if (mesh_eval->totvert == bm->totvert) {
- /* Use a simple heuristic to match original vertices to evaluated ones. */
- auto is_vertex_selected = [&](int vertex_index) -> bool {
- BMVert *vert = bm->vtable[vertex_index];
- return BM_elem_flag_test(vert, BM_ELEM_SELECT);
- };
- get_selected_indices_on_domain(*mesh_eval, domain, is_vertex_selected, visible_rows);
- }
- /* This is safe, because the vector lives in the resource collector. */
- return visible_rows.as_span();
- }
- /* No filter is used. */
- const int domain_size = component->attribute_domain_size(domain);
- return IndexRange(domain_size).as_span();
-}
-
-static GeometryComponentType get_display_component_type(const bContext *C, Object *object_eval)
-{
- SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
- if (sspreadsheet->object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_FINAL) {
- return (GeometryComponentType)sspreadsheet->geometry_component_type;
- }
- if (object_eval->type == OB_POINTCLOUD) {
- return GEO_COMPONENT_TYPE_POINT_CLOUD;
- }
- return GEO_COMPONENT_TYPE_MESH;
-}
-
-void spreadsheet_columns_from_geometry(const bContext *C,
- Object *object_eval,
- SpreadsheetColumnLayout &column_layout,
- ResourceCollector &resources)
-{
- SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
- const AttributeDomain domain = (AttributeDomain)sspreadsheet->attribute_domain;
- const GeometryComponentType component_type = get_display_component_type(C, object_eval);
-
- /* Create a resource collector that owns stuff that needs to live until drawing is done. */
- GeometrySet &geometry_set = resources.add_value(
- get_display_geometry_set(sspreadsheet, object_eval, component_type), "geometry set");
-
- const GeometryComponent *component = geometry_set.get_component_for_read(component_type);
- if (component == nullptr) {
- return;
- }
- if (component_type == GEO_COMPONENT_TYPE_INSTANCES) {
- add_columns_for_instances(
- *static_cast<const InstancesComponent *>(component), column_layout, resources);
- return;
- }
-
- if (!component->attribute_domain_supported(domain)) {
- return;
- }
-
- Vector<std::string> attribute_names = get_sorted_attribute_names_to_display(*component, domain);
-
- Vector<std::unique_ptr<SpreadsheetColumn>> &columns =
- resources.construct<Vector<std::unique_ptr<SpreadsheetColumn>>>("columns");
-
- for (StringRefNull attribute_name : attribute_names) {
- ReadAttributePtr attribute_ptr = component->attribute_try_get_for_read(attribute_name);
- ReadAttribute &attribute = *attribute_ptr;
- resources.add(std::move(attribute_ptr), "attribute");
- add_columns_for_attribute(&attribute, attribute_name, columns);
- }
-
- for (std::unique_ptr<SpreadsheetColumn> &column : columns) {
- column_layout.columns.append(column.get());
- }
-
- /* The filter below only works for mesh vertices currently. */
- Span<int64_t> visible_rows;
- if (component_type == GEO_COMPONENT_TYPE_MESH) {
- visible_rows = filter_mesh_elements_by_selection(
- C, object_eval, static_cast<const MeshComponent *>(component), domain, resources);
- }
- else {
- visible_rows = IndexRange(component->attribute_domain_size(domain)).as_span();
- }
-
- const int domain_size = component->attribute_domain_size(domain);
- column_layout.row_indices = visible_rows;
- column_layout.tot_rows = domain_size;
-}
-
-} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc
new file mode 100644
index 00000000000..8079763a339
--- /dev/null
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc
@@ -0,0 +1,256 @@
+/*
+ * 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.
+ */
+
+#include <iomanip>
+#include <sstream>
+
+#include "spreadsheet_layout.hh"
+
+#include "DNA_userdef_types.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "BLF_api.h"
+
+namespace blender::ed::spreadsheet {
+
+class SpreadsheetLayoutDrawer : public SpreadsheetDrawer {
+ private:
+ const SpreadsheetLayout &spreadsheet_layout_;
+
+ public:
+ SpreadsheetLayoutDrawer(const SpreadsheetLayout &spreadsheet_layout)
+ : spreadsheet_layout_(spreadsheet_layout)
+ {
+ tot_columns = spreadsheet_layout.columns.size();
+ tot_rows = spreadsheet_layout.row_indices.size();
+ left_column_width = spreadsheet_layout.index_column_width;
+ }
+
+ void draw_top_row_cell(int column_index, const CellDrawParams &params) const final
+ {
+ const StringRefNull name = spreadsheet_layout_.columns[column_index].values->name();
+ uiBut *but = uiDefIconTextBut(params.block,
+ UI_BTYPE_LABEL,
+ 0,
+ ICON_NONE,
+ name.c_str(),
+ params.xmin,
+ params.ymin,
+ params.width,
+ params.height,
+ nullptr,
+ 0,
+ 0,
+ 0,
+ 0,
+ nullptr);
+ /* Center-align column headers. */
+ UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT);
+ UI_but_drawflag_disable(but, UI_BUT_TEXT_RIGHT);
+ }
+
+ void draw_left_column_cell(int row_index, const CellDrawParams &params) const final
+ {
+ const int real_index = spreadsheet_layout_.row_indices[row_index];
+ std::string index_str = std::to_string(real_index);
+ uiBut *but = uiDefIconTextBut(params.block,
+ UI_BTYPE_LABEL,
+ 0,
+ ICON_NONE,
+ index_str.c_str(),
+ params.xmin,
+ params.ymin,
+ params.width,
+ params.height,
+ nullptr,
+ 0,
+ 0,
+ 0,
+ 0,
+ nullptr);
+ /* Right-align indices. */
+ UI_but_drawflag_enable(but, UI_BUT_TEXT_RIGHT);
+ UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT);
+ }
+
+ void draw_content_cell(int row_index, int column_index, const CellDrawParams &params) const final
+ {
+ const int real_index = spreadsheet_layout_.row_indices[row_index];
+ const ColumnValues &column = *spreadsheet_layout_.columns[column_index].values;
+ CellValue cell_value;
+ column.get_value(real_index, cell_value);
+
+ if (cell_value.value_int.has_value()) {
+ const int value = *cell_value.value_int;
+ const std::string value_str = std::to_string(value);
+ uiBut *but = uiDefIconTextBut(params.block,
+ UI_BTYPE_LABEL,
+ 0,
+ ICON_NONE,
+ value_str.c_str(),
+ params.xmin,
+ params.ymin,
+ params.width,
+ params.height,
+ nullptr,
+ 0,
+ 0,
+ 0,
+ 0,
+ nullptr);
+ /* Right-align Integers. */
+ UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT);
+ UI_but_drawflag_enable(but, UI_BUT_TEXT_RIGHT);
+ }
+ else if (cell_value.value_float.has_value()) {
+ const float value = *cell_value.value_float;
+ std::stringstream ss;
+ ss << std::fixed << std::setprecision(3) << value;
+ const std::string value_str = ss.str();
+ uiBut *but = uiDefIconTextBut(params.block,
+ UI_BTYPE_LABEL,
+ 0,
+ ICON_NONE,
+ value_str.c_str(),
+ params.xmin,
+ params.ymin,
+ params.width,
+ params.height,
+ nullptr,
+ 0,
+ 0,
+ 0,
+ 0,
+ nullptr);
+ /* Right-align Floats. */
+ UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT);
+ UI_but_drawflag_enable(but, UI_BUT_TEXT_RIGHT);
+ }
+ else if (cell_value.value_bool.has_value()) {
+ const bool value = *cell_value.value_bool;
+ const int icon = value ? ICON_CHECKBOX_HLT : ICON_CHECKBOX_DEHLT;
+ uiBut *but = uiDefIconTextBut(params.block,
+ UI_BTYPE_LABEL,
+ 0,
+ icon,
+ "",
+ params.xmin,
+ params.ymin,
+ params.width,
+ params.height,
+ nullptr,
+ 0,
+ 0,
+ 0,
+ 0,
+ nullptr);
+ UI_but_drawflag_disable(but, UI_BUT_ICON_LEFT);
+ }
+ else if (cell_value.value_float2.has_value()) {
+ const float2 value = *cell_value.value_float2;
+ this->draw_float_vector(params, Span(&value.x, 2));
+ }
+ else if (cell_value.value_float3.has_value()) {
+ const float3 value = *cell_value.value_float3;
+ this->draw_float_vector(params, Span(&value.x, 3));
+ }
+ else if (cell_value.value_color.has_value()) {
+ const ColorGeometry4f value = *cell_value.value_color;
+ this->draw_float_vector(params, Span(&value.r, 4));
+ }
+ else if (cell_value.value_object.has_value()) {
+ const ObjectCellValue value = *cell_value.value_object;
+ uiDefIconTextBut(params.block,
+ UI_BTYPE_LABEL,
+ 0,
+ ICON_OBJECT_DATA,
+ reinterpret_cast<const ID *const>(value.object)->name + 2,
+ params.xmin,
+ params.ymin,
+ params.width,
+ params.height,
+ nullptr,
+ 0,
+ 0,
+ 0,
+ 0,
+ nullptr);
+ }
+ else if (cell_value.value_collection.has_value()) {
+ const CollectionCellValue value = *cell_value.value_collection;
+ uiDefIconTextBut(params.block,
+ UI_BTYPE_LABEL,
+ 0,
+ ICON_OUTLINER_COLLECTION,
+ reinterpret_cast<const ID *const>(value.collection)->name + 2,
+ params.xmin,
+ params.ymin,
+ params.width,
+ params.height,
+ nullptr,
+ 0,
+ 0,
+ 0,
+ 0,
+ nullptr);
+ }
+ }
+
+ void draw_float_vector(const CellDrawParams &params, const Span<float> values) const
+ {
+ BLI_assert(!values.is_empty());
+ const float segment_width = (float)params.width / values.size();
+ for (const int i : values.index_range()) {
+ std::stringstream ss;
+ const float value = values[i];
+ ss << std::fixed << std::setprecision(3) << value;
+ const std::string value_str = ss.str();
+ uiBut *but = uiDefIconTextBut(params.block,
+ UI_BTYPE_LABEL,
+ 0,
+ ICON_NONE,
+ value_str.c_str(),
+ params.xmin + i * segment_width,
+ params.ymin,
+ segment_width,
+ params.height,
+ nullptr,
+ 0,
+ 0,
+ 0,
+ 0,
+ nullptr);
+ /* Right-align Floats. */
+ UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT);
+ UI_but_drawflag_enable(but, UI_BUT_TEXT_RIGHT);
+ }
+ }
+
+ int column_width(int column_index) const final
+ {
+ return spreadsheet_layout_.columns[column_index].width;
+ }
+};
+
+std::unique_ptr<SpreadsheetDrawer> spreadsheet_drawer_from_layout(
+ const SpreadsheetLayout &spreadsheet_layout)
+{
+ return std::make_unique<SpreadsheetLayoutDrawer>(spreadsheet_layout);
+}
+
+} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_layout.hh b/source/blender/editors/space_spreadsheet/spreadsheet_layout.hh
new file mode 100644
index 00000000000..1768af6ae09
--- /dev/null
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_layout.hh
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <optional>
+
+#include "spreadsheet_column_values.hh"
+#include "spreadsheet_draw.hh"
+
+namespace blender::ed::spreadsheet {
+
+/* Layout information for a single column. */
+struct ColumnLayout {
+ const ColumnValues *values;
+ int width;
+};
+
+/* Layout information for the entire spreadsheet. */
+struct SpreadsheetLayout {
+ Vector<ColumnLayout> columns;
+ Span<int64_t> row_indices;
+ int index_column_width = 100;
+};
+
+std::unique_ptr<SpreadsheetDrawer> spreadsheet_drawer_from_layout(
+ const SpreadsheetLayout &spreadsheet_layout);
+
+} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_text/space_text.c b/source/blender/editors/space_text/space_text.c
index 98faf89f8ae..af783051661 100644
--- a/source/blender/editors/space_text/space_text.c
+++ b/source/blender/editors/space_text/space_text.c
@@ -138,13 +138,7 @@ static void text_listener(const wmSpaceTypeListenerParams *params)
switch (wmn->data) {
case ND_DISPLAY:
- ED_area_tag_redraw(area);
- break;
case ND_CURSOR:
- if (st->text && st->text == wmn->reference) {
- text_scroll_to_cursor__area(st, area, true);
- }
-
ED_area_tag_redraw(area);
break;
}
@@ -160,13 +154,8 @@ static void text_listener(const wmSpaceTypeListenerParams *params)
ATTR_FALLTHROUGH; /* fall down to tag redraw */
case NA_ADDED:
case NA_REMOVED:
- ED_area_tag_redraw(area);
- break;
case NA_SELECTED:
- if (st->text && st->text == wmn->reference) {
- text_scroll_to_cursor__area(st, area, true);
- }
-
+ ED_area_tag_redraw(area);
break;
}
diff --git a/source/blender/editors/space_text/text_draw.c b/source/blender/editors/space_text/text_draw.c
index 8b8034124d9..17831c95575 100644
--- a/source/blender/editors/space_text/text_draw.c
+++ b/source/blender/editors/space_text/text_draw.c
@@ -1739,7 +1739,7 @@ void text_update_character_width(SpaceText *st)
/* Moves the view to the cursor location,
* also used to make sure the view isn't outside the file */
-void text_scroll_to_cursor(SpaceText *st, ARegion *region, const bool center)
+void ED_text_scroll_to_cursor(SpaceText *st, ARegion *region, const bool center)
{
Text *text;
int i, x, winx = region->winx;
@@ -1818,7 +1818,7 @@ void text_scroll_to_cursor__area(SpaceText *st, ScrArea *area, const bool center
region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW);
if (region) {
- text_scroll_to_cursor(st, region, center);
+ ED_text_scroll_to_cursor(st, region, center);
}
}
diff --git a/source/blender/editors/space_text/text_intern.h b/source/blender/editors/space_text/text_intern.h
index a33af56e11a..241e0133a8a 100644
--- a/source/blender/editors/space_text/text_intern.h
+++ b/source/blender/editors/space_text/text_intern.h
@@ -39,7 +39,6 @@ void draw_text_main(struct SpaceText *st, struct ARegion *region);
void text_update_line_edited(struct TextLine *line);
void text_update_edited(struct Text *text);
void text_update_character_width(struct SpaceText *st);
-void text_scroll_to_cursor(struct SpaceText *st, struct ARegion *region, const bool center);
void text_scroll_to_cursor__area(struct SpaceText *st, struct ScrArea *area, const bool center);
void text_update_cursor_moved(struct bContext *C);
diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c
index 526285c076a..9ec759ce4ae 100644
--- a/source/blender/editors/space_text/text_ops.c
+++ b/source/blender/editors/space_text/text_ops.c
@@ -266,8 +266,6 @@ static int text_new_exec(bContext *C, wmOperator *UNUSED(op))
PropertyRNA *prop;
text = BKE_text_add(bmain, "Text");
- /* Texts have no user by default... Only the 'real' user flag. */
- id_us_min(&text->id);
/* hook into UI */
UI_context_active_but_prop_get_templateID(C, &ptr, &prop);
@@ -506,12 +504,10 @@ static int text_unlink_exec(bContext *C, wmOperator *UNUSED(op))
if (text->id.prev) {
st->text = text->id.prev;
text_update_cursor_moved(C);
- WM_event_add_notifier(C, NC_TEXT | ND_CURSOR, st->text);
}
else if (text->id.next) {
st->text = text->id.next;
text_update_cursor_moved(C);
- WM_event_add_notifier(C, NC_TEXT | ND_CURSOR, st->text);
}
}
@@ -3200,7 +3196,7 @@ static void text_cursor_set_apply(bContext *C, wmOperator *op, const wmEvent *ev
if (event->type == TIMER) {
text_cursor_set_to_pos(st, region, event->mval[0], event->mval[1], 1);
- text_scroll_to_cursor(st, region, false);
+ ED_text_scroll_to_cursor(st, region, false);
WM_event_add_notifier(C, NC_TEXT | ND_CURSOR, st->text);
}
}
@@ -3210,7 +3206,7 @@ static void text_cursor_set_apply(bContext *C, wmOperator *op, const wmEvent *ev
if (event->type == TIMER) {
text_cursor_set_to_pos(
st, region, CLAMPIS(event->mval[0], 0, region->winx), event->mval[1], 1);
- text_scroll_to_cursor(st, region, false);
+ ED_text_scroll_to_cursor(st, region, false);
WM_event_add_notifier(C, NC_TEXT | ND_CURSOR, st->text);
}
}
@@ -3219,7 +3215,7 @@ static void text_cursor_set_apply(bContext *C, wmOperator *op, const wmEvent *ev
if (event->type != TIMER) {
text_cursor_set_to_pos(st, region, event->mval[0], event->mval[1], 1);
- text_scroll_to_cursor(st, region, false);
+ ED_text_scroll_to_cursor(st, region, false);
WM_event_add_notifier(C, NC_TEXT | ND_CURSOR, st->text);
ssel->mval_prev[0] = event->mval[0];
diff --git a/source/blender/editors/space_userpref/space_userpref.c b/source/blender/editors/space_userpref/space_userpref.c
index d4692f156d3..ceba8ca268d 100644
--- a/source/blender/editors/space_userpref/space_userpref.c
+++ b/source/blender/editors/space_userpref/space_userpref.c
@@ -79,7 +79,7 @@ static SpaceLink *userpref_create(const ScrArea *area, const Scene *UNUSED(scene
BLI_addtail(&spref->regionbase, region);
region->regiontype = RGN_TYPE_EXECUTE;
region->alignment = RGN_ALIGN_BOTTOM | RGN_SPLIT_PREV;
- region->flag |= RGN_FLAG_DYNAMIC_SIZE;
+ region->flag |= RGN_FLAG_DYNAMIC_SIZE | RGN_FLAG_HIDDEN;
/* main region */
region = MEM_callocN(sizeof(ARegion), "main region for userpref");
@@ -251,6 +251,7 @@ void ED_spacetype_userpref(void)
/* regions: execution window */
art = MEM_callocN(sizeof(ARegionType), "spacetype userpref region");
art->regionid = RGN_TYPE_EXECUTE;
+ art->prefsizey = HEADERY;
art->init = userpref_execute_region_init;
art->layout = ED_region_panels_layout;
art->draw = ED_region_panels_draw;
diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c
index 6969ecf197e..660ae9da506 100644
--- a/source/blender/editors/space_view3d/drawobject.c
+++ b/source/blender/editors/space_view3d/drawobject.c
@@ -128,7 +128,7 @@ void ED_draw_object_facemap(Depsgraph *depsgraph,
GPU_front_facing(ob->transflag & OB_NEG_SCALE);
- /* Just to create the data to pass to immediate mode, grr! */
+ /* Just to create the data to pass to immediate mode! (sigh) */
const int *facemap_data = CustomData_get_layer(&me->pdata, CD_FACEMAP);
if (facemap_data) {
GPU_blend(GPU_BLEND_ALPHA);
diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c
index e6916c34a88..001def7318e 100644
--- a/source/blender/editors/space_view3d/space_view3d.c
+++ b/source/blender/editors/space_view3d/space_view3d.c
@@ -625,6 +625,7 @@ static void view3d_ob_drop_copy(wmDrag *drag, wmDropBox *drop)
ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, ID_OB);
RNA_string_set(drop->ptr, "name", id->name + 2);
+ RNA_boolean_set(drop->ptr, "duplicate", false);
}
static void view3d_collection_drop_copy(wmDrag *drag, wmDropBox *drop)
@@ -1595,7 +1596,6 @@ static void space_view3d_refresh(const bContext *C, ScrArea *UNUSED(area))
}
const char *view3d_context_dir[] = {
- "active_base",
"active_object",
NULL,
};
@@ -1608,20 +1608,6 @@ static int view3d_context(const bContext *C, const char *member, bContextDataRes
if (CTX_data_dir(member)) {
CTX_data_dir_set(result, view3d_context_dir);
}
- else if (CTX_data_equals(member, "active_base")) {
- Scene *scene = CTX_data_scene(C);
- ViewLayer *view_layer = CTX_data_view_layer(C);
- if (view_layer->basact) {
- Object *ob = view_layer->basact->object;
- /* if hidden but in edit mode, we still display, can happen with animation */
- if ((view_layer->basact->flag & BASE_VISIBLE_DEPSGRAPH) != 0 ||
- (ob->mode != OB_MODE_OBJECT)) {
- CTX_data_pointer_set(result, &scene->id, &RNA_ObjectBase, view_layer->basact);
- }
- }
-
- return 1;
- }
else if (CTX_data_equals(member, "active_object")) {
/* In most cases the active object is the `view_layer->basact->object`.
* For the 3D view however it can be NULL when hidden.
diff --git a/source/blender/editors/space_view3d/view3d_buttons.c b/source/blender/editors/space_view3d/view3d_buttons.c
index 17f60dfc210..e42f6b5faac 100644
--- a/source/blender/editors/space_view3d/view3d_buttons.c
+++ b/source/blender/editors/space_view3d/view3d_buttons.c
@@ -1031,7 +1031,9 @@ static void v3d_editvertex_buts(uiLayout *layout, View3D *v3d, Object *ob, float
}
}
}
- BKE_nurb_test_2d(nu);
+ if (CU_IS_2D(cu)) {
+ BKE_nurb_project_2d(nu);
+ }
BKE_nurb_handles_test(nu, true, false); /* test for bezier too */
}
}
diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c
index a46d093c039..4a595c716b6 100644
--- a/source/blender/editors/space_view3d/view3d_draw.c
+++ b/source/blender/editors/space_view3d/view3d_draw.c
@@ -778,7 +778,7 @@ static void drawviewborder(Scene *scene, Depsgraph *depsgraph, ARegion *region,
}
/* draw */
- immUniformThemeColorShade(TH_VIEW_OVERLAY, 100);
+ immUniformThemeColorShadeAlpha(TH_VIEW_OVERLAY, 100, 255);
/* TODO Was using:
* UI_draw_roundbox_4fv(false, rect.xmin, rect.ymin, rect.xmax, rect.ymax, 2.0f, color);
@@ -832,52 +832,6 @@ static void drawrenderborder(ARegion *region, View3D *v3d)
immUnbindProgram();
}
-void ED_view3d_draw_depth(Depsgraph *depsgraph, ARegion *region, View3D *v3d, bool alphaoverride)
-{
- struct bThemeState theme_state;
- Scene *scene = DEG_get_evaluated_scene(depsgraph);
- RegionView3D *rv3d = region->regiondata;
-
- short flag = v3d->flag;
- float glalphaclip = U.glalphaclip;
- /* temp set drawtype to solid */
- /* Setting these temporarily is not nice */
- v3d->flag &= ~V3D_SELECT_OUTLINE;
-
- /* not that nice but means we wont zoom into billboards */
- U.glalphaclip = alphaoverride ? 0.5f : glalphaclip;
-
- /* Tools may request depth outside of regular drawing code. */
- UI_Theme_Store(&theme_state);
- UI_SetTheme(SPACE_VIEW3D, RGN_TYPE_WINDOW);
-
- ED_view3d_draw_setup_view(
- G_MAIN->wm.first, NULL, depsgraph, scene, region, v3d, NULL, NULL, NULL);
-
- /* get surface depth without bias */
- rv3d->rflag |= RV3D_ZOFFSET_DISABLED;
-
- /* Needed in cases the 3D Viewport isn't already setup. */
- WM_draw_region_viewport_ensure(region, SPACE_VIEW3D);
- WM_draw_region_viewport_bind(region);
-
- GPUViewport *viewport = WM_draw_region_get_viewport(region);
- /* When Blender is starting, a click event can trigger a depth test while the viewport is not
- * yet available. */
- if (viewport != NULL) {
- DRW_draw_depth_loop(depsgraph, region, v3d, viewport, false);
- }
-
- WM_draw_region_viewport_unbind(region);
-
- rv3d->rflag &= ~RV3D_ZOFFSET_DISABLED;
-
- U.glalphaclip = glalphaclip;
- v3d->flag = flag;
-
- UI_Theme_Restore(&theme_state);
-}
-
/** \} */
/* -------------------------------------------------------------------- */
@@ -1334,6 +1288,11 @@ static void draw_viewport_name(ARegion *region, View3D *v3d, int xoffset, int *y
name_array[name_array_len++] = IFACE_(" (Local)");
}
+ /* Indicate that clipping region is enabled. */
+ if (rv3d->rflag & RV3D_CLIPPING) {
+ name_array[name_array_len++] = IFACE_(" (Clipped)");
+ }
+
if (name_array_len > 1) {
BLI_string_join_array(tmpstr, sizeof(tmpstr), name_array, name_array_len);
name = tmpstr;
@@ -1634,7 +1593,8 @@ void view3d_main_region_draw(const bContext *C, ARegion *region)
/* No depth test for drawing action zones afterwards. */
GPU_depth_test(GPU_DEPTH_NONE);
- v3d->flag |= V3D_INVALID_BACKBUF;
+ v3d->runtime.flag &= ~V3D_RUNTIME_DEPTHBUF_OVERRIDDEN;
+ /* TODO: Clear cache? */
}
/** \} */
@@ -2062,7 +2022,6 @@ ImBuf *ED_view3d_draw_offscreen_imbuf_simple(Depsgraph *depsgraph,
source_shading_settings = shading_override;
}
memcpy(&v3d.shading, source_shading_settings, sizeof(View3DShading));
- v3d.shading.type = drawtype;
if (drawtype == OB_MATERIAL) {
v3d.shading.flag = V3D_SHADING_SCENE_WORLD | V3D_SHADING_SCENE_LIGHTS;
@@ -2072,8 +2031,17 @@ ImBuf *ED_view3d_draw_offscreen_imbuf_simple(Depsgraph *depsgraph,
v3d.shading.flag = V3D_SHADING_SCENE_WORLD_RENDER | V3D_SHADING_SCENE_LIGHTS_RENDER;
v3d.shading.render_pass = SCE_PASS_COMBINED;
}
+ else if (drawtype == OB_TEXTURE) {
+ drawtype = OB_SOLID;
+ v3d.shading.light = V3D_LIGHTING_STUDIO;
+ v3d.shading.color_type = V3D_SHADING_TEXTURE_COLOR;
+ }
+ v3d.shading.type = drawtype;
v3d.flag2 = V3D_HIDE_OVERLAYS;
+ /* HACK: When rendering gpencil objects this opacity is used to mix vertex colors in when not in
+ * render mode. */
+ v3d.overlay.gpencil_vertex_paint_opacity = 1.0f;
if (draw_flags & V3D_OFSDRAW_SHOW_ANNOTATION) {
v3d.flag2 |= V3D_SHOW_ANNOTATION;
@@ -2112,7 +2080,7 @@ ImBuf *ED_view3d_draw_offscreen_imbuf_simple(Depsgraph *depsgraph,
return ED_view3d_draw_offscreen_imbuf(depsgraph,
scene,
- drawtype,
+ v3d.shading.type,
&v3d,
&region,
width,
@@ -2146,8 +2114,15 @@ static bool view3d_clipping_test(const float co[3], const float clip[6][4])
return true;
}
-/* For 'local' ED_view3d_clipping_local must run first
- * then all comparisons can be done in localspace. */
+/**
+ * Return true when `co` is hidden by the 3D views clipping planes.
+ *
+ * \param local: When true use local (object-space) #ED_view3d_clipping_local must run first,
+ * then all comparisons can be done in local-space.
+ * \return True when `co` is outside all clipping planes.
+ *
+ * \note Callers should check #RV3D_CLIPPING_ENABLED first.
+ */
bool ED_view3d_clipping_test(const RegionView3D *rv3d, const float co[3], const bool is_local)
{
return view3d_clipping_test(co, is_local ? rv3d->clip_local : rv3d->clip);
@@ -2168,6 +2143,10 @@ static void validate_object_select_id(struct Depsgraph *depsgraph,
View3D *v3d,
Object *obact)
{
+ /* TODO: Use a flag in the selection engine itself. */
+ if (v3d->runtime.flag & V3D_RUNTIME_DEPTHBUF_OVERRIDDEN) {
+ return;
+ }
Object *obact_eval = DEG_get_evaluated_object(depsgraph, obact);
BLI_assert(region->regiontype == RGN_TYPE_WINDOW);
@@ -2186,11 +2165,7 @@ static void validate_object_select_id(struct Depsgraph *depsgraph,
/* do nothing */
}
else {
- v3d->flag &= ~V3D_INVALID_BACKBUF;
- return;
- }
-
- if (!(v3d->flag & V3D_INVALID_BACKBUF)) {
+ v3d->runtime.flag |= V3D_RUNTIME_DEPTHBUF_OVERRIDDEN;
return;
}
@@ -2199,9 +2174,7 @@ static void validate_object_select_id(struct Depsgraph *depsgraph,
DRW_select_buffer_context_create(&base, 1, -1);
}
- /* TODO: Create a flag in `DRW_manager` because the drawing is no longer
- * made on the back-buffer in this case. */
- v3d->flag &= ~V3D_INVALID_BACKBUF;
+ v3d->runtime.flag |= V3D_RUNTIME_DEPTHBUF_OVERRIDDEN;
}
/* TODO: Creating, attaching texture, and destroying a framebuffer is quite slow.
@@ -2228,26 +2201,7 @@ static void view3d_opengl_read_Z_pixels(GPUViewport *viewport, rcti *rect, void
void ED_view3d_select_id_validate(ViewContext *vc)
{
- /* TODO: Create a flag in `DRW_manager` because the drawing is no longer
- * made on the back-buffer in this case. */
- if (vc->v3d->flag & V3D_INVALID_BACKBUF) {
- validate_object_select_id(vc->depsgraph, vc->view_layer, vc->region, vc->v3d, vc->obact);
- }
-}
-
-void ED_view3d_backbuf_depth_validate(ViewContext *vc)
-{
- if (vc->v3d->flag & V3D_INVALID_BACKBUF) {
- ARegion *region = vc->region;
- Object *obact_eval = DEG_get_evaluated_object(vc->depsgraph, vc->obact);
-
- if (obact_eval && ((obact_eval->base_flag & BASE_VISIBLE_DEPSGRAPH) != 0)) {
- GPUViewport *viewport = WM_draw_region_get_viewport(region);
- DRW_draw_depth_object(vc->scene, vc->region, vc->v3d, viewport, obact_eval);
- }
-
- vc->v3d->flag &= ~V3D_INVALID_BACKBUF;
- }
+ validate_object_select_id(vc->depsgraph, vc->view_layer, vc->region, vc->v3d, vc->obact);
}
/**
@@ -2319,7 +2273,7 @@ void view3d_update_depths_rect(ARegion *region, ViewDepths *d, rcti *rect)
}
/* Note, with nouveau drivers the glReadPixels() is very slow. T24339. */
-void ED_view3d_depth_update(ARegion *region)
+static void view3d_depth_cache_update(ARegion *region)
{
RegionView3D *rv3d = region->regiondata;
@@ -2341,13 +2295,9 @@ void ED_view3d_depth_update(ARegion *region)
if (d->damaged) {
GPUViewport *viewport = WM_draw_region_get_viewport(region);
- rcti r = {
- .xmin = 0,
- .xmax = d->w,
- .ymin = 0,
- .ymax = d->h,
- };
- view3d_opengl_read_Z_pixels(viewport, &r, d->depths);
+ DefaultFramebufferList *fbl = GPU_viewport_framebuffer_list_get(viewport);
+ GPU_framebuffer_read_depth(fbl->depth_only_fb, 0, 0, d->w, d->h, GPU_DATA_FLOAT, d->depths);
+
/* Assumed to be this as they are never changed. */
d->depth_range[0] = 0.0;
d->depth_range[1] = 1.0;
@@ -2380,19 +2330,81 @@ float view3d_depth_near(ViewDepths *d)
return far == far_real ? FLT_MAX : far;
}
-void ED_view3d_draw_depth_gpencil(Depsgraph *depsgraph, Scene *scene, ARegion *region, View3D *v3d)
+/**
+ * Redraw the viewport depth buffer.
+ *
+ * \param mode: V3D_DEPTH_NO_GPENCIL - Redraw viewport without Grease Pencil and Annotations.
+ * V3D_DEPTH_GPENCIL_ONLY - Redraw viewport with Grease Pencil and Annotations only.
+ * V3D_DEPTH_OBJECT_ONLY - Redraw viewport with active object only.
+ * \param update_cache: If true, store the entire depth buffer in #rv3d->depths.
+ */
+void ED_view3d_depth_override(Depsgraph *depsgraph,
+ ARegion *region,
+ View3D *v3d,
+ Object *obact,
+ eV3DDepthOverrideMode mode,
+ bool update_cache)
{
- /* Setup view matrix. */
- ED_view3d_draw_setup_view(NULL, NULL, depsgraph, scene, region, v3d, NULL, NULL, NULL);
+ if (v3d->runtime.flag & V3D_RUNTIME_DEPTHBUF_OVERRIDDEN) {
+ return;
+ }
+ struct bThemeState theme_state;
+ Scene *scene = DEG_get_evaluated_scene(depsgraph);
+ RegionView3D *rv3d = region->regiondata;
- GPU_clear_depth(1.0f);
+ short flag = v3d->flag;
+ /* temp set drawtype to solid */
+ /* Setting these temporarily is not nice */
+ v3d->flag &= ~V3D_SELECT_OUTLINE;
- GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
+ /* Tools may request depth outside of regular drawing code. */
+ UI_Theme_Store(&theme_state);
+ UI_SetTheme(SPACE_VIEW3D, RGN_TYPE_WINDOW);
+
+ ED_view3d_draw_setup_view(
+ G_MAIN->wm.first, NULL, depsgraph, scene, region, v3d, NULL, NULL, NULL);
+
+ /* get surface depth without bias */
+ rv3d->rflag |= RV3D_ZOFFSET_DISABLED;
+
+ /* Needed in cases the 3D Viewport isn't already setup. */
+ WM_draw_region_viewport_ensure(region, SPACE_VIEW3D);
+ WM_draw_region_viewport_bind(region);
GPUViewport *viewport = WM_draw_region_get_viewport(region);
- DRW_draw_depth_loop_gpencil(depsgraph, region, v3d, viewport);
+ /* When Blender is starting, a click event can trigger a depth test while the viewport is not
+ * yet available. */
+ if (viewport != NULL) {
+ switch (mode) {
+ case V3D_DEPTH_NO_GPENCIL:
+ DRW_draw_depth_loop(depsgraph, region, v3d, viewport);
+ break;
+ case V3D_DEPTH_GPENCIL_ONLY:
+ DRW_draw_depth_loop_gpencil(depsgraph, region, v3d, viewport);
+ break;
+ case V3D_DEPTH_OBJECT_ONLY:
+ DRW_draw_depth_object(
+ scene, region, v3d, viewport, DEG_get_evaluated_object(depsgraph, obact));
+ break;
+ }
- GPU_depth_test(GPU_DEPTH_NONE);
+ if (rv3d->depths != NULL) {
+ rv3d->depths->damaged = true;
+ /* TODO: Clear cache? */
+ }
+ if (update_cache) {
+ view3d_depth_cache_update(region);
+ }
+ }
+
+ WM_draw_region_viewport_unbind(region);
+
+ rv3d->rflag &= ~RV3D_ZOFFSET_DISABLED;
+
+ v3d->flag = flag;
+ v3d->runtime.flag |= V3D_RUNTIME_DEPTHBUF_OVERRIDDEN;
+
+ UI_Theme_Restore(&theme_state);
}
/** \} */
diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c
index dc590833368..8b6d0e9ee04 100644
--- a/source/blender/editors/space_view3d/view3d_edit.c
+++ b/source/blender/editors/space_view3d/view3d_edit.c
@@ -2507,7 +2507,7 @@ static void view_dolly_to_vector_3d(ARegion *region,
madd_v3_v3v3fl(rv3d->ofs, orig_ofs, dvec, -(1.0f - dfac));
}
-static void viewdolly_apply(ViewOpsData *vod, const int xy[2], const short zoom_invert)
+static void viewdolly_apply(ViewOpsData *vod, const int xy[2], const bool zoom_invert)
{
float zfac = 1.0;
@@ -3628,9 +3628,8 @@ static int view3d_zoom_border_exec(bContext *C, wmOperator *op)
ED_view3d_dist_range_get(v3d, dist_range);
- /* Get Z Depths, needed for perspective, nice for ortho */
- ED_view3d_draw_depth(CTX_data_ensure_evaluated_depsgraph(C), region, v3d, true);
-
+ ED_view3d_depth_override(
+ CTX_data_ensure_evaluated_depsgraph(C), region, v3d, NULL, V3D_DEPTH_NO_GPENCIL, false);
{
/* avoid allocating the whole depth buffer */
ViewDepths depth_temp = {0};
@@ -3667,8 +3666,8 @@ static int view3d_zoom_border_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
/* convert border to 3d coordinates */
- if ((!ED_view3d_unproject(region, cent[0], cent[1], depth_close, p)) ||
- (!ED_view3d_unproject(region, rect.xmin, rect.ymin, depth_close, p_corner))) {
+ if ((!ED_view3d_unproject_v3(region, cent[0], cent[1], depth_close, p)) ||
+ (!ED_view3d_unproject_v3(region, rect.xmin, rect.ymin, depth_close, p_corner))) {
return OPERATOR_CANCELLED;
}
@@ -3691,7 +3690,8 @@ static int view3d_zoom_border_exec(bContext *C, wmOperator *op)
new_dist = rv3d->dist;
/* convert the drawn rectangle into 3d space */
- if (depth_close != FLT_MAX && ED_view3d_unproject(region, cent[0], cent[1], depth_close, p)) {
+ if (depth_close != FLT_MAX &&
+ ED_view3d_unproject_v3(region, cent[0], cent[1], depth_close, p)) {
negate_v3_v3(new_ofs, p);
}
else {
@@ -5066,7 +5066,7 @@ void ED_view3d_cursor3d_position_rotation(bContext *C,
SCE_SNAP_MODE_FACE,
&(const struct SnapObjectParams){
.snap_select = SNAP_ALL,
- .use_object_edit_cage = false,
+ .edit_mode_type = SNAP_GEOM_FINAL,
.use_occlusion_test = true,
},
mval_fl,
diff --git a/source/blender/editors/space_view3d/view3d_gizmo_navigate.c b/source/blender/editors/space_view3d/view3d_gizmo_navigate.c
index 6fa974cdb09..cd0576f2d21 100644
--- a/source/blender/editors/space_view3d/view3d_gizmo_navigate.c
+++ b/source/blender/editors/space_view3d/view3d_gizmo_navigate.c
@@ -80,32 +80,32 @@ static struct NavigateGizmoInfo g_navigate_params[GZ_INDEX_TOTAL] = {
{
.opname = "VIEW3D_OT_move",
.gizmo = "GIZMO_GT_button_2d",
- ICON_VIEW_PAN,
+ .icon = ICON_VIEW_PAN,
},
{
.opname = "VIEW3D_OT_rotate",
.gizmo = "VIEW3D_GT_navigate_rotate",
- 0,
+ .icon = ICON_NONE,
},
{
.opname = "VIEW3D_OT_zoom",
.gizmo = "GIZMO_GT_button_2d",
- ICON_VIEW_ZOOM,
+ .icon = ICON_VIEW_ZOOM,
},
{
.opname = "VIEW3D_OT_view_persportho",
.gizmo = "GIZMO_GT_button_2d",
- ICON_VIEW_PERSPECTIVE,
+ .icon = ICON_VIEW_PERSPECTIVE,
},
{
.opname = "VIEW3D_OT_view_persportho",
.gizmo = "GIZMO_GT_button_2d",
- ICON_VIEW_ORTHO,
+ .icon = ICON_VIEW_ORTHO,
},
{
.opname = "VIEW3D_OT_view_camera",
.gizmo = "GIZMO_GT_button_2d",
- ICON_VIEW_CAMERA,
+ .icon = ICON_VIEW_CAMERA,
},
};
@@ -116,7 +116,7 @@ struct NavigateWidgetGroup {
rcti rect_visible;
struct {
char is_persp;
- char is_camera;
+ bool is_camera;
char viewlock;
} rv3d;
} state;
@@ -177,7 +177,7 @@ static void WIDGETGROUP_navigate_setup(const bContext *C, wmGizmoGroup *gzgroup)
/* may be overwritten later */
gz->scale_basis = GIZMO_MINI_SIZE / 2.0f;
- if (info->icon != 0) {
+ if (info->icon != ICON_NONE) {
PropertyRNA *prop = RNA_struct_find_property(gz->ptr, "icon");
RNA_property_enum_set(gz->ptr, prop, info->icon);
RNA_enum_set(
@@ -250,6 +250,9 @@ static void WIDGETGROUP_navigate_draw_prepare(const bContext *C, wmGizmoGroup *g
const rcti *rect_visible = ED_region_visible_rect(region);
+ /* Ensure types match so bits are never lost on assignment. */
+ CHECK_TYPE_PAIR(navgroup->state.rv3d.viewlock, rv3d->viewlock);
+
if ((navgroup->state.rect_visible.xmax == rect_visible->xmax) &&
(navgroup->state.rect_visible.ymax == rect_visible->ymax) &&
(navgroup->state.rv3d.is_persp == rv3d->is_persp) &&
diff --git a/source/blender/editors/space_view3d/view3d_gizmo_navigate_type.c b/source/blender/editors/space_view3d/view3d_gizmo_navigate_type.c
index 4ac16e8fbe8..05ea35f114f 100644
--- a/source/blender/editors/space_view3d/view3d_gizmo_navigate_type.c
+++ b/source/blender/editors/space_view3d/view3d_gizmo_navigate_type.c
@@ -372,7 +372,7 @@ static int gizmo_axis_cursor_get(wmGizmo *UNUSED(gz))
return WM_CURSOR_DEFAULT;
}
-static void gizmo_axis_screen_bounds_get(bContext *C, wmGizmo *gz, rcti *r_bounding_box)
+static bool gizmo_axis_screen_bounds_get(bContext *C, wmGizmo *gz, rcti *r_bounding_box)
{
ScrArea *area = CTX_wm_area(C);
const float rad = WIDGET_RADIUS;
@@ -380,6 +380,7 @@ static void gizmo_axis_screen_bounds_get(bContext *C, wmGizmo *gz, rcti *r_bound
r_bounding_box->ymin = gz->matrix_basis[3][1] + area->totrct.ymin - rad;
r_bounding_box->xmax = r_bounding_box->xmin + rad;
r_bounding_box->ymax = r_bounding_box->ymin + rad;
+ return true;
}
void VIEW3D_GT_navigate_rotate(wmGizmoType *gzt)
diff --git a/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c b/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c
index 298a2a7a824..07c3b6bd1d8 100644
--- a/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c
+++ b/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c
@@ -154,10 +154,10 @@ static int gizmo_preselect_elem_test_select(bContext *C, wmGizmo *gz, const int
* Only pre-select a vertex when the cursor is really close to it. */
if (eve_test) {
BMVert *vert = (BMVert *)eve_test;
- float vert_p_co[3], vert_co[3];
+ float vert_p_co[2], vert_co[3];
const float mval_f[2] = {UNPACK2(vc.mval)};
mul_v3_m4v3(vert_co, gz_ele->bases[base_index_vert]->object->obmat, vert->co);
- ED_view3d_project(vc.region, vert_co, vert_p_co);
+ ED_view3d_project_v2(vc.region, vert_co, vert_p_co);
float len = len_v2v2(vert_p_co, mval_f);
if (len < 35) {
best.ele = (BMElem *)eve_test;
diff --git a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c
index 870996ddefa..0d568363b00 100644
--- a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c
+++ b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c
@@ -338,7 +338,7 @@ static bool view3d_ruler_item_mousemove(struct Depsgraph *depsgraph,
SCE_SNAP_MODE_FACE,
&(const struct SnapObjectParams){
.snap_select = SNAP_ALL,
- .use_object_edit_cage = true,
+ .edit_mode_type = SNAP_GEOM_CAGE,
},
mval_fl,
NULL,
@@ -352,7 +352,7 @@ static bool view3d_ruler_item_mousemove(struct Depsgraph *depsgraph,
depsgraph,
&(const struct SnapObjectParams){
.snap_select = SNAP_ALL,
- .use_object_edit_cage = true,
+ .edit_mode_type = SNAP_GEOM_CAGE,
},
ray_start,
ray_normal,
@@ -387,7 +387,11 @@ static bool view3d_ruler_item_mousemove(struct Depsgraph *depsgraph,
}
ED_gizmotypes_snap_3d_update(
- snap_gizmo, depsgraph, ruler_info->region, v3d, ruler_info->wm, mval_fl, co, NULL);
+ snap_gizmo, depsgraph, ruler_info->region, v3d, ruler_info->wm, mval_fl);
+
+ if (ED_gizmotypes_snap_3d_is_enabled(snap_gizmo)) {
+ ED_gizmotypes_snap_3d_data_get(snap_gizmo, co, NULL, NULL, NULL);
+ }
}
return true;
}
@@ -442,7 +446,7 @@ static bool view3d_ruler_to_gpencil(bContext *C, wmGizmoGroup *gzgroup)
gpl = view3d_ruler_layer_get(gpd);
if (gpl == NULL) {
- gpl = BKE_gpencil_layer_addnew(gpd, ruler_name, false);
+ gpl = BKE_gpencil_layer_addnew(gpd, ruler_name, false, false);
copy_v4_v4(gpl->color, U.gpencil_new_layer_col);
gpl->thickness = 1;
gpl->flag |= GP_LAYER_HIDE | GP_LAYER_IS_RULER;
@@ -1120,12 +1124,13 @@ static void WIDGETGROUP_ruler_setup(const bContext *C, wmGizmoGroup *gzgroup)
const wmGizmoType *gzt_snap;
gzt_snap = WM_gizmotype_find("GIZMO_GT_snap_3d", true);
gizmo = WM_gizmo_new_ptr(gzt_snap, gzgroup, NULL);
+
RNA_enum_set(gizmo->ptr,
"snap_elements_force",
(SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE |
/* SCE_SNAP_MODE_VOLUME | SCE_SNAP_MODE_GRID | SCE_SNAP_MODE_INCREMENT | */
SCE_SNAP_MODE_EDGE_PERPENDICULAR | SCE_SNAP_MODE_EDGE_MIDPOINT));
-
+ ED_gizmotypes_snap_3d_flag_set(gizmo, ED_SNAPGIZMO_SNAP_EDIT_GEOM_CAGE);
WM_gizmo_set_color(gizmo, (float[4]){1.0f, 1.0f, 1.0f, 1.0f});
wmOperatorType *ot = WM_operatortype_find("VIEW3D_OT_ruler_add", true);
diff --git a/source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c b/source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c
index 3f258a0699a..ad91af73a71 100644
--- a/source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c
+++ b/source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c
@@ -143,7 +143,7 @@ static void WIDGETGROUP_tool_generic_refresh(const bContext *C, wmGizmoGroup *gz
const bool hide = ED_transform_calc_gizmo_stats(C,
&(struct TransformCalcParams){
.use_only_center = true,
- .orientation_type = orientation + 1,
+ .orientation_index = orientation + 1,
},
&tbounds) == 0;
diff --git a/source/blender/editors/space_view3d/view3d_intern.h b/source/blender/editors/space_view3d/view3d_intern.h
index 118ec2425fc..6f07cb8b44d 100644
--- a/source/blender/editors/space_view3d/view3d_intern.h
+++ b/source/blender/editors/space_view3d/view3d_intern.h
@@ -122,17 +122,7 @@ void VIEW3D_OT_walk(struct wmOperatorType *ot);
void view3d_main_region_draw(const struct bContext *C, struct ARegion *region);
void view3d_draw_region_info(const struct bContext *C, struct ARegion *region);
-void ED_view3d_draw_depth(struct Depsgraph *depsgraph,
- struct ARegion *region,
- View3D *v3d,
- bool alphaoverride);
-
/* view3d_draw_legacy.c */
-void ED_view3d_draw_depth_gpencil(struct Depsgraph *depsgraph,
- Scene *scene,
- struct ARegion *region,
- View3D *v3d);
-
void ED_view3d_draw_select_loop(struct Depsgraph *depsgraph,
ViewContext *vc,
Scene *scene,
@@ -156,6 +146,7 @@ void VIEW3D_OT_select_circle(struct wmOperatorType *ot);
void VIEW3D_OT_select_box(struct wmOperatorType *ot);
void VIEW3D_OT_select_lasso(struct wmOperatorType *ot);
void VIEW3D_OT_select_menu(struct wmOperatorType *ot);
+void VIEW3D_OT_bone_select_menu(struct wmOperatorType *ot);
/* view3d_view.c */
void VIEW3D_OT_smoothview(struct wmOperatorType *ot);
diff --git a/source/blender/editors/space_view3d/view3d_ops.c b/source/blender/editors/space_view3d/view3d_ops.c
index 344168e895b..56dedbbdbb2 100644
--- a/source/blender/editors/space_view3d/view3d_ops.c
+++ b/source/blender/editors/space_view3d/view3d_ops.c
@@ -187,6 +187,7 @@ void view3d_operatortypes(void)
WM_operatortype_append(VIEW3D_OT_cursor3d);
WM_operatortype_append(VIEW3D_OT_select_lasso);
WM_operatortype_append(VIEW3D_OT_select_menu);
+ WM_operatortype_append(VIEW3D_OT_bone_select_menu);
WM_operatortype_append(VIEW3D_OT_camera_to_view);
WM_operatortype_append(VIEW3D_OT_camera_to_view_selected);
WM_operatortype_append(VIEW3D_OT_object_as_camera);
diff --git a/source/blender/editors/space_view3d/view3d_placement.c b/source/blender/editors/space_view3d/view3d_placement.c
index 48f274ca71b..e602521f6a2 100644
--- a/source/blender/editors/space_view3d/view3d_placement.c
+++ b/source/blender/editors/space_view3d/view3d_placement.c
@@ -252,7 +252,7 @@ static int dot_v3_array_find_max_index(const float dirs[][3],
}
/**
- * Re-order \a mat so \a axis_align uses it's own axis which is closest to \a v.
+ * Re-order \a mat so \a axis_align uses its own axis which is closest to \a v.
*/
static bool mat3_align_axis_to_v3(float mat[3][3], const int axis_align, const float v[3])
{
@@ -323,7 +323,7 @@ static bool idp_poject_surface_normal(SnapObjectContext *snap_context,
SCE_SNAP_MODE_FACE,
&(const struct SnapObjectParams){
.snap_select = SNAP_ALL,
- .use_object_edit_cage = true,
+ .edit_mode_type = SNAP_GEOM_EDIT,
},
mval_fl,
NULL,
@@ -941,7 +941,7 @@ static void view3d_interactive_add_calc_plane(bContext *C,
SCE_SNAP_MODE_FACE,
&(const struct SnapObjectParams){
.snap_select = SNAP_ALL,
- .use_object_edit_cage = true,
+ .edit_mode_type = SNAP_GEOM_EDIT,
},
mval_fl,
NULL,
@@ -1058,9 +1058,7 @@ static void view3d_interactive_add_begin(bContext *C, wmOperator *op, const wmEv
ipd->region,
ipd->v3d,
G_MAIN->wm.first,
- mval_fl,
- NULL,
- NULL);
+ mval_fl);
}
}
@@ -1474,10 +1472,27 @@ static int view3d_interactive_add_modal(bContext *C, wmOperator *op, const wmEve
RNA_float_set_array(&op_props, "rotation", rotation);
RNA_float_set_array(&op_props, "location", location);
RNA_float_set_array(&op_props, "scale", scale);
- /* Always use default size here. */
+
+ /* Always use the defaults here since desired bounds have been set interactively, it does
+ * not make sense to use a different values from a previous command. */
if (ipd->primitive_type == PLACE_PRIMITIVE_TYPE_CUBE) {
RNA_float_set(&op_props, "size", 2.0f);
}
+ if (ELEM(ipd->primitive_type,
+ PLACE_PRIMITIVE_TYPE_CYLINDER,
+ PLACE_PRIMITIVE_TYPE_SPHERE_UV,
+ PLACE_PRIMITIVE_TYPE_SPHERE_ICO)) {
+ RNA_float_set(&op_props, "radius", 1.0f);
+ }
+ if (ELEM(
+ ipd->primitive_type, PLACE_PRIMITIVE_TYPE_CYLINDER, PLACE_PRIMITIVE_TYPE_CONE)) {
+ RNA_float_set(&op_props, "depth", 2.0f);
+ }
+ if (ipd->primitive_type == PLACE_PRIMITIVE_TYPE_CONE) {
+ RNA_float_set(&op_props, "radius1", 1.0f);
+ RNA_float_set(&op_props, "radius2", 0.0f);
+ }
+
WM_operator_name_call_ptr(C, ot, WM_OP_EXEC_DEFAULT, &op_props);
WM_operator_properties_free(&op_props);
}
@@ -1501,18 +1516,17 @@ static int view3d_interactive_add_modal(bContext *C, wmOperator *op, const wmEve
ipd->is_snap_found = false;
if (ipd->use_snap) {
if (ipd->snap_gizmo != NULL) {
- ED_gizmotypes_snap_3d_toggle_set(ipd->snap_gizmo, ipd->use_snap);
+ ED_gizmotypes_snap_3d_flag_set(ipd->snap_gizmo, ED_SNAPGIZMO_TOGGLE_ALWAYS_TRUE);
if (ED_gizmotypes_snap_3d_update(ipd->snap_gizmo,
CTX_data_ensure_evaluated_depsgraph(C),
ipd->region,
ipd->v3d,
G_MAIN->wm.first,
- mval_fl,
- ipd->snap_co,
- NULL)) {
+ mval_fl)) {
+ ED_gizmotypes_snap_3d_data_get(ipd->snap_gizmo, ipd->snap_co, NULL, NULL, NULL);
ipd->is_snap_found = true;
}
- ED_gizmotypes_snap_3d_toggle_clear(ipd->snap_gizmo);
+ ED_gizmotypes_snap_3d_flag_clear(ipd->snap_gizmo, ED_SNAPGIZMO_TOGGLE_ALWAYS_TRUE);
}
}
@@ -1748,7 +1762,7 @@ static void WIDGETGROUP_placement_setup(const bContext *UNUSED(C), wmGizmoGroup
gizmo->flag |= WM_GIZMO_HIDDEN_KEYMAP;
}
- /* Sets the gizmos custom-data which has it's own free callback. */
+ /* Sets the gizmos custom-data which has its own free callback. */
preview_plane_cursor_setup(gzgroup);
}
@@ -2047,7 +2061,7 @@ static void cursor_plane_draw(bContext *C, int x, int y, void *customdata)
GPU_matrix_projection_set(rv3d->winmat);
GPU_matrix_set(rv3d->viewmat);
- const float scale_mod = U.gizmo_size * 2 * U.dpi_fac;
+ const float scale_mod = U.gizmo_size * 2 * U.dpi_fac / U.pixelsize;
float final_scale = (scale_mod * pixel_size);
diff --git a/source/blender/editors/space_view3d/view3d_project.c b/source/blender/editors/space_view3d/view3d_project.c
index 24d34e514c5..7547f8ee434 100644
--- a/source/blender/editors/space_view3d/view3d_project.c
+++ b/source/blender/editors/space_view3d/view3d_project.c
@@ -809,23 +809,30 @@ void ED_view3d_ob_project_mat_get_from_obmat(const RegionView3D *rv3d,
/**
* Convert between region relative coordinates (x,y) and depth component z and
* a point in world space. */
-void ED_view3d_project(const struct ARegion *region, const float world[3], float r_region_co[3])
+void ED_view3d_project_v3(const struct ARegion *region, const float world[3], float r_region_co[3])
{
/* Viewport is set up to make coordinates relative to the region, not window. */
RegionView3D *rv3d = region->regiondata;
const int viewport[4] = {0, 0, region->winx, region->winy};
+ GPU_matrix_project_3fv(world, rv3d->viewmat, rv3d->winmat, viewport, r_region_co);
+}
- GPU_matrix_project(world, rv3d->viewmat, rv3d->winmat, viewport, r_region_co);
+void ED_view3d_project_v2(const struct ARegion *region, const float world[3], float r_region_co[2])
+{
+ /* Viewport is set up to make coordinates relative to the region, not window. */
+ RegionView3D *rv3d = region->regiondata;
+ const int viewport[4] = {0, 0, region->winx, region->winy};
+ GPU_matrix_project_2fv(world, rv3d->viewmat, rv3d->winmat, viewport, r_region_co);
}
-bool ED_view3d_unproject(
+bool ED_view3d_unproject_v3(
const struct ARegion *region, float regionx, float regiony, float regionz, float world[3])
{
RegionView3D *rv3d = region->regiondata;
const int viewport[4] = {0, 0, region->winx, region->winy};
const float region_co[3] = {regionx, regiony, regionz};
- return GPU_matrix_unproject(region_co, rv3d->viewmat, rv3d->winmat, viewport, world);
+ return GPU_matrix_unproject_3fv(region_co, rv3d->viewmat, rv3d->winmat, viewport, world);
}
/** \} */
diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c
index 3166b818d3c..757ed13ac28 100644
--- a/source/blender/editors/space_view3d/view3d_select.c
+++ b/source/blender/editors/space_view3d/view3d_select.c
@@ -96,6 +96,7 @@
#include "ED_select_utils.h"
#include "UI_interface.h"
+#include "UI_resources.h"
#include "GPU_matrix.h"
@@ -1432,6 +1433,8 @@ void VIEW3D_OT_select_lasso(wmOperatorType *ot)
typedef struct SelMenuItemF {
char idname[MAX_ID_NAME - 2];
int icon;
+ Base *base_ptr;
+ void *item_ptr;
} SelMenuItemF;
#define SEL_MENU_SIZE 22
@@ -1580,7 +1583,7 @@ static Base *object_mouse_select_menu(bContext *C,
{
short baseCount = 0;
bool ok;
- LinkNode *linklist = NULL;
+ LinkNodePair linklist = {NULL, NULL};
/* handle base->object->select_id */
CTX_DATA_BEGIN (C, Base *, base, selectable_bases) {
@@ -1608,7 +1611,7 @@ static Base *object_mouse_select_menu(bContext *C,
if (ok) {
baseCount++;
- BLI_linklist_prepend(&linklist, base);
+ BLI_linklist_append(&linklist, base);
if (baseCount == SEL_MENU_SIZE) {
break;
@@ -1621,8 +1624,8 @@ static Base *object_mouse_select_menu(bContext *C,
return NULL;
}
if (baseCount == 1) {
- Base *base = (Base *)linklist->link;
- BLI_linklist_free(linklist, NULL);
+ Base *base = (Base *)linklist.list->link;
+ BLI_linklist_free(linklist.list, NULL);
return base;
}
@@ -1632,7 +1635,7 @@ static Base *object_mouse_select_menu(bContext *C,
memset(object_mouse_select_menu_data, 0, sizeof(object_mouse_select_menu_data));
- for (node = linklist, i = 0; node; node = node->next, i++) {
+ for (node = linklist.list, i = 0; node; node = node->next, i++) {
Base *base = node->link;
Object *ob = base->object;
const char *name = ob->id.name + 2;
@@ -1651,10 +1654,231 @@ static Base *object_mouse_select_menu(bContext *C,
WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr);
WM_operator_properties_free(&ptr);
- BLI_linklist_free(linklist, NULL);
+ BLI_linklist_free(linklist.list, NULL);
return NULL;
}
+static int bone_select_menu_exec(bContext *C, wmOperator *op)
+{
+ const int name_index = RNA_enum_get(op->ptr, "name");
+ const bool extend = RNA_boolean_get(op->ptr, "extend");
+ const bool deselect = RNA_boolean_get(op->ptr, "deselect");
+ const bool toggle = RNA_boolean_get(op->ptr, "toggle");
+
+ View3D *v3d = CTX_wm_view3d(C);
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+ const Base *oldbasact = BASACT(view_layer);
+
+ Base *basact = object_mouse_select_menu_data[name_index].base_ptr;
+
+ if (basact == NULL) {
+ return OPERATOR_CANCELLED;
+ }
+
+ BLI_assert(BASE_SELECTABLE(v3d, basact));
+
+ if (basact->object->mode == OB_MODE_EDIT) {
+ EditBone *ebone = (EditBone *)object_mouse_select_menu_data[name_index].item_ptr;
+ ED_armature_edit_select_pick_bone(C, basact, ebone, BONE_SELECTED, extend, deselect, toggle);
+ }
+ else {
+ bPoseChannel *pchan = (bPoseChannel *)object_mouse_select_menu_data[name_index].item_ptr;
+ ED_armature_pose_select_pick_bone(
+ view_layer, v3d, basact->object, pchan->bone, extend, deselect, toggle);
+ }
+
+ /* Weak but ensures we activate the menu again before using the enum. */
+ memset(object_mouse_select_menu_data, 0, sizeof(object_mouse_select_menu_data));
+
+ /* We make the armature selected:
+ * Not-selected active object in posemode won't work well for tools. */
+ ED_object_base_select(basact, BA_SELECT);
+
+ WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, basact->object);
+ WM_event_add_notifier(C, NC_OBJECT | ND_BONE_ACTIVE, basact->object);
+
+ /* In weight-paint, we use selected bone to select vertex-group,
+ * so don't switch to new active object. */
+ if (oldbasact && (oldbasact->object->mode & OB_MODE_ALL_WEIGHT_PAINT)) {
+ /* Prevent activating.
+ * Selection causes this to be considered the 'active' pose in weight-paint mode.
+ * Eventually this limitation may be removed.
+ * For now, de-select all other pose objects deforming this mesh. */
+ ED_armature_pose_select_in_wpaint_mode(view_layer, basact);
+
+ basact = NULL;
+ }
+
+ /* Undo? */
+ Scene *scene = CTX_data_scene(C);
+ DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
+ DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS);
+ WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
+
+ ED_outliner_select_sync_from_object_tag(C);
+
+ return OPERATOR_FINISHED;
+}
+
+void VIEW3D_OT_bone_select_menu(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+
+ /* identifiers */
+ ot->name = "Select Menu";
+ ot->description = "Menu bone selection";
+ ot->idname = "VIEW3D_OT_bone_select_menu";
+
+ /* api callbacks */
+ ot->invoke = WM_menu_invoke;
+ ot->exec = bone_select_menu_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* keyingset to use (dynamic enum) */
+ prop = RNA_def_enum(ot->srna, "name", DummyRNA_NULL_items, 0, "Bone Name", "");
+ RNA_def_enum_funcs(prop, object_select_menu_enum_itemf);
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_ENUM_NO_TRANSLATE);
+ ot->prop = prop;
+
+ RNA_def_boolean(ot->srna, "extend", 0, "Extend", "");
+ RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "");
+ RNA_def_boolean(ot->srna, "toggle", 0, "Toggle", "");
+}
+static bool bone_mouse_select_menu(bContext *C,
+ const uint *buffer,
+ const int hits,
+ const bool is_editmode,
+ const bool extend,
+ const bool deselect,
+ const bool toggle)
+{
+ BLI_assert(buffer);
+
+ short baseCount = 0;
+ LinkNodePair base_list = {NULL, NULL};
+ LinkNodePair bone_list = {NULL, NULL};
+ GSet *added_bones = BLI_gset_ptr_new("Bone mouse select menu");
+
+ /* Select logic taken from ed_armature_pick_bone_from_selectbuffer_impl in armature_select.c */
+ for (int a = 0; a < hits; a++) {
+ void *bone_ptr = NULL;
+ Base *bone_base = NULL;
+ uint hitresult = buffer[3 + (a * 4)];
+
+ if (!(hitresult & BONESEL_ANY)) {
+ /* To avoid including objects in selection. */
+ continue;
+ }
+
+ hitresult &= ~BONESEL_ANY;
+ const uint hit_object = hitresult & 0xFFFF;
+
+ /* Find the hit bone base (armature object). */
+ CTX_DATA_BEGIN (C, Base *, base, selectable_bases) {
+ if (base->object->runtime.select_id == hit_object) {
+ bone_base = base;
+ break;
+ }
+ }
+ CTX_DATA_END;
+
+ if (!bone_base) {
+ continue;
+ }
+
+ /* Determine what the current bone is */
+ if (is_editmode) {
+ EditBone *ebone;
+ const uint hit_bone = (hitresult & ~BONESEL_ANY) >> 16;
+ bArmature *arm = bone_base->object->data;
+ ebone = BLI_findlink(arm->edbo, hit_bone);
+ if (ebone && !(ebone->flag & BONE_UNSELECTABLE)) {
+ bone_ptr = ebone;
+ }
+ }
+ else {
+ bPoseChannel *pchan;
+ const uint hit_bone = (hitresult & ~BONESEL_ANY) >> 16;
+ pchan = BLI_findlink(&bone_base->object->pose->chanbase, hit_bone);
+ if (pchan && !(pchan->bone->flag & BONE_UNSELECTABLE)) {
+ bone_ptr = pchan;
+ }
+ }
+
+ if (!bone_ptr) {
+ continue;
+ }
+ /* We can hit a bone multiple times, so make sure we are not adding an already included bone
+ * to the list.*/
+ const bool is_duplicate_bone = BLI_gset_haskey(added_bones, bone_ptr);
+
+ if (!is_duplicate_bone) {
+ baseCount++;
+ BLI_linklist_append(&base_list, bone_base);
+ BLI_linklist_append(&bone_list, bone_ptr);
+ BLI_gset_insert(added_bones, bone_ptr);
+
+ if (baseCount == SEL_MENU_SIZE) {
+ break;
+ }
+ }
+ }
+
+ BLI_gset_free(added_bones, NULL);
+
+ if (baseCount == 0) {
+ return false;
+ }
+ if (baseCount == 1) {
+ BLI_linklist_free(base_list.list, NULL);
+ BLI_linklist_free(bone_list.list, NULL);
+ return false;
+ }
+
+ /* UI, full in static array values that we later use in an enum function */
+ LinkNode *bone_node, *base_node;
+ int i;
+
+ memset(object_mouse_select_menu_data, 0, sizeof(object_mouse_select_menu_data));
+
+ for (base_node = base_list.list, bone_node = bone_list.list, i = 0; bone_node;
+ base_node = base_node->next, bone_node = bone_node->next, i++) {
+ char *name;
+
+ object_mouse_select_menu_data[i].base_ptr = base_node->link;
+
+ if (is_editmode) {
+ EditBone *ebone = bone_node->link;
+ object_mouse_select_menu_data[i].item_ptr = ebone;
+ name = ebone->name;
+ }
+ else {
+ bPoseChannel *pchan = bone_node->link;
+ object_mouse_select_menu_data[i].item_ptr = pchan;
+ name = pchan->name;
+ }
+
+ BLI_strncpy(object_mouse_select_menu_data[i].idname, name, MAX_ID_NAME - 2);
+ object_mouse_select_menu_data[i].icon = ICON_BONE_DATA;
+ }
+
+ wmOperatorType *ot = WM_operatortype_find("VIEW3D_OT_bone_select_menu", false);
+ PointerRNA ptr;
+
+ WM_operator_properties_create_ptr(&ptr, ot);
+ RNA_boolean_set(&ptr, "extend", extend);
+ RNA_boolean_set(&ptr, "deselect", deselect);
+ RNA_boolean_set(&ptr, "toggle", toggle);
+ WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr);
+ WM_operator_properties_free(&ptr);
+
+ BLI_linklist_free(base_list.list, NULL);
+ BLI_linklist_free(bone_list.list, NULL);
+ return true;
+}
+
static bool selectbuffer_has_bones(const uint *buffer, const uint hits)
{
for (uint i = 0; i < hits; i++) {
@@ -2113,7 +2337,13 @@ static bool ed_object_select_pick(bContext *C,
/* note; shift+alt goes to group-flush-selecting */
if (enumerate) {
- basact = object_mouse_select_menu(C, &vc, buffer, hits, mval, extend, deselect, toggle);
+ if (has_bones &&
+ bone_mouse_select_menu(C, buffer, hits, false, extend, deselect, toggle)) {
+ basact = NULL;
+ }
+ else {
+ basact = object_mouse_select_menu(C, &vc, buffer, hits, mval, extend, deselect, toggle);
+ }
}
else {
basact = mouse_select_eval_buffer(&vc, buffer, hits, startbase, has_bones, do_nearest);
@@ -2410,7 +2640,20 @@ static int view3d_select_exec(bContext *C, wmOperator *op)
}
}
else if (obedit->type == OB_ARMATURE) {
- retval = ED_armature_edit_select_pick(C, location, extend, deselect, toggle);
+ if (enumerate) {
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ ViewContext vc;
+ ED_view3d_viewcontext_init(C, &vc, depsgraph);
+
+ uint buffer[MAXPICKBUF];
+ const int hits = mixed_bones_object_selectbuffer(
+ &vc, buffer, location, VIEW3D_SELECT_FILTER_NOP, false, true);
+ retval = bone_mouse_select_menu(C, buffer, hits, true, extend, deselect, toggle);
+ }
+ if (!retval) {
+ retval = ED_armature_edit_select_pick(C, location, extend, deselect, toggle);
+ }
+
if (!retval && deselect_all) {
retval = ED_armature_edit_deselect_all_visible_multi(C);
}
diff --git a/source/blender/editors/space_view3d/view3d_utils.c b/source/blender/editors/space_view3d/view3d_utils.c
index e3acda9bffb..8ae5d4a29e9 100644
--- a/source/blender/editors/space_view3d/view3d_utils.c
+++ b/source/blender/editors/space_view3d/view3d_utils.c
@@ -36,6 +36,7 @@
#include "MEM_guardedalloc.h"
+#include "BLI_array_utils.h"
#include "BLI_bitmap_draw_2d.h"
#include "BLI_blenlib.h"
#include "BLI_math.h"
@@ -298,8 +299,8 @@ void ED_view3d_clipping_calc(
float xs = (ELEM(val, 0, 3)) ? rect->xmin : rect->xmax;
float ys = (ELEM(val, 0, 1)) ? rect->ymin : rect->ymax;
- ED_view3d_unproject(region, xs, ys, 0.0, bb->vec[val]);
- ED_view3d_unproject(region, xs, ys, 1.0, bb->vec[4 + val]);
+ ED_view3d_unproject_v3(region, xs, ys, 0.0, bb->vec[val]);
+ ED_view3d_unproject_v3(region, xs, ys, 1.0, bb->vec[4 + val]);
}
/* optionally transform to object space */
@@ -1024,6 +1025,7 @@ static float view_autodist_depth_margin(ARegion *region, const int mval[2], int
/**
* Get the world-space 3d location from a screen-space 2d point.
+ * TODO: Implement #alphaoverride. We don't want to zoom into billboards.
*
* \param mval: Input screen-space pixel location.
* \param mouse_worldloc: Output world-space location.
@@ -1034,7 +1036,7 @@ bool ED_view3d_autodist(Depsgraph *depsgraph,
View3D *v3d,
const int mval[2],
float mouse_worldloc[3],
- const bool alphaoverride,
+ const bool UNUSED(alphaoverride),
const float fallback_depth_pt[3])
{
float depth_close;
@@ -1042,7 +1044,7 @@ bool ED_view3d_autodist(Depsgraph *depsgraph,
bool depth_ok = false;
/* Get Z Depths, needed for perspective, nice for ortho */
- ED_view3d_draw_depth(depsgraph, region, v3d, alphaoverride);
+ ED_view3d_depth_override(depsgraph, region, v3d, NULL, V3D_DEPTH_NO_GPENCIL, false);
/* Attempt with low margin's first */
int i = 0;
@@ -1055,7 +1057,7 @@ bool ED_view3d_autodist(Depsgraph *depsgraph,
float centx = (float)mval[0] + 0.5f;
float centy = (float)mval[1] + 0.5f;
- if (ED_view3d_unproject(region, centx, centy, depth_close, mouse_worldloc)) {
+ if (ED_view3d_unproject_v3(region, centx, centy, depth_close, mouse_worldloc)) {
return true;
}
}
@@ -1067,22 +1069,7 @@ bool ED_view3d_autodist(Depsgraph *depsgraph,
return false;
}
-void ED_view3d_autodist_init(Depsgraph *depsgraph, ARegion *region, View3D *v3d, int mode)
-{
- /* Get Z Depths, needed for perspective, nice for ortho */
- switch (mode) {
- case 0:
- ED_view3d_draw_depth(depsgraph, region, v3d, true);
- break;
- case 1: {
- Scene *scene = DEG_get_evaluated_scene(depsgraph);
- ED_view3d_draw_depth_gpencil(depsgraph, scene, region, v3d);
- break;
- }
- }
-}
-
-/* no 4x4 sampling, run #ED_view3d_autodist_init first */
+/* no 4x4 sampling, run #ED_view3d_depth_override first */
bool ED_view3d_autodist_simple(ARegion *region,
const int mval[2],
float mouse_worldloc[3],
@@ -1104,7 +1091,7 @@ bool ED_view3d_autodist_simple(ARegion *region,
float centx = (float)mval[0] + 0.5f;
float centy = (float)mval[1] + 0.5f;
- return ED_view3d_unproject(region, centx, centy, depth, mouse_worldloc);
+ return ED_view3d_unproject_v3(region, centx, centy, depth, mouse_worldloc);
}
bool ED_view3d_autodist_depth(ARegion *region, const int mval[2], int margin, float *depth)
@@ -1643,19 +1630,68 @@ bool ED_view3d_camera_to_view_selected(struct Main *bmain,
/** \name Depth Buffer Utilities
* \{ */
-float ED_view3d_depth_read_cached(const ViewContext *vc, const int mval[2])
+struct ReadData {
+ int count;
+ int count_max;
+ float r_depth;
+};
+
+static bool depth_read_test_fn(const void *value, void *userdata)
+{
+ struct ReadData *data = userdata;
+ float depth = *(float *)value;
+ if (depth < data->r_depth) {
+ data->r_depth = depth;
+ }
+
+ if ((++data->count) >= data->count_max) {
+ /* Outside the margin. */
+ return true;
+ }
+ return false;
+}
+
+bool ED_view3d_depth_read_cached(const ViewDepths *vd,
+ const int mval[2],
+ int margin,
+ float *r_depth)
{
- ViewDepths *vd = vc->rv3d->depths;
+ if (!vd || !vd->depths) {
+ return false;
+ }
int x = mval[0];
int y = mval[1];
+ if (x < 0 || y < 0 || x >= vd->w || y >= vd->h) {
+ return false;
+ }
+
+ float depth = 1.0f;
+ if (margin) {
+ int shape[2] = {vd->w, vd->h};
+ int pixel_count = (min_ii(x + margin + 1, shape[1]) - max_ii(x - margin, 0)) *
+ (min_ii(y + margin + 1, shape[0]) - max_ii(y - margin, 0));
+
+ struct ReadData data;
+ data.count = 0;
+ data.count_max = pixel_count;
+ data.r_depth = 1.0f;
- if (vd && vd->depths && x > 0 && y > 0 && x < vd->w && y < vd->h) {
- return vd->depths[y * vd->w + x];
+ /* TODO: No need to go spiral. */
+ BLI_array_iter_spiral_square(vd->depths, shape, mval, depth_read_test_fn, &data);
+ depth = data.r_depth;
+ }
+ else {
+ depth = vd->depths[y * vd->w + x];
}
BLI_assert(1.0 <= vd->depth_range[1]);
- return 1.0f;
+ if (depth != 1.0f) {
+ *r_depth = depth;
+ return true;
+ }
+
+ return false;
}
bool ED_view3d_depth_read_cached_normal(const ViewContext *vc,
@@ -1676,9 +1712,11 @@ bool ED_view3d_depth_read_cached_normal(const ViewContext *vc,
for (int y = 0; y < 2; y++) {
const int mval_ofs[2] = {mval[0] + (x - 1), mval[1] + (y - 1)};
- const double depth = (double)ED_view3d_depth_read_cached(vc, mval_ofs);
+ float depth_fl = 1.0f;
+ ED_view3d_depth_read_cached(depths, mval_ofs, 0, &depth_fl);
+ const double depth = (double)depth_fl;
if ((depth > depths->depth_range[0]) && (depth < depths->depth_range[1])) {
- if (ED_view3d_depth_unproject(region, mval_ofs, depth, coords[i])) {
+ if (ED_view3d_depth_unproject_v3(region, mval_ofs, depth, coords[i])) {
depths_valid[i] = true;
}
}
@@ -1713,14 +1751,14 @@ bool ED_view3d_depth_read_cached_normal(const ViewContext *vc,
return false;
}
-bool ED_view3d_depth_unproject(const ARegion *region,
- const int mval[2],
- const double depth,
- float r_location_world[3])
+bool ED_view3d_depth_unproject_v3(const ARegion *region,
+ const int mval[2],
+ const double depth,
+ float r_location_world[3])
{
float centx = (float)mval[0] + 0.5f;
float centy = (float)mval[1] + 0.5f;
- return ED_view3d_unproject(region, centx, centy, depth, r_location_world);
+ return ED_view3d_unproject_v3(region, centx, centy, depth, r_location_world);
}
void ED_view3d_depth_tag_update(RegionView3D *rv3d)
diff --git a/source/blender/editors/space_view3d/view3d_walk.c b/source/blender/editors/space_view3d/view3d_walk.c
index cbd65e3175d..ab4cf0c2135 100644
--- a/source/blender/editors/space_view3d/view3d_walk.c
+++ b/source/blender/editors/space_view3d/view3d_walk.c
@@ -407,7 +407,7 @@ static bool walk_floor_distance_get(RegionView3D *rv3d,
&(const struct SnapObjectParams){
.snap_select = SNAP_ALL,
/* Avoid having to convert the edit-mesh to a regular mesh. */
- .use_object_edit_cage = true,
+ .edit_mode_type = SNAP_GEOM_EDIT,
},
ray_start,
ray_normal,
diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c
index 2fbcbe22349..17bfc23aea7 100644
--- a/source/blender/editors/transform/transform.c
+++ b/source/blender/editors/transform/transform.c
@@ -74,7 +74,7 @@
static void drawTransformApply(const struct bContext *C, ARegion *region, void *arg);
-static void initSnapSpatial(TransInfo *t, float r_snap[3]);
+static void initSnapSpatial(TransInfo *t, float r_snap[2]);
bool transdata_check_local_islands(TransInfo *t, short around)
{
@@ -724,81 +724,92 @@ wmKeyMap *transform_modal_keymap(wmKeyConfig *keyconf)
static bool transform_event_modal_constraint(TransInfo *t, short modal_type)
{
- if (!(t->flag & T_NO_CONSTRAINT)) {
- if (t->flag & T_2D_EDIT && ELEM(modal_type, TFM_MODAL_AXIS_Z, TFM_MODAL_PLANE_Z)) {
+ if (t->flag & T_NO_CONSTRAINT) {
+ return false;
+ }
+
+ if (t->flag & T_2D_EDIT && ELEM(modal_type, TFM_MODAL_AXIS_Z, TFM_MODAL_PLANE_Z)) {
+ return false;
+ }
+
+ int constraint_curr = -1;
+
+ if (t->modifiers & (MOD_CONSTRAINT_SELECT_AXIS | MOD_CONSTRAINT_SELECT_PLANE)) {
+ t->modifiers &= ~(MOD_CONSTRAINT_SELECT_AXIS | MOD_CONSTRAINT_SELECT_PLANE);
+
+ /* Avoid changing orientation in this case. */
+ constraint_curr = -2;
+ }
+ else if (t->con.mode & CON_APPLY) {
+ constraint_curr = t->con.mode & (CON_AXIS0 | CON_AXIS1 | CON_AXIS2);
+ }
+
+ int constraint_new;
+ const char *msg_2d = "", *msg_3d = "";
+
+ /* Initialize */
+ switch (modal_type) {
+ case TFM_MODAL_AXIS_X:
+ msg_2d = TIP_("along X");
+ msg_3d = TIP_("along %s X");
+ constraint_new = CON_AXIS0;
+ break;
+ case TFM_MODAL_AXIS_Y:
+ msg_2d = TIP_("along Y");
+ msg_3d = TIP_("along %s Y");
+ constraint_new = CON_AXIS1;
+ break;
+ case TFM_MODAL_AXIS_Z:
+ msg_2d = TIP_("along Z");
+ msg_3d = TIP_("along %s Z");
+ constraint_new = CON_AXIS2;
+ break;
+ case TFM_MODAL_PLANE_X:
+ msg_3d = TIP_("locking %s X");
+ constraint_new = CON_AXIS1 | CON_AXIS2;
+ break;
+ case TFM_MODAL_PLANE_Y:
+ msg_3d = TIP_("locking %s Y");
+ constraint_new = CON_AXIS0 | CON_AXIS2;
+ break;
+ case TFM_MODAL_PLANE_Z:
+ msg_3d = TIP_("locking %s Z");
+ constraint_new = CON_AXIS0 | CON_AXIS1;
+ break;
+ default:
+ /* Invalid key */
return false;
- }
- int constraint_curr = (t->con.mode & CON_APPLY) ?
- t->con.mode & (CON_AXIS0 | CON_AXIS1 | CON_AXIS2) :
- -1;
- int constraint_new;
- const char *msg_2d = "", *msg_3d = "";
+ }
- /* Initialize */
- switch (modal_type) {
- case TFM_MODAL_AXIS_X:
- msg_2d = TIP_("along X");
- msg_3d = TIP_("along %s X");
- constraint_new = CON_AXIS0;
- break;
- case TFM_MODAL_AXIS_Y:
- msg_2d = TIP_("along Y");
- msg_3d = TIP_("along %s Y");
- constraint_new = CON_AXIS1;
- break;
- case TFM_MODAL_AXIS_Z:
- msg_2d = TIP_("along Z");
- msg_3d = TIP_("along %s Z");
- constraint_new = CON_AXIS2;
- break;
- case TFM_MODAL_PLANE_X:
- msg_3d = TIP_("locking %s X");
- constraint_new = CON_AXIS1 | CON_AXIS2;
- break;
- case TFM_MODAL_PLANE_Y:
- msg_3d = TIP_("locking %s Y");
- constraint_new = CON_AXIS0 | CON_AXIS2;
- break;
- case TFM_MODAL_PLANE_Z:
- msg_3d = TIP_("locking %s Z");
- constraint_new = CON_AXIS0 | CON_AXIS1;
- break;
- default:
- /* Invalid key */
- return false;
+ if (t->flag & T_2D_EDIT) {
+ BLI_assert(modal_type < TFM_MODAL_PLANE_X);
+ if (constraint_new == CON_AXIS2) {
+ return false;
}
-
- if (t->flag & T_2D_EDIT) {
- BLI_assert(modal_type < TFM_MODAL_PLANE_X);
- if (constraint_new == CON_AXIS2) {
- return false;
- }
- if (constraint_curr == constraint_new) {
- stopConstraint(t);
- }
- else {
- setUserConstraint(t, constraint_new, msg_2d);
- }
+ if (constraint_curr == constraint_new) {
+ stopConstraint(t);
}
else {
- short orient_index = 1;
- if (t->orient_curr == 0 || ELEM(constraint_curr, -1, constraint_new)) {
- /* Successive presses on existing axis, cycle orientation modes. */
- orient_index = (short)((t->orient_curr + 1) % (int)ARRAY_SIZE(t->orient));
- }
+ setUserConstraint(t, constraint_new, msg_2d);
+ }
+ }
+ else {
+ short orient_index = 1;
+ if (t->orient_curr == O_DEFAULT || ELEM(constraint_curr, -1, constraint_new)) {
+ /* Successive presses on existing axis, cycle orientation modes. */
+ orient_index = (short)((t->orient_curr + 1) % (int)ARRAY_SIZE(t->orient));
+ }
- transform_orientations_current_set(t, orient_index);
- if (orient_index == 0) {
- stopConstraint(t);
- }
- else {
- setUserConstraint(t, constraint_new, msg_3d);
- }
+ transform_orientations_current_set(t, orient_index);
+ if (orient_index == 0) {
+ stopConstraint(t);
+ }
+ else {
+ setUserConstraint(t, constraint_new, msg_3d);
}
- t->redraw |= TREDRAW_HARD;
- return true;
}
- return false;
+ t->redraw |= TREDRAW_HARD;
+ return true;
}
int transformEvent(TransInfo *t, const wmEvent *event)
@@ -814,7 +825,7 @@ int transformEvent(TransInfo *t, const wmEvent *event)
handled = true;
}
else if (event->type == MOUSEMOVE) {
- if (t->modifiers & (MOD_CONSTRAINT_SELECT | MOD_CONSTRAINT_PLANE)) {
+ if (t->modifiers & (MOD_CONSTRAINT_SELECT_AXIS | MOD_CONSTRAINT_SELECT_PLANE)) {
t->con.mode |= CON_SELECT;
}
@@ -1062,10 +1073,10 @@ int transformEvent(TransInfo *t, const wmEvent *event)
t->state = TRANS_CONFIRM;
}
else if ((t->flag & T_NO_CONSTRAINT) == 0) {
- if (t->modifiers & (MOD_CONSTRAINT_SELECT | MOD_CONSTRAINT_PLANE)) {
+ if (t->modifiers & (MOD_CONSTRAINT_SELECT_AXIS | MOD_CONSTRAINT_SELECT_PLANE)) {
/* Confirm. */
postSelectConstraint(t);
- t->modifiers &= ~(MOD_CONSTRAINT_SELECT | MOD_CONSTRAINT_PLANE);
+ t->modifiers &= ~(MOD_CONSTRAINT_SELECT_AXIS | MOD_CONSTRAINT_SELECT_PLANE);
}
else {
if (t->options & CTX_CAMERA) {
@@ -1079,8 +1090,9 @@ int transformEvent(TransInfo *t, const wmEvent *event)
}
}
else {
- t->modifiers |= (event->val == TFM_MODAL_AUTOCONSTRAINT) ? MOD_CONSTRAINT_SELECT :
- MOD_CONSTRAINT_PLANE;
+ t->modifiers |= (event->val == TFM_MODAL_AUTOCONSTRAINT) ?
+ MOD_CONSTRAINT_SELECT_AXIS :
+ MOD_CONSTRAINT_SELECT_PLANE;
if (t->con.mode & CON_APPLY) {
stopConstraint(t);
}
@@ -1412,25 +1424,6 @@ void saveTransform(bContext *C, TransInfo *t, wmOperator *op)
ToolSettings *ts = CTX_data_tool_settings(C);
PropertyRNA *prop;
- if (!(t->con.mode & CON_APPLY) && (t->flag & T_MODAL) &&
- ELEM(t->mode, TFM_TRANSLATION, TFM_RESIZE)) {
- /* When redoing these modes the first time, it's more convenient to save
- * in the Global orientation. */
- if (t->mode == TFM_TRANSLATION) {
- mul_m3_v3(t->spacemtx, t->values_final);
- }
- else {
- float tmat[3][3], sizemat[3][3];
- size_to_mat3(sizemat, t->values_final);
- mul_m3_m3m3(tmat, t->spacemtx, sizemat);
- mat3_to_size(t->values_final, tmat);
- }
-
- BLI_assert(t->orient_curr == 0);
- unit_m3(t->spacemtx);
- t->orient[0].type = V3D_ORIENT_GLOBAL;
- }
-
/* Save back mode in case we're in the generic operator */
if ((prop = RNA_struct_find_property(op->ptr, "mode"))) {
RNA_property_enum_set(op->ptr, prop, t->mode);
@@ -1638,7 +1631,7 @@ static void initSnapSpatial(TransInfo *t, float r_snap[2])
/**
* \note caller needs to free 't' on a 0 return
- * \warning \a event might be NULL (when tweaking from redo panel)
+ * \warning \a event might be NULL (when tweaking from redo panel)
* \see #saveTransform which writes these values back.
*/
bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *event, int mode)
diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h
index f2d5800abb7..f0ced665679 100644
--- a/source/blender/editors/transform/transform.h
+++ b/source/blender/editors/transform/transform.h
@@ -46,6 +46,7 @@
* \{ */
struct ARegion;
+struct BMPartialUpdate;
struct Depsgraph;
struct NumInput;
struct Object;
@@ -88,12 +89,11 @@ typedef enum {
CTX_TEXTURE_SPACE = (1 << 9),
CTX_NO_PET = (1 << 10),
- CTX_NO_MIRROR = (1 << 11),
- CTX_AUTOCONFIRM = (1 << 12),
+ CTX_AUTOCONFIRM = (1 << 11),
/** When transforming object's, adjust the object data so it stays in the same place. */
- CTX_OBMODE_XFORM_OBDATA = (1 << 13),
+ CTX_OBMODE_XFORM_OBDATA = (1 << 12),
/** Transform object parents without moving their children. */
- CTX_OBMODE_XFORM_SKIP_CHILDREN = (1 << 14),
+ CTX_OBMODE_XFORM_SKIP_CHILDREN = (1 << 13),
} eTContext;
/** #TransInfo.flag */
@@ -105,60 +105,59 @@ typedef enum {
/** restrictions flags */
T_NO_CONSTRAINT = 1 << 2,
T_NULL_ONE = 1 << 3,
- T_NO_ZERO = 1 << 4,
- T_ALL_RESTRICTIONS = T_NO_CONSTRAINT | T_NULL_ONE | T_NO_ZERO,
+ T_ALL_RESTRICTIONS = T_NO_CONSTRAINT | T_NULL_ONE,
- T_PROP_EDIT = 1 << 5,
- T_PROP_CONNECTED = 1 << 6,
- T_PROP_PROJECTED = 1 << 7,
+ T_PROP_EDIT = 1 << 4,
+ T_PROP_CONNECTED = 1 << 5,
+ T_PROP_PROJECTED = 1 << 6,
T_PROP_EDIT_ALL = T_PROP_EDIT | T_PROP_CONNECTED | T_PROP_PROJECTED,
- T_V3D_ALIGN = 1 << 8,
+ T_V3D_ALIGN = 1 << 7,
/** For 2D views such as UV or f-curve. */
- T_2D_EDIT = 1 << 9,
- T_CLIP_UV = 1 << 10,
+ T_2D_EDIT = 1 << 8,
+ T_CLIP_UV = 1 << 9,
/** Auto-IK is on. */
- T_AUTOIK = 1 << 11,
+ T_AUTOIK = 1 << 10,
/** Don't use mirror even if the data-block option is set. */
- T_NO_MIRROR = 1 << 12,
+ T_NO_MIRROR = 1 << 11,
/** To indicate that the value set in the `value` parameter is the final
* value of the transformation, modified only by the constrain. */
- T_INPUT_IS_VALUES_FINAL = 1 << 13,
+ T_INPUT_IS_VALUES_FINAL = 1 << 12,
/** To specify if we save back settings at the end. */
- T_MODAL = 1 << 14,
+ T_MODAL = 1 << 13,
/** No re-topology (projection). */
- T_NO_PROJECT = 1 << 15,
+ T_NO_PROJECT = 1 << 14,
- T_RELEASE_CONFIRM = 1 << 16,
+ T_RELEASE_CONFIRM = 1 << 15,
/** Alternative transformation. used to add offset to tracking markers. */
- T_ALT_TRANSFORM = 1 << 17,
+ T_ALT_TRANSFORM = 1 << 16,
/** #TransInfo.center has been set, don't change it. */
- T_OVERRIDE_CENTER = 1 << 18,
+ T_OVERRIDE_CENTER = 1 << 17,
- T_MODAL_CURSOR_SET = 1 << 19,
+ T_MODAL_CURSOR_SET = 1 << 18,
- T_CLNOR_REBUILD = 1 << 20,
+ T_CLNOR_REBUILD = 1 << 19,
/** Merges unselected into selected after transforming (runs after transforming). */
- T_AUTOMERGE = 1 << 21,
+ T_AUTOMERGE = 1 << 20,
/** Runs auto-merge & splits. */
- T_AUTOSPLIT = 1 << 22,
+ T_AUTOSPLIT = 1 << 21,
} eTFlag;
/** #TransInfo.modifiers */
typedef enum {
- MOD_CONSTRAINT_SELECT = 1 << 0,
+ MOD_CONSTRAINT_SELECT_AXIS = 1 << 0,
MOD_PRECISION = 1 << 1,
MOD_SNAP = 1 << 2,
MOD_SNAP_INVERT = 1 << 3,
- MOD_CONSTRAINT_PLANE = 1 << 4,
+ MOD_CONSTRAINT_SELECT_PLANE = 1 << 4,
} eTModifier;
/** #TransSnap.status */
@@ -432,14 +431,14 @@ typedef struct TransCustomDataContainer {
/**
* Container for Transform Data
*
- * Used to implement multi-object modes, so each object can have it's
+ * Used to implement multi-object modes, so each object can have its
* own data array as well as object matrix, local center etc.
*
* Anything that can't be shared between all objects
* and doesn't make sense to store for every vertex (in the #TransDataContainer.data).
*
* \note at some point this could be used to store non object containers
- * although this only makes sense if each container has it's own matrices,
+ * although this only makes sense if each container has its own matrices,
* otherwise all elements may as well be stored in one array (#TransDataContainer.data),
* as is already done for curve-objects, f-curves. etc.
*/
@@ -598,11 +597,18 @@ typedef struct TransInfo {
* mouse button then.) */
bool is_launch_event_tweak;
+ bool is_orient_set;
+
struct {
short type;
float matrix[3][3];
} orient[3];
- short orient_curr;
+
+ enum {
+ O_DEFAULT = 0,
+ O_SCENE,
+ O_SET,
+ } orient_curr;
/** backup from view3d, to restore on end. */
short gizmo_flag;
diff --git a/source/blender/editors/transform/transform_constraints.c b/source/blender/editors/transform/transform_constraints.c
index 2037981e655..8c74d5349ba 100644
--- a/source/blender/editors/transform/transform_constraints.c
+++ b/source/blender/editors/transform/transform_constraints.c
@@ -953,9 +953,8 @@ void startConstraint(TransInfo *t)
void stopConstraint(TransInfo *t)
{
- if (t->orient_curr != 0) {
- t->orient_curr = 0;
- transform_orientations_current_set(t, t->orient_curr);
+ if (t->orient_curr != O_DEFAULT) {
+ transform_orientations_current_set(t, O_DEFAULT);
}
t->con.mode &= ~(CON_APPLY | CON_SELECT);
@@ -971,12 +970,11 @@ void stopConstraint(TransInfo *t)
void initSelectConstraint(TransInfo *t)
{
- if (t->orient_curr == 0) {
- transform_orientations_current_set(t, 1);
+ if (t->orient_curr == O_DEFAULT) {
+ transform_orientations_current_set(t, O_SCENE);
}
setUserConstraint(t, CON_APPLY | CON_SELECT, "%s");
- setNearestAxis(t);
}
void selectConstraint(TransInfo *t)
@@ -1063,7 +1061,7 @@ static void setNearestAxis3d(TransInfo *t)
}
if (len[0] <= len[1] && len[0] <= len[2]) {
- if (t->modifiers & MOD_CONSTRAINT_PLANE) {
+ if (t->modifiers & MOD_CONSTRAINT_SELECT_PLANE) {
t->con.mode |= (CON_AXIS1 | CON_AXIS2);
BLI_snprintf(t->con.text, sizeof(t->con.text), TIP_(" locking %s X axis"), t->spacename);
}
@@ -1073,7 +1071,7 @@ static void setNearestAxis3d(TransInfo *t)
}
}
else if (len[1] <= len[0] && len[1] <= len[2]) {
- if (t->modifiers & MOD_CONSTRAINT_PLANE) {
+ if (t->modifiers & MOD_CONSTRAINT_SELECT_PLANE) {
t->con.mode |= (CON_AXIS0 | CON_AXIS2);
BLI_snprintf(t->con.text, sizeof(t->con.text), TIP_(" locking %s Y axis"), t->spacename);
}
@@ -1083,7 +1081,7 @@ static void setNearestAxis3d(TransInfo *t)
}
}
else if (len[2] <= len[1] && len[2] <= len[0]) {
- if (t->modifiers & MOD_CONSTRAINT_PLANE) {
+ if (t->modifiers & MOD_CONSTRAINT_SELECT_PLANE) {
t->con.mode |= (CON_AXIS0 | CON_AXIS1);
BLI_snprintf(t->con.text, sizeof(t->con.text), TIP_(" locking %s Z axis"), t->spacename);
}
diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c
index c021c084a23..9e285dd2d26 100644
--- a/source/blender/editors/transform/transform_convert.c
+++ b/source/blender/editors/transform/transform_convert.c
@@ -37,6 +37,7 @@
#include "BKE_context.h"
#include "BKE_fcurve.h"
#include "BKE_global.h"
+#include "BKE_image.h"
#include "BKE_layer.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
@@ -505,9 +506,27 @@ bool clipUVTransform(TransInfo *t, float vec[2], const bool resize)
bool clipx = true, clipy = true;
float min[2], max[2];
- min[0] = min[1] = 0.0f;
- max[0] = t->aspect[0];
- max[1] = t->aspect[1];
+ /* Check if the current image in UV editor is a tiled image or not. */
+ const SpaceImage *sima = t->area->spacedata.first;
+ const Image *image = sima->image;
+ const bool is_tiled_image = image && (image->source == IMA_SRC_TILED);
+ /* Stores the coordinates of the closest UDIM tile.
+ * Also acts as an offset to the tile from the origin of UV space. */
+ float base_offset[2] = {0.0f, 0.0f};
+
+ /* If tiled image then constrain to correct/closest UDIM tile, else 0-1 UV space. */
+ if (is_tiled_image) {
+ int nearest_tile_index = BKE_image_find_nearest_tile(image, t->center_global);
+ if (nearest_tile_index != -1) {
+ nearest_tile_index -= 1001;
+ /* Getting coordinates of nearest tile from the tile index. */
+ base_offset[0] = nearest_tile_index % 10;
+ base_offset[1] = nearest_tile_index / 10;
+ }
+ }
+
+ min[0] = min[1] = FLT_MAX;
+ max[0] = max[1] = FLT_MIN;
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
@@ -520,42 +539,48 @@ bool clipUVTransform(TransInfo *t, float vec[2], const bool resize)
}
if (resize) {
- if (min[0] < 0.0f && t->center_global[0] > 0.0f && t->center_global[0] < t->aspect[0] * 0.5f) {
- vec[0] *= t->center_global[0] / (t->center_global[0] - min[0]);
+ if (min[0] < base_offset[0] && t->center_global[0] > base_offset[0] &&
+ t->center_global[0] < base_offset[0] + (t->aspect[0] * 0.5f)) {
+ vec[0] *= (t->center_global[0] - base_offset[0]) / (t->center_global[0] - min[0]);
}
- else if (max[0] > t->aspect[0] && t->center_global[0] < t->aspect[0]) {
- vec[0] *= (t->center_global[0] - t->aspect[0]) / (t->center_global[0] - max[0]);
+ else if (max[0] > (base_offset[0] + t->aspect[0]) &&
+ t->center_global[0] < (base_offset[0] + t->aspect[0])) {
+ vec[0] *= (t->center_global[0] - (base_offset[0] + t->aspect[0])) /
+ (t->center_global[0] - max[0]);
}
else {
clipx = 0;
}
- if (min[1] < 0.0f && t->center_global[1] > 0.0f && t->center_global[1] < t->aspect[1] * 0.5f) {
- vec[1] *= t->center_global[1] / (t->center_global[1] - min[1]);
+ if (min[1] < base_offset[1] && t->center_global[1] > base_offset[1] &&
+ t->center_global[1] < base_offset[1] + (t->aspect[1] * 0.5f)) {
+ vec[1] *= (t->center_global[1] - base_offset[1]) / (t->center_global[1] - min[1]);
}
- else if (max[1] > t->aspect[1] && t->center_global[1] < t->aspect[1]) {
- vec[1] *= (t->center_global[1] - t->aspect[1]) / (t->center_global[1] - max[1]);
+ else if (max[1] > (base_offset[1] + t->aspect[1]) &&
+ t->center_global[1] < (base_offset[1] + t->aspect[1])) {
+ vec[1] *= (t->center_global[1] - (base_offset[1] + t->aspect[1])) /
+ (t->center_global[1] - max[1]);
}
else {
clipy = 0;
}
}
else {
- if (min[0] < 0.0f) {
- vec[0] -= min[0];
+ if (min[0] < base_offset[0]) {
+ vec[0] += base_offset[0] - min[0];
}
- else if (max[0] > t->aspect[0]) {
- vec[0] -= max[0] - t->aspect[0];
+ else if (max[0] > base_offset[0] + t->aspect[0]) {
+ vec[0] -= max[0] - base_offset[0] - t->aspect[0];
}
else {
clipx = 0;
}
- if (min[1] < 0.0f) {
- vec[1] -= min[1];
+ if (min[1] < base_offset[1]) {
+ vec[1] += base_offset[1] - min[1];
}
- else if (max[1] > t->aspect[1]) {
- vec[1] -= max[1] - t->aspect[1];
+ else if (max[1] > base_offset[1] + t->aspect[1]) {
+ vec[1] -= max[1] - base_offset[1] - t->aspect[1];
}
else {
clipy = 0;
@@ -1122,8 +1147,7 @@ static void init_TransDataContainers(TransInfo *t,
for (int i = 0; i < objects_len; i++) {
TransDataContainer *tc = &t->data_container[i];
- if (((t->flag & T_NO_MIRROR) == 0) && ((t->options & CTX_NO_MIRROR) == 0) &&
- (objects[i]->type == OB_MESH)) {
+ if (!(t->flag & T_NO_MIRROR) && (objects[i]->type == OB_MESH)) {
tc->use_mirror_axis_x = (((Mesh *)objects[i]->data)->symmetry & ME_SYMMETRY_X) != 0;
tc->use_mirror_axis_y = (((Mesh *)objects[i]->data)->symmetry & ME_SYMMETRY_Y) != 0;
tc->use_mirror_axis_z = (((Mesh *)objects[i]->data)->symmetry & ME_SYMMETRY_Z) != 0;
@@ -1472,91 +1496,89 @@ void createTransData(bContext *C, TransInfo *t)
/** \name Transform Data Recalc/Flush
* \{ */
-void clipMirrorModifier(TransInfo *t)
+void transform_convert_clip_mirror_modifier_apply(TransDataContainer *tc)
{
- FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- Object *ob = tc->obedit;
- ModifierData *md = ob->modifiers.first;
- float tolerance[3] = {0.0f, 0.0f, 0.0f};
- int axis = 0;
-
- for (; md; md = md->next) {
- if ((md->type == eModifierType_Mirror) && (md->mode & eModifierMode_Realtime)) {
- MirrorModifierData *mmd = (MirrorModifierData *)md;
-
- if (mmd->flag & MOD_MIR_CLIPPING) {
- axis = 0;
- if (mmd->flag & MOD_MIR_AXIS_X) {
- axis |= 1;
- tolerance[0] = mmd->tolerance;
- }
- if (mmd->flag & MOD_MIR_AXIS_Y) {
- axis |= 2;
- tolerance[1] = mmd->tolerance;
- }
- if (mmd->flag & MOD_MIR_AXIS_Z) {
- axis |= 4;
- tolerance[2] = mmd->tolerance;
- }
- if (axis) {
- float mtx[4][4], imtx[4][4];
- int i;
+ Object *ob = tc->obedit;
+ ModifierData *md = ob->modifiers.first;
+ float tolerance[3] = {0.0f, 0.0f, 0.0f};
+ int axis = 0;
+
+ for (; md; md = md->next) {
+ if ((md->type == eModifierType_Mirror) && (md->mode & eModifierMode_Realtime)) {
+ MirrorModifierData *mmd = (MirrorModifierData *)md;
+
+ if (mmd->flag & MOD_MIR_CLIPPING) {
+ axis = 0;
+ if (mmd->flag & MOD_MIR_AXIS_X) {
+ axis |= 1;
+ tolerance[0] = mmd->tolerance;
+ }
+ if (mmd->flag & MOD_MIR_AXIS_Y) {
+ axis |= 2;
+ tolerance[1] = mmd->tolerance;
+ }
+ if (mmd->flag & MOD_MIR_AXIS_Z) {
+ axis |= 4;
+ tolerance[2] = mmd->tolerance;
+ }
+ if (axis) {
+ float mtx[4][4], imtx[4][4];
+ int i;
- if (mmd->mirror_ob) {
- float obinv[4][4];
+ if (mmd->mirror_ob) {
+ float obinv[4][4];
- invert_m4_m4(obinv, mmd->mirror_ob->obmat);
- mul_m4_m4m4(mtx, obinv, ob->obmat);
- invert_m4_m4(imtx, mtx);
- }
+ invert_m4_m4(obinv, mmd->mirror_ob->obmat);
+ mul_m4_m4m4(mtx, obinv, ob->obmat);
+ invert_m4_m4(imtx, mtx);
+ }
- TransData *td = tc->data;
- for (i = 0; i < tc->data_len; i++, td++) {
- int clip;
- float loc[3], iloc[3];
+ TransData *td = tc->data;
+ for (i = 0; i < tc->data_len; i++, td++) {
+ int clip;
+ float loc[3], iloc[3];
- if (td->loc == NULL) {
- break;
- }
+ if (td->loc == NULL) {
+ break;
+ }
- if (td->flag & TD_SKIP) {
- continue;
- }
+ if (td->flag & TD_SKIP) {
+ continue;
+ }
- copy_v3_v3(loc, td->loc);
- copy_v3_v3(iloc, td->iloc);
+ copy_v3_v3(loc, td->loc);
+ copy_v3_v3(iloc, td->iloc);
- if (mmd->mirror_ob) {
- mul_m4_v3(mtx, loc);
- mul_m4_v3(mtx, iloc);
- }
+ if (mmd->mirror_ob) {
+ mul_m4_v3(mtx, loc);
+ mul_m4_v3(mtx, iloc);
+ }
- clip = 0;
- if (axis & 1) {
- if (fabsf(iloc[0]) <= tolerance[0] || loc[0] * iloc[0] < 0.0f) {
- loc[0] = 0.0f;
- clip = 1;
- }
+ clip = 0;
+ if (axis & 1) {
+ if (fabsf(iloc[0]) <= tolerance[0] || loc[0] * iloc[0] < 0.0f) {
+ loc[0] = 0.0f;
+ clip = 1;
}
+ }
- if (axis & 2) {
- if (fabsf(iloc[1]) <= tolerance[1] || loc[1] * iloc[1] < 0.0f) {
- loc[1] = 0.0f;
- clip = 1;
- }
+ if (axis & 2) {
+ if (fabsf(iloc[1]) <= tolerance[1] || loc[1] * iloc[1] < 0.0f) {
+ loc[1] = 0.0f;
+ clip = 1;
}
- if (axis & 4) {
- if (fabsf(iloc[2]) <= tolerance[2] || loc[2] * iloc[2] < 0.0f) {
- loc[2] = 0.0f;
- clip = 1;
- }
+ }
+ if (axis & 4) {
+ if (fabsf(iloc[2]) <= tolerance[2] || loc[2] * iloc[2] < 0.0f) {
+ loc[2] = 0.0f;
+ clip = 1;
}
- if (clip) {
- if (mmd->mirror_ob) {
- mul_m4_v3(imtx, loc);
- }
- copy_v3_v3(td->loc, loc);
+ }
+ if (clip) {
+ if (mmd->mirror_ob) {
+ mul_m4_v3(imtx, loc);
}
+ copy_v3_v3(td->loc, loc);
}
}
}
@@ -1644,38 +1666,6 @@ void animrecord_check_state(TransInfo *t, struct Object *ob)
}
}
-static void recalcData_cursor_image(TransInfo *t)
-{
- TransDataContainer *tc = t->data_container;
- TransData *td = tc->data;
- float aspect_inv[2];
-
- aspect_inv[0] = 1.0f / t->aspect[0];
- aspect_inv[1] = 1.0f / t->aspect[1];
-
- td->loc[0] = td->loc[0] * aspect_inv[0];
- td->loc[1] = td->loc[1] * aspect_inv[1];
-
- DEG_id_tag_update(&t->scene->id, ID_RECALC_COPY_ON_WRITE);
-}
-
-static void recalcData_cursor(TransInfo *t)
-{
- DEG_id_tag_update(&t->scene->id, ID_RECALC_COPY_ON_WRITE);
-}
-
-static void recalcData_obedit(TransInfo *t)
-{
- if (t->state != TRANS_CANCEL) {
- applyProject(t);
- }
- FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- if (tc->data_len) {
- DEG_id_tag_update(tc->obedit->data, 0); /* sets recalc flags */
- }
- }
-}
-
/* called for updating while transform acts, once per redraw */
void recalcData(TransInfo *t)
{
@@ -1708,9 +1698,11 @@ void recalcData(TransInfo *t)
recalcData_mask_common(t);
break;
case TC_MESH_VERTS:
- case TC_MESH_EDGES:
recalcData_mesh(t);
break;
+ case TC_MESH_EDGES:
+ recalcData_mesh_edge(t);
+ break;
case TC_MESH_SKIN:
recalcData_mesh_skin(t);
break;
@@ -1742,7 +1734,7 @@ void recalcData(TransInfo *t)
recalcData_tracking(t);
break;
case TC_MBALL_VERTS:
- recalcData_obedit(t);
+ recalcData_mball(t);
break;
case TC_LATTICE_VERTS:
recalcData_lattice(t);
diff --git a/source/blender/editors/transform/transform_convert.h b/source/blender/editors/transform/transform_convert.h
index 36a51d57f64..918ce0739ed 100644
--- a/source/blender/editors/transform/transform_convert.h
+++ b/source/blender/editors/transform/transform_convert.h
@@ -45,7 +45,7 @@ bool clipUVTransform(TransInfo *t, float vec[2], const bool resize);
void clipUVData(TransInfo *t);
/* transform_convert_mesh.c */
-void mesh_customdatacorrect_init(TransInfo *t);
+void transform_convert_mesh_customdatacorrect_init(TransInfo *t);
/* transform_convert_sequencer.c */
int transform_convert_sequencer_get_snap_bound(TransInfo *t);
@@ -62,7 +62,7 @@ void calc_distanceCurveVerts(TransData *head, TransData *tail, bool cyclic);
struct TransDataCurveHandleFlags *initTransDataCurveHandles(TransData *td, struct BezTriple *bezt);
char transform_convert_frame_side_dir_get(TransInfo *t, float cframe);
bool FrameOnMouseSide(char side, float frame, float cframe);
-void clipMirrorModifier(TransInfo *t);
+void transform_convert_clip_mirror_modifier_apply(TransDataContainer *tc);
void animrecord_check_state(TransInfo *t, struct Object *ob);
/* transform_convert_action.c */
@@ -84,6 +84,8 @@ void special_aftertrans_update__pose(bContext *C, TransInfo *t);
/* transform_convert_cursor.c */
void createTransCursor_image(TransInfo *t);
void createTransCursor_view3d(TransInfo *t);
+void recalcData_cursor_image(TransInfo *t);
+void recalcData_cursor(TransInfo *t);
/* transform_convert_curve.c */
void createTransCurveVerts(TransInfo *t);
@@ -109,6 +111,7 @@ void special_aftertrans_update__mask(bContext *C, TransInfo *t);
/* transform_convert_mball.c */
void createTransMBallVerts(TransInfo *t);
+void recalcData_mball(TransInfo *t);
/* transform_convert_mesh.c */
struct TransIslandData {
@@ -166,6 +169,7 @@ void special_aftertrans_update__mesh(bContext *C, TransInfo *t);
/* transform_convert_mesh_edge.c */
void createTransEdge(TransInfo *t);
+void recalcData_mesh_edge(TransInfo *t);
/* transform_convert_mesh_skin.c */
void createTransMeshSkin(TransInfo *t);
diff --git a/source/blender/editors/transform/transform_convert_armature.c b/source/blender/editors/transform/transform_convert_armature.c
index a4edf51ffee..aaea9d05f84 100644
--- a/source/blender/editors/transform/transform_convert_armature.c
+++ b/source/blender/editors/transform/transform_convert_armature.c
@@ -99,15 +99,7 @@ static void autokeyframe_pose(
bPoseChannel *pchan;
FCurve *fcu;
- /* TODO: this should probably be done per channel instead. */
if (!autokeyframe_cfra_can_key(scene, id)) {
- /* tag channels that should have unkeyed data */
- for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) {
- if (pchan->bone->flag & BONE_TRANSFORM) {
- /* tag this channel */
- pchan->bone->flag |= BONE_UNKEYED;
- }
- }
return;
}
@@ -139,9 +131,6 @@ static void autokeyframe_pose(
ListBase dsources = {NULL, NULL};
- /* clear any 'unkeyed' flag it may have */
- pchan->bone->flag &= ~BONE_UNKEYED;
-
/* add datasource override for the camera object */
ANIM_relative_keyingset_add_source(&dsources, id, &RNA_PoseBone, pchan);
@@ -1279,9 +1268,6 @@ void recalcData_edit_armature(TransInfo *t)
restoreBones(tc);
}
}
-
- /* Tag for redraw/invalidate overlay cache. */
- DEG_id_tag_update(&arm->id, ID_RECALC_SELECT);
}
}
diff --git a/source/blender/editors/transform/transform_convert_cursor.c b/source/blender/editors/transform/transform_convert_cursor.c
index 67d85f9610b..1f3eff31205 100644
--- a/source/blender/editors/transform/transform_convert_cursor.c
+++ b/source/blender/editors/transform/transform_convert_cursor.c
@@ -134,3 +134,29 @@ void createTransCursor_view3d(TransInfo *t)
}
/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Recalc Cursor
+ * \{ */
+
+void recalcData_cursor_image(TransInfo *t)
+{
+ TransDataContainer *tc = t->data_container;
+ TransData *td = tc->data;
+ float aspect_inv[2];
+
+ aspect_inv[0] = 1.0f / t->aspect[0];
+ aspect_inv[1] = 1.0f / t->aspect[1];
+
+ td->loc[0] = td->loc[0] * aspect_inv[0];
+ td->loc[1] = td->loc[1] * aspect_inv[1];
+
+ DEG_id_tag_update(&t->scene->id, ID_RECALC_COPY_ON_WRITE);
+}
+
+void recalcData_cursor(TransInfo *t)
+{
+ DEG_id_tag_update(&t->scene->id, ID_RECALC_COPY_ON_WRITE);
+}
+
+/** \} */
diff --git a/source/blender/editors/transform/transform_convert_curve.c b/source/blender/editors/transform/transform_convert_curve.c
index bef3eca0d9d..e57fd85470f 100644
--- a/source/blender/editors/transform/transform_convert_curve.c
+++ b/source/blender/editors/transform/transform_convert_curve.c
@@ -441,7 +441,6 @@ void createTransCurveVerts(TransInfo *t)
void recalcData_curve(TransInfo *t)
{
if (t->state != TRANS_CANCEL) {
- clipMirrorModifier(t);
applyProject(t);
}
@@ -450,7 +449,7 @@ void recalcData_curve(TransInfo *t)
ListBase *nurbs = BKE_curve_editNurbs_get(cu);
Nurb *nu = nurbs->first;
- DEG_id_tag_update(tc->obedit->data, 0); /* sets recalc flags */
+ DEG_id_tag_update(tc->obedit->data, ID_RECALC_GEOMETRY);
if (t->state == TRANS_CANCEL) {
while (nu) {
@@ -460,12 +459,11 @@ void recalcData_curve(TransInfo *t)
}
}
else {
- /* Normal updating */
- while (nu) {
- BKE_nurb_test_2d(nu);
- BKE_nurb_handles_calc(nu);
- nu = nu->next;
- }
+ /* Apply clipping after so we never project past the clip plane T25423. */
+ transform_convert_clip_mirror_modifier_apply(tc);
+
+ /* Normal updating. */
+ BKE_curve_dimension_update(cu);
}
}
}
diff --git a/source/blender/editors/transform/transform_convert_gpencil.c b/source/blender/editors/transform/transform_convert_gpencil.c
index 45df0e66691..4932a5f8d23 100644
--- a/source/blender/editors/transform/transform_convert_gpencil.c
+++ b/source/blender/editors/transform/transform_convert_gpencil.c
@@ -78,14 +78,12 @@ static short get_bezt_sel_triple_flag(BezTriple *bezt, const bool handles_visibl
flag = ((bezt->f1 & SELECT) ? SEL_F1 : 0) | ((bezt->f2 & SELECT) ? SEL_F2 : 0) |
((bezt->f3 & SELECT) ? SEL_F3 : 0);
}
- else {
- if (bezt->f2 & SELECT) {
- flag = SEL_ALL;
- }
+ else if (bezt->f2 & SELECT) {
+ flag = SEL_ALL;
}
/* Special case for auto & aligned handles */
- if (flag != SEL_ALL && flag & SEL_F2) {
+ if ((flag != SEL_ALL) && (flag & SEL_F2)) {
if (ELEM(bezt->h1, HD_AUTO, HD_ALIGN) && ELEM(bezt->h2, HD_AUTO, HD_ALIGN)) {
flag = SEL_ALL;
}
@@ -316,7 +314,7 @@ static void createTransGPencil_curves(bContext *C,
}
}
else if (handles_visible) {
- if (BEZT_ISSEL_IDX(bezt, j)) {
+ if (sel) {
td->flag = TD_SELECTED;
}
else {
diff --git a/source/blender/editors/transform/transform_convert_lattice.c b/source/blender/editors/transform/transform_convert_lattice.c
index 20ac7dcb998..fbfce41d555 100644
--- a/source/blender/editors/transform/transform_convert_lattice.c
+++ b/source/blender/editors/transform/transform_convert_lattice.c
@@ -122,7 +122,7 @@ void recalcData_lattice(TransInfo *t)
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
Lattice *la = tc->obedit->data;
- DEG_id_tag_update(tc->obedit->data, 0); /* sets recalc flags */
+ DEG_id_tag_update(tc->obedit->data, ID_RECALC_GEOMETRY);
if (la->editlatt->latt->flag & LT_OUTSIDE) {
outside_lattice(la->editlatt->latt);
}
diff --git a/source/blender/editors/transform/transform_convert_mball.c b/source/blender/editors/transform/transform_convert_mball.c
index 6f5c0318054..f38f3ccf421 100644
--- a/source/blender/editors/transform/transform_convert_mball.c
+++ b/source/blender/editors/transform/transform_convert_mball.c
@@ -30,6 +30,8 @@
#include "BKE_context.h"
#include "transform.h"
+#include "transform_snap.h"
+
#include "transform_convert.h"
/* -------------------------------------------------------------------- */
@@ -128,3 +130,21 @@ void createTransMBallVerts(TransInfo *t)
}
/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Recalc Meta Ball
+ * \{ */
+
+void recalcData_mball(TransInfo *t)
+{
+ if (t->state != TRANS_CANCEL) {
+ applyProject(t);
+ }
+ FOREACH_TRANS_DATA_CONTAINER (t, tc) {
+ if (tc->data_len) {
+ DEG_id_tag_update(tc->obedit->data, ID_RECALC_GEOMETRY);
+ }
+ }
+}
+
+/** \} */
diff --git a/source/blender/editors/transform/transform_convert_mesh.c b/source/blender/editors/transform/transform_convert_mesh.c
index 93c36645873..422370cb13b 100644
--- a/source/blender/editors/transform/transform_convert_mesh.c
+++ b/source/blender/editors/transform/transform_convert_mesh.c
@@ -48,7 +48,637 @@
#include "transform_convert.h"
+/* -------------------------------------------------------------------- */
+/** \name Container TransCustomData Creation
+ * \{ */
+
+static void tc_mesh_customdata_free_fn(struct TransInfo *t,
+ struct TransDataContainer *tc,
+ struct TransCustomData *custom_data);
+
+struct TransCustomDataLayer;
+static void tc_mesh_customdatacorrect_free(struct TransCustomDataLayer *tcld);
+
+struct TransCustomDataMesh {
+ struct TransCustomDataLayer *cd_layer_correct;
+ struct {
+ struct BMPartialUpdate *cache;
+
+ /** The size of proportional editing used for `partial_update_cache`. */
+ float prop_size;
+ /** The size of proportional editing for the last update. */
+ float prop_size_prev;
+ } partial_update;
+};
+
+static struct TransCustomDataMesh *tc_mesh_customdata_ensure(TransDataContainer *tc)
+{
+ struct TransCustomDataMesh *tcmd = tc->custom.type.data;
+ BLI_assert(tc->custom.type.data == NULL ||
+ tc->custom.type.free_cb == tc_mesh_customdata_free_fn);
+ if (tc->custom.type.data == NULL) {
+ tc->custom.type.data = MEM_callocN(sizeof(struct TransCustomDataMesh), __func__);
+ tc->custom.type.free_cb = tc_mesh_customdata_free_fn;
+ tcmd = tc->custom.type.data;
+ }
+ return tcmd;
+}
+
+static void tc_mesh_customdata_free(struct TransCustomDataMesh *tcmd)
+{
+ if (tcmd->cd_layer_correct != NULL) {
+ tc_mesh_customdatacorrect_free(tcmd->cd_layer_correct);
+ }
+
+ if (tcmd->partial_update.cache != NULL) {
+ BM_mesh_partial_destroy(tcmd->partial_update.cache);
+ }
+
+ MEM_freeN(tcmd);
+}
+
+static void tc_mesh_customdata_free_fn(struct TransInfo *UNUSED(t),
+ struct TransDataContainer *UNUSED(tc),
+ struct TransCustomData *custom_data)
+{
+ struct TransCustomDataMesh *tcmd = custom_data->data;
+ tc_mesh_customdata_free(tcmd);
+ custom_data->data = NULL;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name CustomData TransCustomDataLayer Creation
+ * \{ */
+
+struct TransCustomDataMergeGroup {
+ /** map {BMVert: TransCustomDataLayerVert} */
+ struct LinkNode **cd_loop_groups;
+};
+
+struct TransCustomDataLayer {
+ BMesh *bm;
+ struct MemArena *arena;
+
+ struct GHash *origfaces;
+ struct BMesh *bm_origfaces;
+
+ /* Special handle for multi-resolution. */
+ int cd_loop_mdisp_offset;
+
+ /* Optionally merge custom-data groups (this keeps UVs connected for example). */
+ struct {
+ /** map {BMVert: TransDataBasic} */
+ struct GHash *origverts;
+ struct TransCustomDataMergeGroup *data;
+ int data_len;
+ /** Array size of 'layer_math_map_len'
+ * maps #TransCustomDataLayerVert.cd_group index to absolute #CustomData layer index */
+ int *customdatalayer_map;
+ /** Number of math BMLoop layers. */
+ int customdatalayer_map_len;
+ } merge_group;
+
+ bool use_merge_group;
+};
+
#define USE_FACE_SUBSTITUTE
+#ifdef USE_FACE_SUBSTITUTE
+# define FACE_SUBSTITUTE_INDEX INT_MIN
+
+/**
+ * Search for a neighboring face with area and preferably without selected vertex.
+ * Used to replace area-less faces in custom-data correction.
+ */
+static BMFace *tc_mesh_customdatacorrect_find_best_face_substitute(BMFace *f)
+{
+ BMFace *best_face = NULL;
+ BMLoop *l;
+ BMIter liter;
+ BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
+ BMLoop *l_radial_next = l->radial_next;
+ BMFace *f_test = l_radial_next->f;
+ if (f_test == f) {
+ continue;
+ }
+ if (is_zero_v3(f_test->no)) {
+ continue;
+ }
+
+ /* Check the loops edge isn't selected. */
+ if (!BM_elem_flag_test(l_radial_next->v, BM_ELEM_SELECT) &&
+ !BM_elem_flag_test(l_radial_next->next->v, BM_ELEM_SELECT)) {
+ /* Prefer edges with unselected vertices.
+ * Useful for extrude. */
+ best_face = f_test;
+ break;
+ }
+ if (best_face == NULL) {
+ best_face = f_test;
+ }
+ }
+ return best_face;
+}
+
+static void tc_mesh_customdatacorrect_face_substitute_set(struct TransCustomDataLayer *tcld,
+ BMFace *f,
+ BMFace *f_copy)
+{
+ BLI_assert(is_zero_v3(f->no));
+ BMesh *bm = tcld->bm;
+ /* It is impossible to calculate the loops weights of a face without area.
+ * Find a substitute. */
+ BMFace *f_substitute = tc_mesh_customdatacorrect_find_best_face_substitute(f);
+ if (f_substitute) {
+ /* Copy the custom-data from the substitute face. */
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ BM_loop_interp_from_face(bm, l_iter, f_substitute, false, false);
+ } while ((l_iter = l_iter->next) != l_first);
+
+ /* Use the substitute face as the reference during the transformation. */
+ BMFace *f_substitute_copy = BM_face_copy(tcld->bm_origfaces, bm, f_substitute, true, true);
+
+ /* Hack: reference substitute face in `f_copy->no`.
+ * `tcld->origfaces` is already used to restore the initial value. */
+ BM_elem_index_set(f_copy, FACE_SUBSTITUTE_INDEX);
+ *((BMFace **)&f_copy->no[0]) = f_substitute_copy;
+ }
+}
+
+static BMFace *tc_mesh_customdatacorrect_face_substitute_get(BMFace *f_copy)
+{
+ BLI_assert(BM_elem_index_get(f_copy) == FACE_SUBSTITUTE_INDEX);
+ return *((BMFace **)&f_copy->no[0]);
+}
+
+#endif /* USE_FACE_SUBSTITUTE */
+
+static void tc_mesh_customdatacorrect_init_vert(struct TransCustomDataLayer *tcld,
+ struct TransDataBasic *td,
+ const int index)
+{
+ BMesh *bm = tcld->bm;
+ BMVert *v = td->extra;
+ BMIter liter;
+ int j, l_num;
+ float *loop_weights;
+
+ // BM_ITER_ELEM (l, &liter, sv->v, BM_LOOPS_OF_VERT) {
+ BM_iter_init(&liter, bm, BM_LOOPS_OF_VERT, v);
+ l_num = liter.count;
+ loop_weights = tcld->use_merge_group ? BLI_array_alloca(loop_weights, l_num) : NULL;
+ for (j = 0; j < l_num; j++) {
+ BMLoop *l = BM_iter_step(&liter);
+ BMLoop *l_prev, *l_next;
+
+ /* Generic custom-data correction. Copy face data. */
+ void **val_p;
+ if (!BLI_ghash_ensure_p(tcld->origfaces, l->f, &val_p)) {
+ BMFace *f_copy = BM_face_copy(tcld->bm_origfaces, bm, l->f, true, true);
+ *val_p = f_copy;
+#ifdef USE_FACE_SUBSTITUTE
+ if (is_zero_v3(l->f->no)) {
+ tc_mesh_customdatacorrect_face_substitute_set(tcld, l->f, f_copy);
+ }
+#endif
+ }
+
+ if (tcld->use_merge_group) {
+ if ((l_prev = BM_loop_find_prev_nodouble(l, l->next, FLT_EPSILON)) &&
+ (l_next = BM_loop_find_next_nodouble(l, l_prev, FLT_EPSILON))) {
+ loop_weights[j] = angle_v3v3v3(l_prev->v->co, l->v->co, l_next->v->co);
+ }
+ else {
+ loop_weights[j] = 0.0f;
+ }
+ }
+ }
+
+ if (tcld->use_merge_group) {
+ /* Store cd_loop_groups. */
+ struct TransCustomDataMergeGroup *merge_data = &tcld->merge_group.data[index];
+ if (l_num != 0) {
+ merge_data->cd_loop_groups = BLI_memarena_alloc(
+ tcld->arena, tcld->merge_group.customdatalayer_map_len * sizeof(void *));
+ for (j = 0; j < tcld->merge_group.customdatalayer_map_len; j++) {
+ const int layer_nr = tcld->merge_group.customdatalayer_map[j];
+ merge_data->cd_loop_groups[j] = BM_vert_loop_groups_data_layer_create(
+ bm, v, layer_nr, loop_weights, tcld->arena);
+ }
+ }
+ else {
+ merge_data->cd_loop_groups = NULL;
+ }
+
+ BLI_ghash_insert(tcld->merge_group.origverts, v, td);
+ }
+}
+
+static void tc_mesh_customdatacorrect_init_container_generic(TransDataContainer *UNUSED(tc),
+ struct TransCustomDataLayer *tcld)
+{
+ BMesh *bm = tcld->bm;
+
+ struct GHash *origfaces = BLI_ghash_ptr_new(__func__);
+ struct BMesh *bm_origfaces = BM_mesh_create(&bm_mesh_allocsize_default,
+ &((struct BMeshCreateParams){
+ .use_toolflags = false,
+ }));
+
+ /* We need to have matching loop custom-data. */
+ BM_mesh_copy_init_customdata_all_layers(bm_origfaces, bm, BM_LOOP, NULL);
+
+ tcld->origfaces = origfaces;
+ tcld->bm_origfaces = bm_origfaces;
+
+ bmesh_edit_begin(bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES);
+ tcld->cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS);
+}
+
+static void tc_mesh_customdatacorrect_init_container_merge_group(TransDataContainer *tc,
+ struct TransCustomDataLayer *tcld)
+{
+ BMesh *bm = tcld->bm;
+ BLI_assert(CustomData_has_math(&bm->ldata));
+
+ /* TODO: We don't need `layer_math_map` when there are no loops linked
+ * to one of the sliding vertices. */
+
+ /* Over allocate, only 'math' layers are indexed. */
+ int *customdatalayer_map = MEM_mallocN(sizeof(int) * bm->ldata.totlayer, __func__);
+ int layer_math_map_len = 0;
+ for (int i = 0; i < bm->ldata.totlayer; i++) {
+ if (CustomData_layer_has_math(&bm->ldata, i)) {
+ customdatalayer_map[layer_math_map_len++] = i;
+ }
+ }
+ BLI_assert(layer_math_map_len != 0);
+
+ tcld->merge_group.data_len = tc->data_len + tc->data_mirror_len;
+ tcld->merge_group.customdatalayer_map = customdatalayer_map;
+ tcld->merge_group.customdatalayer_map_len = layer_math_map_len;
+ tcld->merge_group.origverts = BLI_ghash_ptr_new_ex(__func__, tcld->merge_group.data_len);
+ tcld->merge_group.data = BLI_memarena_alloc(
+ tcld->arena, tcld->merge_group.data_len * sizeof(*tcld->merge_group.data));
+}
+
+static struct TransCustomDataLayer *tc_mesh_customdatacorrect_create_impl(
+ TransDataContainer *tc, const bool use_merge_group)
+{
+ BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
+ BMesh *bm = em->bm;
+
+ if (bm->shapenr > 1) {
+ /* Don't do this at all for non-basis shape keys, too easy to
+ * accidentally break uv maps or vertex colors then */
+ /* create copies of faces for custom-data projection. */
+ return NULL;
+ }
+ if (!CustomData_has_math(&bm->ldata) && !CustomData_has_layer(&bm->ldata, CD_MDISPS)) {
+ /* There is no custom-data to correct. */
+ return NULL;
+ }
+
+ struct TransCustomDataLayer *tcld = MEM_callocN(sizeof(*tcld), __func__);
+ tcld->bm = bm;
+ tcld->arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
+
+ /* Init `cd_loop_mdisp_offset` to -1 to avoid problems with a valid index. */
+ tcld->cd_loop_mdisp_offset = -1;
+ tcld->use_merge_group = use_merge_group;
+
+ tc_mesh_customdatacorrect_init_container_generic(tc, tcld);
+
+ if (tcld->use_merge_group) {
+ tc_mesh_customdatacorrect_init_container_merge_group(tc, tcld);
+ }
+
+ {
+ /* Setup Verts. */
+ int i = 0;
+
+ TransData *tob = tc->data;
+ for (int j = tc->data_len; j--; tob++, i++) {
+ tc_mesh_customdatacorrect_init_vert(tcld, (TransDataBasic *)tob, i);
+ }
+
+ TransDataMirror *td_mirror = tc->data_mirror;
+ for (int j = tc->data_mirror_len; j--; td_mirror++, i++) {
+ tc_mesh_customdatacorrect_init_vert(tcld, (TransDataBasic *)td_mirror, i);
+ }
+ }
+
+ return tcld;
+}
+
+static void tc_mesh_customdatacorrect_create(TransDataContainer *tc, const bool use_merge_group)
+{
+ struct TransCustomDataLayer *customdatacorrect;
+ customdatacorrect = tc_mesh_customdatacorrect_create_impl(tc, use_merge_group);
+
+ if (!customdatacorrect) {
+ return;
+ }
+
+ struct TransCustomDataMesh *tcmd = tc_mesh_customdata_ensure(tc);
+ BLI_assert(tcmd->cd_layer_correct == NULL);
+ tcmd->cd_layer_correct = customdatacorrect;
+}
+
+static void tc_mesh_customdatacorrect_free(struct TransCustomDataLayer *tcld)
+{
+ bmesh_edit_end(tcld->bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES);
+
+ if (tcld->bm_origfaces) {
+ BM_mesh_free(tcld->bm_origfaces);
+ }
+ if (tcld->origfaces) {
+ BLI_ghash_free(tcld->origfaces, NULL, NULL);
+ }
+ if (tcld->merge_group.origverts) {
+ BLI_ghash_free(tcld->merge_group.origverts, NULL, NULL);
+ }
+ if (tcld->arena) {
+ BLI_memarena_free(tcld->arena);
+ }
+ if (tcld->merge_group.customdatalayer_map) {
+ MEM_freeN(tcld->merge_group.customdatalayer_map);
+ }
+
+ MEM_freeN(tcld);
+}
+
+void transform_convert_mesh_customdatacorrect_init(TransInfo *t)
+{
+ bool use_merge_group = false;
+ if (ELEM(t->mode, TFM_EDGE_SLIDE, TFM_VERT_SLIDE)) {
+ if (!(t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT_SLIDE)) {
+ /* No custom-data correction. */
+ return;
+ }
+ use_merge_group = true;
+ }
+ else if (ELEM(t->mode,
+ TFM_TRANSLATION,
+ TFM_ROTATION,
+ TFM_RESIZE,
+ TFM_TOSPHERE,
+ TFM_SHEAR,
+ TFM_BEND,
+ TFM_SHRINKFATTEN,
+ TFM_TRACKBALL,
+ TFM_PUSHPULL,
+ TFM_ALIGN)) {
+ {
+ if (!(t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT)) {
+ /* No custom-data correction. */
+ return;
+ }
+ use_merge_group = (t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT_KEEP_CONNECTED) != 0;
+ }
+ }
+ else {
+ return;
+ }
+
+ FOREACH_TRANS_DATA_CONTAINER (t, tc) {
+ if (tc->custom.type.data != NULL) {
+ struct TransCustomDataMesh *tcmd = tc->custom.type.data;
+ if (tcmd && tcmd->cd_layer_correct) {
+ tc_mesh_customdatacorrect_free(tcmd->cd_layer_correct);
+ tcmd->cd_layer_correct = NULL;
+ }
+ }
+
+ tc_mesh_customdatacorrect_create(tc, use_merge_group);
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name CustomData Layer Correction Apply
+ * \{ */
+
+/**
+ * If we're sliding the vert, return its original location, if not, the current location is good.
+ */
+static const float *tc_mesh_vert_orig_co_get(struct TransCustomDataLayer *tcld, BMVert *v)
+{
+ TransDataBasic *td = BLI_ghash_lookup(tcld->merge_group.origverts, v);
+ return td ? td->iloc : v->co;
+}
+
+static void tc_mesh_customdatacorrect_apply_vert(struct TransCustomDataLayer *tcld,
+ struct TransDataBasic *td,
+ struct TransCustomDataMergeGroup *merge_data,
+ bool do_loop_mdisps)
+{
+ BMesh *bm = tcld->bm;
+ BMVert *v = td->extra;
+ const float *co_orig_3d = td->iloc;
+
+ BMIter liter;
+ int j, l_num;
+ float *loop_weights;
+ const bool is_moved = (len_squared_v3v3(v->co, co_orig_3d) > FLT_EPSILON);
+ const bool do_loop_weight = is_moved && tcld->merge_group.customdatalayer_map_len;
+ const float *v_proj_axis = v->no;
+ /* original (l->prev, l, l->next) projections for each loop ('l' remains unchanged) */
+ float v_proj[3][3];
+
+ if (do_loop_weight) {
+ project_plane_normalized_v3_v3v3(v_proj[1], co_orig_3d, v_proj_axis);
+ }
+
+ // BM_ITER_ELEM (l, &liter, sv->v, BM_LOOPS_OF_VERT)
+ BM_iter_init(&liter, bm, BM_LOOPS_OF_VERT, v);
+ l_num = liter.count;
+ loop_weights = do_loop_weight ? BLI_array_alloca(loop_weights, l_num) : NULL;
+ for (j = 0; j < l_num; j++) {
+ BMFace *f_copy; /* the copy of 'f' */
+ BMLoop *l = BM_iter_step(&liter);
+
+ f_copy = BLI_ghash_lookup(tcld->origfaces, l->f);
+
+#ifdef USE_FACE_SUBSTITUTE
+ /* In some faces it is not possible to calculate interpolation,
+ * so we use a substitute. */
+ if (BM_elem_index_get(f_copy) == FACE_SUBSTITUTE_INDEX) {
+ f_copy = tc_mesh_customdatacorrect_face_substitute_get(f_copy);
+ }
+#endif
+
+ /* only loop data, no vertex data since that contains shape keys,
+ * and we do not want to mess up other shape keys */
+ BM_loop_interp_from_face(bm, l, f_copy, false, false);
+
+ /* weight the loop */
+ if (do_loop_weight) {
+ const float eps = 1.0e-8f;
+ const BMLoop *l_prev = l->prev;
+ const BMLoop *l_next = l->next;
+ const float *co_prev = tc_mesh_vert_orig_co_get(tcld, l_prev->v);
+ const float *co_next = tc_mesh_vert_orig_co_get(tcld, l_next->v);
+ bool co_prev_ok;
+ bool co_next_ok;
+
+ /* In the unlikely case that we're next to a zero length edge -
+ * walk around the to the next.
+ *
+ * Since we only need to check if the vertex is in this corner,
+ * its not important _which_ loop - as long as its not overlapping
+ * 'sv->co_orig_3d', see: T45096. */
+ project_plane_normalized_v3_v3v3(v_proj[0], co_prev, v_proj_axis);
+ while (UNLIKELY(((co_prev_ok = (len_squared_v3v3(v_proj[1], v_proj[0]) > eps)) == false) &&
+ ((l_prev = l_prev->prev) != l->next))) {
+ co_prev = tc_mesh_vert_orig_co_get(tcld, l_prev->v);
+ project_plane_normalized_v3_v3v3(v_proj[0], co_prev, v_proj_axis);
+ }
+ project_plane_normalized_v3_v3v3(v_proj[2], co_next, v_proj_axis);
+ while (UNLIKELY(((co_next_ok = (len_squared_v3v3(v_proj[1], v_proj[2]) > eps)) == false) &&
+ ((l_next = l_next->next) != l->prev))) {
+ co_next = tc_mesh_vert_orig_co_get(tcld, l_next->v);
+ project_plane_normalized_v3_v3v3(v_proj[2], co_next, v_proj_axis);
+ }
+
+ if (co_prev_ok && co_next_ok) {
+ const float dist = dist_signed_squared_to_corner_v3v3v3(
+ v->co, UNPACK3(v_proj), v_proj_axis);
+
+ loop_weights[j] = (dist >= 0.0f) ? 1.0f : ((dist <= -eps) ? 0.0f : (1.0f + (dist / eps)));
+ if (UNLIKELY(!isfinite(loop_weights[j]))) {
+ loop_weights[j] = 0.0f;
+ }
+ }
+ else {
+ loop_weights[j] = 0.0f;
+ }
+ }
+ }
+
+ if (tcld->use_merge_group) {
+ struct LinkNode **cd_loop_groups = merge_data->cd_loop_groups;
+ if (tcld->merge_group.customdatalayer_map_len && cd_loop_groups) {
+ if (do_loop_weight) {
+ for (j = 0; j < tcld->merge_group.customdatalayer_map_len; j++) {
+ BM_vert_loop_groups_data_layer_merge_weights(
+ bm, cd_loop_groups[j], tcld->merge_group.customdatalayer_map[j], loop_weights);
+ }
+ }
+ else {
+ for (j = 0; j < tcld->merge_group.customdatalayer_map_len; j++) {
+ BM_vert_loop_groups_data_layer_merge(
+ bm, cd_loop_groups[j], tcld->merge_group.customdatalayer_map[j]);
+ }
+ }
+ }
+ }
+
+ /* Special handling for multires
+ *
+ * Interpolate from every other loop (not ideal)
+ * However values will only be taken from loops which overlap other mdisps.
+ */
+ const bool update_loop_mdisps = is_moved && do_loop_mdisps && (tcld->cd_loop_mdisp_offset != -1);
+ if (update_loop_mdisps) {
+ float(*faces_center)[3] = BLI_array_alloca(faces_center, l_num);
+ BMLoop *l;
+
+ BM_ITER_ELEM_INDEX (l, &liter, v, BM_LOOPS_OF_VERT, j) {
+ BM_face_calc_center_median(l->f, faces_center[j]);
+ }
+
+ BM_ITER_ELEM_INDEX (l, &liter, v, BM_LOOPS_OF_VERT, j) {
+ BMFace *f_copy = BLI_ghash_lookup(tcld->origfaces, l->f);
+ float f_copy_center[3];
+ BMIter liter_other;
+ BMLoop *l_other;
+ int j_other;
+
+ BM_face_calc_center_median(f_copy, f_copy_center);
+
+ BM_ITER_ELEM_INDEX (l_other, &liter_other, v, BM_LOOPS_OF_VERT, j_other) {
+ BM_face_interp_multires_ex(bm,
+ l_other->f,
+ f_copy,
+ faces_center[j_other],
+ f_copy_center,
+ tcld->cd_loop_mdisp_offset);
+ }
+ }
+ }
+}
+
+static void tc_mesh_customdatacorrect_apply(TransDataContainer *tc, bool is_final)
+{
+ struct TransCustomDataMesh *tcmd = tc->custom.type.data;
+ struct TransCustomDataLayer *tcld = tcmd ? tcmd->cd_layer_correct : NULL;
+ if (tcld == NULL) {
+ return;
+ }
+ const bool use_merge_group = tcld->use_merge_group;
+
+ struct TransCustomDataMergeGroup *merge_data = tcld->merge_group.data;
+ TransData *tob = tc->data;
+ for (int i = tc->data_len; i--; tob++) {
+ tc_mesh_customdatacorrect_apply_vert(tcld, (TransDataBasic *)tob, merge_data, is_final);
+
+ if (use_merge_group) {
+ merge_data++;
+ }
+ }
+
+ TransDataMirror *td_mirror = tc->data_mirror;
+ for (int i = tc->data_mirror_len; i--; td_mirror++) {
+ tc_mesh_customdatacorrect_apply_vert(tcld, (TransDataBasic *)td_mirror, merge_data, is_final);
+
+ if (use_merge_group) {
+ merge_data++;
+ }
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name CustomData Layer Correction Restore
+ * \{ */
+
+static void tc_mesh_customdatacorrect_restore(struct TransInfo *t)
+{
+ FOREACH_TRANS_DATA_CONTAINER (t, tc) {
+ struct TransCustomDataMesh *tcmd = tc->custom.type.data;
+ struct TransCustomDataLayer *tcld = tcmd ? tcmd->cd_layer_correct : NULL;
+ if (!tcld) {
+ continue;
+ }
+
+ BMesh *bm = tcld->bm;
+ BMesh *bm_copy = tcld->bm_origfaces;
+
+ GHashIterator gh_iter;
+ GHASH_ITER (gh_iter, tcld->origfaces) {
+ BMFace *f = BLI_ghashIterator_getKey(&gh_iter);
+ BMFace *f_copy = BLI_ghashIterator_getValue(&gh_iter);
+ BLI_assert(f->len == f_copy->len);
+
+ BMLoop *l_iter, *l_first, *l_copy;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ l_copy = BM_FACE_FIRST_LOOP(f_copy);
+ do {
+ /* TODO: Restore only the elements that transform. */
+ BM_elem_attrs_copy(bm_copy, bm, l_copy, l_iter);
+ l_copy = l_copy->next;
+ } while ((l_iter = l_iter->next) != l_first);
+ }
+ }
+}
+
+/** \} */
/* -------------------------------------------------------------------- */
/** \name Island Creation
@@ -384,6 +1014,14 @@ void transform_convert_mesh_connectivity_distance(struct BMesh *bm,
BMEdge *e;
BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
+
+ /* Always clear to satisfy the assert, also predictable to leave in cleared state. */
+ BM_elem_flag_disable(e, tag_queued);
+
+ if (BM_elem_flag_test(e, BM_ELEM_HIDDEN)) {
+ continue;
+ }
+
BMVert *v1 = e->v1;
BMVert *v2 = e->v2;
int i1 = BM_elem_index_get(v1);
@@ -392,7 +1030,6 @@ void transform_convert_mesh_connectivity_distance(struct BMesh *bm,
if (dists[i1] != FLT_MAX || dists[i2] != FLT_MAX) {
BLI_LINKSTACK_PUSH(queue, e);
}
- BM_elem_flag_disable(e, tag_queued);
BM_elem_flag_set(e, tag_loose, bmesh_test_loose_edge(e));
}
}
@@ -420,6 +1057,7 @@ void transform_convert_mesh_connectivity_distance(struct BMesh *bm,
BMIter eiter;
BM_ITER_ELEM (e_other, &eiter, v2, BM_EDGES_OF_VERT) {
if (e_other != e && BM_elem_flag_test(e_other, tag_queued) == 0 &&
+ !BM_elem_flag_test(e_other, BM_ELEM_HIDDEN) &&
(BM_elem_flag_test(e, tag_loose) || BM_elem_flag_test(e_other, tag_loose))) {
BM_elem_flag_enable(e_other, tag_queued);
BLI_LINKSTACK_PUSH(queue_next, e_other);
@@ -433,6 +1071,11 @@ void transform_convert_mesh_connectivity_distance(struct BMesh *bm,
BMLoop *l;
BMIter liter;
BM_ITER_ELEM (l, &liter, e, BM_LOOPS_OF_EDGE) {
+ if (BM_elem_flag_test(l->f, BM_ELEM_HIDDEN)) {
+ continue;
+ }
+ /* Don't check hidden edges or vertices in this loop
+ * since any hidden edge causes the face to be hidden too. */
for (BMLoop *l_other = l->next->next; l_other != l; l_other = l_other->next) {
BMVert *v_other = l_other->v;
BLI_assert(!ELEM(v_other, v1, v2));
@@ -445,6 +1088,7 @@ void transform_convert_mesh_connectivity_distance(struct BMesh *bm,
BMIter eiter;
BM_ITER_ELEM (e_other, &eiter, v_other, BM_EDGES_OF_VERT) {
if (e_other != e && BM_elem_flag_test(e_other, tag_queued) == 0 &&
+ !BM_elem_flag_test(e_other, BM_ELEM_HIDDEN) &&
(BM_elem_flag_test(e_other, tag_loose) ||
dists[BM_elem_index_get(BM_edge_other_vert(e_other, v_other))] != FLT_MAX)) {
BM_elem_flag_enable(e_other, tag_queued);
@@ -727,10 +1371,10 @@ void transform_convert_mesh_crazyspace_free(struct TransMeshDataCrazySpace *r_cr
/** \name Edit Mesh Verts Transform Creation
* \{ */
-static void transdata_center_get(const struct TransIslandData *island_data,
- const int island_index,
- const float iloc[3],
- float r_center[3])
+static void tc_mesh_transdata_center_copy(const struct TransIslandData *island_data,
+ const int island_index,
+ const float iloc[3],
+ float r_center[3])
{
if (island_data->center && island_index != -1) {
copy_v3_v3(r_center, island_data->center[island_index]);
@@ -769,7 +1413,7 @@ static void VertsToTransData(TransInfo *t,
no = eve->no;
}
- transdata_center_get(island_data, island_index, td->iloc, td->center);
+ tc_mesh_transdata_center_copy(island_data, island_index, td->iloc, td->center);
if ((island_index != -1) && island_data->axismtx) {
copy_m3_m3(td->axismtx, island_data->axismtx[island_index]);
@@ -959,7 +1603,8 @@ void createTransEditVerts(TransInfo *t)
copy_v3_v3(td_mirror->iloc, eve->co);
td_mirror->flag = mirror_data.vert_map[a].flag;
td_mirror->loc_src = v_src->co;
- transdata_center_get(&island_data, island_index, td_mirror->iloc, td_mirror->center);
+ tc_mesh_transdata_center_copy(
+ &island_data, island_index, td_mirror->iloc, td_mirror->center);
td_mirror++;
}
@@ -1031,590 +1676,133 @@ void createTransEditVerts(TransInfo *t)
/** \} */
/* -------------------------------------------------------------------- */
-/** \name CustomData Layer Correction
+/** \name Recalc Mesh Data
* \{ */
-struct TransCustomDataMergeGroup {
- /** map {BMVert: TransCustomDataLayerVert} */
- struct LinkNode **cd_loop_groups;
-};
-
-struct TransCustomDataLayer {
- BMesh *bm;
- struct MemArena *arena;
-
- struct GHash *origfaces;
- struct BMesh *bm_origfaces;
-
- /* Special handle for multi-resolution. */
- int cd_loop_mdisp_offset;
-
- /* Optionally merge custom-data groups (this keeps UVs connected for example). */
- struct {
- /** map {BMVert: TransDataBasic} */
- struct GHash *origverts;
- struct TransCustomDataMergeGroup *data;
- int data_len;
- /** Array size of 'layer_math_map_len'
- * maps #TransCustomDataLayerVert.cd_group index to absolute #CustomData layer index */
- int *customdatalayer_map;
- /** Number of math BMLoop layers. */
- int customdatalayer_map_len;
- } merge_group;
-
- bool use_merge_group;
-};
-
-static void mesh_customdatacorrect_free_cb(struct TransInfo *UNUSED(t),
- struct TransDataContainer *UNUSED(tc),
- struct TransCustomData *custom_data)
+static bool bm_vert_tag_filter_fn(BMVert *v, void *UNUSED(user_data))
{
- struct TransCustomDataLayer *tcld = custom_data->data;
- bmesh_edit_end(tcld->bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES);
-
- if (tcld->bm_origfaces) {
- BM_mesh_free(tcld->bm_origfaces);
- }
- if (tcld->origfaces) {
- BLI_ghash_free(tcld->origfaces, NULL, NULL);
- }
- if (tcld->merge_group.origverts) {
- BLI_ghash_free(tcld->merge_group.origverts, NULL, NULL);
- }
- if (tcld->arena) {
- BLI_memarena_free(tcld->arena);
- }
- if (tcld->merge_group.customdatalayer_map) {
- MEM_freeN(tcld->merge_group.customdatalayer_map);
- }
-
- MEM_freeN(tcld);
- custom_data->data = NULL;
-}
-
-#ifdef USE_FACE_SUBSTITUTE
-
-# define FACE_SUBSTITUTE_INDEX INT_MIN
-
-/**
- * Search for a neighboring face with area and preferably without selected vertex.
- * Used to replace area-less faces in custom-data correction.
- */
-static BMFace *mesh_customdatacorrect_find_best_face_substitute(BMFace *f)
-{
- BMFace *best_face = NULL;
- BMLoop *l;
- BMIter liter;
- BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
- BMLoop *l_radial_next = l->radial_next;
- BMFace *f_test = l_radial_next->f;
- if (f_test == f) {
- continue;
- }
- if (is_zero_v3(f_test->no)) {
- continue;
- }
-
- /* Check the loops edge isn't selected. */
- if (!BM_elem_flag_test(l_radial_next->v, BM_ELEM_SELECT) &&
- !BM_elem_flag_test(l_radial_next->next->v, BM_ELEM_SELECT)) {
- /* Prefer edges with unselected vertices.
- * Useful for extrude. */
- best_face = f_test;
- break;
- }
- if (best_face == NULL) {
- best_face = f_test;
- }
- }
- return best_face;
-}
-
-static void mesh_customdatacorrect_face_substitute_set(struct TransCustomDataLayer *tcld,
- BMFace *f,
- BMFace *f_copy)
-{
- BLI_assert(is_zero_v3(f->no));
- BMesh *bm = tcld->bm;
- /* It is impossible to calculate the loops weights of a face without area.
- * Find a substitute. */
- BMFace *f_substitute = mesh_customdatacorrect_find_best_face_substitute(f);
- if (f_substitute) {
- /* Copy the custom-data from the substitute face. */
- BMLoop *l_iter, *l_first;
- l_iter = l_first = BM_FACE_FIRST_LOOP(f);
- do {
- BM_loop_interp_from_face(bm, l_iter, f_substitute, false, false);
- } while ((l_iter = l_iter->next) != l_first);
-
- /* Use the substitute face as the reference during the transformation. */
- BMFace *f_substitute_copy = BM_face_copy(tcld->bm_origfaces, bm, f_substitute, true, true);
-
- /* Hack: reference substitute face in `f_copy->no`.
- * `tcld->origfaces` is already used to restore the initial value. */
- BM_elem_index_set(f_copy, FACE_SUBSTITUTE_INDEX);
- *((BMFace **)&f_copy->no[0]) = f_substitute_copy;
- }
+ return BM_elem_flag_test(v, BM_ELEM_TAG);
}
-static BMFace *mesh_customdatacorrect_face_substitute_get(BMFace *f_copy)
+static BMPartialUpdate *tc_mesh_ensure_partial_update(TransInfo *t, TransDataContainer *tc)
{
- BLI_assert(BM_elem_index_get(f_copy) == FACE_SUBSTITUTE_INDEX);
- return *((BMFace **)&f_copy->no[0]);
-}
-
-#endif /* USE_FACE_SUBSTITUTE */
+ struct TransCustomDataMesh *tcmd = tc_mesh_customdata_ensure(tc);
-static void mesh_customdatacorrect_init_vert(struct TransCustomDataLayer *tcld,
- struct TransDataBasic *td,
- const int index)
-{
- BMesh *bm = tcld->bm;
- BMVert *v = td->extra;
- BMIter liter;
- int j, l_num;
- float *loop_weights;
+ if (tcmd->partial_update.cache) {
- // BM_ITER_ELEM (l, &liter, sv->v, BM_LOOPS_OF_VERT) {
- BM_iter_init(&liter, bm, BM_LOOPS_OF_VERT, v);
- l_num = liter.count;
- loop_weights = tcld->use_merge_group ? BLI_array_alloca(loop_weights, l_num) : NULL;
- for (j = 0; j < l_num; j++) {
- BMLoop *l = BM_iter_step(&liter);
- BMLoop *l_prev, *l_next;
+ /* Recalculate partial update data when the proportional editing size changes.
+ *
+ * Note that decreasing the proportional editing size requires the existing
+ * partial data is used before recreating this partial data at the smaller size.
+ * Since excluding geometry from being transformed requires an update.
+ *
+ * Extra logic is needed to account for this situation. */
- /* Generic custom-data correction. Copy face data. */
- void **val_p;
- if (!BLI_ghash_ensure_p(tcld->origfaces, l->f, &val_p)) {
- BMFace *f_copy = BM_face_copy(tcld->bm_origfaces, bm, l->f, true, true);
- *val_p = f_copy;
-#ifdef USE_FACE_SUBSTITUTE
- if (is_zero_v3(l->f->no)) {
- mesh_customdatacorrect_face_substitute_set(tcld, l->f, f_copy);
- }
-#endif
+ bool recalc;
+ if (tcmd->partial_update.prop_size_prev < t->prop_size) {
+ /* Size increase, simply recalculate. */
+ recalc = true;
}
-
- if (tcld->use_merge_group) {
- if ((l_prev = BM_loop_find_prev_nodouble(l, l->next, FLT_EPSILON)) &&
- (l_next = BM_loop_find_next_nodouble(l, l_prev, FLT_EPSILON))) {
- loop_weights[j] = angle_v3v3v3(l_prev->v->co, l->v->co, l_next->v->co);
- }
- else {
- loop_weights[j] = 0.0f;
- }
+ else if (tcmd->partial_update.prop_size_prev > t->prop_size) {
+ /* Size decreased, first use this partial data since reducing the size will transform
+ * geometry which needs recalculating. */
+ tcmd->partial_update.prop_size_prev = t->prop_size;
+ recalc = false;
}
- }
-
- if (tcld->use_merge_group) {
- /* Store cd_loop_groups. */
- struct TransCustomDataMergeGroup *merge_data = &tcld->merge_group.data[index];
- if (l_num != 0) {
- merge_data->cd_loop_groups = BLI_memarena_alloc(
- tcld->arena, tcld->merge_group.customdatalayer_map_len * sizeof(void *));
- for (j = 0; j < tcld->merge_group.customdatalayer_map_len; j++) {
- const int layer_nr = tcld->merge_group.customdatalayer_map[j];
- merge_data->cd_loop_groups[j] = BM_vert_loop_groups_data_layer_create(
- bm, v, layer_nr, loop_weights, tcld->arena);
- }
+ else if (tcmd->partial_update.prop_size != t->prop_size) {
+ BLI_assert(tcmd->partial_update.prop_size > tcmd->partial_update.prop_size_prev);
+ recalc = true;
}
else {
- merge_data->cd_loop_groups = NULL;
+ BLI_assert(t->prop_size == tcmd->partial_update.prop_size_prev);
+ recalc = false;
}
- BLI_ghash_insert(tcld->merge_group.origverts, v, td);
- }
-}
-
-static void mesh_customdatacorrect_init_container_generic(TransDataContainer *UNUSED(tc),
- struct TransCustomDataLayer *tcld)
-{
- BMesh *bm = tcld->bm;
-
- struct GHash *origfaces = BLI_ghash_ptr_new(__func__);
- struct BMesh *bm_origfaces = BM_mesh_create(&bm_mesh_allocsize_default,
- &((struct BMeshCreateParams){
- .use_toolflags = false,
- }));
-
- /* We need to have matching loop custom-data. */
- BM_mesh_copy_init_customdata_all_layers(bm_origfaces, bm, BM_LOOP, NULL);
-
- tcld->origfaces = origfaces;
- tcld->bm_origfaces = bm_origfaces;
-
- bmesh_edit_begin(bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES);
- tcld->cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS);
-}
-
-static void mesh_customdatacorrect_init_container_merge_group(TransDataContainer *tc,
- struct TransCustomDataLayer *tcld)
-{
- BMesh *bm = tcld->bm;
- BLI_assert(CustomData_has_math(&bm->ldata));
-
- /* TODO: We don't need `layer_math_map` when there are no loops linked
- * to one of the sliding vertices. */
-
- /* Over allocate, only 'math' layers are indexed. */
- int *customdatalayer_map = MEM_mallocN(sizeof(int) * bm->ldata.totlayer, __func__);
- int layer_math_map_len = 0;
- for (int i = 0; i < bm->ldata.totlayer; i++) {
- if (CustomData_layer_has_math(&bm->ldata, i)) {
- customdatalayer_map[layer_math_map_len++] = i;
+ if (!recalc) {
+ return tcmd->partial_update.cache;
}
- }
- BLI_assert(layer_math_map_len != 0);
-
- tcld->merge_group.data_len = tc->data_len + tc->data_mirror_len;
- tcld->merge_group.customdatalayer_map = customdatalayer_map;
- tcld->merge_group.customdatalayer_map_len = layer_math_map_len;
- tcld->merge_group.origverts = BLI_ghash_ptr_new_ex(__func__, tcld->merge_group.data_len);
- tcld->merge_group.data = BLI_memarena_alloc(
- tcld->arena, tcld->merge_group.data_len * sizeof(*tcld->merge_group.data));
-}
-static void mesh_customdatacorrect_init_container(TransDataContainer *tc,
- const bool use_merge_group)
-{
- if (tc->custom.type.data) {
- /* The custom-data correction has been initiated before.
- * Free since some modes have different settings. */
- mesh_customdatacorrect_free_cb(NULL, tc, &tc->custom.type);
+ BM_mesh_partial_destroy(tcmd->partial_update.cache);
+ tcmd->partial_update.cache = NULL;
}
BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
- BMesh *bm = em->bm;
- if (bm->shapenr > 1) {
- /* Don't do this at all for non-basis shape keys, too easy to
- * accidentally break uv maps or vertex colors then */
- /* create copies of faces for custom-data projection. */
- return;
- }
- if (!CustomData_has_math(&bm->ldata) && !CustomData_has_layer(&bm->ldata, CD_MDISPS)) {
- /* There is no custom-data to correct. */
- return;
- }
+ BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false);
- struct TransCustomDataLayer *tcld = MEM_callocN(sizeof(*tcld), __func__);
- tcld->bm = bm;
- tcld->arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
-
- /* Init `cd_loop_mdisp_offset` to -1 to avoid problems with a valid index. */
- tcld->cd_loop_mdisp_offset = -1;
- tcld->use_merge_group = use_merge_group;
-
- mesh_customdatacorrect_init_container_generic(tc, tcld);
-
- if (tcld->use_merge_group) {
- mesh_customdatacorrect_init_container_merge_group(tc, tcld);
- }
-
- {
- /* Setup Verts. */
- int i = 0;
-
- TransData *tob = tc->data;
- for (int j = tc->data_len; j--; tob++, i++) {
- mesh_customdatacorrect_init_vert(tcld, (TransDataBasic *)tob, i);
- }
-
- TransDataMirror *td_mirror = tc->data_mirror;
- for (int j = tc->data_mirror_len; j--; td_mirror++, i++) {
- mesh_customdatacorrect_init_vert(tcld, (TransDataBasic *)td_mirror, i);
+ int verts_len = 0;
+ int i;
+ TransData *td;
+ for (i = 0, td = tc->data; i < tc->data_len; i++, td++) {
+ if (td->factor != 0.0f) {
+ BMVert *v = (BMVert *)td->extra;
+ BM_elem_flag_enable(v, BM_ELEM_TAG);
+ verts_len += 1;
}
}
- tc->custom.type.data = tcld;
- tc->custom.type.free_cb = mesh_customdatacorrect_free_cb;
-}
+ TransDataMirror *td_mirror = tc->data_mirror;
+ for (i = 0; i < tc->data_mirror_len; i++, td_mirror++) {
+ BMVert *v_mirr = (BMVert *)POINTER_OFFSET(td_mirror->loc_src, -offsetof(BMVert, co));
-void mesh_customdatacorrect_init(TransInfo *t)
-{
- bool use_merge_group = false;
- if (ELEM(t->mode, TFM_EDGE_SLIDE, TFM_VERT_SLIDE)) {
- if (!(t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT_SLIDE)) {
- /* No custom-data correction. */
- return;
- }
- use_merge_group = true;
- }
- else if (ELEM(t->mode,
- TFM_TRANSLATION,
- TFM_ROTATION,
- TFM_RESIZE,
- TFM_TOSPHERE,
- TFM_SHEAR,
- TFM_BEND,
- TFM_SHRINKFATTEN,
- TFM_TRACKBALL,
- TFM_PUSHPULL,
- TFM_ALIGN)) {
- {
- if (!(t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT)) {
- /* No custom-data correction. */
- return;
- }
- use_merge_group = (t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT_KEEP_CONNECTED) != 0;
+ /* The equality check is to account for the case when topology mirror moves
+ * the vertex from it's original location to match it's symmetrical position,
+ * with proportional editing enabled. */
+ if (BM_elem_flag_test(v_mirr, BM_ELEM_TAG) || !equals_v3v3(td_mirror->loc, td_mirror->iloc)) {
+ BMVert *v_mirr_other = (BMVert *)td_mirror->extra;
+ /* This assert should never fail since there is no overlap
+ * between mirrored vertices and non-mirrored. */
+ BLI_assert(!BM_elem_flag_test(v_mirr_other, BM_ELEM_TAG));
+ BM_elem_flag_enable(v_mirr_other, BM_ELEM_TAG);
+ verts_len += 1;
}
}
- else {
- return;
- }
- FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- mesh_customdatacorrect_init_container(tc, use_merge_group);
- }
-}
+ tcmd->partial_update.cache = BM_mesh_partial_create_from_verts(em->bm,
+ &(BMPartialUpdate_Params){
+ .do_tessellate = true,
+ .do_normals = true,
+ },
+ verts_len,
+ bm_vert_tag_filter_fn,
+ NULL);
-/**
- * If we're sliding the vert, return its original location, if not, the current location is good.
- */
-static const float *trans_vert_orig_co_get(struct TransCustomDataLayer *tcld, BMVert *v)
-{
- TransDataBasic *td = BLI_ghash_lookup(tcld->merge_group.origverts, v);
- return td ? td->iloc : v->co;
+ tcmd->partial_update.prop_size_prev = t->prop_size;
+ tcmd->partial_update.prop_size = t->prop_size;
+
+ return tcmd->partial_update.cache;
}
-static void mesh_customdatacorrect_apply_vert(struct TransCustomDataLayer *tcld,
- struct TransDataBasic *td,
- struct TransCustomDataMergeGroup *merge_data,
- bool do_loop_mdisps)
+static void tc_mesh_transdata_mirror_apply(TransDataContainer *tc)
{
- BMesh *bm = tcld->bm;
- BMVert *v = td->extra;
- const float *co_orig_3d = td->iloc;
-
- BMIter liter;
- int j, l_num;
- float *loop_weights;
- const bool is_moved = (len_squared_v3v3(v->co, co_orig_3d) > FLT_EPSILON);
- const bool do_loop_weight = is_moved && tcld->merge_group.customdatalayer_map_len;
- const float *v_proj_axis = v->no;
- /* original (l->prev, l, l->next) projections for each loop ('l' remains unchanged) */
- float v_proj[3][3];
-
- if (do_loop_weight) {
- project_plane_normalized_v3_v3v3(v_proj[1], co_orig_3d, v_proj_axis);
- }
-
- // BM_ITER_ELEM (l, &liter, sv->v, BM_LOOPS_OF_VERT)
- BM_iter_init(&liter, bm, BM_LOOPS_OF_VERT, v);
- l_num = liter.count;
- loop_weights = do_loop_weight ? BLI_array_alloca(loop_weights, l_num) : NULL;
- for (j = 0; j < l_num; j++) {
- BMFace *f_copy; /* the copy of 'f' */
- BMLoop *l = BM_iter_step(&liter);
-
- f_copy = BLI_ghash_lookup(tcld->origfaces, l->f);
-
-#ifdef USE_FACE_SUBSTITUTE
- /* In some faces it is not possible to calculate interpolation,
- * so we use a substitute. */
- if (BM_elem_index_get(f_copy) == FACE_SUBSTITUTE_INDEX) {
- f_copy = mesh_customdatacorrect_face_substitute_get(f_copy);
- }
-#endif
-
- /* only loop data, no vertex data since that contains shape keys,
- * and we do not want to mess up other shape keys */
- BM_loop_interp_from_face(bm, l, f_copy, false, false);
-
- /* weight the loop */
- if (do_loop_weight) {
- const float eps = 1.0e-8f;
- const BMLoop *l_prev = l->prev;
- const BMLoop *l_next = l->next;
- const float *co_prev = trans_vert_orig_co_get(tcld, l_prev->v);
- const float *co_next = trans_vert_orig_co_get(tcld, l_next->v);
- bool co_prev_ok;
- bool co_next_ok;
-
- /* In the unlikely case that we're next to a zero length edge -
- * walk around the to the next.
- *
- * Since we only need to check if the vertex is in this corner,
- * its not important _which_ loop - as long as its not overlapping
- * 'sv->co_orig_3d', see: T45096. */
- project_plane_normalized_v3_v3v3(v_proj[0], co_prev, v_proj_axis);
- while (UNLIKELY(((co_prev_ok = (len_squared_v3v3(v_proj[1], v_proj[0]) > eps)) == false) &&
- ((l_prev = l_prev->prev) != l->next))) {
- co_prev = trans_vert_orig_co_get(tcld, l_prev->v);
- project_plane_normalized_v3_v3v3(v_proj[0], co_prev, v_proj_axis);
- }
- project_plane_normalized_v3_v3v3(v_proj[2], co_next, v_proj_axis);
- while (UNLIKELY(((co_next_ok = (len_squared_v3v3(v_proj[1], v_proj[2]) > eps)) == false) &&
- ((l_next = l_next->next) != l->prev))) {
- co_next = trans_vert_orig_co_get(tcld, l_next->v);
- project_plane_normalized_v3_v3v3(v_proj[2], co_next, v_proj_axis);
- }
-
- if (co_prev_ok && co_next_ok) {
- const float dist = dist_signed_squared_to_corner_v3v3v3(
- v->co, UNPACK3(v_proj), v_proj_axis);
-
- loop_weights[j] = (dist >= 0.0f) ? 1.0f : ((dist <= -eps) ? 0.0f : (1.0f + (dist / eps)));
- if (UNLIKELY(!isfinite(loop_weights[j]))) {
- loop_weights[j] = 0.0f;
+ if (tc->use_mirror_axis_any) {
+ int i;
+ TransData *td;
+ for (i = 0, td = tc->data; i < tc->data_len; i++, td++) {
+ if (td->flag & (TD_MIRROR_EDGE_X | TD_MIRROR_EDGE_Y | TD_MIRROR_EDGE_Z)) {
+ if (td->flag & TD_MIRROR_EDGE_X) {
+ td->loc[0] = 0.0f;
}
- }
- else {
- loop_weights[j] = 0.0f;
- }
- }
- }
-
- if (tcld->use_merge_group) {
- struct LinkNode **cd_loop_groups = merge_data->cd_loop_groups;
- if (tcld->merge_group.customdatalayer_map_len && cd_loop_groups) {
- if (do_loop_weight) {
- for (j = 0; j < tcld->merge_group.customdatalayer_map_len; j++) {
- BM_vert_loop_groups_data_layer_merge_weights(
- bm, cd_loop_groups[j], tcld->merge_group.customdatalayer_map[j], loop_weights);
+ if (td->flag & TD_MIRROR_EDGE_Y) {
+ td->loc[1] = 0.0f;
}
- }
- else {
- for (j = 0; j < tcld->merge_group.customdatalayer_map_len; j++) {
- BM_vert_loop_groups_data_layer_merge(
- bm, cd_loop_groups[j], tcld->merge_group.customdatalayer_map[j]);
+ if (td->flag & TD_MIRROR_EDGE_Z) {
+ td->loc[2] = 0.0f;
}
}
}
- }
-
- /* Special handling for multires
- *
- * Interpolate from every other loop (not ideal)
- * However values will only be taken from loops which overlap other mdisps.
- */
- const bool update_loop_mdisps = is_moved && do_loop_mdisps && (tcld->cd_loop_mdisp_offset != -1);
- if (update_loop_mdisps) {
- float(*faces_center)[3] = BLI_array_alloca(faces_center, l_num);
- BMLoop *l;
-
- BM_ITER_ELEM_INDEX (l, &liter, v, BM_LOOPS_OF_VERT, j) {
- BM_face_calc_center_median(l->f, faces_center[j]);
- }
-
- BM_ITER_ELEM_INDEX (l, &liter, v, BM_LOOPS_OF_VERT, j) {
- BMFace *f_copy = BLI_ghash_lookup(tcld->origfaces, l->f);
- float f_copy_center[3];
- BMIter liter_other;
- BMLoop *l_other;
- int j_other;
-
- BM_face_calc_center_median(f_copy, f_copy_center);
-
- BM_ITER_ELEM_INDEX (l_other, &liter_other, v, BM_LOOPS_OF_VERT, j_other) {
- BM_face_interp_multires_ex(bm,
- l_other->f,
- f_copy,
- faces_center[j_other],
- f_copy_center,
- tcld->cd_loop_mdisp_offset);
- }
- }
- }
-}
-
-static void mesh_customdatacorrect_apply(TransInfo *t, bool is_final)
-{
- FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- if (!tc->custom.type.data) {
- continue;
- }
- struct TransCustomDataLayer *tcld = tc->custom.type.data;
- const bool use_merge_group = tcld->use_merge_group;
-
- struct TransCustomDataMergeGroup *merge_data = tcld->merge_group.data;
- TransData *tob = tc->data;
- for (int i = tc->data_len; i--; tob++) {
- mesh_customdatacorrect_apply_vert(tcld, (TransDataBasic *)tob, merge_data, is_final);
-
- if (use_merge_group) {
- merge_data++;
- }
- }
TransDataMirror *td_mirror = tc->data_mirror;
- for (int i = tc->data_mirror_len; i--; td_mirror++) {
- mesh_customdatacorrect_apply_vert(tcld, (TransDataBasic *)td_mirror, merge_data, is_final);
-
- if (use_merge_group) {
- merge_data++;
+ for (i = 0; i < tc->data_mirror_len; i++, td_mirror++) {
+ copy_v3_v3(td_mirror->loc, td_mirror->loc_src);
+ if (td_mirror->flag & TD_MIRROR_X) {
+ td_mirror->loc[0] *= -1;
}
- }
- }
-}
-
-static void mesh_customdatacorrect_restore(struct TransInfo *t)
-{
- FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- struct TransCustomDataLayer *tcld = tc->custom.type.data;
- if (!tcld) {
- continue;
- }
-
- BMesh *bm = tcld->bm;
- BMesh *bm_copy = tcld->bm_origfaces;
-
- GHashIterator gh_iter;
- GHASH_ITER (gh_iter, tcld->origfaces) {
- BMFace *f = BLI_ghashIterator_getKey(&gh_iter);
- BMFace *f_copy = BLI_ghashIterator_getValue(&gh_iter);
- BLI_assert(f->len == f_copy->len);
-
- BMLoop *l_iter, *l_first, *l_copy;
- l_iter = l_first = BM_FACE_FIRST_LOOP(f);
- l_copy = BM_FACE_FIRST_LOOP(f_copy);
- do {
- /* TODO: Restore only the elements that transform. */
- BM_elem_attrs_copy(bm_copy, bm, l_copy, l_iter);
- l_copy = l_copy->next;
- } while ((l_iter = l_iter->next) != l_first);
- }
- }
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Recalc Mesh Data
- * \{ */
-
-static void mesh_apply_to_mirror(TransInfo *t)
-{
- FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- if (tc->use_mirror_axis_any) {
- int i;
- TransData *td;
- for (i = 0, td = tc->data; i < tc->data_len; i++, td++) {
- if (td->flag & (TD_MIRROR_EDGE_X | TD_MIRROR_EDGE_Y | TD_MIRROR_EDGE_Z)) {
- if (td->flag & TD_MIRROR_EDGE_X) {
- td->loc[0] = 0.0f;
- }
- if (td->flag & TD_MIRROR_EDGE_Y) {
- td->loc[1] = 0.0f;
- }
- if (td->flag & TD_MIRROR_EDGE_Z) {
- td->loc[2] = 0.0f;
- }
- }
+ if (td_mirror->flag & TD_MIRROR_Y) {
+ td_mirror->loc[1] *= -1;
}
-
- TransDataMirror *td_mirror = tc->data_mirror;
- for (i = 0; i < tc->data_mirror_len; i++, td_mirror++) {
- copy_v3_v3(td_mirror->loc, td_mirror->loc_src);
- if (td_mirror->flag & TD_MIRROR_X) {
- td_mirror->loc[0] *= -1;
- }
- if (td_mirror->flag & TD_MIRROR_Y) {
- td_mirror->loc[1] *= -1;
- }
- if (td_mirror->flag & TD_MIRROR_Z) {
- td_mirror->loc[2] *= -1;
- }
+ if (td_mirror->flag & TD_MIRROR_Z) {
+ td_mirror->loc[2] *= -1;
}
}
}
@@ -1623,27 +1811,45 @@ static void mesh_apply_to_mirror(TransInfo *t)
void recalcData_mesh(TransInfo *t)
{
bool is_canceling = t->state == TRANS_CANCEL;
- /* mirror modifier clipping? */
+ /* Apply corrections. */
if (!is_canceling) {
- /* apply clipping after so we never project past the clip plane T25423. */
applyProject(t);
- clipMirrorModifier(t);
- if ((t->flag & T_NO_MIRROR) == 0 && (t->options & CTX_NO_MIRROR) == 0) {
- mesh_apply_to_mirror(t);
- }
+ bool do_mirror = !(t->flag & T_NO_MIRROR);
+ FOREACH_TRANS_DATA_CONTAINER (t, tc) {
+ /* Apply clipping after so we never project past the clip plane T25423. */
+ transform_convert_clip_mirror_modifier_apply(tc);
+
+ if (do_mirror) {
+ tc_mesh_transdata_mirror_apply(tc);
+ }
- mesh_customdatacorrect_apply(t, false);
+ tc_mesh_customdatacorrect_apply(tc, false);
+ }
}
else {
- mesh_customdatacorrect_restore(t);
+ tc_mesh_customdatacorrect_restore(t);
}
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- DEG_id_tag_update(tc->obedit->data, 0); /* sets recalc flags */
+ DEG_id_tag_update(tc->obedit->data, ID_RECALC_GEOMETRY);
BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
- EDBM_mesh_normals_update(em);
- BKE_editmesh_looptri_calc(em);
+
+ /* The additional cost of generating the partial connectivity data isn't justified
+ * when all data needs to be updated.
+ *
+ * While proportional editing can cause all geometry to need updating with a partial selection.
+ * It's impractical to calculate this ahead of time.
+ * Further, the down side of using partial updates when their not needed is negligible. */
+ if (em->bm->totvert == em->bm->totvertsel) {
+ EDBM_mesh_normals_update(em);
+ BKE_editmesh_looptri_calc(em);
+ }
+ else {
+ BMPartialUpdate *partial_update_cache = tc_mesh_ensure_partial_update(t, tc);
+ BM_mesh_normals_update_with_partial(em->bm, partial_update_cache);
+ BKE_editmesh_looptri_calc_with_partial(em, partial_update_cache);
+ }
}
}
/** \} */
@@ -1660,7 +1866,9 @@ void special_aftertrans_update__mesh(bContext *UNUSED(C), TransInfo *t)
if (!is_canceling && ELEM(t->mode, TFM_EDGE_SLIDE, TFM_VERT_SLIDE)) {
/* NOTE(joeedh): Handle multi-res re-projection,
* done on transform completion since it's really slow. */
- mesh_customdatacorrect_apply(t, true);
+ FOREACH_TRANS_DATA_CONTAINER (t, tc) {
+ tc_mesh_customdatacorrect_apply(tc, true);
+ }
}
if (use_automerge) {
diff --git a/source/blender/editors/transform/transform_convert_mesh_edge.c b/source/blender/editors/transform/transform_convert_mesh_edge.c
index bb9296b4b90..3b1191a3401 100644
--- a/source/blender/editors/transform/transform_convert_mesh_edge.c
+++ b/source/blender/editors/transform/transform_convert_mesh_edge.c
@@ -123,4 +123,11 @@ void createTransEdge(TransInfo *t)
}
}
+void recalcData_mesh_edge(TransInfo *t)
+{
+ FOREACH_TRANS_DATA_CONTAINER (t, tc) {
+ DEG_id_tag_update(tc->obedit->data, ID_RECALC_GEOMETRY);
+ }
+}
+
/** \} */
diff --git a/source/blender/editors/transform/transform_convert_mesh_skin.c b/source/blender/editors/transform/transform_convert_mesh_skin.c
index 5dbb1947773..7c61da31f72 100644
--- a/source/blender/editors/transform/transform_convert_mesh_skin.c
+++ b/source/blender/editors/transform/transform_convert_mesh_skin.c
@@ -47,9 +47,9 @@
/** \name Edit Mesh #CD_MVERT_SKIN Transform Creation
* \{ */
-static float *mesh_skin_transdata_center(const struct TransIslandData *island_data,
- const int island_index,
- BMVert *eve)
+static float *tc_mesh_skin_transdata_center(const struct TransIslandData *island_data,
+ const int island_index,
+ BMVert *eve)
{
if (island_data->center && island_index != -1) {
return island_data->center[island_index];
@@ -57,11 +57,11 @@ static float *mesh_skin_transdata_center(const struct TransIslandData *island_da
return eve->co;
}
-static void mesh_skin_transdata_create(TransDataBasic *td,
- BMEditMesh *em,
- BMVert *eve,
- const struct TransIslandData *island_data,
- const int island_index)
+static void tc_mesh_skin_transdata_create(TransDataBasic *td,
+ BMEditMesh *em,
+ BMVert *eve,
+ const struct TransIslandData *island_data,
+ const int island_index)
{
BLI_assert(BM_elem_flag_test(eve, BM_ELEM_HIDDEN) == 0);
MVertSkin *vs = CustomData_bmesh_get(&em->bm->vdata, eve->head.data, CD_MVERT_SKIN);
@@ -78,7 +78,7 @@ static void mesh_skin_transdata_create(TransDataBasic *td,
td->flag |= TD_SELECTED;
}
- copy_v3_v3(td->center, mesh_skin_transdata_center(island_data, island_index, eve));
+ copy_v3_v3(td->center, tc_mesh_skin_transdata_center(island_data, island_index, eve));
td->extra = eve;
}
@@ -209,7 +209,7 @@ void createTransMeshSkin(TransInfo *t)
}
if (mirror_data.vert_map && mirror_data.vert_map[a].index != -1) {
- mesh_skin_transdata_create(
+ tc_mesh_skin_transdata_create(
(TransDataBasic *)td_mirror, em, eve, &island_data, island_index);
int elem_index = mirror_data.vert_map[a].index;
@@ -221,7 +221,7 @@ void createTransMeshSkin(TransInfo *t)
td_mirror++;
}
else if (prop_mode || BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
- mesh_skin_transdata_create((TransDataBasic *)td, em, eve, &island_data, island_index);
+ tc_mesh_skin_transdata_create((TransDataBasic *)td, em, eve, &island_data, island_index);
if (t->around == V3D_AROUND_LOCAL_ORIGINS) {
createSpaceNormal(td->axismtx, eve->no);
@@ -275,7 +275,7 @@ void createTransMeshSkin(TransInfo *t)
/** \name Recalc Mesh Data
* \{ */
-static void mesh_skin_apply_to_mirror(TransInfo *t)
+static void tc_mesh_skin_apply_to_mirror(TransInfo *t)
{
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
if (tc->use_mirror_axis_any) {
@@ -292,13 +292,13 @@ void recalcData_mesh_skin(TransInfo *t)
bool is_canceling = t->state == TRANS_CANCEL;
/* mirror modifier clipping? */
if (!is_canceling) {
- if ((t->flag & T_NO_MIRROR) == 0 && (t->options & CTX_NO_MIRROR) == 0) {
- mesh_skin_apply_to_mirror(t);
+ if (!(t->flag & T_NO_MIRROR)) {
+ tc_mesh_skin_apply_to_mirror(t);
}
}
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- DEG_id_tag_update(tc->obedit->data, 0); /* sets recalc flags */
+ DEG_id_tag_update(tc->obedit->data, ID_RECALC_GEOMETRY);
BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
EDBM_mesh_normals_update(em);
BKE_editmesh_looptri_calc(em);
diff --git a/source/blender/editors/transform/transform_convert_mesh_uv.c b/source/blender/editors/transform/transform_convert_mesh_uv.c
index a5f90e9ac5f..d91a2a8be4b 100644
--- a/source/blender/editors/transform/transform_convert_mesh_uv.c
+++ b/source/blender/editors/transform/transform_convert_mesh_uv.c
@@ -475,7 +475,7 @@ void recalcData_uv(TransInfo *t)
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
if (tc->data_len) {
- DEG_id_tag_update(tc->obedit->data, 0);
+ DEG_id_tag_update(tc->obedit->data, ID_RECALC_GEOMETRY);
}
}
}
diff --git a/source/blender/editors/transform/transform_convert_object.c b/source/blender/editors/transform/transform_convert_object.c
index b546f1d0b4c..c217478bd04 100644
--- a/source/blender/editors/transform/transform_convert_object.c
+++ b/source/blender/editors/transform/transform_convert_object.c
@@ -282,9 +282,17 @@ static void ObjectToTransData(TransInfo *t, TransData *td, Object *ob)
*/
BKE_object_to_mat3(ob, obmtx);
copy_m3_m4(totmat, ob->obmat);
- invert_m3_m3(obinv, totmat);
+
+ /* If the object scale is zero on any axis, this might result in a zero matrix.
+ * In this case, the transformation would not do anything, see: T50103. */
+ orthogonalize_m3_zero_axes(obmtx, 1.0f);
+ orthogonalize_m3_zero_axes(totmat, 1.0f);
+
+ /* Use safe invert even though the input matrices have had zero axes set to unit length,
+ * in the unlikely case of failure (float precision for eg) this uses unit matrix fallback. */
+ invert_m3_m3_safe_ortho(obinv, totmat);
mul_m3_m3m3(td->smtx, obmtx, obinv);
- invert_m3_m3(td->mtx, td->smtx);
+ invert_m3_m3_safe_ortho(td->mtx, td->smtx);
}
else {
/* no conversion to/from dataspace */
diff --git a/source/blender/editors/transform/transform_convert_sequencer.c b/source/blender/editors/transform/transform_convert_sequencer.c
index 30418471d6d..6a09008e657 100644
--- a/source/blender/editors/transform/transform_convert_sequencer.c
+++ b/source/blender/editors/transform/transform_convert_sequencer.c
@@ -90,8 +90,8 @@ static void SeqTransInfo(TransInfo *t, Sequence *seq, int *r_recursive, int *r_c
Scene *scene = t->scene;
int cfra = CFRA;
- int left = SEQ_transform_get_left_handle_frame(seq, false);
- int right = SEQ_transform_get_right_handle_frame(seq, false);
+ int left = SEQ_transform_get_left_handle_frame(seq);
+ int right = SEQ_transform_get_right_handle_frame(seq);
if (seq->depth == 0 && ((seq->flag & SELECT) == 0 || (seq->flag & SEQ_LOCK))) {
*r_recursive = false;
@@ -172,7 +172,7 @@ static void SeqTransInfo(TransInfo *t, Sequence *seq, int *r_recursive, int *r_c
}
}
-static int SeqTransCount(TransInfo *t, Sequence *parent, ListBase *seqbase, int depth)
+static int SeqTransCount(TransInfo *t, ListBase *seqbase, int depth)
{
Sequence *seq;
int tot = 0, recursive, count, flag;
@@ -180,16 +180,11 @@ static int SeqTransCount(TransInfo *t, Sequence *parent, ListBase *seqbase, int
for (seq = seqbase->first; seq; seq = seq->next) {
seq->depth = depth;
- /* 'seq->tmp' is used by seq_tx_get_final_{left, right}
- * to check sequence's range and clamp to it if needed.
- * It's first place where digging into sequences tree, so store link to parent here. */
- seq->tmp = parent;
-
SeqTransInfo(t, seq, &recursive, &count, &flag); /* ignore the flag */
tot += count;
if (recursive) {
- tot += SeqTransCount(t, seq, &seq->seqbase, depth + 1);
+ tot += SeqTransCount(t, &seq->seqbase, depth + 1);
}
}
@@ -206,16 +201,16 @@ static TransData *SeqToTransData(
/* Use seq_tx_get_final_left() and an offset here
* so transform has the left hand location of the strip.
* tdsq->start_offset is used when flushing the tx data back */
- start_left = SEQ_transform_get_left_handle_frame(seq, false);
+ start_left = SEQ_transform_get_left_handle_frame(seq);
td2d->loc[0] = start_left;
tdsq->start_offset = start_left - seq->start; /* use to apply the original location */
break;
case SEQ_LEFTSEL:
- start_left = SEQ_transform_get_left_handle_frame(seq, false);
+ start_left = SEQ_transform_get_left_handle_frame(seq);
td2d->loc[0] = start_left;
break;
case SEQ_RIGHTSEL:
- td2d->loc[0] = SEQ_transform_get_right_handle_frame(seq, false);
+ td2d->loc[0] = SEQ_transform_get_right_handle_frame(seq);
break;
}
@@ -491,7 +486,7 @@ static void freeSeqData(TransInfo *t, TransDataContainer *tc, TransCustomData *c
}
}
- SEQ_sort(t->scene);
+ SEQ_sort(seqbasep);
}
else {
/* Canceled, need to update the strips display */
@@ -562,7 +557,7 @@ void createTransSeqData(TransInfo *t)
}
#endif
- count = SeqTransCount(t, NULL, ed->seqbasep, 0);
+ count = SeqTransCount(t, ed->seqbasep, 0);
/* allocate memory for data */
tc->data_len = count;
@@ -707,7 +702,7 @@ static void flushTransSeq(TransInfo *t)
/* originally TFM_TIME_EXTEND, transform changes */
if (ELEM(t->mode, TFM_SEQ_SLIDE, TFM_TIME_TRANSLATE)) {
- /* Special annoying case here, need to calc metas with TFM_TIME_EXTEND only */
+ /* Special annoying case here, need to calc meta-strips with TFM_TIME_EXTEND only */
/* calc all meta's then effects T27953. */
for (seq = seqbasep->first; seq; seq = seq->next) {
diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c
index e43a3ff3635..71c91221fbb 100644
--- a/source/blender/editors/transform/transform_generics.c
+++ b/source/blender/editors/transform/transform_generics.c
@@ -409,11 +409,10 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
short orient_types[3];
float custom_matrix[3][3];
- short orient_type_scene = V3D_ORIENT_GLOBAL;
- short orient_type_set = V3D_ORIENT_GLOBAL;
- short orient_type_matrix_set = -1;
-
- bool use_orient_axis = false;
+ int orient_type_scene = V3D_ORIENT_GLOBAL;
+ int orient_type_default = -1;
+ int orient_type_set = -1;
+ int orient_type_matrix_set = -1;
if ((t->spacetype == SPACE_VIEW3D) && (t->region->regiontype == RGN_TYPE_WINDOW)) {
TransformOrientationSlot *orient_slot = &t->scene->orientation_slots[SCE_ORIENT_DEFAULT];
@@ -424,40 +423,20 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
}
}
- short orient_type_default = orient_type_scene;
-
- if (op && (prop = RNA_struct_find_property(op->ptr, "orient_axis"))) {
- t->orient_axis = RNA_property_enum_get(op->ptr, prop);
- use_orient_axis = true;
- }
-
- if (op && (prop = RNA_struct_find_property(op->ptr, "orient_axis_ortho"))) {
- t->orient_axis_ortho = RNA_property_enum_get(op->ptr, prop);
- }
-
if (op && ((prop = RNA_struct_find_property(op->ptr, "orient_type")) &&
RNA_property_is_set(op->ptr, prop))) {
orient_type_set = RNA_property_enum_get(op->ptr, prop);
if (orient_type_set >= V3D_ORIENT_CUSTOM + BIF_countTransformOrientation(C)) {
orient_type_set = V3D_ORIENT_GLOBAL;
}
-
- /* Change the default orientation to be used when redoing. */
- orient_type_default = orient_type_set;
}
- else if (t->con.mode & CON_APPLY) {
- orient_type_set = orient_type_scene;
+
+ if (op && (prop = RNA_struct_find_property(op->ptr, "orient_axis"))) {
+ t->orient_axis = RNA_property_enum_get(op->ptr, prop);
}
- else {
- if (orient_type_set == orient_type_scene) {
- BLI_assert(orient_type_set == V3D_ORIENT_GLOBAL);
- orient_type_set = V3D_ORIENT_LOCAL;
- }
- if ((t->flag & T_MODAL) && (use_orient_axis || transform_mode_is_changeable(t->mode)) &&
- (t->mode != TFM_ALIGN)) {
- orient_type_default = V3D_ORIENT_VIEW;
- }
+ if (op && (prop = RNA_struct_find_property(op->ptr, "orient_axis_ortho"))) {
+ t->orient_axis_ortho = RNA_property_enum_get(op->ptr, prop);
}
if (op && ((prop = RNA_struct_find_property(op->ptr, "orient_matrix")) &&
@@ -468,19 +447,41 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
RNA_property_is_set(op->ptr, prop)) {
orient_type_matrix_set = RNA_property_enum_get(op->ptr, prop);
}
- else {
- orient_type_matrix_set = orient_type_set;
+ else if (orient_type_set == -1) {
+ orient_type_set = V3D_ORIENT_CUSTOM_MATRIX;
}
+ }
- if (orient_type_matrix_set == orient_type_set) {
- /* Constraints are forced to use the custom matrix when redoing. */
- orient_type_set = V3D_ORIENT_CUSTOM_MATRIX;
+ if (orient_type_set != -1) {
+ orient_type_default = orient_type_set;
+ t->is_orient_set = true;
+ }
+ else if (orient_type_matrix_set != -1) {
+ orient_type_default = orient_type_set = orient_type_matrix_set;
+ t->is_orient_set = true;
+ }
+ else if (t->con.mode & CON_APPLY) {
+ orient_type_default = orient_type_set = orient_type_scene;
+ }
+ else {
+ orient_type_default = orient_type_scene;
+ if (orient_type_scene == V3D_ORIENT_GLOBAL) {
+ orient_type_set = V3D_ORIENT_LOCAL;
}
+ else {
+ orient_type_set = V3D_ORIENT_GLOBAL;
+ }
+ }
+
+ BLI_assert(!ELEM(-1, orient_type_default, orient_type_set));
+ if (orient_type_matrix_set == orient_type_set) {
+ /* Constraints are forced to use the custom matrix when redoing. */
+ orient_type_set = V3D_ORIENT_CUSTOM_MATRIX;
}
- orient_types[0] = orient_type_default;
- orient_types[1] = orient_type_scene;
- orient_types[2] = orient_type_set;
+ orient_types[O_DEFAULT] = (short)orient_type_default;
+ orient_types[O_SCENE] = (short)orient_type_scene;
+ orient_types[O_SET] = (short)orient_type_set;
for (int i = 0; i < 3; i++) {
/* For efficiency, avoid calculating the same orientation twice. */
@@ -497,9 +498,6 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
}
}
- /* Set orient_curr to -1 in order to force the update in
- * `transform_orientations_current_set`. */
- t->orient_curr = -1;
transform_orientations_current_set(t, (t->con.mode & CON_APPLY) ? 2 : 0);
}
@@ -529,7 +527,7 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
}
else {
/* Avoid mirroring for unsupported contexts. */
- t->options |= CTX_NO_MIRROR;
+ t->flag |= T_NO_MIRROR;
}
/* setting PET flag only if property exist in operator. Otherwise, assume it's not supported */
diff --git a/source/blender/editors/transform/transform_gizmo_3d.c b/source/blender/editors/transform/transform_gizmo_3d.c
index 27df29afd8d..7a780df0def 100644
--- a/source/blender/editors/transform/transform_gizmo_3d.c
+++ b/source/blender/editors/transform/transform_gizmo_3d.c
@@ -512,11 +512,11 @@ static void protectflag_to_drawflags(short protectflag, short *drawflags)
/* for pose mode */
static void protectflag_to_drawflags_pchan(RegionView3D *rv3d,
const bPoseChannel *pchan,
- short orientation_type)
+ short orientation_index)
{
/* Protect-flags apply to local space in pose mode, so only let them influence axis
* visibility if we show the global orientation, otherwise it's confusing. */
- if (orientation_type == V3D_ORIENT_LOCAL) {
+ if (orientation_index == V3D_ORIENT_LOCAL) {
protectflag_to_drawflags(pchan->protectflag, &rv3d->twdrawflag);
}
}
@@ -657,12 +657,9 @@ int ED_transform_calc_gizmo_stats(const bContext *C,
int a, totsel = 0;
const int pivot_point = scene->toolsettings->transform_pivot_point;
- const short orientation_type = params->orientation_type ?
- (params->orientation_type - 1) :
- scene->orientation_slots[SCE_ORIENT_DEFAULT].type;
- const short orientation_index_custom =
- params->orientation_type ? params->orientation_index_custom :
- scene->orientation_slots[SCE_ORIENT_DEFAULT].index_custom;
+ const short orient_index = params->orientation_index ?
+ (params->orientation_index - 1) :
+ BKE_scene_orientation_get_index(scene, SCE_ORIENT_DEFAULT);
/* transform widget matrix */
unit_m4(rv3d->twmat);
@@ -678,7 +675,7 @@ int ED_transform_calc_gizmo_stats(const bContext *C,
if (ob) {
float mat[3][3];
ED_transform_calc_orientation_from_type_ex(
- C, mat, scene, rv3d, ob, obedit, orientation_type, orientation_index_custom, pivot_point);
+ C, mat, scene, rv3d, ob, obedit, orient_index, pivot_point);
copy_m4_m3(rv3d->twmat, mat);
}
@@ -976,7 +973,7 @@ int ED_transform_calc_gizmo_stats(const bContext *C,
Bone *bone = pchan->bone;
if (bone && (bone->flag & BONE_TRANSFORM)) {
calc_tw_center_with_matrix(tbounds, pchan->pose_head, use_mat_local, mat_local);
- protectflag_to_drawflags_pchan(rv3d, pchan, orientation_type);
+ protectflag_to_drawflags_pchan(rv3d, pchan, orient_index);
}
}
totsel += totsel_iter;
@@ -1063,7 +1060,7 @@ int ED_transform_calc_gizmo_stats(const bContext *C,
/* Protect-flags apply to world space in object mode, so only let them influence axis
* visibility if we show the global orientation, otherwise it's confusing. */
- if (orientation_type == V3D_ORIENT_GLOBAL) {
+ if (orient_index == V3D_ORIENT_GLOBAL) {
protectflag_to_drawflags(base->object->protectflag, &rv3d->twdrawflag);
}
totsel++;
@@ -1689,18 +1686,15 @@ static void WIDGETGROUP_gizmo_refresh(const bContext *C, wmGizmoGroup *gzgroup)
}
}
- const TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get_from_flag(
- scene, ggd->twtype_init);
+ const int orient_index = BKE_scene_orientation_get_index_from_flag(scene, ggd->twtype_init);
/* skip, we don't draw anything anyway */
- if ((ggd->all_hidden = (ED_transform_calc_gizmo_stats(
- C,
- &(struct TransformCalcParams){
- .use_only_center = true,
- .orientation_type = orient_slot->type + 1,
- .orientation_index_custom = orient_slot->index_custom,
- },
- &tbounds) == 0))) {
+ if ((ggd->all_hidden = (ED_transform_calc_gizmo_stats(C,
+ &(struct TransformCalcParams){
+ .use_only_center = true,
+ .orientation_index = orient_index + 1,
+ },
+ &tbounds) == 0))) {
return;
}
@@ -2119,14 +2113,12 @@ static void WIDGETGROUP_xform_cage_refresh(const bContext *C, wmGizmoGroup *gzgr
gzgroup->use_fallback_keymap = false;
}
- const TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get(scene,
- SCE_ORIENT_SCALE);
+ const int orient_index = BKE_scene_orientation_get_index_from_flag(scene, SCE_ORIENT_SCALE);
if ((ED_transform_calc_gizmo_stats(C,
&(struct TransformCalcParams){
.use_local_axis = true,
- .orientation_type = orient_slot->type + 1,
- .orientation_index_custom = orient_slot->index_custom,
+ .orientation_index = orient_index + 1,
},
&tbounds) == 0) ||
equals_v3v3(rv3d->tw_axis_min, rv3d->tw_axis_max)) {
@@ -2335,14 +2327,14 @@ static void WIDGETGROUP_xform_shear_refresh(const bContext *C, wmGizmoGroup *gzg
/* Needed to test view orientation changes. */
copy_m3_m4(xgzgroup->prev.viewinv_m3, rv3d->viewinv);
- const TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get(scene,
- SCE_ORIENT_ROTATE);
+ TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get_from_flag(
+ scene, SCE_ORIENT_ROTATE);
+ const int orient_index = BKE_scene_orientation_slot_get_index(orient_slot);
if (ED_transform_calc_gizmo_stats(C,
&(struct TransformCalcParams){
.use_local_axis = false,
- .orientation_type = orient_slot->type + 1,
- .orientation_index_custom = orient_slot->index_custom,
+ .orientation_index = orient_index + 1,
},
&tbounds) == 0) {
for (int i = 0; i < 3; i++) {
diff --git a/source/blender/editors/transform/transform_gizmo_extrude_3d.c b/source/blender/editors/transform/transform_gizmo_extrude_3d.c
index 63c8efdd475..ed6c3eb0255 100644
--- a/source/blender/editors/transform/transform_gizmo_extrude_3d.c
+++ b/source/blender/editors/transform/transform_gizmo_extrude_3d.c
@@ -25,6 +25,7 @@
#include "BKE_context.h"
#include "BKE_global.h"
+#include "BKE_scene.h"
#include "RNA_access.h"
#include "RNA_define.h"
@@ -88,7 +89,7 @@ typedef struct GizmoExtrudeGroup {
struct {
float normal_mat3[3][3]; /* use Z axis for normal. */
- int orientation_type;
+ int orientation_index;
} data;
wmOperatorType *ot_extrude;
@@ -254,8 +255,8 @@ static void gizmo_mesh_extrude_refresh(const bContext *C, wmGizmoGroup *gzgroup)
axis_type = RNA_property_enum_get(&ptr, ggd->gzgt_axis_type_prop);
}
- ggd->data.orientation_type = scene->orientation_slots[SCE_ORIENT_DEFAULT].type;
- const bool use_normal = ((ggd->data.orientation_type != V3D_ORIENT_NORMAL) ||
+ ggd->data.orientation_index = BKE_scene_orientation_get_index(scene, SCE_ORIENT_DEFAULT);
+ const bool use_normal = ((ggd->data.orientation_index != V3D_ORIENT_NORMAL) ||
(axis_type == EXTRUDE_AXIS_NORMAL));
const int axis_len_used = use_normal ? 4 : 3;
@@ -265,7 +266,7 @@ static void gizmo_mesh_extrude_refresh(const bContext *C, wmGizmoGroup *gzgroup)
struct TransformBounds tbounds_normal;
if (!ED_transform_calc_gizmo_stats(C,
&(struct TransformCalcParams){
- .orientation_type = V3D_ORIENT_NORMAL + 1,
+ .orientation_index = V3D_ORIENT_NORMAL + 1,
},
&tbounds_normal)) {
unit_m3(tbounds_normal.axis);
@@ -276,7 +277,7 @@ static void gizmo_mesh_extrude_refresh(const bContext *C, wmGizmoGroup *gzgroup)
/* TODO(campbell): run second since this modifies the 3D view, it should not. */
if (!ED_transform_calc_gizmo_stats(C,
&(struct TransformCalcParams){
- .orientation_type = ggd->data.orientation_type + 1,
+ .orientation_index = ggd->data.orientation_index + 1,
},
&tbounds)) {
return;
@@ -391,7 +392,7 @@ static void gizmo_mesh_extrude_refresh(const bContext *C, wmGizmoGroup *gzgroup)
static void gizmo_mesh_extrude_draw_prepare(const bContext *C, wmGizmoGroup *gzgroup)
{
GizmoExtrudeGroup *ggd = gzgroup->customdata;
- switch (ggd->data.orientation_type) {
+ switch (ggd->data.orientation_index) {
case V3D_ORIENT_VIEW: {
RegionView3D *rv3d = CTX_wm_region_view3d(C);
float mat[3][3];
@@ -451,7 +452,7 @@ static void gizmo_mesh_extrude_invoke_prepare(const bContext *UNUSED(C),
if (i == 3) {
use_normal_matrix = true;
}
- else if (ggd->data.orientation_type == V3D_ORIENT_NORMAL) {
+ else if (ggd->data.orientation_index == V3D_ORIENT_NORMAL) {
use_normal_matrix = true;
}
if (use_normal_matrix) {
diff --git a/source/blender/editors/transform/transform_input.c b/source/blender/editors/transform/transform_input.c
index bfeb96d18c4..414199badd7 100644
--- a/source/blender/editors/transform/transform_input.c
+++ b/source/blender/editors/transform/transform_input.c
@@ -186,57 +186,24 @@ struct InputAngle_Data {
static void InputAngle(TransInfo *UNUSED(t), MouseInput *mi, const double mval[2], float output[3])
{
struct InputAngle_Data *data = mi->data;
- double dx2 = mval[0] - (double)mi->center[0];
- double dy2 = mval[1] - (double)mi->center[1];
- double B = sqrt(dx2 * dx2 + dy2 * dy2);
+ float dir_prev[2], dir_curr[2], mi_center[2];
+ copy_v2_v2(mi_center, mi->center);
- double dx1 = data->mval_prev[0] - (double)mi->center[0];
- double dy1 = data->mval_prev[1] - (double)mi->center[1];
- double A = sqrt(dx1 * dx1 + dy1 * dy1);
+ sub_v2_v2v2(dir_prev, (const float[2]){UNPACK2(data->mval_prev)}, mi_center);
+ sub_v2_v2v2(dir_curr, (const float[2]){UNPACK2(mval)}, mi_center);
- double dx3 = mval[0] - data->mval_prev[0];
- double dy3 = mval[1] - data->mval_prev[1];
+ if (normalize_v2(dir_prev) && normalize_v2(dir_curr)) {
+ float dphi = angle_normalized_v2v2(dir_prev, dir_curr);
- /* use doubles here, to make sure a "1.0" (no rotation)
- * doesn't become 9.999999e-01, which gives 0.02 for acos */
- double deler = (((dx1 * dx1 + dy1 * dy1) + (dx2 * dx2 + dy2 * dy2) - (dx3 * dx3 + dy3 * dy3)) /
- (2.0 * (((A * B) != 0.0) ? (A * B) : 1.0)));
- /* ((A * B) ? (A * B) : 1.0) this takes care of potential divide by zero errors */
-
- float dphi;
-
- dphi = saacos((float)deler);
- if ((dx1 * dy2 - dx2 * dy1) > 0.0) {
- dphi = -dphi;
- }
-
- /* If the angle is zero, because of lack of precision close to the 1.0 value in acos
- * approximate the angle with the opposite side of the normalized triangle
- * This is a good approximation here since the smallest acos value seems to be around
- * 0.02 degree and lower values don't even have a 0.01% error compared to the approximation
- */
- if (dphi == 0) {
- double dx, dy;
-
- dx2 /= A;
- dy2 /= A;
-
- dx1 /= B;
- dy1 /= B;
-
- dx = dx1 - dx2;
- dy = dy1 - dy2;
-
- dphi = sqrt(dx * dx + dy * dy);
- if ((dx1 * dy2 - dx2 * dy1) > 0.0) {
+ if (cross_v2v2(dir_prev, dir_curr) > 0.0f) {
dphi = -dphi;
}
- }
- data->angle += ((double)dphi) * (mi->precision ? (double)mi->precision_factor : 1.0);
+ data->angle += ((double)dphi) * (mi->precision ? (double)mi->precision_factor : 1.0);
- data->mval_prev[0] = mval[0];
- data->mval_prev[1] = mval[1];
+ data->mval_prev[0] = mval[0];
+ data->mval_prev[1] = mval[1];
+ }
output[0] = data->angle;
}
diff --git a/source/blender/editors/transform/transform_mode.c b/source/blender/editors/transform/transform_mode.c
index d14f693da9c..350be247014 100644
--- a/source/blender/editors/transform/transform_mode.c
+++ b/source/blender/editors/transform/transform_mode.c
@@ -45,6 +45,7 @@
#include "transform.h"
#include "transform_convert.h"
+#include "transform_orientations.h"
#include "transform_snap.h"
/* Own include. */
@@ -523,7 +524,7 @@ void constraintSizeLim(TransInfo *t, TransData *td)
/** \name Transform (Rotation Utils)
* \{ */
/* Used by Transform Rotation and Transform Normal Rotation */
-void headerRotation(TransInfo *t, char str[UI_MAX_DRAW_STR], float final)
+void headerRotation(TransInfo *t, char *str, const int str_size, float final)
{
size_t ofs = 0;
@@ -532,25 +533,21 @@ void headerRotation(TransInfo *t, char str[UI_MAX_DRAW_STR], float final)
outputNumInput(&(t->num), c, &t->scene->unit);
- ofs += BLI_snprintf(str + ofs,
- UI_MAX_DRAW_STR - ofs,
- TIP_("Rotation: %s %s %s"),
- &c[0],
- t->con.text,
- t->proptext);
+ ofs += BLI_snprintf_rlen(
+ str + ofs, str_size - ofs, TIP_("Rotation: %s %s %s"), &c[0], t->con.text, t->proptext);
}
else {
- ofs += BLI_snprintf(str + ofs,
- UI_MAX_DRAW_STR - ofs,
- TIP_("Rotation: %.2f%s %s"),
- RAD2DEGF(final),
- t->con.text,
- t->proptext);
+ ofs += BLI_snprintf_rlen(str + ofs,
+ str_size - ofs,
+ TIP_("Rotation: %.2f%s %s"),
+ RAD2DEGF(final),
+ t->con.text,
+ t->proptext);
}
if (t->flag & T_PROP_EDIT_ALL) {
- ofs += BLI_snprintf(
- str + ofs, UI_MAX_DRAW_STR - ofs, TIP_(" Proportional size: %.2f"), t->prop_size);
+ ofs += BLI_snprintf_rlen(
+ str + ofs, str_size - ofs, TIP_(" Proportional size: %.2f"), t->prop_size);
}
}
@@ -810,7 +807,7 @@ void ElementRotation(
/* -------------------------------------------------------------------- */
/** \name Transform (Resize Utils)
* \{ */
-void headerResize(TransInfo *t, const float vec[3], char str[UI_MAX_DRAW_STR])
+void headerResize(TransInfo *t, const float vec[3], char *str, const int str_size)
{
char tvec[NUM_STR_REP_LEN * 3];
size_t ofs = 0;
@@ -826,59 +823,55 @@ void headerResize(TransInfo *t, const float vec[3], char str[UI_MAX_DRAW_STR])
if (t->con.mode & CON_APPLY) {
switch (t->num.idx_max) {
case 0:
- ofs += BLI_snprintf(str + ofs,
- UI_MAX_DRAW_STR - ofs,
- TIP_("Scale: %s%s %s"),
- &tvec[0],
- t->con.text,
- t->proptext);
+ ofs += BLI_snprintf_rlen(
+ str + ofs, str_size - ofs, TIP_("Scale: %s%s %s"), &tvec[0], t->con.text, t->proptext);
break;
case 1:
- ofs += BLI_snprintf(str + ofs,
- UI_MAX_DRAW_STR - ofs,
- TIP_("Scale: %s : %s%s %s"),
- &tvec[0],
- &tvec[NUM_STR_REP_LEN],
- t->con.text,
- t->proptext);
+ ofs += BLI_snprintf_rlen(str + ofs,
+ str_size - ofs,
+ TIP_("Scale: %s : %s%s %s"),
+ &tvec[0],
+ &tvec[NUM_STR_REP_LEN],
+ t->con.text,
+ t->proptext);
break;
case 2:
- ofs += BLI_snprintf(str + ofs,
- UI_MAX_DRAW_STR - ofs,
- TIP_("Scale: %s : %s : %s%s %s"),
- &tvec[0],
- &tvec[NUM_STR_REP_LEN],
- &tvec[NUM_STR_REP_LEN * 2],
- t->con.text,
- t->proptext);
+ ofs += BLI_snprintf_rlen(str + ofs,
+ str_size - ofs,
+ TIP_("Scale: %s : %s : %s%s %s"),
+ &tvec[0],
+ &tvec[NUM_STR_REP_LEN],
+ &tvec[NUM_STR_REP_LEN * 2],
+ t->con.text,
+ t->proptext);
break;
}
}
else {
if (t->flag & T_2D_EDIT) {
- ofs += BLI_snprintf(str + ofs,
- UI_MAX_DRAW_STR - ofs,
- TIP_("Scale X: %s Y: %s%s %s"),
- &tvec[0],
- &tvec[NUM_STR_REP_LEN],
- t->con.text,
- t->proptext);
+ ofs += BLI_snprintf_rlen(str + ofs,
+ str_size - ofs,
+ TIP_("Scale X: %s Y: %s%s %s"),
+ &tvec[0],
+ &tvec[NUM_STR_REP_LEN],
+ t->con.text,
+ t->proptext);
}
else {
- ofs += BLI_snprintf(str + ofs,
- UI_MAX_DRAW_STR - ofs,
- TIP_("Scale X: %s Y: %s Z: %s%s %s"),
- &tvec[0],
- &tvec[NUM_STR_REP_LEN],
- &tvec[NUM_STR_REP_LEN * 2],
- t->con.text,
- t->proptext);
+ ofs += BLI_snprintf_rlen(str + ofs,
+ str_size - ofs,
+ TIP_("Scale X: %s Y: %s Z: %s%s %s"),
+ &tvec[0],
+ &tvec[NUM_STR_REP_LEN],
+ &tvec[NUM_STR_REP_LEN * 2],
+ t->con.text,
+ t->proptext);
}
}
if (t->flag & T_PROP_EDIT_ALL) {
- ofs += BLI_snprintf(
- str + ofs, UI_MAX_DRAW_STR - ofs, TIP_(" Proportional size: %.2f"), t->prop_size);
+ ofs += BLI_snprintf_rlen(
+ str + ofs, str_size - ofs, TIP_(" Proportional size: %.2f"), t->prop_size);
}
}
@@ -1263,7 +1256,7 @@ void transform_mode_init(TransInfo *t, wmOperator *op, const int mode)
if (t->data_type == TC_MESH_VERTS) {
/* Init Custom Data correction.
* Ideally this should be called when creating the TransData. */
- mesh_customdatacorrect_init(t);
+ transform_convert_mesh_customdatacorrect_init(t);
}
/* TODO(germano): Some of these operations change the `t->mode`.
@@ -1271,4 +1264,38 @@ void transform_mode_init(TransInfo *t, wmOperator *op, const int mode)
* BLI_assert(t->mode == mode); */
}
+/**
+ * When in modal and not set, initializes a default orientation for the mode.
+ */
+void transform_mode_default_modal_orientation_set(TransInfo *t, int type)
+{
+ /* Currently only these types are supported. */
+ BLI_assert(ELEM(type, V3D_ORIENT_GLOBAL, V3D_ORIENT_VIEW));
+
+ if (t->is_orient_set) {
+ return;
+ }
+
+ if (!(t->flag & T_MODAL)) {
+ return;
+ }
+
+ if (t->orient[O_DEFAULT].type == type) {
+ return;
+ }
+
+ RegionView3D *rv3d = NULL;
+ if ((type == V3D_ORIENT_VIEW) && (t->spacetype == SPACE_VIEW3D) && t->region &&
+ (t->region->regiontype == RGN_TYPE_WINDOW)) {
+ rv3d = t->region->regiondata;
+ }
+
+ t->orient[O_DEFAULT].type = ED_transform_calc_orientation_from_type_ex(
+ NULL, t->orient[O_DEFAULT].matrix, NULL, rv3d, NULL, NULL, type, 0);
+
+ if (t->orient_curr == O_DEFAULT) {
+ /* Update Orientation. */
+ transform_orientations_current_set(t, O_DEFAULT);
+ }
+}
/** \} */
diff --git a/source/blender/editors/transform/transform_mode.h b/source/blender/editors/transform/transform_mode.h
index 6d7a0b528ae..a2b95eb3de4 100644
--- a/source/blender/editors/transform/transform_mode.h
+++ b/source/blender/editors/transform/transform_mode.h
@@ -47,7 +47,7 @@ void protectedTransBits(short protectflag, float vec[3]);
void protectedSizeBits(short protectflag, float size[3]);
void constraintTransLim(TransInfo *t, TransData *td);
void constraintSizeLim(TransInfo *t, TransData *td);
-void headerRotation(TransInfo *t, char *str, float final);
+void headerRotation(TransInfo *t, char *str, int str_size, float final);
void ElementRotation_ex(TransInfo *t,
TransDataContainer *tc,
TransData *td,
@@ -55,12 +55,13 @@ void ElementRotation_ex(TransInfo *t,
const float *center);
void ElementRotation(
TransInfo *t, TransDataContainer *tc, TransData *td, float mat[3][3], const short around);
-void headerResize(TransInfo *t, const float vec[3], char *str);
+void headerResize(TransInfo *t, const float vec[3], char *str, int str_size);
void ElementResize(TransInfo *t, TransDataContainer *tc, TransData *td, float mat[3][3]);
short getAnimEdit_SnapMode(TransInfo *t);
void doAnimEdit_SnapFrame(
TransInfo *t, TransData *td, TransData2D *td2d, struct AnimData *adt, short autosnap);
void transform_mode_init(TransInfo *t, struct wmOperator *op, const int mode);
+void transform_mode_default_modal_orientation_set(TransInfo *t, int type);
/* transform_mode_align.c */
void initAlign(TransInfo *t);
diff --git a/source/blender/editors/transform/transform_mode_curveshrinkfatten.c b/source/blender/editors/transform/transform_mode_curveshrinkfatten.c
index ee63bf4be6f..68416c780ef 100644
--- a/source/blender/editors/transform/transform_mode_curveshrinkfatten.c
+++ b/source/blender/editors/transform/transform_mode_curveshrinkfatten.c
@@ -107,7 +107,6 @@ void initCurveShrinkFatten(TransInfo *t)
t->num.unit_sys = t->scene->unit.system;
t->num.unit_type[0] = B_UNIT_NONE;
- t->flag |= T_NO_ZERO;
#ifdef USE_NUM_NO_ZERO
t->num.val_flag[0] |= NUM_NO_ZERO;
#endif
diff --git a/source/blender/editors/transform/transform_mode_edge_rotate_normal.c b/source/blender/editors/transform/transform_mode_edge_rotate_normal.c
index c78115561b2..6f2bcc148ce 100644
--- a/source/blender/editors/transform/transform_mode_edge_rotate_normal.c
+++ b/source/blender/editors/transform/transform_mode_edge_rotate_normal.c
@@ -103,7 +103,7 @@ static void applyNormalRotation(TransInfo *t, const int UNUSED(mval[2]))
applyNumInput(&t->num, &angle);
- headerRotation(t, str, angle);
+ headerRotation(t, str, sizeof(str), angle);
axis_angle_normalized_to_mat3(mat, axis, angle);
@@ -148,5 +148,7 @@ void initNormalRotation(TransInfo *t)
storeCustomLNorValue(tc, bm);
}
+
+ transform_mode_default_modal_orientation_set(t, V3D_ORIENT_VIEW);
}
/** \} */
diff --git a/source/blender/editors/transform/transform_mode_edge_seq_slide.c b/source/blender/editors/transform/transform_mode_edge_seq_slide.c
index 4330d5e79be..7e7b79c9f90 100644
--- a/source/blender/editors/transform/transform_mode_edge_seq_slide.c
+++ b/source/blender/editors/transform/transform_mode_edge_seq_slide.c
@@ -72,7 +72,7 @@ static void headerSeqSlide(TransInfo *t, const float val[2], char str[UI_MAX_DRA
BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.0f, %.0f", val[0], val[1]);
}
- ofs += BLI_snprintf(
+ ofs += BLI_snprintf_rlen(
str + ofs, UI_MAX_DRAW_STR - ofs, TIP_("Sequence Slide: %s%s, ("), &tvec[0], t->con.text);
const wmKeyMapItem *kmi = t->custom.mode.data;
@@ -80,10 +80,10 @@ static void headerSeqSlide(TransInfo *t, const float val[2], char str[UI_MAX_DRA
ofs += WM_keymap_item_to_string(kmi, false, str + ofs, UI_MAX_DRAW_STR - ofs);
}
- ofs += BLI_snprintf(str + ofs,
- UI_MAX_DRAW_STR - ofs,
- TIP_(" or Alt) Expand to fit %s"),
- WM_bool_as_string((t->flag & T_ALT_TRANSFORM) != 0));
+ ofs += BLI_snprintf_rlen(str + ofs,
+ UI_MAX_DRAW_STR - ofs,
+ TIP_(" or Alt) Expand to fit %s"),
+ WM_bool_as_string((t->flag & T_ALT_TRANSFORM) != 0));
}
static void applySeqSlideValue(TransInfo *t, const float val[2])
diff --git a/source/blender/editors/transform/transform_mode_edge_slide.c b/source/blender/editors/transform/transform_mode_edge_slide.c
index 16c1c05a6f8..d255a7d5660 100644
--- a/source/blender/editors/transform/transform_mode_edge_slide.c
+++ b/source/blender/editors/transform/transform_mode_edge_slide.c
@@ -1482,15 +1482,15 @@ static void applyEdgeSlide(TransInfo *t, const int UNUSED(mval[2]))
ofs += BLI_strncpy_rlen(str + ofs, &c[0], sizeof(str) - ofs);
}
else {
- ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, "%.4f ", final);
+ ofs += BLI_snprintf_rlen(str + ofs, sizeof(str) - ofs, "%.4f ", final);
}
- ofs += BLI_snprintf(
+ ofs += BLI_snprintf_rlen(
str + ofs, sizeof(str) - ofs, TIP_("(E)ven: %s, "), WM_bool_as_string(use_even));
if (use_even) {
- ofs += BLI_snprintf(
+ ofs += BLI_snprintf_rlen(
str + ofs, sizeof(str) - ofs, TIP_("(F)lipped: %s, "), WM_bool_as_string(flipped));
}
- ofs += BLI_snprintf(
+ ofs += BLI_snprintf_rlen(
str + ofs, sizeof(str) - ofs, TIP_("Alt or (C)lamp: %s"), WM_bool_as_string(is_clamp));
/* done with header string */
diff --git a/source/blender/editors/transform/transform_mode_gpopacity.c b/source/blender/editors/transform/transform_mode_gpopacity.c
index e67c6c03481..7c496d271ef 100644
--- a/source/blender/editors/transform/transform_mode_gpopacity.c
+++ b/source/blender/editors/transform/transform_mode_gpopacity.c
@@ -117,7 +117,6 @@ void initGPOpacity(TransInfo *t)
t->num.unit_sys = t->scene->unit.system;
t->num.unit_type[0] = B_UNIT_NONE;
- t->flag |= T_NO_ZERO;
#ifdef USE_NUM_NO_ZERO
t->num.val_flag[0] |= NUM_NO_ZERO;
#endif
diff --git a/source/blender/editors/transform/transform_mode_gpshrinkfatten.c b/source/blender/editors/transform/transform_mode_gpshrinkfatten.c
index 95e3d89d2b7..608a49f38b1 100644
--- a/source/blender/editors/transform/transform_mode_gpshrinkfatten.c
+++ b/source/blender/editors/transform/transform_mode_gpshrinkfatten.c
@@ -119,7 +119,6 @@ void initGPShrinkFatten(TransInfo *t)
t->num.unit_sys = t->scene->unit.system;
t->num.unit_type[0] = B_UNIT_NONE;
- t->flag |= T_NO_ZERO;
#ifdef USE_NUM_NO_ZERO
t->num.val_flag[0] |= NUM_NO_ZERO;
#endif
diff --git a/source/blender/editors/transform/transform_mode_maskshrinkfatten.c b/source/blender/editors/transform/transform_mode_maskshrinkfatten.c
index 3019984d70b..cfbd6030788 100644
--- a/source/blender/editors/transform/transform_mode_maskshrinkfatten.c
+++ b/source/blender/editors/transform/transform_mode_maskshrinkfatten.c
@@ -89,7 +89,7 @@ static void applyMaskShrinkFatten(TransInfo *t, const int UNUSED(mval[2]))
/* apply shrink/fatten */
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- TransData *td = tc->data;
+ TransData *td;
for (td = tc->data, i = 0; i < tc->data_len; i++, td++) {
if (td->flag & TD_SKIP) {
continue;
@@ -133,7 +133,6 @@ void initMaskShrinkFatten(TransInfo *t)
t->num.unit_sys = t->scene->unit.system;
t->num.unit_type[0] = B_UNIT_NONE;
- t->flag |= T_NO_ZERO;
#ifdef USE_NUM_NO_ZERO
t->num.val_flag[0] |= NUM_NO_ZERO;
#endif
diff --git a/source/blender/editors/transform/transform_mode_mirror.c b/source/blender/editors/transform/transform_mode_mirror.c
index 9891af8b9a4..f225f1a7c06 100644
--- a/source/blender/editors/transform/transform_mode_mirror.c
+++ b/source/blender/editors/transform/transform_mode_mirror.c
@@ -235,8 +235,5 @@ void initMirror(TransInfo *t)
initMouseInputMode(t, &t->mouse, INPUT_NONE);
t->flag |= T_NULL_ONE;
- if ((t->flag & T_EDIT) == 0) {
- t->flag |= T_NO_ZERO;
- }
}
/** \} */
diff --git a/source/blender/editors/transform/transform_mode_resize.c b/source/blender/editors/transform/transform_mode_resize.c
index 4d0bb7fbe9c..1d7d1369f29 100644
--- a/source/blender/editors/transform/transform_mode_resize.c
+++ b/source/blender/editors/transform/transform_mode_resize.c
@@ -113,10 +113,10 @@ static void applyResize(TransInfo *t, const int UNUSED(mval[2]))
pvec[j++] = t->values_final[i];
}
}
- headerResize(t, pvec, str);
+ headerResize(t, pvec, str, sizeof(str));
}
else {
- headerResize(t, t->values_final, str);
+ headerResize(t, t->values_final, str, sizeof(str));
}
copy_m3_m3(t->mat, mat); /* used in gizmo */
@@ -176,7 +176,6 @@ void initResize(TransInfo *t)
t->num.val_flag[2] |= NUM_NULL_ONE;
t->num.flag |= NUM_AFFECT_ALL;
if ((t->flag & T_EDIT) == 0) {
- t->flag |= T_NO_ZERO;
#ifdef USE_NUM_NO_ZERO
t->num.val_flag[0] |= NUM_NO_ZERO;
t->num.val_flag[1] |= NUM_NO_ZERO;
@@ -194,5 +193,7 @@ void initResize(TransInfo *t)
t->num.unit_type[0] = B_UNIT_NONE;
t->num.unit_type[1] = B_UNIT_NONE;
t->num.unit_type[2] = B_UNIT_NONE;
+
+ transform_mode_default_modal_orientation_set(t, V3D_ORIENT_GLOBAL);
}
/** \} */
diff --git a/source/blender/editors/transform/transform_mode_rotate.c b/source/blender/editors/transform/transform_mode_rotate.c
index 8d8594d5775..8350e94e0e8 100644
--- a/source/blender/editors/transform/transform_mode_rotate.c
+++ b/source/blender/editors/transform/transform_mode_rotate.c
@@ -217,7 +217,7 @@ static void applyRotation(TransInfo *t, const int UNUSED(mval[2]))
t->values_final[0] = final;
- headerRotation(t, str, final);
+ headerRotation(t, str, sizeof(str), final);
const bool is_large_rotation = hasNumInput(&t->num);
applyRotationValue(t, final, axis_final, is_large_rotation);
@@ -249,5 +249,7 @@ void initRotation(TransInfo *t)
if (t->flag & T_2D_EDIT) {
t->flag |= T_NO_CONSTRAINT;
}
+
+ transform_mode_default_modal_orientation_set(t, V3D_ORIENT_VIEW);
}
/** \} */
diff --git a/source/blender/editors/transform/transform_mode_shear.c b/source/blender/editors/transform/transform_mode_shear.c
index a41c49710b9..23ee55bf6c5 100644
--- a/source/blender/editors/transform/transform_mode_shear.c
+++ b/source/blender/editors/transform/transform_mode_shear.c
@@ -232,5 +232,7 @@ void initShear(TransInfo *t)
t->num.unit_type[0] = B_UNIT_NONE; /* Don't think we have any unit here? */
t->flag |= T_NO_CONSTRAINT;
+
+ transform_mode_default_modal_orientation_set(t, V3D_ORIENT_VIEW);
}
/** \} */
diff --git a/source/blender/editors/transform/transform_mode_shrink_fatten.c b/source/blender/editors/transform/transform_mode_shrink_fatten.c
index 6e497d85417..d2d73a14396 100644
--- a/source/blender/editors/transform/transform_mode_shrink_fatten.c
+++ b/source/blender/editors/transform/transform_mode_shrink_fatten.c
@@ -79,7 +79,7 @@ static void applyShrinkFatten(TransInfo *t, const int UNUSED(mval[2]))
if (hasNumInput(&t->num)) {
char c[NUM_STR_REP_LEN];
outputNumInput(&(t->num), c, unit);
- ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, "%s", c);
+ ofs += BLI_snprintf_rlen(str + ofs, sizeof(str) - ofs, "%s", c);
}
else {
/* default header print */
@@ -93,12 +93,12 @@ static void applyShrinkFatten(TransInfo *t, const int UNUSED(mval[2]))
true);
}
else {
- ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, "%.4f", distance);
+ ofs += BLI_snprintf_rlen(str + ofs, sizeof(str) - ofs, "%.4f", distance);
}
}
if (t->proptext[0]) {
- ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, " %s", t->proptext);
+ ofs += BLI_snprintf_rlen(str + ofs, sizeof(str) - ofs, " %s", t->proptext);
}
ofs += BLI_strncpy_rlen(str + ofs, ", (", sizeof(str) - ofs);
diff --git a/source/blender/editors/transform/transform_mode_skin_resize.c b/source/blender/editors/transform/transform_mode_skin_resize.c
index 8beacb844b9..75ad83b0787 100644
--- a/source/blender/editors/transform/transform_mode_skin_resize.c
+++ b/source/blender/editors/transform/transform_mode_skin_resize.c
@@ -64,7 +64,7 @@ static void applySkinResize(TransInfo *t, const int UNUSED(mval[2]))
size_to_mat3(mat, t->values_final);
- headerResize(t, t->values_final, str);
+ headerResize(t, t->values_final, str, sizeof(str));
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
TransData *td = tc->data;
@@ -111,7 +111,6 @@ void initSkinResize(TransInfo *t)
t->num.val_flag[2] |= NUM_NULL_ONE;
t->num.flag |= NUM_AFFECT_ALL;
if ((t->flag & T_EDIT) == 0) {
- t->flag |= T_NO_ZERO;
#ifdef USE_NUM_NO_ZERO
t->num.val_flag[0] |= NUM_NO_ZERO;
t->num.val_flag[1] |= NUM_NO_ZERO;
diff --git a/source/blender/editors/transform/transform_mode_timetranslate.c b/source/blender/editors/transform/transform_mode_timetranslate.c
index 5ad6d04b4de..948242e547f 100644
--- a/source/blender/editors/transform/transform_mode_timetranslate.c
+++ b/source/blender/editors/transform/transform_mode_timetranslate.c
@@ -76,10 +76,10 @@ static void headerTimeTranslate(TransInfo *t, char str[UI_MAX_DRAW_STR])
}
}
- ofs += BLI_snprintf(str, UI_MAX_DRAW_STR, TIP_("DeltaX: %s"), &tvec[0]);
+ ofs += BLI_snprintf_rlen(str, UI_MAX_DRAW_STR, TIP_("DeltaX: %s"), &tvec[0]);
if (t->flag & T_PROP_EDIT_ALL) {
- ofs += BLI_snprintf(
+ ofs += BLI_snprintf_rlen(
str + ofs, UI_MAX_DRAW_STR - ofs, TIP_(" Proportional size: %.2f"), t->prop_size);
}
}
diff --git a/source/blender/editors/transform/transform_mode_trackball.c b/source/blender/editors/transform/transform_mode_trackball.c
index 5a57a69f986..d05077ef1ef 100644
--- a/source/blender/editors/transform/transform_mode_trackball.c
+++ b/source/blender/editors/transform/transform_mode_trackball.c
@@ -102,24 +102,24 @@ static void applyTrackball(TransInfo *t, const int UNUSED(mval[2]))
outputNumInput(&(t->num), c, &t->scene->unit);
- ofs += BLI_snprintf(str + ofs,
- sizeof(str) - ofs,
- TIP_("Trackball: %s %s %s"),
- &c[0],
- &c[NUM_STR_REP_LEN],
- t->proptext);
+ ofs += BLI_snprintf_rlen(str + ofs,
+ sizeof(str) - ofs,
+ TIP_("Trackball: %s %s %s"),
+ &c[0],
+ &c[NUM_STR_REP_LEN],
+ t->proptext);
}
else {
- ofs += BLI_snprintf(str + ofs,
- sizeof(str) - ofs,
- TIP_("Trackball: %.2f %.2f %s"),
- RAD2DEGF(phi[0]),
- RAD2DEGF(phi[1]),
- t->proptext);
+ ofs += BLI_snprintf_rlen(str + ofs,
+ sizeof(str) - ofs,
+ TIP_("Trackball: %.2f %.2f %s"),
+ RAD2DEGF(phi[0]),
+ RAD2DEGF(phi[1]),
+ t->proptext);
}
if (t->flag & T_PROP_EDIT_ALL) {
- ofs += BLI_snprintf(
+ ofs += BLI_snprintf_rlen(
str + ofs, sizeof(str) - ofs, TIP_(" Proportional size: %.2f"), t->prop_size);
}
diff --git a/source/blender/editors/transform/transform_mode_translate.c b/source/blender/editors/transform/transform_mode_translate.c
index 41fc6ee0aaf..3088f6a7776 100644
--- a/source/blender/editors/transform/transform_mode_translate.c
+++ b/source/blender/editors/transform/transform_mode_translate.c
@@ -141,67 +141,67 @@ static void headerTranslation(TransInfo *t, const float vec[3], char str[UI_MAX_
if (t->con.mode & CON_APPLY) {
switch (t->num.idx_max) {
case 0:
- ofs += BLI_snprintf(str + ofs,
- UI_MAX_DRAW_STR - ofs,
- "D: %s (%s)%s %s %s",
- &tvec[0],
- distvec,
- t->con.text,
- t->proptext,
- autoik);
+ ofs += BLI_snprintf_rlen(str + ofs,
+ UI_MAX_DRAW_STR - ofs,
+ "D: %s (%s)%s %s %s",
+ &tvec[0],
+ distvec,
+ t->con.text,
+ t->proptext,
+ autoik);
break;
case 1:
- ofs += BLI_snprintf(str + ofs,
- UI_MAX_DRAW_STR - ofs,
- "D: %s D: %s (%s)%s %s %s",
- &tvec[0],
- &tvec[NUM_STR_REP_LEN],
- distvec,
- t->con.text,
- t->proptext,
- autoik);
+ ofs += BLI_snprintf_rlen(str + ofs,
+ UI_MAX_DRAW_STR - ofs,
+ "D: %s D: %s (%s)%s %s %s",
+ &tvec[0],
+ &tvec[NUM_STR_REP_LEN],
+ distvec,
+ t->con.text,
+ t->proptext,
+ autoik);
break;
case 2:
- ofs += BLI_snprintf(str + ofs,
- UI_MAX_DRAW_STR - ofs,
- "D: %s D: %s D: %s (%s)%s %s %s",
- &tvec[0],
- &tvec[NUM_STR_REP_LEN],
- &tvec[NUM_STR_REP_LEN * 2],
- distvec,
- t->con.text,
- t->proptext,
- autoik);
+ ofs += BLI_snprintf_rlen(str + ofs,
+ UI_MAX_DRAW_STR - ofs,
+ "D: %s D: %s D: %s (%s)%s %s %s",
+ &tvec[0],
+ &tvec[NUM_STR_REP_LEN],
+ &tvec[NUM_STR_REP_LEN * 2],
+ distvec,
+ t->con.text,
+ t->proptext,
+ autoik);
break;
}
}
else {
if (t->flag & T_2D_EDIT) {
- ofs += BLI_snprintf(str + ofs,
- UI_MAX_DRAW_STR - ofs,
- "Dx: %s Dy: %s (%s)%s %s",
- &tvec[0],
- &tvec[NUM_STR_REP_LEN],
- distvec,
- t->con.text,
- t->proptext);
+ ofs += BLI_snprintf_rlen(str + ofs,
+ UI_MAX_DRAW_STR - ofs,
+ "Dx: %s Dy: %s (%s)%s %s",
+ &tvec[0],
+ &tvec[NUM_STR_REP_LEN],
+ distvec,
+ t->con.text,
+ t->proptext);
}
else {
- ofs += BLI_snprintf(str + ofs,
- UI_MAX_DRAW_STR - ofs,
- "Dx: %s Dy: %s Dz: %s (%s)%s %s %s",
- &tvec[0],
- &tvec[NUM_STR_REP_LEN],
- &tvec[NUM_STR_REP_LEN * 2],
- distvec,
- t->con.text,
- t->proptext,
- autoik);
+ ofs += BLI_snprintf_rlen(str + ofs,
+ UI_MAX_DRAW_STR - ofs,
+ "Dx: %s Dy: %s Dz: %s (%s)%s %s %s",
+ &tvec[0],
+ &tvec[NUM_STR_REP_LEN],
+ &tvec[NUM_STR_REP_LEN * 2],
+ distvec,
+ t->con.text,
+ t->proptext,
+ autoik);
}
}
if (t->flag & T_PROP_EDIT_ALL) {
- ofs += BLI_snprintf(
+ ofs += BLI_snprintf_rlen(
str + ofs, UI_MAX_DRAW_STR - ofs, TIP_(" Proportional size: %.2f"), t->prop_size);
}
@@ -217,12 +217,12 @@ static void headerTranslation(TransInfo *t, const float vec[3], char str[UI_MAX_
WM_modalkeymap_items_to_string(
t->keymap, TFM_MODAL_INSERTOFS_TOGGLE_DIR, true, str_km, sizeof(str_km));
- ofs += BLI_snprintf(str,
- UI_MAX_DRAW_STR,
- TIP_("Auto-offset set to %s - press %s to toggle direction | %s"),
- str_dir,
- str_km,
- str_old);
+ ofs += BLI_snprintf_rlen(str,
+ UI_MAX_DRAW_STR,
+ TIP_("Auto-offset set to %s - press %s to toggle direction | %s"),
+ str_dir,
+ str_km,
+ str_old);
MEM_freeN((void *)str_old);
}
@@ -470,5 +470,8 @@ void initTranslation(TransInfo *t)
t->num.unit_type[1] = B_UNIT_NONE;
t->num.unit_type[2] = B_UNIT_NONE;
}
+
+ transform_mode_default_modal_orientation_set(
+ t, (t->options & CTX_CAMERA) ? V3D_ORIENT_VIEW : V3D_ORIENT_GLOBAL);
}
/** \} */
diff --git a/source/blender/editors/transform/transform_mode_vert_slide.c b/source/blender/editors/transform/transform_mode_vert_slide.c
index 1e5d027e253..e16aa636872 100644
--- a/source/blender/editors/transform/transform_mode_vert_slide.c
+++ b/source/blender/editors/transform/transform_mode_vert_slide.c
@@ -606,15 +606,15 @@ static void applyVertSlide(TransInfo *t, const int UNUSED(mval[2]))
ofs += BLI_strncpy_rlen(str + ofs, &c[0], sizeof(str) - ofs);
}
else {
- ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, "%.4f ", final);
+ ofs += BLI_snprintf_rlen(str + ofs, sizeof(str) - ofs, "%.4f ", final);
}
- ofs += BLI_snprintf(
+ ofs += BLI_snprintf_rlen(
str + ofs, sizeof(str) - ofs, TIP_("(E)ven: %s, "), WM_bool_as_string(use_even));
if (use_even) {
- ofs += BLI_snprintf(
+ ofs += BLI_snprintf_rlen(
str + ofs, sizeof(str) - ofs, TIP_("(F)lipped: %s, "), WM_bool_as_string(flipped));
}
- ofs += BLI_snprintf(
+ ofs += BLI_snprintf_rlen(
str + ofs, sizeof(str) - ofs, TIP_("Alt or (C)lamp: %s"), WM_bool_as_string(is_clamp));
/* done with header string */
diff --git a/source/blender/editors/transform/transform_orientations.c b/source/blender/editors/transform/transform_orientations.c
index 1470d3b7059..d97bcba161f 100644
--- a/source/blender/editors/transform/transform_orientations.c
+++ b/source/blender/editors/transform/transform_orientations.c
@@ -445,7 +445,7 @@ int BIF_countTransformOrientation(const bContext *C)
return BLI_listbase_count(transform_orientations);
}
-void applyTransformOrientation(const TransformOrientation *ts, float r_mat[3][3], char *r_name)
+void applyTransformOrientation(const TransformOrientation *ts, float r_mat[3][3], char r_name[64])
{
if (r_name) {
BLI_strncpy(r_name, ts->name, MAX_NAME);
@@ -492,12 +492,11 @@ void ED_transform_calc_orientation_from_type(const bContext *C, float r_mat[3][3
Object *obedit = CTX_data_edit_object(C);
RegionView3D *rv3d = region->regiondata;
Object *ob = OBACT(view_layer);
- const short orientation_type = scene->orientation_slots[SCE_ORIENT_DEFAULT].type;
- const short orientation_index_custom = scene->orientation_slots[SCE_ORIENT_DEFAULT].index_custom;
+ const short orient_index = BKE_scene_orientation_get_index(scene, SCE_ORIENT_DEFAULT);
const int pivot_point = scene->toolsettings->transform_pivot_point;
ED_transform_calc_orientation_from_type_ex(
- C, r_mat, scene, rv3d, ob, obedit, orientation_type, orientation_index_custom, pivot_point);
+ C, r_mat, scene, rv3d, ob, obedit, orient_index, pivot_point);
}
/**
@@ -516,11 +515,10 @@ short ED_transform_calc_orientation_from_type_ex(const bContext *C,
RegionView3D *rv3d,
Object *ob,
Object *obedit,
- const short orientation_type,
- int orientation_index_custom,
+ const short orientation_index,
const int pivot_point)
{
- switch (orientation_type) {
+ switch (orientation_index) {
case V3D_ORIENT_GIMBAL: {
if (ob && gimbal_axis(ob, r_mat)) {
break;
@@ -577,24 +575,28 @@ short ED_transform_calc_orientation_from_type_ex(const bContext *C,
}
case V3D_ORIENT_CUSTOM:
default: {
- BLI_assert(orientation_type >= V3D_ORIENT_CUSTOM);
+ BLI_assert(orientation_index >= V3D_ORIENT_CUSTOM);
+ int orientation_index_custom = orientation_index - V3D_ORIENT_CUSTOM;
TransformOrientation *custom_orientation = BKE_scene_transform_orientation_find(
scene, orientation_index_custom);
applyTransformOrientation(custom_orientation, r_mat, NULL);
- return V3D_ORIENT_CUSTOM + orientation_index_custom;
+ break;
}
}
- return orientation_type;
+ return orientation_index;
}
/* Sets the matrix of the specified space orientation.
* If the matrix cannot be obtained, an orientation different from the one
* informed is returned */
-short transform_orientation_matrix_get(
- bContext *C, TransInfo *t, short orientation, const float custom[3][3], float r_spacemtx[3][3])
+short transform_orientation_matrix_get(bContext *C,
+ TransInfo *t,
+ short orient_index,
+ const float custom[3][3],
+ float r_spacemtx[3][3])
{
- if (orientation == V3D_ORIENT_CUSTOM_MATRIX) {
+ if (orient_index == V3D_ORIENT_CUSTOM_MATRIX) {
copy_m3_m3(r_spacemtx, custom);
return V3D_ORIENT_CUSTOM_MATRIX;
}
@@ -603,24 +605,20 @@ short transform_orientation_matrix_get(
Object *obedit = CTX_data_edit_object(C);
Scene *scene = t->scene;
RegionView3D *rv3d = NULL;
- int orientation_index_custom = 0;
-
- if (orientation >= V3D_ORIENT_CUSTOM) {
- orientation_index_custom = orientation - V3D_ORIENT_CUSTOM;
- orientation = V3D_ORIENT_CUSTOM;
- }
- else if (ob && (ob->mode & OB_MODE_ALL_WEIGHT_PAINT) && !(t->options & CTX_PAINT_CURVE)) {
- Object *ob_armature = transform_object_deform_pose_armature_get(t, ob);
- if (ob_armature) {
- ob = ob_armature;
- }
- }
if ((t->spacetype == SPACE_VIEW3D) && t->region && (t->region->regiontype == RGN_TYPE_WINDOW)) {
rv3d = t->region->regiondata;
+
+ if (ob && (ob->mode & OB_MODE_ALL_WEIGHT_PAINT) && !(t->options & CTX_PAINT_CURVE)) {
+ Object *ob_armature = transform_object_deform_pose_armature_get(t, ob);
+ if (ob_armature) {
+ /* The armature matrix is used for GIMBAL, NORMAL and LOCAL orientations. */
+ ob = ob_armature;
+ }
+ }
}
- short orient_type = ED_transform_calc_orientation_from_type_ex(
+ short r_orient_index = ED_transform_calc_orientation_from_type_ex(
C,
r_spacemtx,
/* extra args (can be accessed from context) */
@@ -628,13 +626,12 @@ short transform_orientation_matrix_get(
rv3d,
ob,
obedit,
- orientation,
- orientation_index_custom,
+ orient_index,
t->around);
if (rv3d && (t->options & CTX_PAINT_CURVE)) {
/* Screen space in the 3d region. */
- if (orient_type == V3D_ORIENT_VIEW) {
+ if (r_orient_index == V3D_ORIENT_VIEW) {
unit_m3(r_spacemtx);
}
else {
@@ -643,7 +640,7 @@ short transform_orientation_matrix_get(
}
}
- return orient_type;
+ return r_orient_index;
}
const char *transform_orientations_spacename_get(TransInfo *t, const short orient_type)
@@ -674,10 +671,6 @@ const char *transform_orientations_spacename_get(TransInfo *t, const short orien
void transform_orientations_current_set(TransInfo *t, const short orient_index)
{
- if (t->orient_curr == orient_index) {
- return;
- }
-
const short orientation = t->orient[orient_index].type;
const char *spacename = transform_orientations_spacename_get(t, orientation);
diff --git a/source/blender/editors/transform/transform_orientations.h b/source/blender/editors/transform/transform_orientations.h
index e9c146a6853..de8c9b165c1 100644
--- a/source/blender/editors/transform/transform_orientations.h
+++ b/source/blender/editors/transform/transform_orientations.h
@@ -27,7 +27,7 @@ struct TransInfo;
short transform_orientation_matrix_get(struct bContext *C,
struct TransInfo *t,
- short orientation,
+ short orient_index,
const float custom[3][3],
float r_spacemtx[3][3]);
const char *transform_orientations_spacename_get(struct TransInfo *t, const short orient_type);
diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c
index d0f91802fff..bebef049718 100644
--- a/source/blender/editors/transform/transform_snap.c
+++ b/source/blender/editors/transform/transform_snap.c
@@ -345,7 +345,7 @@ void applyProject(TransInfo *t)
SCE_SNAP_MODE_FACE,
&(const struct SnapObjectParams){
.snap_select = t->tsnap.modeSelect,
- .use_object_edit_cage = (t->flag & T_EDIT) != 0,
+ .edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL,
.use_occlusion_test = false,
.use_backface_culling = t->tsnap.use_backface_culling,
},
@@ -1072,7 +1072,7 @@ static void TargetSnapClosest(TransInfo *t)
if (t->options & CTX_OBJECT) {
int i;
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- TransData *td = tc->data;
+ TransData *td;
for (td = tc->data, i = 0; i < tc->data_len && td->flag & TD_SELECTED; i++, td++) {
const BoundBox *bb = NULL;
@@ -1167,7 +1167,7 @@ short snapObjectsTransform(
t->settings->snap_mode,
&(const struct SnapObjectParams){
.snap_select = t->tsnap.modeSelect,
- .use_object_edit_cage = (t->flag & T_EDIT) != 0,
+ .edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL,
.use_occlusion_test = t->settings->snap_mode != SCE_SNAP_MODE_FACE,
.use_backface_culling = t->tsnap.use_backface_culling,
},
@@ -1201,7 +1201,7 @@ bool peelObjectsTransform(TransInfo *t,
t->depsgraph,
&(const struct SnapObjectParams){
.snap_select = t->tsnap.modeSelect,
- .use_object_edit_cage = (t->flag & T_EDIT) != 0,
+ .edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL,
},
mval,
-1.0f,
diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c
index 58198f21ba2..512f912a532 100644
--- a/source/blender/editors/transform/transform_snap_object.c
+++ b/source/blender/editors/transform/transform_snap_object.c
@@ -43,6 +43,8 @@
#include "BKE_curve.h"
#include "BKE_duplilist.h"
#include "BKE_editmesh.h"
+#include "BKE_geometry_set.h"
+#include "BKE_global.h"
#include "BKE_layer.h"
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
@@ -144,9 +146,37 @@ struct SnapObjectContext {
/** \name Utilities
* \{ */
-static bool editmesh_eval_final_is_bmesh(const BMEditMesh *em)
+/* Mesh used for snapping.
+ * If NULL the BMesh should be used. */
+static Mesh *mesh_for_snap(Object *ob_eval, eSnapEditType edit_mode_type, bool *r_use_hide)
{
- return (em->mesh_eval_final->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH);
+ Mesh *me_eval = ob_eval->data;
+ bool use_hide = false;
+ if (BKE_object_is_in_editmode(ob_eval)) {
+ if (edit_mode_type == SNAP_GEOM_EDIT) {
+ return NULL;
+ }
+
+ BMEditMesh *em_eval = BKE_editmesh_from_object(ob_eval);
+ if ((edit_mode_type == SNAP_GEOM_FINAL) && em_eval->mesh_eval_final) {
+ if (em_eval->mesh_eval_final->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH) {
+ return NULL;
+ }
+ me_eval = em_eval->mesh_eval_final;
+ use_hide = true;
+ }
+ else if ((edit_mode_type == SNAP_GEOM_CAGE) && em_eval->mesh_eval_cage) {
+ if (em_eval->mesh_eval_cage->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH) {
+ return NULL;
+ }
+ me_eval = em_eval->mesh_eval_cage;
+ use_hide = true;
+ }
+ }
+ if (r_use_hide) {
+ *r_use_hide = use_hide;
+ }
+ return me_eval;
}
/** \} */
@@ -208,30 +238,69 @@ static void snap_object_data_clear(SnapObjectData *sod)
memset(&sod->type, 0x0, sizeof(*sod) - offsetof(SnapObjectData, type));
}
-static SnapObjectData *snap_object_data_lookup(SnapObjectContext *sctx, Object *ob)
+static SnapObjectData *snap_object_data_lookup(SnapObjectContext *sctx, Object *ob_eval)
{
- SnapObjectData *sod = BLI_ghash_lookup(sctx->cache.object_map, ob);
+ SnapObjectData *sod = BLI_ghash_lookup(sctx->cache.object_map, ob_eval);
if (sod == NULL) {
if (sctx->cache.data_to_object_map != NULL) {
- ob = BLI_ghash_lookup(sctx->cache.data_to_object_map, ob->data);
+ ob_eval = BLI_ghash_lookup(sctx->cache.data_to_object_map, ob_eval->data);
/* Could be NULl when mixing edit-mode and non edit-mode objects. */
- if (ob != NULL) {
- sod = BLI_ghash_lookup(sctx->cache.object_map, ob);
+ if (ob_eval != NULL) {
+ sod = BLI_ghash_lookup(sctx->cache.object_map, ob_eval);
}
}
}
return sod;
}
-static SnapObjectData *snap_object_data_mesh_get(SnapObjectContext *sctx, Object *ob)
+static SnapObjectData *snap_object_data_mesh_get(SnapObjectContext *sctx,
+ Object *ob_eval,
+ Mesh *me_eval,
+ bool use_hide)
{
SnapObjectData *sod;
void **sod_p;
bool init = false;
- if (BLI_ghash_ensure_p(sctx->cache.object_map, ob, &sod_p)) {
+ if (BLI_ghash_ensure_p(sctx->cache.object_map, ob_eval, &sod_p)) {
sod = *sod_p;
+ bool is_dirty = false;
if (sod->type != SNAP_MESH) {
+ is_dirty = true;
+ }
+ else if (sod->treedata_mesh.tree && sod->treedata_mesh.cached &&
+ !bvhcache_has_tree(me_eval->runtime.bvh_cache, sod->treedata_mesh.tree)) {
+ /* The tree is owned by the Mesh and may have been freed since we last used. */
+ is_dirty = true;
+ }
+ else if (sod->bvhtree[0] && sod->cached[0] &&
+ !bvhcache_has_tree(me_eval->runtime.bvh_cache, sod->bvhtree[0])) {
+ /* The tree is owned by the Mesh and may have been freed since we last used. */
+ is_dirty = true;
+ }
+ else if (sod->bvhtree[1] && sod->cached[1] &&
+ !bvhcache_has_tree(me_eval->runtime.bvh_cache, sod->bvhtree[1])) {
+ /* The tree is owned by the Mesh and may have been freed since we last used. */
+ is_dirty = true;
+ }
+ else if (!sod->treedata_mesh.looptri_allocated &&
+ sod->treedata_mesh.looptri != me_eval->runtime.looptris.array) {
+ is_dirty = true;
+ }
+ else if (!sod->treedata_mesh.vert_allocated && sod->treedata_mesh.vert != me_eval->mvert) {
+ is_dirty = true;
+ }
+ else if (!sod->treedata_mesh.loop_allocated && sod->treedata_mesh.loop != me_eval->mloop) {
+ is_dirty = true;
+ }
+ else if (!sod->treedata_mesh.edge_allocated && sod->treedata_mesh.edge != me_eval->medge) {
+ is_dirty = true;
+ }
+ else if (sod->poly != me_eval->mpoly) {
+ is_dirty = true;
+ }
+
+ if (is_dirty) {
snap_object_data_clear(sod);
init = true;
}
@@ -243,8 +312,32 @@ static SnapObjectData *snap_object_data_mesh_get(SnapObjectContext *sctx, Object
if (init) {
sod->type = SNAP_MESH;
- /* start assuming that it has each of these element types */
- sod->has_looptris = true;
+
+ /* The BVHTree from looptris is always required. */
+ BLI_assert(sod->treedata_mesh.tree == NULL);
+ BKE_bvhtree_from_mesh_get(&sod->treedata_mesh,
+ me_eval,
+ use_hide ? BVHTREE_FROM_LOOPTRI_NO_HIDDEN : BVHTREE_FROM_LOOPTRI,
+ 4);
+
+ if (sod->treedata_mesh.tree == NULL) {
+ sod->treedata_mesh.vert = me_eval->mvert;
+ sod->treedata_mesh.loop = me_eval->mloop;
+ sod->treedata_mesh.looptri = BKE_mesh_runtime_looptri_ensure(me_eval);
+ BLI_assert(sod->has_looptris == false);
+ }
+ else {
+ BLI_assert(sod->treedata_mesh.vert != NULL);
+ BLI_assert(sod->treedata_mesh.loop != NULL);
+ BLI_assert(sod->treedata_mesh.looptri != NULL);
+ sod->has_looptris = true;
+ }
+
+ /* Required for snapping with occlusion. */
+ sod->treedata_mesh.edge = me_eval->medge;
+ sod->poly = me_eval->mpoly;
+
+ /* Start assuming that it has each of these element types. */
sod->has_loose_edge = true;
sod->has_loose_vert = true;
}
@@ -252,26 +345,26 @@ static SnapObjectData *snap_object_data_mesh_get(SnapObjectContext *sctx, Object
return sod;
}
-static struct Mesh_Runtime *snap_object_data_editmesh_runtime_get(Object *ob)
+static struct Mesh_Runtime *snap_object_data_editmesh_runtime_get(Object *ob_eval)
{
- BMEditMesh *em = BKE_editmesh_from_object(ob);
- if (em->mesh_eval_final) {
- return &em->mesh_eval_final->runtime;
+ BMEditMesh *em_eval = BKE_editmesh_from_object(ob_eval);
+ if (em_eval->mesh_eval_final) {
+ return &em_eval->mesh_eval_final->runtime;
}
- if (em->mesh_eval_cage) {
- return &em->mesh_eval_cage->runtime;
+ if (em_eval->mesh_eval_cage) {
+ return &em_eval->mesh_eval_cage->runtime;
}
- return &((Mesh *)ob->data)->runtime;
+ return &((Mesh *)ob_eval->data)->runtime;
}
static SnapObjectData *snap_object_data_editmesh_get(SnapObjectContext *sctx,
- Object *ob,
+ Object *ob_eval,
BMEditMesh *em)
{
SnapObjectData *sod;
void **sod_p;
- bool init = false, init_min_max = true, clear_cache = false;
+ bool init = false;
{
/* Use object-data as the key in ghash since the editmesh
@@ -280,48 +373,53 @@ static SnapObjectData *snap_object_data_editmesh_get(SnapObjectContext *sctx,
sctx->cache.data_to_object_map = BLI_ghash_ptr_new(__func__);
}
void **ob_p;
- if (BLI_ghash_ensure_p(sctx->cache.data_to_object_map, ob->data, &ob_p)) {
- ob = *ob_p;
+ if (BLI_ghash_ensure_p(sctx->cache.data_to_object_map, ob_eval->data, &ob_p)) {
+ ob_eval = *ob_p;
}
else {
- *ob_p = ob;
+ *ob_p = ob_eval;
}
}
- if (BLI_ghash_ensure_p(sctx->cache.object_map, ob, &sod_p)) {
+ if (BLI_ghash_ensure_p(sctx->cache.object_map, ob_eval, &sod_p)) {
sod = *sod_p;
- bool clear = false;
+ bool is_dirty = false;
/* Check if the geometry has changed. */
if (sod->type != SNAP_EDIT_MESH) {
- clear = true;
+ is_dirty = true;
}
else if (sod->treedata_editmesh.em != em) {
- clear_cache = true;
- init = true;
+ is_dirty = true;
}
else if (sod->mesh_runtime) {
- if (sod->mesh_runtime != snap_object_data_editmesh_runtime_get(ob)) {
- clear_cache = true;
- init = true;
+ if (sod->mesh_runtime != snap_object_data_editmesh_runtime_get(ob_eval)) {
+ if (G.moving) {
+ /* Hack to avoid updating while transforming. */
+ BLI_assert(!sod->treedata_editmesh.cached && !sod->cached[0] && !sod->cached[1]);
+ sod->mesh_runtime = snap_object_data_editmesh_runtime_get(ob_eval);
+ }
+ else {
+ is_dirty = true;
+ }
}
else if (sod->treedata_editmesh.tree && sod->treedata_editmesh.cached &&
!bvhcache_has_tree(sod->mesh_runtime->bvh_cache, sod->treedata_editmesh.tree)) {
/* The tree is owned by the EditMesh and may have been freed since we last used! */
- clear = true;
+ is_dirty = true;
}
else if (sod->bvhtree[0] && sod->cached[0] &&
!bvhcache_has_tree(sod->mesh_runtime->bvh_cache, sod->bvhtree[0])) {
/* The tree is owned by the EditMesh and may have been freed since we last used! */
- clear = true;
+ is_dirty = true;
}
else if (sod->bvhtree[1] && sod->cached[1] &&
!bvhcache_has_tree(sod->mesh_runtime->bvh_cache, sod->bvhtree[1])) {
/* The tree is owned by the EditMesh and may have been freed since we last used! */
- clear = true;
+ is_dirty = true;
}
}
- if (clear) {
+ if (is_dirty) {
snap_object_data_clear(sod);
init = true;
}
@@ -334,27 +432,8 @@ static SnapObjectData *snap_object_data_editmesh_get(SnapObjectContext *sctx,
if (init) {
sod->type = SNAP_EDIT_MESH;
sod->treedata_editmesh.em = em;
-
- if (clear_cache) {
- /* Only init min and max when you have a non-custom bvhtree pending. */
- init_min_max = false;
- if (sod->treedata_editmesh.cached) {
- sod->treedata_editmesh.tree = NULL;
- init_min_max = true;
- }
- for (int i = 0; i < ARRAY_SIZE(sod->bvhtree); i++) {
- if (sod->cached[i]) {
- sod->bvhtree[i] = NULL;
- init_min_max = true;
- }
- }
- }
-
- if (init_min_max) {
- bm_mesh_minmax(em->bm, sod->min, sod->max);
- }
-
- sod->mesh_runtime = snap_object_data_editmesh_runtime_get(ob);
+ sod->mesh_runtime = snap_object_data_editmesh_runtime_get(ob_eval);
+ bm_mesh_minmax(em->bm, sod->min, sod->max);
}
return sod;
@@ -367,9 +446,9 @@ static SnapObjectData *snap_object_data_editmesh_get(SnapObjectContext *sctx,
* \{ */
typedef void (*IterSnapObjsCallback)(SnapObjectContext *sctx,
- Object *ob,
+ Object *ob_eval,
float obmat[4][4],
- bool use_obedit,
+ eSnapEditType edit_mode_type,
bool use_backface_culling,
bool is_object_active,
void *data);
@@ -386,10 +465,17 @@ static void iter_snap_objects(SnapObjectContext *sctx,
ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph);
const View3D *v3d = sctx->v3d_data.v3d;
const eSnapSelect snap_select = params->snap_select;
- const bool use_object_edit_cage = params->use_object_edit_cage;
+ const eSnapEditType edit_mode_type = params->edit_mode_type;
const bool use_backface_culling = params->use_backface_culling;
Base *base_act = view_layer->basact;
+ if (snap_select == SNAP_ONLY_ACTIVE) {
+ Object *obj_eval = DEG_get_evaluated_object(depsgraph, base_act->object);
+ sob_callback(
+ sctx, obj_eval, obj_eval->obmat, edit_mode_type, use_backface_culling, true, data);
+ return;
+ }
+
for (Base *base = view_layer->object_bases.first; base != NULL; base = base->next) {
if (!BASE_VISIBLE(v3d, base)) {
continue;
@@ -415,14 +501,16 @@ static void iter_snap_objects(SnapObjectContext *sctx,
}
Object *obj_eval = DEG_get_evaluated_object(depsgraph, base->object);
- if (obj_eval->transflag & OB_DUPLI) {
- DupliObject *dupli_ob;
+ if (obj_eval->transflag & OB_DUPLI ||
+ (obj_eval->runtime.geometry_set_eval != NULL &&
+ BKE_geometry_set_has_instances(obj_eval->runtime.geometry_set_eval))) {
ListBase *lb = object_duplilist(depsgraph, sctx->scene, obj_eval);
- for (dupli_ob = lb->first; dupli_ob; dupli_ob = dupli_ob->next) {
+ for (DupliObject *dupli_ob = lb->first; dupli_ob; dupli_ob = dupli_ob->next) {
+ BLI_assert(DEG_is_evaluated_object(dupli_ob->ob));
sob_callback(sctx,
dupli_ob->ob,
dupli_ob->mat,
- use_object_edit_cage,
+ edit_mode_type,
use_backface_culling,
is_object_active,
data);
@@ -433,7 +521,7 @@ static void iter_snap_objects(SnapObjectContext *sctx,
sob_callback(sctx,
obj_eval,
obj_eval->obmat,
- use_object_edit_cage,
+ edit_mode_type,
use_backface_culling,
is_object_active,
data);
@@ -461,7 +549,7 @@ struct RayCastAll_Data {
float len_diff;
float local_scale;
- Object *ob;
+ Object *ob_eval;
uint ob_uuid;
/* output data */
@@ -473,7 +561,7 @@ static struct SnapObjectHitDepth *hit_depth_create(const float depth,
const float co[3],
const float no[3],
int index,
- Object *ob,
+ Object *ob_eval,
const float obmat[4][4],
uint ob_uuid)
{
@@ -484,7 +572,7 @@ static struct SnapObjectHitDepth *hit_depth_create(const float depth,
copy_v3_v3(hit->no, no);
hit->index = index;
- hit->ob = ob;
+ hit->ob_eval = ob_eval;
copy_m4_m4(hit->obmat, (float(*)[4])obmat);
hit->ob_uuid = ob_uuid;
@@ -526,7 +614,7 @@ static void raycast_all_cb(void *userdata, int index, const BVHTreeRay *ray, BVH
normalize_v3(normal);
struct SnapObjectHitDepth *hit_item = hit_depth_create(
- depth, location, normal, hit->index, data->ob, data->obmat, data->ob_uuid);
+ depth, location, normal, hit->index, data->ob_eval, data->obmat, data->ob_uuid);
BLI_addtail(data->hit_list, hit_item);
}
}
@@ -598,8 +686,8 @@ static void editmesh_looptri_raycast_backface_culling_cb(void *userdata,
static bool raycastMesh(SnapObjectContext *sctx,
const float ray_start[3],
const float ray_dir[3],
- Object *ob,
- Mesh *me,
+ Object *ob_eval,
+ Mesh *me_eval,
const float obmat[4][4],
const uint ob_index,
bool use_hide,
@@ -614,7 +702,7 @@ static bool raycastMesh(SnapObjectContext *sctx,
{
bool retval = false;
- if (me->totpoly == 0) {
+ if (me_eval->totpoly == 0) {
return retval;
}
@@ -638,7 +726,7 @@ static bool raycastMesh(SnapObjectContext *sctx,
}
/* Test BoundBox */
- BoundBox *bb = BKE_mesh_boundbox_get(ob);
+ BoundBox *bb = BKE_mesh_boundbox_get(ob_eval);
if (bb) {
/* was BKE_boundbox_ray_hit_check, see: cf6ca226fa58 */
if (!isect_ray_aabb_v3_simple(
@@ -658,53 +746,18 @@ static bool raycastMesh(SnapObjectContext *sctx,
len_diff = 0.0f;
}
- SnapObjectData *sod = snap_object_data_mesh_get(sctx, ob);
+ SnapObjectData *sod = snap_object_data_mesh_get(sctx, ob_eval, me_eval, use_hide);
BVHTreeFromMesh *treedata = &sod->treedata_mesh;
- /* The tree is owned by the Mesh and may have been freed since we last used. */
- if (treedata->tree) {
- BLI_assert(treedata->cached);
- if (!bvhcache_has_tree(me->runtime.bvh_cache, treedata->tree)) {
- free_bvhtree_from_mesh(treedata);
- }
- else {
- /* Update Pointers. */
- if (treedata->vert && treedata->vert_allocated == false) {
- treedata->vert = me->mvert;
- }
- if (treedata->loop && treedata->loop_allocated == false) {
- treedata->loop = me->mloop;
- }
- if (treedata->looptri && treedata->looptri_allocated == false) {
- treedata->looptri = BKE_mesh_runtime_looptri_ensure(me);
- }
- /* required for snapping with occlusion. */
- treedata->edge = me->medge;
- sod->poly = me->mpoly;
- }
- }
-
if (treedata->tree == NULL) {
- if (use_hide) {
- BKE_bvhtree_from_mesh_get(treedata, me, BVHTREE_FROM_LOOPTRI_NO_HIDDEN, 4);
- }
- else {
- BKE_bvhtree_from_mesh_get(treedata, me, BVHTREE_FROM_LOOPTRI, 4);
- }
-
- /* required for snapping with occlusion. */
- treedata->edge = me->medge;
- sod->poly = me->mpoly;
-
- if (treedata->tree == NULL) {
- return retval;
- }
+ return retval;
}
float timat[3][3]; /* transpose inverse matrix for normals */
transpose_m3_m4(timat, imat);
+ BLI_assert(treedata->raycast_callback != NULL);
if (r_hit_list) {
struct RayCastAll_Data data;
@@ -714,7 +767,7 @@ static bool raycastMesh(SnapObjectContext *sctx,
data.timat = timat;
data.len_diff = len_diff;
data.local_scale = local_scale;
- data.ob = ob;
+ data.ob_eval = ob_eval;
data.ob_uuid = ob_index;
data.hit_list = r_hit_list;
data.retval = retval;
@@ -773,7 +826,7 @@ static bool raycastMesh(SnapObjectContext *sctx,
static bool raycastEditMesh(SnapObjectContext *sctx,
const float ray_start[3],
const float ray_dir[3],
- Object *ob,
+ Object *ob_eval,
BMEditMesh *em,
const float obmat[4][4],
const uint ob_index,
@@ -810,7 +863,7 @@ static bool raycastEditMesh(SnapObjectContext *sctx,
local_depth *= local_scale;
}
- SnapObjectData *sod = snap_object_data_editmesh_get(sctx, ob, em);
+ SnapObjectData *sod = snap_object_data_editmesh_get(sctx, ob_eval, em);
/* Test BoundBox */
@@ -836,7 +889,8 @@ static bool raycastEditMesh(SnapObjectContext *sctx,
if (treedata->tree == NULL) {
/* Operators only update the editmesh looptris of the original mesh. */
- BLI_assert(sod->treedata_editmesh.em == BKE_editmesh_from_object(DEG_get_original_object(ob)));
+ BLI_assert(sod->treedata_editmesh.em ==
+ BKE_editmesh_from_object(DEG_get_original_object(ob_eval)));
em = sod->treedata_editmesh.em;
if (sctx->callbacks.edit_mesh.test_face_fn) {
@@ -883,7 +937,7 @@ static bool raycastEditMesh(SnapObjectContext *sctx,
data.timat = timat;
data.len_diff = len_diff;
data.local_scale = local_scale;
- data.ob = ob;
+ data.ob_eval = ob_eval;
data.ob_uuid = ob_index;
data.hit_list = r_hit_list;
data.retval = retval;
@@ -964,9 +1018,9 @@ struct RaycastObjUserData {
* \note Duplicate args here are documented at #snapObjectsRay
*/
static void raycast_obj_fn(SnapObjectContext *sctx,
- Object *ob,
+ Object *ob_eval,
float obmat[4][4],
- bool use_obedit,
+ eSnapEditType edit_mode_type,
bool use_backface_culling,
bool is_object_active,
void *data)
@@ -979,53 +1033,46 @@ static void raycast_obj_fn(SnapObjectContext *sctx,
bool retval = false;
if (use_occlusion_test) {
- if (use_obedit && sctx->use_v3d && XRAY_FLAG_ENABLED(sctx->v3d_data.v3d)) {
+ if ((edit_mode_type == SNAP_GEOM_EDIT) && sctx->use_v3d &&
+ XRAY_FLAG_ENABLED(sctx->v3d_data.v3d)) {
/* Use of occlude geometry in editing mode disabled. */
return;
}
- if (ELEM(ob->dt, OB_BOUNDBOX, OB_WIRE)) {
+ if (ELEM(ob_eval->dt, OB_BOUNDBOX, OB_WIRE)) {
/* Do not hit objects that are in wire or bounding box
* display mode. */
return;
}
}
- switch (ob->type) {
+ switch (ob_eval->type) {
case OB_MESH: {
- Mesh *me = ob->data;
bool use_hide = false;
- if (BKE_object_is_in_editmode(ob)) {
- if (use_obedit || editmesh_eval_final_is_bmesh(me->edit_mesh)) {
- /* Operators only update the editmesh looptris of the original mesh. */
- BMEditMesh *em_orig = BKE_editmesh_from_object(DEG_get_original_object(ob));
- retval = raycastEditMesh(sctx,
- dt->ray_start,
- dt->ray_dir,
- ob,
- em_orig,
- obmat,
- ob_index,
- use_backface_culling,
- ray_depth,
- dt->r_loc,
- dt->r_no,
- dt->r_index,
- dt->r_hit_list);
- break;
- }
-
- BMEditMesh *em = BKE_editmesh_from_object(ob);
- if (em->mesh_eval_final) {
- me = em->mesh_eval_final;
- use_hide = true;
- }
+ Mesh *me_eval = mesh_for_snap(ob_eval, edit_mode_type, &use_hide);
+ if (me_eval == NULL) {
+ /* Operators only update the editmesh looptris of the original mesh. */
+ BMEditMesh *em_orig = BKE_editmesh_from_object(DEG_get_original_object(ob_eval));
+ retval = raycastEditMesh(sctx,
+ dt->ray_start,
+ dt->ray_dir,
+ ob_eval,
+ em_orig,
+ obmat,
+ ob_index,
+ use_backface_culling,
+ ray_depth,
+ dt->r_loc,
+ dt->r_no,
+ dt->r_index,
+ dt->r_hit_list);
+ break;
}
retval = raycastMesh(sctx,
dt->ray_start,
dt->ray_dir,
- ob,
- me,
+ ob_eval,
+ me_eval,
obmat,
ob_index,
use_hide,
@@ -1041,12 +1088,12 @@ static void raycast_obj_fn(SnapObjectContext *sctx,
case OB_SURF:
case OB_FONT: {
if (!is_object_active) {
- Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob);
+ Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob_eval);
if (mesh_eval) {
retval = raycastMesh(sctx,
dt->ray_start,
dt->ray_dir,
- ob,
+ ob_eval,
mesh_eval,
obmat,
ob_index,
@@ -1065,7 +1112,7 @@ static void raycast_obj_fn(SnapObjectContext *sctx,
if (retval) {
if (dt->r_ob) {
- *dt->r_ob = ob;
+ *dt->r_ob = ob_eval;
}
if (dt->r_obmat) {
copy_m4_m4(dt->r_obmat, obmat);
@@ -1320,6 +1367,33 @@ typedef struct Nearest2dUserData {
bool use_backface_culling;
} Nearest2dUserData;
+static void nearest2d_data_init(SnapObjectData *sod,
+ bool is_persp,
+ bool use_backface_culling,
+ Nearest2dUserData *r_nearest2d)
+{
+ if (sod->type == SNAP_MESH) {
+ r_nearest2d->userdata = &sod->treedata_mesh;
+ r_nearest2d->get_vert_co = (Nearest2DGetVertCoCallback)cb_mvert_co_get;
+ r_nearest2d->get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_medge_verts_get;
+ r_nearest2d->copy_vert_no = (Nearest2DCopyVertNoCallback)cb_mvert_no_copy;
+ r_nearest2d->get_tri_verts_index = (Nearest2DGetTriVertsCallback)cb_mlooptri_verts_get;
+ r_nearest2d->get_tri_edges_index = (Nearest2DGetTriEdgesCallback)cb_mlooptri_edges_get;
+ }
+ else {
+ BLI_assert(sod->type == SNAP_EDIT_MESH);
+ r_nearest2d->userdata = sod->treedata_editmesh.em;
+ r_nearest2d->get_vert_co = (Nearest2DGetVertCoCallback)cb_bvert_co_get;
+ r_nearest2d->get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_bedge_verts_get;
+ r_nearest2d->copy_vert_no = (Nearest2DCopyVertNoCallback)cb_bvert_no_copy;
+ r_nearest2d->get_tri_verts_index = NULL;
+ r_nearest2d->get_tri_edges_index = NULL;
+ }
+
+ r_nearest2d->is_persp = is_persp;
+ r_nearest2d->use_backface_culling = use_backface_culling;
+}
+
static void cb_snap_vert(void *userdata,
int index,
const struct DistProjectedAABBPrecalc *precalc,
@@ -1467,7 +1541,7 @@ static void cb_snap_tri_verts(void *userdata,
static short snap_mesh_polygon(SnapObjectContext *sctx,
SnapData *snapdata,
- Object *ob,
+ Object *ob_eval,
const float obmat[4][4],
bool use_backface_culling,
/* read/write args */
@@ -1492,28 +1566,21 @@ static short snap_mesh_polygon(SnapObjectContext *sctx,
mul_v4_m4v4(clip_planes_local[i], tobmat, snapdata->clip_plane[i]);
}
- Nearest2dUserData nearest2d = {
- .is_persp = snapdata->view_proj == VIEW_PROJ_PERSP,
- .use_backface_culling = use_backface_culling,
- };
-
BVHTreeNearest nearest = {
.index = -1,
.dist_sq = square_f(*dist_px),
};
- SnapObjectData *sod = snap_object_data_lookup(sctx, ob);
-
+ SnapObjectData *sod = snap_object_data_lookup(sctx, ob_eval);
BLI_assert(sod != NULL);
+ Nearest2dUserData nearest2d;
+ nearest2d_data_init(
+ sod, snapdata->view_proj == VIEW_PROJ_PERSP, use_backface_culling, &nearest2d);
+
if (sod->type == SNAP_MESH) {
BVHTreeFromMesh *treedata = &sod->treedata_mesh;
- nearest2d.userdata = treedata;
- nearest2d.get_vert_co = (Nearest2DGetVertCoCallback)cb_mvert_co_get;
- nearest2d.get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_medge_verts_get;
- nearest2d.copy_vert_no = (Nearest2DCopyVertNoCallback)cb_mvert_no_copy;
-
const MPoly *mp = &sod->poly[*r_index];
const MLoop *ml = &treedata->loop[mp->loopstart];
if (snapdata->snap_to_flag & SCE_SNAP_MODE_EDGE) {
@@ -1544,11 +1611,6 @@ static short snap_mesh_polygon(SnapObjectContext *sctx,
BLI_assert(sod->type == SNAP_EDIT_MESH);
BMEditMesh *em = sod->treedata_editmesh.em;
- nearest2d.userdata = em;
- nearest2d.get_vert_co = (Nearest2DGetVertCoCallback)cb_bvert_co_get;
- nearest2d.get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_bedge_verts_get;
- nearest2d.copy_vert_no = (Nearest2DCopyVertNoCallback)cb_bvert_no_copy;
-
BM_mesh_elem_table_ensure(em->bm, BM_FACE);
BMFace *f = BM_face_at_index(em->bm, *r_index);
BMLoop *l_iter, *l_first;
@@ -1605,7 +1667,7 @@ static short snap_mesh_polygon(SnapObjectContext *sctx,
static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx,
SnapData *snapdata,
- Object *ob,
+ Object *ob_eval,
const float obmat[4][4],
float original_dist_px,
const float prev_co[3],
@@ -1619,32 +1681,16 @@ static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx,
{
short elem = SCE_SNAP_MODE_EDGE;
- if (ob->type != OB_MESH) {
+ if (ob_eval->type != OB_MESH) {
return elem;
}
- SnapObjectData *sod = snap_object_data_lookup(sctx, ob);
-
+ SnapObjectData *sod = snap_object_data_lookup(sctx, ob_eval);
BLI_assert(sod != NULL);
Nearest2dUserData nearest2d;
- {
- nearest2d.is_persp = snapdata->view_proj == VIEW_PROJ_PERSP;
- nearest2d.use_backface_culling = use_backface_culling;
- if (sod->type == SNAP_MESH) {
- nearest2d.userdata = &sod->treedata_mesh;
- nearest2d.get_vert_co = (Nearest2DGetVertCoCallback)cb_mvert_co_get;
- nearest2d.get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_medge_verts_get;
- nearest2d.copy_vert_no = (Nearest2DCopyVertNoCallback)cb_mvert_no_copy;
- }
- else {
- BLI_assert(sod->type == SNAP_EDIT_MESH);
- nearest2d.userdata = sod->treedata_editmesh.em;
- nearest2d.get_vert_co = (Nearest2DGetVertCoCallback)cb_bvert_co_get;
- nearest2d.get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_bedge_verts_get;
- nearest2d.copy_vert_no = (Nearest2DCopyVertNoCallback)cb_bvert_no_copy;
- }
- }
+ nearest2d_data_init(
+ sod, snapdata->view_proj == VIEW_PROJ_PERSP, use_backface_culling, &nearest2d);
int vindex[2];
nearest2d.get_edge_verts_index(*r_index, vindex, nearest2d.userdata);
@@ -1769,9 +1815,8 @@ static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx,
}
static short snapArmature(SnapData *snapdata,
- Object *ob,
+ Object *ob_eval,
const float obmat[4][4],
- bool use_obedit,
/* read/write args */
float *dist_px,
/* return args */
@@ -1792,11 +1837,11 @@ static short snapArmature(SnapData *snapdata,
dist_squared_to_projected_aabb_precalc(
&neasrest_precalc, lpmat, snapdata->win_size, snapdata->mval);
- use_obedit = use_obedit && BKE_object_is_in_editmode(ob);
+ bool use_obedit = ((bArmature *)ob_eval->data)->edbo != NULL;
if (use_obedit == false) {
/* Test BoundBox */
- BoundBox *bb = BKE_armature_boundbox_get(ob);
+ BoundBox *bb = BKE_armature_boundbox_get(ob_eval);
if (bb && !snap_bound_box_check_dist(
bb->vec[0], bb->vec[6], lpmat, snapdata->win_size, snapdata->mval, dist_px_sq)) {
return retval;
@@ -1811,7 +1856,7 @@ static short snapArmature(SnapData *snapdata,
bool is_persp = snapdata->view_proj == VIEW_PROJ_PERSP;
- bArmature *arm = ob->data;
+ bArmature *arm = ob_eval->data;
if (arm->edbo) {
LISTBASE_FOREACH (EditBone *, eBone, arm->edbo) {
if (eBone->layer & arm->layer) {
@@ -1855,8 +1900,8 @@ static short snapArmature(SnapData *snapdata,
}
}
}
- else if (ob->pose && ob->pose->chanbase.first) {
- LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) {
+ else if (ob_eval->pose && ob_eval->pose->chanbase.first) {
+ LISTBASE_FOREACH (bPoseChannel *, pchan, &ob_eval->pose->chanbase) {
Bone *bone = pchan->bone;
/* skip hidden bones */
if (bone && !(bone->flag & (BONE_HIDDEN_P | BONE_HIDDEN_PG))) {
@@ -1914,7 +1959,7 @@ static short snapArmature(SnapData *snapdata,
}
static short snapCurve(SnapData *snapdata,
- Object *ob,
+ Object *ob_eval,
const float obmat[4][4],
bool use_obedit,
/* read/write args */
@@ -1931,7 +1976,7 @@ static short snapCurve(SnapData *snapdata,
return 0;
}
- Curve *cu = ob->data;
+ Curve *cu = ob_eval->data;
float dist_px_sq = square_f(*dist_px);
float lpmat[4][4];
@@ -1941,11 +1986,11 @@ static short snapCurve(SnapData *snapdata,
dist_squared_to_projected_aabb_precalc(
&neasrest_precalc, lpmat, snapdata->win_size, snapdata->mval);
- use_obedit = use_obedit && BKE_object_is_in_editmode(ob);
+ use_obedit = use_obedit && BKE_object_is_in_editmode(ob_eval);
if (use_obedit == false) {
/* Test BoundBox */
- BoundBox *bb = BKE_curve_boundbox_get(ob);
+ BoundBox *bb = BKE_curve_boundbox_get(ob_eval);
if (bb && !snap_bound_box_check_dist(
bb->vec[0], bb->vec[6], lpmat, snapdata->win_size, snapdata->mval, dist_px_sq)) {
return 0;
@@ -2065,7 +2110,7 @@ static short snapCurve(SnapData *snapdata,
/* may extend later (for now just snaps to empty center) */
static short snap_object_center(SnapData *snapdata,
- Object *ob,
+ Object *ob_eval,
const float obmat[4][4],
/* read/write args */
float *dist_px,
@@ -2076,7 +2121,7 @@ static short snap_object_center(SnapData *snapdata,
{
short retval = 0;
- if (ob->transflag & OB_DUPLI) {
+ if (ob_eval->transflag & OB_DUPLI) {
return retval;
}
@@ -2219,9 +2264,10 @@ static short snapCamera(const SnapObjectContext *sctx,
static short snapMesh(SnapObjectContext *sctx,
SnapData *snapdata,
- Object *ob,
- Mesh *me,
+ Object *ob_eval,
+ Mesh *me_eval,
const float obmat[4][4],
+ bool use_hide,
bool use_backface_culling,
/* read/write args */
float *dist_px,
@@ -2231,16 +2277,11 @@ static short snapMesh(SnapObjectContext *sctx,
int *r_index)
{
BLI_assert(snapdata->snap_to_flag != SCE_SNAP_MODE_FACE);
-
- if ((snapdata->snap_to_flag & ~SCE_SNAP_MODE_FACE) == SCE_SNAP_MODE_VERTEX) {
- if (me->totvert == 0) {
- return 0;
- }
+ if (me_eval->totvert == 0) {
+ return 0;
}
- else {
- if (me->totedge == 0) {
- return 0;
- }
+ if (me_eval->totedge == 0 && !(snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX)) {
+ return 0;
}
float lpmat[4][4];
@@ -2249,65 +2290,46 @@ static short snapMesh(SnapObjectContext *sctx,
float dist_px_sq = square_f(*dist_px);
/* Test BoundBox */
- BoundBox *bb = BKE_mesh_boundbox_get(ob);
+ BoundBox *bb = BKE_mesh_boundbox_get(ob_eval);
if (bb && !snap_bound_box_check_dist(
bb->vec[0], bb->vec[6], lpmat, snapdata->win_size, snapdata->mval, dist_px_sq)) {
return 0;
}
- SnapObjectData *sod = snap_object_data_mesh_get(sctx, ob);
+ SnapObjectData *sod = snap_object_data_mesh_get(sctx, ob_eval, me_eval, use_hide);
- BVHTreeFromMesh *treedata, dummy_treedata;
+ BVHTreeFromMesh *treedata, treedata_tmp;
treedata = &sod->treedata_mesh;
- /* The tree is owned by the Mesh and may have been freed since we last used! */
- if (treedata->cached && treedata->tree &&
- !bvhcache_has_tree(me->runtime.bvh_cache, treedata->tree)) {
- free_bvhtree_from_mesh(treedata);
- }
- if (sod->cached[0] && sod->bvhtree[0] &&
- !bvhcache_has_tree(me->runtime.bvh_cache, sod->bvhtree[0])) {
- sod->bvhtree[0] = NULL;
- }
- if (sod->cached[1] && sod->bvhtree[1] &&
- !bvhcache_has_tree(me->runtime.bvh_cache, sod->bvhtree[1])) {
- sod->bvhtree[1] = NULL;
- }
-
- if (sod->has_looptris && treedata->tree == NULL) {
- BKE_bvhtree_from_mesh_get(treedata, me, BVHTREE_FROM_LOOPTRI, 4);
- sod->has_looptris = (treedata->tree != NULL);
- if (sod->has_looptris) {
- /* Make sure that the array of edges is referenced in the callbacks. */
- treedata->edge = me->medge; /* CustomData_get_layer(&me->edata, CD_MEDGE);? */
- }
- }
if (sod->has_loose_edge && sod->bvhtree[0] == NULL) {
- sod->bvhtree[0] = BKE_bvhtree_from_mesh_get(&dummy_treedata, me, BVHTREE_FROM_LOOSEEDGES, 2);
- sod->has_loose_edge = sod->bvhtree[0] != NULL;
- sod->cached[0] = dummy_treedata.cached;
-
- if (sod->has_loose_edge) {
- BLI_assert(treedata->vert_allocated == false);
- treedata->vert = dummy_treedata.vert;
- treedata->vert_allocated = dummy_treedata.vert_allocated;
-
- BLI_assert(treedata->edge_allocated == false);
- treedata->edge = dummy_treedata.edge;
- treedata->edge_allocated = dummy_treedata.edge_allocated;
+ sod->bvhtree[0] = BKE_bvhtree_from_mesh_get(
+ &treedata_tmp, me_eval, BVHTREE_FROM_LOOSEEDGES, 2);
+ if (sod->bvhtree[0] == NULL) {
+ sod->has_loose_edge = false;
}
+ sod->cached[0] = treedata_tmp.cached;
+ BLI_assert(!ELEM(true,
+ treedata_tmp.vert_allocated,
+ treedata_tmp.edge_allocated,
+ treedata_tmp.face_allocated,
+ treedata_tmp.loop_allocated,
+ treedata_tmp.looptri_allocated));
}
+
if (snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX) {
if (sod->has_loose_vert && sod->bvhtree[1] == NULL) {
- sod->bvhtree[1] = BKE_bvhtree_from_mesh_get(&dummy_treedata, me, BVHTREE_FROM_LOOSEVERTS, 2);
- sod->has_loose_vert = sod->bvhtree[1] != NULL;
- sod->cached[1] = dummy_treedata.cached;
-
- if (sod->has_loose_vert) {
- BLI_assert(treedata->vert_allocated == false);
- treedata->vert = dummy_treedata.vert;
- treedata->vert_allocated = dummy_treedata.vert_allocated;
+ sod->bvhtree[1] = BKE_bvhtree_from_mesh_get(
+ &treedata_tmp, me_eval, BVHTREE_FROM_LOOSEVERTS, 2);
+ if (sod->bvhtree[1] == NULL) {
+ sod->has_loose_vert = false;
}
+ sod->cached[1] = treedata_tmp.cached;
+ BLI_assert(!ELEM(true,
+ treedata_tmp.vert_allocated,
+ treedata_tmp.edge_allocated,
+ treedata_tmp.face_allocated,
+ treedata_tmp.loop_allocated,
+ treedata_tmp.looptri_allocated));
}
}
else {
@@ -2315,33 +2337,9 @@ static short snapMesh(SnapObjectContext *sctx,
sod->has_loose_vert = false;
}
- /* Update pointers. */
- if (treedata->vert_allocated == false) {
- treedata->vert = me->mvert; /* CustomData_get_layer(&me->vdata, CD_MVERT);? */
- }
- if (treedata->tree || sod->bvhtree[0]) {
- if (treedata->edge_allocated == false) {
- /* If raycast has been executed before, `treedata->edge` can be NULL. */
- treedata->edge = me->medge; /* CustomData_get_layer(&me->edata, CD_MEDGE);? */
- }
- if (treedata->loop && treedata->loop_allocated == false) {
- treedata->loop = me->mloop; /* CustomData_get_layer(&me->edata, CD_MLOOP);? */
- }
- if (treedata->looptri && treedata->looptri_allocated == false) {
- treedata->looptri = BKE_mesh_runtime_looptri_ensure(me);
- }
- }
-
- Nearest2dUserData nearest2d = {
- .userdata = treedata,
- .get_vert_co = (Nearest2DGetVertCoCallback)cb_mvert_co_get,
- .get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_medge_verts_get,
- .get_tri_verts_index = (Nearest2DGetTriVertsCallback)cb_mlooptri_verts_get,
- .get_tri_edges_index = (Nearest2DGetTriEdgesCallback)cb_mlooptri_edges_get,
- .copy_vert_no = (Nearest2DCopyVertNoCallback)cb_mvert_no_copy,
- .is_persp = snapdata->view_proj == VIEW_PROJ_PERSP,
- .use_backface_culling = use_backface_culling,
- };
+ Nearest2dUserData nearest2d;
+ nearest2d_data_init(
+ sod, snapdata->view_proj == VIEW_PROJ_PERSP, use_backface_culling, &nearest2d);
BVHTreeNearest nearest = {
.index = -1,
@@ -2457,7 +2455,7 @@ static short snapMesh(SnapObjectContext *sctx,
static short snapEditMesh(SnapObjectContext *sctx,
SnapData *snapdata,
- Object *ob,
+ Object *ob_eval,
BMEditMesh *em,
const float obmat[4][4],
bool use_backface_culling,
@@ -2486,7 +2484,7 @@ static short snapEditMesh(SnapObjectContext *sctx,
float dist_px_sq = square_f(*dist_px);
- SnapObjectData *sod = snap_object_data_editmesh_get(sctx, ob, em);
+ SnapObjectData *sod = snap_object_data_editmesh_get(sctx, ob_eval, em);
/* Test BoundBox */
@@ -2560,14 +2558,9 @@ static short snapEditMesh(SnapObjectContext *sctx,
}
}
- Nearest2dUserData nearest2d = {
- .userdata = em,
- .get_vert_co = (Nearest2DGetVertCoCallback)cb_bvert_co_get,
- .get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_bedge_verts_get,
- .copy_vert_no = (Nearest2DCopyVertNoCallback)cb_bvert_no_copy,
- .is_persp = snapdata->view_proj == VIEW_PROJ_PERSP,
- .use_backface_culling = use_backface_culling,
- };
+ Nearest2dUserData nearest2d;
+ nearest2d_data_init(
+ sod, snapdata->view_proj == VIEW_PROJ_PERSP, use_backface_culling, &nearest2d);
BVHTreeNearest nearest = {
.index = -1,
@@ -2661,9 +2654,9 @@ struct SnapObjUserData {
* \note Duplicate args here are documented at #snapObjectsRay
*/
static void snap_obj_fn(SnapObjectContext *sctx,
- Object *ob,
+ Object *ob_eval,
float obmat[4][4],
- bool use_obedit,
+ eSnapEditType edit_mode_type,
bool use_backface_culling,
bool UNUSED(is_object_active),
void *data)
@@ -2671,41 +2664,36 @@ static void snap_obj_fn(SnapObjectContext *sctx,
struct SnapObjUserData *dt = data;
short retval = 0;
- switch (ob->type) {
+ switch (ob_eval->type) {
case OB_MESH: {
- Mesh *me = ob->data;
- if (BKE_object_is_in_editmode(ob)) {
- if (use_obedit || editmesh_eval_final_is_bmesh(me->edit_mesh)) {
- /* Operators only update the editmesh looptris of the original mesh. */
- BMEditMesh *em_orig = BKE_editmesh_from_object(DEG_get_original_object(ob));
- retval = snapEditMesh(sctx,
- dt->snapdata,
- ob,
- em_orig,
- obmat,
- use_backface_culling,
- dt->dist_px,
- dt->r_loc,
- dt->r_no,
- dt->r_index);
- break;
- }
-
- BMEditMesh *em = BKE_editmesh_from_object(ob);
- if (em->mesh_eval_final) {
- me = em->mesh_eval_final;
- }
+ bool use_hide;
+ Mesh *me_eval = mesh_for_snap(ob_eval, edit_mode_type, &use_hide);
+ if (me_eval == NULL) {
+ /* Operators only update the editmesh looptris of the original mesh. */
+ BMEditMesh *em_orig = BKE_editmesh_from_object(DEG_get_original_object(ob_eval));
+ retval = snapEditMesh(sctx,
+ dt->snapdata,
+ ob_eval,
+ em_orig,
+ obmat,
+ use_backface_culling,
+ dt->dist_px,
+ dt->r_loc,
+ dt->r_no,
+ dt->r_index);
+ break;
}
- else if (ob->dt == OB_BOUNDBOX) {
+ if (ob_eval->dt == OB_BOUNDBOX) {
/* Do not snap to objects that are in bounding box display mode */
return;
}
retval = snapMesh(sctx,
dt->snapdata,
- ob,
- me,
+ ob_eval,
+ me_eval,
obmat,
+ use_hide,
use_backface_culling,
dt->dist_px,
dt->r_loc,
@@ -2715,21 +2703,28 @@ static void snap_obj_fn(SnapObjectContext *sctx,
}
case OB_ARMATURE:
retval = snapArmature(
- dt->snapdata, ob, obmat, use_obedit, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index);
+ dt->snapdata, ob_eval, obmat, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index);
break;
case OB_CURVE:
- retval = snapCurve(
- dt->snapdata, ob, obmat, use_obedit, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index);
+ retval = snapCurve(dt->snapdata,
+ ob_eval,
+ obmat,
+ edit_mode_type == SNAP_GEOM_EDIT,
+ dt->dist_px,
+ dt->r_loc,
+ dt->r_no,
+ dt->r_index);
break; /* Use ATTR_FALLTHROUGH if we want to snap to the generated mesh. */
case OB_SURF:
case OB_FONT: {
- Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob);
+ Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob_eval);
if (mesh_eval) {
retval |= snapMesh(sctx,
dt->snapdata,
- ob,
+ ob_eval,
mesh_eval,
obmat,
+ false,
use_backface_culling,
dt->dist_px,
dt->r_loc,
@@ -2742,17 +2737,17 @@ static void snap_obj_fn(SnapObjectContext *sctx,
case OB_GPENCIL:
case OB_LAMP:
retval = snap_object_center(
- dt->snapdata, ob, obmat, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index);
+ dt->snapdata, ob_eval, obmat, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index);
break;
case OB_CAMERA:
retval = snapCamera(
- sctx, dt->snapdata, ob, obmat, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index);
+ sctx, dt->snapdata, ob_eval, obmat, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index);
break;
}
if (retval) {
if (dt->r_ob) {
- *dt->r_ob = ob;
+ *dt->r_ob = ob_eval;
}
if (dt->r_obmat) {
copy_m4_m4(dt->r_obmat, obmat);
@@ -3025,7 +3020,7 @@ static short transform_snap_context_project_view3d_mixed_impl(
short retval = 0;
bool has_hit = false;
- Object *ob = NULL;
+ Object *ob_eval = NULL;
float loc[3];
/* Not all snapping callbacks set the normal,
* initialize this since any hit copies both the `loc` and `no`. */
@@ -3062,7 +3057,7 @@ static short transform_snap_context_project_view3d_mixed_impl(
loc,
no,
&index,
- &ob,
+ &ob_eval,
obmat,
NULL);
@@ -3074,7 +3069,7 @@ static short transform_snap_context_project_view3d_mixed_impl(
copy_v3_v3(r_no, no);
}
if (r_ob) {
- *r_ob = ob;
+ *r_ob = ob_eval;
}
if (r_obmat) {
copy_m4_m4(r_obmat, obmat);
@@ -3110,9 +3105,10 @@ static short transform_snap_context_project_view3d_mixed_impl(
snapdata.has_occlusion_plane = false;
/* By convention we only snap to the original elements of a curve. */
- if (has_hit && ob->type != OB_CURVE) {
+ if (has_hit && ob_eval->type != OB_CURVE) {
/* Compute the new clip_pane but do not add it yet. */
float new_clipplane[4];
+ BLI_ASSERT_UNIT_V3(no);
plane_from_point_normal_v3(new_clipplane, loc, no);
if (dot_v3v3(snapdata.clip_plane[0], new_clipplane) > 0.0f) {
/* The plane is facing the wrong direction. */
@@ -3123,8 +3119,15 @@ static short transform_snap_context_project_view3d_mixed_impl(
new_clipplane[3] += 0.01f;
/* Try to snap only to the polygon. */
- elem_test = snap_mesh_polygon(
- sctx, &snapdata, ob, obmat, params->use_backface_culling, &dist_px_tmp, loc, no, &index);
+ elem_test = snap_mesh_polygon(sctx,
+ &snapdata,
+ ob_eval,
+ obmat,
+ params->use_backface_culling,
+ &dist_px_tmp,
+ loc,
+ no,
+ &index);
if (elem_test) {
elem = elem_test;
}
@@ -3139,7 +3142,7 @@ static short transform_snap_context_project_view3d_mixed_impl(
}
elem_test = snapObjectsRay(
- sctx, depsgraph, &snapdata, params, &dist_px_tmp, loc, no, &index, &ob, obmat);
+ sctx, depsgraph, &snapdata, params, &dist_px_tmp, loc, no, &index, &ob_eval, obmat);
if (elem_test) {
elem = elem_test;
}
@@ -3150,7 +3153,7 @@ static short transform_snap_context_project_view3d_mixed_impl(
snapdata.snap_to_flag = snap_to_flag;
elem = snap_mesh_edge_verts_mixed(sctx,
&snapdata,
- ob,
+ ob_eval,
obmat,
*dist_px,
prev_co,
@@ -3169,7 +3172,7 @@ static short transform_snap_context_project_view3d_mixed_impl(
copy_v3_v3(r_no, no);
}
if (r_ob) {
- *r_ob = ob;
+ *r_ob = ob_eval;
}
if (r_obmat) {
copy_m4_m4(r_obmat, obmat);
diff --git a/source/blender/editors/undo/ed_undo.c b/source/blender/editors/undo/ed_undo.c
index b69f62a2875..7811509ab40 100644
--- a/source/blender/editors/undo/ed_undo.c
+++ b/source/blender/editors/undo/ed_undo.c
@@ -273,9 +273,11 @@ static void ed_undo_step_post(bContext *C,
}
}
-/** Undo or redo one step from current active one.
- * May undo or redo several steps at once only if the target step is a 'skipped' one.
- * The target step will be the one immediately before or after the active one. */
+/**
+ * Undo or redo one step from current active one.
+ * May undo or redo several steps at once only if the target step is a 'skipped' one.
+ * The target step will be the one immediately before or after the active one.
+ */
static int ed_undo_step_direction(bContext *C, enum eUndoStepDir step, ReportList *reports)
{
BLI_assert(ELEM(step, STEP_UNDO, STEP_REDO));
@@ -308,9 +310,11 @@ static int ed_undo_step_direction(bContext *C, enum eUndoStepDir step, ReportLis
return OPERATOR_FINISHED;
}
-/** Undo the step matching given name.
- * May undo several steps at once.
- * The target step will be the one immediately before given named one. */
+/**
+ * Undo the step matching given name.
+ * May undo several steps at once.
+ * The target step will be the one immediately before given named one.
+ */
static int ed_undo_step_by_name(bContext *C, const char *undo_name, ReportList *reports)
{
BLI_assert(undo_name != NULL);
@@ -354,9 +358,11 @@ static int ed_undo_step_by_name(bContext *C, const char *undo_name, ReportList *
return OPERATOR_FINISHED;
}
-/** Load the step matching given index in the stack.
- * May undo or redo several steps at once.
- * The target step will be the one indicated by the given index. */
+/**
+ * Load the step matching given index in the stack.
+ * May undo or redo several steps at once.
+ * The target step will be the one indicated by the given index.
+ */
static int ed_undo_step_by_index(bContext *C, const int undo_index, ReportList *reports)
{
BLI_assert(undo_index >= 0);
diff --git a/source/blender/editors/util/CMakeLists.txt b/source/blender/editors/util/CMakeLists.txt
index 7d7d10004a3..54ec6b22e70 100644
--- a/source/blender/editors/util/CMakeLists.txt
+++ b/source/blender/editors/util/CMakeLists.txt
@@ -18,10 +18,10 @@
set(INC
../include
../space_sequencer
+ ../../blenfont
../../blenkernel
../../blenlib
../../blentranslation
- ../../blenfont
../../bmesh
../../depsgraph
../../gpu
@@ -86,6 +86,7 @@ set(SRC
../include/ED_sequencer.h
../include/ED_sound.h
../include/ED_space_api.h
+ ../include/ED_spreadsheet.h
../include/ED_text.h
../include/ED_time_scrub_ui.h
../include/ED_transform.h
diff --git a/source/blender/editors/util/ed_draw.c b/source/blender/editors/util/ed_draw.c
index d7b22b4f601..3b543cdf6ce 100644
--- a/source/blender/editors/util/ed_draw.c
+++ b/source/blender/editors/util/ed_draw.c
@@ -189,7 +189,7 @@ static void metadata_draw_imbuf(ImBuf *ibuf, const rctf *rect, int fontid, const
ofs_y += vertical_offset;
}
} /* Strip */
- else if (i == 1 || i == 2) {
+ else if (ELEM(i, 1, 2)) {
int len = BLI_snprintf_rlen(temp_str, MAX_METADATA_STR, "%s: ", meta_data_list[i + 1]);
if (metadata_is_valid(ibuf, temp_str, i + 1, len)) {
BLF_position(fontid, xmin, ymax - vertical_offset - ofs_y, 0.0f);
diff --git a/source/blender/editors/util/ed_transverts.c b/source/blender/editors/util/ed_transverts.c
index d0234dee856..c1d0dcdb095 100644
--- a/source/blender/editors/util/ed_transverts.c
+++ b/source/blender/editors/util/ed_transverts.c
@@ -54,7 +54,7 @@ void ED_transverts_update_obedit(TransVertStore *tvs, Object *obedit)
const int mode = tvs->mode;
BLI_assert(ED_transverts_check_obedit(obedit) == true);
- DEG_id_tag_update(obedit->data, 0);
+ DEG_id_tag_update(obedit->data, ID_RECALC_GEOMETRY);
if (obedit->type == OB_MESH) {
BMEditMesh *em = BKE_editmesh_from_object(obedit);
@@ -111,7 +111,9 @@ void ED_transverts_update_obedit(TransVertStore *tvs, Object *obedit)
}
}
- BKE_nurb_test_2d(nu);
+ if (CU_IS_2D(cu)) {
+ BKE_nurb_project_2d(nu);
+ }
BKE_nurb_handles_test(nu, true, false); /* test for bezier too */
nu = nu->next;
}
diff --git a/source/blender/editors/util/ed_util.c b/source/blender/editors/util/ed_util.c
index da94eef4917..b80782b51be 100644
--- a/source/blender/editors/util/ed_util.c
+++ b/source/blender/editors/util/ed_util.c
@@ -27,9 +27,6 @@
#include "MEM_guardedalloc.h"
-#include "DNA_armature_types.h"
-#include "DNA_mesh_types.h"
-
#include "BLI_listbase.h"
#include "BLI_path_util.h"
#include "BLI_string.h"
@@ -206,24 +203,9 @@ void ED_editors_exit(Main *bmain, bool do_undo_system)
*
* To reproduce the problem where stale data is used, see: T84920. */
for (Object *ob = bmain->objects.first; ob; ob = ob->id.next) {
- if (ob->type == OB_MESH) {
- Mesh *me = ob->data;
- if (me->edit_mesh) {
- EDBM_mesh_free(me->edit_mesh);
- MEM_freeN(me->edit_mesh);
- me->edit_mesh = NULL;
- if (do_undo_system == false) {
- DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
- }
- }
- }
- else if (ob->type == OB_ARMATURE) {
- bArmature *arm = ob->data;
- if (arm->edbo) {
- ED_armature_edit_free(ob->data);
- if (do_undo_system == false) {
- DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
- }
+ if (ED_object_editmode_free_ex(bmain, ob)) {
+ if (do_undo_system == false) {
+ DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
}
}
}
diff --git a/source/blender/editors/util/ed_util_ops.cc b/source/blender/editors/util/ed_util_ops.cc
index 06f1b999d58..462f7768f81 100644
--- a/source/blender/editors/util/ed_util_ops.cc
+++ b/source/blender/editors/util/ed_util_ops.cc
@@ -149,7 +149,7 @@ static void ED_OT_lib_id_generate_preview(wmOperatorType *ot)
ot->exec = lib_id_generate_preview_exec;
/* flags */
- ot->flag = OPTYPE_INTERNAL;
+ ot->flag = OPTYPE_INTERNAL | OPTYPE_REGISTER | OPTYPE_UNDO;
}
/** \} */
diff --git a/source/blender/editors/util/numinput.c b/source/blender/editors/util/numinput.c
index 91ec8546225..15d672dea56 100644
--- a/source/blender/editors/util/numinput.c
+++ b/source/blender/editors/util/numinput.c
@@ -29,6 +29,7 @@
#include "BLT_translation.h"
#include "BKE_context.h"
+#include "BKE_report.h"
#include "BKE_scene.h"
#include "BKE_unit.h"
@@ -283,10 +284,15 @@ bool user_string_to_number(bContext *C,
const char *str,
const UnitSettings *unit,
int type,
- const char *error_prefix,
- double *r_value)
+ double *r_value,
+ const bool use_single_line_error,
+ char **r_error)
{
#ifdef WITH_PYTHON
+ struct BPy_RunErrInfo err_info = {
+ .use_single_line_error = use_single_line_error,
+ .r_string = r_error,
+ };
double unit_scale = BKE_scene_unit_scale(unit, type, 1.0);
if (BKE_unit_string_contains_unit(str, type)) {
char str_unit_convert[256];
@@ -294,10 +300,10 @@ bool user_string_to_number(bContext *C,
BKE_unit_replace_string(
str_unit_convert, sizeof(str_unit_convert), str, unit_scale, unit->system, type);
- return BPY_run_string_as_number(C, NULL, str_unit_convert, error_prefix, r_value);
+ return BPY_run_string_as_number(C, NULL, str_unit_convert, &err_info, r_value);
}
- int success = BPY_run_string_as_number(C, NULL, str, error_prefix, r_value);
+ int success = BPY_run_string_as_number(C, NULL, str, &err_info, r_value);
*r_value = BKE_unit_apply_preferred_unit(unit, type, *r_value);
*r_value /= unit_scale;
return success;
@@ -577,10 +583,19 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event)
if (n->str[0]) {
const float val_prev = n->val[idx];
Scene *sce = CTX_data_scene(C);
+ char *error = NULL;
double val;
int success = user_string_to_number(
- C, n->str, &sce->unit, n->unit_type[idx], IFACE_("Numeric input evaluation"), &val);
+ C, n->str, &sce->unit, n->unit_type[idx], &val, false, &error);
+
+ if (error) {
+ ReportList *reports = CTX_wm_reports(C);
+ printf("%s\n", error);
+ BKE_report(reports, RPT_ERROR, error);
+ BKE_report(reports, RPT_ERROR, IFACE_("Numeric input evaluation"));
+ MEM_freeN(error);
+ }
if (success) {
n->val[idx] = (float)val;
diff --git a/source/blender/editors/util/select_utils.c b/source/blender/editors/util/select_utils.c
index 14a6d751bb1..4e8cf1e92e6 100644
--- a/source/blender/editors/util/select_utils.c
+++ b/source/blender/editors/util/select_utils.c
@@ -88,11 +88,11 @@ int ED_select_similar_compare_float(const float delta, const float thresh, const
{
switch (compare) {
case SIM_CMP_EQ:
- return (fabsf(delta) < thresh + FLT_EPSILON);
+ return (fabsf(delta) <= thresh);
case SIM_CMP_GT:
- return ((delta + thresh) > -FLT_EPSILON);
+ return ((delta + thresh) >= 0.0);
case SIM_CMP_LT:
- return ((delta - thresh) < FLT_EPSILON);
+ return ((delta - thresh) <= 0.0);
default:
BLI_assert_unreachable();
return 0;
diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c
index e11341429a6..708f04bf044 100644
--- a/source/blender/editors/uvedit/uvedit_ops.c
+++ b/source/blender/editors/uvedit/uvedit_ops.c
@@ -1471,7 +1471,7 @@ static int uv_hide_exec(bContext *C, wmOperator *op)
if (EDBM_mesh_hide(em, swap)) {
EDBM_update_generic(ob->data, true, false);
}
- return OPERATOR_FINISHED;
+ continue;
}
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
@@ -1609,7 +1609,7 @@ static int uv_reveal_exec(bContext *C, wmOperator *op)
if (EDBM_mesh_reveal(em, select)) {
EDBM_update_generic(ob->data, true, false);
}
- return OPERATOR_FINISHED;
+ continue;
}
if (use_face_center) {
if (em->selectmode == SCE_SELECT_FACE) {
diff --git a/source/blender/editors/uvedit/uvedit_parametrizer.c b/source/blender/editors/uvedit/uvedit_parametrizer.c
index e94aaa49839..7d82884760c 100644
--- a/source/blender/editors/uvedit/uvedit_parametrizer.c
+++ b/source/blender/editors/uvedit/uvedit_parametrizer.c
@@ -4546,7 +4546,10 @@ void param_edge_set_seam(ParamHandle *handle, ParamKey *vkeys)
}
}
-void param_construct_end(ParamHandle *handle, ParamBool fill, ParamBool topology_from_uvs)
+void param_construct_end(ParamHandle *handle,
+ ParamBool fill,
+ ParamBool topology_from_uvs,
+ int *count_fail)
{
PHandle *phandle = (PHandle *)handle;
PChart *chart = phandle->construction_chart;
@@ -4574,6 +4577,9 @@ void param_construct_end(ParamHandle *handle, ParamBool fill, ParamBool topology
if (!topology_from_uvs && nboundaries == 0) {
p_chart_delete(chart);
+ if (count_fail != NULL) {
+ *count_fail += 1;
+ }
continue;
}
@@ -4611,12 +4617,11 @@ void param_lscm_begin(ParamHandle *handle, ParamBool live, ParamBool abf)
}
}
-void param_lscm_solve(ParamHandle *handle)
+void param_lscm_solve(ParamHandle *handle, int *count_changed, int *count_failed)
{
PHandle *phandle = (PHandle *)handle;
PChart *chart;
int i;
- PBool result;
param_assert(phandle->state == PHANDLE_STATE_LSCM);
@@ -4624,7 +4629,7 @@ void param_lscm_solve(ParamHandle *handle)
chart = phandle->charts[i];
if (chart->u.lscm.context) {
- result = p_chart_lscm_solve(phandle, chart);
+ const PBool result = p_chart_lscm_solve(phandle, chart);
if (result && !(chart->flag & PCHART_HAS_PINS)) {
p_chart_rotate_minimum_area(chart);
@@ -4637,6 +4642,17 @@ void param_lscm_solve(ParamHandle *handle)
if (!result || !(chart->flag & PCHART_HAS_PINS)) {
p_chart_lscm_end(chart);
}
+
+ if (result) {
+ if (count_changed != NULL) {
+ *count_changed += 1;
+ }
+ }
+ else {
+ if (count_failed != NULL) {
+ *count_failed += 1;
+ }
+ }
}
}
}
diff --git a/source/blender/editors/uvedit/uvedit_parametrizer.h b/source/blender/editors/uvedit/uvedit_parametrizer.h
index 2427e589833..e69ce360e61 100644
--- a/source/blender/editors/uvedit/uvedit_parametrizer.h
+++ b/source/blender/editors/uvedit/uvedit_parametrizer.h
@@ -59,7 +59,10 @@ void param_face_add(ParamHandle *handle,
void param_edge_set_seam(ParamHandle *handle, ParamKey *vkeys);
-void param_construct_end(ParamHandle *handle, ParamBool fill, ParamBool topology_from_uvs);
+void param_construct_end(ParamHandle *handle,
+ ParamBool fill,
+ ParamBool topology_from_uvs,
+ int *count_fail);
void param_delete(ParamHandle *handle);
/* Least Squares Conformal Maps:
@@ -74,7 +77,7 @@ void param_delete(ParamHandle *handle);
*/
void param_lscm_begin(ParamHandle *handle, ParamBool live, ParamBool abf);
-void param_lscm_solve(ParamHandle *handle);
+void param_lscm_solve(ParamHandle *handle, int *count_changed, int *count_failed);
void param_lscm_end(ParamHandle *handle);
/* Stretch */
diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c
index c10e132a4e2..39950f9d0c7 100644
--- a/source/blender/editors/uvedit/uvedit_select.c
+++ b/source/blender/editors/uvedit/uvedit_select.c
@@ -1381,7 +1381,7 @@ static void uv_select_linked_multi(Scene *scene,
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
bool add_to_stack = true;
- if (uv_sync_select && !select_faces) {
+ if (uv_sync_select) {
/* Special case, vertex/edge & sync select being enabled.
*
* Without this, a second linked select will 'grow' each time as each new
@@ -1392,6 +1392,7 @@ static void uv_select_linked_multi(Scene *scene,
* - The only other fully selected face is connected or,
* - There are no connected fully selected faces UV-connected to this loop.
*/
+ BLI_assert(!select_faces);
if (uvedit_face_select_test(scene, l->f, cd_loop_uv_offset)) {
/* pass */
}
diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c
index fc5f41e8ed5..87ae112a237 100644
--- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c
+++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c
@@ -165,6 +165,11 @@ typedef struct UnwrapOptions {
bool correct_aspect;
} UnwrapOptions;
+typedef struct UnwrapResultInfo {
+ int count_changed;
+ int count_failed;
+} UnwrapResultInfo;
+
static bool uvedit_have_selection(const Scene *scene, BMEditMesh *em, const UnwrapOptions *options)
{
BMFace *efa;
@@ -281,7 +286,8 @@ static void construct_param_handle_face_add(ParamHandle *handle,
static ParamHandle *construct_param_handle(const Scene *scene,
Object *ob,
BMesh *bm,
- const UnwrapOptions *options)
+ const UnwrapOptions *options,
+ UnwrapResultInfo *result_info)
{
ParamHandle *handle;
BMFace *efa;
@@ -344,7 +350,10 @@ static ParamHandle *construct_param_handle(const Scene *scene,
}
}
- param_construct_end(handle, options->fill_holes, options->topology_from_uvs);
+ param_construct_end(handle,
+ options->fill_holes,
+ options->topology_from_uvs,
+ result_info ? &result_info->count_failed : NULL);
return handle;
}
@@ -355,7 +364,8 @@ static ParamHandle *construct_param_handle(const Scene *scene,
static ParamHandle *construct_param_handle_multi(const Scene *scene,
Object **objects,
const uint objects_len,
- const UnwrapOptions *options)
+ const UnwrapOptions *options,
+ int *count_fail)
{
ParamHandle *handle;
BMFace *efa;
@@ -431,7 +441,7 @@ static ParamHandle *construct_param_handle_multi(const Scene *scene,
offset += bm->totface;
}
- param_construct_end(handle, options->fill_holes, options->topology_from_uvs);
+ param_construct_end(handle, options->fill_holes, options->topology_from_uvs, count_fail);
return handle;
}
@@ -475,7 +485,8 @@ static void texface_from_original_index(const Scene *scene,
static ParamHandle *construct_param_handle_subsurfed(const Scene *scene,
Object *ob,
BMEditMesh *em,
- const UnwrapOptions *options)
+ const UnwrapOptions *options,
+ UnwrapResultInfo *result_info)
{
ParamHandle *handle;
/* index pointers */
@@ -651,7 +662,10 @@ static ParamHandle *construct_param_handle_subsurfed(const Scene *scene,
}
}
- param_construct_end(handle, options->fill_holes, options->topology_from_uvs);
+ param_construct_end(handle,
+ options->fill_holes,
+ options->topology_from_uvs,
+ result_info ? &result_info->count_failed : NULL);
/* cleanup */
MEM_freeN(faceMap);
@@ -707,7 +721,7 @@ static bool minimize_stretch_init(bContext *C, wmOperator *op)
ms->blend = RNA_float_get(op->ptr, "blend");
ms->iterations = RNA_int_get(op->ptr, "iterations");
ms->i = 0;
- ms->handle = construct_param_handle_multi(scene, objects, objects_len, &options);
+ ms->handle = construct_param_handle_multi(scene, objects, objects_len, &options, NULL);
ms->lasttime = PIL_check_seconds_timer();
param_stretch_begin(ms->handle);
@@ -959,7 +973,7 @@ static void uvedit_pack_islands(const Scene *scene, Object *ob, BMesh *bm)
bool ignore_pinned = false;
ParamHandle *handle;
- handle = construct_param_handle(scene, ob, bm, &options);
+ handle = construct_param_handle(scene, ob, bm, &options, NULL);
param_pack(handle, scene->toolsettings->uvcalc_margin, rotate, ignore_pinned);
param_flush(handle);
param_delete(handle);
@@ -979,7 +993,7 @@ static void uvedit_pack_islands_multi(const Scene *scene,
bool ignore_pinned)
{
ParamHandle *handle;
- handle = construct_param_handle_multi(scene, objects, objects_len, options);
+ handle = construct_param_handle_multi(scene, objects, objects_len, options, NULL);
param_pack(handle, scene->toolsettings->uvcalc_margin, rotate, ignore_pinned);
param_flush(handle);
param_delete(handle);
@@ -1087,7 +1101,7 @@ static int average_islands_scale_exec(bContext *C, wmOperator *UNUSED(op))
return OPERATOR_CANCELLED;
}
- ParamHandle *handle = construct_param_handle_multi(scene, objects, objects_len, &options);
+ ParamHandle *handle = construct_param_handle_multi(scene, objects, objects_len, &options, NULL);
param_average(handle, false);
param_flush(handle);
param_delete(handle);
@@ -1154,10 +1168,10 @@ void ED_uvedit_live_unwrap_begin(Scene *scene, Object *obedit)
};
if (use_subsurf) {
- handle = construct_param_handle_subsurfed(scene, obedit, em, &options);
+ handle = construct_param_handle_subsurfed(scene, obedit, em, &options, NULL);
}
else {
- handle = construct_param_handle(scene, obedit, em->bm, &options);
+ handle = construct_param_handle(scene, obedit, em->bm, &options, NULL);
}
param_lscm_begin(handle, PARAM_TRUE, abf);
@@ -1182,7 +1196,7 @@ void ED_uvedit_live_unwrap_re_solve(void)
{
if (g_live_unwrap.handles) {
for (int i = 0; i < g_live_unwrap.len; i++) {
- param_lscm_solve(g_live_unwrap.handles[i]);
+ param_lscm_solve(g_live_unwrap.handles[i], NULL, NULL);
param_flush(g_live_unwrap.handles[i]);
}
}
@@ -1631,7 +1645,10 @@ static void uv_map_clip_correct(Object *ob, wmOperator *op)
* \{ */
/* Assumes UV Map exists, doesn't run update funcs. */
-static void uvedit_unwrap(const Scene *scene, Object *obedit, const UnwrapOptions *options)
+static void uvedit_unwrap(const Scene *scene,
+ Object *obedit,
+ const UnwrapOptions *options,
+ UnwrapResultInfo *result_info)
{
BMEditMesh *em = BKE_editmesh_from_object(obedit);
if (!CustomData_has_layer(&em->bm->ldata, CD_MLOOPUV)) {
@@ -1643,14 +1660,16 @@ static void uvedit_unwrap(const Scene *scene, Object *obedit, const UnwrapOption
ParamHandle *handle;
if (use_subsurf) {
- handle = construct_param_handle_subsurfed(scene, obedit, em, options);
+ handle = construct_param_handle_subsurfed(scene, obedit, em, options, result_info);
}
else {
- handle = construct_param_handle(scene, obedit, em->bm, options);
+ handle = construct_param_handle(scene, obedit, em->bm, options, result_info);
}
param_lscm_begin(handle, PARAM_FALSE, scene->toolsettings->unwrapper == 0);
- param_lscm_solve(handle);
+ param_lscm_solve(handle,
+ result_info ? &result_info->count_changed : NULL,
+ result_info ? &result_info->count_failed : NULL);
param_lscm_end(handle);
param_average(handle, true);
@@ -1663,11 +1682,12 @@ static void uvedit_unwrap(const Scene *scene, Object *obedit, const UnwrapOption
static void uvedit_unwrap_multi(const Scene *scene,
Object **objects,
const int objects_len,
- const UnwrapOptions *options)
+ const UnwrapOptions *options,
+ UnwrapResultInfo *result_info)
{
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
- uvedit_unwrap(scene, obedit, options);
+ uvedit_unwrap(scene, obedit, options, result_info);
DEG_id_tag_update(obedit->data, ID_RECALC_GEOMETRY);
WM_main_add_notifier(NC_GEOM | ND_DATA, obedit->data);
}
@@ -1687,7 +1707,7 @@ void ED_uvedit_live_unwrap(const Scene *scene, Object **objects, int objects_len
bool rotate = true;
bool ignore_pinned = true;
- uvedit_unwrap_multi(scene, objects, objects_len, &options);
+ uvedit_unwrap_multi(scene, objects, objects_len, &options, NULL);
uvedit_pack_islands_multi(scene, objects, objects_len, &options, rotate, ignore_pinned);
}
}
@@ -1816,11 +1836,28 @@ static int unwrap_exec(bContext *C, wmOperator *op)
}
/* execute unwrap */
- uvedit_unwrap_multi(scene, objects, objects_len, &options);
+ UnwrapResultInfo result_info = {
+ .count_changed = 0,
+ .count_failed = 0,
+ };
+ uvedit_unwrap_multi(scene, objects, objects_len, &options, &result_info);
uvedit_pack_islands_multi(scene, objects, objects_len, &options, rotate, ignore_pinned);
MEM_freeN(objects);
+ if (result_info.count_failed == 0 && result_info.count_changed == 0) {
+ BKE_report(op->reports,
+ RPT_WARNING,
+ "Unwrap could not solve any island(s), edge seams may need to be added");
+ }
+ else if (result_info.count_failed) {
+ BKE_reportf(op->reports,
+ RPT_WARNING,
+ "Unwrap failed to solve %d of %d island(s), edge seams may need to be added",
+ result_info.count_failed,
+ result_info.count_changed + result_info.count_failed);
+ }
+
return OPERATOR_FINISHED;
}
diff --git a/source/blender/freestyle/intern/geometry/Bezier.cpp b/source/blender/freestyle/intern/geometry/Bezier.cpp
index 4eec3d72db9..bf4a78b12c5 100644
--- a/source/blender/freestyle/intern/geometry/Bezier.cpp
+++ b/source/blender/freestyle/intern/geometry/Bezier.cpp
@@ -26,13 +26,7 @@ using namespace std;
namespace Freestyle {
-BezierCurveSegment::BezierCurveSegment()
-{
-}
-
-BezierCurveSegment::~BezierCurveSegment()
-{
-}
+BezierCurveSegment::~BezierCurveSegment() = default;
void BezierCurveSegment::AddControlPoint(const Vec2d &iPoint)
{
diff --git a/source/blender/freestyle/intern/geometry/Bezier.h b/source/blender/freestyle/intern/geometry/Bezier.h
index 4c6b38935f9..83b033c105a 100644
--- a/source/blender/freestyle/intern/geometry/Bezier.h
+++ b/source/blender/freestyle/intern/geometry/Bezier.h
@@ -41,7 +41,6 @@ class BezierCurveSegment {
std::vector<Vec2d> _Vertices;
public:
- BezierCurveSegment();
virtual ~BezierCurveSegment();
void AddControlPoint(const Vec2d &iPoint);
diff --git a/source/blender/freestyle/intern/geometry/FitCurve.cpp b/source/blender/freestyle/intern/geometry/FitCurve.cpp
index 7c0b3bf0224..eb5a24f7373 100644
--- a/source/blender/freestyle/intern/geometry/FitCurve.cpp
+++ b/source/blender/freestyle/intern/geometry/FitCurve.cpp
@@ -462,10 +462,6 @@ static Vector2 V2SubII(Vector2 a, Vector2 b)
//------------------------- WRAPPER -----------------------------//
-FitCurveWrapper::FitCurveWrapper()
-{
-}
-
FitCurveWrapper::~FitCurveWrapper()
{
_vertices.clear();
diff --git a/source/blender/freestyle/intern/geometry/FitCurve.h b/source/blender/freestyle/intern/geometry/FitCurve.h
index c08dc52249e..dc60d67b943 100644
--- a/source/blender/freestyle/intern/geometry/FitCurve.h
+++ b/source/blender/freestyle/intern/geometry/FitCurve.h
@@ -70,7 +70,6 @@ class FitCurveWrapper {
std::vector<Vector2> _vertices;
public:
- FitCurveWrapper();
~FitCurveWrapper();
/*! Fits a set of 2D data points to a set of Bezier Curve segments
diff --git a/source/blender/freestyle/intern/geometry/GridHelpers.cpp b/source/blender/freestyle/intern/geometry/GridHelpers.cpp
index a83dc385758..ee64b83f19e 100644
--- a/source/blender/freestyle/intern/geometry/GridHelpers.cpp
+++ b/source/blender/freestyle/intern/geometry/GridHelpers.cpp
@@ -42,8 +42,6 @@ void GridHelpers::getDefaultViewProscenium(real viewProscenium[4])
viewProscenium[3] = g_freestyle.viewport[3] * (1.0f - borderZone + bufferZone);
}
-GridHelpers::Transform::~Transform()
-{
-}
+GridHelpers::Transform::~Transform() = default;
} /* namespace Freestyle */
diff --git a/source/blender/freestyle/intern/geometry/normal_cycle.cpp b/source/blender/freestyle/intern/geometry/normal_cycle.cpp
index 2310525a1e1..77a80e63b77 100644
--- a/source/blender/freestyle/intern/geometry/normal_cycle.cpp
+++ b/source/blender/freestyle/intern/geometry/normal_cycle.cpp
@@ -36,10 +36,6 @@ namespace Freestyle::OGF {
//_________________________________________________________
-NormalCycle::NormalCycle()
-{
-}
-
void NormalCycle::begin()
{
M_[0] = M_[1] = M_[2] = M_[3] = M_[4] = M_[5] = 0;
diff --git a/source/blender/freestyle/intern/geometry/normal_cycle.h b/source/blender/freestyle/intern/geometry/normal_cycle.h
index 5adef773be1..9d8ffcfd7fb 100644
--- a/source/blender/freestyle/intern/geometry/normal_cycle.h
+++ b/source/blender/freestyle/intern/geometry/normal_cycle.h
@@ -64,7 +64,6 @@ template<class T> inline void ogf_swap(T &x, T &y)
*/
class NormalCycle {
public:
- NormalCycle();
void begin();
void end();
/**
diff --git a/source/blender/freestyle/intern/scene_graph/NodeCamera.cpp b/source/blender/freestyle/intern/scene_graph/NodeCamera.cpp
index 4fc1f227172..cb30a661aaa 100644
--- a/source/blender/freestyle/intern/scene_graph/NodeCamera.cpp
+++ b/source/blender/freestyle/intern/scene_graph/NodeCamera.cpp
@@ -105,17 +105,6 @@ NodeOrthographicCamera::NodeOrthographicCamera(
projection_matrix_[11] = -(zFar + zNear) / (zFar - zNear);
}
-NodeOrthographicCamera::NodeOrthographicCamera(const NodeOrthographicCamera &iBrother)
- : NodeCamera(iBrother),
- left_(iBrother.left_),
- right_(iBrother.right_),
- bottom_(iBrother.bottom_),
- top_(iBrother.top_),
- zNear_(iBrother.zNear_),
- zFar_(iBrother.zFar_)
-{
-}
-
NodePerspectiveCamera::NodePerspectiveCamera() : NodeCamera(NodeCamera::PERSPECTIVE)
{
}
diff --git a/source/blender/freestyle/intern/scene_graph/NodeCamera.h b/source/blender/freestyle/intern/scene_graph/NodeCamera.h
index cc7b1f7f67c..9e3f9046e39 100644
--- a/source/blender/freestyle/intern/scene_graph/NodeCamera.h
+++ b/source/blender/freestyle/intern/scene_graph/NodeCamera.h
@@ -136,7 +136,7 @@ class NodeOrthographicCamera : public NodeCamera {
return zFar_;
}
- NodeOrthographicCamera(const NodeOrthographicCamera &iBrother);
+ NodeOrthographicCamera(const NodeOrthographicCamera &iBrother) = default;
private:
double left_;
diff --git a/source/blender/freestyle/intern/stroke/ChainingIterators.h b/source/blender/freestyle/intern/stroke/ChainingIterators.h
index e3d49c167b5..0a9e7114ecb 100644
--- a/source/blender/freestyle/intern/stroke/ChainingIterators.h
+++ b/source/blender/freestyle/intern/stroke/ChainingIterators.h
@@ -164,7 +164,7 @@ class ChainingIterator : public ViewEdgeInternal::ViewEdgeIterator {
* Indicates whether to force the chaining to stay within
* the set of selected ViewEdges or not.
* \param iRestrictToUnvisited:
- * Indicates whether a ViewEdge that has already been chained must be ignored ot not.
+ * Indicates whether a ViewEdge that has already been chained must be ignored or not.
* \param begin:
* The ViewEdge from which to start the chain.
* \param orientation:
diff --git a/source/blender/freestyle/intern/stroke/Curve.h b/source/blender/freestyle/intern/stroke/Curve.h
index 91470b57ca2..f0db45150a9 100644
--- a/source/blender/freestyle/intern/stroke/Curve.h
+++ b/source/blender/freestyle/intern/stroke/Curve.h
@@ -236,9 +236,7 @@ class CurvePoint : public Interface0D {
CurvePoint &operator=(const CurvePoint &iBrother);
/*! Destructor */
- virtual ~CurvePoint()
- {
- }
+ virtual ~CurvePoint() = default;
/*! Operator == */
bool operator==(const CurvePoint &b)
diff --git a/source/blender/freestyle/intern/stroke/Stroke.cpp b/source/blender/freestyle/intern/stroke/Stroke.cpp
index 79eb37ae870..0de3e03d44a 100644
--- a/source/blender/freestyle/intern/stroke/Stroke.cpp
+++ b/source/blender/freestyle/intern/stroke/Stroke.cpp
@@ -383,10 +383,6 @@ StrokeVertex::StrokeVertex(SVertex *iSVertex, const StrokeAttribute &iAttribute)
_StrokeLength = 0.0f;
}
-StrokeVertex::~StrokeVertex()
-{
-}
-
StrokeVertex &StrokeVertex::operator=(const StrokeVertex &iBrother)
{
((CurvePoint *)this)->operator=(iBrother);
diff --git a/source/blender/freestyle/intern/stroke/Stroke.h b/source/blender/freestyle/intern/stroke/Stroke.h
index 5772b80b093..4a9ed7288c5 100644
--- a/source/blender/freestyle/intern/stroke/Stroke.h
+++ b/source/blender/freestyle/intern/stroke/Stroke.h
@@ -355,9 +355,6 @@ class StrokeVertex : public CurvePoint {
/*! Builds a stroke from a view vertex and an attribute */
StrokeVertex(SVertex *iSVertex, const StrokeAttribute &iAttribute);
- /*! destructor */
- virtual ~StrokeVertex();
-
/* operators */
/*! operator = */
StrokeVertex &operator=(const StrokeVertex &iBrother);
diff --git a/source/blender/freestyle/intern/stroke/StrokeRenderer.cpp b/source/blender/freestyle/intern/stroke/StrokeRenderer.cpp
index a4268e43a56..797fcc1aabc 100644
--- a/source/blender/freestyle/intern/stroke/StrokeRenderer.cpp
+++ b/source/blender/freestyle/intern/stroke/StrokeRenderer.cpp
@@ -37,13 +37,7 @@ namespace Freestyle {
TextureManager *StrokeRenderer::_textureManager = nullptr;
-StrokeRenderer::StrokeRenderer()
-{
-}
-
-StrokeRenderer::~StrokeRenderer()
-{
-}
+StrokeRenderer::~StrokeRenderer() = default;
bool StrokeRenderer::loadTextures()
{
diff --git a/source/blender/freestyle/intern/stroke/StrokeRenderer.h b/source/blender/freestyle/intern/stroke/StrokeRenderer.h
index 2fb08b880d9..d3ed8bde8a3 100644
--- a/source/blender/freestyle/intern/stroke/StrokeRenderer.h
+++ b/source/blender/freestyle/intern/stroke/StrokeRenderer.h
@@ -121,7 +121,6 @@ class TextureManager {
* first rendering */
class StrokeRenderer {
public:
- StrokeRenderer();
virtual ~StrokeRenderer();
/*! Renders a stroke rep */
diff --git a/source/blender/freestyle/intern/system/PseudoNoise.cpp b/source/blender/freestyle/intern/system/PseudoNoise.cpp
index c05c269c404..7f568fcf460 100644
--- a/source/blender/freestyle/intern/system/PseudoNoise.cpp
+++ b/source/blender/freestyle/intern/system/PseudoNoise.cpp
@@ -41,10 +41,6 @@ namespace Freestyle {
real PseudoNoise::_values[];
-PseudoNoise::PseudoNoise()
-{
-}
-
void PseudoNoise::init(long seed)
{
RandGen::srand48(seed);
diff --git a/source/blender/freestyle/intern/system/PseudoNoise.h b/source/blender/freestyle/intern/system/PseudoNoise.h
index 38270016675..3be74bf0110 100644
--- a/source/blender/freestyle/intern/system/PseudoNoise.h
+++ b/source/blender/freestyle/intern/system/PseudoNoise.h
@@ -31,8 +31,6 @@ namespace Freestyle {
class PseudoNoise {
public:
- PseudoNoise();
-
virtual ~PseudoNoise()
{
}
diff --git a/source/blender/freestyle/intern/view_map/ArbitraryGridDensityProvider.cpp b/source/blender/freestyle/intern/view_map/ArbitraryGridDensityProvider.cpp
index 6c5cbc71a76..923748e6d7d 100644
--- a/source/blender/freestyle/intern/view_map/ArbitraryGridDensityProvider.cpp
+++ b/source/blender/freestyle/intern/view_map/ArbitraryGridDensityProvider.cpp
@@ -55,10 +55,6 @@ ArbitraryGridDensityProvider::ArbitraryGridDensityProvider(OccluderSource &sourc
initialize(proscenium);
}
-ArbitraryGridDensityProvider::~ArbitraryGridDensityProvider()
-{
-}
-
void ArbitraryGridDensityProvider::initialize(const real proscenium[4])
{
float prosceniumWidth = (proscenium[1] - proscenium[0]);
@@ -99,10 +95,6 @@ ArbitraryGridDensityProviderFactory::ArbitraryGridDensityProviderFactory(unsigne
{
}
-ArbitraryGridDensityProviderFactory::~ArbitraryGridDensityProviderFactory()
-{
-}
-
AutoPtr<GridDensityProvider> ArbitraryGridDensityProviderFactory::newGridDensityProvider(
OccluderSource &source, const real proscenium[4])
{
diff --git a/source/blender/freestyle/intern/view_map/ArbitraryGridDensityProvider.h b/source/blender/freestyle/intern/view_map/ArbitraryGridDensityProvider.h
index c2d843742df..ce23d23d7ed 100644
--- a/source/blender/freestyle/intern/view_map/ArbitraryGridDensityProvider.h
+++ b/source/blender/freestyle/intern/view_map/ArbitraryGridDensityProvider.h
@@ -39,7 +39,6 @@ class ArbitraryGridDensityProvider : public GridDensityProvider {
const GridHelpers::Transform &transform,
unsigned numCells);
ArbitraryGridDensityProvider(OccluderSource &source, unsigned numCells);
- virtual ~ArbitraryGridDensityProvider();
protected:
unsigned numCells;
@@ -51,7 +50,6 @@ class ArbitraryGridDensityProvider : public GridDensityProvider {
class ArbitraryGridDensityProviderFactory : public GridDensityProviderFactory {
public:
ArbitraryGridDensityProviderFactory(unsigned numCells);
- ~ArbitraryGridDensityProviderFactory();
AutoPtr<GridDensityProvider> newGridDensityProvider(OccluderSource &source,
const real proscenium[4]);
diff --git a/source/blender/freestyle/intern/view_map/AverageAreaGridDensityProvider.cpp b/source/blender/freestyle/intern/view_map/AverageAreaGridDensityProvider.cpp
index 1384dc0f78b..9fa8f9dcbc2 100644
--- a/source/blender/freestyle/intern/view_map/AverageAreaGridDensityProvider.cpp
+++ b/source/blender/freestyle/intern/view_map/AverageAreaGridDensityProvider.cpp
@@ -56,10 +56,6 @@ AverageAreaGridDensityProvider::AverageAreaGridDensityProvider(OccluderSource &s
initialize(proscenium, sizeFactor);
}
-AverageAreaGridDensityProvider::~AverageAreaGridDensityProvider()
-{
-}
-
void AverageAreaGridDensityProvider::initialize(const real proscenium[4], real sizeFactor)
{
float prosceniumWidth = (proscenium[1] - proscenium[0]);
@@ -120,10 +116,6 @@ AverageAreaGridDensityProviderFactory::AverageAreaGridDensityProviderFactory(rea
{
}
-AverageAreaGridDensityProviderFactory::~AverageAreaGridDensityProviderFactory()
-{
-}
-
AutoPtr<GridDensityProvider> AverageAreaGridDensityProviderFactory::newGridDensityProvider(
OccluderSource &source, const real proscenium[4])
{
diff --git a/source/blender/freestyle/intern/view_map/AverageAreaGridDensityProvider.h b/source/blender/freestyle/intern/view_map/AverageAreaGridDensityProvider.h
index 5336cc1ff97..402fc6f210d 100644
--- a/source/blender/freestyle/intern/view_map/AverageAreaGridDensityProvider.h
+++ b/source/blender/freestyle/intern/view_map/AverageAreaGridDensityProvider.h
@@ -39,7 +39,6 @@ class AverageAreaGridDensityProvider : public GridDensityProvider {
const GridHelpers::Transform &transform,
real sizeFactor);
AverageAreaGridDensityProvider(OccluderSource &source, real sizeFactor);
- virtual ~AverageAreaGridDensityProvider();
private:
void initialize(const real proscenium[4], real sizeFactor);
@@ -48,7 +47,6 @@ class AverageAreaGridDensityProvider : public GridDensityProvider {
class AverageAreaGridDensityProviderFactory : public GridDensityProviderFactory {
public:
AverageAreaGridDensityProviderFactory(real sizeFactor);
- ~AverageAreaGridDensityProviderFactory();
AutoPtr<GridDensityProvider> newGridDensityProvider(OccluderSource &source,
const real proscenium[4]);
diff --git a/source/blender/freestyle/intern/view_map/BoxGrid.cpp b/source/blender/freestyle/intern/view_map/BoxGrid.cpp
index e81291c94bc..ffc0d41bfba 100644
--- a/source/blender/freestyle/intern/view_map/BoxGrid.cpp
+++ b/source/blender/freestyle/intern/view_map/BoxGrid.cpp
@@ -38,14 +38,6 @@ namespace Freestyle {
// Cell
/////////
-BoxGrid::Cell::Cell()
-{
-}
-
-BoxGrid::Cell::~Cell()
-{
-}
-
void BoxGrid::Cell::setDimensions(real x, real y, real sizeX, real sizeY)
{
const real epsilon = 1.0e-06;
@@ -87,10 +79,6 @@ BoxGrid::Iterator::Iterator(BoxGrid &grid, Vec3r &center, real /*epsilon*/)
_current = _cell->faces.begin();
}
-BoxGrid::Iterator::~Iterator()
-{
-}
-
// BoxGrid
/////////////////
@@ -124,9 +112,7 @@ BoxGrid::BoxGrid(OccluderSource &source,
}
}
-BoxGrid::~BoxGrid()
-{
-}
+BoxGrid::~BoxGrid() = default;
void BoxGrid::assignCells(OccluderSource & /*source*/,
GridDensityProvider &density,
@@ -242,10 +228,6 @@ bool BoxGrid::enableQI() const
return _enableQI;
}
-BoxGrid::Transform::Transform()
-{
-}
-
Vec3r BoxGrid::Transform::operator()(const Vec3r &point) const
{
return Vec3r(point[0], point[1], -point[2]);
diff --git a/source/blender/freestyle/intern/view_map/BoxGrid.h b/source/blender/freestyle/intern/view_map/BoxGrid.h
index 581ee0a2340..23085d37a3f 100644
--- a/source/blender/freestyle/intern/view_map/BoxGrid.h
+++ b/source/blender/freestyle/intern/view_map/BoxGrid.h
@@ -73,8 +73,7 @@ class BoxGrid {
// Cell(const Cell& other);
// Cell& operator=(const Cell& other);
- explicit Cell();
- ~Cell();
+ explicit Cell() = default;
static bool compareOccludersByShallowestPoint(const OccluderData *a, const OccluderData *b);
@@ -105,7 +104,6 @@ class BoxGrid {
// epsilon is not used in this class, but other grids with the same interface may need an
// epsilon
explicit Iterator(BoxGrid &grid, Vec3r &center, real epsilon = 1.0e-06);
- ~Iterator();
void initBeforeTarget();
void initAfterTarget();
void nextOccluder();
@@ -134,7 +132,7 @@ class BoxGrid {
class Transform : public GridHelpers::Transform {
public:
- explicit Transform();
+ explicit Transform() = default;
explicit Transform(Transform &other);
Vec3r operator()(const Vec3r &point) const;
};
diff --git a/source/blender/freestyle/intern/view_map/CulledOccluderSource.cpp b/source/blender/freestyle/intern/view_map/CulledOccluderSource.cpp
index a6781bfc8d1..61de0078a8f 100644
--- a/source/blender/freestyle/intern/view_map/CulledOccluderSource.cpp
+++ b/source/blender/freestyle/intern/view_map/CulledOccluderSource.cpp
@@ -44,10 +44,6 @@ CulledOccluderSource::CulledOccluderSource(const GridHelpers::Transform &t,
}
}
-CulledOccluderSource::~CulledOccluderSource()
-{
-}
-
bool CulledOccluderSource::testCurrent()
{
if (valid) {
diff --git a/source/blender/freestyle/intern/view_map/CulledOccluderSource.h b/source/blender/freestyle/intern/view_map/CulledOccluderSource.h
index 2bb77bad0f7..16f57f6dc2a 100644
--- a/source/blender/freestyle/intern/view_map/CulledOccluderSource.h
+++ b/source/blender/freestyle/intern/view_map/CulledOccluderSource.h
@@ -36,7 +36,6 @@ class CulledOccluderSource : public OccluderSource {
WingedEdge &we,
ViewMap &viewMap,
bool extensiveFEdgeSearch = true);
- virtual ~CulledOccluderSource();
void cullViewEdges(ViewMap &viewMap, bool extensiveFEdgeSearch);
diff --git a/source/blender/freestyle/intern/view_map/HeuristicGridDensityProviderFactory.cpp b/source/blender/freestyle/intern/view_map/HeuristicGridDensityProviderFactory.cpp
index 26a40ee587c..683482d6848 100644
--- a/source/blender/freestyle/intern/view_map/HeuristicGridDensityProviderFactory.cpp
+++ b/source/blender/freestyle/intern/view_map/HeuristicGridDensityProviderFactory.cpp
@@ -29,10 +29,6 @@ HeuristicGridDensityProviderFactory::HeuristicGridDensityProviderFactory(real si
{
}
-HeuristicGridDensityProviderFactory::~HeuristicGridDensityProviderFactory()
-{
-}
-
AutoPtr<GridDensityProvider> HeuristicGridDensityProviderFactory::newGridDensityProvider(
OccluderSource &source, const real proscenium[4])
{
diff --git a/source/blender/freestyle/intern/view_map/HeuristicGridDensityProviderFactory.h b/source/blender/freestyle/intern/view_map/HeuristicGridDensityProviderFactory.h
index 0ce62572092..df0f4bd3547 100644
--- a/source/blender/freestyle/intern/view_map/HeuristicGridDensityProviderFactory.h
+++ b/source/blender/freestyle/intern/view_map/HeuristicGridDensityProviderFactory.h
@@ -32,7 +32,6 @@ namespace Freestyle {
class HeuristicGridDensityProviderFactory : public GridDensityProviderFactory {
public:
HeuristicGridDensityProviderFactory(real sizeFactor, unsigned numFaces);
- ~HeuristicGridDensityProviderFactory();
AutoPtr<GridDensityProvider> newGridDensityProvider(OccluderSource &source,
const real proscenium[4]);
diff --git a/source/blender/freestyle/intern/view_map/OccluderSource.cpp b/source/blender/freestyle/intern/view_map/OccluderSource.cpp
index 618fa781fa7..57e6e39b809 100644
--- a/source/blender/freestyle/intern/view_map/OccluderSource.cpp
+++ b/source/blender/freestyle/intern/view_map/OccluderSource.cpp
@@ -33,9 +33,7 @@ OccluderSource::OccluderSource(const GridHelpers::Transform &t, WingedEdge &we)
begin();
}
-OccluderSource::~OccluderSource()
-{
-}
+OccluderSource::~OccluderSource() = default;
void OccluderSource::buildCachedPolygon()
{
diff --git a/source/blender/freestyle/intern/view_map/Pow23GridDensityProvider.cpp b/source/blender/freestyle/intern/view_map/Pow23GridDensityProvider.cpp
index 7e7f4e14882..b9bf585a9e7 100644
--- a/source/blender/freestyle/intern/view_map/Pow23GridDensityProvider.cpp
+++ b/source/blender/freestyle/intern/view_map/Pow23GridDensityProvider.cpp
@@ -54,10 +54,6 @@ Pow23GridDensityProvider::Pow23GridDensityProvider(OccluderSource &source, unsig
initialize(proscenium);
}
-Pow23GridDensityProvider::~Pow23GridDensityProvider()
-{
-}
-
void Pow23GridDensityProvider::initialize(const real proscenium[4])
{
float prosceniumWidth = (proscenium[1] - proscenium[0]);
@@ -98,10 +94,6 @@ Pow23GridDensityProviderFactory::Pow23GridDensityProviderFactory(unsigned numFac
{
}
-Pow23GridDensityProviderFactory::~Pow23GridDensityProviderFactory()
-{
-}
-
AutoPtr<GridDensityProvider> Pow23GridDensityProviderFactory::newGridDensityProvider(
OccluderSource &source, const real proscenium[4])
{
diff --git a/source/blender/freestyle/intern/view_map/Pow23GridDensityProvider.h b/source/blender/freestyle/intern/view_map/Pow23GridDensityProvider.h
index fec869e0665..d150786b1c7 100644
--- a/source/blender/freestyle/intern/view_map/Pow23GridDensityProvider.h
+++ b/source/blender/freestyle/intern/view_map/Pow23GridDensityProvider.h
@@ -37,7 +37,6 @@ class Pow23GridDensityProvider : public GridDensityProvider {
const GridHelpers::Transform &transform,
unsigned numFaces);
Pow23GridDensityProvider(OccluderSource &source, unsigned numFaces);
- virtual ~Pow23GridDensityProvider();
protected:
unsigned numFaces;
@@ -49,7 +48,6 @@ class Pow23GridDensityProvider : public GridDensityProvider {
class Pow23GridDensityProviderFactory : public GridDensityProviderFactory {
public:
Pow23GridDensityProviderFactory(unsigned numFaces);
- ~Pow23GridDensityProviderFactory();
AutoPtr<GridDensityProvider> newGridDensityProvider(OccluderSource &source,
const real proscenium[4]);
diff --git a/source/blender/freestyle/intern/view_map/SphericalGrid.cpp b/source/blender/freestyle/intern/view_map/SphericalGrid.cpp
index db567d6b5ef..f6c57588e35 100644
--- a/source/blender/freestyle/intern/view_map/SphericalGrid.cpp
+++ b/source/blender/freestyle/intern/view_map/SphericalGrid.cpp
@@ -38,14 +38,6 @@ namespace Freestyle {
// Cell
/////////
-SphericalGrid::Cell::Cell()
-{
-}
-
-SphericalGrid::Cell::~Cell()
-{
-}
-
void SphericalGrid::Cell::setDimensions(real x, real y, real sizeX, real sizeY)
{
const real epsilon = 1.0e-06;
@@ -87,10 +79,6 @@ SphericalGrid::Iterator::Iterator(SphericalGrid &grid, Vec3r &center, real /*eps
_current = _cell->faces.begin();
}
-SphericalGrid::Iterator::~Iterator()
-{
-}
-
// SphericalGrid
/////////////////
@@ -121,9 +109,7 @@ SphericalGrid::SphericalGrid(OccluderSource &source,
}
}
-SphericalGrid::~SphericalGrid()
-{
-}
+SphericalGrid::~SphericalGrid() = default;
void SphericalGrid::assignCells(OccluderSource & /*source*/,
GridDensityProvider &density,
@@ -238,10 +224,6 @@ bool SphericalGrid::enableQI() const
return _enableQI;
}
-SphericalGrid::Transform::Transform()
-{
-}
-
Vec3r SphericalGrid::Transform::operator()(const Vec3r &point) const
{
return sphericalProjection(point);
diff --git a/source/blender/freestyle/intern/view_map/SphericalGrid.h b/source/blender/freestyle/intern/view_map/SphericalGrid.h
index 0ef68d073ae..efa3530cb2a 100644
--- a/source/blender/freestyle/intern/view_map/SphericalGrid.h
+++ b/source/blender/freestyle/intern/view_map/SphericalGrid.h
@@ -73,8 +73,7 @@ class SphericalGrid {
// Cell(const Cell& other);
// Cell& operator=(const Cell& other);
- explicit Cell();
- ~Cell();
+ explicit Cell() = default;
static bool compareOccludersByShallowestPoint(const OccluderData *a, const OccluderData *b);
@@ -106,7 +105,6 @@ class SphericalGrid {
// epsilon is not used in this class, but other grids with the same interface may need an
// epsilon
explicit Iterator(SphericalGrid &grid, Vec3r &center, real epsilon = 1.0e-06);
- ~Iterator();
void initBeforeTarget();
void initAfterTarget();
void nextOccluder();
@@ -135,7 +133,7 @@ class SphericalGrid {
class Transform : public GridHelpers::Transform {
public:
- explicit Transform();
+ explicit Transform() = default;
explicit Transform(Transform &other);
Vec3r operator()(const Vec3r &point) const;
static Vec3r sphericalProjection(const Vec3r &M);
diff --git a/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp b/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp
index cbb5c730b2b..cd0059f3c21 100644
--- a/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp
+++ b/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp
@@ -2322,10 +2322,6 @@ struct less_Intersection {
};
struct silhouette_binary_rule : public binary_rule<segment, segment> {
- silhouette_binary_rule()
- {
- }
-
bool operator()(segment &s1, segment &s2) override
{
FEdge *f1 = s1.edge();
diff --git a/source/blender/functions/FN_cpp_type.hh b/source/blender/functions/FN_cpp_type.hh
index faf444e91f6..14eab2704e9 100644
--- a/source/blender/functions/FN_cpp_type.hh
+++ b/source/blender/functions/FN_cpp_type.hh
@@ -260,6 +260,11 @@ class CPPType : NonCopyable, NonMovable {
return !(&a == &b);
}
+ /**
+ * Get the `CPPType` that corresponds to a specific static type.
+ * This only works for types that actually implement the template specialization using
+ * `MAKE_CPP_TYPE`.
+ */
template<typename T> static const CPPType &get();
/**
@@ -666,7 +671,7 @@ class CPPType : NonCopyable, NonMovable {
template<typename T> bool is() const
{
- return this == &CPPType::get<T>();
+ return this == &CPPType::get<std::decay_t<T>>();
}
};
@@ -674,6 +679,6 @@ class CPPType : NonCopyable, NonMovable {
/* Utility for allocating an uninitialized buffer for a single value of the given #CPPType. */
#define BUFFER_FOR_CPP_TYPE_VALUE(type, variable_name) \
- blender::DynamicStackBuffer<64, 64> stack_buffer_for_##variable_name(type.size(), \
- type.alignment()); \
+ blender::DynamicStackBuffer<64, 64> stack_buffer_for_##variable_name((type).size(), \
+ (type).alignment()); \
void *variable_name = stack_buffer_for_##variable_name.buffer();
diff --git a/source/blender/functions/FN_generic_pointer.hh b/source/blender/functions/FN_generic_pointer.hh
index 2bd66daa7fe..f88ff09f916 100644
--- a/source/blender/functions/FN_generic_pointer.hh
+++ b/source/blender/functions/FN_generic_pointer.hh
@@ -66,6 +66,16 @@ class GMutablePointer {
return type_ != nullptr && type_->is<T>();
}
+ template<typename T> T relocate_out()
+ {
+ BLI_assert(this->is_type<T>());
+ T value;
+ type_->relocate_to_initialized(data_, &value);
+ data_ = nullptr;
+ type_ = nullptr;
+ return value;
+ }
+
void destruct()
{
BLI_assert(data_ != nullptr);
diff --git a/source/blender/functions/FN_generic_span.hh b/source/blender/functions/FN_generic_span.hh
index 31b67dd3d70..e2c49697ba9 100644
--- a/source/blender/functions/FN_generic_span.hh
+++ b/source/blender/functions/FN_generic_span.hh
@@ -30,7 +30,7 @@ namespace blender::fn {
* A generic span. It behaves just like a blender::Span<T>, but the type is only known at run-time.
*/
class GSpan {
- private:
+ protected:
const CPPType *type_;
const void *data_;
int64_t size_;
@@ -85,6 +85,14 @@ class GSpan {
BLI_assert(type_->is<T>());
return Span<T>(static_cast<const T *>(data_), size_);
}
+
+ GSpan slice(const int64_t start, int64_t size) const
+ {
+ BLI_assert(start >= 0);
+ BLI_assert(size >= 0);
+ const int64_t new_size = std::max<int64_t>(0, std::min(size, size_ - start));
+ return GSpan(*type_, POINTER_OFFSET(data_, type_->size() * start), new_size);
+ }
};
/**
@@ -92,7 +100,7 @@ class GSpan {
* known at run-time.
*/
class GMutableSpan {
- private:
+ protected:
const CPPType *type_;
void *data_;
int64_t size_;
@@ -153,6 +161,14 @@ class GMutableSpan {
BLI_assert(type_->is<T>());
return MutableSpan<T>(static_cast<T *>(data_), size_);
}
+
+ GMutableSpan slice(const int64_t start, int64_t size) const
+ {
+ BLI_assert(start >= 0);
+ BLI_assert(size >= 0);
+ const int64_t new_size = std::max<int64_t>(0, std::min(size, size_ - start));
+ return GMutableSpan(*type_, POINTER_OFFSET(data_, type_->size() * start), new_size);
+ }
};
} // namespace blender::fn
diff --git a/source/blender/functions/FN_generic_value_map.hh b/source/blender/functions/FN_generic_value_map.hh
index 68cb945f1af..4e7fe298874 100644
--- a/source/blender/functions/FN_generic_value_map.hh
+++ b/source/blender/functions/FN_generic_value_map.hh
@@ -93,6 +93,11 @@ template<typename Key> class GValueMap {
return values_.pop_as(key);
}
+ template<typename ForwardKey> GPointer lookup(const ForwardKey &key) const
+ {
+ return values_.lookup_as(key);
+ }
+
/* Remove the value for the given name from the container and remove it. */
template<typename T, typename ForwardKey> T extract(const ForwardKey &key)
{
diff --git a/source/blender/functions/FN_generic_vector_array.hh b/source/blender/functions/FN_generic_vector_array.hh
index ae6eb8a614f..b02ed471875 100644
--- a/source/blender/functions/FN_generic_vector_array.hh
+++ b/source/blender/functions/FN_generic_vector_array.hh
@@ -123,7 +123,7 @@ template<typename T> class GVectorArray_TypedMutableRef {
void extend(const int64_t index, const VArray<T> &values)
{
- GVArrayForVArray<T> array{values};
+ GVArray_For_VArray<T> array{values};
this->extend(index, array);
}
@@ -134,12 +134,12 @@ template<typename T> class GVectorArray_TypedMutableRef {
};
/* A generic virtual vector array implementation for a `GVectorArray`. */
-class GVVectorArrayForGVectorArray : public GVVectorArray {
+class GVVectorArray_For_GVectorArray : public GVVectorArray {
private:
const GVectorArray &vector_array_;
public:
- GVVectorArrayForGVectorArray(const GVectorArray &vector_array)
+ GVVectorArray_For_GVectorArray(const GVectorArray &vector_array)
: GVVectorArray(vector_array.type(), vector_array.size()), vector_array_(vector_array)
{
}
diff --git a/source/blender/functions/FN_generic_virtual_array.hh b/source/blender/functions/FN_generic_virtual_array.hh
index c6230730a8d..d530d10b3c8 100644
--- a/source/blender/functions/FN_generic_virtual_array.hh
+++ b/source/blender/functions/FN_generic_virtual_array.hh
@@ -23,12 +23,23 @@
* the data type is only known at runtime.
*/
+#include <optional>
+
#include "BLI_virtual_array.hh"
#include "FN_generic_span.hh"
namespace blender::fn {
+template<typename T> class GVArray_Typed;
+template<typename T> class GVMutableArray_Typed;
+
+class GVArray;
+class GVMutableArray;
+
+using GVArrayPtr = std::unique_ptr<GVArray>;
+using GVMutableArrayPtr = std::unique_ptr<GVMutableArray>;
+
/* A generically typed version of `VArray<T>`. */
class GVArray {
protected:
@@ -86,13 +97,13 @@ class GVArray {
/* Returns the internally used span of the virtual array. This invokes undefined behavior is the
* virtual array is not stored as a span internally. */
- GSpan get_span() const
+ GSpan get_internal_span() const
{
BLI_assert(this->is_span());
if (size_ == 0) {
return GSpan(*type_);
}
- return this->get_span_impl();
+ return this->get_internal_span_impl();
}
/* Returns true when the virtual array returns the same value for every index. */
@@ -107,57 +118,151 @@ class GVArray {
/* Copies the value that is used for every element into `r_value`, which is expected to point to
* initialized memory. This invokes undefined behavior if the virtual array would not return the
* same value for every index. */
- void get_single(void *r_value) const
+ void get_internal_single(void *r_value) const
{
BLI_assert(this->is_single());
if (size_ == 1) {
this->get(0, r_value);
+ return;
}
- this->get_single_impl(r_value);
+ this->get_internal_single_impl(r_value);
}
- /* Same as `get_single`, but `r_value` points to initialized memory. */
+ /* Same as `get_internal_single`, but `r_value` points to initialized memory. */
void get_single_to_uninitialized(void *r_value) const
{
type_->construct_default(r_value);
- this->get_single(r_value);
+ this->get_internal_single(r_value);
}
+ void materialize(void *dst) const;
+ void materialize(const IndexMask mask, void *dst) const;
+
+ void materialize_to_uninitialized(void *dst) const;
void materialize_to_uninitialized(const IndexMask mask, void *dst) const;
+ template<typename T> const VArray<T> *try_get_internal_varray() const
+ {
+ BLI_assert(type_->is<T>());
+ return (const VArray<T> *)this->try_get_internal_varray_impl();
+ }
+
+ /* Create a typed virtual array for this generic virtual array. */
+ template<typename T> GVArray_Typed<T> typed() const
+ {
+ return GVArray_Typed<T>(*this);
+ }
+
+ GVArrayPtr shallow_copy() const;
+
protected:
virtual void get_impl(const int64_t index, void *r_value) const;
virtual void get_to_uninitialized_impl(const int64_t index, void *r_value) const = 0;
virtual bool is_span_impl() const;
- virtual GSpan get_span_impl() const;
+ virtual GSpan get_internal_span_impl() const;
virtual bool is_single_impl() const;
- virtual void get_single_impl(void *UNUSED(r_value)) const;
+ virtual void get_internal_single_impl(void *UNUSED(r_value)) const;
+
+ virtual void materialize_impl(const IndexMask mask, void *dst) const;
+ virtual void materialize_to_uninitialized_impl(const IndexMask mask, void *dst) const;
+
+ virtual const void *try_get_internal_varray_impl() const;
+};
+
+/* Similar to GVArray, but supports changing the elements in the virtual array. */
+class GVMutableArray : public GVArray {
+ public:
+ GVMutableArray(const CPPType &type, const int64_t size) : GVArray(type, size)
+ {
+ }
+
+ void set_by_copy(const int64_t index, const void *value)
+ {
+ BLI_assert(index >= 0);
+ BLI_assert(index < size_);
+ this->set_by_copy_impl(index, value);
+ }
+
+ void set_by_move(const int64_t index, void *value)
+ {
+ BLI_assert(index >= 0);
+ BLI_assert(index < size_);
+ this->set_by_move_impl(index, value);
+ }
+
+ void set_by_relocate(const int64_t index, void *value)
+ {
+ BLI_assert(index >= 0);
+ BLI_assert(index < size_);
+ this->set_by_relocate_impl(index, value);
+ }
+
+ GMutableSpan get_internal_span()
+ {
+ BLI_assert(this->is_span());
+ GSpan span = static_cast<const GVArray *>(this)->get_internal_span();
+ return GMutableSpan(span.type(), const_cast<void *>(span.data()), span.size());
+ }
+
+ template<typename T> VMutableArray<T> *try_get_internal_mutable_varray()
+ {
+ BLI_assert(type_->is<T>());
+ return (VMutableArray<T> *)this->try_get_internal_mutable_varray_impl();
+ }
+
+ /* Create a typed virtual array for this generic virtual array. */
+ template<typename T> GVMutableArray_Typed<T> typed()
+ {
+ return GVMutableArray_Typed<T>(*this);
+ }
+
+ void fill(const void *value);
+
+ /* Copy the values from the source buffer to all elements in the virtual array. */
+ void set_all(const void *src)
+ {
+ this->set_all_impl(src);
+ }
+
+ protected:
+ virtual void set_by_copy_impl(const int64_t index, const void *value);
+ virtual void set_by_relocate_impl(const int64_t index, void *value);
+ virtual void set_by_move_impl(const int64_t index, void *value) = 0;
+
+ virtual void set_all_impl(const void *src);
+
+ virtual void *try_get_internal_mutable_varray_impl();
};
-class GVArrayForGSpan : public GVArray {
+class GVArray_For_GSpan : public GVArray {
protected:
- const void *data_;
+ const void *data_ = nullptr;
const int64_t element_size_;
public:
- GVArrayForGSpan(const GSpan span)
+ GVArray_For_GSpan(const GSpan span)
: GVArray(span.type(), span.size()), data_(span.data()), element_size_(span.type().size())
{
}
protected:
+ GVArray_For_GSpan(const CPPType &type, const int64_t size)
+ : GVArray(type, size), element_size_(type.size())
+ {
+ }
+
void get_impl(const int64_t index, void *r_value) const override;
void get_to_uninitialized_impl(const int64_t index, void *r_value) const override;
bool is_span_impl() const override;
- GSpan get_span_impl() const override;
+ GSpan get_internal_span_impl() const override;
};
-class GVArrayForEmpty : public GVArray {
+class GVArray_For_Empty : public GVArray {
public:
- GVArrayForEmpty(const CPPType &type) : GVArray(type, 0)
+ GVArray_For_Empty(const CPPType &type) : GVArray(type, 0)
{
}
@@ -168,108 +273,642 @@ class GVArrayForEmpty : public GVArray {
}
};
-class GVArrayForSingleValueRef : public GVArray {
- private:
- const void *value_;
+class GVMutableArray_For_GMutableSpan : public GVMutableArray {
+ protected:
+ void *data_ = nullptr;
+ const int64_t element_size_;
public:
- GVArrayForSingleValueRef(const CPPType &type, const int64_t size, const void *value)
+ GVMutableArray_For_GMutableSpan(const GMutableSpan span)
+ : GVMutableArray(span.type(), span.size()),
+ data_(span.data()),
+ element_size_(span.type().size())
+ {
+ }
+
+ protected:
+ GVMutableArray_For_GMutableSpan(const CPPType &type, const int64_t size)
+ : GVMutableArray(type, size), element_size_(type.size())
+ {
+ }
+
+ void get_impl(const int64_t index, void *r_value) const override;
+ void get_to_uninitialized_impl(const int64_t index, void *r_value) const override;
+
+ void set_by_copy_impl(const int64_t index, const void *value) override;
+ void set_by_move_impl(const int64_t index, void *value) override;
+ void set_by_relocate_impl(const int64_t index, void *value) override;
+
+ bool is_span_impl() const override;
+ GSpan get_internal_span_impl() const override;
+};
+
+/* Generic virtual array where each element has the same value. The value is not owned. */
+class GVArray_For_SingleValueRef : public GVArray {
+ protected:
+ const void *value_ = nullptr;
+
+ public:
+ GVArray_For_SingleValueRef(const CPPType &type, const int64_t size, const void *value)
: GVArray(type, size), value_(value)
{
}
protected:
+ GVArray_For_SingleValueRef(const CPPType &type, const int64_t size) : GVArray(type, size)
+ {
+ }
+
void get_impl(const int64_t index, void *r_value) const override;
void get_to_uninitialized_impl(const int64_t index, void *r_value) const override;
bool is_span_impl() const override;
- GSpan get_span_impl() const override;
+ GSpan get_internal_span_impl() const override;
bool is_single_impl() const override;
- void get_single_impl(void *r_value) const override;
+ void get_internal_single_impl(void *r_value) const override;
};
-template<typename T> class GVArrayForVArray : public GVArray {
- private:
- const VArray<T> &array_;
+/* Same as GVArray_For_SingleValueRef, but the value is owned. */
+class GVArray_For_SingleValue : public GVArray_For_SingleValueRef {
+ public:
+ GVArray_For_SingleValue(const CPPType &type, const int64_t size, const void *value);
+ ~GVArray_For_SingleValue();
+};
+
+/* Used to convert a typed virtual array into a generic one. */
+template<typename T> class GVArray_For_VArray : public GVArray {
+ protected:
+ const VArray<T> *varray_ = nullptr;
public:
- GVArrayForVArray(const VArray<T> &array)
- : GVArray(CPPType::get<T>(), array.size()), array_(array)
+ GVArray_For_VArray(const VArray<T> &varray)
+ : GVArray(CPPType::get<T>(), varray.size()), varray_(&varray)
{
}
protected:
+ GVArray_For_VArray(const int64_t size) : GVArray(CPPType::get<T>(), size)
+ {
+ }
+
void get_impl(const int64_t index, void *r_value) const override
{
- *(T *)r_value = array_.get(index);
+ *(T *)r_value = varray_->get(index);
}
void get_to_uninitialized_impl(const int64_t index, void *r_value) const override
{
- new (r_value) T(array_.get(index));
+ new (r_value) T(varray_->get(index));
}
bool is_span_impl() const override
{
- return array_.is_span();
+ return varray_->is_span();
}
- GSpan get_span_impl() const override
+ GSpan get_internal_span_impl() const override
{
- return GSpan(array_.get_span());
+ return GSpan(varray_->get_internal_span());
}
bool is_single_impl() const override
{
- return array_.is_single();
+ return varray_->is_single();
}
- void get_single_impl(void *r_value) const override
+ void get_internal_single_impl(void *r_value) const override
{
- *(T *)r_value = array_.get_single();
+ *(T *)r_value = varray_->get_internal_single();
+ }
+
+ void materialize_impl(const IndexMask mask, void *dst) const override
+ {
+ varray_->materialize(mask, MutableSpan((T *)dst, mask.min_array_size()));
+ }
+
+ void materialize_to_uninitialized_impl(const IndexMask mask, void *dst) const override
+ {
+ varray_->materialize_to_uninitialized(mask, MutableSpan((T *)dst, mask.min_array_size()));
+ }
+
+ const void *try_get_internal_varray_impl() const override
+ {
+ return varray_;
}
};
-template<typename T> class VArrayForGVArray : public VArray<T> {
- private:
- const GVArray &array_;
+/* Used to convert any generic virtual array into a typed one. */
+template<typename T> class VArray_For_GVArray : public VArray<T> {
+ protected:
+ const GVArray *varray_ = nullptr;
public:
- VArrayForGVArray(const GVArray &array) : VArray<T>(array.size()), array_(array)
+ VArray_For_GVArray(const GVArray &varray) : VArray<T>(varray.size()), varray_(&varray)
{
- BLI_assert(array_.type().template is<T>());
+ BLI_assert(varray_->type().template is<T>());
}
protected:
+ VArray_For_GVArray(const int64_t size) : VArray<T>(size)
+ {
+ }
+
T get_impl(const int64_t index) const override
{
T value;
- array_.get(index, &value);
+ varray_->get(index, &value);
return value;
}
bool is_span_impl() const override
{
- return array_.is_span();
+ return varray_->is_span();
}
- Span<T> get_span_impl() const override
+ Span<T> get_internal_span_impl() const override
{
- return array_.get_span().template typed<T>();
+ return varray_->get_internal_span().template typed<T>();
}
bool is_single_impl() const override
{
- return array_.is_single();
+ return varray_->is_single();
}
- T get_single_impl() const override
+ T get_internal_single_impl() const override
{
T value;
- array_.get_single(&value);
+ varray_->get_internal_single(&value);
return value;
}
};
+/* Used to convert an generic mutable virtual array into a typed one. */
+template<typename T> class VMutableArray_For_GVMutableArray : public VMutableArray<T> {
+ protected:
+ GVMutableArray *varray_ = nullptr;
+
+ public:
+ VMutableArray_For_GVMutableArray(GVMutableArray &varray)
+ : VMutableArray<T>(varray.size()), varray_(&varray)
+ {
+ BLI_assert(varray.type().template is<T>());
+ }
+
+ VMutableArray_For_GVMutableArray(const int64_t size) : VMutableArray<T>(size)
+ {
+ }
+
+ private:
+ T get_impl(const int64_t index) const override
+ {
+ T value;
+ varray_->get(index, &value);
+ return value;
+ }
+
+ void set_impl(const int64_t index, T value) override
+ {
+ varray_->set_by_relocate(index, &value);
+ }
+
+ bool is_span_impl() const override
+ {
+ return varray_->is_span();
+ }
+
+ Span<T> get_internal_span_impl() const override
+ {
+ return varray_->get_internal_span().template typed<T>();
+ }
+
+ bool is_single_impl() const override
+ {
+ return varray_->is_single();
+ }
+
+ T get_internal_single_impl() const override
+ {
+ T value;
+ varray_->get_internal_single(&value);
+ return value;
+ }
+};
+
+/* Used to convert any typed virtual mutable array into a generic one. */
+template<typename T> class GVMutableArray_For_VMutableArray : public GVMutableArray {
+ protected:
+ VMutableArray<T> *varray_ = nullptr;
+
+ public:
+ GVMutableArray_For_VMutableArray(VMutableArray<T> &varray)
+ : GVMutableArray(CPPType::get<T>(), varray.size()), varray_(&varray)
+ {
+ }
+
+ protected:
+ GVMutableArray_For_VMutableArray(const int64_t size) : GVMutableArray(CPPType::get<T>(), size)
+ {
+ }
+
+ void get_impl(const int64_t index, void *r_value) const override
+ {
+ *(T *)r_value = varray_->get(index);
+ }
+
+ void get_to_uninitialized_impl(const int64_t index, void *r_value) const override
+ {
+ new (r_value) T(varray_->get(index));
+ }
+
+ bool is_span_impl() const override
+ {
+ return varray_->is_span();
+ }
+
+ GSpan get_internal_span_impl() const override
+ {
+ Span<T> span = varray_->get_internal_span();
+ return span;
+ }
+
+ bool is_single_impl() const override
+ {
+ return varray_->is_single();
+ }
+
+ void get_internal_single_impl(void *r_value) const override
+ {
+ *(T *)r_value = varray_->get_internal_single();
+ }
+
+ void set_by_copy_impl(const int64_t index, const void *value) override
+ {
+ const T &value_ = *(const T *)value;
+ varray_->set(index, value_);
+ }
+
+ void set_by_relocate_impl(const int64_t index, void *value) override
+ {
+ T &value_ = *(T *)value;
+ varray_->set(index, std::move(value_));
+ value_.~T();
+ }
+
+ void set_by_move_impl(const int64_t index, void *value) override
+ {
+ T &value_ = *(T *)value;
+ varray_->set(index, std::move(value_));
+ }
+
+ void set_all_impl(const void *src) override
+ {
+ varray_->set_all(Span((T *)src, size_));
+ }
+
+ void materialize_impl(const IndexMask mask, void *dst) const override
+ {
+ varray_->materialize(mask, MutableSpan((T *)dst, mask.min_array_size()));
+ }
+
+ void materialize_to_uninitialized_impl(const IndexMask mask, void *dst) const override
+ {
+ varray_->materialize_to_uninitialized(mask, MutableSpan((T *)dst, mask.min_array_size()));
+ }
+
+ const void *try_get_internal_varray_impl() const override
+ {
+ return (const VArray<T> *)varray_;
+ }
+
+ void *try_get_internal_mutable_varray_impl() override
+ {
+ return varray_;
+ }
+};
+
+/* A generic version of VArray_Span. */
+class GVArray_GSpan : public GSpan {
+ private:
+ const GVArray &varray_;
+ void *owned_data_ = nullptr;
+
+ public:
+ GVArray_GSpan(const GVArray &varray);
+ ~GVArray_GSpan();
+};
+
+/* A generic version of VMutableArray_Span. */
+class GVMutableArray_GSpan : public GMutableSpan {
+ private:
+ GVMutableArray &varray_;
+ void *owned_data_ = nullptr;
+ bool save_has_been_called_ = false;
+ bool show_not_saved_warning_ = true;
+
+ public:
+ GVMutableArray_GSpan(GVMutableArray &varray, bool copy_values_to_span = true);
+ ~GVMutableArray_GSpan();
+
+ void save();
+ void disable_not_applied_warning();
+};
+
+/* Similar to GVArray_GSpan, but the resulting span is typed. */
+template<typename T> class GVArray_Span : public Span<T> {
+ private:
+ GVArray_GSpan varray_gspan_;
+
+ public:
+ GVArray_Span(const GVArray &varray) : varray_gspan_(varray)
+ {
+ BLI_assert(varray.type().is<T>());
+ this->data_ = (const T *)varray_gspan_.data();
+ this->size_ = varray_gspan_.size();
+ }
+};
+
+template<typename T> class GVArray_For_OwnedVArray : public GVArray_For_VArray<T> {
+ private:
+ VArrayPtr<T> owned_varray_;
+
+ public:
+ /* Takes ownership of varray and passes a reference to the base class. */
+ GVArray_For_OwnedVArray(VArrayPtr<T> varray)
+ : GVArray_For_VArray<T>(*varray), owned_varray_(std::move(varray))
+ {
+ }
+};
+
+template<typename T> class VArray_For_OwnedGVArray : public VArray_For_GVArray<T> {
+ private:
+ GVArrayPtr owned_varray_;
+
+ public:
+ /* Takes ownership of varray and passes a reference to the base class. */
+ VArray_For_OwnedGVArray(GVArrayPtr varray)
+ : VArray_For_GVArray<T>(*varray), owned_varray_(std::move(varray))
+ {
+ }
+};
+
+template<typename T>
+class GVMutableArray_For_OwnedVMutableArray : public GVMutableArray_For_VMutableArray<T> {
+ private:
+ VMutableArrayPtr<T> owned_varray_;
+
+ public:
+ /* Takes ownership of varray and passes a reference to the base class. */
+ GVMutableArray_For_OwnedVMutableArray(VMutableArrayPtr<T> varray)
+ : GVMutableArray_For_VMutableArray<T>(*varray), owned_varray_(std::move(varray))
+ {
+ }
+};
+
+template<typename T>
+class VMutableArray_For_OwnedGVMutableArray : public VMutableArray_For_GVMutableArray<T> {
+ private:
+ GVMutableArrayPtr owned_varray_;
+
+ public:
+ /* Takes ownership of varray and passes a reference to the base class. */
+ VMutableArray_For_OwnedGVMutableArray(GVMutableArrayPtr varray)
+ : VMutableArray_For_GVMutableArray<T>(*varray), owned_varray_(std::move(varray))
+ {
+ }
+};
+
+/* Utility to embed a typed virtual array into a generic one. This avoids one allocation and give
+ * the compiler more opportunity to optimize the generic virtual array. */
+template<typename T, typename VArrayT>
+class GVArray_For_EmbeddedVArray : public GVArray_For_VArray<T> {
+ private:
+ VArrayT embedded_varray_;
+
+ public:
+ template<typename... Args>
+ GVArray_For_EmbeddedVArray(const int64_t size, Args &&... args)
+ : GVArray_For_VArray<T>(size), embedded_varray_(std::forward<Args>(args)...)
+ {
+ this->varray_ = &embedded_varray_;
+ }
+};
+
+/* Same as GVArray_For_EmbeddedVArray, but for mutable virtual arrays. */
+template<typename T, typename VMutableArrayT>
+class GVMutableArray_For_EmbeddedVMutableArray : public GVMutableArray_For_VMutableArray<T> {
+ private:
+ VMutableArrayT embedded_varray_;
+
+ public:
+ template<typename... Args>
+ GVMutableArray_For_EmbeddedVMutableArray(const int64_t size, Args &&... args)
+ : GVMutableArray_For_VMutableArray<T>(size), embedded_varray_(std::forward<Args>(args)...)
+ {
+ this->varray_ = &embedded_varray_;
+ }
+};
+
+/* Same as VArray_For_ArrayContainer, but for a generic virtual array. */
+template<typename Container, typename T = typename Container::value_type>
+class GVArray_For_ArrayContainer
+ : public GVArray_For_EmbeddedVArray<T, VArray_For_ArrayContainer<Container, T>> {
+ public:
+ GVArray_For_ArrayContainer(Container container)
+ : GVArray_For_EmbeddedVArray<T, VArray_For_ArrayContainer<Container, T>>(
+ container.size(), std::move(container))
+ {
+ }
+};
+
+/* Same as VArray_For_DerivedSpan, but for a generic virtual array. */
+template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)>
+class GVArray_For_DerivedSpan
+ : public GVArray_For_EmbeddedVArray<ElemT, VArray_For_DerivedSpan<StructT, ElemT, GetFunc>> {
+ public:
+ GVArray_For_DerivedSpan(const Span<StructT> data)
+ : GVArray_For_EmbeddedVArray<ElemT, VArray_For_DerivedSpan<StructT, ElemT, GetFunc>>(
+ data.size(), data)
+ {
+ }
+};
+
+/* Same as VMutableArray_For_DerivedSpan, but for a generic virtual array. */
+template<typename StructT,
+ typename ElemT,
+ ElemT (*GetFunc)(const StructT &),
+ void (*SetFunc)(StructT &, ElemT)>
+class GVMutableArray_For_DerivedSpan
+ : public GVMutableArray_For_EmbeddedVMutableArray<
+ ElemT,
+ VMutableArray_For_DerivedSpan<StructT, ElemT, GetFunc, SetFunc>> {
+ public:
+ GVMutableArray_For_DerivedSpan(const MutableSpan<StructT> data)
+ : GVMutableArray_For_EmbeddedVMutableArray<
+ ElemT,
+ VMutableArray_For_DerivedSpan<StructT, ElemT, GetFunc, SetFunc>>(data.size(), data)
+ {
+ }
+};
+
+/* Same as VArray_For_Span, but for a generic virtual array. */
+template<typename T>
+class GVArray_For_Span : public GVArray_For_EmbeddedVArray<T, VArray_For_Span<T>> {
+ public:
+ GVArray_For_Span(const Span<T> data)
+ : GVArray_For_EmbeddedVArray<T, VArray_For_Span<T>>(data.size(), data)
+ {
+ }
+};
+
+/* Same as VMutableArray_For_MutableSpan, but for a generic virtual array. */
+template<typename T>
+class GVMutableArray_For_MutableSpan
+ : public GVMutableArray_For_EmbeddedVMutableArray<T, VMutableArray_For_MutableSpan<T>> {
+ public:
+ GVMutableArray_For_MutableSpan(const MutableSpan<T> data)
+ : GVMutableArray_For_EmbeddedVMutableArray<T, VMutableArray_For_MutableSpan<T>>(data.size(),
+ data)
+ {
+ }
+};
+
+/**
+ * Utility class to create the "best" typed virtual array for a given generic virtual array.
+ * In most cases we don't just want to use VArray_For_GVArray, because it adds an additional
+ * indirection on element-access that can be avoided in many cases (e.g. when the virtual array is
+ * just a span or single value).
+ *
+ * This is not a virtual array itself, but is used to get a virtual array.
+ */
+template<typename T> class GVArray_Typed {
+ private:
+ const VArray<T> *varray_;
+ /* Of these optional virtual arrays, at most one is constructed at any time. */
+ std::optional<VArray_For_Span<T>> varray_span_;
+ std::optional<VArray_For_Single<T>> varray_single_;
+ std::optional<VArray_For_GVArray<T>> varray_any_;
+ GVArrayPtr owned_gvarray_;
+
+ public:
+ explicit GVArray_Typed(const GVArray &gvarray)
+ {
+ BLI_assert(gvarray.type().is<T>());
+ if (gvarray.is_span()) {
+ const GSpan span = gvarray.get_internal_span();
+ varray_span_.emplace(span.typed<T>());
+ varray_ = &*varray_span_;
+ }
+ else if (gvarray.is_single()) {
+ T single_value;
+ gvarray.get_internal_single(&single_value);
+ varray_single_.emplace(single_value, gvarray.size());
+ varray_ = &*varray_single_;
+ }
+ else if (const VArray<T> *internal_varray = gvarray.try_get_internal_varray<T>()) {
+ varray_ = internal_varray;
+ }
+ else {
+ varray_any_.emplace(gvarray);
+ varray_ = &*varray_any_;
+ }
+ }
+
+ /* Same as the constructor above, but also takes ownership of the passed in virtual array. */
+ explicit GVArray_Typed(GVArrayPtr gvarray) : GVArray_Typed(*gvarray)
+ {
+ owned_gvarray_ = std::move(gvarray);
+ }
+
+ const VArray<T> &operator*() const
+ {
+ return *varray_;
+ }
+
+ const VArray<T> *operator->() const
+ {
+ return varray_;
+ }
+
+ /* Support implicit cast to the typed virtual array for convenience when `varray->typed<T>()` is
+ * used within an expression. */
+ operator const VArray<T> &() const
+ {
+ return *varray_;
+ }
+
+ T operator[](const int64_t index) const
+ {
+ return varray_->get(index);
+ }
+
+ int64_t size() const
+ {
+ return varray_->size();
+ }
+
+ IndexRange index_range() const
+ {
+ return IndexRange(this->size());
+ }
+};
+
+/* Same as GVArray_Typed, but for mutable virtual arrays. */
+template<typename T> class GVMutableArray_Typed {
+ private:
+ VMutableArray<T> *varray_;
+ std::optional<VMutableArray_For_MutableSpan<T>> varray_span_;
+ std::optional<VMutableArray_For_GVMutableArray<T>> varray_any_;
+ GVMutableArrayPtr owned_gvarray_;
+
+ public:
+ explicit GVMutableArray_Typed(GVMutableArray &gvarray)
+ {
+ BLI_assert(gvarray.type().is<T>());
+ if (gvarray.is_span()) {
+ const GMutableSpan span = gvarray.get_internal_span();
+ varray_span_.emplace(span.typed<T>());
+ varray_ = &*varray_span_;
+ }
+ else if (VMutableArray<T> *internal_varray = gvarray.try_get_internal_mutable_varray<T>()) {
+ varray_ = internal_varray;
+ }
+ else {
+ varray_any_.emplace(gvarray);
+ varray_ = &*varray_any_;
+ }
+ }
+
+ explicit GVMutableArray_Typed(GVMutableArrayPtr gvarray) : GVMutableArray_Typed(*gvarray)
+ {
+ owned_gvarray_ = std::move(gvarray);
+ }
+
+ VMutableArray<T> &operator*()
+ {
+ return *varray_;
+ }
+
+ VMutableArray<T> *operator->()
+ {
+ return varray_;
+ }
+
+ operator VMutableArray<T> &()
+ {
+ return *varray_;
+ }
+
+ T operator[](const int64_t index) const
+ {
+ return varray_->get(index);
+ }
+
+ int64_t size() const
+ {
+ return varray_->size();
+ }
+};
+
} // namespace blender::fn
diff --git a/source/blender/functions/FN_generic_virtual_vector_array.hh b/source/blender/functions/FN_generic_virtual_vector_array.hh
index ef3f53b5c25..4155a55a801 100644
--- a/source/blender/functions/FN_generic_virtual_vector_array.hh
+++ b/source/blender/functions/FN_generic_virtual_vector_array.hh
@@ -100,13 +100,13 @@ class GVVectorArray {
}
};
-class GVArrayForGVVectorArrayIndex : public GVArray {
+class GVArray_For_GVVectorArrayIndex : public GVArray {
private:
const GVVectorArray &vector_array_;
const int64_t index_;
public:
- GVArrayForGVVectorArrayIndex(const GVVectorArray &vector_array, const int64_t index)
+ GVArray_For_GVVectorArrayIndex(const GVVectorArray &vector_array, const int64_t index)
: GVArray(vector_array.type(), vector_array.get_vector_size(index)),
vector_array_(vector_array),
index_(index)
@@ -118,12 +118,12 @@ class GVArrayForGVVectorArrayIndex : public GVArray {
void get_to_uninitialized_impl(const int64_t index_in_vector, void *r_value) const override;
};
-class GVVectorArrayForSingleGVArray : public GVVectorArray {
+class GVVectorArray_For_SingleGVArray : public GVVectorArray {
private:
const GVArray &array_;
public:
- GVVectorArrayForSingleGVArray(const GVArray &array, const int64_t size)
+ GVVectorArray_For_SingleGVArray(const GVArray &array, const int64_t size)
: GVVectorArray(array.type(), size), array_(array)
{
}
@@ -137,12 +137,12 @@ class GVVectorArrayForSingleGVArray : public GVVectorArray {
bool is_single_vector_impl() const override;
};
-class GVVectorArrayForSingleGSpan : public GVVectorArray {
+class GVVectorArray_For_SingleGSpan : public GVVectorArray {
private:
const GSpan span_;
public:
- GVVectorArrayForSingleGSpan(const GSpan span, const int64_t size)
+ GVVectorArray_For_SingleGSpan(const GSpan span, const int64_t size)
: GVVectorArray(span.type(), size), span_(span)
{
}
@@ -156,12 +156,12 @@ class GVVectorArrayForSingleGSpan : public GVVectorArray {
bool is_single_vector_impl() const override;
};
-template<typename T> class VVectorArrayForGVVectorArray : public VVectorArray<T> {
+template<typename T> class VVectorArray_For_GVVectorArray : public VVectorArray<T> {
private:
const GVVectorArray &vector_array_;
public:
- VVectorArrayForGVVectorArray(const GVVectorArray &vector_array)
+ VVectorArray_For_GVVectorArray(const GVVectorArray &vector_array)
: VVectorArray<T>(vector_array.size()), vector_array_(vector_array)
{
}
diff --git a/source/blender/functions/FN_multi_function_network_optimization.hh b/source/blender/functions/FN_multi_function_network_optimization.hh
index 6d0165643ce..96664fa368e 100644
--- a/source/blender/functions/FN_multi_function_network_optimization.hh
+++ b/source/blender/functions/FN_multi_function_network_optimization.hh
@@ -18,12 +18,12 @@
#include "FN_multi_function_network.hh"
-#include "BLI_resource_collector.hh"
+#include "BLI_resource_scope.hh"
namespace blender::fn::mf_network_optimization {
void dead_node_removal(MFNetwork &network);
-void constant_folding(MFNetwork &network, ResourceCollector &resources);
+void constant_folding(MFNetwork &network, ResourceScope &scope);
void common_subnetwork_elimination(MFNetwork &network);
} // namespace blender::fn::mf_network_optimization
diff --git a/source/blender/functions/FN_multi_function_params.hh b/source/blender/functions/FN_multi_function_params.hh
index 2d3a8dd650e..e292d11def7 100644
--- a/source/blender/functions/FN_multi_function_params.hh
+++ b/source/blender/functions/FN_multi_function_params.hh
@@ -25,8 +25,9 @@
* the function. `MFParams` is then used inside the called function to access the parameters.
*/
-#include "BLI_resource_collector.hh"
+#include "BLI_resource_scope.hh"
+#include "FN_generic_pointer.hh"
#include "FN_generic_vector_array.hh"
#include "FN_generic_virtual_vector_array.hh"
#include "FN_multi_function_signature.hh"
@@ -35,7 +36,7 @@ namespace blender::fn {
class MFParamsBuilder {
private:
- ResourceCollector resources_;
+ ResourceScope scope_;
const MFSignature *signature_;
int64_t min_array_size_;
Vector<const GVArray *> virtual_arrays_;
@@ -55,13 +56,19 @@ class MFParamsBuilder {
template<typename T> void add_readonly_single_input(const T *value, StringRef expected_name = "")
{
- this->add_readonly_single_input(resources_.construct<GVArrayForSingleValueRef>(
+ this->add_readonly_single_input(scope_.construct<GVArray_For_SingleValueRef>(
__func__, CPPType::get<T>(), min_array_size_, value),
expected_name);
}
void add_readonly_single_input(const GSpan span, StringRef expected_name = "")
{
- this->add_readonly_single_input(resources_.construct<GVArrayForGSpan>(__func__, span),
+ this->add_readonly_single_input(scope_.construct<GVArray_For_GSpan>(__func__, span),
+ expected_name);
+ }
+ void add_readonly_single_input(GPointer value, StringRef expected_name = "")
+ {
+ this->add_readonly_single_input(scope_.construct<GVArray_For_SingleValueRef>(
+ __func__, *value.type(), min_array_size_, value.get()),
expected_name);
}
void add_readonly_single_input(const GVArray &ref, StringRef expected_name = "")
@@ -74,7 +81,7 @@ class MFParamsBuilder {
void add_readonly_vector_input(const GVectorArray &vector_array, StringRef expected_name = "")
{
this->add_readonly_vector_input(
- resources_.construct<GVVectorArrayForGVectorArray>(__func__, vector_array), expected_name);
+ scope_.construct<GVVectorArray_For_GVectorArray>(__func__, vector_array), expected_name);
}
void add_readonly_vector_input(const GVVectorArray &ref, StringRef expected_name = "")
{
@@ -136,9 +143,9 @@ class MFParamsBuilder {
return *vector_arrays_[data_index];
}
- ResourceCollector &resources()
+ ResourceScope &resource_scope()
{
- return resources_;
+ return scope_;
}
private:
@@ -177,7 +184,7 @@ class MFParams {
template<typename T> const VArray<T> &readonly_single_input(int param_index, StringRef name = "")
{
const GVArray &array = this->readonly_single_input(param_index, name);
- return builder_->resources_.construct<VArrayForGVArray<T>>(__func__, array);
+ return builder_->scope_.construct<VArray_For_GVArray<T>>(__func__, array);
}
const GVArray &readonly_single_input(int param_index, StringRef name = "")
{
@@ -202,7 +209,7 @@ class MFParams {
const VVectorArray<T> &readonly_vector_input(int param_index, StringRef name = "")
{
const GVVectorArray &vector_array = this->readonly_vector_input(param_index, name);
- return builder_->resources_.construct<VVectorArrayForGVVectorArray<T>>(__func__, vector_array);
+ return builder_->scope_.construct<VVectorArray_For_GVVectorArray<T>>(__func__, vector_array);
}
const GVVectorArray &readonly_vector_input(int param_index, StringRef name = "")
{
diff --git a/source/blender/functions/intern/cpp_types.cc b/source/blender/functions/intern/cpp_types.cc
index 53c5def57e9..9c2c1621e23 100644
--- a/source/blender/functions/intern/cpp_types.cc
+++ b/source/blender/functions/intern/cpp_types.cc
@@ -34,8 +34,8 @@ MAKE_CPP_TYPE(int32, int32_t)
MAKE_CPP_TYPE(uint32, uint32_t)
MAKE_CPP_TYPE(uint8, uint8_t)
-MAKE_CPP_TYPE(Color4f, blender::Color4f)
-MAKE_CPP_TYPE(Color4b, blender::Color4b)
+MAKE_CPP_TYPE(ColorGeometry4f, blender::ColorGeometry4f)
+MAKE_CPP_TYPE(ColorGeometry4b, blender::ColorGeometry4b)
MAKE_CPP_TYPE(string, std::string)
diff --git a/source/blender/functions/intern/generic_vector_array.cc b/source/blender/functions/intern/generic_vector_array.cc
index b3c5517cc43..3335b07e559 100644
--- a/source/blender/functions/intern/generic_vector_array.cc
+++ b/source/blender/functions/intern/generic_vector_array.cc
@@ -60,21 +60,21 @@ void GVectorArray::extend(const int64_t index, const GVArray &values)
void GVectorArray::extend(const int64_t index, const GSpan values)
{
- GVArrayForGSpan varray{values};
+ GVArray_For_GSpan varray{values};
this->extend(index, varray);
}
void GVectorArray::extend(IndexMask mask, const GVVectorArray &values)
{
for (const int i : mask) {
- GVArrayForGVVectorArrayIndex array{values, i};
+ GVArray_For_GVVectorArrayIndex array{values, i};
this->extend(i, array);
}
}
void GVectorArray::extend(IndexMask mask, const GVectorArray &values)
{
- GVVectorArrayForGVectorArray virtual_values{values};
+ GVVectorArray_For_GVectorArray virtual_values{values};
this->extend(mask, virtual_values);
}
diff --git a/source/blender/functions/intern/generic_virtual_array.cc b/source/blender/functions/intern/generic_virtual_array.cc
index 9380eb257b2..87dae06ccdc 100644
--- a/source/blender/functions/intern/generic_virtual_array.cc
+++ b/source/blender/functions/intern/generic_virtual_array.cc
@@ -18,8 +18,72 @@
namespace blender::fn {
+/* --------------------------------------------------------------------
+ * GVArray_For_ShallowCopy.
+ */
+
+class GVArray_For_ShallowCopy : public GVArray {
+ private:
+ const GVArray &varray_;
+
+ public:
+ GVArray_For_ShallowCopy(const GVArray &varray)
+ : GVArray(varray.type(), varray.size()), varray_(varray)
+ {
+ }
+
+ private:
+ void get_impl(const int64_t index, void *r_value) const override
+ {
+ varray_.get(index, r_value);
+ }
+
+ void get_to_uninitialized_impl(const int64_t index, void *r_value) const override
+ {
+ varray_.get_to_uninitialized(index, r_value);
+ }
+
+ void materialize_to_uninitialized_impl(const IndexMask mask, void *dst) const override
+ {
+ varray_.materialize_to_uninitialized(mask, dst);
+ }
+};
+
+/* --------------------------------------------------------------------
+ * GVArray.
+ */
+
+void GVArray::materialize(void *dst) const
+{
+ this->materialize(IndexMask(size_), dst);
+}
+
+void GVArray::materialize(const IndexMask mask, void *dst) const
+{
+ this->materialize_impl(mask, dst);
+}
+
+void GVArray::materialize_impl(const IndexMask mask, void *dst) const
+{
+ for (const int64_t i : mask) {
+ void *elem_dst = POINTER_OFFSET(dst, type_->size() * i);
+ this->get(i, elem_dst);
+ }
+}
+
+void GVArray::materialize_to_uninitialized(void *dst) const
+{
+ this->materialize_to_uninitialized(IndexMask(size_), dst);
+}
+
void GVArray::materialize_to_uninitialized(const IndexMask mask, void *dst) const
{
+ BLI_assert(mask.min_array_size() <= size_);
+ this->materialize_to_uninitialized_impl(mask, dst);
+}
+
+void GVArray::materialize_to_uninitialized_impl(const IndexMask mask, void *dst) const
+{
for (const int64_t i : mask) {
void *elem_dst = POINTER_OFFSET(dst, type_->size() * i);
this->get_to_uninitialized(i, elem_dst);
@@ -37,7 +101,7 @@ bool GVArray::is_span_impl() const
return false;
}
-GSpan GVArray::get_span_impl() const
+GSpan GVArray::get_internal_span_impl() const
{
BLI_assert(false);
return GSpan(*type_);
@@ -48,60 +112,279 @@ bool GVArray::is_single_impl() const
return false;
}
-void GVArray::get_single_impl(void *UNUSED(r_value)) const
+void GVArray::get_internal_single_impl(void *UNUSED(r_value)) const
{
BLI_assert(false);
}
-void GVArrayForGSpan::get_impl(const int64_t index, void *r_value) const
+const void *GVArray::try_get_internal_varray_impl() const
+{
+ return nullptr;
+}
+
+/**
+ * Creates a new `std::unique_ptr<GVArray>` based on this `GVArray`.
+ * The lifetime of the returned virtual array must not be longer than the lifetime of this virtual
+ * array.
+ */
+GVArrayPtr GVArray::shallow_copy() const
+{
+ if (this->is_span()) {
+ return std::make_unique<GVArray_For_GSpan>(this->get_internal_span());
+ }
+ if (this->is_single()) {
+ BUFFER_FOR_CPP_TYPE_VALUE(*type_, buffer);
+ this->get_internal_single(buffer);
+ std::unique_ptr new_varray = std::make_unique<GVArray_For_SingleValue>(*type_, size_, buffer);
+ type_->destruct(buffer);
+ return new_varray;
+ }
+ return std::make_unique<GVArray_For_ShallowCopy>(*this);
+}
+
+/* --------------------------------------------------------------------
+ * GVMutableArray.
+ */
+
+void GVMutableArray::set_by_copy_impl(const int64_t index, const void *value)
+{
+ BUFFER_FOR_CPP_TYPE_VALUE(*type_, buffer);
+ type_->copy_to_uninitialized(value, buffer);
+ this->set_by_move_impl(index, buffer);
+ type_->destruct(buffer);
+}
+
+void GVMutableArray::set_by_relocate_impl(const int64_t index, void *value)
+{
+ this->set_by_move_impl(index, value);
+ type_->destruct(value);
+}
+
+void GVMutableArray::set_all_impl(const void *src)
+{
+ if (this->is_span()) {
+ const GMutableSpan span = this->get_internal_span();
+ type_->copy_to_initialized_n(src, span.data(), size_);
+ }
+ else {
+ for (int64_t i : IndexRange(size_)) {
+ this->set_by_copy(i, POINTER_OFFSET(src, type_->size() * i));
+ }
+ }
+}
+
+void *GVMutableArray::try_get_internal_mutable_varray_impl()
+{
+ return nullptr;
+}
+
+void GVMutableArray::fill(const void *value)
+{
+ if (this->is_span()) {
+ const GMutableSpan span = this->get_internal_span();
+ type_->fill_initialized(value, span.data(), size_);
+ }
+ else {
+ for (int64_t i : IndexRange(size_)) {
+ this->set_by_copy(i, value);
+ }
+ }
+}
+
+/* --------------------------------------------------------------------
+ * GVArray_For_GSpan.
+ */
+
+void GVArray_For_GSpan::get_impl(const int64_t index, void *r_value) const
{
type_->copy_to_initialized(POINTER_OFFSET(data_, element_size_ * index), r_value);
}
-void GVArrayForGSpan::get_to_uninitialized_impl(const int64_t index, void *r_value) const
+void GVArray_For_GSpan::get_to_uninitialized_impl(const int64_t index, void *r_value) const
{
type_->copy_to_uninitialized(POINTER_OFFSET(data_, element_size_ * index), r_value);
}
-bool GVArrayForGSpan::is_span_impl() const
+bool GVArray_For_GSpan::is_span_impl() const
{
return true;
}
-GSpan GVArrayForGSpan::get_span_impl() const
+GSpan GVArray_For_GSpan::get_internal_span_impl() const
{
return GSpan(*type_, data_, size_);
}
-void GVArrayForSingleValueRef::get_impl(const int64_t UNUSED(index), void *r_value) const
+/* --------------------------------------------------------------------
+ * GVMutableArray_For_GMutableSpan.
+ */
+
+void GVMutableArray_For_GMutableSpan::get_impl(const int64_t index, void *r_value) const
+{
+ type_->copy_to_initialized(POINTER_OFFSET(data_, element_size_ * index), r_value);
+}
+
+void GVMutableArray_For_GMutableSpan::get_to_uninitialized_impl(const int64_t index,
+ void *r_value) const
+{
+ type_->copy_to_uninitialized(POINTER_OFFSET(data_, element_size_ * index), r_value);
+}
+
+void GVMutableArray_For_GMutableSpan::set_by_copy_impl(const int64_t index, const void *value)
+{
+ type_->copy_to_initialized(value, POINTER_OFFSET(data_, element_size_ * index));
+}
+
+void GVMutableArray_For_GMutableSpan::set_by_move_impl(const int64_t index, void *value)
+{
+ type_->move_to_initialized(value, POINTER_OFFSET(data_, element_size_ * index));
+}
+
+void GVMutableArray_For_GMutableSpan::set_by_relocate_impl(const int64_t index, void *value)
+{
+ type_->relocate_to_initialized(value, POINTER_OFFSET(data_, element_size_ * index));
+}
+
+bool GVMutableArray_For_GMutableSpan::is_span_impl() const
+{
+ return true;
+}
+
+GSpan GVMutableArray_For_GMutableSpan::get_internal_span_impl() const
+{
+ return GSpan(*type_, data_, size_);
+}
+
+/* --------------------------------------------------------------------
+ * GVArray_For_SingleValueRef.
+ */
+
+void GVArray_For_SingleValueRef::get_impl(const int64_t UNUSED(index), void *r_value) const
{
type_->copy_to_initialized(value_, r_value);
}
-void GVArrayForSingleValueRef::get_to_uninitialized_impl(const int64_t UNUSED(index),
- void *r_value) const
+void GVArray_For_SingleValueRef::get_to_uninitialized_impl(const int64_t UNUSED(index),
+ void *r_value) const
{
type_->copy_to_uninitialized(value_, r_value);
}
-bool GVArrayForSingleValueRef::is_span_impl() const
+bool GVArray_For_SingleValueRef::is_span_impl() const
{
return size_ == 1;
}
-GSpan GVArrayForSingleValueRef::get_span_impl() const
+GSpan GVArray_For_SingleValueRef::get_internal_span_impl() const
{
return GSpan{*type_, value_, 1};
}
-bool GVArrayForSingleValueRef::is_single_impl() const
+bool GVArray_For_SingleValueRef::is_single_impl() const
{
return true;
}
-void GVArrayForSingleValueRef::get_single_impl(void *r_value) const
+void GVArray_For_SingleValueRef::get_internal_single_impl(void *r_value) const
{
type_->copy_to_initialized(value_, r_value);
}
+/* --------------------------------------------------------------------
+ * GVArray_For_SingleValue.
+ */
+
+GVArray_For_SingleValue::GVArray_For_SingleValue(const CPPType &type,
+ const int64_t size,
+ const void *value)
+ : GVArray_For_SingleValueRef(type, size)
+{
+ value_ = MEM_mallocN_aligned(type.size(), type.alignment(), __func__);
+ type.copy_to_uninitialized(value, (void *)value_);
+}
+
+GVArray_For_SingleValue::~GVArray_For_SingleValue()
+{
+ type_->destruct((void *)value_);
+ MEM_freeN((void *)value_);
+}
+
+/* --------------------------------------------------------------------
+ * GVArray_GSpan.
+ */
+
+GVArray_GSpan::GVArray_GSpan(const GVArray &varray) : GSpan(varray.type()), varray_(varray)
+{
+ size_ = varray_.size();
+ if (varray_.is_span()) {
+ data_ = varray_.get_internal_span().data();
+ }
+ else {
+ owned_data_ = MEM_mallocN_aligned(type_->size() * size_, type_->alignment(), __func__);
+ varray_.materialize_to_uninitialized(IndexRange(size_), owned_data_);
+ data_ = owned_data_;
+ }
+}
+
+GVArray_GSpan::~GVArray_GSpan()
+{
+ if (owned_data_ != nullptr) {
+ type_->destruct_n(owned_data_, size_);
+ MEM_freeN(owned_data_);
+ }
+}
+
+/* --------------------------------------------------------------------
+ * GVMutableArray_GSpan.
+ */
+
+GVMutableArray_GSpan::GVMutableArray_GSpan(GVMutableArray &varray, const bool copy_values_to_span)
+ : GMutableSpan(varray.type()), varray_(varray)
+{
+ size_ = varray_.size();
+ if (varray_.is_span()) {
+ data_ = varray_.get_internal_span().data();
+ }
+ else {
+ owned_data_ = MEM_mallocN_aligned(type_->size() * size_, type_->alignment(), __func__);
+ if (copy_values_to_span) {
+ varray_.materialize_to_uninitialized(IndexRange(size_), owned_data_);
+ }
+ else {
+ type_->construct_default_n(owned_data_, size_);
+ }
+ data_ = owned_data_;
+ }
+}
+
+GVMutableArray_GSpan::~GVMutableArray_GSpan()
+{
+ if (show_not_saved_warning_) {
+ if (!save_has_been_called_) {
+ std::cout << "Warning: Call `apply()` to make sure that changes persist in all cases.\n";
+ }
+ }
+ if (owned_data_ != nullptr) {
+ type_->destruct_n(owned_data_, size_);
+ MEM_freeN(owned_data_);
+ }
+}
+
+void GVMutableArray_GSpan::save()
+{
+ save_has_been_called_ = true;
+ if (data_ != owned_data_) {
+ return;
+ }
+ const int64_t element_size = type_->size();
+ for (int64_t i : IndexRange(size_)) {
+ varray_.set_by_copy(i, POINTER_OFFSET(owned_data_, element_size * i));
+ }
+}
+
+void GVMutableArray_GSpan::disable_not_applied_warning()
+{
+ show_not_saved_warning_ = false;
+}
+
} // namespace blender::fn
diff --git a/source/blender/functions/intern/generic_virtual_vector_array.cc b/source/blender/functions/intern/generic_virtual_vector_array.cc
index f6504cee41e..aa3d90883c6 100644
--- a/source/blender/functions/intern/generic_virtual_vector_array.cc
+++ b/source/blender/functions/intern/generic_virtual_vector_array.cc
@@ -18,48 +18,48 @@
namespace blender::fn {
-void GVArrayForGVVectorArrayIndex::get_impl(const int64_t index_in_vector, void *r_value) const
+void GVArray_For_GVVectorArrayIndex::get_impl(const int64_t index_in_vector, void *r_value) const
{
vector_array_.get_vector_element(index_, index_in_vector, r_value);
}
-void GVArrayForGVVectorArrayIndex::get_to_uninitialized_impl(const int64_t index_in_vector,
- void *r_value) const
+void GVArray_For_GVVectorArrayIndex::get_to_uninitialized_impl(const int64_t index_in_vector,
+ void *r_value) const
{
type_->construct_default(r_value);
vector_array_.get_vector_element(index_, index_in_vector, r_value);
}
-int64_t GVVectorArrayForSingleGVArray::get_vector_size_impl(const int64_t UNUSED(index)) const
+int64_t GVVectorArray_For_SingleGVArray::get_vector_size_impl(const int64_t UNUSED(index)) const
{
return array_.size();
}
-void GVVectorArrayForSingleGVArray::get_vector_element_impl(const int64_t UNUSED(index),
- const int64_t index_in_vector,
- void *r_value) const
+void GVVectorArray_For_SingleGVArray::get_vector_element_impl(const int64_t UNUSED(index),
+ const int64_t index_in_vector,
+ void *r_value) const
{
array_.get(index_in_vector, r_value);
}
-bool GVVectorArrayForSingleGVArray::is_single_vector_impl() const
+bool GVVectorArray_For_SingleGVArray::is_single_vector_impl() const
{
return true;
}
-int64_t GVVectorArrayForSingleGSpan::get_vector_size_impl(const int64_t UNUSED(index)) const
+int64_t GVVectorArray_For_SingleGSpan::get_vector_size_impl(const int64_t UNUSED(index)) const
{
return span_.size();
}
-void GVVectorArrayForSingleGSpan::get_vector_element_impl(const int64_t UNUSED(index),
- const int64_t index_in_vector,
- void *r_value) const
+void GVVectorArray_For_SingleGSpan::get_vector_element_impl(const int64_t UNUSED(index),
+ const int64_t index_in_vector,
+ void *r_value) const
{
type_->copy_to_initialized(span_[index_in_vector], r_value);
}
-bool GVVectorArrayForSingleGSpan::is_single_vector_impl() const
+bool GVVectorArray_For_SingleGSpan::is_single_vector_impl() const
{
return true;
}
diff --git a/source/blender/functions/intern/multi_function_network_evaluation.cc b/source/blender/functions/intern/multi_function_network_evaluation.cc
index daa0a42963a..9a0cb0c35ce 100644
--- a/source/blender/functions/intern/multi_function_network_evaluation.cc
+++ b/source/blender/functions/intern/multi_function_network_evaluation.cc
@@ -37,7 +37,7 @@
#include "FN_multi_function_network_evaluation.hh"
-#include "BLI_resource_collector.hh"
+#include "BLI_resource_scope.hh"
#include "BLI_stack.hh"
namespace blender::fn {
@@ -70,13 +70,10 @@ class MFNetworkEvaluationStorage {
void add_vector_output_from_caller(const MFOutputSocket &socket, GVectorArray &vector_array);
/* Get input buffers for function node evaluations. */
- const GVArray &get_single_input__full(const MFInputSocket &socket, ResourceCollector &resources);
- const GVArray &get_single_input__single(const MFInputSocket &socket,
- ResourceCollector &resources);
- const GVVectorArray &get_vector_input__full(const MFInputSocket &socket,
- ResourceCollector &resources);
- const GVVectorArray &get_vector_input__single(const MFInputSocket &socket,
- ResourceCollector &resources);
+ const GVArray &get_single_input__full(const MFInputSocket &socket, ResourceScope &scope);
+ const GVArray &get_single_input__single(const MFInputSocket &socket, ResourceScope &scope);
+ const GVVectorArray &get_vector_input__full(const MFInputSocket &socket, ResourceScope &scope);
+ const GVVectorArray &get_vector_input__single(const MFInputSocket &socket, ResourceScope &scope);
/* Get output buffers for function node evaluations. */
GMutableSpan get_single_output__full(const MFOutputSocket &socket);
@@ -87,16 +84,16 @@ class MFNetworkEvaluationStorage {
/* Get mutable buffers for function node evaluations. */
GMutableSpan get_mutable_single__full(const MFInputSocket &input,
const MFOutputSocket &output,
- ResourceCollector &resources);
+ ResourceScope &scope);
GMutableSpan get_mutable_single__single(const MFInputSocket &input,
const MFOutputSocket &output,
- ResourceCollector &resources);
+ ResourceScope &scope);
GVectorArray &get_mutable_vector__full(const MFInputSocket &input,
const MFOutputSocket &output,
- ResourceCollector &resources);
+ ResourceScope &scope);
GVectorArray &get_mutable_vector__single(const MFInputSocket &input,
const MFOutputSocket &output,
- ResourceCollector &resources);
+ ResourceScope &scope);
/* Mark a node as being done with evaluation. This might free temporary buffers that are no
* longer needed. */
@@ -277,20 +274,20 @@ BLI_NOINLINE void MFNetworkEvaluator::evaluate_function(MFContext &global_contex
/* The function output would be the same for all elements. Therefore, it is enough to call the
* function only on a single element. This can avoid many duplicate computations. */
MFParamsBuilder params{function, 1};
- ResourceCollector &resources = params.resources();
+ ResourceScope &scope = params.resource_scope();
for (int param_index : function.param_indices()) {
MFParamType param_type = function.param_type(param_index);
switch (param_type.category()) {
case MFParamType::SingleInput: {
const MFInputSocket &socket = function_node.input_for_param(param_index);
- const GVArray &values = storage.get_single_input__single(socket, resources);
+ const GVArray &values = storage.get_single_input__single(socket, scope);
params.add_readonly_single_input(values);
break;
}
case MFParamType::VectorInput: {
const MFInputSocket &socket = function_node.input_for_param(param_index);
- const GVVectorArray &values = storage.get_vector_input__single(socket, resources);
+ const GVVectorArray &values = storage.get_vector_input__single(socket, scope);
params.add_readonly_vector_input(values);
break;
}
@@ -309,14 +306,14 @@ BLI_NOINLINE void MFNetworkEvaluator::evaluate_function(MFContext &global_contex
case MFParamType::SingleMutable: {
const MFInputSocket &input = function_node.input_for_param(param_index);
const MFOutputSocket &output = function_node.output_for_param(param_index);
- GMutableSpan values = storage.get_mutable_single__single(input, output, resources);
+ GMutableSpan values = storage.get_mutable_single__single(input, output, scope);
params.add_single_mutable(values);
break;
}
case MFParamType::VectorMutable: {
const MFInputSocket &input = function_node.input_for_param(param_index);
const MFOutputSocket &output = function_node.output_for_param(param_index);
- GVectorArray &values = storage.get_mutable_vector__single(input, output, resources);
+ GVectorArray &values = storage.get_mutable_vector__single(input, output, scope);
params.add_vector_mutable(values);
break;
}
@@ -327,20 +324,20 @@ BLI_NOINLINE void MFNetworkEvaluator::evaluate_function(MFContext &global_contex
}
else {
MFParamsBuilder params{function, storage.mask().min_array_size()};
- ResourceCollector &resources = params.resources();
+ ResourceScope &scope = params.resource_scope();
for (int param_index : function.param_indices()) {
MFParamType param_type = function.param_type(param_index);
switch (param_type.category()) {
case MFParamType::SingleInput: {
const MFInputSocket &socket = function_node.input_for_param(param_index);
- const GVArray &values = storage.get_single_input__full(socket, resources);
+ const GVArray &values = storage.get_single_input__full(socket, scope);
params.add_readonly_single_input(values);
break;
}
case MFParamType::VectorInput: {
const MFInputSocket &socket = function_node.input_for_param(param_index);
- const GVVectorArray &values = storage.get_vector_input__full(socket, resources);
+ const GVVectorArray &values = storage.get_vector_input__full(socket, scope);
params.add_readonly_vector_input(values);
break;
}
@@ -359,14 +356,14 @@ BLI_NOINLINE void MFNetworkEvaluator::evaluate_function(MFContext &global_contex
case MFParamType::SingleMutable: {
const MFInputSocket &input = function_node.input_for_param(param_index);
const MFOutputSocket &output = function_node.output_for_param(param_index);
- GMutableSpan values = storage.get_mutable_single__full(input, output, resources);
+ GMutableSpan values = storage.get_mutable_single__full(input, output, scope);
params.add_single_mutable(values);
break;
}
case MFParamType::VectorMutable: {
const MFInputSocket &input = function_node.input_for_param(param_index);
const MFOutputSocket &output = function_node.output_for_param(param_index);
- GVectorArray &values = storage.get_mutable_vector__full(input, output, resources);
+ GVectorArray &values = storage.get_mutable_vector__full(input, output, scope);
params.add_vector_mutable(values);
break;
}
@@ -400,19 +397,19 @@ bool MFNetworkEvaluator::can_do_single_value_evaluation(const MFFunctionNode &fu
BLI_NOINLINE void MFNetworkEvaluator::initialize_remaining_outputs(
MFParams params, Storage &storage, Span<const MFInputSocket *> remaining_outputs) const
{
- ResourceCollector resources;
+ ResourceScope scope;
for (const MFInputSocket *socket : remaining_outputs) {
int param_index = inputs_.size() + outputs_.first_index_of(socket);
switch (socket->data_type().category()) {
case MFDataType::Single: {
- const GVArray &values = storage.get_single_input__full(*socket, resources);
+ const GVArray &values = storage.get_single_input__full(*socket, scope);
GMutableSpan output_values = params.uninitialized_single_output(param_index);
values.materialize_to_uninitialized(storage.mask(), output_values.data());
break;
}
case MFDataType::Vector: {
- const GVVectorArray &values = storage.get_vector_input__full(*socket, resources);
+ const GVVectorArray &values = storage.get_vector_input__full(*socket, scope);
GVectorArray &output_values = params.vector_output(param_index);
output_values.extend(storage.mask(), values);
break;
@@ -796,7 +793,7 @@ GVectorArray &MFNetworkEvaluationStorage::get_vector_output__single(const MFOutp
GMutableSpan MFNetworkEvaluationStorage::get_mutable_single__full(const MFInputSocket &input,
const MFOutputSocket &output,
- ResourceCollector &resources)
+ ResourceScope &scope)
{
const MFOutputSocket &from = *input.origin();
const MFOutputSocket &to = output;
@@ -810,7 +807,7 @@ GMutableSpan MFNetworkEvaluationStorage::get_mutable_single__full(const MFInputS
if (to_any_value != nullptr) {
BLI_assert(to_any_value->type == ValueType::OutputSingle);
GMutableSpan span = static_cast<OutputSingleValue *>(to_any_value)->span;
- const GVArray &virtual_array = this->get_single_input__full(input, resources);
+ const GVArray &virtual_array = this->get_single_input__full(input, scope);
virtual_array.materialize_to_uninitialized(mask_, span.data());
return span;
}
@@ -825,7 +822,7 @@ GMutableSpan MFNetworkEvaluationStorage::get_mutable_single__full(const MFInputS
}
}
- const GVArray &virtual_array = this->get_single_input__full(input, resources);
+ const GVArray &virtual_array = this->get_single_input__full(input, scope);
void *new_buffer = MEM_mallocN_aligned(min_array_size_ * type.size(), type.alignment(), AT);
GMutableSpan new_array_ref(type, new_buffer, min_array_size_);
virtual_array.materialize_to_uninitialized(mask_, new_array_ref.data());
@@ -838,7 +835,7 @@ GMutableSpan MFNetworkEvaluationStorage::get_mutable_single__full(const MFInputS
GMutableSpan MFNetworkEvaluationStorage::get_mutable_single__single(const MFInputSocket &input,
const MFOutputSocket &output,
- ResourceCollector &resources)
+ ResourceScope &scope)
{
const MFOutputSocket &from = *input.origin();
const MFOutputSocket &to = output;
@@ -853,7 +850,7 @@ GMutableSpan MFNetworkEvaluationStorage::get_mutable_single__single(const MFInpu
BLI_assert(to_any_value->type == ValueType::OutputSingle);
GMutableSpan span = static_cast<OutputSingleValue *>(to_any_value)->span;
BLI_assert(span.size() == 1);
- const GVArray &virtual_array = this->get_single_input__single(input, resources);
+ const GVArray &virtual_array = this->get_single_input__single(input, scope);
virtual_array.get_single_to_uninitialized(span[0]);
return span;
}
@@ -869,7 +866,7 @@ GMutableSpan MFNetworkEvaluationStorage::get_mutable_single__single(const MFInpu
}
}
- const GVArray &virtual_array = this->get_single_input__single(input, resources);
+ const GVArray &virtual_array = this->get_single_input__single(input, scope);
void *new_buffer = allocator_.allocate(type.size(), type.alignment());
virtual_array.get_single_to_uninitialized(new_buffer);
@@ -883,7 +880,7 @@ GMutableSpan MFNetworkEvaluationStorage::get_mutable_single__single(const MFInpu
GVectorArray &MFNetworkEvaluationStorage::get_mutable_vector__full(const MFInputSocket &input,
const MFOutputSocket &output,
- ResourceCollector &resources)
+ ResourceScope &scope)
{
const MFOutputSocket &from = *input.origin();
const MFOutputSocket &to = output;
@@ -897,7 +894,7 @@ GVectorArray &MFNetworkEvaluationStorage::get_mutable_vector__full(const MFInput
if (to_any_value != nullptr) {
BLI_assert(to_any_value->type == ValueType::OutputVector);
GVectorArray &vector_array = *static_cast<OutputVectorValue *>(to_any_value)->vector_array;
- const GVVectorArray &virtual_vector_array = this->get_vector_input__full(input, resources);
+ const GVVectorArray &virtual_vector_array = this->get_vector_input__full(input, scope);
vector_array.extend(mask_, virtual_vector_array);
return vector_array;
}
@@ -912,7 +909,7 @@ GVectorArray &MFNetworkEvaluationStorage::get_mutable_vector__full(const MFInput
}
}
- const GVVectorArray &virtual_vector_array = this->get_vector_input__full(input, resources);
+ const GVVectorArray &virtual_vector_array = this->get_vector_input__full(input, scope);
GVectorArray *new_vector_array = new GVectorArray(base_type, min_array_size_);
new_vector_array->extend(mask_, virtual_vector_array);
@@ -926,7 +923,7 @@ GVectorArray &MFNetworkEvaluationStorage::get_mutable_vector__full(const MFInput
GVectorArray &MFNetworkEvaluationStorage::get_mutable_vector__single(const MFInputSocket &input,
const MFOutputSocket &output,
- ResourceCollector &resources)
+ ResourceScope &scope)
{
const MFOutputSocket &from = *input.origin();
const MFOutputSocket &to = output;
@@ -941,7 +938,7 @@ GVectorArray &MFNetworkEvaluationStorage::get_mutable_vector__single(const MFInp
BLI_assert(to_any_value->type == ValueType::OutputVector);
GVectorArray &vector_array = *static_cast<OutputVectorValue *>(to_any_value)->vector_array;
BLI_assert(vector_array.size() == 1);
- const GVVectorArray &virtual_vector_array = this->get_vector_input__single(input, resources);
+ const GVVectorArray &virtual_vector_array = this->get_vector_input__single(input, scope);
vector_array.extend({0}, virtual_vector_array);
return vector_array;
}
@@ -956,7 +953,7 @@ GVectorArray &MFNetworkEvaluationStorage::get_mutable_vector__single(const MFInp
}
}
- const GVVectorArray &virtual_vector_array = this->get_vector_input__single(input, resources);
+ const GVVectorArray &virtual_vector_array = this->get_vector_input__single(input, scope);
GVectorArray *new_vector_array = new GVectorArray(base_type, 1);
new_vector_array->extend({0}, virtual_vector_array);
@@ -968,7 +965,7 @@ GVectorArray &MFNetworkEvaluationStorage::get_mutable_vector__single(const MFInp
}
const GVArray &MFNetworkEvaluationStorage::get_single_input__full(const MFInputSocket &socket,
- ResourceCollector &resources)
+ ResourceScope &scope)
{
const MFOutputSocket &origin = *socket.origin();
Value *any_value = value_per_output_id_[origin.id()];
@@ -977,11 +974,11 @@ const GVArray &MFNetworkEvaluationStorage::get_single_input__full(const MFInputS
if (any_value->type == ValueType::OwnSingle) {
OwnSingleValue *value = static_cast<OwnSingleValue *>(any_value);
if (value->is_single_allocated) {
- return resources.construct<GVArrayForSingleValueRef>(
+ return scope.construct<GVArray_For_SingleValueRef>(
__func__, value->span.type(), min_array_size_, value->span.data());
}
- return resources.construct<GVArrayForGSpan>(__func__, value->span);
+ return scope.construct<GVArray_For_GSpan>(__func__, value->span);
}
if (any_value->type == ValueType::InputSingle) {
InputSingleValue *value = static_cast<InputSingleValue *>(any_value);
@@ -990,15 +987,15 @@ const GVArray &MFNetworkEvaluationStorage::get_single_input__full(const MFInputS
if (any_value->type == ValueType::OutputSingle) {
OutputSingleValue *value = static_cast<OutputSingleValue *>(any_value);
BLI_assert(value->is_computed);
- return resources.construct<GVArrayForGSpan>(__func__, value->span);
+ return scope.construct<GVArray_For_GSpan>(__func__, value->span);
}
BLI_assert(false);
- return resources.construct<GVArrayForEmpty>(__func__, CPPType::get<float>());
+ return scope.construct<GVArray_For_Empty>(__func__, CPPType::get<float>());
}
const GVArray &MFNetworkEvaluationStorage::get_single_input__single(const MFInputSocket &socket,
- ResourceCollector &resources)
+ ResourceScope &scope)
{
const MFOutputSocket &origin = *socket.origin();
Value *any_value = value_per_output_id_[origin.id()];
@@ -1007,7 +1004,7 @@ const GVArray &MFNetworkEvaluationStorage::get_single_input__single(const MFInpu
if (any_value->type == ValueType::OwnSingle) {
OwnSingleValue *value = static_cast<OwnSingleValue *>(any_value);
BLI_assert(value->span.size() == 1);
- return resources.construct<GVArrayForGSpan>(__func__, value->span);
+ return scope.construct<GVArray_For_GSpan>(__func__, value->span);
}
if (any_value->type == ValueType::InputSingle) {
InputSingleValue *value = static_cast<InputSingleValue *>(any_value);
@@ -1018,15 +1015,15 @@ const GVArray &MFNetworkEvaluationStorage::get_single_input__single(const MFInpu
OutputSingleValue *value = static_cast<OutputSingleValue *>(any_value);
BLI_assert(value->is_computed);
BLI_assert(value->span.size() == 1);
- return resources.construct<GVArrayForGSpan>(__func__, value->span);
+ return scope.construct<GVArray_For_GSpan>(__func__, value->span);
}
BLI_assert(false);
- return resources.construct<GVArrayForEmpty>(__func__, CPPType::get<float>());
+ return scope.construct<GVArray_For_Empty>(__func__, CPPType::get<float>());
}
const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__full(
- const MFInputSocket &socket, ResourceCollector &resources)
+ const MFInputSocket &socket, ResourceScope &scope)
{
const MFOutputSocket &origin = *socket.origin();
Value *any_value = value_per_output_id_[origin.id()];
@@ -1036,10 +1033,10 @@ const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__full(
OwnVectorValue *value = static_cast<OwnVectorValue *>(any_value);
if (value->vector_array->size() == 1) {
GSpan span = (*value->vector_array)[0];
- return resources.construct<GVVectorArrayForSingleGSpan>(__func__, span, min_array_size_);
+ return scope.construct<GVVectorArray_For_SingleGSpan>(__func__, span, min_array_size_);
}
- return resources.construct<GVVectorArrayForGVectorArray>(__func__, *value->vector_array);
+ return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array);
}
if (any_value->type == ValueType::InputVector) {
InputVectorValue *value = static_cast<InputVectorValue *>(any_value);
@@ -1047,16 +1044,15 @@ const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__full(
}
if (any_value->type == ValueType::OutputVector) {
OutputVectorValue *value = static_cast<OutputVectorValue *>(any_value);
- return resources.construct<GVVectorArrayForGVectorArray>(__func__, *value->vector_array);
+ return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array);
}
BLI_assert(false);
- return resources.construct<GVVectorArrayForSingleGSpan>(
- __func__, GSpan(CPPType::get<float>()), 0);
+ return scope.construct<GVVectorArray_For_SingleGSpan>(__func__, GSpan(CPPType::get<float>()), 0);
}
const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__single(
- const MFInputSocket &socket, ResourceCollector &resources)
+ const MFInputSocket &socket, ResourceScope &scope)
{
const MFOutputSocket &origin = *socket.origin();
Value *any_value = value_per_output_id_[origin.id()];
@@ -1065,7 +1061,7 @@ const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__single(
if (any_value->type == ValueType::OwnVector) {
OwnVectorValue *value = static_cast<OwnVectorValue *>(any_value);
BLI_assert(value->vector_array->size() == 1);
- return resources.construct<GVVectorArrayForGVectorArray>(__func__, *value->vector_array);
+ return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array);
}
if (any_value->type == ValueType::InputVector) {
InputVectorValue *value = static_cast<InputVectorValue *>(any_value);
@@ -1075,12 +1071,11 @@ const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__single(
if (any_value->type == ValueType::OutputVector) {
OutputVectorValue *value = static_cast<OutputVectorValue *>(any_value);
BLI_assert(value->vector_array->size() == 1);
- return resources.construct<GVVectorArrayForGVectorArray>(__func__, *value->vector_array);
+ return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array);
}
BLI_assert(false);
- return resources.construct<GVVectorArrayForSingleGSpan>(
- __func__, GSpan(CPPType::get<float>()), 0);
+ return scope.construct<GVVectorArray_For_SingleGSpan>(__func__, GSpan(CPPType::get<float>()), 0);
}
/** \} */
diff --git a/source/blender/functions/intern/multi_function_network_optimization.cc b/source/blender/functions/intern/multi_function_network_optimization.cc
index 6c418dee2c1..4b6b3e81393 100644
--- a/source/blender/functions/intern/multi_function_network_optimization.cc
+++ b/source/blender/functions/intern/multi_function_network_optimization.cc
@@ -219,7 +219,7 @@ static Vector<MFInputSocket *> find_constant_inputs_to_fold(
static void prepare_params_for_constant_folding(const MultiFunction &network_fn,
MFParamsBuilder &params,
- ResourceCollector &resources)
+ ResourceScope &scope)
{
for (int param_index : network_fn.param_indices()) {
MFParamType param_type = network_fn.param_type(param_index);
@@ -229,8 +229,7 @@ static void prepare_params_for_constant_folding(const MultiFunction &network_fn,
case MFDataType::Single: {
/* Allocates memory for a single constant folded value. */
const CPPType &cpp_type = data_type.single_type();
- void *buffer = resources.linear_allocator().allocate(cpp_type.size(),
- cpp_type.alignment());
+ void *buffer = scope.linear_allocator().allocate(cpp_type.size(), cpp_type.alignment());
GMutableSpan array{cpp_type, buffer, 1};
params.add_uninitialized_single_output(array);
break;
@@ -238,7 +237,7 @@ static void prepare_params_for_constant_folding(const MultiFunction &network_fn,
case MFDataType::Vector: {
/* Allocates memory for a constant folded vector. */
const CPPType &cpp_type = data_type.vector_base_type();
- GVectorArray &vector_array = resources.construct<GVectorArray>(AT, cpp_type, 1);
+ GVectorArray &vector_array = scope.construct<GVectorArray>(AT, cpp_type, 1);
params.add_vector_output(vector_array);
break;
}
@@ -248,7 +247,7 @@ static void prepare_params_for_constant_folding(const MultiFunction &network_fn,
static Array<MFOutputSocket *> add_constant_folded_sockets(const MultiFunction &network_fn,
MFParamsBuilder &params,
- ResourceCollector &resources,
+ ResourceScope &scope,
MFNetwork &network)
{
Array<MFOutputSocket *> folded_sockets{network_fn.param_indices().size(), nullptr};
@@ -264,15 +263,15 @@ static Array<MFOutputSocket *> add_constant_folded_sockets(const MultiFunction &
const CPPType &cpp_type = data_type.single_type();
GMutableSpan array = params.computed_array(param_index);
void *buffer = array.data();
- resources.add(buffer, array.type().destruct_cb(), AT);
+ scope.add(buffer, array.type().destruct_cb(), AT);
- constant_fn = &resources.construct<CustomMF_GenericConstant>(AT, cpp_type, buffer);
+ constant_fn = &scope.construct<CustomMF_GenericConstant>(AT, cpp_type, buffer);
break;
}
case MFDataType::Vector: {
GVectorArray &vector_array = params.computed_vector_array(param_index);
GSpan array = vector_array[0];
- constant_fn = &resources.construct<CustomMF_GenericConstantArray>(AT, array);
+ constant_fn = &scope.construct<CustomMF_GenericConstantArray>(AT, array);
break;
}
}
@@ -284,17 +283,15 @@ static Array<MFOutputSocket *> add_constant_folded_sockets(const MultiFunction &
}
static Array<MFOutputSocket *> compute_constant_sockets_and_add_folded_nodes(
- MFNetwork &network,
- Span<const MFInputSocket *> sockets_to_compute,
- ResourceCollector &resources)
+ MFNetwork &network, Span<const MFInputSocket *> sockets_to_compute, ResourceScope &scope)
{
MFNetworkEvaluator network_fn{{}, sockets_to_compute};
MFContextBuilder context;
MFParamsBuilder params{network_fn, 1};
- prepare_params_for_constant_folding(network_fn, params, resources);
+ prepare_params_for_constant_folding(network_fn, params, scope);
network_fn.call({0}, params, context);
- return add_constant_folded_sockets(network_fn, params, resources, network);
+ return add_constant_folded_sockets(network_fn, params, scope, network);
}
class MyClass {
@@ -304,7 +301,7 @@ class MyClass {
/**
* Find function nodes that always output the same value and replace those with constant nodes.
*/
-void constant_folding(MFNetwork &network, ResourceCollector &resources)
+void constant_folding(MFNetwork &network, ResourceScope &scope)
{
Vector<MFDummyNode *> temporary_nodes;
Vector<MFInputSocket *> inputs_to_fold = find_constant_inputs_to_fold(network, temporary_nodes);
@@ -313,7 +310,7 @@ void constant_folding(MFNetwork &network, ResourceCollector &resources)
}
Array<MFOutputSocket *> folded_sockets = compute_constant_sockets_and_add_folded_nodes(
- network, inputs_to_fold, resources);
+ network, inputs_to_fold, scope);
for (int i : inputs_to_fold.index_range()) {
MFOutputSocket &original_socket = *inputs_to_fold[i]->origin();
diff --git a/source/blender/functions/tests/FN_multi_function_network_test.cc b/source/blender/functions/tests/FN_multi_function_network_test.cc
index 51e116b5983..7b9738e5ca4 100644
--- a/source/blender/functions/tests/FN_multi_function_network_test.cc
+++ b/source/blender/functions/tests/FN_multi_function_network_test.cc
@@ -223,7 +223,7 @@ TEST(multi_function_network, Test2)
Array<int> output_value_2(5, -1);
MFParamsBuilder params(network_fn, 5);
- GVVectorArrayForSingleGSpan inputs_1{input_value_1.as_span(), 5};
+ GVVectorArray_For_SingleGSpan inputs_1{input_value_1.as_span(), 5};
params.add_readonly_vector_input(inputs_1);
params.add_readonly_single_input(&input_value_2);
params.add_vector_output(output_value_1);
diff --git a/source/blender/gpencil_modifiers/CMakeLists.txt b/source/blender/gpencil_modifiers/CMakeLists.txt
index e3bb0d773a2..f39306ac9d0 100644
--- a/source/blender/gpencil_modifiers/CMakeLists.txt
+++ b/source/blender/gpencil_modifiers/CMakeLists.txt
@@ -53,8 +53,9 @@ set(SRC
intern/MOD_gpencilbuild.c
intern/MOD_gpencilcolor.c
intern/MOD_gpencilhook.c
- intern/MOD_gpencillineart.c
intern/MOD_gpencillattice.c
+ intern/MOD_gpencillength.c
+ intern/MOD_gpencillineart.c
intern/MOD_gpencilmirror.c
intern/MOD_gpencilmultiply.c
intern/MOD_gpencilnoise.c
@@ -68,18 +69,19 @@ set(SRC
intern/MOD_gpenciltime.c
intern/MOD_gpenciltint.c
+ MOD_gpencil_lineart.h
MOD_gpencil_modifiertypes.h
intern/MOD_gpencil_ui_common.h
intern/MOD_gpencil_util.h
# Lineart code
- intern/lineart/lineart_ops.c
- intern/lineart/lineart_cpu.c
intern/lineart/lineart_chain.c
+ intern/lineart/lineart_cpu.c
+ intern/lineart/lineart_ops.c
intern/lineart/lineart_util.c
- intern/lineart/lineart_intern.h
intern/lineart/MOD_lineart.h
+ intern/lineart/lineart_intern.h
)
diff --git a/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h b/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h
index e6ce7983a0f..f8a28f2e5cb 100644
--- a/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h
+++ b/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h
@@ -35,6 +35,7 @@ extern GpencilModifierTypeInfo modifierType_Gpencil_Array;
extern GpencilModifierTypeInfo modifierType_Gpencil_Build;
extern GpencilModifierTypeInfo modifierType_Gpencil_Opacity;
extern GpencilModifierTypeInfo modifierType_Gpencil_Lattice;
+extern GpencilModifierTypeInfo modifierType_Gpencil_Length;
extern GpencilModifierTypeInfo modifierType_Gpencil_Mirror;
extern GpencilModifierTypeInfo modifierType_Gpencil_Smooth;
extern GpencilModifierTypeInfo modifierType_Gpencil_Hook;
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c b/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c
index a156fca5b7b..94285b5032e 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c
@@ -203,6 +203,20 @@ void gpencil_modifier_curve_panel_draw(const bContext *UNUSED(C), Panel *panel)
uiTemplateCurveMapping(layout, ptr, "curve", 0, false, false, false, false);
}
+void gpencil_modifier_fading_draw(const bContext *UNUSED(C), Panel *panel)
+{
+ PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL);
+
+ uiLayout *layout = panel->layout;
+ uiLayoutSetPropSep(layout, true);
+
+ uiItemR(layout, ptr, "object", 0, NULL, ICON_CUBE);
+ uiLayout *sub = uiLayoutColumn(layout, true);
+ uiItemR(sub, ptr, "fading_start", 0, NULL, ICON_NONE);
+ uiItemR(sub, ptr, "fading_end", 0, IFACE_("End"), ICON_NONE);
+ uiItemR(layout, ptr, "fading_end_factor", 0, NULL, ICON_NONE);
+}
+
/**
* Draw modifier error message.
*/
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.h b/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.h
index 782b36d47ed..75907aaa781 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.h
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.h
@@ -37,6 +37,8 @@ void gpencil_modifier_masking_panel_draw(Panel *panel, bool use_material, bool u
void gpencil_modifier_curve_header_draw(const bContext *C, Panel *panel);
void gpencil_modifier_curve_panel_draw(const bContext *C, Panel *panel);
+void gpencil_modifier_fading_draw(const bContext *UNUSED(C), Panel *panel);
+
void gpencil_modifier_panel_end(struct uiLayout *layout, PointerRNA *ptr);
struct PointerRNA *gpencil_modifier_panel_get_property_pointers(struct Panel *panel,
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c
index 2dc00079f91..b28a44a0521 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c
@@ -54,6 +54,7 @@ void gpencil_modifier_type_init(GpencilModifierTypeInfo *types[])
INIT_GP_TYPE(Build);
INIT_GP_TYPE(Opacity);
INIT_GP_TYPE(Lattice);
+ INIT_GP_TYPE(Length);
INIT_GP_TYPE(Mirror);
INIT_GP_TYPE(Smooth);
INIT_GP_TYPE(Hook);
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c
index 87d677a5c8a..d9f0fc9bddd 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c
@@ -455,8 +455,10 @@ static void generate_geometry(
/* Compute start and end frames for the animation effect
* By default, the upper bound is given by the "maximum length" setting
*/
- float start_frame = gpf->framenum + mmd->start_delay;
- float end_frame = start_frame + mmd->length;
+ float start_frame = is_percentage ? gpf->framenum : gpf->framenum + mmd->start_delay;
+ /* When use percentage don't need a limit in the upper bound, so use a maximum value for the last
+ * frame. */
+ float end_frame = is_percentage ? start_frame + 9999 : start_frame + mmd->length;
if (gpf->next) {
/* Use the next frame or upper bound as end frame, whichever is lower/closer */
@@ -547,6 +549,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel)
PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, &ob_ptr);
int mode = RNA_enum_get(ptr, "mode");
+ const bool use_percentage = RNA_boolean_get(ptr, "use_percentage");
uiLayoutSetPropSep(layout, true);
@@ -558,16 +561,22 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel)
uiItemS(layout);
uiItemR(layout, ptr, "transition", 0, NULL, ICON_NONE);
- uiItemR(layout, ptr, "start_delay", 0, NULL, ICON_NONE);
- uiItemR(layout, ptr, "length", 0, IFACE_("Frames"), ICON_NONE);
+ row = uiLayoutRow(layout, true);
+ uiLayoutSetActive(row, !use_percentage);
+ uiItemR(row, ptr, "start_delay", 0, NULL, ICON_NONE);
+ row = uiLayoutRow(layout, true);
+ uiLayoutSetActive(row, !use_percentage);
+ uiItemR(row, ptr, "length", 0, IFACE_("Frames"), ICON_NONE);
uiItemS(layout);
row = uiLayoutRowWithHeading(layout, true, IFACE_("Use Factor"));
+ uiLayoutSetPropDecorate(row, false);
uiItemR(row, ptr, "use_percentage", 0, "", ICON_NONE);
sub = uiLayoutRow(row, true);
- uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_percentage"));
+ uiLayoutSetActive(sub, use_percentage);
uiItemR(sub, ptr, "percentage_factor", 0, "", ICON_NONE);
+ uiItemDecoratorR(row, ptr, "percentage_factor", 0);
/* Check for incompatible time modifier. */
Object *ob = ob_ptr.data;
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c
new file mode 100644
index 00000000000..fd94ac92bc3
--- /dev/null
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c
@@ -0,0 +1,223 @@
+/*
+ * ***** 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.
+ *
+ * The Original Code is Copyright (C) 2017, Blender Foundation
+ * This is a new part of Blender
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ *
+ */
+
+/** \file
+ * \ingroup modifiers
+ */
+
+#include <stdio.h>
+
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+#include "BLI_utildefines.h"
+
+#include "BLT_translation.h"
+
+#include "DNA_defaults.h"
+#include "DNA_gpencil_modifier_types.h"
+#include "DNA_gpencil_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
+
+#include "BKE_context.h"
+#include "BKE_gpencil_geom.h"
+#include "BKE_gpencil_modifier.h"
+#include "BKE_lib_query.h"
+#include "BKE_main.h"
+#include "BKE_modifier.h"
+#include "BKE_screen.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "RNA_access.h"
+
+#include "MOD_gpencil_modifiertypes.h"
+#include "MOD_gpencil_ui_common.h"
+#include "MOD_gpencil_util.h"
+
+#include "DEG_depsgraph.h"
+
+static void initData(GpencilModifierData *md)
+{
+ LengthGpencilModifierData *gpmd = (LengthGpencilModifierData *)md;
+
+ BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(gpmd, modifier));
+
+ MEMCPY_STRUCT_AFTER(gpmd, DNA_struct_default_get(LengthGpencilModifierData), modifier);
+}
+
+static void copyData(const GpencilModifierData *md, GpencilModifierData *target)
+{
+ BKE_gpencil_modifier_copydata_generic(md, target);
+}
+
+static bool gpencil_modify_stroke(bGPDstroke *gps,
+ float length,
+ const float overshoot_fac,
+ const short len_mode)
+{
+ bool changed = false;
+ if (length == 0.0f) {
+ return changed;
+ }
+
+ if (length > 0.0f) {
+ BKE_gpencil_stroke_stretch(gps, length, overshoot_fac, len_mode);
+ }
+ else {
+ changed |= BKE_gpencil_stroke_shrink(gps, fabs(length), len_mode);
+ }
+
+ return changed;
+}
+
+static void applyLength(LengthGpencilModifierData *lmd, bGPdata *gpd, bGPDstroke *gps)
+{
+ bool changed = false;
+ const float len = (lmd->mode == GP_LENGTH_ABSOLUTE) ? 1.0f :
+ BKE_gpencil_stroke_length(gps, true);
+ if (len < FLT_EPSILON) {
+ return;
+ }
+
+ changed |= gpencil_modify_stroke(gps, len * lmd->start_fac, lmd->overshoot_fac, 1);
+ changed |= gpencil_modify_stroke(gps, len * lmd->end_fac, lmd->overshoot_fac, 2);
+
+ if (changed) {
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
+ }
+}
+
+static void bakeModifier(Main *UNUSED(bmain),
+ Depsgraph *UNUSED(depsgraph),
+ GpencilModifierData *md,
+ Object *ob)
+{
+
+ bGPdata *gpd = ob->data;
+
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
+ LengthGpencilModifierData *lmd = (LengthGpencilModifierData *)md;
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ applyLength(lmd, gpd, gps);
+ }
+ }
+ }
+}
+
+/* -------------------------------- */
+
+/* Generic "generateStrokes" callback */
+static void deformStroke(GpencilModifierData *md,
+ Depsgraph *UNUSED(depsgraph),
+ Object *ob,
+ bGPDlayer *gpl,
+ bGPDframe *UNUSED(gpf),
+ bGPDstroke *gps)
+{
+ bGPdata *gpd = ob->data;
+ LengthGpencilModifierData *lmd = (LengthGpencilModifierData *)md;
+ if (is_stroke_affected_by_modifier(ob,
+ lmd->layername,
+ lmd->material,
+ lmd->pass_index,
+ lmd->layer_pass,
+ 1,
+ gpl,
+ gps,
+ lmd->flag & GP_LENGTH_INVERT_LAYER,
+ lmd->flag & GP_LENGTH_INVERT_PASS,
+ lmd->flag & GP_LENGTH_INVERT_LAYERPASS,
+ lmd->flag & GP_LENGTH_INVERT_MATERIAL)) {
+ applyLength(lmd, gpd, gps);
+ }
+}
+
+static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk, void *userData)
+{
+ LengthGpencilModifierData *mmd = (LengthGpencilModifierData *)md;
+
+ walk(userData, ob, (ID **)&mmd->material, IDWALK_CB_USER);
+}
+
+static void panel_draw(const bContext *UNUSED(C), Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+
+ PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL);
+
+ uiLayoutSetPropSep(layout, true);
+ uiItemR(layout, ptr, "mode", 0, NULL, ICON_NONE);
+
+ uiLayout *col = uiLayoutColumn(layout, true);
+
+ uiItemR(col, ptr, "start_factor", 0, IFACE_("Start"), ICON_NONE);
+ uiItemR(col, ptr, "end_factor", 0, IFACE_("End"), ICON_NONE);
+
+ uiItemR(layout, ptr, "overshoot_factor", UI_ITEM_R_SLIDER, IFACE_("Overshoot"), ICON_NONE);
+
+ gpencil_modifier_panel_end(layout, ptr);
+}
+
+static void mask_panel_draw(const bContext *UNUSED(C), Panel *panel)
+{
+ gpencil_modifier_masking_panel_draw(panel, true, false);
+}
+
+static void panelRegister(ARegionType *region_type)
+{
+ PanelType *panel_type = gpencil_modifier_panel_register(
+ region_type, eGpencilModifierType_Length, panel_draw);
+ gpencil_modifier_subpanel_register(
+ region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type);
+}
+
+GpencilModifierTypeInfo modifierType_Gpencil_Length = {
+ /* name */ "Length",
+ /* structName */ "LengthGpencilModifierData",
+ /* structSize */ sizeof(LengthGpencilModifierData),
+ /* type */ eGpencilModifierTypeType_Gpencil,
+ /* flags */ eGpencilModifierTypeFlag_SupportsEditmode,
+
+ /* copyData */ copyData,
+
+ /* deformStroke */ deformStroke,
+ /* generateStrokes */ NULL,
+ /* bakeModifier */ bakeModifier,
+ /* remapTime */ NULL,
+
+ /* initData */ initData,
+ /* freeData */ NULL,
+ /* isDisabled */ NULL,
+ /* updateDepsgraph */ NULL,
+ /* dependsOnTime */ NULL,
+ /* foreachIDLink */ foreachIDLink,
+ /* foreachTexLink */ NULL,
+ /* panelRegister */ panelRegister,
+};
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c
index 2bab728f5d8..7d2efaebd01 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c
@@ -33,6 +33,7 @@
#include "DNA_defaults.h"
#include "DNA_gpencil_modifier_types.h"
#include "DNA_gpencil_types.h"
+#include "DNA_material_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
@@ -103,7 +104,6 @@ static void generate_strokes_actual(
lmd->transparency_mask,
lmd->thickness,
lmd->opacity,
- lmd->resample_length,
lmd->source_vertex_group,
lmd->vgname,
lmd->flags);
@@ -194,6 +194,27 @@ static bool isDisabled(GpencilModifierData *md, int UNUSED(userRenderParams))
return isModifierDisabled(md);
}
+static void add_this_collection(Collection *c,
+ const ModifierUpdateDepsgraphContext *ctx,
+ const int mode)
+{
+ if (!c) {
+ return;
+ }
+ FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (c, ob, mode) {
+ if (ELEM(ob->type, OB_MESH, OB_MBALL, OB_CURVE, OB_SURF, OB_FONT)) {
+ if (ob->lineart.usage != OBJECT_LRT_EXCLUDE) {
+ DEG_add_object_relation(ctx->node, ob, DEG_OB_COMP_GEOMETRY, "Line Art Modifier");
+ DEG_add_object_relation(ctx->node, ob, DEG_OB_COMP_TRANSFORM, "Line Art Modifier");
+ }
+ }
+ if (ob->type == OB_EMPTY && (ob->transflag & OB_DUPLICOLLECTION)) {
+ add_this_collection(ob->instance_collection, ctx, mode);
+ }
+ }
+ FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_END;
+}
+
static void updateDepsgraph(GpencilModifierData *md,
const ModifierUpdateDepsgraphContext *ctx,
const int mode)
@@ -208,16 +229,7 @@ static void updateDepsgraph(GpencilModifierData *md,
ctx->node, lmd->source_object, DEG_OB_COMP_TRANSFORM, "Line Art Modifier");
}
else {
- FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (ctx->scene->master_collection, ob, mode) {
- if (ob->type == OB_MESH || ob->type == OB_MBALL || ob->type == OB_CURVE ||
- ob->type == OB_SURF || ob->type == OB_FONT) {
- if (ob->lineart.usage != OBJECT_LRT_EXCLUDE) {
- DEG_add_object_relation(ctx->node, ob, DEG_OB_COMP_GEOMETRY, "Line Art Modifier");
- DEG_add_object_relation(ctx->node, ob, DEG_OB_COMP_TRANSFORM, "Line Art Modifier");
- }
- }
- }
- FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_END;
+ add_this_collection(ctx->scene->master_collection, ctx, mode);
}
DEG_add_object_relation(
ctx->node, ctx->scene->camera, DEG_OB_COMP_TRANSFORM, "Line Art Modifier");
@@ -276,8 +288,25 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel)
uiItemR(sub, ptr, "crease_threshold", UI_ITEM_R_SLIDER, " ", ICON_NONE);
uiItemPointerR(layout, ptr, "target_layer", &obj_data_ptr, "layers", NULL, ICON_GREASEPENCIL);
- uiItemPointerR(
- layout, ptr, "target_material", &obj_data_ptr, "materials", NULL, ICON_SHADING_TEXTURE);
+
+ /* Material has to be used by grease pencil object already, it was possible to assign materials
+ * without this requirement in earlier versions of blender. */
+ bool material_valid = false;
+ PointerRNA material_ptr = RNA_pointer_get(ptr, "target_material");
+ if (!RNA_pointer_is_null(&material_ptr)) {
+ Material *current_material = material_ptr.data;
+ Object *ob = ob_ptr.data;
+ material_valid = BKE_gpencil_object_material_index_get(ob, current_material) != -1;
+ }
+ uiLayout *row = uiLayoutRow(layout, true);
+ uiLayoutSetRedAlert(row, !material_valid);
+ uiItemPointerR(row,
+ ptr,
+ "target_material",
+ &obj_data_ptr,
+ "materials",
+ NULL,
+ material_valid ? ICON_SHADING_TEXTURE : ICON_ERROR);
uiItemR(layout, ptr, "use_remove_doubles", 0, NULL, ICON_NONE);
uiItemR(layout, ptr, "use_edge_overlap", 0, IFACE_("Overlapping Edges As Contour"), ICON_NONE);
@@ -319,7 +348,7 @@ static void occlusion_panel_draw(const bContext *UNUSED(C), Panel *panel)
if (use_multiple_levels) {
uiLayout *col = uiLayoutColumn(layout, true);
uiItemR(col, ptr, "level_start", 0, NULL, ICON_NONE);
- uiItemR(col, ptr, "level_end", 0, NULL, ICON_NONE);
+ uiItemR(col, ptr, "level_end", 0, IFACE_("End"), ICON_NONE);
}
else {
uiItemR(layout, ptr, "level_start", 0, IFACE_("Level"), ICON_NONE);
@@ -351,7 +380,7 @@ static void transparency_panel_draw(const bContext *UNUSED(C), Panel *panel)
uiLayout *row = uiLayoutRow(layout, true);
uiLayoutSetPropDecorate(row, false);
- uiLayout *sub = uiLayoutRow(row, true);
+ uiLayout *sub = uiLayoutRowWithHeading(row, true, IFACE_("Masks"));
char text[2] = "0";
PropertyRNA *prop = RNA_struct_find_property(ptr, "use_transparency_mask");
@@ -381,8 +410,6 @@ static void chaining_panel_draw(const bContext *UNUSED(C), Panel *panel)
uiItemR(layout, ptr, "chaining_image_threshold", 0, NULL, ICON_NONE);
- uiItemR(layout, ptr, "resample_length", UI_ITEM_R_SLIDER, NULL, ICON_NONE);
-
uiItemR(layout, ptr, "split_angle", UI_ITEM_R_SLIDER, NULL, ICON_NONE);
}
@@ -411,8 +438,6 @@ static void vgroup_panel_draw(const bContext *UNUSED(C), Panel *panel)
uiItemPointerR(
col, ptr, "vertex_group", &ob_ptr, "vertex_groups", IFACE_("Target"), ICON_NONE);
}
-
- uiItemR(col, ptr, "use_soft_selection", 0, NULL, ICON_NONE);
}
static void baking_panel_draw(const bContext *UNUSED(C), Panel *panel)
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c b/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c
index bffb324f07f..cd29a006aae 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c
@@ -26,7 +26,11 @@
#include "BLI_listbase.h"
#include "BLI_utildefines.h"
+#include "BLT_translation.h"
+
+#include "BLI_hash.h"
#include "BLI_math.h"
+#include "BLI_rand.h"
#include "DNA_defaults.h"
#include "DNA_gpencil_modifier_types.h"
@@ -71,7 +75,7 @@ static void deformStroke(GpencilModifierData *md,
Depsgraph *UNUSED(depsgraph),
Object *ob,
bGPDlayer *gpl,
- bGPDframe *UNUSED(gpf),
+ bGPDframe *gpf,
bGPDstroke *gps)
{
OffsetGpencilModifierData *mmd = (OffsetGpencilModifierData *)md;
@@ -94,6 +98,46 @@ static void deformStroke(GpencilModifierData *md,
mmd->flag & GP_OFFSET_INVERT_MATERIAL)) {
return;
}
+
+ int seed = mmd->seed;
+ /* Make sure different modifiers get different seeds. */
+ seed += BLI_hash_string(ob->id.name + 2);
+ seed += BLI_hash_string(md->name);
+
+ float rand[3][3];
+ float rand_offset = BLI_hash_int_01(seed);
+
+ /* Get stroke index for random offset. */
+ int rnd_index = BLI_findindex(&gpf->strokes, gps);
+ for (int j = 0; j < 3; j++) {
+ const uint primes[3] = {2, 3, 7};
+ double offset[3] = {0.0f, 0.0f, 0.0f};
+ double r[3];
+ /* To ensure a nice distribution, we use halton sequence and offset using the seed. */
+ BLI_halton_3d(primes, offset, rnd_index, r);
+
+ if ((mmd->flag & GP_OFFSET_UNIFORM_RANDOM_SCALE) && j == 2) {
+ float rand_value;
+ rand_value = fmodf(r[0] * 2.0f - 1.0f + rand_offset, 1.0f);
+ rand_value = fmodf(sin(rand_value * 12.9898f + j * 78.233f) * 43758.5453f, 1.0f);
+ copy_v3_fl(rand[j], rand_value);
+ }
+ else {
+ for (int i = 0; i < 3; i++) {
+ rand[j][i] = fmodf(r[i] * 2.0f - 1.0f + rand_offset, 1.0f);
+ rand[j][i] = fmodf(sin(rand[j][i] * 12.9898f + j * 78.233f) * 43758.5453f, 1.0f);
+ }
+ }
+ }
+ /* Calculate Random matrix. */
+ float mat_rnd[4][4];
+ float rnd_loc[3], rnd_rot[3];
+ float rnd_scale[3] = {1.0f, 1.0f, 1.0f};
+ mul_v3_v3v3(rnd_loc, mmd->rnd_offset, rand[0]);
+ mul_v3_v3v3(rnd_rot, mmd->rnd_rot, rand[1]);
+ madd_v3_v3v3(rnd_scale, mmd->rnd_scale, rand[2]);
+ loc_eul_size_to_mat4(mat_rnd, rnd_loc, rnd_rot, rnd_scale);
+
bGPdata *gpd = ob->data;
for (int i = 0; i < gps->totpoints; i++) {
@@ -106,6 +150,9 @@ static void deformStroke(GpencilModifierData *md,
if (weight < 0.0f) {
continue;
}
+ /* Apply randomness matrix. */
+ mul_m4_v3(mat_rnd, &pt->x);
+
/* Calculate matrix. */
mul_v3_v3fl(loc, mmd->loc, weight);
mul_v3_v3fl(rot, mmd->rot, weight);
@@ -161,6 +208,21 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel)
gpencil_modifier_panel_end(layout, ptr);
}
+static void random_panel_draw(const bContext *UNUSED(C), Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+
+ PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL);
+
+ uiLayoutSetPropSep(layout, true);
+
+ uiItemR(layout, ptr, "random_offset", 0, IFACE_("Offset"), ICON_NONE);
+ uiItemR(layout, ptr, "random_rotation", 0, IFACE_("Rotation"), ICON_NONE);
+ uiItemR(layout, ptr, "random_scale", 0, IFACE_("Scale"), ICON_NONE);
+ uiItemR(layout, ptr, "use_uniform_random_scale", 0, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "seed", 0, NULL, ICON_NONE);
+}
+
static void mask_panel_draw(const bContext *UNUSED(C), Panel *panel)
{
gpencil_modifier_masking_panel_draw(panel, true, true);
@@ -171,6 +233,8 @@ static void panelRegister(ARegionType *region_type)
PanelType *panel_type = gpencil_modifier_panel_register(
region_type, eGpencilModifierType_Offset, panel_draw);
gpencil_modifier_subpanel_register(
+ region_type, "randomize", "Randomize", NULL, random_panel_draw, panel_type);
+ gpencil_modifier_subpanel_register(
region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type);
}
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c
index c193fc49362..9f03e493ea8 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c
@@ -47,6 +47,8 @@
#include "BKE_screen.h"
#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_build.h"
+#include "DEG_depsgraph_query.h"
#include "UI_interface.h"
#include "UI_resources.h"
@@ -84,6 +86,39 @@ static void copyData(const GpencilModifierData *md, GpencilModifierData *target)
tgmd->curve_intensity = BKE_curvemapping_copy(gmd->curve_intensity);
}
+static float give_opacity_fading_factor(OpacityGpencilModifierData *mmd,
+ Object *ob_this,
+ float *pos,
+ bool apply_obmat)
+{
+ float factor_depth = 1.0f;
+
+ if (((mmd->flag & GP_OPACITY_FADING) == 0) || ((mmd->object) == NULL)) {
+ return factor_depth;
+ }
+
+ float gvert[3];
+ if (apply_obmat) {
+ mul_v3_m4v3(gvert, ob_this->obmat, pos);
+ }
+ float dist = len_v3v3(mmd->object->obmat[3], gvert);
+ float fading_max = MAX2(mmd->fading_start, mmd->fading_end);
+ float fading_min = MIN2(mmd->fading_start, mmd->fading_end);
+
+ /* Better with ratiof() function from line art. */
+ if (dist > fading_max) {
+ factor_depth = 0.0f;
+ }
+ else if (dist <= fading_max && dist > fading_min) {
+ factor_depth = (fading_max - dist) / (fading_max - fading_min);
+ }
+ else {
+ factor_depth = 1.0f;
+ }
+
+ return factor_depth;
+}
+
/* opacity strokes */
static void deformStroke(GpencilModifierData *md,
Depsgraph *UNUSED(depsgraph),
@@ -138,6 +173,9 @@ static void deformStroke(GpencilModifierData *md,
factor_curve *= BKE_curvemapping_evaluateF(mmd->curve_intensity, 0, value);
}
+ float factor_depth = give_opacity_fading_factor(mmd, ob, &pt->x, true);
+ factor_curve = interpf(factor_curve, mmd->fading_end_factor, factor_depth);
+
if (def_nr < 0) {
if (mmd->flag & GP_OPACITY_NORMALIZE) {
pt->strength = factor_curve;
@@ -166,7 +204,9 @@ static void deformStroke(GpencilModifierData *md,
/* Fill using opacity factor. */
if (mmd->modify_color != GP_MODIFY_COLOR_STROKE) {
- gps->fill_opacity_fac = mmd->factor;
+ float factor_depth = give_opacity_fading_factor(mmd, ob, ob->obmat[3], true);
+ gps->fill_opacity_fac = interpf(mmd->factor, mmd->fading_end_factor, factor_depth);
+
CLAMP(gps->fill_opacity_fac, 0.0f, 1.0f);
}
}
@@ -201,6 +241,18 @@ static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk,
OpacityGpencilModifierData *mmd = (OpacityGpencilModifierData *)md;
walk(userData, ob, (ID **)&mmd->material, IDWALK_CB_USER);
+ walk(userData, ob, (ID **)&mmd->object, IDWALK_CB_NOP);
+}
+
+static void updateDepsgraph(GpencilModifierData *md,
+ const ModifierUpdateDepsgraphContext *ctx,
+ const int UNUSED(mode))
+{
+ OpacityGpencilModifierData *mmd = (OpacityGpencilModifierData *)md;
+ if (mmd->object != NULL) {
+ DEG_add_object_relation(ctx->node, mmd->object, DEG_OB_COMP_TRANSFORM, "Opacity Modifier");
+ }
+ DEG_add_object_relation(ctx->node, ctx->object, DEG_OB_COMP_TRANSFORM, "Opacity Modifier");
}
static void panel_draw(const bContext *UNUSED(C), Panel *panel)
@@ -228,6 +280,20 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel)
gpencil_modifier_panel_end(layout, ptr);
}
+static void fading_header_draw(const bContext *UNUSED(C), Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+
+ PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL);
+
+ uiItemR(layout, ptr, "use_fading", 0, NULL, ICON_NONE);
+}
+
+static void fading_panel_draw(const bContext *C, Panel *panel)
+{
+ gpencil_modifier_fading_draw(C, panel);
+}
+
static void mask_panel_draw(const bContext *UNUSED(C), Panel *panel)
{
PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL);
@@ -266,6 +332,9 @@ static void panelRegister(ARegionType *region_type)
{
PanelType *panel_type = gpencil_modifier_panel_register(
region_type, eGpencilModifierType_Opacity, panel_draw);
+
+ gpencil_modifier_subpanel_register(
+ region_type, "fading", "", fading_header_draw, fading_panel_draw, panel_type);
PanelType *mask_panel_type = gpencil_modifier_subpanel_register(
region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type);
gpencil_modifier_subpanel_register(
@@ -289,7 +358,7 @@ GpencilModifierTypeInfo modifierType_Gpencil_Opacity = {
/* initData */ initData,
/* freeData */ freeData,
/* isDisabled */ NULL,
- /* updateDepsgraph */ NULL,
+ /* updateDepsgraph */ updateDepsgraph,
/* dependsOnTime */ NULL,
/* foreachIDLink */ foreachIDLink,
/* foreachTexLink */ NULL,
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c
index a13f8d64755..126949cd659 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c
@@ -43,6 +43,8 @@
#include "BKE_screen.h"
#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_build.h"
+#include "DEG_depsgraph_query.h"
#include "UI_interface.h"
#include "UI_resources.h"
@@ -128,6 +130,30 @@ static void deformStroke(GpencilModifierData *md,
}
float curvef = 1.0f;
+
+ float factor_depth = 1.0f;
+
+ if (mmd->flag & GP_THICK_FADING) {
+ if (mmd->object) {
+ float gvert[3];
+ mul_v3_m4v3(gvert, ob->obmat, &pt->x);
+ float dist = len_v3v3(mmd->object->obmat[3], gvert);
+ float fading_max = MAX2(mmd->fading_start, mmd->fading_end);
+ float fading_min = MIN2(mmd->fading_start, mmd->fading_end);
+
+ /* Better with ratiof() function from line art. */
+ if (dist > fading_max) {
+ factor_depth = 0.0f;
+ }
+ else if (dist <= fading_max && dist > fading_min) {
+ factor_depth = (fading_max - dist) / (fading_max - fading_min);
+ }
+ else {
+ factor_depth = 1.0f;
+ }
+ }
+ }
+
if ((mmd->flag & GP_THICK_CUSTOM_CURVE) && (mmd->curve_thickness)) {
/* Normalize value to evaluate curve. */
float value = (float)i / (gps->totpoints - 1);
@@ -144,6 +170,11 @@ static void deformStroke(GpencilModifierData *md,
weight *= curvef;
}
+ /* Apply distance fading. */
+ if (mmd->flag & GP_THICK_FADING) {
+ target = interpf(target, mmd->fading_end_factor, factor_depth);
+ }
+
pt->pressure = interpf(target, pt->pressure, weight);
CLAMP_MIN(pt->pressure, 0.0f);
@@ -171,6 +202,32 @@ static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk,
ThickGpencilModifierData *mmd = (ThickGpencilModifierData *)md;
walk(userData, ob, (ID **)&mmd->material, IDWALK_CB_USER);
+ walk(userData, ob, (ID **)&mmd->object, IDWALK_CB_NOP);
+}
+
+static void updateDepsgraph(GpencilModifierData *md,
+ const ModifierUpdateDepsgraphContext *ctx,
+ const int UNUSED(mode))
+{
+ ThickGpencilModifierData *mmd = (ThickGpencilModifierData *)md;
+ if (mmd->object != NULL) {
+ DEG_add_object_relation(ctx->node, mmd->object, DEG_OB_COMP_TRANSFORM, "Thickness Modifier");
+ }
+ DEG_add_object_relation(ctx->node, ctx->object, DEG_OB_COMP_TRANSFORM, "Thickness Modifier");
+}
+
+static void fading_header_draw(const bContext *UNUSED(C), Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+
+ PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL);
+
+ uiItemR(layout, ptr, "use_fading", 0, NULL, ICON_NONE);
+}
+
+static void fading_panel_draw(const bContext *C, Panel *panel)
+{
+ gpencil_modifier_fading_draw(C, panel);
}
static void panel_draw(const bContext *UNUSED(C), Panel *panel)
@@ -202,6 +259,8 @@ static void panelRegister(ARegionType *region_type)
{
PanelType *panel_type = gpencil_modifier_panel_register(
region_type, eGpencilModifierType_Thick, panel_draw);
+ gpencil_modifier_subpanel_register(
+ region_type, "fading", "", fading_header_draw, fading_panel_draw, panel_type);
PanelType *mask_panel_type = gpencil_modifier_subpanel_register(
region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type);
gpencil_modifier_subpanel_register(region_type,
@@ -229,7 +288,7 @@ GpencilModifierTypeInfo modifierType_Gpencil_Thick = {
/* initData */ initData,
/* freeData */ freeData,
/* isDisabled */ NULL,
- /* updateDepsgraph */ NULL,
+ /* updateDepsgraph */ updateDepsgraph,
/* dependsOnTime */ NULL,
/* foreachIDLink */ foreachIDLink,
/* foreachTexLink */ NULL,
diff --git a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h
index 1239deb95bb..861085d3e16 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h
+++ b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h
@@ -23,6 +23,7 @@
#pragma once
+#include "BLI_linklist.h"
#include "BLI_listbase.h"
#include "BLI_math.h" /* Needed here for inline functions. */
#include "BLI_threads.h"
@@ -93,8 +94,8 @@ typedef struct LineartElementLinkNode {
float crease_threshold;
} LineartElementLinkNode;
-typedef struct LineartLineSegment {
- struct LineartLineSegment *next, *prev;
+typedef struct LineartEdgeSegment {
+ struct LineartEdgeSegment *next, *prev;
/** at==0: left at==1: right (this is in 2D projected space) */
double at;
/** Occlusion level after "at" point */
@@ -107,7 +108,7 @@ typedef struct LineartLineSegment {
* enough for most cases.
*/
unsigned char transparency_mask;
-} LineartLineSegment;
+} LineartEdgeSegment;
typedef struct LineartVert {
double gloc[3];
@@ -155,7 +156,7 @@ typedef struct LineartEdge {
/**
* Still need this entry because culled lines will not add to object
- * #LineartElementLinkNode node (known as `reln` internally).
+ * #LineartElementLinkNode node (known as `eln` internally).
*
* TODO: If really need more savings, we can allocate this in a "extended" way too, but we need
* another bit in flags to be able to show the difference.
@@ -163,8 +164,8 @@ typedef struct LineartEdge {
struct Object *object_ref;
} LineartEdge;
-typedef struct LineartLineChain {
- struct LineartLineChain *next, *prev;
+typedef struct LineartEdgeChain {
+ struct LineartEdgeChain *next, *prev;
ListBase chain;
/** Calculated before draw command. */
@@ -179,10 +180,10 @@ typedef struct LineartLineChain {
unsigned char transparency_mask;
struct Object *object_ref;
-} LineartLineChain;
+} LineartEdgeChain;
-typedef struct LineartLineChainItem {
- struct LineartLineChainItem *next, *prev;
+typedef struct LineartEdgeChainItem {
+ struct LineartEdgeChainItem *next, *prev;
/** Need z value for fading */
float pos[3];
/** For restoring position to 3d space */
@@ -192,12 +193,12 @@ typedef struct LineartLineChainItem {
char occlusion;
unsigned char transparency_mask;
size_t index;
-} LineartLineChainItem;
+} LineartEdgeChainItem;
typedef struct LineartChainRegisterEntry {
struct LineartChainRegisterEntry *next, *prev;
- LineartLineChain *rlc;
- LineartLineChainItem *rlci;
+ LineartEdgeChain *ec;
+ LineartEdgeChainItem *eci;
char picked;
/* left/right mark.
@@ -205,6 +206,17 @@ typedef struct LineartChainRegisterEntry {
char is_left;
} LineartChainRegisterEntry;
+enum eLineArtTileRecursiveLimit {
+ /* If tile gets this small, it's already much smaller than a pixel. No need to continue
+ * splitting. */
+ LRT_TILE_RECURSIVE_PERSPECTIVE = 30,
+ /* This is a tried-and-true safe value for high poly models that also needed ortho rendering. */
+ LRT_TILE_RECURSIVE_ORTHO = 10,
+};
+
+#define LRT_TILE_SPLITTING_TRIANGLE_LIMIT 100
+#define LRT_TILE_EDGE_COUNT_INITIAL 32
+
typedef struct LineartRenderBuffer {
struct LineartRenderBuffer *prev, *next;
@@ -215,10 +227,16 @@ typedef struct LineartRenderBuffer {
int tile_count_x, tile_count_y;
double width_per_tile, height_per_tile;
double view_projection[4][4];
+ double view[4][4];
struct LineartBoundingArea *initial_bounding_areas;
unsigned int bounding_area_count;
+ /* When splitting bounding areas, if there's an ortho camera placed at a straight angle, there
+ * will be a lot of triangles aligned in line which can not be separated by continue subdividing
+ * the tile. So we set a strict limit when using ortho camera. See eLineArtTileRecursiveLimit. */
+ int tile_recursive_level;
+
ListBase vertex_buffer_pointers;
ListBase line_buffer_pointers;
ListBase triangle_buffer_pointers;
@@ -237,31 +255,14 @@ typedef struct LineartRenderBuffer {
int triangle_size;
- unsigned int contour_count;
- unsigned int contour_processed;
- LineartEdge *contour_managed;
- /** A single linked list (cast to #LinkNode). */
- LineartEdge *contours;
-
- unsigned int intersection_count;
- unsigned int intersection_processed;
- LineartEdge *intersection_managed;
- LineartEdge *intersection_lines;
-
- unsigned int crease_count;
- unsigned int crease_processed;
- LineartEdge *crease_managed;
- LineartEdge *crease_lines;
-
- unsigned int material_line_count;
- unsigned int material_processed;
- LineartEdge *material_managed;
- LineartEdge *material_lines;
-
- unsigned int edge_mark_count;
- unsigned int edge_mark_processed;
- LineartEdge *edge_mark_managed;
- LineartEdge *edge_marks;
+ /* Although using ListBase here, LineartEdge is single linked list.
+ * list.last is used to store worker progress along the list.
+ * See lineart_main_occlusion_begin() for more info. */
+ ListBase contour;
+ ListBase intersection;
+ ListBase crease;
+ ListBase material;
+ ListBase edge_mark;
ListBase chains;
@@ -311,7 +312,7 @@ typedef struct LineartRenderBuffer {
#define DBL_TRIANGLE_LIM 1e-8
#define DBL_EDGE_LIM 1e-9
-#define LRT_MEMORY_POOL_64MB (1 << 26)
+#define LRT_MEMORY_POOL_1MB (1 << 20)
typedef enum eLineartTriangleFlags {
LRT_CULL_DONT_CARE = 0,
@@ -322,9 +323,11 @@ typedef enum eLineartTriangleFlags {
LRT_TRIANGLE_NO_INTERSECTION = (1 << 4),
} eLineartTriangleFlags;
-/** Controls how many edges a worker thread is processing at one request.
+/**
+ * Controls how many edges a worker thread is processing at one request.
* There's no significant performance impact on choosing different values.
- * Don't make it too small so that the worker thread won't request too many times. */
+ * Don't make it too small so that the worker thread won't request too many times.
+ */
#define LRT_THREAD_EDGE_COUNT 1000
typedef struct LineartRenderTaskInfo {
@@ -332,23 +335,51 @@ typedef struct LineartRenderTaskInfo {
int thread_id;
- LineartEdge *contour;
- LineartEdge *contour_end;
-
- LineartEdge *intersection;
- LineartEdge *intersection_end;
-
- LineartEdge *crease;
- LineartEdge *crease_end;
-
- LineartEdge *material;
- LineartEdge *material_end;
-
- LineartEdge *edge_mark;
- LineartEdge *edge_mark_end;
+ /* These lists only denote the part of the main edge list that the thread should iterate over.
+ * Be careful to not iterate outside of these bounds as it is not thread safe to do so. */
+ ListBase contour;
+ ListBase intersection;
+ ListBase crease;
+ ListBase material;
+ ListBase edge_mark;
} LineartRenderTaskInfo;
+struct BMesh;
+
+typedef struct LineartObjectInfo {
+ struct LineartObjectInfo *next;
+ struct Object *original_ob;
+ struct Mesh *original_me;
+ double model_view_proj[4][4];
+ double model_view[4][4];
+ double normal[4][4];
+ LineartElementLinkNode *v_reln;
+ int usage;
+ int global_i_offset;
+
+ bool free_use_mesh;
+
+ /* Threads will add lines inside here, when all threads are done, we combine those into the
+ * ones in LineartRenderBuffer. */
+ ListBase contour;
+ ListBase intersection;
+ ListBase crease;
+ ListBase material;
+ ListBase edge_mark;
+ ListBase floating;
+
+} LineartObjectInfo;
+
+typedef struct LineartObjectLoadTaskInfo {
+ struct LineartRenderBuffer *rb;
+ struct Depsgraph *dg;
+ /* LinkNode styled list */
+ LineartObjectInfo *pending;
+ /* Used to spread the load across several threads. This can not overflow. */
+ long unsigned int total_faces;
+} LineartObjectLoadTaskInfo;
+
/**
* Bounding area diagram:
* \code{.txt}
@@ -385,10 +416,14 @@ typedef struct LineartBoundingArea {
ListBase up;
ListBase bp;
- short triangle_count;
+ int16_t triangle_count;
+ int16_t max_triangle_count;
+ int16_t line_count;
+ int16_t max_line_count;
- ListBase linked_triangles;
- ListBase linked_lines;
+ /* Use array for speeding up multiple accesses. */
+ struct LineartTriangle **linked_triangles;
+ struct LineartEdge **linked_lines;
/** Reserved for image space reduction && multi-thread chaining. */
ListBase linked_chains;
@@ -527,7 +562,7 @@ void MOD_lineart_chain_connect(LineartRenderBuffer *rb);
void MOD_lineart_chain_discard_short(LineartRenderBuffer *rb, const float threshold);
void MOD_lineart_chain_split_angle(LineartRenderBuffer *rb, float angle_threshold_rad);
-int MOD_lineart_chain_count(const LineartLineChain *rlc);
+int MOD_lineart_chain_count(const LineartEdgeChain *ec);
void MOD_lineart_chain_clear_picked_flag(struct LineartRenderBuffer *rb);
bool MOD_lineart_compute_feature_lines(struct Depsgraph *depsgraph,
@@ -543,7 +578,6 @@ LineartBoundingArea *MOD_lineart_get_bounding_area(LineartRenderBuffer *rb, doub
struct bGPDlayer;
struct bGPDframe;
-struct GpencilModifierData;
void MOD_lineart_gpencil_generate(LineartRenderBuffer *rb,
struct Depsgraph *depsgraph,
@@ -560,11 +594,10 @@ void MOD_lineart_gpencil_generate(LineartRenderBuffer *rb,
unsigned char transparency_mask,
short thickness,
float opacity,
- float resample_length,
const char *source_vgname,
const char *vgname,
int modifier_flags);
-float MOD_lineart_chain_compute_length(LineartLineChain *rlc);
+float MOD_lineart_chain_compute_length(LineartEdgeChain *ec);
void ED_operatortypes_lineart(void);
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c
index 464316b6a10..25c4e959ac1 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c
@@ -31,17 +31,17 @@
#include <math.h>
-#define LRT_OTHER_RV(e, rv) ((rv) == (e)->v1 ? (e)->v2 : ((rv) == (e)->v2 ? (e)->v1 : NULL))
+#define LRT_OTHER_VERT(e, vt) ((vt) == (e)->v1 ? (e)->v2 : ((vt) == (e)->v2 ? (e)->v1 : NULL))
/* Get a connected line, only for lines who has the exact given vert, or (in the case of
* intersection lines) who has a vert that has the exact same position. */
static LineartEdge *lineart_line_get_connected(LineartBoundingArea *ba,
- LineartVert *rv,
- LineartVert **new_rv,
+ LineartVert *vt,
+ LineartVert **new_vt,
int match_flag)
{
- LISTBASE_FOREACH (LinkData *, lip, &ba->linked_lines) {
- LineartEdge *n_e = lip->data;
+ for (int i = 0; i < ba->line_count; i++) {
+ LineartEdge *n_e = ba->linked_lines[i];
if ((!(n_e->flags & LRT_EDGE_FLAG_ALL_TYPE)) || (n_e->flags & LRT_EDGE_FLAG_CHAIN_PICKED)) {
continue;
@@ -51,18 +51,18 @@ static LineartEdge *lineart_line_get_connected(LineartBoundingArea *ba,
continue;
}
- *new_rv = LRT_OTHER_RV(n_e, rv);
- if (*new_rv) {
+ *new_vt = LRT_OTHER_VERT(n_e, vt);
+ if (*new_vt) {
return n_e;
}
if (n_e->flags & LRT_EDGE_FLAG_INTERSECTION) {
- if (rv->fbcoord[0] == n_e->v1->fbcoord[0] && rv->fbcoord[1] == n_e->v1->fbcoord[1]) {
- *new_rv = LRT_OTHER_RV(n_e, n_e->v1);
+ if (vt->fbcoord[0] == n_e->v1->fbcoord[0] && vt->fbcoord[1] == n_e->v1->fbcoord[1]) {
+ *new_vt = LRT_OTHER_VERT(n_e, n_e->v1);
return n_e;
}
- if (rv->fbcoord[0] == n_e->v2->fbcoord[0] && rv->fbcoord[1] == n_e->v2->fbcoord[1]) {
- *new_rv = LRT_OTHER_RV(n_e, n_e->v2);
+ if (vt->fbcoord[0] == n_e->v2->fbcoord[0] && vt->fbcoord[1] == n_e->v2->fbcoord[1]) {
+ *new_vt = LRT_OTHER_VERT(n_e, n_e->v2);
return n_e;
}
}
@@ -71,33 +71,33 @@ static LineartEdge *lineart_line_get_connected(LineartBoundingArea *ba,
return NULL;
}
-static LineartLineChain *lineart_chain_create(LineartRenderBuffer *rb)
+static LineartEdgeChain *lineart_chain_create(LineartRenderBuffer *rb)
{
- LineartLineChain *rlc;
- rlc = lineart_mem_aquire(&rb->render_data_pool, sizeof(LineartLineChain));
+ LineartEdgeChain *ec;
+ ec = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdgeChain));
- BLI_addtail(&rb->chains, rlc);
+ BLI_addtail(&rb->chains, ec);
- return rlc;
+ return ec;
}
-static bool lineart_point_overlapping(LineartLineChainItem *rlci,
+static bool lineart_point_overlapping(LineartEdgeChainItem *eci,
float x,
float y,
double threshold)
{
- if (!rlci) {
+ if (!eci) {
return false;
}
- if (((rlci->pos[0] + threshold) >= x) && ((rlci->pos[0] - threshold) <= x) &&
- ((rlci->pos[1] + threshold) >= y) && ((rlci->pos[1] - threshold) <= y)) {
+ if (((eci->pos[0] + threshold) >= x) && ((eci->pos[0] - threshold) <= x) &&
+ ((eci->pos[1] + threshold) >= y) && ((eci->pos[1] - threshold) <= y)) {
return true;
}
return false;
}
-static LineartLineChainItem *lineart_chain_append_point(LineartRenderBuffer *rb,
- LineartLineChain *rlc,
+static LineartEdgeChainItem *lineart_chain_append_point(LineartRenderBuffer *rb,
+ LineartEdgeChain *ec,
float *fbcoord,
float *gpos,
float *normal,
@@ -106,35 +106,35 @@ static LineartLineChainItem *lineart_chain_append_point(LineartRenderBuffer *rb,
unsigned char transparency_mask,
size_t index)
{
- LineartLineChainItem *rlci;
+ LineartEdgeChainItem *eci;
- if (lineart_point_overlapping(rlc->chain.last, fbcoord[0], fbcoord[1], 1e-5)) {
+ if (lineart_point_overlapping(ec->chain.last, fbcoord[0], fbcoord[1], 1e-5)) {
/* Because the new chain point is overlapping, just replace the type and occlusion level of the
* current point. This makes it so that the line to the point after this one has the correct
* type and level. */
- LineartLineChainItem *old_rlci = rlc->chain.last;
+ LineartEdgeChainItem *old_rlci = ec->chain.last;
old_rlci->line_type = type;
old_rlci->occlusion = level;
old_rlci->transparency_mask = transparency_mask;
return old_rlci;
}
- rlci = lineart_mem_aquire(&rb->render_data_pool, sizeof(LineartLineChainItem));
+ eci = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdgeChainItem));
- copy_v2_v2(rlci->pos, fbcoord);
- copy_v3_v3(rlci->gpos, gpos);
- rlci->index = index;
- copy_v3_v3(rlci->normal, normal);
- rlci->line_type = type & LRT_EDGE_FLAG_ALL_TYPE;
- rlci->occlusion = level;
- rlci->transparency_mask = transparency_mask;
- BLI_addtail(&rlc->chain, rlci);
+ copy_v2_v2(eci->pos, fbcoord);
+ copy_v3_v3(eci->gpos, gpos);
+ eci->index = index;
+ copy_v3_v3(eci->normal, normal);
+ eci->line_type = type & LRT_EDGE_FLAG_ALL_TYPE;
+ eci->occlusion = level;
+ eci->transparency_mask = transparency_mask;
+ BLI_addtail(&ec->chain, eci);
- return rlci;
+ return eci;
}
-static LineartLineChainItem *lineart_chain_prepend_point(LineartRenderBuffer *rb,
- LineartLineChain *rlc,
+static LineartEdgeChainItem *lineart_chain_prepend_point(LineartRenderBuffer *rb,
+ LineartEdgeChain *ec,
float *fbcoord,
float *gpos,
float *normal,
@@ -143,32 +143,32 @@ static LineartLineChainItem *lineart_chain_prepend_point(LineartRenderBuffer *rb
unsigned char transparency_mask,
size_t index)
{
- LineartLineChainItem *rlci;
+ LineartEdgeChainItem *eci;
- if (lineart_point_overlapping(rlc->chain.first, fbcoord[0], fbcoord[1], 1e-5)) {
- return rlc->chain.first;
+ if (lineart_point_overlapping(ec->chain.first, fbcoord[0], fbcoord[1], 1e-5)) {
+ return ec->chain.first;
}
- rlci = lineart_mem_aquire(&rb->render_data_pool, sizeof(LineartLineChainItem));
+ eci = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdgeChainItem));
- copy_v2_v2(rlci->pos, fbcoord);
- copy_v3_v3(rlci->gpos, gpos);
- rlci->index = index;
- copy_v3_v3(rlci->normal, normal);
- rlci->line_type = type & LRT_EDGE_FLAG_ALL_TYPE;
- rlci->occlusion = level;
- rlci->transparency_mask = transparency_mask;
- BLI_addhead(&rlc->chain, rlci);
+ copy_v2_v2(eci->pos, fbcoord);
+ copy_v3_v3(eci->gpos, gpos);
+ eci->index = index;
+ copy_v3_v3(eci->normal, normal);
+ eci->line_type = type & LRT_EDGE_FLAG_ALL_TYPE;
+ eci->occlusion = level;
+ eci->transparency_mask = transparency_mask;
+ BLI_addhead(&ec->chain, eci);
- return rlci;
+ return eci;
}
void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb)
{
- LineartLineChain *rlc;
- LineartLineChainItem *rlci;
+ LineartEdgeChain *ec;
+ LineartEdgeChainItem *eci;
LineartBoundingArea *ba;
- LineartLineSegment *rls;
+ LineartEdgeSegment *es;
int last_occlusion;
unsigned char last_transparency;
/* Used when converting from double. */
@@ -192,14 +192,14 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb)
e->flags |= LRT_EDGE_FLAG_CHAIN_PICKED;
- rlc = lineart_chain_create(rb);
+ ec = lineart_chain_create(rb);
/* One chain can only have one object_ref,
* so we assign it based on the first segment we found. */
- rlc->object_ref = e->object_ref;
+ ec->object_ref = e->object_ref;
- LineartEdge *new_e = e;
- LineartVert *new_rv;
+ LineartEdge *new_e;
+ LineartVert *new_vt;
float N[3] = {0};
if (e->t1) {
@@ -218,19 +218,19 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb)
/* Step 1: grow left. */
ba = MOD_lineart_get_bounding_area(rb, e->v1->fbcoord[0], e->v1->fbcoord[1]);
- new_rv = e->v1;
- rls = e->segments.first;
- VERT_COORD_TO_FLOAT(new_rv);
+ new_vt = e->v1;
+ es = e->segments.first;
+ VERT_COORD_TO_FLOAT(new_vt);
lineart_chain_prepend_point(rb,
- rlc,
+ ec,
use_fbcoord,
use_gpos,
N,
e->flags,
- rls->occlusion,
- rls->transparency_mask,
+ es->occlusion,
+ es->transparency_mask,
e->v1_obindex);
- while (ba && (new_e = lineart_line_get_connected(ba, new_rv, &new_rv, e->flags))) {
+ while (ba && (new_e = lineart_line_get_connected(ba, new_vt, &new_vt, e->flags))) {
new_e->flags |= LRT_EDGE_FLAG_CHAIN_PICKED;
if (new_e->t1 || new_e->t2) {
@@ -248,41 +248,41 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb)
normalize_v3(N);
}
- if (new_rv == new_e->v1) {
- for (rls = new_e->segments.last; rls; rls = rls->prev) {
+ if (new_vt == new_e->v1) {
+ for (es = new_e->segments.last; es; es = es->prev) {
double gpos[3], lpos[3];
double *lfb = new_e->v1->fbcoord, *rfb = new_e->v2->fbcoord;
- double global_at = lfb[3] * rls->at / (rls->at * lfb[3] + (1 - rls->at) * rfb[3]);
- interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, rls->at);
+ double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]);
+ interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->at);
interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at);
POS_TO_FLOAT(lpos, gpos)
lineart_chain_prepend_point(rb,
- rlc,
+ ec,
use_fbcoord,
use_gpos,
N,
new_e->flags,
- rls->occlusion,
- rls->transparency_mask,
+ es->occlusion,
+ es->transparency_mask,
new_e->v1_obindex);
- last_occlusion = rls->occlusion;
- last_transparency = rls->transparency_mask;
+ last_occlusion = es->occlusion;
+ last_transparency = es->transparency_mask;
}
}
- else if (new_rv == new_e->v2) {
- rls = new_e->segments.first;
- last_occlusion = rls->occlusion;
- last_transparency = rls->transparency_mask;
- rls = rls->next;
- for (; rls; rls = rls->next) {
+ else if (new_vt == new_e->v2) {
+ es = new_e->segments.first;
+ last_occlusion = es->occlusion;
+ last_transparency = es->transparency_mask;
+ es = es->next;
+ for (; es; es = es->next) {
double gpos[3], lpos[3];
double *lfb = new_e->v1->fbcoord, *rfb = new_e->v2->fbcoord;
- double global_at = lfb[3] * rls->at / (rls->at * lfb[3] + (1 - rls->at) * rfb[3]);
- interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, rls->at);
+ double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]);
+ interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->at);
interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at);
POS_TO_FLOAT(lpos, gpos)
lineart_chain_prepend_point(rb,
- rlc,
+ ec,
use_fbcoord,
use_gpos,
N,
@@ -290,12 +290,12 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb)
last_occlusion,
last_transparency,
new_e->v2_obindex);
- last_occlusion = rls->occlusion;
- last_transparency = rls->transparency_mask;
+ last_occlusion = es->occlusion;
+ last_transparency = es->transparency_mask;
}
VERT_COORD_TO_FLOAT(new_e->v2);
lineart_chain_prepend_point(rb,
- rlc,
+ ec,
use_fbcoord,
use_gpos,
N,
@@ -304,7 +304,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb)
last_transparency,
new_e->v2_obindex);
}
- ba = MOD_lineart_get_bounding_area(rb, new_rv->fbcoord[0], new_rv->fbcoord[1]);
+ ba = MOD_lineart_get_bounding_area(rb, new_vt->fbcoord[0], new_vt->fbcoord[1]);
}
/* Restore normal value. */
@@ -324,31 +324,31 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb)
}
/* Step 2: Adding all cuts from the given line, so we can continue connecting the right side
* of the line. */
- rls = e->segments.first;
- last_occlusion = ((LineartLineSegment *)rls)->occlusion;
- last_transparency = ((LineartLineSegment *)rls)->transparency_mask;
- for (rls = rls->next; rls; rls = rls->next) {
+ es = e->segments.first;
+ last_occlusion = ((LineartEdgeSegment *)es)->occlusion;
+ last_transparency = ((LineartEdgeSegment *)es)->transparency_mask;
+ for (es = es->next; es; es = es->next) {
double gpos[3], lpos[3];
double *lfb = e->v1->fbcoord, *rfb = e->v2->fbcoord;
- double global_at = lfb[3] * rls->at / (rls->at * lfb[3] + (1 - rls->at) * rfb[3]);
- interp_v3_v3v3_db(lpos, e->v1->fbcoord, e->v2->fbcoord, rls->at);
+ double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]);
+ interp_v3_v3v3_db(lpos, e->v1->fbcoord, e->v2->fbcoord, es->at);
interp_v3_v3v3_db(gpos, e->v1->gloc, e->v2->gloc, global_at);
POS_TO_FLOAT(lpos, gpos)
lineart_chain_append_point(rb,
- rlc,
+ ec,
use_fbcoord,
use_gpos,
N,
e->flags,
- rls->occlusion,
- rls->transparency_mask,
+ es->occlusion,
+ es->transparency_mask,
e->v1_obindex);
- last_occlusion = rls->occlusion;
- last_transparency = rls->transparency_mask;
+ last_occlusion = es->occlusion;
+ last_transparency = es->transparency_mask;
}
VERT_COORD_TO_FLOAT(e->v2)
lineart_chain_append_point(rb,
- rlc,
+ ec,
use_fbcoord,
use_gpos,
N,
@@ -359,8 +359,8 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb)
/* Step 3: grow right. */
ba = MOD_lineart_get_bounding_area(rb, e->v2->fbcoord[0], e->v2->fbcoord[1]);
- new_rv = e->v2;
- while (ba && (new_e = lineart_line_get_connected(ba, new_rv, &new_rv, e->flags))) {
+ new_vt = e->v2;
+ while (ba && (new_e = lineart_line_get_connected(ba, new_vt, &new_vt, e->flags))) {
new_e->flags |= LRT_EDGE_FLAG_CHAIN_PICKED;
if (new_e->t1 || new_e->t2) {
@@ -379,27 +379,27 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb)
}
/* Fix leading vertex type. */
- rlci = rlc->chain.last;
- rlci->line_type = new_e->flags & LRT_EDGE_FLAG_ALL_TYPE;
+ eci = ec->chain.last;
+ eci->line_type = new_e->flags & LRT_EDGE_FLAG_ALL_TYPE;
- if (new_rv == new_e->v1) {
- rls = new_e->segments.last;
- last_occlusion = rls->occlusion;
- last_transparency = rls->transparency_mask;
+ if (new_vt == new_e->v1) {
+ es = new_e->segments.last;
+ last_occlusion = es->occlusion;
+ last_transparency = es->transparency_mask;
/* Fix leading vertex occlusion. */
- rlci->occlusion = last_occlusion;
- rlci->transparency_mask = last_transparency;
- for (rls = new_e->segments.last; rls; rls = rls->prev) {
+ eci->occlusion = last_occlusion;
+ eci->transparency_mask = last_transparency;
+ for (es = new_e->segments.last; es; es = es->prev) {
double gpos[3], lpos[3];
double *lfb = new_e->v1->fbcoord, *rfb = new_e->v2->fbcoord;
- double global_at = lfb[3] * rls->at / (rls->at * lfb[3] + (1 - rls->at) * rfb[3]);
- interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, rls->at);
+ double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]);
+ interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->at);
interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at);
- last_occlusion = rls->prev ? rls->prev->occlusion : last_occlusion;
- last_transparency = rls->prev ? rls->prev->transparency_mask : last_transparency;
+ last_occlusion = es->prev ? es->prev->occlusion : last_occlusion;
+ last_transparency = es->prev ? es->prev->transparency_mask : last_transparency;
POS_TO_FLOAT(lpos, gpos)
lineart_chain_append_point(rb,
- rlc,
+ ec,
use_fbcoord,
use_gpos,
N,
@@ -409,35 +409,35 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb)
new_e->v1_obindex);
}
}
- else if (new_rv == new_e->v2) {
- rls = new_e->segments.first;
- last_occlusion = rls->occlusion;
- last_transparency = rls->transparency_mask;
- rlci->occlusion = last_occlusion;
- rlci->transparency_mask = last_transparency;
- rls = rls->next;
- for (; rls; rls = rls->next) {
+ else if (new_vt == new_e->v2) {
+ es = new_e->segments.first;
+ last_occlusion = es->occlusion;
+ last_transparency = es->transparency_mask;
+ eci->occlusion = last_occlusion;
+ eci->transparency_mask = last_transparency;
+ es = es->next;
+ for (; es; es = es->next) {
double gpos[3], lpos[3];
double *lfb = new_e->v1->fbcoord, *rfb = new_e->v2->fbcoord;
- double global_at = lfb[3] * rls->at / (rls->at * lfb[3] + (1 - rls->at) * rfb[3]);
- interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, rls->at);
+ double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]);
+ interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->at);
interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at);
POS_TO_FLOAT(lpos, gpos)
lineart_chain_append_point(rb,
- rlc,
+ ec,
use_fbcoord,
use_gpos,
N,
new_e->flags,
- rls->occlusion,
- rls->transparency_mask,
+ es->occlusion,
+ es->transparency_mask,
new_e->v2_obindex);
- last_occlusion = rls->occlusion;
- last_transparency = rls->transparency_mask;
+ last_occlusion = es->occlusion;
+ last_transparency = es->transparency_mask;
}
VERT_COORD_TO_FLOAT(new_e->v2)
lineart_chain_append_point(rb,
- rlc,
+ ec,
use_fbcoord,
use_gpos,
N,
@@ -446,57 +446,57 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb)
last_transparency,
new_e->v2_obindex);
}
- ba = MOD_lineart_get_bounding_area(rb, new_rv->fbcoord[0], new_rv->fbcoord[1]);
+ ba = MOD_lineart_get_bounding_area(rb, new_vt->fbcoord[0], new_vt->fbcoord[1]);
}
if (rb->fuzzy_everything) {
- rlc->type = LRT_EDGE_FLAG_CONTOUR;
+ ec->type = LRT_EDGE_FLAG_CONTOUR;
}
else {
- rlc->type = (e->flags & LRT_EDGE_FLAG_ALL_TYPE);
+ ec->type = (e->flags & LRT_EDGE_FLAG_ALL_TYPE);
}
}
LRT_ITER_ALL_LINES_END
}
-static LineartBoundingArea *lineart_bounding_area_get_rlci_recursive(LineartRenderBuffer *rb,
- LineartBoundingArea *root,
- LineartLineChainItem *rlci)
+static LineartBoundingArea *lineart_bounding_area_get_eci_recursive(LineartRenderBuffer *rb,
+ LineartBoundingArea *root,
+ LineartEdgeChainItem *eci)
{
if (root->child == NULL) {
return root;
}
LineartBoundingArea *ch = root->child;
-#define IN_BOUND(ba, rlci) \
- ba.l <= rlci->pos[0] && ba.r >= rlci->pos[0] && ba.b <= rlci->pos[1] && ba.u >= rlci->pos[1]
+#define IN_BOUND(ba, eci) \
+ ba.l <= eci->pos[0] && ba.r >= eci->pos[0] && ba.b <= eci->pos[1] && ba.u >= eci->pos[1]
- if (IN_BOUND(ch[0], rlci)) {
- return lineart_bounding_area_get_rlci_recursive(rb, &ch[0], rlci);
+ if (IN_BOUND(ch[0], eci)) {
+ return lineart_bounding_area_get_eci_recursive(rb, &ch[0], eci);
}
- if (IN_BOUND(ch[1], rlci)) {
- return lineart_bounding_area_get_rlci_recursive(rb, &ch[1], rlci);
+ if (IN_BOUND(ch[1], eci)) {
+ return lineart_bounding_area_get_eci_recursive(rb, &ch[1], eci);
}
- if (IN_BOUND(ch[2], rlci)) {
- return lineart_bounding_area_get_rlci_recursive(rb, &ch[2], rlci);
+ if (IN_BOUND(ch[2], eci)) {
+ return lineart_bounding_area_get_eci_recursive(rb, &ch[2], eci);
}
- if (IN_BOUND(ch[3], rlci)) {
- return lineart_bounding_area_get_rlci_recursive(rb, &ch[3], rlci);
+ if (IN_BOUND(ch[3], eci)) {
+ return lineart_bounding_area_get_eci_recursive(rb, &ch[3], eci);
}
#undef IN_BOUND
return NULL;
}
static LineartBoundingArea *lineart_bounding_area_get_end_point(LineartRenderBuffer *rb,
- LineartLineChainItem *rlci)
+ LineartEdgeChainItem *eci)
{
- if (!rlci) {
+ if (!eci) {
return NULL;
}
- LineartBoundingArea *root = MOD_lineart_get_parent_bounding_area(rb, rlci->pos[0], rlci->pos[1]);
+ LineartBoundingArea *root = MOD_lineart_get_parent_bounding_area(rb, eci->pos[0], eci->pos[1]);
if (root == NULL) {
return NULL;
}
- return lineart_bounding_area_get_rlci_recursive(rb, root, rlci);
+ return lineart_bounding_area_get_eci_recursive(rb, root, eci);
}
/**
@@ -507,61 +507,61 @@ static LineartBoundingArea *lineart_bounding_area_get_end_point(LineartRenderBuf
*/
static void lineart_bounding_area_link_point_recursive(LineartRenderBuffer *rb,
LineartBoundingArea *root,
- LineartLineChain *rlc,
- LineartLineChainItem *rlci)
+ LineartEdgeChain *ec,
+ LineartEdgeChainItem *eci)
{
if (root->child == NULL) {
LineartChainRegisterEntry *cre = lineart_list_append_pointer_pool_sized(
- &root->linked_chains, &rb->render_data_pool, rlc, sizeof(LineartChainRegisterEntry));
+ &root->linked_chains, &rb->render_data_pool, ec, sizeof(LineartChainRegisterEntry));
- cre->rlci = rlci;
+ cre->eci = eci;
- if (rlci == rlc->chain.first) {
+ if (eci == ec->chain.first) {
cre->is_left = 1;
}
}
else {
LineartBoundingArea *ch = root->child;
-#define IN_BOUND(ba, rlci) \
- ba.l <= rlci->pos[0] && ba.r >= rlci->pos[0] && ba.b <= rlci->pos[1] && ba.u >= rlci->pos[1]
+#define IN_BOUND(ba, eci) \
+ ba.l <= eci->pos[0] && ba.r >= eci->pos[0] && ba.b <= eci->pos[1] && ba.u >= eci->pos[1]
- if (IN_BOUND(ch[0], rlci)) {
- lineart_bounding_area_link_point_recursive(rb, &ch[0], rlc, rlci);
+ if (IN_BOUND(ch[0], eci)) {
+ lineart_bounding_area_link_point_recursive(rb, &ch[0], ec, eci);
}
- else if (IN_BOUND(ch[1], rlci)) {
- lineart_bounding_area_link_point_recursive(rb, &ch[1], rlc, rlci);
+ else if (IN_BOUND(ch[1], eci)) {
+ lineart_bounding_area_link_point_recursive(rb, &ch[1], ec, eci);
}
- else if (IN_BOUND(ch[2], rlci)) {
- lineart_bounding_area_link_point_recursive(rb, &ch[2], rlc, rlci);
+ else if (IN_BOUND(ch[2], eci)) {
+ lineart_bounding_area_link_point_recursive(rb, &ch[2], ec, eci);
}
- else if (IN_BOUND(ch[3], rlci)) {
- lineart_bounding_area_link_point_recursive(rb, &ch[3], rlc, rlci);
+ else if (IN_BOUND(ch[3], eci)) {
+ lineart_bounding_area_link_point_recursive(rb, &ch[3], ec, eci);
}
#undef IN_BOUND
}
}
-static void lineart_bounding_area_link_chain(LineartRenderBuffer *rb, LineartLineChain *rlc)
+static void lineart_bounding_area_link_chain(LineartRenderBuffer *rb, LineartEdgeChain *ec)
{
- LineartLineChainItem *pl = rlc->chain.first;
- LineartLineChainItem *pr = rlc->chain.last;
+ LineartEdgeChainItem *pl = ec->chain.first;
+ LineartEdgeChainItem *pr = ec->chain.last;
LineartBoundingArea *ba1 = MOD_lineart_get_parent_bounding_area(rb, pl->pos[0], pl->pos[1]);
LineartBoundingArea *ba2 = MOD_lineart_get_parent_bounding_area(rb, pr->pos[0], pr->pos[1]);
if (ba1) {
- lineart_bounding_area_link_point_recursive(rb, ba1, rlc, pl);
+ lineart_bounding_area_link_point_recursive(rb, ba1, ec, pl);
}
if (ba2) {
- lineart_bounding_area_link_point_recursive(rb, ba2, rlc, pr);
+ lineart_bounding_area_link_point_recursive(rb, ba2, ec, pr);
}
}
void MOD_lineart_chain_split_for_fixed_occlusion(LineartRenderBuffer *rb)
{
- LineartLineChain *rlc, *new_rlc;
- LineartLineChainItem *rlci, *next_rlci;
+ LineartEdgeChain *ec, *new_rlc;
+ LineartEdgeChainItem *eci, *next_rlci;
ListBase swap = {0};
swap.first = rb->chains.first;
@@ -569,59 +569,59 @@ void MOD_lineart_chain_split_for_fixed_occlusion(LineartRenderBuffer *rb)
rb->chains.last = rb->chains.first = NULL;
- while ((rlc = BLI_pophead(&swap)) != NULL) {
- rlc->next = rlc->prev = NULL;
- BLI_addtail(&rb->chains, rlc);
- LineartLineChainItem *first_rlci = (LineartLineChainItem *)rlc->chain.first;
+ while ((ec = BLI_pophead(&swap)) != NULL) {
+ ec->next = ec->prev = NULL;
+ BLI_addtail(&rb->chains, ec);
+ LineartEdgeChainItem *first_rlci = (LineartEdgeChainItem *)ec->chain.first;
int fixed_occ = first_rlci->occlusion;
unsigned char fixed_mask = first_rlci->transparency_mask;
- rlc->level = fixed_occ;
- rlc->transparency_mask = fixed_mask;
- for (rlci = first_rlci->next; rlci; rlci = next_rlci) {
- next_rlci = rlci->next;
- if (rlci->occlusion != fixed_occ || rlci->transparency_mask != fixed_mask) {
+ ec->level = fixed_occ;
+ ec->transparency_mask = fixed_mask;
+ for (eci = first_rlci->next; eci; eci = next_rlci) {
+ next_rlci = eci->next;
+ if (eci->occlusion != fixed_occ || eci->transparency_mask != fixed_mask) {
if (next_rlci) {
- if (lineart_point_overlapping(next_rlci, rlci->pos[0], rlci->pos[1], 1e-5)) {
+ if (lineart_point_overlapping(next_rlci, eci->pos[0], eci->pos[1], 1e-5)) {
continue;
}
}
else {
/* Set the same occlusion level for the end vertex, so when further connection is needed
* the backwards occlusion info is also correct. */
- rlci->occlusion = fixed_occ;
- rlci->transparency_mask = fixed_mask;
+ eci->occlusion = fixed_occ;
+ eci->transparency_mask = fixed_mask;
/* No need to split at the last point anyway. */
break;
}
new_rlc = lineart_chain_create(rb);
- new_rlc->chain.first = rlci;
- new_rlc->chain.last = rlc->chain.last;
- rlc->chain.last = rlci->prev;
- ((LineartLineChainItem *)rlc->chain.last)->next = 0;
- rlci->prev = 0;
+ new_rlc->chain.first = eci;
+ new_rlc->chain.last = ec->chain.last;
+ ec->chain.last = eci->prev;
+ ((LineartEdgeChainItem *)ec->chain.last)->next = 0;
+ eci->prev = 0;
/* End the previous one. */
lineart_chain_append_point(rb,
- rlc,
- rlci->pos,
- rlci->gpos,
- rlci->normal,
- rlci->line_type,
+ ec,
+ eci->pos,
+ eci->gpos,
+ eci->normal,
+ eci->line_type,
fixed_occ,
fixed_mask,
- rlci->index);
- new_rlc->object_ref = rlc->object_ref;
- new_rlc->type = rlc->type;
- rlc = new_rlc;
- fixed_occ = rlci->occlusion;
- fixed_mask = rlci->transparency_mask;
- rlc->level = fixed_occ;
- rlc->transparency_mask = fixed_mask;
+ eci->index);
+ new_rlc->object_ref = ec->object_ref;
+ new_rlc->type = ec->type;
+ ec = new_rlc;
+ fixed_occ = eci->occlusion;
+ fixed_mask = eci->transparency_mask;
+ ec->level = fixed_occ;
+ ec->transparency_mask = fixed_mask;
}
}
}
- LISTBASE_FOREACH (LineartLineChain *, irlc, &rb->chains) {
- lineart_bounding_area_link_chain(rb, irlc);
+ LISTBASE_FOREACH (LineartEdgeChain *, iec, &rb->chains) {
+ lineart_bounding_area_link_chain(rb, iec);
}
}
@@ -629,12 +629,12 @@ void MOD_lineart_chain_split_for_fixed_occlusion(LineartRenderBuffer *rb)
* Note: segment type (crease/material/contour...) is ambiguous after this.
*/
static void lineart_chain_connect(LineartRenderBuffer *UNUSED(rb),
- LineartLineChain *onto,
- LineartLineChain *sub,
+ LineartEdgeChain *onto,
+ LineartEdgeChain *sub,
int reverse_1,
int reverse_2)
{
- LineartLineChainItem *rlci;
+ LineartEdgeChainItem *eci;
if (onto->type == LRT_EDGE_FLAG_INTERSECTION) {
if (sub->object_ref) {
onto->object_ref = sub->object_ref;
@@ -650,38 +650,38 @@ static void lineart_chain_connect(LineartRenderBuffer *UNUSED(rb),
if (reverse_2) { /* L--R R-L. */
BLI_listbase_reverse(&sub->chain);
}
- rlci = sub->chain.first;
- if (lineart_point_overlapping(onto->chain.last, rlci->pos[0], rlci->pos[1], 1e-5)) {
+ eci = sub->chain.first;
+ if (lineart_point_overlapping(onto->chain.last, eci->pos[0], eci->pos[1], 1e-5)) {
BLI_pophead(&sub->chain);
if (sub->chain.first == NULL) {
return;
}
}
- ((LineartLineChainItem *)onto->chain.last)->next = sub->chain.first;
- ((LineartLineChainItem *)sub->chain.first)->prev = onto->chain.last;
+ ((LineartEdgeChainItem *)onto->chain.last)->next = sub->chain.first;
+ ((LineartEdgeChainItem *)sub->chain.first)->prev = onto->chain.last;
onto->chain.last = sub->chain.last;
}
else { /* L-R L--R. */
if (!reverse_2) { /* R-L L--R. */
BLI_listbase_reverse(&sub->chain);
}
- rlci = onto->chain.first;
- if (lineart_point_overlapping(sub->chain.last, rlci->pos[0], rlci->pos[1], 1e-5)) {
+ eci = onto->chain.first;
+ if (lineart_point_overlapping(sub->chain.last, eci->pos[0], eci->pos[1], 1e-5)) {
BLI_pophead(&onto->chain);
if (onto->chain.first == NULL) {
return;
}
}
- ((LineartLineChainItem *)sub->chain.last)->next = onto->chain.first;
- ((LineartLineChainItem *)onto->chain.first)->prev = sub->chain.last;
+ ((LineartEdgeChainItem *)sub->chain.last)->next = onto->chain.first;
+ ((LineartEdgeChainItem *)onto->chain.first)->prev = sub->chain.last;
onto->chain.first = sub->chain.first;
}
}
static LineartChainRegisterEntry *lineart_chain_get_closest_cre(LineartRenderBuffer *rb,
LineartBoundingArea *ba,
- LineartLineChain *rlc,
- LineartLineChainItem *rlci,
+ LineartEdgeChain *ec,
+ LineartEdgeChainItem *eci,
int occlusion,
unsigned char transparency_mask,
float dist,
@@ -694,12 +694,12 @@ static LineartChainRegisterEntry *lineart_chain_get_closest_cre(LineartRenderBuf
/* Keep using for loop because `cre` could be removed from the iteration before getting to the
* next one. */
LISTBASE_FOREACH_MUTABLE (LineartChainRegisterEntry *, cre, &ba->linked_chains) {
- if (cre->rlc->object_ref != rlc->object_ref) {
+ if (cre->ec->object_ref != ec->object_ref) {
if (!rb->fuzzy_everything) {
if (rb->fuzzy_intersections) {
/* If none of those are intersection lines... */
- if ((!(cre->rlc->type & LRT_EDGE_FLAG_INTERSECTION)) &&
- (!(rlc->type & LRT_EDGE_FLAG_INTERSECTION))) {
+ if ((!(cre->ec->type & LRT_EDGE_FLAG_INTERSECTION)) &&
+ (!(ec->type & LRT_EDGE_FLAG_INTERSECTION))) {
continue; /* We don't want to chain along different objects at the moment. */
}
}
@@ -708,18 +708,18 @@ static LineartChainRegisterEntry *lineart_chain_get_closest_cre(LineartRenderBuf
}
}
}
- if (cre->rlc->picked || cre->picked) {
+ if (cre->ec->picked || cre->picked) {
continue;
}
- if (cre->rlc == rlc || (!cre->rlc->chain.first) || (cre->rlc->level != occlusion) ||
- (cre->rlc->transparency_mask != transparency_mask)) {
+ if (cre->ec == ec || (!cre->ec->chain.first) || (cre->ec->level != occlusion) ||
+ (cre->ec->transparency_mask != transparency_mask)) {
continue;
}
if (!rb->fuzzy_everything) {
- if (cre->rlc->type != rlc->type) {
+ if (cre->ec->type != ec->type) {
if (rb->fuzzy_intersections) {
- if (!(cre->rlc->type == LRT_EDGE_FLAG_INTERSECTION ||
- rlc->type == LRT_EDGE_FLAG_INTERSECTION)) {
+ if (!(cre->ec->type == LRT_EDGE_FLAG_INTERSECTION ||
+ ec->type == LRT_EDGE_FLAG_INTERSECTION)) {
continue; /* Fuzzy intersections but no intersection line found. */
}
}
@@ -729,7 +729,7 @@ static LineartChainRegisterEntry *lineart_chain_get_closest_cre(LineartRenderBuf
}
}
- float new_len = len_v2v2(cre->rlci->pos, rlci->pos);
+ float new_len = len_v2v2(cre->eci->pos, eci->pos);
if (new_len < dist) {
closest_cre = cre;
dist = new_len;
@@ -748,7 +748,7 @@ static LineartChainRegisterEntry *lineart_chain_get_closest_cre(LineartRenderBuf
LISTBASE_FOREACH (LinkData *, ld, list) { \
LineartBoundingArea *sba = (LineartBoundingArea *)ld->data; \
adjacent_closest = lineart_chain_get_closest_cre( \
- rb, sba, rlc, rlci, occlusion, transparency_mask, dist, &adjacent_new_len, ba); \
+ rb, sba, ec, eci, occlusion, transparency_mask, dist, &adjacent_new_len, ba); \
if (adjacent_new_len < dist) { \
dist = adjacent_new_len; \
closest_cre = adjacent_closest; \
@@ -756,10 +756,10 @@ static LineartChainRegisterEntry *lineart_chain_get_closest_cre(LineartRenderBuf
} \
}
if (!caller_ba) {
- LRT_TEST_ADJACENT_AREAS(rlci->pos[0] - ba->l, &ba->lp);
- LRT_TEST_ADJACENT_AREAS(ba->r - rlci->pos[0], &ba->rp);
- LRT_TEST_ADJACENT_AREAS(ba->u - rlci->pos[1], &ba->up);
- LRT_TEST_ADJACENT_AREAS(rlci->pos[1] - ba->b, &ba->bp);
+ LRT_TEST_ADJACENT_AREAS(eci->pos[0] - ba->l, &ba->lp);
+ LRT_TEST_ADJACENT_AREAS(ba->r - eci->pos[0], &ba->rp);
+ LRT_TEST_ADJACENT_AREAS(ba->u - eci->pos[1], &ba->up);
+ LRT_TEST_ADJACENT_AREAS(eci->pos[1] - ba->b, &ba->bp);
}
if (result_new_len) {
(*result_new_len) = dist;
@@ -774,8 +774,8 @@ static LineartChainRegisterEntry *lineart_chain_get_closest_cre(LineartRenderBuf
*/
void MOD_lineart_chain_connect(LineartRenderBuffer *rb)
{
- LineartLineChain *rlc;
- LineartLineChainItem *rlci_l, *rlci_r;
+ LineartEdgeChain *ec;
+ LineartEdgeChainItem *rlci_l, *rlci_r;
LineartBoundingArea *ba_l, *ba_r;
LineartChainRegisterEntry *closest_cre_l, *closest_cre_r, *closest_cre;
float dist = rb->chaining_image_threshold;
@@ -793,24 +793,24 @@ void MOD_lineart_chain_connect(LineartRenderBuffer *rb)
rb->chains.last = rb->chains.first = NULL;
- while ((rlc = BLI_pophead(&swap)) != NULL) {
- rlc->next = rlc->prev = NULL;
- if (rlc->picked) {
+ while ((ec = BLI_pophead(&swap)) != NULL) {
+ ec->next = ec->prev = NULL;
+ if (ec->picked) {
continue;
}
- BLI_addtail(&rb->chains, rlc);
+ BLI_addtail(&rb->chains, ec);
- occlusion = rlc->level;
- transparency_mask = rlc->transparency_mask;
+ occlusion = ec->level;
+ transparency_mask = ec->transparency_mask;
- rlci_l = rlc->chain.first;
- rlci_r = rlc->chain.last;
+ rlci_l = ec->chain.first;
+ rlci_r = ec->chain.last;
while ((ba_l = lineart_bounding_area_get_end_point(rb, rlci_l)) &&
(ba_r = lineart_bounding_area_get_end_point(rb, rlci_r))) {
closest_cre_l = lineart_chain_get_closest_cre(
- rb, ba_l, rlc, rlci_l, occlusion, transparency_mask, dist, &dist_l, NULL);
+ rb, ba_l, ec, rlci_l, occlusion, transparency_mask, dist, &dist_l, NULL);
closest_cre_r = lineart_chain_get_closest_cre(
- rb, ba_r, rlc, rlci_r, occlusion, transparency_mask, dist, &dist_r, NULL);
+ rb, ba_r, ec, rlci_r, occlusion, transparency_mask, dist, &dist_r, NULL);
if (closest_cre_l && closest_cre_r) {
if (dist_l < dist_r) {
closest_cre = closest_cre_l;
@@ -834,56 +834,56 @@ void MOD_lineart_chain_connect(LineartRenderBuffer *rb)
break;
}
closest_cre->picked = 1;
- closest_cre->rlc->picked = 1;
+ closest_cre->ec->picked = 1;
if (closest_cre->is_left) {
- lineart_chain_connect(rb, rlc, closest_cre->rlc, reverse_main, 0);
+ lineart_chain_connect(rb, ec, closest_cre->ec, reverse_main, 0);
}
else {
- lineart_chain_connect(rb, rlc, closest_cre->rlc, reverse_main, 1);
+ lineart_chain_connect(rb, ec, closest_cre->ec, reverse_main, 1);
}
- BLI_remlink(&swap, closest_cre->rlc);
- rlci_l = rlc->chain.first;
- rlci_r = rlc->chain.last;
+ BLI_remlink(&swap, closest_cre->ec);
+ rlci_l = ec->chain.first;
+ rlci_r = ec->chain.last;
}
- rlc->picked = 1;
+ ec->picked = 1;
}
}
/**
* Length is in image space.
*/
-float MOD_lineart_chain_compute_length(LineartLineChain *rlc)
+float MOD_lineart_chain_compute_length(LineartEdgeChain *ec)
{
- LineartLineChainItem *rlci;
+ LineartEdgeChainItem *eci;
float offset_accum = 0;
float dist;
float last_point[2];
- rlci = rlc->chain.first;
- copy_v2_v2(last_point, rlci->pos);
- for (rlci = rlc->chain.first; rlci; rlci = rlci->next) {
- dist = len_v2v2(rlci->pos, last_point);
+ eci = ec->chain.first;
+ copy_v2_v2(last_point, eci->pos);
+ for (eci = ec->chain.first; eci; eci = eci->next) {
+ dist = len_v2v2(eci->pos, last_point);
offset_accum += dist;
- copy_v2_v2(last_point, rlci->pos);
+ copy_v2_v2(last_point, eci->pos);
}
return offset_accum;
}
void MOD_lineart_chain_discard_short(LineartRenderBuffer *rb, const float threshold)
{
- LineartLineChain *rlc, *next_rlc;
- for (rlc = rb->chains.first; rlc; rlc = next_rlc) {
- next_rlc = rlc->next;
- if (MOD_lineart_chain_compute_length(rlc) < threshold) {
- BLI_remlink(&rb->chains, rlc);
+ LineartEdgeChain *ec, *next_rlc;
+ for (ec = rb->chains.first; ec; ec = next_rlc) {
+ next_rlc = ec->next;
+ if (MOD_lineart_chain_compute_length(ec) < threshold) {
+ BLI_remlink(&rb->chains, ec);
}
}
}
-int MOD_lineart_chain_count(const LineartLineChain *rlc)
+int MOD_lineart_chain_count(const LineartEdgeChain *ec)
{
int count = 0;
- LISTBASE_FOREACH (LineartLineChainItem *, rlci, &rlc->chain) {
+ LISTBASE_FOREACH (LineartEdgeChainItem *, eci, &ec->chain) {
count++;
}
return count;
@@ -894,8 +894,8 @@ void MOD_lineart_chain_clear_picked_flag(LineartRenderBuffer *rb)
if (rb == NULL) {
return;
}
- LISTBASE_FOREACH (LineartLineChain *, rlc, &rb->chains) {
- rlc->picked = 0;
+ LISTBASE_FOREACH (LineartEdgeChain *, ec, &rb->chains) {
+ ec->picked = 0;
}
}
@@ -905,8 +905,8 @@ void MOD_lineart_chain_clear_picked_flag(LineartRenderBuffer *rb)
*/
void MOD_lineart_chain_split_angle(LineartRenderBuffer *rb, float angle_threshold_rad)
{
- LineartLineChain *rlc, *new_rlc;
- LineartLineChainItem *rlci, *next_rlci, *prev_rlci;
+ LineartEdgeChain *ec, *new_rlc;
+ LineartEdgeChainItem *eci, *next_rlci, *prev_rlci;
ListBase swap = {0};
swap.first = rb->chains.first;
@@ -914,43 +914,43 @@ void MOD_lineart_chain_split_angle(LineartRenderBuffer *rb, float angle_threshol
rb->chains.last = rb->chains.first = NULL;
- while ((rlc = BLI_pophead(&swap)) != NULL) {
- rlc->next = rlc->prev = NULL;
- BLI_addtail(&rb->chains, rlc);
- LineartLineChainItem *first_rlci = (LineartLineChainItem *)rlc->chain.first;
- for (rlci = first_rlci->next; rlci; rlci = next_rlci) {
- next_rlci = rlci->next;
- prev_rlci = rlci->prev;
+ while ((ec = BLI_pophead(&swap)) != NULL) {
+ ec->next = ec->prev = NULL;
+ BLI_addtail(&rb->chains, ec);
+ LineartEdgeChainItem *first_rlci = (LineartEdgeChainItem *)ec->chain.first;
+ for (eci = first_rlci->next; eci; eci = next_rlci) {
+ next_rlci = eci->next;
+ prev_rlci = eci->prev;
float angle = M_PI;
if (next_rlci && prev_rlci) {
- angle = angle_v2v2v2(prev_rlci->pos, rlci->pos, next_rlci->pos);
+ angle = angle_v2v2v2(prev_rlci->pos, eci->pos, next_rlci->pos);
}
else {
break; /* No need to split at the last point anyway.*/
}
if (angle < angle_threshold_rad) {
new_rlc = lineart_chain_create(rb);
- new_rlc->chain.first = rlci;
- new_rlc->chain.last = rlc->chain.last;
- rlc->chain.last = rlci->prev;
- ((LineartLineChainItem *)rlc->chain.last)->next = 0;
- rlci->prev = 0;
+ new_rlc->chain.first = eci;
+ new_rlc->chain.last = ec->chain.last;
+ ec->chain.last = eci->prev;
+ ((LineartEdgeChainItem *)ec->chain.last)->next = 0;
+ eci->prev = 0;
/* End the previous one. */
lineart_chain_append_point(rb,
- rlc,
- rlci->pos,
- rlci->gpos,
- rlci->normal,
- rlci->line_type,
- rlc->level,
- rlci->transparency_mask,
- rlci->index);
- new_rlc->object_ref = rlc->object_ref;
- new_rlc->type = rlc->type;
- new_rlc->level = rlc->level;
- new_rlc->transparency_mask = rlc->transparency_mask;
- rlc = new_rlc;
+ ec,
+ eci->pos,
+ eci->gpos,
+ eci->normal,
+ eci->line_type,
+ ec->level,
+ eci->transparency_mask,
+ eci->index);
+ new_rlc->object_ref = ec->object_ref;
+ new_rlc->type = ec->type;
+ new_rlc->level = ec->level;
+ new_rlc->transparency_mask = ec->transparency_mask;
+ ec = new_rlc;
}
}
}
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
index 7fb3981d9dc..06d63c3ddab 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
@@ -29,6 +29,8 @@
#include "BLI_task.h"
#include "BLI_utildefines.h"
+#include "PIL_time.h"
+
#include "BKE_camera.h"
#include "BKE_collection.h"
#include "BKE_customdata.h"
@@ -52,8 +54,6 @@
#include "DNA_scene_types.h"
#include "MEM_guardedalloc.h"
-#include "BLI_math.h"
-
#include "bmesh.h"
#include "bmesh_class.h"
#include "bmesh_tools.h"
@@ -63,7 +63,7 @@
static LineartBoundingArea *lineart_edge_first_bounding_area(LineartRenderBuffer *rb,
LineartEdge *e);
-static void lineart_bounding_area_link_line(LineartRenderBuffer *rb,
+static void lineart_bounding_area_link_edge(LineartRenderBuffer *rb,
LineartBoundingArea *root_ba,
LineartEdge *e);
@@ -86,14 +86,14 @@ static bool lineart_get_edge_bounding_areas(LineartRenderBuffer *rb,
static void lineart_bounding_area_link_triangle(LineartRenderBuffer *rb,
LineartBoundingArea *root_ba,
- LineartTriangle *rt,
+ LineartTriangle *tri,
double *LRUB,
int recursive,
int recursive_level,
bool do_intersection);
static bool lineart_triangle_edge_image_space_occlusion(SpinLock *spl,
- const LineartTriangle *rt,
+ const LineartTriangle *tri,
const LineartEdge *e,
const double *override_camera_loc,
const bool override_cam_is_persp,
@@ -107,35 +107,35 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *spl,
static void lineart_add_edge_to_list(LineartRenderBuffer *rb, LineartEdge *e);
-static void lineart_discard_segment(LineartRenderBuffer *rb, LineartLineSegment *rls)
+static void lineart_discard_segment(LineartRenderBuffer *rb, LineartEdgeSegment *es)
{
BLI_spin_lock(&rb->lock_cuts);
- memset(rls, 0, sizeof(LineartLineSegment));
+ memset(es, 0, sizeof(LineartEdgeSegment));
/* Storing the node for potentially reuse the memory for new segment data.
* Line Art data is not freed after all calculations are done. */
- BLI_addtail(&rb->wasted_cuts, rls);
+ BLI_addtail(&rb->wasted_cuts, es);
BLI_spin_unlock(&rb->lock_cuts);
}
-static LineartLineSegment *lineart_give_segment(LineartRenderBuffer *rb)
+static LineartEdgeSegment *lineart_give_segment(LineartRenderBuffer *rb)
{
BLI_spin_lock(&rb->lock_cuts);
/* See if there is any already allocated memory we can reuse. */
if (rb->wasted_cuts.first) {
- LineartLineSegment *rls = (LineartLineSegment *)BLI_pophead(&rb->wasted_cuts);
+ LineartEdgeSegment *es = (LineartEdgeSegment *)BLI_pophead(&rb->wasted_cuts);
BLI_spin_unlock(&rb->lock_cuts);
- memset(rls, 0, sizeof(LineartLineSegment));
- return rls;
+ memset(es, 0, sizeof(LineartEdgeSegment));
+ return es;
}
BLI_spin_unlock(&rb->lock_cuts);
/* Otherwise allocate some new memory. */
- return (LineartLineSegment *)lineart_mem_aquire_thread(&rb->render_data_pool,
- sizeof(LineartLineSegment));
+ return (LineartEdgeSegment *)lineart_mem_acquire_thread(&rb->render_data_pool,
+ sizeof(LineartEdgeSegment));
}
/**
@@ -144,9 +144,9 @@ static LineartLineSegment *lineart_give_segment(LineartRenderBuffer *rb)
static void lineart_edge_cut(
LineartRenderBuffer *rb, LineartEdge *e, double start, double end, uchar transparency_mask)
{
- LineartLineSegment *rls, *irls, *next_rls, *prev_rls;
- LineartLineSegment *cut_start_before = 0, *cut_end_before = 0;
- LineartLineSegment *ns = 0, *ns2 = 0;
+ LineartEdgeSegment *es, *ies, *next_es, *prev_es;
+ LineartEdgeSegment *cut_start_before = 0, *cut_end_before = 0;
+ LineartEdgeSegment *ns = 0, *ns2 = 0;
int untouched = 0;
/* If for some reason the occlusion function may give a result that has zero length, or reversed
@@ -173,18 +173,18 @@ static void lineart_edge_cut(
/* Begin looking for starting position of the segment. */
/* Not using a list iteration macro because of it more clear when using for loops to iterate
* through the segments. */
- for (rls = e->segments.first; rls; rls = rls->next) {
- if (LRT_DOUBLE_CLOSE_ENOUGH(rls->at, start)) {
- cut_start_before = rls;
+ for (es = e->segments.first; es; es = es->next) {
+ if (LRT_DOUBLE_CLOSE_ENOUGH(es->at, start)) {
+ cut_start_before = es;
ns = cut_start_before;
break;
}
- if (rls->next == NULL) {
+ if (es->next == NULL) {
break;
}
- irls = rls->next;
- if (irls->at > start + 1e-09 && start > rls->at) {
- cut_start_before = irls;
+ ies = es->next;
+ if (ies->at > start + 1e-09 && start > es->at) {
+ cut_start_before = ies;
ns = lineart_give_segment(rb);
break;
}
@@ -192,25 +192,25 @@ static void lineart_edge_cut(
if (!cut_start_before && LRT_DOUBLE_CLOSE_ENOUGH(1, end)) {
untouched = 1;
}
- for (rls = cut_start_before; rls; rls = rls->next) {
+ for (es = cut_start_before; es; es = es->next) {
/* We tried to cut at existing cutting point (e.g. where the line's occluded by a triangle
* strip). */
- if (LRT_DOUBLE_CLOSE_ENOUGH(rls->at, end)) {
- cut_end_before = rls;
+ if (LRT_DOUBLE_CLOSE_ENOUGH(es->at, end)) {
+ cut_end_before = es;
ns2 = cut_end_before;
break;
}
- /* This check is to prevent `rls->at == 1.0` (where we don't need to cut because we are at the
+ /* This check is to prevent `es->at == 1.0` (where we don't need to cut because we are at the
* end point). */
- if (!rls->next && LRT_DOUBLE_CLOSE_ENOUGH(1, end)) {
- cut_end_before = rls;
+ if (!es->next && LRT_DOUBLE_CLOSE_ENOUGH(1, end)) {
+ cut_end_before = es;
ns2 = cut_end_before;
untouched = 1;
break;
}
/* When an actual cut is needed in the line. */
- if (rls->at > end) {
- cut_end_before = rls;
+ if (es->at > end) {
+ cut_end_before = es;
ns2 = lineart_give_segment(rb);
break;
}
@@ -233,9 +233,9 @@ static void lineart_edge_cut(
if (cut_start_before) {
if (cut_start_before != ns) {
/* Insert cutting points for when a new cut is needed. */
- irls = cut_start_before->prev ? cut_start_before->prev : NULL;
- ns->occlusion = irls ? irls->occlusion : 0;
- ns->transparency_mask = irls->transparency_mask;
+ ies = cut_start_before->prev ? cut_start_before->prev : NULL;
+ ns->occlusion = ies ? ies->occlusion : 0;
+ ns->transparency_mask = ies->transparency_mask;
BLI_insertlinkbefore(&e->segments, cut_start_before, ns);
}
/* Otherwise we already found a existing cutting point, no need to insert a new one. */
@@ -243,24 +243,24 @@ static void lineart_edge_cut(
else {
/* We have yet to reach a existing cutting point even after we searched the whole line, so we
* append the new cut to the end. */
- irls = e->segments.last;
- ns->occlusion = irls->occlusion;
- ns->transparency_mask = irls->transparency_mask;
+ ies = e->segments.last;
+ ns->occlusion = ies->occlusion;
+ ns->transparency_mask = ies->transparency_mask;
BLI_addtail(&e->segments, ns);
}
if (cut_end_before) {
/* The same manipulation as on "cut_start_before". */
if (cut_end_before != ns2) {
- irls = cut_end_before->prev ? cut_end_before->prev : NULL;
- ns2->occlusion = irls ? irls->occlusion : 0;
- ns2->transparency_mask = irls ? irls->transparency_mask : 0;
+ ies = cut_end_before->prev ? cut_end_before->prev : NULL;
+ ns2->occlusion = ies ? ies->occlusion : 0;
+ ns2->transparency_mask = ies ? ies->transparency_mask : 0;
BLI_insertlinkbefore(&e->segments, cut_end_before, ns2);
}
}
else {
- irls = e->segments.last;
- ns2->occlusion = irls->occlusion;
- ns2->transparency_mask = irls->transparency_mask;
+ ies = e->segments.last;
+ ns2->occlusion = ies->occlusion;
+ ns2->transparency_mask = ies->transparency_mask;
BLI_addtail(&e->segments, ns2);
}
@@ -276,29 +276,29 @@ static void lineart_edge_cut(
}
/* Register 1 level of occlusion for all touched segments. */
- for (rls = ns; rls && rls != ns2; rls = rls->next) {
- rls->occlusion++;
- rls->transparency_mask |= transparency_mask;
+ for (es = ns; es && es != ns2; es = es->next) {
+ es->occlusion++;
+ es->transparency_mask |= transparency_mask;
}
/* Reduce adjacent cutting points of the same level, which saves memory. */
char min_occ = 127;
- prev_rls = NULL;
- for (rls = e->segments.first; rls; rls = next_rls) {
- next_rls = rls->next;
+ prev_es = NULL;
+ for (es = e->segments.first; es; es = next_es) {
+ next_es = es->next;
- if (prev_rls && prev_rls->occlusion == rls->occlusion &&
- prev_rls->transparency_mask == rls->transparency_mask) {
- BLI_remlink(&e->segments, rls);
+ if (prev_es && prev_es->occlusion == es->occlusion &&
+ prev_es->transparency_mask == es->transparency_mask) {
+ BLI_remlink(&e->segments, es);
/* This puts the node back to the render buffer, if more cut happens, these unused nodes get
* picked first. */
- lineart_discard_segment(rb, rls);
+ lineart_discard_segment(rb, es);
continue;
}
- min_occ = MIN2(min_occ, rls->occlusion);
+ min_occ = MIN2(min_occ, es->occlusion);
- prev_rls = rls;
+ prev_es = es;
}
e->min_occ = min_occ;
}
@@ -306,12 +306,42 @@ static void lineart_edge_cut(
/**
* To see if given line is connected to an adjacent intersection line.
*/
-BLI_INLINE bool lineart_occlusion_is_adjacent_intersection(LineartEdge *e, LineartTriangle *rt)
+BLI_INLINE bool lineart_occlusion_is_adjacent_intersection(LineartEdge *e, LineartTriangle *tri)
{
LineartVertIntersection *v1 = (void *)e->v1;
LineartVertIntersection *v2 = (void *)e->v2;
- return ((v1->base.flag && v1->intersecting_with == rt) ||
- (v2->base.flag && v2->intersecting_with == rt));
+ return ((v1->base.flag && v1->intersecting_with == tri) ||
+ (v2->base.flag && v2->intersecting_with == tri));
+}
+
+static void lineart_bounding_area_triangle_add(LineartRenderBuffer *rb,
+ LineartBoundingArea *ba,
+ LineartTriangle *tri)
+{
+ if (ba->triangle_count >= ba->max_triangle_count) {
+ LineartTriangle **new_array = lineart_mem_acquire(
+ &rb->render_data_pool, sizeof(LineartTriangle *) * ba->max_triangle_count * 2);
+ memcpy(new_array, ba->linked_triangles, sizeof(LineartTriangle *) * ba->max_triangle_count);
+ ba->max_triangle_count *= 2;
+ ba->linked_triangles = new_array;
+ }
+ ba->linked_triangles[ba->triangle_count] = tri;
+ ba->triangle_count++;
+}
+
+static void lineart_bounding_area_line_add(LineartRenderBuffer *rb,
+ LineartBoundingArea *ba,
+ LineartEdge *e)
+{
+ if (ba->line_count >= ba->max_line_count) {
+ LineartEdge **new_array = lineart_mem_acquire(&rb->render_data_pool,
+ sizeof(LineartEdge *) * ba->max_line_count * 2);
+ memcpy(new_array, ba->linked_lines, sizeof(LineartEdge *) * ba->max_line_count);
+ ba->max_line_count *= 2;
+ ba->linked_lines = new_array;
+ }
+ ba->linked_lines[ba->line_count] = e;
+ ba->line_count++;
}
static void lineart_occlusion_single_line(LineartRenderBuffer *rb, LineartEdge *e, int thread_id)
@@ -319,7 +349,7 @@ static void lineart_occlusion_single_line(LineartRenderBuffer *rb, LineartEdge *
double x = e->v1->fbcoord[0], y = e->v1->fbcoord[1];
LineartBoundingArea *ba = lineart_edge_first_bounding_area(rb, e);
LineartBoundingArea *nba = ba;
- LineartTriangleThread *rt;
+ LineartTriangleThread *tri;
/* These values are used for marching along the line. */
double l, r;
@@ -334,16 +364,16 @@ static void lineart_occlusion_single_line(LineartRenderBuffer *rb, LineartEdge *
while (nba) {
- LISTBASE_FOREACH (LinkData *, lip, &nba->linked_triangles) {
- rt = lip->data;
+ for (int i = 0; i < nba->triangle_count; i++) {
+ tri = (LineartTriangleThread *)nba->linked_triangles[i];
/* If we are already testing the line in this thread, then don't do it. */
- if (rt->testing_e[thread_id] == e || (rt->base.flags & LRT_TRIANGLE_INTERSECTION_ONLY) ||
- lineart_occlusion_is_adjacent_intersection(e, (LineartTriangle *)rt)) {
+ if (tri->testing_e[thread_id] == e || (tri->base.flags & LRT_TRIANGLE_INTERSECTION_ONLY) ||
+ lineart_occlusion_is_adjacent_intersection(e, (LineartTriangle *)tri)) {
continue;
}
- rt->testing_e[thread_id] = e;
+ tri->testing_e[thread_id] = e;
if (lineart_triangle_edge_image_space_occlusion(&rb->lock_task,
- (const LineartTriangle *)rt,
+ (const LineartTriangle *)tri,
e,
rb->camera_pos,
rb->cam_is_persp,
@@ -354,7 +384,7 @@ static void lineart_occlusion_single_line(LineartRenderBuffer *rb, LineartEdge *
rb->shift_y,
&l,
&r)) {
- lineart_edge_cut(rb, e, l, r, rt->base.transparency_mask);
+ lineart_edge_cut(rb, e, l, r, tri->base.transparency_mask);
if (e->min_occ > rb->max_occlusion_level) {
/* No need to calculate any longer on this line because no level more than set value is
* going to show up in the rendered result. */
@@ -376,18 +406,18 @@ static int lineart_occlusion_make_task_info(LineartRenderBuffer *rb, LineartRend
BLI_spin_lock(&rb->lock_task);
#define LRT_ASSIGN_OCCLUSION_TASK(name) \
- if (rb->name##_managed) { \
- data = rb->name##_managed; \
- rti->name = (void *)data; \
+ if (rb->name.last) { \
+ data = rb->name.last; \
+ rti->name.first = (void *)data; \
for (i = 0; i < LRT_THREAD_EDGE_COUNT && data; i++) { \
data = data->next; \
} \
- rti->name##_end = data; \
- rb->name##_managed = data; \
+ rti->name.last = data; \
+ rb->name.last = data; \
res = 1; \
} \
else { \
- rti->name = NULL; \
+ rti->name.first = rti->name.last = NULL; \
}
LRT_ASSIGN_OCCLUSION_TASK(contour);
@@ -410,23 +440,23 @@ static void lineart_occlusion_worker(TaskPool *__restrict UNUSED(pool), LineartR
while (lineart_occlusion_make_task_info(rb, rti)) {
- for (eip = rti->contour; eip && eip != rti->contour_end; eip = eip->next) {
+ for (eip = rti->contour.first; eip && eip != rti->contour.last; eip = eip->next) {
lineart_occlusion_single_line(rb, eip, rti->thread_id);
}
- for (eip = rti->crease; eip && eip != rti->crease_end; eip = eip->next) {
+ for (eip = rti->crease.first; eip && eip != rti->crease.last; eip = eip->next) {
lineart_occlusion_single_line(rb, eip, rti->thread_id);
}
- for (eip = rti->intersection; eip && eip != rti->intersection_end; eip = eip->next) {
+ for (eip = rti->intersection.first; eip && eip != rti->intersection.last; eip = eip->next) {
lineart_occlusion_single_line(rb, eip, rti->thread_id);
}
- for (eip = rti->material; eip && eip != rti->material_end; eip = eip->next) {
+ for (eip = rti->material.first; eip && eip != rti->material.last; eip = eip->next) {
lineart_occlusion_single_line(rb, eip, rti->thread_id);
}
- for (eip = rti->edge_mark; eip && eip != rti->edge_mark_end; eip = eip->next) {
+ for (eip = rti->edge_mark.first; eip && eip != rti->edge_mark.last; eip = eip->next) {
lineart_occlusion_single_line(rb, eip, rti->thread_id);
}
}
@@ -444,13 +474,15 @@ static void lineart_main_occlusion_begin(LineartRenderBuffer *rb)
"Task Pool");
int i;
- rb->contour_managed = rb->contours;
- rb->crease_managed = rb->crease_lines;
- rb->intersection_managed = rb->intersection_lines;
- rb->material_managed = rb->material_lines;
- rb->edge_mark_managed = rb->edge_marks;
+ /* The "last" entry is used to store worker progress in the whole list.
+ * These list themselves are single-direction linked, with list.first being the head. */
+ rb->contour.last = rb->contour.first;
+ rb->crease.last = rb->crease.first;
+ rb->intersection.last = rb->intersection.first;
+ rb->material.last = rb->material.first;
+ rb->edge_mark.last = rb->edge_mark.first;
- TaskPool *tp = BLI_task_pool_create(NULL, TASK_PRIORITY_HIGH);
+ TaskPool *tp = BLI_task_pool_create(NULL, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON);
for (i = 0; i < thread_count; i++) {
rti[i].thread_id = i;
@@ -627,75 +659,75 @@ static bool lineart_point_inside_triangle3d(double v[3], double v0[3], double v1
*/
static LineartElementLinkNode *lineart_memory_get_triangle_space(LineartRenderBuffer *rb)
{
- LineartElementLinkNode *reln;
+ LineartElementLinkNode *eln;
/* We don't need to allocate a whole bunch of triangles because the amount of clipped triangles
* are relatively small. */
- LineartTriangle *render_triangles = lineart_mem_aquire(&rb->render_data_pool,
- 64 * rb->triangle_size);
+ LineartTriangle *render_triangles = lineart_mem_acquire(&rb->render_data_pool,
+ 64 * rb->triangle_size);
- reln = lineart_list_append_pointer_pool_sized(&rb->triangle_buffer_pointers,
- &rb->render_data_pool,
- render_triangles,
- sizeof(LineartElementLinkNode));
- reln->element_count = 64;
- reln->flags |= LRT_ELEMENT_IS_ADDITIONAL;
+ eln = lineart_list_append_pointer_pool_sized(&rb->triangle_buffer_pointers,
+ &rb->render_data_pool,
+ render_triangles,
+ sizeof(LineartElementLinkNode));
+ eln->element_count = 64;
+ eln->flags |= LRT_ELEMENT_IS_ADDITIONAL;
- return reln;
+ return eln;
}
static LineartElementLinkNode *lineart_memory_get_vert_space(LineartRenderBuffer *rb)
{
- LineartElementLinkNode *reln;
+ LineartElementLinkNode *eln;
- LineartVert *render_vertices = lineart_mem_aquire(&rb->render_data_pool,
- sizeof(LineartVert) * 64);
+ LineartVert *render_vertices = lineart_mem_acquire(&rb->render_data_pool,
+ sizeof(LineartVert) * 64);
- reln = lineart_list_append_pointer_pool_sized(&rb->vertex_buffer_pointers,
- &rb->render_data_pool,
- render_vertices,
- sizeof(LineartElementLinkNode));
- reln->element_count = 64;
- reln->flags |= LRT_ELEMENT_IS_ADDITIONAL;
+ eln = lineart_list_append_pointer_pool_sized(&rb->vertex_buffer_pointers,
+ &rb->render_data_pool,
+ render_vertices,
+ sizeof(LineartElementLinkNode));
+ eln->element_count = 64;
+ eln->flags |= LRT_ELEMENT_IS_ADDITIONAL;
- return reln;
+ return eln;
}
static LineartElementLinkNode *lineart_memory_get_edge_space(LineartRenderBuffer *rb)
{
- LineartElementLinkNode *reln;
+ LineartElementLinkNode *eln;
- LineartEdge *render_edges = lineart_mem_aquire(&rb->render_data_pool, sizeof(LineartEdge) * 64);
+ LineartEdge *render_edges = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdge) * 64);
- reln = lineart_list_append_pointer_pool_sized(&rb->line_buffer_pointers,
- &rb->render_data_pool,
- render_edges,
- sizeof(LineartElementLinkNode));
- reln->element_count = 64;
- reln->crease_threshold = rb->crease_threshold;
- reln->flags |= LRT_ELEMENT_IS_ADDITIONAL;
+ eln = lineart_list_append_pointer_pool_sized(&rb->line_buffer_pointers,
+ &rb->render_data_pool,
+ render_edges,
+ sizeof(LineartElementLinkNode));
+ eln->element_count = 64;
+ eln->crease_threshold = rb->crease_threshold;
+ eln->flags |= LRT_ELEMENT_IS_ADDITIONAL;
- return reln;
+ return eln;
}
-static void lineart_triangle_post(LineartTriangle *rt, LineartTriangle *orig)
+static void lineart_triangle_post(LineartTriangle *tri, LineartTriangle *orig)
{
/* Just re-assign normal and set cull flag. */
- copy_v3_v3_db(rt->gn, orig->gn);
- rt->flags = LRT_CULL_GENERATED;
+ copy_v3_v3_db(tri->gn, orig->gn);
+ tri->flags = LRT_CULL_GENERATED;
}
-static void lineart_triangle_set_cull_flag(LineartTriangle *rt, uchar flag)
+static void lineart_triangle_set_cull_flag(LineartTriangle *tri, uchar flag)
{
- uchar intersection_only = (rt->flags & LRT_TRIANGLE_INTERSECTION_ONLY);
- rt->flags = flag;
- rt->flags |= intersection_only;
+ uchar intersection_only = (tri->flags & LRT_TRIANGLE_INTERSECTION_ONLY);
+ tri->flags = flag;
+ tri->flags |= intersection_only;
}
-static bool lineart_edge_match(LineartTriangle *rt, LineartEdge *e, int v1, int v2)
+static bool lineart_edge_match(LineartTriangle *tri, LineartEdge *e, int v1, int v2)
{
- return ((rt->v[v1] == e->v1 && rt->v[v2] == e->v2) ||
- (rt->v[v2] == e->v1 && rt->v[v1] == e->v2));
+ return ((tri->v[v1] == e->v1 && tri->v[v2] == e->v2) ||
+ (tri->v[v2] == e->v1 && tri->v[v1] == e->v2));
}
/**
@@ -703,7 +735,7 @@ static bool lineart_edge_match(LineartTriangle *rt, LineartEdge *e, int v1, int
* reversed by the caller so don't need to implement one in a different direction.
*/
static void lineart_triangle_cull_single(LineartRenderBuffer *rb,
- LineartTriangle *rt,
+ LineartTriangle *tri,
int in0,
int in1,
int in2,
@@ -728,67 +760,67 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb,
char new_flag = 0;
LineartEdge *new_e, *e, *old_e;
- LineartLineSegment *rls;
- LineartTriangleAdjacent *rta;
+ LineartEdgeSegment *es;
+ LineartTriangleAdjacent *ta;
- if (rt->flags & (LRT_CULL_USED | LRT_CULL_GENERATED | LRT_CULL_DISCARD)) {
+ if (tri->flags & (LRT_CULL_USED | LRT_CULL_GENERATED | LRT_CULL_DISCARD)) {
return;
}
- /* See definition of rt->intersecting_verts and the usage in
+ /* See definition of tri->intersecting_verts and the usage in
* lineart_geometry_object_load() for details. */
- rta = (void *)rt->intersecting_verts;
+ ta = (void *)tri->intersecting_verts;
- LineartVert *rv = &((LineartVert *)v_eln->pointer)[v_count];
- LineartTriangle *rt1 = (void *)(((uchar *)t_eln->pointer) + rb->triangle_size * t_count);
- LineartTriangle *rt2 = (void *)(((uchar *)t_eln->pointer) + rb->triangle_size * (t_count + 1));
+ LineartVert *vt = &((LineartVert *)v_eln->pointer)[v_count];
+ LineartTriangle *tri1 = (void *)(((uchar *)t_eln->pointer) + rb->triangle_size * t_count);
+ LineartTriangle *tri2 = (void *)(((uchar *)t_eln->pointer) + rb->triangle_size * (t_count + 1));
new_e = &((LineartEdge *)e_eln->pointer)[e_count];
- /* Init `rl` to the last `rl` entry. */
+ /* Init `edge` to the last `edge` entry. */
e = new_e;
-#define INCREASE_RL \
- e_count++; \
+#define INCREASE_EDGE \
v1_obi = e->v1_obindex; \
v2_obi = e->v2_obindex; \
new_e = &((LineartEdge *)e_eln->pointer)[e_count]; \
+ e_count++; \
e = new_e; \
e->v1_obindex = v1_obi; \
e->v2_obindex = v2_obi; \
- rls = lineart_mem_aquire(&rb->render_data_pool, sizeof(LineartLineSegment)); \
- BLI_addtail(&e->segments, rls);
+ es = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdgeSegment)); \
+ BLI_addtail(&e->segments, es);
-#define SELECT_RL(e_num, v1_link, v2_link, newrt) \
- if (rta->e[e_num]) { \
- old_e = rta->e[e_num]; \
+#define SELECT_EDGE(e_num, v1_link, v2_link, new_tri) \
+ if (ta->e[e_num]) { \
+ old_e = ta->e[e_num]; \
new_flag = old_e->flags; \
old_e->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \
- INCREASE_RL \
+ INCREASE_EDGE \
e->v1 = (v1_link); \
e->v2 = (v2_link); \
e->flags = new_flag; \
e->object_ref = ob; \
- e->t1 = ((old_e->t1 == rt) ? (newrt) : (old_e->t1)); \
- e->t2 = ((old_e->t2 == rt) ? (newrt) : (old_e->t2)); \
+ e->t1 = ((old_e->t1 == tri) ? (new_tri) : (old_e->t1)); \
+ e->t2 = ((old_e->t2 == tri) ? (new_tri) : (old_e->t2)); \
lineart_add_edge_to_list(rb, e); \
}
-#define RELINK_RL(e_num, newrt) \
- if (rta->e[e_num]) { \
- old_e = rta->e[e_num]; \
- old_e->t1 = ((old_e->t1 == rt) ? (newrt) : (old_e->t1)); \
- old_e->t2 = ((old_e->t2 == rt) ? (newrt) : (old_e->t2)); \
+#define RELINK_EDGE(e_num, new_tri) \
+ if (ta->e[e_num]) { \
+ old_e = ta->e[e_num]; \
+ old_e->t1 = ((old_e->t1 == tri) ? (new_tri) : (old_e->t1)); \
+ old_e->t2 = ((old_e->t2 == tri) ? (new_tri) : (old_e->t2)); \
}
-#define REMOVE_TRIANGLE_RL \
- if (rta->e[0]) { \
- rta->e[0]->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \
+#define REMOVE_TRIANGLE_EDGE \
+ if (ta->e[0]) { \
+ ta->e[0]->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \
} \
- if (rta->e[1]) { \
- rta->e[1]->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \
+ if (ta->e[1]) { \
+ ta->e[1]->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \
} \
- if (rta->e[2]) { \
- rta->e[2]->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \
+ if (ta->e[2]) { \
+ ta->e[2]->flags = LRT_EDGE_FLAG_CHAIN_PICKED; \
}
switch (in0 + in1 + in2) {
@@ -797,13 +829,13 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb,
case 3:
/* Triangle completely behind near plane, throw it away
* also remove render lines form being computed. */
- lineart_triangle_set_cull_flag(rt, LRT_CULL_DISCARD);
- REMOVE_TRIANGLE_RL
+ lineart_triangle_set_cull_flag(tri, LRT_CULL_DISCARD);
+ REMOVE_TRIANGLE_EDGE
return;
case 2:
/* Two points behind near plane, cut those and
* generate 2 new points, 3 lines and 1 triangle. */
- lineart_triangle_set_cull_flag(rt, LRT_CULL_USED);
+ lineart_triangle_set_cull_flag(tri, LRT_CULL_USED);
/**
* (!in0) means "when point 0 is visible".
@@ -828,136 +860,136 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb,
if (!in0) {
/* Cut point for line 2---|-----0. */
- sub_v3_v3v3_db(vv1, rt->v[0]->gloc, cam_pos);
- sub_v3_v3v3_db(vv2, cam_pos, rt->v[2]->gloc);
+ sub_v3_v3v3_db(vv1, tri->v[0]->gloc, cam_pos);
+ sub_v3_v3v3_db(vv2, cam_pos, tri->v[2]->gloc);
dot1 = dot_v3v3_db(vv1, view_dir);
dot2 = dot_v3v3_db(vv2, view_dir);
a = dot1 / (dot1 + dot2);
/* Assign it to a new point. */
- interp_v3_v3v3_db(rv[0].gloc, rt->v[0]->gloc, rt->v[2]->gloc, a);
- mul_v4_m4v3_db(rv[0].fbcoord, vp, rv[0].gloc);
- rv[0].index = rt->v[2]->index;
+ interp_v3_v3v3_db(vt[0].gloc, tri->v[0]->gloc, tri->v[2]->gloc, a);
+ mul_v4_m4v3_db(vt[0].fbcoord, vp, vt[0].gloc);
+ vt[0].index = tri->v[2]->index;
/* Cut point for line 1---|-----0. */
- sub_v3_v3v3_db(vv1, rt->v[0]->gloc, cam_pos);
- sub_v3_v3v3_db(vv2, cam_pos, rt->v[1]->gloc);
+ sub_v3_v3v3_db(vv1, tri->v[0]->gloc, cam_pos);
+ sub_v3_v3v3_db(vv2, cam_pos, tri->v[1]->gloc);
dot1 = dot_v3v3_db(vv1, view_dir);
dot2 = dot_v3v3_db(vv2, view_dir);
a = dot1 / (dot1 + dot2);
/* Assign it to another new point. */
- interp_v3_v3v3_db(rv[1].gloc, rt->v[0]->gloc, rt->v[1]->gloc, a);
- mul_v4_m4v3_db(rv[1].fbcoord, vp, rv[1].gloc);
- rv[1].index = rt->v[1]->index;
+ interp_v3_v3v3_db(vt[1].gloc, tri->v[0]->gloc, tri->v[1]->gloc, a);
+ mul_v4_m4v3_db(vt[1].fbcoord, vp, vt[1].gloc);
+ vt[1].index = tri->v[1]->index;
/* New line connecting two new points. */
- INCREASE_RL
+ INCREASE_EDGE
if (allow_boundaries) {
e->flags = LRT_EDGE_FLAG_CONTOUR;
- lineart_prepend_edge_direct(&rb->contours, e);
+ lineart_prepend_edge_direct(&rb->contour.first, e);
}
/* NOTE: inverting `e->v1/v2` (left/right point) doesn't matter as long as
- * `rt->rl` and `rt->v` has the same sequence. and the winding direction
+ * `tri->edge` and `tri->v` has the same sequence. and the winding direction
* can be either CW or CCW but needs to be consistent throughout the calculation. */
- e->v1 = &rv[1];
- e->v2 = &rv[0];
+ e->v1 = &vt[1];
+ e->v2 = &vt[0];
/* Only one adjacent triangle, because the other side is the near plane. */
/* Use `tl` or `tr` doesn't matter. */
- e->t1 = rt1;
+ e->t1 = tri1;
e->object_ref = ob;
/* New line connecting original point 0 and a new point, only when it's a selected line. */
- SELECT_RL(2, rt->v[0], &rv[0], rt1)
+ SELECT_EDGE(2, tri->v[0], &vt[0], tri1)
/* New line connecting original point 0 and another new point. */
- SELECT_RL(0, rt->v[0], &rv[1], rt1)
+ SELECT_EDGE(0, tri->v[0], &vt[1], tri1)
/* Re-assign triangle point array to two new points. */
- rt1->v[0] = rt->v[0];
- rt1->v[1] = &rv[1];
- rt1->v[2] = &rv[0];
+ tri1->v[0] = tri->v[0];
+ tri1->v[1] = &vt[1];
+ tri1->v[2] = &vt[0];
- lineart_triangle_post(rt1, rt);
+ lineart_triangle_post(tri1, tri);
v_count += 2;
t_count += 1;
}
else if (!in2) {
- sub_v3_v3v3_db(vv1, rt->v[2]->gloc, cam_pos);
- sub_v3_v3v3_db(vv2, cam_pos, rt->v[0]->gloc);
+ sub_v3_v3v3_db(vv1, tri->v[2]->gloc, cam_pos);
+ sub_v3_v3v3_db(vv2, cam_pos, tri->v[0]->gloc);
dot1 = dot_v3v3_db(vv1, view_dir);
dot2 = dot_v3v3_db(vv2, view_dir);
a = dot1 / (dot1 + dot2);
- interp_v3_v3v3_db(rv[0].gloc, rt->v[2]->gloc, rt->v[0]->gloc, a);
- mul_v4_m4v3_db(rv[0].fbcoord, vp, rv[0].gloc);
- rv[0].index = rt->v[0]->index;
+ interp_v3_v3v3_db(vt[0].gloc, tri->v[2]->gloc, tri->v[0]->gloc, a);
+ mul_v4_m4v3_db(vt[0].fbcoord, vp, vt[0].gloc);
+ vt[0].index = tri->v[0]->index;
- sub_v3_v3v3_db(vv1, rt->v[2]->gloc, cam_pos);
- sub_v3_v3v3_db(vv2, cam_pos, rt->v[1]->gloc);
+ sub_v3_v3v3_db(vv1, tri->v[2]->gloc, cam_pos);
+ sub_v3_v3v3_db(vv2, cam_pos, tri->v[1]->gloc);
dot1 = dot_v3v3_db(vv1, view_dir);
dot2 = dot_v3v3_db(vv2, view_dir);
a = dot1 / (dot1 + dot2);
- interp_v3_v3v3_db(rv[1].gloc, rt->v[2]->gloc, rt->v[1]->gloc, a);
- mul_v4_m4v3_db(rv[1].fbcoord, vp, rv[1].gloc);
- rv[1].index = rt->v[1]->index;
+ interp_v3_v3v3_db(vt[1].gloc, tri->v[2]->gloc, tri->v[1]->gloc, a);
+ mul_v4_m4v3_db(vt[1].fbcoord, vp, vt[1].gloc);
+ vt[1].index = tri->v[1]->index;
- INCREASE_RL
+ INCREASE_EDGE
if (allow_boundaries) {
e->flags = LRT_EDGE_FLAG_CONTOUR;
- lineart_prepend_edge_direct(&rb->contours, e);
+ lineart_prepend_edge_direct(&rb->contour.first, e);
}
- e->v1 = &rv[0];
- e->v2 = &rv[1];
- e->t1 = rt1;
+ e->v1 = &vt[0];
+ e->v2 = &vt[1];
+ e->t1 = tri1;
e->object_ref = ob;
- SELECT_RL(2, rt->v[2], &rv[0], rt1)
- SELECT_RL(1, rt->v[2], &rv[1], rt1)
+ SELECT_EDGE(2, tri->v[2], &vt[0], tri1)
+ SELECT_EDGE(1, tri->v[2], &vt[1], tri1)
- rt1->v[0] = &rv[0];
- rt1->v[1] = &rv[1];
- rt1->v[2] = rt->v[2];
+ tri1->v[0] = &vt[0];
+ tri1->v[1] = &vt[1];
+ tri1->v[2] = tri->v[2];
- lineart_triangle_post(rt1, rt);
+ lineart_triangle_post(tri1, tri);
v_count += 2;
t_count += 1;
}
else if (!in1) {
- sub_v3_v3v3_db(vv1, rt->v[1]->gloc, cam_pos);
- sub_v3_v3v3_db(vv2, cam_pos, rt->v[2]->gloc);
+ sub_v3_v3v3_db(vv1, tri->v[1]->gloc, cam_pos);
+ sub_v3_v3v3_db(vv2, cam_pos, tri->v[2]->gloc);
dot1 = dot_v3v3_db(vv1, view_dir);
dot2 = dot_v3v3_db(vv2, view_dir);
a = dot1 / (dot1 + dot2);
- interp_v3_v3v3_db(rv[0].gloc, rt->v[1]->gloc, rt->v[2]->gloc, a);
- mul_v4_m4v3_db(rv[0].fbcoord, vp, rv[0].gloc);
- rv[0].index = rt->v[2]->index;
+ interp_v3_v3v3_db(vt[0].gloc, tri->v[1]->gloc, tri->v[2]->gloc, a);
+ mul_v4_m4v3_db(vt[0].fbcoord, vp, vt[0].gloc);
+ vt[0].index = tri->v[2]->index;
- sub_v3_v3v3_db(vv1, rt->v[1]->gloc, cam_pos);
- sub_v3_v3v3_db(vv2, cam_pos, rt->v[0]->gloc);
+ sub_v3_v3v3_db(vv1, tri->v[1]->gloc, cam_pos);
+ sub_v3_v3v3_db(vv2, cam_pos, tri->v[0]->gloc);
dot1 = dot_v3v3_db(vv1, view_dir);
dot2 = dot_v3v3_db(vv2, view_dir);
a = dot1 / (dot1 + dot2);
- interp_v3_v3v3_db(rv[1].gloc, rt->v[1]->gloc, rt->v[0]->gloc, a);
- mul_v4_m4v3_db(rv[1].fbcoord, vp, rv[1].gloc);
- rv[1].index = rt->v[0]->index;
+ interp_v3_v3v3_db(vt[1].gloc, tri->v[1]->gloc, tri->v[0]->gloc, a);
+ mul_v4_m4v3_db(vt[1].fbcoord, vp, vt[1].gloc);
+ vt[1].index = tri->v[0]->index;
- INCREASE_RL
+ INCREASE_EDGE
if (allow_boundaries) {
e->flags = LRT_EDGE_FLAG_CONTOUR;
- lineart_prepend_edge_direct(&rb->contours, e);
+ lineart_prepend_edge_direct(&rb->contour.first, e);
}
- e->v1 = &rv[1];
- e->v2 = &rv[0];
- e->t1 = rt1;
+ e->v1 = &vt[1];
+ e->v2 = &vt[0];
+ e->t1 = tri1;
e->object_ref = ob;
- SELECT_RL(1, rt->v[1], &rv[0], rt1)
- SELECT_RL(0, rt->v[1], &rv[1], rt1)
+ SELECT_EDGE(1, tri->v[1], &vt[0], tri1)
+ SELECT_EDGE(0, tri->v[1], &vt[1], tri1)
- rt1->v[0] = &rv[0];
- rt1->v[1] = rt->v[1];
- rt1->v[2] = &rv[1];
+ tri1->v[0] = &vt[0];
+ tri1->v[1] = tri->v[1];
+ tri1->v[2] = &vt[1];
- lineart_triangle_post(rt1, rt);
+ lineart_triangle_post(tri1, tri);
v_count += 2;
t_count += 1;
@@ -966,7 +998,7 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb,
case 1:
/* One point behind near plane, cut those and
* generate 2 new points, 4 lines and 2 triangles. */
- lineart_triangle_set_cull_flag(rt, LRT_CULL_USED);
+ lineart_triangle_set_cull_flag(tri, LRT_CULL_USED);
/**
* (in0) means "when point 0 is invisible".
@@ -993,152 +1025,152 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb,
*/
if (in0) {
/* Cut point for line 0---|------1. */
- sub_v3_v3v3_db(vv1, rt->v[1]->gloc, cam_pos);
- sub_v3_v3v3_db(vv2, cam_pos, rt->v[0]->gloc);
+ sub_v3_v3v3_db(vv1, tri->v[1]->gloc, cam_pos);
+ sub_v3_v3v3_db(vv2, cam_pos, tri->v[0]->gloc);
dot1 = dot_v3v3_db(vv1, view_dir);
dot2 = dot_v3v3_db(vv2, view_dir);
a = dot2 / (dot1 + dot2);
/* Assign to a new point. */
- interp_v3_v3v3_db(rv[0].gloc, rt->v[0]->gloc, rt->v[1]->gloc, a);
- mul_v4_m4v3_db(rv[0].fbcoord, vp, rv[0].gloc);
- rv[0].index = rt->v[0]->index;
+ interp_v3_v3v3_db(vt[0].gloc, tri->v[0]->gloc, tri->v[1]->gloc, a);
+ mul_v4_m4v3_db(vt[0].fbcoord, vp, vt[0].gloc);
+ vt[0].index = tri->v[0]->index;
/* Cut point for line 0---|------2. */
- sub_v3_v3v3_db(vv1, rt->v[2]->gloc, cam_pos);
- sub_v3_v3v3_db(vv2, cam_pos, rt->v[0]->gloc);
+ sub_v3_v3v3_db(vv1, tri->v[2]->gloc, cam_pos);
+ sub_v3_v3v3_db(vv2, cam_pos, tri->v[0]->gloc);
dot1 = dot_v3v3_db(vv1, view_dir);
dot2 = dot_v3v3_db(vv2, view_dir);
a = dot2 / (dot1 + dot2);
/* Assign to other new point. */
- interp_v3_v3v3_db(rv[1].gloc, rt->v[0]->gloc, rt->v[2]->gloc, a);
- mul_v4_m4v3_db(rv[1].fbcoord, vp, rv[1].gloc);
- rv[1].index = rt->v[0]->index;
+ interp_v3_v3v3_db(vt[1].gloc, tri->v[0]->gloc, tri->v[2]->gloc, a);
+ mul_v4_m4v3_db(vt[1].fbcoord, vp, vt[1].gloc);
+ vt[1].index = tri->v[0]->index;
/* New line connects two new points. */
- INCREASE_RL
+ INCREASE_EDGE
if (allow_boundaries) {
e->flags = LRT_EDGE_FLAG_CONTOUR;
- lineart_prepend_edge_direct(&rb->contours, e);
+ lineart_prepend_edge_direct(&rb->contour.first, e);
}
- e->v1 = &rv[1];
- e->v2 = &rv[0];
- e->t1 = rt1;
+ e->v1 = &vt[1];
+ e->v2 = &vt[0];
+ e->t1 = tri1;
e->object_ref = ob;
/* New line connects new point 0 and old point 1,
* this is a border line. */
- SELECT_RL(0, rt->v[1], &rv[0], rt1)
- SELECT_RL(2, rt->v[2], &rv[1], rt2)
- RELINK_RL(1, rt2)
+ SELECT_EDGE(0, tri->v[1], &vt[0], tri1)
+ SELECT_EDGE(2, tri->v[2], &vt[1], tri2)
+ RELINK_EDGE(1, tri2)
/* We now have one triangle closed. */
- rt1->v[0] = rt->v[1];
- rt1->v[1] = &rv[1];
- rt1->v[2] = &rv[0];
+ tri1->v[0] = tri->v[1];
+ tri1->v[1] = &vt[1];
+ tri1->v[2] = &vt[0];
/* Close the second triangle. */
- rt2->v[0] = &rv[1];
- rt2->v[1] = rt->v[1];
- rt2->v[2] = rt->v[2];
+ tri2->v[0] = &vt[1];
+ tri2->v[1] = tri->v[1];
+ tri2->v[2] = tri->v[2];
- lineart_triangle_post(rt1, rt);
- lineart_triangle_post(rt2, rt);
+ lineart_triangle_post(tri1, tri);
+ lineart_triangle_post(tri2, tri);
v_count += 2;
t_count += 2;
}
else if (in1) {
- sub_v3_v3v3_db(vv1, rt->v[1]->gloc, cam_pos);
- sub_v3_v3v3_db(vv2, cam_pos, rt->v[2]->gloc);
+ sub_v3_v3v3_db(vv1, tri->v[1]->gloc, cam_pos);
+ sub_v3_v3v3_db(vv2, cam_pos, tri->v[2]->gloc);
dot1 = dot_v3v3_db(vv1, view_dir);
dot2 = dot_v3v3_db(vv2, view_dir);
a = dot1 / (dot1 + dot2);
- interp_v3_v3v3_db(rv[0].gloc, rt->v[1]->gloc, rt->v[2]->gloc, a);
- mul_v4_m4v3_db(rv[0].fbcoord, vp, rv[0].gloc);
- rv[0].index = rt->v[1]->index;
+ interp_v3_v3v3_db(vt[0].gloc, tri->v[1]->gloc, tri->v[2]->gloc, a);
+ mul_v4_m4v3_db(vt[0].fbcoord, vp, vt[0].gloc);
+ vt[0].index = tri->v[1]->index;
- sub_v3_v3v3_db(vv1, rt->v[1]->gloc, cam_pos);
- sub_v3_v3v3_db(vv2, cam_pos, rt->v[0]->gloc);
+ sub_v3_v3v3_db(vv1, tri->v[1]->gloc, cam_pos);
+ sub_v3_v3v3_db(vv2, cam_pos, tri->v[0]->gloc);
dot1 = dot_v3v3_db(vv1, view_dir);
dot2 = dot_v3v3_db(vv2, view_dir);
a = dot1 / (dot1 + dot2);
- interp_v3_v3v3_db(rv[1].gloc, rt->v[1]->gloc, rt->v[0]->gloc, a);
- mul_v4_m4v3_db(rv[1].fbcoord, vp, rv[1].gloc);
- rv[1].index = rt->v[1]->index;
+ interp_v3_v3v3_db(vt[1].gloc, tri->v[1]->gloc, tri->v[0]->gloc, a);
+ mul_v4_m4v3_db(vt[1].fbcoord, vp, vt[1].gloc);
+ vt[1].index = tri->v[1]->index;
- INCREASE_RL
+ INCREASE_EDGE
if (allow_boundaries) {
e->flags = LRT_EDGE_FLAG_CONTOUR;
- lineart_prepend_edge_direct(&rb->contours, e);
+ lineart_prepend_edge_direct(&rb->contour.first, e);
}
- e->v1 = &rv[1];
- e->v2 = &rv[0];
- e->t1 = rt1;
+ e->v1 = &vt[1];
+ e->v2 = &vt[0];
+ e->t1 = tri1;
e->object_ref = ob;
- SELECT_RL(1, rt->v[2], &rv[0], rt1)
- SELECT_RL(0, rt->v[0], &rv[1], rt2)
- RELINK_RL(2, rt2)
+ SELECT_EDGE(1, tri->v[2], &vt[0], tri1)
+ SELECT_EDGE(0, tri->v[0], &vt[1], tri2)
+ RELINK_EDGE(2, tri2)
- rt1->v[0] = rt->v[2];
- rt1->v[1] = &rv[1];
- rt1->v[2] = &rv[0];
+ tri1->v[0] = tri->v[2];
+ tri1->v[1] = &vt[1];
+ tri1->v[2] = &vt[0];
- rt2->v[0] = &rv[1];
- rt2->v[1] = rt->v[2];
- rt2->v[2] = rt->v[0];
+ tri2->v[0] = &vt[1];
+ tri2->v[1] = tri->v[2];
+ tri2->v[2] = tri->v[0];
- lineart_triangle_post(rt1, rt);
- lineart_triangle_post(rt2, rt);
+ lineart_triangle_post(tri1, tri);
+ lineart_triangle_post(tri2, tri);
v_count += 2;
t_count += 2;
}
else if (in2) {
- sub_v3_v3v3_db(vv1, rt->v[2]->gloc, cam_pos);
- sub_v3_v3v3_db(vv2, cam_pos, rt->v[0]->gloc);
+ sub_v3_v3v3_db(vv1, tri->v[2]->gloc, cam_pos);
+ sub_v3_v3v3_db(vv2, cam_pos, tri->v[0]->gloc);
dot1 = dot_v3v3_db(vv1, view_dir);
dot2 = dot_v3v3_db(vv2, view_dir);
a = dot1 / (dot1 + dot2);
- interp_v3_v3v3_db(rv[0].gloc, rt->v[2]->gloc, rt->v[0]->gloc, a);
- mul_v4_m4v3_db(rv[0].fbcoord, vp, rv[0].gloc);
- rv[0].index = rt->v[2]->index;
+ interp_v3_v3v3_db(vt[0].gloc, tri->v[2]->gloc, tri->v[0]->gloc, a);
+ mul_v4_m4v3_db(vt[0].fbcoord, vp, vt[0].gloc);
+ vt[0].index = tri->v[2]->index;
- sub_v3_v3v3_db(vv1, rt->v[2]->gloc, cam_pos);
- sub_v3_v3v3_db(vv2, cam_pos, rt->v[1]->gloc);
+ sub_v3_v3v3_db(vv1, tri->v[2]->gloc, cam_pos);
+ sub_v3_v3v3_db(vv2, cam_pos, tri->v[1]->gloc);
dot1 = dot_v3v3_db(vv1, view_dir);
dot2 = dot_v3v3_db(vv2, view_dir);
a = dot1 / (dot1 + dot2);
- interp_v3_v3v3_db(rv[1].gloc, rt->v[2]->gloc, rt->v[1]->gloc, a);
- mul_v4_m4v3_db(rv[1].fbcoord, vp, rv[1].gloc);
- rv[1].index = rt->v[2]->index;
+ interp_v3_v3v3_db(vt[1].gloc, tri->v[2]->gloc, tri->v[1]->gloc, a);
+ mul_v4_m4v3_db(vt[1].fbcoord, vp, vt[1].gloc);
+ vt[1].index = tri->v[2]->index;
- INCREASE_RL
+ INCREASE_EDGE
if (allow_boundaries) {
e->flags = LRT_EDGE_FLAG_CONTOUR;
- lineart_prepend_edge_direct(&rb->contours, e);
+ lineart_prepend_edge_direct(&rb->contour.first, e);
}
- e->v1 = &rv[1];
- e->v2 = &rv[0];
- e->t1 = rt1;
+ e->v1 = &vt[1];
+ e->v2 = &vt[0];
+ e->t1 = tri1;
e->object_ref = ob;
- SELECT_RL(2, rt->v[0], &rv[0], rt1)
- SELECT_RL(1, rt->v[1], &rv[1], rt2)
- RELINK_RL(0, rt2)
+ SELECT_EDGE(2, tri->v[0], &vt[0], tri1)
+ SELECT_EDGE(1, tri->v[1], &vt[1], tri2)
+ RELINK_EDGE(0, tri2)
- rt1->v[0] = rt->v[0];
- rt1->v[1] = &rv[1];
- rt1->v[2] = &rv[0];
+ tri1->v[0] = tri->v[0];
+ tri1->v[1] = &vt[1];
+ tri1->v[2] = &vt[0];
- rt2->v[0] = &rv[1];
- rt2->v[1] = rt->v[0];
- rt2->v[2] = rt->v[1];
+ tri2->v[0] = &vt[1];
+ tri2->v[1] = tri->v[0];
+ tri2->v[2] = tri->v[1];
- lineart_triangle_post(rt1, rt);
- lineart_triangle_post(rt2, rt);
+ lineart_triangle_post(tri1, tri);
+ lineart_triangle_post(tri2, tri);
v_count += 2;
t_count += 2;
@@ -1149,10 +1181,10 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb,
*r_e_count = e_count;
*r_t_count = t_count;
-#undef INCREASE_RL
-#undef SELECT_RL
-#undef RELINK_RL
-#undef REMOVE_TRIANGLE_RL
+#undef INCREASE_EDGE
+#undef SELECT_EDGE
+#undef RELINK_EDGE
+#undef REMOVE_TRIANGLE_EDGE
}
/**
@@ -1163,7 +1195,7 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb,
*/
static void lineart_main_cull_triangles(LineartRenderBuffer *rb, bool clip_far)
{
- LineartTriangle *rt;
+ LineartTriangle *tri;
LineartElementLinkNode *v_eln, *t_eln, *e_eln;
double(*vp)[4] = rb->view_projection;
int i;
@@ -1219,25 +1251,25 @@ static void lineart_main_cull_triangles(LineartRenderBuffer *rb, bool clip_far)
in0 = 0, in1 = 0, in2 = 0; \
if (clip_far) { \
/* Point outside far plane. */ \
- if (rt->v[0]->fbcoord[use_w] > clip_end) { \
+ if (tri->v[0]->fbcoord[use_w] > clip_end) { \
in0 = 1; \
} \
- if (rt->v[1]->fbcoord[use_w] > clip_end) { \
+ if (tri->v[1]->fbcoord[use_w] > clip_end) { \
in1 = 1; \
} \
- if (rt->v[2]->fbcoord[use_w] > clip_end) { \
+ if (tri->v[2]->fbcoord[use_w] > clip_end) { \
in2 = 1; \
} \
} \
else { \
/* Point inside near plane. */ \
- if (rt->v[0]->fbcoord[use_w] < clip_start) { \
+ if (tri->v[0]->fbcoord[use_w] < clip_start) { \
in0 = 1; \
} \
- if (rt->v[1]->fbcoord[use_w] < clip_start) { \
+ if (tri->v[1]->fbcoord[use_w] < clip_start) { \
in1 = 1; \
} \
- if (rt->v[2]->fbcoord[use_w] < clip_start) { \
+ if (tri->v[2]->fbcoord[use_w] < clip_start) { \
in2 = 1; \
} \
}
@@ -1252,19 +1284,19 @@ static void lineart_main_cull_triangles(LineartRenderBuffer *rb, bool clip_far)
}
/* Then go through all the other triangles. */
- LISTBASE_FOREACH (LineartElementLinkNode *, reln, &rb->triangle_buffer_pointers) {
- if (reln->flags & LRT_ELEMENT_IS_ADDITIONAL) {
+ LISTBASE_FOREACH (LineartElementLinkNode *, eln, &rb->triangle_buffer_pointers) {
+ if (eln->flags & LRT_ELEMENT_IS_ADDITIONAL) {
continue;
}
- ob = reln->object_ref;
- for (i = 0; i < reln->element_count; i++) {
+ ob = eln->object_ref;
+ for (i = 0; i < eln->element_count; i++) {
/* Select the triangle in the array. */
- rt = (void *)(((uchar *)reln->pointer) + rb->triangle_size * i);
+ tri = (void *)(((uchar *)eln->pointer) + rb->triangle_size * i);
LRT_CULL_DECIDE_INSIDE
LRT_CULL_ENSURE_MEMORY
lineart_triangle_cull_single(rb,
- rt,
+ tri,
in0,
in1,
in2,
@@ -1298,40 +1330,40 @@ static void lineart_main_free_adjacent_data(LineartRenderBuffer *rb)
while ((ld = BLI_pophead(&rb->triangle_adjacent_pointers)) != NULL) {
MEM_freeN(ld->data);
}
- LISTBASE_FOREACH (LineartElementLinkNode *, reln, &rb->triangle_buffer_pointers) {
- LineartTriangle *rt = reln->pointer;
+ LISTBASE_FOREACH (LineartElementLinkNode *, eln, &rb->triangle_buffer_pointers) {
+ LineartTriangle *tri = eln->pointer;
int i;
- for (i = 0; i < reln->element_count; i++) {
- /* See definition of rt->intersecting_verts and the usage in
+ for (i = 0; i < eln->element_count; i++) {
+ /* See definition of tri->intersecting_verts and the usage in
* lineart_geometry_object_load() for detailed. */
- rt->intersecting_verts = NULL;
- rt = (LineartTriangle *)(((uchar *)rt) + rb->triangle_size);
+ tri->intersecting_verts = NULL;
+ tri = (LineartTriangle *)(((uchar *)tri) + rb->triangle_size);
}
}
}
static void lineart_main_perspective_division(LineartRenderBuffer *rb)
{
- LineartVert *rv;
+ LineartVert *vt;
int i;
if (!rb->cam_is_persp) {
return;
}
- LISTBASE_FOREACH (LineartElementLinkNode *, reln, &rb->vertex_buffer_pointers) {
- rv = reln->pointer;
- for (i = 0; i < reln->element_count; i++) {
+ LISTBASE_FOREACH (LineartElementLinkNode *, eln, &rb->vertex_buffer_pointers) {
+ vt = eln->pointer;
+ for (i = 0; i < eln->element_count; i++) {
/* Do not divide Z, we use Z to back transform cut points in later chaining process. */
- rv[i].fbcoord[0] /= rv[i].fbcoord[3];
- rv[i].fbcoord[1] /= rv[i].fbcoord[3];
+ vt[i].fbcoord[0] /= vt[i].fbcoord[3];
+ vt[i].fbcoord[1] /= vt[i].fbcoord[3];
/* Re-map z into (0-1) range, because we no longer need NDC (Normalized Device Coordinates)
* at the moment.
* The algorithm currently doesn't need Z for operation, we use W instead. If Z is needed in
* the future, the line below correctly transforms it to view space coordinates. */
- // `rv[i].fbcoord[2] = -2 * rv[i].fbcoord[2] / (far - near) - (far + near) / (far - near);
- rv[i].fbcoord[0] -= rb->shift_x * 2;
- rv[i].fbcoord[1] -= rb->shift_y * 2;
+ // `vt[i].fbcoord[2] = -2 * vt[i].fbcoord[2] / (far - near) - (far + near) / (far - near);
+ vt[i].fbcoord[0] -= rb->shift_x * 2;
+ vt[i].fbcoord[1] -= rb->shift_y * 2;
}
}
}
@@ -1343,10 +1375,10 @@ static void lineart_vert_transform(
BMVert *v, int index, LineartVert *RvBuf, double (*mv_mat)[4], double (*mvp_mat)[4])
{
double co[4];
- LineartVert *rv = &RvBuf[index];
+ LineartVert *vt = &RvBuf[index];
copy_v3db_v3fl(co, v->co);
- mul_v3_m4v3_db(rv->gloc, mv_mat, co);
- mul_v4_m4v3_db(rv->fbcoord, mvp_mat, co);
+ mul_v3_m4v3_db(vt->gloc, mv_mat, co);
+ mul_v4_m4v3_db(vt->fbcoord, mvp_mat, co);
}
/**
@@ -1381,12 +1413,12 @@ static char lineart_identify_feature_line(LineartRenderBuffer *rb,
return LRT_EDGE_FLAG_CONTOUR;
}
- LineartTriangle *rt1, *rt2;
+ LineartTriangle *tri1, *tri2;
LineartVert *l;
/* The mesh should already be triangulated now, so we can assume each face is a triangle. */
- rt1 = lineart_triangle_from_index(rb, rt_array, BM_elem_index_get(ll->f));
- rt2 = lineart_triangle_from_index(rb, rt_array, BM_elem_index_get(lr->f));
+ tri1 = lineart_triangle_from_index(rb, rt_array, BM_elem_index_get(ll->f));
+ tri2 = lineart_triangle_from_index(rb, rt_array, BM_elem_index_get(lr->f));
l = &rv_array[BM_elem_index_get(e->v1)];
@@ -1403,14 +1435,14 @@ static char lineart_identify_feature_line(LineartRenderBuffer *rb,
view_vector = rb->view_vector;
}
- dot_1 = dot_v3v3_db(view_vector, rt1->gn);
- dot_2 = dot_v3v3_db(view_vector, rt2->gn);
+ dot_1 = dot_v3v3_db(view_vector, tri1->gn);
+ dot_2 = dot_v3v3_db(view_vector, tri2->gn);
if ((result = dot_1 * dot_2) <= 0 && (dot_1 + dot_2)) {
return LRT_EDGE_FLAG_CONTOUR;
}
- if (rb->use_crease && (dot_v3v3_db(rt1->gn, rt2->gn) < crease_threshold)) {
+ if (rb->use_crease && (dot_v3v3_db(tri1->gn, tri2->gn) < crease_threshold)) {
if (!no_crease) {
return LRT_EDGE_FLAG_CREASE;
}
@@ -1431,45 +1463,82 @@ static void lineart_add_edge_to_list(LineartRenderBuffer *rb, LineartEdge *e)
{
switch (e->flags) {
case LRT_EDGE_FLAG_CONTOUR:
- lineart_prepend_edge_direct(&rb->contours, e);
+ lineart_prepend_edge_direct(&rb->contour.first, e);
break;
case LRT_EDGE_FLAG_CREASE:
- lineart_prepend_edge_direct(&rb->crease_lines, e);
+ lineart_prepend_edge_direct(&rb->crease.first, e);
break;
case LRT_EDGE_FLAG_MATERIAL:
- lineart_prepend_edge_direct(&rb->material_lines, e);
+ lineart_prepend_edge_direct(&rb->material.first, e);
break;
case LRT_EDGE_FLAG_EDGE_MARK:
- lineart_prepend_edge_direct(&rb->edge_marks, e);
+ lineart_prepend_edge_direct(&rb->edge_mark.first, e);
break;
case LRT_EDGE_FLAG_INTERSECTION:
- lineart_prepend_edge_direct(&rb->intersection_lines, e);
+ lineart_prepend_edge_direct(&rb->intersection.first, e);
break;
}
}
-static void lineart_triangle_adjacent_assign(LineartTriangle *rt,
- LineartTriangleAdjacent *rta,
+static void lineart_add_edge_to_list_thread(LineartObjectInfo *obi, LineartEdge *e)
+{
+
+#define LRT_ASSIGN_EDGE(name) \
+ lineart_prepend_edge_direct(&obi->name.first, e); \
+ if (!obi->name.last) { \
+ obi->name.last = e; \
+ }
+ switch (e->flags) {
+ case LRT_EDGE_FLAG_CONTOUR:
+ LRT_ASSIGN_EDGE(contour);
+ break;
+ case LRT_EDGE_FLAG_CREASE:
+ LRT_ASSIGN_EDGE(crease);
+ break;
+ case LRT_EDGE_FLAG_MATERIAL:
+ LRT_ASSIGN_EDGE(material);
+ break;
+ case LRT_EDGE_FLAG_EDGE_MARK:
+ LRT_ASSIGN_EDGE(edge_mark);
+ break;
+ case LRT_EDGE_FLAG_INTERSECTION:
+ LRT_ASSIGN_EDGE(intersection);
+ break;
+ }
+#undef LRT_ASSIGN_EDGE
+}
+
+static void lineart_finalize_object_edge_list(LineartRenderBuffer *rb, LineartObjectInfo *obi)
+{
+#define LRT_OBI_TO_RB(name) \
+ if (obi->name.last) { \
+ ((LineartEdge *)obi->name.last)->next = rb->name.first; \
+ rb->name.first = obi->name.first; \
+ }
+ LRT_OBI_TO_RB(contour);
+ LRT_OBI_TO_RB(crease);
+ LRT_OBI_TO_RB(material);
+ LRT_OBI_TO_RB(edge_mark);
+ LRT_OBI_TO_RB(intersection);
+#undef LRT_OBI_TO_RB
+}
+
+static void lineart_triangle_adjacent_assign(LineartTriangle *tri,
+ LineartTriangleAdjacent *ta,
LineartEdge *e)
{
- if (lineart_edge_match(rt, e, 0, 1)) {
- rta->e[0] = e;
+ if (lineart_edge_match(tri, e, 0, 1)) {
+ ta->e[0] = e;
}
- else if (lineart_edge_match(rt, e, 1, 2)) {
- rta->e[1] = e;
+ else if (lineart_edge_match(tri, e, 1, 2)) {
+ ta->e[1] = e;
}
- else if (lineart_edge_match(rt, e, 2, 0)) {
- rta->e[2] = e;
+ else if (lineart_edge_match(tri, e, 2, 0)) {
+ ta->e[2] = e;
}
}
-static void lineart_geometry_object_load(Depsgraph *dg,
- Object *ob,
- double (*mv_mat)[4],
- double (*mvp_mat)[4],
- LineartRenderBuffer *rb,
- int override_usage,
- int *global_vindex)
+static void lineart_geometry_object_load(LineartObjectInfo *obi, LineartRenderBuffer *rb)
{
BMesh *bm;
BMVert *v;
@@ -1477,252 +1546,244 @@ static void lineart_geometry_object_load(Depsgraph *dg,
BMEdge *e;
BMLoop *loop;
LineartEdge *la_e;
- LineartTriangle *rt;
+ LineartEdgeSegment *la_s;
+ LineartTriangle *tri;
LineartTriangleAdjacent *orta;
- double new_mvp[4][4], new_mv[4][4], normal[4][4];
- float imat[4][4];
- LineartElementLinkNode *reln;
+ double(*model_view_proj)[4] = obi->model_view_proj, (*model_view)[4] = obi->model_view,
+ (*normal)[4] = obi->normal;
+ LineartElementLinkNode *eln;
LineartVert *orv;
LineartEdge *o_la_e;
+ LineartEdgeSegment *o_la_s;
LineartTriangle *ort;
Object *orig_ob;
int CanFindFreestyle = 0;
- int i, global_i = (*global_vindex);
- Mesh *use_mesh;
+ int i;
float use_crease = 0;
- int usage = override_usage ? override_usage : ob->lineart.usage;
+ int usage = obi->usage;
-#define LRT_MESH_FINISH \
- BM_mesh_free(bm); \
- if (ob->type != OB_MESH) { \
- BKE_mesh_free(use_mesh); \
- MEM_freeN(use_mesh); \
+ if (obi->original_me->edit_mesh) {
+ /* Do not use edit_mesh directly because we will modify it, so create a copy. */
+ bm = BM_mesh_copy(obi->original_me->edit_mesh->bm);
+ }
+ else {
+ const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(((Mesh *)(obi->original_me)));
+ bm = BM_mesh_create(&allocsize,
+ &((struct BMeshCreateParams){
+ .use_toolflags = true,
+ }));
+ BM_mesh_bm_from_me(bm,
+ obi->original_me,
+ &((struct BMeshFromMeshParams){
+ .calc_face_normal = true,
+ }));
}
- if (usage == OBJECT_LRT_EXCLUDE) {
- return;
+ if (obi->free_use_mesh) {
+ BKE_mesh_free(obi->original_me);
+ MEM_freeN(obi->original_me);
}
- if (ob->type == OB_MESH || ob->type == OB_MBALL || ob->type == OB_CURVE || ob->type == OB_SURF ||
- ob->type == OB_FONT) {
+ if (rb->remove_doubles) {
+ BMEditMesh *em = BKE_editmesh_create(bm, false);
+ BMOperator findop, weldop;
- if (ob->type == OB_MESH) {
- use_mesh = DEG_get_evaluated_object(dg, ob)->data;
- }
- else {
- use_mesh = BKE_mesh_new_from_object(NULL, ob, false);
- }
+ /* See bmesh_opdefines.c and bmesh_operators.c for op names and argument formatting. */
+ BMO_op_initf(bm, &findop, BMO_FLAG_DEFAULTS, "find_doubles verts=%av dist=%f", 0.0001);
- /* In case we can not get any mesh geometry data from the object */
- if (!use_mesh) {
- return;
- }
+ BMO_op_exec(bm, &findop);
- /* First we need to prepare the matrix used for transforming this specific object. */
- mul_m4db_m4db_m4fl_uniq(new_mvp, mvp_mat, ob->obmat);
- mul_m4db_m4db_m4fl_uniq(new_mv, mv_mat, ob->obmat);
+ /* Weld the vertices. */
+ BMO_op_init(bm, &weldop, BMO_FLAG_DEFAULTS, "weld_verts");
+ BMO_slot_copy(&findop, slots_out, "targetmap.out", &weldop, slots_in, "targetmap");
+ BMO_op_exec(bm, &weldop);
- invert_m4_m4(imat, ob->obmat);
- transpose_m4(imat);
- copy_m4d_m4(normal, imat);
+ BMO_op_finish(bm, &findop);
+ BMO_op_finish(bm, &weldop);
- if (use_mesh->edit_mesh) {
- /* Do not use edit_mesh directly because we will modify it, so create a copy. */
- bm = BM_mesh_copy(use_mesh->edit_mesh->bm);
- }
- else {
- const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(((Mesh *)(use_mesh)));
- bm = BM_mesh_create(&allocsize,
- &((struct BMeshCreateParams){
- .use_toolflags = true,
- }));
- BM_mesh_bm_from_me(bm,
- use_mesh,
- &((struct BMeshFromMeshParams){
- .calc_face_normal = true,
- }));
- }
+ MEM_freeN(em);
+ }
+
+ BM_mesh_elem_hflag_disable_all(bm, BM_FACE | BM_EDGE, BM_ELEM_TAG, false);
+ BM_mesh_triangulate(
+ bm, MOD_TRIANGULATE_QUAD_FIXED, MOD_TRIANGULATE_NGON_BEAUTY, 4, false, NULL, NULL, NULL);
+ BM_mesh_normals_update(bm);
+ BM_mesh_elem_table_ensure(bm, BM_VERT | BM_EDGE | BM_FACE);
+ BM_mesh_elem_index_ensure(bm, BM_VERT | BM_EDGE | BM_FACE);
+
+ if (CustomData_has_layer(&bm->edata, CD_FREESTYLE_EDGE)) {
+ CanFindFreestyle = 1;
+ }
- if (rb->remove_doubles) {
- BMEditMesh *em = BKE_editmesh_create(bm, false);
- BMOperator findop, weldop;
+ /* Only allocate memory for verts and tris as we don't know how many lines we will generate
+ * yet. */
+ orv = lineart_mem_acquire_thread(&rb->render_data_pool, sizeof(LineartVert) * bm->totvert);
+ ort = lineart_mem_acquire_thread(&rb->render_data_pool, bm->totface * rb->triangle_size);
- /* See bmesh_opdefines.c and bmesh_operators.c for op names and argument formatting. */
- BMO_op_initf(bm, &findop, BMO_FLAG_DEFAULTS, "find_doubles verts=%av dist=%f", 0.0001);
+ orig_ob = obi->original_ob;
- BMO_op_exec(bm, &findop);
+ BLI_spin_lock(&rb->lock_task);
+ eln = lineart_list_append_pointer_pool_sized_thread(
+ &rb->vertex_buffer_pointers, &rb->render_data_pool, orv, sizeof(LineartElementLinkNode));
+ BLI_spin_unlock(&rb->lock_task);
- /* Weld the vertices. */
- BMO_op_init(bm, &weldop, BMO_FLAG_DEFAULTS, "weld_verts");
- BMO_slot_copy(&findop, slots_out, "targetmap.out", &weldop, slots_in, "targetmap");
- BMO_op_exec(bm, &weldop);
+ eln->element_count = bm->totvert;
+ eln->object_ref = orig_ob;
+ obi->v_reln = eln;
- BMO_op_finish(bm, &findop);
- BMO_op_finish(bm, &weldop);
+ if (orig_ob->lineart.flags & OBJECT_LRT_OWN_CREASE) {
+ use_crease = cosf(M_PI - orig_ob->lineart.crease_threshold);
+ }
+ else {
+ use_crease = rb->crease_threshold;
+ }
- MEM_freeN(em);
- }
+ /* FIXME(Yiming): Hack for getting clean 3D text, the seam that extruded text object creates
+ * erroneous detection on creases. Future configuration should allow options. */
+ if (orig_ob->type == OB_FONT) {
+ eln->flags |= LRT_ELEMENT_BORDER_ONLY;
+ }
- BM_mesh_elem_hflag_disable_all(bm, BM_FACE | BM_EDGE, BM_ELEM_TAG, false);
- BM_mesh_triangulate(
- bm, MOD_TRIANGULATE_QUAD_FIXED, MOD_TRIANGULATE_NGON_BEAUTY, 4, false, NULL, NULL, NULL);
- BM_mesh_normals_update(bm);
- BM_mesh_elem_table_ensure(bm, BM_VERT | BM_EDGE | BM_FACE);
- BM_mesh_elem_index_ensure(bm, BM_VERT | BM_EDGE | BM_FACE);
+ BLI_spin_lock(&rb->lock_task);
+ eln = lineart_list_append_pointer_pool_sized_thread(
+ &rb->triangle_buffer_pointers, &rb->render_data_pool, ort, sizeof(LineartElementLinkNode));
+ BLI_spin_unlock(&rb->lock_task);
- if (CustomData_has_layer(&bm->edata, CD_FREESTYLE_EDGE)) {
- CanFindFreestyle = 1;
- }
+ eln->element_count = bm->totface;
+ eln->object_ref = orig_ob;
+ eln->flags |= (usage == OBJECT_LRT_NO_INTERSECTION ? LRT_ELEMENT_NO_INTERSECTION : 0);
- /* Only allocate memory for verts and tris as we don't know how many lines we will generate
- * yet. */
- orv = lineart_mem_aquire(&rb->render_data_pool, sizeof(LineartVert) * bm->totvert);
- ort = lineart_mem_aquire(&rb->render_data_pool, bm->totface * rb->triangle_size);
+ /* Note this memory is not from pool, will be deleted after culling. */
+ orta = MEM_callocN(sizeof(LineartTriangleAdjacent) * bm->totface, "LineartTriangleAdjacent");
+ /* Link is minimal so we use pool anyway. */
+ BLI_spin_lock(&rb->lock_task);
+ lineart_list_append_pointer_pool_thread(
+ &rb->triangle_adjacent_pointers, &rb->render_data_pool, orta);
+ BLI_spin_unlock(&rb->lock_task);
- orig_ob = ob->id.orig_id ? (Object *)ob->id.orig_id : ob;
+ for (i = 0; i < bm->totvert; i++) {
+ v = BM_vert_at_index(bm, i);
+ lineart_vert_transform(v, i, orv, model_view, model_view_proj);
+ orv[i].index = i;
+ }
+ /* Register a global index increment. See #lineart_triangle_share_edge() and
+ * #lineart_main_load_geometries() for detailed. It's okay that global_vindex might eventually
+ * overflow, in such large scene it's virtually impossible for two vertex of the same numeric
+ * index to come close together. */
+ obi->global_i_offset = bm->totvert;
- reln = lineart_list_append_pointer_pool_sized(
- &rb->vertex_buffer_pointers, &rb->render_data_pool, orv, sizeof(LineartElementLinkNode));
- reln->element_count = bm->totvert;
- reln->object_ref = orig_ob;
+ tri = ort;
+ for (i = 0; i < bm->totface; i++) {
+ f = BM_face_at_index(bm, i);
- if (ob->lineart.flags & OBJECT_LRT_OWN_CREASE) {
- use_crease = cosf(M_PI - ob->lineart.crease_threshold);
- }
- else {
- use_crease = rb->crease_threshold;
- }
-
- /* FIXME(Yiming): Hack for getting clean 3D text, the seam that extruded text object creates
- * erroneous detection on creases. Future configuration should allow options. */
- if (ob->type == OB_FONT) {
- reln->flags |= LRT_ELEMENT_BORDER_ONLY;
- }
-
- reln = lineart_list_append_pointer_pool_sized(
- &rb->triangle_buffer_pointers, &rb->render_data_pool, ort, sizeof(LineartElementLinkNode));
- reln->element_count = bm->totface;
- reln->object_ref = orig_ob;
- reln->flags |= (usage == OBJECT_LRT_NO_INTERSECTION ? LRT_ELEMENT_NO_INTERSECTION : 0);
-
- /* Note this memory is not from pool, will be deleted after culling. */
- orta = MEM_callocN(sizeof(LineartTriangleAdjacent) * bm->totface, "LineartTriangleAdjacent");
- /* Link is minimal so we use pool anyway. */
- lineart_list_append_pointer_pool(&rb->triangle_adjacent_pointers, &rb->render_data_pool, orta);
-
- for (i = 0; i < bm->totvert; i++) {
- v = BM_vert_at_index(bm, i);
- lineart_vert_transform(v, i, orv, new_mv, new_mvp);
- orv[i].index = i + global_i;
- }
- /* Register a global index increment. See #lineart_triangle_share_edge() and
- * #lineart_main_load_geometries() for detailed. It's okay that global_vindex might eventually
- * overflow, in such large scene it's virtually impossible for two vertex of the same numeric
- * index to come close together. */
- (*global_vindex) += bm->totvert;
-
- rt = ort;
- for (i = 0; i < bm->totface; i++) {
- f = BM_face_at_index(bm, i);
-
- loop = f->l_first;
- rt->v[0] = &orv[BM_elem_index_get(loop->v)];
- loop = loop->next;
- rt->v[1] = &orv[BM_elem_index_get(loop->v)];
- loop = loop->next;
- rt->v[2] = &orv[BM_elem_index_get(loop->v)];
-
- /* Transparency bit assignment. */
- Material *mat = BKE_object_material_get(ob, f->mat_nr + 1);
- rt->transparency_mask = ((mat && (mat->lineart.flags & LRT_MATERIAL_TRANSPARENCY_ENABLED)) ?
- mat->lineart.transparency_mask :
- 0);
-
- double gn[3];
- copy_v3db_v3fl(gn, f->no);
- mul_v3_mat3_m4v3_db(rt->gn, normal, gn);
- normalize_v3_db(rt->gn);
-
- if (usage == OBJECT_LRT_INTERSECTION_ONLY) {
- rt->flags |= LRT_TRIANGLE_INTERSECTION_ONLY;
- }
- else if (usage == OBJECT_LRT_NO_INTERSECTION || usage == OBJECT_LRT_OCCLUSION_ONLY) {
- rt->flags |= LRT_TRIANGLE_NO_INTERSECTION;
- }
+ loop = f->l_first;
+ tri->v[0] = &orv[BM_elem_index_get(loop->v)];
+ loop = loop->next;
+ tri->v[1] = &orv[BM_elem_index_get(loop->v)];
+ loop = loop->next;
+ tri->v[2] = &orv[BM_elem_index_get(loop->v)];
+
+ /* Transparency bit assignment. */
+ Material *mat = BKE_object_material_get(orig_ob, f->mat_nr + 1);
+ tri->transparency_mask = ((mat && (mat->lineart.flags & LRT_MATERIAL_TRANSPARENCY_ENABLED)) ?
+ mat->lineart.transparency_mask :
+ 0);
- /* Re-use this field to refer to adjacent info, will be cleared after culling stage. */
- rt->intersecting_verts = (void *)&orta[i];
+ double gn[3];
+ copy_v3db_v3fl(gn, f->no);
+ mul_v3_mat3_m4v3_db(tri->gn, normal, gn);
+ normalize_v3_db(tri->gn);
- rt = (LineartTriangle *)(((uchar *)rt) + rb->triangle_size);
+ if (usage == OBJECT_LRT_INTERSECTION_ONLY) {
+ tri->flags |= LRT_TRIANGLE_INTERSECTION_ONLY;
}
+ else if (usage == OBJECT_LRT_NO_INTERSECTION || usage == OBJECT_LRT_OCCLUSION_ONLY) {
+ tri->flags |= LRT_TRIANGLE_NO_INTERSECTION;
+ }
+
+ /* Re-use this field to refer to adjacent info, will be cleared after culling stage. */
+ tri->intersecting_verts = (void *)&orta[i];
- /* Use BM_ELEM_TAG in f->head.hflag to store needed faces in the first iteration. */
+ tri = (LineartTriangle *)(((uchar *)tri) + rb->triangle_size);
+ }
- int allocate_la_e = 0;
- for (i = 0; i < bm->totedge; i++) {
- e = BM_edge_at_index(bm, i);
+ /* Use BM_ELEM_TAG in f->head.hflag to store needed faces in the first iteration. */
- /* Because e->head.hflag is char, so line type flags should not exceed positive 7 bits. */
- char eflag = lineart_identify_feature_line(
- rb, e, ort, orv, use_crease, ob->type == OB_FONT, CanFindFreestyle, bm);
- if (eflag) {
- /* Only allocate for feature lines (instead of all lines) to save memory. */
- allocate_la_e++;
- }
- /* Here we just use bm's flag for when loading actual lines, then we don't need to call
- * lineart_identify_feature_line() again, e->head.hflag deleted after loading anyway. Always
- * set the flag, so hflag stays 0 for lines that are not feature lines. */
- e->head.hflag = eflag;
+ int allocate_la_e = 0;
+ for (i = 0; i < bm->totedge; i++) {
+ e = BM_edge_at_index(bm, i);
+
+ /* Because e->head.hflag is char, so line type flags should not exceed positive 7 bits. */
+ char eflag = lineart_identify_feature_line(
+ rb, e, ort, orv, use_crease, orig_ob->type == OB_FONT, CanFindFreestyle, bm);
+ if (eflag) {
+ /* Only allocate for feature lines (instead of all lines) to save memory. */
+ allocate_la_e++;
}
+ /* Here we just use bm's flag for when loading actual lines, then we don't need to call
+ * lineart_identify_feature_line() again, e->head.hflag deleted after loading anyway. Always
+ * set the flag, so hflag stays 0 for lines that are not feature lines. */
+ e->head.hflag = eflag;
+ }
- o_la_e = lineart_mem_aquire(&rb->render_data_pool, sizeof(LineartEdge) * allocate_la_e);
- reln = lineart_list_append_pointer_pool_sized(
- &rb->line_buffer_pointers, &rb->render_data_pool, o_la_e, sizeof(LineartElementLinkNode));
- reln->element_count = allocate_la_e;
- reln->object_ref = orig_ob;
+ o_la_e = lineart_mem_acquire_thread(&rb->render_data_pool, sizeof(LineartEdge) * allocate_la_e);
+ o_la_s = lineart_mem_acquire_thread(&rb->render_data_pool,
+ sizeof(LineartEdgeSegment) * allocate_la_e);
+ BLI_spin_lock(&rb->lock_task);
+ eln = lineart_list_append_pointer_pool_sized_thread(
+ &rb->line_buffer_pointers, &rb->render_data_pool, o_la_e, sizeof(LineartElementLinkNode));
+ BLI_spin_unlock(&rb->lock_task);
+ eln->element_count = allocate_la_e;
+ eln->object_ref = orig_ob;
- la_e = o_la_e;
- for (i = 0; i < bm->totedge; i++) {
- e = BM_edge_at_index(bm, i);
+ la_e = o_la_e;
+ la_s = o_la_s;
+ for (i = 0; i < bm->totedge; i++) {
+ e = BM_edge_at_index(bm, i);
- /* Not a feature line, so we skip. */
- if (!e->head.hflag) {
- continue;
- }
+ /* Not a feature line, so we skip. */
+ if (!e->head.hflag) {
+ continue;
+ }
- la_e->v1 = &orv[BM_elem_index_get(e->v1)];
- la_e->v2 = &orv[BM_elem_index_get(e->v2)];
- la_e->v1_obindex = la_e->v1->index - global_i;
- la_e->v2_obindex = la_e->v2->index - global_i;
- if (e->l) {
- int findex = BM_elem_index_get(e->l->f);
- la_e->t1 = lineart_triangle_from_index(rb, ort, findex);
- lineart_triangle_adjacent_assign(la_e->t1, &orta[findex], la_e);
- if (e->l->radial_next && e->l->radial_next != e->l) {
- findex = BM_elem_index_get(e->l->radial_next->f);
- la_e->t2 = lineart_triangle_from_index(rb, ort, findex);
- lineart_triangle_adjacent_assign(la_e->t2, &orta[findex], la_e);
- }
- }
- la_e->flags = e->head.hflag;
- la_e->object_ref = orig_ob;
-
- LineartLineSegment *rls = lineart_mem_aquire(&rb->render_data_pool,
- sizeof(LineartLineSegment));
- BLI_addtail(&la_e->segments, rls);
- if (usage == OBJECT_LRT_INHERIT || usage == OBJECT_LRT_INCLUDE ||
- usage == OBJECT_LRT_NO_INTERSECTION) {
- lineart_add_edge_to_list(rb, la_e);
+ la_e->v1 = &orv[BM_elem_index_get(e->v1)];
+ la_e->v2 = &orv[BM_elem_index_get(e->v2)];
+ la_e->v1_obindex = la_e->v1->index;
+ la_e->v2_obindex = la_e->v2->index;
+ if (e->l) {
+ int findex = BM_elem_index_get(e->l->f);
+ la_e->t1 = lineart_triangle_from_index(rb, ort, findex);
+ lineart_triangle_adjacent_assign(la_e->t1, &orta[findex], la_e);
+ if (e->l->radial_next && e->l->radial_next != e->l) {
+ findex = BM_elem_index_get(e->l->radial_next->f);
+ la_e->t2 = lineart_triangle_from_index(rb, ort, findex);
+ lineart_triangle_adjacent_assign(la_e->t2, &orta[findex], la_e);
}
-
- la_e++;
+ }
+ la_e->flags = e->head.hflag;
+ la_e->object_ref = orig_ob;
+ BLI_addtail(&la_e->segments, la_s);
+ if (usage == OBJECT_LRT_INHERIT || usage == OBJECT_LRT_INCLUDE ||
+ usage == OBJECT_LRT_NO_INTERSECTION) {
+ lineart_add_edge_to_list_thread(obi, la_e);
}
- LRT_MESH_FINISH
+ la_e++;
+ la_s++;
}
-#undef LRT_MESH_FINISH
+ /* always free bm as it's a copy from before threading */
+ BM_mesh_free(bm);
+}
+
+static void lineart_object_load_worker(TaskPool *__restrict UNUSED(pool),
+ LineartObjectLoadTaskInfo *olti)
+{
+ LineartRenderBuffer *rb = olti->rb;
+ for (LineartObjectInfo *obi = olti->pending; obi; obi = obi->next) {
+ lineart_geometry_object_load(obi, rb);
+ }
}
static bool _lineart_object_not_in_source_collection(Collection *source, Object *ob)
@@ -1802,6 +1863,24 @@ static int lineart_usage_check(Collection *c, Object *ob, LineartRenderBuffer *_
return OBJECT_LRT_INHERIT;
}
+static void lineart_geometry_load_assign_thread(LineartObjectLoadTaskInfo *olti_list,
+ LineartObjectInfo *obi,
+ int thread_count,
+ int this_face_count)
+{
+ LineartObjectLoadTaskInfo *use_olti = olti_list;
+ long unsigned int min_face = use_olti->total_faces;
+ for (int i = 0; i < thread_count; i++) {
+ if (olti_list[i].total_faces < min_face) {
+ min_face = olti_list[i].total_faces;
+ use_olti = &olti_list[i];
+ }
+ }
+ use_olti->total_faces += this_face_count;
+ obi->next = use_olti->pending;
+ use_olti->pending = obi;
+}
+
static void lineart_main_load_geometries(
Depsgraph *depsgraph,
Scene *scene,
@@ -1814,26 +1893,37 @@ static void lineart_main_load_geometries(
Camera *cam = camera->data;
float sensor = BKE_camera_sensor_size(cam->sensor_fit, cam->sensor_x, cam->sensor_y);
- double fov = focallength_to_fov(cam->lens, sensor);
-
+ int fit = BKE_camera_sensor_fit(cam->sensor_fit, rb->w, rb->h);
double asp = ((double)rb->w / (double)rb->h);
+ double t_start;
+
+ if (G.debug_value == 4000) {
+ t_start = PIL_check_seconds_timer();
+ }
+
if (cam->type == CAM_PERSP) {
- if (asp < 1) {
- fov /= asp;
+ if (fit == CAMERA_SENSOR_FIT_VERT && asp > 1) {
+ sensor *= asp;
+ }
+ if (fit == CAMERA_SENSOR_FIT_HOR && asp < 1) {
+ sensor /= asp;
}
+ double fov = focallength_to_fov(cam->lens, sensor);
lineart_matrix_perspective_44d(proj, fov, asp, cam->clip_start, cam->clip_end);
}
else if (cam->type == CAM_ORTHO) {
double w = cam->ortho_scale / 2;
lineart_matrix_ortho_44d(proj, -w, w, -w / asp, w / asp, cam->clip_start, cam->clip_end);
}
+
invert_m4_m4(inv, rb->cam_obmat);
mul_m4db_m4db_m4fl_uniq(result, proj, inv);
copy_m4_m4_db(proj, result);
copy_m4_m4_db(rb->view_projection, proj);
unit_m4_db(view);
+ copy_m4_m4_db(rb->view, view);
BLI_listbase_clear(&rb->triangle_buffer_pointers);
BLI_listbase_clear(&rb->vertex_buffer_pointers);
@@ -1846,67 +1936,141 @@ static void lineart_main_load_geometries(
flags |= DEG_ITER_OBJECT_FLAG_DUPLI;
}
- /* This is to serialize vertex index in the whole scene, so lineart_triangle_share_edge() can
- * work properly from the lack of triangle adjacent info. */
- int global_i = 0;
+ int thread_count = rb->thread_count;
+
+ /* This memory is in render buffer memory pool. so we don't need to free those after loading. */
+ LineartObjectLoadTaskInfo *olti = lineart_mem_acquire(
+ &rb->render_data_pool, sizeof(LineartObjectLoadTaskInfo) * thread_count);
DEG_OBJECT_ITER_BEGIN (depsgraph, ob, flags) {
- int usage = lineart_usage_check(scene->master_collection, ob, rb);
+ LineartObjectInfo *obi = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartObjectInfo));
+ obi->usage = lineart_usage_check(scene->master_collection, ob, rb);
+ Mesh *use_mesh;
+
+ if (obi->usage == OBJECT_LRT_EXCLUDE) {
+ continue;
+ }
+
+ Object *use_ob = DEG_get_evaluated_object(depsgraph, ob);
+
+ if (!(use_ob->type == OB_MESH || use_ob->type == OB_MBALL || use_ob->type == OB_CURVE ||
+ use_ob->type == OB_SURF || use_ob->type == OB_FONT)) {
+ continue;
+ }
+ if (use_ob->type == OB_MESH) {
+ use_mesh = use_ob->data;
+ }
+ else {
+ use_mesh = BKE_mesh_new_from_object(NULL, use_ob, false, true);
+ }
+
+ /* In case we still can not get any mesh geometry data from the object */
+ if (!use_mesh) {
+ continue;
+ }
+
+ if (ob->type != OB_MESH) {
+ obi->free_use_mesh = true;
+ }
- lineart_geometry_object_load(depsgraph, ob, view, proj, rb, usage, &global_i);
+ /* Prepare the matrix used for transforming this specific object (instance). */
+ mul_m4db_m4db_m4fl_uniq(obi->model_view_proj, rb->view_projection, ob->obmat);
+ mul_m4db_m4db_m4fl_uniq(obi->model_view, rb->view, ob->obmat);
+ float imat[4][4];
+ invert_m4_m4(imat, ob->obmat);
+ transpose_m4(imat);
+ copy_m4d_m4(obi->normal, imat);
+
+ obi->original_me = use_mesh;
+ obi->original_ob = (ob->id.orig_id ? (Object *)ob->id.orig_id : (Object *)ob);
+ lineart_geometry_load_assign_thread(olti, obi, thread_count, use_mesh->totpoly);
}
DEG_OBJECT_ITER_END;
+
+ TaskPool *tp = BLI_task_pool_create(NULL, TASK_PRIORITY_HIGH, TASK_ISOLATION_ON);
+
+ for (int i = 0; i < thread_count; i++) {
+ olti[i].rb = rb;
+ olti[i].dg = depsgraph;
+ BLI_task_pool_push(tp, (TaskRunFunction)lineart_object_load_worker, &olti[i], 0, NULL);
+ }
+ BLI_task_pool_work_and_wait(tp);
+ BLI_task_pool_free(tp);
+
+ /* The step below is to serialize vertex index in the whole scene, so
+ * lineart_triangle_share_edge() can work properly from the lack of triangle adjacent info. */
+ int global_i = 0;
+
+ for (int i = 0; i < thread_count; i++) {
+ for (LineartObjectInfo *obi = olti[i].pending; obi; obi = obi->next) {
+ if (!obi->v_reln) {
+ continue;
+ }
+ LineartVert *v = (LineartVert *)obi->v_reln->pointer;
+ int v_count = obi->v_reln->element_count;
+ for (int vi = 0; vi < v_count; vi++) {
+ v[vi].index += global_i;
+ }
+ global_i += v_count;
+ lineart_finalize_object_edge_list(rb, obi);
+ }
+ }
+
+ if (G.debug_value == 4000) {
+ double t_elapsed = PIL_check_seconds_timer() - t_start;
+ printf("Line art loading time: %lf\n", t_elapsed);
+ }
}
/**
* Returns the two other verts of the triangle given a vertex. Returns false if the given vertex
* doesn't belong to this triangle.
*/
-static bool lineart_triangle_get_other_verts(const LineartTriangle *rt,
- const LineartVert *rv,
+static bool lineart_triangle_get_other_verts(const LineartTriangle *tri,
+ const LineartVert *vt,
LineartVert **l,
LineartVert **r)
{
- if (rt->v[0] == rv) {
- *l = rt->v[1];
- *r = rt->v[2];
+ if (tri->v[0] == vt) {
+ *l = tri->v[1];
+ *r = tri->v[2];
return true;
}
- if (rt->v[1] == rv) {
- *l = rt->v[2];
- *r = rt->v[0];
+ if (tri->v[1] == vt) {
+ *l = tri->v[2];
+ *r = tri->v[0];
return true;
}
- if (rt->v[2] == rv) {
- *l = rt->v[0];
- *r = rt->v[1];
+ if (tri->v[2] == vt) {
+ *l = tri->v[0];
+ *r = tri->v[1];
return true;
}
return false;
}
-static bool lineart_edge_from_triangle(const LineartTriangle *rt,
+static bool lineart_edge_from_triangle(const LineartTriangle *tri,
const LineartEdge *e,
bool allow_overlapping_edges)
{
/* Normally we just determine from the pointer address. */
- if (e->t1 == rt || e->t2 == rt) {
+ if (e->t1 == tri || e->t2 == tri) {
return true;
}
/* If allows overlapping, then we compare the vertex coordinates one by one to determine if one
* edge is from specific triangle. This is slower but can handle edge split cases very well. */
if (allow_overlapping_edges) {
-#define LRT_TRI_SAME_POINT(rt, i, pt) \
- ((LRT_DOUBLE_CLOSE_ENOUGH(rt->v[i]->gloc[0], pt->gloc[0]) && \
- LRT_DOUBLE_CLOSE_ENOUGH(rt->v[i]->gloc[1], pt->gloc[1]) && \
- LRT_DOUBLE_CLOSE_ENOUGH(rt->v[i]->gloc[2], pt->gloc[2])) || \
- (LRT_DOUBLE_CLOSE_ENOUGH(rt->v[i]->gloc[0], pt->gloc[0]) && \
- LRT_DOUBLE_CLOSE_ENOUGH(rt->v[i]->gloc[1], pt->gloc[1]) && \
- LRT_DOUBLE_CLOSE_ENOUGH(rt->v[i]->gloc[2], pt->gloc[2])))
- if ((LRT_TRI_SAME_POINT(rt, 0, e->v1) || LRT_TRI_SAME_POINT(rt, 1, e->v1) ||
- LRT_TRI_SAME_POINT(rt, 2, e->v1)) &&
- (LRT_TRI_SAME_POINT(rt, 0, e->v2) || LRT_TRI_SAME_POINT(rt, 1, e->v2) ||
- LRT_TRI_SAME_POINT(rt, 2, e->v2))) {
+#define LRT_TRI_SAME_POINT(tri, i, pt) \
+ ((LRT_DOUBLE_CLOSE_ENOUGH(tri->v[i]->gloc[0], pt->gloc[0]) && \
+ LRT_DOUBLE_CLOSE_ENOUGH(tri->v[i]->gloc[1], pt->gloc[1]) && \
+ LRT_DOUBLE_CLOSE_ENOUGH(tri->v[i]->gloc[2], pt->gloc[2])) || \
+ (LRT_DOUBLE_CLOSE_ENOUGH(tri->v[i]->gloc[0], pt->gloc[0]) && \
+ LRT_DOUBLE_CLOSE_ENOUGH(tri->v[i]->gloc[1], pt->gloc[1]) && \
+ LRT_DOUBLE_CLOSE_ENOUGH(tri->v[i]->gloc[2], pt->gloc[2])))
+ if ((LRT_TRI_SAME_POINT(tri, 0, e->v1) || LRT_TRI_SAME_POINT(tri, 1, e->v1) ||
+ LRT_TRI_SAME_POINT(tri, 2, e->v1)) &&
+ (LRT_TRI_SAME_POINT(tri, 0, e->v2) || LRT_TRI_SAME_POINT(tri, 1, e->v2) ||
+ LRT_TRI_SAME_POINT(tri, 2, e->v2))) {
return true;
}
#undef LRT_TRI_SAME_POINT
@@ -1948,7 +2112,7 @@ static bool lineart_edge_from_triangle(const LineartTriangle *rt,
* in ratio from `e->v1` to `e->v2`. The line is later cut with these two values.
*/
static bool lineart_triangle_edge_image_space_occlusion(SpinLock *UNUSED(spl),
- const LineartTriangle *rt,
+ const LineartTriangle *tri,
const LineartEdge *e,
const double *override_camera_loc,
const bool override_cam_is_persp,
@@ -1975,8 +2139,8 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *UNUSED(spl),
double gloc[4], trans[4];
double cut = -1;
- double *LFBC = e->v1->fbcoord, *RFBC = e->v2->fbcoord, *FBC0 = rt->v[0]->fbcoord,
- *FBC1 = rt->v[1]->fbcoord, *FBC2 = rt->v[2]->fbcoord;
+ double *LFBC = e->v1->fbcoord, *RFBC = e->v2->fbcoord, *FBC0 = tri->v[0]->fbcoord,
+ *FBC1 = tri->v[1]->fbcoord, *FBC2 = tri->v[2]->fbcoord;
/* Overlapping not possible, return early. */
if ((MAX3(FBC0[0], FBC1[0], FBC2[0]) < MIN2(LFBC[0], RFBC[0])) ||
@@ -1988,7 +2152,7 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *UNUSED(spl),
}
/* If the the line is one of the edge in the triangle, then it's not occluded. */
- if (lineart_edge_from_triangle(rt, e, allow_overlapping_edges)) {
+ if (lineart_edge_from_triangle(tri, e, allow_overlapping_edges)) {
return false;
}
@@ -2000,8 +2164,8 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *UNUSED(spl),
/* Sort the intersection distance. */
INTERSECT_SORT_MIN_TO_MAX_3(is[0], is[1], is[2], order);
- sub_v3_v3v3_db(Lv, e->v1->gloc, rt->v[0]->gloc);
- sub_v3_v3v3_db(Rv, e->v2->gloc, rt->v[0]->gloc);
+ sub_v3_v3v3_db(Lv, e->v1->gloc, tri->v[0]->gloc);
+ sub_v3_v3v3_db(Rv, e->v2->gloc, tri->v[0]->gloc);
copy_v3_v3_db(Cv, camera_dir);
@@ -2012,14 +2176,18 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *UNUSED(spl),
copy_v4_v4_db(vd4, override_camera_loc);
}
if (override_cam_is_persp) {
- sub_v3_v3v3_db(Cv, vd4, rt->v[0]->gloc);
+ sub_v3_v3v3_db(Cv, vd4, tri->v[0]->gloc);
}
- dot_l = dot_v3v3_db(Lv, rt->gn);
- dot_r = dot_v3v3_db(Rv, rt->gn);
- dot_f = dot_v3v3_db(Cv, rt->gn);
+ dot_l = dot_v3v3_db(Lv, tri->gn);
+ dot_r = dot_v3v3_db(Rv, tri->gn);
+ dot_f = dot_v3v3_db(Cv, tri->gn);
- if (!dot_f) {
+ /* NOTE(Yiming): When we don't use `dot_f==0` here, it's theoretically possible that _some_
+ * faces in perspective mode would get erroneously caught in this condition where they really are
+ * legit faces that would produce occlusion, but haven't encountered those yet in my test files.
+ */
+ if (fabs(dot_f) < FLT_EPSILON) {
return false;
}
@@ -2258,17 +2426,17 @@ static LineartVert *lineart_triangle_share_point(const LineartTriangle *l,
/**
* To save time and prevent overlapping lines when computing intersection lines.
*/
-static bool lineart_vert_already_intersected_2v(LineartVertIntersection *rv,
+static bool lineart_vert_already_intersected_2v(LineartVertIntersection *vt,
LineartVertIntersection *v1,
LineartVertIntersection *v2)
{
- return ((rv->isec1 == v1->base.index && rv->isec2 == v2->base.index) ||
- (rv->isec2 == v2->base.index && rv->isec1 == v1->base.index));
+ return ((vt->isec1 == v1->base.index && vt->isec2 == v2->base.index) ||
+ (vt->isec2 == v2->base.index && vt->isec1 == v1->base.index));
}
-static void lineart_vert_set_intersection_2v(LineartVert *rv, LineartVert *v1, LineartVert *v2)
+static void lineart_vert_set_intersection_2v(LineartVert *vt, LineartVert *v1, LineartVert *v2)
{
- LineartVertIntersection *irv = (LineartVertIntersection *)rv;
+ LineartVertIntersection *irv = (LineartVertIntersection *)vt;
irv->isec1 = v1->index;
irv->isec2 = v2->index;
}
@@ -2281,7 +2449,7 @@ static void lineart_vert_set_intersection_2v(LineartVert *rv, LineartVert *v1, L
static LineartVert *lineart_triangle_2v_intersection_test(LineartRenderBuffer *rb,
LineartVert *v1,
LineartVert *v2,
- LineartTriangle *rt,
+ LineartTriangle *tri,
LineartTriangle *testing,
LineartVert *last)
{
@@ -2293,11 +2461,11 @@ static LineartVert *lineart_triangle_2v_intersection_test(LineartRenderBuffer *r
LineartVert *l = v1, *r = v2;
for (LinkNode *ln = (void *)testing->intersecting_verts; ln; ln = ln->next) {
- LineartVertIntersection *rv = ln->link;
- if (rv->intersecting_with == rt &&
+ LineartVertIntersection *vt = ln->link;
+ if (vt->intersecting_with == tri &&
lineart_vert_already_intersected_2v(
- rv, (LineartVertIntersection *)l, (LineartVertIntersection *)r)) {
- return (LineartVert *)rv;
+ vt, (LineartVertIntersection *)l, (LineartVertIntersection *)r)) {
+ return (LineartVert *)vt;
}
}
@@ -2331,7 +2499,7 @@ static LineartVert *lineart_triangle_2v_intersection_test(LineartRenderBuffer *r
/* This is an intersection vert, the size is bigger than LineartVert,
* allocated separately. */
- result = lineart_mem_aquire(&rb->render_data_pool, sizeof(LineartVertIntersection));
+ result = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartVertIntersection));
/* Indicate the data structure difference. */
result->flag = LRT_VERT_HAS_INTERSECTION_DATA;
@@ -2347,7 +2515,7 @@ static LineartVert *lineart_triangle_2v_intersection_test(LineartRenderBuffer *r
* Test if two triangles intersect. Generates one intersection line if the check succeeds.
*/
static LineartEdge *lineart_triangle_intersect(LineartRenderBuffer *rb,
- LineartTriangle *rt,
+ LineartTriangle *tri,
LineartTriangle *testing)
{
LineartVert *v1 = 0, *v2 = 0;
@@ -2366,62 +2534,62 @@ static LineartEdge *lineart_triangle_intersect(LineartRenderBuffer *rb,
ZMax = rb->far_clip;
ZMin = rb->near_clip;
copy_v3_v3_db(cl, rb->camera_pos);
- LineartVert *share = lineart_triangle_share_point(testing, rt);
+ LineartVert *share = lineart_triangle_share_point(testing, tri);
if (share) {
/* If triangles have sharing points like `abc` and `acd`, then we only need to detect `bc`
* against `acd` or `cd` against `abc`. */
LineartVert *new_share;
- lineart_triangle_get_other_verts(rt, share, &sv1, &sv2);
+ lineart_triangle_get_other_verts(tri, share, &sv1, &sv2);
- v1 = new_share = lineart_mem_aquire(&rb->render_data_pool, (sizeof(LineartVertIntersection)));
+ v1 = new_share = lineart_mem_acquire(&rb->render_data_pool, (sizeof(LineartVertIntersection)));
new_share->flag = LRT_VERT_HAS_INTERSECTION_DATA;
copy_v3_v3_db(new_share->gloc, share->gloc);
- v2 = lineart_triangle_2v_intersection_test(rb, sv1, sv2, rt, testing, 0);
+ v2 = lineart_triangle_2v_intersection_test(rb, sv1, sv2, tri, testing, 0);
if (v2 == NULL) {
lineart_triangle_get_other_verts(testing, share, &sv1, &sv2);
- v2 = lineart_triangle_2v_intersection_test(rb, sv1, sv2, testing, rt, 0);
+ v2 = lineart_triangle_2v_intersection_test(rb, sv1, sv2, testing, tri, 0);
if (v2 == NULL) {
return 0;
}
lineart_prepend_pool(&testing->intersecting_verts, &rb->render_data_pool, new_share);
}
else {
- lineart_prepend_pool(&rt->intersecting_verts, &rb->render_data_pool, new_share);
+ lineart_prepend_pool(&tri->intersecting_verts, &rb->render_data_pool, new_share);
}
}
else {
/* If not sharing any points, then we need to try all the possibilities. */
- E0T = lineart_triangle_2v_intersection_test(rb, rt->v[0], rt->v[1], rt, testing, 0);
+ E0T = lineart_triangle_2v_intersection_test(rb, tri->v[0], tri->v[1], tri, testing, 0);
if (E0T && (!(*next))) {
(*next) = E0T;
- lineart_vert_set_intersection_2v((*next), rt->v[0], rt->v[1]);
+ lineart_vert_set_intersection_2v((*next), tri->v[0], tri->v[1]);
next = &v2;
}
- E1T = lineart_triangle_2v_intersection_test(rb, rt->v[1], rt->v[2], rt, testing, v1);
+ E1T = lineart_triangle_2v_intersection_test(rb, tri->v[1], tri->v[2], tri, testing, v1);
if (E1T && (!(*next))) {
(*next) = E1T;
- lineart_vert_set_intersection_2v((*next), rt->v[1], rt->v[2]);
+ lineart_vert_set_intersection_2v((*next), tri->v[1], tri->v[2]);
next = &v2;
}
if (!(*next)) {
- E2T = lineart_triangle_2v_intersection_test(rb, rt->v[2], rt->v[0], rt, testing, v1);
+ E2T = lineart_triangle_2v_intersection_test(rb, tri->v[2], tri->v[0], tri, testing, v1);
}
if (E2T && (!(*next))) {
(*next) = E2T;
- lineart_vert_set_intersection_2v((*next), rt->v[2], rt->v[0]);
+ lineart_vert_set_intersection_2v((*next), tri->v[2], tri->v[0]);
next = &v2;
}
if (!(*next)) {
TE0 = lineart_triangle_2v_intersection_test(
- rb, testing->v[0], testing->v[1], testing, rt, v1);
+ rb, testing->v[0], testing->v[1], testing, tri, v1);
}
if (TE0 && (!(*next))) {
(*next) = TE0;
@@ -2430,7 +2598,7 @@ static LineartEdge *lineart_triangle_intersect(LineartRenderBuffer *rb,
}
if (!(*next)) {
TE1 = lineart_triangle_2v_intersection_test(
- rb, testing->v[1], testing->v[2], testing, rt, v1);
+ rb, testing->v[1], testing->v[2], testing, tri, v1);
}
if (TE1 && (!(*next))) {
(*next) = TE1;
@@ -2439,7 +2607,7 @@ static LineartEdge *lineart_triangle_intersect(LineartRenderBuffer *rb,
}
if (!(*next)) {
TE2 = lineart_triangle_2v_intersection_test(
- rb, testing->v[2], testing->v[0], testing, rt, v1);
+ rb, testing->v[2], testing->v[0], testing, tri, v1);
}
if (TE2 && (!(*next))) {
(*next) = TE2;
@@ -2471,70 +2639,66 @@ static LineartEdge *lineart_triangle_intersect(LineartRenderBuffer *rb,
v1->fbcoord[2] = ZMin * ZMax / (ZMax - fabs(v1->fbcoord[2]) * (ZMax - ZMin));
v2->fbcoord[2] = ZMin * ZMax / (ZMax - fabs(v2->fbcoord[2]) * (ZMax - ZMin));
- ((LineartVertIntersection *)v1)->intersecting_with = rt;
+ ((LineartVertIntersection *)v1)->intersecting_with = tri;
((LineartVertIntersection *)v2)->intersecting_with = testing;
- result = lineart_mem_aquire(&rb->render_data_pool, sizeof(LineartEdge));
+ result = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdge));
result->v1 = v1;
result->v2 = v2;
- result->t1 = rt;
+ result->t1 = tri;
result->t2 = testing;
- LineartLineSegment *rls = lineart_mem_aquire(&rb->render_data_pool, sizeof(LineartLineSegment));
- BLI_addtail(&result->segments, rls);
+ LineartEdgeSegment *es = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdgeSegment));
+ BLI_addtail(&result->segments, es);
/* Don't need to OR flags right now, just a type mark. */
result->flags = LRT_EDGE_FLAG_INTERSECTION;
- lineart_prepend_edge_direct(&rb->intersection_lines, result);
+
+ lineart_prepend_edge_direct(&rb->intersection.first, result);
int r1, r2, c1, c2, row, col;
if (lineart_get_edge_bounding_areas(rb, result, &r1, &r2, &c1, &c2)) {
for (row = r1; row != r2 + 1; row++) {
for (col = c1; col != c2 + 1; col++) {
- lineart_bounding_area_link_line(
+ lineart_bounding_area_link_edge(
rb, &rb->initial_bounding_areas[row * LRT_BA_ROWS + col], result);
}
}
}
-
- rb->intersection_count++;
-
return result;
}
static void lineart_triangle_intersect_in_bounding_area(LineartRenderBuffer *rb,
- LineartTriangle *rt,
+ LineartTriangle *tri,
LineartBoundingArea *ba)
{
/* Testing_triangle->testing[0] is used to store pairing triangle reference.
* See definition of LineartTriangleThread for more info. */
LineartTriangle *testing_triangle;
- LineartTriangleThread *rtt;
- LinkData *lip, *next_lip;
+ LineartTriangleThread *tt;
- double *G0 = rt->v[0]->gloc, *G1 = rt->v[1]->gloc, *G2 = rt->v[2]->gloc;
+ double *G0 = tri->v[0]->gloc, *G1 = tri->v[1]->gloc, *G2 = tri->v[2]->gloc;
/* If this is not the smallest subdiv bounding area.*/
if (ba->child) {
- lineart_triangle_intersect_in_bounding_area(rb, rt, &ba->child[0]);
- lineart_triangle_intersect_in_bounding_area(rb, rt, &ba->child[1]);
- lineart_triangle_intersect_in_bounding_area(rb, rt, &ba->child[2]);
- lineart_triangle_intersect_in_bounding_area(rb, rt, &ba->child[3]);
+ lineart_triangle_intersect_in_bounding_area(rb, tri, &ba->child[0]);
+ lineart_triangle_intersect_in_bounding_area(rb, tri, &ba->child[1]);
+ lineart_triangle_intersect_in_bounding_area(rb, tri, &ba->child[2]);
+ lineart_triangle_intersect_in_bounding_area(rb, tri, &ba->child[3]);
return;
}
/* If this _is_ the smallest subdiv bounding area, then do the intersections there. */
- for (lip = ba->linked_triangles.first; lip; lip = next_lip) {
- next_lip = lip->next;
- testing_triangle = lip->data;
- rtt = (LineartTriangleThread *)testing_triangle;
+ for (int i = 0; i < ba->triangle_count; i++) {
+ testing_triangle = ba->linked_triangles[i];
+ tt = (LineartTriangleThread *)testing_triangle;
- if (testing_triangle == rt || rtt->testing_e[0] == (LineartEdge *)rt) {
+ if (testing_triangle == tri || tt->testing_e[0] == (LineartEdge *)tri) {
continue;
}
- rtt->testing_e[0] = (LineartEdge *)rt;
+ tt->testing_e[0] = (LineartEdge *)tri;
if ((testing_triangle->flags & LRT_TRIANGLE_NO_INTERSECTION) ||
((testing_triangle->flags & LRT_TRIANGLE_INTERSECTION_ONLY) &&
- (rt->flags & LRT_TRIANGLE_INTERSECTION_ONLY))) {
+ (tri->flags & LRT_TRIANGLE_INTERSECTION_ONLY))) {
continue;
}
@@ -2548,12 +2712,12 @@ static void lineart_triangle_intersect_in_bounding_area(LineartRenderBuffer *rb,
(MAX3(G0[0], G1[0], G2[0]) < MIN3(RG0[0], RG1[0], RG2[0])) ||
(MIN3(G0[1], G1[1], G2[1]) > MAX3(RG0[1], RG1[1], RG2[1])) ||
(MAX3(G0[1], G1[1], G2[1]) < MIN3(RG0[1], RG1[1], RG2[1])) ||
- lineart_triangle_share_edge(rt, testing_triangle)) {
+ lineart_triangle_share_edge(tri, testing_triangle)) {
continue;
}
/* If we do need to compute intersection, then finally do it. */
- lineart_triangle_intersect(rb, rt, testing_triangle);
+ lineart_triangle_intersect(rb, tri, testing_triangle);
}
}
@@ -2585,22 +2749,11 @@ static void lineart_destroy_render_data(LineartRenderBuffer *rb)
return;
}
- rb->contour_count = 0;
- rb->contour_managed = NULL;
- rb->intersection_count = 0;
- rb->intersection_managed = NULL;
- rb->material_line_count = 0;
- rb->material_managed = NULL;
- rb->crease_count = 0;
- rb->crease_managed = NULL;
- rb->edge_mark_count = 0;
- rb->edge_mark_managed = NULL;
-
- rb->contours = NULL;
- rb->intersection_lines = NULL;
- rb->crease_lines = NULL;
- rb->material_lines = NULL;
- rb->edge_marks = NULL;
+ memset(&rb->contour, 0, sizeof(ListBase));
+ memset(&rb->crease, 0, sizeof(ListBase));
+ memset(&rb->intersection, 0, sizeof(ListBase));
+ memset(&rb->edge_mark, 0, sizeof(ListBase));
+ memset(&rb->material, 0, sizeof(ListBase));
BLI_listbase_clear(&rb->chains);
BLI_listbase_clear(&rb->wasted_cuts);
@@ -2658,9 +2811,17 @@ static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene,
rb->w = scene->r.xsch;
rb->h = scene->r.ysch;
+ if (rb->cam_is_persp) {
+ rb->tile_recursive_level = LRT_TILE_RECURSIVE_PERSPECTIVE;
+ }
+ else {
+ rb->tile_recursive_level = LRT_TILE_RECURSIVE_ORTHO;
+ }
+
double asp = ((double)rb->w / (double)rb->h);
- rb->shift_x = (asp >= 1) ? c->shiftx : c->shiftx * asp;
- rb->shift_y = (asp <= 1) ? c->shifty : c->shifty * asp;
+ int fit = BKE_camera_sensor_fit(c->sensor_fit, rb->w, rb->h);
+ rb->shift_x = fit == CAMERA_SENSOR_FIT_HOR ? c->shiftx : c->shiftx / asp;
+ rb->shift_y = fit == CAMERA_SENSOR_FIT_VERT ? c->shifty : c->shifty * asp;
rb->crease_threshold = cos(M_PI - lmd->crease_threshold);
rb->angle_splitting_threshold = lmd->angle_splitting_threshold;
@@ -2716,7 +2877,7 @@ static void lineart_main_bounding_area_make_initial(LineartRenderBuffer *rb)
rb->height_per_tile = span_h;
rb->bounding_area_count = sp_w * sp_h;
- rb->initial_bounding_areas = lineart_mem_aquire(
+ rb->initial_bounding_areas = lineart_mem_acquire(
&rb->render_data_pool, sizeof(LineartBoundingArea) * rb->bounding_area_count);
/* Initialize tiles. */
@@ -2733,6 +2894,14 @@ static void lineart_main_bounding_area_make_initial(LineartRenderBuffer *rb)
ba->cx = (ba->l + ba->r) / 2;
ba->cy = (ba->u + ba->b) / 2;
+ /* Init linked_triangles array. */
+ ba->max_triangle_count = LRT_TILE_SPLITTING_TRIANGLE_LIMIT;
+ ba->max_line_count = LRT_TILE_EDGE_COUNT_INITIAL;
+ ba->linked_triangles = lineart_mem_acquire(
+ &rb->render_data_pool, sizeof(LineartTriangle *) * ba->max_triangle_count);
+ ba->linked_lines = lineart_mem_acquire(&rb->render_data_pool,
+ sizeof(LineartEdge *) * ba->max_line_count);
+
/* Link adjacent ones. */
if (row) {
lineart_list_append_pointer_pool(
@@ -2910,9 +3079,9 @@ static void lineart_bounding_area_split(LineartRenderBuffer *rb,
LineartBoundingArea *root,
int recursive_level)
{
- LineartBoundingArea *ba = lineart_mem_aquire(&rb->render_data_pool,
- sizeof(LineartBoundingArea) * 4);
- LineartTriangle *rt;
+ LineartBoundingArea *ba = lineart_mem_acquire(&rb->render_data_pool,
+ sizeof(LineartBoundingArea) * 4);
+ LineartTriangle *tri;
LineartEdge *e;
ba[0].l = root->cx;
@@ -2947,35 +3116,47 @@ static void lineart_bounding_area_split(LineartRenderBuffer *rb,
lineart_bounding_areas_connect_new(rb, root);
- while ((rt = lineart_list_pop_pointer_no_free(&root->linked_triangles)) != NULL) {
+ /* Init linked_triangles array. */
+ for (int i = 0; i < 4; i++) {
+ ba[i].max_triangle_count = LRT_TILE_SPLITTING_TRIANGLE_LIMIT;
+ ba[i].max_line_count = LRT_TILE_EDGE_COUNT_INITIAL;
+ ba[i].linked_triangles = lineart_mem_acquire(
+ &rb->render_data_pool, sizeof(LineartTriangle *) * LRT_TILE_SPLITTING_TRIANGLE_LIMIT);
+ ba[i].linked_lines = lineart_mem_acquire(&rb->render_data_pool,
+ sizeof(LineartEdge *) * LRT_TILE_EDGE_COUNT_INITIAL);
+ }
+
+ for (int i = 0; i < root->triangle_count; i++) {
+ tri = root->linked_triangles[i];
LineartBoundingArea *cba = root->child;
double b[4];
- b[0] = MIN3(rt->v[0]->fbcoord[0], rt->v[1]->fbcoord[0], rt->v[2]->fbcoord[0]);
- b[1] = MAX3(rt->v[0]->fbcoord[0], rt->v[1]->fbcoord[0], rt->v[2]->fbcoord[0]);
- b[2] = MAX3(rt->v[0]->fbcoord[1], rt->v[1]->fbcoord[1], rt->v[2]->fbcoord[1]);
- b[3] = MIN3(rt->v[0]->fbcoord[1], rt->v[1]->fbcoord[1], rt->v[2]->fbcoord[1]);
+ b[0] = MIN3(tri->v[0]->fbcoord[0], tri->v[1]->fbcoord[0], tri->v[2]->fbcoord[0]);
+ b[1] = MAX3(tri->v[0]->fbcoord[0], tri->v[1]->fbcoord[0], tri->v[2]->fbcoord[0]);
+ b[2] = MAX3(tri->v[0]->fbcoord[1], tri->v[1]->fbcoord[1], tri->v[2]->fbcoord[1]);
+ b[3] = MIN3(tri->v[0]->fbcoord[1], tri->v[1]->fbcoord[1], tri->v[2]->fbcoord[1]);
if (LRT_BOUND_AREA_CROSSES(b, &cba[0].l)) {
- lineart_bounding_area_link_triangle(rb, &cba[0], rt, b, 0, recursive_level + 1, false);
+ lineart_bounding_area_link_triangle(rb, &cba[0], tri, b, 0, recursive_level + 1, false);
}
if (LRT_BOUND_AREA_CROSSES(b, &cba[1].l)) {
- lineart_bounding_area_link_triangle(rb, &cba[1], rt, b, 0, recursive_level + 1, false);
+ lineart_bounding_area_link_triangle(rb, &cba[1], tri, b, 0, recursive_level + 1, false);
}
if (LRT_BOUND_AREA_CROSSES(b, &cba[2].l)) {
- lineart_bounding_area_link_triangle(rb, &cba[2], rt, b, 0, recursive_level + 1, false);
+ lineart_bounding_area_link_triangle(rb, &cba[2], tri, b, 0, recursive_level + 1, false);
}
if (LRT_BOUND_AREA_CROSSES(b, &cba[3].l)) {
- lineart_bounding_area_link_triangle(rb, &cba[3], rt, b, 0, recursive_level + 1, false);
+ lineart_bounding_area_link_triangle(rb, &cba[3], tri, b, 0, recursive_level + 1, false);
}
}
- while ((e = lineart_list_pop_pointer_no_free(&root->linked_lines)) != NULL) {
- lineart_bounding_area_link_line(rb, root, e);
+ for (int i = 0; i < root->line_count; i++) {
+ e = root->linked_lines[i];
+ lineart_bounding_area_link_edge(rb, root, e);
}
rb->bounding_area_count += 3;
}
-static bool lineart_bounding_area_line_intersect(LineartRenderBuffer *UNUSED(fb),
+static bool lineart_bounding_area_edge_intersect(LineartRenderBuffer *UNUSED(fb),
const double l[2],
const double r[2],
LineartBoundingArea *ba)
@@ -3019,11 +3200,11 @@ static bool lineart_bounding_area_line_intersect(LineartRenderBuffer *UNUSED(fb)
}
static bool lineart_bounding_area_triangle_intersect(LineartRenderBuffer *fb,
- LineartTriangle *rt,
+ LineartTriangle *tri,
LineartBoundingArea *ba)
{
double p1[2], p2[2], p3[2], p4[2];
- double *FBC1 = rt->v[0]->fbcoord, *FBC2 = rt->v[1]->fbcoord, *FBC3 = rt->v[2]->fbcoord;
+ double *FBC1 = tri->v[0]->fbcoord, *FBC2 = tri->v[1]->fbcoord, *FBC3 = tri->v[2]->fbcoord;
p3[0] = p1[0] = (double)ba->l;
p2[1] = p1[1] = (double)ba->b;
@@ -3043,9 +3224,9 @@ static bool lineart_bounding_area_triangle_intersect(LineartRenderBuffer *fb,
return true;
}
- if ((lineart_bounding_area_line_intersect(fb, FBC1, FBC2, ba)) ||
- (lineart_bounding_area_line_intersect(fb, FBC2, FBC3, ba)) ||
- (lineart_bounding_area_line_intersect(fb, FBC3, FBC1, ba))) {
+ if ((lineart_bounding_area_edge_intersect(fb, FBC1, FBC2, ba)) ||
+ (lineart_bounding_area_edge_intersect(fb, FBC2, FBC3, ba)) ||
+ (lineart_bounding_area_edge_intersect(fb, FBC3, FBC1, ba))) {
return true;
}
@@ -3058,27 +3239,27 @@ static bool lineart_bounding_area_triangle_intersect(LineartRenderBuffer *fb,
*/
static void lineart_bounding_area_link_triangle(LineartRenderBuffer *rb,
LineartBoundingArea *root_ba,
- LineartTriangle *rt,
+ LineartTriangle *tri,
double *LRUB,
int recursive,
int recursive_level,
bool do_intersection)
{
- if (!lineart_bounding_area_triangle_intersect(rb, rt, root_ba)) {
+ if (!lineart_bounding_area_triangle_intersect(rb, tri, root_ba)) {
return;
}
if (root_ba->child == NULL) {
- lineart_list_append_pointer_pool(&root_ba->linked_triangles, &rb->render_data_pool, rt);
- root_ba->triangle_count++;
+ lineart_bounding_area_triangle_add(rb, root_ba, tri);
/* If splitting doesn't improve triangle separation, then shouldn't allow splitting anymore.
* Here we use recursive limit. This is especially useful in orthographic render,
* where a lot of faces could easily line up perfectly in image space,
* which can not be separated by simply slicing the image tile. */
- if (root_ba->triangle_count > 200 && recursive && recursive_level < 10) {
+ if (root_ba->triangle_count >= LRT_TILE_SPLITTING_TRIANGLE_LIMIT && recursive &&
+ recursive_level < rb->tile_recursive_level) {
lineart_bounding_area_split(rb, root_ba, recursive_level);
}
if (recursive && do_intersection && rb->use_intersections) {
- lineart_triangle_intersect_in_bounding_area(rb, rt, root_ba);
+ lineart_triangle_intersect_in_bounding_area(rb, tri, root_ba);
}
}
else {
@@ -3086,54 +3267,54 @@ static void lineart_bounding_area_link_triangle(LineartRenderBuffer *rb,
double *B1 = LRUB;
double b[4];
if (!LRUB) {
- b[0] = MIN3(rt->v[0]->fbcoord[0], rt->v[1]->fbcoord[0], rt->v[2]->fbcoord[0]);
- b[1] = MAX3(rt->v[0]->fbcoord[0], rt->v[1]->fbcoord[0], rt->v[2]->fbcoord[0]);
- b[2] = MAX3(rt->v[0]->fbcoord[1], rt->v[1]->fbcoord[1], rt->v[2]->fbcoord[1]);
- b[3] = MIN3(rt->v[0]->fbcoord[1], rt->v[1]->fbcoord[1], rt->v[2]->fbcoord[1]);
+ b[0] = MIN3(tri->v[0]->fbcoord[0], tri->v[1]->fbcoord[0], tri->v[2]->fbcoord[0]);
+ b[1] = MAX3(tri->v[0]->fbcoord[0], tri->v[1]->fbcoord[0], tri->v[2]->fbcoord[0]);
+ b[2] = MAX3(tri->v[0]->fbcoord[1], tri->v[1]->fbcoord[1], tri->v[2]->fbcoord[1]);
+ b[3] = MIN3(tri->v[0]->fbcoord[1], tri->v[1]->fbcoord[1], tri->v[2]->fbcoord[1]);
B1 = b;
}
if (LRT_BOUND_AREA_CROSSES(B1, &ba[0].l)) {
lineart_bounding_area_link_triangle(
- rb, &ba[0], rt, B1, recursive, recursive_level + 1, do_intersection);
+ rb, &ba[0], tri, B1, recursive, recursive_level + 1, do_intersection);
}
if (LRT_BOUND_AREA_CROSSES(B1, &ba[1].l)) {
lineart_bounding_area_link_triangle(
- rb, &ba[1], rt, B1, recursive, recursive_level + 1, do_intersection);
+ rb, &ba[1], tri, B1, recursive, recursive_level + 1, do_intersection);
}
if (LRT_BOUND_AREA_CROSSES(B1, &ba[2].l)) {
lineart_bounding_area_link_triangle(
- rb, &ba[2], rt, B1, recursive, recursive_level + 1, do_intersection);
+ rb, &ba[2], tri, B1, recursive, recursive_level + 1, do_intersection);
}
if (LRT_BOUND_AREA_CROSSES(B1, &ba[3].l)) {
lineart_bounding_area_link_triangle(
- rb, &ba[3], rt, B1, recursive, recursive_level + 1, do_intersection);
+ rb, &ba[3], tri, B1, recursive, recursive_level + 1, do_intersection);
}
}
}
-static void lineart_bounding_area_link_line(LineartRenderBuffer *rb,
+static void lineart_bounding_area_link_edge(LineartRenderBuffer *rb,
LineartBoundingArea *root_ba,
LineartEdge *e)
{
if (root_ba->child == NULL) {
- lineart_list_append_pointer_pool(&root_ba->linked_lines, &rb->render_data_pool, e);
+ lineart_bounding_area_line_add(rb, root_ba, e);
}
else {
- if (lineart_bounding_area_line_intersect(
+ if (lineart_bounding_area_edge_intersect(
rb, e->v1->fbcoord, e->v2->fbcoord, &root_ba->child[0])) {
- lineart_bounding_area_link_line(rb, &root_ba->child[0], e);
+ lineart_bounding_area_link_edge(rb, &root_ba->child[0], e);
}
- if (lineart_bounding_area_line_intersect(
+ if (lineart_bounding_area_edge_intersect(
rb, e->v1->fbcoord, e->v2->fbcoord, &root_ba->child[1])) {
- lineart_bounding_area_link_line(rb, &root_ba->child[1], e);
+ lineart_bounding_area_link_edge(rb, &root_ba->child[1], e);
}
- if (lineart_bounding_area_line_intersect(
+ if (lineart_bounding_area_edge_intersect(
rb, e->v1->fbcoord, e->v2->fbcoord, &root_ba->child[2])) {
- lineart_bounding_area_link_line(rb, &root_ba->child[2], e);
+ lineart_bounding_area_link_edge(rb, &root_ba->child[2], e);
}
- if (lineart_bounding_area_line_intersect(
+ if (lineart_bounding_area_edge_intersect(
rb, e->v1->fbcoord, e->v2->fbcoord, &root_ba->child[3])) {
- lineart_bounding_area_link_line(rb, &root_ba->child[3], e);
+ lineart_bounding_area_link_edge(rb, &root_ba->child[3], e);
}
}
}
@@ -3149,7 +3330,7 @@ static void lineart_main_link_lines(LineartRenderBuffer *rb)
if (lineart_get_edge_bounding_areas(rb, e, &r1, &r2, &c1, &c2)) {
for (row = r1; row != r2 + 1; row++) {
for (col = c1; col != c2 + 1; col++) {
- lineart_bounding_area_link_line(
+ lineart_bounding_area_link_edge(
rb, &rb->initial_bounding_areas[row * LRT_BA_ROWS + col], e);
}
}
@@ -3159,7 +3340,7 @@ static void lineart_main_link_lines(LineartRenderBuffer *rb)
}
static bool lineart_get_triangle_bounding_areas(LineartRenderBuffer *rb,
- LineartTriangle *rt,
+ LineartTriangle *tri,
int *rowbegin,
int *rowend,
int *colbegin,
@@ -3168,14 +3349,14 @@ static bool lineart_get_triangle_bounding_areas(LineartRenderBuffer *rb,
double sp_w = rb->width_per_tile, sp_h = rb->height_per_tile;
double b[4];
- if (!rt->v[0] || !rt->v[1] || !rt->v[2]) {
+ if (!tri->v[0] || !tri->v[1] || !tri->v[2]) {
return false;
}
- b[0] = MIN3(rt->v[0]->fbcoord[0], rt->v[1]->fbcoord[0], rt->v[2]->fbcoord[0]);
- b[1] = MAX3(rt->v[0]->fbcoord[0], rt->v[1]->fbcoord[0], rt->v[2]->fbcoord[0]);
- b[2] = MIN3(rt->v[0]->fbcoord[1], rt->v[1]->fbcoord[1], rt->v[2]->fbcoord[1]);
- b[3] = MAX3(rt->v[0]->fbcoord[1], rt->v[1]->fbcoord[1], rt->v[2]->fbcoord[1]);
+ b[0] = MIN3(tri->v[0]->fbcoord[0], tri->v[1]->fbcoord[0], tri->v[2]->fbcoord[0]);
+ b[1] = MAX3(tri->v[0]->fbcoord[0], tri->v[1]->fbcoord[0], tri->v[2]->fbcoord[0]);
+ b[2] = MIN3(tri->v[0]->fbcoord[1], tri->v[1]->fbcoord[1], tri->v[2]->fbcoord[1]);
+ b[3] = MAX3(tri->v[0]->fbcoord[1], tri->v[1]->fbcoord[1], tri->v[2]->fbcoord[1]);
if (b[0] > 1 || b[1] < -1 || b[2] > 1 || b[3] < -1) {
return false;
@@ -3342,33 +3523,33 @@ LineartBoundingArea *MOD_lineart_get_bounding_area(LineartRenderBuffer *rb, doub
*/
static void lineart_main_add_triangles(LineartRenderBuffer *rb)
{
- LineartTriangle *rt;
+ LineartTriangle *tri;
int i, lim;
int x1, x2, y1, y2;
int r, co;
- LISTBASE_FOREACH (LineartElementLinkNode *, reln, &rb->triangle_buffer_pointers) {
- rt = reln->pointer;
- lim = reln->element_count;
+ LISTBASE_FOREACH (LineartElementLinkNode *, eln, &rb->triangle_buffer_pointers) {
+ tri = eln->pointer;
+ lim = eln->element_count;
for (i = 0; i < lim; i++) {
- if ((rt->flags & LRT_CULL_USED) || (rt->flags & LRT_CULL_DISCARD)) {
- rt = (void *)(((uchar *)rt) + rb->triangle_size);
+ if ((tri->flags & LRT_CULL_USED) || (tri->flags & LRT_CULL_DISCARD)) {
+ tri = (void *)(((uchar *)tri) + rb->triangle_size);
continue;
}
- if (lineart_get_triangle_bounding_areas(rb, rt, &y1, &y2, &x1, &x2)) {
+ if (lineart_get_triangle_bounding_areas(rb, tri, &y1, &y2, &x1, &x2)) {
for (co = x1; co <= x2; co++) {
for (r = y1; r <= y2; r++) {
lineart_bounding_area_link_triangle(rb,
&rb->initial_bounding_areas[r * LRT_BA_ROWS + co],
- rt,
+ tri,
0,
1,
0,
- (!(rt->flags & LRT_TRIANGLE_NO_INTERSECTION)));
+ (!(tri->flags & LRT_TRIANGLE_NO_INTERSECTION)));
}
}
} /* Else throw away. */
- rt = (void *)(((uchar *)rt) + rb->triangle_size);
+ tri = (void *)(((uchar *)tri) + rb->triangle_size);
}
}
}
@@ -3643,6 +3824,14 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, LineartGpencilModif
Scene *scene = DEG_get_evaluated_scene(depsgraph);
int intersections_only = 0; /* Not used right now, but preserve for future. */
+ double t_start;
+
+ if (G.debug_value == 4000) {
+ t_start = PIL_check_seconds_timer();
+ }
+
+ BKE_scene_camera_switch_update(scene);
+
if (!scene->camera) {
return false;
}
@@ -3736,6 +3925,9 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, LineartGpencilModif
if (G.debug_value == 4000) {
lineart_count_and_print_render_buffer_memory(rb);
+
+ double t_elapsed = PIL_check_seconds_timer() - t_start;
+ printf("Line art total time: %lf\n", t_elapsed);
}
return true;
@@ -3768,7 +3960,6 @@ static void lineart_gpencil_generate(LineartRenderBuffer *rb,
uchar transparency_mask,
short thickness,
float opacity,
- float resample_length,
const char *source_vgname,
const char *vgname,
int modifier_flags)
@@ -3802,54 +3993,53 @@ static void lineart_gpencil_generate(LineartRenderBuffer *rb,
int enabled_types = lineart_rb_edge_types(rb);
bool invert_input = modifier_flags & LRT_GPENCIL_INVERT_SOURCE_VGROUP;
bool match_output = modifier_flags & LRT_GPENCIL_MATCH_OUTPUT_VGROUP;
- bool preserve_weight = modifier_flags & LRT_GPENCIL_SOFT_SELECTION;
- LISTBASE_FOREACH (LineartLineChain *, rlc, &rb->chains) {
+ LISTBASE_FOREACH (LineartEdgeChain *, ec, &rb->chains) {
- if (rlc->picked) {
+ if (ec->picked) {
continue;
}
- if (!(rlc->type & (types & enabled_types))) {
+ if (!(ec->type & (types & enabled_types))) {
continue;
}
- if (rlc->level > level_end || rlc->level < level_start) {
+ if (ec->level > level_end || ec->level < level_start) {
continue;
}
- if (orig_ob && orig_ob != rlc->object_ref) {
+ if (orig_ob && orig_ob != ec->object_ref) {
continue;
}
- if (orig_col && rlc->object_ref) {
- if (!BKE_collection_has_object_recursive_instanced(orig_col, (Object *)rlc->object_ref)) {
+ if (orig_col && ec->object_ref) {
+ if (!BKE_collection_has_object_recursive_instanced(orig_col, (Object *)ec->object_ref)) {
continue;
}
}
if (transparency_flags & LRT_GPENCIL_TRANSPARENCY_ENABLE) {
if (transparency_flags & LRT_GPENCIL_TRANSPARENCY_MATCH) {
- if (rlc->transparency_mask != transparency_mask) {
+ if (ec->transparency_mask != transparency_mask) {
continue;
}
}
else {
- if (!(rlc->transparency_mask & transparency_mask)) {
+ if (!(ec->transparency_mask & transparency_mask)) {
continue;
}
}
}
/* Preserved: If we ever do asynchronous generation, this picked flag should be set here. */
- // rlc->picked = 1;
+ // ec->picked = 1;
int array_idx = 0;
- int count = MOD_lineart_chain_count(rlc);
+ int count = MOD_lineart_chain_count(ec);
bGPDstroke *gps = BKE_gpencil_stroke_add(gpf, color_idx, count, thickness, false);
float *stroke_data = MEM_callocN(sizeof(float) * count * GP_PRIM_DATABUF_SIZE,
"line art add stroke");
- LISTBASE_FOREACH (LineartLineChainItem *, rlci, &rlc->chain) {
- stroke_data[array_idx] = rlci->gpos[0];
- stroke_data[array_idx + 1] = rlci->gpos[1];
- stroke_data[array_idx + 2] = rlci->gpos[2];
+ LISTBASE_FOREACH (LineartEdgeChainItem *, eci, &ec->chain) {
+ stroke_data[array_idx] = eci->gpos[0];
+ stroke_data[array_idx + 1] = eci->gpos[1];
+ stroke_data[array_idx + 2] = eci->gpos[2];
mul_m4_v3(gp_obmat_inverse, &stroke_data[array_idx]);
stroke_data[array_idx + 3] = 1; /* thickness. */
stroke_data[array_idx + 4] = opacity; /* hardness?. */
@@ -3858,12 +4048,12 @@ static void lineart_gpencil_generate(LineartRenderBuffer *rb,
BKE_gpencil_stroke_add_points(gps, stroke_data, count, mat);
BKE_gpencil_dvert_ensure(gps);
- gps->mat_nr = material_nr;
+ gps->mat_nr = max_ii(material_nr, 0);
MEM_freeN(stroke_data);
if (source_vgname && vgname) {
- Object *eval_ob = DEG_get_evaluated_object(depsgraph, rlc->object_ref);
+ Object *eval_ob = DEG_get_evaluated_object(depsgraph, ec->object_ref);
int gpdg = -1;
if ((match_output || (gpdg = BKE_object_defgroup_name_index(gpencil_object, vgname)) >= 0)) {
if (eval_ob && eval_ob->type == OB_MESH) {
@@ -3879,25 +4069,20 @@ static void lineart_gpencil_generate(LineartRenderBuffer *rb,
}
}
int sindex = 0, vindex;
- LISTBASE_FOREACH (LineartLineChainItem *, rlci, &rlc->chain) {
- vindex = rlci->index;
+ LISTBASE_FOREACH (LineartEdgeChainItem *, eci, &ec->chain) {
+ vindex = eci->index;
if (vindex >= me->totvert) {
break;
}
MDeformWeight *mdw = BKE_defvert_ensure_index(&me->dvert[vindex], dindex);
MDeformWeight *gdw = BKE_defvert_ensure_index(&gps->dvert[sindex], gpdg);
- if (preserve_weight) {
- float use_weight = mdw->weight;
- if (invert_input) {
- use_weight = 1 - use_weight;
- }
- gdw->weight = MAX2(use_weight, gdw->weight);
- }
- else {
- if (mdw->weight > 0.999f) {
- gdw->weight = 1.0f;
- }
+
+ float use_weight = mdw->weight;
+ if (invert_input) {
+ use_weight = 1 - use_weight;
}
+ gdw->weight = MAX2(use_weight, gdw->weight);
+
sindex++;
}
}
@@ -3908,9 +4093,6 @@ static void lineart_gpencil_generate(LineartRenderBuffer *rb,
}
}
- if (resample_length > 0.0001) {
- BKE_gpencil_stroke_sample(gpencil_object->data, gps, resample_length, false);
- }
if (G.debug_value == 4000) {
BKE_gpencil_stroke_set_random_color(gps);
}
@@ -3941,7 +4123,6 @@ void MOD_lineart_gpencil_generate(LineartRenderBuffer *rb,
uchar transparency_mask,
short thickness,
float opacity,
- float resample_length,
const char *source_vgname,
const char *vgname,
int modifier_flags)
@@ -3991,7 +4172,6 @@ void MOD_lineart_gpencil_generate(LineartRenderBuffer *rb,
transparency_mask,
thickness,
opacity,
- resample_length,
source_vgname,
vgname,
modifier_flags);
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h b/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h
index 2c3130b46c9..e457d4a83a0 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h
@@ -43,6 +43,13 @@ void *lineart_list_append_pointer_pool_sized(ListBase *h,
struct LineartStaticMemPool *smp,
void *data,
int size);
+void *lineart_list_append_pointer_pool_thread(ListBase *h,
+ struct LineartStaticMemPool *smp,
+ void *data);
+void *lineart_list_append_pointer_pool_sized_thread(ListBase *h,
+ LineartStaticMemPool *smp,
+ void *data,
+ int size);
void *list_push_pointer_static(ListBase *h, struct LineartStaticMemPool *smp, void *p);
void *list_push_pointer_static_sized(ListBase *h,
struct LineartStaticMemPool *smp,
@@ -54,11 +61,11 @@ void lineart_list_remove_pointer_item_no_free(ListBase *h, LinkData *lip);
struct LineartStaticMemPoolNode *lineart_mem_new_static_pool(struct LineartStaticMemPool *smp,
size_t size);
-void *lineart_mem_aquire(struct LineartStaticMemPool *smp, size_t size);
-void *lineart_mem_aquire_thread(struct LineartStaticMemPool *smp, size_t size);
+void *lineart_mem_acquire(struct LineartStaticMemPool *smp, size_t size);
+void *lineart_mem_acquire_thread(struct LineartStaticMemPool *smp, size_t size);
void lineart_mem_destroy(struct LineartStaticMemPool *smp);
-void lineart_prepend_edge_direct(struct LineartEdge **first, void *node);
+void lineart_prepend_edge_direct(void **list_head, void *node);
void lineart_prepend_pool(LinkNode **first, struct LineartStaticMemPool *smp, void *link);
void lineart_matrix_ortho_44d(double (*mProjection)[4],
@@ -76,29 +83,42 @@ int lineart_count_intersection_segment_count(struct LineartRenderBuffer *rb);
void lineart_count_and_print_render_buffer_memory(struct LineartRenderBuffer *rb);
#define LRT_ITER_ALL_LINES_BEGIN \
- LineartEdge *e, *next_e, **current_list; \
- e = rb->contours; \
- for (current_list = &rb->contours; e; e = next_e) { \
+ LineartEdge *e, *next_e; \
+ void **current_head; \
+ e = rb->contour.first; \
+ if (!e) { \
+ e = rb->crease.first; \
+ } \
+ if (!e) { \
+ e = rb->material.first; \
+ } \
+ if (!e) { \
+ e = rb->edge_mark.first; \
+ } \
+ if (!e) { \
+ e = rb->intersection.first; \
+ } \
+ for (current_head = &rb->contour.first; e; e = next_e) { \
next_e = e->next;
#define LRT_ITER_ALL_LINES_NEXT \
while (!next_e) { \
- if (current_list == &rb->contours) { \
- current_list = &rb->crease_lines; \
+ if (current_head == &rb->contour.first) { \
+ current_head = &rb->crease.first; \
} \
- else if (current_list == &rb->crease_lines) { \
- current_list = &rb->material_lines; \
+ else if (current_head == &rb->crease.first) { \
+ current_head = &rb->material.first; \
} \
- else if (current_list == &rb->material_lines) { \
- current_list = &rb->edge_marks; \
+ else if (current_head == &rb->material.first) { \
+ current_head = &rb->edge_mark.first; \
} \
- else if (current_list == &rb->edge_marks) { \
- current_list = &rb->intersection_lines; \
+ else if (current_head == &rb->edge_mark.first) { \
+ current_head = &rb->intersection.first; \
} \
else { \
break; \
} \
- next_e = *current_list; \
+ next_e = *current_head; \
}
#define LRT_ITER_ALL_LINES_END \
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c
index 3ec32ad501e..c023c63ebc9 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c
@@ -131,7 +131,6 @@ static bool bake_strokes(Object *ob, Depsgraph *dg, GpencilModifierData *md, int
lmd->transparency_mask,
lmd->thickness,
lmd->opacity,
- lmd->resample_length,
lmd->source_vertex_group,
lmd->vgname,
lmd->flags);
@@ -365,7 +364,8 @@ static int lineart_gpencil_bake_strokes_commom_modal(bContext *C,
static void lineart_gpencil_clear_strokes_exec_common(Object *ob)
{
- if (ob->type != OB_GPENCIL) {
+ /* TODO: move these checks to an operator poll function. */
+ if ((ob == NULL) || ob->type != OB_GPENCIL) {
return;
}
LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) {
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_util.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_util.c
index 4d136fe0d0e..47cca0ecd61 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/lineart_util.c
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_util.c
@@ -43,7 +43,7 @@ void *lineart_list_append_pointer_pool(ListBase *h, LineartStaticMemPool *smp, v
if (h == NULL) {
return 0;
}
- lip = lineart_mem_aquire(smp, sizeof(LinkData));
+ lip = lineart_mem_acquire(smp, sizeof(LinkData));
lip->data = data;
BLI_addtail(h, lip);
return lip;
@@ -57,7 +57,32 @@ void *lineart_list_append_pointer_pool_sized(ListBase *h,
if (h == NULL) {
return 0;
}
- lip = lineart_mem_aquire(smp, size);
+ lip = lineart_mem_acquire(smp, size);
+ lip->data = data;
+ BLI_addtail(h, lip);
+ return lip;
+}
+void *lineart_list_append_pointer_pool_thread(ListBase *h, LineartStaticMemPool *smp, void *data)
+{
+ LinkData *lip;
+ if (h == NULL) {
+ return 0;
+ }
+ lip = lineart_mem_acquire_thread(smp, sizeof(LinkData));
+ lip->data = data;
+ BLI_addtail(h, lip);
+ return lip;
+}
+void *lineart_list_append_pointer_pool_sized_thread(ListBase *h,
+ LineartStaticMemPool *smp,
+ void *data,
+ int size)
+{
+ LinkData *lip;
+ if (h == NULL) {
+ return 0;
+ }
+ lip = lineart_mem_acquire_thread(smp, size);
lip->data = data;
BLI_addtail(h, lip);
return lip;
@@ -82,17 +107,17 @@ void lineart_list_remove_pointer_item_no_free(ListBase *h, LinkData *lip)
LineartStaticMemPoolNode *lineart_mem_new_static_pool(LineartStaticMemPool *smp, size_t size)
{
size_t set_size = size;
- if (set_size < LRT_MEMORY_POOL_64MB) {
- set_size = LRT_MEMORY_POOL_64MB; /* Prevent too many small allocations. */
+ if (set_size < LRT_MEMORY_POOL_1MB) {
+ set_size = LRT_MEMORY_POOL_1MB; /* Prevent too many small allocations. */
}
- size_t total_size = size + sizeof(LineartStaticMemPoolNode);
+ size_t total_size = set_size + sizeof(LineartStaticMemPoolNode);
LineartStaticMemPoolNode *smpn = MEM_callocN(total_size, "mempool");
smpn->size = total_size;
smpn->used_byte = sizeof(LineartStaticMemPoolNode);
BLI_addhead(&smp->pools, smpn);
return smpn;
}
-void *lineart_mem_aquire(LineartStaticMemPool *smp, size_t size)
+void *lineart_mem_acquire(LineartStaticMemPool *smp, size_t size)
{
LineartStaticMemPoolNode *smpn = smp->pools.first;
void *ret;
@@ -107,7 +132,7 @@ void *lineart_mem_aquire(LineartStaticMemPool *smp, size_t size)
return ret;
}
-void *lineart_mem_aquire_thread(LineartStaticMemPool *smp, size_t size)
+void *lineart_mem_acquire_thread(LineartStaticMemPool *smp, size_t size)
{
void *ret;
@@ -135,16 +160,16 @@ void lineart_mem_destroy(LineartStaticMemPool *smp)
}
}
-void lineart_prepend_edge_direct(LineartEdge **first, void *node)
+void lineart_prepend_edge_direct(void **list_head, void *node)
{
LineartEdge *e_n = (LineartEdge *)node;
- e_n->next = (*first);
- (*first) = e_n;
+ e_n->next = (*list_head);
+ (*list_head) = e_n;
}
void lineart_prepend_pool(LinkNode **first, LineartStaticMemPool *smp, void *link)
{
- LinkNode *ln = lineart_mem_aquire_thread(smp, sizeof(LinkNode));
+ LinkNode *ln = lineart_mem_acquire_thread(smp, sizeof(LinkNode));
ln->next = (*first);
ln->link = link;
(*first) = ln;
@@ -211,7 +236,7 @@ void lineart_count_and_print_render_buffer_memory(LineartRenderBuffer *rb)
LISTBASE_FOREACH (LineartStaticMemPoolNode *, smpn, &rb->render_data_pool.pools) {
count_this++;
- sum_this += LRT_MEMORY_POOL_64MB;
+ sum_this += LRT_MEMORY_POOL_1MB;
}
printf("LANPR Memory allocated %zu Standalone nodes, total %zu Bytes.\n", count_this, sum_this);
total += sum_this;
diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt
index 69a79e2f2ce..8468985309f 100644
--- a/source/blender/gpu/CMakeLists.txt
+++ b/source/blender/gpu/CMakeLists.txt
@@ -62,6 +62,7 @@ set(SRC
intern/gpu_buffers.c
intern/gpu_capabilities.cc
intern/gpu_codegen.c
+ intern/gpu_compute.cc
intern/gpu_context.cc
intern/gpu_debug.cc
intern/gpu_drawlist.cc
@@ -91,6 +92,7 @@ set(SRC
opengl/gl_backend.cc
opengl/gl_batch.cc
+ opengl/gl_compute.cc
opengl/gl_context.cc
opengl/gl_debug.cc
opengl/gl_debug_layer.cc
@@ -113,6 +115,7 @@ set(SRC
GPU_buffers.h
GPU_capabilities.h
GPU_common.h
+ GPU_compute.h
GPU_context.h
GPU_debug.h
GPU_drawlist.h
@@ -163,6 +166,7 @@ set(SRC
opengl/gl_backend.hh
opengl/gl_batch.hh
+ opengl/gl_compute.hh
opengl/gl_context.hh
opengl/gl_debug.hh
opengl/gl_drawlist.hh
@@ -275,6 +279,7 @@ data_to_c_simple(shaders/material/gpu_shader_material_anisotropic.glsl SRC)
data_to_c_simple(shaders/material/gpu_shader_material_attribute.glsl SRC)
data_to_c_simple(shaders/material/gpu_shader_material_background.glsl SRC)
data_to_c_simple(shaders/material/gpu_shader_material_bevel.glsl SRC)
+data_to_c_simple(shaders/material/gpu_shader_material_wavelength.glsl SRC)
data_to_c_simple(shaders/material/gpu_shader_material_blackbody.glsl SRC)
data_to_c_simple(shaders/material/gpu_shader_material_bright_contrast.glsl SRC)
data_to_c_simple(shaders/material/gpu_shader_material_bump.glsl SRC)
@@ -381,11 +386,18 @@ endif()
blender_add_lib(bf_gpu "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
+if(CXX_WARN_NO_SUGGEST_OVERRIDE)
+ target_compile_options(bf_gpu PRIVATE $<$<COMPILE_LANGUAGE:CXX>:-Wsuggest-override>)
+endif()
+
if(WITH_GTESTS)
if(WITH_OPENGL_DRAW_TESTS)
set(TEST_SRC
tests/gpu_testing.cc
+ tests/gpu_index_buffer_test.cc
+ tests/gpu_shader_test.cc
+
tests/gpu_testing.hh
)
set(TEST_INC
diff --git a/source/blender/gpu/GPU_capabilities.h b/source/blender/gpu/GPU_capabilities.h
index b95053a3715..0c054d4f264 100644
--- a/source/blender/gpu/GPU_capabilities.h
+++ b/source/blender/gpu/GPU_capabilities.h
@@ -37,6 +37,17 @@ int GPU_max_textures(void);
int GPU_max_textures_vert(void);
int GPU_max_textures_geom(void);
int GPU_max_textures_frag(void);
+int GPU_max_work_group_count(int index);
+int GPU_max_work_group_size(int index);
+int GPU_max_uniforms_vert(void);
+int GPU_max_uniforms_frag(void);
+int GPU_max_batch_indices(void);
+int GPU_max_batch_vertices(void);
+int GPU_max_vertex_attribs(void);
+int GPU_max_varying_floats(void);
+
+int GPU_extensions_len(void);
+const char *GPU_extension_get(int i);
int GPU_texture_size_with_limit(int res, bool limit_gl_texture_size);
@@ -46,6 +57,8 @@ bool GPU_use_main_context_workaround(void);
bool GPU_use_hq_normals_workaround(void);
bool GPU_crappy_amd_driver(void);
+bool GPU_compute_shader_support(void);
+bool GPU_shader_storage_buffer_objects_support(void);
bool GPU_shader_image_load_store_support(void);
bool GPU_mem_stats_supported(void);
diff --git a/source/blender/gpu/GPU_common.h b/source/blender/gpu/GPU_common.h
index 1be74701176..c00bf581e8c 100644
--- a/source/blender/gpu/GPU_common.h
+++ b/source/blender/gpu/GPU_common.h
@@ -24,6 +24,7 @@
#pragma once
#define PROGRAM_NO_OPTI 0
+//#define GPU_NO_USE_PY_REFERENCES
#if defined(NDEBUG)
# define TRUST_NO_ONE 0
diff --git a/source/blender/blenkernel/BKE_mesh_boolean_convert.h b/source/blender/gpu/GPU_compute.h
index c126d714542..a048f72c0a0 100644
--- a/source/blender/blenkernel/BKE_mesh_boolean_convert.h
+++ b/source/blender/gpu/GPU_compute.h
@@ -12,28 +12,26 @@
* 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.
- *
- * The Original Code is Copyright (C) 2019 Blender Foundation.
- * All rights reserved.
*/
-#pragma once
-
/** \file
- * \ingroup bke
+ * \ingroup gpu
*/
+#pragma once
+
+#include "BLI_sys_types.h"
+
+#include "GPU_shader.h"
+
#ifdef __cplusplus
extern "C" {
#endif
-Mesh *BKE_mesh_boolean(const Mesh **meshes,
- const float (*obmats[])[4][4],
- const short **material_remaps,
- const int meshes_len,
- const bool use_self,
- const bool hole_tolerant,
- const int boolean_mode);
+void GPU_compute_dispatch(GPUShader *shader,
+ uint groups_x_len,
+ uint groups_y_len,
+ uint groups_z_len);
#ifdef __cplusplus
}
diff --git a/source/blender/gpu/GPU_framebuffer.h b/source/blender/gpu/GPU_framebuffer.h
index af94c1fb0e6..6482d9a9d3e 100644
--- a/source/blender/gpu/GPU_framebuffer.h
+++ b/source/blender/gpu/GPU_framebuffer.h
@@ -209,6 +209,11 @@ void GPU_framebuffer_recursive_downsample(GPUFrameBuffer *fb,
void (*callback)(void *userData, int level),
void *userData);
+#ifndef GPU_NO_USE_PY_REFERENCES
+void **GPU_framebuffer_py_reference_get(GPUFrameBuffer *gpu_fb);
+void GPU_framebuffer_py_reference_set(GPUFrameBuffer *gpu_fb, void **py_ref);
+#endif
+
void GPU_framebuffer_push(GPUFrameBuffer *fb);
GPUFrameBuffer *GPU_framebuffer_pop(void);
uint GPU_framebuffer_stack_level_get(void);
diff --git a/source/blender/gpu/GPU_index_buffer.h b/source/blender/gpu/GPU_index_buffer.h
index 76aab3c196b..a89ed91674f 100644
--- a/source/blender/gpu/GPU_index_buffer.h
+++ b/source/blender/gpu/GPU_index_buffer.h
@@ -40,6 +40,8 @@ typedef struct GPUIndexBufBuilder {
uint max_allowed_index;
uint max_index_len;
uint index_len;
+ uint index_min;
+ uint index_max;
GPUPrimType prim_type;
uint32_t *data;
} GPUIndexBufBuilder;
@@ -49,6 +51,14 @@ void GPU_indexbuf_init_ex(GPUIndexBufBuilder *, GPUPrimType, uint index_len, uin
/* supports only GPU_PRIM_POINTS, GPU_PRIM_LINES and GPU_PRIM_TRIS. */
void GPU_indexbuf_init(GPUIndexBufBuilder *, GPUPrimType, uint prim_len, uint vertex_len);
+GPUIndexBuf *GPU_indexbuf_build_on_device(uint index_len);
+
+/*
+ * Thread safe.
+ *
+ * Function inspired by the reduction directives of multithread work APIs..
+ */
+void GPU_indexbuf_join(GPUIndexBufBuilder *builder, const GPUIndexBufBuilder *parent_builder);
void GPU_indexbuf_add_generic_vert(GPUIndexBufBuilder *, uint v);
void GPU_indexbuf_add_primitive_restart(GPUIndexBufBuilder *);
@@ -70,6 +80,8 @@ void GPU_indexbuf_set_tri_restart(GPUIndexBufBuilder *builder, uint elem);
GPUIndexBuf *GPU_indexbuf_build(GPUIndexBufBuilder *);
void GPU_indexbuf_build_in_place(GPUIndexBufBuilder *, GPUIndexBuf *);
+void GPU_indexbuf_bind_as_ssbo(GPUIndexBuf *elem, int binding);
+
/* Create a sub-range of an existing index-buffer. */
GPUIndexBuf *GPU_indexbuf_create_subrange(GPUIndexBuf *elem_src, uint start, uint length);
void GPU_indexbuf_create_subrange_in_place(GPUIndexBuf *elem,
@@ -77,6 +89,15 @@ void GPU_indexbuf_create_subrange_in_place(GPUIndexBuf *elem,
uint start,
uint length);
+/**
+ * (Download and) return a pointer containing the data of an index buffer.
+ *
+ * Note that the returned pointer is still owned by the driver. To get an
+ * local copy, use `GPU_indexbuf_unmap` after calling `GPU_indexbuf_read`.
+ */
+const uint32_t *GPU_indexbuf_read(GPUIndexBuf *elem);
+uint32_t *GPU_indexbuf_unmap(const GPUIndexBuf *elem, const uint32_t *mapped_buffer);
+
void GPU_indexbuf_discard(GPUIndexBuf *elem);
bool GPU_indexbuf_is_init(GPUIndexBuf *elem);
diff --git a/source/blender/gpu/GPU_matrix.h b/source/blender/gpu/GPU_matrix.h
index aad6ae9e2ba..e073263f352 100644
--- a/source/blender/gpu/GPU_matrix.h
+++ b/source/blender/gpu/GPU_matrix.h
@@ -118,21 +118,27 @@ bool GPU_matrix_unproject_precalc(struct GPUMatrixUnproject_Precalc *unproj_prec
const float proj[4][4],
const int view[4]);
-void GPU_matrix_project(const float world[3],
- const float model[4][4],
- const float proj[4][4],
- const int view[4],
- float r_win[3]);
-
-bool GPU_matrix_unproject(const float win[3],
- const float model[4][4],
- const float proj[4][4],
- const int view[4],
- float r_world[3]);
-
-void GPU_matrix_unproject_with_precalc(const struct GPUMatrixUnproject_Precalc *unproj_precalc,
- const float win[3],
- float r_world[3]);
+void GPU_matrix_project_3fv(const float world[3],
+ const float model[4][4],
+ const float proj[4][4],
+ const int view[4],
+ float r_win[3]);
+
+void GPU_matrix_project_2fv(const float world[3],
+ const float model[4][4],
+ const float proj[4][4],
+ const int view[4],
+ float r_win[2]);
+
+bool GPU_matrix_unproject_3fv(const float win[3],
+ const float model[4][4],
+ const float proj[4][4],
+ const int view[4],
+ float r_world[3]);
+
+void GPU_matrix_unproject_3fv_with_precalc(const struct GPUMatrixUnproject_Precalc *unproj_precalc,
+ const float win[3],
+ float r_world[3]);
/* 2D Projection Matrix */
diff --git a/source/blender/gpu/GPU_platform.h b/source/blender/gpu/GPU_platform.h
index c457b829bf7..fa7d5d7fba8 100644
--- a/source/blender/gpu/GPU_platform.h
+++ b/source/blender/gpu/GPU_platform.h
@@ -68,6 +68,9 @@ extern "C" {
bool GPU_type_matches(eGPUDeviceType device, eGPUOSType os, eGPUDriverType driver);
eGPUSupportLevel GPU_platform_support_level(void);
+const char *GPU_platform_vendor(void);
+const char *GPU_platform_renderer(void);
+const char *GPU_platform_version(void);
const char *GPU_platform_support_level_key(void);
const char *GPU_platform_gpu_name(void);
diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h
index 9824c7016dc..3923c920c9e 100644
--- a/source/blender/gpu/GPU_shader.h
+++ b/source/blender/gpu/GPU_shader.h
@@ -27,6 +27,7 @@
extern "C" {
#endif
+struct GPUIndexBuf;
struct GPUVertBuf;
/** Opaque type hiding #blender::gpu::Shader */
@@ -45,6 +46,10 @@ GPUShader *GPU_shader_create(const char *vertcode,
const char *libcode,
const char *defines,
const char *shname);
+GPUShader *GPU_shader_create_compute(const char *computecode,
+ const char *libcode,
+ const char *defines,
+ const char *shname);
GPUShader *GPU_shader_create_from_python(const char *vertcode,
const char *fragcode,
const char *geomcode,
@@ -53,6 +58,7 @@ GPUShader *GPU_shader_create_from_python(const char *vertcode,
GPUShader *GPU_shader_create_ex(const char *vertcode,
const char *fragcode,
const char *geomcode,
+ const char *computecode,
const char *libcode,
const char *defines,
const eGPUShaderTFBType tf_type,
@@ -126,6 +132,7 @@ int GPU_shader_get_uniform(GPUShader *shader, const char *name);
int GPU_shader_get_builtin_uniform(GPUShader *shader, int builtin);
int GPU_shader_get_builtin_block(GPUShader *shader, int builtin);
int GPU_shader_get_uniform_block(GPUShader *shader, const char *name);
+int GPU_shader_get_ssbo(GPUShader *shader, const char *name);
int GPU_shader_get_uniform_block_binding(GPUShader *shader, const char *name);
int GPU_shader_get_texture_binding(GPUShader *shader, const char *name);
diff --git a/source/blender/gpu/GPU_state.h b/source/blender/gpu/GPU_state.h
index 0687f271670..a338728804c 100644
--- a/source/blender/gpu/GPU_state.h
+++ b/source/blender/gpu/GPU_state.h
@@ -39,6 +39,7 @@ typedef enum eGPUBarrier {
GPU_BARRIER_NONE = 0,
GPU_BARRIER_SHADER_IMAGE_ACCESS = (1 << 0),
GPU_BARRIER_TEXTURE_FETCH = (1 << 1),
+ GPU_BARRIER_SHADER_STORAGE = (1 << 2),
} eGPUBarrier;
ENUM_OPERATORS(eGPUBarrier, GPU_BARRIER_TEXTURE_FETCH)
diff --git a/source/blender/gpu/GPU_texture.h b/source/blender/gpu/GPU_texture.h
index d9a01663de0..f980c8fdcd7 100644
--- a/source/blender/gpu/GPU_texture.h
+++ b/source/blender/gpu/GPU_texture.h
@@ -269,6 +269,12 @@ bool GPU_texture_cube(const GPUTexture *tex);
bool GPU_texture_depth(const GPUTexture *tex);
bool GPU_texture_stencil(const GPUTexture *tex);
bool GPU_texture_integer(const GPUTexture *tex);
+
+#ifndef GPU_NO_USE_PY_REFERENCES
+void **GPU_texture_py_reference_get(GPUTexture *tex);
+void GPU_texture_py_reference_set(GPUTexture *tex, void **py_ref);
+#endif
+
int GPU_texture_opengl_bindcode(const GPUTexture *tex);
void GPU_texture_get_mipmap_size(GPUTexture *tex, int lvl, int *size);
diff --git a/source/blender/gpu/GPU_vertex_buffer.h b/source/blender/gpu/GPU_vertex_buffer.h
index aae58de533b..2c54016daa7 100644
--- a/source/blender/gpu/GPU_vertex_buffer.h
+++ b/source/blender/gpu/GPU_vertex_buffer.h
@@ -59,6 +59,7 @@ typedef enum {
GPU_USAGE_STREAM,
GPU_USAGE_STATIC, /* do not keep data in memory */
GPU_USAGE_DYNAMIC,
+ GPU_USAGE_DEVICE_ONLY, /* Do not do host->device data transfers. */
} GPUUsageType;
/** Opaque type hiding blender::gpu::VertBuf. */
@@ -70,6 +71,14 @@ GPUVertBuf *GPU_vertbuf_create_with_format_ex(const GPUVertFormat *, GPUUsageTyp
#define GPU_vertbuf_create_with_format(format) \
GPU_vertbuf_create_with_format_ex(format, GPU_USAGE_STATIC)
+/**
+ * (Download and) return a pointer containing the data of a vertex buffer.
+ *
+ * Note that the returned pointer is still owned by the driver. To get an
+ * local copy, use `GPU_vertbuf_unmap` after calling `GPU_vertbuf_read`.
+ */
+const void *GPU_vertbuf_read(GPUVertBuf *verts);
+void *GPU_vertbuf_unmap(const GPUVertBuf *verts, const void *mapped_data);
void GPU_vertbuf_clear(GPUVertBuf *verts);
void GPU_vertbuf_discard(GPUVertBuf *);
@@ -138,6 +147,7 @@ uint GPU_vertbuf_get_vertex_len(const GPUVertBuf *verts);
GPUVertBufStatus GPU_vertbuf_get_status(const GPUVertBuf *verts);
void GPU_vertbuf_use(GPUVertBuf *);
+void GPU_vertbuf_bind_as_ssbo(struct GPUVertBuf *verts, int binding);
/* XXX do not use. */
void GPU_vertbuf_update_sub(GPUVertBuf *verts, uint start, uint len, void *data);
diff --git a/source/blender/gpu/intern/gpu_backend.hh b/source/blender/gpu/intern/gpu_backend.hh
index 04ec82a9213..73792215569 100644
--- a/source/blender/gpu/intern/gpu_backend.hh
+++ b/source/blender/gpu/intern/gpu_backend.hh
@@ -47,6 +47,7 @@ class GPUBackend {
static GPUBackend *get(void);
virtual void samplers_update(void) = 0;
+ virtual void compute_dispatch(int groups_x_len, int groups_y_len, int groups_z_len) = 0;
virtual Context *context_alloc(void *ghost_window) = 0;
diff --git a/source/blender/gpu/intern/gpu_batch_private.hh b/source/blender/gpu/intern/gpu_batch_private.hh
index d0fbd1432b3..eed96864f20 100644
--- a/source/blender/gpu/intern/gpu_batch_private.hh
+++ b/source/blender/gpu/intern/gpu_batch_private.hh
@@ -42,8 +42,7 @@ namespace gpu {
*/
class Batch : public GPUBatch {
public:
- Batch(){};
- virtual ~Batch(){};
+ virtual ~Batch() = default;
virtual void draw(int v_first, int v_count, int i_first, int i_count) = 0;
diff --git a/source/blender/gpu/intern/gpu_capabilities.cc b/source/blender/gpu/intern/gpu_capabilities.cc
index 6d9182dcf17..c6e9dc210cb 100644
--- a/source/blender/gpu/intern/gpu_capabilities.cc
+++ b/source/blender/gpu/intern/gpu_capabilities.cc
@@ -82,6 +82,56 @@ int GPU_max_textures(void)
return GCaps.max_textures;
}
+int GPU_max_work_group_count(int index)
+{
+ return GCaps.max_work_group_count[index];
+}
+
+int GPU_max_work_group_size(int index)
+{
+ return GCaps.max_work_group_size[index];
+}
+
+int GPU_max_uniforms_vert(void)
+{
+ return GCaps.max_uniforms_vert;
+}
+
+int GPU_max_uniforms_frag(void)
+{
+ return GCaps.max_uniforms_frag;
+}
+
+int GPU_max_batch_indices(void)
+{
+ return GCaps.max_batch_indices;
+}
+
+int GPU_max_batch_vertices(void)
+{
+ return GCaps.max_batch_vertices;
+}
+
+int GPU_max_vertex_attribs(void)
+{
+ return GCaps.max_vertex_attribs;
+}
+
+int GPU_max_varying_floats(void)
+{
+ return GCaps.max_varying_floats;
+}
+
+int GPU_extensions_len(void)
+{
+ return GCaps.extensions_len;
+}
+
+const char *GPU_extension_get(int i)
+{
+ return GCaps.extension_get ? GCaps.extension_get(i) : "\0";
+}
+
bool GPU_mip_render_workaround(void)
{
return GCaps.mip_render_workaround;
@@ -108,6 +158,16 @@ bool GPU_use_hq_normals_workaround(void)
return GCaps.use_hq_normals_workaround;
}
+bool GPU_compute_shader_support(void)
+{
+ return GCaps.compute_shader_support;
+}
+
+bool GPU_shader_storage_buffer_objects_support(void)
+{
+ return GCaps.shader_storage_buffer_objects_support;
+}
+
bool GPU_shader_image_load_store_support(void)
{
return GCaps.shader_image_load_store_support;
diff --git a/source/blender/gpu/intern/gpu_capabilities_private.hh b/source/blender/gpu/intern/gpu_capabilities_private.hh
index 2b3292749f8..95cf7fd335d 100644
--- a/source/blender/gpu/intern/gpu_capabilities_private.hh
+++ b/source/blender/gpu/intern/gpu_capabilities_private.hh
@@ -41,7 +41,20 @@ struct GPUCapabilities {
int max_textures_vert = 0;
int max_textures_geom = 0;
int max_textures_frag = 0;
+ int max_work_group_count[3] = {0, 0, 0};
+ int max_work_group_size[3] = {0, 0, 0};
+ int max_uniforms_vert = 0;
+ int max_uniforms_frag = 0;
+ int max_batch_indices = 0;
+ int max_batch_vertices = 0;
+ int max_vertex_attribs = 0;
+ int max_varying_floats = 0;
+ int extensions_len = 0;
+ const char *(*extension_get)(int);
+
bool mem_stats_support = false;
+ bool compute_shader_support = false;
+ bool shader_storage_buffer_objects_support = false;
bool shader_image_load_store_support = false;
/* OpenGL related workarounds. */
bool mip_render_workaround = false;
diff --git a/source/blender/gpu/intern/gpu_compute.cc b/source/blender/gpu/intern/gpu_compute.cc
new file mode 100644
index 00000000000..7a8ae2acf9a
--- /dev/null
+++ b/source/blender/gpu/intern/gpu_compute.cc
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#include "GPU_compute.h"
+
+#include "gpu_backend.hh"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void GPU_compute_dispatch(GPUShader *shader,
+ uint groups_x_len,
+ uint groups_y_len,
+ uint groups_z_len)
+{
+ blender::gpu::GPUBackend &gpu_backend = *blender::gpu::GPUBackend::get();
+ GPU_shader_bind(shader);
+ gpu_backend.compute_dispatch(groups_x_len, groups_y_len, groups_z_len);
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/gpu/intern/gpu_context.cc b/source/blender/gpu/intern/gpu_context.cc
index a9d32dcf297..b5a437b46f7 100644
--- a/source/blender/gpu/intern/gpu_context.cc
+++ b/source/blender/gpu/intern/gpu_context.cc
@@ -24,7 +24,7 @@
* Use these instead of glGenBuffers & its friends
* - alloc must be called from a thread that is bound
* to the context that will be used for drawing with
- * this vao.
+ * this VAO.
* - free can be called from any thread
*/
diff --git a/source/blender/gpu/intern/gpu_debug.cc b/source/blender/gpu/intern/gpu_debug.cc
index 63e7024b74b..2d9fb7822ae 100644
--- a/source/blender/gpu/intern/gpu_debug.cc
+++ b/source/blender/gpu/intern/gpu_debug.cc
@@ -86,8 +86,8 @@ bool GPU_debug_group_match(const char *ref)
if (ctx == nullptr) {
return false;
}
- DebugStack &stack = ctx->debug_stack;
- for (StringRef &name : stack) {
+ const DebugStack &stack = ctx->debug_stack;
+ for (const StringRef &name : stack) {
if (name == ref) {
return true;
}
diff --git a/source/blender/gpu/intern/gpu_framebuffer.cc b/source/blender/gpu/intern/gpu_framebuffer.cc
index 1e3cf479462..1293cc0953d 100644
--- a/source/blender/gpu/intern/gpu_framebuffer.cc
+++ b/source/blender/gpu/intern/gpu_framebuffer.cc
@@ -71,6 +71,12 @@ FrameBuffer::~FrameBuffer()
reinterpret_cast<Texture *>(attachment.tex)->detach_from(this);
}
}
+
+#ifndef GPU_NO_USE_PY_REFERENCES
+ if (this->py_ref) {
+ *this->py_ref = nullptr;
+ }
+#endif
}
/** \} */
@@ -473,6 +479,19 @@ void GPU_framebuffer_recursive_downsample(GPUFrameBuffer *gpu_fb,
unwrap(gpu_fb)->recursive_downsample(max_lvl, callback, userData);
}
+#ifndef GPU_NO_USE_PY_REFERENCES
+void **GPU_framebuffer_py_reference_get(GPUFrameBuffer *gpu_fb)
+{
+ return unwrap(gpu_fb)->py_ref;
+}
+
+void GPU_framebuffer_py_reference_set(GPUFrameBuffer *gpu_fb, void **py_ref)
+{
+ BLI_assert(py_ref == nullptr || unwrap(gpu_fb)->py_ref == nullptr);
+ unwrap(gpu_fb)->py_ref = py_ref;
+}
+#endif
+
/** \} */
/* -------------------------------------------------------------------- */
diff --git a/source/blender/gpu/intern/gpu_framebuffer_private.hh b/source/blender/gpu/intern/gpu_framebuffer_private.hh
index d63d72cf4f7..2b00c239af4 100644
--- a/source/blender/gpu/intern/gpu_framebuffer_private.hh
+++ b/source/blender/gpu/intern/gpu_framebuffer_private.hh
@@ -100,6 +100,20 @@ class FrameBuffer {
bool scissor_test_ = false;
bool dirty_state_ = true;
+#ifndef GPU_NO_USE_PY_REFERENCES
+ public:
+ /**
+ * Reference of a pointer that needs to be cleaned when deallocating the frame-buffer.
+ * Points to #BPyGPUFrameBuffer.fb
+ */
+ void **py_ref = nullptr;
+#endif
+
+ public:
+ /* Reference of a pointer that needs to be cleaned when deallocating the frame-buffer.
+ * Points to #BPyGPUFrameBuffer::fb */
+ void **ref = nullptr;
+
public:
FrameBuffer(const char *name);
virtual ~FrameBuffer();
diff --git a/source/blender/gpu/intern/gpu_index_buffer.cc b/source/blender/gpu/intern/gpu_index_buffer.cc
index 65932d2dbf4..be7470e79c1 100644
--- a/source/blender/gpu/intern/gpu_index_buffer.cc
+++ b/source/blender/gpu/intern/gpu_index_buffer.cc
@@ -25,12 +25,15 @@
#include "MEM_guardedalloc.h"
+#include "BLI_math_base.h"
#include "BLI_utildefines.h"
#include "gpu_backend.hh"
#include "gpu_index_buffer_private.hh"
+#include <cstring>
+
#define KEEP_SINGLE_COPY 1
#define RESTART_INDEX 0xFFFFFFFF
@@ -50,6 +53,8 @@ void GPU_indexbuf_init_ex(GPUIndexBufBuilder *builder,
builder->max_allowed_index = vertex_len - 1;
builder->max_index_len = index_len;
builder->index_len = 0; // start empty
+ builder->index_min = UINT32_MAX;
+ builder->index_max = 0;
builder->prim_type = prim_type;
builder->data = (uint *)MEM_callocN(builder->max_index_len * sizeof(uint), "GPUIndexBuf data");
}
@@ -66,6 +71,22 @@ void GPU_indexbuf_init(GPUIndexBufBuilder *builder,
GPU_indexbuf_init_ex(builder, prim_type, prim_len * (uint)verts_per_prim, vertex_len);
}
+GPUIndexBuf *GPU_indexbuf_build_on_device(uint index_len)
+{
+ GPUIndexBuf *elem_ = GPU_indexbuf_calloc();
+ IndexBuf *elem = unwrap(elem_);
+ elem->init_build_on_device(index_len);
+ return elem_;
+}
+
+void GPU_indexbuf_join(GPUIndexBufBuilder *builder_to, const GPUIndexBufBuilder *builder_from)
+{
+ BLI_assert(builder_to->data == builder_from->data);
+ builder_to->index_len = max_uu(builder_to->index_len, builder_from->index_len);
+ builder_to->index_min = min_uu(builder_to->index_min, builder_from->index_min);
+ builder_to->index_max = max_uu(builder_to->index_max, builder_from->index_max);
+}
+
void GPU_indexbuf_add_generic_vert(GPUIndexBufBuilder *builder, uint v)
{
#if TRUST_NO_ONE
@@ -74,6 +95,8 @@ void GPU_indexbuf_add_generic_vert(GPUIndexBufBuilder *builder, uint v)
assert(v <= builder->max_allowed_index);
#endif
builder->data[builder->index_len++] = v;
+ builder->index_min = MIN2(builder->index_min, v);
+ builder->index_max = MAX2(builder->index_max, v);
}
void GPU_indexbuf_add_primitive_restart(GPUIndexBufBuilder *builder)
@@ -132,9 +155,9 @@ void GPU_indexbuf_set_point_vert(GPUIndexBufBuilder *builder, uint elem, uint v1
BLI_assert(builder->prim_type == GPU_PRIM_POINTS);
BLI_assert(elem < builder->max_index_len);
builder->data[elem++] = v1;
- if (builder->index_len < elem) {
- builder->index_len = elem;
- }
+ builder->index_min = MIN2(builder->index_min, v1);
+ builder->index_max = MAX2(builder->index_max, v1);
+ builder->index_len = MAX2(builder->index_len, elem);
}
void GPU_indexbuf_set_line_verts(GPUIndexBufBuilder *builder, uint elem, uint v1, uint v2)
@@ -147,9 +170,9 @@ void GPU_indexbuf_set_line_verts(GPUIndexBufBuilder *builder, uint elem, uint v1
uint idx = elem * 2;
builder->data[idx++] = v1;
builder->data[idx++] = v2;
- if (builder->index_len < idx) {
- builder->index_len = idx;
- }
+ builder->index_min = MIN3(builder->index_min, v1, v2);
+ builder->index_max = MAX3(builder->index_max, v1, v2);
+ builder->index_len = MAX2(builder->index_len, idx);
}
void GPU_indexbuf_set_tri_verts(GPUIndexBufBuilder *builder, uint elem, uint v1, uint v2, uint v3)
@@ -164,9 +187,10 @@ void GPU_indexbuf_set_tri_verts(GPUIndexBufBuilder *builder, uint elem, uint v1,
builder->data[idx++] = v1;
builder->data[idx++] = v2;
builder->data[idx++] = v3;
- if (builder->index_len < idx) {
- builder->index_len = idx;
- }
+
+ builder->index_min = MIN4(builder->index_min, v1, v2, v3);
+ builder->index_max = MAX4(builder->index_max, v1, v2, v3);
+ builder->index_len = MAX2(builder->index_len, idx);
}
void GPU_indexbuf_set_point_restart(GPUIndexBufBuilder *builder, uint elem)
@@ -174,9 +198,7 @@ void GPU_indexbuf_set_point_restart(GPUIndexBufBuilder *builder, uint elem)
BLI_assert(builder->prim_type == GPU_PRIM_POINTS);
BLI_assert(elem < builder->max_index_len);
builder->data[elem++] = RESTART_INDEX;
- if (builder->index_len < elem) {
- builder->index_len = elem;
- }
+ builder->index_len = MAX2(builder->index_len, elem);
}
void GPU_indexbuf_set_line_restart(GPUIndexBufBuilder *builder, uint elem)
@@ -186,9 +208,7 @@ void GPU_indexbuf_set_line_restart(GPUIndexBufBuilder *builder, uint elem)
uint idx = elem * 2;
builder->data[idx++] = RESTART_INDEX;
builder->data[idx++] = RESTART_INDEX;
- if (builder->index_len < idx) {
- builder->index_len = idx;
- }
+ builder->index_len = MAX2(builder->index_len, idx);
}
void GPU_indexbuf_set_tri_restart(GPUIndexBufBuilder *builder, uint elem)
@@ -199,9 +219,7 @@ void GPU_indexbuf_set_tri_restart(GPUIndexBufBuilder *builder, uint elem)
builder->data[idx++] = RESTART_INDEX;
builder->data[idx++] = RESTART_INDEX;
builder->data[idx++] = RESTART_INDEX;
- if (builder->index_len < idx) {
- builder->index_len = idx;
- }
+ builder->index_len = MAX2(builder->index_len, idx);
}
/** \} */
@@ -219,7 +237,7 @@ IndexBuf::~IndexBuf()
}
}
-void IndexBuf::init(uint indices_len, uint32_t *indices)
+void IndexBuf::init(uint indices_len, uint32_t *indices, uint min_index, uint max_index)
{
is_init_ = true;
data_ = indices;
@@ -229,8 +247,7 @@ void IndexBuf::init(uint indices_len, uint32_t *indices)
#if GPU_TRACK_INDEX_RANGE
/* Everything remains 32 bit while building to keep things simple.
* Find min/max after, then convert to smallest index type possible. */
- uint min_index, max_index;
- uint range = this->index_range(&min_index, &max_index);
+ uint range = min_index < max_index ? max_index - min_index : 0;
/* count the primitive restart index. */
range += 1;
@@ -241,6 +258,15 @@ void IndexBuf::init(uint indices_len, uint32_t *indices)
#endif
}
+void IndexBuf::init_build_on_device(uint index_len)
+{
+ is_init_ = true;
+ index_start_ = 0;
+ index_len_ = index_len;
+ index_type_ = GPU_INDEX_U32;
+ data_ = nullptr;
+}
+
void IndexBuf::init_subrange(IndexBuf *elem_src, uint start, uint length)
{
/* We don't support nested subranges. */
@@ -307,6 +333,14 @@ void IndexBuf::squeeze_indices_short(uint min_idx, uint max_idx)
}
}
+uint32_t *IndexBuf::unmap(const uint32_t *mapped_memory) const
+{
+ size_t size = size_get();
+ uint32_t *result = static_cast<uint32_t *>(MEM_mallocN(size, __func__));
+ memcpy(result, mapped_memory, size);
+ return result;
+}
+
} // namespace blender::gpu
/** \} */
@@ -339,7 +373,7 @@ void GPU_indexbuf_build_in_place(GPUIndexBufBuilder *builder, GPUIndexBuf *elem)
BLI_assert(builder->data != nullptr);
/* Transfer data ownership to GPUIndexBuf.
* It will be uploaded upon first use. */
- unwrap(elem)->init(builder->index_len, builder->data);
+ unwrap(elem)->init(builder->index_len, builder->data, builder->index_min, builder->index_max);
builder->data = nullptr;
}
@@ -351,6 +385,16 @@ void GPU_indexbuf_create_subrange_in_place(GPUIndexBuf *elem,
unwrap(elem)->init_subrange(unwrap(elem_src), start, length);
}
+const uint32_t *GPU_indexbuf_read(GPUIndexBuf *elem)
+{
+ return unwrap(elem)->read();
+}
+
+uint32_t *GPU_indexbuf_unmap(const GPUIndexBuf *elem, const uint32_t *mapped_buffer)
+{
+ return unwrap(elem)->unmap(mapped_buffer);
+}
+
void GPU_indexbuf_discard(GPUIndexBuf *elem)
{
delete unwrap(elem);
@@ -366,4 +410,9 @@ int GPU_indexbuf_primitive_len(GPUPrimType prim_type)
return indices_per_primitive(prim_type);
}
+void GPU_indexbuf_bind_as_ssbo(GPUIndexBuf *elem, int binding)
+{
+ unwrap(elem)->bind_as_ssbo(binding);
+}
+
/** \} */
diff --git a/source/blender/gpu/intern/gpu_index_buffer_private.hh b/source/blender/gpu/intern/gpu_index_buffer_private.hh
index 2405db8664a..e8a47417604 100644
--- a/source/blender/gpu/intern/gpu_index_buffer_private.hh
+++ b/source/blender/gpu/intern/gpu_index_buffer_private.hh
@@ -73,15 +73,16 @@ class IndexBuf {
IndexBuf(){};
virtual ~IndexBuf();
- void init(uint indices_len, uint32_t *indices);
+ void init(uint indices_len, uint32_t *indices, uint min_index, uint max_index);
void init_subrange(IndexBuf *elem_src, uint start, uint length);
+ void init_build_on_device(uint index_len);
uint32_t index_len_get(void) const
{
return index_len_;
}
/* Return size in byte of the drawable data buffer range. Actual buffer size might be bigger. */
- size_t size_get(void)
+ size_t size_get(void) const
{
return index_len_ * to_bytesize(index_type_);
};
@@ -91,6 +92,11 @@ class IndexBuf {
return is_init_;
};
+ virtual void bind_as_ssbo(uint binding) = 0;
+
+ virtual const uint32_t *read() const = 0;
+ uint32_t *unmap(const uint32_t *mapped_memory) const;
+
private:
inline void squeeze_indices_short(uint min_idx, uint max_idx);
inline uint index_range(uint *r_min, uint *r_max);
@@ -105,6 +111,10 @@ static inline IndexBuf *unwrap(GPUIndexBuf *indexbuf)
{
return reinterpret_cast<IndexBuf *>(indexbuf);
}
+static inline const IndexBuf *unwrap(const GPUIndexBuf *indexbuf)
+{
+ return reinterpret_cast<const IndexBuf *>(indexbuf);
+}
static inline int indices_per_primitive(GPUPrimType prim_type)
{
diff --git a/source/blender/gpu/intern/gpu_material_library.c b/source/blender/gpu/intern/gpu_material_library.c
index 175facc0a8d..3c216c1a991 100644
--- a/source/blender/gpu/intern/gpu_material_library.c
+++ b/source/blender/gpu/intern/gpu_material_library.c
@@ -47,6 +47,7 @@ extern char datatoc_gpu_shader_material_anisotropic_glsl[];
extern char datatoc_gpu_shader_material_attribute_glsl[];
extern char datatoc_gpu_shader_material_background_glsl[];
extern char datatoc_gpu_shader_material_bevel_glsl[];
+extern char datatoc_gpu_shader_material_wavelength_glsl[];
extern char datatoc_gpu_shader_material_blackbody_glsl[];
extern char datatoc_gpu_shader_material_bright_contrast_glsl[];
extern char datatoc_gpu_shader_material_bump_glsl[];
@@ -191,6 +192,11 @@ static GPUMaterialLibrary gpu_shader_material_bevel_library = {
.dependencies = {NULL},
};
+static GPUMaterialLibrary gpu_shader_material_wavelength_library = {
+ .code = datatoc_gpu_shader_material_wavelength_glsl,
+ .dependencies = {NULL},
+};
+
static GPUMaterialLibrary gpu_shader_material_blackbody_library = {
.code = datatoc_gpu_shader_material_blackbody_glsl,
.dependencies = {NULL},
@@ -593,6 +599,7 @@ static GPUMaterialLibrary *gpu_material_libraries[] = {
&gpu_shader_material_attribute_library,
&gpu_shader_material_background_library,
&gpu_shader_material_bevel_library,
+ &gpu_shader_material_wavelength_library,
&gpu_shader_material_blackbody_library,
&gpu_shader_material_bright_contrast_library,
&gpu_shader_material_bump_library,
diff --git a/source/blender/gpu/intern/gpu_matrix.cc b/source/blender/gpu/intern/gpu_matrix.cc
index 2ae50d913da..6eb9cb823d5 100644
--- a/source/blender/gpu/intern/gpu_matrix.cc
+++ b/source/blender/gpu/intern/gpu_matrix.cc
@@ -474,11 +474,11 @@ void GPU_matrix_look_at(float eyeX,
GPU_matrix_translate_3f(-eyeX, -eyeY, -eyeZ);
}
-void GPU_matrix_project(const float world[3],
- const float model[4][4],
- const float proj[4][4],
- const int view[4],
- float win[3])
+void GPU_matrix_project_3fv(const float world[3],
+ const float model[4][4],
+ const float proj[4][4],
+ const int view[4],
+ float win[3])
{
float v[4];
@@ -494,6 +494,25 @@ void GPU_matrix_project(const float world[3],
win[2] = (v[2] + 1) * 0.5f;
}
+void GPU_matrix_project_2fv(const float world[3],
+ const float model[4][4],
+ const float proj[4][4],
+ const int view[4],
+ float win[2])
+{
+ float v[4];
+
+ mul_v4_m4v3(v, model, world);
+ mul_m4_v4(proj, v);
+
+ if (v[3] != 0.0f) {
+ mul_v2_fl(v, 1.0f / v[3]);
+ }
+
+ win[0] = view[0] + (view[2] * (v[0] + 1)) * 0.5f;
+ win[1] = view[1] + (view[3] * (v[1] + 1)) * 0.5f;
+}
+
/**
* The same result could be obtained as follows:
*
@@ -543,7 +562,7 @@ bool GPU_matrix_unproject_precalc(struct GPUMatrixUnproject_Precalc *precalc,
&precalc->dims.zmax);
if (isinf(precalc->dims.zmax)) {
/* We cannot retrieve the actual value of the clip_end.
- * Use `FLT_MAX` to avoid nans. */
+ * Use `FLT_MAX` to avoid NAN's. */
precalc->dims.zmax = FLT_MAX;
}
for (int i = 0; i < 4; i++) {
@@ -556,9 +575,9 @@ bool GPU_matrix_unproject_precalc(struct GPUMatrixUnproject_Precalc *precalc,
return true;
}
-void GPU_matrix_unproject_with_precalc(const struct GPUMatrixUnproject_Precalc *precalc,
- const float win[3],
- float r_world[3])
+void GPU_matrix_unproject_3fv_with_precalc(const struct GPUMatrixUnproject_Precalc *precalc,
+ const float win[3],
+ float r_world[3])
{
float in[3] = {
(win[0] - precalc->view[0]) / precalc->view[2],
@@ -569,18 +588,18 @@ void GPU_matrix_unproject_with_precalc(const struct GPUMatrixUnproject_Precalc *
mul_v3_m4v3(r_world, precalc->model_inverted, in);
}
-bool GPU_matrix_unproject(const float win[3],
- const float model[4][4],
- const float proj[4][4],
- const int view[4],
- float r_world[3])
+bool GPU_matrix_unproject_3fv(const float win[3],
+ const float model[4][4],
+ const float proj[4][4],
+ const int view[4],
+ float r_world[3])
{
struct GPUMatrixUnproject_Precalc precalc;
if (!GPU_matrix_unproject_precalc(&precalc, model, proj, view)) {
zero_v3(r_world);
return false;
}
- GPU_matrix_unproject_with_precalc(&precalc, win, r_world);
+ GPU_matrix_unproject_3fv_with_precalc(&precalc, win, r_world);
return true;
}
diff --git a/source/blender/gpu/intern/gpu_platform.cc b/source/blender/gpu/intern/gpu_platform.cc
index 6b9878f2ba4..49dde473300 100644
--- a/source/blender/gpu/intern/gpu_platform.cc
+++ b/source/blender/gpu/intern/gpu_platform.cc
@@ -41,10 +41,10 @@ namespace blender::gpu {
GPUPlatformGlobal GPG;
-void GPUPlatformGlobal::create_key(eGPUSupportLevel support_level,
- const char *vendor,
- const char *renderer,
- const char *version)
+static char *create_key(eGPUSupportLevel support_level,
+ const char *vendor,
+ const char *renderer,
+ const char *version)
{
DynStr *ds = BLI_dynstr_new();
BLI_dynstr_appendf(ds, "{%s/%s/%s}=", vendor, renderer, version);
@@ -58,29 +58,56 @@ void GPUPlatformGlobal::create_key(eGPUSupportLevel support_level,
BLI_dynstr_append(ds, "UNSUPPORTED");
}
- support_key = BLI_dynstr_get_cstring(ds);
+ char *support_key = BLI_dynstr_get_cstring(ds);
BLI_dynstr_free(ds);
BLI_str_replace_char(support_key, '\n', ' ');
BLI_str_replace_char(support_key, '\r', ' ');
+ return support_key;
}
-void GPUPlatformGlobal::create_gpu_name(const char *vendor,
- const char *renderer,
- const char *version)
+static char *create_gpu_name(const char *vendor, const char *renderer, const char *version)
{
DynStr *ds = BLI_dynstr_new();
BLI_dynstr_appendf(ds, "%s %s %s", vendor, renderer, version);
- gpu_name = BLI_dynstr_get_cstring(ds);
+ char *gpu_name = BLI_dynstr_get_cstring(ds);
BLI_dynstr_free(ds);
BLI_str_replace_char(gpu_name, '\n', ' ');
BLI_str_replace_char(gpu_name, '\r', ' ');
+ return gpu_name;
+}
+
+void GPUPlatformGlobal::init(eGPUDeviceType gpu_device,
+ eGPUOSType os_type,
+ eGPUDriverType driver_type,
+ eGPUSupportLevel gpu_support_level,
+ const char *vendor_str,
+ const char *renderer_str,
+ const char *version_str)
+{
+ this->clear();
+
+ this->initialized = true;
+
+ this->device = gpu_device;
+ this->os = os_type;
+ this->driver = driver_type;
+ this->support_level = gpu_support_level;
+
+ this->vendor = BLI_strdup(vendor_str);
+ this->renderer = BLI_strdup(renderer_str);
+ this->version = BLI_strdup(version_str);
+ this->support_key = create_key(gpu_support_level, vendor_str, renderer_str, version_str);
+ this->gpu_name = create_gpu_name(vendor_str, renderer_str, version_str);
}
void GPUPlatformGlobal::clear()
{
- MEM_SAFE_FREE(GPG.support_key);
- MEM_SAFE_FREE(GPG.gpu_name);
+ MEM_SAFE_FREE(vendor);
+ MEM_SAFE_FREE(renderer);
+ MEM_SAFE_FREE(version);
+ MEM_SAFE_FREE(support_key);
+ MEM_SAFE_FREE(gpu_name);
initialized = false;
}
@@ -96,22 +123,44 @@ using namespace blender::gpu;
eGPUSupportLevel GPU_platform_support_level()
{
+ BLI_assert(GPG.initialized);
return GPG.support_level;
}
-const char *GPU_platform_support_level_key()
+const char *GPU_platform_vendor(void)
+{
+ BLI_assert(GPG.initialized);
+ return GPG.vendor;
+}
+
+const char *GPU_platform_renderer(void)
+{
+ BLI_assert(GPG.initialized);
+ return GPG.renderer;
+}
+
+const char *GPU_platform_version(void)
+{
+ BLI_assert(GPG.initialized);
+ return GPG.version;
+}
+
+const char *GPU_platform_support_level_key(void)
{
+ BLI_assert(GPG.initialized);
return GPG.support_key;
}
const char *GPU_platform_gpu_name(void)
{
+ BLI_assert(GPG.initialized);
return GPG.gpu_name;
}
/* GPU Types */
bool GPU_type_matches(eGPUDeviceType device, eGPUOSType os, eGPUDriverType driver)
{
+ BLI_assert(GPG.initialized);
return (GPG.device & device) && (GPG.os & os) && (GPG.driver & driver);
}
diff --git a/source/blender/gpu/intern/gpu_platform_private.hh b/source/blender/gpu/intern/gpu_platform_private.hh
index 02d99efa4a9..f823269ab54 100644
--- a/source/blender/gpu/intern/gpu_platform_private.hh
+++ b/source/blender/gpu/intern/gpu_platform_private.hh
@@ -34,16 +34,20 @@ class GPUPlatformGlobal {
eGPUOSType os;
eGPUDriverType driver;
eGPUSupportLevel support_level;
+ char *vendor = nullptr;
+ char *renderer = nullptr;
+ char *version = nullptr;
char *support_key = nullptr;
char *gpu_name = nullptr;
public:
- void create_key(eGPUSupportLevel support_level,
- const char *vendor,
- const char *renderer,
- const char *version);
-
- void create_gpu_name(const char *vendor, const char *renderer, const char *version);
+ void init(eGPUDeviceType gpu_device,
+ eGPUOSType os_type,
+ eGPUDriverType driver_type,
+ eGPUSupportLevel gpu_support_level,
+ const char *vendor_str,
+ const char *renderer_str,
+ const char *version_str);
void clear(void);
};
diff --git a/source/blender/gpu/intern/gpu_shader.cc b/source/blender/gpu/intern/gpu_shader.cc
index bb657ff1645..e2eb8953292 100644
--- a/source/blender/gpu/intern/gpu_shader.cc
+++ b/source/blender/gpu/intern/gpu_shader.cc
@@ -57,6 +57,8 @@ static CLG_LogRef LOG = {"gpu.shader"};
using namespace blender;
using namespace blender::gpu;
+static bool gpu_shader_srgb_uniform_dirty_get();
+
/* -------------------------------------------------------------------- */
/** \name Debug functions
* \{ */
@@ -157,7 +159,7 @@ void Shader::print_log(Span<const char *> sources, char *log, const char *stage,
}
/* Print line from the source file that is producing the error. */
if ((error_line != -1) && (error_line != last_error_line || error_char != last_error_char)) {
- const char *src_line_end = src_line;
+ const char *src_line_end;
found_line_id = false;
/* error_line is 1 based in this case. */
int src_line_index = 1;
@@ -288,6 +290,7 @@ static void standard_defines(Vector<const char *> &sources)
GPUShader *GPU_shader_create_ex(const char *vertcode,
const char *fragcode,
const char *geomcode,
+ const char *computecode,
const char *libcode,
const char *defines,
const eGPUShaderTFBType tf_type,
@@ -295,8 +298,10 @@ GPUShader *GPU_shader_create_ex(const char *vertcode,
const int tf_count,
const char *shname)
{
- /* At least a vertex shader and a fragment shader are required. */
- BLI_assert((fragcode != nullptr) && (vertcode != nullptr));
+ /* At least a vertex shader and a fragment shader are required, or only a compute shader. */
+ BLI_assert(((fragcode != nullptr) && (vertcode != nullptr) && (computecode == nullptr)) ||
+ ((fragcode == nullptr) && (vertcode == nullptr) && (geomcode == nullptr) &&
+ (computecode != nullptr)));
Shader *shader = GPUBackend::get()->shader_alloc(shname);
@@ -347,6 +352,21 @@ GPUShader *GPU_shader_create_ex(const char *vertcode,
shader->geometry_shader_from_glsl(sources);
}
+ if (computecode) {
+ Vector<const char *> sources;
+ standard_defines(sources);
+ sources.append("#define GPU_COMPUTE_SHADER\n");
+ if (defines) {
+ sources.append(defines);
+ }
+ if (libcode) {
+ sources.append(libcode);
+ }
+ sources.append(computecode);
+
+ shader->compute_shader_from_glsl(sources);
+ }
+
if (tf_names != nullptr && tf_count > 0) {
BLI_assert(tf_type != GPU_SHADER_TFB_NONE);
shader->transform_feedback_names_set(Span<const char *>(tf_names, tf_count), tf_type);
@@ -378,8 +398,33 @@ GPUShader *GPU_shader_create(const char *vertcode,
const char *defines,
const char *shname)
{
- return GPU_shader_create_ex(
- vertcode, fragcode, geomcode, libcode, defines, GPU_SHADER_TFB_NONE, nullptr, 0, shname);
+ return GPU_shader_create_ex(vertcode,
+ fragcode,
+ geomcode,
+ nullptr,
+ libcode,
+ defines,
+ GPU_SHADER_TFB_NONE,
+ nullptr,
+ 0,
+ shname);
+}
+
+GPUShader *GPU_shader_create_compute(const char *computecode,
+ const char *libcode,
+ const char *defines,
+ const char *shname)
+{
+ return GPU_shader_create_ex(nullptr,
+ nullptr,
+ nullptr,
+ computecode,
+ libcode,
+ defines,
+ GPU_SHADER_TFB_NONE,
+ nullptr,
+ 0,
+ shname);
}
GPUShader *GPU_shader_create_from_python(const char *vertcode,
@@ -400,6 +445,7 @@ GPUShader *GPU_shader_create_from_python(const char *vertcode,
GPUShader *sh = GPU_shader_create_ex(vertcode,
fragcode,
geomcode,
+ nullptr,
libcode,
defines,
GPU_SHADER_TFB_NONE,
@@ -501,9 +547,13 @@ void GPU_shader_bind(GPUShader *gpu_shader)
GPU_matrix_bind(gpu_shader);
GPU_shader_set_srgb_uniform(gpu_shader);
}
-
- if (GPU_matrix_dirty_get()) {
- GPU_matrix_bind(gpu_shader);
+ else {
+ if (gpu_shader_srgb_uniform_dirty_get()) {
+ GPU_shader_set_srgb_uniform(gpu_shader);
+ }
+ if (GPU_matrix_dirty_get()) {
+ GPU_matrix_bind(gpu_shader);
+ }
}
}
@@ -561,6 +611,13 @@ int GPU_shader_get_builtin_block(GPUShader *shader, int builtin)
return interface->ubo_builtin((GPUUniformBlockBuiltin)builtin);
}
+int GPU_shader_get_ssbo(GPUShader *shader, const char *name)
+{
+ ShaderInterface *interface = unwrap(shader)->interface;
+ const ShaderInput *ssbo = interface->ssbo_get(name);
+ return ssbo ? ssbo->location : -1;
+}
+
/* DEPRECATED. */
int GPU_shader_get_uniform_block(GPUShader *shader, const char *name)
{
@@ -715,6 +772,12 @@ void GPU_shader_uniform_4fv_array(GPUShader *sh, const char *name, int len, cons
* \{ */
static int g_shader_builtin_srgb_transform = 0;
+static bool g_shader_builtin_srgb_is_dirty = false;
+
+static bool gpu_shader_srgb_uniform_dirty_get()
+{
+ return g_shader_builtin_srgb_is_dirty;
+}
void GPU_shader_set_srgb_uniform(GPUShader *shader)
{
@@ -722,11 +785,15 @@ void GPU_shader_set_srgb_uniform(GPUShader *shader)
if (loc != -1) {
GPU_shader_uniform_vector_int(shader, loc, 1, 1, &g_shader_builtin_srgb_transform);
}
+ g_shader_builtin_srgb_is_dirty = false;
}
void GPU_shader_set_framebuffer_srgb_target(int use_srgb_to_linear)
{
- g_shader_builtin_srgb_transform = use_srgb_to_linear;
+ if (g_shader_builtin_srgb_transform != use_srgb_to_linear) {
+ g_shader_builtin_srgb_transform = use_srgb_to_linear;
+ g_shader_builtin_srgb_is_dirty = true;
+ }
}
/** \} */
diff --git a/source/blender/gpu/intern/gpu_shader_interface.cc b/source/blender/gpu/intern/gpu_shader_interface.cc
index 81c1e013877..ae94112b17b 100644
--- a/source/blender/gpu/intern/gpu_shader_interface.cc
+++ b/source/blender/gpu/intern/gpu_shader_interface.cc
@@ -32,10 +32,8 @@
namespace blender::gpu {
-ShaderInterface::ShaderInterface()
-{
- /* TODO(fclem): add unique ID for debugging. */
-}
+/* TODO(fclem): add unique ID for debugging. */
+ShaderInterface::ShaderInterface() = default;
ShaderInterface::~ShaderInterface()
{
@@ -82,6 +80,8 @@ void ShaderInterface::debug_print()
Span<ShaderInput> attrs = Span<ShaderInput>(inputs_, attr_len_);
Span<ShaderInput> ubos = Span<ShaderInput>(inputs_ + attr_len_, ubo_len_);
Span<ShaderInput> uniforms = Span<ShaderInput>(inputs_ + attr_len_ + ubo_len_, uniform_len_);
+ Span<ShaderInput> ssbos = Span<ShaderInput>(inputs_ + attr_len_ + ubo_len_ + uniform_len_,
+ ssbo_len_);
char *name_buf = name_buffer_;
const char format[] = " | %.8x : %4d : %s\n";
@@ -119,6 +119,13 @@ void ShaderInterface::debug_print()
}
}
+ if (ssbos.size() > 0) {
+ printf("\n Shader Storage Objects :\n");
+ }
+ for (const ShaderInput &ssbo : ssbos) {
+ printf(format, ssbo.name_hash, ssbo.binding, name_buf + ssbo.name_offset);
+ }
+
printf("\n");
}
diff --git a/source/blender/gpu/intern/gpu_shader_interface.hh b/source/blender/gpu/intern/gpu_shader_interface.hh
index aec58544111..ebed7b15170 100644
--- a/source/blender/gpu/intern/gpu_shader_interface.hh
+++ b/source/blender/gpu/intern/gpu_shader_interface.hh
@@ -60,6 +60,7 @@ class ShaderInterface {
uint attr_len_ = 0;
uint ubo_len_ = 0;
uint uniform_len_ = 0;
+ uint ssbo_len_ = 0;
/** Enabled bind-points that needs to be fed with data. */
uint16_t enabled_attr_mask_ = 0;
uint16_t enabled_ubo_mask_ = 0;
@@ -99,6 +100,11 @@ class ShaderInterface {
return input_lookup(inputs_ + attr_len_ + ubo_len_, uniform_len_, binding);
}
+ inline const ShaderInput *ssbo_get(const char *name) const
+ {
+ return input_lookup(inputs_ + attr_len_ + ubo_len_ + uniform_len_, ssbo_len_, name);
+ }
+
inline const char *input_name_get(const ShaderInput *input) const
{
return name_buffer_ + input->name_offset;
diff --git a/source/blender/gpu/intern/gpu_shader_private.hh b/source/blender/gpu/intern/gpu_shader_private.hh
index d9327bbc0f4..281f01dbc22 100644
--- a/source/blender/gpu/intern/gpu_shader_private.hh
+++ b/source/blender/gpu/intern/gpu_shader_private.hh
@@ -49,6 +49,7 @@ class Shader {
virtual void vertex_shader_from_glsl(MutableSpan<const char *> sources) = 0;
virtual void geometry_shader_from_glsl(MutableSpan<const char *> sources) = 0;
virtual void fragment_shader_from_glsl(MutableSpan<const char *> sources) = 0;
+ virtual void compute_shader_from_glsl(MutableSpan<const char *> sources) = 0;
virtual bool finalize(void) = 0;
virtual void transform_feedback_names_set(Span<const char *> name_list,
diff --git a/source/blender/gpu/intern/gpu_state.cc b/source/blender/gpu/intern/gpu_state.cc
index 782d25747bb..5c33066c720 100644
--- a/source/blender/gpu/intern/gpu_state.cc
+++ b/source/blender/gpu/intern/gpu_state.cc
@@ -248,7 +248,7 @@ eGPUWriteMask GPU_write_mask_get()
uint GPU_stencil_mask_get()
{
- GPUStateMutable &state = Context::get()->state_manager->mutable_state;
+ const GPUStateMutable &state = Context::get()->state_manager->mutable_state;
return state.stencil_write_mask;
}
@@ -267,7 +267,7 @@ eGPUStencilTest GPU_stencil_test_get()
/* NOTE: Already premultiplied by U.pixelsize. */
float GPU_line_width_get()
{
- GPUStateMutable &state = Context::get()->state_manager->mutable_state;
+ const GPUStateMutable &state = Context::get()->state_manager->mutable_state;
return state.line_width;
}
@@ -292,7 +292,7 @@ void GPU_viewport_size_get_i(int coords[4])
bool GPU_depth_mask_get()
{
- GPUState &state = Context::get()->state_manager->state;
+ const GPUState &state = Context::get()->state_manager->state;
return (state.write_mask & GPU_WRITE_DEPTH) != 0;
}
diff --git a/source/blender/gpu/intern/gpu_texture.cc b/source/blender/gpu/intern/gpu_texture.cc
index c3e9058c6c7..997064e82a2 100644
--- a/source/blender/gpu/intern/gpu_texture.cc
+++ b/source/blender/gpu/intern/gpu_texture.cc
@@ -60,6 +60,12 @@ Texture::~Texture()
fb_[i]->attachment_remove(fb_attachment_[i]);
}
}
+
+#ifndef GPU_NO_USE_PY_REFERENCES
+ if (this->py_ref) {
+ *this->py_ref = nullptr;
+ }
+#endif
}
bool Texture::init_1D(int w, int layers, eGPUTextureFormat format)
@@ -581,6 +587,19 @@ bool GPU_texture_array(const GPUTexture *tex)
return (reinterpret_cast<const Texture *>(tex)->type_get() & GPU_TEXTURE_ARRAY) != 0;
}
+#ifndef GPU_NO_USE_PY_REFERENCES
+void **GPU_texture_py_reference_get(GPUTexture *tex)
+{
+ return unwrap(tex)->py_ref;
+}
+
+void GPU_texture_py_reference_set(GPUTexture *tex, void **py_ref)
+{
+ BLI_assert(py_ref == nullptr || unwrap(tex)->py_ref == nullptr);
+ unwrap(tex)->py_ref = py_ref;
+}
+#endif
+
/* TODO remove */
int GPU_texture_opengl_bindcode(const GPUTexture *tex)
{
diff --git a/source/blender/gpu/intern/gpu_texture_private.hh b/source/blender/gpu/intern/gpu_texture_private.hh
index 3d808bce152..a8f2e482bdd 100644
--- a/source/blender/gpu/intern/gpu_texture_private.hh
+++ b/source/blender/gpu/intern/gpu_texture_private.hh
@@ -80,6 +80,13 @@ class Texture {
int refcount = 1;
/** Width & Height (of source data), optional. */
int src_w = 0, src_h = 0;
+#ifndef GPU_NO_USE_PY_REFERENCES
+ /**
+ * Reference of a pointer that needs to be cleaned when deallocating the texture.
+ * Points to #BPyGPUTexture.tex
+ */
+ void **py_ref = nullptr;
+#endif
protected:
/* ---- Texture format (immutable after init). ---- */
diff --git a/source/blender/gpu/intern/gpu_vertex_buffer.cc b/source/blender/gpu/intern/gpu_vertex_buffer.cc
index 09b9eba9f95..3ecbb740a0c 100644
--- a/source/blender/gpu/intern/gpu_vertex_buffer.cc
+++ b/source/blender/gpu/intern/gpu_vertex_buffer.cc
@@ -149,6 +149,16 @@ GPUVertBuf *GPU_vertbuf_duplicate(GPUVertBuf *verts_)
return wrap(unwrap(verts_)->duplicate());
}
+const void *GPU_vertbuf_read(GPUVertBuf *verts)
+{
+ return unwrap(verts)->read();
+}
+
+void *GPU_vertbuf_unmap(const GPUVertBuf *verts, const void *mapped_data)
+{
+ return unwrap(verts)->unmap(mapped_data);
+}
+
/** Same as discard but does not free. */
void GPU_vertbuf_clear(GPUVertBuf *verts)
{
@@ -324,6 +334,11 @@ void GPU_vertbuf_use(GPUVertBuf *verts)
unwrap(verts)->upload();
}
+void GPU_vertbuf_bind_as_ssbo(struct GPUVertBuf *verts, int binding)
+{
+ unwrap(verts)->bind_as_ssbo(binding);
+}
+
/* XXX this is just a wrapper for the use of the Hair refine workaround.
* To be used with GPU_vertbuf_use(). */
void GPU_vertbuf_update_sub(GPUVertBuf *verts, uint start, uint len, void *data)
diff --git a/source/blender/gpu/intern/gpu_vertex_buffer_private.hh b/source/blender/gpu/intern/gpu_vertex_buffer_private.hh
index 67a09f6f83c..9531c2c1a5f 100644
--- a/source/blender/gpu/intern/gpu_vertex_buffer_private.hh
+++ b/source/blender/gpu/intern/gpu_vertex_buffer_private.hh
@@ -66,6 +66,7 @@ class VertBuf {
void allocate(uint vert_len);
void resize(uint vert_len);
void upload(void);
+ virtual void bind_as_ssbo(uint binding) = 0;
VertBuf *duplicate(void);
@@ -96,6 +97,8 @@ class VertBuf {
}
virtual void update_sub(uint start, uint len, void *data) = 0;
+ virtual const void *read() const = 0;
+ virtual void *unmap(const void *mapped_data) const = 0;
protected:
virtual void acquire_data(void) = 0;
diff --git a/source/blender/gpu/opengl/gl_backend.cc b/source/blender/gpu/opengl/gl_backend.cc
index ef7788194a1..d85f9f7684d 100644
--- a/source/blender/gpu/opengl/gl_backend.cc
+++ b/source/blender/gpu/opengl/gl_backend.cc
@@ -41,39 +41,42 @@ namespace blender::gpu {
void GLBackend::platform_init()
{
BLI_assert(!GPG.initialized);
- GPG.initialized = true;
+
+ const char *vendor = (const char *)glGetString(GL_VENDOR);
+ const char *renderer = (const char *)glGetString(GL_RENDERER);
+ const char *version = (const char *)glGetString(GL_VERSION);
+ eGPUDeviceType device = GPU_DEVICE_ANY;
+ eGPUOSType os = GPU_OS_ANY;
+ eGPUDriverType driver = GPU_DRIVER_ANY;
+ eGPUSupportLevel support_level = GPU_SUPPORT_LEVEL_SUPPORTED;
#ifdef _WIN32
- GPG.os = GPU_OS_WIN;
+ os = GPU_OS_WIN;
#elif defined(__APPLE__)
- GPG.os = GPU_OS_MAC;
+ os = GPU_OS_MAC;
#else
- GPG.os = GPU_OS_UNIX;
+ os = GPU_OS_UNIX;
#endif
- const char *vendor = (const char *)glGetString(GL_VENDOR);
- const char *renderer = (const char *)glGetString(GL_RENDERER);
- const char *version = (const char *)glGetString(GL_VERSION);
-
if (strstr(vendor, "ATI") || strstr(vendor, "AMD")) {
- GPG.device = GPU_DEVICE_ATI;
- GPG.driver = GPU_DRIVER_OFFICIAL;
+ device = GPU_DEVICE_ATI;
+ driver = GPU_DRIVER_OFFICIAL;
}
else if (strstr(vendor, "NVIDIA")) {
- GPG.device = GPU_DEVICE_NVIDIA;
- GPG.driver = GPU_DRIVER_OFFICIAL;
+ device = GPU_DEVICE_NVIDIA;
+ driver = GPU_DRIVER_OFFICIAL;
}
else if (strstr(vendor, "Intel") ||
/* src/mesa/drivers/dri/intel/intel_context.c */
strstr(renderer, "Mesa DRI Intel") || strstr(renderer, "Mesa DRI Mobile Intel")) {
- GPG.device = GPU_DEVICE_INTEL;
- GPG.driver = GPU_DRIVER_OFFICIAL;
+ device = GPU_DEVICE_INTEL;
+ driver = GPU_DRIVER_OFFICIAL;
if (strstr(renderer, "UHD Graphics") ||
/* Not UHD but affected by the same bugs. */
strstr(renderer, "HD Graphics 530") || strstr(renderer, "Kaby Lake GT2") ||
strstr(renderer, "Whiskey Lake")) {
- GPG.device |= GPU_DEVICE_INTEL_UHD;
+ device |= GPU_DEVICE_INTEL_UHD;
}
}
else if ((strstr(renderer, "Mesa DRI R")) ||
@@ -81,49 +84,47 @@ void GLBackend::platform_init()
(strstr(renderer, "AMD") && strstr(vendor, "X.Org")) ||
(strstr(renderer, "Gallium ") && strstr(renderer, " on ATI ")) ||
(strstr(renderer, "Gallium ") && strstr(renderer, " on AMD "))) {
- GPG.device = GPU_DEVICE_ATI;
- GPG.driver = GPU_DRIVER_OPENSOURCE;
+ device = GPU_DEVICE_ATI;
+ driver = GPU_DRIVER_OPENSOURCE;
}
else if (strstr(renderer, "Nouveau") || strstr(vendor, "nouveau")) {
- GPG.device = GPU_DEVICE_NVIDIA;
- GPG.driver = GPU_DRIVER_OPENSOURCE;
+ device = GPU_DEVICE_NVIDIA;
+ driver = GPU_DRIVER_OPENSOURCE;
}
else if (strstr(vendor, "Mesa")) {
- GPG.device = GPU_DEVICE_SOFTWARE;
- GPG.driver = GPU_DRIVER_SOFTWARE;
+ device = GPU_DEVICE_SOFTWARE;
+ driver = GPU_DRIVER_SOFTWARE;
}
else if (strstr(vendor, "Microsoft")) {
- GPG.device = GPU_DEVICE_SOFTWARE;
- GPG.driver = GPU_DRIVER_SOFTWARE;
+ device = GPU_DEVICE_SOFTWARE;
+ driver = GPU_DRIVER_SOFTWARE;
}
else if (strstr(vendor, "Apple")) {
/* Apple Silicon. */
- GPG.device = GPU_DEVICE_APPLE;
- GPG.driver = GPU_DRIVER_OFFICIAL;
+ device = GPU_DEVICE_APPLE;
+ driver = GPU_DRIVER_OFFICIAL;
}
else if (strstr(renderer, "Apple Software Renderer")) {
- GPG.device = GPU_DEVICE_SOFTWARE;
- GPG.driver = GPU_DRIVER_SOFTWARE;
+ device = GPU_DEVICE_SOFTWARE;
+ driver = GPU_DRIVER_SOFTWARE;
}
else if (strstr(renderer, "llvmpipe") || strstr(renderer, "softpipe")) {
- GPG.device = GPU_DEVICE_SOFTWARE;
- GPG.driver = GPU_DRIVER_SOFTWARE;
+ device = GPU_DEVICE_SOFTWARE;
+ driver = GPU_DRIVER_SOFTWARE;
}
else {
printf("Warning: Could not find a matching GPU name. Things may not behave as expected.\n");
printf("Detected OpenGL configuration:\n");
printf("Vendor: %s\n", vendor);
printf("Renderer: %s\n", renderer);
- GPG.device = GPU_DEVICE_ANY;
- GPG.driver = GPU_DRIVER_ANY;
}
/* Detect support level */
if (!GLEW_VERSION_3_3) {
- GPG.support_level = GPU_SUPPORT_LEVEL_UNSUPPORTED;
+ support_level = GPU_SUPPORT_LEVEL_UNSUPPORTED;
}
else {
- if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_WIN, GPU_DRIVER_ANY)) {
+ if ((device & GPU_DEVICE_INTEL) && (os & GPU_OS_WIN)) {
/* Old Intel drivers with known bugs that cause material properties to crash.
* Version Build 10.18.14.5067 is the latest available and appears to be working
* ok with our workarounds, so excluded from this list. */
@@ -132,19 +133,19 @@ void GLBackend::platform_init()
strstr(version, "Build 9.18") || strstr(version, "Build 10.18.10.3") ||
strstr(version, "Build 10.18.10.4") || strstr(version, "Build 10.18.10.5") ||
strstr(version, "Build 10.18.14.4")) {
- GPG.support_level = GPU_SUPPORT_LEVEL_LIMITED;
+ support_level = GPU_SUPPORT_LEVEL_LIMITED;
}
}
- if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_UNIX, GPU_DRIVER_ANY)) {
+ if ((device & GPU_DEVICE_ATI) && (os & GPU_OS_UNIX)) {
/* Platform seems to work when SB backend is disabled. This can be done
* by adding the environment variable `R600_DEBUG=nosb`. */
if (strstr(renderer, "AMD CEDAR")) {
- GPG.support_level = GPU_SUPPORT_LEVEL_LIMITED;
+ support_level = GPU_SUPPORT_LEVEL_LIMITED;
}
}
}
- GPG.create_key(GPG.support_level, vendor, renderer, version);
- GPG.create_gpu_name(vendor, renderer, version);
+
+ GPG.init(device, os, driver, support_level, vendor, renderer, version);
}
void GLBackend::platform_exit()
@@ -204,6 +205,11 @@ static bool detect_mip_render_workaround()
return enable_workaround;
}
+static const char *gl_extension_get(int i)
+{
+ return (char *)glGetStringi(GL_EXTENSIONS, i);
+}
+
static void detect_workarounds()
{
const char *vendor = (const char *)glGetString(GL_VENDOR);
@@ -286,8 +292,9 @@ static void detect_workarounds()
strstr(renderer, " RX 480 ") || strstr(renderer, " RX 490 ") ||
strstr(renderer, " RX 560 ") || strstr(renderer, " RX 560X ") ||
strstr(renderer, " RX 570 ") || strstr(renderer, " RX 580 ") ||
- strstr(renderer, " RX 590 ") || strstr(renderer, " RX550/550 ") ||
- strstr(renderer, " (TM) 520 ") || strstr(renderer, " (TM) 530 ") ||
+ strstr(renderer, " RX 580X ") || strstr(renderer, " RX 590 ") ||
+ strstr(renderer, " RX550/550 ") || strstr(renderer, "(TM) 520 ") ||
+ strstr(renderer, "(TM) 530 ") || strstr(renderer, "(TM) 535 ") ||
strstr(renderer, " R5 ") || strstr(renderer, " R7 ") || strstr(renderer, " R9 ")) {
GCaps.use_hq_normals_workaround = true;
}
@@ -418,8 +425,28 @@ void GLBackend::capabilities_init()
glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &GCaps.max_textures_vert);
glGetIntegerv(GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS, &GCaps.max_textures_geom);
glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &GCaps.max_textures);
+ glGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, &GCaps.max_uniforms_vert);
+ glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &GCaps.max_uniforms_frag);
+ glGetIntegerv(GL_MAX_ELEMENTS_INDICES, &GCaps.max_batch_indices);
+ glGetIntegerv(GL_MAX_ELEMENTS_VERTICES, &GCaps.max_batch_vertices);
+ glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &GCaps.max_vertex_attribs);
+ glGetIntegerv(GL_MAX_VARYING_FLOATS, &GCaps.max_varying_floats);
+
+ glGetIntegerv(GL_NUM_EXTENSIONS, &GCaps.extensions_len);
+ GCaps.extension_get = gl_extension_get;
+
GCaps.mem_stats_support = GLEW_NVX_gpu_memory_info || GLEW_ATI_meminfo;
GCaps.shader_image_load_store_support = GLEW_ARB_shader_image_load_store;
+ GCaps.compute_shader_support = GLEW_ARB_compute_shader;
+ if (GCaps.compute_shader_support) {
+ glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 0, &GCaps.max_work_group_count[0]);
+ glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 1, &GCaps.max_work_group_count[1]);
+ glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 2, &GCaps.max_work_group_count[2]);
+ glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 0, &GCaps.max_work_group_size[0]);
+ glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 1, &GCaps.max_work_group_size[1]);
+ glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 2, &GCaps.max_work_group_size[2]);
+ }
+ GCaps.shader_storage_buffer_objects_support = GLEW_ARB_shader_storage_buffer_object;
/* GL specific capabilities. */
glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &GLContext::max_texture_3d_size);
glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &GLContext::max_cubemap_size);
diff --git a/source/blender/gpu/opengl/gl_backend.hh b/source/blender/gpu/opengl/gl_backend.hh
index 231e5811b45..e9dcdffced0 100644
--- a/source/blender/gpu/opengl/gl_backend.hh
+++ b/source/blender/gpu/opengl/gl_backend.hh
@@ -28,6 +28,7 @@
#include "BLI_vector.hh"
#include "gl_batch.hh"
+#include "gl_compute.hh"
#include "gl_context.hh"
#include "gl_drawlist.hh"
#include "gl_framebuffer.hh"
@@ -126,6 +127,12 @@ class GLBackend : public GPUBackend {
return shared_orphan_list_;
};
+ void compute_dispatch(int groups_x_len, int groups_y_len, int groups_z_len) override
+ {
+ GLContext::get()->state_manager_active_get()->apply_state();
+ GLCompute::dispatch(groups_x_len, groups_y_len, groups_z_len);
+ }
+
private:
static void platform_init(void);
static void platform_exit(void);
diff --git a/source/blender/gpu/opengl/gl_batch.cc b/source/blender/gpu/opengl/gl_batch.cc
index 321d9552828..fc1d1006d0d 100644
--- a/source/blender/gpu/opengl/gl_batch.cc
+++ b/source/blender/gpu/opengl/gl_batch.cc
@@ -44,7 +44,7 @@
using namespace blender::gpu;
/* -------------------------------------------------------------------- */
-/** \name Vao cache
+/** \name VAO Cache
*
* Each #GLBatch has a small cache of VAO objects that are used to avoid VAO reconfiguration.
* TODO(fclem): Could be revisited to avoid so much cross references.
@@ -279,20 +279,6 @@ GLuint GLVaoCache::vao_get(GPUBatch *batch)
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Creation & Deletion
- * \{ */
-
-GLBatch::GLBatch()
-{
-}
-
-GLBatch::~GLBatch()
-{
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
/** \name Drawing
* \{ */
diff --git a/source/blender/gpu/opengl/gl_batch.hh b/source/blender/gpu/opengl/gl_batch.hh
index 218b9ffe4b7..704b6471dd5 100644
--- a/source/blender/gpu/opengl/gl_batch.hh
+++ b/source/blender/gpu/opengl/gl_batch.hh
@@ -43,7 +43,7 @@ class GLShaderInterface;
#define GPU_VAO_STATIC_LEN 3
-/* Vao management: remembers all geometry state (vertex attribute bindings & element buffer)
+/* VAO management: remembers all geometry state (vertex attribute bindings & element buffer)
* for each shader interface. Start with a static number of vaos and fallback to dynamic count
* if necessary. Once a batch goes dynamic it does not go back. */
class GLVaoCache {
@@ -96,9 +96,6 @@ class GLBatch : public Batch {
GLVaoCache vao_cache_;
public:
- GLBatch();
- ~GLBatch();
-
void draw(int v_first, int v_count, int i_first, int i_count) override;
void bind(int i_first);
diff --git a/source/blender/gpu/opengl/gl_compute.cc b/source/blender/gpu/opengl/gl_compute.cc
new file mode 100644
index 00000000000..fa8317dde4a
--- /dev/null
+++ b/source/blender/gpu/opengl/gl_compute.cc
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#include "gl_compute.hh"
+
+#include "gl_debug.hh"
+
+#include "glew-mx.h"
+
+namespace blender::gpu {
+
+void GLCompute::dispatch(int group_x_len, int group_y_len, int group_z_len)
+{
+ glDispatchCompute(group_x_len, group_y_len, group_z_len);
+ debug::check_gl_error("Dispatch Compute");
+}
+
+} // namespace blender::gpu
diff --git a/source/blender/gpu/opengl/gl_compute.hh b/source/blender/gpu/opengl/gl_compute.hh
new file mode 100644
index 00000000000..2fd918ddd10
--- /dev/null
+++ b/source/blender/gpu/opengl/gl_compute.hh
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#pragma once
+
+namespace blender::gpu {
+
+class GLCompute {
+ public:
+ static void dispatch(int group_x_len, int group_y_len, int group_z_len);
+};
+
+} // namespace blender::gpu
diff --git a/source/blender/gpu/opengl/gl_drawlist.cc b/source/blender/gpu/opengl/gl_drawlist.cc
index d3401036154..50ab5574cd2 100644
--- a/source/blender/gpu/opengl/gl_drawlist.cc
+++ b/source/blender/gpu/opengl/gl_drawlist.cc
@@ -68,6 +68,7 @@ GLDrawList::GLDrawList(int length)
batch_ = nullptr;
buffer_id_ = 0;
command_len_ = 0;
+ base_index_ = 0;
command_offset_ = 0;
data_size_ = 0;
data_ = nullptr;
diff --git a/source/blender/gpu/opengl/gl_immediate.hh b/source/blender/gpu/opengl/gl_immediate.hh
index d0bf0fcdf1f..b42f439f9ec 100644
--- a/source/blender/gpu/opengl/gl_immediate.hh
+++ b/source/blender/gpu/opengl/gl_immediate.hh
@@ -38,7 +38,7 @@ namespace blender::gpu {
class GLImmediate : public Immediate {
private:
- /* Use two buffers for strict and unstrict vertex count to
+ /* Use two buffers for strict and non-strict vertex count to
* avoid some huge driver slowdown (see T70922).
* Use accessor functions to get / modify. */
struct {
diff --git a/source/blender/gpu/opengl/gl_index_buffer.cc b/source/blender/gpu/opengl/gl_index_buffer.cc
index e2c18c5d0b9..e305f765ad9 100644
--- a/source/blender/gpu/opengl/gl_index_buffer.cc
+++ b/source/blender/gpu/opengl/gl_index_buffer.cc
@@ -40,17 +40,14 @@ void GLIndexBuf::bind()
return;
}
- if (ibo_id_ == 0) {
+ const bool allocate_on_device = ibo_id_ == 0;
+ if (allocate_on_device) {
glGenBuffers(1, &ibo_id_);
-
- if (data_ == nullptr) {
- debug::raise_gl_error("Trying to use Index Buffer but the buffer contains no data");
- }
}
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo_id_);
- if (data_ != nullptr) {
+ if (data_ != nullptr || allocate_on_device) {
size_t size = this->size_get();
/* Sends data to GPU. */
glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, data_, GL_STATIC_DRAW);
@@ -59,4 +56,29 @@ void GLIndexBuf::bind()
}
}
+void GLIndexBuf::bind_as_ssbo(uint binding)
+{
+ bind();
+ BLI_assert(ibo_id_ != 0);
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, binding, ibo_id_);
+}
+
+const uint32_t *GLIndexBuf::read() const
+{
+ BLI_assert(is_active());
+ void *data = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_READ_ONLY);
+ uint32_t *result = static_cast<uint32_t *>(data);
+ return result;
+}
+
+bool GLIndexBuf::is_active() const
+{
+ if (!ibo_id_) {
+ return false;
+ }
+ int active_ibo_id = 0;
+ glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &active_ibo_id);
+ return ibo_id_ == active_ibo_id;
+}
+
} // namespace blender::gpu
diff --git a/source/blender/gpu/opengl/gl_index_buffer.hh b/source/blender/gpu/opengl/gl_index_buffer.hh
index b84934bb77f..0dbdaa6d398 100644
--- a/source/blender/gpu/opengl/gl_index_buffer.hh
+++ b/source/blender/gpu/opengl/gl_index_buffer.hh
@@ -34,6 +34,7 @@ namespace blender::gpu {
class GLIndexBuf : public IndexBuf {
friend class GLBatch;
friend class GLDrawList;
+ friend class GLShader; /* For compute shaders. */
private:
GLuint ibo_id_ = 0;
@@ -42,6 +43,9 @@ class GLIndexBuf : public IndexBuf {
~GLIndexBuf();
void bind(void);
+ void bind_as_ssbo(uint binding) override;
+
+ const uint32_t *read() const override;
void *offset_ptr(uint additional_vertex_offset) const
{
@@ -57,6 +61,9 @@ class GLIndexBuf : public IndexBuf {
return (index_type_ == GPU_INDEX_U16) ? 0xFFFFu : 0xFFFFFFFFu;
}
+ private:
+ bool is_active() const;
+
MEM_CXX_CLASS_ALLOC_FUNCS("GLIndexBuf")
};
diff --git a/source/blender/gpu/opengl/gl_shader.cc b/source/blender/gpu/opengl/gl_shader.cc
index dd08a67517e..e77347d99eb 100644
--- a/source/blender/gpu/opengl/gl_shader.cc
+++ b/source/blender/gpu/opengl/gl_shader.cc
@@ -26,6 +26,7 @@
#include "BLI_string.h"
#include "BLI_vector.hh"
+#include "GPU_capabilities.h"
#include "GPU_platform.h"
#include "gl_backend.hh"
@@ -63,6 +64,7 @@ GLShader::~GLShader()
glDeleteShader(vert_shader_);
glDeleteShader(geom_shader_);
glDeleteShader(frag_shader_);
+ glDeleteShader(compute_shader_);
glDeleteProgram(shader_program_);
}
@@ -72,7 +74,7 @@ GLShader::~GLShader()
/** \name Shader stage creation
* \{ */
-char *GLShader::glsl_patch_get()
+static char *glsl_patch_default_get()
{
/** Used for shader patching. Init once. */
static char patch[512] = "\0";
@@ -111,6 +113,30 @@ char *GLShader::glsl_patch_get()
return patch;
}
+static char *glsl_patch_compute_get()
+{
+ /** Used for shader patching. Init once. */
+ static char patch[512] = "\0";
+ if (patch[0] != '\0') {
+ return patch;
+ }
+
+ size_t slen = 0;
+ /* Version need to go first. */
+ STR_CONCAT(patch, slen, "#version 430\n");
+ STR_CONCAT(patch, slen, "#extension GL_ARB_compute_shader :enable\n");
+ BLI_assert(slen < sizeof(patch));
+ return patch;
+}
+
+char *GLShader::glsl_patch_get(GLenum gl_stage)
+{
+ if (gl_stage == GL_COMPUTE_SHADER) {
+ return glsl_patch_compute_get();
+ }
+ return glsl_patch_default_get();
+}
+
/* Create, compile and attach the shader stage to the shader program. */
GLuint GLShader::create_shader_stage(GLenum gl_stage, MutableSpan<const char *> sources)
{
@@ -121,7 +147,7 @@ GLuint GLShader::create_shader_stage(GLenum gl_stage, MutableSpan<const char *>
}
/* Patch the shader code using the first source slot. */
- sources[0] = glsl_patch_get();
+ sources[0] = glsl_patch_get(gl_stage);
glShaderSource(shader, sources.size(), sources.data(), nullptr);
glCompileShader(shader);
@@ -142,6 +168,9 @@ GLuint GLShader::create_shader_stage(GLenum gl_stage, MutableSpan<const char *>
case GL_FRAGMENT_SHADER:
this->print_log(sources, log, "FragShader", !status);
break;
+ case GL_COMPUTE_SHADER:
+ this->print_log(sources, log, "ComputeShader", !status);
+ break;
}
}
}
@@ -172,6 +201,11 @@ void GLShader::fragment_shader_from_glsl(MutableSpan<const char *> sources)
frag_shader_ = this->create_shader_stage(GL_FRAGMENT_SHADER, sources);
}
+void GLShader::compute_shader_from_glsl(MutableSpan<const char *> sources)
+{
+ compute_shader_ = this->create_shader_stage(GL_COMPUTE_SHADER, sources);
+}
+
bool GLShader::finalize()
{
if (compilation_failed_) {
diff --git a/source/blender/gpu/opengl/gl_shader.hh b/source/blender/gpu/opengl/gl_shader.hh
index 152eb2f068a..48aaaf2283d 100644
--- a/source/blender/gpu/opengl/gl_shader.hh
+++ b/source/blender/gpu/opengl/gl_shader.hh
@@ -43,6 +43,7 @@ class GLShader : public Shader {
GLuint vert_shader_ = 0;
GLuint geom_shader_ = 0;
GLuint frag_shader_ = 0;
+ GLuint compute_shader_ = 0;
/** True if any shader failed to compile. */
bool compilation_failed_ = false;
@@ -56,6 +57,7 @@ class GLShader : public Shader {
void vertex_shader_from_glsl(MutableSpan<const char *> sources) override;
void geometry_shader_from_glsl(MutableSpan<const char *> sources) override;
void fragment_shader_from_glsl(MutableSpan<const char *> sources) override;
+ void compute_shader_from_glsl(MutableSpan<const char *> sources) override;
bool finalize(void) override;
void transform_feedback_names_set(Span<const char *> name_list,
@@ -75,7 +77,7 @@ class GLShader : public Shader {
int program_handle_get(void) const override;
private:
- char *glsl_patch_get(void);
+ char *glsl_patch_get(GLenum gl_stage);
GLuint create_shader_stage(GLenum gl_stage, MutableSpan<const char *> sources);
diff --git a/source/blender/gpu/opengl/gl_shader_interface.cc b/source/blender/gpu/opengl/gl_shader_interface.cc
index 5870c645bf4..9cf072b2e8a 100644
--- a/source/blender/gpu/opengl/gl_shader_interface.cc
+++ b/source/blender/gpu/opengl/gl_shader_interface.cc
@@ -29,6 +29,8 @@
#include "gl_shader_interface.hh"
+#include "GPU_capabilities.h"
+
namespace blender::gpu {
/* -------------------------------------------------------------------- */
@@ -125,6 +127,18 @@ static inline int image_binding(int32_t program,
return -1;
}
}
+
+static inline int ssbo_binding(int32_t program, uint32_t ssbo_index)
+{
+ GLint binding = -1;
+ GLenum property = GL_BUFFER_BINDING;
+ GLint values_written = 0;
+ glGetProgramResourceiv(
+ program, GL_SHADER_STORAGE_BLOCK, ssbo_index, 1, &property, 1, &values_written, &binding);
+
+ return binding;
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -149,6 +163,13 @@ GLShaderInterface::GLShaderInterface(GLuint program)
glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &active_uniform_len);
uniform_len = active_uniform_len;
+ GLint max_ssbo_name_len = 0, ssbo_len = 0;
+ if (GPU_shader_storage_buffer_objects_support()) {
+ glGetProgramInterfaceiv(program, GL_SHADER_STORAGE_BLOCK, GL_ACTIVE_RESOURCES, &ssbo_len);
+ glGetProgramInterfaceiv(
+ program, GL_SHADER_STORAGE_BLOCK, GL_MAX_NAME_LENGTH, &max_ssbo_name_len);
+ }
+
BLI_assert(ubo_len <= 16 && "enabled_ubo_mask_ is uint16_t");
/* Work around driver bug with Intel HD 4600 on Windows 7/8, where
@@ -162,6 +183,9 @@ GLShaderInterface::GLShaderInterface(GLuint program)
if (uniform_len > 0 && max_uniform_name_len == 0) {
max_uniform_name_len = 256;
}
+ if (ssbo_len > 0 && max_ssbo_name_len == 0) {
+ max_ssbo_name_len = 256;
+ }
/* GL_ACTIVE_UNIFORMS lied to us! Remove the UBO uniforms from the total before
* allocating the uniform array. */
@@ -186,11 +210,12 @@ GLShaderInterface::GLShaderInterface(GLuint program)
}
MEM_freeN(ubo_uni_ids);
- int input_tot_len = attr_len + ubo_len + uniform_len;
+ int input_tot_len = attr_len + ubo_len + uniform_len + ssbo_len;
inputs_ = (ShaderInput *)MEM_callocN(sizeof(ShaderInput) * input_tot_len, __func__);
const uint32_t name_buffer_len = attr_len * max_attr_name_len + ubo_len * max_ubo_name_len +
- uniform_len * max_uniform_name_len;
+ uniform_len * max_uniform_name_len +
+ ssbo_len * max_ssbo_name_len;
name_buffer_ = (char *)MEM_mallocN(name_buffer_len, "name_buffer");
uint32_t name_buffer_offset = 0;
@@ -257,6 +282,22 @@ GLShaderInterface::GLShaderInterface(GLuint program)
}
}
+ /* SSBOs */
+ for (int i = 0; i < ssbo_len; i++) {
+ char *name = name_buffer_ + name_buffer_offset;
+ GLsizei remaining_buffer = name_buffer_len - name_buffer_offset;
+ GLsizei name_len = 0;
+ glGetProgramResourceName(
+ program, GL_SHADER_STORAGE_BLOCK, i, remaining_buffer, &name_len, name);
+
+ const GLint binding = ssbo_binding(program, i);
+
+ ShaderInput *input = &inputs_[attr_len_ + ubo_len_ + uniform_len_ + ssbo_len_++];
+ input->binding = input->location = binding;
+
+ name_buffer_offset += this->set_input_name(input, name, name_len);
+ }
+
/* Builtin Uniforms */
for (int32_t u_int = 0; u_int < GPU_NUM_UNIFORMS; u_int++) {
GPUUniformBuiltin u = static_cast<GPUUniformBuiltin>(u_int);
diff --git a/source/blender/gpu/opengl/gl_state.hh b/source/blender/gpu/opengl/gl_state.hh
index 651c3c22afa..3b4b40b1d10 100644
--- a/source/blender/gpu/opengl/gl_state.hh
+++ b/source/blender/gpu/opengl/gl_state.hh
@@ -121,6 +121,9 @@ static inline GLbitfield to_gl(eGPUBarrier barrier_bits)
if (barrier_bits & GPU_BARRIER_TEXTURE_FETCH) {
barrier |= GL_TEXTURE_FETCH_BARRIER_BIT;
}
+ if (barrier_bits & GPU_BARRIER_SHADER_STORAGE) {
+ barrier |= GL_SHADER_STORAGE_BARRIER_BIT;
+ }
return barrier;
}
diff --git a/source/blender/gpu/opengl/gl_texture.cc b/source/blender/gpu/opengl/gl_texture.cc
index 51cfcd20a6c..e2478a9976c 100644
--- a/source/blender/gpu/opengl/gl_texture.cc
+++ b/source/blender/gpu/opengl/gl_texture.cc
@@ -290,7 +290,9 @@ void GLTexture::update_sub(
}
}
-/** This will create the mipmap images and populate them with filtered data from base level.
+/**
+ * This will create the mipmap images and populate them with filtered data from base level.
+ *
* WARNING: Depth textures are not populated but they have their mips correctly defined.
* WARNING: This resets the mipmap range.
*/
@@ -366,7 +368,7 @@ void GLTexture::copy_to(Texture *dst_)
void *GLTexture::read(int mip, eGPUDataFormat type)
{
BLI_assert(!(format_flag_ & GPU_FORMAT_COMPRESSED));
- BLI_assert(mip <= mipmaps_);
+ BLI_assert(mip <= mipmaps_ || mip == 0);
BLI_assert(validate_data_format(format_, type));
/* NOTE: mip_size_get() won't override any dimension that is equal to 0. */
diff --git a/source/blender/gpu/opengl/gl_vertex_buffer.cc b/source/blender/gpu/opengl/gl_vertex_buffer.cc
index a56d5269fde..ce16a491528 100644
--- a/source/blender/gpu/opengl/gl_vertex_buffer.cc
+++ b/source/blender/gpu/opengl/gl_vertex_buffer.cc
@@ -29,6 +29,10 @@ namespace blender::gpu {
void GLVertBuf::acquire_data()
{
+ if (usage_ == GPU_USAGE_DEVICE_ONLY) {
+ return;
+ }
+
/* Discard previous data if any. */
MEM_SAFE_FREE(data);
data = (uchar *)MEM_mallocN(sizeof(uchar) * this->size_alloc_get(), __func__);
@@ -36,6 +40,10 @@ void GLVertBuf::acquire_data()
void GLVertBuf::resize_data()
{
+ if (usage_ == GPU_USAGE_DEVICE_ONLY) {
+ return;
+ }
+
data = (uchar *)MEM_reallocN(data, sizeof(uchar) * this->size_alloc_get());
}
@@ -94,8 +102,10 @@ void GLVertBuf::bind()
vbo_size_ = this->size_used_get();
/* Orphan the vbo to avoid sync then upload data. */
glBufferData(GL_ARRAY_BUFFER, vbo_size_, nullptr, to_gl(usage_));
- glBufferSubData(GL_ARRAY_BUFFER, 0, vbo_size_, data);
-
+ /* Do not transfer data from host to device when buffer is device only. */
+ if (usage_ != GPU_USAGE_DEVICE_ONLY) {
+ glBufferSubData(GL_ARRAY_BUFFER, 0, vbo_size_, data);
+ }
memory_usage += vbo_size_;
if (usage_ == GPU_USAGE_STATIC) {
@@ -106,6 +116,37 @@ void GLVertBuf::bind()
}
}
+void GLVertBuf::bind_as_ssbo(uint binding)
+{
+ bind();
+ BLI_assert(vbo_id_ != 0);
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, binding, vbo_id_);
+}
+
+const void *GLVertBuf::read() const
+{
+ BLI_assert(is_active());
+ void *result = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY);
+ return result;
+}
+
+void *GLVertBuf::unmap(const void *mapped_data) const
+{
+ void *result = MEM_mallocN(vbo_size_, __func__);
+ memcpy(result, mapped_data, vbo_size_);
+ return result;
+}
+
+bool GLVertBuf::is_active() const
+{
+ if (!vbo_id_) {
+ return false;
+ }
+ int active_vbo_id = 0;
+ glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &active_vbo_id);
+ return vbo_id_ == active_vbo_id;
+}
+
void GLVertBuf::update_sub(uint start, uint len, void *data)
{
glBufferSubData(GL_ARRAY_BUFFER, start, len, data);
diff --git a/source/blender/gpu/opengl/gl_vertex_buffer.hh b/source/blender/gpu/opengl/gl_vertex_buffer.hh
index e2bf6cd00e8..6c38a2225b3 100644
--- a/source/blender/gpu/opengl/gl_vertex_buffer.hh
+++ b/source/blender/gpu/opengl/gl_vertex_buffer.hh
@@ -47,12 +47,19 @@ class GLVertBuf : public VertBuf {
void update_sub(uint start, uint len, void *data) override;
+ const void *read() const override;
+ void *unmap(const void *mapped_data) const override;
+
protected:
void acquire_data(void) override;
void resize_data(void) override;
void release_data(void) override;
void upload_data(void) override;
void duplicate_data(VertBuf *dst) override;
+ void bind_as_ssbo(uint binding) override;
+
+ private:
+ bool is_active() const;
MEM_CXX_CLASS_ALLOC_FUNCS("GLVertBuf");
};
@@ -65,6 +72,7 @@ static inline GLenum to_gl(GPUUsageType type)
case GPU_USAGE_DYNAMIC:
return GL_DYNAMIC_DRAW;
case GPU_USAGE_STATIC:
+ case GPU_USAGE_DEVICE_ONLY:
return GL_STATIC_DRAW;
default:
BLI_assert(0);
diff --git a/source/blender/gpu/shaders/gpu_shader_2D_widget_base_vert.glsl b/source/blender/gpu/shaders/gpu_shader_2D_widget_base_vert.glsl
index 81e0965fad3..b5036b51d9d 100644
--- a/source/blender/gpu/shaders/gpu_shader_2D_widget_base_vert.glsl
+++ b/source/blender/gpu/shaders/gpu_shader_2D_widget_base_vert.glsl
@@ -61,11 +61,27 @@ vec2 do_widget(void)
const vec2 ofs = vec2(0.5, -0.5);
lineWidth = abs(rect.x - recti.x);
vec2 emboss_ofs = vec2(0.0, -lineWidth);
- vec2 v_pos[4] = vec2[4](rect.xz + emboss_ofs + ofs.yy,
- rect.xw + ofs.yx,
- rect.yz + emboss_ofs + ofs.xy,
- rect.yw + ofs.xx);
- vec2 pos = v_pos[gl_VertexID];
+
+ vec2 pos;
+ switch (gl_VertexID) {
+ default:
+ case 0: {
+ pos = rect.xz + emboss_ofs + ofs.yy;
+ break;
+ }
+ case 1: {
+ pos = rect.xw + ofs.yx;
+ break;
+ }
+ case 2: {
+ pos = rect.yz + emboss_ofs + ofs.xy;
+ break;
+ }
+ case 3: {
+ pos = rect.yw + ofs.xx;
+ break;
+ }
+ }
uvInterp = pos - rect.xz;
outRectSize = rect.yw - rect.xz;
diff --git a/source/blender/gpu/shaders/gpu_shader_text_frag.glsl b/source/blender/gpu/shaders/gpu_shader_text_frag.glsl
index d85884e0a25..2568cd74445 100644
--- a/source/blender/gpu/shaders/gpu_shader_text_frag.glsl
+++ b/source/blender/gpu/shaders/gpu_shader_text_frag.glsl
@@ -7,7 +7,7 @@ flat in int interp_size;
out vec4 fragColor;
-uniform sampler1DArray glyph;
+uniform sampler2D glyph;
const vec2 offsets4[4] = vec2[4](
vec2(-0.5, 0.5), vec2(0.5, 0.5), vec2(-0.5, -0.5), vec2(-0.5, -0.5));
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_geometry.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_geometry.glsl
index 4db27c3049d..a14ff5021bf 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_geometry.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_geometry.glsl
@@ -37,8 +37,13 @@ void node_geometry(vec3 I,
normal = (toworld * vec4(N, 0.0)).xyz;
true_normal = normal;
# endif
+
+# ifdef HAIR_SHADER
+ tangent = -hairTangent;
+# else
tangent_orco_z(orco, orco);
node_tangent(N, orco, objmat, tangent);
+# endif
parametric = vec3(barycentric, 0.0);
backfacing = (gl_FrontFacing) ? 0.0 : 1.0;
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl
index 2e0515e324e..d77259638fd 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl
@@ -55,6 +55,7 @@ void node_bsdf_principled(vec4 base_color,
float specular_weight = (1.0 - transmission);
clearcoat = max(clearcoat, 0.0);
transmission_roughness = 1.0 - (1.0 - roughness) * (1.0 - transmission_roughness);
+ specular = max(0.0, specular);
CLOSURE_VARS_DECLARE_4(Diffuse, Glossy, Glossy, Refraction);
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl
index 5a68f802659..5129bf71903 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl
@@ -23,8 +23,8 @@ void node_subsurface_scattering(vec4 color,
/* Not perfect for texture_blur values between 0.0 and 1.0.
* Interpolate between separated color and color applied on irradiance. */
float one_minus_texture_blur = 1.0 - texture_blur;
- vec3 sss_albedo = color.rgb * texture_blur + one_minus_texture_blur;
- vec3 radiance_tint = color.rgb * one_minus_texture_blur + texture_blur;
+ vec3 sss_albedo = color.rgb * one_minus_texture_blur + texture_blur;
+ vec3 radiance_tint = color.rgb * texture_blur + one_minus_texture_blur;
/* Consider output radiance as irradiance. */
out_Diffuse_0.radiance *= radiance_tint;
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl
index d8d9ecdf287..5745f11ede4 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl
@@ -2,7 +2,7 @@
* coordinates to act as a seed since the noise functions don't have seed values.
* A seed value is needed for generating distortion textures and color outputs.
* The offset's components are in the range [100, 200], not too high to cause
- * bad precision and not to small to be noticeable. We use float seed because
+ * bad precision and not too small to be noticeable. We use float seed because
* OSL only support float hashes.
*/
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl
index 60ed098beb3..4ad5d4232de 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl
@@ -150,3 +150,9 @@ void vector_math_faceforward(
{
outVector = faceforward(a, b, c);
}
+
+void vector_math_multiply_add(
+ vec3 a, vec3 b, vec3 c, float scale, out vec3 outVector, out float outValue)
+{
+ outVector = a * b + c;
+}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_wavelength.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_wavelength.glsl
new file mode 100644
index 00000000000..2c5d38eabbe
--- /dev/null
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_wavelength.glsl
@@ -0,0 +1,15 @@
+void node_wavelength(float wavelength,
+ sampler1DArray spectrummap,
+ float layer,
+ vec3 xyz_to_r,
+ vec3 xyz_to_g,
+ vec3 xyz_to_b,
+ out vec4 color)
+{
+ mat3 xyz_to_rgb = mat3(xyz_to_r, xyz_to_g, xyz_to_b);
+ float t = (wavelength - 380.0) / (780.0 - 380.0);
+ vec3 xyz = texture(spectrummap, vec2(t, layer)).rgb;
+ vec3 rgb = xyz * xyz_to_rgb;
+ rgb *= 1.0 / 2.52; /* Empirical scale from lg to make all comps <= 1. */
+ color = vec4(clamp(rgb, 0.0, 1.0), 1.0);
+}
diff --git a/source/blender/gpu/tests/gpu_index_buffer_test.cc b/source/blender/gpu/tests/gpu_index_buffer_test.cc
new file mode 100644
index 00000000000..78e351af7f1
--- /dev/null
+++ b/source/blender/gpu/tests/gpu_index_buffer_test.cc
@@ -0,0 +1,47 @@
+/* Apache License, Version 2.0 */
+
+#include "testing/testing.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "GPU_index_buffer.h"
+
+#include "gpu_testing.hh"
+
+namespace blender::gpu::tests {
+
+TEST_F(GPUTest, gpu_index_buffer_subbuilders)
+{
+ const uint num_subbuilders = 10;
+ const uint verts_per_subbuilders = 100;
+ const uint vertex_len = num_subbuilders * verts_per_subbuilders;
+
+ GPUIndexBufBuilder builder;
+ GPU_indexbuf_init(&builder, GPU_PRIM_POINTS, vertex_len, vertex_len);
+
+ GPUIndexBufBuilder subbuilders[num_subbuilders];
+ for (int subbuilder_index = 0; subbuilder_index < num_subbuilders; subbuilder_index++) {
+ memcpy(&subbuilders[subbuilder_index], &builder, sizeof(builder));
+ }
+
+ for (int subbuilder_index = 0; subbuilder_index < num_subbuilders; subbuilder_index++) {
+ GPUIndexBufBuilder &subbuilder = subbuilders[subbuilder_index];
+ for (int subbuilder_vert_index = 0; subbuilder_vert_index < verts_per_subbuilders;
+ subbuilder_vert_index++) {
+ int vert_index_to_update = subbuilder_index * verts_per_subbuilders + subbuilder_vert_index;
+ GPU_indexbuf_set_point_vert(&subbuilder, vert_index_to_update, vert_index_to_update);
+ }
+ }
+
+ for (int subbuilder_index = 0; subbuilder_index < num_subbuilders; subbuilder_index++) {
+ EXPECT_EQ(builder.index_len, subbuilder_index * verts_per_subbuilders);
+ GPU_indexbuf_join(&builder, &subbuilders[subbuilder_index]);
+ EXPECT_EQ(builder.index_len, (subbuilder_index + 1) * verts_per_subbuilders);
+ }
+
+ GPUIndexBuf *index_buffer = GPU_indexbuf_build(&builder);
+ EXPECT_NE(index_buffer, nullptr);
+ GPU_INDEXBUF_DISCARD_SAFE(index_buffer);
+}
+
+} // namespace blender::gpu::tests
diff --git a/source/blender/gpu/tests/gpu_shader_test.cc b/source/blender/gpu/tests/gpu_shader_test.cc
new file mode 100644
index 00000000000..e8645b89e41
--- /dev/null
+++ b/source/blender/gpu/tests/gpu_shader_test.cc
@@ -0,0 +1,301 @@
+/* Apache License, Version 2.0 */
+
+#include "testing/testing.h"
+
+#include "GPU_capabilities.h"
+#include "GPU_compute.h"
+#include "GPU_index_buffer.h"
+#include "GPU_shader.h"
+#include "GPU_texture.h"
+#include "GPU_vertex_buffer.h"
+#include "GPU_vertex_format.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "gpu_testing.hh"
+
+#include "GPU_glew.h"
+
+namespace blender::gpu::tests {
+
+TEST_F(GPUTest, gpu_shader_compute_2d)
+{
+
+ if (!GPU_compute_shader_support()) {
+ /* We can't test as a the platform does not support compute shaders. */
+ std::cout << "Skipping compute shader test: platform not supported";
+ return;
+ }
+
+ static constexpr uint SIZE = 512;
+
+ /* Build compute shader. */
+ const char *compute_glsl = R"(
+
+layout(local_size_x = 1, local_size_y = 1) in;
+layout(rgba32f, binding = 0) uniform image2D img_output;
+
+void main() {
+ vec4 pixel = vec4(1.0, 0.5, 0.2, 1.0);
+ imageStore(img_output, ivec2(gl_GlobalInvocationID.xy), pixel);
+}
+
+)";
+
+ GPUShader *shader = GPU_shader_create_compute(
+ compute_glsl, nullptr, nullptr, "gpu_shader_compute_2d");
+ EXPECT_NE(shader, nullptr);
+
+ /* Create texture to store result and attach to shader. */
+ GPUTexture *texture = GPU_texture_create_2d(
+ "gpu_shader_compute_2d", SIZE, SIZE, 0, GPU_RGBA32F, nullptr);
+ EXPECT_NE(texture, nullptr);
+
+ GPU_shader_bind(shader);
+ GPU_texture_image_bind(texture, GPU_shader_get_texture_binding(shader, "img_output"));
+
+ /* Dispatch compute task. */
+ GPU_compute_dispatch(shader, SIZE, SIZE, 1);
+
+ /* Check if compute has been done. */
+ GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH);
+ float *data = static_cast<float *>(GPU_texture_read(texture, GPU_DATA_FLOAT, 0));
+ EXPECT_NE(data, nullptr);
+ for (int index = 0; index < SIZE * SIZE; index++) {
+ EXPECT_FLOAT_EQ(data[index * 4 + 0], 1.0f);
+ EXPECT_FLOAT_EQ(data[index * 4 + 1], 0.5f);
+ EXPECT_FLOAT_EQ(data[index * 4 + 2], 0.2f);
+ EXPECT_FLOAT_EQ(data[index * 4 + 3], 1.0f);
+ }
+ MEM_freeN(data);
+
+ /* Cleanup. */
+ GPU_shader_unbind();
+ GPU_texture_unbind(texture);
+ GPU_texture_free(texture);
+ GPU_shader_free(shader);
+}
+
+TEST_F(GPUTest, gpu_shader_compute_1d)
+{
+
+ if (!GPU_compute_shader_support()) {
+ /* We can't test as a the platform does not support compute shaders. */
+ std::cout << "Skipping compute shader test: platform not supported";
+ return;
+ }
+
+ static constexpr uint SIZE = 10;
+
+ /* Build compute shader. */
+ const char *compute_glsl = R"(
+
+layout(local_size_x = 1) in;
+
+layout(rgba32f, binding = 1) uniform image1D outputVboData;
+
+void main() {
+ int index = int(gl_GlobalInvocationID.x);
+ vec4 pos = vec4(gl_GlobalInvocationID.x);
+ imageStore(outputVboData, index, pos);
+}
+
+)";
+
+ GPUShader *shader = GPU_shader_create_compute(
+ compute_glsl, nullptr, nullptr, "gpu_shader_compute_1d");
+ EXPECT_NE(shader, nullptr);
+
+ /* Construct Texture. */
+ GPUTexture *texture = GPU_texture_create_1d("gpu_shader_compute_1d", SIZE, 0, GPU_RGBA32F, NULL);
+ EXPECT_NE(texture, nullptr);
+
+ GPU_shader_bind(shader);
+ GPU_texture_image_bind(texture, GPU_shader_get_texture_binding(shader, "outputVboData"));
+
+ /* Dispatch compute task. */
+ GPU_compute_dispatch(shader, SIZE, 1, 1);
+
+ /* Check if compute has been done. */
+ GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH);
+
+ /* Create texture to load back result. */
+ float *data = static_cast<float *>(GPU_texture_read(texture, GPU_DATA_FLOAT, 0));
+ EXPECT_NE(data, nullptr);
+ for (int index = 0; index < SIZE; index++) {
+ float expected_value = index;
+ EXPECT_FLOAT_EQ(data[index * 4 + 0], expected_value);
+ EXPECT_FLOAT_EQ(data[index * 4 + 1], expected_value);
+ EXPECT_FLOAT_EQ(data[index * 4 + 2], expected_value);
+ EXPECT_FLOAT_EQ(data[index * 4 + 3], expected_value);
+ }
+ MEM_freeN(data);
+
+ /* Cleanup. */
+ GPU_shader_unbind();
+ GPU_texture_unbind(texture);
+ GPU_texture_free(texture);
+ GPU_shader_free(shader);
+}
+
+TEST_F(GPUTest, gpu_shader_compute_vbo)
+{
+
+ if (!GPU_compute_shader_support()) {
+ /* We can't test as a the platform does not support compute shaders. */
+ std::cout << "Skipping compute shader test: platform not supported";
+ return;
+ }
+
+ static constexpr uint SIZE = 128;
+
+ /* Build compute shader. */
+ const char *compute_glsl = R"(
+
+layout(local_size_x = 1) in;
+
+layout(std430, binding = 0) writeonly buffer outputVboData
+{
+ vec4 out_positions[];
+};
+
+void main() {
+ uint index = gl_GlobalInvocationID.x;
+ vec4 pos = vec4(gl_GlobalInvocationID.x);
+ out_positions[index] = pos;
+}
+
+)";
+
+ GPUShader *shader = GPU_shader_create_compute(
+ compute_glsl, nullptr, nullptr, "gpu_shader_compute_vbo");
+ EXPECT_NE(shader, nullptr);
+ GPU_shader_bind(shader);
+
+ /* Construct VBO. */
+ static GPUVertFormat format = {0};
+ GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+ GPUVertBuf *vbo = GPU_vertbuf_create_with_format_ex(&format, GPU_USAGE_DEVICE_ONLY);
+ GPU_vertbuf_data_alloc(vbo, SIZE);
+ GPU_vertbuf_bind_as_ssbo(vbo, GPU_shader_get_ssbo(shader, "outputVboData"));
+
+ /* Dispatch compute task. */
+ GPU_compute_dispatch(shader, SIZE, 1, 1);
+
+ /* Check if compute has been done. */
+ GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE);
+
+ /* Download the vertex buffer. */
+ const float *data = static_cast<const float *>(GPU_vertbuf_read(vbo));
+ ASSERT_NE(data, nullptr);
+ for (int index = 0; index < SIZE; index++) {
+ float expected_value = index;
+ EXPECT_FLOAT_EQ(data[index * 4 + 0], expected_value);
+ EXPECT_FLOAT_EQ(data[index * 4 + 1], expected_value);
+ EXPECT_FLOAT_EQ(data[index * 4 + 2], expected_value);
+ EXPECT_FLOAT_EQ(data[index * 4 + 3], expected_value);
+ }
+
+ /* Cleanup. */
+ GPU_shader_unbind();
+ GPU_vertbuf_discard(vbo);
+ GPU_shader_free(shader);
+}
+
+TEST_F(GPUTest, gpu_shader_compute_ibo)
+{
+
+ if (!GPU_compute_shader_support()) {
+ /* We can't test as a the platform does not support compute shaders. */
+ std::cout << "Skipping compute shader test: platform not supported";
+ return;
+ }
+
+ static constexpr uint SIZE = 128;
+
+ /* Build compute shader. */
+ const char *compute_glsl = R"(
+
+layout(local_size_x = 1) in;
+
+layout(std430, binding = 1) writeonly buffer outputIboData
+{
+ uint out_indexes[];
+};
+
+void main() {
+ uint store_index = int(gl_GlobalInvocationID.x);
+ out_indexes[store_index] = store_index;
+}
+
+)";
+
+ GPUShader *shader = GPU_shader_create_compute(
+ compute_glsl, nullptr, nullptr, "gpu_shader_compute_vbo");
+ EXPECT_NE(shader, nullptr);
+ GPU_shader_bind(shader);
+
+ /* Construct IBO. */
+ GPUIndexBuf *ibo = GPU_indexbuf_build_on_device(SIZE);
+ GPU_indexbuf_bind_as_ssbo(ibo, GPU_shader_get_ssbo(shader, "outputIboData"));
+
+ /* Dispatch compute task. */
+ GPU_compute_dispatch(shader, SIZE, 1, 1);
+
+ /* Check if compute has been done. */
+ GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE);
+
+ /* Download the index buffer. */
+ const uint32_t *data = GPU_indexbuf_read(ibo);
+ ASSERT_NE(data, nullptr);
+ for (int index = 0; index < SIZE; index++) {
+ uint32_t expected = index;
+ EXPECT_EQ(data[index], expected);
+ }
+
+ /* Cleanup. */
+ GPU_shader_unbind();
+ GPU_indexbuf_discard(ibo);
+ GPU_shader_free(shader);
+}
+
+TEST_F(GPUTest, gpu_shader_ssbo_binding)
+{
+ if (!GPU_compute_shader_support()) {
+ /* We can't test as a the platform does not support compute shaders. */
+ std::cout << "Skipping compute shader test: platform not supported";
+ return;
+ }
+
+ /* Build compute shader. */
+ const char *compute_glsl = R"(
+
+layout(local_size_x = 1) in;
+
+layout(std430, binding = 0) buffer ssboBinding0
+{
+ int data0[];
+};
+layout(std430, binding = 1) buffer ssboBinding1
+{
+ int data1[];
+};
+
+void main() {
+}
+
+)";
+
+ GPUShader *shader = GPU_shader_create_compute(compute_glsl, nullptr, nullptr, "gpu_shader_ssbo");
+ EXPECT_NE(shader, nullptr);
+ GPU_shader_bind(shader);
+
+ EXPECT_EQ(0, GPU_shader_get_ssbo(shader, "ssboBinding0"));
+ EXPECT_EQ(1, GPU_shader_get_ssbo(shader, "ssboBinding1"));
+
+ /* Cleanup. */
+ GPU_shader_unbind();
+ GPU_shader_free(shader);
+}
+
+} // namespace blender::gpu::tests
diff --git a/source/blender/gpu/tests/gpu_testing.cc b/source/blender/gpu/tests/gpu_testing.cc
index c193a19ad9c..ac42c5875c8 100644
--- a/source/blender/gpu/tests/gpu_testing.cc
+++ b/source/blender/gpu/tests/gpu_testing.cc
@@ -2,6 +2,8 @@
#include "testing/testing.h"
+#include "CLG_log.h"
+
#include "GPU_context.h"
#include "GPU_init_exit.h"
#include "gpu_testing.hh"
@@ -13,9 +15,10 @@ namespace blender::gpu {
void GPUTest::SetUp()
{
GHOST_GLSettings glSettings = {0};
+ CLG_init();
ghost_system = GHOST_CreateSystem();
ghost_context = GHOST_CreateOpenGLContext(ghost_system, glSettings);
- context = GPU_context_create(NULL);
+ context = GPU_context_create(nullptr);
GPU_init();
}
@@ -26,6 +29,7 @@ void GPUTest::TearDown()
GPU_context_discard(context);
GHOST_DisposeOpenGLContext(ghost_system, ghost_context);
GHOST_DisposeSystem(ghost_system);
+ CLG_exit();
}
} // namespace blender::gpu
diff --git a/source/blender/imbuf/CMakeLists.txt b/source/blender/imbuf/CMakeLists.txt
index 7ce795280a3..be0e364c85f 100644
--- a/source/blender/imbuf/CMakeLists.txt
+++ b/source/blender/imbuf/CMakeLists.txt
@@ -166,12 +166,6 @@ if(WITH_CODEC_FFMPEG)
${OPENJPEG_LIBRARIES}
)
add_definitions(-DWITH_FFMPEG)
-
- remove_strict_c_flags_file(
- intern/anim_movie.c
- intern/indexer.c
- intern/util.c
- )
endif()
if(WITH_IMAGE_DDS)
diff --git a/source/blender/imbuf/IMB_colormanagement.h b/source/blender/imbuf/IMB_colormanagement.h
index f31839751b1..53b0e295385 100644
--- a/source/blender/imbuf/IMB_colormanagement.h
+++ b/source/blender/imbuf/IMB_colormanagement.h
@@ -69,9 +69,9 @@ bool IMB_colormanagement_space_name_is_data(const char *name);
BLI_INLINE float IMB_colormanagement_get_luminance(const float rgb[3]);
BLI_INLINE unsigned char IMB_colormanagement_get_luminance_byte(const unsigned char[3]);
-BLI_INLINE void IMB_colormangement_xyz_to_rgb(float rgb[3], const float xyz[3]);
-BLI_INLINE void IMB_colormangement_rgb_to_xyz(float xyz[3], const float rgb[3]);
-const float *IMB_colormangement_get_xyz_to_rgb(void);
+BLI_INLINE void IMB_colormanagement_xyz_to_rgb(float rgb[3], const float xyz[3]);
+BLI_INLINE void IMB_colormanagement_rgb_to_xyz(float xyz[3], const float rgb[3]);
+const float *IMB_colormanagement_get_xyz_to_rgb(void);
/* ** Color space transformation functions ** */
void IMB_colormanagement_transform(float *buffer,
diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h
index d131e4dacdc..69a80d6e0d3 100644
--- a/source/blender/imbuf/IMB_imbuf.h
+++ b/source/blender/imbuf/IMB_imbuf.h
@@ -70,6 +70,7 @@ extern "C" {
*/
struct ImBuf;
struct rcti;
+struct rctf;
/**
*
@@ -323,6 +324,11 @@ typedef enum IMB_Proxy_Size {
IMB_PROXY_MAX_SLOT = 4,
} IMB_Proxy_Size;
+typedef enum eIMBInterpolationFilterMode {
+ IMB_FILTER_NEAREST,
+ IMB_FILTER_BILINEAR,
+} eIMBInterpolationFilterMode;
+
/* Defaults to BL_proxy within the directory of the animation. */
void IMB_anim_set_index_dir(struct anim *anim, const char *dir);
void IMB_anim_get_fname(struct anim *anim, char *file, int size);
@@ -380,8 +386,6 @@ bool IMB_anim_can_produce_frames(const struct anim *anim);
*/
int ismovie(const char *filepath);
-void IMB_anim_set_preseek(struct anim *anim, int preseek);
-int IMB_anim_get_preseek(struct anim *anim);
int IMB_anim_get_image_width(struct anim *anim);
int IMB_anim_get_image_height(struct anim *anim);
@@ -729,11 +733,17 @@ void IMB_processor_apply_threaded(
void(init_handle)(void *handle, int start_line, int tot_line, void *customdata),
void *(do_thread)(void *));
-typedef void (*ScanlineThreadFunc)(void *custom_data, int start_scanline, int num_scanlines);
+typedef void (*ScanlineThreadFunc)(void *custom_data, int scanline);
void IMB_processor_apply_threaded_scanlines(int total_scanlines,
ScanlineThreadFunc do_thread,
void *custom_data);
+void IMB_transform(struct ImBuf *src,
+ struct ImBuf *dst,
+ float transform_matrix[3][3],
+ struct rctf *src_crop,
+ const eIMBInterpolationFilterMode filter);
+
/* ffmpeg */
void IMB_ffmpeg_init(void);
const char *IMB_ffmpeg_last_error(void);
diff --git a/source/blender/imbuf/intern/IMB_anim.h b/source/blender/imbuf/intern/IMB_anim.h
index 1239d3881de..cfeffcca0ea 100644
--- a/source/blender/imbuf/intern/IMB_anim.h
+++ b/source/blender/imbuf/intern/IMB_anim.h
@@ -87,7 +87,7 @@ struct anim_index;
struct anim {
int ib_flags;
int curtype;
- int curposition; /* index 0 = 1e, 1 = 2e, enz. */
+ int cur_position; /* index 0 = 1e, 1 = 2e, enz. */
int duration_in_frames;
int frs_sec;
double frs_sec_base;
@@ -105,7 +105,6 @@ struct anim {
int orientation;
size_t framesize;
int interlacing;
- int preseek;
int streamindex;
/* avi */
@@ -132,10 +131,10 @@ struct anim {
struct SwsContext *img_convert_ctx;
int videoStream;
- struct ImBuf *last_frame;
- int64_t last_pts;
- int64_t next_pts;
- AVPacket next_packet;
+ struct ImBuf *cur_frame_final;
+ int64_t cur_pts;
+ int64_t cur_key_frame_pts;
+ AVPacket *cur_packet;
#endif
char index_dir[768];
diff --git a/source/blender/imbuf/intern/IMB_indexer.h b/source/blender/imbuf/intern/IMB_indexer.h
index 363ad45e0e6..37309ccc13a 100644
--- a/source/blender/imbuf/intern/IMB_indexer.h
+++ b/source/blender/imbuf/intern/IMB_indexer.h
@@ -49,6 +49,7 @@
typedef struct anim_index_entry {
int frameno;
uint64_t seek_pos;
+ uint64_t seek_pos_pts;
uint64_t seek_pos_dts;
uint64_t pts;
} anim_index_entry;
@@ -77,14 +78,19 @@ typedef struct anim_index_builder {
} anim_index_builder;
anim_index_builder *IMB_index_builder_create(const char *name);
-void IMB_index_builder_add_entry(
- anim_index_builder *fp, int frameno, uint64_t seek_pos, uint64_t seek_pos_dts, uint64_t pts);
+void IMB_index_builder_add_entry(anim_index_builder *fp,
+ int frameno,
+ uint64_t seek_pos,
+ uint64_t seek_pos_pts,
+ uint64_t seek_pos_dts,
+ uint64_t pts);
void IMB_index_builder_proc_frame(anim_index_builder *fp,
unsigned char *buffer,
int data_size,
int frameno,
uint64_t seek_pos,
+ uint64_t seek_pos_pts,
uint64_t seek_pos_dts,
uint64_t pts);
@@ -92,6 +98,7 @@ void IMB_index_builder_finish(anim_index_builder *fp, int rollback);
struct anim_index *IMB_indexer_open(const char *name);
uint64_t IMB_indexer_get_seek_pos(struct anim_index *idx, int frame_index);
+uint64_t IMB_indexer_get_seek_pos_pts(struct anim_index *idx, int frame_index);
uint64_t IMB_indexer_get_seek_pos_dts(struct anim_index *idx, int frame_index);
int IMB_indexer_get_frame_index(struct anim_index *idx, int frameno);
diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c
index d36c7bbe486..796f7469fb3 100644
--- a/source/blender/imbuf/intern/anim_movie.c
+++ b/source/blender/imbuf/intern/anim_movie.c
@@ -79,6 +79,7 @@
# include <libavcodec/avcodec.h>
# include <libavformat/avformat.h>
+# include <libavutil/imgutils.h>
# include <libavutil/rational.h>
# include <libswscale/swscale.h>
@@ -432,8 +433,7 @@ static int startavi(struct anim *anim)
anim->orientation = 0;
anim->framesize = anim->x * anim->y * 4;
- anim->curposition = 0;
- anim->preseek = 0;
+ anim->cur_position = 0;
# if 0
printf("x:%d y:%d size:%d interl:%d dur:%d\n",
@@ -519,12 +519,10 @@ static int startffmpeg(struct anim *anim)
double frs_den;
int streamcount;
-# ifdef FFMPEG_SWSCALE_COLOR_SPACE_SUPPORT
/* The following for color space determination */
int srcRange, dstRange, brightness, contrast, saturation;
int *table;
const int *inv_table;
-# endif
if (anim == NULL) {
return (-1);
@@ -547,7 +545,7 @@ static int startffmpeg(struct anim *anim)
video_stream_index = -1;
for (i = 0; i < pFormatCtx->nb_streams; i++) {
- if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
+ if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
if (streamcount > 0) {
streamcount--;
continue;
@@ -563,26 +561,38 @@ static int startffmpeg(struct anim *anim)
}
video_stream = pFormatCtx->streams[video_stream_index];
- pCodecCtx = video_stream->codec;
/* Find the decoder for the video stream */
- pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
+ pCodec = avcodec_find_decoder(video_stream->codecpar->codec_id);
if (pCodec == NULL) {
avformat_close_input(&pFormatCtx);
return -1;
}
- pCodecCtx->workaround_bugs = 1;
+ pCodecCtx = avcodec_alloc_context3(NULL);
+ avcodec_parameters_to_context(pCodecCtx, video_stream->codecpar);
+ pCodecCtx->workaround_bugs = FF_BUG_AUTODETECT;
- pCodecCtx->thread_count = BLI_system_thread_count();
- pCodecCtx->thread_type = FF_THREAD_SLICE;
+ if (pCodec->capabilities & AV_CODEC_CAP_AUTO_THREADS) {
+ pCodecCtx->thread_count = 0;
+ }
+ else {
+ pCodecCtx->thread_count = BLI_system_thread_count();
+ }
+
+ if (pCodec->capabilities & AV_CODEC_CAP_FRAME_THREADS) {
+ pCodecCtx->thread_type = FF_THREAD_FRAME;
+ }
+ else if (pCodec->capabilities & AV_CODEC_CAP_SLICE_THREADS) {
+ pCodecCtx->thread_type = FF_THREAD_SLICE;
+ }
if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
avformat_close_input(&pFormatCtx);
return -1;
}
if (pCodecCtx->pix_fmt == AV_PIX_FMT_NONE) {
- avcodec_close(anim->pCodecCtx);
+ avcodec_free_context(&anim->pCodecCtx);
avformat_close_input(&pFormatCtx);
return -1;
}
@@ -628,7 +638,7 @@ static int startffmpeg(struct anim *anim)
anim->params = 0;
anim->x = pCodecCtx->width;
- anim->y = av_get_cropped_height_from_codec(pCodecCtx);
+ anim->y = pCodecCtx->height;
anim->pFormatCtx = pFormatCtx;
anim->pCodecCtx = pCodecCtx;
@@ -639,11 +649,12 @@ static int startffmpeg(struct anim *anim)
anim->orientation = 0;
anim->framesize = anim->x * anim->y * 4;
- anim->curposition = -1;
- anim->last_frame = 0;
- anim->last_pts = -1;
- anim->next_pts = -1;
- anim->next_packet.stream_index = -1;
+ anim->cur_position = -1;
+ anim->cur_frame_final = 0;
+ anim->cur_pts = -1;
+ anim->cur_key_frame_pts = -1;
+ anim->cur_packet = av_packet_alloc();
+ anim->cur_packet->stream_index = -1;
anim->pFrame = av_frame_alloc();
anim->pFrameComplete = false;
@@ -657,8 +668,9 @@ static int startffmpeg(struct anim *anim)
if (av_frame_get_buffer(anim->pFrameRGB, 32) < 0) {
fprintf(stderr, "Could not allocate frame data.\n");
- avcodec_close(anim->pCodecCtx);
+ avcodec_free_context(&anim->pCodecCtx);
avformat_close_input(&anim->pFormatCtx);
+ av_packet_free(&anim->cur_packet);
av_frame_free(&anim->pFrameRGB);
av_frame_free(&anim->pFrameDeinterlaced);
av_frame_free(&anim->pFrame);
@@ -667,10 +679,11 @@ static int startffmpeg(struct anim *anim)
}
}
- if (avpicture_get_size(AV_PIX_FMT_RGBA, anim->x, anim->y) != anim->x * anim->y * 4) {
+ if (av_image_get_buffer_size(AV_PIX_FMT_RGBA, anim->x, anim->y, 1) != anim->x * anim->y * 4) {
fprintf(stderr, "ffmpeg has changed alloc scheme ... ARGHHH!\n");
- avcodec_close(anim->pCodecCtx);
+ avcodec_free_context(&anim->pCodecCtx);
avformat_close_input(&anim->pFormatCtx);
+ av_packet_free(&anim->cur_packet);
av_frame_free(&anim->pFrameRGB);
av_frame_free(&anim->pFrameDeinterlaced);
av_frame_free(&anim->pFrame);
@@ -679,21 +692,17 @@ static int startffmpeg(struct anim *anim)
}
if (anim->ib_flags & IB_animdeinterlace) {
- avpicture_fill((AVPicture *)anim->pFrameDeinterlaced,
- MEM_callocN(avpicture_get_size(anim->pCodecCtx->pix_fmt,
- anim->pCodecCtx->width,
- anim->pCodecCtx->height),
- "ffmpeg deinterlace"),
- anim->pCodecCtx->pix_fmt,
- anim->pCodecCtx->width,
- anim->pCodecCtx->height);
- }
-
- if (pCodecCtx->has_b_frames) {
- anim->preseek = 25; /* FIXME: detect gopsize ... */
- }
- else {
- anim->preseek = 0;
+ av_image_fill_arrays(anim->pFrameDeinterlaced->data,
+ anim->pFrameDeinterlaced->linesize,
+ MEM_callocN(av_image_get_buffer_size(anim->pCodecCtx->pix_fmt,
+ anim->pCodecCtx->width,
+ anim->pCodecCtx->height,
+ 1),
+ "ffmpeg deinterlace"),
+ anim->pCodecCtx->pix_fmt,
+ anim->pCodecCtx->width,
+ anim->pCodecCtx->height,
+ 1);
}
anim->img_convert_ctx = sws_getContext(anim->x,
@@ -702,15 +711,16 @@ static int startffmpeg(struct anim *anim)
anim->x,
anim->y,
AV_PIX_FMT_RGBA,
- SWS_FAST_BILINEAR | SWS_PRINT_INFO | SWS_FULL_CHR_H_INT,
+ SWS_BILINEAR | SWS_PRINT_INFO | SWS_FULL_CHR_H_INT,
NULL,
NULL,
NULL);
if (!anim->img_convert_ctx) {
fprintf(stderr, "Can't transform color space??? Bailing out...\n");
- avcodec_close(anim->pCodecCtx);
+ avcodec_free_context(&anim->pCodecCtx);
avformat_close_input(&anim->pFormatCtx);
+ av_packet_free(&anim->cur_packet);
av_frame_free(&anim->pFrameRGB);
av_frame_free(&anim->pFrameDeinterlaced);
av_frame_free(&anim->pFrame);
@@ -718,7 +728,6 @@ static int startffmpeg(struct anim *anim)
return -1;
}
-# ifdef FFMPEG_SWSCALE_COLOR_SPACE_SUPPORT
/* Try do detect if input has 0-255 YCbCR range (JFIF Jpeg MotionJpeg) */
if (!sws_getColorspaceDetails(anim->img_convert_ctx,
(int **)&inv_table,
@@ -745,7 +754,6 @@ static int startffmpeg(struct anim *anim)
else {
fprintf(stderr, "Warning: Could not set libswscale colorspace details.\n");
}
-# endif
return 0;
}
@@ -753,13 +761,13 @@ static int startffmpeg(struct anim *anim)
/* postprocess the image in anim->pFrame and do color conversion
* and deinterlacing stuff.
*
- * Output is anim->last_frame
+ * Output is anim->cur_frame_final
*/
static void ffmpeg_postprocess(struct anim *anim)
{
AVFrame *input = anim->pFrame;
- ImBuf *ibuf = anim->last_frame;
+ ImBuf *ibuf = anim->cur_frame_final;
int filter_y = 0;
if (!anim->pFrameComplete) {
@@ -784,11 +792,11 @@ static void ffmpeg_postprocess(struct anim *anim)
input->data[3]);
if (anim->ib_flags & IB_animdeinterlace) {
- if (avpicture_deinterlace((AVPicture *)anim->pFrameDeinterlaced,
- (const AVPicture *)anim->pFrame,
- anim->pCodecCtx->pix_fmt,
- anim->pCodecCtx->width,
- anim->pCodecCtx->height) < 0) {
+ if (av_image_deinterlace(anim->pFrameDeinterlaced,
+ anim->pFrame,
+ anim->pCodecCtx->pix_fmt,
+ anim->pCodecCtx->width,
+ anim->pCodecCtx->height) < 0) {
filter_y = true;
}
else {
@@ -797,81 +805,87 @@ static void ffmpeg_postprocess(struct anim *anim)
}
if (!need_aligned_ffmpeg_buffer(anim)) {
- avpicture_fill((AVPicture *)anim->pFrameRGB,
- (unsigned char *)ibuf->rect,
- AV_PIX_FMT_RGBA,
- anim->x,
- anim->y);
- }
-
- if (ENDIAN_ORDER == B_ENDIAN) {
- int *dstStride = anim->pFrameRGB->linesize;
- uint8_t **dst = anim->pFrameRGB->data;
- const int dstStride2[4] = {dstStride[0], 0, 0, 0};
- uint8_t *dst2[4] = {dst[0], 0, 0, 0};
- int x, y, h, w;
- unsigned char *bottom;
- unsigned char *top;
-
- sws_scale(anim->img_convert_ctx,
- (const uint8_t *const *)input->data,
- input->linesize,
- 0,
- anim->y,
- dst2,
- dstStride2);
-
- bottom = (unsigned char *)ibuf->rect;
- top = bottom + ibuf->x * (ibuf->y - 1) * 4;
-
- h = (ibuf->y + 1) / 2;
- w = ibuf->x;
-
- for (y = 0; y < h; y++) {
- unsigned char tmp[4];
- unsigned int *tmp_l = (unsigned int *)tmp;
-
- for (x = 0; x < w; x++) {
- tmp[0] = bottom[0];
- tmp[1] = bottom[1];
- tmp[2] = bottom[2];
- tmp[3] = bottom[3];
-
- bottom[0] = top[0];
- bottom[1] = top[1];
- bottom[2] = top[2];
- bottom[3] = top[3];
-
- *(unsigned int *)top = *tmp_l;
-
- bottom += 4;
- top += 4;
- }
- top -= 8 * w;
+ av_image_fill_arrays(anim->pFrameRGB->data,
+ anim->pFrameRGB->linesize,
+ (unsigned char *)ibuf->rect,
+ AV_PIX_FMT_RGBA,
+ anim->x,
+ anim->y,
+ 1);
+ }
+
+# if defined(__x86_64__) || defined(_M_X64)
+ /* Scale and flip image over Y axis in one go, using negative strides.
+ * This doesn't work with ARM/PowerPC though and may be misusing the API.
+ * Limit it x86_64 where it appears to work.
+ * http://trac.ffmpeg.org/ticket/9060 */
+ int *dstStride = anim->pFrameRGB->linesize;
+ uint8_t **dst = anim->pFrameRGB->data;
+ const int dstStride2[4] = {-dstStride[0], 0, 0, 0};
+ uint8_t *dst2[4] = {dst[0] + (anim->y - 1) * dstStride[0], 0, 0, 0};
+
+ sws_scale(anim->img_convert_ctx,
+ (const uint8_t *const *)input->data,
+ input->linesize,
+ 0,
+ anim->y,
+ dst2,
+ dstStride2);
+# else
+ /* Scale with swscale then flip image over Y axis. */
+ int *dstStride = anim->pFrameRGB->linesize;
+ uint8_t **dst = anim->pFrameRGB->data;
+ const int dstStride2[4] = {dstStride[0], 0, 0, 0};
+ uint8_t *dst2[4] = {dst[0], 0, 0, 0};
+ int x, y, h, w;
+ unsigned char *bottom;
+ unsigned char *top;
+
+ sws_scale(anim->img_convert_ctx,
+ (const uint8_t *const *)input->data,
+ input->linesize,
+ 0,
+ anim->y,
+ dst2,
+ dstStride2);
+
+ bottom = (unsigned char *)ibuf->rect;
+ top = bottom + ibuf->x * (ibuf->y - 1) * 4;
+
+ h = (ibuf->y + 1) / 2;
+ w = ibuf->x;
+
+ for (y = 0; y < h; y++) {
+ unsigned char tmp[4];
+ unsigned int *tmp_l = (unsigned int *)tmp;
+
+ for (x = 0; x < w; x++) {
+ tmp[0] = bottom[0];
+ tmp[1] = bottom[1];
+ tmp[2] = bottom[2];
+ tmp[3] = bottom[3];
+
+ bottom[0] = top[0];
+ bottom[1] = top[1];
+ bottom[2] = top[2];
+ bottom[3] = top[3];
+
+ *(unsigned int *)top = *tmp_l;
+
+ bottom += 4;
+ top += 4;
}
+ top -= 8 * w;
}
- else {
- int *dstStride = anim->pFrameRGB->linesize;
- uint8_t **dst = anim->pFrameRGB->data;
- const int dstStride2[4] = {-dstStride[0], 0, 0, 0};
- uint8_t *dst2[4] = {dst[0] + (anim->y - 1) * dstStride[0], 0, 0, 0};
-
- sws_scale(anim->img_convert_ctx,
- (const uint8_t *const *)input->data,
- input->linesize,
- 0,
- anim->y,
- dst2,
- dstStride2);
- }
+# endif
if (need_aligned_ffmpeg_buffer(anim)) {
- uint8_t *src = anim->pFrameRGB->data[0];
- uint8_t *dst = (uint8_t *)ibuf->rect;
+ uint8_t *buf_src = anim->pFrameRGB->data[0];
+ uint8_t *buf_dst = (uint8_t *)ibuf->rect;
for (int y = 0; y < anim->y; y++) {
- memcpy(dst, src, anim->x * 4);
- dst += anim->x * 4;
- src += anim->pFrameRGB->linesize[0];
+ memcpy(buf_dst, buf_src, anim->x * 4);
+ buf_dst += anim->x * 4;
+ buf_src += anim->pFrameRGB->linesize[0];
}
}
@@ -880,7 +894,7 @@ static void ffmpeg_postprocess(struct anim *anim)
}
}
-/* decode one video frame also considering the packet read into next_packet */
+/* decode one video frame also considering the packet read into cur_packet */
static int ffmpeg_decode_video_frame(struct anim *anim)
{
@@ -888,125 +902,81 @@ static int ffmpeg_decode_video_frame(struct anim *anim)
av_log(anim->pFormatCtx, AV_LOG_DEBUG, " DECODE VIDEO FRAME\n");
- if (anim->next_packet.stream_index == anim->videoStream) {
- av_free_packet(&anim->next_packet);
- anim->next_packet.stream_index = -1;
+ if (anim->cur_packet->stream_index == anim->videoStream) {
+ av_packet_unref(anim->cur_packet);
+ anim->cur_packet->stream_index = -1;
}
- while ((rval = av_read_frame(anim->pFormatCtx, &anim->next_packet)) >= 0) {
+ while ((rval = av_read_frame(anim->pFormatCtx, anim->cur_packet)) >= 0) {
av_log(anim->pFormatCtx,
AV_LOG_DEBUG,
"%sREAD: strID=%d (VID: %d) dts=%" PRId64 " pts=%" PRId64 " %s\n",
- (anim->next_packet.stream_index == anim->videoStream) ? "->" : " ",
- anim->next_packet.stream_index,
+ (anim->cur_packet->stream_index == anim->videoStream) ? "->" : " ",
+ anim->cur_packet->stream_index,
anim->videoStream,
- (anim->next_packet.dts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->next_packet.dts,
- (anim->next_packet.pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->next_packet.pts,
- (anim->next_packet.flags & AV_PKT_FLAG_KEY) ? " KEY" : "");
- if (anim->next_packet.stream_index == anim->videoStream) {
+ (anim->cur_packet->dts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->cur_packet->dts,
+ (anim->cur_packet->pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->cur_packet->pts,
+ (anim->cur_packet->flags & AV_PKT_FLAG_KEY) ? " KEY" : "");
+ if (anim->cur_packet->stream_index == anim->videoStream) {
anim->pFrameComplete = 0;
- avcodec_decode_video2(
- anim->pCodecCtx, anim->pFrame, &anim->pFrameComplete, &anim->next_packet);
+ avcodec_send_packet(anim->pCodecCtx, anim->cur_packet);
+ anim->pFrameComplete = avcodec_receive_frame(anim->pCodecCtx, anim->pFrame) == 0;
if (anim->pFrameComplete) {
- anim->next_pts = av_get_pts_from_frame(anim->pFormatCtx, anim->pFrame);
+ anim->cur_pts = av_get_pts_from_frame(anim->pFrame);
+ if (anim->pFrame->key_frame) {
+ anim->cur_key_frame_pts = anim->cur_pts;
+ }
av_log(anim->pFormatCtx,
AV_LOG_DEBUG,
- " FRAME DONE: next_pts=%" PRId64 " pkt_pts=%" PRId64 ", guessed_pts=%" PRId64 "\n",
+ " FRAME DONE: cur_pts=%" PRId64 ", guessed_pts=%" PRId64 "\n",
(anim->pFrame->pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->pFrame->pts,
- (anim->pFrame->pkt_pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->pFrame->pkt_pts,
- (int64_t)anim->next_pts);
+ (int64_t)anim->cur_pts);
break;
}
}
- av_free_packet(&anim->next_packet);
- anim->next_packet.stream_index = -1;
+ av_packet_unref(anim->cur_packet);
+ anim->cur_packet->stream_index = -1;
}
if (rval == AVERROR_EOF) {
- /* this sets size and data fields to zero,
- * which is necessary to decode the remaining data
- * in the decoder engine after EOF. It also prevents a memory
- * leak, since av_read_frame spills out a full size packet even
- * on EOF... (and: it's safe to call on NULL packets) */
-
- av_free_packet(&anim->next_packet);
-
- anim->next_packet.size = 0;
- anim->next_packet.data = 0;
-
+ /* Flush any remaining frames out of the decoder. */
anim->pFrameComplete = 0;
- avcodec_decode_video2(
- anim->pCodecCtx, anim->pFrame, &anim->pFrameComplete, &anim->next_packet);
+ avcodec_send_packet(anim->pCodecCtx, NULL);
+ anim->pFrameComplete = avcodec_receive_frame(anim->pCodecCtx, anim->pFrame) == 0;
if (anim->pFrameComplete) {
- anim->next_pts = av_get_pts_from_frame(anim->pFormatCtx, anim->pFrame);
+ anim->cur_pts = av_get_pts_from_frame(anim->pFrame);
+ if (anim->pFrame->key_frame) {
+ anim->cur_key_frame_pts = anim->cur_pts;
+ }
av_log(anim->pFormatCtx,
AV_LOG_DEBUG,
- " FRAME DONE (after EOF): next_pts=%" PRId64 " pkt_pts=%" PRId64
- ", guessed_pts=%" PRId64 "\n",
+ " FRAME DONE (after EOF): cur_pts=%" PRId64 ", guessed_pts=%" PRId64 "\n",
(anim->pFrame->pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->pFrame->pts,
- (anim->pFrame->pkt_pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->pFrame->pkt_pts,
- (int64_t)anim->next_pts);
+ (int64_t)anim->cur_pts);
rval = 0;
}
}
if (rval < 0) {
- anim->next_packet.stream_index = -1;
+ av_packet_unref(anim->cur_packet);
+ anim->cur_packet->stream_index = -1;
av_log(anim->pFormatCtx,
AV_LOG_ERROR,
" DECODE READ FAILED: av_read_frame() "
- "returned error: %d\n",
- rval);
+ "returned error: %s\n",
+ av_err2str(rval));
}
return (rval >= 0);
}
-static void ffmpeg_decode_video_frame_scan(struct anim *anim, int64_t pts_to_search)
-{
- /* there seem to exist *very* silly GOP lengths out in the wild... */
- int count = 1000;
-
- av_log(anim->pFormatCtx,
- AV_LOG_DEBUG,
- "SCAN start: considering pts=%" PRId64 " in search of %" PRId64 "\n",
- (int64_t)anim->next_pts,
- (int64_t)pts_to_search);
-
- while (count > 0 && anim->next_pts < pts_to_search) {
- av_log(anim->pFormatCtx,
- AV_LOG_DEBUG,
- " WHILE: pts=%" PRId64 " in search of %" PRId64 "\n",
- (int64_t)anim->next_pts,
- (int64_t)pts_to_search);
- if (!ffmpeg_decode_video_frame(anim)) {
- break;
- }
- count--;
- }
- if (count == 0) {
- av_log(anim->pFormatCtx,
- AV_LOG_ERROR,
- "SCAN failed: completely lost in stream, "
- "bailing out at PTS=%" PRId64 ", searching for PTS=%" PRId64 "\n",
- (int64_t)anim->next_pts,
- (int64_t)pts_to_search);
- }
- if (anim->next_pts == pts_to_search) {
- av_log(anim->pFormatCtx, AV_LOG_DEBUG, "SCAN HAPPY: we found our PTS!\n");
- }
- else {
- av_log(anim->pFormatCtx, AV_LOG_ERROR, "SCAN UNHAPPY: PTS not matched!\n");
- }
-}
-
static int match_format(const char *name, AVFormatContext *pFormatCtx)
{
const char *p;
@@ -1049,163 +1019,365 @@ static int ffmpeg_seek_by_byte(AVFormatContext *pFormatCtx)
return false;
}
-static ImBuf *ffmpeg_fetchibuf(struct anim *anim, int position, IMB_Timecode_Type tc)
+static int64_t ffmpeg_get_seek_pos(struct anim *anim, int position)
{
- int64_t pts_to_search = 0;
- double frame_rate;
- double pts_time_base;
- int64_t st_time;
- struct anim_index *tc_index = 0;
- AVStream *v_st;
- int new_frame_index = 0; /* To quiet gcc barking... */
- int old_frame_index = 0; /* To quiet gcc barking... */
+ AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream];
+ double frame_rate = av_q2d(av_guess_frame_rate(anim->pFormatCtx, v_st, NULL));
+ int64_t st_time = anim->pFormatCtx->start_time;
+ int64_t pos = (int64_t)(position)*AV_TIME_BASE;
+ /* Step back half a time base position to make sure that we get the requested
+ * frame and not the one after it.
+ */
+ pos -= (AV_TIME_BASE / 2);
+ pos /= frame_rate;
- if (anim == NULL) {
- return 0;
- }
-
- av_log(anim->pFormatCtx, AV_LOG_DEBUG, "FETCH: pos=%d\n", position);
+ av_log(anim->pFormatCtx,
+ AV_LOG_DEBUG,
+ "NO INDEX seek pos = %" PRId64 ", st_time = %" PRId64 "\n",
+ pos,
+ (st_time != AV_NOPTS_VALUE) ? st_time : 0);
- if (tc != IMB_TC_NONE) {
- tc_index = IMB_anim_open_index(anim, tc);
+ if (pos < 0) {
+ pos = 0;
}
- v_st = anim->pFormatCtx->streams[anim->videoStream];
+ if (st_time != AV_NOPTS_VALUE) {
+ pos += st_time;
+ }
- frame_rate = av_q2d(av_guess_frame_rate(anim->pFormatCtx, v_st, NULL));
+ return pos;
+}
- st_time = anim->pFormatCtx->start_time;
- pts_time_base = av_q2d(v_st->time_base);
+/* This gives us an estimate of which pts our requested frame will have.
+ * Note that this might be off a bit in certain video files, but it should still be close enough.
+ */
+static int64_t ffmpeg_get_pts_to_search(struct anim *anim,
+ struct anim_index *tc_index,
+ int position)
+{
+ int64_t pts_to_search;
if (tc_index) {
- new_frame_index = IMB_indexer_get_frame_index(tc_index, position);
- old_frame_index = IMB_indexer_get_frame_index(tc_index, anim->curposition);
+ int new_frame_index = IMB_indexer_get_frame_index(tc_index, position);
pts_to_search = IMB_indexer_get_pts(tc_index, new_frame_index);
}
else {
- pts_to_search = (long long)floor(((double)position) / pts_time_base / frame_rate + 0.5);
+ int64_t st_time = anim->pFormatCtx->start_time;
+ AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream];
+ AVRational frame_rate = av_guess_frame_rate(anim->pFormatCtx, v_st, NULL);
+ AVRational time_base = v_st->time_base;
+
+ int64_t steps_per_frame = (frame_rate.den * time_base.den) / (frame_rate.num * time_base.num);
+ pts_to_search = position * steps_per_frame;
- if (st_time != AV_NOPTS_VALUE) {
- pts_to_search += st_time / pts_time_base / AV_TIME_BASE;
+ if (st_time != AV_NOPTS_VALUE && st_time != 0) {
+ int64_t start_frame = (double)st_time / AV_TIME_BASE * av_q2d(frame_rate);
+ pts_to_search += start_frame * steps_per_frame;
}
}
+ return pts_to_search;
+}
+
+/* Check if the pts will get us the same frame that we already have in memory from last decode. */
+static bool ffmpeg_pts_matches_last_frame(struct anim *anim, int64_t pts_to_search)
+{
+ if (anim->pFrame && anim->cur_frame_final) {
+ int64_t diff = pts_to_search - anim->cur_pts;
+ return diff >= 0 && diff < anim->pFrame->pkt_duration;
+ }
+
+ return false;
+}
+
+static bool ffmpeg_is_first_frame_decode(struct anim *anim, int position)
+{
+ return position == 0 && anim->cur_position == -1;
+}
+
+/* Decode frames one by one until its PTS matches pts_to_search. */
+static void ffmpeg_decode_video_frame_scan(struct anim *anim, int64_t pts_to_search)
+{
+ av_log(anim->pFormatCtx, AV_LOG_DEBUG, "FETCH: within current GOP\n");
av_log(anim->pFormatCtx,
AV_LOG_DEBUG,
- "FETCH: looking for PTS=%" PRId64 " (pts_timebase=%g, frame_rate=%g, st_time=%" PRId64
- ")\n",
- (int64_t)pts_to_search,
- pts_time_base,
- frame_rate,
- st_time);
+ "SCAN start: considering pts=%" PRId64 " in search of %" PRId64 "\n",
+ (int64_t)anim->cur_pts,
+ (int64_t)pts_to_search);
+
+ int64_t start_gop_frame = anim->cur_key_frame_pts;
+ bool scan_fuzzy = false;
- if (anim->last_frame && anim->last_pts <= pts_to_search && anim->next_pts > pts_to_search) {
+ while (anim->cur_pts < pts_to_search) {
av_log(anim->pFormatCtx,
AV_LOG_DEBUG,
- "FETCH: frame repeat: last: %" PRId64 " next: %" PRId64 "\n",
- (int64_t)anim->last_pts,
- (int64_t)anim->next_pts);
- IMB_refImBuf(anim->last_frame);
- anim->curposition = position;
- return anim->last_frame;
- }
+ " WHILE: pts=%" PRId64 " in search of %" PRId64 "\n",
+ (int64_t)anim->cur_pts,
+ (int64_t)pts_to_search);
+ if (!ffmpeg_decode_video_frame(anim)) {
+ break;
+ }
- if (position > anim->curposition + 1 && anim->preseek && !tc_index &&
- position - (anim->curposition + 1) < anim->preseek) {
- av_log(anim->pFormatCtx, AV_LOG_DEBUG, "FETCH: within preseek interval (no index)\n");
+ if (start_gop_frame != anim->cur_key_frame_pts) {
+ break;
+ }
- ffmpeg_decode_video_frame_scan(anim, pts_to_search);
+ if (anim->cur_pts < pts_to_search &&
+ anim->cur_pts + anim->pFrame->pkt_duration > pts_to_search) {
+ /* Our estimate of the pts was a bit off, but we have the frame we want. */
+ av_log(anim->pFormatCtx, AV_LOG_DEBUG, "SCAN fuzzy frame match\n");
+ scan_fuzzy = true;
+ break;
+ }
}
- else if (tc_index && IMB_indexer_can_scan(tc_index, old_frame_index, new_frame_index)) {
+
+ if (start_gop_frame != anim->cur_key_frame_pts) {
+ /* We went into an other GOP frame. This should never happen as we should have positioned us
+ * correctly by seeking into the GOP frame that contains the frame we want. */
av_log(anim->pFormatCtx,
- AV_LOG_DEBUG,
- "FETCH: within preseek interval "
- "(index tells us)\n");
+ AV_LOG_ERROR,
+ "SCAN failed: completely lost in stream, "
+ "bailing out at PTS=%" PRId64 ", searching for PTS=%" PRId64 "\n",
+ (int64_t)anim->cur_pts,
+ (int64_t)pts_to_search);
+ }
- ffmpeg_decode_video_frame_scan(anim, pts_to_search);
+ if (scan_fuzzy || anim->cur_pts == pts_to_search) {
+ av_log(anim->pFormatCtx, AV_LOG_DEBUG, "SCAN HAPPY: we found our PTS!\n");
}
- else if (position != anim->curposition + 1) {
- int64_t pos;
- int ret;
+ else {
+ av_log(anim->pFormatCtx, AV_LOG_ERROR, "SCAN UNHAPPY: PTS not matched!\n");
+ }
+}
- if (tc_index) {
- uint64_t dts;
+/* Wrapper over av_seek_frame(), for formats that doesn't have its own read_seek() or
+ * read_seek2() functions defined. When seeking in these formats, rule to seek to last
+ * necessary I-frame is not honored. It is not even guaranteed that I-frame, that must be
+ * decoded will be read. See https://trac.ffmpeg.org/ticket/1607 and
+ * https://developer.blender.org/T86944. */
+static int ffmpeg_generic_seek_workaround(struct anim *anim,
+ int64_t *requested_pos,
+ int64_t pts_to_search)
+{
+ AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream];
+ double frame_rate = av_q2d(av_guess_frame_rate(anim->pFormatCtx, v_st, NULL));
+ int64_t current_pos = *requested_pos;
+ int64_t offset = 0;
- pos = IMB_indexer_get_seek_pos(tc_index, new_frame_index);
- dts = IMB_indexer_get_seek_pos_dts(tc_index, new_frame_index);
+ int64_t cur_pts, prev_pts = -1;
- av_log(anim->pFormatCtx, AV_LOG_DEBUG, "TC INDEX seek pos = %" PRId64 "\n", pos);
- av_log(anim->pFormatCtx, AV_LOG_DEBUG, "TC INDEX seek dts = %" PRIu64 "\n", dts);
+ /* Step backward frame by frame until we find the key frame we are looking for. */
+ while (current_pos != 0) {
+ current_pos = *requested_pos - ((int64_t)(offset)*AV_TIME_BASE / frame_rate);
+ current_pos = max_ii(current_pos, 0);
- if (ffmpeg_seek_by_byte(anim->pFormatCtx)) {
- av_log(anim->pFormatCtx, AV_LOG_DEBUG, "... using BYTE pos\n");
+ /* Seek to timestamp. */
+ if (av_seek_frame(anim->pFormatCtx, -1, current_pos, AVSEEK_FLAG_BACKWARD) < 0) {
+ break;
+ }
- ret = av_seek_frame(anim->pFormatCtx, -1, pos, AVSEEK_FLAG_BYTE);
- av_update_cur_dts(anim->pFormatCtx, v_st, dts);
- }
- else {
- av_log(anim->pFormatCtx, AV_LOG_DEBUG, "... using DTS pos\n");
- ret = av_seek_frame(anim->pFormatCtx, anim->videoStream, dts, AVSEEK_FLAG_BACKWARD);
+ /* Read first video stream packet. */
+ AVPacket *read_packet = av_packet_alloc();
+ while (av_read_frame(anim->pFormatCtx, read_packet) >= 0) {
+ if (read_packet->stream_index == anim->videoStream) {
+ break;
}
+ av_packet_unref(read_packet);
}
- else {
- pos = (int64_t)position * AV_TIME_BASE / frame_rate;
- av_log(anim->pFormatCtx,
- AV_LOG_DEBUG,
- "NO INDEX seek pos = %" PRId64 ", st_time = %" PRId64 "\n",
- pos,
- (st_time != AV_NOPTS_VALUE) ? st_time : 0);
+ /* If this packet contains an I-frame, this could be the frame that we need. */
+ bool is_key_frame = read_packet->flags & AV_PKT_FLAG_KEY;
+ /* We need to check the packet timestamp as the key frame could be for a GOP forward in the the
+ * video stream. So if it has a larger timestamp than the frame we want, ignore it.
+ */
+ cur_pts = timestamp_from_pts_or_dts(read_packet->pts, read_packet->dts);
+ av_packet_free(&read_packet);
- if (pos < 0) {
- pos = 0;
+ if (is_key_frame) {
+ if (cur_pts <= pts_to_search) {
+ /* We found the I-frame we were looking for! */
+ break;
}
-
- if (st_time != AV_NOPTS_VALUE) {
- pos += st_time;
+ if (cur_pts == prev_pts) {
+ /* We got the same key frame packet twice.
+ * This probably means that we have hit the beginning of the stream. */
+ break;
}
+ }
- av_log(anim->pFormatCtx, AV_LOG_DEBUG, "NO INDEX final seek pos = %" PRId64 "\n", pos);
+ prev_pts = cur_pts;
+ offset++;
+ }
- ret = av_seek_frame(anim->pFormatCtx, -1, pos, AVSEEK_FLAG_BACKWARD);
- }
+ *requested_pos = current_pos;
- if (ret < 0) {
- av_log(anim->pFormatCtx,
- AV_LOG_ERROR,
- "FETCH: "
- "error while seeking to DTS = %" PRId64 " (frameno = %d, PTS = %" PRId64
- "): errcode = %d\n",
- pos,
- position,
- (int64_t)pts_to_search,
- ret);
+ /* Re-seek to timestamp that gave I-frame, so it can be read by decode function. */
+ return av_seek_frame(anim->pFormatCtx, -1, current_pos, AVSEEK_FLAG_BACKWARD);
+}
+
+/* Seek to last necessary key frame. */
+static int ffmpeg_seek_to_key_frame(struct anim *anim,
+ int position,
+ struct anim_index *tc_index,
+ int64_t pts_to_search)
+{
+ int64_t pos;
+ int ret;
+
+ if (tc_index) {
+ /* We can use timestamps generated from our indexer to seek. */
+ int new_frame_index = IMB_indexer_get_frame_index(tc_index, position);
+ int old_frame_index = IMB_indexer_get_frame_index(tc_index, anim->cur_position);
+
+ if (IMB_indexer_can_scan(tc_index, old_frame_index, new_frame_index)) {
+ /* No need to seek, return early. */
+ return 0;
}
+ uint64_t pts;
+ uint64_t dts;
+
+ pos = IMB_indexer_get_seek_pos(tc_index, new_frame_index);
+ pts = IMB_indexer_get_seek_pos_pts(tc_index, new_frame_index);
+ dts = IMB_indexer_get_seek_pos_dts(tc_index, new_frame_index);
+
+ anim->cur_key_frame_pts = timestamp_from_pts_or_dts(pts, dts);
- avcodec_flush_buffers(anim->pCodecCtx);
+ av_log(anim->pFormatCtx, AV_LOG_DEBUG, "TC INDEX seek pos = %" PRId64 "\n", pos);
+ av_log(anim->pFormatCtx, AV_LOG_DEBUG, "TC INDEX seek pts = %" PRIu64 "\n", pts);
+ av_log(anim->pFormatCtx, AV_LOG_DEBUG, "TC INDEX seek dts = %" PRIu64 "\n", dts);
- anim->next_pts = -1;
+ if (ffmpeg_seek_by_byte(anim->pFormatCtx)) {
+ av_log(anim->pFormatCtx, AV_LOG_DEBUG, "... using BYTE pos\n");
- if (anim->next_packet.stream_index == anim->videoStream) {
- av_free_packet(&anim->next_packet);
- anim->next_packet.stream_index = -1;
+ ret = av_seek_frame(anim->pFormatCtx, -1, pos, AVSEEK_FLAG_BYTE);
}
+ else {
+ av_log(anim->pFormatCtx, AV_LOG_DEBUG, "... using PTS pos\n");
+ ret = av_seek_frame(
+ anim->pFormatCtx, anim->videoStream, anim->cur_key_frame_pts, AVSEEK_FLAG_BACKWARD);
+ }
+ }
+ else {
+ /* We have to manually seek with ffmpeg to get to the key frame we want to start decoding from.
+ */
+ pos = ffmpeg_get_seek_pos(anim, position);
+ av_log(anim->pFormatCtx, AV_LOG_DEBUG, "NO INDEX final seek pos = %" PRId64 "\n", pos);
+
+ AVFormatContext *format_ctx = anim->pFormatCtx;
- /* memset(anim->pFrame, ...) ?? */
+ if (format_ctx->iformat->read_seek2 || format_ctx->iformat->read_seek) {
+ ret = av_seek_frame(anim->pFormatCtx, -1, pos, AVSEEK_FLAG_BACKWARD);
+ }
+ else {
+ ret = ffmpeg_generic_seek_workaround(anim, &pos, pts_to_search);
+ av_log(anim->pFormatCtx, AV_LOG_DEBUG, "Adjusted final seek pos = %" PRId64 "\n", pos);
+ }
if (ret >= 0) {
- ffmpeg_decode_video_frame_scan(anim, pts_to_search);
+ /* Double check if we need to seek and decode all packets. */
+ AVPacket *current_gop_start_packet = av_packet_alloc();
+ while (av_read_frame(anim->pFormatCtx, current_gop_start_packet) >= 0) {
+ if (current_gop_start_packet->stream_index == anim->videoStream) {
+ break;
+ }
+ av_packet_unref(current_gop_start_packet);
+ }
+ int64_t gop_pts = timestamp_from_pts_or_dts(current_gop_start_packet->pts,
+ current_gop_start_packet->dts);
+
+ av_packet_free(&current_gop_start_packet);
+ bool same_gop = gop_pts == anim->cur_key_frame_pts;
+
+ if (same_gop && position > anim->cur_position) {
+ /* Change back to our old frame position so we can simply continue decoding from there. */
+ AVPacket *temp = av_packet_alloc();
+ while (av_read_frame(anim->pFormatCtx, temp) >= 0) {
+ int64_t temp_pts = timestamp_from_pts_or_dts(temp->pts, temp->dts);
+ int64_t cur_pts = timestamp_from_pts_or_dts(anim->cur_packet->pts,
+ anim->cur_packet->dts);
+ if (temp->stream_index == anim->videoStream && temp_pts == cur_pts) {
+ break;
+ }
+ av_packet_unref(temp);
+ }
+ av_packet_free(&temp);
+ return 0;
+ }
+
+ anim->cur_key_frame_pts = gop_pts;
+ /* Seek back so we are at the correct position after we decoded a frame. */
+ av_seek_frame(anim->pFormatCtx, -1, pos, AVSEEK_FLAG_BACKWARD);
}
}
- else if (position == 0 && anim->curposition == -1) {
- /* first frame without seeking special case... */
- ffmpeg_decode_video_frame(anim);
+
+ if (ret < 0) {
+ av_log(anim->pFormatCtx,
+ AV_LOG_ERROR,
+ "FETCH: "
+ "error while seeking to DTS = %" PRId64 " (frameno = %d, PTS = %" PRId64
+ "): errcode = %d\n",
+ pos,
+ position,
+ pts_to_search,
+ ret);
}
- else {
+ /* Flush the internal buffers of ffmpeg. This needs to be done after seeking to avoid decoding
+ * errors. */
+ avcodec_flush_buffers(anim->pCodecCtx);
+
+ anim->cur_pts = -1;
+
+ if (anim->cur_packet->stream_index == anim->videoStream) {
+ av_packet_unref(anim->cur_packet);
+ anim->cur_packet->stream_index = -1;
+ }
+
+ return ret;
+}
+
+static ImBuf *ffmpeg_fetchibuf(struct anim *anim, int position, IMB_Timecode_Type tc)
+{
+ if (anim == NULL) {
+ return NULL;
+ }
+
+ av_log(anim->pFormatCtx, AV_LOG_DEBUG, "FETCH: pos=%d\n", position);
+
+ struct anim_index *tc_index = IMB_anim_open_index(anim, tc);
+ int64_t pts_to_search = ffmpeg_get_pts_to_search(anim, tc_index, position);
+ AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream];
+ double frame_rate = av_q2d(av_guess_frame_rate(anim->pFormatCtx, v_st, NULL));
+ double pts_time_base = av_q2d(v_st->time_base);
+ int64_t st_time = anim->pFormatCtx->start_time;
+
+ av_log(anim->pFormatCtx,
+ AV_LOG_DEBUG,
+ "FETCH: looking for PTS=%" PRId64 " (pts_timebase=%g, frame_rate=%g, st_time=%" PRId64
+ ")\n",
+ (int64_t)pts_to_search,
+ pts_time_base,
+ frame_rate,
+ st_time);
+
+ if (ffmpeg_pts_matches_last_frame(anim, pts_to_search)) {
+ av_log(anim->pFormatCtx,
+ AV_LOG_DEBUG,
+ "FETCH: frame repeat: pts: %" PRId64 "\n",
+ (int64_t)anim->cur_pts);
+ IMB_refImBuf(anim->cur_frame_final);
+ anim->cur_position = position;
+ return anim->cur_frame_final;
+ }
+
+ if (position == anim->cur_position + 1 || ffmpeg_is_first_frame_decode(anim, position)) {
av_log(anim->pFormatCtx, AV_LOG_DEBUG, "FETCH: no seek necessary, just continue...\n");
+ ffmpeg_decode_video_frame(anim);
+ }
+ else if (ffmpeg_seek_to_key_frame(anim, position, tc_index, pts_to_search) >= 0) {
+ ffmpeg_decode_video_frame_scan(anim, pts_to_search);
}
- IMB_freeImBuf(anim->last_frame);
+ IMB_freeImBuf(anim->cur_frame_final);
/* Certain versions of FFmpeg have a bug in libswscale which ends up in crash
* when destination buffer is not properly aligned. For example, this happens
@@ -1225,23 +1397,20 @@ static ImBuf *ffmpeg_fetchibuf(struct anim *anim, int position, IMB_Timecode_Typ
*
* The issue was reported to FFmpeg under ticket #8747 in the FFmpeg tracker
* and is fixed in the newer versions than 4.3.1. */
- anim->last_frame = IMB_allocImBuf(anim->x, anim->y, 32, 0);
- anim->last_frame->rect = MEM_mallocN_aligned((size_t)4 * anim->x * anim->y, 32, "ffmpeg ibuf");
- anim->last_frame->mall |= IB_rect;
+ anim->cur_frame_final = IMB_allocImBuf(anim->x, anim->y, 32, 0);
+ anim->cur_frame_final->rect = MEM_mallocN_aligned(
+ (size_t)4 * anim->x * anim->y, 32, "ffmpeg ibuf");
+ anim->cur_frame_final->mall |= IB_rect;
- anim->last_frame->rect_colorspace = colormanage_colorspace_get_named(anim->colorspace);
+ anim->cur_frame_final->rect_colorspace = colormanage_colorspace_get_named(anim->colorspace);
ffmpeg_postprocess(anim);
- anim->last_pts = anim->next_pts;
-
- ffmpeg_decode_video_frame(anim);
-
- anim->curposition = position;
+ anim->cur_position = position;
- IMB_refImBuf(anim->last_frame);
+ IMB_refImBuf(anim->cur_frame_final);
- return anim->last_frame;
+ return anim->cur_frame_final;
}
static void free_anim_ffmpeg(struct anim *anim)
@@ -1251,32 +1420,30 @@ static void free_anim_ffmpeg(struct anim *anim)
}
if (anim->pCodecCtx) {
- avcodec_close(anim->pCodecCtx);
+ avcodec_free_context(&anim->pCodecCtx);
avformat_close_input(&anim->pFormatCtx);
+ av_packet_free(&anim->cur_packet);
- /* Special case here: pFrame could share pointers with codec,
- * so in order to avoid double-free we don't use av_frame_free()
- * to free the frame.
- *
- * Could it be a bug in FFmpeg?
- */
- av_free(anim->pFrame);
+ av_frame_free(&anim->pFrame);
if (!need_aligned_ffmpeg_buffer(anim)) {
/* If there's no need for own aligned buffer it means that FFmpeg's
* frame shares the same buffer as temporary ImBuf. In this case we
* should not free the buffer when freeing the FFmpeg buffer.
*/
- avpicture_fill((AVPicture *)anim->pFrameRGB, NULL, AV_PIX_FMT_RGBA, anim->x, anim->y);
+ av_image_fill_arrays(anim->pFrameRGB->data,
+ anim->pFrameRGB->linesize,
+ NULL,
+ AV_PIX_FMT_RGBA,
+ anim->x,
+ anim->y,
+ 1);
}
av_frame_free(&anim->pFrameRGB);
av_frame_free(&anim->pFrameDeinterlaced);
sws_freeContext(anim->img_convert_ctx);
- IMB_freeImBuf(anim->last_frame);
- if (anim->next_packet.stream_index != -1) {
- av_free_packet(&anim->next_packet);
- }
+ IMB_freeImBuf(anim->cur_frame_final);
}
anim->duration_in_frames = 0;
}
@@ -1410,13 +1577,13 @@ struct ImBuf *IMB_anim_absolute(struct anim *anim,
an_stringenc(anim->name, head, tail, digits, pic);
ibuf = IMB_loadiffname(anim->name, IB_rect, anim->colorspace);
if (ibuf) {
- anim->curposition = position;
+ anim->cur_position = position;
}
break;
case ANIM_MOVIE:
ibuf = movie_fetchibuf(anim, position);
if (ibuf) {
- anim->curposition = position;
+ anim->cur_position = position;
IMB_convert_rgba_to_abgr(ibuf);
}
break;
@@ -1424,7 +1591,7 @@ struct ImBuf *IMB_anim_absolute(struct anim *anim,
case ANIM_AVI:
ibuf = avi_fetchibuf(anim, position);
if (ibuf) {
- anim->curposition = position;
+ anim->cur_position = position;
}
break;
#endif
@@ -1432,7 +1599,7 @@ struct ImBuf *IMB_anim_absolute(struct anim *anim,
case ANIM_FFMPEG:
ibuf = ffmpeg_fetchibuf(anim, position, tc);
if (ibuf) {
- anim->curposition = position;
+ anim->cur_position = position;
}
filter_y = 0; /* done internally */
break;
@@ -1443,7 +1610,7 @@ struct ImBuf *IMB_anim_absolute(struct anim *anim,
if (filter_y) {
IMB_filtery(ibuf);
}
- BLI_snprintf(ibuf->name, sizeof(ibuf->name), "%s.%04d", anim->name, anim->curposition + 1);
+ BLI_snprintf(ibuf->name, sizeof(ibuf->name), "%s.%04d", anim->name, anim->cur_position + 1);
}
return ibuf;
}
@@ -1498,16 +1665,6 @@ bool IMB_anim_get_fps(struct anim *anim, short *frs_sec, float *frs_sec_base, bo
return false;
}
-void IMB_anim_set_preseek(struct anim *anim, int preseek)
-{
- anim->preseek = preseek;
-}
-
-int IMB_anim_get_preseek(struct anim *anim)
-{
- return anim->preseek;
-}
-
int IMB_anim_get_image_width(struct anim *anim)
{
return anim->x;
diff --git a/source/blender/imbuf/intern/bmp.c b/source/blender/imbuf/intern/bmp.c
index a5c558fc216..ad72f373d12 100644
--- a/source/blender/imbuf/intern/bmp.c
+++ b/source/blender/imbuf/intern/bmp.c
@@ -74,7 +74,7 @@ typedef struct BMPHEADER {
static bool checkbmp(const uchar *mem, const size_t size)
{
- if (size < BMP_FILEHEADER_SIZE) {
+ if (size < (BMP_FILEHEADER_SIZE + sizeof(BMPINFOHEADER))) {
return false;
}
@@ -113,56 +113,57 @@ bool imb_is_a_bmp(const uchar *buf, size_t size)
static size_t imb_bmp_calc_row_size_in_bytes(size_t x, size_t depth)
{
- if (depth <= 8) {
- return (depth * x + 31) / 32 * 4;
- }
- return (depth >> 3) * x;
+ /* https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader#calculating-surface-stride
+ */
+ return (((x * depth) + 31) & ~31) >> 3;
}
ImBuf *imb_bmp_decode(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE])
{
ImBuf *ibuf = NULL;
BMPINFOHEADER bmi;
- int x, y, depth, ibuf_depth, skip;
+ int ibuf_depth;
const uchar *bmp;
uchar *rect;
ushort col;
- double xppm, yppm;
bool top_to_bottom = false;
- (void)size; /* unused */
-
if (checkbmp(mem, size) == 0) {
return NULL;
}
colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_BYTE);
- const size_t pixel_data_offset = LITTLE_LONG(*(int *)(mem + 10));
- bmp = mem + pixel_data_offset;
+ /* For systems where an int needs to be 4 bytes aligned. */
+ memcpy(&bmi, mem + BMP_FILEHEADER_SIZE, sizeof(bmi));
+
+ const size_t palette_offset = (size_t)BMP_FILEHEADER_SIZE + LITTLE_LONG(bmi.biSize);
+ const int depth = LITTLE_SHORT(bmi.biBitCount);
+ const int xppm = LITTLE_LONG(bmi.biXPelsPerMeter);
+ const int yppm = LITTLE_LONG(bmi.biYPelsPerMeter);
+ const int x = LITTLE_LONG(bmi.biWidth);
+ int y = LITTLE_LONG(bmi.biHeight);
- if (CHECK_HEADER_FIELD_BMP(mem)) {
- /* skip fileheader */
- mem += BMP_FILEHEADER_SIZE;
+ /* Negative height means bitmap is stored top-to-bottom. */
+ if (y < 0) {
+ y = -y;
+ top_to_bottom = true;
}
- else {
+
+ /* Validate and cross-check offsets and sizes. */
+ if (x < 1 ||
+ !(depth == 1 || depth == 4 || depth == 8 || depth == 16 || depth == 24 || depth == 32)) {
return NULL;
}
- /* for systems where an int needs to be 4 bytes aligned */
- memcpy(&bmi, mem, sizeof(bmi));
-
- skip = LITTLE_LONG(bmi.biSize);
- x = LITTLE_LONG(bmi.biWidth);
- y = LITTLE_LONG(bmi.biHeight);
- depth = LITTLE_SHORT(bmi.biBitCount);
- xppm = LITTLE_LONG(bmi.biXPelsPerMeter);
- yppm = LITTLE_LONG(bmi.biYPelsPerMeter);
-
+ const size_t pixel_data_offset = (size_t)LITTLE_LONG(*(int *)(mem + 10));
+ const size_t header_bytes = BMP_FILEHEADER_SIZE + sizeof(BMPINFOHEADER);
+ const size_t num_actual_data_bytes = size - pixel_data_offset;
const size_t row_size_in_bytes = imb_bmp_calc_row_size_in_bytes(x, depth);
const size_t num_expected_data_bytes = row_size_in_bytes * y;
- const size_t num_actual_data_bytes = size - pixel_data_offset;
- if (num_actual_data_bytes < num_expected_data_bytes) {
+ if (num_actual_data_bytes < num_expected_data_bytes || num_actual_data_bytes > size ||
+ pixel_data_offset < header_bytes || pixel_data_offset > (size - num_expected_data_bytes) ||
+ palette_offset < header_bytes || palette_offset > pixel_data_offset) {
return NULL;
}
@@ -173,14 +174,10 @@ ImBuf *imb_bmp_decode(const uchar *mem, size_t size, int flags, char colorspace[
ibuf_depth = depth;
}
- if (y < 0) {
- /* Negative height means bitmap is stored top-to-bottom... */
- y = -y;
- top_to_bottom = true;
- }
+ bmp = mem + pixel_data_offset;
#if 0
- printf("skip: %d, x: %d y: %d, depth: %d (%x)\n", skip, x, y, depth, bmi.biBitCount);
+ printf("palette_offset: %d, x: %d y: %d, depth: %d\n", palette_offset, x, y, depth);
#endif
if (flags & IB_test) {
@@ -195,7 +192,7 @@ ImBuf *imb_bmp_decode(const uchar *mem, size_t size, int flags, char colorspace[
rect = (uchar *)ibuf->rect;
if (depth <= 8) {
- const char(*palette)[4] = (void *)(mem + skip);
+ const char(*palette)[4] = (const char(*)[4])(mem + palette_offset);
const int startmask = ((1 << depth) - 1) << 8;
for (size_t i = y; i > 0; i--) {
int index;
diff --git a/source/blender/imbuf/intern/cineon/cineon_dpx.c b/source/blender/imbuf/intern/cineon/cineon_dpx.c
index de54e6dab9d..91d7b9a8b9e 100644
--- a/source/blender/imbuf/intern/cineon/cineon_dpx.c
+++ b/source/blender/imbuf/intern/cineon/cineon_dpx.c
@@ -198,10 +198,10 @@ ImBuf *imb_load_cineon(const unsigned char *mem,
int flags,
char colorspace[IM_MAX_SPACE])
{
- if (imb_is_a_cineon(mem, size)) {
- return imb_load_dpx_cineon(mem, size, 1, flags, colorspace);
+ if (!imb_is_a_cineon(mem, size)) {
+ return NULL;
}
- return NULL;
+ return imb_load_dpx_cineon(mem, size, 1, flags, colorspace);
}
bool imb_save_dpx(struct ImBuf *buf, const char *filepath, int flags)
@@ -219,8 +219,8 @@ ImBuf *imb_load_dpx(const unsigned char *mem,
int flags,
char colorspace[IM_MAX_SPACE])
{
- if (imb_is_a_dpx(mem, size)) {
- return imb_load_dpx_cineon(mem, size, 0, flags, colorspace);
+ if (!imb_is_a_dpx(mem, size)) {
+ return NULL;
}
- return NULL;
+ return imb_load_dpx_cineon(mem, size, 0, flags, colorspace);
}
diff --git a/source/blender/imbuf/intern/colormanagement.c b/source/blender/imbuf/intern/colormanagement.c
index fc0b99a82fa..71e513fb405 100644
--- a/source/blender/imbuf/intern/colormanagement.c
+++ b/source/blender/imbuf/intern/colormanagement.c
@@ -1409,7 +1409,7 @@ bool IMB_colormanagement_space_name_is_data(const char *name)
return (colorspace && colorspace->is_data);
}
-const float *IMB_colormangement_get_xyz_to_rgb()
+const float *IMB_colormanagement_get_xyz_to_rgb()
{
return &imbuf_xyz_to_rgb[0][0];
}
@@ -3539,12 +3539,11 @@ typedef struct PartialThreadData {
int xmin, ymin, xmax;
} PartialThreadData;
-static void partial_buffer_update_rect_thread_do(void *data_v,
- int start_scanline,
- int num_scanlines)
+static void partial_buffer_update_rect_thread_do(void *data_v, int scanline)
{
PartialThreadData *data = (PartialThreadData *)data_v;
- int ymin = data->ymin + start_scanline;
+ int ymin = data->ymin + scanline;
+ const int num_scanlines = 1;
partial_buffer_update_rect(data->ibuf,
data->display_buffer,
data->linear_buffer,
diff --git a/source/blender/imbuf/intern/colormanagement_inline.c b/source/blender/imbuf/intern/colormanagement_inline.c
index e93fdd51040..c304ad8d8e5 100644
--- a/source/blender/imbuf/intern/colormanagement_inline.c
+++ b/source/blender/imbuf/intern/colormanagement_inline.c
@@ -55,12 +55,12 @@ unsigned char IMB_colormanagement_get_luminance_byte(const unsigned char rgb[3])
return unit_float_to_uchar_clamp(val);
}
-void IMB_colormangement_xyz_to_rgb(float rgb[3], const float xyz[3])
+void IMB_colormanagement_xyz_to_rgb(float rgb[3], const float xyz[3])
{
mul_v3_m3v3(rgb, imbuf_xyz_to_rgb, xyz);
}
-void IMB_colormangement_rgb_to_xyz(float xyz[3], const float rgb[3])
+void IMB_colormanagement_rgb_to_xyz(float xyz[3], const float rgb[3])
{
mul_v3_m3v3(xyz, imbuf_rgb_to_xyz, rgb);
}
diff --git a/source/blender/imbuf/intern/dds/ColorBlock.cpp b/source/blender/imbuf/intern/dds/ColorBlock.cpp
index 3b1b970fb3c..a05c7e2a70d 100644
--- a/source/blender/imbuf/intern/dds/ColorBlock.cpp
+++ b/source/blender/imbuf/intern/dds/ColorBlock.cpp
@@ -46,11 +46,6 @@ inline static uint colorDistance(Color32 c0, Color32 c1)
}
#endif
-/** Default constructor. */
-ColorBlock::ColorBlock()
-{
-}
-
/** Init the color block from an array of colors. */
ColorBlock::ColorBlock(const uint *linearImage)
{
diff --git a/source/blender/imbuf/intern/dds/ColorBlock.h b/source/blender/imbuf/intern/dds/ColorBlock.h
index 9c3d73bbb0d..158695cfbf3 100644
--- a/source/blender/imbuf/intern/dds/ColorBlock.h
+++ b/source/blender/imbuf/intern/dds/ColorBlock.h
@@ -34,7 +34,7 @@
/** Uncompressed 4x4 color block. */
struct ColorBlock {
- ColorBlock();
+ ColorBlock() = default;
ColorBlock(const uint *linearImage);
ColorBlock(const ColorBlock &block);
ColorBlock(const Image *img, uint x, uint y);
diff --git a/source/blender/imbuf/intern/dds/DirectDrawSurface.cpp b/source/blender/imbuf/intern/dds/DirectDrawSurface.cpp
index 2a36946df8f..b665996b18f 100644
--- a/source/blender/imbuf/intern/dds/DirectDrawSurface.cpp
+++ b/source/blender/imbuf/intern/dds/DirectDrawSurface.cpp
@@ -890,10 +890,6 @@ DirectDrawSurface::DirectDrawSurface(unsigned char *mem, uint size) : stream(mem
}
}
-DirectDrawSurface::~DirectDrawSurface()
-{
-}
-
bool DirectDrawSurface::isValid() const
{
if (header.fourcc != FOURCC_DDS || header.size != 124) {
diff --git a/source/blender/imbuf/intern/dds/DirectDrawSurface.h b/source/blender/imbuf/intern/dds/DirectDrawSurface.h
index b6c2d1962e2..b63d705dadf 100644
--- a/source/blender/imbuf/intern/dds/DirectDrawSurface.h
+++ b/source/blender/imbuf/intern/dds/DirectDrawSurface.h
@@ -136,7 +136,6 @@ struct DDSHeader {
class DirectDrawSurface {
public:
DirectDrawSurface(unsigned char *mem, uint size);
- ~DirectDrawSurface();
bool isValid() const;
bool isSupported() const;
diff --git a/source/blender/imbuf/intern/divers.c b/source/blender/imbuf/intern/divers.c
index 5f580449e12..47712456014 100644
--- a/source/blender/imbuf/intern/divers.c
+++ b/source/blender/imbuf/intern/divers.c
@@ -536,13 +536,12 @@ typedef struct FloatToFloatThreadData {
int stride_from;
} FloatToFloatThreadData;
-static void imb_buffer_float_from_float_thread_do(void *data_v,
- int start_scanline,
- int num_scanlines)
+static void imb_buffer_float_from_float_thread_do(void *data_v, int scanline)
{
+ const int num_scanlines = 1;
FloatToFloatThreadData *data = (FloatToFloatThreadData *)data_v;
- size_t offset_from = ((size_t)start_scanline) * data->stride_from * data->channels_from;
- size_t offset_to = ((size_t)start_scanline) * data->stride_to * data->channels_from;
+ size_t offset_from = ((size_t)scanline) * data->stride_from * data->channels_from;
+ size_t offset_to = ((size_t)scanline) * data->stride_to * data->channels_from;
IMB_buffer_float_from_float(data->rect_to + offset_to,
data->rect_from + offset_from,
data->channels_from,
diff --git a/source/blender/imbuf/intern/imageprocess.c b/source/blender/imbuf/intern/imageprocess.c
index 7ada0130059..a9b6e2bbb88 100644
--- a/source/blender/imbuf/intern/imageprocess.c
+++ b/source/blender/imbuf/intern/imageprocess.c
@@ -127,6 +127,22 @@ void bicubic_interpolation(ImBuf *in, ImBuf *out, float u, float v, int xout, in
/** \name Bi-Linear Interpolation
* \{ */
+BLI_INLINE void bilinear_interpolation_color_fl(
+ struct ImBuf *in, unsigned char UNUSED(outI[4]), float outF[4], float u, float v)
+{
+ BLI_assert(outF);
+ BLI_assert(in->rect_float);
+ BLI_bilinear_interpolation_fl(in->rect_float, outF, in->x, in->y, 4, u, v);
+}
+
+BLI_INLINE void bilinear_interpolation_color_char(
+ struct ImBuf *in, unsigned char outI[4], float UNUSED(outF[4]), float u, float v)
+{
+ BLI_assert(outI);
+ BLI_assert(in->rect);
+ BLI_bilinear_interpolation_char((unsigned char *)in->rect, outI, in->x, in->y, 4, u, v);
+}
+
void bilinear_interpolation_color(
struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v)
{
@@ -238,60 +254,58 @@ void bilinear_interpolation(ImBuf *in, ImBuf *out, float u, float v, int xout, i
/** \name Nearest Interpolation
* \{ */
-/* function assumes out to be zero'ed, only does RGBA */
-void nearest_interpolation_color(
- struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v)
+/* functions assumes out to be zero'ed, only does RGBA */
+BLI_INLINE void nearest_interpolation_color_char(
+ struct ImBuf *in, unsigned char outI[4], float UNUSED(outF[4]), float u, float v)
{
- const float *dataF;
- unsigned char *dataI;
- int y1, x1;
-
+ BLI_assert(outI);
+ BLI_assert(in->rect);
/* ImBuf in must have a valid rect or rect_float, assume this is already checked */
+ int x1 = (int)(u);
+ int y1 = (int)(v);
- x1 = (int)(u);
- y1 = (int)(v);
+ /* sample area entirely outside image? */
+ if (x1 < 0 || x1 >= in->x || y1 < 0 || y1 >= in->y) {
+ outI[0] = outI[1] = outI[2] = outI[3] = 0;
+ return;
+ }
+
+ const size_t offset = (in->x * y1 + x1) * 4;
+ const unsigned char *dataI = (unsigned char *)in->rect + offset;
+ outI[0] = dataI[0];
+ outI[1] = dataI[1];
+ outI[2] = dataI[2];
+ outI[3] = dataI[3];
+}
+
+BLI_INLINE void nearest_interpolation_color_fl(
+ struct ImBuf *in, unsigned char UNUSED(outI[4]), float outF[4], float u, float v)
+{
+ BLI_assert(outF);
+ BLI_assert(in->rect_float);
+ /* ImBuf in must have a valid rect or rect_float, assume this is already checked */
+ int x1 = (int)(u);
+ int y1 = (int)(v);
/* sample area entirely outside image? */
- if (x1 < 0 || x1 > in->x - 1 || y1 < 0 || y1 > in->y - 1) {
- if (outI) {
- outI[0] = outI[1] = outI[2] = outI[3] = 0;
- }
- if (outF) {
- outF[0] = outF[1] = outF[2] = outF[3] = 0.0f;
- }
+ if (x1 < 0 || x1 >= in->x || y1 < 0 || y1 >= in->y) {
+ zero_v4(outF);
return;
}
- /* sample including outside of edges of image */
- if (x1 < 0 || y1 < 0) {
- if (outI) {
- outI[0] = 0;
- outI[1] = 0;
- outI[2] = 0;
- outI[3] = 0;
- }
- if (outF) {
- outF[0] = 0.0f;
- outF[1] = 0.0f;
- outF[2] = 0.0f;
- outF[3] = 0.0f;
- }
+ const size_t offset = (in->x * y1 + x1) * 4;
+ const float *dataF = in->rect_float + offset;
+ copy_v4_v4(outF, dataF);
+}
+
+void nearest_interpolation_color(
+ struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v)
+{
+ if (outF) {
+ nearest_interpolation_color_fl(in, outI, outF, u, v);
}
else {
- dataI = (unsigned char *)in->rect + ((size_t)in->x) * y1 * 4 + 4 * x1;
- if (outI) {
- outI[0] = dataI[0];
- outI[1] = dataI[1];
- outI[2] = dataI[2];
- outI[3] = dataI[3];
- }
- dataF = in->rect_float + ((size_t)in->x) * y1 * 4 + 4 * x1;
- if (outF) {
- outF[0] = dataF[0];
- outF[1] = dataF[1];
- outF[2] = dataF[2];
- outF[3] = dataF[3];
- }
+ nearest_interpolation_color_char(in, outI, outF, u, v);
}
}
@@ -350,6 +364,141 @@ void nearest_interpolation(ImBuf *in, ImBuf *out, float u, float v, int xout, in
}
/* -------------------------------------------------------------------- */
+/** \name Image transform
+ * \{ */
+typedef struct TransformUserData {
+ ImBuf *src;
+ ImBuf *dst;
+ float start_uv[2];
+ float add_x[2];
+ float add_y[2];
+ rctf src_crop;
+} TransformUserData;
+
+static void imb_transform_calc_start_uv(const float transform_matrix[3][3], float r_start_uv[2])
+{
+ float orig[2];
+ orig[0] = 0.0f;
+ orig[1] = 0.0f;
+ mul_v2_m3v2(r_start_uv, transform_matrix, orig);
+}
+
+static void imb_transform_calc_add_x(const float transform_matrix[3][3],
+ const float start_uv[2],
+ const int width,
+ float r_add_x[2])
+{
+ float uv_max_x[2];
+ uv_max_x[0] = width;
+ uv_max_x[1] = 0.0f;
+ mul_v2_m3v2(r_add_x, transform_matrix, uv_max_x);
+ sub_v2_v2(r_add_x, start_uv);
+ mul_v2_fl(r_add_x, 1.0f / width);
+}
+
+static void imb_transform_calc_add_y(const float transform_matrix[3][3],
+ const float start_uv[2],
+ const int height,
+ float r_add_y[2])
+{
+ float uv_max_y[2];
+ uv_max_y[0] = 0.0f;
+ uv_max_y[1] = height;
+ mul_v2_m3v2(r_add_y, transform_matrix, uv_max_y);
+ sub_v2_v2(r_add_y, start_uv);
+ mul_v2_fl(r_add_y, 1.0f / height);
+}
+
+typedef void (*InterpolationColorFunction)(
+ struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v);
+BLI_INLINE void imb_transform_scanlines(const TransformUserData *user_data,
+ int scanline,
+ InterpolationColorFunction interpolation)
+{
+ const int width = user_data->dst->x;
+
+ float uv[2];
+ madd_v2_v2v2fl(uv, user_data->start_uv, user_data->add_y, scanline);
+
+ unsigned char *outI = NULL;
+ float *outF = NULL;
+ pixel_from_buffer(user_data->dst, &outI, &outF, 0, scanline);
+
+ for (int xi = 0; xi < width; xi++) {
+ if (uv[0] >= user_data->src_crop.xmin && uv[0] < user_data->src_crop.xmax &&
+ uv[1] >= user_data->src_crop.ymin && uv[1] < user_data->src_crop.ymax) {
+ interpolation(user_data->src, outI, outF, uv[0], uv[1]);
+ }
+ add_v2_v2(uv, user_data->add_x);
+ if (outI) {
+ outI += 4;
+ }
+ if (outF) {
+ outF += 4;
+ }
+ }
+}
+
+static void imb_transform_nearest_scanlines(void *custom_data, int scanline)
+{
+ const TransformUserData *user_data = custom_data;
+ InterpolationColorFunction interpolation = NULL;
+ if (user_data->dst->rect_float) {
+ interpolation = nearest_interpolation_color_fl;
+ }
+ else {
+ interpolation = nearest_interpolation_color_char;
+ }
+ imb_transform_scanlines(user_data, scanline, interpolation);
+}
+
+static void imb_transform_bilinear_scanlines(void *custom_data, int scanline)
+{
+ const TransformUserData *user_data = custom_data;
+ InterpolationColorFunction interpolation = NULL;
+ if (user_data->dst->rect_float) {
+ interpolation = bilinear_interpolation_color_fl;
+ }
+ else if (user_data->dst->rect) {
+ interpolation = bilinear_interpolation_color_char;
+ }
+ imb_transform_scanlines(user_data, scanline, interpolation);
+}
+
+static ScanlineThreadFunc imb_transform_scanline_func(const eIMBInterpolationFilterMode filter)
+{
+ ScanlineThreadFunc scanline_func = NULL;
+ switch (filter) {
+ case IMB_FILTER_NEAREST:
+ scanline_func = imb_transform_nearest_scanlines;
+ break;
+ case IMB_FILTER_BILINEAR:
+ scanline_func = imb_transform_bilinear_scanlines;
+ break;
+ }
+ return scanline_func;
+}
+
+void IMB_transform(struct ImBuf *src,
+ struct ImBuf *dst,
+ float transform_matrix[3][3],
+ struct rctf *src_crop,
+ const eIMBInterpolationFilterMode filter)
+{
+ TransformUserData user_data;
+ user_data.src = src;
+ user_data.dst = dst;
+ user_data.src_crop = *src_crop;
+ imb_transform_calc_start_uv(transform_matrix, user_data.start_uv);
+ imb_transform_calc_add_x(transform_matrix, user_data.start_uv, src->x, user_data.add_x);
+ imb_transform_calc_add_y(transform_matrix, user_data.start_uv, src->y, user_data.add_y);
+ ScanlineThreadFunc scanline_func = imb_transform_scanline_func(filter);
+ IMB_processor_apply_threaded_scanlines(dst->y, scanline_func, &user_data);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Threaded Image Processing
* \{ */
@@ -374,7 +523,7 @@ void IMB_processor_apply_threaded(
int total_tasks = (buffer_lines + lines_per_task - 1) / lines_per_task;
int i, start_line;
- task_pool = BLI_task_pool_create(do_thread, TASK_PRIORITY_LOW);
+ task_pool = BLI_task_pool_create(do_thread, TASK_PRIORITY_LOW, TASK_ISOLATION_ON);
handles = MEM_callocN(handle_size * total_tasks, "processor apply threaded handles");
@@ -409,41 +558,28 @@ void IMB_processor_apply_threaded(
typedef struct ScanlineGlobalData {
void *custom_data;
ScanlineThreadFunc do_thread;
- int scanlines_per_task;
- int total_scanlines;
} ScanlineGlobalData;
-static void processor_apply_scanline_func(TaskPool *__restrict pool, void *taskdata)
+static void processor_apply_parallel(void *__restrict userdata,
+ const int scanline,
+ const TaskParallelTLS *__restrict UNUSED(tls))
{
- ScanlineGlobalData *data = BLI_task_pool_user_data(pool);
- int start_scanline = POINTER_AS_INT(taskdata);
- int num_scanlines = min_ii(data->scanlines_per_task, data->total_scanlines - start_scanline);
- data->do_thread(data->custom_data, start_scanline, num_scanlines);
+ ScanlineGlobalData *data = userdata;
+ data->do_thread(data->custom_data, scanline);
}
void IMB_processor_apply_threaded_scanlines(int total_scanlines,
ScanlineThreadFunc do_thread,
void *custom_data)
{
- const int scanlines_per_task = 64;
- ScanlineGlobalData data;
- data.custom_data = custom_data;
- data.do_thread = do_thread;
- data.scanlines_per_task = scanlines_per_task;
- data.total_scanlines = total_scanlines;
- const int total_tasks = (total_scanlines + scanlines_per_task - 1) / scanlines_per_task;
- TaskPool *task_pool = BLI_task_pool_create(&data, TASK_PRIORITY_LOW);
- for (int i = 0, start_line = 0; i < total_tasks; i++) {
- BLI_task_pool_push(
- task_pool, processor_apply_scanline_func, POINTER_FROM_INT(start_line), false, NULL);
- start_line += scanlines_per_task;
- }
-
- /* work and wait until tasks are done */
- BLI_task_pool_work_and_wait(task_pool);
-
- /* Free memory. */
- BLI_task_pool_free(task_pool);
+ TaskParallelSettings settings;
+ ScanlineGlobalData data = {
+ .do_thread = do_thread,
+ .custom_data = custom_data,
+ };
+
+ BLI_parallel_range_settings_defaults(&settings);
+ BLI_task_parallel_range(0, total_scanlines, &data, processor_apply_parallel, &settings);
}
/** \} */
diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c
index d331cfd533a..a530acb15b0 100644
--- a/source/blender/imbuf/intern/indexer.c
+++ b/source/blender/imbuf/intern/indexer.c
@@ -24,6 +24,7 @@
#include "MEM_guardedalloc.h"
+#include "BLI_endian_defines.h"
#include "BLI_endian_switch.h"
#include "BLI_fileops.h"
#include "BLI_ghash.h"
@@ -40,17 +41,16 @@
#include "IMB_indexer.h"
#include "imbuf.h"
-#include "BKE_global.h"
-
#ifdef WITH_AVI
# include "AVI_avi.h"
#endif
#ifdef WITH_FFMPEG
# include "ffmpeg_compat.h"
+# include <libavutil/imgutils.h>
#endif
-static const char magic[] = "BlenMIdx";
+static const char binary_header_str[] = "BlenMIdx";
static const char temp_ext[] = "_part";
static const int proxy_sizes[] = {IMB_PROXY_25, IMB_PROXY_50, IMB_PROXY_75, IMB_PROXY_100};
@@ -65,7 +65,7 @@ static int tc_types[] = {
};
#endif
-#define INDEX_FILE_VERSION 1
+#define INDEX_FILE_VERSION 2
/* ----------------------------------------------------------------------
* - time code index functions
@@ -96,16 +96,25 @@ anim_index_builder *IMB_index_builder_create(const char *name)
return NULL;
}
- fprintf(rv->fp, "%s%c%.3d", magic, (ENDIAN_ORDER == B_ENDIAN) ? 'V' : 'v', INDEX_FILE_VERSION);
+ fprintf(rv->fp,
+ "%s%c%.3d",
+ binary_header_str,
+ (ENDIAN_ORDER == B_ENDIAN) ? 'V' : 'v',
+ INDEX_FILE_VERSION);
return rv;
}
-void IMB_index_builder_add_entry(
- anim_index_builder *fp, int frameno, uint64_t seek_pos, uint64_t seek_pos_dts, uint64_t pts)
+void IMB_index_builder_add_entry(anim_index_builder *fp,
+ int frameno,
+ uint64_t seek_pos,
+ uint64_t seek_pos_pts,
+ uint64_t seek_pos_dts,
+ uint64_t pts)
{
fwrite(&frameno, sizeof(int), 1, fp->fp);
fwrite(&seek_pos, sizeof(uint64_t), 1, fp->fp);
+ fwrite(&seek_pos_pts, sizeof(uint64_t), 1, fp->fp);
fwrite(&seek_pos_dts, sizeof(uint64_t), 1, fp->fp);
fwrite(&pts, sizeof(uint64_t), 1, fp->fp);
}
@@ -115,6 +124,7 @@ void IMB_index_builder_proc_frame(anim_index_builder *fp,
int data_size,
int frameno,
uint64_t seek_pos,
+ uint64_t seek_pos_pts,
uint64_t seek_pos_dts,
uint64_t pts)
{
@@ -122,13 +132,14 @@ void IMB_index_builder_proc_frame(anim_index_builder *fp,
anim_index_entry e;
e.frameno = frameno;
e.seek_pos = seek_pos;
+ e.seek_pos_pts = seek_pos_pts;
e.seek_pos_dts = seek_pos_dts;
e.pts = pts;
fp->proc_frame(fp, buffer, data_size, &e);
}
else {
- IMB_index_builder_add_entry(fp, frameno, seek_pos, seek_pos_dts, pts);
+ IMB_index_builder_add_entry(fp, frameno, seek_pos, seek_pos_pts, seek_pos_dts, pts);
}
}
@@ -159,22 +170,26 @@ struct anim_index *IMB_indexer_open(const char *name)
int i;
if (!fp) {
+ fprintf(stderr, "Couldn't open indexer file: %s\n", name);
return NULL;
}
if (fread(header, 12, 1, fp) != 1) {
+ fprintf(stderr, "Couldn't read indexer file: %s\n", name);
fclose(fp);
return NULL;
}
header[12] = 0;
- if (memcmp(header, magic, 8) != 0) {
+ if (memcmp(header, binary_header_str, 8) != 0) {
+ fprintf(stderr, "Error reading %s: Binary file type string missmatch\n", name);
fclose(fp);
return NULL;
}
if (atoi(header + 9) != INDEX_FILE_VERSION) {
+ fprintf(stderr, "Error reading %s: File version missmatch\n", name);
fclose(fp);
return NULL;
}
@@ -187,6 +202,7 @@ struct anim_index *IMB_indexer_open(const char *name)
idx->num_entries = (ftell(fp) - 12) / (sizeof(int) + /* framepos */
sizeof(uint64_t) + /* seek_pos */
+ sizeof(uint64_t) + /* seek_pos_pts */
sizeof(uint64_t) + /* seek_pos_dts */
sizeof(uint64_t) /* pts */
);
@@ -200,12 +216,13 @@ struct anim_index *IMB_indexer_open(const char *name)
for (i = 0; i < idx->num_entries; i++) {
items_read += fread(&idx->entries[i].frameno, sizeof(int), 1, fp);
items_read += fread(&idx->entries[i].seek_pos, sizeof(uint64_t), 1, fp);
+ items_read += fread(&idx->entries[i].seek_pos_pts, sizeof(uint64_t), 1, fp);
items_read += fread(&idx->entries[i].seek_pos_dts, sizeof(uint64_t), 1, fp);
items_read += fread(&idx->entries[i].pts, sizeof(uint64_t), 1, fp);
}
- if (UNLIKELY(items_read != idx->num_entries * 4)) {
- perror("error reading animation index file");
+ if (UNLIKELY(items_read != idx->num_entries * 5)) {
+ fprintf(stderr, "Error: Element data size missmatch in: %s\n", name);
MEM_freeN(idx->entries);
MEM_freeN(idx);
fclose(fp);
@@ -216,6 +233,7 @@ struct anim_index *IMB_indexer_open(const char *name)
for (i = 0; i < idx->num_entries; i++) {
BLI_endian_switch_int32(&idx->entries[i].frameno);
BLI_endian_switch_uint64(&idx->entries[i].seek_pos);
+ BLI_endian_switch_uint64(&idx->entries[i].seek_pos_pts);
BLI_endian_switch_uint64(&idx->entries[i].seek_pos_dts);
BLI_endian_switch_uint64(&idx->entries[i].pts);
}
@@ -237,6 +255,17 @@ uint64_t IMB_indexer_get_seek_pos(struct anim_index *idx, int frame_index)
return idx->entries[frame_index].seek_pos;
}
+uint64_t IMB_indexer_get_seek_pos_pts(struct anim_index *idx, int frame_index)
+{
+ if (frame_index < 0) {
+ frame_index = 0;
+ }
+ if (frame_index >= idx->num_entries) {
+ frame_index = idx->num_entries - 1;
+ }
+ return idx->entries[frame_index].seek_pos_pts;
+}
+
uint64_t IMB_indexer_get_seek_pos_dts(struct anim_index *idx, int frame_index)
{
if (frame_index < 0) {
@@ -318,8 +347,7 @@ int IMB_proxy_size_to_array_index(IMB_Proxy_Size pr_size)
{
switch (pr_size) {
case IMB_PROXY_NONE:
- /* if we got here, something is broken anyways, so sane defaults... */
- return 0;
+ return -1;
case IMB_PROXY_25:
return 0;
case IMB_PROXY_50:
@@ -329,16 +357,16 @@ int IMB_proxy_size_to_array_index(IMB_Proxy_Size pr_size)
case IMB_PROXY_100:
return 3;
default:
- return 0;
+ BLI_assert(!"Unhandled proxy size enum!");
+ return -1;
}
}
int IMB_timecode_to_array_index(IMB_Timecode_Type tc)
{
switch (tc) {
- case IMB_TC_NONE: /* if we got here, something is broken anyways,
- * so sane defaults... */
- return 0;
+ case IMB_TC_NONE:
+ return -1;
case IMB_TC_RECORD_RUN:
return 0;
case IMB_TC_FREE_RUN:
@@ -348,7 +376,8 @@ int IMB_timecode_to_array_index(IMB_Timecode_Type tc)
case IMB_TC_RECORD_RUN_NO_GAPS:
return 3;
default:
- return 0;
+ BLI_assert(!"Unhandled timecode type enum!");
+ return -1;
}
}
@@ -384,6 +413,8 @@ static bool get_proxy_filename(struct anim *anim,
char index_dir[FILE_MAXDIR];
int i = IMB_proxy_size_to_array_index(preview_size);
+ BLI_assert(i >= 0);
+
char proxy_name[256];
char stream_suffix[20];
const char *name = (temp) ? "proxy_%d%s_part.avi" : "proxy_%d%s.avi";
@@ -415,6 +446,9 @@ static void get_tc_filename(struct anim *anim, IMB_Timecode_Type tc, char *fname
{
char index_dir[FILE_MAXDIR];
int i = IMB_timecode_to_array_index(tc);
+
+ BLI_assert(i >= 0);
+
const char *index_names[] = {
"record_run%s%s.blen_tc",
"free_run%s%s.blen_tc",
@@ -465,13 +499,6 @@ struct proxy_output_ctx {
struct anim *anim;
};
-// work around stupid swscaler 16 bytes alignment bug...
-
-static int round_up(int x, int mod)
-{
- return x + ((mod - (x % mod)) % mod);
-}
-
static struct proxy_output_ctx *alloc_proxy_output_ffmpeg(
struct anim *anim, AVStream *st, int proxy_size, int width, int height, int quality)
{
@@ -488,23 +515,16 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg(
rv->of = avformat_alloc_context();
rv->of->oformat = av_guess_format("avi", NULL, NULL);
- BLI_strncpy(rv->of->filename, fname, sizeof(rv->of->filename));
+ rv->of->url = av_strdup(fname);
- fprintf(stderr, "Starting work on proxy: %s\n", rv->of->filename);
+ fprintf(stderr, "Starting work on proxy: %s\n", rv->of->url);
rv->st = avformat_new_stream(rv->of, NULL);
rv->st->id = 0;
- rv->c = rv->st->codec;
+ rv->c = avcodec_alloc_context3(NULL);
rv->c->codec_type = AVMEDIA_TYPE_VIDEO;
rv->c->codec_id = AV_CODEC_ID_H264;
- rv->c->width = width;
- rv->c->height = height;
- rv->c->gop_size = 2;
- rv->c->max_b_frames = 0;
- /* Correct wrong default ffmpeg param which crash x264. */
- rv->c->qmin = 10;
- rv->c->qmax = 51;
rv->of->oformat->video_codec = rv->c->codec_id;
rv->codec = avcodec_find_encoder(rv->c->codec_id);
@@ -513,10 +533,19 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg(
fprintf(stderr,
"No ffmpeg encoder available? "
"Proxy not built!\n");
- av_free(rv->of);
+ avcodec_free_context(&rv->c);
+ avformat_free_context(rv->of);
+ MEM_freeN(rv);
return NULL;
}
+ avcodec_get_context_defaults3(rv->c, rv->codec);
+
+ rv->c->width = width;
+ rv->c->height = height;
+ rv->c->gop_size = 10;
+ rv->c->max_b_frames = 0;
+
if (rv->codec->pix_fmts) {
rv->c->pix_fmt = rv->codec->pix_fmts[0];
}
@@ -524,14 +553,14 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg(
rv->c->pix_fmt = AV_PIX_FMT_YUVJ420P;
}
- rv->c->sample_aspect_ratio = rv->st->sample_aspect_ratio = st->codec->sample_aspect_ratio;
+ rv->c->sample_aspect_ratio = rv->st->sample_aspect_ratio = st->sample_aspect_ratio;
rv->c->time_base.den = 25;
rv->c->time_base.num = 1;
rv->st->time_base = rv->c->time_base;
- /* This range matches eFFMpegCrf. Crf_range_min corresponds to lowest quality, crf_range_max to
- * highest quality. */
+ /* This range matches #eFFMpegCrf. `crf_range_min` corresponds to lowest quality,
+ * `crf_range_max` to highest quality. */
const int crf_range_min = 32;
const int crf_range_max = 17;
int crf = round_fl_to_int((quality / 100.0f) * (crf_range_max - crf_range_min) + crf_range_min);
@@ -539,40 +568,79 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg(
AVDictionary *codec_opts = NULL;
/* High quality preset value. */
av_dict_set_int(&codec_opts, "crf", crf, 0);
- /* Prefer smaller file-size. */
- av_dict_set(&codec_opts, "preset", "slow", 0);
- /* Thread count. */
- av_dict_set_int(&codec_opts, "threads", BLI_system_thread_count(), 0);
+ /* Prefer smaller file-size. Presets from `veryslow` to `veryfast` produce output with very
+ * similar file-size, but there is big difference in performance.
+ * In some cases `veryfast` preset will produce smallest file-size. */
+ av_dict_set(&codec_opts, "preset", "veryfast", 0);
+ av_dict_set(&codec_opts, "tune", "fastdecode", 0);
+
+ if (rv->codec->capabilities & AV_CODEC_CAP_AUTO_THREADS) {
+ rv->c->thread_count = 0;
+ }
+ else {
+ rv->c->thread_count = BLI_system_thread_count();
+ }
+
+ if (rv->codec->capabilities & AV_CODEC_CAP_FRAME_THREADS) {
+ rv->c->thread_type = FF_THREAD_FRAME;
+ }
+ else if (rv->codec->capabilities & AV_CODEC_CAP_SLICE_THREADS) {
+ rv->c->thread_type = FF_THREAD_SLICE;
+ }
if (rv->of->flags & AVFMT_GLOBALHEADER) {
- rv->c->flags |= CODEC_FLAG_GLOBAL_HEADER;
+ rv->c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
- if (avio_open(&rv->of->pb, fname, AVIO_FLAG_WRITE) < 0) {
+ avcodec_parameters_from_context(rv->st->codecpar, rv->c);
+
+ int ret = avio_open(&rv->of->pb, fname, AVIO_FLAG_WRITE);
+
+ if (ret < 0) {
fprintf(stderr,
- "Couldn't open outputfile! "
- "Proxy not built!\n");
- av_free(rv->of);
- return 0;
+ "Couldn't open IO: %s\n"
+ "Proxy not built!\n",
+ av_err2str(ret));
+ avcodec_free_context(&rv->c);
+ avformat_free_context(rv->of);
+ MEM_freeN(rv);
+ return NULL;
}
- avcodec_open2(rv->c, rv->codec, &codec_opts);
+ ret = avcodec_open2(rv->c, rv->codec, &codec_opts);
+ if (ret < 0) {
+ fprintf(stderr,
+ "Couldn't open codec: %s\n"
+ "Proxy not built!\n",
+ av_err2str(ret));
+ avcodec_free_context(&rv->c);
+ avformat_free_context(rv->of);
+ MEM_freeN(rv);
+ return NULL;
+ }
- rv->orig_height = av_get_cropped_height_from_codec(st->codec);
+ rv->orig_height = st->codecpar->height;
- if (st->codec->width != width || st->codec->height != height ||
- st->codec->pix_fmt != rv->c->pix_fmt) {
+ if (st->codecpar->width != width || st->codecpar->height != height ||
+ st->codecpar->format != rv->c->pix_fmt) {
rv->frame = av_frame_alloc();
- avpicture_fill((AVPicture *)rv->frame,
- MEM_mallocN(avpicture_get_size(rv->c->pix_fmt, round_up(width, 16), height),
- "alloc proxy output frame"),
- rv->c->pix_fmt,
- round_up(width, 16),
- height);
-
- rv->sws_ctx = sws_getContext(st->codec->width,
+
+ av_image_fill_arrays(rv->frame->data,
+ rv->frame->linesize,
+ MEM_mallocN(av_image_get_buffer_size(rv->c->pix_fmt, width, height, 1),
+ "alloc proxy output frame"),
+ rv->c->pix_fmt,
+ width,
+ height,
+ 1);
+
+ rv->frame->format = rv->c->pix_fmt;
+ rv->frame->width = width;
+ rv->frame->height = height;
+
+ rv->sws_ctx = sws_getContext(st->codecpar->width,
rv->orig_height,
- st->codec->pix_fmt,
+ st->codecpar->format,
width,
height,
rv->c->pix_fmt,
@@ -582,26 +650,30 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg(
NULL);
}
- if (avformat_write_header(rv->of, NULL) < 0) {
+ ret = avformat_write_header(rv->of, NULL);
+ if (ret < 0) {
fprintf(stderr,
- "Couldn't set output parameters? "
- "Proxy not built!\n");
- av_free(rv->of);
- return 0;
+ "Couldn't write header: %s\n"
+ "Proxy not built!\n",
+ av_err2str(ret));
+
+ if (rv->frame) {
+ av_frame_free(&rv->frame);
+ }
+
+ avcodec_free_context(&rv->c);
+ avformat_free_context(rv->of);
+ MEM_freeN(rv);
+ return NULL;
}
return rv;
}
-static int add_to_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, AVFrame *frame)
+static void add_to_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, AVFrame *frame)
{
- AVPacket packet = {0};
- int ret, got_output;
-
- av_init_packet(&packet);
-
if (!ctx) {
- return 0;
+ return;
}
if (ctx->sws_ctx && frame &&
@@ -621,35 +693,49 @@ static int add_to_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, AVFrame *fra
frame->pts = ctx->cfra++;
}
- ret = avcodec_encode_video2(ctx->c, &packet, frame, &got_output);
+ int ret = avcodec_send_frame(ctx->c, frame);
if (ret < 0) {
- fprintf(stderr, "Error encoding proxy frame %d for '%s'\n", ctx->cfra - 1, ctx->of->filename);
- return 0;
+ /* Can't send frame to encoder. This shouldn't happen. */
+ fprintf(stderr, "Can't send video frame: %s\n", av_err2str(ret));
+ return;
}
+ AVPacket *packet = av_packet_alloc();
+
+ while (ret >= 0) {
+ ret = avcodec_receive_packet(ctx->c, packet);
- if (got_output) {
- if (packet.pts != AV_NOPTS_VALUE) {
- packet.pts = av_rescale_q(packet.pts, ctx->c->time_base, ctx->st->time_base);
+ if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
+ /* No more packets to flush. */
+ break;
}
- if (packet.dts != AV_NOPTS_VALUE) {
- packet.dts = av_rescale_q(packet.dts, ctx->c->time_base, ctx->st->time_base);
+ if (ret < 0) {
+ fprintf(stderr,
+ "Error encoding proxy frame %d for '%s': %s\n",
+ ctx->cfra - 1,
+ ctx->of->url,
+ av_err2str(ret));
+ break;
}
- packet.stream_index = ctx->st->index;
+ packet->stream_index = ctx->st->index;
+ av_packet_rescale_ts(packet, ctx->c->time_base, ctx->st->time_base);
+# ifdef FFMPEG_USE_DURATION_WORKAROUND
+ my_guess_pkt_duration(ctx->of, ctx->st, packet);
+# endif
- if (av_interleaved_write_frame(ctx->of, &packet) != 0) {
+ int write_ret = av_interleaved_write_frame(ctx->of, packet);
+ if (write_ret != 0) {
fprintf(stderr,
"Error writing proxy frame %d "
- "into '%s'\n",
+ "into '%s': %s\n",
ctx->cfra - 1,
- ctx->of->filename);
- return 0;
+ ctx->of->url,
+ av_err2str(write_ret));
+ break;
}
-
- return 1;
}
- return 0;
+ av_packet_free(&packet);
}
static void free_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, int rollback)
@@ -662,15 +748,15 @@ static void free_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, int rollback)
}
if (!rollback) {
- while (add_to_proxy_output_ffmpeg(ctx, NULL)) {
- }
+ /* Flush the remaining packets. */
+ add_to_proxy_output_ffmpeg(ctx, NULL);
}
avcodec_flush_buffers(ctx->c);
av_write_trailer(ctx->of);
- avcodec_close(ctx->c);
+ avcodec_free_context(&ctx->c);
if (ctx->of->oformat) {
if (!(ctx->of->oformat->flags & AVFMT_NOFILE)) {
@@ -719,9 +805,10 @@ typedef struct FFmpegIndexBuilderContext {
IMB_Proxy_Size proxy_sizes_in_use;
uint64_t seek_pos;
- uint64_t last_seek_pos;
- uint64_t seek_pos_dts;
uint64_t seek_pos_pts;
+ uint64_t seek_pos_dts;
+ uint64_t last_seek_pos;
+ uint64_t last_seek_pos_pts;
uint64_t last_seek_pos_dts;
uint64_t start_pts;
double frame_rate;
@@ -765,7 +852,7 @@ static IndexBuildContext *index_ffmpeg_create_context(struct anim *anim,
/* Find the video stream */
context->videoStream = -1;
for (i = 0; i < context->iFormatCtx->nb_streams; i++) {
- if (context->iFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
+ if (context->iFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
if (streamcount > 0) {
streamcount--;
continue;
@@ -782,9 +869,8 @@ static IndexBuildContext *index_ffmpeg_create_context(struct anim *anim,
}
context->iStream = context->iFormatCtx->streams[context->videoStream];
- context->iCodecCtx = context->iStream->codec;
- context->iCodec = avcodec_find_decoder(context->iCodecCtx->codec_id);
+ context->iCodec = avcodec_find_decoder(context->iStream->codecpar->codec_id);
if (context->iCodec == NULL) {
avformat_close_input(&context->iFormatCtx);
@@ -792,27 +878,39 @@ static IndexBuildContext *index_ffmpeg_create_context(struct anim *anim,
return NULL;
}
- context->iCodecCtx->workaround_bugs = 1;
+ context->iCodecCtx = avcodec_alloc_context3(NULL);
+ avcodec_parameters_to_context(context->iCodecCtx, context->iStream->codecpar);
+ context->iCodecCtx->workaround_bugs = FF_BUG_AUTODETECT;
- AVDictionary *codec_opts = NULL;
- /* Thread count. */
- av_dict_set_int(&codec_opts, "threads", BLI_system_thread_count(), 0);
+ if (context->iCodec->capabilities & AV_CODEC_CAP_AUTO_THREADS) {
+ context->iCodecCtx->thread_count = 0;
+ }
+ else {
+ context->iCodecCtx->thread_count = BLI_system_thread_count();
+ }
- if (avcodec_open2(context->iCodecCtx, context->iCodec, &codec_opts) < 0) {
+ if (context->iCodec->capabilities & AV_CODEC_CAP_FRAME_THREADS) {
+ context->iCodecCtx->thread_type = FF_THREAD_FRAME;
+ }
+ else if (context->iCodec->capabilities & AV_CODEC_CAP_SLICE_THREADS) {
+ context->iCodecCtx->thread_type = FF_THREAD_SLICE;
+ }
+
+ if (avcodec_open2(context->iCodecCtx, context->iCodec, NULL) < 0) {
avformat_close_input(&context->iFormatCtx);
+ avcodec_free_context(&context->iCodecCtx);
MEM_freeN(context);
return NULL;
}
for (i = 0; i < num_proxy_sizes; i++) {
if (proxy_sizes_in_use & proxy_sizes[i]) {
- context->proxy_ctx[i] = alloc_proxy_output_ffmpeg(
- anim,
- context->iStream,
- proxy_sizes[i],
- context->iCodecCtx->width * proxy_fac[i],
- av_get_cropped_height_from_codec(context->iCodecCtx) * proxy_fac[i],
- quality);
+ context->proxy_ctx[i] = alloc_proxy_output_ffmpeg(anim,
+ context->iStream,
+ proxy_sizes[i],
+ context->iCodecCtx->width * proxy_fac[i],
+ context->iCodecCtx->height * proxy_fac[i],
+ quality);
if (!context->proxy_ctx[i]) {
proxy_sizes_in_use &= ~proxy_sizes[i];
}
@@ -851,7 +949,7 @@ static void index_rebuild_ffmpeg_finish(FFmpegIndexBuilderContext *context, int
}
}
- avcodec_close(context->iCodecCtx);
+ avcodec_free_context(&context->iCodecCtx);
avformat_close_input(&context->iFormatCtx);
MEM_freeN(context);
@@ -863,8 +961,9 @@ static void index_rebuild_ffmpeg_proc_decoded_frame(FFmpegIndexBuilderContext *c
{
int i;
uint64_t s_pos = context->seek_pos;
+ uint64_t s_pts = context->seek_pos_pts;
uint64_t s_dts = context->seek_pos_dts;
- uint64_t pts = av_get_pts_from_frame(context->iFormatCtx, in_frame);
+ uint64_t pts = av_get_pts_from_frame(in_frame);
for (i = 0; i < context->num_proxy_sizes; i++) {
add_to_proxy_output_ffmpeg(context->proxy_ctx[i], in_frame);
@@ -878,15 +977,15 @@ static void index_rebuild_ffmpeg_proc_decoded_frame(FFmpegIndexBuilderContext *c
context->frameno = floor(
(pts - context->start_pts) * context->pts_time_base * context->frame_rate + 0.5);
- /* decoding starts *always* on I-Frames,
- * so: P-Frames won't work, even if all the
- * information is in place, when we seek
- * to the I-Frame presented *after* the P-Frame,
- * but located before the P-Frame within
- * the stream */
+ int64_t seek_pos_pts = timestamp_from_pts_or_dts(context->seek_pos_pts, context->seek_pos_dts);
- if (pts < context->seek_pos_pts) {
+ if (pts < seek_pos_pts) {
+ /* Decoding starts *always* on I-Frames. In this case our position is
+ * before our seek I-Frame. So we need to pick the previous available
+ * I-Frame to be able to decode this one properly.
+ */
s_pos = context->last_seek_pos;
+ s_pts = context->last_seek_pos_pts;
s_dts = context->last_seek_pos_dts;
}
@@ -903,6 +1002,7 @@ static void index_rebuild_ffmpeg_proc_decoded_frame(FFmpegIndexBuilderContext *c
curr_packet->size,
tc_frameno,
s_pos,
+ s_pts,
s_dts,
pts);
}
@@ -916,23 +1016,18 @@ static int index_rebuild_ffmpeg(FFmpegIndexBuilderContext *context,
short *do_update,
float *progress)
{
- AVFrame *in_frame = 0;
- AVPacket next_packet;
+ AVFrame *in_frame = av_frame_alloc();
+ AVPacket *next_packet = av_packet_alloc();
uint64_t stream_size;
- memset(&next_packet, 0, sizeof(AVPacket));
-
- in_frame = av_frame_alloc();
-
stream_size = avio_size(context->iFormatCtx->pb);
context->frame_rate = av_q2d(av_guess_frame_rate(context->iFormatCtx, context->iStream, NULL));
context->pts_time_base = av_q2d(context->iStream->time_base);
- while (av_read_frame(context->iFormatCtx, &next_packet) >= 0) {
- int frame_finished = 0;
+ while (av_read_frame(context->iFormatCtx, next_packet) >= 0) {
float next_progress =
- (float)((int)floor(((double)next_packet.pos) * 100 / ((double)stream_size) + 0.5)) / 100;
+ (float)((int)floor(((double)next_packet->pos) * 100 / ((double)stream_size) + 0.5)) / 100;
if (*progress != next_progress) {
*progress = next_progress;
@@ -940,50 +1035,62 @@ static int index_rebuild_ffmpeg(FFmpegIndexBuilderContext *context,
}
if (*stop) {
- av_free_packet(&next_packet);
break;
}
- if (next_packet.stream_index == context->videoStream) {
- if (next_packet.flags & AV_PKT_FLAG_KEY) {
+ if (next_packet->stream_index == context->videoStream) {
+ if (next_packet->flags & AV_PKT_FLAG_KEY) {
context->last_seek_pos = context->seek_pos;
+ context->last_seek_pos_pts = context->seek_pos_pts;
context->last_seek_pos_dts = context->seek_pos_dts;
- context->seek_pos = next_packet.pos;
- context->seek_pos_dts = next_packet.dts;
- context->seek_pos_pts = next_packet.pts;
+
+ context->seek_pos = next_packet->pos;
+ context->seek_pos_pts = next_packet->pts;
+ context->seek_pos_dts = next_packet->dts;
}
- avcodec_decode_video2(context->iCodecCtx, in_frame, &frame_finished, &next_packet);
- }
+ int ret = avcodec_send_packet(context->iCodecCtx, next_packet);
+ while (ret >= 0) {
+ ret = avcodec_receive_frame(context->iCodecCtx, in_frame);
- if (frame_finished) {
- index_rebuild_ffmpeg_proc_decoded_frame(context, &next_packet, in_frame);
+ if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
+ /* No more frames to flush. */
+ break;
+ }
+ if (ret < 0) {
+ fprintf(stderr, "Error decoding proxy frame: %s\n", av_err2str(ret));
+ break;
+ }
+ index_rebuild_ffmpeg_proc_decoded_frame(context, next_packet, in_frame);
+ }
}
- av_free_packet(&next_packet);
+ av_packet_unref(next_packet);
}
/* process pictures still stuck in decoder engine after EOF
- * according to ffmpeg docs using 0-size packets.
+ * according to ffmpeg docs using NULL packets.
*
* At least, if we haven't already stopped... */
- /* this creates the 0-size packet and prevents a memory leak. */
- av_free_packet(&next_packet);
-
if (!*stop) {
- int frame_finished;
+ int ret = avcodec_send_packet(context->iCodecCtx, NULL);
- do {
- frame_finished = 0;
+ while (ret >= 0) {
+ ret = avcodec_receive_frame(context->iCodecCtx, in_frame);
- avcodec_decode_video2(context->iCodecCtx, in_frame, &frame_finished, &next_packet);
-
- if (frame_finished) {
- index_rebuild_ffmpeg_proc_decoded_frame(context, &next_packet, in_frame);
+ if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
+ /* No more frames to flush. */
+ break;
+ }
+ if (ret < 0) {
+ fprintf(stderr, "Error flushing proxy frame: %s\n", av_err2str(ret));
+ break;
}
- } while (frame_finished);
+ index_rebuild_ffmpeg_proc_decoded_frame(context, next_packet, in_frame);
+ }
}
+ av_packet_free(&next_packet);
av_free(in_frame);
return 1;
@@ -1321,6 +1428,10 @@ struct anim *IMB_anim_open_proxy(struct anim *anim, IMB_Proxy_Size preview_size)
char fname[FILE_MAX];
int i = IMB_proxy_size_to_array_index(preview_size);
+ if (i < 0) {
+ return NULL;
+ }
+
if (anim->proxy_anim[i]) {
return anim->proxy_anim[i];
}
@@ -1344,6 +1455,10 @@ struct anim_index *IMB_anim_open_index(struct anim *anim, IMB_Timecode_Type tc)
char fname[FILE_MAX];
int i = IMB_timecode_to_array_index(tc);
+ if (i < 0) {
+ return NULL;
+ }
+
if (anim->curr_idx[i]) {
return anim->curr_idx[i];
}
diff --git a/source/blender/imbuf/intern/iris.c b/source/blender/imbuf/intern/iris.c
index 112b95bf1a1..547af472d73 100644
--- a/source/blender/imbuf/intern/iris.c
+++ b/source/blender/imbuf/intern/iris.c
@@ -270,11 +270,13 @@ struct ImBuf *imb_loadiris(const uchar *mem, size_t size, int flags, char colors
ImBuf *ibuf = NULL;
uchar dirty_flag = 0;
- if (size < HEADER_SIZE) {
+ if (!imb_is_a_iris(mem, size)) {
return NULL;
}
- if (!imb_is_a_iris(mem, size)) {
+ /* Could pe part of the magic check above,
+ * by convention this check only requests the size needed to read it's magic though. */
+ if (size < HEADER_SIZE) {
return NULL;
}
diff --git a/source/blender/imbuf/intern/jpeg.c b/source/blender/imbuf/intern/jpeg.c
index 440375f60dc..48b5b0c34db 100644
--- a/source/blender/imbuf/intern/jpeg.c
+++ b/source/blender/imbuf/intern/jpeg.c
@@ -516,7 +516,8 @@ static void write_jpeg(struct jpeg_compress_struct *cinfo, struct ImBuf *ibuf)
* The first "Blender" is a simple identify to help
* in the read process.
*/
- text_len = BLI_snprintf(text, text_size, "Blender:%s:%s", prop->name, IDP_String(prop));
+ text_len = BLI_snprintf_rlen(
+ text, text_size, "Blender:%s:%s", prop->name, IDP_String(prop));
jpeg_write_marker(cinfo, JPEG_COM, (JOCTET *)text, text_len + 1);
/* TODO(sergey): Ideally we will try to re-use allocation as
diff --git a/source/blender/imbuf/intern/metadata.c b/source/blender/imbuf/intern/metadata.c
index d8abd3411cb..c59997b34f5 100644
--- a/source/blender/imbuf/intern/metadata.c
+++ b/source/blender/imbuf/intern/metadata.c
@@ -38,8 +38,6 @@
#include "IMB_metadata.h"
-#define METADATA_MAX_VALUE_LENGTH 1024
-
void IMB_metadata_ensure(struct IDProperty **metadata)
{
if (*metadata != NULL) {
@@ -99,11 +97,11 @@ void IMB_metadata_set_field(struct IDProperty *metadata, const char *key, const
}
if (prop == NULL) {
- prop = IDP_NewString(value, key, METADATA_MAX_VALUE_LENGTH);
+ prop = IDP_NewString(value, key, 0);
IDP_AddToGroup(metadata, prop);
}
- IDP_AssignString(prop, value, METADATA_MAX_VALUE_LENGTH);
+ IDP_AssignString(prop, value, 0);
}
void IMB_metadata_foreach(struct ImBuf *ibuf, IMBMetadataForeachCb callback, void *userdata)
diff --git a/source/blender/imbuf/intern/openexr/openexr_api.cpp b/source/blender/imbuf/intern/openexr/openexr_api.cpp
index 2a9cb9af891..382d86f2645 100644
--- a/source/blender/imbuf/intern/openexr/openexr_api.cpp
+++ b/source/blender/imbuf/intern/openexr/openexr_api.cpp
@@ -120,10 +120,6 @@ class IMemStream : public Imf::IStream {
_exrbuf = exrbuf;
}
- ~IMemStream() override
- {
- }
-
bool read(char c[], int n) override
{
if (n + _exrpos <= _exrsize) {
diff --git a/source/blender/imbuf/intern/radiance_hdr.c b/source/blender/imbuf/intern/radiance_hdr.c
index 285b18595f7..94b2a62aa26 100644
--- a/source/blender/imbuf/intern/radiance_hdr.c
+++ b/source/blender/imbuf/intern/radiance_hdr.c
@@ -229,87 +229,89 @@ struct ImBuf *imb_loadhdr(const unsigned char *mem,
const unsigned char *ptr, *mem_eof = mem + size;
char oriY[80], oriX[80];
- if (imb_is_a_hdr(mem, size)) {
- colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_FLOAT);
-
- /* find empty line, next line is resolution info */
- size_t x;
- for (x = 1; x < size; x++) {
- if ((mem[x - 1] == '\n') && (mem[x] == '\n')) {
- found = 1;
- break;
- }
+ if (!imb_is_a_hdr(mem, size)) {
+ return NULL;
+ }
+
+ colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_FLOAT);
+
+ /* find empty line, next line is resolution info */
+ size_t x;
+ for (x = 1; x < size; x++) {
+ if ((mem[x - 1] == '\n') && (mem[x] == '\n')) {
+ found = 1;
+ break;
}
- if (found && (x < (size + 2))) {
- if (sscanf((char *)&mem[x + 1],
- "%79s %d %79s %d",
- (char *)&oriY,
- &height,
- (char *)&oriX,
- &width) != 4) {
- return NULL;
- }
+ }
- /* find end of this line, data right behind it */
- ptr = (unsigned char *)strchr((char *)&mem[x + 1], '\n');
- ptr++;
+ if ((found && (x < (size + 2))) == 0) {
+ /* Data not found! */
+ return NULL;
+ }
- if (flags & IB_test) {
- ibuf = IMB_allocImBuf(width, height, 32, 0);
- }
- else {
- ibuf = IMB_allocImBuf(width, height, 32, (flags & IB_rect) | IB_rectfloat);
- }
+ if (sscanf((const char *)&mem[x + 1],
+ "%79s %d %79s %d",
+ (char *)&oriY,
+ &height,
+ (char *)&oriX,
+ &width) != 4) {
+ return NULL;
+ }
- if (UNLIKELY(ibuf == NULL)) {
- return NULL;
- }
- ibuf->ftype = IMB_FTYPE_RADHDR;
+ /* find end of this line, data right behind it */
+ ptr = (const unsigned char *)strchr((const char *)&mem[x + 1], '\n');
+ ptr++;
- if (flags & IB_alphamode_detect) {
- ibuf->flags |= IB_alphamode_premul;
- }
+ if (flags & IB_test) {
+ ibuf = IMB_allocImBuf(width, height, 32, 0);
+ }
+ else {
+ ibuf = IMB_allocImBuf(width, height, 32, (flags & IB_rect) | IB_rectfloat);
+ }
- if (flags & IB_test) {
- return ibuf;
- }
+ if (UNLIKELY(ibuf == NULL)) {
+ return NULL;
+ }
- /* read in and decode the actual data */
- sline = (RGBE *)MEM_mallocN(sizeof(*sline) * width, __func__);
- rect_float = ibuf->rect_float;
+ ibuf->ftype = IMB_FTYPE_RADHDR;
- for (size_t y = 0; y < height; y++) {
- ptr = freadcolrs(sline, ptr, width, mem_eof);
- if (ptr == NULL) {
- printf(
- "WARNING! HDR decode error, image may be just truncated, or completely wrong...\n");
- break;
- }
- for (x = 0; x < width; x++) {
- /* convert to ldr */
- RGBE2FLOAT(sline[x], fcol);
- *rect_float++ = fcol[RED];
- *rect_float++ = fcol[GRN];
- *rect_float++ = fcol[BLU];
- *rect_float++ = 1.0f;
- }
- }
- MEM_freeN(sline);
- if (oriY[0] == '-') {
- IMB_flipy(ibuf);
- }
+ if (flags & IB_alphamode_detect) {
+ ibuf->flags |= IB_alphamode_premul;
+ }
- if (flags & IB_rect) {
- IMB_rect_from_float(ibuf);
- }
+ if (flags & IB_test) {
+ return ibuf;
+ }
- return ibuf;
+ /* read in and decode the actual data */
+ sline = (RGBE *)MEM_mallocN(sizeof(*sline) * width, __func__);
+ rect_float = ibuf->rect_float;
+
+ for (size_t y = 0; y < height; y++) {
+ ptr = freadcolrs(sline, ptr, width, mem_eof);
+ if (ptr == NULL) {
+ printf("WARNING! HDR decode error, image may be just truncated, or completely wrong...\n");
+ break;
}
- // else printf("Data not found!\n");
+ for (x = 0; x < width; x++) {
+ /* convert to ldr */
+ RGBE2FLOAT(sline[x], fcol);
+ *rect_float++ = fcol[RED];
+ *rect_float++ = fcol[GRN];
+ *rect_float++ = fcol[BLU];
+ *rect_float++ = 1.0f;
+ }
+ }
+ MEM_freeN(sline);
+ if (oriY[0] == '-') {
+ IMB_flipy(ibuf);
+ }
+
+ if (flags & IB_rect) {
+ IMB_rect_from_float(ibuf);
}
- // else printf("Not a valid radiance HDR file!\n");
- return NULL;
+ return ibuf;
}
/* ImBuf write */
diff --git a/source/blender/imbuf/intern/rectop.c b/source/blender/imbuf/intern/rectop.c
index 6ae93def50f..4b5d68b9c13 100644
--- a/source/blender/imbuf/intern/rectop.c
+++ b/source/blender/imbuf/intern/rectop.c
@@ -988,8 +988,9 @@ typedef struct RectBlendThreadData {
bool accumulate;
} RectBlendThreadData;
-static void rectblend_thread_do(void *data_v, int start_scanline, int num_scanlines)
+static void rectblend_thread_do(void *data_v, int scanline)
{
+ const int num_scanlines = 1;
RectBlendThreadData *data = (RectBlendThreadData *)data_v;
IMB_rectblend(data->dbuf,
data->obuf,
@@ -999,11 +1000,11 @@ static void rectblend_thread_do(void *data_v, int start_scanline, int num_scanli
data->texmask,
data->mask_max,
data->destx,
- data->desty + start_scanline,
+ data->desty + scanline,
data->origx,
- data->origy + start_scanline,
+ data->origy + scanline,
data->srcx,
- data->srcy + start_scanline,
+ data->srcy + scanline,
data->width,
num_scanlines,
data->mode,
diff --git a/source/blender/imbuf/intern/tiff.c b/source/blender/imbuf/intern/tiff.c
index 2fb14e40d9d..d9e1db27ef0 100644
--- a/source/blender/imbuf/intern/tiff.c
+++ b/source/blender/imbuf/intern/tiff.c
@@ -38,6 +38,7 @@
#include "imbuf.h"
+#include "BLI_endian_defines.h"
#include "BLI_math.h"
#include "BLI_utildefines.h"
@@ -374,7 +375,7 @@ static void scanline_separate_32bit(float *rectf, const float *fbuf, int scanlin
static void imb_read_tiff_resolution(ImBuf *ibuf, TIFF *image)
{
- uint16 unit;
+ uint16_t unit;
float xres;
float yres;
@@ -569,18 +570,14 @@ ImBuf *imb_loadtiff(const unsigned char *mem,
TIFF *image = NULL;
ImBuf *ibuf = NULL, *hbuf;
ImbTIFFMemFile memFile;
- uint32 width, height;
+ uint32_t width, height;
char *format = NULL;
int level;
short spp;
int ib_depth;
int found;
- /* check whether or not we have a TIFF file */
- if (size < IMB_TIFF_NCB) {
- fprintf(stderr, "imb_loadtiff: size < IMB_TIFF_NCB\n");
- return NULL;
- }
+ /* Check whether or not we have a TIFF file. */
if (imb_is_a_tiff(mem, size) == 0) {
return NULL;
}
@@ -694,7 +691,7 @@ void imb_loadtiletiff(
ImBuf *ibuf, const unsigned char *mem, size_t size, int tx, int ty, unsigned int *rect)
{
TIFF *image = NULL;
- uint32 width, height;
+ uint32_t width, height;
ImbTIFFMemFile memFile;
image = imb_tiff_client_open(&memFile, mem, size);
@@ -765,7 +762,7 @@ void imb_loadtiletiff(
bool imb_savetiff(ImBuf *ibuf, const char *filepath, int flags)
{
TIFF *image = NULL;
- uint16 samplesperpixel, bitspersample;
+ uint16_t samplesperpixel, bitspersample;
size_t npixels;
unsigned char *pixels = NULL;
unsigned char *from = NULL, *to = NULL;
@@ -778,7 +775,7 @@ bool imb_savetiff(ImBuf *ibuf, const char *filepath, int flags)
/* check for a valid number of bytes per pixel. Like the PNG writer,
* the TIFF writer supports 1, 3 or 4 bytes per pixel, corresponding
* to gray, RGB, RGBA respectively. */
- samplesperpixel = (uint16)((ibuf->planes + 7) >> 3);
+ samplesperpixel = (uint16_t)((ibuf->planes + 7) >> 3);
if ((samplesperpixel > 4) || (samplesperpixel == 2)) {
fprintf(stderr,
"imb_savetiff: unsupported number of bytes per "
diff --git a/source/blender/imbuf/intern/util.c b/source/blender/imbuf/intern/util.c
index 64dad5de902..1bb047f1317 100644
--- a/source/blender/imbuf/intern/util.c
+++ b/source/blender/imbuf/intern/util.c
@@ -182,7 +182,7 @@ bool IMB_ispic_type_matches(const char *filepath, int filetype)
const ImFileType *type = IMB_file_type_from_ftype(filetype);
if (type != NULL) {
- /* Requesting to load a type that can't check it's own header doesn't make sense.
+ /* Requesting to load a type that can't check its own header doesn't make sense.
* Keep the check for developers. */
BLI_assert(type->is_a != NULL);
if (type->is_a != NULL) {
@@ -245,7 +245,6 @@ static void ffmpeg_log_callback(void *ptr, int level, const char *format, va_lis
void IMB_ffmpeg_init(void)
{
- av_register_all();
avdevice_register_all();
ffmpeg_last_error[0] = '\0';
@@ -269,7 +268,6 @@ static int isffmpeg(const char *filepath)
unsigned int i;
int videoStream;
AVCodec *pCodec;
- AVCodecContext *pCodecCtx;
if (BLI_path_extension_check_n(filepath,
".swf",
@@ -310,8 +308,8 @@ static int isffmpeg(const char *filepath)
/* Find the first video stream */
videoStream = -1;
for (i = 0; i < pFormatCtx->nb_streams; i++) {
- if (pFormatCtx->streams[i] && pFormatCtx->streams[i]->codec &&
- (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)) {
+ if (pFormatCtx->streams[i] && pFormatCtx->streams[i]->codecpar &&
+ (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)) {
videoStream = i;
break;
}
@@ -322,21 +320,15 @@ static int isffmpeg(const char *filepath)
return 0;
}
- pCodecCtx = pFormatCtx->streams[videoStream]->codec;
+ AVCodecParameters *codec_par = pFormatCtx->streams[videoStream]->codecpar;
/* Find the decoder for the video stream */
- pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
+ pCodec = avcodec_find_decoder(codec_par->codec_id);
if (pCodec == NULL) {
avformat_close_input(&pFormatCtx);
return 0;
}
- if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
- avformat_close_input(&pFormatCtx);
- return 0;
- }
-
- avcodec_close(pCodecCtx);
avformat_close_input(&pFormatCtx);
return 1;
diff --git a/source/blender/io/alembic/ABC_alembic.h b/source/blender/io/alembic/ABC_alembic.h
index 9785f6d68ab..5664a43233a 100644
--- a/source/blender/io/alembic/ABC_alembic.h
+++ b/source/blender/io/alembic/ABC_alembic.h
@@ -49,6 +49,7 @@ struct AlembicExportParams {
bool uvs;
bool normals;
bool vcolors;
+ bool orcos;
bool apply_subdiv;
bool curves_as_mesh;
bool flatten_hierarchy;
diff --git a/source/blender/io/alembic/exporter/abc_custom_props.cc b/source/blender/io/alembic/exporter/abc_custom_props.cc
index 382afdc294d..f5593e7ee30 100644
--- a/source/blender/io/alembic/exporter/abc_custom_props.cc
+++ b/source/blender/io/alembic/exporter/abc_custom_props.cc
@@ -51,10 +51,6 @@ CustomPropertiesExporter::CustomPropertiesExporter(ABCAbstractWriter *owner) : o
{
}
-CustomPropertiesExporter::~CustomPropertiesExporter()
-{
-}
-
void CustomPropertiesExporter::write_all(const IDProperty *group)
{
if (group == nullptr) {
diff --git a/source/blender/io/alembic/exporter/abc_custom_props.h b/source/blender/io/alembic/exporter/abc_custom_props.h
index d3f9b2fc43c..fcd47382d96 100644
--- a/source/blender/io/alembic/exporter/abc_custom_props.h
+++ b/source/blender/io/alembic/exporter/abc_custom_props.h
@@ -61,7 +61,7 @@ class CustomPropertiesExporter {
public:
CustomPropertiesExporter(ABCAbstractWriter *owner);
- virtual ~CustomPropertiesExporter();
+ virtual ~CustomPropertiesExporter() = default;
void write_all(const IDProperty *group);
diff --git a/source/blender/io/alembic/exporter/abc_writer_abstract.cc b/source/blender/io/alembic/exporter/abc_writer_abstract.cc
index e99048cc0ee..27b5c2fa2a4 100644
--- a/source/blender/io/alembic/exporter/abc_writer_abstract.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_abstract.cc
@@ -45,10 +45,6 @@ ABCAbstractWriter::ABCAbstractWriter(const ABCWriterConstructorArgs &args)
{
}
-ABCAbstractWriter::~ABCAbstractWriter()
-{
-}
-
bool ABCAbstractWriter::is_supported(const HierarchyContext * /*context*/) const
{
return true;
diff --git a/source/blender/io/alembic/exporter/abc_writer_abstract.h b/source/blender/io/alembic/exporter/abc_writer_abstract.h
index 4997e498199..d3500394555 100644
--- a/source/blender/io/alembic/exporter/abc_writer_abstract.h
+++ b/source/blender/io/alembic/exporter/abc_writer_abstract.h
@@ -50,7 +50,6 @@ class ABCAbstractWriter : public AbstractHierarchyWriter {
public:
explicit ABCAbstractWriter(const ABCWriterConstructorArgs &args);
- virtual ~ABCAbstractWriter();
virtual void write(HierarchyContext &context) override;
diff --git a/source/blender/io/alembic/exporter/abc_writer_instance.cc b/source/blender/io/alembic/exporter/abc_writer_instance.cc
index 7f3b044cb8b..1737e8c091e 100644
--- a/source/blender/io/alembic/exporter/abc_writer_instance.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_instance.cc
@@ -35,10 +35,6 @@ ABCInstanceWriter::ABCInstanceWriter(const ABCWriterConstructorArgs &args)
{
}
-ABCInstanceWriter::~ABCInstanceWriter()
-{
-}
-
void ABCInstanceWriter::create_alembic_objects(const HierarchyContext *context)
{
OObject original = args_.hierarchy_iterator->get_alembic_object(context->original_export_path);
diff --git a/source/blender/io/alembic/exporter/abc_writer_instance.h b/source/blender/io/alembic/exporter/abc_writer_instance.h
index f7d6450055a..1e4285d5e02 100644
--- a/source/blender/io/alembic/exporter/abc_writer_instance.h
+++ b/source/blender/io/alembic/exporter/abc_writer_instance.h
@@ -31,7 +31,6 @@ namespace blender::io::alembic {
class ABCInstanceWriter : public ABCAbstractWriter {
public:
explicit ABCInstanceWriter(const ABCWriterConstructorArgs &args);
- virtual ~ABCInstanceWriter();
virtual void create_alembic_objects(const HierarchyContext *context) override;
virtual Alembic::Abc::OObject get_alembic_object() const override;
diff --git a/source/blender/io/alembic/exporter/abc_writer_mball.cc b/source/blender/io/alembic/exporter/abc_writer_mball.cc
index a797310f864..ad78f8ce802 100644
--- a/source/blender/io/alembic/exporter/abc_writer_mball.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_mball.cc
@@ -69,7 +69,7 @@ Mesh *ABCMetaballWriter::get_export_mesh(Object *object_eval, bool &r_needsfree)
return mesh_eval;
}
r_needsfree = true;
- return BKE_mesh_new_from_object(args_.depsgraph, object_eval, false);
+ return BKE_mesh_new_from_object(args_.depsgraph, object_eval, false, false);
}
void ABCMetaballWriter::free_export_mesh(Mesh *mesh)
diff --git a/source/blender/io/alembic/exporter/abc_writer_mesh.cc b/source/blender/io/alembic/exporter/abc_writer_mesh.cc
index fbc662113cc..fd7db005dd2 100644
--- a/source/blender/io/alembic/exporter/abc_writer_mesh.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_mesh.cc
@@ -113,10 +113,6 @@ void ABCGenericMeshWriter::create_alembic_objects(const HierarchyContext *contex
liquid_sim_modifier_ = get_liquid_sim_modifier(scene_eval, context->object);
}
-ABCGenericMeshWriter::~ABCGenericMeshWriter()
-{
-}
-
Alembic::Abc::OObject ABCGenericMeshWriter::get_alembic_object() const
{
if (is_subd_) {
@@ -204,6 +200,7 @@ void ABCGenericMeshWriter::do_write(HierarchyContext &context)
}
m_custom_data_config.pack_uvs = args_.export_params->packuv;
+ m_custom_data_config.mesh = mesh;
m_custom_data_config.mpoly = mesh->mpoly;
m_custom_data_config.mloop = mesh->mloop;
m_custom_data_config.totpoly = mesh->totpoly;
@@ -254,7 +251,7 @@ void ABCGenericMeshWriter::write_mesh(HierarchyContext &context, Mesh *mesh)
UVSample uvs_and_indices;
- if (!frame_has_been_written_ && args_.export_params->uvs) {
+ if (args_.export_params->uvs) {
const char *name = get_uv_sample(uvs_and_indices, m_custom_data_config, &mesh->ldata);
if (!uvs_and_indices.indices.empty() && !uvs_and_indices.uvs.empty()) {
@@ -283,6 +280,10 @@ void ABCGenericMeshWriter::write_mesh(HierarchyContext &context, Mesh *mesh)
mesh_sample.setNormals(normals_sample);
}
+ if (args_.export_params->orcos) {
+ write_generated_coordinates(abc_poly_mesh_schema_.getArbGeomParams(), m_custom_data_config);
+ }
+
if (liquid_sim_modifier_ != nullptr) {
get_velocities(mesh, velocities);
mesh_sample.setVelocities(V3fArraySample(velocities));
@@ -316,7 +317,7 @@ void ABCGenericMeshWriter::write_subd(HierarchyContext &context, struct Mesh *me
V3fArraySample(points), Int32ArraySample(poly_verts), Int32ArraySample(loop_counts));
UVSample sample;
- if (!frame_has_been_written_ && args_.export_params->uvs) {
+ if (args_.export_params->uvs) {
const char *name = get_uv_sample(sample, m_custom_data_config, &mesh->ldata);
if (!sample.indices.empty() && !sample.uvs.empty()) {
@@ -333,6 +334,10 @@ void ABCGenericMeshWriter::write_subd(HierarchyContext &context, struct Mesh *me
abc_subdiv_schema_.getArbGeomParams(), m_custom_data_config, &mesh->ldata, CD_MLOOPUV);
}
+ if (args_.export_params->orcos) {
+ write_generated_coordinates(abc_poly_mesh_schema_.getArbGeomParams(), m_custom_data_config);
+ }
+
if (!crease_indices.empty()) {
subdiv_sample.setCreaseIndices(Int32ArraySample(crease_indices));
subdiv_sample.setCreaseLengths(Int32ArraySample(crease_lengths));
diff --git a/source/blender/io/alembic/exporter/abc_writer_mesh.h b/source/blender/io/alembic/exporter/abc_writer_mesh.h
index ed4fb4e4514..0e1792b9dab 100644
--- a/source/blender/io/alembic/exporter/abc_writer_mesh.h
+++ b/source/blender/io/alembic/exporter/abc_writer_mesh.h
@@ -51,7 +51,6 @@ class ABCGenericMeshWriter : public ABCAbstractWriter {
public:
explicit ABCGenericMeshWriter(const ABCWriterConstructorArgs &args);
- virtual ~ABCGenericMeshWriter();
virtual void create_alembic_objects(const HierarchyContext *context) override;
virtual Alembic::Abc::OObject get_alembic_object() const override;
diff --git a/source/blender/io/alembic/intern/abc_customdata.cc b/source/blender/io/alembic/intern/abc_customdata.cc
index 66e05504303..ccf353595c9 100644
--- a/source/blender/io/alembic/intern/abc_customdata.cc
+++ b/source/blender/io/alembic/intern/abc_customdata.cc
@@ -22,12 +22,14 @@
*/
#include "abc_customdata.h"
+#include "abc_axis_conversion.h"
#include <Alembic/AbcGeom/All.h>
#include <algorithm>
#include <unordered_map>
#include "DNA_customdata_types.h"
+#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "BLI_math_base.h"
@@ -50,8 +52,13 @@ using Alembic::Abc::V2fArraySample;
using Alembic::AbcGeom::OC4fGeomParam;
using Alembic::AbcGeom::OV2fGeomParam;
+using Alembic::AbcGeom::OV3fGeomParam;
namespace blender::io::alembic {
+/* ORCO, Generated Coordinates, and Reference Points ("Pref") are all terms for the same thing.
+ * Other applications (Maya, Houdini) write these to a property called "Pref". */
+static const std::string propNameOriginalCoordinates("Pref");
+
static void get_uvs(const CDStreamConfig &config,
std::vector<Imath::V2f> &uvs,
std::vector<uint32_t> &uvidx,
@@ -222,6 +229,32 @@ static void write_mcol(const OCompoundProperty &prop,
param.set(sample);
}
+void write_generated_coordinates(const OCompoundProperty &prop, CDStreamConfig &config)
+{
+ const void *customdata = CustomData_get_layer(&config.mesh->vdata, CD_ORCO);
+ if (customdata == nullptr) {
+ /* Data not available, so don't even bother creating an Alembic property for it. */
+ return;
+ }
+ const float(*orcodata)[3] = static_cast<const float(*)[3]>(customdata);
+
+ /* Convert 3D vertices from float[3] z=up to V3f y=up. */
+ std::vector<Imath::V3f> coords(config.totvert);
+ float orco_yup[3];
+ for (int vertex_idx = 0; vertex_idx < config.totvert; vertex_idx++) {
+ copy_yup_from_zup(orco_yup, orcodata[vertex_idx]);
+ coords[vertex_idx].setValue(orco_yup[0], orco_yup[1], orco_yup[2]);
+ }
+
+ if (!config.abc_ocro.valid()) {
+ /* Create the Alembic property and keep a reference so future frames can reuse it. */
+ config.abc_ocro = OV3fGeomParam(prop, propNameOriginalCoordinates, false, kVertexScope, 1);
+ }
+
+ OV3fGeomParam::Sample sample(coords, kVertexScope);
+ config.abc_ocro.set(sample);
+}
+
void write_custom_data(const OCompoundProperty &prop,
CDStreamConfig &config,
CustomData *data,
@@ -263,6 +296,7 @@ using Alembic::Abc::PropertyHeader;
using Alembic::AbcGeom::IC3fGeomParam;
using Alembic::AbcGeom::IC4fGeomParam;
using Alembic::AbcGeom::IV2fGeomParam;
+using Alembic::AbcGeom::IV3fGeomParam;
static void read_uvs(const CDStreamConfig &config,
void *data,
@@ -448,6 +482,44 @@ static void read_custom_data_uvs(const ICompoundProperty &prop,
read_uvs(config, cd_data, sample.getVals(), sample.getIndices());
}
+void read_generated_coordinates(const ICompoundProperty &prop,
+ const CDStreamConfig &config,
+ const Alembic::Abc::ISampleSelector &iss)
+{
+ if (prop.getPropertyHeader(propNameOriginalCoordinates) == nullptr) {
+ /* The ORCO property isn't there, so don't bother trying to process it. */
+ return;
+ }
+
+ IV3fGeomParam param(prop, propNameOriginalCoordinates);
+ if (!param.valid() || param.isIndexed()) {
+ /* Invalid or indexed coordinates aren't supported. */
+ return;
+ }
+ if (param.getScope() != kVertexScope) {
+ /* These are original vertex coordinates, so must be vertex-scoped. */
+ return;
+ }
+
+ IV3fGeomParam::Sample sample = param.getExpandedValue(iss);
+ Alembic::AbcGeom::V3fArraySamplePtr abc_ocro = sample.getVals();
+ const size_t totvert = abc_ocro.get()->size();
+
+ void *cd_data;
+ if (CustomData_has_layer(&config.mesh->vdata, CD_ORCO)) {
+ cd_data = CustomData_get_layer(&config.mesh->vdata, CD_ORCO);
+ }
+ else {
+ cd_data = CustomData_add_layer(&config.mesh->vdata, CD_ORCO, CD_CALLOC, nullptr, totvert);
+ }
+
+ float(*orcodata)[3] = static_cast<float(*)[3]>(cd_data);
+ for (int vertex_idx = 0; vertex_idx < totvert; ++vertex_idx) {
+ const Imath::V3f &abc_coords = (*abc_ocro)[vertex_idx];
+ copy_zup_from_yup(orcodata[vertex_idx], abc_coords.getValue());
+ }
+}
+
void read_custom_data(const std::string &iobject_full_name,
const ICompoundProperty &prop,
const CDStreamConfig &config,
diff --git a/source/blender/io/alembic/intern/abc_customdata.h b/source/blender/io/alembic/intern/abc_customdata.h
index 4eb515f132c..9ee964c0545 100644
--- a/source/blender/io/alembic/intern/abc_customdata.h
+++ b/source/blender/io/alembic/intern/abc_customdata.h
@@ -72,12 +72,16 @@ struct CDStreamConfig {
const char **modifier_error_message;
- /* Alembic needs Blender to keep references to C++ objects (the destructors
- * finalize the writing to ABC). This map stores OV2fGeomParam objects for the
- * 2nd and subsequent UV maps; the primary UV map is kept alive by the Alembic
- * mesh sample itself. */
+ /* Alembic needs Blender to keep references to C++ objects (the destructors finalize the writing
+ * to ABC). The following fields are all used to keep these references. */
+
+ /* Mapping from UV map name to its ABC property, for the 2nd and subsequent UV maps; the primary
+ * UV map is kept alive by the Alembic mesh sample itself. */
std::map<std::string, Alembic::AbcGeom::OV2fGeomParam> abc_uv_maps;
+ /* OCRO coordinates, aka Generated Coordinates. */
+ Alembic::AbcGeom::OV3fGeomParam abc_ocro;
+
CDStreamConfig()
: mloop(NULL),
totloop(0),
@@ -102,6 +106,12 @@ struct CDStreamConfig {
* For now the active layer is used, maybe needs a better way to choose this. */
const char *get_uv_sample(UVSample &sample, const CDStreamConfig &config, CustomData *data);
+void write_generated_coordinates(const OCompoundProperty &prop, CDStreamConfig &config);
+
+void read_generated_coordinates(const ICompoundProperty &prop,
+ const CDStreamConfig &config,
+ const Alembic::Abc::ISampleSelector &iss);
+
void write_custom_data(const OCompoundProperty &prop,
CDStreamConfig &config,
CustomData *data,
diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.cc b/source/blender/io/alembic/intern/abc_reader_mesh.cc
index 8133f615080..11b6c1c18ca 100644
--- a/source/blender/io/alembic/intern/abc_reader_mesh.cc
+++ b/source/blender/io/alembic/intern/abc_reader_mesh.cc
@@ -439,6 +439,7 @@ static void read_mesh_sample(const std::string &iobject_full_name,
if ((settings->read_flag & MOD_MESHSEQ_READ_VERT) != 0) {
read_mverts(config, abc_mesh_data);
+ read_generated_coordinates(schema.getArbGeomParams(), config, selector);
}
if ((settings->read_flag & MOD_MESHSEQ_READ_POLY) != 0) {
@@ -558,7 +559,7 @@ void AbcMeshReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelec
/* XXX fixme after 2.80; mesh->flag isn't copied by BKE_mesh_nomain_to_mesh() */
/* read_mesh can be freed by BKE_mesh_nomain_to_mesh(), so get the flag before that happens. */
short autosmooth = (read_mesh->flag & ME_AUTOSMOOTH);
- BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object, &CD_MASK_MESH, true);
+ BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object, &CD_MASK_EVERYTHING, true);
mesh->flag |= autosmooth;
}
@@ -868,7 +869,7 @@ void AbcSubDReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelec
Mesh *read_mesh = this->read_mesh(mesh, sample_sel, MOD_MESHSEQ_READ_ALL, nullptr);
if (read_mesh != mesh) {
- BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object, &CD_MASK_MESH, true);
+ BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object, &CD_MASK_EVERYTHING, true);
}
ISubDSchema::Sample sample;
diff --git a/source/blender/io/alembic/intern/abc_reader_object.cc b/source/blender/io/alembic/intern/abc_reader_object.cc
index 5ca7022bb36..d428d98fdb9 100644
--- a/source/blender/io/alembic/intern/abc_reader_object.cc
+++ b/source/blender/io/alembic/intern/abc_reader_object.cc
@@ -97,10 +97,6 @@ void AbcObjectReader::determine_inherits_xform()
}
}
-AbcObjectReader::~AbcObjectReader()
-{
-}
-
const IObject &AbcObjectReader::iobject() const
{
return m_iobject;
diff --git a/source/blender/io/alembic/intern/abc_reader_object.h b/source/blender/io/alembic/intern/abc_reader_object.h
index 8e00ed42777..dacdcf3f722 100644
--- a/source/blender/io/alembic/intern/abc_reader_object.h
+++ b/source/blender/io/alembic/intern/abc_reader_object.h
@@ -100,7 +100,7 @@ class AbcObjectReader {
public:
explicit AbcObjectReader(const Alembic::Abc::IObject &object, ImportSettings &settings);
- virtual ~AbcObjectReader();
+ virtual ~AbcObjectReader() = default;
const Alembic::Abc::IObject &iobject() const;
diff --git a/source/blender/io/collada/DocumentImporter.cpp b/source/blender/io/collada/DocumentImporter.cpp
index 214b5207a96..beadfc98c74 100644
--- a/source/blender/io/collada/DocumentImporter.cpp
+++ b/source/blender/io/collada/DocumentImporter.cpp
@@ -817,6 +817,8 @@ void DocumentImporter::write_profile_COMMON(COLLADAFW::EffectCommon *ef, Materia
matNode.set_ambient(ef->getAmbient());
matNode.set_specular(ef->getSpecular());
matNode.set_reflective(ef->getReflective());
+
+ matNode.update_material_nodetree();
}
/**
diff --git a/source/blender/io/collada/ErrorHandler.cpp b/source/blender/io/collada/ErrorHandler.cpp
index 844065e3ba3..86349e04ff6 100644
--- a/source/blender/io/collada/ErrorHandler.cpp
+++ b/source/blender/io/collada/ErrorHandler.cpp
@@ -36,11 +36,6 @@ ErrorHandler::ErrorHandler() : mError(false)
}
//--------------------------------------------------------------------
-ErrorHandler::~ErrorHandler()
-{
-}
-
-//--------------------------------------------------------------------
bool ErrorHandler::handleError(const COLLADASaxFWL::IError *error)
{
/* This method must return false when Collada should continue.
diff --git a/source/blender/io/collada/ErrorHandler.h b/source/blender/io/collada/ErrorHandler.h
index 1da17dbed42..f5fa9a4ad91 100644
--- a/source/blender/io/collada/ErrorHandler.h
+++ b/source/blender/io/collada/ErrorHandler.h
@@ -34,8 +34,6 @@ class ErrorHandler : public COLLADASaxFWL::IErrorHandler {
/** Constructor. */
ErrorHandler();
- /** Destructor. */
- virtual ~ErrorHandler();
/** handle any error thrown by the parser. */
bool virtual handleError(const COLLADASaxFWL::IError *error);
/** True if there was an error during parsing. */
diff --git a/source/blender/io/collada/ExtraHandler.cpp b/source/blender/io/collada/ExtraHandler.cpp
index 11cb75fb5e9..78785e9aead 100644
--- a/source/blender/io/collada/ExtraHandler.cpp
+++ b/source/blender/io/collada/ExtraHandler.cpp
@@ -30,10 +30,6 @@ ExtraHandler::ExtraHandler(DocumentImporter *dimp, AnimationImporter *aimp)
this->aimp = aimp;
}
-ExtraHandler::~ExtraHandler()
-{
-}
-
bool ExtraHandler::elementBegin(const char *elementName, const char **attributes)
{
/* \todo attribute handling for profile tags */
diff --git a/source/blender/io/collada/ExtraHandler.h b/source/blender/io/collada/ExtraHandler.h
index 4b13aaca2aa..98e30318aad 100644
--- a/source/blender/io/collada/ExtraHandler.h
+++ b/source/blender/io/collada/ExtraHandler.h
@@ -40,9 +40,6 @@ class ExtraHandler : public COLLADASaxFWL::IExtraDataCallbackHandler {
/** Constructor. */
ExtraHandler(DocumentImporter *dimp, AnimationImporter *aimp);
- /** Destructor. */
- virtual ~ExtraHandler();
-
/** Handle the beginning of an element. */
bool elementBegin(const char *elementName, const char **attributes);
diff --git a/source/blender/io/collada/ExtraTags.cpp b/source/blender/io/collada/ExtraTags.cpp
index 8c63a21f88a..f38b0d769d7 100644
--- a/source/blender/io/collada/ExtraTags.cpp
+++ b/source/blender/io/collada/ExtraTags.cpp
@@ -32,9 +32,7 @@ ExtraTags::ExtraTags(std::string profile)
this->tags = std::map<std::string, std::string>();
}
-ExtraTags::~ExtraTags()
-{
-}
+ExtraTags::~ExtraTags() = default;
bool ExtraTags::isProfile(std::string profile)
{
diff --git a/source/blender/io/collada/Materials.cpp b/source/blender/io/collada/Materials.cpp
index 6ba31599fcd..ac4c65464c8 100644
--- a/source/blender/io/collada/Materials.cpp
+++ b/source/blender/io/collada/Materials.cpp
@@ -25,8 +25,6 @@ MaterialNode::MaterialNode(bContext *C, Material *ma, KeyImageMap &key_image_map
shader_node = add_node(SH_NODE_BSDF_PRINCIPLED, 0, 300, "");
output_node = add_node(SH_NODE_OUTPUT_MATERIAL, 300, 300, "");
add_link(shader_node, 0, output_node, 0);
-
- ntreeUpdateTree(CTX_data_main(C), ntree);
}
}
@@ -61,8 +59,6 @@ MaterialNode::MaterialNode(bContext *C,
shader_node = add_node(SH_NODE_BSDF_PRINCIPLED, 0, 300, "");
output_node = add_node(SH_NODE_OUTPUT_MATERIAL, 300, 300, "");
add_link(shader_node, 0, output_node, 0);
-
- ntreeUpdateTree(CTX_data_main(C), ntree);
#endif
}
@@ -109,6 +105,11 @@ bNodeTree *MaterialNode::prepare_material_nodetree()
return ntree;
}
+void MaterialNode::update_material_nodetree()
+{
+ ntreeUpdateTree(CTX_data_main(mContext), ntree);
+}
+
bNode *MaterialNode::add_node(int node_type, int locx, int locy, std::string label)
{
bNode *node = nodeAddStaticNode(mContext, ntree, node_type);
@@ -132,6 +133,19 @@ void MaterialNode::add_link(bNode *from_node, int from_index, bNode *to_node, in
nodeAddLink(ntree, from_node, from_socket, to_node, to_socket);
}
+void MaterialNode::add_link(bNode *from_node,
+ const char *from_label,
+ bNode *to_node,
+ const char *to_label)
+{
+ bNodeSocket *from_socket = nodeFindSocket(from_node, SOCK_OUT, from_label);
+ bNodeSocket *to_socket = nodeFindSocket(to_node, SOCK_IN, to_label);
+
+ if (from_socket && to_socket) {
+ nodeAddLink(ntree, from_node, from_socket, to_node, to_socket);
+ }
+}
+
void MaterialNode::set_reflectivity(COLLADAFW::FloatOrParam &val)
{
float reflectivity = val.getFloatValue();
@@ -217,22 +231,32 @@ void MaterialNode::set_alpha(COLLADAFW::EffectCommon::OpaqueMode mode,
void MaterialNode::set_diffuse(COLLADAFW::ColorOrTexture &cot)
{
int locy = -300 * (node_map.size() - 2);
- if (cot.isColor()) {
- COLLADAFW::Color col = cot.getColor();
- bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Base Color");
- float *fcol = (float *)socket->default_value;
- fcol[0] = material->r = col.getRed();
- fcol[1] = material->g = col.getGreen();
- fcol[2] = material->b = col.getBlue();
- fcol[3] = material->a = col.getAlpha();
- }
- else if (cot.isTexture()) {
+ if (cot.isTexture()) {
bNode *texture_node = add_texture_node(cot, -300, locy, "Base Color");
if (texture_node != nullptr) {
add_link(texture_node, 0, shader_node, 0);
}
}
+ else {
+ bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Base Color");
+ float *fcol = (float *)socket->default_value;
+
+ if (cot.isColor()) {
+ COLLADAFW::Color col = cot.getColor();
+ fcol[0] = material->r = col.getRed();
+ fcol[1] = material->g = col.getGreen();
+ fcol[2] = material->b = col.getBlue();
+ fcol[3] = material->a = col.getAlpha();
+ }
+ else {
+ /* no diffuse term = same as black */
+ fcol[0] = material->r = 0.0f;
+ fcol[1] = material->g = 0.0f;
+ fcol[2] = material->b = 0.0f;
+ fcol[3] = material->a = 1.0f;
+ }
+ }
}
Image *MaterialNode::get_diffuse_image()
@@ -325,7 +349,7 @@ void MaterialNode::set_emission(COLLADAFW::ColorOrTexture &cot)
else if (cot.isTexture()) {
bNode *texture_node = add_texture_node(cot, -300, locy, "Emission");
if (texture_node != nullptr) {
- add_link(texture_node, 0, shader_node, 0);
+ add_link(texture_node, "Color", shader_node, "Emission");
}
}
@@ -362,18 +386,38 @@ void MaterialNode::set_opacity(COLLADAFW::ColorOrTexture &cot)
void MaterialNode::set_specular(COLLADAFW::ColorOrTexture &cot)
{
+ bool has_specularity = true;
int locy = -300 * (node_map.size() - 2);
if (cot.isColor()) {
COLLADAFW::Color col = cot.getColor();
- bNode *node = add_node(SH_NODE_RGB, -300, locy, "Specular");
- set_color(node, col);
- /* TODO: Connect node */
+
+ if (col.getRed() == 0 && col.getGreen() == 0 && col.getBlue() == 0) {
+ has_specularity = false;
+ }
+ else {
+ bNode *node = add_node(SH_NODE_RGB, -300, locy, "Specular");
+ set_color(node, col);
+ /* TODO: Connect node */
+ }
}
- /* texture */
else if (cot.isTexture()) {
add_texture_node(cot, -300, locy, "Specular");
/* TODO: Connect node */
}
+ else {
+ /* no specular term) */
+ has_specularity = false;
+ }
+
+ if (!has_specularity) {
+ /* If specularity is black or not defined reset the Specular value to 0
+ TODO: This is a solution only for a corner case. We must find a better
+ way to handle specularity in general. Also note that currently we
+ do not export specularity values, see EffectExporter::operator()
+ */
+ bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Specular");
+ ((bNodeSocketValueFloat *)socket->default_value)->value = 0.0f;
+ }
}
bNode *MaterialNode::add_texture_node(COLLADAFW::ColorOrTexture &cot,
diff --git a/source/blender/io/collada/Materials.h b/source/blender/io/collada/Materials.h
index f671a00758d..1886acb7a6a 100644
--- a/source/blender/io/collada/Materials.h
+++ b/source/blender/io/collada/Materials.h
@@ -48,6 +48,7 @@ class MaterialNode {
bNodeTree *prepare_material_nodetree();
bNode *add_node(int node_type, int locx, int locy, std::string label);
void add_link(bNode *from_node, int from_index, bNode *to_node, int to_index);
+ void add_link(bNode *from_node, const char *from_label, bNode *to_node, const char *to_label);
bNode *add_texture_node(COLLADAFW::ColorOrTexture &cot, int locx, int locy, std::string label);
void setShaderType();
@@ -68,4 +69,6 @@ class MaterialNode {
void set_alpha(COLLADAFW::EffectCommon::OpaqueMode mode,
COLLADAFW::ColorOrTexture &cot,
COLLADAFW::FloatOrParam &val);
+
+ void update_material_nodetree();
};
diff --git a/source/blender/io/collada/SkinInfo.cpp b/source/blender/io/collada/SkinInfo.cpp
index 8f6f1e467d9..12dee388a58 100644
--- a/source/blender/io/collada/SkinInfo.cpp
+++ b/source/blender/io/collada/SkinInfo.cpp
@@ -55,10 +55,7 @@ template<class T> static const char *bc_get_joint_name(T *node)
/* This is used to store data passed in write_controller_data.
* Arrays from COLLADAFW::SkinControllerData lose ownership, so do this class members
* so that arrays don't get freed until we free them explicitly. */
-SkinInfo::SkinInfo()
-{
- /* pass */
-}
+SkinInfo::SkinInfo() = default;
SkinInfo::SkinInfo(const SkinInfo &skin)
: weights(skin.weights),
diff --git a/source/blender/io/common/IO_abstract_hierarchy_iterator.h b/source/blender/io/common/IO_abstract_hierarchy_iterator.h
index 300c555ac8f..0bebc4384a9 100644
--- a/source/blender/io/common/IO_abstract_hierarchy_iterator.h
+++ b/source/blender/io/common/IO_abstract_hierarchy_iterator.h
@@ -123,7 +123,7 @@ struct HierarchyContext {
*/
class AbstractHierarchyWriter {
public:
- virtual ~AbstractHierarchyWriter();
+ virtual ~AbstractHierarchyWriter() = default;
virtual void write(HierarchyContext &context) = 0;
/* TODO(Sybren): add function like absent() that's called when a writer was previously created,
* but wasn't used while exporting the current frame (for example, a particle-instanced mesh of
@@ -186,9 +186,6 @@ class ObjectIdentifier {
ObjectIdentifier(Object *object, Object *duplicated_by, const PersistentID &persistent_id);
public:
- ObjectIdentifier(const ObjectIdentifier &other);
- ~ObjectIdentifier();
-
static ObjectIdentifier for_graph_root();
static ObjectIdentifier for_real_object(Object *object);
static ObjectIdentifier for_hierarchy_context(const HierarchyContext *context);
diff --git a/source/blender/io/common/intern/abstract_hierarchy_iterator.cc b/source/blender/io/common/intern/abstract_hierarchy_iterator.cc
index a33d636500f..3cda4d125d0 100644
--- a/source/blender/io/common/intern/abstract_hierarchy_iterator.cc
+++ b/source/blender/io/common/intern/abstract_hierarchy_iterator.cc
@@ -137,10 +137,6 @@ AbstractHierarchyWriter *EnsuredWriter::operator->()
return writer_;
}
-AbstractHierarchyWriter::~AbstractHierarchyWriter()
-{
-}
-
bool AbstractHierarchyWriter::check_is_animated(const HierarchyContext &context) const
{
const Object *object = context.object;
diff --git a/source/blender/io/common/intern/dupli_parent_finder.cc b/source/blender/io/common/intern/dupli_parent_finder.cc
index 73e33eff164..221e70ed587 100644
--- a/source/blender/io/common/intern/dupli_parent_finder.cc
+++ b/source/blender/io/common/intern/dupli_parent_finder.cc
@@ -25,14 +25,6 @@
namespace blender::io {
-DupliParentFinder::DupliParentFinder()
-{
-}
-
-DupliParentFinder::~DupliParentFinder()
-{
-}
-
void DupliParentFinder::insert(const DupliObject *dupli_ob)
{
dupli_set_.insert(dupli_ob->ob);
diff --git a/source/blender/io/common/intern/dupli_parent_finder.hh b/source/blender/io/common/intern/dupli_parent_finder.hh
index 3dcf037bb5e..b7632a72222 100644
--- a/source/blender/io/common/intern/dupli_parent_finder.hh
+++ b/source/blender/io/common/intern/dupli_parent_finder.hh
@@ -43,9 +43,6 @@ class DupliParentFinder final {
InstancerPIDToDuplisMap instancer_pid_to_duplis_;
public:
- DupliParentFinder();
- ~DupliParentFinder();
-
void insert(const DupliObject *dupli_ob);
bool is_duplicated(const Object *object) const;
diff --git a/source/blender/io/common/intern/object_identifier.cc b/source/blender/io/common/intern/object_identifier.cc
index 5d0b89b0630..2df1befcd69 100644
--- a/source/blender/io/common/intern/object_identifier.cc
+++ b/source/blender/io/common/intern/object_identifier.cc
@@ -35,15 +35,6 @@ ObjectIdentifier::ObjectIdentifier(Object *object,
{
}
-ObjectIdentifier::ObjectIdentifier(const ObjectIdentifier &other)
- : object(other.object), duplicated_by(other.duplicated_by), persistent_id(other.persistent_id)
-{
-}
-
-ObjectIdentifier::~ObjectIdentifier()
-{
-}
-
ObjectIdentifier ObjectIdentifier::for_real_object(Object *object)
{
return ObjectIdentifier(object, nullptr, PersistentID());
diff --git a/source/blender/io/gpencil/CMakeLists.txt b/source/blender/io/gpencil/CMakeLists.txt
index 11c9affbe5a..fec95be6aa8 100644
--- a/source/blender/io/gpencil/CMakeLists.txt
+++ b/source/blender/io/gpencil/CMakeLists.txt
@@ -39,22 +39,19 @@ set(INC_SYS
)
set(SRC
- intern/gpencil_io_capi.cc
+ intern/gpencil_io_base.cc
+ intern/gpencil_io_capi.cc
+ intern/gpencil_io_import_base.cc
+ intern/gpencil_io_import_svg.cc
- # This line must be removed if NanoSVG is moved to extern
- nanosvg/nanosvg.h
+ # This line must be removed if NanoSVG is moved to extern
+ nanosvg/nanosvg.h
- gpencil_io.h
-
- intern/gpencil_io_base.h
- intern/gpencil_io_base.cc
-
- intern/gpencil_io_import_base.h
- intern/gpencil_io_import_svg.h
- intern/gpencil_io_import_base.cc
- intern/gpencil_io_import_svg.cc
-
- intern/gpencil_io_export_base.h
+ gpencil_io.h
+ intern/gpencil_io_base.hh
+ intern/gpencil_io_export_base.hh
+ intern/gpencil_io_import_base.hh
+ intern/gpencil_io_import_svg.hh
)
set(LIB
@@ -65,8 +62,9 @@ set(LIB
if(WITH_PUGIXML)
list(APPEND SRC
- intern/gpencil_io_export_svg.h
intern/gpencil_io_export_svg.cc
+
+ intern/gpencil_io_export_svg.hh
)
list(APPEND INC_SYS
${PUGIXML_INCLUDE_DIR}
@@ -79,8 +77,9 @@ endif()
if(WITH_HARU)
list(APPEND SRC
- intern/gpencil_io_export_pdf.h
intern/gpencil_io_export_pdf.cc
+
+ intern/gpencil_io_export_pdf.hh
)
list(APPEND INC_SYS
${HARU_INCLUDE_DIRS}
diff --git a/source/blender/io/gpencil/gpencil_io.h b/source/blender/io/gpencil/gpencil_io.h
index f4b2e59f8c5..24b13479359 100644
--- a/source/blender/io/gpencil/gpencil_io.h
+++ b/source/blender/io/gpencil/gpencil_io.h
@@ -78,7 +78,7 @@ typedef enum eGpencilExportSelect {
GP_EXPORT_VISIBLE = 2,
} eGpencilExportSelect;
-/* Framerange to be exported. */
+/** Frame-range to be exported. */
typedef enum eGpencilExportFrame {
GP_EXPORT_FRAME_ACTIVE = 0,
GP_EXPORT_FRAME_SELECTED = 1,
diff --git a/source/blender/io/gpencil/intern/gpencil_io_base.cc b/source/blender/io/gpencil/intern/gpencil_io_base.cc
index 855252e648c..a2c1b8f5af6 100644
--- a/source/blender/io/gpencil/intern/gpencil_io_base.cc
+++ b/source/blender/io/gpencil/intern/gpencil_io_base.cc
@@ -41,6 +41,7 @@
#include "BKE_gpencil_geom.h"
#include "BKE_main.h"
#include "BKE_material.h"
+#include "BKE_scene.h"
#include "UI_view2d.h"
@@ -49,7 +50,7 @@
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
-#include "gpencil_io_base.h"
+#include "gpencil_io_base.hh"
using blender::Span;
@@ -69,7 +70,21 @@ GpencilIO::GpencilIO(const GpencilIOParams *iparams)
cfra_ = iparams->frame_cur;
/* Calculate camera matrix. */
- Object *cam_ob = params_.v3d->camera;
+ prepare_camera_params(scene_, iparams);
+}
+
+void GpencilIO::prepare_camera_params(Scene *scene, const GpencilIOParams *iparams)
+{
+ params_ = *iparams;
+ const bool is_pdf = params_.mode == GP_EXPORT_TO_PDF;
+ const bool any_camera = (params_.v3d->camera != nullptr);
+ const bool force_camera_view = is_pdf && any_camera;
+
+ /* Ensure camera switch is applied. */
+ BKE_scene_camera_switch_update(scene);
+
+ /* Calculate camera matrix. */
+ Object *cam_ob = scene->camera;
if (cam_ob != nullptr) {
/* Set up parameters. */
CameraParams params;
@@ -85,16 +100,18 @@ GpencilIO::GpencilIO(const GpencilIOParams *iparams)
invert_m4_m4(viewmat, cam_ob->obmat);
mul_m4_m4m4(persmat_, params.winmat, viewmat);
+ is_ortho_ = params.is_ortho;
}
else {
unit_m4(persmat_);
+ is_ortho_ = false;
}
winx_ = params_.region->winx;
winy_ = params_.region->winy;
/* Camera rectangle. */
- if (rv3d_->persp == RV3D_CAMOB) {
+ if ((rv3d_->persp == RV3D_CAMOB) || (force_camera_view)) {
render_x_ = (scene_->r.xsch * scene_->r.size) / 100;
render_y_ = (scene_->r.ysch * scene_->r.size) / 100;
@@ -112,11 +129,14 @@ GpencilIO::GpencilIO(const GpencilIOParams *iparams)
}
else {
is_camera_ = false;
+ is_ortho_ = false;
/* Calc selected object boundbox. Need set initial value to some variables. */
camera_ratio_ = 1.0f;
offset_.x = 0.0f;
offset_.y = 0.0f;
+ create_object_list();
+
selected_objects_boundbox_calc();
rctf boundbox;
selected_objects_boundbox_get(&boundbox);
@@ -228,13 +248,15 @@ bool GpencilIO::gpencil_3D_point_to_screen_space(const float3 co, float2 &r_co)
}
/** Convert to render space. */
-float2 GpencilIO::gpencil_3D_point_to_render_space(const float3 co)
+float2 GpencilIO::gpencil_3D_point_to_render_space(const float3 co, const bool is_ortho)
{
float3 parent_co = diff_mat_ * co;
mul_m4_v3(persmat_, parent_co);
- parent_co.x = parent_co.x / max_ff(FLT_MIN, parent_co[2]);
- parent_co.y = parent_co.y / max_ff(FLT_MIN, parent_co[2]);
+ if (!is_ortho) {
+ parent_co.x = parent_co.x / max_ff(FLT_MIN, parent_co.z);
+ parent_co.y = parent_co.y / max_ff(FLT_MIN, parent_co.z);
+ }
float2 r_co;
r_co.x = (parent_co.x + 1.0f) / 2.0f * (float)render_x_;
@@ -257,7 +279,7 @@ float2 GpencilIO::gpencil_3D_point_to_2D(const float3 co)
{
const bool is_camera = (bool)(rv3d_->persp == RV3D_CAMOB);
if (is_camera) {
- return gpencil_3D_point_to_render_space(co);
+ return gpencil_3D_point_to_render_space(co, is_orthographic());
}
float2 result;
gpencil_3D_point_to_screen_space(co, result);
@@ -296,7 +318,7 @@ void GpencilIO::prepare_stroke_export_colors(Object *ob, bGPDstroke *gps)
/* Stroke color. */
copy_v4_v4(stroke_color_, gp_style->stroke_rgba);
- avg_opacity_ = 0;
+ avg_opacity_ = 0.0f;
/* Get average vertex color and apply. */
float avg_color[4] = {0.0f, 0.0f, 0.0f, 0.0f};
for (const bGPDspoint &pt : Span(gps->points, gps->totpoints)) {
@@ -324,6 +346,11 @@ bool GpencilIO::is_camera_mode()
return is_camera_;
}
+bool GpencilIO::is_orthographic()
+{
+ return is_ortho_;
+}
+
/* Calculate selected strokes boundbox. */
void GpencilIO::selected_objects_boundbox_calc()
{
diff --git a/source/blender/io/gpencil/intern/gpencil_io_base.h b/source/blender/io/gpencil/intern/gpencil_io_base.hh
index 986221618b7..c3c6f1156bb 100644
--- a/source/blender/io/gpencil/intern/gpencil_io_base.h
+++ b/source/blender/io/gpencil/intern/gpencil_io_base.hh
@@ -50,6 +50,7 @@ class GpencilIO {
GpencilIO(const GpencilIOParams *iparams);
void frame_number_set(const int value);
+ void prepare_camera_params(Scene *scene, const GpencilIOParams *iparams);
protected:
GpencilIOParams params_;
@@ -87,13 +88,14 @@ class GpencilIO {
/* Geometry functions. */
bool gpencil_3D_point_to_screen_space(const float3 co, float2 &r_co);
- float2 gpencil_3D_point_to_render_space(const float3 co);
+ float2 gpencil_3D_point_to_render_space(const float3 co, const bool is_ortho);
float2 gpencil_3D_point_to_2D(const float3 co);
float stroke_point_radius_get(struct bGPDlayer *gpl, struct bGPDstroke *gps);
void create_object_list();
bool is_camera_mode();
+ bool is_orthographic();
float stroke_average_opacity_get();
@@ -107,6 +109,7 @@ class GpencilIO {
private:
float avg_opacity_;
bool is_camera_;
+ bool is_ortho_;
rctf select_boundbox_;
/* Camera matrix. */
diff --git a/source/blender/io/gpencil/intern/gpencil_io_capi.cc b/source/blender/io/gpencil/intern/gpencil_io_capi.cc
index 231d23948ef..544c51e0b4f 100644
--- a/source/blender/io/gpencil/intern/gpencil_io_capi.cc
+++ b/source/blender/io/gpencil/intern/gpencil_io_capi.cc
@@ -21,7 +21,7 @@
* \ingroup bgpencil
*/
-#include <stdio.h>
+#include <cstdio>
#include "BLI_listbase.h"
@@ -40,14 +40,14 @@
#include "../gpencil_io.h"
#ifdef WITH_HARU
-# include "gpencil_io_export_pdf.h"
+# include "gpencil_io_export_pdf.hh"
#endif
#ifdef WITH_PUGIXML
-# include "gpencil_io_export_svg.h"
+# include "gpencil_io_export_svg.hh"
#endif
-#include "gpencil_io_import_svg.h"
+#include "gpencil_io_import_svg.hh"
#ifdef WITH_HARU
using blender::io::gpencil::GpencilExporterPDF;
@@ -121,6 +121,7 @@ static bool gpencil_io_export_pdf(Depsgraph *depsgraph,
CFRA = i;
BKE_scene_graph_update_for_newframe(depsgraph);
+ exporter->prepare_camera_params(scene, iparams);
exporter->frame_number_set(i);
exporter->add_newpage();
exporter->add_body();
@@ -129,9 +130,11 @@ static bool gpencil_io_export_pdf(Depsgraph *depsgraph,
/* Back to original frame. */
exporter->frame_number_set(iparams->frame_cur);
CFRA = iparams->frame_cur;
+ BKE_scene_camera_switch_update(scene);
BKE_scene_graph_update_for_newframe(depsgraph);
}
else {
+ exporter->prepare_camera_params(scene, iparams);
exporter->add_newpage();
exporter->add_body();
result = exporter->write();
@@ -144,6 +147,7 @@ static bool gpencil_io_export_pdf(Depsgraph *depsgraph,
/* Export current frame in SVG. */
#ifdef WITH_PUGIXML
static bool gpencil_io_export_frame_svg(GpencilExporterSVG *exporter,
+ Scene *scene,
const GpencilIOParams *iparams,
const bool newpage,
const bool body,
@@ -151,6 +155,8 @@ static bool gpencil_io_export_frame_svg(GpencilExporterSVG *exporter,
{
bool result = false;
exporter->frame_number_set(iparams->frame_cur);
+ exporter->prepare_camera_params(scene, iparams);
+
if (newpage) {
result |= exporter->add_newpage();
}
@@ -185,7 +191,7 @@ bool gpencil_io_export(const char *filename, GpencilIOParams *iparams)
#ifdef WITH_PUGIXML
case GP_EXPORT_TO_SVG: {
GpencilExporterSVG exporter = GpencilExporterSVG(filename, iparams);
- return gpencil_io_export_frame_svg(&exporter, iparams, true, true, true);
+ return gpencil_io_export_frame_svg(&exporter, scene_, iparams, true, true, true);
break;
}
#endif
diff --git a/source/blender/io/gpencil/intern/gpencil_io_export_base.h b/source/blender/io/gpencil/intern/gpencil_io_export_base.hh
index 19a24a75fd2..ffb1c6ce262 100644
--- a/source/blender/io/gpencil/intern/gpencil_io_export_base.h
+++ b/source/blender/io/gpencil/intern/gpencil_io_export_base.hh
@@ -21,7 +21,7 @@
/** \file
* \ingroup bgpencil
*/
-#include "gpencil_io_base.h"
+#include "gpencil_io_base.hh"
namespace blender::io::gpencil {
diff --git a/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc b/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc
index ba16d635c2d..0f90855dcb8 100644
--- a/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc
+++ b/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc
@@ -49,7 +49,7 @@
#include "UI_view2d.h"
#include "gpencil_io.h"
-#include "gpencil_io_export_pdf.h"
+#include "gpencil_io_export_pdf.hh"
namespace blender ::io ::gpencil {
@@ -69,7 +69,6 @@ GpencilExporterPDF::GpencilExporterPDF(const char *filename, const GpencilIOPara
pdf_ = nullptr;
page_ = nullptr;
- gstate_ = nullptr;
}
bool GpencilExporterPDF::new_document()
@@ -169,16 +168,29 @@ void GpencilExporterPDF::export_gpencil_layers()
if (!ED_gpencil_stroke_material_visible(ob, gps)) {
continue;
}
- /* Duplicate the stroke to apply any layer thickness change. */
- bGPDstroke *gps_duplicate = BKE_gpencil_stroke_duplicate(gps, true, false);
- MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob,
- gps_duplicate->mat_nr + 1);
+ /* Skip invisible lines. */
+ prepare_stroke_export_colors(ob, gps);
+ const float fill_opacity = fill_color_[3] * gpl->opacity;
+ const float stroke_opacity = stroke_color_[3] * stroke_average_opacity_get() *
+ gpl->opacity;
+ if ((fill_opacity < GPENCIL_ALPHA_OPACITY_THRESH) &&
+ (stroke_opacity < GPENCIL_ALPHA_OPACITY_THRESH)) {
+ continue;
+ }
+ MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1);
const bool is_stroke = ((gp_style->flag & GP_MATERIAL_STROKE_SHOW) &&
- (gp_style->stroke_rgba[3] > GPENCIL_ALPHA_OPACITY_THRESH));
+ (gp_style->stroke_rgba[3] > GPENCIL_ALPHA_OPACITY_THRESH) &&
+ (stroke_opacity > GPENCIL_ALPHA_OPACITY_THRESH));
const bool is_fill = ((gp_style->flag & GP_MATERIAL_FILL_SHOW) &&
(gp_style->fill_rgba[3] > GPENCIL_ALPHA_OPACITY_THRESH));
- prepare_stroke_export_colors(ob, gps_duplicate);
+
+ if ((!is_stroke) && (!is_fill)) {
+ continue;
+ }
+
+ /* Duplicate the stroke to apply any layer thickness change. */
+ bGPDstroke *gps_duplicate = BKE_gpencil_stroke_duplicate(gps, true, false);
/* Apply layer thickness change. */
gps_duplicate->thickness += gpl->line_change;
@@ -283,29 +295,35 @@ void GpencilExporterPDF::color_set(bGPDlayer *gpl, const bool do_fill)
{
const float fill_opacity = fill_color_[3] * gpl->opacity;
const float stroke_opacity = stroke_color_[3] * stroke_average_opacity_get() * gpl->opacity;
+ const bool need_state = (do_fill && fill_opacity < 1.0f) || (stroke_opacity < 1.0f);
HPDF_Page_GSave(page_);
- gstate_ = HPDF_CreateExtGState(pdf_);
+ HPDF_ExtGState gstate = (need_state) ? HPDF_CreateExtGState(pdf_) : nullptr;
float col[3];
if (do_fill) {
interp_v3_v3v3(col, fill_color_, gpl->tintcolor, gpl->tintcolor[3]);
linearrgb_to_srgb_v3_v3(col, col);
CLAMP3(col, 0.0f, 1.0f);
-
- HPDF_ExtGState_SetAlphaFill(gstate_, clamp_f(fill_opacity, 0.0f, 1.0f));
HPDF_Page_SetRGBFill(page_, col[0], col[1], col[2]);
+ if (gstate) {
+ HPDF_ExtGState_SetAlphaFill(gstate, clamp_f(fill_opacity, 0.0f, 1.0f));
+ }
}
else {
interp_v3_v3v3(col, stroke_color_, gpl->tintcolor, gpl->tintcolor[3]);
linearrgb_to_srgb_v3_v3(col, col);
CLAMP3(col, 0.0f, 1.0f);
- HPDF_ExtGState_SetAlphaFill(gstate_, clamp_f(stroke_opacity, 0.0f, 1.0f));
- HPDF_ExtGState_SetAlphaStroke(gstate_, clamp_f(stroke_opacity, 0.0f, 1.0f));
HPDF_Page_SetRGBFill(page_, col[0], col[1], col[2]);
HPDF_Page_SetRGBStroke(page_, col[0], col[1], col[2]);
+ if (gstate) {
+ HPDF_ExtGState_SetAlphaFill(gstate, clamp_f(stroke_opacity, 0.0f, 1.0f));
+ HPDF_ExtGState_SetAlphaStroke(gstate, clamp_f(stroke_opacity, 0.0f, 1.0f));
+ }
+ }
+ if (gstate) {
+ HPDF_Page_SetExtGState(page_, gstate);
}
- HPDF_Page_SetExtGState(page_, gstate_);
}
} // namespace blender::io::gpencil
diff --git a/source/blender/io/gpencil/intern/gpencil_io_export_pdf.h b/source/blender/io/gpencil/intern/gpencil_io_export_pdf.hh
index 009c05a8b49..89d97f79783 100644
--- a/source/blender/io/gpencil/intern/gpencil_io_export_pdf.h
+++ b/source/blender/io/gpencil/intern/gpencil_io_export_pdf.hh
@@ -22,7 +22,7 @@
* \ingroup bgpencil
*/
-#include "gpencil_io_export_base.h"
+#include "gpencil_io_export_base.hh"
#include "hpdf.h"
struct GpencilIOParams;
@@ -49,8 +49,6 @@ class GpencilExporterPDF : public GpencilExporter {
HPDF_Doc pdf_;
/* PDF page. */
HPDF_Page page_;
- /* State. */
- HPDF_ExtGState gstate_;
bool create_document();
bool add_page();
diff --git a/source/blender/io/gpencil/intern/gpencil_io_export_svg.cc b/source/blender/io/gpencil/intern/gpencil_io_export_svg.cc
index 89584cd242f..c62764cca06 100644
--- a/source/blender/io/gpencil/intern/gpencil_io_export_svg.cc
+++ b/source/blender/io/gpencil/intern/gpencil_io_export_svg.cc
@@ -49,7 +49,7 @@
#include "UI_view2d.h"
#include "gpencil_io.h"
-#include "gpencil_io_export_svg.h"
+#include "gpencil_io_export_svg.hh"
#include "pugixml.hpp"
@@ -119,6 +119,7 @@ void GpencilExporterSVG::create_document_header()
main_node_.append_attribute("version").set_value("1.0");
main_node_.append_attribute("x").set_value("0px");
main_node_.append_attribute("y").set_value("0px");
+ main_node_.append_attribute("xmlns").set_value("http://www.w3.org/2000/svg");
std::string width;
std::string height;
@@ -396,7 +397,7 @@ void GpencilExporterSVG::color_string_set(bGPDlayer *gpl,
* \param node: Parent node
* \param x: X location
* \param y: Y location
- * \param width: width of the recntagle
+ * \param width: width of the rectangle
* \param height: Height of the rectangle
* \param thickness: Thickness of the line
* \param hexcolor: Color of the line
@@ -427,7 +428,7 @@ void GpencilExporterSVG::add_rect(pugi::xml_node node,
* \param x: X location
* \param y: Y location
* \param text: Text to include
- * \param size: Size of th etext
+ * \param size: Size of the text
* \param hexcolor: Color of the text
*/
void GpencilExporterSVG::add_text(pugi::xml_node node,
@@ -448,7 +449,7 @@ void GpencilExporterSVG::add_text(pugi::xml_node node,
}
/** Convert a color to Hex value (#FFFFFF). */
-std::string GpencilExporterSVG::rgb_to_hexstr(float color[3])
+std::string GpencilExporterSVG::rgb_to_hexstr(const float color[3])
{
uint8_t r = color[0] * 255.0f;
uint8_t g = color[1] * 255.0f;
diff --git a/source/blender/io/gpencil/intern/gpencil_io_export_svg.h b/source/blender/io/gpencil/intern/gpencil_io_export_svg.hh
index f564736c16e..3bff31f20bf 100644
--- a/source/blender/io/gpencil/intern/gpencil_io_export_svg.h
+++ b/source/blender/io/gpencil/intern/gpencil_io_export_svg.hh
@@ -23,7 +23,7 @@
*/
#include "BLI_path_util.h"
-#include "gpencil_io_export_base.h"
+#include "gpencil_io_export_base.hh"
#include "pugixml.hpp"
struct GpencilIOParams;
@@ -70,20 +70,20 @@ class GpencilExporterSVG : public GpencilExporter {
void export_stroke_to_path(struct bGPDlayer *gpl,
struct bGPDstroke *gps,
pugi::xml_node node_gpl,
- const bool is_fill);
+ const bool do_fill);
void export_stroke_to_polyline(struct bGPDlayer *gpl,
struct bGPDstroke *gps,
pugi::xml_node node_gpl,
const bool is_stroke,
- const bool is_fill);
+ const bool do_fill);
void color_string_set(struct bGPDlayer *gpl,
struct bGPDstroke *gps,
pugi::xml_node node_gps,
- const bool is_fill);
+ const bool do_fill);
- std::string rgb_to_hexstr(float color[3]);
+ std::string rgb_to_hexstr(const float color[3]);
};
} // namespace blender::io::gpencil
diff --git a/source/blender/io/gpencil/intern/gpencil_io_import_base.cc b/source/blender/io/gpencil/intern/gpencil_io_import_base.cc
index 2e7cfdeb5cd..d0b6e20bda2 100644
--- a/source/blender/io/gpencil/intern/gpencil_io_import_base.cc
+++ b/source/blender/io/gpencil/intern/gpencil_io_import_base.cc
@@ -33,7 +33,7 @@
#include "ED_gpencil.h"
-#include "gpencil_io_import_base.h"
+#include "gpencil_io_import_base.hh"
namespace blender::io::gpencil {
diff --git a/source/blender/io/gpencil/intern/gpencil_io_import_base.h b/source/blender/io/gpencil/intern/gpencil_io_import_base.hh
index efe6264e4e9..7d6fad2340b 100644
--- a/source/blender/io/gpencil/intern/gpencil_io_import_base.h
+++ b/source/blender/io/gpencil/intern/gpencil_io_import_base.hh
@@ -21,7 +21,7 @@
/** \file
* \ingroup bgpencil
*/
-#include "gpencil_io_base.h"
+#include "gpencil_io_base.hh"
namespace blender::io::gpencil {
diff --git a/source/blender/io/gpencil/intern/gpencil_io_import_svg.cc b/source/blender/io/gpencil/intern/gpencil_io_import_svg.cc
index 7f450477ac2..52fcc017ffb 100644
--- a/source/blender/io/gpencil/intern/gpencil_io_import_svg.cc
+++ b/source/blender/io/gpencil/intern/gpencil_io_import_svg.cc
@@ -36,7 +36,7 @@
#include "ED_gpencil.h"
#include "gpencil_io.h"
-#include "gpencil_io_import_svg.h"
+#include "gpencil_io_import_svg.hh"
/* Custom flags for NanoSVG. */
#define NANOSVG_ALL_COLOR_KEYWORDS
@@ -69,9 +69,7 @@ bool GpencilImporterSVG::read()
params_.ob = create_object();
if (params_.ob == nullptr) {
std::cout << "Unable to create new object.\n";
- if (svg_data) {
- nsvgDelete(svg_data);
- }
+ nsvgDelete(svg_data);
return false;
}
@@ -102,7 +100,7 @@ bool GpencilImporterSVG::read()
bGPDlayer *gpl = (bGPDlayer *)BLI_findstring(
&gpd_->layers, layer_id, offsetof(bGPDlayer, info));
if (gpl == nullptr) {
- gpl = BKE_gpencil_layer_addnew(gpd_, layer_id, true);
+ gpl = BKE_gpencil_layer_addnew(gpd_, layer_id, true, false);
/* Disable lights. */
gpl->flag &= ~GP_LAYER_USE_LIGHTS;
}
@@ -118,15 +116,17 @@ bool GpencilImporterSVG::read()
}
/* Create_shape materials. */
- const char *const mat_names[] = {"Stroke", "Fill"};
+ const char *const mat_names[] = {"Stroke", "Fill", "Both"};
int index = 0;
- if ((is_stroke) && (is_fill)) {
+ if ((is_stroke) && (!is_fill)) {
index = 0;
- is_fill = false;
}
else if ((!is_stroke) && (is_fill)) {
index = 1;
}
+ else if ((is_stroke) && (is_fill)) {
+ index = 2;
+ }
int32_t mat_index = create_material(mat_names[index], is_stroke, is_fill);
/* Loop all paths to create the stroke data. */
@@ -196,10 +196,10 @@ void GpencilImporterSVG::create_stroke(bGPdata *gpd,
pt->strength = shape->opacity;
pt->pressure = 1.0f;
pt->z = 0.0f;
- /* TODO: (antoniov) Can be improved loading curve data instead of loading strokes. */
+ /* TODO(antoniov): Can be improved loading curve data instead of loading strokes. */
interp_v2_v2v2v2v2_cubic(&pt->x, &p[0], &p[2], &p[4], &p[6], a);
- /* Scale from milimeters. */
+ /* Scale from millimeters. */
mul_v3_fl(&pt->x, 0.001f);
mul_m4_v3(matrix, &pt->x);
diff --git a/source/blender/io/gpencil/intern/gpencil_io_import_svg.h b/source/blender/io/gpencil/intern/gpencil_io_import_svg.hh
index 6a34ec8423b..0e9271dd2c6 100644
--- a/source/blender/io/gpencil/intern/gpencil_io_import_svg.h
+++ b/source/blender/io/gpencil/intern/gpencil_io_import_svg.hh
@@ -21,7 +21,7 @@
/** \file
* \ingroup bgpencil
*/
-#include "gpencil_io_import_base.h"
+#include "gpencil_io_import_base.hh"
struct GpencilIOParams;
struct NSVGshape;
diff --git a/source/blender/io/gpencil/nanosvg/nanosvg.h b/source/blender/io/gpencil/nanosvg/nanosvg.h
index 1009d684f7c..94dad37861a 100644
--- a/source/blender/io/gpencil/nanosvg/nanosvg.h
+++ b/source/blender/io/gpencil/nanosvg/nanosvg.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-14 Mikko Mononen memon@inside.org
+ * Copyright (c) 2013-14 `Mikko Mononen <memon@inside.org>`
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
@@ -459,9 +459,9 @@ typedef struct NSVGparser {
float dpi;
char pathFlag;
char defsFlag;
- /** Blender breadscrum for layers. */
+ /** Blender breadcrumb for layers. */
char breadcrumb[NSVG_MAX_BREADCRUMB][64];
- /** Blender number of elements in breadscrum. */
+ /** Blender number of elements in breadcrumb. */
int breadcrumb_len;
} NSVGparser;
@@ -1289,7 +1289,7 @@ static const char *nsvg__parseNumber(const char *s, char *it, const int size)
return s;
}
-static const char *nsvg__getNextPathItem(const char *s, char *it)
+static const char *nsvg__getNextPathItem(const char *s, char *it, char cmd, int nargs)
{
it[0] = '\0';
// Skip white spaces and commas
@@ -1297,6 +1297,15 @@ static const char *nsvg__getNextPathItem(const char *s, char *it)
s++;
if (!*s)
return s;
+
+ /* Blender: Special case for arc command's 4th and 5th arguments. */
+ if (ELEM(cmd, 'a', 'A') && ELEM(nargs, 3, 4)) {
+ it[0] = s[0];
+ it[1] = '\0';
+ s++;
+ return s;
+ }
+
if (*s == '-' || *s == '+' || *s == '.' || nsvg__isdigit(*s)) {
s = nsvg__parseNumber(s, it, 64);
}
@@ -1576,8 +1585,8 @@ static int nsvg__isCoordinate(const char *s)
// optional sign
if (*s == '-' || *s == '+')
s++;
- // must have at least one digit
- return nsvg__isdigit(*s);
+ // must have at least one digit, or start by a dot
+ return (nsvg__isdigit(*s) || *s == '.');
}
static NSVGcoordinate nsvg__parseCoordinateRaw(const char *str)
@@ -2353,7 +2362,12 @@ static void nsvg__pathArcTo(NSVGparser *p, float *cpx, float *cpy, float *args,
// The loop assumes an iteration per end point (including start and end), this +1.
ndivs = (int)(fabsf(da) / (NSVG_PI * 0.5f) + 1.0f);
hda = (da / (float)ndivs) / 2.0f;
- kappa = fabsf(4.0f / 3.0f * (1.0f - cosf(hda)) / sinf(hda));
+ // Fix for ticket #179: division by 0: avoid cotangens around 0 (infinite)
+ if ((hda < 1e-3f) && (hda > -1e-3f))
+ hda *= 0.5f;
+ else
+ hda = (1.0f - cosf(hda)) / sinf(hda);
+ kappa = fabsf(4.0f / 3.0f * hda);
if (da < 0.0f)
kappa = -kappa;
@@ -2413,7 +2427,7 @@ static void nsvg__parsePath(NSVGparser *p, const char **attr)
nargs = 0;
while (*s) {
- s = nsvg__getNextPathItem(s, item);
+ s = nsvg__getNextPathItem(s, item, cmd, nargs);
if (!*item)
break;
if (cmd != '\0' && nsvg__isCoordinate(item)) {
@@ -2740,7 +2754,7 @@ static void nsvg__parsePoly(NSVGparser *p, const char **attr, int closeFlag)
s = attr[i + 1];
nargs = 0;
while (*s) {
- s = nsvg__getNextPathItem(s, item);
+ s = nsvg__getNextPathItem(s, item, '\0', nargs);
args[nargs++] = (float)nsvg__atof(item);
if (nargs >= 2) {
if (npts == 0)
diff --git a/source/blender/io/usd/intern/usd_writer_abstract.cc b/source/blender/io/usd/intern/usd_writer_abstract.cc
index 694fc76a446..5e66136abf1 100644
--- a/source/blender/io/usd/intern/usd_writer_abstract.cc
+++ b/source/blender/io/usd/intern/usd_writer_abstract.cc
@@ -41,10 +41,6 @@ USDAbstractWriter::USDAbstractWriter(const USDExporterContext &usd_export_contex
{
}
-USDAbstractWriter::~USDAbstractWriter()
-{
-}
-
bool USDAbstractWriter::is_supported(const HierarchyContext * /*context*/) const
{
return true;
diff --git a/source/blender/io/usd/intern/usd_writer_abstract.h b/source/blender/io/usd/intern/usd_writer_abstract.h
index 2143164e3dd..6f143a7e241 100644
--- a/source/blender/io/usd/intern/usd_writer_abstract.h
+++ b/source/blender/io/usd/intern/usd_writer_abstract.h
@@ -49,7 +49,6 @@ class USDAbstractWriter : public AbstractHierarchyWriter {
public:
USDAbstractWriter(const USDExporterContext &usd_export_context);
- virtual ~USDAbstractWriter();
virtual void write(HierarchyContext &context) override;
diff --git a/source/blender/io/usd/intern/usd_writer_metaball.cc b/source/blender/io/usd/intern/usd_writer_metaball.cc
index 8e32bd4705a..28c96c3a511 100644
--- a/source/blender/io/usd/intern/usd_writer_metaball.cc
+++ b/source/blender/io/usd/intern/usd_writer_metaball.cc
@@ -62,7 +62,7 @@ Mesh *USDMetaballWriter::get_export_mesh(Object *object_eval, bool &r_needsfree)
return mesh_eval;
}
r_needsfree = true;
- return BKE_mesh_new_from_object(usd_export_context_.depsgraph, object_eval, false);
+ return BKE_mesh_new_from_object(usd_export_context_.depsgraph, object_eval, false, false);
}
void USDMetaballWriter::free_export_mesh(Mesh *mesh)
diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h
index d88db091cc2..dd262f78f6b 100644
--- a/source/blender/makesdna/DNA_ID.h
+++ b/source/blender/makesdna/DNA_ID.h
@@ -366,7 +366,7 @@ typedef struct Library {
struct PackedFile *packedfile;
- /* Temp data needed by read/write code. */
+ /* Temp data needed by read/write code, and liboverride recursive resync. */
int temp_index;
/** See BLENDER_FILE_VERSION, BLENDER_FILE_SUBVERSION, needed for do_versions. */
short versionfile, subversionfile;
@@ -491,6 +491,11 @@ enum {
* Note that this also applies to shapekeys, even though they are not 100% embedded data...
*/
LIB_EMBEDDED_DATA_LIB_OVERRIDE = 1 << 12,
+ /**
+ * The override data-block appears to not be needed anymore after resync with linked data, but it
+ * was kept around (because e.g. detected as user-edited).
+ */
+ LIB_LIB_OVERRIDE_RESYNC_LEFTOVER = 1 << 13,
};
/**
@@ -550,8 +555,15 @@ enum {
/* RESET_AFTER_USE tag existing data before linking so we know what is new. */
LIB_TAG_PRE_EXISTING = 1 << 11,
- /* The data-block is a copy-on-write/localized version. */
+ /**
+ * The data-block is a copy-on-write/localized version.
+ *
+ * \warning This should not be cleared on existing data.
+ * If support for this is needed, see T88026 as this flag controls memory ownership
+ * of physics *shared* pointers.
+ */
LIB_TAG_COPIED_ON_WRITE = 1 << 12,
+
LIB_TAG_COPIED_ON_WRITE_EVAL_RESULT = 1 << 13,
LIB_TAG_LOCALIZED = 1 << 14,
@@ -586,14 +598,16 @@ typedef enum IDRecalcFlag {
/* ** Object transformation changed. ** */
ID_RECALC_TRANSFORM = (1 << 0),
- /* ** Object geometry changed. **
+ /* ** Geometry changed. **
*
* When object of armature type gets tagged with this flag, its pose is
* re-evaluated.
* When object of other type is tagged with this flag it makes the modifier
* stack to be re-evaluated.
* When object data type (mesh, curve, ...) gets tagged with this flag it
- * makes all objects which shares this data-block to be updated. */
+ * makes all objects which shares this data-block to be updated.
+ * When a collection gets tagged with this flag, all objects depending on the geometry and
+ * transforms on any of the objects in the collection are updated. */
ID_RECALC_GEOMETRY = (1 << 1),
/* ** Animation or time changed and animation is to be re-evaluated. ** */
diff --git a/source/blender/makesdna/DNA_action_types.h b/source/blender/makesdna/DNA_action_types.h
index 5cc525a6cff..583e56de9c2 100644
--- a/source/blender/makesdna/DNA_action_types.h
+++ b/source/blender/makesdna/DNA_action_types.h
@@ -265,9 +265,10 @@ typedef struct bPoseChannel {
* since the alternative is highly complicated - campbell
*/
struct bPoseChannel *custom_tx;
- float custom_scale;
-
- char _pad1[4];
+ float custom_scale; /* Deprecated */
+ float custom_scale_xyz[3];
+ float custom_translation[3];
+ float custom_rotation_euler[3];
/** Transforms - written in by actions or transform. */
float loc[3];
@@ -417,9 +418,9 @@ typedef enum ePchan_DrawFlag {
PCHAN_DRAW_NO_CUSTOM_BONE_SIZE = (1 << 0),
} ePchan_DrawFlag;
-#define PCHAN_CUSTOM_DRAW_SIZE(pchan) \
- (pchan)->custom_scale *( \
- ((pchan)->drawflag & PCHAN_DRAW_NO_CUSTOM_BONE_SIZE) ? 1.0f : (pchan)->bone->length)
+/* Note: It doesn't take custom_scale_xyz into account */
+#define PCHAN_CUSTOM_BONE_LENGTH(pchan) \
+ (((pchan)->drawflag & PCHAN_DRAW_NO_CUSTOM_BONE_SIZE) ? 1.0f : (pchan)->bone->length)
#ifdef DNA_DEPRECATED_ALLOW
/* PoseChannel->bboneflag */
diff --git a/source/blender/makesdna/DNA_armature_types.h b/source/blender/makesdna/DNA_armature_types.h
index 09304ce09f2..85780bc33c5 100644
--- a/source/blender/makesdna/DNA_armature_types.h
+++ b/source/blender/makesdna/DNA_armature_types.h
@@ -134,7 +134,7 @@ typedef struct bArmature {
/** ID data is older than edit-mode data (TODO: move to edit-mode struct). */
char needs_flush_to_id;
- char _pad0[7];
+ char _pad0[3];
int flag;
int drawtype;
@@ -146,6 +146,9 @@ typedef struct bArmature {
unsigned int layer_used;
/** For buttons to work, both variables in this order together. */
unsigned int layer, layer_protected;
+
+ /** Relative position of the axes on the bone, from head (0.0f) to tail (1.0f). */
+ float axes_position;
} bArmature;
/* armature->flag */
@@ -230,8 +233,10 @@ typedef enum eBone_Flag {
BONE_MULT_VG_ENV = (1 << 11),
/** bone doesn't deform geometry */
BONE_NO_DEFORM = (1 << 12),
+#ifdef DNA_DEPRECATED_ALLOW
/** set to prevent destruction of its unkeyframed pose (after transform) */
BONE_UNKEYED = (1 << 13),
+#endif
/** set to prevent hinge child bones from influencing the transform center */
BONE_HINGE_CHILD_TRANSFORM = (1 << 14),
#ifdef DNA_DEPRECATED_ALLOW
diff --git a/source/blender/makesdna/DNA_boid_types.h b/source/blender/makesdna/DNA_boid_types.h
index 882d4eb1b3b..540446ccd9d 100644
--- a/source/blender/makesdna/DNA_boid_types.h
+++ b/source/blender/makesdna/DNA_boid_types.h
@@ -97,7 +97,8 @@ typedef struct BoidRuleFollowLeader {
} BoidRuleFollowLeader;
typedef struct BoidRuleAverageSpeed {
BoidRule rule;
- float wander, level, speed, rt;
+ float wander, level, speed;
+ char _pad0[4];
} BoidRuleAverageSpeed;
typedef struct BoidRuleFight {
BoidRule rule;
@@ -178,7 +179,7 @@ typedef struct BoidState {
//} BoidSignal;
// typedef struct BoidSignalDefine {
// struct BoidSignalDefine *next, *prev;
-// int id, rt;
+// int id, _pad[4];
// char name[32];
//} BoidSignalDefine;
diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h
index a11e7d77c67..986c009ac26 100644
--- a/source/blender/makesdna/DNA_brush_types.h
+++ b/source/blender/makesdna/DNA_brush_types.h
@@ -133,7 +133,8 @@ typedef struct BrushGpencilSettings {
/** Factor to extend stroke extremes using fill tool. */
float fill_extend_fac;
- char _pad3[4];
+ /** Number of pixels to dilate fill area. */
+ int dilate_pixels;
struct CurveMapping *curve_sensitivity;
struct CurveMapping *curve_strength;
diff --git a/source/blender/makesdna/DNA_constraint_types.h b/source/blender/makesdna/DNA_constraint_types.h
index 07fbf263d80..a94bc4625df 100644
--- a/source/blender/makesdna/DNA_constraint_types.h
+++ b/source/blender/makesdna/DNA_constraint_types.h
@@ -534,6 +534,8 @@ typedef struct bRotLimitConstraint {
float zmin, zmax;
short flag;
short flag2;
+ char euler_order;
+ char _pad[3];
} bRotLimitConstraint;
/* Limit Scale Constraint */
diff --git a/source/blender/makesdna/DNA_curve_types.h b/source/blender/makesdna/DNA_curve_types.h
index 4f914089347..f6242679808 100644
--- a/source/blender/makesdna/DNA_curve_types.h
+++ b/source/blender/makesdna/DNA_curve_types.h
@@ -43,25 +43,7 @@ struct Key;
struct Material;
struct Object;
struct VFont;
-
-/* These two Lines with # tell makesdna this struct can be excluded. */
-#
-#
-typedef struct PathPoint {
- /** Grr, cant get rid of tilt yet. */
- float vec[4];
- float quat[4];
- float radius, weight;
-} PathPoint;
-
-/* These two Lines with # tell makesdna this struct can be excluded. */
-#
-#
-typedef struct Path {
- struct PathPoint *data;
- int len;
- float totdist;
-} Path;
+struct CurveEval;
/* These two Lines with # tell makesdna this struct can be excluded. */
#
@@ -72,7 +54,7 @@ typedef struct BevPoint {
float sina, cosa;
/** 3D Only. */
float dir[3], tan[3], quat[4];
- short split_tag, dupe_tag;
+ short dupe_tag;
} BevPoint;
/* These two Lines with # tell makesdna this struct can be excluded. */
@@ -319,6 +301,12 @@ typedef struct Curve {
char _pad2[6];
float fsize_realtime;
+ /**
+ * A pointer to curve data from geometry nodes, currently only set for evaluated
+ * objects by the dependency graph iterator, and owned by #geometry_set_eval.
+ */
+ struct CurveEval *curve_eval;
+
void *batch_cache;
} Curve;
@@ -347,7 +335,7 @@ enum {
CU_BACK = 1 << 2,
CU_PATH = 1 << 3,
CU_FOLLOW = 1 << 4,
- /* CU_UV_ORCO = 1 << 5, */ /* DEPRECATED */
+ CU_PATH_CLAMP = 1 << 5,
CU_DEFORM_BOUNDS_OFF = 1 << 6,
CU_STRETCH = 1 << 7,
/* CU_OFFS_PATHDIST = 1 << 8, */ /* DEPRECATED */
@@ -425,7 +413,6 @@ enum {
/* Nurb.flag */
enum {
CU_SMOOTH = 1 << 0,
- CU_2D = 1 << 3, /* moved from type since 2.4x */
};
/* Nurb.type */
diff --git a/source/blender/makesdna/DNA_effect_types.h b/source/blender/makesdna/DNA_effect_types.h
index 33f2e1b47c0..307a212a139 100644
--- a/source/blender/makesdna/DNA_effect_types.h
+++ b/source/blender/makesdna/DNA_effect_types.h
@@ -71,13 +71,15 @@ extern "C" {
typedef struct Effect {
struct Effect *next, *prev;
- short type, flag, buttype, rt;
+ short type, flag, buttype;
+ char _pad0[2];
} Effect;
typedef struct BuildEff {
struct BuildEff *next, *prev;
- short type, flag, buttype, rt;
+ short type, flag, buttype;
+ char _pad0[2];
float len, sfra;
@@ -88,7 +90,8 @@ typedef struct BuildEff {
typedef struct Particle {
float co[3], no[3];
float time, lifetime;
- short mat_nr, rt;
+ short mat_nr;
+ char _pad0[2];
} Particle;
struct Collection;
diff --git a/source/blender/makesdna/DNA_genfile.h b/source/blender/makesdna/DNA_genfile.h
index 6fcbc53d47b..74d668a1081 100644
--- a/source/blender/makesdna/DNA_genfile.h
+++ b/source/blender/makesdna/DNA_genfile.h
@@ -63,6 +63,7 @@ typedef enum eSDNA_Type {
#define SDNA_TYPE_VOID 9
SDNA_TYPE_INT64 = 10,
SDNA_TYPE_UINT64 = 11,
+ SDNA_TYPE_INT8 = 12,
} eSDNA_Type;
/**
diff --git a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h
index 2478bbe88ba..a4ab38f6022 100644
--- a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h
+++ b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h
@@ -184,6 +184,8 @@
.layer_pass = 0, \
.hardeness = 1.0f, \
.curve_intensity = NULL, \
+ .fading_end = 10.0f, \
+ .fading_end_factor = 0.2f, \
}
#define _DNA_DEFAULT_SimplifyGpencilModifierData \
@@ -251,6 +253,8 @@
.thickness_fac = 1.0f, \
.thickness = 30, \
.layer_pass = 0, \
+ .fading_end = 10.0f, \
+ .fading_end_factor = 0.2f, \
}
#define _DNA_DEFAULT_TimeGpencilModifierData \
@@ -288,11 +292,21 @@
.edge_types = LRT_EDGE_FLAG_ALL_TYPE, \
.thickness = 25, \
.opacity = 1.0f, \
- .flags = LRT_GPENCIL_MATCH_OUTPUT_VGROUP | LRT_GPENCIL_SOFT_SELECTION, \
+ .flags = LRT_GPENCIL_MATCH_OUTPUT_VGROUP, \
.crease_threshold = DEG2RAD(140.0f), \
.calculation_flags = LRT_ALLOW_DUPLI_OBJECTS | LRT_ALLOW_CLIPPING_BOUNDARIES, \
.angle_splitting_threshold = DEG2RAD(60.0f), \
.chaining_image_threshold = 0.001f, \
}
+#define _DNA_DEFAULT_LengthGpencilModifierData \
+ { \
+ .start_fac = 0.1f,\
+ .end_fac = 0.1f,\
+ .overshoot_fac = 0.01f,\
+ .pass_index = 0,\
+ .material = NULL,\
+ }
+
+
/* clang-format off */
diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h
index bc046a3b830..410212ce100 100644
--- a/source/blender/makesdna/DNA_gpencil_modifier_types.h
+++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h
@@ -54,6 +54,7 @@ typedef enum GpencilModifierType {
eGpencilModifierType_Multiply = 17,
eGpencilModifierType_Texture = 18,
eGpencilModifierType_Lineart = 19,
+ eGpencilModifierType_Length = 20,
/* Keep last. */
NUM_GREASEPENCIL_MODIFIER_TYPES,
} GpencilModifierType;
@@ -187,7 +188,12 @@ typedef struct ThickGpencilModifierData {
int thickness;
/** Custom index for passes. */
int layer_pass;
- char _pad[4];
+ /** Start/end distances of the fading effect. */
+ float fading_start;
+ float fading_end;
+ float fading_end_factor;
+ /** Fading reference object */
+ struct Object *object;
struct CurveMapping *curve_thickness;
} ThickGpencilModifierData;
@@ -199,6 +205,7 @@ typedef enum eThickGpencil_Flag {
GP_THICK_NORMALIZE = (1 << 4),
GP_THICK_INVERT_LAYERPASS = (1 << 5),
GP_THICK_INVERT_MATERIAL = (1 << 6),
+ GP_THICK_FADING = (1 << 7),
} eThickGpencil_Flag;
typedef struct TimeGpencilModifierData {
@@ -291,9 +298,16 @@ typedef struct OpacityGpencilModifierData {
int flag;
/** Main Opacity factor. */
float factor;
+ /** Fading controlling object */
+ int _pad0;
+ struct Object *object;
+ /** Start/end distances of the fading effect. */
+ float fading_start;
+ float fading_end;
+ float fading_end_factor;
/** Modify stroke, fill or both. */
char modify_color;
- char _pad[3];
+ char _pad1[3];
/** Custom index for passes. */
int layer_pass;
@@ -309,6 +323,7 @@ typedef enum eOpacityGpencil_Flag {
GP_OPACITY_INVERT_MATERIAL = (1 << 5),
GP_OPACITY_CUSTOM_CURVE = (1 << 6),
GP_OPACITY_NORMALIZE = (1 << 7),
+ GP_OPACITY_FADING = (1 << 8),
} eOpacityGpencil_Flag;
typedef struct ArrayGpencilModifierData {
@@ -470,6 +485,39 @@ typedef enum eLatticeGpencil_Flag {
GP_LATTICE_INVERT_MATERIAL = (1 << 4),
} eLatticeGpencil_Flag;
+typedef struct LengthGpencilModifierData {
+ GpencilModifierData modifier;
+ /** Material for filtering. */
+ struct Material *material;
+ /** Layer name. */
+ char layername[64];
+ /** Custom index for passes. */
+ int pass_index;
+ /** Flags. */
+ int flag;
+ /** Custom index for passes. */
+ int layer_pass;
+ /** Length. */
+ float start_fac, end_fac;
+ /** Overshoot trajectory factor. */
+ float overshoot_fac;
+ /** Modifier mode. */
+ int mode;
+ char _pad[4];
+} LengthGpencilModifierData;
+
+typedef enum eLengthGpencil_Flag {
+ GP_LENGTH_INVERT_LAYER = (1 << 0),
+ GP_LENGTH_INVERT_PASS = (1 << 1),
+ GP_LENGTH_INVERT_LAYERPASS = (1 << 2),
+ GP_LENGTH_INVERT_MATERIAL = (1 << 3),
+} eLengthGpencil_Flag;
+
+typedef enum eLengthGpencil_Type {
+ GP_LENGTH_RELATIVE = 0,
+ GP_LENGTH_ABSOLUTE = 1,
+} eLengthGpencil_Type;
+
typedef struct MirrorGpencilModifierData {
GpencilModifierData modifier;
struct Object *object;
@@ -616,6 +664,14 @@ typedef struct OffsetGpencilModifierData {
float loc[3];
float rot[3];
float scale[3];
+ /** Random Offset. */
+ float rnd_offset[3];
+ /** Random Rotation. */
+ float rnd_rot[3];
+ /** Random Scales. */
+ float rnd_scale[3];
+ /** (first element is the index) random values. */
+ int seed;
/** Custom index for passes. */
int layer_pass;
} OffsetGpencilModifierData;
@@ -626,6 +682,7 @@ typedef enum eOffsetGpencil_Flag {
GP_OFFSET_INVERT_VGROUP = (1 << 2),
GP_OFFSET_INVERT_LAYERPASS = (1 << 3),
GP_OFFSET_INVERT_MATERIAL = (1 << 4),
+ GP_OFFSET_UNIFORM_RANDOM_SCALE = (1 << 5),
} eOffsetGpencil_Flag;
typedef struct SmoothGpencilModifierData {
@@ -819,7 +876,7 @@ typedef enum eLineartGpencilModifierSource {
typedef enum eLineArtGPencilModifierFlags {
LRT_GPENCIL_INVERT_SOURCE_VGROUP = (1 << 0),
LRT_GPENCIL_MATCH_OUTPUT_VGROUP = (1 << 1),
- LRT_GPENCIL_SOFT_SELECTION = (1 << 2),
+ LRT_GPENCIL_BINARY_WEIGHTS = (1 << 2) /* Deprecated, this is removed for lack of use case. */,
LRT_GPENCIL_IS_BAKED = (1 << 3),
} eLineArtGPencilModifierFlags;
@@ -868,7 +925,7 @@ typedef struct LineartGpencilModifierData {
/* CPU mode */
float chaining_image_threshold;
- float resample_length;
+ int _pad;
/* Ported from SceneLineArt flags. */
int calculation_flags;
diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h
index 8facdca2f9c..ea3c1ff7275 100644
--- a/source/blender/makesdna/DNA_gpencil_types.h
+++ b/source/blender/makesdna/DNA_gpencil_types.h
@@ -47,7 +47,7 @@ struct Curve;
#define GP_DEFAULT_CURVE_EDIT_CORNER_ANGLE M_PI_2
#define GPENCIL_MIN_FILL_FAC 0.05f
-#define GPENCIL_MAX_FILL_FAC 5.0f
+#define GPENCIL_MAX_FILL_FAC 8.0f
/* ***************************************** */
/* GP Stroke Points */
@@ -112,6 +112,8 @@ typedef enum eGPDspoint_Flag {
GP_SPOINT_TAG = (1 << 1),
/* stroke point is temp tagged (for some editing operation) */
GP_SPOINT_TEMP_TAG = (1 << 2),
+ /* stroke point is temp tagged (for some editing operation) */
+ GP_SPOINT_TEMP_TAG2 = (1 << 3),
} eGPSPoint_Flag;
/* ***************************************** */
@@ -558,6 +560,8 @@ typedef enum eGPDlayer_Flag {
GP_LAYER_USE_MASK = (1 << 13), /*TODO: DEPRECATED */
/* Ruler Layer */
GP_LAYER_IS_RULER = (1 << 14),
+ /* Disable masks in viewlayer render */
+ GP_LAYER_DISABLE_MASKS_IN_VIEWLAYER = (1 << 15),
} eGPDlayer_Flag;
/** #bGPDlayer.onion_flag */
diff --git a/source/blender/makesdna/DNA_ipo_types.h b/source/blender/makesdna/DNA_ipo_types.h
index 8bb94976414..c5e207c4e20 100644
--- a/source/blender/makesdna/DNA_ipo_types.h
+++ b/source/blender/makesdna/DNA_ipo_types.h
@@ -77,8 +77,9 @@ typedef struct IpoCurve {
short totvert;
/** Interpolation and extrapolation modes . */
short ipo, extrap;
- /** Flag= settings; rt= ???. */
- short flag, rt;
+ /** Flag= settings. */
+ short flag;
+ char _pad0[2];
/** Minimum/maximum y-extents for curve. */
float ymin, ymax;
/** ???. */
diff --git a/source/blender/makesdna/DNA_light_defaults.h b/source/blender/makesdna/DNA_light_defaults.h
index f3ccf14ac5b..5e5ce4bf540 100644
--- a/source/blender/makesdna/DNA_light_defaults.h
+++ b/source/blender/makesdna/DNA_light_defaults.h
@@ -68,6 +68,7 @@
.volume_fac = 1.0f, \
.att_dist = 40.0f, \
.sun_angle = DEG2RADF(0.526f), \
+ .area_spread = DEG2RADF(180.0f), \
}
/** \} */
diff --git a/source/blender/makesdna/DNA_light_types.h b/source/blender/makesdna/DNA_light_types.h
index 3b7440aedd2..82ff3c95834 100644
--- a/source/blender/makesdna/DNA_light_types.h
+++ b/source/blender/makesdna/DNA_light_types.h
@@ -70,9 +70,9 @@ typedef struct Light {
short area_shape;
float area_size, area_sizey, area_sizez;
+ float area_spread;
float sun_angle;
- char _pad3[4];
/* texact is for buttons */
short texact, shadhalostep;
diff --git a/source/blender/makesdna/DNA_lineart_types.h b/source/blender/makesdna/DNA_lineart_types.h
index 31e221b74a0..860a6579bf7 100644
--- a/source/blender/makesdna/DNA_lineart_types.h
+++ b/source/blender/makesdna/DNA_lineart_types.h
@@ -35,9 +35,6 @@
#include "DNA_ID.h"
#include "DNA_listBase.h"
-struct Object;
-struct Material;
-
/* Notice that we need to have this file although no struct defines.
* Edge flags and usage flags are used by with scene/object/gpencil modifier bits, and those values
* needs to stay consistent throughout. */
diff --git a/source/blender/makesdna/DNA_mesh_defaults.h b/source/blender/makesdna/DNA_mesh_defaults.h
index 8326db66049..889f92eec95 100644
--- a/source/blender/makesdna/DNA_mesh_defaults.h
+++ b/source/blender/makesdna/DNA_mesh_defaults.h
@@ -37,6 +37,7 @@
.face_sets_color_seed = 0, \
.face_sets_color_default = 1, \
.flag = ME_REMESH_FIX_POLES | ME_REMESH_REPROJECT_VOLUME, \
+ .editflag = ME_EDIT_MIRROR_VERTEX_GROUPS \
}
/** \} */
diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h
index af20a63b907..3eb5920dfe6 100644
--- a/source/blender/makesdna/DNA_mesh_types.h
+++ b/source/blender/makesdna/DNA_mesh_types.h
@@ -263,7 +263,7 @@ enum {
/* me->editflag */
enum {
- ME_EDIT_VERTEX_GROUPS_X_SYMMETRY = 1 << 0,
+ ME_EDIT_MIRROR_VERTEX_GROUPS = 1 << 0,
ME_EDIT_MIRROR_Y = 1 << 1, /* unused so far */
ME_EDIT_MIRROR_Z = 1 << 2, /* unused so far */
@@ -272,7 +272,11 @@ enum {
ME_EDIT_PAINT_VERT_SEL = 1 << 5,
};
-/* we cant have both flags enabled at once,
+/* Helper macro to see if vertex group X mirror is on. */
+#define ME_USING_MIRROR_X_VERTEX_GROUPS(_me) \
+ (((_me)->editflag & ME_EDIT_MIRROR_VERTEX_GROUPS) && ((_me)->symmetry & ME_SYMMETRY_X))
+
+/* We cant have both flags enabled at once,
* flags defined in DNA_scene_types.h */
#define ME_EDIT_PAINT_SEL_MODE(_me) \
(((_me)->editflag & ME_EDIT_PAINT_FACE_SEL) ? \
diff --git a/source/blender/makesdna/DNA_modifier_defaults.h b/source/blender/makesdna/DNA_modifier_defaults.h
index 9335c360363..f6dac88051b 100644
--- a/source/blender/makesdna/DNA_modifier_defaults.h
+++ b/source/blender/makesdna/DNA_modifier_defaults.h
@@ -414,7 +414,8 @@
{ \
.cache_file = NULL, \
.object_path = "", \
- .read_flag = MOD_MESHSEQ_READ_VERT | MOD_MESHSEQ_READ_POLY | MOD_MESHSEQ_READ_UV | MOD_MESHSEQ_READ_COLOR, \
+ .read_flag = MOD_MESHSEQ_READ_VERT | MOD_MESHSEQ_READ_POLY | MOD_MESHSEQ_READ_UV | \
+ MOD_MESHSEQ_READ_COLOR | MOD_MESHSEQ_INTERPOLATE_VERTICES, \
.velocity_scale = 1.0f, \
.reader = NULL, \
.reader_object_path = "", \
@@ -428,6 +429,7 @@
{ \
.flag = MOD_MIR_AXIS_X | MOD_MIR_VGROUP, \
.tolerance = 0.001f, \
+ .bisect_threshold = 0.001f, \
.uv_offset = {0.0f, 0.0f}, \
.uv_offset_copy = {0.0f, 0.0f}, \
.mirror_ob = NULL, \
diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h
index ca6f1467d9c..c61e940190f 100644
--- a/source/blender/makesdna/DNA_modifier_types.h
+++ b/source/blender/makesdna/DNA_modifier_types.h
@@ -368,6 +368,8 @@ typedef struct MirrorModifierData {
short axis DNA_DEPRECATED;
short flag;
float tolerance;
+ float bisect_threshold;
+ char _pad[4];
float uv_offset[2];
float uv_offset_copy[2];
struct Object *mirror_ob;
diff --git a/source/blender/makesdna/DNA_movieclip_defaults.h b/source/blender/makesdna/DNA_movieclip_defaults.h
index 4aa1bd779c2..753147eb072 100644
--- a/source/blender/makesdna/DNA_movieclip_defaults.h
+++ b/source/blender/makesdna/DNA_movieclip_defaults.h
@@ -32,7 +32,7 @@
.build_size_flag = IMB_PROXY_25, \
.build_tc_flag = IMB_TC_RECORD_RUN | IMB_TC_FREE_RUN | \
IMB_TC_INTERPOLATED_REC_DATE_FREE_RUN | IMB_TC_RECORD_RUN_NO_GAPS, \
- .quality = 90, \
+ .quality = 50, \
}
diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h
index f24d0e40d19..2ae48bfe0ad 100644
--- a/source/blender/makesdna/DNA_node_types.h
+++ b/source/blender/makesdna/DNA_node_types.h
@@ -45,6 +45,8 @@ struct bNodePreview;
struct bNodeTreeExec;
struct bNodeType;
struct uiBlock;
+struct Tex;
+struct Material;
#define NODE_MAXSTR 64
@@ -98,8 +100,8 @@ typedef struct bNodeSocket {
void *storage;
short type, flag;
- /** Max. number of links. Read via nodeSocketLinkLimit, because the limit might be defined on the
- * socket type. */
+ /** Max. number of links. Read via nodeSocketLinkLimit,
+ * because the limit might be defined on the socket type. */
short limit;
/** Input/output type. */
short in_out;
@@ -125,6 +127,7 @@ typedef struct bNodeSocket {
/** Custom dynamic defined label, MAX_NAME. */
char label[64];
+ char description[64];
/** Cached data from execution. */
void *cache;
@@ -164,9 +167,11 @@ typedef enum eNodeSocketDatatype {
SOCK_IMAGE = 9,
SOCK_GEOMETRY = 10,
SOCK_COLLECTION = 11,
+ SOCK_TEXTURE = 12,
+ SOCK_MATERIAL = 13,
} eNodeSocketDatatype;
-/* socket shape */
+/* Socket shape. */
typedef enum eNodeSocketDisplayShape {
SOCK_DISPLAY_SHAPE_CIRCLE = 0,
SOCK_DISPLAY_SHAPE_SQUARE = 1,
@@ -176,38 +181,43 @@ typedef enum eNodeSocketDisplayShape {
SOCK_DISPLAY_SHAPE_DIAMOND_DOT = 5,
} eNodeSocketDisplayShape;
-/* socket side (input/output) */
+/* Socket side (input/output). */
typedef enum eNodeSocketInOut {
SOCK_IN = 1 << 0,
SOCK_OUT = 1 << 1,
} eNodeSocketInOut;
-/* sock->flag, first bit is select */
+/* #bNodeSocket.flag, first bit is selection. */
typedef enum eNodeSocketFlag {
- /** hidden is user defined, to hide unused */
+ /** Hidden is user defined, to hide unused sockets. */
SOCK_HIDDEN = (1 << 1),
- /** for quick check if socket is linked */
+ /** For quick check if socket is linked. */
SOCK_IN_USE = (1 << 2),
- /** unavailable is for dynamic sockets */
+ /** Unavailable is for dynamic sockets. */
SOCK_UNAVAIL = (1 << 3),
// /** DEPRECATED dynamic socket (can be modified by user) */
// SOCK_DYNAMIC = (1 << 4),
// /** DEPRECATED group socket should not be exposed */
// SOCK_INTERNAL = (1 << 5),
- /** socket collapsed in UI */
+ /** Socket collapsed in UI. */
SOCK_COLLAPSED = (1 << 6),
- /** hide socket value, if it gets auto default */
+ /** Hide socket value, if it gets auto default. */
SOCK_HIDE_VALUE = (1 << 7),
- /** socket hidden automatically, to distinguish from manually hidden */
+ /** Socket hidden automatically, to distinguish from manually hidden. */
SOCK_AUTO_HIDDEN__DEPRECATED = (1 << 8),
SOCK_NO_INTERNAL_LINK = (1 << 9),
/** Draw socket in a more compact form. */
SOCK_COMPACT = (1 << 10),
/** Make the input socket accept multiple incoming links in the UI. */
SOCK_MULTI_INPUT = (1 << 11),
+ /**
+ * Don't show the socket's label in the interface, for situations where the
+ * type is obvious and the name takes up too much space.
+ */
+ SOCK_HIDE_LABEL = (1 << 12),
} eNodeSocketFlag;
-/* limit data in bNode to what we want to see saved? */
+/* TODO: Limit data in bNode to what we want to see saved. */
typedef struct bNode {
struct bNode *next, *prev, *new_node;
@@ -348,6 +358,8 @@ typedef struct bNode {
* composite out nodes when editing tree
*/
#define NODE_DO_OUTPUT_RECALC (1 << 17)
+/* A preview for the data in this node can be displayed in the spreadsheet editor. */
+#define NODE_ACTIVE_PREVIEW (1 << 18)
/* node->update */
/* XXX NODE_UPDATE is a generic update flag. More fine-grained updates
@@ -454,7 +466,6 @@ typedef struct bNodeTree {
short is_updating;
/** Generic temporary flag for recursion check (DFS/BFS). */
short done;
- char _pad2[4];
/** Specific node type this tree is used for. */
int nodetype DNA_DEPRECATED;
@@ -465,6 +476,8 @@ typedef struct bNodeTree {
short render_quality;
/** Tile size for compositor engine. */
int chunksize;
+ /** Execution mode to use for compositor engine. */
+ int execution_mode;
rctf viewer_border;
@@ -495,7 +508,7 @@ typedef struct bNodeTree {
*/
struct bNodeTreeExec *execdata;
- /* callbacks */
+ /* Callbacks. */
void (*progress)(void *, float progress);
/** \warning may be called by different threads */
void (*stats_draw)(void *, const char *str);
@@ -538,6 +551,12 @@ typedef enum eNodeTreeUpdate {
NTREE_UPDATE_GROUP = (NTREE_UPDATE_GROUP_IN | NTREE_UPDATE_GROUP_OUT),
} eNodeTreeUpdate;
+/* tree->execution_mode */
+typedef enum eNodeTreeExecutionMode {
+ NTREE_EXECUTION_MODE_TILED = 0,
+ NTREE_EXECUTION_MODE_FULL_FRAME = 1,
+} eNodeTreeExecutionMode;
+
/* socket value structs for input buttons
* DEPRECATED now using ID properties
*/
@@ -590,7 +609,15 @@ typedef struct bNodeSocketValueCollection {
struct Collection *value;
} bNodeSocketValueCollection;
-/* data structs, for node->storage */
+typedef struct bNodeSocketValueTexture {
+ struct Tex *value;
+} bNodeSocketValueTexture;
+
+typedef struct bNodeSocketValueMaterial {
+ struct Material *value;
+} bNodeSocketValueMaterial;
+
+/* Data structs, for node->storage. */
enum {
CMP_NODE_MASKTYPE_ADD = 0,
CMP_NODE_MASKTYPE_SUBTRACT = 1,
@@ -614,7 +641,7 @@ enum {
CMP_NODEFLAG_MASK_NO_FEATHER = (1 << 1),
CMP_NODEFLAG_MASK_MOTION_BLUR = (1 << 2),
- /* we may want multiple aspect options, exposed as an rna enum */
+ /* We may want multiple aspect options, exposed as an rna enum. */
CMP_NODEFLAG_MASK_FIXED = (1 << 8),
CMP_NODEFLAG_MASK_FIXED_SCENE = (1 << 9),
};
@@ -629,7 +656,7 @@ typedef struct NodeFrame {
short label_size;
} NodeFrame;
-/* this one has been replaced with ImageUser, keep it for do_versions() */
+/* This one has been replaced with ImageUser, keep it for do_versions(). */
typedef struct NodeImageAnim {
int frames DNA_DEPRECATED;
int sfra DNA_DEPRECATED;
@@ -683,7 +710,7 @@ typedef struct NodeEllipseMask {
char _pad[4];
} NodeEllipseMask;
-/* layer info for image node outputs */
+/* Layer info for image node outputs. */
typedef struct NodeImageLayer {
/* index in the Image->layers->passes lists */
int pass_index DNA_DEPRECATED;
@@ -715,6 +742,12 @@ typedef struct NodeBilateralBlurData {
char _pad[2];
} NodeBilateralBlurData;
+typedef struct NodeAntiAliasingData {
+ float threshold;
+ float contrast_limit;
+ float corner_rounding;
+} NodeAntiAliasingData;
+
/* NOTE: Only for do-version code. */
typedef struct NodeHueSat {
float hue, sat, val;
@@ -1108,6 +1141,14 @@ typedef struct NodeDenoise {
char hdr;
} NodeDenoise;
+typedef struct NodeAttributeClamp {
+ /* CustomDataType. */
+ uint8_t data_type;
+
+ /* NodeClampOperation. */
+ uint8_t operation;
+} NodeAttributeClamp;
+
typedef struct NodeAttributeCompare {
/* FloatCompareOperation. */
uint8_t operation;
@@ -1119,6 +1160,14 @@ typedef struct NodeAttributeCompare {
char _pad[5];
} NodeAttributeCompare;
+typedef struct NodeAttributeMapRange {
+ /* GeometryNodeAttributeDataType */
+ uint8_t data_type;
+
+ /* NodeMapRangeType. */
+ uint8_t interpolation_type;
+} NodeAttributeMapRange;
+
typedef struct NodeAttributeMath {
/* NodeMathOperation. */
uint8_t operation;
@@ -1159,10 +1208,31 @@ typedef struct NodeAttributeVectorMath {
uint8_t input_type_c;
} NodeAttributeVectorMath;
+typedef struct NodeAttributeVectorRotate {
+ /* GeometryNodeAttributeVectorRotateMode */
+ uint8_t mode;
+
+ /* GeometryNodeAttributeInputMode */
+ uint8_t input_type_vector;
+ uint8_t input_type_center;
+ uint8_t input_type_axis;
+ uint8_t input_type_angle;
+ uint8_t input_type_rotation;
+ char _pad[2];
+} NodeAttributeVectorRotate;
+
typedef struct NodeAttributeColorRamp {
ColorBand color_ramp;
} NodeAttributeColorRamp;
+typedef struct NodeAttributeCurveMap {
+ /* CustomDataType. */
+ uint8_t data_type;
+ char _pad[7];
+ CurveMapping *curve_vec;
+ CurveMapping *curve_rgb;
+} NodeAttributeCurveMap;
+
typedef struct NodeInputVector {
float vector[3];
} NodeInputVector;
@@ -1255,10 +1325,9 @@ typedef struct NodeAttributeSeparateXYZ {
typedef struct NodeAttributeConvert {
/* CustomDataType. */
- uint8_t data_type;
- char _pad[1];
+ int8_t data_type;
/* AttributeDomain. */
- int16_t domain;
+ int8_t domain;
} NodeAttributeConvert;
typedef struct NodeGeometryMeshCircle {
@@ -1283,6 +1352,23 @@ typedef struct NodeGeometryMeshLine {
uint8_t count_mode;
} NodeGeometryMeshLine;
+typedef struct NodeSwitch {
+ /* NodeSwitch. */
+ uint8_t input_type;
+} NodeSwitch;
+
+typedef struct NodeGeometryCurveResample {
+ /* GeometryNodeCurveSampleMode. */
+ uint8_t mode;
+} NodeGeometryCurveResample;
+
+typedef struct NodeGeometryAttributeTransfer {
+ /* AttributeDomain. */
+ int8_t domain;
+ /* GeometryNodeAttributeTransferMapMode. */
+ uint8_t mapping;
+} NodeGeometryAttributeTransfer;
+
/* script node mode */
#define NODE_SCRIPT_INTERNAL 0
#define NODE_SCRIPT_EXTERNAL 1
@@ -1457,7 +1543,7 @@ enum {
#define SHD_AO_INSIDE 1
#define SHD_AO_LOCAL 2
-/* Mapping node vector types */
+/* Mapping node vector types. */
enum {
NODE_MAPPING_TYPE_POINT = 0,
NODE_MAPPING_TYPE_TEXTURE = 1,
@@ -1465,7 +1551,7 @@ enum {
NODE_MAPPING_TYPE_NORMAL = 3,
};
-/* Rotation node vector types */
+/* Rotation node vector types. */
enum {
NODE_VECTOR_ROTATE_TYPE_AXIS = 0,
NODE_VECTOR_ROTATE_TYPE_AXIS_X = 1,
@@ -1552,6 +1638,7 @@ typedef enum NodeVectorMathOperation {
NODE_VECTOR_MATH_TANGENT = 23,
NODE_VECTOR_MATH_REFRACT = 24,
NODE_VECTOR_MATH_FACEFORWARD = 25,
+ NODE_VECTOR_MATH_MULTIPLY_ADD = 26,
} NodeVectorMathOperation;
/* Boolean math node operations. */
@@ -1678,14 +1765,12 @@ typedef enum GeometryNodeAttributeProximityTargetType {
GEO_NODE_ATTRIBUTE_PROXIMITY_TARGET_GEOMETRY_ELEMENT_FACES = 2,
} GeometryNodeAttributeProximityTargetType;
-/* Boolean Node */
typedef enum GeometryNodeBooleanOperation {
GEO_NODE_BOOLEAN_INTERSECT = 0,
GEO_NODE_BOOLEAN_UNION = 1,
GEO_NODE_BOOLEAN_DIFFERENCE = 2,
} GeometryNodeBooleanOperation;
-/* Triangulate Node */
typedef enum GeometryNodeTriangulateNGons {
GEO_NODE_TRIANGULATE_NGON_BEAUTY = 0,
GEO_NODE_TRIANGULATE_NGON_EARCLIP = 1,
@@ -1726,6 +1811,14 @@ typedef enum GeometryNodeRotatePointsType {
GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE = 1,
} GeometryNodeRotatePointsType;
+typedef enum GeometryNodeAttributeVectorRotateMode {
+ GEO_NODE_VECTOR_ROTATE_TYPE_AXIS = 0,
+ GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_X = 1,
+ GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_Y = 2,
+ GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_Z = 3,
+ GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ = 4,
+} GeometryNodeAttributeVectorRotateMode;
+
typedef enum GeometryNodeAttributeRandomizeMode {
GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE = 0,
GEO_NODE_ATTRIBUTE_RANDOMIZE_ADD = 1,
@@ -1777,6 +1870,16 @@ typedef enum GeometryNodeMeshLineCountMode {
GEO_NODE_MESH_LINE_COUNT_RESOLUTION = 1,
} GeometryNodeMeshLineCountMode;
+typedef enum GeometryNodeCurveSampleMode {
+ GEO_NODE_CURVE_SAMPLE_COUNT = 0,
+ GEO_NODE_CURVE_SAMPLE_LENGTH = 1,
+} GeometryNodeCurveSampleMode;
+
+typedef enum GeometryNodeAttributeTransferMapMode {
+ GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED = 0,
+ GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST = 1,
+} GeometryNodeAttributeTransferMapMode;
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/makesdna/DNA_object_force_types.h b/source/blender/makesdna/DNA_object_force_types.h
index 37013f5b4d6..3d8418fb734 100644
--- a/source/blender/makesdna/DNA_object_force_types.h
+++ b/source/blender/makesdna/DNA_object_force_types.h
@@ -182,8 +182,8 @@ typedef struct EffectorWeights {
/** Effector type specific weights. */
float weight[14];
float global_gravity;
- short flag, rt[3];
- char _pad[4];
+ short flag;
+ char _pad[2];
} EffectorWeights;
/* EffectorWeights->flag */
@@ -267,10 +267,9 @@ typedef struct SoftBody {
char namedVG_Spring_K[64];
/* baking */
- int sfra, efra;
- int interval;
+ char _pad1[6];
/** Local==1: use local coords for baking. */
- short local, solverflags;
+ char local, solverflags;
/* -- these must be kept for backwards compatibility -- */
/** Array of size totpointkey. */
diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h
index 686cf2048eb..9951bdefbbb 100644
--- a/source/blender/makesdna/DNA_object_types.h
+++ b/source/blender/makesdna/DNA_object_types.h
@@ -144,6 +144,9 @@ typedef struct Object_Runtime {
*/
char is_data_eval_owned;
+ /** Start time of the mode transfer overlay animation. */
+ double overlay_mode_transfer_start_time;
+
/** Axis aligned boundbox (in localspace). */
struct BoundBox *bb;
@@ -169,6 +172,12 @@ typedef struct Object_Runtime {
struct GeometrySet *geometry_set_eval;
/**
+ * A GHash that contains geometry sets for intermediate stages of evaluation. The keys are just a
+ * hash and are not owned by the map. The geometry sets are owned.
+ */
+ void *geometry_set_previews;
+
+ /**
* Mesh structure created during object evaluation.
* It has deformation only modifiers applied on it.
*/
diff --git a/source/blender/makesdna/DNA_particle_types.h b/source/blender/makesdna/DNA_particle_types.h
index cc40e26b92b..30b1fbe09d3 100644
--- a/source/blender/makesdna/DNA_particle_types.h
+++ b/source/blender/makesdna/DNA_particle_types.h
@@ -64,7 +64,7 @@ typedef struct BoidParticle {
struct BoidData data;
float gravity[3];
float wander[3];
- float rt;
+ char _pad0[4];
} BoidParticle;
typedef struct ParticleSpring {
@@ -82,7 +82,7 @@ typedef struct ChildParticle {
float w[4];
/** Face vertex weights and offset. */
float fuv[4], foffset;
- float rt;
+ char _pad0[4];
} ChildParticle;
typedef struct ParticleTarget {
@@ -99,7 +99,8 @@ typedef struct ParticleDupliWeight {
short count;
short flag;
/** Only updated on file save and used on file load. */
- short index, rt;
+ short index;
+ char _pad0[2];
} ParticleDupliWeight;
typedef struct ParticleData {
@@ -191,7 +192,8 @@ typedef struct ParticleSettings {
struct EffectorWeights *effector_weights;
struct Collection *collision_group;
- int flag, rt;
+ int flag;
+ char _pad1[4];
short type, from, distr, texact;
/* physics modes */
short phystype, rotmode, avemode, reactevent;
diff --git a/source/blender/makesdna/DNA_pointcache_types.h b/source/blender/makesdna/DNA_pointcache_types.h
index de2fa3f10fe..ad5f386bf2b 100644
--- a/source/blender/makesdna/DNA_pointcache_types.h
+++ b/source/blender/makesdna/DNA_pointcache_types.h
@@ -107,7 +107,8 @@ typedef struct PointCache {
int totpoint;
/** Modifier stack index. */
int index;
- short compression, rt;
+ short compression;
+ char _pad0[2];
char name[64];
char prev_name[64];
diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h
index c7f7e610a1a..0b07b8271a5 100644
--- a/source/blender/makesdna/DNA_scene_types.h
+++ b/source/blender/makesdna/DNA_scene_types.h
@@ -952,7 +952,8 @@ typedef struct ParticleEditSettings {
/** Runtime. */
void *paintcursor;
- float emitterdist, rt;
+ float emitterdist;
+ char _pad0[4];
int selectmode;
int edittype;
@@ -1550,7 +1551,8 @@ typedef struct UnitSettings {
typedef struct PhysicsSettings {
float gravity[3];
- int flag, quick_cache_step, rt;
+ int flag, quick_cache_step;
+ char _pad0[4];
} PhysicsSettings;
/* ------------------------------------------- */
diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h
index fa11a7dfd13..7e0bf81457d 100644
--- a/source/blender/makesdna/DNA_sequence_types.h
+++ b/source/blender/makesdna/DNA_sequence_types.h
@@ -57,6 +57,7 @@ typedef struct StripElem {
char name[256];
/** Ignore when zeroed. */
int orig_width, orig_height;
+ float orig_fps;
} StripElem;
typedef struct StripCrop {
@@ -172,7 +173,7 @@ typedef struct Sequence {
float sat;
float mul, handsize;
- short anim_preseek;
+ short anim_preseek; /* UNUSED. */
/** Streamindex for movie or sound files with several streams. */
short streamindex;
/** For multicam source selection. */
@@ -517,11 +518,11 @@ enum {
SEQ_AUDIO_PAN_ANIMATED = (1 << 26),
SEQ_AUDIO_DRAW_WAVEFORM = (1 << 27),
- /* don't include Grease Pencil in OpenGL previews of Scene strips */
- SEQ_SCENE_NO_GPENCIL = (1 << 28),
+ /* don't include Annotations in OpenGL previews of Scene strips */
+ SEQ_SCENE_NO_ANNOTATION = (1 << 28),
SEQ_USE_VIEWS = (1 << 29),
- /* access scene strips directly (like a metastrip) */
+ /* Access scene strips directly (like a meta-strip). */
SEQ_SCENE_STRIPS = (1 << 30),
SEQ_INVALID_EFFECT = (1u << 31),
diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h
index c170e711756..71764d9308c 100644
--- a/source/blender/makesdna/DNA_space_types.h
+++ b/source/blender/makesdna/DNA_space_types.h
@@ -323,6 +323,8 @@ typedef enum eSpaceOutliner_Filter {
SO_FILTER_NO_CHILDREN = (1 << 4),
SO_FILTER_UNUSED_5 = (1 << 5), /* cleared */
+ /** Show overrides that are defined/controlled by Blender. */
+ SO_FILTER_SHOW_SYSTEM_OVERRIDES = SO_FILTER_UNUSED_5, /* re-use */
SO_FILTER_NO_OB_MESH = (1 << 6),
SO_FILTER_NO_OB_ARMATURE = (1 << 7),
SO_FILTER_NO_OB_EMPTY = (1 << 8),
@@ -391,6 +393,7 @@ typedef enum eSpaceOutliner_Mode {
/* SO_KEYMAP = 13, */ /* deprecated! */
SO_ID_ORPHANS = 14,
SO_VIEW_LAYER = 15,
+ SO_OVERRIDES_LIBRARY = 16,
} eSpaceOutliner_Mode;
/* SpaceOutliner.storeflag */
@@ -777,8 +780,16 @@ typedef struct FileAssetSelectParams {
FileSelectParams base_params;
FileSelectAssetLibraryUID asset_library;
+
+ short import_type; /* eFileAssetImportType */
+ char _pad[6];
} FileAssetSelectParams;
+typedef enum eFileAssetImportType {
+ FILE_ASSET_IMPORT_LINK = 0,
+ FILE_ASSET_IMPORT_APPEND = 1,
+} eFileAssetImportType;
+
/**
* A wrapper to store previous and next folder lists (#FolderList) for a specific browse mode
* (#eFileBrowse_Mode).
@@ -1520,6 +1531,7 @@ typedef struct bNodeTreePath {
/** MAX_NAME. */
char node_name[64];
+ char display_name[64];
} bNodeTreePath;
typedef struct SpaceNode {
@@ -1850,6 +1862,46 @@ typedef struct SpaceStatusBar {
/** \name Spreadsheet
* \{ */
+typedef struct SpreadsheetColumnID {
+ char *name;
+} SpreadsheetColumnID;
+
+typedef struct SpreadsheetColumn {
+ struct SpreadsheetColumn *next, *prev;
+ /**
+ * Identifies the data in the column.
+ * This is a pointer instead of a struct to make it easier if we want to "subclass"
+ * #SpreadsheetColumnID in the future for different kinds of ids.
+ */
+ SpreadsheetColumnID *id;
+} SpreadsheetColumn;
+
+/**
+ * An item in SpaceSpreadsheet.context_path.
+ * This is a bases struct for the structs below.
+ */
+typedef struct SpreadsheetContext {
+ struct SpreadsheetContext *next, *prev;
+ /* eSpaceSpreadsheet_ContextType. */
+ int type;
+ char _pad[4];
+} SpreadsheetContext;
+
+typedef struct SpreadsheetContextObject {
+ SpreadsheetContext base;
+ struct Object *object;
+} SpreadsheetContextObject;
+
+typedef struct SpreadsheetContextModifier {
+ SpreadsheetContext base;
+ char *modifier_name;
+} SpreadsheetContextModifier;
+
+typedef struct SpreadsheetContextNode {
+ SpreadsheetContext base;
+ char *node_name;
+} SpreadsheetContextNode;
+
typedef struct SpaceSpreadsheet {
SpaceLink *next, *prev;
/** Storage of regions for inactive spaces. */
@@ -1859,7 +1911,16 @@ typedef struct SpaceSpreadsheet {
char _pad0[6];
/* End 'SpaceLink' header. */
- struct ID *pinned_id;
+ /* List of #SpreadsheetColumn. */
+ ListBase columns;
+
+ /**
+ * List of #SpreadsheetContext.
+ * This is a path to the data that is displayed in the spreadsheet.
+ * It can be set explicitly by an action of the user (e.g. clicking the preview icon in a
+ * geometry node) or it can be derived from context automatically based on some heuristic.
+ */
+ ListBase context_path;
/* eSpaceSpreadsheet_FilterFlag. */
uint8_t filter_flag;
@@ -1871,22 +1932,41 @@ typedef struct SpaceSpreadsheet {
/* eSpaceSpreadsheet_ObjectContext. */
uint8_t object_eval_state;
- char _pad1[4];
+ /* eSpaceSpreadsheet_Flag. */
+ uint32_t flag;
SpaceSpreadsheet_Runtime *runtime;
} SpaceSpreadsheet;
-/** \} */
+typedef enum eSpaceSpreadsheet_Flag {
+ SPREADSHEET_FLAG_PINNED = (1 << 0),
+ SPREADSHEET_FLAG_CONTEXT_PATH_COLLAPSED = (1 << 1),
+} eSpaceSpreadsheet_Flag;
typedef enum eSpaceSpreadsheet_FilterFlag {
SPREADSHEET_FILTER_SELECTED_ONLY = (1 << 0),
} eSpaceSpreadsheet_FilterFlag;
typedef enum eSpaceSpreadsheet_ObjectEvalState {
- SPREADSHEET_OBJECT_EVAL_STATE_FINAL = 0,
+ SPREADSHEET_OBJECT_EVAL_STATE_EVALUATED = 0,
SPREADSHEET_OBJECT_EVAL_STATE_ORIGINAL = 1,
} eSpaceSpreadsheet_Context;
+typedef enum eSpaceSpreadsheet_ContextType {
+ SPREADSHEET_CONTEXT_OBJECT = 0,
+ SPREADSHEET_CONTEXT_MODIFIER = 1,
+ SPREADSHEET_CONTEXT_NODE = 2,
+} eSpaceSpreadsheet_ContextType;
+
+/**
+ * We can't just use UI_UNIT_X, because it does not take `widget.points` into account, which
+ * modifies the width of text as well.
+ */
+#define SPREADSHEET_WIDTH_UNIT \
+ (UI_UNIT_X * UI_style_get_dpi()->widget.points / (float)UI_DEFAULT_TEXT_POINTS)
+
+/** \} */
+
/* -------------------------------------------------------------------- */
/** \name Space Defines (eSpace_Type)
* \{ */
diff --git a/source/blender/makesdna/DNA_texture_types.h b/source/blender/makesdna/DNA_texture_types.h
index 5381c524e4d..60c255e8637 100644
--- a/source/blender/makesdna/DNA_texture_types.h
+++ b/source/blender/makesdna/DNA_texture_types.h
@@ -57,7 +57,8 @@ typedef struct MTex {
short colormodel, pmapto, pmaptoneg;
short normapspace, which_output;
float r, g, b, k;
- float def_var, rt;
+ float def_var;
+ char _pad1[4];
/* common */
float colfac, varfac;
diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h
index 4595b12e9d4..61d2c04d98b 100644
--- a/source/blender/makesdna/DNA_userdef_types.h
+++ b/source/blender/makesdna/DNA_userdef_types.h
@@ -642,11 +642,12 @@ typedef struct UserDef_Experimental {
* when the release cycle is not alpha. */
char use_new_hair_type;
char use_new_point_cloud_type;
+ char use_full_frame_compositor;
char use_sculpt_vertex_colors;
- char use_switch_object_operator;
char use_sculpt_tools_tilt;
char use_asset_browser;
- char _pad[6];
+ char use_override_templates;
+ char _pad[5];
/** `makesdna` does not allow empty structs. */
} UserDef_Experimental;
diff --git a/source/blender/makesdna/DNA_view3d_defaults.h b/source/blender/makesdna/DNA_view3d_defaults.h
index 10d0bafec61..9dfc37e57b1 100644
--- a/source/blender/makesdna/DNA_view3d_defaults.h
+++ b/source/blender/makesdna/DNA_view3d_defaults.h
@@ -70,6 +70,7 @@
\
.gpencil_paper_opacity = 0.5f, \
.gpencil_grid_opacity = 0.9f, \
+ .gpencil_vertex_paint_opacity = 1.0f, \
}
#define _DNA_DEFAULT_View3DCursor \
diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h
index b8e2256c3c6..2f4e4e57b9f 100644
--- a/source/blender/makesdna/DNA_view3d_types.h
+++ b/source/blender/makesdna/DNA_view3d_types.h
@@ -367,7 +367,7 @@ typedef struct View3D {
#define V3D_LOCAL_COLLECTIONS (1 << 0)
#define V3D_FLAG_UNUSED_1 (1 << 1) /* cleared */
#define V3D_HIDE_HELPLINES (1 << 2)
-#define V3D_INVALID_BACKBUF (1 << 3)
+#define V3D_FLAG_UNUSED_2 (1 << 3) /* cleared */
#define V3D_XR_SESSION_MIRROR (1 << 4)
#define V3D_FLAG_UNUSED_10 (1 << 10) /* cleared */
@@ -380,6 +380,8 @@ typedef struct View3D {
enum {
/** The 3D view which the XR session was created in is flagged with this. */
V3D_RUNTIME_XR_SESSION_ROOT = (1 << 0),
+ /** Some operators override the depth buffer for dedicated occlusion operations. */
+ V3D_RUNTIME_DEPTHBUF_OVERRIDDEN = (1 << 1),
};
/** #RegionView3D.persp */
diff --git a/source/blender/makesdna/DNA_windowmanager_types.h b/source/blender/makesdna/DNA_windowmanager_types.h
index 68d69a671ba..59091fec4b8 100644
--- a/source/blender/makesdna/DNA_windowmanager_types.h
+++ b/source/blender/makesdna/DNA_windowmanager_types.h
@@ -278,8 +278,14 @@ typedef struct wmWindow {
char event_queue_check_click;
/** Enable when #KM_PRESS events are not handled (keyboard/mouse-buttons only). */
char event_queue_check_drag;
+ /**
+ * Enable when the drag was handled,
+ * to avoid mouse-motion continually triggering drag events which are not handled
+ * but add overhead to gizmo handling (for example), see T87511.
+ */
+ char event_queue_check_drag_handled;
- char _pad0[2];
+ char _pad0[1];
/** Internal, lock pie creation from this event until released. */
short pie_event_type_lock;
@@ -374,7 +380,12 @@ typedef struct wmKeyMapItem {
/** Unique identifier. Positive for kmi that override builtins, negative otherwise. */
short id;
char _pad[2];
- /** Rna pointer to access properties. */
+ /**
+ * RNA pointer to access properties.
+ *
+ * \note The `ptr.owner_id` value must be NULL, as a signal not to use the context
+ * when running property callbacks such as ENUM item functions.
+ */
struct PointerRNA *ptr;
} wmKeyMapItem;
diff --git a/source/blender/makesdna/DNA_xr_types.h b/source/blender/makesdna/DNA_xr_types.h
index 2ce32a723a7..8e63760fef7 100644
--- a/source/blender/makesdna/DNA_xr_types.h
+++ b/source/blender/makesdna/DNA_xr_types.h
@@ -58,6 +58,21 @@ typedef enum eXRSessionBasePoseType {
XR_BASE_POSE_CUSTOM = 2,
} eXRSessionBasePoseType;
+/** XR action type. Enum values match those in GHOST_XrActionType enum for consistency. */
+typedef enum eXrActionType {
+ XR_BOOLEAN_INPUT = 1,
+ XR_FLOAT_INPUT = 2,
+ XR_VECTOR2F_INPUT = 3,
+ XR_POSE_INPUT = 4,
+ XR_VIBRATION_OUTPUT = 100,
+} eXrActionType;
+
+typedef enum eXrOpFlag {
+ XR_OP_PRESS = 0,
+ XR_OP_RELEASE = 1,
+ XR_OP_MODAL = 2,
+} eXrOpFlag;
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/makesdna/intern/dna_defaults.c b/source/blender/makesdna/intern/dna_defaults.c
index 95272fb7804..2d55ea05867 100644
--- a/source/blender/makesdna/intern/dna_defaults.c
+++ b/source/blender/makesdna/intern/dna_defaults.c
@@ -316,6 +316,7 @@ SDNA_DEFAULT_DECL_STRUCT(ThickGpencilModifierData);
SDNA_DEFAULT_DECL_STRUCT(TimeGpencilModifierData);
SDNA_DEFAULT_DECL_STRUCT(TintGpencilModifierData);
SDNA_DEFAULT_DECL_STRUCT(LineartGpencilModifierData);
+SDNA_DEFAULT_DECL_STRUCT(LengthGpencilModifierData);
#undef SDNA_DEFAULT_DECL_STRUCT
@@ -541,6 +542,7 @@ const void *DNA_default_table[SDNA_TYPE_MAX] = {
SDNA_DEFAULT_DECL(TimeGpencilModifierData),
SDNA_DEFAULT_DECL(TintGpencilModifierData),
SDNA_DEFAULT_DECL(LineartGpencilModifierData),
+ SDNA_DEFAULT_DECL(LengthGpencilModifierData),
};
#undef SDNA_DEFAULT_DECL
#undef SDNA_DEFAULT_DECL_EX
diff --git a/source/blender/makesdna/intern/dna_genfile.c b/source/blender/makesdna/intern/dna_genfile.c
index 3690a1126d4..a23c9087ffc 100644
--- a/source/blender/makesdna/intern/dna_genfile.c
+++ b/source/blender/makesdna/intern/dna_genfile.c
@@ -792,6 +792,9 @@ static void cast_primitive_type(const eSDNA_Type old_type,
old_value_i = *((uint64_t *)old_data);
old_value_f = (double)old_value_i;
break;
+ case SDNA_TYPE_INT8:
+ old_value_i = (uint64_t) * ((int8_t *)old_data);
+ old_value_f = (double)old_value_i;
}
switch (new_type) {
@@ -828,6 +831,9 @@ static void cast_primitive_type(const eSDNA_Type old_type,
case SDNA_TYPE_UINT64:
*((uint64_t *)new_data) = old_value_i;
break;
+ case SDNA_TYPE_INT8:
+ *((int8_t *)new_data) = (int8_t)old_value_i;
+ break;
}
old_data += oldlen;
@@ -1194,7 +1200,10 @@ static void reconstruct_struct(const DNA_ReconstructInfo *reconstruct_info,
new_block + step->data.substruct.new_offset);
break;
case RECONSTRUCT_STEP_INIT_ZERO:
- /* Do nothing, because the memory block has been calloced. */
+ /* Do nothing, because the memory block are zeroed (from #MEM_callocN).
+ *
+ * Note that the struct could be initialized with the default struct,
+ * however this complicates versioning, especially with flags, see: D4500. */
break;
}
}
@@ -1555,9 +1564,8 @@ DNA_ReconstructInfo *DNA_reconstruct_info_create(const SDNA *oldsdna,
ReconstructStep *steps = create_reconstruct_steps_for_struct(
oldsdna, newsdna, compare_flags, old_struct, new_struct);
- int steps_len = new_struct->members_len;
/* Comment the line below to skip the compression for debugging purposes. */
- steps_len = compress_reconstruct_steps(steps, new_struct->members_len);
+ const int steps_len = compress_reconstruct_steps(steps, new_struct->members_len);
reconstruct_info->steps[new_struct_nr] = steps;
reconstruct_info->step_counts[new_struct_nr] = steps_len;
@@ -1652,6 +1660,7 @@ int DNA_elem_type_size(const eSDNA_Type elem_nr)
switch (elem_nr) {
case SDNA_TYPE_CHAR:
case SDNA_TYPE_UCHAR:
+ case SDNA_TYPE_INT8:
return 1;
case SDNA_TYPE_SHORT:
case SDNA_TYPE_USHORT:
diff --git a/source/blender/makesdna/intern/dna_utils.c b/source/blender/makesdna/intern/dna_utils.c
index 3cf5c52a4c6..f050934b5b3 100644
--- a/source/blender/makesdna/intern/dna_utils.c
+++ b/source/blender/makesdna/intern/dna_utils.c
@@ -235,9 +235,6 @@ void DNA_alias_maps(enum eDNA_RenameDir version_dir, GHash **r_struct_map, GHash
if (version_dir == DNA_RENAME_STATIC_FROM_ALIAS) {
const char *renames[][2] = {
- /* Disable 'int8_t' until we support 'signed char', since changing negative
- * values to a different type isn't supported and will change the value. */
- /* {"int8_t", "char"}, */
{"uint8_t", "uchar"},
{"int16_t", "short"},
{"uint16_t", "ushort"},
diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c
index 26fc56cfa1d..85bcc94c335 100644
--- a/source/blender/makesdna/intern/makesdna.c
+++ b/source/blender/makesdna/intern/makesdna.c
@@ -1222,6 +1222,7 @@ static int make_structDNA(const char *base_directory,
add_type("int64_t", 8); /* SDNA_TYPE_INT64 */
add_type("uint64_t", 8); /* SDNA_TYPE_UINT64 */
add_type("void", 0); /* SDNA_TYPE_VOID */
+ add_type("int8_t", 1); /* SDNA_TYPE_INT8 */
/* the defines above shouldn't be output in the padding file... */
const int firststruct = types_len;
@@ -1516,16 +1517,12 @@ int main(int argc, char **argv)
*
* - 'long': even though DNA supports, 'long' shouldn't be used since it can be either 32 or 64bit,
* use int, int32_t or int64_t instead.
- * - 'int8_t': as DNA doesn't yet support 'signed char' types,
- * all char types are assumed to be unsigned.
- * We should be able to support this, it's just not something which has been added yet.
*
* Only valid use would be as a runtime variable if an API expected a long,
* but so far we don't have this happening.
*/
#ifdef __GNUC__
# pragma GCC poison long
-# pragma GCC poison int8_t
#endif
#include "DNA_ID.h"
diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h
index ba67cedfdbe..379e3e77b6e 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -336,6 +336,7 @@ extern StructRNA RNA_LatticeModifier;
extern StructRNA RNA_LatticePoint;
extern StructRNA RNA_LayerCollection;
extern StructRNA RNA_LayerObjects;
+extern StructRNA RNA_LengthGpencilModifier;
extern StructRNA RNA_Library;
extern StructRNA RNA_Light;
extern StructRNA RNA_LightProbe;
@@ -614,6 +615,10 @@ extern StructRNA RNA_Spline;
extern StructRNA RNA_SplineIKConstraint;
extern StructRNA RNA_SplinePoint;
extern StructRNA RNA_SpotLight;
+extern StructRNA RNA_SpreadsheetContext;
+extern StructRNA RNA_SpreadsheetContextObject;
+extern StructRNA RNA_SpreadsheetContextModifier;
+extern StructRNA RNA_SpreadsheetContextNode;
extern StructRNA RNA_Stereo3dDisplay;
extern StructRNA RNA_StretchToConstraint;
extern StructRNA RNA_StringAttribute;
@@ -841,6 +846,7 @@ const char *RNA_property_description(PropertyRNA *prop);
PropertyType RNA_property_type(PropertyRNA *prop);
PropertySubType RNA_property_subtype(PropertyRNA *prop);
PropertyUnit RNA_property_unit(PropertyRNA *prop);
+PropertyScaleType RNA_property_ui_scale(PropertyRNA *prop);
int RNA_property_flag(PropertyRNA *prop);
int RNA_property_override_flag(PropertyRNA *prop);
int RNA_property_tags(PropertyRNA *prop);
diff --git a/source/blender/makesrna/RNA_define.h b/source/blender/makesrna/RNA_define.h
index bc1a8f52b8d..a31182b2f5a 100644
--- a/source/blender/makesrna/RNA_define.h
+++ b/source/blender/makesrna/RNA_define.h
@@ -388,6 +388,7 @@ void RNA_def_property_string_default(PropertyRNA *prop, const char *value);
void RNA_def_property_ui_text(PropertyRNA *prop, const char *name, const char *description);
void RNA_def_property_ui_range(
PropertyRNA *prop, double min, double max, double step, int precision);
+void RNA_def_property_ui_scale_type(PropertyRNA *prop, PropertyScaleType scale_type);
void RNA_def_property_ui_icon(PropertyRNA *prop, int icon, int consecutive);
void RNA_def_property_update(PropertyRNA *prop, int noteflag, const char *updatefunc);
@@ -513,7 +514,7 @@ const char *RNA_property_typename(PropertyType type);
#define IS_DNATYPE_FLOAT_COMPAT(_str) (strcmp(_str, "float") == 0 || strcmp(_str, "double") == 0)
#define IS_DNATYPE_INT_COMPAT(_str) \
(strcmp(_str, "int") == 0 || strcmp(_str, "short") == 0 || strcmp(_str, "char") == 0 || \
- strcmp(_str, "uchar") == 0 || strcmp(_str, "ushort") == 0)
+ strcmp(_str, "uchar") == 0 || strcmp(_str, "ushort") == 0 || strcmp(_str, "int8_t") == 0)
#define IS_DNATYPE_BOOLEAN_COMPAT(_str) \
(IS_DNATYPE_INT_COMPAT(_str) || strcmp(_str, "int64_t") == 0 || strcmp(_str, "uint64_t") == 0)
diff --git a/source/blender/makesrna/RNA_enum_types.h b/source/blender/makesrna/RNA_enum_types.h
index b0895609e9a..71af69f77a1 100644
--- a/source/blender/makesrna/RNA_enum_types.h
+++ b/source/blender/makesrna/RNA_enum_types.h
@@ -237,6 +237,7 @@ extern const EnumPropertyItem rna_enum_curveprofile_preset_items[];
extern const EnumPropertyItem rna_enum_preference_section_items[];
extern const EnumPropertyItem rna_enum_attribute_type_items[];
+extern const EnumPropertyItem rna_enum_attribute_type_with_auto_items[];
extern const EnumPropertyItem rna_enum_attribute_domain_items[];
extern const EnumPropertyItem rna_enum_attribute_domain_with_auto_items[];
extern const EnumPropertyItem *rna_enum_attribute_domain_itemf(struct ID *id, bool *r_free);
diff --git a/source/blender/makesrna/RNA_types.h b/source/blender/makesrna/RNA_types.h
index c4f6707dad5..8e1cfafcc42 100644
--- a/source/blender/makesrna/RNA_types.h
+++ b/source/blender/makesrna/RNA_types.h
@@ -82,19 +82,46 @@ typedef enum PropertyType {
/* also update rna_property_subtype_unit when you change this */
typedef enum PropertyUnit {
PROP_UNIT_NONE = (0 << 16),
- PROP_UNIT_LENGTH = (1 << 16), /* m */
- PROP_UNIT_AREA = (2 << 16), /* m^2 */
- PROP_UNIT_VOLUME = (3 << 16), /* m^3 */
- PROP_UNIT_MASS = (4 << 16), /* kg */
- PROP_UNIT_ROTATION = (5 << 16), /* radians */
- PROP_UNIT_TIME = (6 << 16), /* frame */
- PROP_UNIT_VELOCITY = (7 << 16), /* m/s */
- PROP_UNIT_ACCELERATION = (8 << 16), /* m/(s^2) */
- PROP_UNIT_CAMERA = (9 << 16), /* mm */
- PROP_UNIT_POWER = (10 << 16), /* W */
- PROP_UNIT_TEMPERATURE = (11 << 16), /* C */
+ PROP_UNIT_LENGTH = (1 << 16), /* m */
+ PROP_UNIT_AREA = (2 << 16), /* m^2 */
+ PROP_UNIT_VOLUME = (3 << 16), /* m^3 */
+ PROP_UNIT_MASS = (4 << 16), /* kg */
+ PROP_UNIT_ROTATION = (5 << 16), /* radians */
+ PROP_UNIT_TIME = (6 << 16), /* frame */
+ PROP_UNIT_TIME_ABSOLUTE = (7 << 16), /* time in seconds (independent of scene) */
+ PROP_UNIT_VELOCITY = (8 << 16), /* m/s */
+ PROP_UNIT_ACCELERATION = (9 << 16), /* m/(s^2) */
+ PROP_UNIT_CAMERA = (10 << 16), /* mm */
+ PROP_UNIT_POWER = (11 << 16), /* W */
+ PROP_UNIT_TEMPERATURE = (12 << 16), /* C */
} PropertyUnit;
+/**
+ * Use values besides #PROP_SCALE_LINEAR
+ * so the movement of the mouse doesn't map linearly to the value of the slider.
+ *
+ * For some settings it's useful to space motion in a non-linear way, see T77868.
+ *
+ * NOTE: The scale types are available for all float sliders.
+ * For integer sliders they are only available if they use the visible value bar.
+ * Sliders with logarithmic scale and value bar must have a range > 0
+ * while logarithmic sliders without the value bar can have a range of >= 0.
+ */
+typedef enum PropertyScaleType {
+ /** Linear scale (default). */
+ PROP_SCALE_LINEAR = 0,
+ /**
+ * Logarithmic scale
+ * - Maximum range: `0 <= x < inf`
+ */
+ PROP_SCALE_LOG = 1,
+ /**
+ * Cubic scale.
+ * - Maximum range: `-inf < x < inf`
+ */
+ PROP_SCALE_CUBIC = 2,
+} PropertyScaleType;
+
#define RNA_SUBTYPE_UNIT(subtype) ((subtype)&0x00FF0000)
#define RNA_SUBTYPE_VALUE(subtype) ((subtype) & ~0x00FF0000)
#define RNA_SUBTYPE_UNIT_VALUE(subtype) ((subtype) >> 16)
@@ -131,6 +158,7 @@ typedef enum PropertySubType {
PROP_FACTOR = 15,
PROP_ANGLE = 16 | PROP_UNIT_ROTATION,
PROP_TIME = 17 | PROP_UNIT_TIME,
+ PROP_TIME_ABSOLUTE = 17 | PROP_UNIT_TIME_ABSOLUTE,
/** Distance in 3d space, don't use for pixel distance for eg. */
PROP_DISTANCE = 18 | PROP_UNIT_LENGTH,
PROP_DISTANCE_CAMERA = 19 | PROP_UNIT_CAMERA,
@@ -619,7 +647,7 @@ typedef enum StructFlag {
/** Indicates that this struct is an ID struct, and to use reference-counting. */
STRUCT_ID = (1 << 0),
STRUCT_ID_REFCOUNT = (1 << 1),
- /** defaults on, clear for user preferences and similar */
+ /** defaults on, indicates when changes in members of a StructRNA should trigger undo steps. */
STRUCT_UNDO = (1 << 2),
/* internal flags */
@@ -636,6 +664,12 @@ typedef enum StructFlag {
STRUCT_PUBLIC_NAMESPACE = (1 << 9),
/** All subtypes are added too. */
STRUCT_PUBLIC_NAMESPACE_INHERIT = (1 << 10),
+ /**
+ * When the #PointerRNA.owner_id is NULL, this signifies the property should be accessed
+ * without any context (the key-map UI and import/export for example).
+ * So accessing the property should not read from the current context to derive values/limits.
+ */
+ STRUCT_NO_CONTEXT_WITHOUT_OWNER_ID = (1 << 11),
} StructFlag;
typedef int (*StructValidateFunc)(struct PointerRNA *ptr, void *data, int *have_function);
diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt
index 4fafa356879..95b7b7e5406 100644
--- a/source/blender/makesrna/intern/CMakeLists.txt
+++ b/source/blender/makesrna/intern/CMakeLists.txt
@@ -99,7 +99,7 @@ set(DEFSRC
)
if(WITH_EXPERIMENTAL_FEATURES)
- add_definitions(-DWITH_GEOMETRY_NODES)
+ add_definitions(-DWITH_SIMULATION_DATABLOCK)
add_definitions(-DWITH_POINT_CLOUD)
add_definitions(-DWITH_HAIR_NODES)
list(APPEND DEFSRC
diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c
index b20d9218316..7a9cfa79324 100644
--- a/source/blender/makesrna/intern/makesrna.c
+++ b/source/blender/makesrna/intern/makesrna.c
@@ -684,6 +684,29 @@ static char *rna_def_property_get_func(
}
}
}
+
+ /* Check log scale sliders for negative range. */
+ if (prop->type == PROP_FLOAT) {
+ FloatPropertyRNA *fprop = (FloatPropertyRNA *)prop;
+ /* NOTE: UI_BTYPE_NUM_SLIDER can't have a softmin of zero. */
+ if ((fprop->ui_scale_type == PROP_SCALE_LOG) && (fprop->hardmin < 0 || fprop->softmin < 0)) {
+ CLOG_ERROR(
+ &LOG, "\"%s.%s\", range for log scale < 0.", srna->identifier, prop->identifier);
+ DefRNA.error = true;
+ return NULL;
+ }
+ }
+ if (prop->type == PROP_INT) {
+ IntPropertyRNA *iprop = (IntPropertyRNA *)prop;
+ /* Only UI_BTYPE_NUM_SLIDER is implemented and that one can't have a softmin of zero. */
+ if ((iprop->ui_scale_type == PROP_SCALE_LOG) &&
+ (iprop->hardmin <= 0 || iprop->softmin <= 0)) {
+ CLOG_ERROR(
+ &LOG, "\"%s.%s\", range for log scale <= 0.", srna->identifier, prop->identifier);
+ DefRNA.error = true;
+ return NULL;
+ }
+ }
}
func = rna_alloc_function_name(srna->identifier, rna_safe_id(prop->identifier), "get");
@@ -3178,6 +3201,8 @@ static const char *rna_property_subtypename(PropertySubType type)
return "PROP_ANGLE";
case PROP_TIME:
return "PROP_TIME";
+ case PROP_TIME_ABSOLUTE:
+ return "PROP_TIME_ABSOLUTE";
case PROP_DISTANCE:
return "PROP_DISTANCE";
case PROP_DISTANCE_CAMERA:
@@ -3243,6 +3268,8 @@ static const char *rna_property_subtype_unit(PropertySubType type)
return "PROP_UNIT_ROTATION";
case PROP_UNIT_TIME:
return "PROP_UNIT_TIME";
+ case PROP_UNIT_TIME_ABSOLUTE:
+ return "PROP_UNIT_TIME_ABSOLUTE";
case PROP_UNIT_VELOCITY:
return "PROP_UNIT_VELOCITY";
case PROP_UNIT_ACCELERATION:
@@ -3935,6 +3962,8 @@ static void rna_generate_property(FILE *f, StructRNA *srna, const char *nest, Pr
rna_function_string(iprop->getarray_ex),
rna_function_string(iprop->setarray_ex),
rna_function_string(iprop->range_ex));
+ rna_int_print(f, iprop->ui_scale_type);
+ fprintf(f, ", ");
rna_int_print(f, iprop->softmin);
fprintf(f, ", ");
rna_int_print(f, iprop->softmax);
@@ -3969,6 +3998,8 @@ static void rna_generate_property(FILE *f, StructRNA *srna, const char *nest, Pr
rna_function_string(fprop->getarray_ex),
rna_function_string(fprop->setarray_ex),
rna_function_string(fprop->range_ex));
+ rna_float_print(f, fprop->ui_scale_type);
+ fprintf(f, ", ");
rna_float_print(f, fprop->softmin);
fprintf(f, ", ");
rna_float_print(f, fprop->softmax);
@@ -4341,7 +4372,7 @@ static RNAProcessItem PROCESS_ITEMS[] = {
{"rna_screen.c", NULL, RNA_def_screen},
{"rna_sculpt_paint.c", NULL, RNA_def_sculpt_paint},
{"rna_sequencer.c", "rna_sequencer_api.c", RNA_def_sequencer},
-#ifdef WITH_GEOMETRY_NODES
+#ifdef WITH_SIMULATION_DATABLOCK
{"rna_simulation.c", NULL, RNA_def_simulation},
#endif
{"rna_space.c", "rna_space_api.c", RNA_def_space},
diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c
index 6e2005b7314..43b65b087bf 100644
--- a/source/blender/makesrna/intern/rna_ID.c
+++ b/source/blender/makesrna/intern/rna_ID.c
@@ -85,6 +85,47 @@ const EnumPropertyItem rna_enum_id_type_items[] = {
{0, NULL, 0, NULL, NULL},
};
+static const EnumPropertyItem rna_enum_override_library_property_operation_items[] = {
+ {IDOVERRIDE_LIBRARY_OP_NOOP,
+ "NOOP",
+ 0,
+ "No-Op",
+ "Does nothing, prevents adding actual overrides (NOT USED)"},
+ {IDOVERRIDE_LIBRARY_OP_REPLACE,
+ "REPLACE",
+ 0,
+ "Replace",
+ "Replace value of reference by overriding one"},
+ {IDOVERRIDE_LIBRARY_OP_ADD,
+ "DIFF_ADD",
+ 0,
+ "Differential",
+ "Stores and apply difference between reference and local value (NOT USED)"},
+ {IDOVERRIDE_LIBRARY_OP_SUBTRACT,
+ "DIFF_SUB",
+ 0,
+ "Differential",
+ "Stores and apply difference between reference and local value (NOT USED)"},
+ {IDOVERRIDE_LIBRARY_OP_MULTIPLY,
+ "FACT_MULTIPLY",
+ 0,
+ "Factor",
+ "Stores and apply multiplication factor between reference and local value (NOT USED)"},
+ {IDOVERRIDE_LIBRARY_OP_INSERT_AFTER,
+ "INSERT_AFTER",
+ 0,
+ "Insert After",
+ "Insert a new item into collection after the one referenced in subitem_reference_name or "
+ "_index"},
+ {IDOVERRIDE_LIBRARY_OP_INSERT_BEFORE,
+ "INSERT_BEFORE",
+ 0,
+ "Insert Before",
+ "Insert a new item into collection after the one referenced in subitem_reference_name or "
+ "_index (NOT USED)"},
+ {0, NULL, 0, NULL, NULL},
+};
+
#ifdef RNA_RUNTIME
# include "DNA_anim_types.h"
@@ -107,6 +148,8 @@ const EnumPropertyItem rna_enum_id_type_items[] = {
# include "DEG_depsgraph_build.h"
# include "DEG_depsgraph_query.h"
+# include "ED_asset.h"
+
# include "WM_api.h"
void rna_ID_override_library_property_operation_refname_get(PointerRNA *ptr, char *value)
@@ -303,7 +346,7 @@ short RNA_type_to_ID_code(const StructRNA *type)
if (base_type == &RNA_Screen) {
return ID_SCR;
}
-# ifdef WITH_GEOMETRY_NODES
+# ifdef WITH_SIMULATION_DATABLOCK
if (base_type == &RNA_Simulation) {
return ID_SIM;
}
@@ -411,7 +454,7 @@ StructRNA *ID_code_to_RNA_type(short idcode)
case ID_SCR:
return &RNA_Screen;
case ID_SIM:
-# ifdef WITH_GEOMETRY_NODES
+# ifdef WITH_SIMULATION_DATABLOCK
return &RNA_Simulation;
# else
return &RNA_ID;
@@ -534,6 +577,22 @@ static ID *rna_ID_copy(ID *id, Main *bmain)
return newid;
}
+static void rna_ID_asset_mark(ID *id, bContext *C)
+{
+ if (ED_asset_mark_id(C, id)) {
+ WM_main_add_notifier(NC_ID | NA_EDITED, NULL);
+ WM_main_add_notifier(NC_ASSET | NA_ADDED, NULL);
+ }
+}
+
+static void rna_ID_asset_clear(ID *id)
+{
+ if (ED_asset_clear_id(id)) {
+ WM_main_add_notifier(NC_ID | NA_EDITED, NULL);
+ WM_main_add_notifier(NC_ASSET | NA_REMOVED, NULL);
+ }
+}
+
static ID *rna_ID_override_create(ID *id, Main *bmain, bool remap_local_usages)
{
if (!ID_IS_OVERRIDABLE_LIBRARY(id)) {
@@ -549,9 +608,88 @@ static ID *rna_ID_override_create(ID *id, Main *bmain, bool remap_local_usages)
if (remap_local_usages) {
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
}
+
+ WM_main_add_notifier(NC_ID | NA_ADDED, NULL);
+
return local_id;
}
+static ID *rna_ID_override_hierarchy_create(
+ ID *id, Main *bmain, Scene *scene, ViewLayer *view_layer, ID *id_reference)
+{
+ if (!ID_IS_OVERRIDABLE_LIBRARY(id)) {
+ return NULL;
+ }
+
+ BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
+
+ ID *id_root_override = NULL;
+ BKE_lib_override_library_create(bmain, scene, view_layer, id, id_reference, &id_root_override);
+
+ WM_main_add_notifier(NC_ID | NA_ADDED, NULL);
+
+ return id_root_override;
+}
+
+static void rna_ID_override_template_create(ID *id, ReportList *reports)
+{
+ if (!U.experimental.use_override_templates) {
+ BKE_report(reports, RPT_ERROR, "Override template experimental feature is disabled");
+ return;
+ }
+ if (ID_IS_LINKED(id)) {
+ BKE_report(reports, RPT_ERROR, "Unable to create override template for linked data-blocks");
+ return;
+ }
+ if (ID_IS_OVERRIDE_LIBRARY(id)) {
+ BKE_report(
+ reports, RPT_ERROR, "Unable to create override template for overridden data-blocks");
+ return;
+ }
+ BKE_lib_override_library_template_create(id);
+}
+
+static IDOverrideLibraryProperty *rna_ID_override_library_properties_add(
+ IDOverrideLibrary *override_library, ReportList *reports, const char rna_path[])
+{
+ bool created;
+ IDOverrideLibraryProperty *result = BKE_lib_override_library_property_get(
+ override_library, rna_path, &created);
+
+ if (!created) {
+ BKE_report(reports, RPT_DEBUG, "No new override property created, property already exists");
+ }
+
+ return result;
+}
+
+static IDOverrideLibraryPropertyOperation *rna_ID_override_library_property_operations_add(
+ IDOverrideLibraryProperty *override_property,
+ ReportList *reports,
+ int operation,
+ const char *subitem_refname,
+ const char *subitem_locname,
+ int subitem_refindex,
+ int subitem_locindex)
+{
+ bool created;
+ bool strict;
+ IDOverrideLibraryPropertyOperation *result = BKE_lib_override_library_property_operation_get(
+ override_property,
+ operation,
+ subitem_refname,
+ subitem_locname,
+ subitem_refindex,
+ subitem_locindex,
+ false,
+ &strict,
+ &created);
+ if (!created) {
+ BKE_report(reports, RPT_DEBUG, "No new override operation created, operation already exists");
+ }
+ return result;
+}
+
static void rna_ID_update_tag(ID *id, Main *bmain, ReportList *reports, int flag)
{
/* XXX, new function for this! */
@@ -1019,7 +1157,7 @@ static void rna_ImagePreview_icon_reload(PreviewImage *prv)
static PointerRNA rna_IDPreview_get(PointerRNA *ptr)
{
ID *id = (ID *)ptr->data;
- PreviewImage *prv_img = BKE_previewimg_id_ensure(id);
+ PreviewImage *prv_img = BKE_previewimg_id_get(id);
return rna_pointer_inherit_refine(ptr, &RNA_ImagePreview, prv_img);
}
@@ -1267,47 +1405,6 @@ static void rna_def_ID_override_library_property_operation(BlenderRNA *brna)
StructRNA *srna;
PropertyRNA *prop;
- static const EnumPropertyItem override_library_property_operation_items[] = {
- {IDOVERRIDE_LIBRARY_OP_NOOP,
- "NOOP",
- 0,
- "No-Op",
- "Does nothing, prevents adding actual overrides (NOT USED)"},
- {IDOVERRIDE_LIBRARY_OP_REPLACE,
- "REPLACE",
- 0,
- "Replace",
- "Replace value of reference by overriding one"},
- {IDOVERRIDE_LIBRARY_OP_ADD,
- "DIFF_ADD",
- 0,
- "Differential",
- "Stores and apply difference between reference and local value (NOT USED)"},
- {IDOVERRIDE_LIBRARY_OP_SUBTRACT,
- "DIFF_SUB",
- 0,
- "Differential",
- "Stores and apply difference between reference and local value (NOT USED)"},
- {IDOVERRIDE_LIBRARY_OP_MULTIPLY,
- "FACT_MULTIPLY",
- 0,
- "Factor",
- "Stores and apply multiplication factor between reference and local value (NOT USED)"},
- {IDOVERRIDE_LIBRARY_OP_INSERT_AFTER,
- "INSERT_AFTER",
- 0,
- "Insert After",
- "Insert a new item into collection after the one referenced in subitem_reference_name or "
- "_index"},
- {IDOVERRIDE_LIBRARY_OP_INSERT_BEFORE,
- "INSERT_BEFORE",
- 0,
- "Insert Before",
- "Insert a new item into collection after the one referenced in subitem_reference_name or "
- "_index (NOT USED)"},
- {0, NULL, 0, NULL, NULL},
- };
-
static const EnumPropertyItem override_library_property_flag_items[] = {
{IDOVERRIDE_LIBRARY_FLAG_MANDATORY,
"MANDATORY",
@@ -1329,7 +1426,7 @@ static void rna_def_ID_override_library_property_operation(BlenderRNA *brna)
prop = RNA_def_enum(srna,
"operation",
- override_library_property_operation_items,
+ rna_enum_override_library_property_operation_items,
IDOVERRIDE_LIBRARY_OP_REPLACE,
"Operation",
"What override operation is performed");
@@ -1386,6 +1483,66 @@ static void rna_def_ID_override_library_property_operation(BlenderRNA *brna)
RNA_def_property_clear_flag(prop, PROP_EDITABLE); /* For now. */
}
+static void rna_def_ID_override_library_property_operations(BlenderRNA *brna, PropertyRNA *cprop)
+{
+ StructRNA *srna;
+ FunctionRNA *func;
+ PropertyRNA *parm;
+
+ RNA_def_property_srna(cprop, "IDOverrideLibraryPropertyOperations");
+ srna = RNA_def_struct(brna, "IDOverrideLibraryPropertyOperations", NULL);
+ RNA_def_struct_sdna(srna, "IDOverrideLibraryProperty");
+ RNA_def_struct_ui_text(srna, "Override Operations", "Collection of override operations");
+
+ /* Add Property */
+ func = RNA_def_function(srna, "add", "rna_ID_override_library_property_operations_add");
+ RNA_def_function_ui_description(func, "Add a new operation");
+ RNA_def_function_flag(func, FUNC_USE_REPORTS);
+ parm = RNA_def_enum(func,
+ "operation",
+ rna_enum_override_library_property_operation_items,
+ IDOVERRIDE_LIBRARY_OP_REPLACE,
+ "Operation",
+ "What override operation is performed");
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_string(func,
+ "subitem_reference_name",
+ NULL,
+ INT_MAX,
+ "Subitem Reference Name",
+ "Used to handle insertions into collection");
+ parm = RNA_def_string(func,
+ "subitem_local_name",
+ NULL,
+ INT_MAX,
+ "Subitem Local Name",
+ "Used to handle insertions into collection");
+ parm = RNA_def_int(func,
+ "subitem_reference_index",
+ -1,
+ -1,
+ INT_MAX,
+ "Subitem Reference Index",
+ "Used to handle insertions into collection",
+ -1,
+ INT_MAX);
+ parm = RNA_def_int(func,
+ "subitem_local_index",
+ -1,
+ -1,
+ INT_MAX,
+ "Subitem Local Index",
+ "Used to handle insertions into collection",
+ -1,
+ INT_MAX);
+ parm = RNA_def_pointer(func,
+ "property",
+ "IDOverrideLibraryPropertyOperation",
+ "New Operation",
+ "Created operation");
+ RNA_def_function_return(func, parm);
+}
+
static void rna_def_ID_override_library_property(BlenderRNA *brna)
{
StructRNA *srna;
@@ -1405,18 +1562,47 @@ static void rna_def_ID_override_library_property(BlenderRNA *brna)
"RNA path leading to that property, from owning ID");
RNA_def_property_clear_flag(prop, PROP_EDITABLE); /* For now. */
- RNA_def_collection(srna,
- "operations",
- "IDOverrideLibraryPropertyOperation",
- "Operations",
- "List of overriding operations for a property");
+ prop = RNA_def_collection(srna,
+ "operations",
+ "IDOverrideLibraryPropertyOperation",
+ "Operations",
+ "List of overriding operations for a property");
+ rna_def_ID_override_library_property_operations(brna, prop);
rna_def_ID_override_library_property_operation(brna);
}
+static void rna_def_ID_override_library_properties(BlenderRNA *brna, PropertyRNA *cprop)
+{
+ StructRNA *srna;
+ FunctionRNA *func;
+ PropertyRNA *parm;
+
+ RNA_def_property_srna(cprop, "IDOverrideLibraryProperties");
+ srna = RNA_def_struct(brna, "IDOverrideLibraryProperties", NULL);
+ RNA_def_struct_sdna(srna, "IDOverrideLibrary");
+ RNA_def_struct_ui_text(srna, "Override Properties", "Collection of override properties");
+
+ /* Add Property */
+ func = RNA_def_function(srna, "add", "rna_ID_override_library_properties_add");
+ RNA_def_function_ui_description(
+ func, "Add a property to the override library when it doesn't exist yet");
+ RNA_def_function_flag(func, FUNC_USE_REPORTS);
+ parm = RNA_def_pointer(func,
+ "property",
+ "IDOverrideLibraryProperty",
+ "New Property",
+ "Newly created override property or existing one");
+ RNA_def_function_return(func, parm);
+ parm = RNA_def_string(
+ func, "rna_path", NULL, 256, "RNA Path", "RNA-Path of the property to add");
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+}
+
static void rna_def_ID_override_library(BlenderRNA *brna)
{
StructRNA *srna;
+ PropertyRNA *prop;
srna = RNA_def_struct(brna, "IDOverrideLibrary", NULL);
RNA_def_struct_ui_text(
@@ -1425,11 +1611,12 @@ static void rna_def_ID_override_library(BlenderRNA *brna)
RNA_def_pointer(
srna, "reference", "ID", "Reference ID", "Linked ID used as reference by this override");
- RNA_def_collection(srna,
- "properties",
- "IDOverrideLibraryProperty",
- "Properties",
- "List of overridden properties");
+ prop = RNA_def_collection(srna,
+ "properties",
+ "IDOverrideLibraryProperty",
+ "Properties",
+ "List of overridden properties");
+ rna_def_ID_override_library_properties(brna, prop);
rna_def_ID_override_library_property(brna);
}
@@ -1540,12 +1727,12 @@ static void rna_def_ID(BlenderRNA *brna)
srna, "override_library", "IDOverrideLibrary", "Library Override", "Library override data");
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
- prop = RNA_def_pointer(
- srna,
- "preview",
- "ImagePreview",
- "Preview",
- "Preview image and icon of this data-block (None if not supported for this type of data)");
+ prop = RNA_def_pointer(srna,
+ "preview",
+ "ImagePreview",
+ "Preview",
+ "Preview image and icon of this data-block (always None if not supported "
+ "for this type of data)");
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON);
RNA_def_property_pointer_funcs(prop, "rna_IDPreview_get", NULL, NULL, NULL);
@@ -1567,6 +1754,18 @@ static void rna_def_ID(BlenderRNA *brna)
parm = RNA_def_pointer(func, "id", "ID", "", "New copy of the ID");
RNA_def_function_return(func, parm);
+ func = RNA_def_function(srna, "asset_mark", "rna_ID_asset_mark");
+ RNA_def_function_ui_description(
+ func,
+ "Enable easier reuse of the data-block through the Asset Browser, with the help of "
+ "customizable metadata (like previews, descriptions and tags)");
+ RNA_def_function_flag(func, FUNC_USE_CONTEXT);
+
+ func = RNA_def_function(srna, "asset_clear", "rna_ID_asset_clear");
+ RNA_def_function_ui_description(
+ func,
+ "Delete all asset metadata and turn the asset data-block back into a normal data-block");
+
func = RNA_def_function(srna, "override_create", "rna_ID_override_create");
RNA_def_function_ui_description(func,
"Create an overridden local copy of this linked data-block (not "
@@ -1581,6 +1780,34 @@ static void rna_def_ID(BlenderRNA *brna)
"Whether local usages of the linked ID should be remapped to the new "
"library override of it");
+ func = RNA_def_function(srna, "override_hierarchy_create", "rna_ID_override_hierarchy_create");
+ RNA_def_function_ui_description(
+ func,
+ "Create an overridden local copy of this linked data-block, and most of its dependencies "
+ "when it is a Collection or and Object");
+ RNA_def_function_flag(func, FUNC_USE_MAIN);
+ parm = RNA_def_pointer(func, "id", "ID", "", "New overridden local copy of the root ID");
+ RNA_def_function_return(func, parm);
+ parm = RNA_def_pointer(
+ func, "scene", "Scene", "", "In which scene the new overrides should be instantiated");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ parm = RNA_def_pointer(func,
+ "view_layer",
+ "ViewLayer",
+ "",
+ "In which view layer the new overrides should be instantiated");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ RNA_def_pointer(func,
+ "reference",
+ "ID",
+ "",
+ "Another ID (usually an Object or Collection) used to decide where to "
+ "instantiate the new overrides");
+
+ func = RNA_def_function(srna, "override_template_create", "rna_ID_override_template_create");
+ RNA_def_function_ui_description(func, "Create an override template for this ID");
+ RNA_def_function_flag(func, FUNC_USE_REPORTS);
+
func = RNA_def_function(srna, "user_clear", "rna_ID_user_clear");
RNA_def_function_ui_description(func,
"Clear the user count of a data-block so its not saved, "
@@ -1643,6 +1870,13 @@ static void rna_def_ID(BlenderRNA *brna)
"e.g. when calling :class:`bpy.types.Scene.update`");
RNA_def_enum_flag(func, "refresh", update_flag_items, 0, "", "Type of updates to perform");
+ func = RNA_def_function(srna, "preview_ensure", "BKE_previewimg_id_ensure");
+ RNA_def_function_ui_description(func,
+ "Ensure that this ID has preview data (if ID type supports it)");
+ parm = RNA_def_pointer(
+ func, "preview_image", "ImagePreview", "", "The existing or created preview");
+ RNA_def_function_return(func, parm);
+
# ifdef WITH_PYTHON
RNA_def_struct_register_funcs(srna, NULL, NULL, "rna_ID_instance");
# endif
diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c
index f94dc38ddfe..948fef1b51e 100644
--- a/source/blender/makesrna/intern/rna_access.c
+++ b/source/blender/makesrna/intern/rna_access.c
@@ -1189,6 +1189,24 @@ PropertyUnit RNA_property_unit(PropertyRNA *prop)
return RNA_SUBTYPE_UNIT(RNA_property_subtype(prop));
}
+PropertyScaleType RNA_property_ui_scale(PropertyRNA *prop)
+{
+ PropertyRNA *rna_prop = rna_ensure_property(prop);
+
+ switch (rna_prop->type) {
+ case PROP_INT: {
+ IntPropertyRNA *iprop = (IntPropertyRNA *)rna_prop;
+ return iprop->ui_scale_type;
+ }
+ case PROP_FLOAT: {
+ FloatPropertyRNA *fprop = (FloatPropertyRNA *)rna_prop;
+ return fprop->ui_scale_type;
+ }
+ default:
+ return PROP_SCALE_LINEAR;
+ }
+}
+
int RNA_property_flag(PropertyRNA *prop)
{
return rna_ensure_property(prop)->flag;
@@ -1623,35 +1641,35 @@ void RNA_property_enum_items_ex(bContext *C,
*r_free = false;
- if (!use_static && eprop->item_fn && (C != NULL || (prop->flag & PROP_ENUM_NO_CONTEXT))) {
- const EnumPropertyItem *item;
+ if (!use_static && (eprop->item_fn != NULL)) {
+ const bool no_context = (prop->flag & PROP_ENUM_NO_CONTEXT) ||
+ ((ptr->type->flag & STRUCT_NO_CONTEXT_WITHOUT_OWNER_ID) &&
+ (ptr->owner_id == NULL));
+ if (C != NULL || no_context) {
+ const EnumPropertyItem *item;
- if (prop->flag & PROP_ENUM_NO_CONTEXT) {
- item = eprop->item_fn(NULL, ptr, prop, r_free);
- }
- else {
- item = eprop->item_fn(C, ptr, prop, r_free);
- }
+ item = eprop->item_fn(no_context ? NULL : C, ptr, prop, r_free);
- /* any callbacks returning NULL should be fixed */
- BLI_assert(item != NULL);
+ /* any callbacks returning NULL should be fixed */
+ BLI_assert(item != NULL);
- if (r_totitem) {
- int tot;
- for (tot = 0; item[tot].identifier; tot++) {
- /* pass */
+ if (r_totitem) {
+ int tot;
+ for (tot = 0; item[tot].identifier; tot++) {
+ /* pass */
+ }
+ *r_totitem = tot;
}
- *r_totitem = tot;
- }
- *r_item = item;
- }
- else {
- *r_item = eprop->item;
- if (r_totitem) {
- *r_totitem = eprop->totitem;
+ *r_item = item;
+ return;
}
}
+
+ *r_item = eprop->item;
+ if (r_totitem) {
+ *r_totitem = eprop->totitem;
+ }
}
void RNA_property_enum_items(bContext *C,
@@ -1753,42 +1771,42 @@ void RNA_property_enum_items_gettexted_all(bContext *C,
*r_totitem = eprop->totitem;
}
- if (eprop->item_fn && (C != NULL || (prop->flag & PROP_ENUM_NO_CONTEXT))) {
- const EnumPropertyItem *item;
- int i;
- bool free = false;
+ if (eprop->item_fn != NULL) {
+ const bool no_context = (prop->flag & PROP_ENUM_NO_CONTEXT) ||
+ ((ptr->type->flag & STRUCT_NO_CONTEXT_WITHOUT_OWNER_ID) &&
+ (ptr->owner_id == NULL));
+ if (C != NULL || no_context) {
+ const EnumPropertyItem *item;
+ int i;
+ bool free = false;
- if (prop->flag & PROP_ENUM_NO_CONTEXT) {
- item = eprop->item_fn(NULL, ptr, prop, &free);
- }
- else {
- item = eprop->item_fn(C, ptr, prop, &free);
- }
+ item = eprop->item_fn(no_context ? NULL : NULL, ptr, prop, &free);
- /* any callbacks returning NULL should be fixed */
- BLI_assert(item != NULL);
+ /* any callbacks returning NULL should be fixed */
+ BLI_assert(item != NULL);
- for (i = 0; i < eprop->totitem; i++) {
- bool exists = false;
- int i_fixed;
+ for (i = 0; i < eprop->totitem; i++) {
+ bool exists = false;
+ int i_fixed;
- /* Items that do not exist on list are returned,
- * but have their names/identifiers NULL'ed out. */
- for (i_fixed = 0; item[i_fixed].identifier; i_fixed++) {
- if (STREQ(item[i_fixed].identifier, item_array[i].identifier)) {
- exists = true;
- break;
+ /* Items that do not exist on list are returned,
+ * but have their names/identifiers NULL'ed out. */
+ for (i_fixed = 0; item[i_fixed].identifier; i_fixed++) {
+ if (STREQ(item[i_fixed].identifier, item_array[i].identifier)) {
+ exists = true;
+ break;
+ }
}
- }
- if (!exists) {
- item_array[i].name = NULL;
- item_array[i].identifier = "";
+ if (!exists) {
+ item_array[i].name = NULL;
+ item_array[i].identifier = "";
+ }
}
- }
- if (free) {
- MEM_freeN((void *)item);
+ if (free) {
+ MEM_freeN((void *)item);
+ }
}
}
@@ -6097,13 +6115,25 @@ char *RNA_path_full_ID_py(Main *bmain, ID *id)
path = "";
}
- char id_esc[(sizeof(id->name) - 2) * 2];
+ char lib_filepath_esc[(sizeof(id->lib->filepath) * 2) + 4];
+ if (id->lib != NULL) {
+ int ofs = 0;
+ memcpy(lib_filepath_esc, ", \"", 3);
+ ofs += 3;
+ ofs += BLI_str_escape(lib_filepath_esc + ofs, id->lib->filepath, sizeof(lib_filepath_esc));
+ memcpy(lib_filepath_esc + ofs, "\"", 2);
+ }
+ else {
+ lib_filepath_esc[0] = '\0';
+ }
+ char id_esc[(sizeof(id->name) - 2) * 2];
BLI_str_escape(id_esc, id->name + 2, sizeof(id_esc));
- return BLI_sprintfN("bpy.data.%s[\"%s\"]%s%s",
+ return BLI_sprintfN("bpy.data.%s[\"%s\"%s]%s%s",
BKE_idtype_idcode_to_name_plural(GS(id->name)),
id_esc,
+ lib_filepath_esc,
path[0] ? "." : "",
path);
}
diff --git a/source/blender/makesrna/intern/rna_action.c b/source/blender/makesrna/intern/rna_action.c
index 69ddcee60fa..fb9f5e0292e 100644
--- a/source/blender/makesrna/intern/rna_action.c
+++ b/source/blender/makesrna/intern/rna_action.c
@@ -407,8 +407,10 @@ static void rna_def_dopesheet(BlenderRNA *brna)
/* Multi-word fuzzy search option for name/text filters */
prop = RNA_def_property(srna, "use_multi_word_filter", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", ADS_FLAG_FUZZY_NAMES);
- RNA_def_property_ui_text(
- prop, "Multi-Word Fuzzy Filter", "Perform fuzzy/multi-word matching (WARNING: May be slow)");
+ RNA_def_property_ui_text(prop,
+ "Multi-Word Fuzzy Filter",
+ "Perform fuzzy/multi-word matching.\n"
+ "Warning: May be slow");
RNA_def_property_ui_icon(prop, ICON_SORTALPHA, 0);
RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
diff --git a/source/blender/makesrna/intern/rna_action_api.c b/source/blender/makesrna/intern/rna_action_api.c
index 48da32afba5..308a34f9cf1 100644
--- a/source/blender/makesrna/intern/rna_action_api.c
+++ b/source/blender/makesrna/intern/rna_action_api.c
@@ -41,10 +41,32 @@
# include "DNA_anim_types.h"
# include "DNA_curve_types.h"
+static void rna_Action_flip_with_pose(bAction *act, ReportList *reports, Object *ob)
+{
+ if (ob->type != OB_ARMATURE) {
+ BKE_report(reports, RPT_ERROR, "Only armature objects are supported");
+ return;
+ }
+ BKE_action_flip_with_pose(act, ob);
+
+ /* Only for redraw. */
+ WM_main_add_notifier(NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
+}
+
#else
-void RNA_api_action(StructRNA *UNUSED(srna))
+void RNA_api_action(StructRNA *srna)
{
+ FunctionRNA *func;
+ PropertyRNA *parm;
+
+ func = RNA_def_function(srna, "flip_with_pose", "rna_Action_flip_with_pose");
+ RNA_def_function_ui_description(func, "Flip the action around the X axis using a pose");
+ RNA_def_function_flag(func, FUNC_USE_REPORTS);
+
+ parm = RNA_def_pointer(
+ func, "object", "Object", "", "The reference armature object to use when flipping");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
}
#endif
diff --git a/source/blender/makesrna/intern/rna_armature.c b/source/blender/makesrna/intern/rna_armature.c
index 554f04ca23c..c8fccfc27f8 100644
--- a/source/blender/makesrna/intern/rna_armature.c
+++ b/source/blender/makesrna/intern/rna_armature.c
@@ -674,7 +674,7 @@ static bool rna_Armature_is_editmode_get(PointerRNA *ptr)
return (arm->edbo != NULL);
}
-static void rna_Armature_transform(bArmature *arm, float *mat)
+static void rna_Armature_transform(bArmature *arm, float mat[16])
{
ED_armature_transform(arm, (const float(*)[4])mat, true);
}
@@ -1537,6 +1537,16 @@ static void rna_def_armature(BlenderRNA *brna)
RNA_def_property_update(prop, 0, "rna_Armature_redraw_data");
RNA_def_property_flag(prop, PROP_LIB_EXCEPTION);
+ prop = RNA_def_property(srna, "axes_position", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_float_sdna(prop, NULL, "axes_position");
+ RNA_def_property_range(prop, 0.0, 1.0);
+ RNA_def_property_ui_range(prop, 0.0, 1.0, 10, 1);
+ RNA_def_property_ui_text(prop,
+ "Axes Position",
+ "The position for the axes on the bone. Increasing the value moves it "
+ "closer to the tip; decreasing moves it closer to the root");
+ RNA_def_property_update(prop, 0, "rna_Armature_redraw_data");
+
prop = RNA_def_property(srna, "show_names", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", ARM_DRAWNAMES);
RNA_def_property_ui_text(prop, "Display Names", "Display bone names");
diff --git a/source/blender/makesrna/intern/rna_armature_api.c b/source/blender/makesrna/intern/rna_armature_api.c
index 36aa0401c7d..a02f55667e3 100644
--- a/source/blender/makesrna/intern/rna_armature_api.c
+++ b/source/blender/makesrna/intern/rna_armature_api.c
@@ -44,7 +44,7 @@ static void rna_EditBone_align_roll(EditBone *ebo, float no[3])
ebo->roll = ED_armature_ebone_roll_to_vector(ebo, no, false);
}
-static float rna_Bone_do_envelope(Bone *bone, float *vec)
+static float rna_Bone_do_envelope(Bone *bone, float vec[3])
{
float scale = (bone->flag & BONE_MULT_VG_ENV) == BONE_MULT_VG_ENV ? bone->weight : 1.0f;
return distfactor_to_bone(vec,
@@ -56,11 +56,11 @@ static float rna_Bone_do_envelope(Bone *bone, float *vec)
}
static void rna_Bone_convert_local_to_pose(Bone *bone,
- float *r_matrix,
- float *matrix,
- float *matrix_local,
- float *parent_matrix,
- float *parent_matrix_local,
+ float r_matrix[16],
+ float matrix[16],
+ float matrix_local[16],
+ float parent_matrix[16],
+ float parent_matrix_local[16],
bool invert)
{
BoneParentTransform bpt;
@@ -89,14 +89,14 @@ static void rna_Bone_convert_local_to_pose(Bone *bone,
BKE_bone_parent_transform_apply(&bpt, (float(*)[4])matrix, (float(*)[4])r_matrix);
}
-static void rna_Bone_MatrixFromAxisRoll(float *axis, float roll, float *r_matrix)
+static void rna_Bone_MatrixFromAxisRoll(float axis[3], float roll, float r_matrix[9])
{
vec_roll_to_mat3(axis, roll, (float(*)[3])r_matrix);
}
-static void rna_Bone_AxisRollFromMatrix(float *matrix,
- float *axis_override,
- float *r_axis,
+static void rna_Bone_AxisRollFromMatrix(float matrix[9],
+ float axis_override[3],
+ float r_axis[3],
float *r_roll)
{
float mat[3][3];
diff --git a/source/blender/makesrna/intern/rna_attribute.c b/source/blender/makesrna/intern/rna_attribute.c
index 7976df3e4e4..a256002ffc1 100644
--- a/source/blender/makesrna/intern/rna_attribute.c
+++ b/source/blender/makesrna/intern/rna_attribute.c
@@ -49,6 +49,19 @@ const EnumPropertyItem rna_enum_attribute_type_items[] = {
{0, NULL, 0, NULL, NULL},
};
+const EnumPropertyItem rna_enum_attribute_type_with_auto_items[] = {
+ {CD_AUTO_FROM_NAME, "AUTO", 0, "Auto", ""},
+ {CD_PROP_FLOAT, "FLOAT", 0, "Float", "Floating-point value"},
+ {CD_PROP_INT32, "INT", 0, "Integer", "32-bit integer"},
+ {CD_PROP_FLOAT3, "FLOAT_VECTOR", 0, "Vector", "3D vector with floating-point values"},
+ {CD_PROP_COLOR, "FLOAT_COLOR", 0, "Color", "RGBA color with floating-point precisions"},
+ {CD_MLOOPCOL, "BYTE_COLOR", 0, "Byte Color", "RGBA color with 8-bit precision"},
+ {CD_PROP_STRING, "STRING", 0, "String", "Text string"},
+ {CD_PROP_BOOL, "BOOLEAN", 0, "Boolean", "True or false"},
+ {CD_PROP_FLOAT2, "FLOAT2", 0, "2D Vector", "2D vector with floating-point values"},
+ {0, NULL, 0, NULL, NULL},
+};
+
const EnumPropertyItem rna_enum_attribute_domain_items[] = {
/* Not implement yet */
// {ATTR_DOMAIN_GEOMETRY, "GEOMETRY", 0, "Geometry", "Attribute on (whole) geometry"},
@@ -58,7 +71,7 @@ const EnumPropertyItem rna_enum_attribute_domain_items[] = {
{ATTR_DOMAIN_CORNER, "CORNER", 0, "Face Corner", "Attribute on mesh face corner"},
/* Not implement yet */
// {ATTR_DOMAIN_GRIDS, "GRIDS", 0, "Grids", "Attribute on mesh multires grids"},
- {ATTR_DOMAIN_CURVE, "CURVE", 0, "Curve", "Attribute on hair curve"},
+ {ATTR_DOMAIN_CURVE, "CURVE", 0, "Spline", "Attribute on spline"},
{0, NULL, 0, NULL, NULL},
};
@@ -68,6 +81,7 @@ const EnumPropertyItem rna_enum_attribute_domain_with_auto_items[] = {
{ATTR_DOMAIN_EDGE, "EDGE", 0, "Edge", "Attribute on mesh edge"},
{ATTR_DOMAIN_FACE, "FACE", 0, "Face", "Attribute on mesh faces"},
{ATTR_DOMAIN_CORNER, "CORNER", 0, "Face Corner", "Attribute on mesh face corner"},
+ {ATTR_DOMAIN_CURVE, "CURVE", 0, "Spline", "Attribute on spline"},
{0, NULL, 0, NULL, NULL},
};
diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c
index e7daa55af6c..7e1d513502c 100644
--- a/source/blender/makesrna/intern/rna_brush.c
+++ b/source/blender/makesrna/intern/rna_brush.c
@@ -1617,6 +1617,15 @@ static void rna_def_gpencil_options(BlenderRNA *brna)
prop, "Stroke Extension", "Strokes end extension for closing gaps, use zero to disable");
RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0);
+ /* Number of pixels to dilate fill area. */
+ prop = RNA_def_property(srna, "dilate", PROP_INT, PROP_PIXEL);
+ RNA_def_property_int_sdna(prop, NULL, "dilate_pixels");
+ RNA_def_property_range(prop, 0, 20);
+ RNA_def_property_int_default(prop, 1);
+ RNA_def_property_ui_text(prop, "Dilate", "Number of pixels to dilate fill area");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
+
/* Flags */
prop = RNA_def_property(srna, "use_pressure", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSH_USE_PRESSURE);
diff --git a/source/blender/makesrna/intern/rna_cloth.c b/source/blender/makesrna/intern/rna_cloth.c
index 2bc00dd5af5..9e57368f8f9 100644
--- a/source/blender/makesrna/intern/rna_cloth.c
+++ b/source/blender/makesrna/intern/rna_cloth.c
@@ -652,6 +652,7 @@ static void rna_def_cloth_sim_settings(BlenderRNA *brna)
prop = RNA_def_property(srna, "mass", PROP_FLOAT, PROP_UNIT_MASS);
RNA_def_property_range(prop, 0.0f, FLT_MAX);
RNA_def_property_ui_text(prop, "Vertex Mass", "The mass of each vertex on the cloth material");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_update(prop, 0, "rna_cloth_update");
prop = RNA_def_property(srna, "vertex_group_mass", PROP_STRING, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_color.c b/source/blender/makesrna/intern/rna_color.c
index 206ebc2cb14..54f9a93d90a 100644
--- a/source/blender/makesrna/intern/rna_color.c
+++ b/source/blender/makesrna/intern/rna_color.c
@@ -650,7 +650,7 @@ static void rna_ColorManagedColorspaceSettings_reload_update(Main *bmain,
seq->strip->proxy->anim = NULL;
}
- SEQ_relations_invalidate_cache_preprocessed(scene, seq);
+ SEQ_relations_invalidate_cache_raw(scene, seq);
}
else {
SEQ_ALL_BEGIN (scene->ed, seq) {
diff --git a/source/blender/makesrna/intern/rna_constraint.c b/source/blender/makesrna/intern/rna_constraint.c
index 843cb326be2..b363dcd4ba9 100644
--- a/source/blender/makesrna/intern/rna_constraint.c
+++ b/source/blender/makesrna/intern/rna_constraint.c
@@ -1923,7 +1923,8 @@ static void rna_def_constraint_follow_path(BlenderRNA *brna)
prop = RNA_def_property(srna, "offset_factor", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "offset_fac");
- RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_range(prop, -FLT_MAX, FLT_MAX);
+ RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.01, 3);
RNA_def_property_ui_text(
prop, "Offset Factor", "Percentage value defining target position along length of curve");
RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
@@ -2597,6 +2598,12 @@ static void rna_def_constraint_rotation_limit(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Maximum Z", "Highest Z value to allow");
RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
+ prop = RNA_def_property(srna, "euler_order", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "euler_order");
+ RNA_def_property_enum_items(prop, euler_order_items);
+ RNA_def_property_ui_text(prop, "Euler Order", "Explicitly specify the euler rotation order");
+ RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
+
prop = RNA_def_property(srna, "use_transform_limit", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag2", LIMIT_TRANSFORM);
RNA_def_property_ui_text(
diff --git a/source/blender/makesrna/intern/rna_curve.c b/source/blender/makesrna/intern/rna_curve.c
index 3e90b4bd9d4..dd7d50a80ba 100644
--- a/source/blender/makesrna/intern/rna_curve.c
+++ b/source/blender/makesrna/intern/rna_curve.c
@@ -357,9 +357,8 @@ static void rna_Curve_dimension_set(PointerRNA *ptr, int value)
}
else {
cu->flag &= ~CU_3D;
+ BKE_curve_dimension_update(cu);
}
-
- BKE_curve_curve_dimension_update(cu);
}
static const EnumPropertyItem *rna_Curve_fill_mode_itemf(bContext *UNUSED(C),
@@ -721,10 +720,6 @@ static Nurb *rna_Curve_spline_new(Curve *cu, int type)
nu->resolv = cu->resolv;
nu->flag = CU_SMOOTH;
- if ((cu->flag & CU_3D) == 0) {
- nu->flag |= CU_2D;
- }
-
BLI_addtail(BKE_curve_nurbs_get(cu), nu);
return nu;
@@ -1035,6 +1030,14 @@ static void rna_def_path(BlenderRNA *UNUSED(brna), StructRNA *srna)
RNA_def_property_ui_text(prop, "Follow", "Make curve path children to rotate along the path");
RNA_def_property_update(prop, 0, "rna_Curve_update_data");
+ prop = RNA_def_property(srna, "use_path_clamp", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", CU_PATH_CLAMP);
+ RNA_def_property_ui_text(
+ prop,
+ "Clamp",
+ "Clamp the curve path children so they can't travel past the start/end point of the curve");
+ RNA_def_property_update(prop, 0, "rna_Curve_update_data");
+
prop = RNA_def_property(srna, "use_stretch", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", CU_STRETCH);
RNA_def_property_ui_text(prop,
@@ -1373,7 +1376,7 @@ static void rna_def_charinfo(BlenderRNA *brna)
prop = RNA_def_property(srna, "material_index", PROP_INT, PROP_UNSIGNED);
// RNA_def_property_int_sdna(prop, NULL, "mat_nr");
- RNA_def_property_ui_text(prop, "Material Index", "");
+ RNA_def_property_ui_text(prop, "Material Index", "Material slot index of this character");
RNA_def_property_int_funcs(prop,
"rna_ChariInfo_material_index_get",
"rna_ChariInfo_material_index_set",
@@ -1834,7 +1837,7 @@ static void rna_def_curve(BlenderRNA *brna)
prop = RNA_def_property(srna, "texspace_location", PROP_FLOAT, PROP_TRANSLATION);
RNA_def_property_array(prop, 3);
- RNA_def_property_ui_text(prop, "Texture Space Location", "Texture space location");
+ RNA_def_property_ui_text(prop, "Texture Space Location", "");
RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT);
RNA_def_property_editable_func(prop, "rna_Curve_texspace_editable");
RNA_def_property_float_funcs(
@@ -1844,7 +1847,7 @@ static void rna_def_curve(BlenderRNA *brna)
prop = RNA_def_property(srna, "texspace_size", PROP_FLOAT, PROP_XYZ);
RNA_def_property_array(prop, 3);
RNA_def_property_flag(prop, PROP_PROPORTIONAL);
- RNA_def_property_ui_text(prop, "Texture Space Size", "Texture space size");
+ RNA_def_property_ui_text(prop, "Texture Space Size", "");
RNA_def_property_editable_func(prop, "rna_Curve_texspace_editable");
RNA_def_property_float_funcs(
prop, "rna_Curve_texspace_size_get", "rna_Curve_texspace_size_set", NULL);
@@ -2065,7 +2068,7 @@ static void rna_def_curve_nurb(BlenderRNA *brna)
prop = RNA_def_property(srna, "material_index", PROP_INT, PROP_UNSIGNED);
RNA_def_property_int_sdna(prop, NULL, "mat_nr");
- RNA_def_property_ui_text(prop, "Material Index", "");
+ RNA_def_property_ui_text(prop, "Material Index", "Material slot index of this curve");
RNA_def_property_int_funcs(prop, NULL, NULL, "rna_Curve_material_index_range");
RNA_def_property_update(prop, 0, "rna_Curve_update_data");
diff --git a/source/blender/makesrna/intern/rna_curve_api.c b/source/blender/makesrna/intern/rna_curve_api.c
index 94fdb130026..beea607e8c0 100644
--- a/source/blender/makesrna/intern/rna_curve_api.c
+++ b/source/blender/makesrna/intern/rna_curve_api.c
@@ -35,7 +35,7 @@
#include "rna_internal.h" /* own include */
#ifdef RNA_RUNTIME
-static void rna_Curve_transform(Curve *cu, float *mat, bool shape_keys)
+static void rna_Curve_transform(Curve *cu, float mat[16], bool shape_keys)
{
BKE_curve_transform(cu, (const float(*)[4])mat, shape_keys, true);
diff --git a/source/blender/makesrna/intern/rna_curveprofile.c b/source/blender/makesrna/intern/rna_curveprofile.c
index bb54d55f8bd..b3ab8cc15a2 100644
--- a/source/blender/makesrna/intern/rna_curveprofile.c
+++ b/source/blender/makesrna/intern/rna_curveprofile.c
@@ -137,7 +137,7 @@ static void rna_CurveProfile_remove_point(CurveProfile *profile,
static void rna_CurveProfile_evaluate(struct CurveProfile *profile,
ReportList *reports,
float length_portion,
- float *location)
+ float location[2])
{
if (!profile->table) {
BKE_report(reports, RPT_ERROR, "CurveProfile table not initialized, call initialize()");
diff --git a/source/blender/makesrna/intern/rna_define.c b/source/blender/makesrna/intern/rna_define.c
index 08f52be4257..9b9d561603b 100644
--- a/source/blender/makesrna/intern/rna_define.c
+++ b/source/blender/makesrna/intern/rna_define.c
@@ -1754,6 +1754,28 @@ void RNA_def_property_ui_range(
}
}
+void RNA_def_property_ui_scale_type(PropertyRNA *prop, PropertyScaleType ui_scale_type)
+{
+ StructRNA *srna = DefRNA.laststruct;
+
+ switch (prop->type) {
+ case PROP_INT: {
+ IntPropertyRNA *iprop = (IntPropertyRNA *)prop;
+ iprop->ui_scale_type = ui_scale_type;
+ break;
+ }
+ case PROP_FLOAT: {
+ FloatPropertyRNA *fprop = (FloatPropertyRNA *)prop;
+ fprop->ui_scale_type = ui_scale_type;
+ break;
+ }
+ default:
+ CLOG_ERROR(&LOG, "\"%s.%s\", invalid type for scale.", srna->identifier, prop->identifier);
+ DefRNA.error = true;
+ break;
+ }
+}
+
void RNA_def_property_range(PropertyRNA *prop, double min, double max)
{
StructRNA *srna = DefRNA.laststruct;
@@ -2410,6 +2432,10 @@ void RNA_def_property_int_sdna(PropertyRNA *prop, const char *structname, const
iprop->softmin = -10000; /* rather arbitrary .. */
iprop->softmax = 10000;
}
+ else if (dp->dnatype && STREQ(dp->dnatype, "int8_t")) {
+ iprop->hardmin = iprop->softmin = INT8_MIN;
+ iprop->hardmax = iprop->softmax = INT8_MAX;
+ }
if (prop->subtype == PROP_UNSIGNED || prop->subtype == PROP_PERCENTAGE ||
prop->subtype == PROP_FACTOR) {
diff --git a/source/blender/makesrna/intern/rna_depsgraph.c b/source/blender/makesrna/intern/rna_depsgraph.c
index 36d39c5c201..85071e8cd19 100644
--- a/source/blender/makesrna/intern/rna_depsgraph.c
+++ b/source/blender/makesrna/intern/rna_depsgraph.c
@@ -505,11 +505,10 @@ static void rna_def_depsgraph_instance(BlenderRNA *brna)
PropertyRNA *prop;
srna = RNA_def_struct(brna, "DepsgraphObjectInstance", NULL);
- RNA_def_struct_ui_text(
- srna,
- "Dependency Graph Object Instance",
- "Extended information about dependency graph object iterator "
- "(WARNING: all data here is *evaluated* one, not original .blend IDs...)");
+ RNA_def_struct_ui_text(srna,
+ "Dependency Graph Object Instance",
+ "Extended information about dependency graph object iterator "
+ "(Warning: All data here is 'evaluated' one, not original .blend IDs)");
prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE);
RNA_def_property_struct_type(prop, "Object");
@@ -773,7 +772,7 @@ static void rna_def_depsgraph(BlenderRNA *brna)
RNA_def_property_ui_text(prop,
"Object Instances",
"All object instances to display or render "
- "(WARNING: only use this as an iterator, never as a sequence, "
+ "(Warning: Only use this as an iterator, never as a sequence, "
"and do not keep any references to its items)");
prop = RNA_def_property(srna, "updates", PROP_COLLECTION, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_fcurve.c b/source/blender/makesrna/intern/rna_fcurve.c
index 1c7f6ef5cb3..1b89d866aba 100644
--- a/source/blender/makesrna/intern/rna_fcurve.c
+++ b/source/blender/makesrna/intern/rna_fcurve.c
@@ -1160,6 +1160,7 @@ static void rna_def_fmodifier_generator(BlenderRNA *brna)
/* define common props */
prop = RNA_def_property(srna, "use_additive", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_GENERATOR_ADDITIVE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop,
"Additive",
"Values generated by this modifier are applied on top of "
@@ -1168,12 +1169,14 @@ static void rna_def_fmodifier_generator(BlenderRNA *brna)
prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, generator_mode_items);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Mode", "Type of generator to use");
RNA_def_property_update(
prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_verify_data_update");
/* order of the polynomial */
prop = RNA_def_property(srna, "poly_order", PROP_INT, PROP_NONE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop,
"Polynomial Order",
@@ -1184,6 +1187,7 @@ static void rna_def_fmodifier_generator(BlenderRNA *brna)
/* coefficients array */
prop = RNA_def_property(srna, "coefficients", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_array(prop, 32);
RNA_def_property_flag(prop, PROP_DYNAMIC);
RNA_def_property_dynamic_array_funcs(prop, "rna_FModifierGenerator_coefficients_get_length");
@@ -1221,25 +1225,30 @@ static void rna_def_fmodifier_function_generator(BlenderRNA *brna)
/* coefficients */
prop = RNA_def_property(srna, "amplitude", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop, "Amplitude", "Scale factor determining the maximum/minimum values");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "phase_multiplier", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop, "Phase Multiple", "Scale factor determining the 'speed' of the function");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "phase_offset", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Phase Offset", "Constant factor to offset time by for function");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "value_offset", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Value Offset", "Constant factor to offset values by");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
/* flags */
prop = RNA_def_property(srna, "use_additive", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_GENERATOR_ADDITIVE);
RNA_def_property_ui_text(prop,
"Additive",
@@ -1248,6 +1257,7 @@ static void rna_def_fmodifier_function_generator(BlenderRNA *brna)
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "function_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_enum_sdna(prop, NULL, "type");
RNA_def_property_enum_items(prop, prop_type_items);
RNA_def_property_ui_text(prop, "Type", "Type of built-in function to use");
@@ -1271,17 +1281,20 @@ static void rna_def_fmodifier_envelope_ctrl(BlenderRNA *brna)
*/
prop = RNA_def_property(srna, "min", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "min");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Minimum Value", "Lower bound of envelope at this control-point");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "max", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "max");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Maximum Value", "Upper bound of envelope at this control-point");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
/* Frame */
prop = RNA_def_property(srna, "frame", PROP_FLOAT, PROP_TIME);
RNA_def_property_float_sdna(prop, NULL, "time");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Frame", "Frame this control-point occurs on");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
@@ -1340,6 +1353,7 @@ static void rna_def_fmodifier_envelope(BlenderRNA *brna)
/* Collections */
prop = RNA_def_property(srna, "control_points", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "data", "totvert");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_struct_type(prop, "FModifierEnvelopeControlPoint");
RNA_def_property_ui_text(
prop, "Control Points", "Control points defining the shape of the envelope");
@@ -1348,18 +1362,21 @@ static void rna_def_fmodifier_envelope(BlenderRNA *brna)
/* Range Settings */
prop = RNA_def_property(srna, "reference_value", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "midval");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop, "Reference Value", "Value that envelope's influence is centered around / based on");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "default_min", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "min");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop, "Default Minimum", "Lower distance from Reference Value for 1:1 default influence");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "default_max", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "max");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop, "Default Maximum", "Upper distance from Reference Value for 1:1 default influence");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
@@ -1395,12 +1412,14 @@ static void rna_def_fmodifier_cycles(BlenderRNA *brna)
/* before */
prop = RNA_def_property(srna, "mode_before", PROP_ENUM, PROP_NONE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_enum_sdna(prop, NULL, "before_mode");
RNA_def_property_enum_items(prop, prop_type_items);
RNA_def_property_ui_text(prop, "Before Mode", "Cycling mode to use before first keyframe");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "cycles_before", PROP_INT, PROP_NONE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_int_sdna(prop, NULL, "before_cycles");
RNA_def_property_ui_text(
prop,
@@ -1410,12 +1429,14 @@ static void rna_def_fmodifier_cycles(BlenderRNA *brna)
/* after */
prop = RNA_def_property(srna, "mode_after", PROP_ENUM, PROP_NONE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_enum_sdna(prop, NULL, "after_mode");
RNA_def_property_enum_items(prop, prop_type_items);
RNA_def_property_ui_text(prop, "After Mode", "Cycling mode to use after last keyframe");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "cycles_after", PROP_INT, PROP_NONE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_int_sdna(prop, NULL, "after_cycles");
RNA_def_property_ui_text(prop,
"After Cycles",
@@ -1450,26 +1471,31 @@ static void rna_def_fmodifier_limits(BlenderRNA *brna)
prop = RNA_def_property(srna, "use_min_x", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_LIMIT_XMIN);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Minimum X", "Use the minimum X value");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "use_min_y", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_LIMIT_YMIN);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Minimum Y", "Use the minimum Y value");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "use_max_x", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_LIMIT_XMAX);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Maximum X", "Use the maximum X value");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "use_max_y", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_LIMIT_YMAX);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Maximum Y", "Use the maximum Y value");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "min_x", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "rect.xmin");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_float_funcs(
prop, NULL, "rna_FModifierLimits_minx_set", "rna_FModifierLimits_minx_range");
RNA_def_property_ui_text(prop, "Minimum X", "Lowest X value to allow");
@@ -1477,6 +1503,7 @@ static void rna_def_fmodifier_limits(BlenderRNA *brna)
prop = RNA_def_property(srna, "min_y", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "rect.ymin");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_float_funcs(
prop, NULL, "rna_FModifierLimits_miny_set", "rna_FModifierLimits_miny_range");
RNA_def_property_ui_text(prop, "Minimum Y", "Lowest Y value to allow");
@@ -1484,6 +1511,7 @@ static void rna_def_fmodifier_limits(BlenderRNA *brna)
prop = RNA_def_property(srna, "max_x", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "rect.xmax");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_float_funcs(
prop, NULL, "rna_FModifierLimits_maxx_set", "rna_FModifierLimits_maxx_range");
RNA_def_property_ui_text(prop, "Maximum X", "Highest X value to allow");
@@ -1491,6 +1519,7 @@ static void rna_def_fmodifier_limits(BlenderRNA *brna)
prop = RNA_def_property(srna, "max_y", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "rect.ymax");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_float_funcs(
prop, NULL, "rna_FModifierLimits_maxy_set", "rna_FModifierLimits_maxy_range");
RNA_def_property_ui_text(prop, "Maximum Y", "Highest Y value to allow");
@@ -1519,16 +1548,19 @@ static void rna_def_fmodifier_noise(BlenderRNA *brna)
prop = RNA_def_property(srna, "blend_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "modification");
RNA_def_property_enum_items(prop, prop_modification_items);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Blend Type", "Method of modifying the existing F-Curve");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "scale", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "size");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Scale", "Scaling (in time) of the noise");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "strength", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "strength");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop,
"Strength",
@@ -1537,16 +1569,19 @@ static void rna_def_fmodifier_noise(BlenderRNA *brna)
prop = RNA_def_property(srna, "phase", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "phase");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Phase", "A random seed for the noise effect");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "offset", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "offset");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Offset", "Time offset for the noise effect");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "depth", PROP_INT, PROP_UNSIGNED);
RNA_def_property_int_sdna(prop, NULL, "depth");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Depth", "Amount of fine level detail present in the noise");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
}
@@ -1569,11 +1604,13 @@ static void rna_def_fmodifier_stepped(BlenderRNA *brna)
/* properties */
prop = RNA_def_property(srna, "frame_step", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "step_size");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Step Size", "Number of frames to hold each value");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "frame_offset", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "offset");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop,
"Offset",
"Reference number of frames before frames get held "
@@ -1582,18 +1619,21 @@ static void rna_def_fmodifier_stepped(BlenderRNA *brna)
prop = RNA_def_property(srna, "use_frame_start", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_STEPPED_NO_BEFORE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop, "Use Start Frame", "Restrict modifier to only act after its 'start' frame");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "use_frame_end", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FCM_STEPPED_NO_AFTER);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop, "Use End Frame", "Restrict modifier to only act before its 'end' frame");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, "rna_FModifier_update");
prop = RNA_def_property(srna, "frame_start", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "start_frame");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_float_funcs(prop,
NULL,
"rna_FModifierStepped_frame_start_set",
@@ -1604,6 +1644,7 @@ static void rna_def_fmodifier_stepped(BlenderRNA *brna)
prop = RNA_def_property(srna, "frame_end", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "end_frame");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_float_funcs(
prop, NULL, "rna_FModifierStepped_frame_end_set", "rna_FModifierStepped_end_frame_range");
RNA_def_property_ui_text(
@@ -1641,12 +1682,14 @@ static void rna_def_fmodifier(BlenderRNA *brna)
prop = RNA_def_property(srna, "show_expanded", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE);
RNA_def_property_boolean_sdna(prop, NULL, "ui_expand_flag", 0);
+ // RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_boolean_funcs(prop, NULL, "rna_FModifier_show_expanded_set");
RNA_def_property_ui_text(prop, "Expanded", "F-Curve Modifier's panel is expanded in UI");
RNA_def_property_ui_icon(prop, ICON_DISCLOSURE_TRI_RIGHT, 1);
prop = RNA_def_property(srna, "mute", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FMODIFIER_FLAG_MUTED);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Enabled", "Enable F-Curve modifier evaluation");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME_PROP, "rna_FModifier_update");
RNA_def_property_ui_icon(prop, ICON_CHECKBOX_HLT, -1);
@@ -1654,6 +1697,7 @@ static void rna_def_fmodifier(BlenderRNA *brna)
prop = RNA_def_property(srna, "is_valid", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", FMODIFIER_FLAG_DISABLED);
+ // RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop, "Disabled", "F-Curve Modifier has invalid settings and will not be evaluated");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME_PROP, "rna_FModifier_update");
@@ -1661,6 +1705,7 @@ static void rna_def_fmodifier(BlenderRNA *brna)
/* TODO: setting this to true must ensure that all others in stack are turned off too... */
prop = RNA_def_property(srna, "active", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FMODIFIER_FLAG_ACTIVE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Active", "F-Curve modifier will show settings in the editor");
RNA_def_property_boolean_funcs(prop, NULL, "rna_FModifier_active_set");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME_PROP, "rna_FModifier_active_update");
@@ -1669,6 +1714,7 @@ static void rna_def_fmodifier(BlenderRNA *brna)
/* restricted range */
prop = RNA_def_property(srna, "use_restricted_range", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FMODIFIER_FLAG_RANGERESTRICT);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop,
"Restrict Frame Range",
@@ -1678,6 +1724,7 @@ static void rna_def_fmodifier(BlenderRNA *brna)
prop = RNA_def_property(srna, "frame_start", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "sfra");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_float_funcs(
prop, NULL, "rna_FModifier_start_frame_set", "rna_FModifier_start_frame_range");
RNA_def_property_ui_text(
@@ -1688,6 +1735,7 @@ static void rna_def_fmodifier(BlenderRNA *brna)
prop = RNA_def_property(srna, "frame_end", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "efra");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_float_funcs(
prop, NULL, "rna_FModifier_end_frame_set", "rna_FModifier_end_frame_range");
RNA_def_property_ui_text(
@@ -1698,6 +1746,7 @@ static void rna_def_fmodifier(BlenderRNA *brna)
prop = RNA_def_property(srna, "blend_in", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "blendin");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_float_funcs(prop, NULL, NULL, "rna_FModifier_blending_range");
RNA_def_property_ui_text(
prop, "Blend In", "Number of frames from start frame for influence to take effect");
@@ -1705,6 +1754,7 @@ static void rna_def_fmodifier(BlenderRNA *brna)
prop = RNA_def_property(srna, "blend_out", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "blendout");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_float_funcs(prop, NULL, NULL, "rna_FModifier_blending_range");
RNA_def_property_ui_text(
prop, "Blend Out", "Number of frames from end frame for influence to fade out");
@@ -1713,12 +1763,14 @@ static void rna_def_fmodifier(BlenderRNA *brna)
/* influence */
prop = RNA_def_property(srna, "use_influence", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FMODIFIER_FLAG_USEINFLUENCE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop, "Use Influence", "F-Curve Modifier's effects will be tempered by a default factor");
RNA_def_property_update(prop, NC_ANIMATION | ND_KEYFRAME_PROP, "rna_FModifier_update");
prop = RNA_def_property(srna, "influence", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "influence");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_float_default(prop, 1.0f);
RNA_def_property_ui_text(
@@ -2162,6 +2214,7 @@ static void rna_def_fcurve_modifiers(BlenderRNA *brna, PropertyRNA *cprop)
/* Collection active property */
prop = RNA_def_property(srna, "active", PROP_POINTER, PROP_NONE);
RNA_def_property_struct_type(prop, "FModifier");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_pointer_funcs(
prop, "rna_FCurve_active_modifier_get", "rna_FCurve_active_modifier_set", NULL, NULL);
RNA_def_property_flag(prop, PROP_EDITABLE);
@@ -2378,6 +2431,7 @@ static void rna_def_fcurve(BlenderRNA *brna)
prop = RNA_def_property(srna, "mute", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FCURVE_MUTED);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Muted", "Disable F-Curve Modifier evaluation");
RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, "rna_FCurve_update_eval");
diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c
index 1a0497b72f4..19ed5f960cf 100644
--- a/source/blender/makesrna/intern/rna_gpencil.c
+++ b/source/blender/makesrna/intern/rna_gpencil.c
@@ -1037,7 +1037,7 @@ static bGPDframe *rna_GPencil_frame_copy(bGPDlayer *layer, bGPDframe *src)
static bGPDlayer *rna_GPencil_layer_new(bGPdata *gpd, const char *name, bool setactive)
{
- bGPDlayer *gpl = BKE_gpencil_layer_addnew(gpd, name, setactive != 0);
+ bGPDlayer *gpl = BKE_gpencil_layer_addnew(gpd, name, setactive != 0, false);
WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
@@ -1625,7 +1625,7 @@ static void rna_def_gpencil_stroke(BlenderRNA *brna)
/* Material Index */
prop = RNA_def_property(srna, "material_index", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "mat_nr");
- RNA_def_property_ui_text(prop, "Material Index", "Index of material used in this stroke");
+ RNA_def_property_ui_text(prop, "Material Index", "Material slot index of this stroke");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
/* Settings */
@@ -2114,6 +2114,12 @@ static void rna_def_gpencil_layer(BlenderRNA *brna)
"ViewLayer",
"Only include Layer in this View Layer render output (leave blank to include always)");
+ prop = RNA_def_property(srna, "use_viewlayer_masks", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", GP_LAYER_DISABLE_MASKS_IN_VIEWLAYER);
+ RNA_def_property_ui_text(
+ prop, "Use Masks in Render", "Include the mask layers when rendering the viewlayer");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
+
/* blend mode */
prop = RNA_def_property(srna, "blend_mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "blend_mode");
@@ -2197,7 +2203,10 @@ static void rna_def_gpencil_layer(BlenderRNA *brna)
prop = RNA_def_property(srna, "use_mask_layer", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LAYER_USE_MASK);
- RNA_def_property_ui_text(prop, "Mask Layer", "Mask pixels from underlying layers drawing");
+ RNA_def_property_ui_text(
+ prop,
+ "Use Mask",
+ "The visibility of drawings on this layer is affected by the layers in its masks list");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
prop = RNA_def_property(srna, "use_lights", PROP_BOOLEAN, PROP_NONE);
@@ -2568,7 +2577,7 @@ static void rna_def_gpencil_data(BlenderRNA *brna)
RNA_def_property_ui_text(
prop,
"Auto-Lock Layers",
- "Lock automatically all layers except active one to avoid accidental changes");
+ "Automatically lock all layers except the active one to avoid accidental changes");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_autolock");
prop = RNA_def_property(srna, "edit_line_color", PROP_FLOAT, PROP_COLOR_GAMMA);
diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c
index 0aa2e652d2d..92d65961743 100644
--- a/source/blender/makesrna/intern/rna_gpencil_modifier.c
+++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c
@@ -68,6 +68,11 @@ const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = {
ICON_MOD_BUILD,
"Build",
"Create duplication of strokes"},
+ {eGpencilModifierType_Lineart,
+ "GP_LINEART",
+ ICON_MOD_EDGESPLIT, /* TODO: Use a proper icon. */
+ "Line Art",
+ "Generate line art strokes from selected source"},
{eGpencilModifierType_Mirror,
"GP_MIRROR",
ICON_MOD_MIRROR,
@@ -88,11 +93,6 @@ const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = {
ICON_MOD_SUBSURF,
"Subdivide",
"Subdivide stroke adding more control points"},
- {eGpencilModifierType_Lineart,
- "GP_LINEART",
- ICON_MOD_EDGESPLIT, /* TODO: Use a proper icon. */
- "Line Art",
- "Generate line art strokes from selected source"},
{0, "", 0, N_("Deform"), ""},
{eGpencilModifierType_Armature,
"GP_ARMATURE",
@@ -109,6 +109,11 @@ const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = {
ICON_MOD_LATTICE,
"Lattice",
"Deform strokes using lattice"},
+ {eGpencilModifierType_Length,
+ "GP_LENGTH",
+ ICON_MOD_EDGESPLIT,
+ "Length",
+ "Extend or shrink strokes"},
{eGpencilModifierType_Noise, "GP_NOISE", ICON_MOD_NOISE, "Noise", "Add noise to strokes"},
{eGpencilModifierType_Offset,
"GP_OFFSET",
@@ -188,12 +193,18 @@ static const EnumPropertyItem gpencil_tint_type_items[] = {
{GP_TINT_GRADIENT, "GRADIENT", 0, "Gradient", ""},
{0, NULL, 0, NULL, NULL},
};
+static const EnumPropertyItem gpencil_length_mode_items[] = {
+ {GP_LENGTH_RELATIVE, "RELATIVE", 0, "Relative", "Length in ratio to the stroke's length"},
+ {GP_LENGTH_ABSOLUTE, "ABSOLUTE", 0, "Absolute", "Length in geometry space"},
+ {0, NULL, 0, NULL, NULL},
+};
#endif
#ifdef RNA_RUNTIME
# include "DNA_curve_types.h"
# include "DNA_fluid_types.h"
+# include "DNA_material_types.h"
# include "DNA_particle_types.h"
# include "BKE_cachefile.h"
@@ -232,6 +243,8 @@ static StructRNA *rna_GpencilModifier_refine(struct PointerRNA *ptr)
return &RNA_OpacityGpencilModifier;
case eGpencilModifierType_Lattice:
return &RNA_LatticeGpencilModifier;
+ case eGpencilModifierType_Length:
+ return &RNA_LengthGpencilModifier;
case eGpencilModifierType_Mirror:
return &RNA_MirrorGpencilModifier;
case eGpencilModifierType_Smooth:
@@ -350,6 +363,8 @@ static void greasepencil_modifier_object_set(Object *self,
RNA_GP_MOD_OBJECT_SET(Armature, object, OB_ARMATURE);
RNA_GP_MOD_OBJECT_SET(Lattice, object, OB_LATTICE);
RNA_GP_MOD_OBJECT_SET(Mirror, object, OB_EMPTY);
+RNA_GP_MOD_OBJECT_SET(Opacity, object, OB_EMPTY);
+RNA_GP_MOD_OBJECT_SET(Thick, object, OB_EMPTY);
# undef RNA_GP_MOD_OBJECT_SET
@@ -434,6 +449,195 @@ static void rna_GpencilModifier_opacity_update(Main *bmain, Scene *scene, Pointe
rna_GpencilModifier_update(bmain, scene, ptr);
}
+bool rna_GpencilModifier_material_poll(PointerRNA *ptr, PointerRNA value)
+{
+ Object *ob = (Object *)ptr->owner_id;
+ Material *ma = (Material *)value.owner_id;
+
+ return BKE_gpencil_object_material_index_get(ob, ma) != -1;
+}
+
+static void rna_GpencilModifier_material_set(PointerRNA *ptr,
+ PointerRNA value,
+ Material **ma_target,
+ struct ReportList *reports)
+{
+ Object *ob = (Object *)ptr->owner_id;
+ Material *ma = (Material *)value.owner_id;
+
+ if (ma == NULL || BKE_gpencil_object_material_index_get(ob, ma) != -1) {
+ id_lib_extern((ID *)ob);
+ *ma_target = ma;
+ }
+ else {
+ BKE_reportf(
+ reports,
+ RPT_ERROR,
+ "Cannot assign material '%s', it has to be used by the grease pencil object already",
+ ma->id.name);
+ }
+}
+
+static void rna_LineartGpencilModifier_material_set(PointerRNA *ptr,
+ PointerRNA value,
+ struct ReportList *reports)
+{
+ LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)ptr->data;
+ Material **ma_target = &lmd->target_material;
+
+ rna_GpencilModifier_material_set(ptr, value, ma_target, reports);
+}
+
+static void rna_NoiseGpencilModifier_material_set(PointerRNA *ptr,
+ PointerRNA value,
+ struct ReportList *reports)
+{
+ NoiseGpencilModifierData *nmd = (NoiseGpencilModifierData *)ptr->data;
+ Material **ma_target = &nmd->material;
+
+ rna_GpencilModifier_material_set(ptr, value, ma_target, reports);
+}
+
+static void rna_SmoothGpencilModifier_material_set(PointerRNA *ptr,
+ PointerRNA value,
+ struct ReportList *reports)
+{
+ SmoothGpencilModifierData *smd = (SmoothGpencilModifierData *)ptr->data;
+ Material **ma_target = &smd->material;
+
+ rna_GpencilModifier_material_set(ptr, value, ma_target, reports);
+}
+
+static void rna_SubdivGpencilModifier_material_set(PointerRNA *ptr,
+ PointerRNA value,
+ struct ReportList *reports)
+{
+ SubdivGpencilModifierData *smd = (SubdivGpencilModifierData *)ptr->data;
+ Material **ma_target = &smd->material;
+
+ rna_GpencilModifier_material_set(ptr, value, ma_target, reports);
+}
+
+static void rna_SimplifyGpencilModifier_material_set(PointerRNA *ptr,
+ PointerRNA value,
+ struct ReportList *reports)
+{
+ SimplifyGpencilModifierData *smd = (SimplifyGpencilModifierData *)ptr->data;
+ Material **ma_target = &smd->material;
+
+ rna_GpencilModifier_material_set(ptr, value, ma_target, reports);
+}
+
+static void rna_ThickGpencilModifier_material_set(PointerRNA *ptr,
+ PointerRNA value,
+ struct ReportList *reports)
+{
+ ThickGpencilModifierData *tmd = (ThickGpencilModifierData *)ptr->data;
+ Material **ma_target = &tmd->material;
+
+ rna_GpencilModifier_material_set(ptr, value, ma_target, reports);
+}
+
+static void rna_OffsetGpencilModifier_material_set(PointerRNA *ptr,
+ PointerRNA value,
+ struct ReportList *reports)
+{
+ OffsetGpencilModifierData *omd = (OffsetGpencilModifierData *)ptr->data;
+ Material **ma_target = &omd->material;
+
+ rna_GpencilModifier_material_set(ptr, value, ma_target, reports);
+}
+
+static void rna_TintGpencilModifier_material_set(PointerRNA *ptr,
+ PointerRNA value,
+ struct ReportList *reports)
+{
+ TintGpencilModifierData *tmd = (TintGpencilModifierData *)ptr->data;
+ Material **ma_target = &tmd->material;
+
+ rna_GpencilModifier_material_set(ptr, value, ma_target, reports);
+}
+
+static void rna_ColorGpencilModifier_material_set(PointerRNA *ptr,
+ PointerRNA value,
+ struct ReportList *reports)
+{
+ ColorGpencilModifierData *cmd = (ColorGpencilModifierData *)ptr->data;
+ Material **ma_target = &cmd->material;
+
+ rna_GpencilModifier_material_set(ptr, value, ma_target, reports);
+}
+
+static void rna_ArrayGpencilModifier_material_set(PointerRNA *ptr,
+ PointerRNA value,
+ struct ReportList *reports)
+{
+ ArrayGpencilModifierData *amd = (ArrayGpencilModifierData *)ptr->data;
+ Material **ma_target = &amd->material;
+
+ rna_GpencilModifier_material_set(ptr, value, ma_target, reports);
+}
+
+static void rna_OpacityGpencilModifier_material_set(PointerRNA *ptr,
+ PointerRNA value,
+ struct ReportList *reports)
+{
+ OpacityGpencilModifierData *omd = (OpacityGpencilModifierData *)ptr->data;
+ Material **ma_target = &omd->material;
+
+ rna_GpencilModifier_material_set(ptr, value, ma_target, reports);
+}
+
+static void rna_LatticeGpencilModifier_material_set(PointerRNA *ptr,
+ PointerRNA value,
+ struct ReportList *reports)
+{
+ LatticeGpencilModifierData *lmd = (LatticeGpencilModifierData *)ptr->data;
+ Material **ma_target = &lmd->material;
+
+ rna_GpencilModifier_material_set(ptr, value, ma_target, reports);
+}
+
+static void rna_MirrorGpencilModifier_material_set(PointerRNA *ptr,
+ PointerRNA value,
+ struct ReportList *reports)
+{
+ MirrorGpencilModifierData *mmd = (MirrorGpencilModifierData *)ptr->data;
+ Material **ma_target = &mmd->material;
+
+ rna_GpencilModifier_material_set(ptr, value, ma_target, reports);
+}
+
+static void rna_HookGpencilModifier_material_set(PointerRNA *ptr,
+ PointerRNA value,
+ struct ReportList *reports)
+{
+ HookGpencilModifierData *hmd = (HookGpencilModifierData *)ptr->data;
+ Material **ma_target = &hmd->material;
+
+ rna_GpencilModifier_material_set(ptr, value, ma_target, reports);
+}
+
+static void rna_MultiplyGpencilModifier_material_set(PointerRNA *ptr,
+ PointerRNA value,
+ struct ReportList *reports)
+{
+ MultiplyGpencilModifierData *mmd = (MultiplyGpencilModifierData *)ptr->data;
+ Material **ma_target = &mmd->material;
+
+ rna_GpencilModifier_material_set(ptr, value, ma_target, reports);
+}
+
+static void rna_TextureGpencilModifier_material_set(PointerRNA *ptr,
+ PointerRNA value,
+ struct ReportList *reports)
+{
+ TextureGpencilModifierData *tmd = (TextureGpencilModifierData *)ptr->data;
+ Material **ma_target = &tmd->material;
+
+ rna_GpencilModifier_material_set(ptr, value, ma_target, reports);
+}
+
#else
static void rna_def_modifier_gpencilnoise(BlenderRNA *brna)
@@ -446,14 +650,20 @@ static void rna_def_modifier_gpencilnoise(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "NoiseGpencilModifierData");
RNA_def_struct_ui_icon(srna, ICON_MOD_NOISE);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "layer", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "layername");
RNA_def_property_ui_text(prop, "Layer", "Layer name");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE);
- RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_pointer_funcs(prop,
+ NULL,
+ "rna_NoiseGpencilModifier_material_set",
+ NULL,
+ "rna_GpencilModifier_material_poll");
RNA_def_property_ui_text(prop, "Material", "Material used for filtering effect");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
@@ -567,6 +777,8 @@ static void rna_def_modifier_gpencilnoise(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_NOISE_INVERT_LAYERPASS);
RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_modifier_gpencilsmooth(BlenderRNA *brna)
@@ -579,14 +791,20 @@ static void rna_def_modifier_gpencilsmooth(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "SmoothGpencilModifierData");
RNA_def_struct_ui_icon(srna, ICON_MOD_SMOOTH);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "layer", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "layername");
RNA_def_property_ui_text(prop, "Layer", "Layer name");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE);
- RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_pointer_funcs(prop,
+ NULL,
+ "rna_SmoothGpencilModifier_material_set",
+ NULL,
+ "rna_GpencilModifier_material_poll");
RNA_def_property_ui_text(prop, "Material", "Material used for filtering effect");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
@@ -680,6 +898,8 @@ static void rna_def_modifier_gpencilsmooth(BlenderRNA *brna)
RNA_def_property_pointer_sdna(prop, NULL, "curve_intensity");
RNA_def_property_ui_text(prop, "Curve", "Custom curve to apply effect");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_modifier_gpencilsubdiv(BlenderRNA *brna)
@@ -692,14 +912,20 @@ static void rna_def_modifier_gpencilsubdiv(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "SubdivGpencilModifierData");
RNA_def_struct_ui_icon(srna, ICON_MOD_SUBSURF);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "layer", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "layername");
RNA_def_property_ui_text(prop, "Layer", "Layer name");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE);
- RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_pointer_funcs(prop,
+ NULL,
+ "rna_SubdivGpencilModifier_material_set",
+ NULL,
+ "rna_GpencilModifier_material_poll");
RNA_def_property_ui_text(prop, "Material", "Material used for filtering effect");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
@@ -746,6 +972,8 @@ static void rna_def_modifier_gpencilsubdiv(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_SUBDIV_INVERT_LAYERPASS);
RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_modifier_gpencilsimplify(BlenderRNA *brna)
@@ -782,14 +1010,20 @@ static void rna_def_modifier_gpencilsimplify(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "SimplifyGpencilModifierData");
RNA_def_struct_ui_icon(srna, ICON_MOD_SIMPLIFY);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "layer", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "layername");
RNA_def_property_ui_text(prop, "Layer", "Layer name");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE);
- RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_pointer_funcs(prop,
+ NULL,
+ "rna_SimplifyGpencilModifier_material_set",
+ NULL,
+ "rna_GpencilModifier_material_poll");
RNA_def_property_ui_text(prop, "Material", "Material used for filtering effect");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
@@ -859,6 +1093,8 @@ static void rna_def_modifier_gpencilsimplify(BlenderRNA *brna)
RNA_def_property_ui_range(prop, 0, 1.0, 0.01, 3);
RNA_def_property_ui_text(prop, "Distance", "Distance between points");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_modifier_gpencilthick(BlenderRNA *brna)
@@ -871,14 +1107,20 @@ static void rna_def_modifier_gpencilthick(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "ThickGpencilModifierData");
RNA_def_struct_ui_icon(srna, ICON_MOD_THICKNESS);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "layer", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "layername");
RNA_def_property_ui_text(prop, "Layer", "Layer name");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE);
- RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_pointer_funcs(prop,
+ NULL,
+ "rna_ThickGpencilModifier_material_set",
+ NULL,
+ "rna_GpencilModifier_material_poll");
RNA_def_property_ui_text(prop, "Material", "Material used for filtering effect");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
@@ -901,6 +1143,37 @@ static void rna_def_modifier_gpencilthick(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Thickness Factor", "Factor to multiply the thickness with");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+ prop = RNA_def_property(srna, "use_fading", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_THICK_FADING);
+ RNA_def_property_ui_text(prop, "Fading", "Fading effect");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ /* Distance reference object */
+ prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Object", "Object used as distance reference");
+ RNA_def_property_pointer_funcs(prop, NULL, "rna_ThickGpencilModifier_object_set", NULL, NULL);
+ RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update");
+
+ prop = RNA_def_property(srna, "fading_start", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "fading_start");
+ RNA_def_property_ui_range(prop, 0, 1000.0, 1.0, 2);
+ RNA_def_property_ui_text(prop, "Fading Start", "Start distance of fading effect");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "fading_end", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "fading_end");
+ RNA_def_property_ui_range(prop, 0, 1000.0, 1.0, 2);
+ RNA_def_property_ui_text(prop, "Fading End", "End distance of fading effect");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "fading_end_factor", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "fading_end_factor");
+ RNA_def_property_range(prop, 0.0, FLT_MAX);
+ RNA_def_property_ui_range(prop, 0.0, 10.0, 0.1, 3);
+ RNA_def_property_ui_text(prop, "End Factor", "Fading end thickness factor");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
prop = RNA_def_property(srna, "pass_index", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "pass_index");
RNA_def_property_range(prop, 0, 100);
@@ -953,6 +1226,8 @@ static void rna_def_modifier_gpencilthick(BlenderRNA *brna)
RNA_def_property_pointer_sdna(prop, NULL, "curve_thickness");
RNA_def_property_ui_text(prop, "Curve", "Custom curve to apply effect");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_modifier_gpenciloffset(BlenderRNA *brna)
@@ -965,14 +1240,20 @@ static void rna_def_modifier_gpenciloffset(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "OffsetGpencilModifierData");
RNA_def_struct_ui_icon(srna, ICON_MOD_OFFSET);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "layer", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "layername");
RNA_def_property_ui_text(prop, "Layer", "Layer name");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE);
- RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_pointer_funcs(prop,
+ NULL,
+ "rna_OffsetGpencilModifier_material_set",
+ NULL,
+ "rna_GpencilModifier_material_poll");
RNA_def_property_ui_text(prop, "Material", "Material used for filtering effect");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
@@ -1036,6 +1317,36 @@ static void rna_def_modifier_gpenciloffset(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Scale", "Values for changes in scale");
RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT);
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "random_offset", PROP_FLOAT, PROP_XYZ);
+ RNA_def_property_float_sdna(prop, NULL, "rnd_offset");
+ RNA_def_property_ui_text(prop, "Random Offset", "Value for changes in location");
+ RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT);
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "random_rotation", PROP_FLOAT, PROP_EULER);
+ RNA_def_property_float_sdna(prop, NULL, "rnd_rot");
+ RNA_def_property_ui_text(prop, "Random Rotation", "Value for changes in rotation");
+ RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT);
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "random_scale", PROP_FLOAT, PROP_XYZ);
+ RNA_def_property_float_sdna(prop, NULL, "rnd_scale");
+ RNA_def_property_ui_text(prop, "Scale", "Value for changes in scale");
+ RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT);
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "seed", PROP_INT, PROP_UNSIGNED);
+ RNA_def_property_ui_text(prop, "Seed", "Random seed");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "use_uniform_random_scale", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_OFFSET_UNIFORM_RANDOM_SCALE);
+ RNA_def_property_ui_text(
+ prop, "Uniform Scale", "Use the same random seed for each scale axis for a uniform scale");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_modifier_gpenciltint(BlenderRNA *brna)
@@ -1056,10 +1367,11 @@ static void rna_def_modifier_gpenciltint(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "TintGpencilModifierData");
RNA_def_struct_ui_icon(srna, ICON_COLOR);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE);
RNA_def_property_ui_text(prop, "Object", "Parent object to define the center of the effect");
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_pointer_funcs(prop, NULL, "rna_TintGpencilModifier_object_set", NULL, NULL);
RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update");
@@ -1069,8 +1381,12 @@ static void rna_def_modifier_gpenciltint(BlenderRNA *brna)
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE);
- RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_pointer_funcs(prop,
+ NULL,
+ "rna_TintGpencilModifier_material_set",
+ NULL,
+ "rna_GpencilModifier_material_poll");
RNA_def_property_ui_text(prop, "Material", "Material used for filtering effect");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
@@ -1171,6 +1487,8 @@ static void rna_def_modifier_gpenciltint(BlenderRNA *brna)
RNA_def_property_pointer_sdna(prop, NULL, "curve_intensity");
RNA_def_property_ui_text(prop, "Curve", "Custom curve to apply effect");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_modifier_gpenciltime(BlenderRNA *brna)
@@ -1183,6 +1501,8 @@ static void rna_def_modifier_gpenciltime(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "TimeGpencilModifierData");
RNA_def_struct_ui_icon(srna, ICON_MOD_TIME);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "mode");
RNA_def_property_enum_items(prop, rna_enum_time_mode_items);
@@ -1250,6 +1570,8 @@ static void rna_def_modifier_gpenciltime(BlenderRNA *brna)
RNA_def_property_ui_text(
prop, "Custom Range", "Define a custom range of frames to use in modifier");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_modifier_gpencilcolor(BlenderRNA *brna)
@@ -1262,6 +1584,8 @@ static void rna_def_modifier_gpencilcolor(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "ColorGpencilModifierData");
RNA_def_struct_ui_icon(srna, ICON_MOD_TINT);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "modify_color", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, modifier_modify_color_items); /* share the enum */
RNA_def_property_ui_text(prop, "Mode", "Set what colors of the stroke are affected");
@@ -1273,8 +1597,12 @@ static void rna_def_modifier_gpencilcolor(BlenderRNA *brna)
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE);
- RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_pointer_funcs(prop,
+ NULL,
+ "rna_ColorGpencilModifier_material_set",
+ NULL,
+ "rna_GpencilModifier_material_poll");
RNA_def_property_ui_text(prop, "Material", "Material used for filtering effect");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
@@ -1341,6 +1669,8 @@ static void rna_def_modifier_gpencilcolor(BlenderRNA *brna)
RNA_def_property_pointer_sdna(prop, NULL, "curve_intensity");
RNA_def_property_ui_text(prop, "Curve", "Custom curve to apply effect");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_modifier_gpencilopacity(BlenderRNA *brna)
@@ -1353,6 +1683,8 @@ static void rna_def_modifier_gpencilopacity(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "OpacityGpencilModifierData");
RNA_def_struct_ui_icon(srna, ICON_MOD_OPACITY);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "modify_color", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, modifier_modify_opacity_items);
RNA_def_property_ui_text(prop, "Mode", "Set what colors of the stroke are affected");
@@ -1364,8 +1696,12 @@ static void rna_def_modifier_gpencilopacity(BlenderRNA *brna)
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE);
- RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_pointer_funcs(prop,
+ NULL,
+ "rna_OpacityGpencilModifier_material_set",
+ NULL,
+ "rna_GpencilModifier_material_poll");
RNA_def_property_ui_text(prop, "Material", "Material used for filtering effect");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
@@ -1390,6 +1726,37 @@ static void rna_def_modifier_gpencilopacity(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Hardness", "Factor of stroke hardness");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+ prop = RNA_def_property(srna, "use_fading", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_OPACITY_FADING);
+ RNA_def_property_ui_text(prop, "Fading", "Fading effect");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ /* Distance reference object */
+ prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Object", "Object used as distance reference");
+ RNA_def_property_pointer_funcs(prop, NULL, "rna_OpacityGpencilModifier_object_set", NULL, NULL);
+ RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update");
+
+ prop = RNA_def_property(srna, "fading_start", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "fading_start");
+ RNA_def_property_ui_range(prop, 0, 1000.0, 1.0, 2);
+ RNA_def_property_ui_text(prop, "Fading Start", "Start distance of fading effect");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "fading_end", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "fading_end");
+ RNA_def_property_ui_range(prop, 0, 1000.0, 1.0, 2);
+ RNA_def_property_ui_text(prop, "Fading End", "End distance of fading effect");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "fading_end_factor", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "fading_end_factor");
+ RNA_def_property_range(prop, 0.0, FLT_MAX);
+ RNA_def_property_ui_range(prop, 0.0, 10.0, 0.1, 3);
+ RNA_def_property_ui_text(prop, "End Factor", "Fading end thickness factor");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
prop = RNA_def_property(srna, "pass_index", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "pass_index");
RNA_def_property_range(prop, 0, 100);
@@ -1442,6 +1809,8 @@ static void rna_def_modifier_gpencilopacity(BlenderRNA *brna)
RNA_def_property_pointer_sdna(prop, NULL, "curve_intensity");
RNA_def_property_ui_text(prop, "Curve", "Custom curve to apply effect");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_modifier_gpencilarray(BlenderRNA *brna)
@@ -1454,14 +1823,20 @@ static void rna_def_modifier_gpencilarray(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "ArrayGpencilModifierData");
RNA_def_struct_ui_icon(srna, ICON_MOD_ARRAY);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "layer", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "layername");
RNA_def_property_ui_text(prop, "Layer", "Layer name");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE);
- RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_pointer_funcs(prop,
+ NULL,
+ "rna_ArrayGpencilModifier_material_set",
+ NULL,
+ "rna_GpencilModifier_material_poll");
RNA_def_property_ui_text(prop, "Material", "Material used for filtering effect");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
@@ -1486,7 +1861,6 @@ static void rna_def_modifier_gpencilarray(BlenderRNA *brna)
"Use the location and rotation of another object to determine the distance and "
"rotational change between arrayed items");
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update");
prop = RNA_def_property(srna, "constant_offset", PROP_FLOAT, PROP_TRANSLATION);
@@ -1581,6 +1955,8 @@ static void rna_def_modifier_gpencilarray(BlenderRNA *brna)
RNA_def_property_ui_text(
prop, "Uniform Scale", "Use the same random seed for each scale axis for a uniform scale");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_modifier_gpencilbuild(BlenderRNA *brna)
@@ -1643,6 +2019,8 @@ static void rna_def_modifier_gpencilbuild(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "BuildGpencilModifierData");
RNA_def_struct_ui_icon(srna, ICON_MOD_BUILD);
+ RNA_define_lib_overridable(true);
+
/* Mode */
prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, prop_gpencil_build_mode_items);
@@ -1740,6 +2118,8 @@ static void rna_def_modifier_gpencilbuild(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BUILD_INVERT_LAYERPASS);
RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_modifier_gpencillattice(BlenderRNA *brna)
@@ -1753,14 +2133,20 @@ static void rna_def_modifier_gpencillattice(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "LatticeGpencilModifierData");
RNA_def_struct_ui_icon(srna, ICON_MOD_LATTICE);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "layer", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "layername");
RNA_def_property_ui_text(prop, "Layer", "Layer name");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE);
- RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_pointer_funcs(prop,
+ NULL,
+ "rna_LatticeGpencilModifier_material_set",
+ NULL,
+ "rna_GpencilModifier_material_poll");
RNA_def_property_ui_text(prop, "Material", "Material used for filtering effect");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
@@ -1812,7 +2198,6 @@ static void rna_def_modifier_gpencillattice(BlenderRNA *brna)
RNA_def_property_pointer_funcs(
prop, NULL, "rna_LatticeGpencilModifier_object_set", NULL, "rna_Lattice_object_poll");
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update");
prop = RNA_def_property(srna, "strength", PROP_FLOAT, PROP_NONE);
@@ -1820,6 +2205,8 @@ static void rna_def_modifier_gpencillattice(BlenderRNA *brna)
RNA_def_property_ui_range(prop, 0, 1, 10, 2);
RNA_def_property_ui_text(prop, "Strength", "Strength of modifier effect");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_modifier_gpencilmirror(BlenderRNA *brna)
@@ -1832,14 +2219,20 @@ static void rna_def_modifier_gpencilmirror(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "MirrorGpencilModifierData");
RNA_def_struct_ui_icon(srna, ICON_MOD_MIRROR);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "layer", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "layername");
RNA_def_property_ui_text(prop, "Layer", "Layer name");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE);
- RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_pointer_funcs(prop,
+ NULL,
+ "rna_MirrorGpencilModifier_material_set",
+ NULL,
+ "rna_GpencilModifier_material_poll");
RNA_def_property_ui_text(prop, "Material", "Material used for filtering effect");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
@@ -1879,7 +2272,6 @@ static void rna_def_modifier_gpencilmirror(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Object", "Object used as center");
RNA_def_property_pointer_funcs(prop, NULL, "rna_MirrorGpencilModifier_object_set", NULL, NULL);
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update");
prop = RNA_def_property(srna, "use_clip", PROP_BOOLEAN, PROP_NONE);
@@ -1901,6 +2293,8 @@ static void rna_def_modifier_gpencilmirror(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_MIRROR_AXIS_Z);
RNA_def_property_ui_text(prop, "Z", "Mirror the Z axis");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_modifier_gpencilhook(BlenderRNA *brna)
@@ -1914,11 +2308,12 @@ static void rna_def_modifier_gpencilhook(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "HookGpencilModifierData");
RNA_def_struct_ui_icon(srna, ICON_HOOK);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE);
RNA_def_property_ui_text(
prop, "Object", "Parent Object for hook, also recalculates and clears offset");
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_pointer_funcs(prop, NULL, "rna_HookGpencilModifier_object_set", NULL, NULL);
RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update");
@@ -1936,8 +2331,12 @@ static void rna_def_modifier_gpencilhook(BlenderRNA *brna)
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE);
- RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_pointer_funcs(prop,
+ NULL,
+ "rna_HookGpencilModifier_material_set",
+ NULL,
+ "rna_GpencilModifier_material_poll");
RNA_def_property_ui_text(prop, "Material", "Material used for filtering effect");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
@@ -2025,6 +2424,8 @@ static void rna_def_modifier_gpencilhook(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_HOOK_UNIFORM_SPACE);
RNA_def_property_ui_text(prop, "Uniform Falloff", "Compensate for non-uniform object scale");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_modifier_gpencilarmature(BlenderRNA *brna)
@@ -2038,12 +2439,13 @@ static void rna_def_modifier_gpencilarmature(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "ArmatureGpencilModifierData");
RNA_def_struct_ui_icon(srna, ICON_MOD_ARMATURE);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE);
RNA_def_property_ui_text(prop, "Object", "Armature object to deform with");
RNA_def_property_pointer_funcs(
prop, NULL, "rna_ArmatureGpencilModifier_object_set", NULL, "rna_Armature_object_poll");
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update");
prop = RNA_def_property(srna, "use_bone_envelopes", PROP_BOOLEAN, PROP_NONE);
@@ -2075,6 +2477,8 @@ static void rna_def_modifier_gpencilarmature(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "deformflag", ARM_DEF_INVERT_VGROUP);
RNA_def_property_ui_text(prop, "Invert", "Invert vertex group influence");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_modifier_gpencilmultiply(BlenderRNA *brna)
@@ -2087,14 +2491,20 @@ static void rna_def_modifier_gpencilmultiply(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "MultiplyGpencilModifierData");
RNA_def_struct_ui_icon(srna, ICON_GP_MULTIFRAME_EDITING);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "layer", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "layername");
RNA_def_property_ui_text(prop, "Layer", "Layer name");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE);
- RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_pointer_funcs(prop,
+ NULL,
+ "rna_MultiplyGpencilModifier_material_set",
+ NULL,
+ "rna_GpencilModifier_material_poll");
RNA_def_property_ui_text(prop, "Material", "Material used for filtering effect");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
@@ -2167,6 +2577,8 @@ static void rna_def_modifier_gpencilmultiply(BlenderRNA *brna)
RNA_def_property_range(prop, 0, 1);
RNA_def_property_ui_text(prop, "Center", "Fade center");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_modifier_gpenciltexture(BlenderRNA *brna)
@@ -2205,6 +2617,8 @@ static void rna_def_modifier_gpenciltexture(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "TextureGpencilModifierData");
RNA_def_struct_ui_icon(srna, ICON_TEXTURE);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "layer", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "layername");
RNA_def_property_ui_text(prop, "Layer", "Layer name");
@@ -2216,8 +2630,12 @@ static void rna_def_modifier_gpenciltexture(BlenderRNA *brna)
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE);
- RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_pointer_funcs(prop,
+ NULL,
+ "rna_TextureGpencilModifier_material_set",
+ NULL,
+ "rna_GpencilModifier_material_poll");
RNA_def_property_ui_text(prop, "Material", "Material used for filtering effect");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
@@ -2311,6 +2729,8 @@ static void rna_def_modifier_gpenciltexture(BlenderRNA *brna)
RNA_def_property_enum_items(prop, mode_items);
RNA_def_property_ui_text(prop, "Mode", "");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_modifier_gpencillineart(BlenderRNA *brna)
@@ -2331,6 +2751,8 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "LineartGpencilModifierData");
RNA_def_struct_ui_icon(srna, ICON_MOD_EDGESPLIT);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "use_fuzzy_intersections", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "calculation_flags", LRT_INTERSECTION_AS_CONTOUR);
RNA_def_property_ui_text(prop,
@@ -2407,7 +2829,6 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna)
prop = RNA_def_property(srna, "source_object", PROP_POINTER, PROP_NONE);
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
RNA_def_property_struct_type(prop, "Object");
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop, "Source Object", "Source object that this modifier uses data from");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update");
@@ -2415,7 +2836,6 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna)
prop = RNA_def_property(srna, "source_collection", PROP_POINTER, PROP_NONE);
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
RNA_def_property_struct_type(prop, "Collection");
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop, "Source Collection", "Source collection that this modifier uses data from");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update");
@@ -2466,9 +2886,13 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna)
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "target_material", PROP_POINTER, PROP_NONE);
- RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
RNA_def_property_struct_type(prop, "Material");
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_pointer_funcs(prop,
+ NULL,
+ "rna_LineartGpencilModifier_material_set",
+ NULL,
+ "rna_GpencilModifier_material_poll");
RNA_def_property_ui_text(
prop, "Target Material", "Grease Pencil material assigned to the generated strokes");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
@@ -2501,12 +2925,6 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Match Output", "Match output vertex group based on name");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
- prop = RNA_def_property(srna, "use_soft_selection", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "flags", LRT_GPENCIL_SOFT_SELECTION);
- RNA_def_property_ui_text(
- prop, "Clip", "Preserve original vertex weight instead of clipping to 0/1");
- RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
-
prop = RNA_def_property(srna, "is_baked", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flags", LRT_GPENCIL_IS_BAKED);
RNA_def_property_ui_text(prop, "Is Baked", "This modifier has baked data");
@@ -2524,15 +2942,6 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna)
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
- prop = RNA_def_property(srna, "resample_length", PROP_FLOAT, PROP_DISTANCE);
- RNA_def_property_ui_text(prop,
- "Resample Length",
- "Resample the strokes so that the stroke points have the specified "
- "length between them. Zero length disables the resampling");
- RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.01f, 2);
- RNA_def_property_range(prop, 0.0f, 1.0f);
- RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
-
prop = RNA_def_property(srna, "use_transparency", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "transparency_flags", LRT_GPENCIL_TRANSPARENCY_ENABLE);
RNA_def_property_ui_text(
@@ -2550,6 +2959,92 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna)
RNA_def_property_array(prop, 8);
RNA_def_property_ui_text(prop, "Mask", "");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ RNA_define_lib_overridable(false);
+}
+
+static void rna_def_modifier_gpencillength(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "LengthGpencilModifier", "GpencilModifier");
+ RNA_def_struct_ui_text(srna, "Length Modifier", "Stretch or shrink strokes");
+ RNA_def_struct_sdna(srna, "LengthGpencilModifierData");
+ RNA_def_struct_ui_icon(srna, ICON_MOD_EDGESPLIT);
+
+ RNA_define_lib_overridable(true);
+
+ prop = RNA_def_property(srna, "start_factor", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "start_fac");
+ RNA_def_property_ui_range(prop, -10.0f, 10.0f, 0.1, 1);
+ RNA_def_property_ui_text(prop, "Start Factor", "Length difference for each segment");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "end_factor", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "end_fac");
+ RNA_def_property_ui_range(prop, -10.0f, 10.0f, 0.1, 1);
+ RNA_def_property_ui_text(prop, "End Factor", "Length difference for each segment");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "overshoot_factor", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_float_sdna(prop, NULL, "overshoot_fac");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(
+ prop,
+ "Overshoot Factor",
+ "Defines how precise must follow the stroke trajectory for the overshoot extremes");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "mode");
+ RNA_def_property_enum_items(prop, gpencil_length_mode_items);
+ RNA_def_property_ui_text(prop, "Mode", "Mode to define length");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "layer", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "layername");
+ RNA_def_property_ui_text(prop, "Layer", "Layer name");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE);
+ RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
+ RNA_def_property_ui_text(prop, "Material", "Material used for filtering effect");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "pass_index", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "pass_index");
+ RNA_def_property_range(prop, 0, 100);
+ RNA_def_property_ui_text(prop, "Pass", "Pass index");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "invert_layers", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LENGTH_INVERT_LAYER);
+ RNA_def_property_ui_text(prop, "Inverse Layers", "Inverse filter");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "invert_materials", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LENGTH_INVERT_MATERIAL);
+ RNA_def_property_ui_text(prop, "Inverse Materials", "Inverse filter");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "invert_material_pass", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LENGTH_INVERT_PASS);
+ RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "layer_pass", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "layer_pass");
+ RNA_def_property_range(prop, 0, 100);
+ RNA_def_property_ui_text(prop, "Pass", "Layer pass index");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ prop = RNA_def_property(srna, "invert_layer_pass", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LENGTH_INVERT_LAYERPASS);
+ RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter");
+ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
+
+ RNA_define_lib_overridable(false);
}
void RNA_def_greasepencil_modifier(BlenderRNA *brna)
@@ -2627,6 +3122,7 @@ void RNA_def_greasepencil_modifier(BlenderRNA *brna)
rna_def_modifier_gpencilmultiply(brna);
rna_def_modifier_gpenciltexture(brna);
rna_def_modifier_gpencillineart(brna);
+ rna_def_modifier_gpencillength(brna);
}
#endif
diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h
index 95972dd444f..bfe9d4bb77c 100644
--- a/source/blender/makesrna/intern/rna_internal.h
+++ b/source/blender/makesrna/intern/rna_internal.h
@@ -476,7 +476,7 @@ void RNA_def_main_hairs(BlenderRNA *brna, PropertyRNA *cprop);
void RNA_def_main_pointclouds(BlenderRNA *brna, PropertyRNA *cprop);
#endif
void RNA_def_main_volumes(BlenderRNA *brna, PropertyRNA *cprop);
-#ifdef WITH_GEOMETRY_NODES
+#ifdef WITH_SIMULATION_DATABLOCK
void RNA_def_main_simulations(BlenderRNA *brna, PropertyRNA *cprop);
#endif
diff --git a/source/blender/makesrna/intern/rna_internal_types.h b/source/blender/makesrna/intern/rna_internal_types.h
index 0c0260c889c..245730919b0 100644
--- a/source/blender/makesrna/intern/rna_internal_types.h
+++ b/source/blender/makesrna/intern/rna_internal_types.h
@@ -400,6 +400,7 @@ typedef struct IntPropertyRNA {
PropIntArraySetFuncEx setarray_ex;
PropIntRangeFuncEx range_ex;
+ PropertyScaleType ui_scale_type;
int softmin, softmax;
int hardmin, hardmax;
int step;
@@ -423,6 +424,7 @@ typedef struct FloatPropertyRNA {
PropFloatArraySetFuncEx setarray_ex;
PropFloatRangeFuncEx range_ex;
+ PropertyScaleType ui_scale_type;
float softmin, softmax;
float hardmin, hardmax;
float step;
diff --git a/source/blender/makesrna/intern/rna_key.c b/source/blender/makesrna/intern/rna_key.c
index 3b9fc970072..a48727526c9 100644
--- a/source/blender/makesrna/intern/rna_key.c
+++ b/source/blender/makesrna/intern/rna_key.c
@@ -699,6 +699,16 @@ static void rna_Key_update_data(Main *bmain, Scene *UNUSED(scene), PointerRNA *p
}
}
+static void rna_ShapeKey_update_minmax(Main *bmain, Scene *scene, PointerRNA *ptr)
+{
+ KeyBlock *data = (KeyBlock *)ptr->data;
+ if (IN_RANGE_INCL(data->curval, data->slidermin, data->slidermax)) {
+ return;
+ }
+ CLAMP(data->curval, data->slidermin, data->slidermax);
+ rna_Key_update_data(bmain, scene, ptr);
+}
+
static KeyBlock *rna_ShapeKeyData_find_keyblock(Key *key, float *point)
{
KeyBlock *kb;
@@ -955,6 +965,7 @@ static void rna_def_keyblock(BlenderRNA *brna)
RNA_def_property_float_funcs(
prop, NULL, "rna_ShapeKey_slider_min_set", "rna_ShapeKey_slider_min_range");
RNA_def_property_ui_text(prop, "Slider Min", "Minimum for slider");
+ RNA_def_property_update(prop, 0, "rna_ShapeKey_update_minmax");
prop = RNA_def_property(srna, "slider_max", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "slidermax");
@@ -963,6 +974,7 @@ static void rna_def_keyblock(BlenderRNA *brna)
RNA_def_property_float_funcs(
prop, NULL, "rna_ShapeKey_slider_max_set", "rna_ShapeKey_slider_max_range");
RNA_def_property_ui_text(prop, "Slider Max", "Maximum for slider");
+ RNA_def_property_update(prop, 0, "rna_ShapeKey_update_minmax");
prop = RNA_def_property(srna, "data", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "data", "totelem");
diff --git a/source/blender/makesrna/intern/rna_lattice_api.c b/source/blender/makesrna/intern/rna_lattice_api.c
index 0b61603dd09..5b69a743d47 100644
--- a/source/blender/makesrna/intern/rna_lattice_api.c
+++ b/source/blender/makesrna/intern/rna_lattice_api.c
@@ -33,7 +33,7 @@
#include "rna_internal.h" /* own include */
#ifdef RNA_RUNTIME
-static void rna_Lattice_transform(Lattice *lt, float *mat, bool shape_keys)
+static void rna_Lattice_transform(Lattice *lt, float mat[16], bool shape_keys)
{
BKE_lattice_transform(lt, (float(*)[4])mat, shape_keys);
diff --git a/source/blender/makesrna/intern/rna_light.c b/source/blender/makesrna/intern/rna_light.c
index bb99e5c6c1d..0593db0dd56 100644
--- a/source/blender/makesrna/intern/rna_light.c
+++ b/source/blender/makesrna/intern/rna_light.c
@@ -480,6 +480,15 @@ static void rna_def_area_light(BlenderRNA *brna)
"Size Y",
"Size of the area of the area light in the Y direction for rectangle shapes");
RNA_def_property_update(prop, 0, "rna_Light_draw_update");
+
+ prop = RNA_def_property(srna, "spread", PROP_FLOAT, PROP_ANGLE);
+ RNA_def_property_float_sdna(prop, NULL, "area_spread");
+ RNA_def_property_range(prop, DEG2RADF(1.0f), DEG2RADF(180.0f));
+ RNA_def_property_ui_text(
+ prop,
+ "Spread",
+ "How widely the emitted light fans out, as in the case of a gridded softbox");
+ RNA_def_property_update(prop, 0, "rna_Light_draw_update");
}
static void rna_def_spot_light(BlenderRNA *brna)
diff --git a/source/blender/makesrna/intern/rna_main.c b/source/blender/makesrna/intern/rna_main.c
index c80f856dd6b..464abc6b543 100644
--- a/source/blender/makesrna/intern/rna_main.c
+++ b/source/blender/makesrna/intern/rna_main.c
@@ -134,7 +134,7 @@ RNA_MAIN_LISTBASE_FUNCS_DEF(pointclouds)
RNA_MAIN_LISTBASE_FUNCS_DEF(scenes)
RNA_MAIN_LISTBASE_FUNCS_DEF(screens)
RNA_MAIN_LISTBASE_FUNCS_DEF(shapekeys)
-# ifdef WITH_GEOMETRY_NODES
+# ifdef WITH_SIMULATION_DATABLOCK
RNA_MAIN_LISTBASE_FUNCS_DEF(simulations)
# endif
RNA_MAIN_LISTBASE_FUNCS_DEF(sounds)
@@ -407,7 +407,7 @@ void RNA_def_main(BlenderRNA *brna)
"Volumes",
"Volume data-blocks",
RNA_def_main_volumes},
-# ifdef WITH_GEOMETRY_NODES
+# ifdef WITH_SIMULATION_DATABLOCK
{"simulations",
"Simulation",
"rna_Main_simulations_begin",
diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c
index d24be91f731..8e6ff961721 100644
--- a/source/blender/makesrna/intern/rna_main_api.c
+++ b/source/blender/makesrna/intern/rna_main_api.c
@@ -806,7 +806,7 @@ static Volume *rna_Main_volumes_new(Main *bmain, const char *name)
return volume;
}
-# ifdef WITH_GEOMETRY_NODES
+# ifdef WITH_SIMULATION_DATABLOCK
static Simulation *rna_Main_simulations_new(Main *bmain, const char *name)
{
char safe_name[MAX_ID_NAME - 2];
@@ -870,7 +870,7 @@ RNA_MAIN_ID_TAG_FUNCS_DEF(hairs, hairs, ID_HA)
RNA_MAIN_ID_TAG_FUNCS_DEF(pointclouds, pointclouds, ID_PT)
# endif
RNA_MAIN_ID_TAG_FUNCS_DEF(volumes, volumes, ID_VO)
-# ifdef WITH_GEOMETRY_NODES
+# ifdef WITH_SIMULATION_DATABLOCK
RNA_MAIN_ID_TAG_FUNCS_DEF(simulations, simulations, ID_SIM)
# endif
@@ -996,7 +996,7 @@ void RNA_def_main_objects(BlenderRNA *brna, PropertyRNA *cprop)
RNA_def_function_return(func, parm);
func = RNA_def_function(srna, "remove", "rna_Main_ID_remove");
- RNA_def_function_ui_description(func, "Remove a object from the current blendfile");
+ RNA_def_function_ui_description(func, "Remove an object from the current blendfile");
RNA_def_function_flag(func, FUNC_USE_REPORTS);
parm = RNA_def_pointer(func, "object", "Object", "", "Object to remove");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
@@ -1200,7 +1200,7 @@ void RNA_def_main_lights(BlenderRNA *brna, PropertyRNA *cprop)
parm = RNA_def_string(func, "name", "Light", 0, "", "New name for the data-block");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
parm = RNA_def_enum(
- func, "type", rna_enum_light_type_items, 0, "Type", "The type of texture to add");
+ func, "type", rna_enum_light_type_items, 0, "Type", "The type of light to add");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
/* return type */
parm = RNA_def_pointer(func, "light", "Light", "", "New light data-block");
@@ -1216,7 +1216,7 @@ void RNA_def_main_lights(BlenderRNA *brna, PropertyRNA *cprop)
"do_unlink",
true,
"",
- "Unlink all usages of this Light before deleting it "
+ "Unlink all usages of this light before deleting it "
"(WARNING: will also delete objects instancing that light data)");
RNA_def_boolean(func,
"do_id_user",
@@ -1248,7 +1248,7 @@ void RNA_def_main_libraries(BlenderRNA *brna, PropertyRNA *cprop)
func = RNA_def_function(srna, "remove", "rna_Main_ID_remove");
RNA_def_function_flag(func, FUNC_USE_REPORTS);
- RNA_def_function_ui_description(func, "Remove a camera from the current blendfile");
+ RNA_def_function_ui_description(func, "Remove a library from the current blendfile");
parm = RNA_def_pointer(func, "library", "Library", "", "Library to remove");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0);
@@ -1258,9 +1258,9 @@ void RNA_def_main_libraries(BlenderRNA *brna, PropertyRNA *cprop)
"do_id_user",
true,
"",
- "Decrement user counter of all datablocks used by this object");
+ "Decrement user counter of all datablocks used by this library");
RNA_def_boolean(
- func, "do_ui_user", true, "", "Make sure interface does not reference this object");
+ func, "do_ui_user", true, "", "Make sure interface does not reference this library");
}
void RNA_def_main_screens(BlenderRNA *brna, PropertyRNA *cprop)
@@ -1327,7 +1327,7 @@ void RNA_def_main_images(BlenderRNA *brna, PropertyRNA *cprop)
RNA_def_function_flag(func, FUNC_USE_REPORTS);
RNA_def_function_ui_description(func, "Load a new image into the main database");
parm = RNA_def_string_file_path(
- func, "filepath", "File Path", 0, "", "path of the file to load");
+ func, "filepath", "File Path", 0, "", "Path of the file to load");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
RNA_def_boolean(func,
"check_existing",
@@ -1372,7 +1372,7 @@ void RNA_def_main_lattices(BlenderRNA *brna, PropertyRNA *cprop)
parm = RNA_def_string(func, "name", "Lattice", 0, "", "New name for the data-block");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
/* return type */
- parm = RNA_def_pointer(func, "lattice", "Lattice", "", "New lattices data-block");
+ parm = RNA_def_pointer(func, "lattice", "Lattice", "", "New lattice data-block");
RNA_def_function_return(func, parm);
func = RNA_def_function(srna, "remove", "rna_Main_ID_remove");
@@ -1857,7 +1857,7 @@ void RNA_def_main_armatures(BlenderRNA *brna, PropertyRNA *cprop)
func = RNA_def_function(srna, "remove", "rna_Main_ID_remove");
RNA_def_function_flag(func, FUNC_USE_REPORTS);
- RNA_def_function_ui_description(func, "Remove a armature from the current blendfile");
+ RNA_def_function_ui_description(func, "Remove an armature from the current blendfile");
parm = RNA_def_pointer(func, "armature", "Armature", "", "Armature to remove");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0);
@@ -1900,7 +1900,7 @@ void RNA_def_main_actions(BlenderRNA *brna, PropertyRNA *cprop)
func = RNA_def_function(srna, "remove", "rna_Main_ID_remove");
RNA_def_function_flag(func, FUNC_USE_REPORTS);
- RNA_def_function_ui_description(func, "Remove a action from the current blendfile");
+ RNA_def_function_ui_description(func, "Remove an action from the current blendfile");
parm = RNA_def_pointer(func, "action", "Action", "", "Action to remove");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0);
@@ -2158,7 +2158,7 @@ void RNA_def_main_masks(BlenderRNA *brna, PropertyRNA *cprop)
/* remove func */
func = RNA_def_function(srna, "remove", "rna_Main_ID_remove");
RNA_def_function_flag(func, FUNC_USE_REPORTS);
- RNA_def_function_ui_description(func, "Remove a masks from the current blendfile.");
+ RNA_def_function_ui_description(func, "Remove a mask from the current blendfile");
parm = RNA_def_pointer(func, "mask", "Mask", "", "Mask to remove");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0);
@@ -2238,11 +2238,11 @@ void RNA_def_main_lightprobes(BlenderRNA *brna, PropertyRNA *cprop)
RNA_def_struct_ui_text(srna, "Main Light Probes", "Collection of light probes");
func = RNA_def_function(srna, "new", "rna_Main_lightprobe_new");
- RNA_def_function_ui_description(func, "Add a new probe to the main database");
+ RNA_def_function_ui_description(func, "Add a new light probe to the main database");
parm = RNA_def_string(func, "name", "Probe", 0, "", "New name for the data-block");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
parm = RNA_def_enum(
- func, "type", rna_enum_lightprobes_type_items, 0, "Type", "The type of lightprobe to add");
+ func, "type", rna_enum_lightprobes_type_items, 0, "Type", "The type of light probe to add");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
/* return type */
parm = RNA_def_pointer(func, "lightprobe", "LightProbe", "", "New light probe data-block");
@@ -2250,15 +2250,15 @@ void RNA_def_main_lightprobes(BlenderRNA *brna, PropertyRNA *cprop)
func = RNA_def_function(srna, "remove", "rna_Main_ID_remove");
RNA_def_function_flag(func, FUNC_USE_REPORTS);
- RNA_def_function_ui_description(func, "Remove a probe from the current blendfile");
- parm = RNA_def_pointer(func, "lightprobe", "LightProbe", "", "Probe to remove");
+ RNA_def_function_ui_description(func, "Remove a light probe from the current blendfile");
+ parm = RNA_def_pointer(func, "lightprobe", "LightProbe", "", "Light probe to remove");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0);
RNA_def_boolean(func,
"do_unlink",
true,
"",
- "Unlink all usages of this probe before deleting it "
+ "Unlink all usages of this light probe before deleting it "
"(WARNING: will also delete objects instancing that light probe data)");
RNA_def_boolean(func,
"do_id_user",
@@ -2412,7 +2412,7 @@ void RNA_def_main_volumes(BlenderRNA *brna, PropertyRNA *cprop)
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
}
-# ifdef WITH_GEOMETRY_NODES
+# ifdef WITH_SIMULATION_DATABLOCK
void RNA_def_main_simulations(BlenderRNA *brna, PropertyRNA *cprop)
{
StructRNA *srna;
diff --git a/source/blender/makesrna/intern/rna_material.c b/source/blender/makesrna/intern/rna_material.c
index 7ef1904fc34..a4052430d9a 100644
--- a/source/blender/makesrna/intern/rna_material.c
+++ b/source/blender/makesrna/intern/rna_material.c
@@ -840,6 +840,8 @@ void RNA_def_material(BlenderRNA *brna)
prop = RNA_def_property(srna, "node_tree", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "nodetree");
RNA_def_property_clear_flag(prop, PROP_PTR_NO_OWNERSHIP);
+ /* XXX: remove once overrides in material node trees are supported. */
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE);
RNA_def_property_ui_text(prop, "Node Tree", "Node tree for node based materials");
prop = RNA_def_property(srna, "use_nodes", PROP_BOOLEAN, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c
index cf7d1f30dde..d5a1047d287 100644
--- a/source/blender/makesrna/intern/rna_mesh.c
+++ b/source/blender/makesrna/intern/rna_mesh.c
@@ -958,13 +958,14 @@ static void rna_MeshPaintMaskLayer_data_begin(CollectionPropertyIterator *iter,
{
Mesh *me = rna_mesh(ptr);
CustomDataLayer *layer = (CustomDataLayer *)ptr->data;
- rna_iterator_array_begin(iter, layer->data, sizeof(MFloatProperty), me->totvert, 0, NULL);
+ rna_iterator_array_begin(
+ iter, layer->data, sizeof(MFloatProperty), (me->edit_mesh) ? 0 : me->totvert, 0, NULL);
}
static int rna_MeshPaintMaskLayer_data_length(PointerRNA *ptr)
{
Mesh *me = rna_mesh(ptr);
- return me->totvert;
+ return (me->edit_mesh) ? 0 : me->totvert;
}
/* End paint mask */
@@ -987,13 +988,14 @@ static void rna_MeshFaceMapLayer_data_begin(CollectionPropertyIterator *iter, Po
{
Mesh *me = rna_mesh(ptr);
CustomDataLayer *layer = (CustomDataLayer *)ptr->data;
- rna_iterator_array_begin(iter, layer->data, sizeof(int), me->totpoly, 0, NULL);
+ rna_iterator_array_begin(
+ iter, layer->data, sizeof(int), (me->edit_mesh) ? 0 : me->totpoly, 0, NULL);
}
static int rna_MeshFaceMapLayer_data_length(PointerRNA *ptr)
{
Mesh *me = rna_mesh(ptr);
- return me->totpoly;
+ return (me->edit_mesh) ? 0 : me->totpoly;
}
static PointerRNA rna_Mesh_face_map_new(struct Mesh *me, ReportList *reports, const char *name)
@@ -1821,7 +1823,7 @@ static void rna_def_mlooptri(BlenderRNA *brna)
prop = RNA_def_property(srna, "material_index", PROP_INT, PROP_UNSIGNED);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_int_funcs(prop, "rna_MeshLoopTriangle_material_index_get", NULL, NULL);
- RNA_def_property_ui_text(prop, "Material Index", "");
+ RNA_def_property_ui_text(prop, "Material Index", "Material slot index of this triangle");
prop = RNA_def_property(srna, "use_smooth", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
@@ -1931,7 +1933,7 @@ static void rna_def_mpolygon(BlenderRNA *brna)
prop = RNA_def_property(srna, "material_index", PROP_INT, PROP_UNSIGNED);
RNA_def_property_int_sdna(prop, NULL, "mat_nr");
- RNA_def_property_ui_text(prop, "Material Index", "");
+ RNA_def_property_ui_text(prop, "Material Index", "Material slot index of this polygon");
# if 0
RNA_def_property_int_funcs(prop, NULL, NULL, "rna_MeshPoly_material_index_range");
# endif
@@ -3289,10 +3291,12 @@ static void rna_def_mesh(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Z", "Enable symmetry in the Z axis");
RNA_def_property_update(prop, 0, "rna_Mesh_update_draw");
- prop = RNA_def_property(srna, "use_mirror_vertex_group_x", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "editflag", ME_EDIT_VERTEX_GROUPS_X_SYMMETRY);
- RNA_def_property_ui_text(
- prop, "Vertex Groups X Symmetry", "Mirror the left/right vertex groups when painting");
+ prop = RNA_def_property(srna, "use_mirror_vertex_groups", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "editflag", ME_EDIT_MIRROR_VERTEX_GROUPS);
+ RNA_def_property_ui_text(prop,
+ "Mirror Vertex Groups",
+ "Mirror the left/right vertex groups when painting. The symmetry axis "
+ "is determined by the symmetry settings");
RNA_def_property_update(prop, 0, "rna_Mesh_update_draw");
/* End Symmetry */
diff --git a/source/blender/makesrna/intern/rna_mesh_api.c b/source/blender/makesrna/intern/rna_mesh_api.c
index f92a2932f06..2b0582cae9a 100644
--- a/source/blender/makesrna/intern/rna_mesh_api.c
+++ b/source/blender/makesrna/intern/rna_mesh_api.c
@@ -172,7 +172,7 @@ static void rna_Mesh_normals_split_custom_set_from_vertices(Mesh *mesh,
DEG_id_tag_update(&mesh->id, 0);
}
-static void rna_Mesh_transform(Mesh *mesh, float *mat, bool shape_keys)
+static void rna_Mesh_transform(Mesh *mesh, float mat[16], bool shape_keys)
{
BKE_mesh_transform(mesh, (float(*)[4])mat, shape_keys);
diff --git a/source/blender/makesrna/intern/rna_meta_api.c b/source/blender/makesrna/intern/rna_meta_api.c
index 19d0b35959b..93bd9fe3b9c 100644
--- a/source/blender/makesrna/intern/rna_meta_api.c
+++ b/source/blender/makesrna/intern/rna_meta_api.c
@@ -35,7 +35,7 @@
#include "rna_internal.h" /* own include */
#ifdef RNA_RUNTIME
-static void rna_Meta_transform(struct MetaBall *mb, float *mat)
+static void rna_Meta_transform(struct MetaBall *mb, float mat[16])
{
BKE_mball_transform(mb, (float(*)[4])mat, true);
diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c
index 98a2b683f18..674e5845ccb 100644
--- a/source/blender/makesrna/intern/rna_modifier.c
+++ b/source/blender/makesrna/intern/rna_modifier.c
@@ -2226,6 +2226,14 @@ static void rna_def_modifier_mirror(BlenderRNA *brna)
prop, "Merge Distance", "Distance within which mirrored vertices are merged");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
+ prop = RNA_def_property(srna, "bisect_threshold", PROP_FLOAT, PROP_DISTANCE);
+ RNA_def_property_float_sdna(prop, NULL, "bisect_threshold");
+ RNA_def_property_range(prop, 0, FLT_MAX);
+ RNA_def_property_ui_range(prop, 0, 1, 0.01, 6);
+ RNA_def_property_ui_text(
+ prop, "Bisect Distance", "Distance from the bisect plane within which vertices are removed");
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
prop = RNA_def_property(srna, "mirror_object", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "mirror_ob");
RNA_def_property_ui_text(prop, "Mirror Object", "Object to use as mirror");
@@ -2773,7 +2781,8 @@ static void rna_def_modifier_boolean(BlenderRNA *brna)
prop = RNA_def_property(srna, "double_threshold", PROP_FLOAT, PROP_DISTANCE);
RNA_def_property_float_sdna(prop, NULL, "double_threshold");
RNA_def_property_range(prop, 0, 1.0f);
- RNA_def_property_ui_range(prop, 0, 1, 0.0001, 6);
+ RNA_def_property_ui_range(prop, 0, 1, 1.0, 6);
+ RNA_def_property_ui_scale_type(prop, PROP_SCALE_LOG);
RNA_def_property_ui_text(
prop, "Overlap Threshold", "Threshold for checking overlapping geometry");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
@@ -5522,6 +5531,7 @@ static void rna_def_modifier_remesh(BlenderRNA *brna)
RNA_def_property_float_sdna(prop, NULL, "voxel_size");
RNA_def_property_range(prop, 0.0001f, FLT_MAX);
RNA_def_property_ui_range(prop, 0.0001, 2, 0.1, 3);
+ RNA_def_property_ui_scale_type(prop, PROP_SCALE_LOG);
RNA_def_property_ui_text(prop,
"Voxel Size",
"Size of the voxel in object space used for volume evaluation. Lower "
@@ -5920,8 +5930,9 @@ static void rna_def_modifier_triangulate(BlenderRNA *brna)
RNA_def_property_ui_text(
prop,
"Keep Normals",
- "Try to preserve custom normals (WARNING: depending on chosen triangulation method, "
- "shading may not be fully preserved, 'Fixed' method usually gives the best result here)");
+ "Try to preserve custom normals.\n"
+ "Warning: Depending on chosen triangulation method, "
+ "shading may not be fully preserved, \"Fixed\" method usually gives the best result here");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
RNA_define_lib_overridable(false);
diff --git a/source/blender/makesrna/intern/rna_movieclip.c b/source/blender/makesrna/intern/rna_movieclip.c
index c9520c939f4..f8fa2aab5e7 100644
--- a/source/blender/makesrna/intern/rna_movieclip.c
+++ b/source/blender/makesrna/intern/rna_movieclip.c
@@ -402,7 +402,7 @@ static void rna_def_movieclip(BlenderRNA *brna)
"Start Frame",
"Global scene frame number at which this movie starts playing "
"(affects all data associated with a clip)");
- RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, "rna_MovieClip_reload_update");
+ RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, NULL);
/* frame_offset */
prop = RNA_def_property(srna, "frame_offset", PROP_INT, PROP_NONE);
@@ -412,7 +412,7 @@ static void rna_def_movieclip(BlenderRNA *brna)
"Frame Offset",
"Offset of footage first frame relative to its file name "
"(affects only how footage is loading, does not change data associated with a clip)");
- RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, "rna_MovieClip_reload_update");
+ RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, NULL);
/* length */
prop = RNA_def_property(srna, "frame_duration", PROP_INT, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c
index d69a1e3dd06..2a1ea3d6716 100644
--- a/source/blender/makesrna/intern/rna_nodetree.c
+++ b/source/blender/makesrna/intern/rna_nodetree.c
@@ -65,6 +65,22 @@
const EnumPropertyItem rna_enum_node_socket_in_out_items[] = {
{SOCK_IN, "IN", 0, "Input", ""}, {SOCK_OUT, "OUT", 0, "Output", ""}, {0, NULL, 0, NULL, NULL}};
+static const EnumPropertyItem node_socket_data_type_items[] = {
+ {SOCK_FLOAT, "FLOAT", 0, "Float", ""},
+ {SOCK_INT, "INT", 0, "Integer", ""},
+ {SOCK_BOOLEAN, "BOOLEAN", 0, "Boolean", ""},
+ {SOCK_VECTOR, "VECTOR", 0, "Vector", ""},
+ {SOCK_STRING, "STRING", 0, "String", ""},
+ {SOCK_RGBA, "RGBA", 0, "Color", ""},
+ {SOCK_OBJECT, "OBJECT", 0, "Object", ""},
+ {SOCK_IMAGE, "IMAGE", 0, "Image", ""},
+ {SOCK_GEOMETRY, "GEOMETRY", 0, "Geometry", ""},
+ {SOCK_COLLECTION, "COLLECTION", 0, "Collection", ""},
+ {SOCK_TEXTURE, "TEXTURE", 0, "Texture", ""},
+ {SOCK_MATERIAL, "MATERIAL", 0, "Material", ""},
+ {0, NULL, 0, NULL, NULL},
+};
+
#ifndef RNA_RUNTIME
static const EnumPropertyItem rna_enum_node_socket_display_shape_items[] = {
{SOCK_DISPLAY_SHAPE_CIRCLE, "CIRCLE", 0, "Circle", ""},
@@ -78,7 +94,7 @@ static const EnumPropertyItem rna_enum_node_socket_display_shape_items[] = {
static const EnumPropertyItem node_socket_type_items[] = {
{SOCK_CUSTOM, "CUSTOM", 0, "Custom", ""},
{SOCK_FLOAT, "VALUE", 0, "Value", ""},
- {SOCK_INT, "INT", 0, "Int", ""},
+ {SOCK_INT, "INT", 0, "Integer", ""},
{SOCK_BOOLEAN, "BOOLEAN", 0, "Boolean", ""},
{SOCK_VECTOR, "VECTOR", 0, "Vector", ""},
{SOCK_STRING, "STRING", 0, "String", ""},
@@ -88,6 +104,8 @@ static const EnumPropertyItem node_socket_type_items[] = {
{SOCK_IMAGE, "IMAGE", 0, "Image", ""},
{SOCK_GEOMETRY, "GEOMETRY", 0, "Geometry", ""},
{SOCK_COLLECTION, "COLLECTION", 0, "Collection", ""},
+ {SOCK_TEXTURE, "TEXTURE", 0, "Texture", ""},
+ {SOCK_MATERIAL, "MATERIAL", 0, "Material", ""},
{0, NULL, 0, NULL, NULL},
};
@@ -109,6 +127,20 @@ static const EnumPropertyItem node_chunksize_items[] = {
};
#endif
+static const EnumPropertyItem rna_enum_execution_mode_items[] = {
+ {NTREE_EXECUTION_MODE_TILED,
+ "TILED",
+ 0,
+ "Tiled",
+ "Compositing is tiled, having as priority to display first tiles as fast as possible"},
+ {NTREE_EXECUTION_MODE_FULL_FRAME,
+ "FULL_FRAME",
+ 0,
+ "Full Frame",
+ "Composites full image result as fast as possible"},
+ {0, NULL, 0, NULL, NULL},
+};
+
const EnumPropertyItem rna_enum_mapping_type_items[] = {
{NODE_MAPPING_TYPE_POINT, "POINT", 0, "Point", "Transform a point"},
{NODE_MAPPING_TYPE_TEXTURE,
@@ -216,6 +248,7 @@ const EnumPropertyItem rna_enum_node_vec_math_items[] = {
{NODE_VECTOR_MATH_SUBTRACT, "SUBTRACT", 0, "Subtract", "A - B"},
{NODE_VECTOR_MATH_MULTIPLY, "MULTIPLY", 0, "Multiply", "Entry-wise multiply"},
{NODE_VECTOR_MATH_DIVIDE, "DIVIDE", 0, "Divide", "Entry-wise divide"},
+ {NODE_VECTOR_MATH_MULTIPLY_ADD, "MULTIPLY_ADD", 0, "Multiply Add", "A * B + C"},
{0, "", ICON_NONE, NULL, NULL},
{NODE_VECTOR_MATH_CROSS_PRODUCT, "CROSS_PRODUCT", 0, "Cross Product", "A cross B"},
{NODE_VECTOR_MATH_PROJECT, "PROJECT", 0, "Project", "Project A onto B"},
@@ -329,8 +362,12 @@ const EnumPropertyItem rna_enum_node_map_range_items[] = {
};
const EnumPropertyItem rna_enum_node_clamp_items[] = {
- {NODE_CLAMP_MINMAX, "MINMAX", 0, "Min Max", "Clamp values using Min and Max values"},
- {NODE_CLAMP_RANGE, "RANGE", 0, "Range", "Clamp values between Min and Max range"},
+ {NODE_CLAMP_MINMAX, "MINMAX", 0, "Min Max", "Constrain value between min and max"},
+ {NODE_CLAMP_RANGE,
+ "RANGE",
+ 0,
+ "Range",
+ "Constrain value between min and max, swapping arguments when min > max"},
{0, NULL, 0, NULL, NULL},
};
@@ -449,6 +486,12 @@ static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_vecto
ITEM_VECTOR,
{0, NULL, 0, NULL, NULL},
};
+static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_float_vector[] = {
+ ITEM_ATTRIBUTE,
+ ITEM_FLOAT,
+ ITEM_VECTOR,
+ {0, NULL, 0, NULL, NULL},
+};
static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_float[] = {
ITEM_ATTRIBUTE,
ITEM_FLOAT,
@@ -487,6 +530,7 @@ static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_no_bo
# include "NOD_common.h"
# include "NOD_composite.h"
+# include "NOD_geometry.h"
# include "NOD_shader.h"
# include "NOD_socket.h"
@@ -946,6 +990,32 @@ static void rna_NodeTree_get_from_context(
RNA_parameter_list_free(&list);
}
+static bool rna_NodeTree_valid_socket_type(eNodeSocketDatatype socket_type,
+ bNodeTreeType *ntreetype)
+{
+ extern FunctionRNA rna_NodeTree_valid_socket_type_func;
+
+ PointerRNA ptr;
+ ParameterList list;
+ FunctionRNA *func;
+ void *ret;
+ bool valid;
+
+ RNA_pointer_create(NULL, ntreetype->rna_ext.srna, NULL, &ptr); /* dummy */
+ func = &rna_NodeTree_valid_socket_type_func;
+
+ RNA_parameter_list_create(&list, &ptr, func);
+ RNA_parameter_set_lookup(&list, "type", &socket_type);
+ ntreetype->rna_ext.call(NULL, &ptr, func, &list);
+
+ RNA_parameter_get_lookup(&list, "valid", &ret);
+ valid = *(bool *)ret;
+
+ RNA_parameter_list_free(&list);
+
+ return valid;
+}
+
static void rna_NodeTree_unregister(Main *UNUSED(bmain), StructRNA *type)
{
bNodeTreeType *nt = RNA_struct_blender_type_get(type);
@@ -974,7 +1044,7 @@ static StructRNA *rna_NodeTree_register(Main *bmain,
bNodeTreeType *nt, dummynt;
bNodeTree dummyntree;
PointerRNA dummyptr;
- int have_function[3];
+ int have_function[4];
/* setup dummy tree & tree type to store static properties in */
memset(&dummynt, 0, sizeof(bNodeTreeType));
@@ -1020,6 +1090,7 @@ static StructRNA *rna_NodeTree_register(Main *bmain,
nt->poll = (have_function[0]) ? rna_NodeTree_poll : NULL;
nt->update = (have_function[1]) ? rna_NodeTree_update_reg : NULL;
nt->get_from_context = (have_function[2]) ? rna_NodeTree_get_from_context : NULL;
+ nt->valid_socket_type = (have_function[3]) ? rna_NodeTree_valid_socket_type : NULL;
ntreeTypeAdd(nt);
@@ -1074,13 +1145,25 @@ static bNode *rna_NodeTree_node_new(bNodeTree *ntree,
return NULL;
}
- if (ntype->poll && !ntype->poll(ntype, ntree)) {
- BKE_reportf(reports,
- RPT_ERROR,
- "Cannot add node of type %s to node tree '%s'",
- type,
- ntree->id.name + 2);
- return NULL;
+ const char *disabled_hint = NULL;
+ if (ntype->poll && !ntype->poll(ntype, ntree, &disabled_hint)) {
+ if (disabled_hint) {
+ BKE_reportf(reports,
+ RPT_ERROR,
+ "Cannot add node of type %s to node tree '%s'\n %s",
+ type,
+ ntree->id.name + 2,
+ disabled_hint);
+ return NULL;
+ }
+ else {
+ BKE_reportf(reports,
+ RPT_ERROR,
+ "Cannot add node of type %s to node tree '%s'",
+ type,
+ ntree->id.name + 2);
+ return NULL;
+ }
}
node = nodeAddNode(C, ntree, type);
@@ -1200,6 +1283,13 @@ static bNodeLink *rna_NodeTree_link_new(bNodeTree *ntree,
if (nodeCountSocketLinks(ntree, tosock) + 1 > nodeSocketLinkLimit(tosock)) {
nodeRemSocketLinks(ntree, tosock);
}
+ if (tosock->flag & SOCK_MULTI_INPUT) {
+ LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) {
+ if (link->fromsock == fromsock && link->tosock == tosock) {
+ nodeRemLink(ntree, link);
+ }
+ }
+ }
}
ret = nodeAddLink(ntree, fromnode, fromsock, tonode, tosock);
@@ -1355,6 +1445,7 @@ static void rna_NodeTree_socket_remove(bNodeTree *ntree,
ntreeRemoveSocketInterface(ntree, sock);
ntreeUpdateTree(bmain, ntree);
+ DEG_id_tag_update(&ntree->id, 0);
WM_main_add_notifier(NC_NODE | NA_EDITED, ntree);
}
}
@@ -1520,7 +1611,7 @@ char *rna_Node_ImageUser_path(PointerRNA *ptr)
return NULL;
}
-static bool rna_Node_poll(bNodeType *ntype, bNodeTree *ntree)
+static bool rna_Node_poll(bNodeType *ntype, bNodeTree *ntree, const char **UNUSED(r_disabled_hint))
{
extern FunctionRNA rna_Node_poll_func;
@@ -1545,7 +1636,9 @@ static bool rna_Node_poll(bNodeType *ntype, bNodeTree *ntree)
return visible;
}
-static bool rna_Node_poll_instance(bNode *node, bNodeTree *ntree)
+static bool rna_Node_poll_instance(bNode *node,
+ bNodeTree *ntree,
+ const char **UNUSED(disabled_info))
{
extern FunctionRNA rna_Node_poll_instance_func;
@@ -1570,10 +1663,12 @@ static bool rna_Node_poll_instance(bNode *node, bNodeTree *ntree)
return visible;
}
-static bool rna_Node_poll_instance_default(bNode *node, bNodeTree *ntree)
+static bool rna_Node_poll_instance_default(bNode *node,
+ bNodeTree *ntree,
+ const char **disabled_info)
{
/* use the basic poll function */
- return rna_Node_poll(node->typeinfo, ntree);
+ return rna_Node_poll(node->typeinfo, ntree, disabled_info);
}
static void rna_Node_update_reg(bNodeTree *ntree, bNode *node)
@@ -1886,6 +1981,44 @@ static const EnumPropertyItem *itemf_function_check(
return item_array;
}
+static bool switch_type_supported(const EnumPropertyItem *item)
+{
+ return ELEM(item->value,
+ SOCK_FLOAT,
+ SOCK_INT,
+ SOCK_BOOLEAN,
+ SOCK_VECTOR,
+ SOCK_STRING,
+ SOCK_RGBA,
+ SOCK_GEOMETRY,
+ SOCK_OBJECT,
+ SOCK_COLLECTION,
+ SOCK_TEXTURE,
+ SOCK_MATERIAL);
+}
+
+static const EnumPropertyItem *rna_GeometryNodeSwitch_type_itemf(bContext *UNUSED(C),
+ PointerRNA *UNUSED(ptr),
+ PropertyRNA *UNUSED(prop),
+ bool *r_free)
+{
+ *r_free = true;
+ return itemf_function_check(node_socket_data_type_items, switch_type_supported);
+}
+
+static bool attribute_clamp_type_supported(const EnumPropertyItem *item)
+{
+ return ELEM(item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3, CD_PROP_INT32, CD_PROP_COLOR);
+}
+static const EnumPropertyItem *rna_GeometryNodeAttributeClamp_type_itemf(bContext *UNUSED(C),
+ PointerRNA *UNUSED(ptr),
+ PropertyRNA *UNUSED(prop),
+ bool *r_free)
+{
+ *r_free = true;
+ return itemf_function_check(rna_enum_attribute_type_items, attribute_clamp_type_supported);
+}
+
static bool attribute_random_type_supported(const EnumPropertyItem *item)
{
return ELEM(item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3, CD_PROP_BOOL, CD_PROP_INT32);
@@ -1943,6 +2076,7 @@ static void rna_GeometryNodeAttributeRandomize_data_type_update(Main *bmain,
static bool attribute_convert_type_supported(const EnumPropertyItem *item)
{
return ELEM(item->value,
+ CD_AUTO_FROM_NAME,
CD_PROP_FLOAT,
CD_PROP_FLOAT2,
CD_PROP_FLOAT3,
@@ -1954,7 +2088,8 @@ static const EnumPropertyItem *rna_GeometryNodeAttributeConvert_type_itemf(
bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free)
{
*r_free = true;
- return itemf_function_check(rna_enum_attribute_type_items, attribute_convert_type_supported);
+ return itemf_function_check(rna_enum_attribute_type_with_auto_items,
+ attribute_convert_type_supported);
}
static bool attribute_fill_type_supported(const EnumPropertyItem *item)
@@ -2065,6 +2200,28 @@ static void rna_GeometryNodeAttributeVectorMath_operation_update(Main *bmain,
rna_Node_socket_update(bmain, scene, ptr);
}
+static bool attribute_map_range_type_supported(const EnumPropertyItem *item)
+{
+ return ELEM(item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3);
+}
+static const EnumPropertyItem *rna_GeometryNodeAttributeMapRange_type_itemf(
+ bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free)
+{
+ *r_free = true;
+ return itemf_function_check(rna_enum_attribute_type_items, attribute_map_range_type_supported);
+}
+
+static bool attribute_curve_map_type_supported(const EnumPropertyItem *item)
+{
+ return ELEM(item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3, CD_PROP_COLOR);
+}
+static const EnumPropertyItem *rna_GeometryNodeAttributeCurveMap_type_itemf(
+ bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free)
+{
+ *r_free = true;
+ return itemf_function_check(rna_enum_attribute_type_items, attribute_curve_map_type_supported);
+}
+
static StructRNA *rna_ShaderNode_register(Main *bmain,
ReportList *reports,
void *data,
@@ -3001,7 +3158,7 @@ static void rna_NodeSocketStandard_draw(ID *id,
}
static void rna_NodeSocketStandard_draw_color(
- ID *id, bNodeSocket *sock, struct bContext *C, PointerRNA *nodeptr, float *r_color)
+ ID *id, bNodeSocket *sock, struct bContext *C, PointerRNA *nodeptr, float r_color[4])
{
PointerRNA ptr;
RNA_pointer_create(id, &RNA_NodeSocket, sock, &ptr);
@@ -3021,7 +3178,7 @@ static void rna_NodeSocketInterfaceStandard_draw(ID *id,
static void rna_NodeSocketInterfaceStandard_draw_color(ID *id,
bNodeSocket *sock,
struct bContext *C,
- float *r_color)
+ float r_color[4])
{
PointerRNA ptr;
RNA_pointer_create(id, &RNA_NodeSocket, sock, &ptr);
@@ -3179,18 +3336,20 @@ static PointerRNA rna_NodeInternal_output_template(StructRNA *srna, int index)
static bool rna_NodeInternal_poll(StructRNA *srna, bNodeTree *ntree)
{
bNodeType *ntype = RNA_struct_blender_type_get(srna);
- return ntype && (!ntype->poll || ntype->poll(ntype, ntree));
+ const char *disabled_hint;
+ return ntype && (!ntype->poll || ntype->poll(ntype, ntree, &disabled_hint));
}
static bool rna_NodeInternal_poll_instance(bNode *node, bNodeTree *ntree)
{
bNodeType *ntype = node->typeinfo;
+ const char *disabled_hint;
if (ntype->poll_instance) {
- return ntype->poll_instance(node, ntree);
+ return ntype->poll_instance(node, ntree, &disabled_hint);
}
else {
/* fall back to basic poll function */
- return !ntype->poll || ntype->poll(ntype, ntree);
+ return !ntype->poll || ntype->poll(ntype, ntree, &disabled_hint);
}
}
@@ -3256,6 +3415,35 @@ static StructRNA *rna_NodeCustomGroup_register(Main *bmain,
return nt->rna_ext.srna;
}
+static StructRNA *rna_GeometryNodeCustomGroup_register(Main *bmain,
+ ReportList *reports,
+ void *data,
+ const char *identifier,
+ StructValidateFunc validate,
+ StructCallbackFunc call,
+ StructFreeFunc free)
+{
+ bNodeType *nt = rna_Node_register_base(
+ bmain, reports, &RNA_GeometryNodeCustomGroup, data, identifier, validate, call, free);
+
+ if (!nt) {
+ return NULL;
+ }
+
+ nt->group_update_func = node_group_update;
+ nt->type = NODE_CUSTOM_GROUP;
+
+ register_node_type_geo_custom_group(nt);
+
+ nodeRegisterType(nt);
+
+ WM_main_add_notifier(NC_NODE | NA_EDITED, NULL);
+
+ return nt->rna_ext.srna;
+}
+
+void register_node_type_geo_custom_group(bNodeType *ntype);
+
static StructRNA *rna_ShaderNodeCustomGroup_register(Main *bmain,
ReportList *reports,
void *data,
@@ -3344,7 +3532,8 @@ static void rna_NodeGroup_node_tree_set(PointerRNA *ptr,
bNode *node = ptr->data;
bNodeTree *ngroup = value.data;
- if (nodeGroupPoll(ntree, ngroup)) {
+ const char *disabled_hint = NULL;
+ if (nodeGroupPoll(ntree, ngroup, &disabled_hint)) {
if (node->id) {
id_us_min(node->id);
}
@@ -3366,7 +3555,8 @@ static bool rna_NodeGroup_node_tree_poll(PointerRNA *ptr, const PointerRNA value
return false;
}
- return nodeGroupPoll(ntree, ngroup);
+ const char *disabled_hint = NULL;
+ return nodeGroupPoll(ntree, ngroup, &disabled_hint);
}
static StructRNA *rna_NodeGroup_interface_typef(PointerRNA *ptr)
@@ -3807,7 +3997,7 @@ static void rna_NodeCryptomatte_layer_name_set(PointerRNA *ptr, int new_value)
}
}
-static const EnumPropertyItem *rna_NodeCryptomatte_layer_name_itemf(bContext *UNUSED(C),
+static const EnumPropertyItem *rna_NodeCryptomatte_layer_name_itemf(bContext *C,
PointerRNA *ptr,
PropertyRNA *UNUSED(prop),
bool *r_free)
@@ -3818,7 +4008,7 @@ static const EnumPropertyItem *rna_NodeCryptomatte_layer_name_itemf(bContext *UN
EnumPropertyItem template = {0, "", 0, "", ""};
int totitem = 0;
- ntreeCompositCryptomatteUpdateLayerNames(node);
+ ntreeCompositCryptomatteUpdateLayerNames(CTX_data_scene(C), node);
int layer_index;
LISTBASE_FOREACH_INDEX (CryptomatteLayer *, layer, &storage->runtime.layers, layer_index) {
template.value = layer_index;
@@ -3910,7 +4100,7 @@ static void rna_NodeCryptomatte_matte_set(PointerRNA *ptr, const char *value)
static void rna_NodeCryptomatte_update_add(Main *bmain, Scene *scene, PointerRNA *ptr)
{
- ntreeCompositCryptomatteSyncFromAdd(ptr->data);
+ ntreeCompositCryptomatteSyncFromAdd(scene, ptr->data);
rna_Node_update(bmain, scene, ptr);
}
@@ -4290,6 +4480,13 @@ void rna_ShaderNodePointDensity_density_minmax(bNode *self,
RE_point_density_minmax(depsgraph, pd, r_min, r_max);
}
+bool rna_NodeSocketMaterial_default_value_poll(PointerRNA *UNUSED(ptr), PointerRNA value)
+{
+ /* Do not show grease pencil materials for now. */
+ Material *ma = (Material *)value.data;
+ return ma->gp_style == NULL;
+}
+
#else
static const EnumPropertyItem prop_image_layer_items[] = {
@@ -8634,6 +8831,41 @@ static void def_cmp_denoise(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
+static void def_cmp_antialiasing(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ RNA_def_struct_sdna_from(srna, "NodeAntiAliasingData", "storage");
+
+ prop = RNA_def_property(srna, "threshold", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_float_sdna(prop, NULL, "threshold");
+ RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.1, 3);
+ RNA_def_property_ui_text(
+ prop,
+ "Threshold",
+ "Threshold to detect edges (smaller threshold makes more sensitive detection)");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+
+ prop = RNA_def_property(srna, "contrast_limit", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_float_sdna(prop, NULL, "contrast_limit");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.1, 3);
+ RNA_def_property_ui_text(
+ prop,
+ "Contrast Limit",
+ "How much to eliminate spurious edges to avoid artifacts (the larger value makes less "
+ "active; the value 2.0, for example, means discard a detected edge if there is a "
+ "neighboring edge that has 2.0 times bigger contrast than the current one)");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+
+ prop = RNA_def_property(srna, "corner_rounding", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_float_sdna(prop, NULL, "corner_rounding");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.1, 3);
+ RNA_def_property_ui_text(prop, "Corner Rounding", "How much sharp corners will be rounded");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+}
+
/* -- Texture Nodes --------------------------------------------------------- */
static void def_tex_output(StructRNA *srna)
@@ -8722,7 +8954,7 @@ static void def_geo_boolean(StructRNA *srna)
RNA_def_property_enum_items(prop, rna_node_geometry_boolean_method_items);
RNA_def_property_enum_default(prop, GEO_NODE_BOOLEAN_INTERSECT);
RNA_def_property_ui_text(prop, "Operation", "");
- RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
static void def_geo_triangulate(StructRNA *srna)
@@ -8834,9 +9066,9 @@ static void def_geo_attribute_convert(StructRNA *srna)
RNA_def_struct_sdna_from(srna, "NodeAttributeConvert", "storage");
prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE);
- RNA_def_property_enum_items(prop, rna_enum_attribute_type_items);
+ RNA_def_property_enum_items(prop, rna_enum_attribute_type_with_auto_items);
RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeConvert_type_itemf");
- RNA_def_property_enum_default(prop, CD_PROP_FLOAT);
+ RNA_def_property_enum_default(prop, CD_AUTO_FROM_NAME);
RNA_def_property_ui_text(prop, "Data Type", "The data type to save the result attribute with");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update");
@@ -8915,6 +9147,26 @@ static void def_geo_attribute_vector_math(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
+static void def_geo_attribute_map_range(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ RNA_def_struct_sdna_from(srna, "NodeAttributeMapRange", "storage");
+
+ prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_bitflag_sdna(prop, NULL, "data_type");
+ RNA_def_property_enum_items(prop, rna_enum_attribute_type_items);
+ RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeMapRange_type_itemf");
+ RNA_def_property_ui_text(prop, "Data Type", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+
+ prop = RNA_def_property(srna, "interpolation_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "interpolation_type");
+ RNA_def_property_enum_items(prop, rna_enum_node_map_range_items);
+ RNA_def_property_ui_text(prop, "Interpolation Type", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_ShaderNode_socket_update");
+}
+
static void def_geo_point_instance(StructRNA *srna)
{
static const EnumPropertyItem instance_type_items[] = {
@@ -8975,6 +9227,26 @@ static void def_geo_attribute_mix(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
+static void def_geo_attribute_clamp(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ RNA_def_struct_sdna_from(srna, "NodeAttributeClamp", "storage");
+
+ prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_bitflag_sdna(prop, NULL, "data_type");
+ RNA_def_property_enum_items(prop, rna_enum_attribute_type_items);
+ RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeClamp_type_itemf");
+ RNA_def_property_ui_text(prop, "Data Type", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+
+ prop = RNA_def_property(srna, "operation", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_enum_node_clamp_items);
+ RNA_def_property_enum_default(prop, NODE_CLAMP_MINMAX);
+ RNA_def_property_ui_text(prop, "Operation", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+}
+
static void def_geo_attribute_attribute_compare(StructRNA *srna)
{
PropertyRNA *prop;
@@ -8988,12 +9260,12 @@ static void def_geo_attribute_attribute_compare(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
prop = RNA_def_property(srna, "input_type_a", PROP_ENUM, PROP_NONE);
- RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_any);
+ RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_no_boolean);
RNA_def_property_ui_text(prop, "Input Type A", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
prop = RNA_def_property(srna, "input_type_b", PROP_ENUM, PROP_NONE);
- RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_any);
+ RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_no_boolean);
RNA_def_property_ui_text(prop, "Input Type B", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
@@ -9037,6 +9309,85 @@ static void def_geo_attribute_color_ramp(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
+static void def_geo_attribute_curve_map(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ RNA_def_struct_sdna_from(srna, "NodeAttributeCurveMap", "storage");
+
+ prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "data_type");
+ RNA_def_property_enum_items(prop, rna_enum_attribute_type_items);
+ RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeCurveMap_type_itemf");
+ RNA_def_property_ui_text(prop, "Data Type", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+
+ prop = RNA_def_property(srna, "curve_vec", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "CurveMapping");
+ RNA_def_property_ui_text(prop, "Mapping", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+
+ prop = RNA_def_property(srna, "curve_rgb", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "CurveMapping");
+ RNA_def_property_ui_text(prop, "Mapping", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+}
+
+static void def_geo_attribute_vector_rotate(StructRNA *srna)
+{
+ static const EnumPropertyItem rotate_mode_items[] = {
+ {GEO_NODE_VECTOR_ROTATE_TYPE_AXIS,
+ "AXIS_ANGLE",
+ 0,
+ "Axis Angle",
+ "Rotate a point using axis angle"},
+ {GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_X, "X_AXIS", 0, "X Axis", "Rotate a point using X axis"},
+ {GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_Y, "Y_AXIS", 0, "Y Axis", "Rotate a point using Y axis"},
+ {GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_Z, "Z_AXIS", 0, "Z Axis", "Rotate a point using Z axis"},
+ {GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ,
+ "EULER_XYZ",
+ 0,
+ "Euler",
+ "Rotate a point using XYZ order"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ PropertyRNA *prop;
+
+ RNA_def_struct_sdna_from(srna, "NodeAttributeVectorRotate", "storage");
+
+ prop = RNA_def_property(srna, "rotation_mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "mode");
+ RNA_def_property_enum_items(prop, rotate_mode_items);
+ RNA_def_property_ui_text(prop, "Mode", "Type of rotation");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_ShaderNode_socket_update");
+
+ prop = RNA_def_property(srna, "input_type_vector", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_vector);
+ RNA_def_property_ui_text(prop, "Input Type Vector", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+
+ prop = RNA_def_property(srna, "input_type_center", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_vector);
+ RNA_def_property_ui_text(prop, "Input Type Center", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+
+ prop = RNA_def_property(srna, "input_type_axis", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_vector);
+ RNA_def_property_ui_text(prop, "Input Type Axis", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+
+ prop = RNA_def_property(srna, "input_type_angle", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_float);
+ RNA_def_property_ui_text(prop, "Input Type Angle", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+
+ prop = RNA_def_property(srna, "input_type_rotation", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_vector);
+ RNA_def_property_ui_text(prop, "Input Type Rotation", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+}
+
static void def_geo_point_rotate(StructRNA *srna)
{
static const EnumPropertyItem type_items[] = {
@@ -9174,7 +9525,7 @@ static void def_geo_point_scale(StructRNA *srna)
RNA_def_struct_sdna_from(srna, "NodeGeometryPointScale", "storage");
prop = RNA_def_property(srna, "input_type", PROP_ENUM, PROP_NONE);
- RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_vector);
+ RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_float_vector);
RNA_def_property_ui_text(prop, "Input Type", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
@@ -9191,19 +9542,6 @@ static void def_geo_point_translate(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
-static void def_geo_attribute_sample_texture(StructRNA *srna)
-{
- PropertyRNA *prop;
-
- prop = RNA_def_property(srna, "texture", PROP_POINTER, PROP_NONE);
- RNA_def_property_pointer_sdna(prop, NULL, "id");
- RNA_def_property_struct_type(prop, "Texture");
- RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
- RNA_def_property_ui_text(prop, "Texture", "Texture to sample values from");
- RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update_relations");
-}
-
static void def_geo_object_info(StructRNA *srna)
{
PropertyRNA *prop;
@@ -9471,6 +9809,93 @@ static void def_geo_mesh_line(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
+static void def_geo_switch(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ RNA_def_struct_sdna_from(srna, "NodeSwitch", "storage");
+ prop = RNA_def_property(srna, "input_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "input_type");
+ RNA_def_property_enum_items(prop, node_socket_data_type_items);
+ RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeSwitch_type_itemf");
+ RNA_def_property_ui_text(prop, "Input Type", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+}
+
+static void def_geo_curve_resample(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ static EnumPropertyItem mode_items[] = {
+ {GEO_NODE_CURVE_SAMPLE_COUNT,
+ "COUNT",
+ 0,
+ "Count",
+ "Sample the specified number of points along each spline"},
+ {GEO_NODE_CURVE_SAMPLE_LENGTH,
+ "LENGTH",
+ 0,
+ "Length",
+ "Calculate the number of samples by splitting each spline into segments with the specified "
+ "length"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ RNA_def_struct_sdna_from(srna, "NodeGeometryCurveResample", "storage");
+
+ prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, mode_items);
+ RNA_def_property_ui_text(prop, "Mode", "How to specify the amount of samples");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+}
+
+static void def_geo_attribute_transfer(StructRNA *srna)
+{
+ static EnumPropertyItem mapping_items[] = {
+ {GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED,
+ "NEAREST_FACE_INTERPOLATED",
+ 0,
+ "Nearest Face Interpolated",
+ "Transfer the attribute from the nearest face on a surface (loose points and edges are "
+ "ignored)"},
+ {GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST,
+ "NEAREST",
+ 0,
+ "Nearest",
+ "Transfer the element from the nearest element (using face and edge centers for the "
+ "distance computation)"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ PropertyRNA *prop;
+
+ RNA_def_struct_sdna_from(srna, "NodeGeometryAttributeTransfer", "storage");
+
+ prop = RNA_def_property(srna, "domain", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_enum_attribute_domain_with_auto_items);
+ RNA_def_property_enum_default(prop, ATTR_DOMAIN_AUTO);
+ RNA_def_property_ui_text(prop, "Domain", "The geometry domain to save the result attribute in");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+
+ prop = RNA_def_property(srna, "mapping", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, mapping_items);
+ RNA_def_property_ui_text(prop, "Mapping", "Mapping between geometries");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+}
+
+static void def_geo_input_material(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "id");
+ RNA_def_property_struct_type(prop, "Material");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_ui_text(prop, "Material", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+}
+
/* -------------------------------------------------------------------------- */
static void rna_def_shader_node(BlenderRNA *brna)
@@ -9566,6 +9991,11 @@ static void rna_def_node_socket(BlenderRNA *brna)
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Identifier", "Unique identifier for mapping sockets");
+ prop = RNA_def_property(srna, "description", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "description");
+ RNA_def_property_ui_text(prop, "Tooltip", "Socket tooltip");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocket_update");
+
prop = RNA_def_property(srna, "is_output", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_funcs(prop, "rna_NodeSocket_is_output_get", NULL);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
@@ -9594,6 +10024,12 @@ static void rna_def_node_socket(BlenderRNA *brna)
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Linked", "True if the socket is connected");
+ prop = RNA_def_property(srna, "is_multi_input", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", SOCK_MULTI_INPUT);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(
+ prop, "Multi Input", "True if the socket can accept multiple ordered input links");
+
prop = RNA_def_property(srna, "show_expanded", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE);
RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", SOCK_COLLAPSED);
@@ -9704,6 +10140,11 @@ static void rna_def_node_socket_interface(BlenderRNA *brna)
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Identifier", "Unique identifier for mapping sockets");
+ prop = RNA_def_property(srna, "description", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "description");
+ RNA_def_property_ui_text(prop, "Tooltip", "Socket tooltip");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update");
+
prop = RNA_def_property(srna, "is_output", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_funcs(prop, "rna_NodeSocket_is_output_get", NULL);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
@@ -10227,6 +10668,80 @@ static void rna_def_node_socket_collection(BlenderRNA *brna,
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update");
}
+static void rna_def_node_socket_texture(BlenderRNA *brna,
+ const char *identifier,
+ const char *interface_idname)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, identifier, "NodeSocketStandard");
+ RNA_def_struct_ui_text(srna, "Texture Node Socket", "Texture socket of a node");
+ RNA_def_struct_sdna(srna, "bNodeSocket");
+
+ RNA_def_struct_sdna_from(srna, "bNodeSocketValueTexture", "default_value");
+
+ prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "value");
+ RNA_def_property_struct_type(prop, "Texture");
+ RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket");
+ RNA_def_property_update(
+ prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update");
+ RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE);
+
+ /* socket interface */
+ srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard");
+ RNA_def_struct_ui_text(srna, "Texture Node Socket Interface", "Texture socket of a node");
+ RNA_def_struct_sdna(srna, "bNodeSocket");
+
+ RNA_def_struct_sdna_from(srna, "bNodeSocketValueTexture", "default_value");
+
+ prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "value");
+ RNA_def_property_struct_type(prop, "Texture");
+ RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update");
+}
+
+static void rna_def_node_socket_material(BlenderRNA *brna,
+ const char *identifier,
+ const char *interface_idname)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, identifier, "NodeSocketStandard");
+ RNA_def_struct_ui_text(srna, "Material Node Socket", "Material socket of a node");
+ RNA_def_struct_sdna(srna, "bNodeSocket");
+
+ RNA_def_struct_sdna_from(srna, "bNodeSocketValueMaterial", "default_value");
+
+ prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "value");
+ RNA_def_property_struct_type(prop, "Material");
+ RNA_def_property_pointer_funcs(
+ prop, NULL, NULL, NULL, "rna_NodeSocketMaterial_default_value_poll");
+ RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket");
+ RNA_def_property_update(
+ prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update");
+ RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE);
+
+ /* socket interface */
+ srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard");
+ RNA_def_struct_ui_text(srna, "Material Node Socket Interface", "Material socket of a node");
+ RNA_def_struct_sdna(srna, "bNodeSocket");
+
+ RNA_def_struct_sdna_from(srna, "bNodeSocketValueMaterial", "default_value");
+
+ prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "value");
+ RNA_def_property_struct_type(prop, "Material");
+ RNA_def_property_pointer_funcs(
+ prop, NULL, NULL, NULL, "rna_NodeSocketMaterial_default_value_poll");
+ RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update");
+}
+
static void rna_def_node_socket_standard_types(BlenderRNA *brna)
{
/* XXX Workaround: Registered functions are not exposed in python by bpy,
@@ -10326,6 +10841,8 @@ static void rna_def_node_socket_standard_types(BlenderRNA *brna)
rna_def_node_socket_float(
brna, "NodeSocketFloatTime", "NodeSocketInterfaceFloatTime", PROP_TIME);
rna_def_node_socket_float(
+ brna, "NodeSocketFloatTimeAbsolute", "NodeSocketInterfaceFloatTimeAbsolute", PROP_TIME_ABSOLUTE);
+ rna_def_node_socket_float(
brna, "NodeSocketFloatDistance", "NodeSocketInterfaceFloatDistance", PROP_DISTANCE);
rna_def_node_socket_int(brna, "NodeSocketInt", "NodeSocketInterfaceInt", PROP_NONE);
@@ -10371,6 +10888,10 @@ static void rna_def_node_socket_standard_types(BlenderRNA *brna)
rna_def_node_socket_geometry(brna, "NodeSocketGeometry", "NodeSocketInterfaceGeometry");
rna_def_node_socket_collection(brna, "NodeSocketCollection", "NodeSocketInterfaceCollection");
+
+ rna_def_node_socket_texture(brna, "NodeSocketTexture", "NodeSocketInterfaceTexture");
+
+ rna_def_node_socket_material(brna, "NodeSocketMaterial", "NodeSocketInterfaceMaterial");
}
static void rna_def_internal_node(BlenderRNA *brna)
@@ -10654,6 +11175,12 @@ static void rna_def_node(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Show Texture", "Display node in viewport textured shading mode");
RNA_def_property_update(prop, 0, "rna_Node_update");
+ prop = RNA_def_property(srna, "active_preview", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", NODE_ACTIVE_PREVIEW);
+ RNA_def_property_ui_text(prop, "Active Preview", "Node is previewed in other editor");
+ RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE);
+ RNA_def_property_update(prop, NC_NODE, NULL);
+
/* generic property update function */
func = RNA_def_function(srna, "socket_value_update", "rna_Node_socket_value_update");
RNA_def_function_ui_description(func, "Update after property changes");
@@ -11149,6 +11676,14 @@ static void rna_def_nodetree(BlenderRNA *brna)
parm = RNA_def_pointer(
func, "result_3", "ID", "From ID", "Original ID data-block selected from the context");
RNA_def_function_output(func, parm);
+
+ /* Check for support of a socket type. */
+ func = RNA_def_function(srna, "valid_socket_type", NULL);
+ RNA_def_function_ui_description(func, "Check if the socket type is valid for the node tree");
+ RNA_def_function_flag(func, FUNC_NO_SELF | FUNC_REGISTER_OPTIONAL);
+ parm = RNA_def_enum(func, "type", node_socket_type_items, 0, "", "");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ RNA_def_function_return(func, RNA_def_boolean(func, "valid", false, "", ""));
}
static void rna_def_composite_nodetree(BlenderRNA *brna)
@@ -11162,6 +11697,12 @@ static void rna_def_composite_nodetree(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "bNodeTree");
RNA_def_struct_ui_icon(srna, ICON_RENDERLAYERS);
+ prop = RNA_def_property(srna, "execution_mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "execution_mode");
+ RNA_def_property_enum_items(prop, rna_enum_execution_mode_items);
+ RNA_def_property_ui_text(prop, "Execution Mode", "Set how compositing is executed");
+ RNA_def_property_update(prop, NC_NODE | ND_DISPLAY, "rna_NodeTree_update");
+
prop = RNA_def_property(srna, "render_quality", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "render_quality");
RNA_def_property_enum_items(prop, node_quality_items);
@@ -11386,6 +11927,12 @@ void RNA_def_nodetree(BlenderRNA *brna)
"Custom Group",
"Base node type for custom registered node group types",
"rna_NodeCustomGroup_register");
+ def_custom_group(brna,
+ "GeometryNodeCustomGroup",
+ "GeometryNode",
+ "Geometry Custom Group",
+ "Custom Geometry Group Node for Python nodes",
+ "rna_GeometryNodeCustomGroup_register");
/* special socket types */
rna_def_cmp_output_file_slot_file(brna);
diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c
index b53318cfbfe..b339682222c 100644
--- a/source/blender/makesrna/intern/rna_object.c
+++ b/source/blender/makesrna/intern/rna_object.c
@@ -62,6 +62,8 @@
#include "WM_api.h"
#include "WM_types.h"
+#include "DEG_depsgraph_query.h"
+
const EnumPropertyItem rna_enum_object_mode_items[] = {
{OB_MODE_OBJECT, "OBJECT", ICON_OBJECT_DATAMODE, "Object Mode", ""},
{OB_MODE_EDIT, "EDIT", ICON_EDITMODE_HLT, "Edit Mode", ""},
@@ -84,7 +86,7 @@ const EnumPropertyItem rna_enum_object_mode_items[] = {
{OB_MODE_PAINT_GPENCIL,
"PAINT_GPENCIL",
ICON_GREASEPENCIL,
- "Draw",
+ "Draw Mode",
"Paint Grease Pencil Strokes"},
{OB_MODE_WEIGHT_GPENCIL,
"WEIGHT_GPENCIL",
@@ -1253,10 +1255,16 @@ static int rna_Object_rotation_4d_editable(PointerRNA *ptr, int index)
return PROP_EDITABLE;
}
+static int rna_MaterialSlot_index(PointerRNA *ptr)
+{
+ /* There is an offset of one, so that `ptr->data` is not null. */
+ return POINTER_AS_INT(ptr->data) - 1;
+}
+
static int rna_MaterialSlot_material_editable(PointerRNA *ptr, const char **UNUSED(r_info))
{
Object *ob = (Object *)ptr->owner_id;
- const int index = (Material **)ptr->data - ob->mat;
+ const int index = rna_MaterialSlot_index(ptr);
bool is_editable;
if ((ob->matbits == NULL) || ob->matbits[index]) {
@@ -1273,9 +1281,14 @@ static PointerRNA rna_MaterialSlot_material_get(PointerRNA *ptr)
{
Object *ob = (Object *)ptr->owner_id;
Material *ma;
- const int index = (Material **)ptr->data - ob->mat;
+ const int index = rna_MaterialSlot_index(ptr);
- ma = BKE_object_material_get(ob, index + 1);
+ if (DEG_is_evaluated_object(ob)) {
+ ma = BKE_object_material_get_eval(ob, index + 1);
+ }
+ else {
+ ma = BKE_object_material_get(ob, index + 1);
+ }
return rna_pointer_inherit_refine(ptr, &RNA_Material, ma);
}
@@ -1284,7 +1297,7 @@ static void rna_MaterialSlot_material_set(PointerRNA *ptr,
struct ReportList *UNUSED(reports))
{
Object *ob = (Object *)ptr->owner_id;
- int index = (Material **)ptr->data - ob->mat;
+ int index = rna_MaterialSlot_index(ptr);
BLI_assert(BKE_id_is_in_global_main(&ob->id));
BLI_assert(BKE_id_is_in_global_main(value.data));
@@ -1309,15 +1322,17 @@ static bool rna_MaterialSlot_material_poll(PointerRNA *ptr, PointerRNA value)
static int rna_MaterialSlot_link_get(PointerRNA *ptr)
{
Object *ob = (Object *)ptr->owner_id;
- int index = (Material **)ptr->data - ob->mat;
-
- return ob->matbits[index] != 0;
+ int index = rna_MaterialSlot_index(ptr);
+ if (index < ob->totcol) {
+ return ob->matbits[index] != 0;
+ }
+ return false;
}
static void rna_MaterialSlot_link_set(PointerRNA *ptr, int value)
{
Object *ob = (Object *)ptr->owner_id;
- int index = (Material **)ptr->data - ob->mat;
+ int index = rna_MaterialSlot_index(ptr);
if (value) {
ob->matbits[index] = 1;
@@ -1335,7 +1350,7 @@ static int rna_MaterialSlot_name_length(PointerRNA *ptr)
{
Object *ob = (Object *)ptr->owner_id;
Material *ma;
- int index = (Material **)ptr->data - ob->mat;
+ int index = rna_MaterialSlot_index(ptr);
ma = BKE_object_material_get(ob, index + 1);
@@ -1350,7 +1365,7 @@ static void rna_MaterialSlot_name_get(PointerRNA *ptr, char *str)
{
Object *ob = (Object *)ptr->owner_id;
Material *ma;
- int index = (Material **)ptr->data - ob->mat;
+ int index = rna_MaterialSlot_index(ptr);
ma = BKE_object_material_get(ob, index + 1);
@@ -1373,10 +1388,49 @@ static void rna_MaterialSlot_update(Main *bmain, Scene *scene, PointerRNA *ptr)
static char *rna_MaterialSlot_path(PointerRNA *ptr)
{
+ int index = rna_MaterialSlot_index(ptr);
+ return BLI_sprintfN("material_slots[%d]", index);
+}
+
+static int rna_Object_material_slots_length(PointerRNA *ptr)
+{
Object *ob = (Object *)ptr->owner_id;
- int index = (Material **)ptr->data - ob->mat;
+ if (DEG_is_evaluated_object(ob)) {
+ return BKE_object_material_count_eval(ob);
+ }
+ else {
+ return ob->totcol;
+ }
+}
- return BLI_sprintfN("material_slots[%d]", index);
+static void rna_Object_material_slots_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
+{
+ const int length = rna_Object_material_slots_length(ptr);
+ iter->internal.count.item = 0;
+ iter->internal.count.ptr = ptr->owner_id;
+ iter->valid = length > 0;
+}
+
+static void rna_Object_material_slots_next(CollectionPropertyIterator *iter)
+{
+ const int length = rna_Object_material_slots_length(&iter->ptr);
+ iter->internal.count.item++;
+ iter->valid = iter->internal.count.item < length;
+}
+
+static PointerRNA rna_Object_material_slots_get(CollectionPropertyIterator *iter)
+{
+ PointerRNA ptr;
+ RNA_pointer_create((ID *)iter->internal.count.ptr,
+ &RNA_MaterialSlot,
+ /* Add one, so that `ptr->data` is not null. */
+ POINTER_FROM_INT(iter->internal.count.item + 1),
+ &ptr);
+ return ptr;
+}
+
+static void rna_Object_material_slots_end(CollectionPropertyIterator *UNUSED(iter))
+{
}
static PointerRNA rna_Object_display_get(PointerRNA *ptr)
@@ -1448,11 +1502,6 @@ static PointerRNA rna_Object_field_get(PointerRNA *ptr)
{
Object *ob = (Object *)ptr->owner_id;
- /* weak */
- if (!ob->pd) {
- ob->pd = BKE_partdeflect_new(0);
- }
-
return rna_pointer_inherit_refine(ptr, &RNA_FieldSettings, ob->pd);
}
@@ -1464,11 +1513,6 @@ static PointerRNA rna_Object_collision_get(PointerRNA *ptr)
return PointerRNA_NULL;
}
- /* weak */
- if (!ob->pd) {
- ob->pd = BKE_partdeflect_new(0);
- }
-
return rna_pointer_inherit_refine(ptr, &RNA_CollisionSettings, ob->pd);
}
@@ -2106,6 +2150,81 @@ static void rna_object_lineart_update(Main *UNUSED(bmain), Scene *UNUSED(scene),
WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, ptr->owner_id);
}
+static bool mesh_symmetry_get_common(PointerRNA *ptr, const eMeshSymmetryType sym)
+{
+ const Object *ob = (Object *)ptr->owner_id;
+ if (ob->type != OB_MESH) {
+ return false;
+ }
+
+ const Mesh *mesh = ob->data;
+ return mesh->symmetry & sym;
+}
+
+static bool rna_Object_mesh_symmetry_x_get(PointerRNA *ptr)
+{
+ return mesh_symmetry_get_common(ptr, ME_SYMMETRY_X);
+}
+
+static bool rna_Object_mesh_symmetry_y_get(PointerRNA *ptr)
+{
+ return mesh_symmetry_get_common(ptr, ME_SYMMETRY_Y);
+}
+
+static bool rna_Object_mesh_symmetry_z_get(PointerRNA *ptr)
+{
+ return mesh_symmetry_get_common(ptr, ME_SYMMETRY_Z);
+}
+
+static void mesh_symmetry_set_common(PointerRNA *ptr,
+ const bool value,
+ const eMeshSymmetryType sym)
+{
+ Object *ob = (Object *)ptr->owner_id;
+ if (ob->type != OB_MESH) {
+ return;
+ }
+
+ Mesh *mesh = ob->data;
+ if (value) {
+ mesh->symmetry |= sym;
+ }
+ else {
+ mesh->symmetry &= ~sym;
+ }
+}
+
+static void rna_Object_mesh_symmetry_x_set(PointerRNA *ptr, bool value)
+{
+ mesh_symmetry_set_common(ptr, value, ME_SYMMETRY_X);
+}
+
+static void rna_Object_mesh_symmetry_y_set(PointerRNA *ptr, bool value)
+{
+ mesh_symmetry_set_common(ptr, value, ME_SYMMETRY_Y);
+}
+
+static void rna_Object_mesh_symmetry_z_set(PointerRNA *ptr, bool value)
+{
+ mesh_symmetry_set_common(ptr, value, ME_SYMMETRY_Z);
+}
+
+static int rna_Object_mesh_symmetry_yz_editable(PointerRNA *ptr, const char **UNUSED(r_info))
+{
+ const Object *ob = (Object *)ptr->owner_id;
+ if (ob->type != OB_MESH) {
+ return 0;
+ }
+
+ const Mesh *mesh = ob->data;
+ if (ob->mode == OB_MODE_WEIGHT_PAINT && mesh->editflag & ME_EDIT_MIRROR_VERTEX_GROUPS) {
+ /* Only X symmetry is available in weightpaint mode. */
+ return 0;
+ }
+
+ return PROP_EDITABLE;
+}
+
#else
static void rna_def_vertex_group(BlenderRNA *brna)
@@ -2798,6 +2917,7 @@ static void rna_def_object(BlenderRNA *brna)
RNA_def_property_multi_array(prop, 2, boundbox_dimsize);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_override_clear_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON);
RNA_def_property_float_funcs(prop, "rna_Object_boundbox_get", NULL, NULL);
RNA_def_property_ui_text(
prop,
@@ -2882,12 +3002,18 @@ static void rna_def_object(BlenderRNA *brna)
/* materials */
prop = RNA_def_property(srna, "material_slots", PROP_COLLECTION, PROP_NONE);
- RNA_def_property_collection_sdna(prop, NULL, "mat", "totcol");
RNA_def_property_struct_type(prop, "MaterialSlot");
RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_PROP_NAME);
- /* don't dereference pointer! */
- RNA_def_property_collection_funcs(
- prop, NULL, NULL, NULL, "rna_iterator_array_get", NULL, NULL, NULL, NULL);
+ /* Don't dereference the material slot pointer, it is the slot index encoded in a pointer. */
+ RNA_def_property_collection_funcs(prop,
+ "rna_Object_material_slots_begin",
+ "rna_Object_material_slots_next",
+ "rna_Object_material_slots_end",
+ "rna_Object_material_slots_get",
+ "rna_Object_material_slots_length",
+ NULL,
+ NULL,
+ NULL);
RNA_def_property_ui_text(prop, "Material Slots", "Material slots in the object");
prop = RNA_def_property(srna, "active_material", PROP_POINTER, PROP_NONE);
@@ -2968,12 +3094,11 @@ static void rna_def_object(BlenderRNA *brna)
RNA_def_property_float_funcs(
prop, "rna_Object_dimensions_get", "rna_Object_dimensions_set", NULL);
RNA_def_property_ui_range(prop, 0.0f, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT);
- RNA_def_property_ui_text(
- prop,
- "Dimensions",
- "Absolute bounding box dimensions of the object (WARNING: assigning to it or "
- "its members multiple consecutive times will not work correctly, "
- "as this needs up-to-date evaluated data)");
+ RNA_def_property_ui_text(prop,
+ "Dimensions",
+ "Absolute bounding box dimensions of the object.\n"
+ "Warning: Assigning to it or its members multiple consecutive times "
+ "will not work correctly, as this needs up-to-date evaluated data");
RNA_def_property_update(prop, NC_OBJECT | ND_TRANSFORM, "rna_Object_internal_update");
/* delta transforms */
@@ -3076,8 +3201,8 @@ static void rna_def_object(BlenderRNA *brna)
RNA_def_property_ui_text(
prop,
"Local Matrix",
- "Parent relative transformation matrix - "
- "WARNING: Only takes into account 'Object' parenting, so e.g. in case of bone parenting "
+ "Parent relative transformation matrix.\n"
+ "Warning: Only takes into account object parenting, so e.g. in case of bone parenting "
"you get a matrix relative to the Armature object, not to the actual parent bone");
RNA_def_property_float_funcs(
prop, "rna_Object_matrix_local_get", "rna_Object_matrix_local_set", NULL);
@@ -3499,6 +3624,28 @@ static void rna_def_object(BlenderRNA *brna)
RNA_def_property_struct_type(prop, "ObjectLineArt");
RNA_def_property_ui_text(prop, "Line Art", "Line art settings for the object");
+ /* Mesh Symmetry Settings */
+
+ prop = RNA_def_property(srna, "use_mesh_mirror_x", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_funcs(
+ prop, "rna_Object_mesh_symmetry_x_get", "rna_Object_mesh_symmetry_x_set");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "X", "Enable mesh symmetry in the X axis");
+
+ prop = RNA_def_property(srna, "use_mesh_mirror_y", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_funcs(
+ prop, "rna_Object_mesh_symmetry_y_get", "rna_Object_mesh_symmetry_y_set");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_editable_func(prop, "rna_Object_mesh_symmetry_yz_editable");
+ RNA_def_property_ui_text(prop, "Y", "Enable mesh symmetry in the Y axis");
+
+ prop = RNA_def_property(srna, "use_mesh_mirror_z", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_funcs(
+ prop, "rna_Object_mesh_symmetry_z_get", "rna_Object_mesh_symmetry_z_set");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_editable_func(prop, "rna_Object_mesh_symmetry_yz_editable");
+ RNA_def_property_ui_text(prop, "Z", "Enable mesh symmetry in the Z axis");
+
RNA_define_lib_overridable(false);
/* anim */
diff --git a/source/blender/makesrna/intern/rna_object_api.c b/source/blender/makesrna/intern/rna_object_api.c
index df628caa000..e463323c6dc 100644
--- a/source/blender/makesrna/intern/rna_object_api.c
+++ b/source/blender/makesrna/intern/rna_object_api.c
@@ -296,8 +296,8 @@ static bool rna_Object_visible_in_viewport_get(Object *ob, View3D *v3d)
static void rna_Object_mat_convert_space(Object *ob,
ReportList *reports,
bPoseChannel *pchan,
- float *mat,
- float *mat_ret,
+ float mat[16],
+ float mat_ret[16],
int from,
int to)
{
diff --git a/source/blender/makesrna/intern/rna_object_force.c b/source/blender/makesrna/intern/rna_object_force.c
index 14dd8a68fee..eb7da0d0ce2 100644
--- a/source/blender/makesrna/intern/rna_object_force.c
+++ b/source/blender/makesrna/intern/rna_object_force.c
@@ -648,6 +648,13 @@ static void rna_FieldSettings_update(Main *UNUSED(bmain), Scene *UNUSED(scene),
ob->pd->tex = NULL;
}
+ /* In the case of specific force-fields that are using the #EffectorData's normal, we need to
+ * rebuild mesh and BVH-tree for #SurfaceModifier to work correctly. */
+ if (ELEM(ob->pd->shape, PFIELD_SHAPE_SURFACE, PFIELD_SHAPE_POINTS) ||
+ ob->pd->forcefield == PFIELD_GUIDE) {
+ DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
+ }
+
DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM);
WM_main_add_notifier(NC_OBJECT | ND_DRAW, ob);
}
diff --git a/source/blender/makesrna/intern/rna_particle.c b/source/blender/makesrna/intern/rna_particle.c
index d94e68a6808..7ff2a82a465 100644
--- a/source/blender/makesrna/intern/rna_particle.c
+++ b/source/blender/makesrna/intern/rna_particle.c
@@ -1413,24 +1413,12 @@ static const EnumPropertyItem *rna_Particle_ren_as_itemf(bContext *UNUSED(C),
static PointerRNA rna_Particle_field1_get(PointerRNA *ptr)
{
ParticleSettings *part = (ParticleSettings *)ptr->owner_id;
-
- /* weak */
- if (!part->pd) {
- part->pd = BKE_partdeflect_new(0);
- }
-
return rna_pointer_inherit_refine(ptr, &RNA_FieldSettings, part->pd);
}
static PointerRNA rna_Particle_field2_get(PointerRNA *ptr)
{
ParticleSettings *part = (ParticleSettings *)ptr->owner_id;
-
- /* weak */
- if (!part->pd2) {
- part->pd2 = BKE_partdeflect_new(0);
- }
-
return rna_pointer_inherit_refine(ptr, &RNA_FieldSettings, part->pd2);
}
diff --git a/source/blender/makesrna/intern/rna_pose.c b/source/blender/makesrna/intern/rna_pose.c
index ba65e42895c..b8bb4f58dcd 100644
--- a/source/blender/makesrna/intern/rna_pose.c
+++ b/source/blender/makesrna/intern/rna_pose.c
@@ -1359,12 +1359,27 @@ static void rna_def_pose_channel(BlenderRNA *brna)
RNA_def_property_editable_func(prop, "rna_PoseChannel_proxy_editable");
RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Pose_dependency_update");
- prop = RNA_def_property(srna, "custom_shape_scale", PROP_FLOAT, PROP_NONE);
- RNA_def_property_float_sdna(prop, NULL, "custom_scale");
- RNA_def_property_range(prop, 0.0f, 1000.0f);
+ prop = RNA_def_property(srna, "custom_shape_scale_xyz", PROP_FLOAT, PROP_XYZ);
+ RNA_def_property_float_sdna(prop, NULL, "custom_scale_xyz");
+ RNA_def_property_flag(prop, PROP_PROPORTIONAL);
+ RNA_def_property_float_array_default(prop, rna_default_scale_3d);
RNA_def_property_ui_text(prop, "Custom Shape Scale", "Adjust the size of the custom shape");
RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Pose_update");
+ prop = RNA_def_property(srna, "custom_shape_translation", PROP_FLOAT, PROP_XYZ);
+ RNA_def_property_float_sdna(prop, NULL, "custom_translation");
+ RNA_def_property_flag(prop, PROP_PROPORTIONAL);
+ RNA_def_property_ui_text(
+ prop, "Custom Shape Translation", "Adjust the location of the custom shape");
+ RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT);
+ RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Pose_update");
+
+ prop = RNA_def_property(srna, "custom_shape_rotation_euler", PROP_FLOAT, PROP_EULER);
+ RNA_def_property_float_sdna(prop, NULL, "custom_rotation_euler");
+ RNA_def_property_ui_text(
+ prop, "Custom Shape Rotation", "Adjust the rotation of the custom shape");
+ RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Pose_update");
+
prop = RNA_def_property(srna, "use_custom_shape_bone_size", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_negative_sdna(prop, NULL, "drawflag", PCHAN_DRAW_NO_CUSTOM_BONE_SIZE);
RNA_def_property_ui_text(
diff --git a/source/blender/makesrna/intern/rna_pose_api.c b/source/blender/makesrna/intern/rna_pose_api.c
index 29516830058..0d35365c2d8 100644
--- a/source/blender/makesrna/intern/rna_pose_api.c
+++ b/source/blender/makesrna/intern/rna_pose_api.c
@@ -47,7 +47,7 @@
# include "BLI_ghash.h"
-static float rna_PoseBone_do_envelope(bPoseChannel *chan, float *vec)
+static float rna_PoseBone_do_envelope(bPoseChannel *chan, float vec[3])
{
Bone *bone = chan->bone;
diff --git a/source/blender/makesrna/intern/rna_render.c b/source/blender/makesrna/intern/rna_render.c
index 0411ef6d6ee..dd91a5509f5 100644
--- a/source/blender/makesrna/intern/rna_render.c
+++ b/source/blender/makesrna/intern/rna_render.c
@@ -298,13 +298,13 @@ static void rna_RenderEngine_unregister(Main *bmain, StructRNA *type)
return;
}
+ /* Stop all renders in case we were using this one. */
+ ED_render_engine_changed(bmain, false);
RE_FreeAllPersistentData();
+
RNA_struct_free_extension(type, &et->rna_ext);
RNA_struct_free(&BLENDER_RNA, type);
BLI_freelinkN(&R_engines, et);
-
- /* Stop all renders in case we were using this one. */
- ED_render_engine_changed(bmain, false);
}
static StructRNA *rna_RenderEngine_register(Main *bmain,
diff --git a/source/blender/makesrna/intern/rna_rna.c b/source/blender/makesrna/intern/rna_rna.c
index d8336e79064..9e7d0f99dfa 100644
--- a/source/blender/makesrna/intern/rna_rna.c
+++ b/source/blender/makesrna/intern/rna_rna.c
@@ -20,6 +20,8 @@
#include <stdlib.h>
+#include <CLG_log.h>
+
#include "DNA_ID.h"
#include "BLI_utildefines.h"
@@ -79,7 +81,16 @@ const EnumPropertyItem rna_enum_property_subtype_items[] = {
{PROP_PERCENTAGE, "PERCENTAGE", 0, "Percentage", ""},
{PROP_FACTOR, "FACTOR", 0, "Factor", ""},
{PROP_ANGLE, "ANGLE", 0, "Angle", ""},
- {PROP_TIME, "TIME", 0, "Time", ""},
+ {PROP_TIME,
+ "TIME",
+ 0,
+ "Time (Scene Relative)",
+ "Time specified in frames, converted to seconds based on scene frame rate"},
+ {PROP_TIME_ABSOLUTE,
+ "TIME_ABSOLUTE",
+ 0,
+ "Time (Absolute)",
+ "Time specified in seconds, independent of the scene"},
{PROP_DISTANCE, "DISTANCE", 0, "Distance", ""},
{PROP_DISTANCE_CAMERA, "DISTANCE_CAMERA", 0, "Camera Distance", ""},
{PROP_POWER, "POWER", 0, "Power", ""},
@@ -132,6 +143,8 @@ const EnumPropertyItem rna_enum_property_unit_items[] = {
# include "BKE_idprop.h"
# include "BKE_lib_override.h"
+static CLG_LogRef LOG_COMPARE_OVERRIDE = {"rna.rna_compare_override"};
+
/* Struct */
static void rna_Struct_identifier_get(PointerRNA *ptr, char *value)
@@ -1366,11 +1379,11 @@ static int rna_property_override_diff_propptr(Main *bmain,
/* In case one of the owner of the checked property is tagged as needing resync, do
* not change the 'match reference' status of its ID pointer properties overrides,
* since many non-matching ones are likely due to missing resync. */
- printf(
- "%s: Not checking matching ID pointer properties, since owner %s is tagged as "
- "needing resync.\n",
- __func__,
- id_a->name);
+ CLOG_INFO(&LOG_COMPARE_OVERRIDE,
+ 4,
+ "Not checking matching ID pointer properties, since owner %s is tagged as "
+ "needing resync.\n",
+ id_a->name);
}
else if (id_a->override_library != NULL && id_a->override_library->reference == id_b) {
opop->flag |= IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE;
diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c
index 3344b8c286d..2ccc8fe949c 100644
--- a/source/blender/makesrna/intern/rna_scene.c
+++ b/source/blender/makesrna/intern/rna_scene.c
@@ -778,7 +778,6 @@ static void rna_Scene_objects_begin(CollectionPropertyIterator *iter, PointerRNA
Scene *scene = (Scene *)ptr->data;
iter->internal.custom = MEM_callocN(sizeof(BLI_Iterator), __func__);
- ((BLI_Iterator *)iter->internal.custom)->valid = true;
BKE_scene_objects_iterator_begin(iter->internal.custom, (void *)scene);
iter->valid = ((BLI_Iterator *)iter->internal.custom)->valid;
}
@@ -1896,10 +1895,10 @@ static void rna_Scene_use_persistent_data_update(Main *UNUSED(bmain),
Scene *UNUSED(scene),
PointerRNA *ptr)
{
- Scene *sce = (Scene *)ptr->owner_id;
+ Scene *scene = (Scene *)ptr->owner_id;
- if (!(sce->r.mode & R_PERSISTENT_DATA)) {
- RE_FreePersistentData();
+ if (!(scene->r.mode & R_PERSISTENT_DATA)) {
+ RE_FreePersistentData(scene);
}
}
@@ -1913,10 +1912,9 @@ static void rna_Scene_transform_orientation_slots_begin(CollectionPropertyIterat
iter, orient_slot, sizeof(*orient_slot), ARRAY_SIZE(scene->orientation_slots), 0, NULL);
}
-static int rna_Scene_transform_orientation_slots_length(PointerRNA *ptr)
+static int rna_Scene_transform_orientation_slots_length(PointerRNA *UNUSED(ptr))
{
- Scene *scene = (Scene *)ptr->owner_id;
- return ARRAY_SIZE(scene->orientation_slots);
+ return ARRAY_SIZE(((Scene *)NULL)->orientation_slots);
}
static bool rna_Scene_use_audio_get(PointerRNA *ptr)
@@ -2484,6 +2482,10 @@ const EnumPropertyItem *rna_TransformOrientation_itemf(bContext *C,
PropertyRNA *UNUSED(prop),
bool *r_free)
{
+ if (C == NULL) {
+ return rna_enum_transform_orientation_items;
+ }
+
Scene *scene;
if (ptr->owner_id && (GS(ptr->owner_id->name) == ID_SCE)) {
scene = (Scene *)ptr->owner_id;
@@ -2494,11 +2496,15 @@ const EnumPropertyItem *rna_TransformOrientation_itemf(bContext *C,
return rna_TransformOrientation_impl_itemf(scene, false, r_free);
}
-const EnumPropertyItem *rna_TransformOrientation_with_scene_itemf(bContext *UNUSED(C),
+const EnumPropertyItem *rna_TransformOrientation_with_scene_itemf(bContext *C,
PointerRNA *ptr,
PropertyRNA *UNUSED(prop),
bool *r_free)
{
+ if (C == NULL) {
+ return rna_enum_transform_orientation_items;
+ }
+
Scene *scene = (Scene *)ptr->owner_id;
TransformOrientationSlot *orient_slot = ptr->data;
bool include_default = (orient_slot != &scene->orientation_slots[SCE_ORIENT_DEFAULT]);
@@ -2685,6 +2691,7 @@ static void rna_def_view3d_cursor(BlenderRNA *brna)
RNA_def_struct_path_func(srna, "rna_View3DCursor_path");
RNA_def_struct_ui_text(srna, "3D Cursor", "");
RNA_def_struct_ui_icon(srna, ICON_CURSOR);
+ RNA_def_struct_clear_flag(srna, STRUCT_UNDO);
prop = RNA_def_property(srna, "location", PROP_FLOAT, PROP_XYZ_LENGTH);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
@@ -2855,7 +2862,7 @@ static void rna_def_tool_settings(BlenderRNA *brna)
RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE);
RNA_def_property_boolean_sdna(prop, NULL, "auto_normalize", 1);
RNA_def_property_ui_text(prop,
- "WPaint Auto-Normalize",
+ "Weight Paint Auto-Normalize",
"Ensure all bone-deforming vertex groups add up "
"to 1.0 while weight painting");
RNA_def_property_update(prop, 0, "rna_Scene_update_active_object_data");
@@ -2864,7 +2871,7 @@ static void rna_def_tool_settings(BlenderRNA *brna)
RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE);
RNA_def_property_boolean_sdna(prop, NULL, "wpaint_lock_relative", 1);
RNA_def_property_ui_text(prop,
- "WPaint Lock-Relative",
+ "Weight Paint Lock-Relative",
"Display bone-deforming groups as if all locked deform groups "
"were deleted, and the remaining ones were re-normalized");
RNA_def_property_update(prop, 0, "rna_Scene_update_active_object_data");
@@ -2873,7 +2880,7 @@ static void rna_def_tool_settings(BlenderRNA *brna)
RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE);
RNA_def_property_boolean_sdna(prop, NULL, "multipaint", 1);
RNA_def_property_ui_text(prop,
- "WPaint Multi-Paint",
+ "Weight Paint Multi-Paint",
"Paint across the weights of all selected bones, "
"maintaining their relative influence");
RNA_def_property_update(prop, 0, "rna_Scene_update_active_object_data");
@@ -6605,8 +6612,10 @@ static void rna_def_scene_render_data(BlenderRNA *brna)
/* persistent data */
prop = RNA_def_property(srna, "use_persistent_data", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "mode", R_PERSISTENT_DATA);
- RNA_def_property_ui_text(
- prop, "Persistent Data", "Keep render data around for faster re-renders");
+ RNA_def_property_ui_text(prop,
+ "Persistent Data",
+ "Keep render data around for faster re-renders and animation renders, "
+ "at the cost of increased memory usage");
RNA_def_property_update(prop, 0, "rna_Scene_use_persistent_data_update");
/* Freestyle line thickness options */
diff --git a/source/blender/makesrna/intern/rna_scene_api.c b/source/blender/makesrna/intern/rna_scene_api.c
index c2089004da2..c49b41867a8 100644
--- a/source/blender/makesrna/intern/rna_scene_api.c
+++ b/source/blender/makesrna/intern/rna_scene_api.c
@@ -98,7 +98,7 @@ static void rna_Scene_frame_set(Scene *scene, Main *bmain, int frame, float subf
}
}
-static void rna_Scene_uvedit_aspect(Scene *UNUSED(scene), Object *ob, float *aspect)
+static void rna_Scene_uvedit_aspect(Scene *UNUSED(scene), Object *ob, float aspect[2])
{
if ((ob->type == OB_MESH) && (ob->mode == OB_MODE_EDIT)) {
BMEditMesh *em;
diff --git a/source/blender/makesrna/intern/rna_screen.c b/source/blender/makesrna/intern/rna_screen.c
index 6cf1d7a923b..fba1d3cda9a 100644
--- a/source/blender/makesrna/intern/rna_screen.c
+++ b/source/blender/makesrna/intern/rna_screen.c
@@ -112,18 +112,6 @@ static bool rna_Screen_fullscreen_get(PointerRNA *ptr)
return (screen->state == SCREENMAXIMIZED);
}
-/* UI compatible list: should not be needed, but for now we need to keep EMPTY
- * at least in the static version of this enum for python scripts. */
-static const EnumPropertyItem *rna_Area_type_itemf(bContext *UNUSED(C),
- PointerRNA *UNUSED(ptr),
- PropertyRNA *UNUSED(prop),
- bool *r_free)
-{
- /* +1 to skip SPACE_EMPTY */
- *r_free = false;
- return rna_enum_space_type_items + 1;
-}
-
static int rna_Area_type_get(PointerRNA *ptr)
{
ScrArea *area = (ScrArea *)ptr->data;
@@ -142,6 +130,11 @@ static void rna_Area_type_set(PointerRNA *ptr, int value)
}
ScrArea *area = (ScrArea *)ptr->data;
+ /* Empty areas are locked. */
+ if ((value == SPACE_EMPTY) || (area->spacetype == SPACE_EMPTY)) {
+ return;
+ }
+
area->butspacetype = value;
}
@@ -188,16 +181,20 @@ static void rna_Area_type_update(bContext *C, PointerRNA *ptr)
}
static const EnumPropertyItem *rna_Area_ui_type_itemf(bContext *C,
- PointerRNA *UNUSED(ptr),
+ PointerRNA *ptr,
PropertyRNA *UNUSED(prop),
bool *r_free)
{
EnumPropertyItem *item = NULL;
int totitem = 0;
- /* +1 to skip SPACE_EMPTY */
- for (const EnumPropertyItem *item_from = rna_enum_space_type_items + 1; item_from->identifier;
- item_from++) {
+ ScrArea *area = (ScrArea *)ptr->data;
+ const EnumPropertyItem *item_from = rna_enum_space_type_items;
+ if (area->spacetype != SPACE_EMPTY) {
+ item_from += 1; /* +1 to skip SPACE_EMPTY */
+ }
+
+ for (; item_from->identifier; item_from++) {
if (ELEM(item_from->value, SPACE_TOPBAR, SPACE_STATUSBAR)) {
continue;
}
@@ -224,6 +221,10 @@ static const EnumPropertyItem *rna_Area_ui_type_itemf(bContext *C,
static int rna_Area_ui_type_get(PointerRNA *ptr)
{
ScrArea *area = ptr->data;
+ /* This is for the Python API which may inspect empty areas. */
+ if (UNLIKELY(area->spacetype == SPACE_EMPTY)) {
+ return SPACE_EMPTY;
+ }
const int area_type = rna_Area_type_get(ptr);
const bool area_changing = area->butspacetype != SPACE_EMPTY;
int value = area_type << 16;
@@ -252,6 +253,10 @@ static void rna_Area_ui_type_set(PointerRNA *ptr, int value)
{
ScrArea *area = ptr->data;
const int space_type = value >> 16;
+ /* Empty areas are locked. */
+ if ((space_type == SPACE_EMPTY) || (area->spacetype == SPACE_EMPTY)) {
+ return;
+ }
SpaceType *st = BKE_spacetype_from_id(space_type);
rna_Area_type_set(ptr, space_type);
@@ -380,12 +385,17 @@ static void rna_def_area(BlenderRNA *brna)
RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", HEADER_NO_PULLDOWN);
RNA_def_property_ui_text(prop, "Show Menus", "Show menus in the header");
+ /* Note on space type use of #SPACE_EMPTY, this is not visible to the user,
+ * and script authors should be able to assign this value, however the value may be set
+ * and needs to be read back by script authors.
+ *
+ * This happens when an area is full-screen (when #ScrArea.full is set).
+ * in this case reading the empty value is needed, but it should never be set, see: T87187. */
prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "spacetype");
RNA_def_property_enum_items(prop, rna_enum_space_type_items);
RNA_def_property_enum_default(prop, SPACE_VIEW3D);
- RNA_def_property_enum_funcs(
- prop, "rna_Area_type_get", "rna_Area_type_set", "rna_Area_type_itemf");
+ RNA_def_property_enum_funcs(prop, "rna_Area_type_get", "rna_Area_type_set", NULL);
RNA_def_property_ui_text(prop, "Editor Type", "Current editor type for this area");
RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c
index 4481555b931..81acedb76f2 100644
--- a/source/blender/makesrna/intern/rna_sculpt_paint.c
+++ b/source/blender/makesrna/intern/rna_sculpt_paint.c
@@ -786,7 +786,8 @@ static void rna_def_sculpt(BlenderRNA *brna)
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Sculpt_ShowMask_update");
prop = RNA_def_property(srna, "detail_size", PROP_FLOAT, PROP_PIXEL);
- RNA_def_property_ui_range(prop, 0.5, 40.0, 10, 2);
+ RNA_def_property_ui_range(prop, 0.5, 40.0, 0.1, 2);
+ RNA_def_property_ui_scale_type(prop, PROP_SCALE_CUBIC);
RNA_def_property_ui_text(
prop, "Detail Size", "Maximum edge length for dynamic topology sculpting (in pixels)");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c
index 24d051fecc8..2652003cb1c 100644
--- a/source/blender/makesrna/intern/rna_sequencer.c
+++ b/source/blender/makesrna/intern/rna_sequencer.c
@@ -293,7 +293,7 @@ static void do_sequence_frame_change_update(Scene *scene, Sequence *seq)
if (SEQ_transform_test_overlap(seqbase, seq)) {
SEQ_transform_seqbase_shuffle(seqbase, seq, scene); /* XXX - BROKEN!, uses context seqbasep */
}
- SEQ_sort(scene);
+ SEQ_sort(seqbase);
}
/* A simple wrapper around above func, directly usable as prop update func.
@@ -440,8 +440,7 @@ static void rna_Sequence_frame_length_set(PointerRNA *ptr, int value)
Scene *scene = (Scene *)ptr->owner_id;
SEQ_relations_invalidate_cache_composite(scene, seq);
- SEQ_transform_set_right_handle_frame(seq,
- SEQ_transform_get_left_handle_frame(seq, false) + value);
+ SEQ_transform_set_right_handle_frame(seq, SEQ_transform_get_left_handle_frame(seq) + value);
do_sequence_frame_change_update(scene, seq);
SEQ_relations_invalidate_cache_composite(scene, seq);
}
@@ -449,8 +448,7 @@ static void rna_Sequence_frame_length_set(PointerRNA *ptr, int value)
static int rna_Sequence_frame_length_get(PointerRNA *ptr)
{
Sequence *seq = (Sequence *)ptr->data;
- return SEQ_transform_get_right_handle_frame(seq, false) -
- SEQ_transform_get_left_handle_frame(seq, false);
+ return SEQ_transform_get_right_handle_frame(seq) - SEQ_transform_get_left_handle_frame(seq);
}
static int rna_Sequence_frame_editable(PointerRNA *ptr, const char **UNUSED(r_info))
@@ -476,7 +474,7 @@ static void rna_Sequence_channel_set(PointerRNA *ptr, int value)
/* XXX - BROKEN!, uses context seqbasep */
SEQ_transform_seqbase_shuffle_ex(seqbase, seq, scene, channel_delta);
}
- SEQ_sort(scene);
+ SEQ_sort(seqbase);
SEQ_relations_invalidate_cache_composite(scene, seq);
}
@@ -505,7 +503,7 @@ static Sequence *sequence_get_by_transform(Editing *ed, StripTransform *transfor
data.data = transform;
/* irritating we need to search for our sequence! */
- SEQ_iterator_seqbase_recursive_apply(&ed->seqbase, transform_seq_cmp_fn, &data);
+ SEQ_seqbase_recursive_apply(&ed->seqbase, transform_seq_cmp_fn, &data);
return data.seq;
}
@@ -557,7 +555,7 @@ static Sequence *sequence_get_by_crop(Editing *ed, StripCrop *crop)
data.data = crop;
/* irritating we need to search for our sequence! */
- SEQ_iterator_seqbase_recursive_apply(&ed->seqbase, crop_seq_cmp_fn, &data);
+ SEQ_seqbase_recursive_apply(&ed->seqbase, crop_seq_cmp_fn, &data);
return data.seq;
}
@@ -951,7 +949,7 @@ static Sequence *sequence_get_by_proxy(Editing *ed, StripProxy *proxy)
data.seq = NULL;
data.data = proxy;
- SEQ_iterator_seqbase_recursive_apply(&ed->seqbase, seqproxy_seq_cmp_fn, &data);
+ SEQ_seqbase_recursive_apply(&ed->seqbase, seqproxy_seq_cmp_fn, &data);
return data.seq;
}
@@ -990,18 +988,14 @@ static int colbalance_seq_cmp_fn(Sequence *seq, void *arg_pt)
{
SequenceSearchData *data = arg_pt;
- if (seq->modifiers.first) {
- SequenceModifierData *smd = seq->modifiers.first;
+ for (SequenceModifierData *smd = seq->modifiers.first; smd; smd = smd->next) {
+ if (smd->type == seqModifierType_ColorBalance) {
+ ColorBalanceModifierData *cbmd = (ColorBalanceModifierData *)smd;
- for (smd = seq->modifiers.first; smd; smd = smd->next) {
- if (smd->type == seqModifierType_ColorBalance) {
- ColorBalanceModifierData *cbmd = (ColorBalanceModifierData *)smd;
-
- if (&cbmd->color_balance == data->data) {
- data->seq = seq;
- data->smd = smd;
- return -1; /* done so bail out */
- }
+ if (&cbmd->color_balance == data->data) {
+ data->seq = seq;
+ data->smd = smd;
+ return -1; /* done so bail out */
}
}
}
@@ -1020,7 +1014,7 @@ static Sequence *sequence_get_by_colorbalance(Editing *ed,
data.data = cb;
/* irritating we need to search for our sequence! */
- SEQ_iterator_seqbase_recursive_apply(&ed->seqbase, colbalance_seq_cmp_fn, &data);
+ SEQ_seqbase_recursive_apply(&ed->seqbase, colbalance_seq_cmp_fn, &data);
*r_smd = data.smd;
@@ -1144,7 +1138,7 @@ static Sequence *sequence_get_by_modifier(Editing *ed, SequenceModifierData *smd
data.data = smd;
/* irritating we need to search for our sequence! */
- SEQ_iterator_seqbase_recursive_apply(&ed->seqbase, modifier_seq_cmp_fn, &data);
+ SEQ_seqbase_recursive_apply(&ed->seqbase, modifier_seq_cmp_fn, &data);
return data.seq;
}
@@ -1350,6 +1344,11 @@ static void rna_def_strip_element(BlenderRNA *brna)
RNA_def_property_int_sdna(prop, NULL, "orig_height");
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Orig Height", "Original image height");
+
+ prop = RNA_def_property(srna, "orig_fps", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "orig_fps");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Orig FPS", "Original frames per second");
}
static void rna_def_strip_crop(BlenderRNA *brna)
@@ -1517,7 +1516,7 @@ static void rna_def_strip_proxy(BlenderRNA *brna)
prop = RNA_def_property(srna, "quality", PROP_INT, PROP_UNSIGNED);
RNA_def_property_int_sdna(prop, NULL, "quality");
- RNA_def_property_ui_text(prop, "Quality", "JPEG Quality of proxies to build");
+ RNA_def_property_ui_text(prop, "Quality", "Quality of proxies to build");
RNA_def_property_ui_range(prop, 1, 100, 1, -1);
prop = RNA_def_property(srna, "timecode", PROP_ENUM, PROP_NONE);
@@ -2394,11 +2393,10 @@ static void rna_def_scene(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Input", "Input type to use for the Scene strip");
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_use_sequence");
- prop = RNA_def_property(srna, "use_grease_pencil", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", SEQ_SCENE_NO_GPENCIL);
- RNA_def_property_ui_text(
- prop, "Use Grease Pencil", "Show Grease Pencil strokes in OpenGL previews");
- RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, NULL);
+ prop = RNA_def_property(srna, "use_annotations", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", SEQ_SCENE_NO_ANNOTATION);
+ RNA_def_property_ui_text(prop, "Use Annotations", "Show Annotations in OpenGL previews");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
rna_def_filter_video(srna);
rna_def_proxy(srna);
@@ -2417,12 +2415,6 @@ static void rna_def_movie(BlenderRNA *brna)
RNA_def_struct_ui_text(srna, "Movie Sequence", "Sequence strip to load a video");
RNA_def_struct_sdna(srna, "Sequence");
- prop = RNA_def_property(srna, "mpeg_preseek", PROP_INT, PROP_NONE);
- RNA_def_property_int_sdna(prop, NULL, "anim_preseek");
- RNA_def_property_range(prop, 0, 50);
- RNA_def_property_ui_text(prop, "MPEG Preseek", "For MPEG movies, preseek this many frames");
- RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, NULL);
-
prop = RNA_def_property(srna, "stream_index", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "streamindex");
RNA_def_property_range(prop, 0, 20);
@@ -2930,7 +2922,7 @@ static void rna_def_text(StructRNA *srna)
prop = RNA_def_property(srna, "use_box", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_TEXT_BOX);
- RNA_def_property_ui_text(prop, "Shadow", "Display colored box behind text");
+ RNA_def_property_ui_text(prop, "Box", "Display colored box behind text");
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update");
prop = RNA_def_property(srna, "use_bold", PROP_BOOLEAN, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_sequencer_api.c b/source/blender/makesrna/intern/rna_sequencer_api.c
index a49a404fe6c..8aab0c079a3 100644
--- a/source/blender/makesrna/intern/rna_sequencer_api.c
+++ b/source/blender/makesrna/intern/rna_sequencer_api.c
@@ -224,13 +224,15 @@ static Sequence *rna_Sequences_new_image(ID *id,
const char *name,
const char *file,
int channel,
- int frame_start)
+ int frame_start,
+ int fit_method)
{
Scene *scene = (Scene *)id;
SeqLoadData load_data;
SEQ_add_load_data_init(&load_data, name, file, frame_start, channel);
load_data.image.len = 1;
+ load_data.fit_method = fit_method;
Sequence *seq = SEQ_add_image_strip(bmain, scene, seqbase, &load_data);
char dir[FILE_MAX], filename[FILE_MAX];
@@ -253,10 +255,11 @@ static Sequence *rna_Sequences_editing_new_image(ID *id,
const char *name,
const char *file,
int channel,
- int frame_start)
+ int frame_start,
+ int fit_method)
{
return rna_Sequences_new_image(
- id, &ed->seqbase, bmain, reports, name, file, channel, frame_start);
+ id, &ed->seqbase, bmain, reports, name, file, channel, frame_start, fit_method);
}
static Sequence *rna_Sequences_meta_new_image(ID *id,
@@ -266,10 +269,11 @@ static Sequence *rna_Sequences_meta_new_image(ID *id,
const char *name,
const char *file,
int channel,
- int frame_start)
+ int frame_start,
+ int fit_method)
{
return rna_Sequences_new_image(
- id, &seq->seqbase, bmain, reports, name, file, channel, frame_start);
+ id, &seq->seqbase, bmain, reports, name, file, channel, frame_start, fit_method);
}
static Sequence *rna_Sequences_new_movie(ID *id,
@@ -278,11 +282,13 @@ static Sequence *rna_Sequences_new_movie(ID *id,
const char *name,
const char *file,
int channel,
- int frame_start)
+ int frame_start,
+ int fit_method)
{
Scene *scene = (Scene *)id;
SeqLoadData load_data;
SEQ_add_load_data_init(&load_data, name, file, frame_start, channel);
+ load_data.fit_method = fit_method;
load_data.allow_invalid_file = true;
Sequence *seq = SEQ_add_movie_strip(bmain, scene, seqbase, &load_data);
@@ -299,9 +305,11 @@ static Sequence *rna_Sequences_editing_new_movie(ID *id,
const char *name,
const char *file,
int channel,
- int frame_start)
+ int frame_start,
+ int fit_method)
{
- return rna_Sequences_new_movie(id, &ed->seqbase, bmain, name, file, channel, frame_start);
+ return rna_Sequences_new_movie(
+ id, &ed->seqbase, bmain, name, file, channel, frame_start, fit_method);
}
static Sequence *rna_Sequences_meta_new_movie(ID *id,
@@ -310,9 +318,11 @@ static Sequence *rna_Sequences_meta_new_movie(ID *id,
const char *name,
const char *file,
int channel,
- int frame_start)
+ int frame_start,
+ int fit_method)
{
- return rna_Sequences_new_movie(id, &seq->seqbase, bmain, name, file, channel, frame_start);
+ return rna_Sequences_new_movie(
+ id, &seq->seqbase, bmain, name, file, channel, frame_start, fit_method);
}
# ifdef WITH_AUDASPACE
@@ -724,6 +734,18 @@ void RNA_api_sequences(BlenderRNA *brna, PropertyRNA *cprop, const bool metastri
{0, NULL, 0, NULL, NULL},
};
+ static const EnumPropertyItem scale_fit_methods[] = {
+ {SEQ_SCALE_TO_FIT, "FIT", 0, "Scale to Fit", "Scale image so fits in preview"},
+ {SEQ_SCALE_TO_FILL,
+ "FILL",
+ 0,
+ "Scale to Fill",
+ "Scale image so it fills preview completely"},
+ {SEQ_STRETCH_TO_FILL, "STRETCH", 0, "Stretch to Fill", "Stretch image so it fills preview"},
+ {SEQ_USE_ORIGINAL_SIZE, "ORIGINAL", 0, "Use Original Size", "Don't scale the image"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
const char *new_clip_func_name = "rna_Sequences_editing_new_clip";
const char *new_mask_func_name = "rna_Sequences_editing_new_mask";
const char *new_scene_func_name = "rna_Sequences_editing_new_scene";
@@ -849,6 +871,9 @@ void RNA_api_sequences(BlenderRNA *brna, PropertyRNA *cprop, const bool metastri
-MAXFRAME,
MAXFRAME);
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_enum(
+ func, "fit_method", scale_fit_methods, SEQ_USE_ORIGINAL_SIZE, "Image Fit Method", NULL);
+ RNA_def_parameter_flags(parm, 0, PARM_PYFUNC_OPTIONAL);
/* return type */
parm = RNA_def_pointer(func, "sequence", "Sequence", "", "New Sequence");
RNA_def_function_return(func, parm);
@@ -873,6 +898,9 @@ void RNA_api_sequences(BlenderRNA *brna, PropertyRNA *cprop, const bool metastri
-MAXFRAME,
MAXFRAME);
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_enum(
+ func, "fit_method", scale_fit_methods, SEQ_USE_ORIGINAL_SIZE, "Image Fit Method", NULL);
+ RNA_def_parameter_flags(parm, 0, PARM_PYFUNC_OPTIONAL);
/* return type */
parm = RNA_def_pointer(func, "sequence", "Sequence", "", "New Sequence");
RNA_def_function_return(func, parm);
diff --git a/source/blender/makesrna/intern/rna_shader_fx.c b/source/blender/makesrna/intern/rna_shader_fx.c
index 451cc56eba7..4a6cb0d740a 100644
--- a/source/blender/makesrna/intern/rna_shader_fx.c
+++ b/source/blender/makesrna/intern/rna_shader_fx.c
@@ -216,6 +216,8 @@ static void rna_def_shader_fx_blur(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "BlurShaderFxData");
RNA_def_struct_ui_icon(srna, ICON_SHADERFX);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "size", PROP_FLOAT, PROP_XYZ);
RNA_def_property_float_sdna(prop, NULL, "radius");
RNA_def_property_range(prop, 0.0f, FLT_MAX);
@@ -240,6 +242,8 @@ static void rna_def_shader_fx_blur(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "flag", FX_BLUR_DOF_MODE);
RNA_def_property_ui_text(prop, "Use as Depth Of Field", "Blur using camera depth of field");
RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_shader_fx_colorize(BlenderRNA *brna)
@@ -252,6 +256,8 @@ static void rna_def_shader_fx_colorize(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "ColorizeShaderFxData");
RNA_def_struct_ui_icon(srna, ICON_SHADERFX);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "factor", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "factor");
RNA_def_property_range(prop, 0, 1.0);
@@ -277,6 +283,8 @@ static void rna_def_shader_fx_colorize(BlenderRNA *brna)
RNA_def_property_enum_items(prop, rna_enum_shaderfx_colorize_modes_items);
RNA_def_property_ui_text(prop, "Mode", "Effect mode");
RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_shader_fx_wave(BlenderRNA *brna)
@@ -294,6 +302,8 @@ static void rna_def_shader_fx_wave(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "WaveShaderFxData");
RNA_def_struct_ui_icon(srna, ICON_SHADERFX);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "orientation", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "orientation");
RNA_def_property_enum_items(prop, prop_shaderfx_wave_type_items);
@@ -317,6 +327,8 @@ static void rna_def_shader_fx_wave(BlenderRNA *brna)
RNA_def_property_range(prop, -FLT_MAX, FLT_MAX);
RNA_def_property_ui_text(prop, "Phase", "Phase Shift of Wave");
RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_shader_fx_pixel(BlenderRNA *brna)
@@ -329,6 +341,8 @@ static void rna_def_shader_fx_pixel(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "PixelShaderFxData");
RNA_def_struct_ui_icon(srna, ICON_SHADERFX);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "size", PROP_INT, PROP_PIXEL);
RNA_def_property_int_sdna(prop, NULL, "size");
RNA_def_property_range(prop, 1, SHRT_MAX);
@@ -340,6 +354,8 @@ static void rna_def_shader_fx_pixel(BlenderRNA *brna)
RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", FX_PIXEL_FILTER_NEAREST);
RNA_def_property_ui_text(prop, "Antialiasing", "Antialias pixels");
RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_shader_fx_rim(BlenderRNA *brna)
@@ -352,6 +368,8 @@ static void rna_def_shader_fx_rim(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "RimShaderFxData");
RNA_def_struct_ui_icon(srna, ICON_SHADERFX);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "offset", PROP_INT, PROP_PIXEL);
RNA_def_property_int_sdna(prop, NULL, "offset");
RNA_def_property_range(prop, SHRT_MIN, SHRT_MAX);
@@ -392,6 +410,8 @@ static void rna_def_shader_fx_rim(BlenderRNA *brna)
RNA_def_property_int_default(prop, 4);
RNA_def_property_ui_text(prop, "Samples", "Number of Blur Samples (zero, disable blur)");
RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_shader_fx_shadow(BlenderRNA *brna)
@@ -409,11 +429,12 @@ static void rna_def_shader_fx_shadow(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "ShadowShaderFxData");
RNA_def_struct_ui_icon(srna, ICON_SHADERFX);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE);
RNA_def_property_ui_text(prop, "Object", "Object to determine center of rotation");
RNA_def_property_pointer_funcs(prop, NULL, "rna_ShadowShaderFx_object_set", NULL, NULL);
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_update(prop, 0, "rna_ShaderFx_dependency_update");
prop = RNA_def_property(srna, "offset", PROP_INT, PROP_PIXEL);
@@ -490,6 +511,8 @@ static void rna_def_shader_fx_shadow(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "flag", FX_SHADOW_USE_WAVE);
RNA_def_property_ui_text(prop, "Wave", "Use wave effect");
RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_shader_fx_glow(BlenderRNA *brna)
@@ -502,6 +525,8 @@ static void rna_def_shader_fx_glow(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "GlowShaderFxData");
RNA_def_struct_ui_icon(srna, ICON_SHADERFX);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "glow_color", PROP_FLOAT, PROP_COLOR);
RNA_def_property_range(prop, 0.0, 1.0);
RNA_def_property_float_sdna(prop, NULL, "glow_color");
@@ -569,6 +594,8 @@ static void rna_def_shader_fx_glow(BlenderRNA *brna)
RNA_def_property_enum_items(prop, rna_enum_glow_blend_modes_items);
RNA_def_property_ui_text(prop, "Blend Mode", "Blend mode");
RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_shader_fx_swirl(BlenderRNA *brna)
@@ -581,6 +608,8 @@ static void rna_def_shader_fx_swirl(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "SwirlShaderFxData");
RNA_def_struct_ui_icon(srna, ICON_SHADERFX);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "radius", PROP_INT, PROP_PIXEL);
RNA_def_property_int_sdna(prop, NULL, "radius");
RNA_def_property_range(prop, 0, SHRT_MAX);
@@ -603,8 +632,9 @@ static void rna_def_shader_fx_swirl(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Object", "Object to determine center location");
RNA_def_property_pointer_funcs(prop, NULL, "rna_SwirlShaderFx_object_set", NULL, NULL);
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
- RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_update(prop, 0, "rna_ShaderFx_dependency_update");
+
+ RNA_define_lib_overridable(false);
}
static void rna_def_shader_fx_flip(BlenderRNA *brna)
@@ -617,6 +647,8 @@ static void rna_def_shader_fx_flip(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "FlipShaderFxData");
RNA_def_struct_ui_icon(srna, ICON_SHADERFX);
+ RNA_define_lib_overridable(true);
+
prop = RNA_def_property(srna, "flip_horizontal", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FX_FLIP_HORIZONTAL);
RNA_def_property_ui_text(prop, "Horizontal", "Flip image horizontally");
@@ -626,6 +658,8 @@ static void rna_def_shader_fx_flip(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "flag", FX_FLIP_VERTICAL);
RNA_def_property_ui_text(prop, "Vertical", "Flip image vertically");
RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update");
+
+ RNA_define_lib_overridable(false);
}
void RNA_def_shader_fx(BlenderRNA *brna)
diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c
index f9b1816e1ba..39edc6c3b9a 100644
--- a/source/blender/makesrna/intern/rna_space.c
+++ b/source/blender/makesrna/intern/rna_space.c
@@ -34,6 +34,7 @@
#include "BKE_node.h"
#include "BKE_studiolight.h"
+#include "ED_spreadsheet.h"
#include "ED_text.h"
#include "BLI_listbase.h"
@@ -1673,7 +1674,7 @@ static void rna_SpaceImageEditor_zoom_get(PointerRNA *ptr, float *values)
values[0] = values[1] = 1;
- /* find aregion */
+ /* Find #ARegion. */
area = rna_area_from_space(ptr); /* can be NULL */
region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW);
if (region) {
@@ -1793,7 +1794,13 @@ static void rna_SpaceTextEditor_text_set(PointerRNA *ptr,
st->text = value.data;
- WM_main_add_notifier(NC_TEXT | NA_SELECTED, st->text);
+ ScrArea *area = rna_area_from_space(ptr);
+ if (area) {
+ ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW);
+ if (region) {
+ ED_text_scroll_to_cursor(st, region, true);
+ }
+ }
}
static bool rna_SpaceTextEditor_text_is_syntax_highlight_supported(struct SpaceText *space)
@@ -3031,14 +3038,6 @@ static void rna_SpaceFileBrowser_browse_mode_update(Main *UNUSED(bmain),
ED_area_tag_refresh(area);
}
-static void rna_SpaceSpreadsheet_pinned_id_set(PointerRNA *ptr,
- PointerRNA value,
- struct ReportList *UNUSED(reports))
-{
- SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)ptr->data;
- sspreadsheet->pinned_id = value.data;
-}
-
static void rna_SpaceSpreadsheet_geometry_component_type_update(Main *UNUSED(bmain),
Scene *UNUSED(scene),
PointerRNA *ptr)
@@ -3047,9 +3046,13 @@ static void rna_SpaceSpreadsheet_geometry_component_type_update(Main *UNUSED(bma
if (sspreadsheet->geometry_component_type == GEO_COMPONENT_TYPE_POINT_CLOUD) {
sspreadsheet->attribute_domain = ATTR_DOMAIN_POINT;
}
+ if (sspreadsheet->geometry_component_type == GEO_COMPONENT_TYPE_CURVE &&
+ !ELEM(sspreadsheet->attribute_domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE)) {
+ sspreadsheet->attribute_domain = ATTR_DOMAIN_POINT;
+ }
}
-const EnumPropertyItem *rna_SpaceSpreadsheet_attribute_domain_itemf(bContext *C,
+const EnumPropertyItem *rna_SpaceSpreadsheet_attribute_domain_itemf(bContext *UNUSED(C),
PointerRNA *ptr,
PropertyRNA *UNUSED(prop),
bool *r_free)
@@ -3057,20 +3060,23 @@ const EnumPropertyItem *rna_SpaceSpreadsheet_attribute_domain_itemf(bContext *C,
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)ptr->data;
GeometryComponentType component_type = sspreadsheet->geometry_component_type;
if (sspreadsheet->object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_ORIGINAL) {
- Object *active_object = CTX_data_active_object(C);
- Object *used_object = (sspreadsheet->pinned_id && GS(sspreadsheet->pinned_id->name) == ID_OB) ?
- (Object *)sspreadsheet->pinned_id :
- active_object;
- if (used_object != NULL) {
- if (used_object->type == OB_POINTCLOUD) {
- component_type = GEO_COMPONENT_TYPE_POINT_CLOUD;
- }
- else {
- component_type = GEO_COMPONENT_TYPE_MESH;
+ ID *used_id = ED_spreadsheet_get_current_id(sspreadsheet);
+ if (used_id != NULL) {
+ if (GS(used_id->name) == ID_OB) {
+ Object *used_object = (Object *)used_id;
+ if (used_object->type == OB_POINTCLOUD) {
+ component_type = GEO_COMPONENT_TYPE_POINT_CLOUD;
+ }
+ else {
+ component_type = GEO_COMPONENT_TYPE_MESH;
+ }
}
}
}
+ static EnumPropertyItem mesh_vertex_domain_item = {
+ ATTR_DOMAIN_POINT, "POINT", 0, "Vertex", "Attribute per point/vertex"};
+
EnumPropertyItem *item_array = NULL;
int items_len = 0;
for (const EnumPropertyItem *item = rna_enum_attribute_domain_items; item->identifier != NULL;
@@ -3089,7 +3095,17 @@ const EnumPropertyItem *rna_SpaceSpreadsheet_attribute_domain_itemf(bContext *C,
continue;
}
}
- RNA_enum_item_add(&item_array, &items_len, item);
+ if (component_type == GEO_COMPONENT_TYPE_CURVE) {
+ if (!ELEM(item->value, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE)) {
+ continue;
+ }
+ }
+ if (item->value == ATTR_DOMAIN_POINT && component_type == GEO_COMPONENT_TYPE_MESH) {
+ RNA_enum_item_add(&item_array, &items_len, &mesh_vertex_domain_item);
+ }
+ else {
+ RNA_enum_item_add(&item_array, &items_len, item);
+ }
}
RNA_enum_item_end(&item_array, &items_len);
@@ -3097,6 +3113,61 @@ const EnumPropertyItem *rna_SpaceSpreadsheet_attribute_domain_itemf(bContext *C,
return item_array;
}
+static SpreadsheetContext *rna_SpaceSpreadsheet_context_path_append(SpaceSpreadsheet *sspreadsheet,
+ int type)
+{
+ SpreadsheetContext *context = ED_spreadsheet_context_new(type);
+ BLI_addtail(&sspreadsheet->context_path, context);
+ ED_spreadsheet_context_path_update_tag(sspreadsheet);
+ WM_main_add_notifier(NC_SPACE | ND_SPACE_SPREADSHEET, NULL);
+ return context;
+}
+
+static void rna_SpaceSpreadsheet_context_path_clear(SpaceSpreadsheet *sspreadsheet)
+{
+ ED_spreadsheet_context_path_clear(sspreadsheet);
+ ED_spreadsheet_context_path_update_tag(sspreadsheet);
+ WM_main_add_notifier(NC_SPACE | ND_SPACE_SPREADSHEET, NULL);
+}
+
+static StructRNA *rna_spreadsheet_context_refine(PointerRNA *ptr)
+{
+ SpreadsheetContext *context = ptr->data;
+ switch (context->type) {
+ case SPREADSHEET_CONTEXT_OBJECT:
+ return &RNA_SpreadsheetContextObject;
+ case SPREADSHEET_CONTEXT_MODIFIER:
+ return &RNA_SpreadsheetContextModifier;
+ case SPREADSHEET_CONTEXT_NODE:
+ return &RNA_SpreadsheetContextNode;
+ }
+ BLI_assert_unreachable();
+ return NULL;
+}
+
+static void rna_spreadsheet_context_update(Main *UNUSED(bmain),
+ Scene *UNUSED(scene),
+ PointerRNA *ptr)
+{
+ bScreen *screen = (bScreen *)ptr->owner_id;
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
+ SpaceLink *sl = area->spacedata.first;
+ if (sl->spacetype == SPACE_SPREADSHEET) {
+ SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl;
+ ED_spreadsheet_context_path_update_tag(sspreadsheet);
+ }
+ }
+}
+
+static void rna_spreadsheet_set_geometry_node_context(SpaceSpreadsheet *sspreadsheet,
+ SpaceNode *snode,
+ bNode *node)
+{
+ ED_spreadsheet_set_geometry_node_context(sspreadsheet, snode, node);
+ ED_spreadsheet_context_path_update_tag(sspreadsheet);
+ WM_main_add_notifier(NC_SPACE | ND_SPACE_SPREADSHEET, NULL);
+}
+
#else
static const EnumPropertyItem dt_uv_items[] = {
@@ -3399,6 +3470,11 @@ static void rna_def_space_outliner(BlenderRNA *brna)
ICON_RNA,
"Data API",
"Display low level Blender data and its properties"},
+ {SO_OVERRIDES_LIBRARY,
+ "LIBRARY_OVERRIDES",
+ ICON_LIBRARY_DATA_OVERRIDE,
+ "Library Overrides",
+ "Display data-blocks with library overrides and list their overridden properties"},
{SO_ID_ORPHANS,
"ORPHAN_DATA",
ICON_ORPHAN_DATA,
@@ -3588,6 +3664,16 @@ static void rna_def_space_outliner(BlenderRNA *brna)
"Show Library Overrides",
"For libraries with overrides created, show the overridden values");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_OUTLINER, NULL);
+
+ prop = RNA_def_property(srna, "use_filter_lib_override_system", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "filter", SO_FILTER_SHOW_SYSTEM_OVERRIDES);
+ RNA_def_property_ui_text(
+ prop,
+ "Show System Overrides",
+ "For libraries with overrides created, show the overridden values that are "
+ "defined/controlled automatically (e.g. to make users of an overridden data-block point to "
+ "the override data, not the original linked data)");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_OUTLINER, NULL);
}
static void rna_def_space_view3d_shading(BlenderRNA *brna)
@@ -4080,7 +4166,7 @@ static void rna_def_space_view3d_overlay(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "overlay.flag", V3D_OVERLAY_LOOK_DEV);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_ui_text(prop, "HDRI Preview", "Show HDRI preview spheres");
- RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL);
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, NULL);
prop = RNA_def_property(srna, "show_wireframes", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "overlay.flag", V3D_OVERLAY_WIREFRAMES);
@@ -4406,7 +4492,6 @@ static void rna_def_space_view3d_overlay(BlenderRNA *brna)
prop = RNA_def_property(srna, "gpencil_vertex_paint_opacity", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "overlay.gpencil_vertex_paint_opacity");
RNA_def_property_range(prop, 0.0f, 1.0f);
- RNA_def_property_float_default(prop, 1.0f);
RNA_def_property_ui_text(prop, "Opacity", "Vertex Paint mix factor");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, "rna_GPencil_update");
}
@@ -5381,7 +5466,7 @@ static void rna_def_space_sequencer(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_USE_PROXIES);
RNA_def_property_ui_text(
prop, "Use Proxies", "Use optimized files for faster scrubbing when available");
- RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL);
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, "rna_SequenceEditor_update_cache");
/* grease pencil */
prop = RNA_def_property(srna, "grease_pencil", PROP_POINTER, PROP_NONE);
@@ -6476,6 +6561,16 @@ static void rna_def_fileselect_asset_params(BlenderRNA *brna)
{0, NULL, 0, NULL, NULL},
};
+ static const EnumPropertyItem asset_import_type_items[] = {
+ {FILE_ASSET_IMPORT_LINK, "LINK", 0, "Link", "Import the assets as linked data-block"},
+ {FILE_ASSET_IMPORT_APPEND,
+ "APPEND",
+ 0,
+ "Append",
+ "Import the assets as copied data-block, with no link to the original asset data-block"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
srna = RNA_def_struct(brna, "FileAssetSelectParams", "FileSelectParams");
RNA_def_struct_ui_text(
srna, "Asset Select Parameters", "Settings for the file selection in Asset Browser mode");
@@ -6497,6 +6592,13 @@ static void rna_def_fileselect_asset_params(BlenderRNA *brna)
NULL);
RNA_def_property_ui_text(prop, "Asset Category", "Determine which kind of assets to display");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
+
+ prop = RNA_def_property(srna, "import_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, asset_import_type_items);
+ RNA_def_property_ui_text(prop, "Import Type", "Determine how the asset will be imported");
+ /* Asset drag info saved by buttons stores the import type, so the space must redraw when import
+ * type changes. */
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
}
static void rna_def_filemenu_entry(BlenderRNA *brna)
@@ -7309,10 +7411,93 @@ static void rna_def_space_clip(BlenderRNA *brna)
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_CLIP, NULL);
}
-static void rna_def_space_spreadsheet(BlenderRNA *brna)
+static const EnumPropertyItem spreadsheet_context_type_items[] = {
+ {SPREADSHEET_CONTEXT_OBJECT, "OBJECT", ICON_NONE, "Object", ""},
+ {SPREADSHEET_CONTEXT_MODIFIER, "MODIFIER", ICON_NONE, "Modifier", ""},
+ {SPREADSHEET_CONTEXT_NODE, "NODE", ICON_NONE, "Node", ""},
+ {0, NULL, 0, NULL, NULL},
+};
+
+static void rna_def_space_spreadsheet_context(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "SpreadsheetContext", NULL);
+ RNA_def_struct_ui_text(srna, "Spreadsheet Context", "Element of spreadsheet context path");
+ RNA_def_struct_refine_func(srna, "rna_spreadsheet_context_refine");
+
+ prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, spreadsheet_context_type_items);
+ RNA_def_property_ui_text(prop, "Type", "Type of the context");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+}
+
+static void rna_def_space_spreadsheet_context_object(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "SpreadsheetContextObject", "SpreadsheetContext");
+
+ prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "Object");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, "rna_spreadsheet_context_update");
+}
+
+static void rna_def_space_spreadsheet_context_modifier(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "SpreadsheetContextModifier", "SpreadsheetContext");
+
+ prop = RNA_def_property(srna, "modifier_name", PROP_STRING, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Modifier Name", "");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, "rna_spreadsheet_context_update");
+}
+
+static void rna_def_space_spreadsheet_context_node(BlenderRNA *brna)
{
+ StructRNA *srna;
PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "SpreadsheetContextNode", "SpreadsheetContext");
+
+ prop = RNA_def_property(srna, "node_name", PROP_STRING, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Node Name", "");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, "rna_spreadsheet_context_update");
+}
+
+static void rna_def_space_spreadsheet_context_path(BlenderRNA *brna, PropertyRNA *cprop)
+{
+ StructRNA *srna;
+ PropertyRNA *parm;
+ FunctionRNA *func;
+
+ RNA_def_property_srna(cprop, "SpreadsheetContextPath");
+ srna = RNA_def_struct(brna, "SpreadsheetContextPath", NULL);
+ RNA_def_struct_sdna(srna, "SpaceSpreadsheet");
+
+ func = RNA_def_function(srna, "append", "rna_SpaceSpreadsheet_context_path_append");
+ RNA_def_function_ui_description(func, "Append a context path element");
+ parm = RNA_def_property(func, "type", PROP_ENUM, PROP_NONE);
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ RNA_def_property_enum_items(parm, spreadsheet_context_type_items);
+ parm = RNA_def_pointer(
+ func, "context", "SpreadsheetContext", "", "Newly created context path element");
+ RNA_def_function_return(func, parm);
+
+ func = RNA_def_function(srna, "clear", "rna_SpaceSpreadsheet_context_path_clear");
+ RNA_def_function_ui_description(func, "Clear entire context path");
+}
+
+static void rna_def_space_spreadsheet(BlenderRNA *brna)
+{
+ PropertyRNA *prop, *parm;
StructRNA *srna;
+ FunctionRNA *func;
static const EnumPropertyItem geometry_component_type_items[] = {
{GEO_COMPONENT_TYPE_MESH,
@@ -7325,6 +7510,11 @@ static void rna_def_space_spreadsheet(BlenderRNA *brna)
ICON_POINTCLOUD_DATA,
"Point Cloud",
"Point cloud component containing only point data"},
+ {GEO_COMPONENT_TYPE_CURVE,
+ "CURVE",
+ ICON_CURVE_DATA,
+ "Curve",
+ "Curve component containing spline and control point data"},
{GEO_COMPONENT_TYPE_INSTANCES,
"INSTANCES",
ICON_EMPTY_AXIS,
@@ -7334,11 +7524,11 @@ static void rna_def_space_spreadsheet(BlenderRNA *brna)
};
static const EnumPropertyItem object_eval_state_items[] = {
- {SPREADSHEET_OBJECT_EVAL_STATE_FINAL,
- "FINAL",
+ {SPREADSHEET_OBJECT_EVAL_STATE_EVALUATED,
+ "EVALUATED",
ICON_NONE,
- "Final",
- "Use data from object with all modifiers applied"},
+ "Evaluated",
+ "Use data from fully or partially evaluated object"},
{SPREADSHEET_OBJECT_EVAL_STATE_ORIGINAL,
"ORIGINAL",
ICON_NONE,
@@ -7347,17 +7537,31 @@ static void rna_def_space_spreadsheet(BlenderRNA *brna)
{0, NULL, 0, NULL, NULL},
};
+ rna_def_space_spreadsheet_context(brna);
+ rna_def_space_spreadsheet_context_object(brna);
+ rna_def_space_spreadsheet_context_modifier(brna);
+ rna_def_space_spreadsheet_context_node(brna);
+
srna = RNA_def_struct(brna, "SpaceSpreadsheet", "Space");
RNA_def_struct_ui_text(srna, "Space Spreadsheet", "Spreadsheet space data");
rna_def_space_generic_show_region_toggles(srna, (1 << RGN_TYPE_FOOTER));
- prop = RNA_def_property(srna, "pinned_id", PROP_POINTER, PROP_NONE);
- RNA_def_property_flag(prop, PROP_EDITABLE);
- RNA_def_property_pointer_funcs(prop, NULL, "rna_SpaceSpreadsheet_pinned_id_set", NULL, NULL);
- RNA_def_property_ui_text(prop, "Pinned ID", "Data-block whose values are displayed");
+ prop = RNA_def_property(srna, "is_pinned", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", SPREADSHEET_FLAG_PINNED);
+ RNA_def_property_ui_text(prop, "Is Pinned", "Context path is pinned");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL);
+
+ prop = RNA_def_property(srna, "display_context_path_collapsed", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", SPREADSHEET_FLAG_CONTEXT_PATH_COLLAPSED);
+ RNA_def_property_ui_text(prop, "Display Context Path Collapsed", "");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL);
+ prop = RNA_def_property(srna, "context_path", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_struct_type(prop, "SpreadsheetContext");
+ RNA_def_property_ui_text(prop, "Context Path", "Context path to the data being displayed");
+ rna_def_space_spreadsheet_context_path(brna, prop);
+
prop = RNA_def_property(srna, "show_only_selected", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "filter_flag", SPREADSHEET_FILTER_SELECTED_ONLY);
RNA_def_property_ui_text(
@@ -7382,6 +7586,16 @@ static void rna_def_space_spreadsheet(BlenderRNA *brna)
RNA_def_property_enum_items(prop, object_eval_state_items);
RNA_def_property_ui_text(prop, "Object Evaluation State", "");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL);
+
+ func = RNA_def_function(
+ srna, "set_geometry_node_context", "rna_spreadsheet_set_geometry_node_context");
+ RNA_def_function_ui_description(
+ func, "Update context_path to point to a specific node in a node editor");
+ parm = RNA_def_pointer(
+ func, "node_editor", "SpaceNodeEditor", "", "Editor to take the context from");
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_pointer(func, "node", "Node", "", "");
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
}
void RNA_def_space(BlenderRNA *brna)
diff --git a/source/blender/makesrna/intern/rna_tracking.c b/source/blender/makesrna/intern/rna_tracking.c
index c4a44556cf4..336359a9dc0 100644
--- a/source/blender/makesrna/intern/rna_tracking.c
+++ b/source/blender/makesrna/intern/rna_tracking.c
@@ -697,7 +697,7 @@ static MovieTrackingMarker *rna_trackingMarkers_find_frame(MovieTrackingTrack *t
static MovieTrackingMarker *rna_trackingMarkers_insert_frame(MovieTrackingTrack *track,
int framenr,
- float *co)
+ float co[2])
{
MovieTrackingMarker marker, *new_marker;
@@ -2367,6 +2367,7 @@ static void rna_def_trackingObject(BlenderRNA *brna)
RNA_def_property_int_sdna(prop, NULL, "keyframe1");
RNA_def_property_ui_text(
prop, "Keyframe A", "First keyframe used for reconstruction initialization");
+ RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, NULL);
/* keyframe_b */
prop = RNA_def_property(srna, "keyframe_b", PROP_INT, PROP_NONE);
@@ -2374,6 +2375,7 @@ static void rna_def_trackingObject(BlenderRNA *brna)
RNA_def_property_int_sdna(prop, NULL, "keyframe2");
RNA_def_property_ui_text(
prop, "Keyframe B", "Second keyframe used for reconstruction initialization");
+ RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, NULL);
}
static void rna_def_trackingObjects(BlenderRNA *brna, PropertyRNA *cprop)
diff --git a/source/blender/makesrna/intern/rna_ui.c b/source/blender/makesrna/intern/rna_ui.c
index aefb77c4077..8f596ec6a5c 100644
--- a/source/blender/makesrna/intern/rna_ui.c
+++ b/source/blender/makesrna/intern/rna_ui.c
@@ -599,7 +599,7 @@ static void uilist_filter_items(uiList *ui_list,
items_shown = flt_data->items_shown = shown_idx;
flt_data->items_filter_neworder = MEM_mallocN(sizeof(int) * items_shown, __func__);
/* And now, bring back new indices into the [0, items_shown[ range!
- * XXX This is O(N²)... :/
+ * XXX This is O(N^2). :/
*/
for (shown_idx = 0, prev_ni = -1; shown_idx < items_shown; shown_idx++) {
for (i = 0, t_ni = len, t_idx = -1; i < items_shown; i++) {
diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c
index 4c86405a44d..4d45d1d6d63 100644
--- a/source/blender/makesrna/intern/rna_userdef.c
+++ b/source/blender/makesrna/intern/rna_userdef.c
@@ -779,7 +779,7 @@ static const EnumPropertyItem *rna_userdef_audio_device_itemf(bContext *UNUSED(C
RNA_enum_item_add(&item, &totitem, &new_item);
}
-# ifndef NDEBUG
+# if !defined(NDEBUG) || !defined(WITH_AUDASPACE)
if (i == 0) {
EnumPropertyItem new_item = {i, "SOUND_NONE", 0, "No Sound", ""};
RNA_enum_item_add(&item, &totitem, &new_item);
@@ -1072,10 +1072,9 @@ static void rna_UserDef_studiolight_solid_lights_begin(CollectionPropertyIterato
rna_iterator_array_begin(iter, sl->light, sizeof(*sl->light), ARRAY_SIZE(sl->light), 0, NULL);
}
-static int rna_UserDef_studiolight_solid_lights_length(PointerRNA *ptr)
+static int rna_UserDef_studiolight_solid_lights_length(PointerRNA *UNUSED(ptr))
{
- StudioLight *sl = (StudioLight *)ptr->data;
- return ARRAY_SIZE(sl->light);
+ return ARRAY_SIZE(((StudioLight *)NULL)->light);
}
/* StudioLight.light_ambient */
@@ -6160,7 +6159,7 @@ static void rna_def_userdef_filepaths(BlenderRNA *brna)
prop,
"Python Scripts Directory",
"Alternate script path, matching the default layout with subdirectories: "
- "startup, add-ons and modules (requires restart)");
+ "startup, addons, modules, and presets (requires restart)");
/* TODO, editing should reset sys.path! */
prop = RNA_def_property(srna, "i18n_branches_directory", PROP_STRING, PROP_DIRPATH);
@@ -6215,7 +6214,8 @@ static void rna_def_userdef_filepaths(BlenderRNA *brna)
RNA_def_property_ui_text(prop,
"Auto Save Temporary Files",
"Automatic saving of temporary files in temp directory, "
- "uses process ID (sculpt and edit mode data won't be saved)");
+ "uses process ID.\n"
+ "Warning: Sculpt and edit mode data won't be saved");
RNA_def_property_update(prop, 0, "rna_userdef_autosave_update");
prop = RNA_def_property(srna, "auto_save_time", PROP_INT, PROP_NONE);
@@ -6275,6 +6275,14 @@ static void rna_def_userdef_experimental(BlenderRNA *brna)
RNA_def_property_ui_text(
prop, "New Point Cloud Type", "Enable the new point cloud type in the ui");
+ prop = RNA_def_property(srna, "use_full_frame_compositor", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "use_full_frame_compositor", 1);
+ RNA_def_property_ui_text(prop,
+ "Full Frame Compositor",
+ "Enable compositor full frame execution mode option (no tiling, "
+ "reduces execution time and memory usage)");
+ RNA_def_property_update(prop, 0, "rna_userdef_update");
+
prop = RNA_def_property(srna, "use_new_hair_type", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "use_new_hair_type", 1);
RNA_def_property_ui_text(prop, "New Hair Type", "Enable the new hair type in the ui");
@@ -6288,11 +6296,6 @@ static void rna_def_userdef_experimental(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "use_sculpt_vertex_colors", 1);
RNA_def_property_ui_text(prop, "Sculpt Vertex Colors", "Use the new Vertex Painting system");
- prop = RNA_def_property(srna, "use_switch_object_operator", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "use_switch_object_operator", 1);
- RNA_def_property_ui_text(
- prop, "Switch Object Operator", "Enable the operator to switch objects by pressing D");
-
prop = RNA_def_property(srna, "use_sculpt_tools_tilt", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "use_sculpt_tools_tilt", 1);
RNA_def_property_ui_text(
@@ -6304,6 +6307,11 @@ static void rna_def_userdef_experimental(BlenderRNA *brna)
prop,
"Asset Browser",
"Enable Asset Browser editor and operators to manage data-blocks as asset");
+
+ prop = RNA_def_property(srna, "use_override_templates", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "use_override_templates", 1);
+ RNA_def_property_ui_text(
+ prop, "Override Templates", "Enable library override template in the python API");
}
static void rna_def_userdef_addon_collection(BlenderRNA *brna, PropertyRNA *cprop)
diff --git a/source/blender/makesrna/intern/rna_volume.c b/source/blender/makesrna/intern/rna_volume.c
index 1bddd9152db..76db6f3e325 100644
--- a/source/blender/makesrna/intern/rna_volume.c
+++ b/source/blender/makesrna/intern/rna_volume.c
@@ -137,7 +137,7 @@ static void rna_Volume_grids_end(CollectionPropertyIterator *UNUSED(iter))
static PointerRNA rna_Volume_grids_get(CollectionPropertyIterator *iter)
{
Volume *volume = iter->internal.count.ptr;
- const VolumeGrid *grid = BKE_volume_grid_get(volume, iter->internal.count.item);
+ const VolumeGrid *grid = BKE_volume_grid_get_for_read(volume, iter->internal.count.item);
return rna_pointer_inherit_refine(&iter->parent, &RNA_VolumeGrid, (void *)grid);
}
@@ -207,6 +207,16 @@ static int rna_VolumeGrids_frame_filepath_length(PointerRNA *ptr)
return strlen(BKE_volume_grids_frame_filepath(volume));
}
+static bool rna_Volume_load(Volume *volume, Main *bmain)
+{
+ return BKE_volume_load(volume, bmain);
+}
+
+static bool rna_Volume_save(Volume *volume, Main *bmain, ReportList *reports, const char *filepath)
+{
+ return BKE_volume_save(volume, bmain, reports, filepath);
+}
+
#else
static void rna_def_volume_grid(BlenderRNA *brna)
@@ -335,7 +345,7 @@ static void rna_def_volume_grids(BlenderRNA *brna, PropertyRNA *cprop)
FunctionRNA *func;
PropertyRNA *parm;
- func = RNA_def_function(srna, "load", "BKE_volume_load");
+ func = RNA_def_function(srna, "load", "rna_Volume_load");
RNA_def_function_ui_description(func, "Load list of grids and metadata from file");
RNA_def_function_flag(func, FUNC_USE_MAIN);
parm = RNA_def_boolean(func, "success", 0, "", "True if grid list was successfully loaded");
@@ -344,7 +354,7 @@ static void rna_def_volume_grids(BlenderRNA *brna, PropertyRNA *cprop)
func = RNA_def_function(srna, "unload", "BKE_volume_unload");
RNA_def_function_ui_description(func, "Unload all grid and voxel data from memory");
- func = RNA_def_function(srna, "save", "BKE_volume_save");
+ func = RNA_def_function(srna, "save", "rna_Volume_save");
RNA_def_function_ui_description(func, "Save grids and metadata to file");
RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_REPORTS);
parm = RNA_def_string_file_path(func, "filepath", NULL, 0, "", "File path to save to");
diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c
index d67d62d7af9..2b1a5f7fde1 100644
--- a/source/blender/makesrna/intern/rna_wm.c
+++ b/source/blender/makesrna/intern/rna_wm.c
@@ -604,14 +604,22 @@ static PointerRNA rna_Operator_options_get(PointerRNA *ptr)
static PointerRNA rna_Operator_properties_get(PointerRNA *ptr)
{
wmOperator *op = (wmOperator *)ptr->data;
- return rna_pointer_inherit_refine(ptr, op->type->srna, op->properties);
+
+ PointerRNA result;
+ WM_operator_properties_create_ptr(&result, op->type);
+ result.data = op->properties;
+ return result;
}
static PointerRNA rna_OperatorMacro_properties_get(PointerRNA *ptr)
{
wmOperatorTypeMacro *otmacro = (wmOperatorTypeMacro *)ptr->data;
wmOperatorType *ot = WM_operatortype_find(otmacro->idname, true);
- return rna_pointer_inherit_refine(ptr, ot->srna, otmacro->properties);
+
+ PointerRNA result;
+ WM_operator_properties_create_ptr(&result, ot);
+ result.data = otmacro->properties;
+ return result;
}
static const EnumPropertyItem *rna_Event_value_itemf(bContext *UNUSED(C),
@@ -880,6 +888,7 @@ static PointerRNA rna_KeyMapItem_properties_get(PointerRNA *ptr)
wmKeyMapItem *kmi = ptr->data;
if (kmi->ptr) {
+ BLI_assert(kmi->ptr->owner_id == NULL);
return *(kmi->ptr);
}
@@ -1781,41 +1790,40 @@ static void rna_Operator_bl_label_set(PointerRNA *ptr, const char *value)
}
}
-static void rna_Operator_bl_translation_context_set(PointerRNA *ptr, const char *value)
-{
- wmOperator *data = (wmOperator *)(ptr->data);
- char *str = (char *)data->type->translation_context;
- if (!str[0]) {
- BLI_strncpy(str, value, RNA_DYN_DESCR_MAX); /* utf8 already ensured */
- }
- else {
- BLI_assert(!"setting the bl_translation_context on a non-builtin operator");
- }
-}
-
-static void rna_Operator_bl_description_set(PointerRNA *ptr, const char *value)
-{
- wmOperator *data = (wmOperator *)(ptr->data);
- char *str = (char *)data->type->description;
- if (!str[0]) {
- BLI_strncpy(str, value, RNA_DYN_DESCR_MAX); /* utf8 already ensured */
- }
- else {
- BLI_assert(!"setting the bl_description on a non-builtin operator");
- }
-}
+/**
+ * Use callbacks that check for NULL instead of clearing #PROP_NEVER_NULL on the string property,
+ * so the internal value may be NULL, without allowing Python to assign `None` which doesn't
+ * make any sense in this case.
+ */
+# define OPERATOR_STR_MAYBE_NULL_GETSET(attr, len) \
+ static void rna_Operator_bl_##attr##_set(PointerRNA *ptr, const char *value) \
+ { \
+ wmOperator *data = (wmOperator *)(ptr->data); \
+ char *str = (char *)data->type->attr; \
+ if (str && !str[0]) { \
+ BLI_strncpy(str, value, len); /* utf8 already ensured */ \
+ } \
+ else { \
+ BLI_assert( \
+ !"setting the bl_" STRINGIFY(translation_context) " on a non-builtin operator"); \
+ } \
+ } \
+ static void rna_Operator_bl_##attr##_get(PointerRNA *ptr, char *value) \
+ { \
+ const wmOperator *data = (wmOperator *)(ptr->data); \
+ const char *str = data->type->attr; \
+ BLI_strncpy(value, str ? str : "", len); \
+ } \
+ static int rna_Operator_bl_##attr##_length(PointerRNA *ptr) \
+ { \
+ const wmOperator *data = (wmOperator *)(ptr->data); \
+ const char *str = data->type->attr; \
+ return BLI_strnlen(str ? str : "", len); \
+ }
-static void rna_Operator_bl_undo_group_set(PointerRNA *ptr, const char *value)
-{
- wmOperator *data = (wmOperator *)(ptr->data);
- char *str = (char *)data->type->undo_group;
- if (!str[0]) {
- BLI_strncpy(str, value, OP_MAX_TYPENAME); /* utf8 already ensured */
- }
- else {
- BLI_assert(!"setting the bl_undo_group on a non-builtin operator");
- }
-}
+OPERATOR_STR_MAYBE_NULL_GETSET(translation_context, RNA_DYN_DESCR_MAX)
+OPERATOR_STR_MAYBE_NULL_GETSET(description, RNA_DYN_DESCR_MAX)
+OPERATOR_STR_MAYBE_NULL_GETSET(undo_group, OP_MAX_TYPENAME)
static void rna_KeyMapItem_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
{
@@ -1930,26 +1938,32 @@ static void rna_def_operator(BlenderRNA *brna)
prop = RNA_def_property(srna, "bl_translation_context", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "type->translation_context");
RNA_def_property_string_maxlength(prop, RNA_DYN_DESCR_MAX); /* else it uses the pointer size! */
- RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Operator_bl_translation_context_set");
+ RNA_def_property_string_funcs(prop,
+ "rna_Operator_bl_translation_context_get",
+ "rna_Operator_bl_translation_context_length",
+ "rna_Operator_bl_translation_context_set");
RNA_def_property_string_default(prop, BLT_I18NCONTEXT_OPERATOR_DEFAULT);
RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
- RNA_def_property_clear_flag(prop, PROP_NEVER_NULL); /* check for NULL */
prop = RNA_def_property(srna, "bl_description", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "type->description");
RNA_def_property_string_maxlength(prop, RNA_DYN_DESCR_MAX); /* else it uses the pointer size! */
- RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Operator_bl_description_set");
+ RNA_def_property_string_funcs(prop,
+ "rna_Operator_bl_description_get",
+ "rna_Operator_bl_description_length",
+ "rna_Operator_bl_description_set");
/* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */
RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
- RNA_def_property_clear_flag(prop, PROP_NEVER_NULL); /* check for NULL */
prop = RNA_def_property(srna, "bl_undo_group", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "type->undo_group");
RNA_def_property_string_maxlength(prop, OP_MAX_TYPENAME); /* else it uses the pointer size! */
- RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Operator_bl_undo_group_set");
+ RNA_def_property_string_funcs(prop,
+ "rna_Operator_bl_undo_group_get",
+ "rna_Operator_bl_undo_group_length",
+ "rna_Operator_bl_undo_group_set");
/* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */
RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
- RNA_def_property_clear_flag(prop, PROP_NEVER_NULL); /* check for NULL */
prop = RNA_def_property(srna, "bl_options", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "type->flag");
@@ -1969,7 +1983,7 @@ static void rna_def_operator(BlenderRNA *brna)
RNA_def_struct_refine_func(srna, "rna_OperatorProperties_refine");
RNA_def_struct_idprops_func(srna, "rna_OperatorProperties_idprops");
RNA_def_struct_property_tags(srna, rna_enum_operator_property_tags);
- RNA_def_struct_flag(srna, STRUCT_NO_DATABLOCK_IDPROPERTIES);
+ RNA_def_struct_flag(srna, STRUCT_NO_DATABLOCK_IDPROPERTIES | STRUCT_NO_CONTEXT_WITHOUT_OWNER_ID);
RNA_def_struct_clear_flag(srna, STRUCT_UNDO);
}
@@ -2022,26 +2036,32 @@ static void rna_def_macro_operator(BlenderRNA *brna)
prop = RNA_def_property(srna, "bl_translation_context", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "type->translation_context");
RNA_def_property_string_maxlength(prop, RNA_DYN_DESCR_MAX); /* else it uses the pointer size! */
- RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Operator_bl_translation_context_set");
+ RNA_def_property_string_funcs(prop,
+ "rna_Operator_bl_translation_context_get",
+ "rna_Operator_bl_translation_context_length",
+ "rna_Operator_bl_translation_context_set");
RNA_def_property_string_default(prop, BLT_I18NCONTEXT_OPERATOR_DEFAULT);
RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
- RNA_def_property_clear_flag(prop, PROP_NEVER_NULL); /* check for NULL */
prop = RNA_def_property(srna, "bl_description", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "type->description");
RNA_def_property_string_maxlength(prop, RNA_DYN_DESCR_MAX); /* else it uses the pointer size! */
- RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Operator_bl_description_set");
+ RNA_def_property_string_funcs(prop,
+ "rna_Operator_bl_description_get",
+ "rna_Operator_bl_description_length",
+ "rna_Operator_bl_description_set");
/* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */
RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
- RNA_def_property_clear_flag(prop, PROP_NEVER_NULL); /* check for NULL */
prop = RNA_def_property(srna, "bl_undo_group", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "type->undo_group");
RNA_def_property_string_maxlength(prop, OP_MAX_TYPENAME); /* else it uses the pointer size! */
- RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Operator_bl_undo_group_set");
+ RNA_def_property_string_funcs(prop,
+ "rna_Operator_bl_undo_group_get",
+ "rna_Operator_bl_undo_group_length",
+ "rna_Operator_bl_undo_group_set");
/* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */
RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
- RNA_def_property_clear_flag(prop, PROP_NEVER_NULL); /* check for NULL */
prop = RNA_def_property(srna, "bl_options", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "type->flag");
diff --git a/source/blender/makesrna/intern/rna_wm_gizmo.c b/source/blender/makesrna/intern/rna_wm_gizmo.c
index f7d139dd706..e91df38c96e 100644
--- a/source/blender/makesrna/intern/rna_wm_gizmo.c
+++ b/source/blender/makesrna/intern/rna_wm_gizmo.c
@@ -534,12 +534,16 @@ static void rna_Gizmo_unregister(struct Main *bmain, StructRNA *type)
return;
}
+ WM_gizmotype_remove_ptr(NULL, bmain, gzt);
+
+ /* Free extension after removing instances so `__del__` doesn't crash, see: T85567. */
RNA_struct_free_extension(type, &gzt->rna_ext);
RNA_struct_free(&BLENDER_RNA, type);
- WM_main_add_notifier(NC_SCREEN | NA_EDITED, NULL);
+ /* Free gizmo group after the extension as it owns the identifier memory. */
+ WM_gizmotype_free_ptr(gzt);
- WM_gizmotype_remove_ptr(NULL, bmain, gzt);
+ WM_main_add_notifier(NC_SCREEN | NA_EDITED, NULL);
}
static void **rna_Gizmo_instance(PointerRNA *ptr)
@@ -934,12 +938,16 @@ static void rna_GizmoGroup_unregister(struct Main *bmain, StructRNA *type)
return;
}
+ WM_gizmo_group_type_remove_ptr(bmain, gzgt);
+
+ /* Free extension after removing instances so `__del__` doesn't crash, see: T85567. */
RNA_struct_free_extension(type, &gzgt->rna_ext);
RNA_struct_free(&BLENDER_RNA, type);
- WM_main_add_notifier(NC_SCREEN | NA_EDITED, NULL);
+ /* Free gizmo group after the extension as it owns the identifier memory. */
+ WM_gizmo_group_type_free_ptr(gzgt);
- WM_gizmo_group_type_remove_ptr(bmain, gzgt);
+ WM_main_add_notifier(NC_SCREEN | NA_EDITED, NULL);
}
static void **rna_GizmoGroup_instance(PointerRNA *ptr)
diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt
index c19782df44b..0138dd0c3ad 100644
--- a/source/blender/modifiers/CMakeLists.txt
+++ b/source/blender/modifiers/CMakeLists.txt
@@ -50,7 +50,7 @@ set(SRC
intern/MOD_armature.c
intern/MOD_array.c
intern/MOD_bevel.c
- intern/MOD_boolean.c
+ intern/MOD_boolean.cc
intern/MOD_build.c
intern/MOD_cast.c
intern/MOD_cloth.c
@@ -79,6 +79,7 @@ set(SRC
intern/MOD_mirror.c
intern/MOD_multires.c
intern/MOD_nodes.cc
+ intern/MOD_nodes_evaluator.cc
intern/MOD_none.c
intern/MOD_normal_edit.c
intern/MOD_ocean.c
@@ -118,6 +119,7 @@ set(SRC
MOD_modifiertypes.h
MOD_nodes.h
intern/MOD_meshcache_util.h
+ intern/MOD_nodes_evaluator.hh
intern/MOD_solidify_util.h
intern/MOD_ui_common.h
intern/MOD_util.h
@@ -181,6 +183,30 @@ endif()
if(WITH_GMP)
add_definitions(-DWITH_GMP)
+
+ list(APPEND INC_SYS
+ ${GMP_INCLUDE_DIRS}
+ )
+
+ list(APPEND LIB
+ ${GMP_LIBRARIES}
+ )
+endif()
+
+if(WITH_TBB)
+ add_definitions(-DWITH_TBB)
+ if(WIN32)
+ # TBB includes Windows.h which will define min/max macros
+ # that will collide with the stl versions.
+ add_definitions(-DNOMINMAX)
+ endif()
+ list(APPEND INC_SYS
+ ${TBB_INCLUDE_DIRS}
+ )
+
+ list(APPEND LIB
+ ${TBB_LIBRARIES}
+ )
endif()
if(WITH_OPENVDB)
@@ -198,7 +224,7 @@ if(WITH_OPENVDB)
endif()
if(WITH_EXPERIMENTAL_FEATURES)
- add_definitions(-DWITH_GEOMETRY_NODES)
+ add_definitions(-DWITH_SIMULATION_DATABLOCK)
add_definitions(-DWITH_POINT_CLOUD)
add_definitions(-DWITH_HAIR_NODES)
endif()
diff --git a/source/blender/modifiers/intern/MOD_armature.c b/source/blender/modifiers/intern/MOD_armature.c
index 6769f14f88f..649d36e3d57 100644
--- a/source/blender/modifiers/intern/MOD_armature.c
+++ b/source/blender/modifiers/intern/MOD_armature.c
@@ -296,7 +296,6 @@ ModifierTypeInfo modifierType_Armature = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_array.c b/source/blender/modifiers/intern/MOD_array.c
index b48bf722526..93a9e76ffe4 100644
--- a/source/blender/modifiers/intern/MOD_array.c
+++ b/source/blender/modifiers/intern/MOD_array.c
@@ -39,6 +39,7 @@
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
+#include "BKE_anim_path.h"
#include "BKE_context.h"
#include "BKE_curve.h"
#include "BKE_displist.h"
@@ -471,9 +472,9 @@ static Mesh *arrayModifier_doArray(ArrayModifierData *amd,
if (amd->fit_type == MOD_ARR_FITCURVE && amd->curve_ob != NULL) {
Object *curve_ob = amd->curve_ob;
CurveCache *curve_cache = curve_ob->runtime.curve_cache;
- if (curve_cache != NULL && curve_cache->path != NULL) {
+ if (curve_cache != NULL && curve_cache->anim_path_accum_length != NULL) {
float scale_fac = mat4_to_scale(curve_ob->obmat);
- length = scale_fac * curve_cache->path->totdist;
+ length = scale_fac * BKE_anim_path_get_length(curve_cache);
}
}
@@ -1020,7 +1021,6 @@ ModifierTypeInfo modifierType_Array = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ NULL,
diff --git a/source/blender/modifiers/intern/MOD_bevel.c b/source/blender/modifiers/intern/MOD_bevel.c
index a94411d897e..8fdd222402e 100644
--- a/source/blender/modifiers/intern/MOD_bevel.c
+++ b/source/blender/modifiers/intern/MOD_bevel.c
@@ -448,7 +448,6 @@ ModifierTypeInfo modifierType_Bevel = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
/* freeData */ freeData,
diff --git a/source/blender/modifiers/intern/MOD_boolean.c b/source/blender/modifiers/intern/MOD_boolean.c
deleted file mode 100644
index fae8ac72108..00000000000
--- a/source/blender/modifiers/intern/MOD_boolean.c
+++ /dev/null
@@ -1,967 +0,0 @@
-/*
- * 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.
- *
- * The Original Code is Copyright (C) 2005 by the Blender Foundation.
- * All rights reserved.
- */
-
-/** \file
- * \ingroup modifiers
- */
-
-// #define DEBUG_TIME
-
-#include <stdio.h>
-
-#include "BLI_utildefines.h"
-
-#include "BLI_alloca.h"
-#include "BLI_array.h"
-#include "BLI_math_geom.h"
-#include "BLI_math_matrix.h"
-
-#include "BLT_translation.h"
-
-#include "DNA_collection_types.h"
-#include "DNA_defaults.h"
-#include "DNA_mesh_types.h"
-#include "DNA_meshdata_types.h"
-#include "DNA_object_types.h"
-#include "DNA_scene_types.h"
-#include "DNA_screen_types.h"
-
-#include "BKE_collection.h"
-#include "BKE_context.h"
-#include "BKE_global.h" /* only to check G.debug */
-#include "BKE_lib_id.h"
-#include "BKE_lib_query.h"
-#include "BKE_material.h"
-#include "BKE_mesh.h"
-#include "BKE_mesh_boolean_convert.h"
-#include "BKE_mesh_wrapper.h"
-#include "BKE_modifier.h"
-#include "BKE_screen.h"
-
-#include "UI_interface.h"
-#include "UI_resources.h"
-
-#include "RNA_access.h"
-
-#include "MOD_ui_common.h"
-#include "MOD_util.h"
-
-#include "DEG_depsgraph_query.h"
-
-#include "MEM_guardedalloc.h"
-
-#include "bmesh.h"
-#include "bmesh_tools.h"
-#include "tools/bmesh_boolean.h"
-#include "tools/bmesh_intersect.h"
-
-#ifdef DEBUG_TIME
-# include "PIL_time.h"
-# include "PIL_time_utildefines.h"
-#endif
-
-#ifdef WITH_GMP
-static const bool bypass_bmesh = true;
-#else
-static const bool bypass_bmesh = false;
-#endif
-
-static void initData(ModifierData *md)
-{
- BooleanModifierData *bmd = (BooleanModifierData *)md;
-
- BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(bmd, modifier));
-
- MEMCPY_STRUCT_AFTER(bmd, DNA_struct_default_get(BooleanModifierData), modifier);
-}
-
-static bool isDisabled(const struct Scene *UNUSED(scene),
- ModifierData *md,
- bool UNUSED(useRenderParams))
-{
- BooleanModifierData *bmd = (BooleanModifierData *)md;
- Collection *col = bmd->collection;
-
- if (bmd->flag & eBooleanModifierFlag_Object) {
- return !bmd->object || bmd->object->type != OB_MESH;
- }
- if (bmd->flag & eBooleanModifierFlag_Collection) {
- /* The Exact solver tolerates an empty collection. */
- return !col && bmd->solver != eBooleanModifierSolver_Exact;
- }
- return false;
-}
-
-static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *userData)
-{
- BooleanModifierData *bmd = (BooleanModifierData *)md;
-
- walk(userData, ob, (ID **)&bmd->collection, IDWALK_CB_NOP);
- walk(userData, ob, (ID **)&bmd->object, IDWALK_CB_NOP);
-}
-
-static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
-{
- BooleanModifierData *bmd = (BooleanModifierData *)md;
- if ((bmd->flag & eBooleanModifierFlag_Object) && bmd->object != NULL) {
- DEG_add_object_relation(ctx->node, bmd->object, DEG_OB_COMP_TRANSFORM, "Boolean Modifier");
- DEG_add_object_relation(ctx->node, bmd->object, DEG_OB_COMP_GEOMETRY, "Boolean Modifier");
- }
-
- Collection *col = bmd->collection;
-
- if ((bmd->flag & eBooleanModifierFlag_Collection) && col != NULL) {
- FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (col, operand_ob) {
- if (operand_ob->type == OB_MESH && operand_ob != ctx->object) {
- DEG_add_object_relation(ctx->node, operand_ob, DEG_OB_COMP_TRANSFORM, "Boolean Modifier");
- DEG_add_object_relation(ctx->node, operand_ob, DEG_OB_COMP_GEOMETRY, "Boolean Modifier");
- }
- }
- FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
- }
- /* We need own transformation as well. */
- DEG_add_modifier_to_transform_relation(ctx->node, "Boolean Modifier");
-}
-
-static Mesh *get_quick_mesh(
- Object *ob_self, Mesh *mesh_self, Object *ob_operand_ob, Mesh *mesh_operand_ob, int operation)
-{
- Mesh *result = NULL;
-
- if (mesh_self->totpoly == 0 || mesh_operand_ob->totpoly == 0) {
- switch (operation) {
- case eBooleanModifierOp_Intersect:
- result = BKE_mesh_new_nomain(0, 0, 0, 0, 0);
- break;
-
- case eBooleanModifierOp_Union:
- if (mesh_self->totpoly != 0) {
- result = mesh_self;
- }
- else {
- result = (Mesh *)BKE_id_copy_ex(NULL, &mesh_operand_ob->id, NULL, LIB_ID_COPY_LOCALIZE);
-
- float imat[4][4];
- float omat[4][4];
-
- invert_m4_m4(imat, ob_self->obmat);
- mul_m4_m4m4(omat, imat, ob_operand_ob->obmat);
-
- const int mverts_len = result->totvert;
- MVert *mv = result->mvert;
-
- for (int i = 0; i < mverts_len; i++, mv++) {
- mul_m4_v3(omat, mv->co);
- }
-
- result->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
- }
-
- break;
-
- case eBooleanModifierOp_Difference:
- result = mesh_self;
- break;
- }
- }
-
- return result;
-}
-
-/* has no meaning for faces, do this so we can tell which face is which */
-#define BM_FACE_TAG BM_ELEM_DRAW
-
-/**
- * Compare selected/unselected.
- */
-static int bm_face_isect_pair(BMFace *f, void *UNUSED(user_data))
-{
- return BM_elem_flag_test(f, BM_FACE_TAG) ? 1 : 0;
-}
-
-static bool BMD_error_messages(const Object *ob, ModifierData *md, Collection *col)
-{
- BooleanModifierData *bmd = (BooleanModifierData *)md;
-
- bool error_returns_result = false;
-
- const bool operand_collection = (bmd->flag & eBooleanModifierFlag_Collection) != 0;
- const bool use_exact = bmd->solver == eBooleanModifierSolver_Exact;
- const bool operation_intersect = bmd->operation == eBooleanModifierOp_Intersect;
-
-#ifndef WITH_GMP
- /* If compiled without GMP, return a error. */
- if (use_exact) {
- BKE_modifier_set_error(ob, md, "Compiled without GMP, using fast solver");
- error_returns_result = false;
- }
-#endif
-
- /* If intersect is selected using fast solver, return a error. */
- if (operand_collection && operation_intersect && !use_exact) {
- BKE_modifier_set_error(ob, md, "Cannot execute, intersect only available using exact solver");
- error_returns_result = true;
- }
-
- /* If the selected collection is empty and using fast solver, return a error. */
- if (operand_collection) {
- if (!use_exact && BKE_collection_is_empty(col)) {
- BKE_modifier_set_error(ob, md, "Cannot execute, fast solver and empty collection");
- error_returns_result = true;
- }
-
- /* If the selected collection contain non mesh objects, return a error. */
- if (col) {
- FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (col, operand_ob) {
- if (operand_ob->type != OB_MESH) {
- BKE_modifier_set_error(
- ob, md, "Cannot execute, the selected collection contains non mesh objects");
- error_returns_result = true;
- }
- }
- FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
- }
- }
-
- return error_returns_result;
-}
-
-static BMesh *BMD_mesh_bm_create(
- Mesh *mesh, Object *object, Mesh *mesh_operand_ob, Object *operand_ob, bool *r_is_flip)
-{
- BMesh *bm;
-
- *r_is_flip = (is_negative_m4(object->obmat) != is_negative_m4(operand_ob->obmat));
-
- const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh, mesh_operand_ob);
-
- bm = BM_mesh_create(&allocsize,
- &((struct BMeshCreateParams){
- .use_toolflags = false,
- }));
-
- BM_mesh_bm_from_me(bm,
- mesh_operand_ob,
- &((struct BMeshFromMeshParams){
- .calc_face_normal = true,
- }));
-
- if (UNLIKELY(*r_is_flip)) {
- const int cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS);
- BMIter iter;
- BMFace *efa;
- BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
- BM_face_normal_flip_ex(bm, efa, cd_loop_mdisp_offset, true);
- }
- }
-
- BM_mesh_bm_from_me(bm,
- mesh,
- &((struct BMeshFromMeshParams){
- .calc_face_normal = true,
- }));
-
- return bm;
-}
-
-/* Snap entries that are near 0 or 1 or -1 to those values. */
-static void clean_obmat(float cleaned[4][4], const float mat[4][4])
-{
- const float fuzz = 1e-6f;
- for (int i = 0; i < 4; i++) {
- for (int j = 0; j < 4; j++) {
- float f = mat[i][j];
- if (fabsf(f) <= fuzz) {
- f = 0.0f;
- }
- else if (fabsf(f - 1.0f) <= fuzz) {
- f = 1.0f;
- }
- else if (fabsf(f + 1.0f) <= fuzz) {
- f = -1.0f;
- }
- cleaned[i][j] = f;
- }
- }
-}
-
-static void BMD_mesh_intersection(BMesh *bm,
- ModifierData *md,
- const ModifierEvalContext *ctx,
- Mesh *mesh_operand_ob,
- Object *object,
- Object *operand_ob,
- bool is_flip)
-{
- BooleanModifierData *bmd = (BooleanModifierData *)md;
-
- /* main bmesh intersection setup */
- /* create tessface & intersect */
- const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop);
- int tottri;
- BMLoop *(*looptris)[3];
-
-#ifdef WITH_GMP
- const bool use_exact = bmd->solver == eBooleanModifierSolver_Exact;
- const bool use_self = (bmd->flag & eBooleanModifierFlag_Self) != 0;
-#else
- const bool use_exact = false;
- const bool use_self = false;
-#endif
-
- looptris = MEM_malloc_arrayN(looptris_tot, sizeof(*looptris), __func__);
-
- BM_mesh_calc_tessellation_beauty(bm, looptris, &tottri);
-
- /* postpone this until after tessellating
- * so we can use the original normals before the vertex are moved */
- {
- BMIter iter;
- int i;
- const int i_verts_end = mesh_operand_ob->totvert;
- const int i_faces_end = mesh_operand_ob->totpoly;
-
- float imat[4][4];
- float omat[4][4];
-
- if (use_exact) {
- /* The user-expected coplanar faces will actually be coplanar more
- * often if use an object matrix that doesn't multiply by values
- * other than 0, -1, or 1 in the scaling part of the matrix.
- */
- float cleaned_object_obmat[4][4];
- float cleaned_operand_obmat[4][4];
- clean_obmat(cleaned_object_obmat, object->obmat);
- invert_m4_m4(imat, cleaned_object_obmat);
- clean_obmat(cleaned_operand_obmat, operand_ob->obmat);
- mul_m4_m4m4(omat, imat, cleaned_operand_obmat);
- }
- else {
- invert_m4_m4(imat, object->obmat);
- mul_m4_m4m4(omat, imat, operand_ob->obmat);
- }
-
- BMVert *eve;
- i = 0;
- BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
- mul_m4_v3(omat, eve->co);
- if (++i == i_verts_end) {
- break;
- }
- }
-
- /* we need face normals because of 'BM_face_split_edgenet'
- * we could calculate on the fly too (before calling split). */
- {
- float nmat[3][3];
- copy_m3_m4(nmat, omat);
- invert_m3(nmat);
-
- if (UNLIKELY(is_flip)) {
- negate_m3(nmat);
- }
-
- const short ob_src_totcol = operand_ob->totcol;
- short *material_remap = BLI_array_alloca(material_remap, ob_src_totcol ? ob_src_totcol : 1);
-
- /* Using original (not evaluated) object here since we are writing to it. */
- /* XXX Pretty sure comment above is fully wrong now with CoW & co ? */
- BKE_object_material_remap_calc(ctx->object, operand_ob, material_remap);
-
- BMFace *efa;
- i = 0;
- BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
- mul_transposed_m3_v3(nmat, efa->no);
- normalize_v3(efa->no);
-
- /* Temp tag to test which side split faces are from. */
- BM_elem_flag_enable(efa, BM_FACE_TAG);
-
- /* remap material */
- if (LIKELY(efa->mat_nr < ob_src_totcol)) {
- efa->mat_nr = material_remap[efa->mat_nr];
- }
-
- if (++i == i_faces_end) {
- break;
- }
- }
- }
- }
-
- /* not needed, but normals for 'dm' will be invalid,
- * currently this is ok for 'BM_mesh_intersect' */
- // BM_mesh_normals_update(bm);
-
- bool use_separate = false;
- bool use_dissolve = true;
- bool use_island_connect = true;
-
- /* change for testing */
- if (G.debug & G_DEBUG) {
- use_separate = (bmd->bm_flag & eBooleanModifierBMeshFlag_BMesh_Separate) != 0;
- use_dissolve = (bmd->bm_flag & eBooleanModifierBMeshFlag_BMesh_NoDissolve) == 0;
- use_island_connect = (bmd->bm_flag & eBooleanModifierBMeshFlag_BMesh_NoConnectRegions) == 0;
- }
-
- if (use_exact) {
- BM_mesh_boolean(
- bm, looptris, tottri, bm_face_isect_pair, NULL, 2, use_self, false, false, bmd->operation);
- }
- else {
- BM_mesh_intersect(bm,
- looptris,
- tottri,
- bm_face_isect_pair,
- NULL,
- false,
- use_separate,
- use_dissolve,
- use_island_connect,
- false,
- false,
- bmd->operation,
- bmd->double_threshold);
- }
- MEM_freeN(looptris);
-}
-
-static int bm_face_isect_nary(BMFace *f, void *user_data)
-{
- int *shape = (int *)user_data;
- return shape[BM_elem_index_get(f)];
-}
-
-/* The Exact solver can do all operands of a collection at once. */
-static Mesh *collection_boolean_exact(BooleanModifierData *bmd,
- const ModifierEvalContext *ctx,
- Mesh *mesh)
-{
- int i;
- Mesh *result = mesh;
- Collection *col = bmd->collection;
- int num_shapes = 1;
- Mesh **meshes = NULL;
- Object **objects = NULL;
- BLI_array_declare(meshes);
- BLI_array_declare(objects);
- BMAllocTemplate bat;
- bat.totvert = mesh->totvert;
- bat.totedge = mesh->totedge;
- bat.totloop = mesh->totloop;
- bat.totface = mesh->totpoly;
- BLI_array_append(meshes, mesh);
- BLI_array_append(objects, ctx->object);
- Mesh *col_mesh;
- /* Allow col to be empty: then target mesh will just remove self-intersections. */
- if (col) {
- FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (col, ob) {
- if (ob->type == OB_MESH && ob != ctx->object) {
- col_mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(ob, false);
- /* XXX This is utterly non-optimal, we may go from a bmesh to a mesh back to a bmesh!
- * But for 2.90 better not try to be smart here. */
- BKE_mesh_wrapper_ensure_mdata(col_mesh);
- BLI_array_append(meshes, col_mesh);
- BLI_array_append(objects, ob);
- bat.totvert += col_mesh->totvert;
- bat.totedge += col_mesh->totedge;
- bat.totloop += col_mesh->totloop;
- bat.totface += col_mesh->totpoly;
- ++num_shapes;
- }
- }
- FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
- }
- int *shape_face_end = MEM_mallocN(num_shapes * sizeof(int), __func__);
- int *shape_vert_end = MEM_mallocN(num_shapes * sizeof(int), __func__);
- bool is_neg_mat0 = is_negative_m4(ctx->object->obmat);
- BMesh *bm = BM_mesh_create(&bat,
- &((struct BMeshCreateParams){
- .use_toolflags = false,
- }));
- for (i = 0; i < num_shapes; i++) {
- Mesh *me = meshes[i];
- Object *ob = objects[i];
- /* Need normals for triangulation. */
- BM_mesh_bm_from_me(bm,
- me,
- &((struct BMeshFromMeshParams){
- .calc_face_normal = true,
- }));
- shape_face_end[i] = me->totpoly + (i == 0 ? 0 : shape_face_end[i - 1]);
- shape_vert_end[i] = me->totvert + (i == 0 ? 0 : shape_vert_end[i - 1]);
- if (i > 0) {
- bool is_flip = (is_neg_mat0 != is_negative_m4(ob->obmat));
- if (UNLIKELY(is_flip)) {
- const int cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS);
- BMIter iter;
- BMFace *efa;
- BM_mesh_elem_index_ensure(bm, BM_FACE);
- BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
- if (BM_elem_index_get(efa) >= shape_face_end[i - 1]) {
- BM_face_normal_flip_ex(bm, efa, cd_loop_mdisp_offset, true);
- }
- }
- }
- }
- }
-
- /* Triangulate the mesh. */
- const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop);
- int tottri;
- BMLoop *(*looptris)[3];
- looptris = MEM_malloc_arrayN(looptris_tot, sizeof(*looptris), __func__);
- BM_mesh_calc_tessellation_beauty(bm, looptris, &tottri);
-
- /* Move the vertices of all but the first shape into transformation space of first mesh.
- * Do this after tesselation so don't need to recalculate normals.
- * The Exact solver doesn't need normals on the input faces. */
- float imat[4][4];
- float omat[4][4];
- float cleaned_object_obmat[4][4];
- clean_obmat(cleaned_object_obmat, ctx->object->obmat);
- invert_m4_m4(imat, cleaned_object_obmat);
- int curshape = 0;
- int curshape_vert_end = shape_vert_end[0];
- BMVert *eve;
- BMIter iter;
- i = 0;
- BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
- if (i == curshape_vert_end) {
- curshape++;
- curshape_vert_end = shape_vert_end[curshape];
- clean_obmat(cleaned_object_obmat, objects[curshape]->obmat);
- mul_m4_m4m4(omat, imat, cleaned_object_obmat);
- }
- if (curshape > 0) {
- mul_m4_v3(omat, eve->co);
- }
- i++;
- }
-
- /* Remap the materials. Fill a shape array for test function. Calculate normals. */
- int *shape = MEM_mallocN(bm->totface * sizeof(int), __func__);
- curshape = 0;
- int curshape_face_end = shape_face_end[0];
- int curshape_ncol = ctx->object->totcol;
- short *material_remap = NULL;
- BMFace *efa;
- i = 0;
- BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
- if (i == curshape_face_end) {
- curshape++;
- curshape_face_end = shape_face_end[curshape];
- if (material_remap != NULL) {
- MEM_freeN(material_remap);
- }
- curshape_ncol = objects[curshape]->totcol;
- material_remap = MEM_mallocN(curshape_ncol ? curshape_ncol : 1, __func__);
- BKE_object_material_remap_calc(ctx->object, objects[curshape], material_remap);
- }
- shape[i] = curshape;
- if (curshape > 0) {
- /* Normals for other shapes changed because vertex positions changed.
- * Boolean doesn't need these, but post-boolean code (interpolation) does. */
- BM_face_normal_update(efa);
- if (LIKELY(efa->mat_nr < curshape_ncol)) {
- efa->mat_nr = material_remap[efa->mat_nr];
- }
- }
- i++;
- }
-
- BM_mesh_elem_index_ensure(bm, BM_FACE);
- BM_mesh_boolean(bm,
- looptris,
- tottri,
- bm_face_isect_nary,
- shape,
- num_shapes,
- true,
- false,
- false,
- bmd->operation);
-
- result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, mesh);
- BM_mesh_free(bm);
- result->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
-
- MEM_freeN(shape);
- MEM_freeN(shape_face_end);
- MEM_freeN(shape_vert_end);
- MEM_freeN(looptris);
- if (material_remap != NULL) {
- MEM_freeN(material_remap);
- }
- BLI_array_free(meshes);
- BLI_array_free(objects);
- return result;
-}
-
-#ifdef WITH_GMP
-
-/* Get a mapping from material slot numbers in the src_ob to slot numbers in the dst_ob.
- * If a material doesn't exist in the dst_ob, the mapping just goes to the same slot
- * or to zero if there aren't enough slots in the destination.
- * Caller must MEM_freeN the returned array. */
-static short *get_material_remap(Object *dest_ob, Object *src_ob)
-{
- short *remap;
- int n = dest_ob->totcol;
- if (n <= 0) {
- n = 1;
- }
- remap = MEM_mallocN(n * sizeof(short), __func__);
- BKE_object_material_remap_calc(dest_ob, src_ob, remap);
- return remap;
-}
-
-/* New method: bypass trip through BMesh. */
-static Mesh *exact_boolean_mesh(BooleanModifierData *bmd,
- const ModifierEvalContext *ctx,
- Mesh *mesh)
-{
- Mesh *result;
- Mesh *mesh_operand;
- short *remap;
- Mesh **meshes = NULL;
- const float(**obmats)[4][4] = NULL;
- short **material_remaps = NULL;
- BLI_array_declare(meshes);
- BLI_array_declare(obmats);
- BLI_array_declare(material_remaps);
-
-# ifdef DEBUG_TIME
- TIMEIT_START(boolean_bmesh);
-# endif
-
- if ((bmd->flag & eBooleanModifierFlag_Object) && bmd->object == NULL) {
- return mesh;
- }
-
- BLI_array_append(meshes, mesh);
- BLI_array_append(obmats, &ctx->object->obmat);
- BLI_array_append(material_remaps, NULL);
- if (bmd->flag & eBooleanModifierFlag_Object) {
- mesh_operand = BKE_modifier_get_evaluated_mesh_from_evaluated_object(bmd->object, false);
- BKE_mesh_wrapper_ensure_mdata(mesh_operand);
- BLI_array_append(meshes, mesh_operand);
- BLI_array_append(obmats, &bmd->object->obmat);
- remap = get_material_remap(ctx->object, bmd->object);
- BLI_array_append(material_remaps, remap);
- }
- else if (bmd->flag & eBooleanModifierFlag_Collection) {
- Collection *collection = bmd->collection;
- /* Allow collection to be empty: then target mesh will just removed self-intersections. */
- if (collection) {
- FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (collection, ob) {
- if (ob->type == OB_MESH && ob != ctx->object) {
- Mesh *collection_mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(ob, false);
- BKE_mesh_wrapper_ensure_mdata(collection_mesh);
- BLI_array_append(meshes, collection_mesh);
- BLI_array_append(obmats, &ob->obmat);
- remap = get_material_remap(ctx->object, ob);
- BLI_array_append(material_remaps, remap);
- }
- }
- FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
- }
- }
-
- const bool use_self = (bmd->flag & eBooleanModifierFlag_Self) != 0;
- const bool hole_tolerant = (bmd->flag & eBooleanModifierFlag_HoleTolerant) != 0;
- result = BKE_mesh_boolean((const Mesh **)meshes,
- (const float(**)[4][4])obmats,
- (const short **)material_remaps,
- BLI_array_len(meshes),
- use_self,
- hole_tolerant,
- bmd->operation);
-
- BLI_array_free(meshes);
- BLI_array_free(obmats);
- for (int i = 0; i < BLI_array_len(material_remaps); i++) {
- remap = material_remaps[i];
- if (remap) {
- MEM_freeN(remap);
- }
- }
- BLI_array_free(material_remaps);
-
-# ifdef DEBUG_TIME
- TIMEIT_END(boolean_bmesh);
-# endif
-
- return result;
-}
-#endif
-
-static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh)
-{
- BooleanModifierData *bmd = (BooleanModifierData *)md;
- Object *object = ctx->object;
- Mesh *result = mesh;
- Mesh *mesh_operand_ob;
- BMesh *bm;
- Collection *collection = bmd->collection;
-
- bool is_flip = false;
- const bool confirm_return = true;
-#ifdef WITH_GMP
- const bool use_exact = bmd->solver == eBooleanModifierSolver_Exact;
- if (use_exact && bypass_bmesh) {
- return exact_boolean_mesh(bmd, ctx, mesh);
- }
-#else
- UNUSED_VARS(bypass_bmesh);
- const bool use_exact = false;
-#endif
-
-#ifdef DEBUG_TIME
- TIMEIT_START(boolean_bmesh);
-#endif
-
- if (bmd->flag & eBooleanModifierFlag_Object) {
- if (bmd->object == NULL) {
- return result;
- }
-
- BMD_error_messages(ctx->object, md, NULL);
-
- Object *operand_ob = bmd->object;
-
-#ifdef DEBUG_TIME
- TIMEIT_BLOCK_INIT(operand_get_evaluated_mesh);
- TIMEIT_BLOCK_START(operand_get_evaluated_mesh);
-#endif
- mesh_operand_ob = BKE_modifier_get_evaluated_mesh_from_evaluated_object(operand_ob, false);
-#ifdef DEBUG_TIME
- TIMEIT_BLOCK_END(operand_get_evaluated_mesh);
- TIMEIT_BLOCK_STATS(operand_get_evaluated_mesh);
-#endif
-
- if (mesh_operand_ob) {
- /* XXX This is utterly non-optimal, we may go from a bmesh to a mesh back to a bmesh!
- * But for 2.90 better not try to be smart here. */
- BKE_mesh_wrapper_ensure_mdata(mesh_operand_ob);
- /* when one of objects is empty (has got no faces) we could speed up
- * calculation a bit returning one of objects' derived meshes (or empty one)
- * Returning mesh is depended on modifiers operation (sergey) */
- result = get_quick_mesh(object, mesh, operand_ob, mesh_operand_ob, bmd->operation);
-
- if (result == NULL) {
-#ifdef DEBUG_TIME
- TIMEIT_BLOCK_INIT(object_BMD_mesh_bm_create);
- TIMEIT_BLOCK_START(object_BMD_mesh_bm_create);
-#endif
- bm = BMD_mesh_bm_create(mesh, object, mesh_operand_ob, operand_ob, &is_flip);
-#ifdef DEBUG_TIME
- TIMEIT_BLOCK_END(object_BMD_mesh_bm_create);
- TIMEIT_BLOCK_STATS(object_BMD_mesh_bm_create);
-#endif
-
-#ifdef DEBUG_TIME
- TIMEIT_BLOCK_INIT(BMD_mesh_intersection);
- TIMEIT_BLOCK_START(BMD_mesh_intersection);
-#endif
- BMD_mesh_intersection(bm, md, ctx, mesh_operand_ob, object, operand_ob, is_flip);
-#ifdef DEBUG_TIME
- TIMEIT_BLOCK_END(BMD_mesh_intersection);
- TIMEIT_BLOCK_STATS(BMD_mesh_intersection);
-#endif
-
-#ifdef DEBUG_TIME
- TIMEIT_BLOCK_INIT(BKE_mesh_from_bmesh_for_eval_nomain);
- TIMEIT_BLOCK_START(BKE_mesh_from_bmesh_for_eval_nomain);
-#endif
- result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, mesh);
-#ifdef DEBUG_TIME
- TIMEIT_BLOCK_END(BKE_mesh_from_bmesh_for_eval_nomain);
- TIMEIT_BLOCK_STATS(BKE_mesh_from_bmesh_for_eval_nomain);
-#endif
- BM_mesh_free(bm);
- result->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
- }
-
- /* if new mesh returned, return it; otherwise there was
- * an error, so delete the modifier object */
- if (result == NULL) {
- BKE_modifier_set_error(object, md, "Cannot execute boolean operation");
- }
- }
- }
-
- else {
- if (collection == NULL && !use_exact) {
- return result;
- }
-
- /* Return result for certain errors. */
- if (BMD_error_messages(ctx->object, md, collection) == confirm_return) {
- return result;
- }
-
- if (use_exact) {
- result = collection_boolean_exact(bmd, ctx, mesh);
- }
- else {
- FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (collection, operand_ob) {
- if (operand_ob->type == OB_MESH && operand_ob != ctx->object) {
-
- mesh_operand_ob = BKE_modifier_get_evaluated_mesh_from_evaluated_object(operand_ob,
- false);
-
- if (mesh_operand_ob) {
- /* XXX This is utterly non-optimal, we may go from a bmesh to a mesh back to a bmesh!
- * But for 2.90 better not try to be smart here. */
- BKE_mesh_wrapper_ensure_mdata(mesh_operand_ob);
-
- bm = BMD_mesh_bm_create(mesh, object, mesh_operand_ob, operand_ob, &is_flip);
-
- BMD_mesh_intersection(bm, md, ctx, mesh_operand_ob, object, operand_ob, is_flip);
-
- /* Needed for multiple objects to work. */
- BM_mesh_bm_to_me(NULL,
- bm,
- mesh,
- (&(struct BMeshToMeshParams){
- .calc_object_remap = false,
- }));
-
- result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, mesh);
- BM_mesh_free(bm);
- result->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
- }
- }
- }
- FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
- }
- }
-
-#ifdef DEBUG_TIME
- TIMEIT_END(boolean_bmesh);
-#endif
-
- return result;
-}
-
-static void requiredDataMask(Object *UNUSED(ob),
- ModifierData *UNUSED(md),
- CustomData_MeshMasks *r_cddata_masks)
-{
- r_cddata_masks->vmask |= CD_MASK_MDEFORMVERT;
- r_cddata_masks->emask |= CD_MASK_MEDGE;
- r_cddata_masks->fmask |= CD_MASK_MTFACE;
-}
-
-static void panel_draw(const bContext *UNUSED(C), Panel *panel)
-{
- uiLayout *layout = panel->layout;
-
- PointerRNA *ptr = modifier_panel_get_property_pointers(panel, NULL);
-
- uiItemR(layout, ptr, "operation", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
-
- uiLayoutSetPropSep(layout, true);
-
- uiItemR(layout, ptr, "operand_type", 0, NULL, ICON_NONE);
-
- const bool operand_object = RNA_enum_get(ptr, "operand_type") == eBooleanModifierFlag_Object;
-
- if (operand_object) {
- uiItemR(layout, ptr, "object", 0, NULL, ICON_NONE);
- }
- else {
- uiItemR(layout, ptr, "collection", 0, NULL, ICON_NONE);
- }
-
- uiItemR(layout, ptr, "solver", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
-
- modifier_panel_end(layout, ptr);
-}
-
-static void solver_options_panel_draw(const bContext *UNUSED(C), Panel *panel)
-{
- uiLayout *layout = panel->layout;
-
- PointerRNA *ptr = modifier_panel_get_property_pointers(panel, NULL);
-
- const bool use_exact = RNA_enum_get(ptr, "solver") == eBooleanModifierSolver_Exact;
- const bool operand_object = RNA_enum_get(ptr, "operand_type") == eBooleanModifierFlag_Object;
-
- uiLayoutSetPropSep(layout, true);
-
- uiLayout *col = uiLayoutColumn(layout, true);
- if (use_exact) {
- /* When operand is collection, we always use_self. */
- if (operand_object) {
- uiItemR(col, ptr, "use_self", 0, NULL, ICON_NONE);
- }
- uiItemR(col, ptr, "use_hole_tolerant", 0, NULL, ICON_NONE);
- }
- else {
- uiItemR(col, ptr, "double_threshold", 0, NULL, ICON_NONE);
- }
-
- if (G.debug) {
- uiItemR(col, ptr, "debug_options", 0, NULL, ICON_NONE);
- }
-}
-
-static void panelRegister(ARegionType *region_type)
-{
- PanelType *panel = modifier_panel_register(region_type, eModifierType_Boolean, panel_draw);
- modifier_subpanel_register(
- region_type, "solver_options", "Solver Options", NULL, solver_options_panel_draw, panel);
-}
-
-ModifierTypeInfo modifierType_Boolean = {
- /* name */ "Boolean",
- /* structName */ "BooleanModifierData",
- /* structSize */ sizeof(BooleanModifierData),
- /* srna */ &RNA_BooleanModifier,
- /* type */ eModifierTypeType_Nonconstructive,
- /* flags */ eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_SupportsEditmode,
- /* icon */ ICON_MOD_BOOLEAN,
-
- /* copyData */ BKE_modifier_copydata_generic,
-
- /* deformVerts */ NULL,
- /* deformMatrices */ NULL,
- /* deformVertsEM */ NULL,
- /* deformMatricesEM */ NULL,
- /* modifyMesh */ modifyMesh,
- /* modifyHair */ NULL,
- /* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
-
- /* initData */ initData,
- /* requiredDataMask */ requiredDataMask,
- /* freeData */ NULL,
- /* isDisabled */ isDisabled,
- /* updateDepsgraph */ updateDepsgraph,
- /* dependsOnTime */ NULL,
- /* dependsOnNormals */ NULL,
- /* foreachIDLink */ foreachIDLink,
- /* foreachTexLink */ NULL,
- /* freeRuntimeData */ NULL,
- /* panelRegister */ panelRegister,
- /* blendWrite */ NULL,
- /* blendRead */ NULL,
-};
diff --git a/source/blender/modifiers/intern/MOD_boolean.cc b/source/blender/modifiers/intern/MOD_boolean.cc
new file mode 100644
index 00000000000..4b9b24e4e47
--- /dev/null
+++ b/source/blender/modifiers/intern/MOD_boolean.cc
@@ -0,0 +1,651 @@
+/*
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2005 by the Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup modifiers
+ */
+
+#include <cstdio>
+
+#include "BLI_utildefines.h"
+
+#include "BLI_array.hh"
+#include "BLI_float4x4.hh"
+#include "BLI_math_geom.h"
+#include "BLI_math_matrix.h"
+#include "BLI_vector.hh"
+
+#include "BLT_translation.h"
+
+#include "DNA_collection_types.h"
+#include "DNA_defaults.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
+
+#include "BKE_collection.h"
+#include "BKE_context.h"
+#include "BKE_global.h" /* only to check G.debug */
+#include "BKE_lib_id.h"
+#include "BKE_lib_query.h"
+#include "BKE_material.h"
+#include "BKE_mesh.h"
+#include "BKE_mesh_boolean_convert.hh"
+#include "BKE_mesh_wrapper.h"
+#include "BKE_modifier.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "RNA_access.h"
+
+#include "MOD_ui_common.h"
+#include "MOD_util.h"
+
+#include "DEG_depsgraph_query.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "bmesh.h"
+#include "bmesh_tools.h"
+#include "tools/bmesh_boolean.h"
+#include "tools/bmesh_intersect.h"
+
+// #define DEBUG_TIME
+
+#ifdef DEBUG_TIME
+# include "BLI_timeit.hh"
+#endif
+
+using blender::Array;
+using blender::float4x4;
+using blender::Vector;
+
+static void initData(ModifierData *md)
+{
+ BooleanModifierData *bmd = (BooleanModifierData *)md;
+
+ BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(bmd, modifier));
+
+ MEMCPY_STRUCT_AFTER(bmd, DNA_struct_default_get(BooleanModifierData), modifier);
+}
+
+static bool isDisabled(const struct Scene *UNUSED(scene),
+ ModifierData *md,
+ bool UNUSED(useRenderParams))
+{
+ BooleanModifierData *bmd = (BooleanModifierData *)md;
+ Collection *col = bmd->collection;
+
+ if (bmd->flag & eBooleanModifierFlag_Object) {
+ return !bmd->object || bmd->object->type != OB_MESH;
+ }
+ if (bmd->flag & eBooleanModifierFlag_Collection) {
+ /* The Exact solver tolerates an empty collection. */
+ return !col && bmd->solver != eBooleanModifierSolver_Exact;
+ }
+ return false;
+}
+
+static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *userData)
+{
+ BooleanModifierData *bmd = (BooleanModifierData *)md;
+
+ walk(userData, ob, (ID **)&bmd->collection, IDWALK_CB_NOP);
+ walk(userData, ob, (ID **)&bmd->object, IDWALK_CB_NOP);
+}
+
+static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
+{
+ BooleanModifierData *bmd = (BooleanModifierData *)md;
+ if ((bmd->flag & eBooleanModifierFlag_Object) && bmd->object != nullptr) {
+ DEG_add_object_relation(ctx->node, bmd->object, DEG_OB_COMP_TRANSFORM, "Boolean Modifier");
+ DEG_add_object_relation(ctx->node, bmd->object, DEG_OB_COMP_GEOMETRY, "Boolean Modifier");
+ }
+
+ Collection *col = bmd->collection;
+
+ if ((bmd->flag & eBooleanModifierFlag_Collection) && col != nullptr) {
+ DEG_add_collection_geometry_relation(ctx->node, col, "Boolean Modifier");
+ }
+ /* We need own transformation as well. */
+ DEG_add_modifier_to_transform_relation(ctx->node, "Boolean Modifier");
+}
+
+static Mesh *get_quick_mesh(
+ Object *ob_self, Mesh *mesh_self, Object *ob_operand_ob, Mesh *mesh_operand_ob, int operation)
+{
+ Mesh *result = nullptr;
+
+ if (mesh_self->totpoly == 0 || mesh_operand_ob->totpoly == 0) {
+ switch (operation) {
+ case eBooleanModifierOp_Intersect:
+ result = BKE_mesh_new_nomain(0, 0, 0, 0, 0);
+ break;
+
+ case eBooleanModifierOp_Union:
+ if (mesh_self->totpoly != 0) {
+ result = mesh_self;
+ }
+ else {
+ result = (Mesh *)BKE_id_copy_ex(
+ nullptr, &mesh_operand_ob->id, nullptr, LIB_ID_COPY_LOCALIZE);
+
+ float imat[4][4];
+ float omat[4][4];
+ invert_m4_m4(imat, ob_self->obmat);
+ mul_m4_m4m4(omat, imat, ob_operand_ob->obmat);
+
+ const int mverts_len = result->totvert;
+ MVert *mv = result->mvert;
+
+ for (int i = 0; i < mverts_len; i++, mv++) {
+ mul_m4_v3(omat, mv->co);
+ }
+
+ result->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
+ }
+
+ break;
+
+ case eBooleanModifierOp_Difference:
+ result = mesh_self;
+ break;
+ }
+ }
+
+ return result;
+}
+
+/* has no meaning for faces, do this so we can tell which face is which */
+#define BM_FACE_TAG BM_ELEM_DRAW
+
+/**
+ * Compare selected/unselected.
+ */
+static int bm_face_isect_pair(BMFace *f, void *UNUSED(user_data))
+{
+ return BM_elem_flag_test(f, BM_FACE_TAG) ? 1 : 0;
+}
+
+static bool BMD_error_messages(const Object *ob, ModifierData *md)
+{
+ BooleanModifierData *bmd = (BooleanModifierData *)md;
+ Collection *col = bmd->collection;
+
+ bool error_returns_result = false;
+
+ const bool operand_collection = (bmd->flag & eBooleanModifierFlag_Collection) != 0;
+ const bool use_exact = bmd->solver == eBooleanModifierSolver_Exact;
+ const bool operation_intersect = bmd->operation == eBooleanModifierOp_Intersect;
+
+#ifndef WITH_GMP
+ /* If compiled without GMP, return a error. */
+ if (use_exact) {
+ BKE_modifier_set_error(ob, md, "Compiled without GMP, using fast solver");
+ error_returns_result = false;
+ }
+#endif
+
+ /* If intersect is selected using fast solver, return a error. */
+ if (operand_collection && operation_intersect && !use_exact) {
+ BKE_modifier_set_error(ob, md, "Cannot execute, intersect only available using exact solver");
+ error_returns_result = true;
+ }
+
+ /* If the selected collection is empty and using fast solver, return a error. */
+ if (operand_collection) {
+ if (!use_exact && BKE_collection_is_empty(col)) {
+ BKE_modifier_set_error(ob, md, "Cannot execute, fast solver and empty collection");
+ error_returns_result = true;
+ }
+
+ /* If the selected collection contain non mesh objects, return a error. */
+ if (col) {
+ FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (col, operand_ob) {
+ if (operand_ob->type != OB_MESH) {
+ BKE_modifier_set_error(
+ ob, md, "Cannot execute, the selected collection contains non mesh objects");
+ error_returns_result = true;
+ }
+ }
+ FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
+ }
+ }
+
+ return error_returns_result;
+}
+
+static BMesh *BMD_mesh_bm_create(
+ Mesh *mesh, Object *object, Mesh *mesh_operand_ob, Object *operand_ob, bool *r_is_flip)
+{
+#ifdef DEBUG_TIME
+ SCOPED_TIMER(__func__)
+#endif
+
+ *r_is_flip = (is_negative_m4(object->obmat) != is_negative_m4(operand_ob->obmat));
+
+ const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh, mesh_operand_ob);
+
+ BMeshCreateParams bmcp = {false};
+ BMesh *bm = BM_mesh_create(&allocsize, &bmcp);
+
+ BMeshFromMeshParams params{};
+ params.calc_face_normal = true;
+ BM_mesh_bm_from_me(bm, mesh_operand_ob, &params);
+
+ if (UNLIKELY(*r_is_flip)) {
+ const int cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS);
+ BMIter iter;
+ BMFace *efa;
+ BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
+ BM_face_normal_flip_ex(bm, efa, cd_loop_mdisp_offset, true);
+ }
+ }
+
+ BM_mesh_bm_from_me(bm, mesh, &params);
+
+ return bm;
+}
+
+static void BMD_mesh_intersection(BMesh *bm,
+ ModifierData *md,
+ const ModifierEvalContext *ctx,
+ Mesh *mesh_operand_ob,
+ Object *object,
+ Object *operand_ob,
+ bool is_flip)
+{
+#ifdef DEBUG_TIME
+ SCOPED_TIMER(__func__)
+#endif
+
+ BooleanModifierData *bmd = (BooleanModifierData *)md;
+
+ /* main bmesh intersection setup */
+ /* create tessface & intersect */
+ const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop);
+ BMLoop *(*looptris)[3] = (BMLoop * (*)[3])
+ MEM_malloc_arrayN(looptris_tot, sizeof(*looptris), __func__);
+
+ BM_mesh_calc_tessellation_beauty(bm, looptris);
+
+ /* postpone this until after tessellating
+ * so we can use the original normals before the vertex are moved */
+ {
+ BMIter iter;
+ int i;
+ const int i_verts_end = mesh_operand_ob->totvert;
+ const int i_faces_end = mesh_operand_ob->totpoly;
+
+ float imat[4][4];
+ float omat[4][4];
+ invert_m4_m4(imat, object->obmat);
+ mul_m4_m4m4(omat, imat, operand_ob->obmat);
+
+ BMVert *eve;
+ i = 0;
+ BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
+ mul_m4_v3(omat, eve->co);
+ if (++i == i_verts_end) {
+ break;
+ }
+ }
+
+ /* we need face normals because of 'BM_face_split_edgenet'
+ * we could calculate on the fly too (before calling split). */
+ float nmat[3][3];
+ copy_m3_m4(nmat, omat);
+ invert_m3(nmat);
+
+ if (UNLIKELY(is_flip)) {
+ negate_m3(nmat);
+ }
+
+ Array<short> material_remap(operand_ob->totcol ? operand_ob->totcol : 1);
+
+ /* Using original (not evaluated) object here since we are writing to it. */
+ /* XXX Pretty sure comment above is fully wrong now with CoW & co ? */
+ BKE_object_material_remap_calc(ctx->object, operand_ob, material_remap.data());
+
+ BMFace *efa;
+ i = 0;
+ BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
+ mul_transposed_m3_v3(nmat, efa->no);
+ normalize_v3(efa->no);
+
+ /* Temp tag to test which side split faces are from. */
+ BM_elem_flag_enable(efa, BM_FACE_TAG);
+
+ /* remap material */
+ if (LIKELY(efa->mat_nr < operand_ob->totcol)) {
+ efa->mat_nr = material_remap[efa->mat_nr];
+ }
+
+ if (++i == i_faces_end) {
+ break;
+ }
+ }
+ }
+
+ /* not needed, but normals for 'dm' will be invalid,
+ * currently this is ok for 'BM_mesh_intersect' */
+ // BM_mesh_normals_update(bm);
+
+ bool use_separate = false;
+ bool use_dissolve = true;
+ bool use_island_connect = true;
+
+ /* change for testing */
+ if (G.debug & G_DEBUG) {
+ use_separate = (bmd->bm_flag & eBooleanModifierBMeshFlag_BMesh_Separate) != 0;
+ use_dissolve = (bmd->bm_flag & eBooleanModifierBMeshFlag_BMesh_NoDissolve) == 0;
+ use_island_connect = (bmd->bm_flag & eBooleanModifierBMeshFlag_BMesh_NoConnectRegions) == 0;
+ }
+
+ BM_mesh_intersect(bm,
+ looptris,
+ looptris_tot,
+ bm_face_isect_pair,
+ nullptr,
+ false,
+ use_separate,
+ use_dissolve,
+ use_island_connect,
+ false,
+ false,
+ bmd->operation,
+ bmd->double_threshold);
+
+ MEM_freeN(looptris);
+}
+
+#ifdef WITH_GMP
+
+/* Get a mapping from material slot numbers in the src_ob to slot numbers in the dst_ob.
+ * If a material doesn't exist in the dst_ob, the mapping just goes to the same slot
+ * or to zero if there aren't enough slots in the destination.
+ * Caller owns the returned array. */
+static Array<short> get_material_remap(Object *dest_ob, Object *src_ob)
+{
+ int n = dest_ob->totcol;
+ if (n <= 0) {
+ n = 1;
+ }
+ Array<short> remap(n);
+ BKE_object_material_remap_calc(dest_ob, src_ob, remap.data());
+ return remap;
+}
+
+static Mesh *exact_boolean_mesh(BooleanModifierData *bmd,
+ const ModifierEvalContext *ctx,
+ Mesh *mesh)
+{
+ Vector<const Mesh *> meshes;
+ Vector<float4x4 *> obmats;
+ Vector<Array<short>> material_remaps;
+
+# ifdef DEBUG_TIME
+ SCOPED_TIMER(__func__)
+# endif
+
+ if ((bmd->flag & eBooleanModifierFlag_Object) && bmd->object == nullptr) {
+ return mesh;
+ }
+
+ meshes.append(mesh);
+ obmats.append((float4x4 *)&ctx->object->obmat);
+ material_remaps.append({});
+ if (bmd->flag & eBooleanModifierFlag_Object) {
+ Mesh *mesh_operand = BKE_modifier_get_evaluated_mesh_from_evaluated_object(bmd->object, false);
+ if (!mesh_operand) {
+ return mesh;
+ }
+ BKE_mesh_wrapper_ensure_mdata(mesh_operand);
+ meshes.append(mesh_operand);
+ obmats.append((float4x4 *)&bmd->object->obmat);
+ material_remaps.append(get_material_remap(ctx->object, bmd->object));
+ }
+ else if (bmd->flag & eBooleanModifierFlag_Collection) {
+ Collection *collection = bmd->collection;
+ /* Allow collection to be empty; then target mesh will just removed self-intersections. */
+ if (collection) {
+ FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (collection, ob) {
+ if (ob->type == OB_MESH && ob != ctx->object) {
+ Mesh *collection_mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(ob, false);
+ if (!collection_mesh) {
+ continue;
+ }
+ BKE_mesh_wrapper_ensure_mdata(collection_mesh);
+ meshes.append(collection_mesh);
+ obmats.append((float4x4 *)&ob->obmat);
+ material_remaps.append(get_material_remap(ctx->object, ob));
+ }
+ }
+ FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
+ }
+ }
+
+ const bool use_self = (bmd->flag & eBooleanModifierFlag_Self) != 0;
+ const bool hole_tolerant = (bmd->flag & eBooleanModifierFlag_HoleTolerant) != 0;
+ return blender::meshintersect::direct_mesh_boolean(meshes,
+ obmats,
+ *(float4x4 *)&ctx->object->obmat,
+ material_remaps,
+ use_self,
+ hole_tolerant,
+ bmd->operation);
+}
+#endif
+
+static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh)
+{
+ BooleanModifierData *bmd = (BooleanModifierData *)md;
+ Object *object = ctx->object;
+ Mesh *result = mesh;
+ Collection *collection = bmd->collection;
+
+ /* Return result for certain errors. */
+ if (BMD_error_messages(ctx->object, md)) {
+ return result;
+ }
+
+#ifdef WITH_GMP
+ if (bmd->solver == eBooleanModifierSolver_Exact) {
+ return exact_boolean_mesh(bmd, ctx, mesh);
+ }
+#endif
+
+#ifdef DEBUG_TIME
+ SCOPED_TIMER(__func__)
+#endif
+
+ if (bmd->flag & eBooleanModifierFlag_Object) {
+ if (bmd->object == nullptr) {
+ return result;
+ }
+
+ Object *operand_ob = bmd->object;
+
+ Mesh *mesh_operand_ob = BKE_modifier_get_evaluated_mesh_from_evaluated_object(operand_ob,
+ false);
+
+ if (mesh_operand_ob) {
+ /* XXX This is utterly non-optimal, we may go from a bmesh to a mesh back to a bmesh!
+ * But for 2.90 better not try to be smart here. */
+ BKE_mesh_wrapper_ensure_mdata(mesh_operand_ob);
+ /* when one of objects is empty (has got no faces) we could speed up
+ * calculation a bit returning one of objects' derived meshes (or empty one)
+ * Returning mesh is depended on modifiers operation (sergey) */
+ result = get_quick_mesh(object, mesh, operand_ob, mesh_operand_ob, bmd->operation);
+
+ if (result == nullptr) {
+ bool is_flip;
+ BMesh *bm = BMD_mesh_bm_create(mesh, object, mesh_operand_ob, operand_ob, &is_flip);
+
+ BMD_mesh_intersection(bm, md, ctx, mesh_operand_ob, object, operand_ob, is_flip);
+
+ result = BKE_mesh_from_bmesh_for_eval_nomain(bm, nullptr, mesh);
+
+ BM_mesh_free(bm);
+ result->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
+ }
+
+ if (result == nullptr) {
+ BKE_modifier_set_error(object, md, "Cannot execute boolean operation");
+ }
+ }
+ }
+ else {
+ if (collection == nullptr) {
+ return result;
+ }
+
+ FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (collection, operand_ob) {
+ if (operand_ob->type == OB_MESH && operand_ob != ctx->object) {
+ Mesh *mesh_operand_ob = BKE_modifier_get_evaluated_mesh_from_evaluated_object(operand_ob,
+ false);
+
+ if (mesh_operand_ob) {
+ /* XXX This is utterly non-optimal, we may go from a bmesh to a mesh back to a bmesh!
+ * But for 2.90 better not try to be smart here. */
+ BKE_mesh_wrapper_ensure_mdata(mesh_operand_ob);
+
+ bool is_flip;
+ BMesh *bm = BMD_mesh_bm_create(mesh, object, mesh_operand_ob, operand_ob, &is_flip);
+
+ BMD_mesh_intersection(bm, md, ctx, mesh_operand_ob, object, operand_ob, is_flip);
+
+ /* Needed for multiple objects to work. */
+ BMeshToMeshParams params{};
+ params.calc_object_remap = false;
+ BM_mesh_bm_to_me(nullptr, bm, mesh, &params);
+
+ result = BKE_mesh_from_bmesh_for_eval_nomain(bm, nullptr, mesh);
+ BM_mesh_free(bm);
+ result->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
+ }
+ }
+ }
+ FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
+ }
+
+ return result;
+}
+
+static void requiredDataMask(Object *UNUSED(ob),
+ ModifierData *UNUSED(md),
+ CustomData_MeshMasks *r_cddata_masks)
+{
+ r_cddata_masks->vmask |= CD_MASK_MDEFORMVERT;
+ r_cddata_masks->emask |= CD_MASK_MEDGE;
+ r_cddata_masks->fmask |= CD_MASK_MTFACE;
+}
+
+static void panel_draw(const bContext *UNUSED(C), Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+ PointerRNA *ptr = modifier_panel_get_property_pointers(panel, nullptr);
+
+ uiItemR(layout, ptr, "operation", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
+
+ uiLayoutSetPropSep(layout, true);
+
+ uiItemR(layout, ptr, "operand_type", 0, nullptr, ICON_NONE);
+ if (RNA_enum_get(ptr, "operand_type") == eBooleanModifierFlag_Object) {
+ uiItemR(layout, ptr, "object", 0, nullptr, ICON_NONE);
+ }
+ else {
+ uiItemR(layout, ptr, "collection", 0, nullptr, ICON_NONE);
+ }
+
+ uiItemR(layout, ptr, "solver", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
+
+ modifier_panel_end(layout, ptr);
+}
+
+static void solver_options_panel_draw(const bContext *UNUSED(C), Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+ PointerRNA *ptr = modifier_panel_get_property_pointers(panel, nullptr);
+
+ const bool use_exact = RNA_enum_get(ptr, "solver") == eBooleanModifierSolver_Exact;
+
+ uiLayoutSetPropSep(layout, true);
+
+ uiLayout *col = uiLayoutColumn(layout, true);
+ if (use_exact) {
+ /* When operand is collection, we always use_self. */
+ if (RNA_enum_get(ptr, "operand_type") == eBooleanModifierFlag_Object) {
+ uiItemR(col, ptr, "use_self", 0, nullptr, ICON_NONE);
+ }
+ uiItemR(col, ptr, "use_hole_tolerant", 0, nullptr, ICON_NONE);
+ }
+ else {
+ uiItemR(col, ptr, "double_threshold", 0, nullptr, ICON_NONE);
+ }
+
+ if (G.debug) {
+ uiItemR(col, ptr, "debug_options", 0, nullptr, ICON_NONE);
+ }
+}
+
+static void panelRegister(ARegionType *region_type)
+{
+ PanelType *panel = modifier_panel_register(region_type, eModifierType_Boolean, panel_draw);
+ modifier_subpanel_register(
+ region_type, "solver_options", "Solver Options", nullptr, solver_options_panel_draw, panel);
+}
+
+ModifierTypeInfo modifierType_Boolean = {
+ /* name */ "Boolean",
+ /* structName */ "BooleanModifierData",
+ /* structSize */ sizeof(BooleanModifierData),
+ /* srna */ &RNA_BooleanModifier,
+ /* type */ eModifierTypeType_Nonconstructive,
+ /* flags */
+ (ModifierTypeFlag)(eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_SupportsEditmode),
+ /* icon */ ICON_MOD_BOOLEAN,
+
+ /* copyData */ BKE_modifier_copydata_generic,
+
+ /* deformVerts */ nullptr,
+ /* deformMatrices */ nullptr,
+ /* deformVertsEM */ nullptr,
+ /* deformMatricesEM */ nullptr,
+ /* modifyMesh */ modifyMesh,
+ /* modifyHair */ nullptr,
+ /* modifyGeometrySet */ nullptr,
+
+ /* initData */ initData,
+ /* requiredDataMask */ requiredDataMask,
+ /* freeData */ nullptr,
+ /* isDisabled */ isDisabled,
+ /* updateDepsgraph */ updateDepsgraph,
+ /* dependsOnTime */ nullptr,
+ /* dependsOnNormals */ nullptr,
+ /* foreachIDLink */ foreachIDLink,
+ /* foreachTexLink */ nullptr,
+ /* freeRuntimeData */ nullptr,
+ /* panelRegister */ panelRegister,
+ /* blendWrite */ nullptr,
+ /* blendRead */ nullptr,
+};
diff --git a/source/blender/modifiers/intern/MOD_build.c b/source/blender/modifiers/intern/MOD_build.c
index 0b1c661baed..c38e5126f6b 100644
--- a/source/blender/modifiers/intern/MOD_build.c
+++ b/source/blender/modifiers/intern/MOD_build.c
@@ -347,7 +347,6 @@ ModifierTypeInfo modifierType_Build = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ NULL,
diff --git a/source/blender/modifiers/intern/MOD_cast.c b/source/blender/modifiers/intern/MOD_cast.c
index f905a38ae12..715bc26e5d3 100644
--- a/source/blender/modifiers/intern/MOD_cast.c
+++ b/source/blender/modifiers/intern/MOD_cast.c
@@ -591,7 +591,6 @@ ModifierTypeInfo modifierType_Cast = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_cloth.c b/source/blender/modifiers/intern/MOD_cloth.c
index a25d65347c5..40d027f3044 100644
--- a/source/blender/modifiers/intern/MOD_cloth.c
+++ b/source/blender/modifiers/intern/MOD_cloth.c
@@ -311,7 +311,6 @@ ModifierTypeInfo modifierType_Cloth = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_collision.c b/source/blender/modifiers/intern/MOD_collision.c
index e72e0279263..5dd57469914 100644
--- a/source/blender/modifiers/intern/MOD_collision.c
+++ b/source/blender/modifiers/intern/MOD_collision.c
@@ -317,7 +317,6 @@ ModifierTypeInfo modifierType_Collision = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ NULL,
diff --git a/source/blender/modifiers/intern/MOD_correctivesmooth.c b/source/blender/modifiers/intern/MOD_correctivesmooth.c
index 001c7d8d098..fef235b456b 100644
--- a/source/blender/modifiers/intern/MOD_correctivesmooth.c
+++ b/source/blender/modifiers/intern/MOD_correctivesmooth.c
@@ -852,7 +852,6 @@ ModifierTypeInfo modifierType_CorrectiveSmooth = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_curve.c b/source/blender/modifiers/intern/MOD_curve.c
index d5d53edfd54..20dbb299767 100644
--- a/source/blender/modifiers/intern/MOD_curve.c
+++ b/source/blender/modifiers/intern/MOD_curve.c
@@ -236,7 +236,6 @@ ModifierTypeInfo modifierType_Curve = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_datatransfer.c b/source/blender/modifiers/intern/MOD_datatransfer.c
index 8b299a82f95..dbdc76f0edc 100644
--- a/source/blender/modifiers/intern/MOD_datatransfer.c
+++ b/source/blender/modifiers/intern/MOD_datatransfer.c
@@ -495,7 +495,6 @@ ModifierTypeInfo modifierType_DataTransfer = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_decimate.c b/source/blender/modifiers/intern/MOD_decimate.c
index 03db09a5aec..faad1175f3a 100644
--- a/source/blender/modifiers/intern/MOD_decimate.c
+++ b/source/blender/modifiers/intern/MOD_decimate.c
@@ -300,7 +300,6 @@ ModifierTypeInfo modifierType_Decimate = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_displace.c b/source/blender/modifiers/intern/MOD_displace.c
index abe78943508..a7ac9f618af 100644
--- a/source/blender/modifiers/intern/MOD_displace.c
+++ b/source/blender/modifiers/intern/MOD_displace.c
@@ -507,7 +507,6 @@ ModifierTypeInfo modifierType_Displace = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_dynamicpaint.c b/source/blender/modifiers/intern/MOD_dynamicpaint.c
index 3e607e88cdd..8b1d541d45d 100644
--- a/source/blender/modifiers/intern/MOD_dynamicpaint.c
+++ b/source/blender/modifiers/intern/MOD_dynamicpaint.c
@@ -221,7 +221,6 @@ ModifierTypeInfo modifierType_DynamicPaint = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_edgesplit.c b/source/blender/modifiers/intern/MOD_edgesplit.c
index e02befd7efa..2874bebe13a 100644
--- a/source/blender/modifiers/intern/MOD_edgesplit.c
+++ b/source/blender/modifiers/intern/MOD_edgesplit.c
@@ -187,7 +187,6 @@ ModifierTypeInfo modifierType_EdgeSplit = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ NULL,
diff --git a/source/blender/modifiers/intern/MOD_explode.c b/source/blender/modifiers/intern/MOD_explode.c
index c12019a325e..e1197439c7c 100644
--- a/source/blender/modifiers/intern/MOD_explode.c
+++ b/source/blender/modifiers/intern/MOD_explode.c
@@ -1255,7 +1255,6 @@ ModifierTypeInfo modifierType_Explode = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_fluid.c b/source/blender/modifiers/intern/MOD_fluid.c
index 8a8d6f2305f..36d2ab2a11a 100644
--- a/source/blender/modifiers/intern/MOD_fluid.c
+++ b/source/blender/modifiers/intern/MOD_fluid.c
@@ -239,7 +239,6 @@ ModifierTypeInfo modifierType_Fluid = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_hook.c b/source/blender/modifiers/intern/MOD_hook.c
index 2fa05a319d5..ff581e92cdd 100644
--- a/source/blender/modifiers/intern/MOD_hook.c
+++ b/source/blender/modifiers/intern/MOD_hook.c
@@ -574,7 +574,6 @@ ModifierTypeInfo modifierType_Hook = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_laplaciandeform.c b/source/blender/modifiers/intern/MOD_laplaciandeform.c
index bda0f9ba5a4..6efeec1970f 100644
--- a/source/blender/modifiers/intern/MOD_laplaciandeform.c
+++ b/source/blender/modifiers/intern/MOD_laplaciandeform.c
@@ -889,7 +889,6 @@ ModifierTypeInfo modifierType_LaplacianDeform = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_laplaciansmooth.c b/source/blender/modifiers/intern/MOD_laplaciansmooth.c
index fc527304e76..78e0bf3fa8f 100644
--- a/source/blender/modifiers/intern/MOD_laplaciansmooth.c
+++ b/source/blender/modifiers/intern/MOD_laplaciansmooth.c
@@ -635,7 +635,6 @@ ModifierTypeInfo modifierType_LaplacianSmooth = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ init_data,
/* requiredDataMask */ required_data_mask,
diff --git a/source/blender/modifiers/intern/MOD_lattice.c b/source/blender/modifiers/intern/MOD_lattice.c
index e3c42e39dda..29d1ecf6050 100644
--- a/source/blender/modifiers/intern/MOD_lattice.c
+++ b/source/blender/modifiers/intern/MOD_lattice.c
@@ -193,7 +193,6 @@ ModifierTypeInfo modifierType_Lattice = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_mask.cc b/source/blender/modifiers/intern/MOD_mask.cc
index 191d39d9fce..a77f6cfe8ba 100644
--- a/source/blender/modifiers/intern/MOD_mask.cc
+++ b/source/blender/modifiers/intern/MOD_mask.cc
@@ -69,6 +69,19 @@ using blender::MutableSpan;
using blender::Span;
using blender::Vector;
+/* For delete geometry node. */
+void copy_masked_vertices_to_new_mesh(const Mesh &src_mesh, Mesh &dst_mesh, Span<int> vertex_map);
+void copy_masked_edges_to_new_mesh(const Mesh &src_mesh,
+ Mesh &dst_mesh,
+ Span<int> vertex_map,
+ Span<int> edge_map);
+void copy_masked_polys_to_new_mesh(const Mesh &src_mesh,
+ Mesh &dst_mesh,
+ Span<int> vertex_map,
+ Span<int> edge_map,
+ Span<int> masked_poly_indices,
+ Span<int> new_loop_starts);
+
static void initData(ModifierData *md)
{
MaskModifierData *mmd = (MaskModifierData *)md;
@@ -237,9 +250,7 @@ static void computed_masked_polygons(const Mesh *mesh,
*r_num_masked_loops = num_masked_loops;
}
-static void copy_masked_vertices_to_new_mesh(const Mesh &src_mesh,
- Mesh &dst_mesh,
- Span<int> vertex_map)
+void copy_masked_vertices_to_new_mesh(const Mesh &src_mesh, Mesh &dst_mesh, Span<int> vertex_map)
{
BLI_assert(src_mesh.totvert == vertex_map.size());
for (const int i_src : vertex_map.index_range()) {
@@ -256,10 +267,10 @@ static void copy_masked_vertices_to_new_mesh(const Mesh &src_mesh,
}
}
-static void copy_masked_edges_to_new_mesh(const Mesh &src_mesh,
- Mesh &dst_mesh,
- Span<int> vertex_map,
- Span<int> edge_map)
+void copy_masked_edges_to_new_mesh(const Mesh &src_mesh,
+ Mesh &dst_mesh,
+ Span<int> vertex_map,
+ Span<int> edge_map)
{
BLI_assert(src_mesh.totvert == vertex_map.size());
BLI_assert(src_mesh.totedge == edge_map.size());
@@ -279,12 +290,12 @@ static void copy_masked_edges_to_new_mesh(const Mesh &src_mesh,
}
}
-static void copy_masked_polys_to_new_mesh(const Mesh &src_mesh,
- Mesh &dst_mesh,
- Span<int> vertex_map,
- Span<int> edge_map,
- Span<int> masked_poly_indices,
- Span<int> new_loop_starts)
+void copy_masked_polys_to_new_mesh(const Mesh &src_mesh,
+ Mesh &dst_mesh,
+ Span<int> vertex_map,
+ Span<int> edge_map,
+ Span<int> masked_poly_indices,
+ Span<int> new_loop_starts)
{
for (const int i_dst : masked_poly_indices.index_range()) {
const int i_src = masked_poly_indices[i_dst];
@@ -463,7 +474,6 @@ ModifierTypeInfo modifierType_Mask = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ nullptr,
/* modifyGeometrySet */ nullptr,
- /* modifyVolume */ nullptr,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_mesh_to_volume.cc b/source/blender/modifiers/intern/MOD_mesh_to_volume.cc
index cc007651733..778b5746471 100644
--- a/source/blender/modifiers/intern/MOD_mesh_to_volume.cc
+++ b/source/blender/modifiers/intern/MOD_mesh_to_volume.cc
@@ -20,6 +20,7 @@
#include <vector>
+#include "BKE_geometry_set.hh"
#include "BKE_lib_query.h"
#include "BKE_mesh_runtime.h"
#include "BKE_mesh_wrapper.h"
@@ -204,7 +205,9 @@ static float compute_voxel_size(const ModifierEvalContext *ctx,
}
#endif
-static Volume *modifyVolume(ModifierData *md, const ModifierEvalContext *ctx, Volume *input_volume)
+static Volume *mesh_to_volume(ModifierData *md,
+ const ModifierEvalContext *ctx,
+ Volume *input_volume)
{
#ifdef WITH_OPENVDB
using namespace blender;
@@ -278,6 +281,17 @@ static Volume *modifyVolume(ModifierData *md, const ModifierEvalContext *ctx, Vo
#endif
}
+static void modifyGeometrySet(ModifierData *md,
+ const ModifierEvalContext *ctx,
+ GeometrySet *geometry_set)
+{
+ Volume *input_volume = geometry_set->get_volume_for_write();
+ Volume *result_volume = mesh_to_volume(md, ctx, input_volume);
+ if (result_volume != input_volume) {
+ geometry_set->replace_volume(result_volume);
+ }
+}
+
ModifierTypeInfo modifierType_MeshToVolume = {
/* name */ "Mesh to Volume",
/* structName */ "MeshToVolumeModifierData",
@@ -295,8 +309,7 @@ ModifierTypeInfo modifierType_MeshToVolume = {
/* deformMatricesEM */ nullptr,
/* modifyMesh */ nullptr,
/* modifyHair */ nullptr,
- /* modifyGeometrySet */ nullptr,
- /* modifyVolume */ modifyVolume,
+ /* modifyGeometrySet */ modifyGeometrySet,
/* initData */ initData,
/* requiredDataMask */ nullptr,
diff --git a/source/blender/modifiers/intern/MOD_meshcache.c b/source/blender/modifiers/intern/MOD_meshcache.c
index 6d2e0f242d7..361454120ca 100644
--- a/source/blender/modifiers/intern/MOD_meshcache.c
+++ b/source/blender/modifiers/intern/MOD_meshcache.c
@@ -389,7 +389,6 @@ ModifierTypeInfo modifierType_MeshCache = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ NULL,
diff --git a/source/blender/modifiers/intern/MOD_meshdeform.c b/source/blender/modifiers/intern/MOD_meshdeform.c
index a94dd6da477..8e1e03570b5 100644
--- a/source/blender/modifiers/intern/MOD_meshdeform.c
+++ b/source/blender/modifiers/intern/MOD_meshdeform.c
@@ -644,7 +644,6 @@ ModifierTypeInfo modifierType_MeshDeform = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_meshsequencecache.c b/source/blender/modifiers/intern/MOD_meshsequencecache.c
index 2c01857adb1..c2f9cd8c867 100644
--- a/source/blender/modifiers/intern/MOD_meshsequencecache.c
+++ b/source/blender/modifiers/intern/MOD_meshsequencecache.c
@@ -271,7 +271,6 @@ ModifierTypeInfo modifierType_MeshSequenceCache = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ NULL,
diff --git a/source/blender/modifiers/intern/MOD_mirror.c b/source/blender/modifiers/intern/MOD_mirror.c
index afe94d8dead..6116cf8146a 100644
--- a/source/blender/modifiers/intern/MOD_mirror.c
+++ b/source/blender/modifiers/intern/MOD_mirror.c
@@ -165,6 +165,13 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel)
uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_mirror_merge"));
uiItemR(sub, ptr, "merge_threshold", 0, "", ICON_NONE);
+ bool is_bisect_set[3];
+ RNA_boolean_get_array(ptr, "use_bisect_axis", is_bisect_set);
+
+ sub = uiLayoutRow(col, true);
+ uiLayoutSetActive(sub, is_bisect_set[0] || is_bisect_set[1] || is_bisect_set[2]);
+ uiItemR(sub, ptr, "bisect_threshold", 0, IFACE_("Bisect Distance"), ICON_NONE);
+
modifier_panel_end(layout, ptr);
}
@@ -232,7 +239,6 @@ ModifierTypeInfo modifierType_Mirror = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ NULL,
diff --git a/source/blender/modifiers/intern/MOD_multires.c b/source/blender/modifiers/intern/MOD_multires.c
index 1182c8db093..c3b34f3cd23 100644
--- a/source/blender/modifiers/intern/MOD_multires.c
+++ b/source/blender/modifiers/intern/MOD_multires.c
@@ -520,7 +520,6 @@ ModifierTypeInfo modifierType_Multires = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc
index 6236dc87791..8fa80025790 100644
--- a/source/blender/modifiers/intern/MOD_nodes.cc
+++ b/source/blender/modifiers/intern/MOD_nodes.cc
@@ -29,12 +29,14 @@
#include "BLI_float3.hh"
#include "BLI_listbase.h"
+#include "BLI_multi_value_map.hh"
#include "BLI_set.hh"
#include "BLI_string.h"
#include "BLI_utildefines.h"
#include "DNA_collection_types.h"
#include "DNA_defaults.h"
+#include "DNA_material_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_modifier_types.h"
@@ -43,17 +45,23 @@
#include "DNA_pointcloud_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
+#include "DNA_space_types.h"
+#include "DNA_windowmanager_types.h"
#include "BKE_customdata.h"
+#include "BKE_geometry_set_instances.hh"
#include "BKE_global.h"
#include "BKE_idprop.h"
#include "BKE_lib_query.h"
+#include "BKE_main.h"
#include "BKE_mesh.h"
#include "BKE_modifier.h"
#include "BKE_node_ui_storage.hh"
+#include "BKE_object.h"
#include "BKE_pointcloud.h"
#include "BKE_screen.h"
#include "BKE_simulation.h"
+#include "BKE_workspace.h"
#include "BLO_read_write.h"
@@ -68,13 +76,14 @@
#include "MOD_modifiertypes.h"
#include "MOD_nodes.h"
+#include "MOD_nodes_evaluator.hh"
#include "MOD_ui_common.h"
+#include "ED_spreadsheet.h"
+
#include "NOD_derived_node_tree.hh"
#include "NOD_geometry.h"
-#include "NOD_geometry_exec.hh"
#include "NOD_node_tree_multi_function.hh"
-#include "NOD_type_callbacks.hh"
using blender::float3;
using blender::FunctionRef;
@@ -85,11 +94,8 @@ using blender::Span;
using blender::StringRef;
using blender::StringRefNull;
using blender::Vector;
-using blender::bke::PersistentCollectionHandle;
-using blender::bke::PersistentDataHandleMap;
-using blender::bke::PersistentObjectHandle;
using blender::fn::GMutablePointer;
-using blender::fn::GValueMap;
+using blender::fn::GPointer;
using blender::nodes::GeoNodeExecParams;
using namespace blender::fn::multi_function_types;
using namespace blender::nodes::derived_node_tree_types;
@@ -118,6 +124,18 @@ static void addIdsUsedBySocket(const ListBase *sockets, Set<ID *> &ids)
ids.add(&collection->id);
}
}
+ else if (socket->type == SOCK_MATERIAL) {
+ Material *material = ((bNodeSocketValueMaterial *)socket->default_value)->value;
+ if (material != nullptr) {
+ ids.add(&material->id);
+ }
+ }
+ else if (socket->type == SOCK_TEXTURE) {
+ Tex *texture = ((bNodeSocketValueTexture *)socket->default_value)->value;
+ if (texture != nullptr) {
+ ids.add(&texture->id);
+ }
+ }
}
}
@@ -129,7 +147,7 @@ static void find_used_ids_from_nodes(const bNodeTree &tree, Set<ID *> &ids)
addIdsUsedBySocket(&node->inputs, ids);
addIdsUsedBySocket(&node->outputs, ids);
- if (node->type == NODE_GROUP) {
+ if (ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP)) {
const bNodeTree *group = (bNodeTree *)node->id;
if (group != nullptr && handled_groups.add(group)) {
find_used_ids_from_nodes(*group, ids);
@@ -153,40 +171,34 @@ static void find_used_ids_from_settings(const NodesModifierSettings &settings, S
&ids);
}
-static void add_collection_object_relations_recursive(const ModifierUpdateDepsgraphContext *ctx,
- Collection &collection);
+/* We don't know exactly what attributes from the other object we will need. */
+static const CustomData_MeshMasks dependency_data_mask{CD_MASK_PROP_ALL | CD_MASK_MDEFORMVERT,
+ CD_MASK_PROP_ALL,
+ CD_MASK_PROP_ALL,
+ CD_MASK_PROP_ALL,
+ CD_MASK_PROP_ALL};
+
+static void add_collection_relation(const ModifierUpdateDepsgraphContext *ctx,
+ Collection &collection)
+{
+ DEG_add_collection_geometry_relation(ctx->node, &collection, "Nodes Modifier");
+ DEG_add_collection_geometry_customdata_mask(ctx->node, &collection, &dependency_data_mask);
+}
static void add_object_relation(const ModifierUpdateDepsgraphContext *ctx, Object &object)
{
DEG_add_object_relation(ctx->node, &object, DEG_OB_COMP_TRANSFORM, "Nodes Modifier");
if (&(ID &)object != &ctx->object->id) {
- if (object.type == OB_EMPTY) {
- Collection *collection_instance = object.instance_collection;
- if (collection_instance != nullptr) {
- add_collection_object_relations_recursive(ctx, *collection_instance);
- }
+ if (object.type == OB_EMPTY && object.instance_collection != nullptr) {
+ add_collection_relation(ctx, *object.instance_collection);
}
- else {
+ else if (DEG_object_has_geometry_component(&object)) {
DEG_add_object_relation(ctx->node, &object, DEG_OB_COMP_GEOMETRY, "Nodes Modifier");
+ DEG_add_customdata_mask(ctx->node, &object, &dependency_data_mask);
}
}
}
-static void add_collection_object_relations_recursive(const ModifierUpdateDepsgraphContext *ctx,
- Collection &collection)
-{
- LISTBASE_FOREACH (CollectionObject *, collection_object, &collection.gobject) {
- BLI_assert(collection_object->ob != nullptr);
- Object &object = *collection_object->ob;
- add_object_relation(ctx, object);
- }
- LISTBASE_FOREACH (CollectionChild *, collection_child, &collection.children) {
- BLI_assert(collection_child->collection != nullptr);
- Collection &collection = *collection_child->collection;
- add_collection_object_relations_recursive(ctx, collection);
- }
-}
-
static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
{
NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
@@ -198,18 +210,28 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte
find_used_ids_from_settings(nmd->settings, used_ids);
find_used_ids_from_nodes(*nmd->node_group, used_ids);
for (ID *id : used_ids) {
- if (GS(id->name) == ID_OB) {
- Object *object = reinterpret_cast<Object *>(id);
- add_object_relation(ctx, *object);
- }
- if (GS(id->name) == ID_GR) {
- Collection *collection = reinterpret_cast<Collection *>(id);
- add_collection_object_relations_recursive(ctx, *collection);
+ switch ((ID_Type)GS(id->name)) {
+ case ID_OB: {
+ Object *object = reinterpret_cast<Object *>(id);
+ add_object_relation(ctx, *object);
+ break;
+ }
+ case ID_GR: {
+ Collection *collection = reinterpret_cast<Collection *>(id);
+ add_collection_relation(ctx, *collection);
+ break;
+ }
+ case ID_TE: {
+ DEG_add_generic_id_relation(ctx->node, id, "Nodes Modifier");
+ }
+ default: {
+ /* Purposefully don't add relations for materials. While there are material sockets,
+ * the pointers are only passed around as handles rather than dereferenced. */
+ break;
+ }
}
}
}
-
- /* TODO: Add dependency for adding and removing objects in collections. */
}
static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *userData)
@@ -252,368 +274,16 @@ static bool isDisabled(const struct Scene *UNUSED(scene),
return false;
}
-class GeometryNodesEvaluator {
- private:
- blender::LinearAllocator<> allocator_;
- Map<std::pair<DInputSocket, DOutputSocket>, GMutablePointer> value_by_input_;
- Vector<DInputSocket> group_outputs_;
- blender::nodes::MultiFunctionByNode &mf_by_node_;
- const blender::nodes::DataTypeConversions &conversions_;
- const PersistentDataHandleMap &handle_map_;
- const Object *self_object_;
- const ModifierData *modifier_;
- Depsgraph *depsgraph_;
-
- public:
- GeometryNodesEvaluator(const Map<DOutputSocket, GMutablePointer> &group_input_data,
- Vector<DInputSocket> group_outputs,
- blender::nodes::MultiFunctionByNode &mf_by_node,
- const PersistentDataHandleMap &handle_map,
- const Object *self_object,
- const ModifierData *modifier,
- Depsgraph *depsgraph)
- : group_outputs_(std::move(group_outputs)),
- mf_by_node_(mf_by_node),
- conversions_(blender::nodes::get_implicit_type_conversions()),
- handle_map_(handle_map),
- self_object_(self_object),
- modifier_(modifier),
- depsgraph_(depsgraph)
- {
- for (auto item : group_input_data.items()) {
- this->forward_to_inputs(item.key, item.value);
- }
- }
-
- Vector<GMutablePointer> execute()
- {
- Vector<GMutablePointer> results;
- for (const DInputSocket &group_output : group_outputs_) {
- Vector<GMutablePointer> result = this->get_input_values(group_output);
- results.append(result[0]);
- }
- for (GMutablePointer value : value_by_input_.values()) {
- value.destruct();
- }
- return results;
- }
-
- private:
- Vector<GMutablePointer> get_input_values(const DInputSocket socket_to_compute)
- {
- Vector<DSocket> from_sockets;
- socket_to_compute.foreach_origin_socket([&](DSocket socket) { from_sockets.append(socket); });
-
- if (from_sockets.is_empty()) {
- /* The input is not connected, use the value from the socket itself. */
- const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket_to_compute->typeinfo());
- return {get_unlinked_input_value(socket_to_compute, type)};
- }
-
- /* Multi-input sockets contain a vector of inputs. */
- if (socket_to_compute->is_multi_input_socket()) {
- return this->get_inputs_from_incoming_links(socket_to_compute, from_sockets);
- }
-
- const DSocket from_socket = from_sockets[0];
- GMutablePointer value = this->get_input_from_incoming_link(socket_to_compute, from_socket);
- return {value};
- }
-
- Vector<GMutablePointer> get_inputs_from_incoming_links(const DInputSocket socket_to_compute,
- const Span<DSocket> from_sockets)
- {
- Vector<GMutablePointer> values;
- for (const int i : from_sockets.index_range()) {
- const DSocket from_socket = from_sockets[i];
- const int first_occurence = from_sockets.take_front(i).first_index_try(from_socket);
- if (first_occurence == -1) {
- values.append(this->get_input_from_incoming_link(socket_to_compute, from_socket));
- }
- else {
- /* If the same from-socket occurs more than once, we make a copy of the first value. This
- * can happen when a node linked to a multi-input-socket is muted. */
- GMutablePointer value = values[first_occurence];
- const CPPType *type = value.type();
- void *copy_buffer = allocator_.allocate(type->size(), type->alignment());
- type->copy_to_uninitialized(value.get(), copy_buffer);
- values.append({type, copy_buffer});
- }
- }
- return values;
- }
-
- GMutablePointer get_input_from_incoming_link(const DInputSocket socket_to_compute,
- const DSocket from_socket)
- {
- if (from_socket->is_output()) {
- const DOutputSocket from_output_socket{from_socket};
- const std::pair<DInputSocket, DOutputSocket> key = std::make_pair(socket_to_compute,
- from_output_socket);
- std::optional<GMutablePointer> value = value_by_input_.pop_try(key);
- if (value.has_value()) {
- /* This input has been computed before, return it directly. */
- return {*value};
- }
-
- /* Compute the socket now. */
- this->compute_output_and_forward(from_output_socket);
- return {value_by_input_.pop(key)};
- }
-
- /* Get value from an unlinked input socket. */
- const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket_to_compute->typeinfo());
- const DInputSocket from_input_socket{from_socket};
- return {get_unlinked_input_value(from_input_socket, type)};
- }
-
- void compute_output_and_forward(const DOutputSocket socket_to_compute)
- {
- const DNode node{socket_to_compute.context(), &socket_to_compute->node()};
-
- if (!socket_to_compute->is_available()) {
- /* If the output is not available, use a default value. */
- const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket_to_compute->typeinfo());
- void *buffer = allocator_.allocate(type.size(), type.alignment());
- type.copy_to_uninitialized(type.default_value(), buffer);
- this->forward_to_inputs(socket_to_compute, {type, buffer});
- return;
- }
-
- /* Prepare inputs required to execute the node. */
- GValueMap<StringRef> node_inputs_map{allocator_};
- for (const InputSocketRef *input_socket : node->inputs()) {
- if (input_socket->is_available()) {
- Vector<GMutablePointer> values = this->get_input_values({node.context(), input_socket});
- for (int i = 0; i < values.size(); ++i) {
- /* Values from Multi Input Sockets are stored in input map with the format
- * <identifier>[<index>]. */
- blender::StringRefNull key = allocator_.copy_string(
- input_socket->identifier() + (i > 0 ? ("[" + std::to_string(i)) + "]" : ""));
- node_inputs_map.add_new_direct(key, std::move(values[i]));
- }
- }
- }
-
- /* Execute the node. */
- GValueMap<StringRef> node_outputs_map{allocator_};
- GeoNodeExecParams params{
- node, node_inputs_map, node_outputs_map, handle_map_, self_object_, modifier_, depsgraph_};
- this->execute_node(node, params);
-
- /* Forward computed outputs to linked input sockets. */
- for (const OutputSocketRef *output_socket : node->outputs()) {
- if (output_socket->is_available()) {
- GMutablePointer value = node_outputs_map.extract(output_socket->identifier());
- this->forward_to_inputs({node.context(), output_socket}, value);
- }
- }
- }
-
- void execute_node(const DNode node, GeoNodeExecParams params)
- {
- const bNode &bnode = params.node();
-
- this->store_ui_hints(node, params);
-
- /* Use the geometry-node-execute callback if it exists. */
- if (bnode.typeinfo->geometry_node_execute != nullptr) {
- bnode.typeinfo->geometry_node_execute(params);
- return;
- }
-
- /* Use the multi-function implementation if it exists. */
- const MultiFunction *multi_function = mf_by_node_.lookup_default(node, nullptr);
- if (multi_function != nullptr) {
- this->execute_multi_function_node(node, params, *multi_function);
- return;
- }
-
- /* Just output default values if no implementation exists. */
- this->execute_unknown_node(node, params);
- }
-
- void store_ui_hints(const DNode node, GeoNodeExecParams params) const
- {
- for (const InputSocketRef *socket_ref : node->inputs()) {
- if (!socket_ref->is_available()) {
- continue;
- }
- if (socket_ref->bsocket()->type != SOCK_GEOMETRY) {
- continue;
- }
- if (socket_ref->is_multi_input_socket()) {
- /* Not needed currently. */
- continue;
- }
-
- bNodeTree *btree_cow = node->btree();
- bNodeTree *btree_original = (bNodeTree *)DEG_get_original_id((ID *)btree_cow);
- const NodeTreeEvaluationContext context(*self_object_, *modifier_);
-
- const GeometrySet &geometry_set = params.get_input<GeometrySet>(socket_ref->identifier());
- const Vector<const GeometryComponent *> components = geometry_set.get_components_for_read();
-
- for (const GeometryComponent *component : components) {
- component->attribute_foreach(
- [&](StringRefNull attribute_name, const AttributeMetaData &meta_data) {
- BKE_nodetree_attribute_hint_add(*btree_original,
- context,
- *node->bnode(),
- attribute_name,
- meta_data.domain,
- meta_data.data_type);
- return true;
- });
- }
- }
- }
-
- void execute_multi_function_node(const DNode node,
- GeoNodeExecParams params,
- const MultiFunction &fn)
- {
- MFContextBuilder fn_context;
- MFParamsBuilder fn_params{fn, 1};
- Vector<GMutablePointer> input_data;
- for (const InputSocketRef *socket_ref : node->inputs()) {
- if (socket_ref->is_available()) {
- GMutablePointer data = params.extract_input(socket_ref->identifier());
- fn_params.add_readonly_single_input(GSpan(*data.type(), data.get(), 1));
- input_data.append(data);
- }
- }
- Vector<GMutablePointer> output_data;
- for (const OutputSocketRef *socket_ref : node->outputs()) {
- if (socket_ref->is_available()) {
- const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket_ref->typeinfo());
- void *buffer = allocator_.allocate(type.size(), type.alignment());
- fn_params.add_uninitialized_single_output(GMutableSpan(type, buffer, 1));
- output_data.append(GMutablePointer(type, buffer));
- }
- }
- fn.call(IndexRange(1), fn_params, fn_context);
- for (GMutablePointer value : input_data) {
- value.destruct();
- }
- int output_index = 0;
- for (const int i : node->outputs().index_range()) {
- if (node->output(i).is_available()) {
- GMutablePointer value = output_data[output_index];
- params.set_output_by_move(node->output(i).identifier(), value);
- value.destruct();
- output_index++;
- }
- }
- }
-
- void execute_unknown_node(const DNode node, GeoNodeExecParams params)
- {
- for (const OutputSocketRef *socket : node->outputs()) {
- if (socket->is_available()) {
- const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket->typeinfo());
- params.set_output_by_copy(socket->identifier(), {type, type.default_value()});
- }
- }
- }
-
- void forward_to_inputs(const DOutputSocket from_socket, GMutablePointer value_to_forward)
- {
- /* For all sockets that are linked with the from_socket push the value to their node. */
- Vector<DInputSocket> to_sockets_all;
- from_socket.foreach_target_socket(
- [&](DInputSocket to_socket) { to_sockets_all.append_non_duplicates(to_socket); });
-
- const CPPType &from_type = *value_to_forward.type();
- Vector<DInputSocket> to_sockets_same_type;
- for (const DInputSocket &to_socket : to_sockets_all) {
- const CPPType &to_type = *blender::nodes::socket_cpp_type_get(*to_socket->typeinfo());
- const std::pair<DInputSocket, DOutputSocket> key = std::make_pair(to_socket, from_socket);
- if (from_type == to_type) {
- to_sockets_same_type.append(to_socket);
- }
- else {
- void *buffer = allocator_.allocate(to_type.size(), to_type.alignment());
- if (conversions_.is_convertible(from_type, to_type)) {
- conversions_.convert(from_type, to_type, value_to_forward.get(), buffer);
- }
- else {
- to_type.copy_to_uninitialized(to_type.default_value(), buffer);
- }
- add_value_to_input_socket(key, GMutablePointer{to_type, buffer});
- }
- }
-
- if (to_sockets_same_type.size() == 0) {
- /* This value is not further used, so destruct it. */
- value_to_forward.destruct();
- }
- else if (to_sockets_same_type.size() == 1) {
- /* This value is only used on one input socket, no need to copy it. */
- const DInputSocket to_socket = to_sockets_same_type[0];
- const std::pair<DInputSocket, DOutputSocket> key = std::make_pair(to_socket, from_socket);
-
- add_value_to_input_socket(key, value_to_forward);
- }
- else {
- /* Multiple inputs use the value, make a copy for every input except for one. */
- const DInputSocket first_to_socket = to_sockets_same_type[0];
- Span<DInputSocket> other_to_sockets = to_sockets_same_type.as_span().drop_front(1);
- const CPPType &type = *value_to_forward.type();
- const std::pair<DInputSocket, DOutputSocket> first_key = std::make_pair(first_to_socket,
- from_socket);
- add_value_to_input_socket(first_key, value_to_forward);
- for (const DInputSocket &to_socket : other_to_sockets) {
- const std::pair<DInputSocket, DOutputSocket> key = std::make_pair(to_socket, from_socket);
- void *buffer = allocator_.allocate(type.size(), type.alignment());
- type.copy_to_uninitialized(value_to_forward.get(), buffer);
- add_value_to_input_socket(key, GMutablePointer{type, buffer});
- }
- }
- }
-
- void add_value_to_input_socket(const std::pair<DInputSocket, DOutputSocket> key,
- GMutablePointer value)
- {
- value_by_input_.add_new(key, value);
+static bool logging_enabled(const ModifierEvalContext *ctx)
+{
+ if (!DEG_is_active(ctx->depsgraph)) {
+ return false;
}
-
- GMutablePointer get_unlinked_input_value(const DInputSocket &socket,
- const CPPType &required_type)
- {
- bNodeSocket *bsocket = socket->bsocket();
- const CPPType &type = *blender::nodes::socket_cpp_type_get(*socket->typeinfo());
- void *buffer = allocator_.allocate(type.size(), type.alignment());
-
- if (bsocket->type == SOCK_OBJECT) {
- Object *object = socket->default_value<bNodeSocketValueObject>()->value;
- PersistentObjectHandle object_handle = handle_map_.lookup(object);
- new (buffer) PersistentObjectHandle(object_handle);
- }
- else if (bsocket->type == SOCK_COLLECTION) {
- Collection *collection = socket->default_value<bNodeSocketValueCollection>()->value;
- PersistentCollectionHandle collection_handle = handle_map_.lookup(collection);
- new (buffer) PersistentCollectionHandle(collection_handle);
- }
- else {
- blender::nodes::socket_cpp_value_get(*bsocket, buffer);
- }
-
- if (type == required_type) {
- return {type, buffer};
- }
- if (conversions_.is_convertible(type, required_type)) {
- void *converted_buffer = allocator_.allocate(required_type.size(),
- required_type.alignment());
- conversions_.convert(type, required_type, buffer, converted_buffer);
- type.destruct(buffer);
- return {required_type, converted_buffer};
- }
- void *default_buffer = allocator_.allocate(required_type.size(), required_type.alignment());
- required_type.copy_to_uninitialized(required_type.default_value(), default_buffer);
- return {required_type, default_buffer};
+ if ((ctx->flag & MOD_APPLY_ORCO) != 0) {
+ return false;
}
-};
+ return true;
+}
/**
* This code is responsible for creating the new property and also creating the group of
@@ -634,9 +304,7 @@ struct SocketPropertyType {
IDProperty *(*create_default_ui_prop)(const bNodeSocket &socket, const char *name);
PropertyType (*rna_subtype_get)(const bNodeSocket &socket);
bool (*is_correct_type)(const IDProperty &property);
- void (*init_cpp_value)(const IDProperty &property,
- const PersistentDataHandleMap &handles,
- void *r_value);
+ void (*init_cpp_value)(const IDProperty &property, void *r_value);
};
static IDProperty *socket_add_property(IDProperty *settings_prop_group,
@@ -659,6 +327,14 @@ static IDProperty *socket_add_property(IDProperty *settings_prop_group,
IDP_AddToGroup(ui_container, prop_ui_group);
}
+ /* Set property description (tooltip). */
+ IDPropertyTemplate property_description_template;
+ property_description_template.string.str = socket.description;
+ property_description_template.string.len = BLI_strnlen(socket.description, MAX_NAME) + 1;
+ property_description_template.string.subtype = IDP_STRING_SUB_UTF8;
+ IDProperty *description = IDP_New(IDP_STRING, &property_description_template, "description");
+ IDP_AddToGroup(prop_ui_group, description);
+
/* Create the properties for the socket's UI settings. */
if (property_type.create_min_ui_prop != nullptr) {
IDP_AddToGroup(prop_ui_group, property_type.create_min_ui_prop(socket, "min"));
@@ -720,10 +396,15 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso
[](const bNodeSocket &socket) {
return (PropertyType)((bNodeSocketValueFloat *)socket.default_value)->subtype;
},
- [](const IDProperty &property) { return property.type == IDP_FLOAT; },
- [](const IDProperty &property,
- const PersistentDataHandleMap &UNUSED(handles),
- void *r_value) { *(float *)r_value = IDP_Float(&property); },
+ [](const IDProperty &property) { return ELEM(property.type, IDP_FLOAT, IDP_DOUBLE); },
+ [](const IDProperty &property, void *r_value) {
+ if (property.type == IDP_FLOAT) {
+ *(float *)r_value = IDP_Float(&property);
+ }
+ else if (property.type == IDP_DOUBLE) {
+ *(float *)r_value = (float)IDP_Double(&property);
+ }
+ },
};
return &float_type;
}
@@ -757,9 +438,7 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso
return (PropertyType)((bNodeSocketValueInt *)socket.default_value)->subtype;
},
[](const IDProperty &property) { return property.type == IDP_INT; },
- [](const IDProperty &property,
- const PersistentDataHandleMap &UNUSED(handles),
- void *r_value) { *(int *)r_value = IDP_Int(&property); },
+ [](const IDProperty &property, void *r_value) { *(int *)r_value = IDP_Int(&property); },
};
return &int_type;
}
@@ -802,9 +481,9 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso
return property.type == IDP_ARRAY && property.subtype == IDP_FLOAT &&
property.len == 3;
},
- [](const IDProperty &property,
- const PersistentDataHandleMap &UNUSED(handles),
- void *r_value) { copy_v3_v3((float *)r_value, (const float *)IDP_Array(&property)); },
+ [](const IDProperty &property, void *r_value) {
+ copy_v3_v3((float *)r_value, (const float *)IDP_Array(&property));
+ },
};
return &vector_type;
}
@@ -834,9 +513,9 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso
},
nullptr,
[](const IDProperty &property) { return property.type == IDP_INT; },
- [](const IDProperty &property,
- const PersistentDataHandleMap &UNUSED(handles),
- void *r_value) { *(bool *)r_value = IDP_Int(&property) != 0; },
+ [](const IDProperty &property, void *r_value) {
+ *(bool *)r_value = IDP_Int(&property) != 0;
+ },
};
return &boolean_type;
}
@@ -856,9 +535,9 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso
},
nullptr,
[](const IDProperty &property) { return property.type == IDP_STRING; },
- [](const IDProperty &property,
- const PersistentDataHandleMap &UNUSED(handles),
- void *r_value) { new (r_value) std::string(IDP_String(&property)); },
+ [](const IDProperty &property, void *r_value) {
+ new (r_value) std::string(IDP_String(&property));
+ },
};
return &string_type;
}
@@ -875,10 +554,10 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso
nullptr,
nullptr,
[](const IDProperty &property) { return property.type == IDP_ID; },
- [](const IDProperty &property, const PersistentDataHandleMap &handles, void *r_value) {
+ [](const IDProperty &property, void *r_value) {
ID *id = IDP_Id(&property);
Object *object = (id && GS(id->name) == ID_OB) ? (Object *)id : nullptr;
- new (r_value) PersistentObjectHandle(handles.lookup(object));
+ *(Object **)r_value = object;
},
};
return &object_type;
@@ -896,10 +575,52 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso
nullptr,
nullptr,
[](const IDProperty &property) { return property.type == IDP_ID; },
- [](const IDProperty &property, const PersistentDataHandleMap &handles, void *r_value) {
+ [](const IDProperty &property, void *r_value) {
ID *id = IDP_Id(&property);
Collection *collection = (id && GS(id->name) == ID_GR) ? (Collection *)id : nullptr;
- new (r_value) PersistentCollectionHandle(handles.lookup(collection));
+ *(Collection **)r_value = collection;
+ },
+ };
+ return &collection_type;
+ }
+ case SOCK_TEXTURE: {
+ static const SocketPropertyType collection_type = {
+ [](const bNodeSocket &socket, const char *name) {
+ bNodeSocketValueTexture *value = (bNodeSocketValueTexture *)socket.default_value;
+ IDPropertyTemplate idprop = {0};
+ idprop.id = (ID *)value->value;
+ return IDP_New(IDP_ID, &idprop, name);
+ },
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ [](const IDProperty &property) { return property.type == IDP_ID; },
+ [](const IDProperty &property, void *r_value) {
+ ID *id = IDP_Id(&property);
+ Tex *texture = (id && GS(id->name) == ID_TE) ? (Tex *)id : nullptr;
+ *(Tex **)r_value = texture;
+ },
+ };
+ return &collection_type;
+ }
+ case SOCK_MATERIAL: {
+ static const SocketPropertyType collection_type = {
+ [](const bNodeSocket &socket, const char *name) {
+ bNodeSocketValueMaterial *value = (bNodeSocketValueMaterial *)socket.default_value;
+ IDPropertyTemplate idprop = {0};
+ idprop.id = (ID *)value->value;
+ return IDP_New(IDP_ID, &idprop, name);
+ },
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ [](const IDProperty &property) { return property.type == IDP_ID; },
+ [](const IDProperty &property, void *r_value) {
+ ID *id = IDP_Id(&property);
+ Material *material = (id && GS(id->name) == ID_MA) ? (Material *)id : nullptr;
+ *(Material **)r_value = material;
},
};
return &collection_type;
@@ -987,7 +708,6 @@ void MOD_nodes_init(Main *bmain, NodesModifierData *nmd)
}
static void initialize_group_input(NodesModifierData &nmd,
- const PersistentDataHandleMap &handle_map,
const bNodeSocket &socket,
const CPPType &cpp_type,
void *r_value)
@@ -1011,22 +731,7 @@ static void initialize_group_input(NodesModifierData &nmd,
blender::nodes::socket_cpp_value_get(socket, r_value);
return;
}
- property_type->init_cpp_value(*property, handle_map, r_value);
-}
-
-static void fill_data_handle_map(const NodesModifierSettings &settings,
- const DerivedNodeTree &tree,
- PersistentDataHandleMap &handle_map)
-{
- Set<ID *> used_ids;
- find_used_ids_from_settings(settings, used_ids);
- find_used_ids_from_nodes(*tree.root_context().tree().btree(), used_ids);
-
- int current_handle = 0;
- for (ID *id : used_ids) {
- handle_map.add(current_handle, *id);
- current_handle++;
- }
+ property_type->init_cpp_value(*property, r_value);
}
static void reset_tree_ui_storage(Span<const blender::nodes::NodeTreeRef *> trees,
@@ -1042,29 +747,190 @@ static void reset_tree_ui_storage(Span<const blender::nodes::NodeTreeRef *> tree
}
}
+static Vector<SpaceSpreadsheet *> find_spreadsheet_editors(Main *bmain)
+{
+ wmWindowManager *wm = (wmWindowManager *)bmain->wm.first;
+ if (wm == nullptr) {
+ return {};
+ }
+ Vector<SpaceSpreadsheet *> spreadsheets;
+ LISTBASE_FOREACH (wmWindow *, window, &wm->windows) {
+ bScreen *screen = BKE_workspace_active_screen_get(window->workspace_hook);
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
+ SpaceLink *sl = (SpaceLink *)area->spacedata.first;
+ if (sl->spacetype == SPACE_SPREADSHEET) {
+ spreadsheets.append((SpaceSpreadsheet *)sl);
+ }
+ }
+ }
+ return spreadsheets;
+}
+
+using PreviewSocketMap = blender::MultiValueMap<DSocket, uint64_t>;
+
+static DSocket try_find_preview_socket_in_node(const DNode node)
+{
+ for (const SocketRef *socket : node->outputs()) {
+ if (socket->bsocket()->type == SOCK_GEOMETRY) {
+ return {node.context(), socket};
+ }
+ }
+ for (const SocketRef *socket : node->inputs()) {
+ if (socket->bsocket()->type == SOCK_GEOMETRY &&
+ (socket->bsocket()->flag & SOCK_MULTI_INPUT) == 0) {
+ return {node.context(), socket};
+ }
+ }
+ return {};
+}
+
+static DSocket try_get_socket_to_preview_for_spreadsheet(SpaceSpreadsheet *sspreadsheet,
+ NodesModifierData *nmd,
+ const ModifierEvalContext *ctx,
+ const DerivedNodeTree &tree)
+{
+ Vector<SpreadsheetContext *> context_path = sspreadsheet->context_path;
+ if (context_path.size() < 3) {
+ return {};
+ }
+ if (context_path[0]->type != SPREADSHEET_CONTEXT_OBJECT) {
+ return {};
+ }
+ if (context_path[1]->type != SPREADSHEET_CONTEXT_MODIFIER) {
+ return {};
+ }
+ SpreadsheetContextObject *object_context = (SpreadsheetContextObject *)context_path[0];
+ if (object_context->object != DEG_get_original_object(ctx->object)) {
+ return {};
+ }
+ SpreadsheetContextModifier *modifier_context = (SpreadsheetContextModifier *)context_path[1];
+ if (StringRef(modifier_context->modifier_name) != nmd->modifier.name) {
+ return {};
+ }
+ for (SpreadsheetContext *context : context_path.as_span().drop_front(2)) {
+ if (context->type != SPREADSHEET_CONTEXT_NODE) {
+ return {};
+ }
+ }
+
+ Span<SpreadsheetContextNode *> nested_group_contexts =
+ context_path.as_span().drop_front(2).drop_back(1).cast<SpreadsheetContextNode *>();
+ SpreadsheetContextNode *last_context = (SpreadsheetContextNode *)context_path.last();
+
+ const DTreeContext *context = &tree.root_context();
+ for (SpreadsheetContextNode *node_context : nested_group_contexts) {
+ const NodeTreeRef &tree_ref = context->tree();
+ const NodeRef *found_node = nullptr;
+ for (const NodeRef *node_ref : tree_ref.nodes()) {
+ if (node_ref->name() == node_context->node_name) {
+ found_node = node_ref;
+ break;
+ }
+ }
+ if (found_node == nullptr) {
+ return {};
+ }
+ context = context->child_context(*found_node);
+ if (context == nullptr) {
+ return {};
+ }
+ }
+
+ const NodeTreeRef &tree_ref = context->tree();
+ for (const NodeRef *node_ref : tree_ref.nodes()) {
+ if (node_ref->name() == last_context->node_name) {
+ return try_find_preview_socket_in_node({context, node_ref});
+ }
+ }
+ return {};
+}
+
+static void find_sockets_to_preview(NodesModifierData *nmd,
+ const ModifierEvalContext *ctx,
+ const DerivedNodeTree &tree,
+ PreviewSocketMap &r_sockets_to_preview)
+{
+ Main *bmain = DEG_get_bmain(ctx->depsgraph);
+
+ /* Based on every visible spreadsheet context path, get a list of sockets that need to have their
+ * intermediate geometries cached for display. */
+ Vector<SpaceSpreadsheet *> spreadsheets = find_spreadsheet_editors(bmain);
+ for (SpaceSpreadsheet *sspreadsheet : spreadsheets) {
+ const DSocket socket = try_get_socket_to_preview_for_spreadsheet(sspreadsheet, nmd, ctx, tree);
+ if (socket) {
+ const uint64_t key = ED_spreadsheet_context_path_hash(sspreadsheet);
+ r_sockets_to_preview.add_non_duplicates(socket, key);
+ }
+ }
+}
+
+static void log_preview_socket_value(const Span<GPointer> values,
+ Object *object,
+ Span<uint64_t> keys)
+{
+ GeometrySet geometry_set = *(const GeometrySet *)values[0].get();
+ geometry_set.ensure_owns_direct_data();
+ for (uint64_t key : keys) {
+ BKE_object_preview_geometry_set_add(object, key, new GeometrySet(geometry_set));
+ }
+}
+
+static void log_ui_hints(const DSocket socket,
+ const Span<GPointer> values,
+ Object *self_object,
+ NodesModifierData *nmd)
+{
+ const DNode node = socket.node();
+ if (node->is_reroute_node() || socket->typeinfo()->type != SOCK_GEOMETRY) {
+ return;
+ }
+ bNodeTree *btree_cow = node->btree();
+ bNodeTree *btree_original = (bNodeTree *)DEG_get_original_id((ID *)btree_cow);
+ const NodeTreeEvaluationContext context{*self_object, nmd->modifier};
+ for (const GPointer &data : values) {
+ if (data.type() == &CPPType::get<GeometrySet>()) {
+ const GeometrySet &geometry_set = *(const GeometrySet *)data.get();
+ blender::bke::geometry_set_instances_attribute_foreach(
+ geometry_set,
+ [&](StringRefNull attribute_name, const AttributeMetaData &meta_data) {
+ BKE_nodetree_attribute_hint_add(*btree_original,
+ context,
+ *node->bnode(),
+ attribute_name,
+ meta_data.domain,
+ meta_data.data_type);
+ return true;
+ },
+ 8);
+ }
+ }
+}
+
/**
* Evaluate a node group to compute the output geometry.
* Currently, this uses a fairly basic and inefficient algorithm that might compute things more
* often than necessary. It's going to be replaced soon.
*/
static GeometrySet compute_geometry(const DerivedNodeTree &tree,
- Span<const OutputSocketRef *> group_input_sockets,
+ Span<const NodeRef *> group_input_nodes,
const InputSocketRef &socket_to_compute,
GeometrySet input_geometry_set,
NodesModifierData *nmd,
const ModifierEvalContext *ctx)
{
- blender::ResourceCollector resources;
- blender::LinearAllocator<> &allocator = resources.linear_allocator();
- blender::nodes::MultiFunctionByNode mf_by_node = get_multi_function_per_node(tree, resources);
-
- PersistentDataHandleMap handle_map;
- fill_data_handle_map(nmd->settings, tree, handle_map);
+ blender::ResourceScope scope;
+ blender::LinearAllocator<> &allocator = scope.linear_allocator();
+ blender::nodes::MultiFunctionByNode mf_by_node = get_multi_function_per_node(tree, scope);
Map<DOutputSocket, GMutablePointer> group_inputs;
const DTreeContext *root_context = &tree.root_context();
- if (group_input_sockets.size() > 0) {
+ for (const NodeRef *group_input_node : group_input_nodes) {
+ Span<const OutputSocketRef *> group_input_sockets = group_input_node->outputs().drop_back(1);
+ if (group_input_sockets.is_empty()) {
+ continue;
+ }
+
Span<const OutputSocketRef *> remaining_input_sockets = group_input_sockets;
/* If the group expects a geometry as first input, use the geometry that has been passed to
@@ -1072,7 +938,7 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree,
const OutputSocketRef *first_input_socket = group_input_sockets[0];
if (first_input_socket->bsocket()->type == SOCK_GEOMETRY) {
GeometrySet *geometry_set_in =
- allocator.construct<GeometrySet>(std::move(input_geometry_set)).release();
+ allocator.construct<GeometrySet>(input_geometry_set).release();
group_inputs.add_new({root_context, first_input_socket}, geometry_set_in);
remaining_input_sockets = remaining_input_sockets.drop_front(1);
}
@@ -1081,28 +947,44 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree,
for (const OutputSocketRef *socket : remaining_input_sockets) {
const CPPType &cpp_type = *blender::nodes::socket_cpp_type_get(*socket->typeinfo());
void *value_in = allocator.allocate(cpp_type.size(), cpp_type.alignment());
- initialize_group_input(*nmd, handle_map, *socket->bsocket(), cpp_type, value_in);
+ initialize_group_input(*nmd, *socket->bsocket(), cpp_type, value_in);
group_inputs.add_new({root_context, socket}, {cpp_type, value_in});
}
}
+ /* Don't keep a reference to the input geometry components to avoid copies during evaluation. */
+ input_geometry_set.clear();
+
Vector<DInputSocket> group_outputs;
group_outputs.append({root_context, &socket_to_compute});
- GeometryNodesEvaluator evaluator{group_inputs,
- group_outputs,
- mf_by_node,
- handle_map,
- ctx->object,
- (ModifierData *)nmd,
- ctx->depsgraph};
+ PreviewSocketMap preview_sockets;
+ find_sockets_to_preview(nmd, ctx, tree, preview_sockets);
- Vector<GMutablePointer> results = evaluator.execute();
- BLI_assert(results.size() == 1);
- GMutablePointer result = results[0];
-
- GeometrySet output_geometry = std::move(*(GeometrySet *)result.get());
- return output_geometry;
+ auto log_socket_value = [&](const DSocket socket, const Span<GPointer> values) {
+ if (!logging_enabled(ctx)) {
+ return;
+ }
+ Span<uint64_t> keys = preview_sockets.lookup(socket);
+ if (!keys.is_empty()) {
+ log_preview_socket_value(values, ctx->object, keys);
+ }
+ log_ui_hints(socket, values, ctx->object, nmd);
+ };
+
+ blender::modifiers::geometry_nodes::GeometryNodesEvaluationParams eval_params;
+ eval_params.input_values = group_inputs;
+ eval_params.output_sockets = group_outputs;
+ eval_params.mf_by_node = &mf_by_node;
+ eval_params.modifier_ = nmd;
+ eval_params.depsgraph = ctx->depsgraph;
+ eval_params.self_object = ctx->object;
+ eval_params.log_socket_value_fn = log_socket_value;
+ blender::modifiers::geometry_nodes::evaluate_geometry_nodes(eval_params);
+
+ BLI_assert(eval_params.r_output_values.size() == 1);
+ GMutablePointer result = eval_params.r_output_values[0];
+ return result.relocate_out<GeometrySet>();
}
/**
@@ -1174,18 +1056,10 @@ static void modifyGeometry(ModifierData *md,
Span<const NodeRef *> input_nodes = root_tree_ref.nodes_by_type("NodeGroupInput");
Span<const NodeRef *> output_nodes = root_tree_ref.nodes_by_type("NodeGroupOutput");
- if (input_nodes.size() > 1) {
- return;
- }
if (output_nodes.size() != 1) {
return;
}
- Span<const OutputSocketRef *> group_inputs;
- if (input_nodes.size() == 1) {
- group_inputs = input_nodes[0]->outputs().drop_back(1);
- }
-
Span<const InputSocketRef *> group_outputs = output_nodes[0]->inputs().drop_back(1);
if (group_outputs.size() == 0) {
@@ -1197,10 +1071,12 @@ static void modifyGeometry(ModifierData *md,
return;
}
- reset_tree_ui_storage(tree.used_node_tree_refs(), *ctx->object, *md);
+ if (logging_enabled(ctx)) {
+ reset_tree_ui_storage(tree.used_node_tree_refs(), *ctx->object, *md);
+ }
geometry_set = compute_geometry(
- tree, group_inputs, *group_outputs[0], std::move(geometry_set), nmd, ctx);
+ tree, input_nodes, *group_outputs[0], std::move(geometry_set), nmd, ctx);
}
static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh)
@@ -1209,6 +1085,11 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
geometry_set.get_component_for_write<MeshComponent>().copy_vertex_group_names_from_object(
*ctx->object);
modifyGeometry(md, ctx, geometry_set);
+
+ /* This function is only called when applying modifiers. In this case it makes sense to realize
+ * instances, otherwise in some cases there might be no results when applying the modifier. */
+ geometry_set = blender::bke::geometry_set_realize_mesh_for_modifier(geometry_set);
+
Mesh *new_mesh = geometry_set.get_component_for_write<MeshComponent>().release();
if (new_mesh == nullptr) {
return BKE_mesh_new_nomain(0, 0, 0, 0, 0);
@@ -1242,36 +1123,45 @@ static void draw_property_for_socket(uiLayout *layout,
/* IDProperties can be removed with python, so there could be a situation where
* there isn't a property for a socket or it doesn't have the correct type. */
- if (property != nullptr && property_type->is_correct_type(*property)) {
+ if (property == nullptr || !property_type->is_correct_type(*property)) {
+ return;
+ }
- char socket_id_esc[sizeof(socket.identifier) * 2];
- BLI_str_escape(socket_id_esc, socket.identifier, sizeof(socket_id_esc));
+ char socket_id_esc[sizeof(socket.identifier) * 2];
+ BLI_str_escape(socket_id_esc, socket.identifier, sizeof(socket_id_esc));
- char rna_path[sizeof(socket_id_esc) + 4];
- BLI_snprintf(rna_path, ARRAY_SIZE(rna_path), "[\"%s\"]", socket_id_esc);
+ char rna_path[sizeof(socket_id_esc) + 4];
+ BLI_snprintf(rna_path, ARRAY_SIZE(rna_path), "[\"%s\"]", socket_id_esc);
- /* Use #uiItemPointerR to draw pointer properties because #uiItemR would not have enough
- * information about what type of ID to select for editing the values. This is because
- * pointer IDProperties contain no information about their type. */
- switch (socket.type) {
- case SOCK_OBJECT: {
- uiItemPointerR(
- layout, md_ptr, rna_path, bmain_ptr, "objects", socket.name, ICON_OBJECT_DATA);
- break;
- }
- case SOCK_COLLECTION: {
- uiItemPointerR(layout,
- md_ptr,
- rna_path,
- bmain_ptr,
- "collections",
- socket.name,
- ICON_OUTLINER_COLLECTION);
- break;
- }
- default:
- uiItemR(layout, md_ptr, rna_path, 0, socket.name, ICON_NONE);
+ /* Use #uiItemPointerR to draw pointer properties because #uiItemR would not have enough
+ * information about what type of ID to select for editing the values. This is because
+ * pointer IDProperties contain no information about their type. */
+ switch (socket.type) {
+ case SOCK_OBJECT: {
+ uiItemPointerR(
+ layout, md_ptr, rna_path, bmain_ptr, "objects", socket.name, ICON_OBJECT_DATA);
+ break;
+ }
+ case SOCK_COLLECTION: {
+ uiItemPointerR(layout,
+ md_ptr,
+ rna_path,
+ bmain_ptr,
+ "collections",
+ socket.name,
+ ICON_OUTLINER_COLLECTION);
+ break;
+ }
+ case SOCK_MATERIAL: {
+ uiItemPointerR(layout, md_ptr, rna_path, bmain_ptr, "materials", socket.name, ICON_MATERIAL);
+ break;
+ }
+ case SOCK_TEXTURE: {
+ uiItemPointerR(layout, md_ptr, rna_path, bmain_ptr, "textures", socket.name, ICON_TEXTURE);
+ break;
}
+ default:
+ uiItemR(layout, md_ptr, rna_path, 0, socket.name, ICON_NONE);
}
}
@@ -1359,6 +1249,7 @@ static void requiredDataMask(Object *UNUSED(ob),
/* We don't know what the node tree will need. If there are vertex groups, it is likely that the
* node tree wants to access them. */
r_cddata_masks->vmask |= CD_MASK_MDEFORMVERT;
+ r_cddata_masks->vmask |= CD_MASK_PROP_ALL;
}
ModifierTypeInfo modifierType_Nodes = {
@@ -1382,7 +1273,6 @@ ModifierTypeInfo modifierType_Nodes = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ nullptr,
/* modifyGeometrySet */ modifyGeometrySet,
- /* modifyVolume */ nullptr,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc
new file mode 100644
index 00000000000..170ad0f2c48
--- /dev/null
+++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc
@@ -0,0 +1,1553 @@
+/*
+ * 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.
+ */
+
+#include "MOD_nodes_evaluator.hh"
+
+#include "NOD_geometry_exec.hh"
+#include "NOD_type_conversions.hh"
+
+#include "DEG_depsgraph_query.h"
+
+#include "FN_generic_value_map.hh"
+#include "FN_multi_function.hh"
+
+#include "BLI_enumerable_thread_specific.hh"
+#include "BLI_stack.hh"
+#include "BLI_task.h"
+#include "BLI_task.hh"
+#include "BLI_vector_set.hh"
+
+namespace blender::modifiers::geometry_nodes {
+
+using fn::CPPType;
+using fn::GValueMap;
+using nodes::GeoNodeExecParams;
+using namespace fn::multi_function_types;
+
+enum class ValueUsage : uint8_t {
+ /* The value is definitely used. */
+ Required,
+ /* The value may be used. */
+ Maybe,
+ /* The value will definitely not be used. */
+ Unused,
+};
+
+struct SingleInputValue {
+ /**
+ * Points either to null or to a value of the type of input.
+ */
+ void *value = nullptr;
+};
+
+struct MultiInputValueItem {
+ /**
+ * The socket where this value is coming from. This is required to sort the inputs correctly
+ * based on the link order later on.
+ */
+ DSocket origin;
+ /**
+ * Should only be null directly after construction. After that it should always point to a value
+ * of the correct type.
+ */
+ void *value = nullptr;
+};
+
+struct MultiInputValue {
+ /**
+ * Collection of all the inputs that have been provided already. Note, the same origin can occur
+ * multiple times. However, it is guaranteed that if two items have the same origin, they will
+ * also have the same value (the pointer is different, but they point to values that would
+ * compare equal).
+ */
+ Vector<MultiInputValueItem> items;
+ /**
+ * Number of items that need to be added until all inputs have been provided.
+ */
+ int expected_size = 0;
+};
+
+struct InputState {
+
+ /**
+ * Type of the socket. If this is null, the socket should just be ignored.
+ */
+ const CPPType *type = nullptr;
+
+ /**
+ * Value of this input socket. By default, the value is empty. When other nodes are done
+ * computing their outputs, the computed values will be forwarded to linked input sockets.
+ * The value will then live here until it is consumed by the node or it was found that the value
+ * is not needed anymore.
+ * Whether the `single` or `multi` value is used depends on the socket.
+ */
+ union {
+ SingleInputValue *single;
+ MultiInputValue *multi;
+ } value;
+
+ /**
+ * How the node intends to use this input. By default all inputs may be used. Based on which
+ * outputs are used, a node can tell the evaluator that an input will definitely be used or is
+ * never used. This allows the evaluator to free values early, avoid copies and other unnecessary
+ * computations.
+ */
+ ValueUsage usage = ValueUsage::Maybe;
+
+ /**
+ * True when this input is/was used for an execution. While a node is running, only the inputs
+ * that have this set to true are allowed to be used. This makes sure that inputs created while
+ * the node is running correctly trigger the node to run again. Furthermore, it gives the node a
+ * consistent view of which inputs are available that does not change unexpectedly.
+ *
+ * While the node is running, this can be checked without a lock, because no one is writing to
+ * it. If this is true, the value can be read without a lock as well, because the value is not
+ * changed by others anymore.
+ */
+ bool was_ready_for_execution = false;
+};
+
+struct OutputState {
+ /**
+ * If this output has been computed and forwarded already. If this is true, the value is not
+ * computed/forwarded again.
+ */
+ bool has_been_computed = false;
+
+ /**
+ * Keeps track of how the output value is used. If a connected input becomes required, this
+ * output has to become required as well. The output becomes ignored when it has zero potential
+ * users that are counted below.
+ */
+ ValueUsage output_usage = ValueUsage::Maybe;
+
+ /**
+ * This is a copy of `output_usage` that is done right before node execution starts. This is
+ * done so that the node gets a consistent view of what outputs are used, even when this changes
+ * while the node is running (the node might be reevaluated in that case).
+ *
+ * While the node is running, this can be checked without a lock, because no one is writing to
+ * it.
+ */
+ ValueUsage output_usage_for_execution = ValueUsage::Maybe;
+
+ /**
+ * Counts how many times the value from this output might be used. If this number reaches zero,
+ * the output is not needed anymore.
+ */
+ int potential_users = 0;
+};
+
+enum class NodeScheduleState {
+ /**
+ * Default state of every node.
+ */
+ NotScheduled,
+ /**
+ * The node has been added to the task group and will be executed by it in the future.
+ */
+ Scheduled,
+ /**
+ * The node is currently running.
+ */
+ Running,
+ /**
+ * The node is running and has been rescheduled while running. In this case the node will run
+ * again. However, we don't add it to the task group immediately, because then the node might run
+ * twice at the same time, which is not allowed. Instead, once the node is done running, it will
+ * reschedule itself.
+ */
+ RunningAndRescheduled,
+};
+
+struct NodeState {
+ /**
+ * Needs to be locked when any data in this state is accessed that is not explicitly marked as
+ * otherwise.
+ */
+ std::mutex mutex;
+
+ /**
+ * States of the individual input and output sockets. One can index into these arrays without
+ * locking. However, to access the data inside a lock is generally necessary.
+ *
+ * These spans have to be indexed with the socket index. Unavailable sockets have a state as
+ * well. Maybe we can handle unavailable sockets differently in Blender in general, so I did not
+ * want to add complexity around it here.
+ */
+ MutableSpan<InputState> inputs;
+ MutableSpan<OutputState> outputs;
+
+ /**
+ * Nodes that don't support laziness have some special handling the first time they are executed.
+ */
+ bool non_lazy_node_is_initialized = false;
+
+ /**
+ * Used to check that nodes that don't support laziness do not run more than once.
+ */
+ bool has_been_executed = false;
+
+ /**
+ * Becomes true when the node will never be executed again and its inputs are destructed.
+ * Generally, a node has finished once all of its outputs with (potential) users have been
+ * computed.
+ */
+ bool node_has_finished = false;
+
+ /**
+ * Counts the number of values that still have to be forwarded to this node until it should run
+ * again. It counts values from a multi input socket separately.
+ * This is used as an optimization so that nodes are not scheduled unnecessarily in many cases.
+ */
+ int missing_required_inputs = 0;
+
+ /**
+ * A node is always in one specific schedule state. This helps to ensure that the same node does
+ * not run twice at the same time accidentally.
+ */
+ NodeScheduleState schedule_state = NodeScheduleState::NotScheduled;
+};
+
+/**
+ * Container for a node and its state. Packing them into a single struct allows the use of
+ * `VectorSet` instead of a `Map` for `node_states_` which simplifies parallel loops over all
+ * states.
+ *
+ * Equality operators and a hash function for `DNode` are provided so that one can lookup this type
+ * in `node_states_` just with a `DNode`.
+ */
+struct NodeWithState {
+ DNode node;
+ /* Store a pointer instead of `NodeState` directly to keep it small and movable. */
+ NodeState *state = nullptr;
+
+ friend bool operator==(const NodeWithState &a, const NodeWithState &b)
+ {
+ return a.node == b.node;
+ }
+
+ friend bool operator==(const NodeWithState &a, const DNode &b)
+ {
+ return a.node == b;
+ }
+
+ friend bool operator==(const DNode &a, const NodeWithState &b)
+ {
+ return a == b.node;
+ }
+
+ uint64_t hash() const
+ {
+ return node.hash();
+ }
+
+ static uint64_t hash_as(const DNode &node)
+ {
+ return node.hash();
+ }
+};
+
+class GeometryNodesEvaluator;
+
+/**
+ * Utility class that locks the state of a node. Having this is a separate class is useful because
+ * it allows methods to communicate that they expect the node to be locked.
+ */
+class LockedNode : NonCopyable, NonMovable {
+ private:
+ GeometryNodesEvaluator &evaluator_;
+
+ public:
+ /**
+ * This is the node that is currently locked.
+ */
+ const DNode node;
+ NodeState &node_state;
+
+ /**
+ * Used to delay notifying (and therefore locking) other nodes until the current node is not
+ * locked anymore. This might not be strictly necessary to avoid deadlocks in the current code,
+ * but it is a good measure to avoid accidentally adding a deadlock later on. By not locking
+ * more than one node per thread at a time, deadlocks are avoided.
+ *
+ * The notifications will be send right after the node is not locked anymore.
+ */
+ Vector<DOutputSocket> delayed_required_outputs;
+ Vector<DOutputSocket> delayed_unused_outputs;
+ Vector<DNode> delayed_scheduled_nodes;
+
+ LockedNode(GeometryNodesEvaluator &evaluator, const DNode node, NodeState &node_state)
+ : evaluator_(evaluator), node(node), node_state(node_state)
+ {
+ node_state.mutex.lock();
+ }
+
+ ~LockedNode();
+};
+
+static const CPPType *get_socket_cpp_type(const DSocket socket)
+{
+ return nodes::socket_cpp_type_get(*socket->typeinfo());
+}
+
+static const CPPType *get_socket_cpp_type(const SocketRef &socket)
+{
+ return nodes::socket_cpp_type_get(*socket.typeinfo());
+}
+
+static bool node_supports_laziness(const DNode node)
+{
+ return node->typeinfo()->geometry_node_execute_supports_laziness;
+}
+
+/** Implements the callbacks that might be called when a node is executed. */
+class NodeParamsProvider : public nodes::GeoNodeExecParamsProvider {
+ private:
+ GeometryNodesEvaluator &evaluator_;
+ NodeState &node_state_;
+
+ public:
+ NodeParamsProvider(GeometryNodesEvaluator &evaluator, DNode dnode, NodeState &node_state);
+
+ bool can_get_input(StringRef identifier) const override;
+ bool can_set_output(StringRef identifier) const override;
+ GMutablePointer extract_input(StringRef identifier) override;
+ Vector<GMutablePointer> extract_multi_input(StringRef identifier) override;
+ GPointer get_input(StringRef identifier) const override;
+ GMutablePointer alloc_output_value(const CPPType &type) override;
+ void set_output(StringRef identifier, GMutablePointer value) override;
+ void set_input_unused(StringRef identifier) override;
+ bool output_is_required(StringRef identifier) const override;
+
+ bool lazy_require_input(StringRef identifier) override;
+ bool lazy_output_is_required(StringRef identifier) const override;
+};
+
+class GeometryNodesEvaluator {
+ private:
+ /**
+ * This allocator lives on after the evaluator has been destructed. Therefore outputs of the
+ * entire evaluator should be allocated here.
+ */
+ LinearAllocator<> &outer_allocator_;
+ /**
+ * A local linear allocator for each thread. Only use this for values that do not need to live
+ * longer than the lifetime of the evaluator itself. Considerations for the future:
+ * - We could use an allocator that can free here, some temporary values don't live long.
+ * - If we ever run into false sharing bottlenecks, we could use local allocators that allocate
+ * on cache line boundaries. Note, just because a value is allocated in one specific thread,
+ * does not mean that it will only be used by that thread.
+ */
+ EnumerableThreadSpecific<LinearAllocator<>> local_allocators_;
+
+ /**
+ * Every node that is reachable from the output gets its own state. Once all states have been
+ * constructed, this map can be used for lookups from multiple threads.
+ */
+ VectorSet<NodeWithState> node_states_;
+
+ /**
+ * Contains all the tasks for the nodes that are currently scheduled.
+ */
+ TaskPool *task_pool_ = nullptr;
+
+ GeometryNodesEvaluationParams &params_;
+ const blender::nodes::DataTypeConversions &conversions_;
+
+ friend NodeParamsProvider;
+
+ public:
+ GeometryNodesEvaluator(GeometryNodesEvaluationParams &params)
+ : outer_allocator_(params.allocator),
+ params_(params),
+ conversions_(blender::nodes::get_implicit_type_conversions())
+ {
+ }
+
+ void execute()
+ {
+ task_pool_ = BLI_task_pool_create(this, TASK_PRIORITY_HIGH, TASK_ISOLATION_OFF);
+
+ this->create_states_for_reachable_nodes();
+ this->forward_group_inputs();
+ this->schedule_initial_nodes();
+
+ /* This runs until all initially requested inputs have been computed. */
+ BLI_task_pool_work_and_wait(task_pool_);
+ BLI_task_pool_free(task_pool_);
+
+ this->extract_group_outputs();
+ this->destruct_node_states();
+ }
+
+ void create_states_for_reachable_nodes()
+ {
+ /* This does a depth first search for all the nodes that are reachable from the group
+ * outputs. This finds all nodes that are relevant. */
+ Stack<DNode> nodes_to_check;
+ /* Start at the output sockets. */
+ for (const DInputSocket &socket : params_.output_sockets) {
+ nodes_to_check.push(socket.node());
+ }
+ /* Use the local allocator because the states do not need to outlive the evaluator. */
+ LinearAllocator<> &allocator = local_allocators_.local();
+ while (!nodes_to_check.is_empty()) {
+ const DNode node = nodes_to_check.pop();
+ if (node_states_.contains_as(node)) {
+ /* This node has been handled already. */
+ continue;
+ }
+ /* Create a new state for the node. */
+ NodeState &node_state = *allocator.construct<NodeState>().release();
+ node_states_.add_new({node, &node_state});
+
+ /* Push all linked origins on the stack. */
+ for (const InputSocketRef *input_ref : node->inputs()) {
+ const DInputSocket input{node.context(), input_ref};
+ input.foreach_origin_socket(
+ [&](const DSocket origin) { nodes_to_check.push(origin.node()); });
+ }
+ }
+
+ /* Initialize the more complex parts of the node states in parallel. At this point no new
+ * node states are added anymore, so it is safe to lookup states from `node_states_` from
+ * multiple threads. */
+ parallel_for(IndexRange(node_states_.size()), 50, [&, this](const IndexRange range) {
+ LinearAllocator<> &allocator = this->local_allocators_.local();
+ for (const NodeWithState &item : node_states_.as_span().slice(range)) {
+ this->initialize_node_state(item.node, *item.state, allocator);
+ }
+ });
+ }
+
+ void initialize_node_state(const DNode node, NodeState &node_state, LinearAllocator<> &allocator)
+ {
+ /* Construct arrays of the correct size. */
+ node_state.inputs = allocator.construct_array<InputState>(node->inputs().size());
+ node_state.outputs = allocator.construct_array<OutputState>(node->outputs().size());
+
+ /* Initialize input states. */
+ for (const int i : node->inputs().index_range()) {
+ InputState &input_state = node_state.inputs[i];
+ const DInputSocket socket = node.input(i);
+ if (!socket->is_available()) {
+ /* Unavailable sockets should never be used. */
+ input_state.type = nullptr;
+ input_state.usage = ValueUsage::Unused;
+ continue;
+ }
+ const CPPType *type = get_socket_cpp_type(socket);
+ input_state.type = type;
+ if (type == nullptr) {
+ /* This is not a known data socket, it shouldn't be used. */
+ input_state.usage = ValueUsage::Unused;
+ continue;
+ }
+ /* Construct the correct struct that can hold the input(s). */
+ if (socket->is_multi_input_socket()) {
+ input_state.value.multi = allocator.construct<MultiInputValue>().release();
+ /* Count how many values should be added until the socket is complete. */
+ socket.foreach_origin_socket(
+ [&](DSocket UNUSED(origin)) { input_state.value.multi->expected_size++; });
+ /* If no links are connected, we do read the value from socket itself. */
+ if (input_state.value.multi->expected_size == 0) {
+ input_state.value.multi->expected_size = 1;
+ }
+ }
+ else {
+ input_state.value.single = allocator.construct<SingleInputValue>().release();
+ }
+ }
+ /* Initialize output states. */
+ for (const int i : node->outputs().index_range()) {
+ OutputState &output_state = node_state.outputs[i];
+ const DOutputSocket socket = node.output(i);
+ if (!socket->is_available()) {
+ /* Unavailable outputs should never be used. */
+ output_state.output_usage = ValueUsage::Unused;
+ continue;
+ }
+ const CPPType *type = get_socket_cpp_type(socket);
+ if (type == nullptr) {
+ /* Non data sockets should never be used. */
+ output_state.output_usage = ValueUsage::Unused;
+ continue;
+ }
+ /* Count the number of potential users for this socket. */
+ socket.foreach_target_socket(
+ [&, this](const DInputSocket target_socket) {
+ const DNode target_node = target_socket.node();
+ if (!this->node_states_.contains_as(target_node)) {
+ /* The target node is not computed because it is not computed to the output. */
+ return;
+ }
+ output_state.potential_users += 1;
+ },
+ {});
+ if (output_state.potential_users == 0) {
+ /* If it does not have any potential users, it is unused. */
+ output_state.output_usage = ValueUsage::Unused;
+ }
+ }
+ }
+
+ void destruct_node_states()
+ {
+ parallel_for(IndexRange(node_states_.size()), 50, [&, this](const IndexRange range) {
+ for (const NodeWithState &item : node_states_.as_span().slice(range)) {
+ this->destruct_node_state(item.node, *item.state);
+ }
+ });
+ }
+
+ void destruct_node_state(const DNode node, NodeState &node_state)
+ {
+ /* Need to destruct stuff manually, because it's allocated by a custom allocator. */
+ for (const int i : node->inputs().index_range()) {
+ InputState &input_state = node_state.inputs[i];
+ if (input_state.type == nullptr) {
+ continue;
+ }
+ const InputSocketRef &socket_ref = node->input(i);
+ if (socket_ref.is_multi_input_socket()) {
+ MultiInputValue &multi_value = *input_state.value.multi;
+ for (MultiInputValueItem &item : multi_value.items) {
+ input_state.type->destruct(item.value);
+ }
+ multi_value.~MultiInputValue();
+ }
+ else {
+ SingleInputValue &single_value = *input_state.value.single;
+ void *value = single_value.value;
+ if (value != nullptr) {
+ input_state.type->destruct(value);
+ }
+ single_value.~SingleInputValue();
+ }
+ }
+
+ destruct_n(node_state.inputs.data(), node_state.inputs.size());
+ destruct_n(node_state.outputs.data(), node_state.outputs.size());
+
+ node_state.~NodeState();
+ }
+
+ void forward_group_inputs()
+ {
+ for (auto &&item : params_.input_values.items()) {
+ const DOutputSocket socket = item.key;
+ GMutablePointer value = item.value;
+ this->log_socket_value(socket, value);
+
+ const DNode node = socket.node();
+ if (!node_states_.contains_as(node)) {
+ /* The socket is not connected to any output. */
+ value.destruct();
+ continue;
+ }
+ this->forward_output(socket, value);
+ }
+ }
+
+ void schedule_initial_nodes()
+ {
+ for (const DInputSocket &socket : params_.output_sockets) {
+ const DNode node = socket.node();
+ NodeState &node_state = this->get_node_state(node);
+ LockedNode locked_node{*this, node, node_state};
+ /* Setting an input as required will schedule any linked node. */
+ this->set_input_required(locked_node, socket);
+ }
+ }
+
+ void schedule_node(LockedNode &locked_node)
+ {
+ switch (locked_node.node_state.schedule_state) {
+ case NodeScheduleState::NotScheduled: {
+ /* The node will be scheduled once it is not locked anymore. We could schedule the node
+ * right here, but that would result in a deadlock if the task pool decides to run the task
+ * immediately (this only happens when Blender is started with a single thread). */
+ locked_node.node_state.schedule_state = NodeScheduleState::Scheduled;
+ locked_node.delayed_scheduled_nodes.append(locked_node.node);
+ break;
+ }
+ case NodeScheduleState::Scheduled: {
+ /* Scheduled already, nothing to do. */
+ break;
+ }
+ case NodeScheduleState::Running: {
+ /* Reschedule node while it is running.
+ * The node will reschedule itself when it is done. */
+ locked_node.node_state.schedule_state = NodeScheduleState::RunningAndRescheduled;
+ break;
+ }
+ case NodeScheduleState::RunningAndRescheduled: {
+ /* Scheduled already, nothing to do. */
+ break;
+ }
+ }
+ }
+
+ static void run_node_from_task_pool(TaskPool *task_pool, void *task_data)
+ {
+ void *user_data = BLI_task_pool_user_data(task_pool);
+ GeometryNodesEvaluator &evaluator = *(GeometryNodesEvaluator *)user_data;
+ const NodeWithState *node_with_state = (const NodeWithState *)task_data;
+
+ evaluator.node_task_run(node_with_state->node, *node_with_state->state);
+ }
+
+ void node_task_run(const DNode node, NodeState &node_state)
+ {
+ /* These nodes are sometimes scheduled. We could also check for them in other places, but
+ * it's the easiest to do it here. */
+ if (node->is_group_input_node() || node->is_group_output_node()) {
+ return;
+ }
+
+ const bool do_execute_node = this->node_task_preprocessing(node, node_state);
+
+ /* Only execute the node if all prerequisites are met. There has to be an output that is
+ * required and all required inputs have to be provided already. */
+ if (do_execute_node) {
+ this->execute_node(node, node_state);
+ }
+
+ this->node_task_postprocessing(node, node_state);
+ }
+
+ bool node_task_preprocessing(const DNode node, NodeState &node_state)
+ {
+ LockedNode locked_node{*this, node, node_state};
+ BLI_assert(node_state.schedule_state == NodeScheduleState::Scheduled);
+ node_state.schedule_state = NodeScheduleState::Running;
+
+ /* Early return if the node has finished already. */
+ if (locked_node.node_state.node_has_finished) {
+ return false;
+ }
+ /* Prepare outputs and check if actually any new outputs have to be computed. */
+ if (!this->prepare_node_outputs_for_execution(locked_node)) {
+ return false;
+ }
+ /* Initialize nodes that don't support laziness. This is done after at least one output is
+ * required and before we check that all required inputs are provided. This reduces the
+ * number of "round-trips" through the task pool by one for most nodes. */
+ if (!node_state.non_lazy_node_is_initialized && !node_supports_laziness(node)) {
+ this->initialize_non_lazy_node(locked_node);
+ node_state.non_lazy_node_is_initialized = true;
+ }
+ /* Prepare inputs and check if all required inputs are provided. */
+ if (!this->prepare_node_inputs_for_execution(locked_node)) {
+ return false;
+ }
+ return true;
+ }
+
+ /* A node is finished when it has computed all outputs that may be used. */
+ bool finish_node_if_possible(LockedNode &locked_node)
+ {
+ if (locked_node.node_state.node_has_finished) {
+ /* Early return in case this node is known to have finished already. */
+ return true;
+ }
+
+ /* Check if there is any output that might be used but has not been computed yet. */
+ bool has_remaining_output = false;
+ for (OutputState &output_state : locked_node.node_state.outputs) {
+ if (output_state.has_been_computed) {
+ continue;
+ }
+ if (output_state.output_usage != ValueUsage::Unused) {
+ has_remaining_output = true;
+ break;
+ }
+ }
+ if (!has_remaining_output) {
+ /* If there are no remaining outputs, all the inputs can be destructed and/or can become
+ * unused. This can also trigger a chain reaction where nodes to the left become finished
+ * too. */
+ for (const int i : locked_node.node->inputs().index_range()) {
+ const DInputSocket socket = locked_node.node.input(i);
+ InputState &input_state = locked_node.node_state.inputs[i];
+ if (input_state.usage == ValueUsage::Maybe) {
+ this->set_input_unused(locked_node, socket);
+ }
+ else if (input_state.usage == ValueUsage::Required) {
+ /* The value was required, so it cannot become unused. However, we can destruct the
+ * value. */
+ this->destruct_input_value_if_exists(locked_node, socket);
+ }
+ }
+ locked_node.node_state.node_has_finished = true;
+ }
+ return locked_node.node_state.node_has_finished;
+ }
+
+ bool prepare_node_outputs_for_execution(LockedNode &locked_node)
+ {
+ bool execution_is_necessary = false;
+ for (OutputState &output_state : locked_node.node_state.outputs) {
+ /* Update the output usage for execution to the latest value. */
+ output_state.output_usage_for_execution = output_state.output_usage;
+ if (!output_state.has_been_computed) {
+ if (output_state.output_usage == ValueUsage::Required) {
+ /* Only evaluate when there is an output that is required but has not been computed. */
+ execution_is_necessary = true;
+ }
+ }
+ }
+ return execution_is_necessary;
+ }
+
+ void initialize_non_lazy_node(LockedNode &locked_node)
+ {
+ for (const int i : locked_node.node->inputs().index_range()) {
+ InputState &input_state = locked_node.node_state.inputs[i];
+ if (input_state.type == nullptr) {
+ /* Ignore unavailable/non-data sockets. */
+ continue;
+ }
+ /* Nodes that don't support laziness require all inputs. */
+ const DInputSocket input_socket = locked_node.node.input(i);
+ this->set_input_required(locked_node, input_socket);
+ }
+ }
+
+ /**
+ * Checks if requested inputs are available and "marks" all the inputs that are available
+ * during the node execution. Inputs that are provided after this function ends but before the
+ * node is executed, cannot be read by the node in the execution (note that this only affects
+ * nodes that support lazy inputs).
+ */
+ bool prepare_node_inputs_for_execution(LockedNode &locked_node)
+ {
+ for (const int i : locked_node.node_state.inputs.index_range()) {
+ InputState &input_state = locked_node.node_state.inputs[i];
+ if (input_state.type == nullptr) {
+ /* Ignore unavailable and non-data sockets. */
+ continue;
+ }
+ const DInputSocket socket = locked_node.node.input(i);
+ const bool is_required = input_state.usage == ValueUsage::Required;
+
+ /* No need to check this socket again. */
+ if (input_state.was_ready_for_execution) {
+ continue;
+ }
+
+ if (socket->is_multi_input_socket()) {
+ MultiInputValue &multi_value = *input_state.value.multi;
+ /* Checks if all the linked sockets have been provided already. */
+ if (multi_value.items.size() == multi_value.expected_size) {
+ input_state.was_ready_for_execution = true;
+ this->log_socket_value(socket, input_state, multi_value.items);
+ }
+ else if (is_required) {
+ /* The input is required but is not fully provided yet. Therefore the node cannot be
+ * executed yet. */
+ return false;
+ }
+ }
+ else {
+ SingleInputValue &single_value = *input_state.value.single;
+ if (single_value.value != nullptr) {
+ input_state.was_ready_for_execution = true;
+ this->log_socket_value(socket, GPointer{input_state.type, single_value.value});
+ }
+ else if (is_required) {
+ /* The input is required but has not been provided yet. Therefore the node cannot be
+ * executed yet. */
+ return false;
+ }
+ }
+ }
+ /* All required inputs have been provided. */
+ return true;
+ }
+
+ /**
+ * Actually execute the node. All the required inputs are available and at least one output is
+ * required.
+ */
+ void execute_node(const DNode node, NodeState &node_state)
+ {
+ const bNode &bnode = *node->bnode();
+
+ if (node_state.has_been_executed) {
+ if (!node_supports_laziness(node)) {
+ /* Nodes that don't support laziness must not be executed more than once. */
+ BLI_assert_unreachable();
+ }
+ }
+ node_state.has_been_executed = true;
+
+ /* Use the geometry node execute callback if it exists. */
+ if (bnode.typeinfo->geometry_node_execute != nullptr) {
+ this->execute_geometry_node(node, node_state);
+ return;
+ }
+
+ /* Use the multi-function implementation if it exists. */
+ const MultiFunction *multi_function = params_.mf_by_node->lookup_default(node, nullptr);
+ if (multi_function != nullptr) {
+ this->execute_multi_function_node(node, *multi_function, node_state);
+ return;
+ }
+
+ this->execute_unknown_node(node, node_state);
+ }
+
+ void execute_geometry_node(const DNode node, NodeState &node_state)
+ {
+ const bNode &bnode = *node->bnode();
+
+ NodeParamsProvider params_provider{*this, node, node_state};
+ GeoNodeExecParams params{params_provider};
+ bnode.typeinfo->geometry_node_execute(params);
+ }
+
+ void execute_multi_function_node(const DNode node,
+ const MultiFunction &fn,
+ NodeState &node_state)
+ {
+ MFContextBuilder fn_context;
+ MFParamsBuilder fn_params{fn, 1};
+ LinearAllocator<> &allocator = local_allocators_.local();
+
+ /* Prepare the inputs for the multi function. */
+ for (const int i : node->inputs().index_range()) {
+ const InputSocketRef &socket_ref = node->input(i);
+ if (!socket_ref.is_available()) {
+ continue;
+ }
+ BLI_assert(!socket_ref.is_multi_input_socket());
+ InputState &input_state = node_state.inputs[i];
+ BLI_assert(input_state.was_ready_for_execution);
+ SingleInputValue &single_value = *input_state.value.single;
+ BLI_assert(single_value.value != nullptr);
+ fn_params.add_readonly_single_input(GPointer{*input_state.type, single_value.value});
+ }
+ /* Prepare the outputs for the multi function. */
+ Vector<GMutablePointer> outputs;
+ for (const int i : node->outputs().index_range()) {
+ const OutputSocketRef &socket_ref = node->output(i);
+ if (!socket_ref.is_available()) {
+ continue;
+ }
+ const CPPType &type = *get_socket_cpp_type(socket_ref);
+ void *buffer = allocator.allocate(type.size(), type.alignment());
+ fn_params.add_uninitialized_single_output(GMutableSpan{type, buffer, 1});
+ outputs.append({type, buffer});
+ }
+
+ fn.call(IndexRange(1), fn_params, fn_context);
+
+ /* Forward the computed outputs. */
+ int output_index = 0;
+ for (const int i : node->outputs().index_range()) {
+ const OutputSocketRef &socket_ref = node->output(i);
+ if (!socket_ref.is_available()) {
+ continue;
+ }
+ OutputState &output_state = node_state.outputs[i];
+ const DOutputSocket socket{node.context(), &socket_ref};
+ GMutablePointer value = outputs[output_index];
+ this->forward_output(socket, value);
+ output_state.has_been_computed = true;
+ output_index++;
+ }
+ }
+
+ void execute_unknown_node(const DNode node, NodeState &node_state)
+ {
+ LinearAllocator<> &allocator = local_allocators_.local();
+ for (const OutputSocketRef *socket : node->outputs()) {
+ if (!socket->is_available()) {
+ continue;
+ }
+ const CPPType *type = get_socket_cpp_type(*socket);
+ if (type == nullptr) {
+ continue;
+ }
+ /* Just forward the default value of the type as a fallback. That's typically better than
+ * crashing or doing nothing. */
+ OutputState &output_state = node_state.outputs[socket->index()];
+ output_state.has_been_computed = true;
+ void *buffer = allocator.allocate(type->size(), type->alignment());
+ type->copy_to_uninitialized(type->default_value(), buffer);
+ this->forward_output({node.context(), socket}, {*type, buffer});
+ }
+ }
+
+ void node_task_postprocessing(const DNode node, NodeState &node_state)
+ {
+ LockedNode locked_node{*this, node, node_state};
+
+ const bool node_has_finished = this->finish_node_if_possible(locked_node);
+ const bool reschedule_requested = node_state.schedule_state ==
+ NodeScheduleState::RunningAndRescheduled;
+ node_state.schedule_state = NodeScheduleState::NotScheduled;
+ if (reschedule_requested && !node_has_finished) {
+ /* Either the node rescheduled itself or another node tried to schedule it while it ran. */
+ this->schedule_node(locked_node);
+ }
+
+ this->assert_expected_outputs_have_been_computed(locked_node);
+ }
+
+ void assert_expected_outputs_have_been_computed(LockedNode &locked_node)
+ {
+#ifdef DEBUG
+ /* Outputs can only be computed when all required inputs have been provided. */
+ if (locked_node.node_state.missing_required_inputs > 0) {
+ return;
+ }
+ /* If the node is still scheduled, it is not necessary that all its expected outputs are
+ * computed yet. */
+ if (locked_node.node_state.schedule_state == NodeScheduleState::Scheduled) {
+ return;
+ }
+
+ const bool supports_laziness = node_supports_laziness(locked_node.node);
+ /* Iterating over sockets instead of the states directly, because that makes it easier to
+ * figure out which socket is missing when one of the asserts is hit. */
+ for (const OutputSocketRef *socket_ref : locked_node.node->outputs()) {
+ OutputState &output_state = locked_node.node_state.outputs[socket_ref->index()];
+ if (supports_laziness) {
+ /* Expected that at least all required sockets have been computed. If more outputs become
+ * required later, the node will be executed again. */
+ if (output_state.output_usage_for_execution == ValueUsage::Required) {
+ BLI_assert(output_state.has_been_computed);
+ }
+ }
+ else {
+ /* Expect that all outputs that may be used have been computed, because the node cannot
+ * be executed again. */
+ if (output_state.output_usage_for_execution != ValueUsage::Unused) {
+ BLI_assert(output_state.has_been_computed);
+ }
+ }
+ }
+#else
+ UNUSED_VARS(locked_node);
+#endif
+ }
+
+ void extract_group_outputs()
+ {
+ for (const DInputSocket &socket : params_.output_sockets) {
+ BLI_assert(socket->is_available());
+ BLI_assert(!socket->is_multi_input_socket());
+
+ const DNode node = socket.node();
+ NodeState &node_state = this->get_node_state(node);
+ InputState &input_state = node_state.inputs[socket->index()];
+
+ SingleInputValue &single_value = *input_state.value.single;
+ void *value = single_value.value;
+
+ /* The value should have been computed by now. If this assert is hit, it means that there
+ * was some scheduling issue before. */
+ BLI_assert(value != nullptr);
+
+ /* Move value into memory owned by the outer allocator. */
+ const CPPType &type = *input_state.type;
+ void *buffer = outer_allocator_.allocate(type.size(), type.alignment());
+ type.move_to_uninitialized(value, buffer);
+
+ params_.r_output_values.append({type, buffer});
+ }
+ }
+
+ /**
+ * Load the required input from the socket or trigger nodes to the left to compute the value.
+ * When this function is called, the node will always be executed again eventually (either
+ * immediately, or when all required inputs have been computed by other nodes).
+ */
+ void set_input_required(LockedNode &locked_node, const DInputSocket input_socket)
+ {
+ BLI_assert(locked_node.node == input_socket.node());
+ InputState &input_state = locked_node.node_state.inputs[input_socket->index()];
+
+ /* Value set as unused cannot become used again. */
+ BLI_assert(input_state.usage != ValueUsage::Unused);
+
+ if (input_state.usage == ValueUsage::Required) {
+ /* The value is already required, but the node might expect to be evaluated again. */
+ this->schedule_node(locked_node);
+ /* Returning here also ensure that the code below is executed at most once per input. */
+ return;
+ }
+ input_state.usage = ValueUsage::Required;
+
+ if (input_state.was_ready_for_execution) {
+ /* The value was already ready, but the node might expect to be evaluated again. */
+ this->schedule_node(locked_node);
+ return;
+ }
+
+ /* Count how many values still have to be added to this input until it is "complete". */
+ int missing_values = 0;
+ if (input_socket->is_multi_input_socket()) {
+ MultiInputValue &multi_value = *input_state.value.multi;
+ missing_values = multi_value.expected_size - multi_value.items.size();
+ }
+ else {
+ SingleInputValue &single_value = *input_state.value.single;
+ if (single_value.value == nullptr) {
+ missing_values = 1;
+ }
+ }
+ if (missing_values == 0) {
+ /* The input is fully available already, but the node might expect to be evaluated again. */
+ this->schedule_node(locked_node);
+ return;
+ }
+ /* Increase the total number of missing required inputs. This ensures that the node will be
+ * scheduled correctly when all inputs have been provided. */
+ locked_node.node_state.missing_required_inputs += missing_values;
+
+ /* Get all origin sockets, because we have to tag those as required as well. */
+ Vector<DSocket> origin_sockets;
+ input_socket.foreach_origin_socket(
+ [&](const DSocket origin_socket) { origin_sockets.append(origin_socket); });
+
+ if (origin_sockets.is_empty()) {
+ /* If there are no origin sockets, just load the value from the socket directly. */
+ this->load_unlinked_input_value(locked_node, input_socket, input_state, input_socket);
+ locked_node.node_state.missing_required_inputs -= 1;
+ this->schedule_node(locked_node);
+ return;
+ }
+ bool will_be_triggered_by_other_node = false;
+ for (const DSocket origin_socket : origin_sockets) {
+ if (origin_socket->is_input()) {
+ /* Load the value directly from the origin socket. In most cases this is an unlinked
+ * group input. */
+ this->load_unlinked_input_value(locked_node, input_socket, input_state, origin_socket);
+ locked_node.node_state.missing_required_inputs -= 1;
+ this->schedule_node(locked_node);
+ return;
+ }
+ /* The value has not been computed yet, so when it will be forwarded by another node, this
+ * node will be triggered. */
+ will_be_triggered_by_other_node = true;
+
+ locked_node.delayed_required_outputs.append(DOutputSocket(origin_socket));
+ }
+ /* If this node will be triggered by another node, we don't have to schedule it now. */
+ if (!will_be_triggered_by_other_node) {
+ this->schedule_node(locked_node);
+ }
+ }
+
+ void set_input_unused(LockedNode &locked_node, const DInputSocket socket)
+ {
+ InputState &input_state = locked_node.node_state.inputs[socket->index()];
+
+ /* A required socket cannot become unused. */
+ BLI_assert(input_state.usage != ValueUsage::Required);
+
+ if (input_state.usage == ValueUsage::Unused) {
+ /* Nothing to do in this case. */
+ return;
+ }
+ input_state.usage = ValueUsage::Unused;
+
+ /* If the input is unused, it's value can be destructed now. */
+ this->destruct_input_value_if_exists(locked_node, socket);
+
+ if (input_state.was_ready_for_execution) {
+ /* If the value was already computed, we don't need to notify origin nodes. */
+ return;
+ }
+
+ /* Notify origin nodes that might want to set its inputs as unused as well. */
+ socket.foreach_origin_socket([&](const DSocket origin_socket) {
+ if (origin_socket->is_input()) {
+ /* Values from these sockets are loaded directly from the sockets, so there is no node to
+ * notify. */
+ return;
+ }
+ /* Delay notification of the other node until this node is not locked anymore. */
+ locked_node.delayed_unused_outputs.append(DOutputSocket(origin_socket));
+ });
+ }
+
+ void send_output_required_notification(const DOutputSocket socket)
+ {
+ const DNode node = socket.node();
+ NodeState &node_state = this->get_node_state(node);
+ OutputState &output_state = node_state.outputs[socket->index()];
+
+ LockedNode locked_node{*this, node, node_state};
+ if (output_state.output_usage == ValueUsage::Required) {
+ /* Output is marked as required already. So the node is scheduled already. */
+ return;
+ }
+ /* The origin node needs to be scheduled so that it provides the requested input
+ * eventually. */
+ output_state.output_usage = ValueUsage::Required;
+ this->schedule_node(locked_node);
+ }
+
+ void send_output_unused_notification(const DOutputSocket socket)
+ {
+ const DNode node = socket.node();
+ NodeState &node_state = this->get_node_state(node);
+ OutputState &output_state = node_state.outputs[socket->index()];
+
+ LockedNode locked_node{*this, node, node_state};
+ output_state.potential_users -= 1;
+ if (output_state.potential_users == 0) {
+ /* The output socket has no users anymore. */
+ output_state.output_usage = ValueUsage::Unused;
+ /* Schedule the origin node in case it wants to set its inputs as unused as well. */
+ this->schedule_node(locked_node);
+ }
+ }
+
+ void add_node_to_task_pool(const DNode node)
+ {
+ /* Push the task to the pool while it is not locked to avoid a deadlock in case when the task
+ * is executed immediately. */
+ const NodeWithState *node_with_state = node_states_.lookup_key_ptr_as(node);
+ BLI_task_pool_push(
+ task_pool_, run_node_from_task_pool, (void *)node_with_state, false, nullptr);
+ }
+
+ /**
+ * Moves a newly computed value from an output socket to all the inputs that might need it.
+ */
+ void forward_output(const DOutputSocket from_socket, GMutablePointer value_to_forward)
+ {
+ BLI_assert(value_to_forward.get() != nullptr);
+
+ Vector<DInputSocket> to_sockets;
+ auto handle_target_socket_fn = [&, this](const DInputSocket to_socket) {
+ if (this->should_forward_to_socket(to_socket)) {
+ to_sockets.append(to_socket);
+ }
+ };
+ auto handle_skipped_socket_fn = [&, this](const DSocket socket) {
+ /* Log socket value on intermediate sockets to support e.g. attribute search or spreadsheet
+ * breadcrumbs on group nodes. */
+ this->log_socket_value(socket, value_to_forward);
+ };
+ from_socket.foreach_target_socket(handle_target_socket_fn, handle_skipped_socket_fn);
+
+ LinearAllocator<> &allocator = local_allocators_.local();
+
+ const CPPType &from_type = *value_to_forward.type();
+ Vector<DInputSocket> to_sockets_same_type;
+ for (const DInputSocket &to_socket : to_sockets) {
+ const CPPType &to_type = *get_socket_cpp_type(to_socket);
+ if (from_type == to_type) {
+ /* All target sockets that do not need a conversion will be handled afterwards. */
+ to_sockets_same_type.append(to_socket);
+ continue;
+ }
+ this->forward_to_socket_with_different_type(
+ allocator, value_to_forward, from_socket, to_socket, to_type);
+ }
+ this->forward_to_sockets_with_same_type(
+ allocator, to_sockets_same_type, value_to_forward, from_socket);
+ }
+
+ bool should_forward_to_socket(const DInputSocket socket)
+ {
+ const DNode to_node = socket.node();
+ const NodeWithState *target_node_with_state = node_states_.lookup_key_ptr_as(to_node);
+ if (target_node_with_state == nullptr) {
+ /* If the socket belongs to a node that has no state, the entire node is not used. */
+ return false;
+ }
+ NodeState &target_node_state = *target_node_with_state->state;
+ InputState &target_input_state = target_node_state.inputs[socket->index()];
+
+ std::lock_guard lock{target_node_state.mutex};
+ /* Do not forward to an input socket whose value won't be used. */
+ return target_input_state.usage != ValueUsage::Unused;
+ }
+
+ void forward_to_socket_with_different_type(LinearAllocator<> &allocator,
+ const GPointer value_to_forward,
+ const DOutputSocket from_socket,
+ const DInputSocket to_socket,
+ const CPPType &to_type)
+ {
+ const CPPType &from_type = *value_to_forward.type();
+
+ /* Allocate a buffer for the converted value. */
+ void *buffer = allocator.allocate(to_type.size(), to_type.alignment());
+
+ if (conversions_.is_convertible(from_type, to_type)) {
+ /* Do the conversion if possible. */
+ conversions_.convert_to_uninitialized(from_type, to_type, value_to_forward.get(), buffer);
+ }
+ else {
+ /* Cannot convert, use default value instead. */
+ to_type.copy_to_uninitialized(to_type.default_value(), buffer);
+ }
+ this->add_value_to_input_socket(to_socket, from_socket, {to_type, buffer});
+ }
+
+ void forward_to_sockets_with_same_type(LinearAllocator<> &allocator,
+ Span<DInputSocket> to_sockets,
+ GMutablePointer value_to_forward,
+ const DOutputSocket from_socket)
+ {
+ if (to_sockets.is_empty()) {
+ /* Value is not used anymore, so it can be destructed. */
+ value_to_forward.destruct();
+ }
+ else if (to_sockets.size() == 1) {
+ /* Value is only used by one input socket, no need to copy it. */
+ const DInputSocket to_socket = to_sockets[0];
+ this->add_value_to_input_socket(to_socket, from_socket, value_to_forward);
+ }
+ else {
+ /* Multiple inputs use the value, make a copy for every input except for one. */
+ /* First make the copies, so that the next node does not start modifying the value while we
+ * are still making copies. */
+ const CPPType &type = *value_to_forward.type();
+ for (const DInputSocket &to_socket : to_sockets.drop_front(1)) {
+ void *buffer = allocator.allocate(type.size(), type.alignment());
+ type.copy_to_uninitialized(value_to_forward.get(), buffer);
+ this->add_value_to_input_socket(to_socket, from_socket, {type, buffer});
+ }
+ /* Forward the original value to one of the targets. */
+ const DInputSocket to_socket = to_sockets[0];
+ this->add_value_to_input_socket(to_socket, from_socket, value_to_forward);
+ }
+ }
+
+ void add_value_to_input_socket(const DInputSocket socket,
+ const DOutputSocket origin,
+ GMutablePointer value)
+ {
+ BLI_assert(socket->is_available());
+
+ const DNode node = socket.node();
+ NodeState &node_state = this->get_node_state(node);
+ InputState &input_state = node_state.inputs[socket->index()];
+
+ /* Lock the node because we want to change its state. */
+ LockedNode locked_node{*this, node, node_state};
+
+ if (socket->is_multi_input_socket()) {
+ /* Add a new value to the multi-input. */
+ MultiInputValue &multi_value = *input_state.value.multi;
+ multi_value.items.append({origin, value.get()});
+ }
+ else {
+ /* Assign the value to the input. */
+ SingleInputValue &single_value = *input_state.value.single;
+ BLI_assert(single_value.value == nullptr);
+ single_value.value = value.get();
+ }
+
+ if (input_state.usage == ValueUsage::Required) {
+ node_state.missing_required_inputs--;
+ if (node_state.missing_required_inputs == 0) {
+ /* Schedule node if all the required inputs have been provided. */
+ this->schedule_node(locked_node);
+ }
+ }
+ }
+
+ void load_unlinked_input_value(LockedNode &locked_node,
+ const DInputSocket input_socket,
+ InputState &input_state,
+ const DSocket origin_socket)
+ {
+ /* Only takes locked node as parameter, because the node needs to be locked. */
+ UNUSED_VARS(locked_node);
+
+ GMutablePointer value = this->get_value_from_socket(origin_socket, *input_state.type);
+ if (input_socket->is_multi_input_socket()) {
+ MultiInputValue &multi_value = *input_state.value.multi;
+ multi_value.items.append({origin_socket, value.get()});
+ }
+ else {
+ SingleInputValue &single_value = *input_state.value.single;
+ single_value.value = value.get();
+ }
+ }
+
+ void destruct_input_value_if_exists(LockedNode &locked_node, const DInputSocket socket)
+ {
+ InputState &input_state = locked_node.node_state.inputs[socket->index()];
+ if (socket->is_multi_input_socket()) {
+ MultiInputValue &multi_value = *input_state.value.multi;
+ for (MultiInputValueItem &item : multi_value.items) {
+ input_state.type->destruct(item.value);
+ }
+ multi_value.items.clear();
+ }
+ else {
+ SingleInputValue &single_value = *input_state.value.single;
+ if (single_value.value != nullptr) {
+ input_state.type->destruct(single_value.value);
+ single_value.value = nullptr;
+ }
+ }
+ }
+
+ GMutablePointer get_value_from_socket(const DSocket socket, const CPPType &required_type)
+ {
+ LinearAllocator<> &allocator = local_allocators_.local();
+
+ bNodeSocket *bsocket = socket->bsocket();
+ const CPPType &type = *get_socket_cpp_type(socket);
+ void *buffer = allocator.allocate(type.size(), type.alignment());
+ blender::nodes::socket_cpp_value_get(*bsocket, buffer);
+
+ if (type == required_type) {
+ return {type, buffer};
+ }
+ if (conversions_.is_convertible(type, required_type)) {
+ /* Convert the loaded value to the required type if possible. */
+ void *converted_buffer = allocator.allocate(required_type.size(), required_type.alignment());
+ conversions_.convert_to_uninitialized(type, required_type, buffer, converted_buffer);
+ type.destruct(buffer);
+ return {required_type, converted_buffer};
+ }
+ /* Use a default fallback value when the loaded type is not compatible. */
+ void *default_buffer = allocator.allocate(required_type.size(), required_type.alignment());
+ required_type.copy_to_uninitialized(required_type.default_value(), default_buffer);
+ return {required_type, default_buffer};
+ }
+
+ NodeState &get_node_state(const DNode node)
+ {
+ return *node_states_.lookup_key_as(node).state;
+ }
+
+ void log_socket_value(const DSocket socket, Span<GPointer> values)
+ {
+ if (params_.log_socket_value_fn) {
+ params_.log_socket_value_fn(socket, values);
+ }
+ }
+
+ void log_socket_value(const DSocket socket,
+ InputState &input_state,
+ Span<MultiInputValueItem> values)
+ {
+ Vector<GPointer, 16> value_pointers;
+ value_pointers.reserve(values.size());
+ const CPPType &type = *input_state.type;
+ for (const MultiInputValueItem &item : values) {
+ value_pointers.append({type, item.value});
+ }
+ this->log_socket_value(socket, value_pointers);
+ }
+
+ void log_socket_value(const DSocket socket, GPointer value)
+ {
+ this->log_socket_value(socket, Span<GPointer>(&value, 1));
+ }
+};
+
+LockedNode::~LockedNode()
+{
+ /* First unlock the current node. */
+ node_state.mutex.unlock();
+ /* Then send notifications to the other nodes. */
+ for (const DOutputSocket &socket : delayed_required_outputs) {
+ evaluator_.send_output_required_notification(socket);
+ }
+ for (const DOutputSocket &socket : delayed_unused_outputs) {
+ evaluator_.send_output_unused_notification(socket);
+ }
+ for (const DNode &node : delayed_scheduled_nodes) {
+ evaluator_.add_node_to_task_pool(node);
+ }
+}
+
+NodeParamsProvider::NodeParamsProvider(GeometryNodesEvaluator &evaluator,
+ DNode dnode,
+ NodeState &node_state)
+ : evaluator_(evaluator), node_state_(node_state)
+{
+ this->dnode = dnode;
+ this->self_object = evaluator.params_.self_object;
+ this->modifier = &evaluator.params_.modifier_->modifier;
+ this->depsgraph = evaluator.params_.depsgraph;
+}
+
+bool NodeParamsProvider::can_get_input(StringRef identifier) const
+{
+ const DInputSocket socket = this->dnode.input_by_identifier(identifier);
+ BLI_assert(socket);
+
+ InputState &input_state = node_state_.inputs[socket->index()];
+ if (!input_state.was_ready_for_execution) {
+ return false;
+ }
+
+ if (socket->is_multi_input_socket()) {
+ MultiInputValue &multi_value = *input_state.value.multi;
+ return multi_value.items.size() == multi_value.expected_size;
+ }
+ SingleInputValue &single_value = *input_state.value.single;
+ return single_value.value != nullptr;
+}
+
+bool NodeParamsProvider::can_set_output(StringRef identifier) const
+{
+ const DOutputSocket socket = this->dnode.output_by_identifier(identifier);
+ BLI_assert(socket);
+
+ OutputState &output_state = node_state_.outputs[socket->index()];
+ return !output_state.has_been_computed;
+}
+
+GMutablePointer NodeParamsProvider::extract_input(StringRef identifier)
+{
+ const DInputSocket socket = this->dnode.input_by_identifier(identifier);
+ BLI_assert(socket);
+ BLI_assert(!socket->is_multi_input_socket());
+ BLI_assert(this->can_get_input(identifier));
+
+ InputState &input_state = node_state_.inputs[socket->index()];
+ SingleInputValue &single_value = *input_state.value.single;
+ void *value = single_value.value;
+ single_value.value = nullptr;
+ return {*input_state.type, value};
+}
+
+Vector<GMutablePointer> NodeParamsProvider::extract_multi_input(StringRef identifier)
+{
+ const DInputSocket socket = this->dnode.input_by_identifier(identifier);
+ BLI_assert(socket);
+ BLI_assert(socket->is_multi_input_socket());
+ BLI_assert(this->can_get_input(identifier));
+
+ InputState &input_state = node_state_.inputs[socket->index()];
+ MultiInputValue &multi_value = *input_state.value.multi;
+
+ Vector<GMutablePointer> ret_values;
+ socket.foreach_origin_socket([&](DSocket origin) {
+ for (const MultiInputValueItem &item : multi_value.items) {
+ if (item.origin == origin) {
+ ret_values.append({*input_state.type, item.value});
+ return;
+ }
+ }
+ BLI_assert_unreachable();
+ });
+ if (ret_values.is_empty()) {
+ /* If the socket is not linked, we just use the value from the socket itself. */
+ BLI_assert(multi_value.items.size() == 1);
+ MultiInputValueItem &item = multi_value.items[0];
+ BLI_assert(item.origin == socket);
+ ret_values.append({*input_state.type, item.value});
+ }
+ multi_value.items.clear();
+ return ret_values;
+}
+
+GPointer NodeParamsProvider::get_input(StringRef identifier) const
+{
+ const DInputSocket socket = this->dnode.input_by_identifier(identifier);
+ BLI_assert(socket);
+ BLI_assert(!socket->is_multi_input_socket());
+ BLI_assert(this->can_get_input(identifier));
+
+ InputState &input_state = node_state_.inputs[socket->index()];
+ SingleInputValue &single_value = *input_state.value.single;
+ return {*input_state.type, single_value.value};
+}
+
+GMutablePointer NodeParamsProvider::alloc_output_value(const CPPType &type)
+{
+ LinearAllocator<> &allocator = evaluator_.local_allocators_.local();
+ return {type, allocator.allocate(type.size(), type.alignment())};
+}
+
+void NodeParamsProvider::set_output(StringRef identifier, GMutablePointer value)
+{
+ const DOutputSocket socket = this->dnode.output_by_identifier(identifier);
+ BLI_assert(socket);
+
+ evaluator_.log_socket_value(socket, value);
+
+ OutputState &output_state = node_state_.outputs[socket->index()];
+ BLI_assert(!output_state.has_been_computed);
+ evaluator_.forward_output(socket, value);
+ output_state.has_been_computed = true;
+}
+
+bool NodeParamsProvider::lazy_require_input(StringRef identifier)
+{
+ BLI_assert(node_supports_laziness(this->dnode));
+ const DInputSocket socket = this->dnode.input_by_identifier(identifier);
+ BLI_assert(socket);
+
+ InputState &input_state = node_state_.inputs[socket->index()];
+ if (input_state.was_ready_for_execution) {
+ return false;
+ }
+ LockedNode locked_node{evaluator_, this->dnode, node_state_};
+ evaluator_.set_input_required(locked_node, socket);
+ return true;
+}
+
+void NodeParamsProvider::set_input_unused(StringRef identifier)
+{
+ const DInputSocket socket = this->dnode.input_by_identifier(identifier);
+ BLI_assert(socket);
+
+ LockedNode locked_node{evaluator_, this->dnode, node_state_};
+ evaluator_.set_input_unused(locked_node, socket);
+}
+
+bool NodeParamsProvider::output_is_required(StringRef identifier) const
+{
+ const DOutputSocket socket = this->dnode.output_by_identifier(identifier);
+ BLI_assert(socket);
+
+ OutputState &output_state = node_state_.outputs[socket->index()];
+ if (output_state.has_been_computed) {
+ return false;
+ }
+ return output_state.output_usage_for_execution != ValueUsage::Unused;
+}
+
+bool NodeParamsProvider::lazy_output_is_required(StringRef identifier) const
+{
+ BLI_assert(node_supports_laziness(this->dnode));
+ const DOutputSocket socket = this->dnode.output_by_identifier(identifier);
+ BLI_assert(socket);
+
+ OutputState &output_state = node_state_.outputs[socket->index()];
+ if (output_state.has_been_computed) {
+ return false;
+ }
+ return output_state.output_usage_for_execution == ValueUsage::Required;
+}
+
+void evaluate_geometry_nodes(GeometryNodesEvaluationParams &params)
+{
+ GeometryNodesEvaluator evaluator{params};
+ evaluator.execute();
+}
+
+} // namespace blender::modifiers::geometry_nodes
diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.hh b/source/blender/modifiers/intern/MOD_nodes_evaluator.hh
new file mode 100644
index 00000000000..84249e4244e
--- /dev/null
+++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.hh
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "BLI_map.hh"
+
+#include "NOD_derived_node_tree.hh"
+#include "NOD_node_tree_multi_function.hh"
+
+#include "FN_generic_pointer.hh"
+
+#include "DNA_modifier_types.h"
+
+namespace blender::modifiers::geometry_nodes {
+
+using namespace nodes::derived_node_tree_types;
+using fn::GMutablePointer;
+using fn::GPointer;
+
+using LogSocketValueFn = std::function<void(DSocket, Span<GPointer>)>;
+
+struct GeometryNodesEvaluationParams {
+ blender::LinearAllocator<> allocator;
+
+ Map<DOutputSocket, GMutablePointer> input_values;
+ Vector<DInputSocket> output_sockets;
+ nodes::MultiFunctionByNode *mf_by_node;
+ const NodesModifierData *modifier_;
+ Depsgraph *depsgraph;
+ Object *self_object;
+ LogSocketValueFn log_socket_value_fn;
+
+ Vector<GMutablePointer> r_output_values;
+};
+
+void evaluate_geometry_nodes(GeometryNodesEvaluationParams &params);
+
+} // namespace blender::modifiers::geometry_nodes
diff --git a/source/blender/modifiers/intern/MOD_none.c b/source/blender/modifiers/intern/MOD_none.c
index 4daa527577b..a01f63be791 100644
--- a/source/blender/modifiers/intern/MOD_none.c
+++ b/source/blender/modifiers/intern/MOD_none.c
@@ -60,7 +60,6 @@ ModifierTypeInfo modifierType_None = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ NULL,
/* requiredDataMask */ NULL,
diff --git a/source/blender/modifiers/intern/MOD_normal_edit.c b/source/blender/modifiers/intern/MOD_normal_edit.c
index ec10b18665e..4bfd6aba4b2 100644
--- a/source/blender/modifiers/intern/MOD_normal_edit.c
+++ b/source/blender/modifiers/intern/MOD_normal_edit.c
@@ -805,7 +805,6 @@ ModifierTypeInfo modifierType_NormalEdit = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_ocean.c b/source/blender/modifiers/intern/MOD_ocean.c
index 9c940745920..f7ac59f9e4b 100644
--- a/source/blender/modifiers/intern/MOD_ocean.c
+++ b/source/blender/modifiers/intern/MOD_ocean.c
@@ -737,7 +737,6 @@ ModifierTypeInfo modifierType_Ocean = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_particleinstance.c b/source/blender/modifiers/intern/MOD_particleinstance.c
index e7f1fa9943e..60c5667472e 100644
--- a/source/blender/modifiers/intern/MOD_particleinstance.c
+++ b/source/blender/modifiers/intern/MOD_particleinstance.c
@@ -679,7 +679,6 @@ ModifierTypeInfo modifierType_ParticleInstance = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_particlesystem.c b/source/blender/modifiers/intern/MOD_particlesystem.c
index 4c1179af431..38cce5e6a50 100644
--- a/source/blender/modifiers/intern/MOD_particlesystem.c
+++ b/source/blender/modifiers/intern/MOD_particlesystem.c
@@ -337,7 +337,6 @@ ModifierTypeInfo modifierType_ParticleSystem = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_remesh.c b/source/blender/modifiers/intern/MOD_remesh.c
index 175435fcd44..88851f91337 100644
--- a/source/blender/modifiers/intern/MOD_remesh.c
+++ b/source/blender/modifiers/intern/MOD_remesh.c
@@ -301,7 +301,6 @@ ModifierTypeInfo modifierType_Remesh = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ NULL,
diff --git a/source/blender/modifiers/intern/MOD_screw.c b/source/blender/modifiers/intern/MOD_screw.c
index 84360caa345..b236e0896b7 100644
--- a/source/blender/modifiers/intern/MOD_screw.c
+++ b/source/blender/modifiers/intern/MOD_screw.c
@@ -1258,7 +1258,6 @@ ModifierTypeInfo modifierType_Screw = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ NULL,
diff --git a/source/blender/modifiers/intern/MOD_shapekey.c b/source/blender/modifiers/intern/MOD_shapekey.c
index 81a0ee72496..b517bc102f8 100644
--- a/source/blender/modifiers/intern/MOD_shapekey.c
+++ b/source/blender/modifiers/intern/MOD_shapekey.c
@@ -141,7 +141,6 @@ ModifierTypeInfo modifierType_ShapeKey = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ NULL,
/* requiredDataMask */ NULL,
diff --git a/source/blender/modifiers/intern/MOD_shrinkwrap.c b/source/blender/modifiers/intern/MOD_shrinkwrap.c
index 93626309727..a12724ec23c 100644
--- a/source/blender/modifiers/intern/MOD_shrinkwrap.c
+++ b/source/blender/modifiers/intern/MOD_shrinkwrap.c
@@ -293,7 +293,6 @@ ModifierTypeInfo modifierType_Shrinkwrap = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_simpledeform.c b/source/blender/modifiers/intern/MOD_simpledeform.c
index ea31bdc6e31..db01dec4d19 100644
--- a/source/blender/modifiers/intern/MOD_simpledeform.c
+++ b/source/blender/modifiers/intern/MOD_simpledeform.c
@@ -21,10 +21,9 @@
* \ingroup modifiers
*/
-#include "BLI_utildefines.h"
-
#include "BLI_math.h"
-
+#include "BLI_task.h"
+#include "BLI_utildefines.h"
#include "BLT_translation.h"
#include "DNA_defaults.h"
@@ -57,6 +56,21 @@
#define BEND_EPS 0.000001f
+ALIGN_STRUCT struct DeformUserData {
+ bool invert_vgroup;
+ char mode;
+ char deform_axis;
+ int lock_axis;
+ int vgroup;
+ int limit_axis;
+ float weight;
+ float smd_factor;
+ float smd_limit[2];
+ float (*vertexCos)[3];
+ const SpaceTransform *transf;
+ const MDeformVert *dvert;
+};
+
/* Re-maps the indices for X Y Z by shifting them up and wrapping, such that
* X = Y, Y = Z, Z = X (for X axis), and X = Z, Y = X, Z = Y (for Y axis). This
* exists because the deformations (excluding bend) are based on the Z axis.
@@ -170,10 +184,12 @@ static void simpleDeform_bend(const float factor,
sint = sinf(theta);
cost = cosf(theta);
+ /* NOTE: the operations below a susceptible to float precision errors
+ * regarding the order of operations, take care when changing, see: T85470 */
switch (axis) {
case 0:
r_co[0] = x;
- r_co[1] = (y - 1.0f / factor) * cost + 1.0f / factor;
+ r_co[1] = y * cost + (1.0f - cost) / factor;
r_co[2] = -(y - 1.0f / factor) * sint;
{
r_co[0] += dcut[0];
@@ -182,7 +198,7 @@ static void simpleDeform_bend(const float factor,
}
break;
case 1:
- r_co[0] = (x - 1.0f / factor) * cost + 1.0f / factor;
+ r_co[0] = x * cost + (1.0f - cost) / factor;
r_co[1] = y;
r_co[2] = -(x - 1.0f / factor) * sint;
{
@@ -193,7 +209,7 @@ static void simpleDeform_bend(const float factor,
break;
default:
r_co[0] = -(y - 1.0f / factor) * sint;
- r_co[1] = (y - 1.0f / factor) * cost + 1.0f / factor;
+ r_co[1] = y * cost + (1.0f - cost) / factor;
r_co[2] = z;
{
r_co[0] += cost * dcut[0];
@@ -203,6 +219,88 @@ static void simpleDeform_bend(const float factor,
}
}
+static void simple_helper(void *__restrict userdata,
+ const int iter,
+ const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ const struct DeformUserData *curr_deform_data = userdata;
+ float weight = BKE_defvert_array_find_weight_safe(
+ curr_deform_data->dvert, iter, curr_deform_data->vgroup);
+ const uint *axis_map = axis_map_table[(curr_deform_data->mode != MOD_SIMPLEDEFORM_MODE_BEND) ?
+ curr_deform_data->deform_axis :
+ 2];
+ const float base_limit[2] = {0.0f, 0.0f};
+
+ if (curr_deform_data->invert_vgroup) {
+ weight = 1.0f - weight;
+ }
+
+ if (weight != 0.0f) {
+ float co[3], dcut[3] = {0.0f, 0.0f, 0.0f};
+
+ if (curr_deform_data->transf) {
+ BLI_space_transform_apply(curr_deform_data->transf, curr_deform_data->vertexCos[iter]);
+ }
+
+ copy_v3_v3(co, curr_deform_data->vertexCos[iter]);
+
+ /* Apply axis limits, and axis mappings */
+ if (curr_deform_data->lock_axis & MOD_SIMPLEDEFORM_LOCK_AXIS_X) {
+ axis_limit(0, base_limit, co, dcut);
+ }
+ if (curr_deform_data->lock_axis & MOD_SIMPLEDEFORM_LOCK_AXIS_Y) {
+ axis_limit(1, base_limit, co, dcut);
+ }
+ if (curr_deform_data->lock_axis & MOD_SIMPLEDEFORM_LOCK_AXIS_Z) {
+ axis_limit(2, base_limit, co, dcut);
+ }
+ axis_limit(curr_deform_data->limit_axis, curr_deform_data->smd_limit, co, dcut);
+
+ /* apply the deform to a mapped copy of the vertex, and then re-map it back. */
+ float co_remap[3];
+ float dcut_remap[3];
+ copy_v3_v3_map(co_remap, co, axis_map);
+ copy_v3_v3_map(dcut_remap, dcut, axis_map);
+ switch (curr_deform_data->mode) {
+ case MOD_SIMPLEDEFORM_MODE_TWIST:
+ simpleDeform_twist(curr_deform_data->smd_factor,
+ curr_deform_data->deform_axis,
+ dcut_remap,
+ co_remap); /* apply deform */
+ break;
+ case MOD_SIMPLEDEFORM_MODE_BEND:
+ simpleDeform_bend(curr_deform_data->smd_factor,
+ curr_deform_data->deform_axis,
+ dcut_remap,
+ co_remap); /* apply deform */
+ break;
+ case MOD_SIMPLEDEFORM_MODE_TAPER:
+ simpleDeform_taper(curr_deform_data->smd_factor,
+ curr_deform_data->deform_axis,
+ dcut_remap,
+ co_remap); /* apply deform */
+ break;
+ case MOD_SIMPLEDEFORM_MODE_STRETCH:
+ simpleDeform_stretch(curr_deform_data->smd_factor,
+ curr_deform_data->deform_axis,
+ dcut_remap,
+ co_remap); /* apply deform */
+ break;
+ default:
+ return; /* No simple-deform mode? */
+ }
+ copy_v3_v3_unmap(co, co_remap, axis_map);
+
+ /* Use vertex weight has coef of linear interpolation */
+ interp_v3_v3v3(
+ curr_deform_data->vertexCos[iter], curr_deform_data->vertexCos[iter], co, weight);
+
+ if (curr_deform_data->transf) {
+ BLI_space_transform_invert(curr_deform_data->transf, curr_deform_data->vertexCos[iter]);
+ }
+ }
+}
+
/* simple deform modifier */
static void SimpleDeformModifier_do(SimpleDeformModifierData *smd,
const ModifierEvalContext *UNUSED(ctx),
@@ -211,14 +309,9 @@ static void SimpleDeformModifier_do(SimpleDeformModifierData *smd,
float (*vertexCos)[3],
int numVerts)
{
- const float base_limit[2] = {0.0f, 0.0f};
int i;
float smd_limit[2], smd_factor;
SpaceTransform *transf = NULL, tmp_transf;
- void (*simpleDeform_callback)(const float factor,
- const int axis,
- const float dcut[3],
- float co[3]) = NULL; /* Mode callback */
int vgroup;
MDeformVert *dvert;
@@ -300,23 +393,6 @@ static void SimpleDeformModifier_do(SimpleDeformModifierData *smd,
smd_factor = smd->factor / max_ff(FLT_EPSILON, smd_limit[1] - smd_limit[0]);
}
- switch (smd->mode) {
- case MOD_SIMPLEDEFORM_MODE_TWIST:
- simpleDeform_callback = simpleDeform_twist;
- break;
- case MOD_SIMPLEDEFORM_MODE_BEND:
- simpleDeform_callback = simpleDeform_bend;
- break;
- case MOD_SIMPLEDEFORM_MODE_TAPER:
- simpleDeform_callback = simpleDeform_taper;
- break;
- case MOD_SIMPLEDEFORM_MODE_STRETCH:
- simpleDeform_callback = simpleDeform_stretch;
- break;
- default:
- return; /* No simple-deform mode? */
- }
-
if (smd->mode == MOD_SIMPLEDEFORM_MODE_BEND) {
if (fabsf(smd_factor) < BEND_EPS) {
return;
@@ -325,53 +401,26 @@ static void SimpleDeformModifier_do(SimpleDeformModifierData *smd,
MOD_get_vgroup(ob, mesh, smd->vgroup_name, &dvert, &vgroup);
const bool invert_vgroup = (smd->flag & MOD_SIMPLEDEFORM_FLAG_INVERT_VGROUP) != 0;
- const uint *axis_map =
- axis_map_table[(smd->mode != MOD_SIMPLEDEFORM_MODE_BEND) ? deform_axis : 2];
-
- for (i = 0; i < numVerts; i++) {
- float weight = BKE_defvert_array_find_weight_safe(dvert, i, vgroup);
-
- if (invert_vgroup) {
- weight = 1.0f - weight;
- }
-
- if (weight != 0.0f) {
- float co[3], dcut[3] = {0.0f, 0.0f, 0.0f};
- if (transf) {
- BLI_space_transform_apply(transf, vertexCos[i]);
- }
-
- copy_v3_v3(co, vertexCos[i]);
-
- /* Apply axis limits, and axis mappings */
- if (lock_axis & MOD_SIMPLEDEFORM_LOCK_AXIS_X) {
- axis_limit(0, base_limit, co, dcut);
- }
- if (lock_axis & MOD_SIMPLEDEFORM_LOCK_AXIS_Y) {
- axis_limit(1, base_limit, co, dcut);
- }
- if (lock_axis & MOD_SIMPLEDEFORM_LOCK_AXIS_Z) {
- axis_limit(2, base_limit, co, dcut);
- }
- axis_limit(limit_axis, smd_limit, co, dcut);
-
- /* apply the deform to a mapped copy of the vertex, and then re-map it back. */
- float co_remap[3];
- float dcut_remap[3];
- copy_v3_v3_map(co_remap, co, axis_map);
- copy_v3_v3_map(dcut_remap, dcut, axis_map);
- simpleDeform_callback(smd_factor, deform_axis, dcut_remap, co_remap); /* apply deform */
- copy_v3_v3_unmap(co, co_remap, axis_map);
-
- /* Use vertex weight has coef of linear interpolation */
- interp_v3_v3v3(vertexCos[i], vertexCos[i], co, weight);
-
- if (transf) {
- BLI_space_transform_invert(transf, vertexCos[i]);
- }
- }
- }
+ /* Build our data. */
+ const struct DeformUserData deform_pool_data = {
+ .mode = smd->mode,
+ .smd_factor = smd_factor,
+ .deform_axis = deform_axis,
+ .transf = transf,
+ .vertexCos = vertexCos,
+ .invert_vgroup = invert_vgroup,
+ .lock_axis = lock_axis,
+ .vgroup = vgroup,
+ .smd_limit[0] = smd_limit[0],
+ .smd_limit[1] = smd_limit[1],
+ .dvert = dvert,
+ .limit_axis = limit_axis,
+ };
+ /* Do deformation. */
+ TaskParallelSettings settings;
+ BLI_parallel_range_settings_defaults(&settings);
+ BLI_task_parallel_range(0, numVerts, (void *)&deform_pool_data, simple_helper, &settings);
}
/* SimpleDeform */
@@ -553,7 +602,6 @@ ModifierTypeInfo modifierType_SimpleDeform = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_skin.c b/source/blender/modifiers/intern/MOD_skin.c
index 5e412185cea..58d70ef3a4a 100644
--- a/source/blender/modifiers/intern/MOD_skin.c
+++ b/source/blender/modifiers/intern/MOD_skin.c
@@ -90,6 +90,46 @@
#include "bmesh.h"
+/* -------------------------------------------------------------------- */
+/** \name Generic BMesh Utilities
+ * \{ */
+
+static void vert_face_normal_mark_set(BMVert *v)
+{
+ BMIter iter;
+ BMFace *f;
+ BM_ITER_ELEM (f, &iter, v, BM_FACES_OF_VERT) {
+ f->no[0] = FLT_MAX;
+ }
+}
+
+static void vert_face_normal_mark_update(BMVert *v)
+{
+ BMIter iter;
+ BMFace *f;
+ BM_ITER_ELEM (f, &iter, v, BM_FACES_OF_VERT) {
+ if (f->no[0] == FLT_MAX) {
+ BM_face_normal_update(f);
+ }
+ }
+}
+
+/**
+ * Recalculate the normals of all faces connected to `verts`.
+ */
+static void vert_array_face_normal_update(BMVert **verts, int verts_len)
+{
+ for (int i = 0; i < verts_len; i++) {
+ vert_face_normal_mark_set(verts[i]);
+ }
+
+ for (int i = 0; i < verts_len; i++) {
+ vert_face_normal_mark_update(verts[i]);
+ }
+}
+
+/** \} */
+
typedef struct {
float mat[3][3];
/* Vert that edge is pointing away from, no relation to
@@ -1352,13 +1392,25 @@ static void skin_fix_hole_no_good_verts(BMesh *bm, Frame *frame, BMFace *split_f
split_face = collapse_face_corners(bm, split_face, 4, vert_buf);
}
- /* Done with dynamic array, split_face must now be a quad */
- BLI_array_free(vert_buf);
+ /* `split_face` should now be a quad. */
BLI_assert(split_face->len == 4);
+
+ /* Account for the highly unlikely case that it's not a quad. */
if (split_face->len != 4) {
+ /* Reuse `vert_buf` for updating normals. */
+ BLI_array_clear(vert_buf);
+ BLI_array_grow_items(vert_buf, split_face->len);
+
+ BM_iter_as_array(bm, BM_FACES_OF_VERT, split_face, (void **)vert_buf, split_face->len);
+
+ vert_array_face_normal_update(vert_buf, split_face->len);
+ BLI_array_free(vert_buf);
return;
}
+ /* Done with dynamic array. */
+ BLI_array_free(vert_buf);
+
/* Get split face's verts */
// BM_iter_as_array(bm, BM_VERTS_OF_FACE, split_face, (void **)verts, 4);
BM_face_as_array_vert_quad(split_face, verts);
@@ -1373,6 +1425,8 @@ static void skin_fix_hole_no_good_verts(BMesh *bm, Frame *frame, BMFace *split_f
}
BMO_op_exec(bm, &op);
BMO_op_finish(bm, &op);
+
+ vert_array_face_normal_update(frame->verts, 4);
}
/* If the frame has some vertices that are inside the hull (detached)
@@ -1731,6 +1785,11 @@ static void skin_smooth_hulls(BMesh *bm,
/* Done with original coordinates */
BM_data_layer_free_n(bm, &bm->vdata, CD_SHAPEKEY, skey);
+
+ BMFace *f;
+ BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
+ BM_face_normal_update(f);
+ }
}
/* Returns true if all hulls are successfully built, false otherwise */
@@ -2044,7 +2103,6 @@ ModifierTypeInfo modifierType_Skin = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_smooth.c b/source/blender/modifiers/intern/MOD_smooth.c
index c5011ed15c1..97027e2ecff 100644
--- a/source/blender/modifiers/intern/MOD_smooth.c
+++ b/source/blender/modifiers/intern/MOD_smooth.c
@@ -286,7 +286,6 @@ ModifierTypeInfo modifierType_Smooth = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_softbody.c b/source/blender/modifiers/intern/MOD_softbody.c
index 9a657c44fca..d7d2f948955 100644
--- a/source/blender/modifiers/intern/MOD_softbody.c
+++ b/source/blender/modifiers/intern/MOD_softbody.c
@@ -120,7 +120,6 @@ ModifierTypeInfo modifierType_Softbody = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ NULL,
/* requiredDataMask */ NULL,
diff --git a/source/blender/modifiers/intern/MOD_solidify.c b/source/blender/modifiers/intern/MOD_solidify.c
index 8e519a72df1..736dd08a713 100644
--- a/source/blender/modifiers/intern/MOD_solidify.c
+++ b/source/blender/modifiers/intern/MOD_solidify.c
@@ -276,7 +276,6 @@ ModifierTypeInfo modifierType_Solidify = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_solidify_extrude.c b/source/blender/modifiers/intern/MOD_solidify_extrude.c
index 99069919120..a77a6e595ad 100644
--- a/source/blender/modifiers/intern/MOD_solidify_extrude.c
+++ b/source/blender/modifiers/intern/MOD_solidify_extrude.c
@@ -216,7 +216,7 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex
numVerts, sizeof(*old_vert_arr), "old_vert_arr in solidify");
uint *edge_users = NULL;
- char *edge_order = NULL;
+ int *edge_order = NULL;
float(*vert_nors)[3] = NULL;
float(*poly_nors)[3] = NULL;
diff --git a/source/blender/modifiers/intern/MOD_subsurf.c b/source/blender/modifiers/intern/MOD_subsurf.c
index c3611488f5f..4dc45ad0324 100644
--- a/source/blender/modifiers/intern/MOD_subsurf.c
+++ b/source/blender/modifiers/intern/MOD_subsurf.c
@@ -509,7 +509,6 @@ ModifierTypeInfo modifierType_Subsurf = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_surface.c b/source/blender/modifiers/intern/MOD_surface.c
index 7416a4baf38..bfd4cd81803 100644
--- a/source/blender/modifiers/intern/MOD_surface.c
+++ b/source/blender/modifiers/intern/MOD_surface.c
@@ -184,13 +184,17 @@ static void deformVerts(ModifierData *md,
surmd->cfra = cfra;
- surmd->bvhtree = MEM_callocN(sizeof(BVHTreeFromMesh), "BVHTreeFromMesh");
+ const bool has_poly = surmd->mesh->totpoly > 0;
+ const bool has_edge = surmd->mesh->totedge > 0;
+ if (has_poly || has_edge) {
+ surmd->bvhtree = MEM_callocN(sizeof(BVHTreeFromMesh), "BVHTreeFromMesh");
- if (surmd->mesh->totpoly) {
- BKE_bvhtree_from_mesh_get(surmd->bvhtree, surmd->mesh, BVHTREE_FROM_LOOPTRI, 2);
- }
- else {
- BKE_bvhtree_from_mesh_get(surmd->bvhtree, surmd->mesh, BVHTREE_FROM_EDGES, 2);
+ if (has_poly) {
+ BKE_bvhtree_from_mesh_get(surmd->bvhtree, surmd->mesh, BVHTREE_FROM_LOOPTRI, 2);
+ }
+ else if (has_edge) {
+ BKE_bvhtree_from_mesh_get(surmd->bvhtree, surmd->mesh, BVHTREE_FROM_EDGES, 2);
+ }
}
}
}
@@ -241,7 +245,6 @@ ModifierTypeInfo modifierType_Surface = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ NULL,
diff --git a/source/blender/modifiers/intern/MOD_surfacedeform.c b/source/blender/modifiers/intern/MOD_surfacedeform.c
index 99011c5e351..0cc68c2c4a3 100644
--- a/source/blender/modifiers/intern/MOD_surfacedeform.c
+++ b/source/blender/modifiers/intern/MOD_surfacedeform.c
@@ -1646,7 +1646,6 @@ ModifierTypeInfo modifierType_SurfaceDeform = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_triangulate.c b/source/blender/modifiers/intern/MOD_triangulate.c
index 04d24ac0883..ef633494c7b 100644
--- a/source/blender/modifiers/intern/MOD_triangulate.c
+++ b/source/blender/modifiers/intern/MOD_triangulate.c
@@ -178,7 +178,6 @@ ModifierTypeInfo modifierType_Triangulate = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ NULL, // requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_ui_common.c b/source/blender/modifiers/intern/MOD_ui_common.c
index 6b2facc16a2..0be5c164089 100644
--- a/source/blender/modifiers/intern/MOD_ui_common.c
+++ b/source/blender/modifiers/intern/MOD_ui_common.c
@@ -344,14 +344,39 @@ static void modifier_panel_header(const bContext *C, Panel *panel)
}
} /* Tessellation point for curve-typed objects. */
else if (ELEM(ob->type, OB_CURVE, OB_SURF, OB_FONT)) {
- if (mti->type != eModifierTypeType_Constructive) {
+ /* Some modifiers can work with pre-tessellated curves only. */
+ if (ELEM(md->type, eModifierType_Hook, eModifierType_Softbody, eModifierType_MeshDeform)) {
+ /* Add button (appearing to be ON) and add tip why this cant be changed. */
+ sub = uiLayoutRow(row, true);
+ uiBlock *block = uiLayoutGetBlock(sub);
+ static int apply_on_spline_always_on_hack = eModifierMode_ApplyOnSpline;
+ uiBut *but = uiDefIconButBitI(block,
+ UI_BTYPE_TOGGLE,
+ eModifierMode_ApplyOnSpline,
+ 0,
+ ICON_SURFACE_DATA,
+ 0,
+ 0,
+ UI_UNIT_X - 2,
+ UI_UNIT_Y,
+ &apply_on_spline_always_on_hack,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ TIP_("Apply on Spline"));
+ UI_but_disable(
+ but, TIP_("This modifier can only deform control points, not the filled curve/surface"));
+ buttons_number++;
+ }
+ else if (mti->type != eModifierTypeType_Constructive) {
/* Constructive modifiers tessellates curve before applying. */
uiItemR(row, ptr, "use_apply_on_spline", 0, "", ICON_NONE);
buttons_number++;
}
}
/* Collision and Surface are always enabled, hide buttons. */
- if ((md->type != eModifierType_Collision) && (md->type != eModifierType_Surface)) {
+ if (!ELEM(md->type, eModifierType_Collision, eModifierType_Surface)) {
if (mti->flags & eModifierTypeFlag_SupportsEditmode) {
sub = uiLayoutRow(row, true);
uiLayoutSetActive(sub, (md->mode & eModifierMode_Realtime));
diff --git a/source/blender/modifiers/intern/MOD_uvproject.c b/source/blender/modifiers/intern/MOD_uvproject.c
index 3162a33edc2..724d1370a47 100644
--- a/source/blender/modifiers/intern/MOD_uvproject.c
+++ b/source/blender/modifiers/intern/MOD_uvproject.c
@@ -385,7 +385,6 @@ ModifierTypeInfo modifierType_UVProject = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_uvwarp.c b/source/blender/modifiers/intern/MOD_uvwarp.c
index 77b79167c2f..5742144b6dd 100644
--- a/source/blender/modifiers/intern/MOD_uvwarp.c
+++ b/source/blender/modifiers/intern/MOD_uvwarp.c
@@ -342,7 +342,6 @@ ModifierTypeInfo modifierType_UVWarp = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_volume_displace.cc b/source/blender/modifiers/intern/MOD_volume_displace.cc
index 745e089b8ff..af4b31d6bfc 100644
--- a/source/blender/modifiers/intern/MOD_volume_displace.cc
+++ b/source/blender/modifiers/intern/MOD_volume_displace.cc
@@ -18,6 +18,7 @@
* \ingroup modifiers
*/
+#include "BKE_geometry_set.hh"
#include "BKE_lib_query.h"
#include "BKE_mesh_runtime.h"
#include "BKE_modifier.h"
@@ -284,7 +285,7 @@ struct DisplaceGridOp {
#endif
-static Volume *modifyVolume(ModifierData *md, const ModifierEvalContext *ctx, Volume *volume)
+static void displace_volume(ModifierData *md, const ModifierEvalContext *ctx, Volume *volume)
{
#ifdef WITH_OPENVDB
VolumeDisplaceModifierData *vdmd = reinterpret_cast<VolumeDisplaceModifierData *>(md);
@@ -293,7 +294,7 @@ static Volume *modifyVolume(ModifierData *md, const ModifierEvalContext *ctx, Vo
BKE_volume_load(volume, DEG_get_bmain(ctx->depsgraph));
const int grid_amount = BKE_volume_num_grids(volume);
for (int grid_index = 0; grid_index < grid_amount; grid_index++) {
- VolumeGrid *volume_grid = BKE_volume_grid_get(volume, grid_index);
+ VolumeGrid *volume_grid = BKE_volume_grid_get_for_write(volume, grid_index);
BLI_assert(volume_grid != nullptr);
openvdb::GridBase::Ptr grid = BKE_volume_grid_openvdb_for_write(volume, volume_grid, false);
@@ -303,14 +304,22 @@ static Volume *modifyVolume(ModifierData *md, const ModifierEvalContext *ctx, Vo
BKE_volume_grid_type_operation(grid_type, displace_grid_op);
}
- return volume;
#else
- UNUSED_VARS(md, ctx);
+ UNUSED_VARS(md, volume, ctx);
BKE_modifier_set_error(ctx->object, md, "Compiled without OpenVDB");
- return volume;
#endif
}
+static void modifyGeometrySet(ModifierData *md,
+ const ModifierEvalContext *ctx,
+ GeometrySet *geometry_set)
+{
+ Volume *input_volume = geometry_set->get_volume_for_write();
+ if (input_volume != nullptr) {
+ displace_volume(md, ctx, input_volume);
+ }
+}
+
ModifierTypeInfo modifierType_VolumeDisplace = {
/* name */ "Volume Displace",
/* structName */ "VolumeDisplaceModifierData",
@@ -328,8 +337,7 @@ ModifierTypeInfo modifierType_VolumeDisplace = {
/* deformMatricesEM */ nullptr,
/* modifyMesh */ nullptr,
/* modifyHair */ nullptr,
- /* modifyGeometrySet */ nullptr,
- /* modifyVolume */ modifyVolume,
+ /* modifyGeometrySet */ modifyGeometrySet,
/* initData */ initData,
/* requiredDataMask */ nullptr,
diff --git a/source/blender/modifiers/intern/MOD_volume_to_mesh.cc b/source/blender/modifiers/intern/MOD_volume_to_mesh.cc
index 41ed7ae983a..c0bf07b8eec 100644
--- a/source/blender/modifiers/intern/MOD_volume_to_mesh.cc
+++ b/source/blender/modifiers/intern/MOD_volume_to_mesh.cc
@@ -155,10 +155,10 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
return create_empty_mesh(input_mesh);
}
- Volume *volume = static_cast<Volume *>(vmmd->object->data);
+ const Volume *volume = static_cast<Volume *>(vmmd->object->data);
BKE_volume_load(volume, DEG_get_bmain(ctx->depsgraph));
- VolumeGrid *volume_grid = BKE_volume_grid_find(volume, vmmd->grid_name);
+ const VolumeGrid *volume_grid = BKE_volume_grid_find_for_read(volume, vmmd->grid_name);
if (volume_grid == nullptr) {
BKE_modifier_set_error(ctx->object, md, "Cannot find '%s' grid", vmmd->grid_name);
return create_empty_mesh(input_mesh);
@@ -223,7 +223,6 @@ ModifierTypeInfo modifierType_VolumeToMesh = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ nullptr,
/* modifyGeometrySet */ nullptr,
- /* modifyVolume */ nullptr,
/* initData */ initData,
/* requiredDataMask */ nullptr,
diff --git a/source/blender/modifiers/intern/MOD_warp.c b/source/blender/modifiers/intern/MOD_warp.c
index 9d3d5b0658c..3bebc52c503 100644
--- a/source/blender/modifiers/intern/MOD_warp.c
+++ b/source/blender/modifiers/intern/MOD_warp.c
@@ -539,7 +539,6 @@ ModifierTypeInfo modifierType_Warp = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_wave.c b/source/blender/modifiers/intern/MOD_wave.c
index 863656b85a5..c6bab89247e 100644
--- a/source/blender/modifiers/intern/MOD_wave.c
+++ b/source/blender/modifiers/intern/MOD_wave.c
@@ -492,7 +492,6 @@ ModifierTypeInfo modifierType_Wave = {
/* modifyMesh */ NULL,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_weighted_normal.c b/source/blender/modifiers/intern/MOD_weighted_normal.c
index 40265e37db9..2f2da7d6554 100644
--- a/source/blender/modifiers/intern/MOD_weighted_normal.c
+++ b/source/blender/modifiers/intern/MOD_weighted_normal.c
@@ -764,7 +764,6 @@ ModifierTypeInfo modifierType_WeightedNormal = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_weightvgedit.c b/source/blender/modifiers/intern/MOD_weightvgedit.c
index 915adccc745..b5f72c88800 100644
--- a/source/blender/modifiers/intern/MOD_weightvgedit.c
+++ b/source/blender/modifiers/intern/MOD_weightvgedit.c
@@ -428,7 +428,6 @@ ModifierTypeInfo modifierType_WeightVGEdit = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_weightvgmix.c b/source/blender/modifiers/intern/MOD_weightvgmix.c
index 52cee7ce7e5..a71a2f3b480 100644
--- a/source/blender/modifiers/intern/MOD_weightvgmix.c
+++ b/source/blender/modifiers/intern/MOD_weightvgmix.c
@@ -514,7 +514,6 @@ ModifierTypeInfo modifierType_WeightVGMix = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_weightvgproximity.c b/source/blender/modifiers/intern/MOD_weightvgproximity.c
index aac29cabf0f..cd03175f16c 100644
--- a/source/blender/modifiers/intern/MOD_weightvgproximity.c
+++ b/source/blender/modifiers/intern/MOD_weightvgproximity.c
@@ -768,7 +768,6 @@ ModifierTypeInfo modifierType_WeightVGProximity = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_weld.c b/source/blender/modifiers/intern/MOD_weld.c
index fd1254fc948..1590f342666 100644
--- a/source/blender/modifiers/intern/MOD_weld.c
+++ b/source/blender/modifiers/intern/MOD_weld.c
@@ -2053,7 +2053,6 @@ ModifierTypeInfo modifierType_Weld = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/modifiers/intern/MOD_wireframe.c b/source/blender/modifiers/intern/MOD_wireframe.c
index 3d8e74d2cf5..16bf1f7d763 100644
--- a/source/blender/modifiers/intern/MOD_wireframe.c
+++ b/source/blender/modifiers/intern/MOD_wireframe.c
@@ -196,7 +196,6 @@ ModifierTypeInfo modifierType_Wireframe = {
/* modifyMesh */ modifyMesh,
/* modifyHair */ NULL,
/* modifyGeometrySet */ NULL,
- /* modifyVolume */ NULL,
/* initData */ initData,
/* requiredDataMask */ requiredDataMask,
diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt
index 42679f39f18..54925ea3317 100644
--- a/source/blender/nodes/CMakeLists.txt
+++ b/source/blender/nodes/CMakeLists.txt
@@ -47,6 +47,7 @@ set(INC
set(SRC
composite/nodes/node_composite_alphaOver.c
+ composite/nodes/node_composite_antialiasing.c
composite/nodes/node_composite_bilateralblur.c
composite/nodes/node_composite_blur.c
composite/nodes/node_composite_bokehblur.c
@@ -140,33 +141,48 @@ set(SRC
function/node_function_util.cc
geometry/nodes/node_geo_align_rotation_to_vector.cc
+ geometry/nodes/node_geo_attribute_clamp.cc
geometry/nodes/node_geo_attribute_color_ramp.cc
geometry/nodes/node_geo_attribute_combine_xyz.cc
geometry/nodes/node_geo_attribute_compare.cc
geometry/nodes/node_geo_attribute_convert.cc
+ geometry/nodes/node_geo_attribute_curve_map.cc
geometry/nodes/node_geo_attribute_fill.cc
+ geometry/nodes/node_geo_attribute_map_range.cc
geometry/nodes/node_geo_attribute_math.cc
geometry/nodes/node_geo_attribute_mix.cc
geometry/nodes/node_geo_attribute_proximity.cc
geometry/nodes/node_geo_attribute_randomize.cc
+ geometry/nodes/node_geo_attribute_remove.cc
geometry/nodes/node_geo_attribute_sample_texture.cc
geometry/nodes/node_geo_attribute_separate_xyz.cc
+ geometry/nodes/node_geo_attribute_transfer.cc
geometry/nodes/node_geo_attribute_vector_math.cc
- geometry/nodes/node_geo_attribute_remove.cc
+ geometry/nodes/node_geo_attribute_vector_rotate.cc
geometry/nodes/node_geo_boolean.cc
+ geometry/nodes/node_geo_bounding_box.cc
geometry/nodes/node_geo_collection_info.cc
geometry/nodes/node_geo_common.cc
+ geometry/nodes/node_geo_convex_hull.cc
+ geometry/nodes/node_geo_curve_length.cc
+ geometry/nodes/node_geo_curve_to_mesh.cc
+ geometry/nodes/node_geo_curve_resample.cc
+ geometry/nodes/node_geo_delete_geometry.cc
geometry/nodes/node_geo_edge_split.cc
+ geometry/nodes/node_geo_input_material.cc
geometry/nodes/node_geo_is_viewport.cc
geometry/nodes/node_geo_join_geometry.cc
+ geometry/nodes/node_geo_material_assign.cc
+ geometry/nodes/node_geo_material_replace.cc
geometry/nodes/node_geo_mesh_primitive_circle.cc
geometry/nodes/node_geo_mesh_primitive_cone.cc
geometry/nodes/node_geo_mesh_primitive_cube.cc
geometry/nodes/node_geo_mesh_primitive_cylinder.cc
+ geometry/nodes/node_geo_mesh_primitive_grid.cc
geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc
geometry/nodes/node_geo_mesh_primitive_line.cc
- geometry/nodes/node_geo_mesh_primitive_plane.cc
geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc
+ geometry/nodes/node_geo_mesh_to_curve.cc
geometry/nodes/node_geo_object_info.cc
geometry/nodes/node_geo_point_distribute.cc
geometry/nodes/node_geo_point_instance.cc
@@ -175,8 +191,10 @@ set(SRC
geometry/nodes/node_geo_point_separate.cc
geometry/nodes/node_geo_point_translate.cc
geometry/nodes/node_geo_points_to_volume.cc
+ geometry/nodes/node_geo_select_by_material.cc
geometry/nodes/node_geo_subdivide.cc
geometry/nodes/node_geo_subdivision_surface.cc
+ geometry/nodes/node_geo_switch.cc
geometry/nodes/node_geo_transform.cc
geometry/nodes/node_geo_triangulate.cc
geometry/nodes/node_geo_volume_to_mesh.cc
@@ -207,7 +225,7 @@ set(SRC
shader/nodes/node_shader_camera.c
shader/nodes/node_shader_clamp.cc
shader/nodes/node_shader_common.c
- shader/nodes/node_shader_curves.c
+ shader/nodes/node_shader_curves.cc
shader/nodes/node_shader_displacement.c
shader/nodes/node_shader_eevee_specular.c
shader/nodes/node_shader_emission.c
@@ -225,7 +243,7 @@ set(SRC
shader/nodes/node_shader_map_range.cc
shader/nodes/node_shader_mapping.c
shader/nodes/node_shader_math.cc
- shader/nodes/node_shader_mixRgb.c
+ shader/nodes/node_shader_mixRgb.cc
shader/nodes/node_shader_mix_shader.c
shader/nodes/node_shader_normal.c
shader/nodes/node_shader_normal_map.c
@@ -306,13 +324,14 @@ set(SRC
intern/derived_node_tree.cc
intern/math_functions.cc
intern/node_common.c
- intern/node_exec.c
+ intern/node_exec.cc
intern/node_geometry_exec.cc
intern/node_socket.cc
intern/node_tree_multi_function.cc
intern/node_tree_ref.cc
intern/node_util.c
intern/type_callbacks.cc
+ intern/type_conversions.cc
composite/node_composite_util.h
function/node_function_util.hh
@@ -334,6 +353,7 @@ set(SRC
NOD_static_types.h
NOD_texture.h
NOD_type_callbacks.hh
+ NOD_type_conversions.hh
intern/node_common.h
intern/node_exec.h
intern/node_util.h
@@ -345,6 +365,23 @@ set(LIB
bf_intern_sky
)
+if(WITH_BULLET)
+ list(APPEND INC_SYS
+ ${BULLET_INCLUDE_DIRS}
+ "../../../intern/rigidbody/"
+ )
+ if(NOT WITH_SYSTEM_BULLET)
+ list(APPEND LIB
+ extern_bullet
+ )
+ endif()
+
+ list(APPEND LIB
+ ${BULLET_LIBRARIES}
+ )
+ add_definitions(-DWITH_BULLET)
+endif()
+
if(WITH_PYTHON)
list(APPEND INC
../python
@@ -389,6 +426,18 @@ if(WITH_OPENSUBDIV)
add_definitions(-DWITH_OPENSUBDIV)
endif()
+if(WITH_GMP)
+ add_definitions(-DWITH_GMP)
+
+ list(APPEND INC_SYS
+ ${GMP_INCLUDE_DIRS}
+ )
+
+ list(APPEND LIB
+ ${GMP_LIBRARIES}
+ )
+endif()
+
if(WITH_OPENVDB)
list(APPEND INC_SYS
${OPENVDB_INCLUDE_DIRS}
diff --git a/source/blender/nodes/NOD_composite.h b/source/blender/nodes/NOD_composite.h
index b0dd0edeec5..258e4c961c9 100644
--- a/source/blender/nodes/NOD_composite.h
+++ b/source/blender/nodes/NOD_composite.h
@@ -79,6 +79,7 @@ void register_node_type_cmp_inpaint(void);
void register_node_type_cmp_despeckle(void);
void register_node_type_cmp_defocus(void);
void register_node_type_cmp_denoise(void);
+void register_node_type_cmp_antialiasing(void);
void register_node_type_cmp_valtorgb(void);
void register_node_type_cmp_rgbtobw(void);
diff --git a/source/blender/nodes/NOD_derived_node_tree.hh b/source/blender/nodes/NOD_derived_node_tree.hh
index c29de611e18..de9e4c8c812 100644
--- a/source/blender/nodes/NOD_derived_node_tree.hh
+++ b/source/blender/nodes/NOD_derived_node_tree.hh
@@ -59,6 +59,7 @@ class DTreeContext {
const NodeTreeRef *tree_;
/* All the children contexts of this context. */
Map<const NodeRef *, DTreeContext *> children_;
+ DerivedNodeTree *derived_tree_;
friend DerivedNodeTree;
@@ -67,6 +68,7 @@ class DTreeContext {
const DTreeContext *parent_context() const;
const NodeRef *parent_node() const;
const DTreeContext *child_context(const NodeRef &node) const;
+ const DerivedNodeTree &derived_tree() const;
bool is_root() const;
};
@@ -90,6 +92,12 @@ class DNode {
operator bool() const;
uint64_t hash() const;
+
+ DInputSocket input(int index) const;
+ DOutputSocket output(int index) const;
+
+ DInputSocket input_by_identifier(StringRef identifier) const;
+ DOutputSocket output_by_identifier(StringRef identifier) const;
};
/* A (nullable) reference to a socket and the context it is in. It is unique within an entire
@@ -117,6 +125,8 @@ class DSocket {
operator bool() const;
uint64_t hash() const;
+
+ DNode node() const;
};
/* A (nullable) reference to an input socket and the context it is in. */
@@ -132,7 +142,7 @@ class DInputSocket : public DSocket {
DOutputSocket get_corresponding_group_node_output() const;
Vector<DOutputSocket, 4> get_corresponding_group_input_sockets() const;
- void foreach_origin_socket(FunctionRef<void(DSocket)> callback) const;
+ void foreach_origin_socket(FunctionRef<void(DSocket)> origin_fn) const;
};
/* A (nullable) reference to an output socket and the context it is in. */
@@ -148,7 +158,8 @@ class DOutputSocket : public DSocket {
DInputSocket get_corresponding_group_node_input() const;
DInputSocket get_active_corresponding_group_output_socket() const;
- void foreach_target_socket(FunctionRef<void(DInputSocket)> callback) const;
+ void foreach_target_socket(FunctionRef<void(DInputSocket)> target_fn,
+ FunctionRef<void(DSocket)> skipped_fn) const;
};
class DerivedNodeTree {
@@ -165,6 +176,7 @@ class DerivedNodeTree {
Span<const NodeTreeRef *> used_node_tree_refs() const;
bool has_link_cycles() const;
+ bool has_undefined_nodes_or_sockets() const;
void foreach_node(FunctionRef<void(DNode)> callback) const;
std::string to_dot() const;
@@ -214,6 +226,11 @@ inline const DTreeContext *DTreeContext::child_context(const NodeRef &node) cons
return children_.lookup_default(&node, nullptr);
}
+inline const DerivedNodeTree &DTreeContext::derived_tree() const
+{
+ return *derived_tree_;
+}
+
inline bool DTreeContext::is_root() const
{
return parent_context_ == nullptr;
@@ -264,6 +281,26 @@ inline uint64_t DNode::hash() const
return get_default_hash_2(context_, node_ref_);
}
+inline DInputSocket DNode::input(int index) const
+{
+ return {context_, &node_ref_->input(index)};
+}
+
+inline DOutputSocket DNode::output(int index) const
+{
+ return {context_, &node_ref_->output(index)};
+}
+
+inline DInputSocket DNode::input_by_identifier(StringRef identifier) const
+{
+ return {context_, &node_ref_->input_by_identifier(identifier)};
+}
+
+inline DOutputSocket DNode::output_by_identifier(StringRef identifier) const
+{
+ return {context_, &node_ref_->output_by_identifier(identifier)};
+}
+
/* --------------------------------------------------------------------
* DSocket inline methods.
*/
@@ -319,6 +356,12 @@ inline uint64_t DSocket::hash() const
return get_default_hash_2(context_, socket_ref_);
}
+inline DNode DSocket::node() const
+{
+ BLI_assert(socket_ref_ != nullptr);
+ return {context_, &socket_ref_->node()};
+}
+
/* --------------------------------------------------------------------
* DInputSocket inline methods.
*/
diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h
index 03115852d80..b7e1b0b657c 100644
--- a/source/blender/nodes/NOD_geometry.h
+++ b/source/blender/nodes/NOD_geometry.h
@@ -20,38 +20,56 @@
extern "C" {
#endif
+#include "BKE_node.h"
+
extern struct bNodeTreeType *ntreeType_Geometry;
void register_node_tree_type_geo(void);
void register_node_type_geo_group(void);
+void register_node_type_geo_custom_group(bNodeType *ntype);
void register_node_type_geo_align_rotation_to_vector(void);
+void register_node_type_geo_attribute_clamp(void);
void register_node_type_geo_attribute_color_ramp(void);
void register_node_type_geo_attribute_combine_xyz(void);
void register_node_type_geo_attribute_compare(void);
void register_node_type_geo_attribute_convert(void);
+void register_node_type_geo_attribute_curve_map(void);
void register_node_type_geo_attribute_fill(void);
+void register_node_type_geo_attribute_map_range(void);
void register_node_type_geo_attribute_math(void);
void register_node_type_geo_attribute_mix(void);
void register_node_type_geo_attribute_proximity(void);
void register_node_type_geo_attribute_randomize(void);
void register_node_type_geo_attribute_separate_xyz(void);
+void register_node_type_geo_attribute_transfer(void);
void register_node_type_geo_attribute_vector_math(void);
+void register_node_type_geo_attribute_vector_rotate(void);
void register_node_type_geo_attribute_remove(void);
void register_node_type_geo_boolean(void);
+void register_node_type_geo_bounding_box(void);
void register_node_type_geo_collection_info(void);
+void register_node_type_geo_convex_hull(void);
+void register_node_type_geo_curve_length(void);
+void register_node_type_geo_curve_to_mesh(void);
+void register_node_type_geo_curve_resample(void);
+void register_node_type_geo_delete_geometry(void);
void register_node_type_geo_edge_split(void);
+void register_node_type_geo_input_material(void);
void register_node_type_geo_is_viewport(void);
void register_node_type_geo_join_geometry(void);
+void register_node_type_geo_material_assign(void);
+void register_node_type_geo_material_replace(void);
void register_node_type_geo_mesh_primitive_circle(void);
void register_node_type_geo_mesh_primitive_cone(void);
void register_node_type_geo_mesh_primitive_cube(void);
-void register_node_type_geo_mesh_primitive_plane(void);
void register_node_type_geo_mesh_primitive_cylinder(void);
+void register_node_type_geo_mesh_primitive_grid(void);
void register_node_type_geo_mesh_primitive_ico_sphere(void);
void register_node_type_geo_mesh_primitive_line(void);
void register_node_type_geo_mesh_primitive_uv_sphere(void);
+void register_node_type_geo_mesh_to_curve(void);
void register_node_type_geo_object_info(void);
void register_node_type_geo_point_distribute(void);
void register_node_type_geo_point_instance(void);
@@ -61,8 +79,10 @@ void register_node_type_geo_point_separate(void);
void register_node_type_geo_point_translate(void);
void register_node_type_geo_points_to_volume(void);
void register_node_type_geo_sample_texture(void);
+void register_node_type_geo_select_by_material(void);
void register_node_type_geo_subdivide(void);
void register_node_type_geo_subdivision_surface(void);
+void register_node_type_geo_switch(void);
void register_node_type_geo_transform(void);
void register_node_type_geo_triangulate(void);
void register_node_type_geo_volume_to_mesh(void);
diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh
index ba606dacdb0..86c98525814 100644
--- a/source/blender/nodes/NOD_geometry_exec.hh
+++ b/source/blender/nodes/NOD_geometry_exec.hh
@@ -22,7 +22,6 @@
#include "BKE_geometry_set.hh"
#include "BKE_geometry_set_instances.hh"
#include "BKE_node_ui_storage.hh"
-#include "BKE_persistent_data_handle.hh"
#include "DNA_node_types.h"
@@ -33,55 +32,90 @@ struct ModifierData;
namespace blender::nodes {
-using bke::BooleanReadAttribute;
-using bke::BooleanWriteAttribute;
-using bke::Color4fReadAttribute;
-using bke::Color4fWriteAttribute;
-using bke::Float2ReadAttribute;
-using bke::Float2WriteAttribute;
-using bke::Float3ReadAttribute;
-using bke::Float3WriteAttribute;
-using bke::FloatReadAttribute;
-using bke::FloatWriteAttribute;
using bke::geometry_set_realize_instances;
-using bke::Int32ReadAttribute;
-using bke::Int32WriteAttribute;
-using bke::PersistentDataHandleMap;
-using bke::PersistentObjectHandle;
-using bke::ReadAttribute;
-using bke::ReadAttributePtr;
-using bke::WriteAttribute;
-using bke::WriteAttributePtr;
+using bke::OutputAttribute;
+using bke::OutputAttribute_Typed;
+using bke::ReadAttributeLookup;
+using bke::WriteAttributeLookup;
using fn::CPPType;
using fn::GMutablePointer;
+using fn::GMutableSpan;
using fn::GPointer;
+using fn::GSpan;
using fn::GValueMap;
+using fn::GVArray;
+using fn::GVArray_GSpan;
+using fn::GVArray_Span;
+using fn::GVArray_Typed;
+using fn::GVArrayPtr;
+using fn::GVMutableArray;
+using fn::GVMutableArray_GSpan;
+using fn::GVMutableArray_Typed;
+using fn::GVMutableArrayPtr;
+
+/**
+ * This class exists to separate the memory management details of the geometry nodes evaluator from
+ * the node execution functions and related utilities.
+ */
+class GeoNodeExecParamsProvider {
+ public:
+ DNode dnode;
+ const Object *self_object = nullptr;
+ const ModifierData *modifier = nullptr;
+ Depsgraph *depsgraph = nullptr;
+
+ /**
+ * Returns true when the node is allowed to get/extract the input value. The identifier is
+ * expected to be valid. This may return false if the input value has been consumed already.
+ */
+ virtual bool can_get_input(StringRef identifier) const = 0;
+
+ /**
+ * Returns true when the node is allowed to set the output value. The identifier is expected to
+ * be valid. This may return false if the output value has been set already.
+ */
+ virtual bool can_set_output(StringRef identifier) const = 0;
+
+ /**
+ * Take ownership of an input value. The caller is responsible for destructing the value. It does
+ * not have to be freed, because the memory is managed by the geometry nodes evaluator.
+ */
+ virtual GMutablePointer extract_input(StringRef identifier) = 0;
+
+ /**
+ * Similar to #extract_input, but has to be used for multi-input sockets.
+ */
+ virtual Vector<GMutablePointer> extract_multi_input(StringRef identifier) = 0;
+
+ /**
+ * Get the input value for the identifier without taking ownership of it.
+ */
+ virtual GPointer get_input(StringRef identifier) const = 0;
+
+ /**
+ * Prepare a memory buffer for an output value of the node. The returned memory has to be
+ * initialized by the caller. The identifier and type are expected to be correct.
+ */
+ virtual GMutablePointer alloc_output_value(const CPPType &type) = 0;
+
+ /**
+ * The value has been allocated with #alloc_output_value.
+ */
+ virtual void set_output(StringRef identifier, GMutablePointer value) = 0;
+
+ /* A description for these methods is provided in GeoNodeExecParams. */
+ virtual void set_input_unused(StringRef identifier) = 0;
+ virtual bool output_is_required(StringRef identifier) const = 0;
+ virtual bool lazy_require_input(StringRef identifier) = 0;
+ virtual bool lazy_output_is_required(StringRef identifier) const = 0;
+};
class GeoNodeExecParams {
private:
- const DNode node_;
- GValueMap<StringRef> &input_values_;
- GValueMap<StringRef> &output_values_;
- const PersistentDataHandleMap &handle_map_;
- const Object *self_object_;
- const ModifierData *modifier_;
- Depsgraph *depsgraph_;
+ GeoNodeExecParamsProvider *provider_;
public:
- GeoNodeExecParams(const DNode node,
- GValueMap<StringRef> &input_values,
- GValueMap<StringRef> &output_values,
- const PersistentDataHandleMap &handle_map,
- const Object *self_object,
- const ModifierData *modifier,
- Depsgraph *depsgraph)
- : node_(node),
- input_values_(input_values),
- output_values_(output_values),
- handle_map_(handle_map),
- self_object_(self_object),
- modifier_(modifier),
- depsgraph_(depsgraph)
+ GeoNodeExecParams(GeoNodeExecParamsProvider &provider) : provider_(&provider)
{
}
@@ -94,9 +128,9 @@ class GeoNodeExecParams {
GMutablePointer extract_input(StringRef identifier)
{
#ifdef DEBUG
- this->check_extract_input(identifier);
+ this->check_input_access(identifier);
#endif
- return input_values_.extract(identifier);
+ return provider_->extract_input(identifier);
}
/**
@@ -107,9 +141,10 @@ class GeoNodeExecParams {
template<typename T> T extract_input(StringRef identifier)
{
#ifdef DEBUG
- this->check_extract_input(identifier, &CPPType::get<T>());
+ this->check_input_access(identifier, &CPPType::get<T>());
#endif
- return input_values_.extract<T>(identifier);
+ GMutablePointer gvalue = this->extract_input(identifier);
+ return gvalue.relocate_out<T>();
}
/**
@@ -119,18 +154,10 @@ class GeoNodeExecParams {
*/
template<typename T> Vector<T> extract_multi_input(StringRef identifier)
{
+ Vector<GMutablePointer> gvalues = provider_->extract_multi_input(identifier);
Vector<T> values;
- int index = 0;
- while (true) {
- std::string sub_identifier = identifier;
- if (index > 0) {
- sub_identifier += "[" + std::to_string(index) + "]";
- }
- if (!input_values_.contains(sub_identifier)) {
- break;
- }
- values.append(input_values_.extract<T, StringRef>(sub_identifier));
- index++;
+ for (GMutablePointer gvalue : gvalues) {
+ values.append(gvalue.relocate_out<T>());
}
return values;
}
@@ -141,67 +168,83 @@ class GeoNodeExecParams {
template<typename T> const T &get_input(StringRef identifier) const
{
#ifdef DEBUG
- this->check_extract_input(identifier, &CPPType::get<T>());
+ this->check_input_access(identifier, &CPPType::get<T>());
#endif
- return input_values_.lookup<T>(identifier);
+ GPointer gvalue = provider_->get_input(identifier);
+ BLI_assert(gvalue.is_type<T>());
+ return *(const T *)gvalue.get();
}
/**
- * Move-construct a new value based on the given value and store it for the given socket
- * identifier.
+ * Store the output value for the given socket identifier.
*/
- void set_output_by_move(StringRef identifier, GMutablePointer value)
+ template<typename T> void set_output(StringRef identifier, T &&value)
{
+ using StoredT = std::decay_t<T>;
+ const CPPType &type = CPPType::get<std::decay_t<T>>();
#ifdef DEBUG
- BLI_assert(value.type() != nullptr);
- BLI_assert(value.get() != nullptr);
- this->check_set_output(identifier, *value.type());
+ this->check_output_access(identifier, type);
#endif
- output_values_.add_new_by_move(identifier, value);
+ GMutablePointer gvalue = provider_->alloc_output_value(type);
+ new (gvalue.get()) StoredT(std::forward<T>(value));
+ provider_->set_output(identifier, gvalue);
}
- void set_output_by_copy(StringRef identifier, GPointer value)
+ /**
+ * Tell the evaluator that a specific input won't be used anymore.
+ */
+ void set_input_unused(StringRef identifier)
{
-#ifdef DEBUG
- BLI_assert(value.type() != nullptr);
- BLI_assert(value.get() != nullptr);
- this->check_set_output(identifier, *value.type());
-#endif
- output_values_.add_new_by_copy(identifier, value);
+ provider_->set_input_unused(identifier);
}
/**
- * Store the output value for the given socket identifier.
+ * Returns true when the output has to be computed.
+ * Nodes that support laziness could use the #lazy_output_is_required variant to possibly avoid
+ * some computations.
*/
- template<typename T> void set_output(StringRef identifier, T &&value)
+ bool output_is_required(StringRef identifier) const
{
-#ifdef DEBUG
- this->check_set_output(identifier, CPPType::get<std::decay_t<T>>());
-#endif
- output_values_.add_new(identifier, std::forward<T>(value));
+ return provider_->output_is_required(identifier);
}
/**
- * Get the node that is currently being executed.
+ * Tell the evaluator that a specific input is required.
+ * This returns true when the input will only be available in the next execution.
+ * False is returned if the input is available already.
+ * This can only be used when the node supports laziness.
*/
- const bNode &node() const
+ bool lazy_require_input(StringRef identifier)
{
- return *node_->bnode();
+ return provider_->lazy_require_input(identifier);
}
- const PersistentDataHandleMap &handle_map() const
+ /**
+ * Asks the evaluator if a specific output is required right now. If this returns false, the
+ * value might still need to be computed later.
+ * This can only be used when the node supports laziness.
+ */
+ bool lazy_output_is_required(StringRef identifier)
+ {
+ return provider_->lazy_output_is_required(identifier);
+ }
+
+ /**
+ * Get the node that is currently being executed.
+ */
+ const bNode &node() const
{
- return handle_map_;
+ return *provider_->dnode->bnode();
}
const Object *self_object() const
{
- return self_object_;
+ return provider_->self_object;
}
Depsgraph *depsgraph() const
{
- return depsgraph_;
+ return provider_->depsgraph;
}
/**
@@ -217,20 +260,21 @@ class GeoNodeExecParams {
* \note This will add an error message if the string socket is active and
* the input attribute does not exist.
*/
- ReadAttributePtr get_input_attribute(const StringRef name,
- const GeometryComponent &component,
- const AttributeDomain domain,
- const CustomDataType type,
- const void *default_value) const;
+ GVArrayPtr get_input_attribute(const StringRef name,
+ const GeometryComponent &component,
+ const AttributeDomain domain,
+ const CustomDataType type,
+ const void *default_value) const;
template<typename T>
- bke::TypedReadAttribute<T> get_input_attribute(const StringRef name,
- const GeometryComponent &component,
- const AttributeDomain domain,
- const T &default_value) const
+ GVArray_Typed<T> get_input_attribute(const StringRef name,
+ const GeometryComponent &component,
+ const AttributeDomain domain,
+ const T &default_value) const
{
const CustomDataType type = bke::cpp_type_to_custom_data_type(CPPType::get<T>());
- return this->get_input_attribute(name, component, domain, type, &default_value);
+ GVArrayPtr varray = this->get_input_attribute(name, component, domain, type, &default_value);
+ return GVArray_Typed<T>(std::move(varray));
}
/**
@@ -247,8 +291,8 @@ class GeoNodeExecParams {
private:
/* Utilities for detecting common errors at when using this class. */
- void check_extract_input(StringRef identifier, const CPPType *requested_type = nullptr) const;
- void check_set_output(StringRef identifier, const CPPType &value_type) const;
+ void check_input_access(StringRef identifier, const CPPType *requested_type = nullptr) const;
+ void check_output_access(StringRef identifier, const CPPType &value_type) const;
/* Find the active socket socket with the input name (not the identifier). */
const bNodeSocket *find_available_socket(const StringRef name) const;
diff --git a/source/blender/nodes/NOD_math_functions.hh b/source/blender/nodes/NOD_math_functions.hh
index 08496e044c5..f3eb1c24087 100644
--- a/source/blender/nodes/NOD_math_functions.hh
+++ b/source/blender/nodes/NOD_math_functions.hh
@@ -347,6 +347,8 @@ inline bool try_dispatch_float_math_fl3_fl3_fl3_to_fl3(const NodeVectorMathOpera
};
switch (operation) {
+ case NODE_VECTOR_MATH_MULTIPLY_ADD:
+ return dispatch([](float3 a, float3 b, float3 c) { return a * b + c; });
case NODE_VECTOR_MATH_WRAP:
return dispatch([](float3 a, float3 b, float3 c) {
return float3(wrapf(a.x, b.x, c.x), wrapf(a.y, b.y, c.y), wrapf(a.z, b.z, c.z));
@@ -379,7 +381,8 @@ inline bool try_dispatch_float_math_fl3_fl3_fl_to_fl3(const NodeVectorMathOperat
switch (operation) {
case NODE_VECTOR_MATH_REFRACT:
- return dispatch([](float3 a, float3 b, float c) { return float3::refract(a, b.normalized(), c); });
+ return dispatch(
+ [](float3 a, float3 b, float c) { return float3::refract(a, b.normalized(), c); });
default:
return false;
}
diff --git a/source/blender/nodes/NOD_node_tree_multi_function.hh b/source/blender/nodes/NOD_node_tree_multi_function.hh
index df31ee18369..e3f31e011e4 100644
--- a/source/blender/nodes/NOD_node_tree_multi_function.hh
+++ b/source/blender/nodes/NOD_node_tree_multi_function.hh
@@ -29,7 +29,7 @@
#include "NOD_type_callbacks.hh"
#include "BLI_multi_value_map.hh"
-#include "BLI_resource_collector.hh"
+#include "BLI_resource_scope.hh"
namespace blender::nodes {
@@ -190,7 +190,7 @@ class MFNetworkTreeMap {
* This data is necessary throughout the generation of a MFNetwork from a node tree.
*/
struct CommonMFNetworkBuilderData {
- ResourceCollector &resources;
+ ResourceScope &scope;
fn::MFNetwork &network;
MFNetworkTreeMap &network_map;
const DerivedNodeTree &tree;
@@ -225,9 +225,9 @@ class MFNetworkBuilderBase {
* Returns a resource collector that will only be destructed after the multi-function network is
* destructed.
*/
- ResourceCollector &resources()
+ ResourceScope &resource_scope()
{
- return common_.resources;
+ return common_.scope;
}
/**
@@ -236,9 +236,9 @@ class MFNetworkBuilderBase {
template<typename T, typename... Args> T &construct_fn(Args &&... args)
{
BLI_STATIC_ASSERT((std::is_base_of_v<fn::MultiFunction, T>), "");
- void *buffer = common_.resources.linear_allocator().allocate(sizeof(T), alignof(T));
+ void *buffer = common_.scope.linear_allocator().allocate(sizeof(T), alignof(T));
T *fn = new (buffer) T(std::forward<Args>(args)...);
- common_.resources.add(destruct_ptr<T>(fn), fn->name().c_str());
+ common_.scope.add(destruct_ptr<T>(fn), fn->name().c_str());
return *fn;
}
};
@@ -382,39 +382,9 @@ class NodeMFNetworkBuilder : public MFNetworkBuilderBase {
MFNetworkTreeMap insert_node_tree_into_mf_network(fn::MFNetwork &network,
const DerivedNodeTree &tree,
- ResourceCollector &resources);
+ ResourceScope &scope);
using MultiFunctionByNode = Map<DNode, const fn::MultiFunction *>;
-MultiFunctionByNode get_multi_function_per_node(const DerivedNodeTree &tree,
- ResourceCollector &resources);
-
-class DataTypeConversions {
- private:
- Map<std::pair<fn::MFDataType, fn::MFDataType>, const fn::MultiFunction *> conversions_;
-
- public:
- void add(fn::MFDataType from_type, fn::MFDataType to_type, const fn::MultiFunction &fn)
- {
- conversions_.add_new({from_type, to_type}, &fn);
- }
-
- const fn::MultiFunction *get_conversion(fn::MFDataType from, fn::MFDataType to) const
- {
- return conversions_.lookup_default({from, to}, nullptr);
- }
-
- bool is_convertible(const CPPType &from_type, const CPPType &to_type) const
- {
- return conversions_.contains(
- {fn::MFDataType::ForSingle(from_type), fn::MFDataType::ForSingle(to_type)});
- }
-
- void convert(const CPPType &from_type,
- const CPPType &to_type,
- const void *from_value,
- void *to_value) const;
-};
-
-const DataTypeConversions &get_implicit_type_conversions();
+MultiFunctionByNode get_multi_function_per_node(const DerivedNodeTree &tree, ResourceScope &scope);
} // namespace blender::nodes
diff --git a/source/blender/nodes/NOD_node_tree_ref.hh b/source/blender/nodes/NOD_node_tree_ref.hh
index 3710bd2fe00..b028fc28bbc 100644
--- a/source/blender/nodes/NOD_node_tree_ref.hh
+++ b/source/blender/nodes/NOD_node_tree_ref.hh
@@ -70,6 +70,8 @@ class NodeTreeRef;
class LinkRef;
class InternalLinkRef;
+using SocketIndexByIdentifierMap = Map<std::string, int>;
+
class SocketRef : NonCopyable, NonMovable {
protected:
NodeRef *node_;
@@ -81,15 +83,19 @@ class SocketRef : NonCopyable, NonMovable {
Vector<LinkRef *> directly_linked_links_;
/* These sockets are linked directly, i.e. with a single link in between. */
- MutableSpan<SocketRef *> directly_linked_sockets_;
+ MutableSpan<const SocketRef *> directly_linked_sockets_;
/* These sockets are linked when reroutes, muted links and muted nodes have been taken into
* account. */
- MutableSpan<SocketRef *> logically_linked_sockets_;
+ MutableSpan<const SocketRef *> logically_linked_sockets_;
+ /* These are the sockets that have been skipped when searching for logically linked sockets.
+ * That includes for example the input and output socket of an intermediate reroute node. */
+ MutableSpan<const SocketRef *> logically_linked_skipped_sockets_;
friend NodeTreeRef;
public:
Span<const SocketRef *> logically_linked_sockets() const;
+ Span<const SocketRef *> logically_linked_skipped_sockets() const;
Span<const SocketRef *> directly_linked_sockets() const;
Span<const LinkRef *> directly_linked_links() const;
@@ -121,6 +127,7 @@ class SocketRef : NonCopyable, NonMovable {
bNodeTree *btree() const;
bool is_available() const;
+ bool is_undefined() const;
void *default_value() const;
template<typename T> T *default_value() const;
@@ -128,16 +135,31 @@ class SocketRef : NonCopyable, NonMovable {
class InputSocketRef final : public SocketRef {
public:
+ friend NodeTreeRef;
+
Span<const OutputSocketRef *> logically_linked_sockets() const;
Span<const OutputSocketRef *> directly_linked_sockets() const;
bool is_multi_input_socket() const;
+
+ private:
+ void foreach_logical_origin(FunctionRef<void(const OutputSocketRef &)> origin_fn,
+ FunctionRef<void(const SocketRef &)> skipped_fn,
+ bool only_follow_first_input_link,
+ Vector<const InputSocketRef *> &handled_sockets) const;
};
class OutputSocketRef final : public SocketRef {
public:
+ friend NodeTreeRef;
+
Span<const InputSocketRef *> logically_linked_sockets() const;
Span<const InputSocketRef *> directly_linked_sockets() const;
+
+ private:
+ void foreach_logical_target(FunctionRef<void(const InputSocketRef &)> target_fn,
+ FunctionRef<void(const SocketRef &)> skipped_fn,
+ Vector<const OutputSocketRef *> &handled_sockets) const;
};
class NodeRef : NonCopyable, NonMovable {
@@ -149,6 +171,8 @@ class NodeRef : NonCopyable, NonMovable {
Vector<InputSocketRef *> inputs_;
Vector<OutputSocketRef *> outputs_;
Vector<InternalLinkRef *> internal_links_;
+ SocketIndexByIdentifierMap *input_index_by_identifier_;
+ SocketIndexByIdentifierMap *output_index_by_identifier_;
friend NodeTreeRef;
@@ -162,6 +186,9 @@ class NodeRef : NonCopyable, NonMovable {
const InputSocketRef &input(int index) const;
const OutputSocketRef &output(int index) const;
+ const InputSocketRef &input_by_identifier(StringRef identifier) const;
+ const OutputSocketRef &output_by_identifier(StringRef identifier) const;
+
bNode *bnode() const;
bNodeTree *btree() const;
@@ -178,6 +205,7 @@ class NodeRef : NonCopyable, NonMovable {
bool is_group_output_node() const;
bool is_muted() const;
bool is_frame() const;
+ bool is_undefined() const;
void *storage() const;
template<typename T> T *storage() const;
@@ -225,6 +253,7 @@ class NodeTreeRef : NonCopyable, NonMovable {
Vector<OutputSocketRef *> output_sockets_;
Vector<LinkRef *> links_;
MultiValueMap<const bNodeType *, NodeRef *> nodes_by_type_;
+ Vector<std::unique_ptr<SocketIndexByIdentifierMap>> owned_identifier_maps_;
public:
NodeTreeRef(bNodeTree *btree);
@@ -241,6 +270,7 @@ class NodeTreeRef : NonCopyable, NonMovable {
Span<const LinkRef *> links() const;
bool has_link_cycles() const;
+ bool has_undefined_nodes_or_sockets() const;
bNodeTree *btree() const;
StringRefNull name() const;
@@ -257,12 +287,7 @@ class NodeTreeRef : NonCopyable, NonMovable {
bNodeSocket *bsocket);
void create_linked_socket_caches();
-
- void foreach_logical_origin(InputSocketRef &socket,
- FunctionRef<void(OutputSocketRef &)> callback,
- bool only_follow_first_input_link = false);
- void foreach_logical_target(OutputSocketRef &socket,
- FunctionRef<void(InputSocketRef &)> callback);
+ void create_socket_identifier_maps();
};
using NodeTreeRefMap = Map<bNodeTree *, std::unique_ptr<const NodeTreeRef>>;
@@ -287,6 +312,11 @@ inline Span<const SocketRef *> SocketRef::logically_linked_sockets() const
return logically_linked_sockets_;
}
+inline Span<const SocketRef *> SocketRef::logically_linked_skipped_sockets() const
+{
+ return logically_linked_skipped_sockets_;
+}
+
inline Span<const SocketRef *> SocketRef::directly_linked_sockets() const
{
return directly_linked_sockets_;
@@ -399,6 +429,11 @@ inline bool SocketRef::is_available() const
return (bsocket_->flag & SOCK_UNAVAIL) == 0;
}
+inline bool SocketRef::is_undefined() const
+{
+ return bsocket_->typeinfo == &NodeSocketTypeUndefined;
+}
+
inline void *SocketRef::default_value() const
{
return bsocket_->default_value;
@@ -476,6 +511,18 @@ inline const OutputSocketRef &NodeRef::output(int index) const
return *outputs_[index];
}
+inline const InputSocketRef &NodeRef::input_by_identifier(StringRef identifier) const
+{
+ const int index = input_index_by_identifier_->lookup_as(identifier);
+ return this->input(index);
+}
+
+inline const OutputSocketRef &NodeRef::output_by_identifier(StringRef identifier) const
+{
+ const int index = output_index_by_identifier_->lookup_as(identifier);
+ return this->output(index);
+}
+
inline bNode *NodeRef::bnode() const
{
return bnode_;
@@ -518,7 +565,7 @@ inline bool NodeRef::is_reroute_node() const
inline bool NodeRef::is_group_node() const
{
- return bnode_->type == NODE_GROUP;
+ return bnode_->type == NODE_GROUP || bnode_->type == NODE_CUSTOM_GROUP;
}
inline bool NodeRef::is_group_input_node() const
@@ -536,6 +583,11 @@ inline bool NodeRef::is_frame() const
return bnode_->type == NODE_FRAME;
}
+inline bool NodeRef::is_undefined() const
+{
+ return bnode_->typeinfo == &NodeTypeUndefined;
+}
+
inline bool NodeRef::is_muted() const
{
return (bnode_->flag & NODE_MUTED) != 0;
diff --git a/source/blender/nodes/NOD_socket.h b/source/blender/nodes/NOD_socket.h
index 3344a25bdea..6bcfda70d04 100644
--- a/source/blender/nodes/NOD_socket.h
+++ b/source/blender/nodes/NOD_socket.h
@@ -41,7 +41,7 @@ extern "C" {
struct bNodeSocket *node_add_socket_from_template(struct bNodeTree *ntree,
struct bNode *node,
struct bNodeSocketTemplate *stemp,
- int in_out);
+ eNodeSocketInOut in_out);
void node_verify_socket_templates(struct bNodeTree *ntree, struct bNode *node);
diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h
index ec687ae6b70..b255c6e5f23 100644
--- a/source/blender/nodes/NOD_static_types.h
+++ b/source/blender/nodes/NOD_static_types.h
@@ -224,6 +224,7 @@ DefNode(CompositorNode, CMP_NODE_CRYPTOMATTE, def_cmp_cryptomatte, "CRYPTO
DefNode(CompositorNode, CMP_NODE_CRYPTOMATTE_LEGACY, def_cmp_cryptomatte_legacy, "CRYPTOMATTE", Cryptomatte, "Cryptomatte (Legacy)", "" )
DefNode(CompositorNode, CMP_NODE_DENOISE, def_cmp_denoise, "DENOISE", Denoise, "Denoise", "" )
DefNode(CompositorNode, CMP_NODE_EXPOSURE, 0, "EXPOSURE", Exposure, "Exposure", "" )
+DefNode(CompositorNode, CMP_NODE_ANTIALIASING, def_cmp_antialiasing, "ANTIALIASING", AntiAliasing, "Anti-Aliasing", "" )
DefNode(TextureNode, TEX_NODE_OUTPUT, def_tex_output, "OUTPUT", Output, "Output", "" )
DefNode(TextureNode, TEX_NODE_CHECKER, 0, "CHECKER", Checker, "Checker", "" )
@@ -262,50 +263,67 @@ DefNode(TextureNode, TEX_NODE_PROC+TEX_DISTNOISE, 0, "TEX_DI
DefNode(FunctionNode, FN_NODE_BOOLEAN_MATH, def_boolean_math, "BOOLEAN_MATH", BooleanMath, "Boolean Math", "")
DefNode(FunctionNode, FN_NODE_FLOAT_COMPARE, def_float_compare, "FLOAT_COMPARE", FloatCompare, "Float Compare", "")
-DefNode(FunctionNode, FN_NODE_RANDOM_FLOAT, 0, "RANDOM_FLOAT", RandomFloat, "Random Float", "")
-DefNode(FunctionNode, FN_NODE_INPUT_VECTOR, def_fn_input_vector, "INPUT_VECTOR", InputVector, "Vector", "")
DefNode(FunctionNode, FN_NODE_INPUT_STRING, def_fn_input_string, "INPUT_STRING", InputString, "String", "")
+DefNode(FunctionNode, FN_NODE_INPUT_VECTOR, def_fn_input_vector, "INPUT_VECTOR", InputVector, "Vector", "")
+DefNode(FunctionNode, FN_NODE_RANDOM_FLOAT, 0, "RANDOM_FLOAT", RandomFloat, "Random Float", "")
-DefNode(GeometryNode, GEO_NODE_TRIANGULATE, def_geo_triangulate, "TRIANGULATE", Triangulate, "Triangulate", "")
-DefNode(GeometryNode, GEO_NODE_EDGE_SPLIT, 0, "EDGE_SPLIT", EdgeSplit, "Edge Split", "")
-DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform", "")
-DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, 0, "SUBDIVISION_SURFACE", SubdivisionSurface, "Subdivision Surface", "")
-DefNode(GeometryNode, GEO_NODE_BOOLEAN, def_geo_boolean, "BOOLEAN", Boolean, "Boolean", "")
-DefNode(GeometryNode, GEO_NODE_POINT_DISTRIBUTE, def_geo_point_distribute, "POINT_DISTRIBUTE", PointDistribute, "Point Distribute", "")
-DefNode(GeometryNode, GEO_NODE_POINT_INSTANCE, def_geo_point_instance, "POINT_INSTANCE", PointInstance, "Point Instance", "")
-DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, def_geo_object_info, "OBJECT_INFO", ObjectInfo, "Object Info", "")
-DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_RANDOMIZE, def_geo_attribute_randomize, "ATTRIBUTE_RANDOMIZE", AttributeRandomize, "Attribute Randomize", "")
-DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MATH, def_geo_attribute_math, "ATTRIBUTE_MATH", AttributeMath, "Attribute Math", "")
-DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "")
-DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_FILL, def_geo_attribute_fill, "ATTRIBUTE_FILL", AttributeFill, "Attribute Fill", "")
-DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MIX, def_geo_attribute_mix, "ATTRIBUTE_MIX", AttributeMix, "Attribute Mix", "")
+DefNode(GeometryNode, GEO_NODE_ALIGN_ROTATION_TO_VECTOR, def_geo_align_rotation_to_vector, "ALIGN_ROTATION_TO_VECTOR", AlignRotationToVector, "Align Rotation to Vector", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CLAMP, def_geo_attribute_clamp, "ATTRIBUTE_CLAMP", AttributeClamp, "Attribute Clamp", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_COLOR_RAMP, def_geo_attribute_color_ramp, "ATTRIBUTE_COLOR_RAMP", AttributeColorRamp, "Attribute Color Ramp", "")
-DefNode(GeometryNode, GEO_NODE_POINT_SEPARATE, 0, "POINT_SEPARATE", PointSeparate, "Point Separate", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_COMBINE_XYZ, def_geo_attribute_combine_xyz, "ATTRIBUTE_COMBINE_XYZ", AttributeCombineXYZ, "Attribute Combine XYZ", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_COMPARE, def_geo_attribute_attribute_compare, "ATTRIBUTE_COMPARE", AttributeCompare, "Attribute Compare", "")
-DefNode(GeometryNode, GEO_NODE_POINT_ROTATE, def_geo_point_rotate, "POINT_ROTATE", RotatePoints, "Point Rotate", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CONVERT, def_geo_attribute_convert, "ATTRIBUTE_CONVERT", AttributeConvert, "Attribute Convert", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CURVE_MAP, def_geo_attribute_curve_map, "ATTRIBUTE_CURVE_MAP", AttributeCurveMap, "Attribute Curve Map", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_FILL, def_geo_attribute_fill, "ATTRIBUTE_FILL", AttributeFill, "Attribute Fill", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MAP_RANGE, def_geo_attribute_map_range, "ATTRIBUTE_MAP_RANGE", AttributeMapRange, "Attribute Map Range", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MATH, def_geo_attribute_math, "ATTRIBUTE_MATH", AttributeMath, "Attribute Math", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MIX, def_geo_attribute_mix, "ATTRIBUTE_MIX", AttributeMix, "Attribute Mix", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_PROXIMITY, def_geo_attribute_proximity, "ATTRIBUTE_PROXIMITY", AttributeProximity, "Attribute Proximity", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_RANDOMIZE, def_geo_attribute_randomize, "ATTRIBUTE_RANDOMIZE", AttributeRandomize, "Attribute Randomize", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_REMOVE, 0, "ATTRIBUTE_REMOVE", AttributeRemove, "Attribute Remove", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE, 0, "ATTRIBUTE_SAMPLE_TEXTURE", AttributeSampleTexture, "Attribute Sample Texture", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_SEPARATE_XYZ, def_geo_attribute_separate_xyz, "ATTRIBUTE_SEPARATE_XYZ", AttributeSeparateXYZ, "Attribute Separate XYZ", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_TRANSFER, def_geo_attribute_transfer, "ATTRIBUTE_TRANSFER", AttributeTransfer, "Attribute Transfer", "")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_VECTOR_MATH, def_geo_attribute_vector_math, "ATTRIBUTE_VECTOR_MATH", AttributeVectorMath, "Attribute Vector Math", "")
-DefNode(GeometryNode, GEO_NODE_ALIGN_ROTATION_TO_VECTOR, def_geo_align_rotation_to_vector, "ALIGN_ROTATION_TO_VECTOR", AlignRotationToVector, "Align Rotation to Vector", "")
-DefNode(GeometryNode, GEO_NODE_POINT_SCALE, def_geo_point_scale, "POINT_SCALE", PointScale, "Point Scale", "")
-DefNode(GeometryNode, GEO_NODE_POINT_TRANSLATE, def_geo_point_translate, "POINT_TRANSLATE", PointTranslate, "Point Translate", "")
-DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE, def_geo_attribute_sample_texture, "ATTRIBUTE_SAMPLE_TEXTURE", AttributeSampleTexture, "Attribute Sample Texture", "")
-DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POINTS_TO_VOLUME", PointsToVolume, "Points to Volume", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_VECTOR_ROTATE, def_geo_attribute_vector_rotate, "ATTRIBUTE_VECTOR_ROTATE", AttributeVectorRotate, "Attribute Vector Rotate", "")
+DefNode(GeometryNode, GEO_NODE_BOOLEAN, def_geo_boolean, "BOOLEAN", Boolean, "Boolean", "")
+DefNode(GeometryNode, GEO_NODE_BOUNDING_BOX, 0, "BOUNDING_BOX", BoundBox, "Bounding Box", "")
DefNode(GeometryNode, GEO_NODE_COLLECTION_INFO, def_geo_collection_info, "COLLECTION_INFO", CollectionInfo, "Collection Info", "")
+DefNode(GeometryNode, GEO_NODE_CONVEX_HULL, 0, "CONVEX_HULL", ConvexHull, "Convex Hull", "")
+DefNode(GeometryNode, GEO_NODE_CURVE_LENGTH, 0, "CURVE_LENGTH", CurveLength, "Curve Length", "")
+DefNode(GeometryNode, GEO_NODE_CURVE_RESAMPLE, def_geo_curve_resample, "CURVE_RESAMPLE", CurveResample, "Resample Curve", "")
+DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "")
+DefNode(GeometryNode, GEO_NODE_DELETE_GEOMETRY, 0, "DELETE_GEOMETRY", DeleteGeometry, "Delete Geometry", "")
+DefNode(GeometryNode, GEO_NODE_EDGE_SPLIT, 0, "EDGE_SPLIT", EdgeSplit, "Edge Split", "")
+DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL, def_geo_input_material, "INPUT_MATERIAL", InputMaterial, "Material", "")
DefNode(GeometryNode, GEO_NODE_IS_VIEWPORT, 0, "IS_VIEWPORT", IsViewport, "Is Viewport", "")
-DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_PROXIMITY, def_geo_attribute_proximity, "ATTRIBUTE_PROXIMITY", AttributeProximity, "Attribute Proximity", "")
-DefNode(GeometryNode, GEO_NODE_VOLUME_TO_MESH, def_geo_volume_to_mesh, "VOLUME_TO_MESH", VolumeToMesh, "Volume to Mesh", "")
-DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_COMBINE_XYZ, def_geo_attribute_combine_xyz, "ATTRIBUTE_COMBINE_XYZ", AttributeCombineXYZ, "Attribute Combine XYZ", "")
-DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_SEPARATE_XYZ, def_geo_attribute_separate_xyz, "ATTRIBUTE_SEPARATE_XYZ", AttributeSeparateXYZ, "Attribute Separate XYZ", "")
-DefNode(GeometryNode, GEO_NODE_SUBDIVIDE, 0, "SUBDIVIDE", Subdivide, "Subdivide", "")
-DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_REMOVE, 0, "ATTRIBUTE_REMOVE", AttributeRemove, "Attribute Remove", "")
-DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CONVERT, def_geo_attribute_convert, "ATTRIBUTE_CONVERT", AttributeConvert, "Attribute Convert", "")
-DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CUBE, 0, "MESH_PRIMITIVE_CUBE", MeshCube, "Cube", "")
+DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "")
+DefNode(GeometryNode, GEO_NODE_MATERIAL_ASSIGN, 0, "MATERIAL_ASSIGN", MaterialAssign, "Material Assign", "")
+DefNode(GeometryNode, GEO_NODE_MATERIAL_REPLACE, 0, "MATERIAL_REPLACE", MaterialReplace, "Material Replace", "")
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CIRCLE, def_geo_mesh_circle, "MESH_PRIMITIVE_CIRCLE", MeshCircle, "Circle", "")
-DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, 0, "MESH_PRIMITIVE_UV_SPHERE", MeshUVSphere, "UV Sphere", "")
+DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CONE, def_geo_mesh_cone, "MESH_PRIMITIVE_CONE", MeshCone, "Cone", "")
+DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CUBE, 0, "MESH_PRIMITIVE_CUBE", MeshCube, "Cube", "")
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CYLINDER, def_geo_mesh_cylinder, "MESH_PRIMITIVE_CYLINDER", MeshCylinder, "Cylinder", "")
+DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_GRID, 0, "MESH_PRIMITIVE_GRID", MeshGrid, "Grid", "")
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE, 0, "MESH_PRIMITIVE_ICO_SPHERE", MeshIcoSphere, "Ico Sphere", "")
-DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CONE, def_geo_mesh_cone, "MESH_PRIMITIVE_CONE", MeshCone, "Cone", "")
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_LINE, def_geo_mesh_line, "MESH_PRIMITIVE_LINE", MeshLine, "Line", "")
-DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_PLANE, 0, "MESH_PRIMITIVE_PLANE", MeshPlane, "Plane", "")
+DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, 0, "MESH_PRIMITIVE_UV_SPHERE", MeshUVSphere, "UV Sphere", "")
+DefNode(GeometryNode, GEO_NODE_MESH_TO_CURVE, 0, "MESH_TO_CURVE", MeshToCurve, "Mesh to Curve", "")
+DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, def_geo_object_info, "OBJECT_INFO", ObjectInfo, "Object Info", "")
+DefNode(GeometryNode, GEO_NODE_POINT_DISTRIBUTE, def_geo_point_distribute, "POINT_DISTRIBUTE", PointDistribute, "Point Distribute", "")
+DefNode(GeometryNode, GEO_NODE_POINT_INSTANCE, def_geo_point_instance, "POINT_INSTANCE", PointInstance, "Point Instance", "")
+DefNode(GeometryNode, GEO_NODE_POINT_ROTATE, def_geo_point_rotate, "POINT_ROTATE", RotatePoints, "Point Rotate", "")
+DefNode(GeometryNode, GEO_NODE_POINT_SCALE, def_geo_point_scale, "POINT_SCALE", PointScale, "Point Scale", "")
+DefNode(GeometryNode, GEO_NODE_POINT_SEPARATE, 0, "POINT_SEPARATE", PointSeparate, "Point Separate", "")
+DefNode(GeometryNode, GEO_NODE_POINT_TRANSLATE, def_geo_point_translate, "POINT_TRANSLATE", PointTranslate, "Point Translate", "")
+DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POINTS_TO_VOLUME", PointsToVolume, "Points to Volume", "")
+DefNode(GeometryNode, GEO_NODE_SELECT_BY_MATERIAL, 0, "SELECT_BY_MATERIAL", SelectByMaterial, "Select by Material", "")
+DefNode(GeometryNode, GEO_NODE_SUBDIVIDE, 0, "SUBDIVIDE", Subdivide, "Subdivide", "")
+DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, 0, "SUBDIVISION_SURFACE", SubdivisionSurface, "Subdivision Surface", "")
+DefNode(GeometryNode, GEO_NODE_SWITCH, def_geo_switch, "SWITCH", Switch, "Switch", "")
+DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform", "")
+DefNode(GeometryNode, GEO_NODE_TRIANGULATE, def_geo_triangulate, "TRIANGULATE", Triangulate, "Triangulate", "")
+DefNode(GeometryNode, GEO_NODE_VOLUME_TO_MESH, def_geo_volume_to_mesh, "VOLUME_TO_MESH", VolumeToMesh, "Volume to Mesh", "")
/* undefine macros */
#undef DefNode
diff --git a/source/blender/nodes/NOD_type_conversions.hh b/source/blender/nodes/NOD_type_conversions.hh
new file mode 100644
index 00000000000..ec4859f0657
--- /dev/null
+++ b/source/blender/nodes/NOD_type_conversions.hh
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "FN_multi_function.hh"
+
+namespace blender::nodes {
+
+using fn::CPPType;
+using fn::GVArray;
+
+struct ConversionFunctions {
+ const fn::MultiFunction *multi_function;
+ void (*convert_single_to_initialized)(const void *src, void *dst);
+ void (*convert_single_to_uninitialized)(const void *src, void *dst);
+};
+
+class DataTypeConversions {
+ private:
+ Map<std::pair<fn::MFDataType, fn::MFDataType>, ConversionFunctions> conversions_;
+
+ public:
+ void add(fn::MFDataType from_type,
+ fn::MFDataType to_type,
+ const fn::MultiFunction &fn,
+ void (*convert_single_to_initialized)(const void *src, void *dst),
+ void (*convert_single_to_uninitialized)(const void *src, void *dst))
+ {
+ conversions_.add_new({from_type, to_type},
+ {&fn, convert_single_to_initialized, convert_single_to_uninitialized});
+ }
+
+ const ConversionFunctions *get_conversion_functions(fn::MFDataType from, fn::MFDataType to) const
+ {
+ return conversions_.lookup_ptr({from, to});
+ }
+
+ const ConversionFunctions *get_conversion_functions(const CPPType &from, const CPPType &to) const
+ {
+ return this->get_conversion_functions(fn::MFDataType::ForSingle(from),
+ fn::MFDataType::ForSingle(to));
+ }
+
+ const fn::MultiFunction *get_conversion_multi_function(fn::MFDataType from,
+ fn::MFDataType to) const
+ {
+ const ConversionFunctions *functions = this->get_conversion_functions(from, to);
+ return functions ? functions->multi_function : nullptr;
+ }
+
+ bool is_convertible(const CPPType &from_type, const CPPType &to_type) const
+ {
+ return conversions_.contains(
+ {fn::MFDataType::ForSingle(from_type), fn::MFDataType::ForSingle(to_type)});
+ }
+
+ void convert_to_uninitialized(const CPPType &from_type,
+ const CPPType &to_type,
+ const void *from_value,
+ void *to_value) const;
+
+ fn::GVArrayPtr try_convert(fn::GVArrayPtr varray, const CPPType &to_type) const;
+
+ fn::GVMutableArrayPtr try_convert(fn::GVMutableArrayPtr varray, const CPPType &to_type) const;
+};
+
+const DataTypeConversions &get_implicit_type_conversions();
+
+} // namespace blender::nodes
diff --git a/source/blender/nodes/composite/node_composite_tree.c b/source/blender/nodes/composite/node_composite_tree.c
index 085b5c463b9..19815d01278 100644
--- a/source/blender/nodes/composite/node_composite_tree.c
+++ b/source/blender/nodes/composite/node_composite_tree.c
@@ -205,6 +205,12 @@ static void composite_node_add_init(bNodeTree *UNUSED(bnodetree), bNode *bnode)
}
}
+static bool composite_node_tree_socket_type_valid(eNodeSocketDatatype socket_type,
+ bNodeTreeType *UNUSED(ntreetype))
+{
+ return ELEM(socket_type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA);
+}
+
bNodeTreeType *ntreeType_Composite;
void register_node_tree_type_cmp(void)
@@ -227,6 +233,7 @@ void register_node_tree_type_cmp(void)
tt->update = update;
tt->get_from_context = composite_get_from_context;
tt->node_add_init = composite_node_add_init;
+ tt->valid_socket_type = composite_node_tree_socket_type_valid;
tt->rna_ext.srna = &RNA_CompositorNodeTree;
diff --git a/source/blender/nodes/composite/node_composite_util.c b/source/blender/nodes/composite/node_composite_util.c
index b6cbffea413..6cc17d8c272 100644
--- a/source/blender/nodes/composite/node_composite_util.c
+++ b/source/blender/nodes/composite/node_composite_util.c
@@ -23,9 +23,15 @@
#include "node_composite_util.h"
-bool cmp_node_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree)
+bool cmp_node_poll_default(bNodeType *UNUSED(ntype),
+ bNodeTree *ntree,
+ const char **r_disabled_hint)
{
- return STREQ(ntree->idname, "CompositorNodeTree");
+ if (!STREQ(ntree->idname, "CompositorNodeTree")) {
+ *r_disabled_hint = "Not a compositor node tree";
+ return false;
+ }
+ return true;
}
void cmp_node_update_default(bNodeTree *UNUSED(ntree), bNode *node)
diff --git a/source/blender/nodes/composite/node_composite_util.h b/source/blender/nodes/composite/node_composite_util.h
index 800c55df4d6..4fcccbb79f0 100644
--- a/source/blender/nodes/composite/node_composite_util.h
+++ b/source/blender/nodes/composite/node_composite_util.h
@@ -54,7 +54,9 @@ extern "C" {
#define CMP_SCALE_MAX 12000
-bool cmp_node_poll_default(struct bNodeType *ntype, struct bNodeTree *ntree);
+bool cmp_node_poll_default(struct bNodeType *ntype,
+ struct bNodeTree *ntree,
+ const char **r_disabled_info);
void cmp_node_update_default(struct bNodeTree *ntree, struct bNode *node);
void cmp_node_type_base(
struct bNodeType *ntype, int type, const char *name, short nclass, short flag);
diff --git a/source/blender/nodes/composite/nodes/node_composite_antialiasing.c b/source/blender/nodes/composite/nodes/node_composite_antialiasing.c
new file mode 100644
index 00000000000..7437496d878
--- /dev/null
+++ b/source/blender/nodes/composite/nodes/node_composite_antialiasing.c
@@ -0,0 +1,65 @@
+/*
+ * ***** 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.
+ *
+ * The Original Code is Copyright (C) 2017 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): IRIE Shinsuke
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/nodes/composite/nodes/node_composite_antialiasing.c
+ * \ingroup cmpnodes
+ */
+
+#include "node_composite_util.h"
+
+/* **************** Anti-Aliasing (SMAA 1x) ******************** */
+
+static bNodeSocketTemplate cmp_node_antialiasing_in[] = {
+ {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, {-1, ""}};
+
+static bNodeSocketTemplate cmp_node_antialiasing_out[] = {{SOCK_RGBA, N_("Image")}, {-1, ""}};
+
+static void node_composit_init_antialiasing(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeAntiAliasingData *data = MEM_callocN(sizeof(NodeAntiAliasingData), "node antialiasing data");
+
+ data->threshold = 1.0f;
+ data->contrast_limit = 0.2f;
+ data->corner_rounding = 0.25f;
+
+ node->storage = data;
+}
+
+void register_node_type_cmp_antialiasing(void)
+{
+ static bNodeType ntype;
+
+ cmp_node_type_base(
+ &ntype, CMP_NODE_ANTIALIASING, "Anti-Aliasing", NODE_CLASS_OP_FILTER, NODE_PREVIEW);
+ node_type_socket_templates(&ntype, cmp_node_antialiasing_in, cmp_node_antialiasing_out);
+ node_type_size(&ntype, 170, 140, 200);
+ node_type_init(&ntype, node_composit_init_antialiasing);
+ node_type_storage(
+ &ntype, "NodeAntiAliasingData", node_free_standard_storage, node_copy_standard_storage);
+
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc
index c5135482ec2..51dd73a86af 100644
--- a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc
@@ -40,61 +40,74 @@
/** \name Cryptomatte
* \{ */
+static blender::bke::cryptomatte::CryptomatteSessionPtr cryptomatte_init_from_node_render(
+ const bNode &node, const bool use_meta_data)
+{
+ blender::bke::cryptomatte::CryptomatteSessionPtr session;
+
+ Scene *scene = (Scene *)node.id;
+ if (!scene) {
+ return session;
+ }
+ BLI_assert(GS(scene->id.name) == ID_SCE);
+
+ if (use_meta_data) {
+ Render *render = RE_GetSceneRender(scene);
+ RenderResult *render_result = render ? RE_AcquireResultRead(render) : nullptr;
+ if (render_result) {
+ session = blender::bke::cryptomatte::CryptomatteSessionPtr(
+ BKE_cryptomatte_init_from_render_result(render_result));
+ }
+ if (render) {
+ RE_ReleaseResult(render);
+ }
+ }
+
+ if (session == nullptr) {
+ session = blender::bke::cryptomatte::CryptomatteSessionPtr(
+ BKE_cryptomatte_init_from_scene(scene));
+ }
+ return session;
+}
+
+static blender::bke::cryptomatte::CryptomatteSessionPtr cryptomatte_init_from_node_image(
+ const Scene &scene, const bNode &node)
+{
+ blender::bke::cryptomatte::CryptomatteSessionPtr session;
+ Image *image = (Image *)node.id;
+ if (!image) {
+ return session;
+ }
+ BLI_assert(GS(image->id.name) == ID_IM);
+
+ NodeCryptomatte *node_cryptomatte = static_cast<NodeCryptomatte *>(node.storage);
+ ImageUser *iuser = &node_cryptomatte->iuser;
+ BKE_image_user_frame_calc(image, iuser, scene.r.cfra);
+ ImBuf *ibuf = BKE_image_acquire_ibuf(image, iuser, nullptr);
+ RenderResult *render_result = image->rr;
+ if (render_result) {
+ session = blender::bke::cryptomatte::CryptomatteSessionPtr(
+ BKE_cryptomatte_init_from_render_result(render_result));
+ }
+ BKE_image_release_ibuf(image, ibuf, nullptr);
+ return session;
+}
static blender::bke::cryptomatte::CryptomatteSessionPtr cryptomatte_init_from_node(
- const bNode &node, const int frame_number, const bool use_meta_data)
+ const Scene &scene, const bNode &node, const bool use_meta_data)
{
blender::bke::cryptomatte::CryptomatteSessionPtr session;
if (node.type != CMP_NODE_CRYPTOMATTE) {
return session;
}
- NodeCryptomatte *node_cryptomatte = static_cast<NodeCryptomatte *>(node.storage);
switch (node.custom1) {
case CMP_CRYPTOMATTE_SRC_RENDER: {
- Scene *scene = (Scene *)node.id;
- if (!scene) {
- return session;
- }
- BLI_assert(GS(scene->id.name) == ID_SCE);
-
- if (use_meta_data) {
- Render *render = (scene) ? RE_GetSceneRender(scene) : nullptr;
- RenderResult *render_result = render ? RE_AcquireResultRead(render) : nullptr;
- if (render_result) {
- session = blender::bke::cryptomatte::CryptomatteSessionPtr(
- BKE_cryptomatte_init_from_render_result(render_result));
- }
- if (render) {
- RE_ReleaseResult(render);
- }
- }
-
- if (session == nullptr) {
- session = blender::bke::cryptomatte::CryptomatteSessionPtr(
- BKE_cryptomatte_init_from_scene(scene));
- }
-
- break;
+ return cryptomatte_init_from_node_render(node, use_meta_data);
}
case CMP_CRYPTOMATTE_SRC_IMAGE: {
- Image *image = (Image *)node.id;
- if (!image) {
- break;
- }
- BLI_assert(GS(image->id.name) == ID_IM);
-
- ImageUser *iuser = &node_cryptomatte->iuser;
- BKE_image_user_frame_calc(image, iuser, frame_number);
- ImBuf *ibuf = BKE_image_acquire_ibuf(image, iuser, nullptr);
- RenderResult *render_result = image->rr;
- if (render_result) {
- session = blender::bke::cryptomatte::CryptomatteSessionPtr(
- BKE_cryptomatte_init_from_render_result(render_result));
- }
- BKE_image_release_ibuf(image, ibuf, nullptr);
- break;
+ return cryptomatte_init_from_node_image(scene, node);
}
}
return session;
@@ -111,7 +124,10 @@ static CryptomatteEntry *cryptomatte_find(const NodeCryptomatte &n, float encode
return nullptr;
}
-static void cryptomatte_add(bNode &node, NodeCryptomatte &node_cryptomatte, float encoded_hash)
+static void cryptomatte_add(const Scene &scene,
+ bNode &node,
+ NodeCryptomatte &node_cryptomatte,
+ float encoded_hash)
{
/* Check if entry already exist. */
if (cryptomatte_find(node_cryptomatte, encoded_hash)) {
@@ -121,9 +137,8 @@ static void cryptomatte_add(bNode &node, NodeCryptomatte &node_cryptomatte, floa
CryptomatteEntry *entry = static_cast<CryptomatteEntry *>(
MEM_callocN(sizeof(CryptomatteEntry), __func__));
entry->encoded_hash = encoded_hash;
- /* TODO(jbakker): Get current frame from scene. */
blender::bke::cryptomatte::CryptomatteSessionPtr session = cryptomatte_init_from_node(
- node, 0, true);
+ scene, node, true);
if (session) {
BKE_cryptomatte_find_name(session.get(), encoded_hash, entry->name, sizeof(entry->name));
}
@@ -151,12 +166,12 @@ static bNodeSocketTemplate cmp_node_cryptomatte_out[] = {
{-1, ""},
};
-void ntreeCompositCryptomatteSyncFromAdd(bNode *node)
+void ntreeCompositCryptomatteSyncFromAdd(const Scene *scene, bNode *node)
{
BLI_assert(ELEM(node->type, CMP_NODE_CRYPTOMATTE, CMP_NODE_CRYPTOMATTE_LEGACY));
NodeCryptomatte *n = static_cast<NodeCryptomatte *>(node->storage);
if (n->runtime.add[0] != 0.0f) {
- cryptomatte_add(*node, *n, n->runtime.add[0]);
+ cryptomatte_add(*scene, *node, *n, n->runtime.add[0]);
zero_v3(n->runtime.add);
}
}
@@ -170,14 +185,14 @@ void ntreeCompositCryptomatteSyncFromRemove(bNode *node)
zero_v3(n->runtime.remove);
}
}
-void ntreeCompositCryptomatteUpdateLayerNames(bNode *node)
+void ntreeCompositCryptomatteUpdateLayerNames(const Scene *scene, bNode *node)
{
BLI_assert(node->type == CMP_NODE_CRYPTOMATTE);
NodeCryptomatte *n = static_cast<NodeCryptomatte *>(node->storage);
BLI_freelistN(&n->runtime.layers);
blender::bke::cryptomatte::CryptomatteSessionPtr session = cryptomatte_init_from_node(
- *node, 0, false);
+ *scene, *node, false);
if (session) {
for (blender::StringRef layer_name :
@@ -190,12 +205,15 @@ void ntreeCompositCryptomatteUpdateLayerNames(bNode *node)
}
}
-void ntreeCompositCryptomatteLayerPrefix(const bNode *node, char *r_prefix, size_t prefix_len)
+void ntreeCompositCryptomatteLayerPrefix(const Scene *scene,
+ const bNode *node,
+ char *r_prefix,
+ size_t prefix_len)
{
BLI_assert(node->type == CMP_NODE_CRYPTOMATTE);
NodeCryptomatte *node_cryptomatte = (NodeCryptomatte *)node->storage;
blender::bke::cryptomatte::CryptomatteSessionPtr session = cryptomatte_init_from_node(
- *node, 0, false);
+ *scene, *node, false);
std::string first_layer_name;
if (session) {
@@ -216,10 +234,10 @@ void ntreeCompositCryptomatteLayerPrefix(const bNode *node, char *r_prefix, size
BLI_strncpy(r_prefix, cstr, prefix_len);
}
-CryptomatteSession *ntreeCompositCryptomatteSession(bNode *node)
+CryptomatteSession *ntreeCompositCryptomatteSession(const Scene *scene, bNode *node)
{
blender::bke::cryptomatte::CryptomatteSessionPtr session_ptr = cryptomatte_init_from_node(
- *node, 0, true);
+ *scene, *node, true);
return session_ptr.release();
}
@@ -263,7 +281,9 @@ static void node_copy_cryptomatte(bNodeTree *UNUSED(dest_ntree),
dest_node->storage = dest_nc;
}
-static bool node_poll_cryptomatte(bNodeType *UNUSED(ntype), bNodeTree *ntree)
+static bool node_poll_cryptomatte(bNodeType *UNUSED(ntype),
+ bNodeTree *ntree,
+ const char **r_disabled_hint)
{
if (STREQ(ntree->idname, "CompositorNodeTree")) {
Scene *scene;
@@ -276,8 +296,13 @@ static bool node_poll_cryptomatte(bNodeType *UNUSED(ntype), bNodeTree *ntree)
}
}
+ if (scene == nullptr) {
+ *r_disabled_hint =
+ "The node tree must be the compositing node tree of any scene in the file";
+ }
return scene != nullptr;
}
+ *r_disabled_hint = "Not a compositor node tree";
return false;
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_image.c b/source/blender/nodes/composite/nodes/node_composite_image.c
index 53ea02ff8a7..243300b0a44 100644
--- a/source/blender/nodes/composite/nodes/node_composite_image.c
+++ b/source/blender/nodes/composite/nodes/node_composite_image.c
@@ -526,24 +526,32 @@ static void node_composit_init_rlayers(const bContext *C, PointerRNA *ptr)
}
}
-static bool node_composit_poll_rlayers(bNodeType *UNUSED(ntype), bNodeTree *ntree)
+static bool node_composit_poll_rlayers(bNodeType *UNUSED(ntype),
+ bNodeTree *ntree,
+ const char **r_disabled_hint)
{
- if (STREQ(ntree->idname, "CompositorNodeTree")) {
- Scene *scene;
+ if (!STREQ(ntree->idname, "CompositorNodeTree")) {
+ *r_disabled_hint = "Not a compositor node tree";
+ return false;
+ }
- /* XXX ugly: check if ntree is a local scene node tree.
- * Render layers node can only be used in local scene->nodetree,
- * since it directly links to the scene.
- */
- for (scene = G.main->scenes.first; scene; scene = scene->id.next) {
- if (scene->nodetree == ntree) {
- break;
- }
+ Scene *scene;
+
+ /* XXX ugly: check if ntree is a local scene node tree.
+ * Render layers node can only be used in local scene->nodetree,
+ * since it directly links to the scene.
+ */
+ for (scene = G.main->scenes.first; scene; scene = scene->id.next) {
+ if (scene->nodetree == ntree) {
+ break;
}
+ }
- return (scene != NULL);
+ if (scene == NULL) {
+ *r_disabled_hint = "The node tree must be the compositing node tree of any scene in the file";
+ return false;
}
- return false;
+ return true;
}
static void node_composit_free_rlayers(bNode *node)
diff --git a/source/blender/nodes/function/node_function_util.cc b/source/blender/nodes/function/node_function_util.cc
index 827572d1069..8ff8b416310 100644
--- a/source/blender/nodes/function/node_function_util.cc
+++ b/source/blender/nodes/function/node_function_util.cc
@@ -17,10 +17,16 @@
#include "node_function_util.hh"
#include "node_util.h"
-bool fn_node_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree)
+static bool fn_node_poll_default(bNodeType *UNUSED(ntype),
+ bNodeTree *ntree,
+ const char **r_disabled_hint)
{
/* Function nodes are only supported in simulation node trees so far. */
- return STREQ(ntree->idname, "GeometryNodeTree");
+ if (!STREQ(ntree->idname, "GeometryNodeTree")) {
+ *r_disabled_hint = "Not a geometry node tree";
+ return false;
+ }
+ return true;
}
void fn_node_type_base(bNodeType *ntype, int type, const char *name, short nclass, short flag)
diff --git a/source/blender/nodes/function/node_function_util.hh b/source/blender/nodes/function/node_function_util.hh
index d57d1383019..9fbd6712827 100644
--- a/source/blender/nodes/function/node_function_util.hh
+++ b/source/blender/nodes/function/node_function_util.hh
@@ -38,4 +38,3 @@
void fn_node_type_base(
struct bNodeType *ntype, int type, const char *name, short nclass, short flag);
-bool fn_node_poll_default(struct bNodeType *ntype, struct bNodeTree *ntree);
diff --git a/source/blender/nodes/geometry/node_geometry_tree.cc b/source/blender/nodes/geometry/node_geometry_tree.cc
index 2c4c7da64cc..f4cd00b88ed 100644
--- a/source/blender/nodes/geometry/node_geometry_tree.cc
+++ b/source/blender/nodes/geometry/node_geometry_tree.cc
@@ -67,6 +67,8 @@ static void geometry_node_tree_get_from_context(const bContext *C,
static void geometry_node_tree_update(bNodeTree *ntree)
{
+ ntreeSetOutput(ntree);
+
/* Needed to give correct types to reroutes. */
ntree_update_reroute_nodes(ntree);
}
@@ -82,6 +84,34 @@ static void foreach_nodeclass(Scene *UNUSED(scene), void *calldata, bNodeClassCa
func(calldata, NODE_CLASS_LAYOUT, N_("Layout"));
}
+static bool geometry_node_tree_validate_link(bNodeTree *UNUSED(ntree), bNodeLink *link)
+{
+ /* Geometry, string, object, material, texture and collection sockets can only be connected to
+ * themselves. The other types can be converted between each other. */
+ if (ELEM(link->fromsock->type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_BOOLEAN, SOCK_INT) &&
+ ELEM(link->tosock->type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_BOOLEAN, SOCK_INT)) {
+ return true;
+ }
+ return (link->tosock->type == link->fromsock->type);
+}
+
+static bool geometry_node_tree_socket_type_valid(eNodeSocketDatatype socket_type,
+ bNodeTreeType *UNUSED(ntreetype))
+{
+ return ELEM(socket_type,
+ SOCK_FLOAT,
+ SOCK_VECTOR,
+ SOCK_RGBA,
+ SOCK_BOOLEAN,
+ SOCK_INT,
+ SOCK_STRING,
+ SOCK_OBJECT,
+ SOCK_GEOMETRY,
+ SOCK_COLLECTION,
+ SOCK_TEXTURE,
+ SOCK_MATERIAL);
+}
+
void register_node_tree_type_geo(void)
{
bNodeTreeType *tt = ntreeType_Geometry = static_cast<bNodeTreeType *>(
@@ -95,6 +125,8 @@ void register_node_tree_type_geo(void)
tt->update = geometry_node_tree_update;
tt->get_from_context = geometry_node_tree_get_from_context;
tt->foreach_nodeclass = foreach_nodeclass;
+ tt->valid_socket_type = geometry_node_tree_socket_type_valid;
+ tt->validate_link = geometry_node_tree_validate_link;
ntreeTypeAdd(tt);
}
diff --git a/source/blender/nodes/geometry/node_geometry_util.cc b/source/blender/nodes/geometry/node_geometry_util.cc
index 0f725ecf211..93cada2982b 100644
--- a/source/blender/nodes/geometry/node_geometry_util.cc
+++ b/source/blender/nodes/geometry/node_geometry_util.cc
@@ -56,9 +56,15 @@ void update_attribute_input_socket_availabilities(bNode &node,
} // namespace blender::nodes
-bool geo_node_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree)
+bool geo_node_poll_default(bNodeType *UNUSED(ntype),
+ bNodeTree *ntree,
+ const char **r_disabled_hint)
{
- return STREQ(ntree->idname, "GeometryNodeTree");
+ if (!STREQ(ntree->idname, "GeometryNodeTree")) {
+ *r_disabled_hint = "Not a geometry node tree";
+ return false;
+ }
+ return true;
}
void geo_node_type_base(bNodeType *ntype, int type, const char *name, short nclass, short flag)
diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh
index fb80bd08797..79a98c5ebf0 100644
--- a/source/blender/nodes/geometry/node_geometry_util.hh
+++ b/source/blender/nodes/geometry/node_geometry_util.hh
@@ -36,7 +36,9 @@
void geo_node_type_base(
struct bNodeType *ntype, int type, const char *name, short nclass, short flag);
-bool geo_node_poll_default(struct bNodeType *ntype, struct bNodeTree *ntree);
+bool geo_node_poll_default(struct bNodeType *ntype,
+ struct bNodeTree *ntree,
+ const char **r_disabled_hint);
namespace blender::nodes {
void update_attribute_input_socket_availabilities(bNode &node,
@@ -58,4 +60,14 @@ Mesh *create_cylinder_or_cone_mesh(const float radius_top,
const int verts_num,
const GeometryNodeMeshCircleFillType fill_type);
+Mesh *create_cube_mesh(const float size);
+
+/**
+ * Copies the point domain attributes from `in_component` that are in the mask to `out_component`.
+ */
+void copy_point_attributes_based_on_mask(const GeometryComponent &in_component,
+ GeometryComponent &result_component,
+ Span<bool> masks,
+ const bool invert);
+
} // namespace blender::nodes
diff --git a/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc b/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc
index fca460e3566..d1b71d6f2ba 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc
@@ -14,13 +14,14 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "node_geometry_util.hh"
-
#include "BLI_math_rotation.h"
+#include "BLI_task.hh"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "node_geometry_util.hh"
+
static bNodeSocketTemplate geo_node_align_rotation_to_vector_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("Factor")},
@@ -50,91 +51,117 @@ static void geo_node_align_rotation_to_vector_layout(uiLayout *layout,
namespace blender::nodes {
-static void align_rotations_auto_pivot(const Float3ReadAttribute &vectors,
- const FloatReadAttribute &factors,
+static void geo_node_align_rotation_to_vector_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryAlignRotationToVector *node_storage = (NodeGeometryAlignRotationToVector *)
+ MEM_callocN(sizeof(NodeGeometryAlignRotationToVector), __func__);
+
+ node_storage->axis = GEO_NODE_ALIGN_ROTATION_TO_VECTOR_AXIS_X;
+ node_storage->input_type_factor = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
+ node_storage->input_type_vector = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
+
+ node->storage = node_storage;
+}
+
+static void geo_node_align_rotation_to_vector_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryAlignRotationToVector *node_storage = (NodeGeometryAlignRotationToVector *)
+ node->storage;
+ update_attribute_input_socket_availabilities(
+ *node, "Factor", (GeometryNodeAttributeInputMode)node_storage->input_type_factor);
+ update_attribute_input_socket_availabilities(
+ *node, "Vector", (GeometryNodeAttributeInputMode)node_storage->input_type_vector);
+}
+
+static void align_rotations_auto_pivot(const VArray<float3> &vectors,
+ const VArray<float> &factors,
const float3 local_main_axis,
- MutableSpan<float3> rotations)
+ const MutableSpan<float3> rotations)
{
- for (const int i : IndexRange(vectors.size())) {
- const float3 vector = vectors[i];
- if (is_zero_v3(vector)) {
- continue;
- }
+ parallel_for(IndexRange(vectors.size()), 128, [&](IndexRange range) {
+ for (const int i : range) {
+ const float3 vector = vectors[i];
+ if (is_zero_v3(vector)) {
+ continue;
+ }
- float old_rotation[3][3];
- eul_to_mat3(old_rotation, rotations[i]);
- float3 old_axis;
- mul_v3_m3v3(old_axis, old_rotation, local_main_axis);
+ float old_rotation[3][3];
+ eul_to_mat3(old_rotation, rotations[i]);
+ float3 old_axis;
+ mul_v3_m3v3(old_axis, old_rotation, local_main_axis);
- const float3 new_axis = vector.normalized();
- float3 rotation_axis = float3::cross_high_precision(old_axis, new_axis);
- if (is_zero_v3(rotation_axis)) {
- /* The vectors are linearly dependent, so we fall back to another axis. */
- rotation_axis = float3::cross_high_precision(old_axis, float3(1, 0, 0));
+ const float3 new_axis = vector.normalized();
+ float3 rotation_axis = float3::cross_high_precision(old_axis, new_axis);
if (is_zero_v3(rotation_axis)) {
- /* This is now guaranteed to not be zero. */
- rotation_axis = float3::cross_high_precision(old_axis, float3(0, 1, 0));
+ /* The vectors are linearly dependent, so we fall back to another axis. */
+ rotation_axis = float3::cross_high_precision(old_axis, float3(1, 0, 0));
+ if (is_zero_v3(rotation_axis)) {
+ /* This is now guaranteed to not be zero. */
+ rotation_axis = float3::cross_high_precision(old_axis, float3(0, 1, 0));
+ }
}
- }
- const float full_angle = angle_normalized_v3v3(old_axis, new_axis);
- const float angle = factors[i] * full_angle;
+ const float full_angle = angle_normalized_v3v3(old_axis, new_axis);
+ const float angle = factors[i] * full_angle;
- float rotation[3][3];
- axis_angle_to_mat3(rotation, rotation_axis, angle);
+ float rotation[3][3];
+ axis_angle_to_mat3(rotation, rotation_axis, angle);
- float new_rotation_matrix[3][3];
- mul_m3_m3m3(new_rotation_matrix, rotation, old_rotation);
+ float new_rotation_matrix[3][3];
+ mul_m3_m3m3(new_rotation_matrix, rotation, old_rotation);
- float3 new_rotation;
- mat3_to_eul(new_rotation, new_rotation_matrix);
+ float3 new_rotation;
+ mat3_to_eul(new_rotation, new_rotation_matrix);
- rotations[i] = new_rotation;
- }
+ rotations[i] = new_rotation;
+ }
+ });
}
-static void align_rotations_fixed_pivot(const Float3ReadAttribute &vectors,
- const FloatReadAttribute &factors,
+static void align_rotations_fixed_pivot(const VArray<float3> &vectors,
+ const VArray<float> &factors,
const float3 local_main_axis,
const float3 local_pivot_axis,
- MutableSpan<float3> rotations)
+ const MutableSpan<float3> rotations)
{
if (local_main_axis == local_pivot_axis) {
/* Can't compute any meaningful rotation angle in this case. */
return;
}
- for (const int i : IndexRange(vectors.size())) {
- const float3 vector = vectors[i];
- if (is_zero_v3(vector)) {
- continue;
- }
+ parallel_for(IndexRange(vectors.size()), 128, [&](IndexRange range) {
+ for (const int i : range) {
+ const float3 vector = vectors[i];
+ if (is_zero_v3(vector)) {
+ continue;
+ }
- float old_rotation[3][3];
- eul_to_mat3(old_rotation, rotations[i]);
- float3 old_axis;
- mul_v3_m3v3(old_axis, old_rotation, local_main_axis);
- float3 pivot_axis;
- mul_v3_m3v3(pivot_axis, old_rotation, local_pivot_axis);
-
- float full_angle = angle_signed_on_axis_v3v3_v3(vector, old_axis, pivot_axis);
- if (full_angle > M_PI) {
- /* Make sure the point is rotated as little as possible. */
- full_angle -= 2.0f * M_PI;
- }
- const float angle = factors[i] * full_angle;
+ float old_rotation[3][3];
+ eul_to_mat3(old_rotation, rotations[i]);
+ float3 old_axis;
+ mul_v3_m3v3(old_axis, old_rotation, local_main_axis);
+ float3 pivot_axis;
+ mul_v3_m3v3(pivot_axis, old_rotation, local_pivot_axis);
+
+ float full_angle = angle_signed_on_axis_v3v3_v3(vector, old_axis, pivot_axis);
+ if (full_angle > M_PI) {
+ /* Make sure the point is rotated as little as possible. */
+ full_angle -= 2.0f * M_PI;
+ }
+ const float angle = factors[i] * full_angle;
- float rotation[3][3];
- axis_angle_to_mat3(rotation, pivot_axis, angle);
+ float rotation[3][3];
+ axis_angle_to_mat3(rotation, pivot_axis, angle);
- float new_rotation_matrix[3][3];
- mul_m3_m3m3(new_rotation_matrix, rotation, old_rotation);
+ float new_rotation_matrix[3][3];
+ mul_m3_m3m3(new_rotation_matrix, rotation, old_rotation);
- float3 new_rotation;
- mat3_to_eul(new_rotation, new_rotation_matrix);
+ float3 new_rotation;
+ mat3_to_eul(new_rotation, new_rotation_matrix);
- rotations[i] = new_rotation;
- }
+ rotations[i] = new_rotation;
+ }
+ });
}
static void align_rotations_on_component(GeometryComponent &component,
@@ -144,30 +171,30 @@ static void align_rotations_on_component(GeometryComponent &component,
const NodeGeometryAlignRotationToVector &storage = *(const NodeGeometryAlignRotationToVector *)
node.storage;
- OutputAttributePtr rotation_attribute = component.attribute_try_get_for_output(
- "rotation", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3);
- if (!rotation_attribute) {
+ OutputAttribute_Typed<float3> rotations = component.attribute_try_get_for_output<float3>(
+ "rotation", ATTR_DOMAIN_POINT, {0, 0, 0});
+ if (!rotations) {
return;
}
- MutableSpan<float3> rotations = rotation_attribute->get_span<float3>();
- FloatReadAttribute factors = params.get_input_attribute<float>(
+ GVArray_Typed<float> factors = params.get_input_attribute<float>(
"Factor", component, ATTR_DOMAIN_POINT, 1.0f);
- Float3ReadAttribute vectors = params.get_input_attribute<float3>(
+ GVArray_Typed<float3> vectors = params.get_input_attribute<float3>(
"Vector", component, ATTR_DOMAIN_POINT, {0, 0, 1});
float3 local_main_axis{0, 0, 0};
local_main_axis[storage.axis] = 1;
if (storage.pivot_axis == GEO_NODE_ALIGN_ROTATION_TO_VECTOR_PIVOT_AXIS_AUTO) {
- align_rotations_auto_pivot(vectors, factors, local_main_axis, rotations);
+ align_rotations_auto_pivot(vectors, factors, local_main_axis, rotations.as_span());
}
else {
float3 local_pivot_axis{0, 0, 0};
local_pivot_axis[storage.pivot_axis - 1] = 1;
- align_rotations_fixed_pivot(vectors, factors, local_main_axis, local_pivot_axis, rotations);
+ align_rotations_fixed_pivot(
+ vectors, factors, local_main_axis, local_pivot_axis, rotations.as_span());
}
- rotation_attribute.apply_span_and_save();
+ rotations.save();
}
static void geo_node_align_rotation_to_vector_exec(GeoNodeExecParams params)
@@ -183,32 +210,13 @@ static void geo_node_align_rotation_to_vector_exec(GeoNodeExecParams params)
align_rotations_on_component(geometry_set.get_component_for_write<PointCloudComponent>(),
params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ align_rotations_on_component(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
-static void geo_node_align_rotation_to_vector_init(bNodeTree *UNUSED(ntree), bNode *node)
-{
- NodeGeometryAlignRotationToVector *node_storage = (NodeGeometryAlignRotationToVector *)
- MEM_callocN(sizeof(NodeGeometryAlignRotationToVector), __func__);
-
- node_storage->axis = GEO_NODE_ALIGN_ROTATION_TO_VECTOR_AXIS_X;
- node_storage->input_type_factor = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
- node_storage->input_type_vector = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
-
- node->storage = node_storage;
-}
-
-static void geo_node_align_rotation_to_vector_update(bNodeTree *UNUSED(ntree), bNode *node)
-{
- NodeGeometryAlignRotationToVector *node_storage = (NodeGeometryAlignRotationToVector *)
- node->storage;
- update_attribute_input_socket_availabilities(
- *node, "Factor", (GeometryNodeAttributeInputMode)node_storage->input_type_factor);
- update_attribute_input_socket_availabilities(
- *node, "Vector", (GeometryNodeAttributeInputMode)node_storage->input_type_vector);
-}
-
} // namespace blender::nodes
void register_node_type_geo_align_rotation_to_vector()
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc
new file mode 100644
index 00000000000..71643df1cb6
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc
@@ -0,0 +1,284 @@
+
+/*
+ * 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.
+ */
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_attribute_clamp_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_STRING, N_("Attribute")},
+ {SOCK_STRING, N_("Result")},
+ {SOCK_VECTOR, N_("Min"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_VECTOR, N_("Max"), 1.0f, 1.0f, 1.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_FLOAT, N_("Min"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_FLOAT, N_("Max"), 1.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_INT, N_("Min"), 0.0f, 0.0f, 0.0f, 0.0f, -100000, 100000},
+ {SOCK_INT, N_("Max"), 100.0f, 0.0f, 0.0f, 0.0f, -100000, 100000},
+ {SOCK_RGBA, N_("Min"), 0.5, 0.5, 0.5, 1.0},
+ {SOCK_RGBA, N_("Max"), 0.5, 0.5, 0.5, 1.0},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_attribute_clamp_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+static void geo_node_attribute_clamp_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
+ uiItemR(layout, ptr, "operation", 0, "", ICON_NONE);
+}
+
+static void geo_node_attribute_clamp_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeAttributeClamp *data = (NodeAttributeClamp *)MEM_callocN(sizeof(NodeAttributeClamp),
+ __func__);
+ data->data_type = CD_PROP_FLOAT;
+ data->operation = NODE_CLAMP_MINMAX;
+ node->storage = data;
+}
+
+static void geo_node_attribute_clamp_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ bNodeSocket *sock_min_vector = (bNodeSocket *)BLI_findlink(&node->inputs, 3);
+ bNodeSocket *sock_max_vector = sock_min_vector->next;
+ bNodeSocket *sock_min_float = sock_max_vector->next;
+ bNodeSocket *sock_max_float = sock_min_float->next;
+ bNodeSocket *sock_min_int = sock_max_float->next;
+ bNodeSocket *sock_max_int = sock_min_int->next;
+ bNodeSocket *sock_min_color = sock_max_int->next;
+ bNodeSocket *sock_max_color = sock_min_color->next;
+
+ const NodeAttributeClamp &storage = *(const NodeAttributeClamp *)node->storage;
+ const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type);
+ nodeSetSocketAvailability(sock_min_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(sock_max_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(sock_min_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(sock_max_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(sock_min_int, data_type == CD_PROP_INT32);
+ nodeSetSocketAvailability(sock_max_int, data_type == CD_PROP_INT32);
+ nodeSetSocketAvailability(sock_min_color, data_type == CD_PROP_COLOR);
+ nodeSetSocketAvailability(sock_max_color, data_type == CD_PROP_COLOR);
+}
+
+namespace blender::nodes {
+
+template<typename T> T clamp_value(const T val, const T min, const T max);
+
+template<> inline float clamp_value(const float val, const float min, const float max)
+{
+ return std::min(std::max(val, min), max);
+}
+
+template<> inline int clamp_value(const int val, const int min, const int max)
+{
+ return std::min(std::max(val, min), max);
+}
+
+template<> inline float3 clamp_value(const float3 val, const float3 min, const float3 max)
+{
+ float3 tmp;
+ tmp.x = std::min(std::max(val.x, min.x), max.x);
+ tmp.y = std::min(std::max(val.y, min.y), max.y);
+ tmp.z = std::min(std::max(val.z, min.z), max.z);
+ return tmp;
+}
+
+template<>
+inline ColorGeometry4f clamp_value(const ColorGeometry4f val,
+ const ColorGeometry4f min,
+ const ColorGeometry4f max)
+{
+ ColorGeometry4f tmp;
+ tmp.r = std::min(std::max(val.r, min.r), max.r);
+ tmp.g = std::min(std::max(val.g, min.g), max.g);
+ tmp.b = std::min(std::max(val.b, min.b), max.b);
+ tmp.a = std::min(std::max(val.a, min.a), max.a);
+ return tmp;
+}
+
+template<typename T>
+static void clamp_attribute(const VArray<T> &inputs,
+ const MutableSpan<T> outputs,
+ const T min,
+ const T max)
+{
+ for (const int i : IndexRange(outputs.size())) {
+ outputs[i] = clamp_value<T>(inputs[i], min, max);
+ }
+}
+
+static AttributeDomain get_result_domain(const GeometryComponent &component,
+ StringRef source_name,
+ StringRef result_name)
+{
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
+ if (result_info) {
+ return result_info->domain;
+ }
+ std::optional<AttributeMetaData> source_info = component.attribute_get_meta_data(source_name);
+ if (source_info) {
+ return source_info->domain;
+ }
+ return ATTR_DOMAIN_POINT;
+}
+
+static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParams &params)
+{
+ const std::string attribute_name = params.get_input<std::string>("Attribute");
+ const std::string result_name = params.get_input<std::string>("Result");
+
+ if (attribute_name.empty() || result_name.empty()) {
+ return;
+ }
+
+ if (!component.attribute_exists(attribute_name)) {
+ params.error_message_add(NodeWarningType::Error,
+ TIP_("No attribute with name \"") + attribute_name + "\"");
+ return;
+ }
+
+ const NodeAttributeClamp &storage = *(const NodeAttributeClamp *)params.node().storage;
+ const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type);
+ const AttributeDomain domain = get_result_domain(component, attribute_name, result_name);
+ const int operation = static_cast<int>(storage.operation);
+
+ GVArrayPtr attribute_input = component.attribute_try_get_for_read(
+ attribute_name, domain, data_type);
+
+ OutputAttribute attribute_result = component.attribute_try_get_for_output_only(
+ result_name, domain, data_type);
+
+ if (!attribute_result) {
+ params.error_message_add(NodeWarningType::Error,
+ TIP_("Could not find or create attribute with name \"") +
+ result_name + "\"");
+ return;
+ }
+
+ switch (data_type) {
+ case CD_PROP_FLOAT3: {
+ float3 min = params.get_input<float3>("Min");
+ float3 max = params.get_input<float3>("Max");
+ if (operation == NODE_CLAMP_RANGE) {
+ if (min.x > max.x) {
+ std::swap(min.x, max.x);
+ }
+ if (min.y > max.y) {
+ std::swap(min.y, max.y);
+ }
+ if (min.z > max.z) {
+ std::swap(min.z, max.z);
+ }
+ }
+ MutableSpan<float3> results = attribute_result.as_span<float3>();
+ clamp_attribute<float3>(attribute_input->typed<float3>(), results, min, max);
+ break;
+ }
+ case CD_PROP_FLOAT: {
+ const float min = params.get_input<float>("Min_001");
+ const float max = params.get_input<float>("Max_001");
+ MutableSpan<float> results = attribute_result.as_span<float>();
+ if (operation == NODE_CLAMP_RANGE && min > max) {
+ clamp_attribute<float>(attribute_input->typed<float>(), results, max, min);
+ }
+ else {
+ clamp_attribute<float>(attribute_input->typed<float>(), results, min, max);
+ }
+ break;
+ }
+ case CD_PROP_INT32: {
+ const int min = params.get_input<int>("Min_002");
+ const int max = params.get_input<int>("Max_002");
+ MutableSpan<int> results = attribute_result.as_span<int>();
+ if (operation == NODE_CLAMP_RANGE && min > max) {
+ clamp_attribute<int>(attribute_input->typed<int>(), results, max, min);
+ }
+ else {
+ clamp_attribute<int>(attribute_input->typed<int>(), results, min, max);
+ }
+ break;
+ }
+ case CD_PROP_COLOR: {
+ ColorGeometry4f min = params.get_input<ColorGeometry4f>("Min_003");
+ ColorGeometry4f max = params.get_input<ColorGeometry4f>("Max_003");
+ if (operation == NODE_CLAMP_RANGE) {
+ if (min.r > max.r) {
+ std::swap(min.r, max.r);
+ }
+ if (min.g > max.g) {
+ std::swap(min.g, max.g);
+ }
+ if (min.b > max.b) {
+ std::swap(min.b, max.b);
+ }
+ if (min.a > max.a) {
+ std::swap(min.a, max.a);
+ }
+ }
+ MutableSpan<ColorGeometry4f> results = attribute_result.as_span<ColorGeometry4f>();
+ clamp_attribute<ColorGeometry4f>(
+ attribute_input->typed<ColorGeometry4f>(), results, min, max);
+ break;
+ }
+ default: {
+ BLI_assert(false);
+ break;
+ }
+ }
+
+ attribute_result.save();
+}
+
+static void geo_node_attribute_clamp_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ geometry_set = geometry_set_realize_instances(geometry_set);
+
+ if (geometry_set.has<MeshComponent>()) {
+ clamp_attribute(geometry_set.get_component_for_write<MeshComponent>(), params);
+ }
+ if (geometry_set.has<PointCloudComponent>()) {
+ clamp_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params);
+ }
+ if (geometry_set.has<CurveComponent>()) {
+ clamp_attribute(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
+
+ params.set_output("Geometry", geometry_set);
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_attribute_clamp()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_ATTRIBUTE_CLAMP, "Attribute Clamp", NODE_CLASS_ATTRIBUTE, 0);
+ node_type_socket_templates(&ntype, geo_node_attribute_clamp_in, geo_node_attribute_clamp_out);
+ node_type_init(&ntype, geo_node_attribute_clamp_init);
+ node_type_update(&ntype, geo_node_attribute_clamp_update);
+ ntype.geometry_node_execute = blender::nodes::geo_node_attribute_clamp_exec;
+ ntype.draw_buttons = geo_node_attribute_clamp_layout;
+ node_type_storage(
+ &ntype, "NodeAttributeClamp", node_free_standard_storage, node_copy_standard_storage);
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc
index 98bf612f589..5293dd8c876 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc
@@ -14,13 +14,15 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "node_geometry_util.hh"
+#include "BLI_task.hh"
#include "BKE_colorband.h"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "node_geometry_util.hh"
+
static bNodeSocketTemplate geo_node_attribute_color_ramp_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("Attribute")},
@@ -42,20 +44,28 @@ static void geo_node_attribute_color_ramp_layout(uiLayout *layout,
namespace blender::nodes {
+static void geo_node_attribute_color_ramp_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeAttributeColorRamp *node_storage = (NodeAttributeColorRamp *)MEM_callocN(
+ sizeof(NodeAttributeColorRamp), __func__);
+ BKE_colorband_init(&node_storage->color_ramp, true);
+ node->storage = node_storage;
+}
+
static AttributeDomain get_result_domain(const GeometryComponent &component,
StringRef input_name,
StringRef result_name)
{
/* Use the domain of the result attribute if it already exists. */
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
- if (result_attribute) {
- return result_attribute->domain();
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
+ if (result_info) {
+ return result_info->domain;
}
/* Otherwise use the input attribute's domain if it exists. */
- ReadAttributePtr input_attribute = component.attribute_try_get_for_read(input_name);
- if (input_attribute) {
- return input_attribute->domain();
+ std::optional<AttributeMetaData> source_info = component.attribute_get_meta_data(input_name);
+ if (source_info) {
+ return source_info->domain;
}
return ATTR_DOMAIN_POINT;
@@ -71,27 +81,27 @@ static void execute_on_component(const GeoNodeExecParams &params, GeometryCompon
/* Always output a color attribute for now. We might want to allow users to customize.
* Using the type of an existing attribute could work, but does not have a real benefit
* currently. */
- const CustomDataType result_type = CD_PROP_COLOR;
const AttributeDomain result_domain = get_result_domain(component, input_name, result_name);
- OutputAttributePtr attribute_result = component.attribute_try_get_for_output(
- result_name, result_domain, result_type);
+ OutputAttribute_Typed<ColorGeometry4f> attribute_result =
+ component.attribute_try_get_for_output_only<ColorGeometry4f>(result_name, result_domain);
if (!attribute_result) {
return;
}
- FloatReadAttribute attribute_in = component.attribute_get_for_read<float>(
+ GVArray_Typed<float> attribute_in = component.attribute_get_for_read<float>(
input_name, result_domain, 0.0f);
- Span<float> data_in = attribute_in.get_span();
- MutableSpan<Color4f> data_out = attribute_result->get_span_for_write_only<Color4f>();
+ MutableSpan<ColorGeometry4f> results = attribute_result.as_span();
ColorBand *color_ramp = &node_storage->color_ramp;
- for (const int i : data_in.index_range()) {
- BKE_colorband_evaluate(color_ramp, data_in[i], data_out[i]);
- }
+ parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ BKE_colorband_evaluate(color_ramp, attribute_in[i], results[i]);
+ }
+ });
- attribute_result.apply_span_and_save();
+ attribute_result.save();
}
static void geo_node_attribute_color_ramp_exec(GeoNodeExecParams params)
@@ -106,18 +116,13 @@ static void geo_node_attribute_color_ramp_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
execute_on_component(params, geometry_set.get_component_for_write<PointCloudComponent>());
}
+ if (geometry_set.has<CurveComponent>()) {
+ execute_on_component(params, geometry_set.get_component_for_write<CurveComponent>());
+ }
params.set_output("Geometry", std::move(geometry_set));
}
-static void geo_node_attribute_color_ramp_init(bNodeTree *UNUSED(ntree), bNode *node)
-{
- NodeAttributeColorRamp *node_storage = (NodeAttributeColorRamp *)MEM_callocN(
- sizeof(NodeAttributeColorRamp), __func__);
- BKE_colorband_init(&node_storage->color_ramp, true);
- node->storage = node_storage;
-}
-
} // namespace blender::nodes
void register_node_type_geo_attribute_color_ramp()
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc
index 9c5c7e270b1..d8c52d16f41 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc
@@ -14,11 +14,11 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "node_geometry_util.hh"
-
#include "UI_interface.h"
#include "UI_resources.h"
+#include "node_geometry_util.hh"
+
static bNodeSocketTemplate geo_node_attribute_combine_xyz_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("X")},
@@ -77,9 +77,9 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
StringRef result_name)
{
/* Use the domain of the result attribute if it already exists. */
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
- if (result_attribute) {
- return result_attribute->domain();
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
+ if (result_info) {
+ return result_info->domain;
}
/* Otherwise use the highest priority domain from existing input attributes, or the default. */
@@ -94,27 +94,25 @@ static void combine_attributes(GeometryComponent &component, const GeoNodeExecPa
}
const AttributeDomain result_domain = get_result_domain(component, params, result_name);
- OutputAttributePtr attribute_result = component.attribute_try_get_for_output(
- result_name, result_domain, CD_PROP_FLOAT3);
+ OutputAttribute_Typed<float3> attribute_result =
+ component.attribute_try_get_for_output_only<float3>(result_name, result_domain);
if (!attribute_result) {
return;
}
- FloatReadAttribute attribute_x = params.get_input_attribute<float>(
+ GVArray_Typed<float> attribute_x = params.get_input_attribute<float>(
"X", component, result_domain, 0.0f);
- FloatReadAttribute attribute_y = params.get_input_attribute<float>(
+ GVArray_Typed<float> attribute_y = params.get_input_attribute<float>(
"Y", component, result_domain, 0.0f);
- FloatReadAttribute attribute_z = params.get_input_attribute<float>(
+ GVArray_Typed<float> attribute_z = params.get_input_attribute<float>(
"Z", component, result_domain, 0.0f);
- MutableSpan<float3> results = attribute_result->get_span_for_write_only<float3>();
- for (const int i : results.index_range()) {
+ for (const int i : IndexRange(attribute_result->size())) {
const float x = attribute_x[i];
const float y = attribute_y[i];
const float z = attribute_z[i];
- const float3 result = float3(x, y, z);
- results[i] = result;
+ attribute_result->set(i, {x, y, z});
}
- attribute_result.apply_span_and_save();
+ attribute_result.save();
}
static void geo_node_attribute_combine_xyz_exec(GeoNodeExecParams params)
@@ -129,6 +127,9 @@ static void geo_node_attribute_combine_xyz_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
combine_attributes(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ combine_attributes(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc
index db1a5d18744..57ac68b4cd4 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc
@@ -14,23 +14,13 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "node_geometry_util.hh"
-
-#include "BKE_attribute.h"
-#include "BKE_attribute_access.hh"
-
-#include "BLI_array.hh"
-#include "BLI_math_base_safe.h"
-#include "BLI_rand.hh"
-
-#include "DNA_mesh_types.h"
-#include "DNA_pointcloud_types.h"
-
#include "UI_interface.h"
#include "UI_resources.h"
#include "NOD_math_functions.hh"
+#include "node_geometry_util.hh"
+
static bNodeSocketTemplate geo_node_attribute_compare_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("A")},
@@ -91,21 +81,18 @@ static void geo_node_attribute_compare_update(bNodeTree *UNUSED(ntree), bNode *n
nodeSetSocketAvailability(socket_threshold, operation_tests_equality(*node_storage));
}
-static void do_math_operation(const FloatReadAttribute &input_a,
- const FloatReadAttribute &input_b,
+static void do_math_operation(const VArray<float> &input_a,
+ const VArray<float> &input_b,
const FloatCompareOperation operation,
MutableSpan<bool> span_result)
{
const int size = input_a.size();
- Span<float> span_a = input_a.get_span();
- Span<float> span_b = input_b.get_span();
-
if (try_dispatch_float_math_fl_fl_to_bool(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
for (const int i : IndexRange(size)) {
- const float a = span_a[i];
- const float b = span_b[i];
+ const float a = input_a[i];
+ const float b = input_b[i];
const bool out = math_function(a, b);
span_result[i] = out;
}
@@ -117,8 +104,8 @@ static void do_math_operation(const FloatReadAttribute &input_a,
BLI_assert(false);
}
-static void do_equal_operation_float(const FloatReadAttribute &input_a,
- const FloatReadAttribute &input_b,
+static void do_equal_operation_float(const VArray<float> &input_a,
+ const VArray<float> &input_b,
const float threshold,
MutableSpan<bool> span_result)
{
@@ -130,8 +117,8 @@ static void do_equal_operation_float(const FloatReadAttribute &input_a,
}
}
-static void do_equal_operation_float3(const Float3ReadAttribute &input_a,
- const Float3ReadAttribute &input_b,
+static void do_equal_operation_float3(const VArray<float3> &input_a,
+ const VArray<float3> &input_b,
const float threshold,
MutableSpan<bool> span_result)
{
@@ -144,22 +131,22 @@ static void do_equal_operation_float3(const Float3ReadAttribute &input_a,
}
}
-static void do_equal_operation_color4f(const Color4fReadAttribute &input_a,
- const Color4fReadAttribute &input_b,
+static void do_equal_operation_color4f(const VArray<ColorGeometry4f> &input_a,
+ const VArray<ColorGeometry4f> &input_b,
const float threshold,
MutableSpan<bool> span_result)
{
const float threshold_squared = pow2f(threshold);
const int size = input_a.size();
for (const int i : IndexRange(size)) {
- const Color4f a = input_a[i];
- const Color4f b = input_b[i];
+ const ColorGeometry4f a = input_a[i];
+ const ColorGeometry4f b = input_b[i];
span_result[i] = len_squared_v4v4(a, b) < threshold_squared;
}
}
-static void do_equal_operation_bool(const BooleanReadAttribute &input_a,
- const BooleanReadAttribute &input_b,
+static void do_equal_operation_bool(const VArray<bool> &input_a,
+ const VArray<bool> &input_b,
const float UNUSED(threshold),
MutableSpan<bool> span_result)
{
@@ -171,8 +158,8 @@ static void do_equal_operation_bool(const BooleanReadAttribute &input_a,
}
}
-static void do_not_equal_operation_float(const FloatReadAttribute &input_a,
- const FloatReadAttribute &input_b,
+static void do_not_equal_operation_float(const VArray<float> &input_a,
+ const VArray<float> &input_b,
const float threshold,
MutableSpan<bool> span_result)
{
@@ -184,8 +171,8 @@ static void do_not_equal_operation_float(const FloatReadAttribute &input_a,
}
}
-static void do_not_equal_operation_float3(const Float3ReadAttribute &input_a,
- const Float3ReadAttribute &input_b,
+static void do_not_equal_operation_float3(const VArray<float3> &input_a,
+ const VArray<float3> &input_b,
const float threshold,
MutableSpan<bool> span_result)
{
@@ -198,22 +185,22 @@ static void do_not_equal_operation_float3(const Float3ReadAttribute &input_a,
}
}
-static void do_not_equal_operation_color4f(const Color4fReadAttribute &input_a,
- const Color4fReadAttribute &input_b,
+static void do_not_equal_operation_color4f(const VArray<ColorGeometry4f> &input_a,
+ const VArray<ColorGeometry4f> &input_b,
const float threshold,
MutableSpan<bool> span_result)
{
const float threshold_squared = pow2f(threshold);
const int size = input_a.size();
for (const int i : IndexRange(size)) {
- const Color4f a = input_a[i];
- const Color4f b = input_b[i];
+ const ColorGeometry4f a = input_a[i];
+ const ColorGeometry4f b = input_b[i];
span_result[i] = len_squared_v4v4(a, b) >= threshold_squared;
}
}
-static void do_not_equal_operation_bool(const BooleanReadAttribute &input_a,
- const BooleanReadAttribute &input_b,
+static void do_not_equal_operation_bool(const VArray<bool> &input_a,
+ const VArray<bool> &input_b,
const float UNUSED(threshold),
MutableSpan<bool> span_result)
{
@@ -247,9 +234,9 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
StringRef result_name)
{
/* Use the domain of the result attribute if it already exists. */
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
- if (result_attribute) {
- return result_attribute->domain();
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
+ if (result_info) {
+ return result_info->domain;
}
/* Otherwise use the highest priority domain from existing input attributes, or the default. */
@@ -264,20 +251,19 @@ static void attribute_compare_calc(GeometryComponent &component, const GeoNodeEx
node_storage->operation);
const std::string result_name = params.get_input<std::string>("Result");
- const CustomDataType result_type = CD_PROP_BOOL;
const AttributeDomain result_domain = get_result_domain(component, params, result_name);
- OutputAttributePtr attribute_result = component.attribute_try_get_for_output(
- result_name, result_domain, result_type);
+ OutputAttribute_Typed<bool> attribute_result = component.attribute_try_get_for_output_only<bool>(
+ result_name, result_domain);
if (!attribute_result) {
return;
}
const CustomDataType input_data_type = get_data_type(component, params, *node_storage);
- ReadAttributePtr attribute_a = params.get_input_attribute(
+ GVArrayPtr attribute_a = params.get_input_attribute(
"A", component, result_domain, input_data_type, nullptr);
- ReadAttributePtr attribute_b = params.get_input_attribute(
+ GVArrayPtr attribute_b = params.get_input_attribute(
"B", component, result_domain, input_data_type, nullptr);
if (!attribute_a || !attribute_b) {
@@ -285,7 +271,7 @@ static void attribute_compare_calc(GeometryComponent &component, const GeoNodeEx
return;
}
- MutableSpan<bool> result_span = attribute_result->get_span_for_write_only<bool>();
+ MutableSpan<bool> result_span = attribute_result.as_span();
/* Use specific types for correct equality operations, but for other operations we use implicit
* conversions and float comparison. In other words, the comparison is not element-wise. */
@@ -293,38 +279,51 @@ static void attribute_compare_calc(GeometryComponent &component, const GeoNodeEx
const float threshold = params.get_input<float>("Threshold");
if (operation == NODE_FLOAT_COMPARE_EQUAL) {
if (input_data_type == CD_PROP_FLOAT) {
- do_equal_operation_float(*attribute_a, *attribute_b, threshold, result_span);
+ do_equal_operation_float(
+ attribute_a->typed<float>(), attribute_b->typed<float>(), threshold, result_span);
}
else if (input_data_type == CD_PROP_FLOAT3) {
- do_equal_operation_float3(*attribute_a, *attribute_b, threshold, result_span);
+ do_equal_operation_float3(
+ attribute_a->typed<float3>(), attribute_b->typed<float3>(), threshold, result_span);
}
else if (input_data_type == CD_PROP_COLOR) {
- do_equal_operation_color4f(*attribute_a, *attribute_b, threshold, result_span);
+ do_equal_operation_color4f(attribute_a->typed<ColorGeometry4f>(),
+ attribute_b->typed<ColorGeometry4f>(),
+ threshold,
+ result_span);
}
else if (input_data_type == CD_PROP_BOOL) {
- do_equal_operation_bool(*attribute_a, *attribute_b, threshold, result_span);
+ do_equal_operation_bool(
+ attribute_a->typed<bool>(), attribute_b->typed<bool>(), threshold, result_span);
}
}
else if (operation == NODE_FLOAT_COMPARE_NOT_EQUAL) {
if (input_data_type == CD_PROP_FLOAT) {
- do_not_equal_operation_float(*attribute_a, *attribute_b, threshold, result_span);
+ do_not_equal_operation_float(
+ attribute_a->typed<float>(), attribute_b->typed<float>(), threshold, result_span);
}
else if (input_data_type == CD_PROP_FLOAT3) {
- do_not_equal_operation_float3(*attribute_a, *attribute_b, threshold, result_span);
+ do_not_equal_operation_float3(
+ attribute_a->typed<float3>(), attribute_b->typed<float3>(), threshold, result_span);
}
else if (input_data_type == CD_PROP_COLOR) {
- do_not_equal_operation_color4f(*attribute_a, *attribute_b, threshold, result_span);
+ do_not_equal_operation_color4f(attribute_a->typed<ColorGeometry4f>(),
+ attribute_b->typed<ColorGeometry4f>(),
+ threshold,
+ result_span);
}
else if (input_data_type == CD_PROP_BOOL) {
- do_not_equal_operation_bool(*attribute_a, *attribute_b, threshold, result_span);
+ do_not_equal_operation_bool(
+ attribute_a->typed<bool>(), attribute_b->typed<bool>(), threshold, result_span);
}
}
}
else {
- do_math_operation(*attribute_a, *attribute_b, operation, result_span);
+ do_math_operation(
+ attribute_a->typed<float>(), attribute_b->typed<float>(), operation, result_span);
}
- attribute_result.apply_span_and_save();
+ attribute_result.save();
}
static void geo_node_attribute_compare_exec(GeoNodeExecParams params)
@@ -339,6 +338,9 @@ static void geo_node_attribute_compare_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
attribute_compare_calc(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ attribute_compare_calc(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc
index 11d220dd903..7b40456b180 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc
@@ -14,11 +14,11 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "node_geometry_util.hh"
-
#include "UI_interface.h"
#include "UI_resources.h"
+#include "node_geometry_util.hh"
+
static bNodeSocketTemplate geo_node_attribute_convert_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("Attribute")},
@@ -35,8 +35,10 @@ static void geo_node_attribute_convert_layout(uiLayout *layout,
bContext *UNUSED(C),
PointerRNA *ptr)
{
- uiItemR(layout, ptr, "domain", 0, "", ICON_NONE);
- uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+ uiItemR(layout, ptr, "domain", 0, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "data_type", 0, IFACE_("Type"), ICON_NONE);
}
static void geo_node_attribute_convert_init(bNodeTree *UNUSED(tree), bNode *node)
@@ -44,41 +46,69 @@ static void geo_node_attribute_convert_init(bNodeTree *UNUSED(tree), bNode *node
NodeAttributeConvert *data = (NodeAttributeConvert *)MEM_callocN(sizeof(NodeAttributeConvert),
__func__);
- data->data_type = CD_PROP_FLOAT;
+ data->data_type = CD_AUTO_FROM_NAME;
data->domain = ATTR_DOMAIN_AUTO;
node->storage = data;
}
namespace blender::nodes {
-static AttributeDomain get_result_domain(const GeometryComponent &component,
- StringRef source_name,
- StringRef result_name)
+static AttributeMetaData get_result_domain_and_type(const GeometryComponent &component,
+ const StringRef source_name,
+ const StringRef result_name)
+{
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
+ if (result_info) {
+ return *result_info;
+ }
+ std::optional<AttributeMetaData> source_info = component.attribute_get_meta_data(source_name);
+ if (source_info) {
+ return *source_info;
+ }
+ /* The node won't do anything in this case, but we still have to return a value. */
+ return AttributeMetaData{ATTR_DOMAIN_POINT, CD_PROP_BOOL};
+}
+
+static bool conversion_can_be_skipped(const GeometryComponent &component,
+ const StringRef source_name,
+ const StringRef result_name,
+ const AttributeDomain result_domain,
+ const CustomDataType result_type)
{
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
- if (result_attribute) {
- return result_attribute->domain();
+ if (source_name != result_name) {
+ return false;
}
- ReadAttributePtr source_attribute = component.attribute_try_get_for_read(source_name);
- if (source_attribute) {
- return source_attribute->domain();
+ std::optional<AttributeMetaData> info = component.attribute_get_meta_data(result_name);
+ if (!info) {
+ return false;
}
- return ATTR_DOMAIN_POINT;
+ if (info->domain != result_domain) {
+ return false;
+ }
+ if (info->data_type != result_type) {
+ return false;
+ }
+ return true;
}
static void attribute_convert_calc(GeometryComponent &component,
const GeoNodeExecParams &params,
const StringRef source_name,
const StringRef result_name,
- const CustomDataType result_type,
+ const CustomDataType data_type,
const AttributeDomain domain)
{
- const AttributeDomain result_domain = (domain == ATTR_DOMAIN_AUTO) ?
- get_result_domain(
- component, source_name, result_name) :
- domain;
+ const AttributeMetaData auto_info = get_result_domain_and_type(
+ component, source_name, result_name);
+ const AttributeDomain result_domain = (domain == ATTR_DOMAIN_AUTO) ? auto_info.domain : domain;
+ const CustomDataType result_type = (data_type == CD_AUTO_FROM_NAME) ? auto_info.data_type :
+ data_type;
+
+ if (conversion_can_be_skipped(component, source_name, result_name, result_domain, result_type)) {
+ return;
+ }
- ReadAttributePtr source_attribute = component.attribute_try_get_for_read(
+ GVArrayPtr source_attribute = component.attribute_try_get_for_read(
source_name, result_domain, result_type);
if (!source_attribute) {
params.error_message_add(NodeWarningType::Error,
@@ -86,25 +116,22 @@ static void attribute_convert_calc(GeometryComponent &component,
return;
}
- OutputAttributePtr result_attribute = component.attribute_try_get_for_output(
+ OutputAttribute result_attribute = component.attribute_try_get_for_output_only(
result_name, result_domain, result_type);
if (!result_attribute) {
return;
}
- fn::GSpan source_span = source_attribute->get_span();
- fn::GMutableSpan result_span = result_attribute->get_span_for_write_only();
- if (source_span.is_empty() || result_span.is_empty()) {
- return;
- }
+ GVArray_GSpan source_span{*source_attribute};
+ GMutableSpan result_span = result_attribute.as_span();
+
BLI_assert(source_span.size() == result_span.size());
const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(result_type);
BLI_assert(cpp_type != nullptr);
cpp_type->copy_to_initialized_n(source_span.data(), result_span.data(), result_span.size());
-
- result_attribute.apply_span_and_save();
+ result_attribute.save();
}
static void geo_node_attribute_convert_exec(GeoNodeExecParams params)
@@ -140,6 +167,14 @@ static void geo_node_attribute_convert_exec(GeoNodeExecParams params)
data_type,
domain);
}
+ if (geometry_set.has<CurveComponent>()) {
+ attribute_convert_calc(geometry_set.get_component_for_write<CurveComponent>(),
+ params,
+ source_name,
+ result_name,
+ data_type,
+ domain);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc
new file mode 100644
index 00000000000..599c9e58e52
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc
@@ -0,0 +1,232 @@
+/*
+ * 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.
+ */
+
+#include "BLI_blenlib.h"
+#include "BLI_task.hh"
+
+#include "BKE_colortools.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_attribute_curve_map_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_STRING, N_("Attribute")},
+ {SOCK_STRING, N_("Result")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_attribute_curve_map_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+static void geo_node_attribute_curve_map_layout(uiLayout *layout,
+ bContext *UNUSED(C),
+ PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
+ bNode *node = (bNode *)ptr->data;
+ NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)node->storage;
+ switch (data->data_type) {
+ case CD_PROP_FLOAT:
+ uiTemplateCurveMapping(layout, ptr, "curve_vec", 0, false, false, false, false);
+ break;
+ case CD_PROP_FLOAT3:
+ uiTemplateCurveMapping(layout, ptr, "curve_vec", 'v', false, false, false, false);
+ break;
+ case CD_PROP_COLOR:
+ uiTemplateCurveMapping(layout, ptr, "curve_rgb", 'c', false, false, false, false);
+ break;
+ }
+}
+
+static void geo_node_attribute_curve_map_free_storage(bNode *node)
+{
+ if (node->storage) {
+ NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)node->storage;
+ BKE_curvemapping_free(data->curve_vec);
+ BKE_curvemapping_free(data->curve_rgb);
+ MEM_freeN(node->storage);
+ }
+}
+
+static void geo_node_attribute_curve_map_copy_storage(bNodeTree *UNUSED(dest_ntree),
+ bNode *dest_node,
+ const bNode *src_node)
+{
+ dest_node->storage = MEM_dupallocN(src_node->storage);
+ NodeAttributeCurveMap *src_data = (NodeAttributeCurveMap *)src_node->storage;
+ NodeAttributeCurveMap *dest_data = (NodeAttributeCurveMap *)dest_node->storage;
+ dest_data->curve_vec = BKE_curvemapping_copy(src_data->curve_vec);
+ dest_data->curve_rgb = BKE_curvemapping_copy(src_data->curve_rgb);
+}
+
+static void geo_node_attribute_curve_map_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)MEM_callocN(sizeof(NodeAttributeCurveMap),
+ __func__);
+
+ data->data_type = CD_PROP_FLOAT;
+ data->curve_vec = BKE_curvemapping_add(4, -1.0f, -1.0f, 1.0f, 1.0f);
+ data->curve_vec->cur = 3;
+ data->curve_rgb = BKE_curvemapping_add(4, 0.0f, 0.0f, 1.0f, 1.0f);
+ node->storage = data;
+}
+
+static void geo_node_attribute_curve_map_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ /* Set the active curve when data type is changed. */
+ NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)node->storage;
+ if (data->data_type == CD_PROP_FLOAT) {
+ data->curve_vec->cur = 3;
+ }
+ else if (data->data_type == CD_PROP_FLOAT3) {
+ data->curve_vec->cur = 0;
+ }
+}
+
+namespace blender::nodes {
+
+static AttributeDomain get_result_domain(const GeometryComponent &component,
+ StringRef input_name,
+ StringRef result_name)
+{
+ /* Use the domain of the result attribute if it already exists. */
+ ReadAttributeLookup result_attribute = component.attribute_try_get_for_read(result_name);
+ if (result_attribute) {
+ return result_attribute.domain;
+ }
+
+ /* Otherwise use the input attribute's domain if it exists. */
+ ReadAttributeLookup input_attribute = component.attribute_try_get_for_read(input_name);
+ if (input_attribute) {
+ return input_attribute.domain;
+ }
+
+ return ATTR_DOMAIN_POINT;
+}
+
+static void execute_on_component(const GeoNodeExecParams &params, GeometryComponent &component)
+{
+ const bNode &bnode = params.node();
+ NodeAttributeCurveMap &node_storage = *(NodeAttributeCurveMap *)bnode.storage;
+ const std::string result_name = params.get_input<std::string>("Result");
+ const std::string input_name = params.get_input<std::string>("Attribute");
+
+ const CustomDataType result_type = (CustomDataType)node_storage.data_type;
+ const AttributeDomain result_domain = get_result_domain(component, input_name, result_name);
+
+ OutputAttribute attribute_result = component.attribute_try_get_for_output_only(
+ result_name, result_domain, result_type);
+ if (!attribute_result) {
+ return;
+ }
+
+ switch (result_type) {
+ case CD_PROP_FLOAT: {
+ const CurveMapping *cumap = (CurveMapping *)node_storage.curve_vec;
+ GVArray_Typed<float> attribute_in = component.attribute_get_for_read<float>(
+ input_name, result_domain, float(0.0f));
+ MutableSpan<float> results = attribute_result.as_span<float>();
+ parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i] = BKE_curvemapping_evaluateF(cumap, 3, attribute_in[i]);
+ }
+ });
+ break;
+ }
+ case CD_PROP_FLOAT3: {
+ const CurveMapping *cumap = (CurveMapping *)node_storage.curve_vec;
+ GVArray_Typed<float3> attribute_in = component.attribute_get_for_read<float3>(
+ input_name, result_domain, float3(0.0f));
+ MutableSpan<float3> results = attribute_result.as_span<float3>();
+ parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ BKE_curvemapping_evaluate3F(cumap, results[i], attribute_in[i]);
+ }
+ });
+ break;
+ }
+ case CD_PROP_COLOR: {
+ const CurveMapping *cumap = (CurveMapping *)node_storage.curve_rgb;
+ GVArray_Typed<ColorGeometry4f> attribute_in =
+ component.attribute_get_for_read<ColorGeometry4f>(
+ input_name, result_domain, ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f));
+ MutableSpan<ColorGeometry4f> results = attribute_result.as_span<ColorGeometry4f>();
+ parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ BKE_curvemapping_evaluateRGBF(cumap, results[i], attribute_in[i]);
+ }
+ });
+ break;
+ }
+ default: {
+ BLI_assert_unreachable();
+ break;
+ }
+ }
+
+ attribute_result.save();
+}
+
+static void geo_node_attribute_curve_map_exec(GeoNodeExecParams params)
+{
+ const bNode &bnode = params.node();
+ NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)bnode.storage;
+ BKE_curvemapping_init(data->curve_vec);
+ BKE_curvemapping_init(data->curve_rgb);
+
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ geometry_set = geometry_set_realize_instances(geometry_set);
+
+ if (geometry_set.has<MeshComponent>()) {
+ execute_on_component(params, geometry_set.get_component_for_write<MeshComponent>());
+ }
+ if (geometry_set.has<PointCloudComponent>()) {
+ execute_on_component(params, geometry_set.get_component_for_write<PointCloudComponent>());
+ }
+ if (geometry_set.has<CurveComponent>()) {
+ execute_on_component(params, geometry_set.get_component_for_write<CurveComponent>());
+ }
+
+ params.set_output("Geometry", std::move(geometry_set));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_attribute_curve_map()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_ATTRIBUTE_CURVE_MAP, "Attribute Curve Map", NODE_CLASS_ATTRIBUTE, 0);
+ node_type_socket_templates(
+ &ntype, geo_node_attribute_curve_map_in, geo_node_attribute_curve_map_out);
+ node_type_update(&ntype, geo_node_attribute_curve_map_update);
+ node_type_init(&ntype, geo_node_attribute_curve_map_init);
+ node_type_size_preset(&ntype, NODE_SIZE_LARGE);
+ node_type_storage(&ntype,
+ "NodeAttributeCurveMap",
+ geo_node_attribute_curve_map_free_storage,
+ geo_node_attribute_curve_map_copy_storage);
+ ntype.geometry_node_execute = blender::nodes::geo_node_attribute_curve_map_exec;
+ ntype.draw_buttons = geo_node_attribute_curve_map_layout;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc
index cbe44c54fb4..51fd65f65fd 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc
@@ -27,6 +27,11 @@
#include "WM_types.h"
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
static bNodeSocketTemplate geo_node_attribute_fill_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("Attribute")},
@@ -188,13 +193,12 @@ static void geo_node_attribute_fill_layout(uiLayout *layout, bContext *C, Pointe
draw_input_socket(C, layout, node_ptr, "Value", "data_type");
}
-static AttributeDomain get_result_domain(const GeometryComponent &component,
- StringRef attribute_name)
+static AttributeDomain get_result_domain(const GeometryComponent &component, const StringRef name)
{
/* Use the domain of the result attribute if it already exists. */
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(attribute_name);
- if (result_attribute) {
- return result_attribute->domain();
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(name);
+ if (result_info) {
+ return result_info->domain;
}
return ATTR_DOMAIN_POINT;
}
@@ -213,7 +217,7 @@ static void fill_attribute(GeometryComponent &component, const GeoNodeExecParams
get_result_domain(component, attribute_name) :
domain;
- OutputAttributePtr attribute = component.attribute_try_get_for_output(
+ OutputAttribute attribute = component.attribute_try_get_for_output_only(
attribute_name, result_domain, data_type);
if (!attribute) {
return;
@@ -222,38 +226,34 @@ static void fill_attribute(GeometryComponent &component, const GeoNodeExecParams
switch (data_type) {
case CD_PROP_FLOAT: {
const float value = params.get_input<float>("Value_001");
- MutableSpan<float> attribute_span = attribute->get_span_for_write_only<float>();
- attribute_span.fill(value);
+ attribute->fill(&value);
break;
}
case CD_PROP_FLOAT3: {
const float3 value = params.get_input<float3>("Value");
- MutableSpan<float3> attribute_span = attribute->get_span_for_write_only<float3>();
- attribute_span.fill(value);
+ attribute->fill(&value);
break;
}
case CD_PROP_COLOR: {
- const Color4f value = params.get_input<Color4f>("Value_002");
- MutableSpan<Color4f> attribute_span = attribute->get_span_for_write_only<Color4f>();
- attribute_span.fill(value);
+ const ColorGeometry4f value = params.get_input<ColorGeometry4f>("Value_002");
+ attribute->fill(&value);
break;
}
case CD_PROP_BOOL: {
const bool value = params.get_input<bool>("Value_003");
- MutableSpan<bool> attribute_span = attribute->get_span_for_write_only<bool>();
- attribute_span.fill(value);
+ attribute->fill(&value);
break;
}
case CD_PROP_INT32: {
const int value = params.get_input<int>("Value_004");
- MutableSpan<int> attribute_span = attribute->get_span_for_write_only<int>();
- attribute_span.fill(value);
+ attribute->fill(&value);
+ break;
}
default:
break;
}
- attribute.apply_span_and_save();
+ attribute.save();
}
static void geo_node_attribute_fill_exec(GeoNodeExecParams params)
@@ -268,6 +268,9 @@ static void geo_node_attribute_fill_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
fill_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ fill_attribute(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc
new file mode 100644
index 00000000000..40fe675bd6c
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc
@@ -0,0 +1,437 @@
+/*
+ * 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.
+ */
+
+#include "BLI_math_base_safe.h"
+#include "BLI_task.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_attribute_map_range_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_STRING, N_("Attribute")},
+ {SOCK_STRING, N_("Result")},
+ {SOCK_FLOAT, N_("From Min"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_FLOAT, N_("From Max"), 1.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_FLOAT, N_("To Min"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_FLOAT, N_("To Max"), 1.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_FLOAT, N_("Steps"), 4.0f, 4.0f, 4.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_VECTOR, N_("From Min"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_VECTOR, N_("From Max"), 1.0f, 1.0f, 1.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_VECTOR, N_("To Min"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_VECTOR, N_("To Max"), 1.0f, 1.0f, 1.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_VECTOR, N_("Steps"), 4.0f, 4.0f, 4.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_BOOLEAN, N_("Clamp")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_attribute_map_range_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+static void fn_attribute_map_range_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
+ uiItemR(layout, ptr, "interpolation_type", 0, "", ICON_NONE);
+}
+
+static void geo_node_attribute_map_range_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeAttributeMapRange *data = (NodeAttributeMapRange *)MEM_callocN(sizeof(NodeAttributeMapRange),
+ __func__);
+ data->data_type = CD_PROP_FLOAT;
+ data->interpolation_type = NODE_MAP_RANGE_LINEAR;
+
+ node->storage = data;
+}
+static void geo_node_attribute_map_range_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeAttributeMapRange &node_storage = *(NodeAttributeMapRange *)node->storage;
+
+ bNodeSocket *sock_from_min_float = (bNodeSocket *)BLI_findlink(&node->inputs, 3);
+ bNodeSocket *sock_from_max_float = sock_from_min_float->next;
+ bNodeSocket *sock_to_min_float = sock_from_max_float->next;
+ bNodeSocket *sock_to_max_float = sock_to_min_float->next;
+ bNodeSocket *sock_steps_float = sock_to_max_float->next;
+
+ bNodeSocket *sock_from_min_vector = sock_steps_float->next;
+ bNodeSocket *sock_from_max_vector = sock_from_min_vector->next;
+ bNodeSocket *sock_to_min_vector = sock_from_max_vector->next;
+ bNodeSocket *sock_to_max_vector = sock_to_min_vector->next;
+ bNodeSocket *sock_steps_vector = sock_to_max_vector->next;
+
+ bNodeSocket *sock_clamp = sock_steps_vector->next;
+
+ const CustomDataType data_type = static_cast<CustomDataType>(node_storage.data_type);
+
+ nodeSetSocketAvailability(sock_clamp,
+ node_storage.interpolation_type == NODE_MAP_RANGE_LINEAR ||
+ node_storage.interpolation_type == NODE_MAP_RANGE_STEPPED);
+
+ nodeSetSocketAvailability(sock_from_min_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(sock_from_max_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(sock_to_min_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(sock_to_max_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(sock_steps_float,
+ data_type == CD_PROP_FLOAT &&
+ node_storage.interpolation_type == NODE_MAP_RANGE_STEPPED);
+
+ nodeSetSocketAvailability(sock_from_min_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(sock_from_max_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(sock_to_min_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(sock_to_max_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(sock_steps_vector,
+ data_type == CD_PROP_FLOAT3 &&
+ node_storage.interpolation_type == NODE_MAP_RANGE_STEPPED);
+}
+
+namespace blender::nodes {
+
+static float map_linear(const float value,
+ const float min_from,
+ const float max_from,
+ const float min_to,
+ const float max_to)
+{
+ /* First we calculate a fraction that measures how far along
+ * the [min_from, max_from] interval the value lies.
+ *
+ * value
+ * min_from [------>|------------------------] max_from
+ * factor (e.g. 0.25)
+ *
+ * Then to find where the value is mapped, we add the same fraction
+ * of the [min_to, max_to] interval to min_to.
+ *
+ * min_to [--->|-----------] max_to
+ * v
+ * min_to + (max_to - min_to) * factor
+ */
+ const float factor = safe_divide(value - min_from, max_from - min_from);
+ return min_to + factor * (max_to - min_to);
+}
+
+static float map_stepped(const float value,
+ const float min_from,
+ const float max_from,
+ const float min_to,
+ const float max_to,
+ const float steps)
+{
+ /* First the factor is calculated here in the same way as for the linear mapping.
+ *
+ * Then the factor is mapped to multiples of 1.0 / steps.
+ * This is best understood with a few examples. Assume steps == 3.
+ * ____________________________________
+ * | factor | * 4.0 | floor() | / 3.0 |
+ * |--------|-------|---------|-------|
+ * | 0.0 | 0.0 | 0.0 | 0.0 |
+ * | 0.1 | 0.4 | 0.0 | 0.0 |
+ * | 0.25 | 1.0 | 1.0 | 0.333 |
+ * | 0.45 | 1.8 | 1.0 | 0.333 |
+ * | 0.5 | 2.0 | 2.0 | 0.666 |
+ * | 0.55 | 2.2 | 2.0 | 0.666 |
+ * | 0.999 | 3.999 | 3.0 | 1.0 |
+ * | 1.0 | 4.0 | 4.0 | 1.333 |
+ * ------------------------------------
+ * Note that the factor is not always mapped the closest multiple of 1.0 /steps.
+ */
+ const float factor = safe_divide(value - min_from, max_from - min_from);
+ const float factor_mapped = safe_divide(floorf(factor * (steps + 1.0f)), steps);
+ return min_to + factor_mapped * (max_to - min_to);
+}
+
+static float smoothstep_polynomial(float x)
+{
+ /* This polynomial is only meant to be used for the [0, 1] range. */
+ return (3.0f - 2.0f * x) * (x * x);
+}
+
+static float map_smoothstep(const float value,
+ const float min_from,
+ const float max_from,
+ const float min_to,
+ const float max_to)
+{
+ const float factor = safe_divide(value - min_from, max_from - min_from);
+ const float factor_clamped = std::clamp(factor, 0.0f, 1.0f);
+ const float factor_mapped = smoothstep_polynomial(factor_clamped);
+ return min_to + factor_mapped * (max_to - min_to);
+}
+
+static float smootherstep_polynomial(float x)
+{
+ /* This polynomial is only meant to be used for the [0, 1] range. */
+ return x * x * x * (x * (x * 6.0f - 15.0f) + 10.0f);
+}
+
+static float map_smootherstep(const float value,
+ const float min_from,
+ const float max_from,
+ const float min_to,
+ const float max_to)
+{
+ const float factor = safe_divide(value - min_from, max_from - min_from);
+ const float factor_clamped = std::clamp(factor, 0.0f, 1.0f);
+ const float factor_mapped = smootherstep_polynomial(factor_clamped);
+ return min_to + factor_mapped * (max_to - min_to);
+}
+
+static void map_range_float(const VArray<float> &attribute_input,
+ MutableSpan<float> results,
+ const GeoNodeExecParams &params)
+{
+ const bNode &node = params.node();
+ NodeAttributeMapRange &node_storage = *(NodeAttributeMapRange *)node.storage;
+ const int interpolation_type = node_storage.interpolation_type;
+ const float min_from = params.get_input<float>("From Min");
+ const float max_from = params.get_input<float>("From Max");
+ const float min_to = params.get_input<float>("To Min");
+ const float max_to = params.get_input<float>("To Max");
+
+ VArray_Span<float> span{attribute_input};
+
+ switch (interpolation_type) {
+ case NODE_MAP_RANGE_LINEAR: {
+ parallel_for(span.index_range(), 2048, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i] = map_linear(span[i], min_from, max_from, min_to, max_to);
+ }
+ });
+ break;
+ }
+ case NODE_MAP_RANGE_STEPPED: {
+ const float steps = params.get_input<float>("Steps");
+ parallel_for(span.index_range(), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i] = map_stepped(span[i], min_from, max_from, min_to, max_to, steps);
+ }
+ });
+ break;
+ }
+ case NODE_MAP_RANGE_SMOOTHSTEP: {
+ parallel_for(span.index_range(), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i] = map_smoothstep(span[i], min_from, max_from, min_to, max_to);
+ }
+ });
+ break;
+ }
+ case NODE_MAP_RANGE_SMOOTHERSTEP: {
+ parallel_for(span.index_range(), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i] = map_smootherstep(span[i], min_from, max_from, min_to, max_to);
+ }
+ });
+ break;
+ }
+ }
+
+ if (ELEM(interpolation_type, NODE_MAP_RANGE_LINEAR, NODE_MAP_RANGE_STEPPED) &&
+ params.get_input<bool>("Clamp")) {
+ /* Users can specify min_to > max_to, but clamping expects min < max. */
+ const float clamp_min = min_to < max_to ? min_to : max_to;
+ const float clamp_max = min_to < max_to ? max_to : min_to;
+
+ parallel_for(results.index_range(), 2048, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i] = std::clamp(results[i], clamp_min, clamp_max);
+ }
+ });
+ }
+}
+
+static void map_range_float3(const VArray<float3> &attribute_input,
+ const MutableSpan<float3> results,
+ const GeoNodeExecParams &params)
+{
+ const bNode &node = params.node();
+ NodeAttributeMapRange &node_storage = *(NodeAttributeMapRange *)node.storage;
+ const int interpolation_type = node_storage.interpolation_type;
+ const float3 min_from = params.get_input<float3>("From Min_001");
+ const float3 max_from = params.get_input<float3>("From Max_001");
+ const float3 min_to = params.get_input<float3>("To Min_001");
+ const float3 max_to = params.get_input<float3>("To Max_001");
+
+ VArray_Span<float3> span{attribute_input};
+
+ switch (interpolation_type) {
+ case NODE_MAP_RANGE_LINEAR: {
+ parallel_for(span.index_range(), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i].x = map_linear(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x);
+ results[i].y = map_linear(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y);
+ results[i].z = map_linear(span[i].z, min_from.z, max_from.z, min_to.z, max_to.z);
+ }
+ });
+ break;
+ }
+ case NODE_MAP_RANGE_STEPPED: {
+ const float3 steps = params.get_input<float3>("Steps_001");
+ parallel_for(span.index_range(), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i].x = map_stepped(
+ span[i].x, min_from.x, max_from.x, min_to.x, max_to.x, steps.x);
+ results[i].y = map_stepped(
+ span[i].y, min_from.y, max_from.y, min_to.y, max_to.y, steps.y);
+ results[i].z = map_stepped(
+ span[i].z, min_from.z, max_from.z, min_to.z, max_to.z, steps.z);
+ }
+ });
+ break;
+ }
+ case NODE_MAP_RANGE_SMOOTHSTEP: {
+ parallel_for(span.index_range(), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i].x = map_smoothstep(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x);
+ results[i].y = map_smoothstep(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y);
+ results[i].z = map_smoothstep(span[i].z, min_from.z, max_from.z, min_to.z, max_to.z);
+ }
+ });
+ break;
+ }
+ case NODE_MAP_RANGE_SMOOTHERSTEP: {
+ parallel_for(span.index_range(), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i].x = map_smootherstep(span[i].x, min_from.x, max_from.x, min_to.x, max_to.x);
+ results[i].y = map_smootherstep(span[i].y, min_from.y, max_from.y, min_to.y, max_to.y);
+ results[i].z = map_smootherstep(span[i].z, min_from.z, max_from.z, min_to.z, max_to.z);
+ }
+ });
+ break;
+ }
+ }
+
+ if (ELEM(interpolation_type, NODE_MAP_RANGE_LINEAR, NODE_MAP_RANGE_STEPPED) &&
+ params.get_input<bool>("Clamp")) {
+ /* Users can specify min_to > max_to, but clamping expects min < max. */
+ float3 clamp_min;
+ float3 clamp_max;
+ clamp_min.x = min_to.x < max_to.x ? min_to.x : max_to.x;
+ clamp_max.x = min_to.x < max_to.x ? max_to.x : min_to.x;
+ clamp_min.y = min_to.y < max_to.y ? min_to.y : max_to.y;
+ clamp_max.y = min_to.y < max_to.y ? max_to.y : min_to.y;
+ clamp_min.z = min_to.z < max_to.z ? min_to.z : max_to.z;
+ clamp_max.z = min_to.z < max_to.z ? max_to.z : min_to.z;
+
+ for (int i : results.index_range()) {
+ clamp_v3_v3v3(results[i], clamp_min, clamp_max);
+ }
+ }
+}
+
+static AttributeDomain get_result_domain(const GeometryComponent &component,
+ StringRef source_name,
+ StringRef result_name)
+{
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
+ if (result_info) {
+ return result_info->domain;
+ }
+ std::optional<AttributeMetaData> source_info = component.attribute_get_meta_data(source_name);
+ if (source_info) {
+ return source_info->domain;
+ }
+ return ATTR_DOMAIN_POINT;
+}
+
+static void map_range_attribute(GeometryComponent &component, const GeoNodeExecParams &params)
+{
+ const std::string input_name = params.get_input<std::string>("Attribute");
+ const std::string result_name = params.get_input<std::string>("Result");
+
+ if (input_name.empty() || result_name.empty()) {
+ return;
+ }
+
+ const bNode &node = params.node();
+ NodeAttributeMapRange &node_storage = *(NodeAttributeMapRange *)node.storage;
+ const CustomDataType data_type = static_cast<CustomDataType>(node_storage.data_type);
+
+ const AttributeDomain domain = get_result_domain(component, input_name, result_name);
+
+ GVArrayPtr attribute_input = component.attribute_try_get_for_read(input_name, domain, data_type);
+
+ if (!attribute_input) {
+ params.error_message_add(NodeWarningType::Error,
+ TIP_("No attribute with name \"") + input_name + "\"");
+ return;
+ }
+
+ OutputAttribute attribute_result = component.attribute_try_get_for_output_only(
+ result_name, domain, data_type);
+ if (!attribute_result) {
+ params.error_message_add(NodeWarningType::Error,
+ TIP_("Could not find or create attribute with name \"") +
+ result_name + "\"");
+ return;
+ }
+
+ switch (data_type) {
+ case CD_PROP_FLOAT: {
+ map_range_float(attribute_input->typed<float>(), attribute_result.as_span<float>(), params);
+ break;
+ }
+ case CD_PROP_FLOAT3: {
+ map_range_float3(
+ attribute_input->typed<float3>(), attribute_result.as_span<float3>(), params);
+ break;
+ }
+ default:
+ BLI_assert_unreachable();
+ }
+
+ attribute_result.save();
+}
+
+static void geo_node_attribute_map_range_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ if (geometry_set.has<MeshComponent>()) {
+ map_range_attribute(geometry_set.get_component_for_write<MeshComponent>(), params);
+ }
+ if (geometry_set.has<PointCloudComponent>()) {
+ map_range_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params);
+ }
+ if (geometry_set.has<CurveComponent>()) {
+ map_range_attribute(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
+
+ params.set_output("Geometry", geometry_set);
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_attribute_map_range()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_ATTRIBUTE_MAP_RANGE, "Attribute Map Range", NODE_CLASS_ATTRIBUTE, 0);
+ node_type_socket_templates(
+ &ntype, geo_node_attribute_map_range_in, geo_node_attribute_map_range_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_attribute_map_range_exec;
+ node_type_init(&ntype, geo_node_attribute_map_range_init);
+ node_type_update(&ntype, geo_node_attribute_map_range_update);
+ node_type_storage(
+ &ntype, "NodeAttributeMapRange", node_free_standard_storage, node_copy_standard_storage);
+ ntype.draw_buttons = fn_attribute_map_range_layout;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc
index 8c6d49e322c..5b78b4cd39e 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc
@@ -14,23 +14,15 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "node_geometry_util.hh"
-
-#include "BKE_attribute.h"
-#include "BKE_attribute_access.hh"
-
-#include "BLI_array.hh"
-#include "BLI_math_base_safe.h"
-#include "BLI_rand.hh"
-
-#include "DNA_mesh_types.h"
-#include "DNA_pointcloud_types.h"
+#include "BLI_task.hh"
#include "UI_interface.h"
#include "UI_resources.h"
#include "NOD_math_functions.hh"
+#include "node_geometry_util.hh"
+
static bNodeSocketTemplate geo_node_attribute_math_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("A")},
@@ -163,46 +155,52 @@ static void geo_node_attribute_math_update(bNodeTree *UNUSED(ntree), bNode *node
operation_use_input_c(operation));
}
-static void do_math_operation(Span<float> span_a,
- Span<float> span_b,
- Span<float> span_c,
+static void do_math_operation(const VArray<float> &span_a,
+ const VArray<float> &span_b,
+ const VArray<float> &span_c,
MutableSpan<float> span_result,
const NodeMathOperation operation)
{
bool success = try_dispatch_float_math_fl_fl_fl_to_fl(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(span_result.size())) {
- span_result[i] = math_function(span_a[i], span_b[i], span_c[i]);
- }
+ parallel_for(IndexRange(span_result.size()), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ span_result[i] = math_function(span_a[i], span_b[i], span_c[i]);
+ }
+ });
});
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
}
-static void do_math_operation(Span<float> span_a,
- Span<float> span_b,
+static void do_math_operation(const VArray<float> &span_a,
+ const VArray<float> &span_b,
MutableSpan<float> span_result,
const NodeMathOperation operation)
{
bool success = try_dispatch_float_math_fl_fl_to_fl(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(span_result.size())) {
- span_result[i] = math_function(span_a[i], span_b[i]);
- }
+ parallel_for(IndexRange(span_result.size()), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ span_result[i] = math_function(span_a[i], span_b[i]);
+ }
+ });
});
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
}
-static void do_math_operation(Span<float> span_input,
+static void do_math_operation(const VArray<float> &span_input,
MutableSpan<float> span_result,
const NodeMathOperation operation)
{
bool success = try_dispatch_float_math_fl_to_fl(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(span_result.size())) {
- span_result[i] = math_function(span_input[i]);
- }
+ parallel_for(IndexRange(span_result.size()), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ span_result[i] = math_function(span_input[i]);
+ }
+ });
});
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
@@ -214,9 +212,9 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
StringRef result_name)
{
/* Use the domain of the result attribute if it already exists. */
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
- if (result_attribute) {
- return result_attribute->domain();
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
+ if (result_info) {
+ return result_info->domain;
}
/* Otherwise use the highest priority domain from existing input attributes, or the default. */
@@ -238,56 +236,39 @@ static void attribute_math_calc(GeometryComponent &component, const GeoNodeExecP
const std::string result_name = params.get_input<std::string>("Result");
/* The result type of this node is always float. */
- const CustomDataType result_type = CD_PROP_FLOAT;
const AttributeDomain result_domain = get_result_domain(
component, params, operation, result_name);
- OutputAttributePtr attribute_result = component.attribute_try_get_for_output(
- result_name, result_domain, result_type);
+ OutputAttribute_Typed<float> attribute_result =
+ component.attribute_try_get_for_output_only<float>(result_name, result_domain);
if (!attribute_result) {
return;
}
- ReadAttributePtr attribute_a = params.get_input_attribute(
- "A", component, result_domain, result_type, nullptr);
- if (!attribute_a) {
- return;
- }
+ GVArray_Typed<float> attribute_a = params.get_input_attribute<float>(
+ "A", component, result_domain, 0.0f);
+
+ MutableSpan<float> result_span = attribute_result.as_span();
- /* Note that passing the data with `get_span<float>()` works
+ /* Note that passing the data with `get_internal_span<float>()` works
* because the attributes were accessed with #CD_PROP_FLOAT. */
if (operation_use_input_b(operation)) {
- ReadAttributePtr attribute_b = params.get_input_attribute(
- "B", component, result_domain, result_type, nullptr);
- if (!attribute_b) {
- return;
- }
+ GVArray_Typed<float> attribute_b = params.get_input_attribute<float>(
+ "B", component, result_domain, 0.0f);
if (operation_use_input_c(operation)) {
- ReadAttributePtr attribute_c = params.get_input_attribute(
- "C", component, result_domain, result_type, nullptr);
- if (!attribute_c) {
- return;
- }
- do_math_operation(attribute_a->get_span<float>(),
- attribute_b->get_span<float>(),
- attribute_c->get_span<float>(),
- attribute_result->get_span_for_write_only<float>(),
- operation);
+ GVArray_Typed<float> attribute_c = params.get_input_attribute<float>(
+ "C", component, result_domain, 0.0f);
+ do_math_operation(attribute_a, attribute_b, attribute_c, result_span, operation);
}
else {
- do_math_operation(attribute_a->get_span<float>(),
- attribute_b->get_span<float>(),
- attribute_result->get_span_for_write_only<float>(),
- operation);
+ do_math_operation(attribute_a, attribute_b, result_span, operation);
}
}
else {
- do_math_operation(attribute_a->get_span<float>(),
- attribute_result->get_span_for_write_only<float>(),
- operation);
+ do_math_operation(attribute_a, result_span, operation);
}
- attribute_result.apply_span_and_save();
+ attribute_result.save();
}
static void geo_node_attribute_math_exec(GeoNodeExecParams params)
@@ -302,6 +283,9 @@ static void geo_node_attribute_math_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
attribute_math_calc(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ attribute_math_calc(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc
index 9d8cd3dfa82..a6bd6c0ee32 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc
@@ -14,6 +14,8 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#include "BLI_task.hh"
+
#include "BKE_material.h"
#include "DNA_material_types.h"
@@ -57,73 +59,110 @@ static void geo_node_attribute_mix_layout(uiLayout *layout, bContext *UNUSED(C),
namespace blender::nodes {
+static void geo_node_attribute_mix_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeAttributeMix *data = (NodeAttributeMix *)MEM_callocN(sizeof(NodeAttributeMix),
+ "attribute mix node");
+ data->blend_type = MA_RAMP_BLEND;
+ data->input_type_factor = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
+ data->input_type_a = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE;
+ data->input_type_b = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE;
+ node->storage = data;
+}
+
+static void geo_node_attribute_mix_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeAttributeMix *node_storage = (NodeAttributeMix *)node->storage;
+ update_attribute_input_socket_availabilities(
+ *node, "Factor", (GeometryNodeAttributeInputMode)node_storage->input_type_factor);
+ update_attribute_input_socket_availabilities(
+ *node, "A", (GeometryNodeAttributeInputMode)node_storage->input_type_a);
+ update_attribute_input_socket_availabilities(
+ *node, "B", (GeometryNodeAttributeInputMode)node_storage->input_type_b);
+}
+
static void do_mix_operation_float(const int blend_mode,
- const FloatReadAttribute &factors,
- const FloatReadAttribute &inputs_a,
- const FloatReadAttribute &inputs_b,
- FloatWriteAttribute results)
+ const VArray<float> &factors,
+ const VArray<float> &inputs_a,
+ const VArray<float> &inputs_b,
+ VMutableArray<float> &results)
{
const int size = results.size();
- for (const int i : IndexRange(size)) {
- const float factor = factors[i];
- float3 a{inputs_a[i]};
- const float3 b{inputs_b[i]};
- ramp_blend(blend_mode, a, factor, b);
- const float result = a.x;
- results.set(i, result);
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const float factor = factors[i];
+ float3 a{inputs_a[i]};
+ const float3 b{inputs_b[i]};
+ ramp_blend(blend_mode, a, factor, b);
+ const float result = a.x;
+ results.set(i, result);
+ }
+ });
}
static void do_mix_operation_float3(const int blend_mode,
- const FloatReadAttribute &factors,
- const Float3ReadAttribute &inputs_a,
- const Float3ReadAttribute &inputs_b,
- Float3WriteAttribute results)
+ const VArray<float> &factors,
+ const VArray<float3> &inputs_a,
+ const VArray<float3> &inputs_b,
+ VMutableArray<float3> &results)
{
const int size = results.size();
- for (const int i : IndexRange(size)) {
- const float factor = factors[i];
- float3 a = inputs_a[i];
- const float3 b = inputs_b[i];
- ramp_blend(blend_mode, a, factor, b);
- results.set(i, a);
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const float factor = factors[i];
+ float3 a = inputs_a[i];
+ const float3 b = inputs_b[i];
+ ramp_blend(blend_mode, a, factor, b);
+ results.set(i, a);
+ }
+ });
}
static void do_mix_operation_color4f(const int blend_mode,
- const FloatReadAttribute &factors,
- const Color4fReadAttribute &inputs_a,
- const Color4fReadAttribute &inputs_b,
- Color4fWriteAttribute results)
+ const VArray<float> &factors,
+ const VArray<ColorGeometry4f> &inputs_a,
+ const VArray<ColorGeometry4f> &inputs_b,
+ VMutableArray<ColorGeometry4f> &results)
{
const int size = results.size();
- for (const int i : IndexRange(size)) {
- const float factor = factors[i];
- Color4f a = inputs_a[i];
- const Color4f b = inputs_b[i];
- ramp_blend(blend_mode, a, factor, b);
- results.set(i, a);
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const float factor = factors[i];
+ ColorGeometry4f a = inputs_a[i];
+ const ColorGeometry4f b = inputs_b[i];
+ ramp_blend(blend_mode, a, factor, b);
+ results.set(i, a);
+ }
+ });
}
static void do_mix_operation(const CustomDataType result_type,
int blend_mode,
- const FloatReadAttribute &attribute_factor,
- const ReadAttribute &attribute_a,
- const ReadAttribute &attribute_b,
- WriteAttribute &attribute_result)
+ const VArray<float> &attribute_factor,
+ const GVArray &attribute_a,
+ const GVArray &attribute_b,
+ GVMutableArray &attribute_result)
{
if (result_type == CD_PROP_FLOAT) {
- do_mix_operation_float(
- blend_mode, attribute_factor, attribute_a, attribute_b, attribute_result);
+ do_mix_operation_float(blend_mode,
+ attribute_factor,
+ attribute_a.typed<float>(),
+ attribute_b.typed<float>(),
+ attribute_result.typed<float>());
}
else if (result_type == CD_PROP_FLOAT3) {
- do_mix_operation_float3(
- blend_mode, attribute_factor, attribute_a, attribute_b, attribute_result);
+ do_mix_operation_float3(blend_mode,
+ attribute_factor,
+ attribute_a.typed<float3>(),
+ attribute_b.typed<float3>(),
+ attribute_result.typed<float3>());
}
else if (result_type == CD_PROP_COLOR) {
- do_mix_operation_color4f(
- blend_mode, attribute_factor, attribute_a, attribute_b, attribute_result);
+ do_mix_operation_color4f(blend_mode,
+ attribute_factor,
+ attribute_a.typed<ColorGeometry4f>(),
+ attribute_b.typed<ColorGeometry4f>(),
+ attribute_result.typed<ColorGeometry4f>());
}
}
@@ -132,9 +171,9 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
StringRef result_name)
{
/* Use the domain of the result attribute if it already exists. */
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
- if (result_attribute) {
- return result_attribute->domain();
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
+ if (result_info) {
+ return result_info->domain;
}
/* Otherwise use the highest priority domain from existing input attributes, or the default. */
@@ -158,17 +197,17 @@ static void attribute_mix_calc(GeometryComponent &component, const GeoNodeExecPa
const AttributeDomain result_domain = get_result_domain(component, params, result_name);
- OutputAttributePtr attribute_result = component.attribute_try_get_for_output(
+ OutputAttribute attribute_result = component.attribute_try_get_for_output_only(
result_name, result_domain, result_type);
if (!attribute_result) {
return;
}
- FloatReadAttribute attribute_factor = params.get_input_attribute<float>(
+ GVArray_Typed<float> attribute_factor = params.get_input_attribute<float>(
"Factor", component, result_domain, 0.5f);
- ReadAttributePtr attribute_a = params.get_input_attribute(
+ GVArrayPtr attribute_a = params.get_input_attribute(
"A", component, result_domain, result_type, nullptr);
- ReadAttributePtr attribute_b = params.get_input_attribute(
+ GVArrayPtr attribute_b = params.get_input_attribute(
"B", component, result_domain, result_type, nullptr);
do_mix_operation(result_type,
@@ -192,32 +231,13 @@ static void geo_node_attribute_mix_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
attribute_mix_calc(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ attribute_mix_calc(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
-static void geo_node_attribute_mix_init(bNodeTree *UNUSED(ntree), bNode *node)
-{
- NodeAttributeMix *data = (NodeAttributeMix *)MEM_callocN(sizeof(NodeAttributeMix),
- "attribute mix node");
- data->blend_type = MA_RAMP_BLEND;
- data->input_type_factor = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
- data->input_type_a = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE;
- data->input_type_b = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE;
- node->storage = data;
-}
-
-static void geo_node_attribute_mix_update(bNodeTree *UNUSED(ntree), bNode *node)
-{
- NodeAttributeMix *node_storage = (NodeAttributeMix *)node->storage;
- update_attribute_input_socket_availabilities(
- *node, "Factor", (GeometryNodeAttributeInputMode)node_storage->input_type_factor);
- update_attribute_input_socket_availabilities(
- *node, "A", (GeometryNodeAttributeInputMode)node_storage->input_type_a);
- update_attribute_input_socket_availabilities(
- *node, "B", (GeometryNodeAttributeInputMode)node_storage->input_type_b);
-}
-
} // namespace blender::nodes
void register_node_type_geo_attribute_mix()
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc
index f09a9bf056e..9c22b7fa87f 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc
@@ -62,7 +62,7 @@ namespace blender::nodes {
static void proximity_calc(MutableSpan<float> distance_span,
MutableSpan<float3> location_span,
- Span<float3> positions,
+ const VArray<float3> &positions,
BVHTreeFromMesh &tree_data_mesh,
BVHTreeFromPointCloud &tree_data_pointcloud,
const bool bvh_mesh_success,
@@ -169,19 +169,18 @@ static void attribute_calc_proximity(GeometryComponent &component,
const AttributeDomain result_domain = ATTR_DOMAIN_POINT;
const std::string distance_attribute_name = params.get_input<std::string>("Distance");
- OutputAttributePtr distance_attribute = component.attribute_try_get_for_output(
- distance_attribute_name, result_domain, CD_PROP_FLOAT);
+ OutputAttribute_Typed<float> distance_attribute =
+ component.attribute_try_get_for_output_only<float>(distance_attribute_name, result_domain);
const std::string location_attribute_name = params.get_input<std::string>("Position");
- OutputAttributePtr location_attribute = component.attribute_try_get_for_output(
- location_attribute_name, result_domain, CD_PROP_FLOAT3);
-
- ReadAttributePtr position_attribute = component.attribute_try_get_for_read("position");
- BLI_assert(position_attribute->custom_data_type() == CD_PROP_FLOAT3);
+ OutputAttribute_Typed<float3> location_attribute =
+ component.attribute_try_get_for_output_only<float3>(location_attribute_name, result_domain);
+ ReadAttributeLookup position_attribute = component.attribute_try_get_for_read("position");
if (!position_attribute || (!distance_attribute && !location_attribute)) {
return;
}
+ BLI_assert(position_attribute.varray->type().is<float3>());
const bNode &node = params.node();
const NodeGeometryAttributeProximity &storage = *(const NodeGeometryAttributeProximity *)
@@ -204,18 +203,15 @@ static void attribute_calc_proximity(GeometryComponent &component,
tree_data_pointcloud);
}
- Span<float3> position_span = position_attribute->get_span<float3>();
-
- MutableSpan<float> distance_span = distance_attribute ?
- distance_attribute->get_span_for_write_only<float>() :
- MutableSpan<float>();
- MutableSpan<float3> location_span = location_attribute ?
- location_attribute->get_span_for_write_only<float3>() :
- MutableSpan<float3>();
+ GVArray_Typed<float3> positions{*position_attribute.varray};
+ MutableSpan<float> distance_span = distance_attribute ? distance_attribute.as_span() :
+ MutableSpan<float>();
+ MutableSpan<float3> location_span = location_attribute ? location_attribute.as_span() :
+ MutableSpan<float3>();
proximity_calc(distance_span,
location_span,
- position_span,
+ positions,
tree_data_mesh,
tree_data_pointcloud,
bvh_mesh_success,
@@ -231,10 +227,10 @@ static void attribute_calc_proximity(GeometryComponent &component,
}
if (distance_attribute) {
- distance_attribute.apply_span_and_save();
+ distance_attribute.save();
}
if (location_attribute) {
- location_attribute.apply_span_and_save();
+ location_attribute.save();
}
}
@@ -257,6 +253,10 @@ static void geo_node_attribute_proximity_exec(GeoNodeExecParams params)
attribute_calc_proximity(
geometry_set.get_component_for_write<PointCloudComponent>(), geometry_set_target, params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ attribute_calc_proximity(
+ geometry_set.get_component_for_write<CurveComponent>(), geometry_set_target, params);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc
index 27c35da7d37..286411b7d28 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc
@@ -14,16 +14,14 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "node_geometry_util.hh"
-
#include "BLI_hash.h"
#include "BLI_rand.hh"
+#include "BLI_task.hh"
#include "UI_interface.h"
#include "UI_resources.h"
-#include "DNA_mesh_types.h"
-#include "DNA_pointcloud_types.h"
+#include "node_geometry_util.hh"
static bNodeSocketTemplate geo_node_attribute_randomize_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
@@ -128,28 +126,36 @@ static void randomize_attribute(MutableSpan<T> span,
/* The operations could be templated too, but it doesn't make the code much shorter. */
switch (operation) {
case GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE:
- for (const int i : span.index_range()) {
- const T random_value = random_value_in_range<T>(ids[i], seed, min, max);
- span[i] = random_value;
- }
+ parallel_for(span.index_range(), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const T random_value = random_value_in_range<T>(ids[i], seed, min, max);
+ span[i] = random_value;
+ }
+ });
break;
case GEO_NODE_ATTRIBUTE_RANDOMIZE_ADD:
- for (const int i : span.index_range()) {
- const T random_value = random_value_in_range<T>(ids[i], seed, min, max);
- span[i] = span[i] + random_value;
- }
+ parallel_for(span.index_range(), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const T random_value = random_value_in_range<T>(ids[i], seed, min, max);
+ span[i] = span[i] + random_value;
+ }
+ });
break;
case GEO_NODE_ATTRIBUTE_RANDOMIZE_SUBTRACT:
- for (const int i : span.index_range()) {
- const T random_value = random_value_in_range<T>(ids[i], seed, min, max);
- span[i] = span[i] - random_value;
- }
+ parallel_for(span.index_range(), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const T random_value = random_value_in_range<T>(ids[i], seed, min, max);
+ span[i] = span[i] - random_value;
+ }
+ });
break;
case GEO_NODE_ATTRIBUTE_RANDOMIZE_MULTIPLY:
- for (const int i : span.index_range()) {
- const T random_value = random_value_in_range<T>(ids[i], seed, min, max);
- span[i] = span[i] * random_value;
- }
+ parallel_for(span.index_range(), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const T random_value = random_value_in_range<T>(ids[i], seed, min, max);
+ span[i] = span[i] * random_value;
+ }
+ });
break;
default:
BLI_assert(false);
@@ -164,10 +170,12 @@ static void randomize_attribute_bool(MutableSpan<bool> span,
{
BLI_assert(operation == GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE);
UNUSED_VARS_NDEBUG(operation);
- for (const int i : span.index_range()) {
- const bool random_value = BLI_hash_int_2d_to_float(ids[i], seed) > 0.5f;
- span[i] = random_value;
- }
+ parallel_for(span.index_range(), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const bool random_value = BLI_hash_int_2d_to_float(ids[i], seed) > 0.5f;
+ span[i] = random_value;
+ }
+ });
}
Array<uint32_t> get_geometry_element_ids_as_uints(const GeometryComponent &component,
@@ -176,15 +184,17 @@ Array<uint32_t> get_geometry_element_ids_as_uints(const GeometryComponent &compo
const int domain_size = component.attribute_domain_size(domain);
/* Hash the reserved name attribute "id" as a (hopefully) stable seed for each point. */
- ReadAttributePtr hash_attribute = component.attribute_try_get_for_read("id", domain);
+ GVArrayPtr hash_attribute = component.attribute_try_get_for_read("id", domain);
Array<uint32_t> hashes(domain_size);
if (hash_attribute) {
BLI_assert(hashes.size() == hash_attribute->size());
- const CPPType &cpp_type = hash_attribute->cpp_type();
- fn::GSpan items = hash_attribute->get_span();
- for (const int i : hashes.index_range()) {
- hashes[i] = cpp_type.hash(items[i]);
- }
+ const CPPType &cpp_type = hash_attribute->type();
+ GVArray_GSpan items{*hash_attribute};
+ parallel_for(hashes.index_range(), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ hashes[i] = cpp_type.hash(items[i]);
+ }
+ });
}
else {
/* If there is no "id" attribute for per-point variation, just create it here. */
@@ -199,12 +209,12 @@ Array<uint32_t> get_geometry_element_ids_as_uints(const GeometryComponent &compo
static AttributeDomain get_result_domain(const GeometryComponent &component,
const GeoNodeExecParams &params,
- StringRef attribute_name)
+ const StringRef name)
{
/* Use the domain of the result attribute if it already exists. */
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(attribute_name);
- if (result_attribute) {
- return result_attribute->domain();
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(name);
+ if (result_info) {
+ return result_info->domain;
}
/* Otherwise use the input domain chosen in the interface. */
@@ -231,15 +241,13 @@ static void randomize_attribute_on_component(GeometryComponent &component,
const AttributeDomain domain = get_result_domain(component, params, attribute_name);
- OutputAttributePtr attribute = component.attribute_try_get_for_output(
+ OutputAttribute attribute = component.attribute_try_get_for_output(
attribute_name, domain, data_type);
if (!attribute) {
return;
}
- fn::GMutableSpan span = (operation == GEO_NODE_ATTRIBUTE_RANDOMIZE_REPLACE_CREATE) ?
- attribute->get_span_for_write_only() :
- attribute->get_span();
+ GMutableSpan span = attribute.as_span();
Array<uint32_t> hashes = get_geometry_element_ids_as_uints(component, domain);
@@ -272,8 +280,8 @@ static void randomize_attribute_on_component(GeometryComponent &component,
}
}
- attribute.apply_span_and_save();
-} // namespace blender::nodes
+ attribute.save();
+}
static void geo_node_random_attribute_exec(GeoNodeExecParams params)
{
@@ -307,6 +315,14 @@ static void geo_node_random_attribute_exec(GeoNodeExecParams params)
operation,
seed);
}
+ if (geometry_set.has<CurveComponent>()) {
+ randomize_attribute_on_component(geometry_set.get_component_for_write<CurveComponent>(),
+ params,
+ attribute_name,
+ data_type,
+ operation,
+ seed);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc
index 837f0c3629a..e4f3230ebb9 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc
@@ -70,6 +70,10 @@ static void geo_node_attribute_remove_exec(GeoNodeExecParams params)
remove_attribute(
geometry_set.get_component_for_write<PointCloudComponent>(), params, attribute_names);
}
+ if (geometry_set.has<CurveComponent>()) {
+ remove_attribute(
+ geometry_set.get_component_for_write<CurveComponent>(), params, attribute_names);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc
index d0b2595b5b9..d6b1ad3e9e0 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc
@@ -15,6 +15,7 @@
*/
#include "BLI_compiler_attrs.h"
+#include "BLI_task.hh"
#include "DNA_texture_types.h"
@@ -29,6 +30,7 @@
static bNodeSocketTemplate geo_node_attribute_sample_texture_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_TEXTURE, N_("Texture")},
{SOCK_STRING, N_("Mapping")},
{SOCK_STRING, N_("Result")},
{-1, ""},
@@ -39,29 +41,22 @@ static bNodeSocketTemplate geo_node_attribute_sample_texture_out[] = {
{-1, ""},
};
-static void geo_node_attribute_sample_texture_layout(uiLayout *layout,
- bContext *C,
- PointerRNA *ptr)
-{
- uiTemplateID(layout, C, ptr, "texture", "texture.new", nullptr, nullptr, 0, ICON_NONE, nullptr);
-}
-
namespace blender::nodes {
static AttributeDomain get_result_domain(const GeometryComponent &component,
- StringRef result_attribute_name,
- StringRef map_attribute_name)
+ const StringRef result_name,
+ const StringRef map_name)
{
/* Use the domain of the result attribute if it already exists. */
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_attribute_name);
- if (result_attribute) {
- return result_attribute->domain();
+ std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name);
+ if (result_info) {
+ return result_info->domain;
}
/* Otherwise use the name of the map attribute. */
- ReadAttributePtr map_attribute = component.attribute_try_get_for_read(map_attribute_name);
- if (map_attribute) {
- return map_attribute->domain();
+ std::optional<AttributeMetaData> map_info = component.attribute_get_meta_data(map_name);
+ if (map_info) {
+ return map_info->domain;
}
/* The node won't execute in this case, but we still have to return a value. */
@@ -70,8 +65,7 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
static void execute_on_component(GeometryComponent &component, const GeoNodeExecParams &params)
{
- const bNode &node = params.node();
- Tex *texture = reinterpret_cast<Tex *>(node.id);
+ Tex *texture = params.get_input<Tex *>("Texture");
if (texture == nullptr) {
return;
}
@@ -85,25 +79,29 @@ static void execute_on_component(GeometryComponent &component, const GeoNodeExec
const AttributeDomain result_domain = get_result_domain(
component, result_attribute_name, mapping_name);
- OutputAttributePtr attribute_out = component.attribute_try_get_for_output(
- result_attribute_name, result_domain, CD_PROP_COLOR);
+ OutputAttribute_Typed<ColorGeometry4f> attribute_out =
+ component.attribute_try_get_for_output_only<ColorGeometry4f>(result_attribute_name,
+ result_domain);
if (!attribute_out) {
return;
}
- Float3ReadAttribute mapping_attribute = component.attribute_get_for_read<float3>(
+ GVArray_Typed<float3> mapping_attribute = component.attribute_get_for_read<float3>(
mapping_name, result_domain, {0, 0, 0});
- MutableSpan<Color4f> colors = attribute_out->get_span<Color4f>();
- for (const int i : IndexRange(mapping_attribute.size())) {
- TexResult texture_result = {0};
- const float3 position = mapping_attribute[i];
- /* For legacy reasons we have to map [0, 1] to [-1, 1] to support uv mappings. */
- const float3 remapped_position = position * 2.0f - float3(1.0f);
- BKE_texture_get_value(nullptr, texture, remapped_position, &texture_result, false);
- colors[i] = {texture_result.tr, texture_result.tg, texture_result.tb, texture_result.ta};
- }
- attribute_out.apply_span_and_save();
+ MutableSpan<ColorGeometry4f> colors = attribute_out.as_span();
+ parallel_for(IndexRange(mapping_attribute.size()), 128, [&](IndexRange range) {
+ for (const int i : range) {
+ TexResult texture_result = {0};
+ const float3 position = mapping_attribute[i];
+ /* For legacy reasons we have to map [0, 1] to [-1, 1] to support uv mappings. */
+ const float3 remapped_position = position * 2.0f - float3(1.0f);
+ BKE_texture_get_value(nullptr, texture, remapped_position, &texture_result, false);
+ colors[i] = {texture_result.tr, texture_result.tg, texture_result.tb, texture_result.ta};
+ }
+ });
+
+ attribute_out.save();
}
static void geo_node_attribute_sample_texture_exec(GeoNodeExecParams params)
@@ -118,6 +116,9 @@ static void geo_node_attribute_sample_texture_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
execute_on_component(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ execute_on_component(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
@@ -137,6 +138,5 @@ void register_node_type_geo_sample_texture()
node_type_socket_templates(
&ntype, geo_node_attribute_sample_texture_in, geo_node_attribute_sample_texture_out);
ntype.geometry_node_execute = blender::nodes::geo_node_attribute_sample_texture_exec;
- ntype.draw_buttons = geo_node_attribute_sample_texture_layout;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc
index 55b933e8582..137a72bb707 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc
@@ -14,13 +14,11 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "DNA_material_types.h"
-
-#include "node_geometry_util.hh"
-
#include "UI_interface.h"
#include "UI_resources.h"
+#include "node_geometry_util.hh"
+
static bNodeSocketTemplate geo_node_attribute_separate_xyz_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("Vector")},
@@ -73,23 +71,23 @@ static void extract_input(const int index, const Span<float3> &input, MutableSpa
static AttributeDomain get_result_domain(const GeometryComponent &component,
const GeoNodeExecParams &params,
- StringRef result_name_x,
- StringRef result_name_y,
- StringRef result_name_z)
+ const StringRef name_x,
+ const StringRef name_y,
+ const StringRef name_z)
{
/* Use the highest priority domain from any existing attribute outputs. */
Vector<AttributeDomain, 3> output_domains;
- ReadAttributePtr attribute_x = component.attribute_try_get_for_read(result_name_x);
- ReadAttributePtr attribute_y = component.attribute_try_get_for_read(result_name_y);
- ReadAttributePtr attribute_z = component.attribute_try_get_for_read(result_name_z);
- if (attribute_x) {
- output_domains.append(attribute_x->domain());
+ std::optional<AttributeMetaData> info_x = component.attribute_get_meta_data(name_x);
+ std::optional<AttributeMetaData> info_y = component.attribute_get_meta_data(name_y);
+ std::optional<AttributeMetaData> info_z = component.attribute_get_meta_data(name_z);
+ if (info_x) {
+ output_domains.append(info_x->domain);
}
- if (attribute_y) {
- output_domains.append(attribute_y->domain());
+ if (info_y) {
+ output_domains.append(info_y->domain);
}
- if (attribute_z) {
- output_domains.append(attribute_z->domain());
+ if (info_z) {
+ output_domains.append(info_z->domain);
}
if (output_domains.size() > 0) {
return bke::attribute_domain_highest_priority(output_domains);
@@ -109,37 +107,32 @@ static void separate_attribute(GeometryComponent &component, const GeoNodeExecPa
}
/* The node is only for float3 to float conversions. */
- const CustomDataType input_type = CD_PROP_FLOAT3;
- const CustomDataType result_type = CD_PROP_FLOAT;
const AttributeDomain result_domain = get_result_domain(
component, params, result_name_x, result_name_y, result_name_z);
- ReadAttributePtr attribute_input = params.get_input_attribute(
- "Vector", component, result_domain, input_type, nullptr);
- if (!attribute_input) {
- return;
- }
- const Span<float3> input_span = attribute_input->get_span<float3>();
+ GVArray_Typed<float3> attribute_input = params.get_input_attribute<float3>(
+ "Vector", component, result_domain, {0, 0, 0});
+ VArray_Span<float3> input_span{*attribute_input};
- OutputAttributePtr attribute_result_x = component.attribute_try_get_for_output(
- result_name_x, result_domain, result_type);
- OutputAttributePtr attribute_result_y = component.attribute_try_get_for_output(
- result_name_y, result_domain, result_type);
- OutputAttributePtr attribute_result_z = component.attribute_try_get_for_output(
- result_name_z, result_domain, result_type);
+ OutputAttribute_Typed<float> attribute_result_x =
+ component.attribute_try_get_for_output_only<float>(result_name_x, result_domain);
+ OutputAttribute_Typed<float> attribute_result_y =
+ component.attribute_try_get_for_output_only<float>(result_name_y, result_domain);
+ OutputAttribute_Typed<float> attribute_result_z =
+ component.attribute_try_get_for_output_only<float>(result_name_z, result_domain);
/* Only extract the components for the outputs with a given attribute. */
if (attribute_result_x) {
- extract_input(0, input_span, attribute_result_x->get_span_for_write_only<float>());
- attribute_result_x.apply_span_and_save();
+ extract_input(0, input_span, attribute_result_x.as_span());
+ attribute_result_x.save();
}
if (attribute_result_y) {
- extract_input(1, input_span, attribute_result_y->get_span_for_write_only<float>());
- attribute_result_y.apply_span_and_save();
+ extract_input(1, input_span, attribute_result_y.as_span());
+ attribute_result_y.save();
}
if (attribute_result_z) {
- extract_input(2, input_span, attribute_result_z->get_span_for_write_only<float>());
- attribute_result_z.apply_span_and_save();
+ extract_input(2, input_span, attribute_result_z.as_span());
+ attribute_result_z.save();
}
}
@@ -155,6 +148,9 @@ static void geo_node_attribute_separate_xyz_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
separate_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ separate_attribute(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc
new file mode 100644
index 00000000000..4b677dc5c82
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc
@@ -0,0 +1,597 @@
+/*
+ * 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.
+ */
+
+#include "BLI_kdopbvh.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_pointcloud_types.h"
+
+#include "BKE_bvhutils.h"
+#include "BKE_mesh_runtime.h"
+#include "BKE_mesh_sample.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_attribute_transfer_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_GEOMETRY, N_("Source Geometry")},
+ {SOCK_STRING, N_("Source")},
+ {SOCK_STRING, N_("Destination")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_attribute_transfer_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+static void geo_node_attribute_transfer_layout(uiLayout *layout,
+ bContext *UNUSED(C),
+ PointerRNA *ptr)
+{
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+ uiItemR(layout, ptr, "domain", 0, IFACE_("Domain"), ICON_NONE);
+ uiItemR(layout, ptr, "mapping", 0, IFACE_("Mapping"), ICON_NONE);
+}
+
+namespace blender::nodes {
+
+static void geo_node_attribute_transfer_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryAttributeTransfer *data = (NodeGeometryAttributeTransfer *)MEM_callocN(
+ sizeof(NodeGeometryAttributeTransfer), __func__);
+ data->domain = ATTR_DOMAIN_AUTO;
+ node->storage = data;
+}
+
+static void get_result_domain_and_data_type(const GeometrySet &src_geometry,
+ const GeometryComponent &dst_component,
+ const StringRef attribute_name,
+ CustomDataType *r_data_type,
+ AttributeDomain *r_domain)
+{
+ Vector<CustomDataType> data_types;
+ Vector<AttributeDomain> domains;
+
+ const PointCloudComponent *pointcloud_component =
+ src_geometry.get_component_for_read<PointCloudComponent>();
+ if (pointcloud_component != nullptr) {
+ std::optional<AttributeMetaData> meta_data = pointcloud_component->attribute_get_meta_data(
+ attribute_name);
+ if (meta_data.has_value()) {
+ data_types.append(meta_data->data_type);
+ domains.append(meta_data->domain);
+ }
+ }
+
+ const MeshComponent *mesh_component = src_geometry.get_component_for_read<MeshComponent>();
+ if (mesh_component != nullptr) {
+ std::optional<AttributeMetaData> meta_data = mesh_component->attribute_get_meta_data(
+ attribute_name);
+ if (meta_data.has_value()) {
+ data_types.append(meta_data->data_type);
+ domains.append(meta_data->domain);
+ }
+ }
+
+ *r_data_type = bke::attribute_data_type_highest_complexity(data_types);
+
+ if (dst_component.type() == GEO_COMPONENT_TYPE_POINT_CLOUD) {
+ *r_domain = ATTR_DOMAIN_POINT;
+ }
+ else {
+ *r_domain = bke::attribute_domain_highest_priority(domains);
+ }
+}
+
+static Span<MLoopTri> get_mesh_looptris(const Mesh &mesh)
+{
+ /* This only updates a cache and can be considered to be logically const. */
+ const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(const_cast<Mesh *>(&mesh));
+ const int looptris_len = BKE_mesh_runtime_looptri_len(&mesh);
+ return {looptris, looptris_len};
+}
+
+static void get_closest_in_bvhtree(BVHTreeFromMesh &tree_data,
+ const VArray<float3> &positions,
+ const MutableSpan<int> r_indices,
+ const MutableSpan<float> r_distances_sq,
+ const MutableSpan<float3> r_positions)
+{
+ BLI_assert(positions.size() == r_indices.size() || r_indices.is_empty());
+ BLI_assert(positions.size() == r_distances_sq.size() || r_distances_sq.is_empty());
+ BLI_assert(positions.size() == r_positions.size() || r_positions.is_empty());
+
+ for (const int i : positions.index_range()) {
+ BVHTreeNearest nearest;
+ nearest.dist_sq = FLT_MAX;
+ const float3 position = positions[i];
+ BLI_bvhtree_find_nearest(
+ tree_data.tree, position, &nearest, tree_data.nearest_callback, &tree_data);
+ if (!r_indices.is_empty()) {
+ r_indices[i] = nearest.index;
+ }
+ if (!r_distances_sq.is_empty()) {
+ r_distances_sq[i] = nearest.dist_sq;
+ }
+ if (!r_positions.is_empty()) {
+ r_positions[i] = nearest.co;
+ }
+ }
+}
+
+static void get_closest_pointcloud_points(const PointCloud &pointcloud,
+ const VArray<float3> &positions,
+ const MutableSpan<int> r_indices,
+ const MutableSpan<float> r_distances_sq)
+{
+ BLI_assert(positions.size() == r_indices.size());
+ BLI_assert(pointcloud.totpoint > 0);
+
+ BVHTreeFromPointCloud tree_data;
+ BKE_bvhtree_from_pointcloud_get(&tree_data, &pointcloud, 2);
+
+ for (const int i : positions.index_range()) {
+ BVHTreeNearest nearest;
+ nearest.dist_sq = FLT_MAX;
+ const float3 position = positions[i];
+ BLI_bvhtree_find_nearest(
+ tree_data.tree, position, &nearest, tree_data.nearest_callback, &tree_data);
+ r_indices[i] = nearest.index;
+ r_distances_sq[i] = nearest.dist_sq;
+ }
+
+ free_bvhtree_from_pointcloud(&tree_data);
+}
+
+static void get_closest_mesh_points(const Mesh &mesh,
+ const VArray<float3> &positions,
+ const MutableSpan<int> r_point_indices,
+ const MutableSpan<float> r_distances_sq,
+ const MutableSpan<float3> r_positions)
+{
+ BLI_assert(mesh.totvert > 0);
+ BVHTreeFromMesh tree_data;
+ BKE_bvhtree_from_mesh_get(&tree_data, const_cast<Mesh *>(&mesh), BVHTREE_FROM_VERTS, 2);
+ get_closest_in_bvhtree(tree_data, positions, r_point_indices, r_distances_sq, r_positions);
+ free_bvhtree_from_mesh(&tree_data);
+}
+
+static void get_closest_mesh_edges(const Mesh &mesh,
+ const VArray<float3> &positions,
+ const MutableSpan<int> r_edge_indices,
+ const MutableSpan<float> r_distances_sq,
+ const MutableSpan<float3> r_positions)
+{
+ BLI_assert(mesh.totedge > 0);
+ BVHTreeFromMesh tree_data;
+ BKE_bvhtree_from_mesh_get(&tree_data, const_cast<Mesh *>(&mesh), BVHTREE_FROM_EDGES, 2);
+ get_closest_in_bvhtree(tree_data, positions, r_edge_indices, r_distances_sq, r_positions);
+ free_bvhtree_from_mesh(&tree_data);
+}
+
+static void get_closest_mesh_looptris(const Mesh &mesh,
+ const VArray<float3> &positions,
+ const MutableSpan<int> r_looptri_indices,
+ const MutableSpan<float> r_distances_sq,
+ const MutableSpan<float3> r_positions)
+{
+ BLI_assert(mesh.totpoly > 0);
+ BVHTreeFromMesh tree_data;
+ BKE_bvhtree_from_mesh_get(&tree_data, const_cast<Mesh *>(&mesh), BVHTREE_FROM_LOOPTRI, 2);
+ get_closest_in_bvhtree(tree_data, positions, r_looptri_indices, r_distances_sq, r_positions);
+ free_bvhtree_from_mesh(&tree_data);
+}
+
+static void get_closest_mesh_polygons(const Mesh &mesh,
+ const VArray<float3> &positions,
+ const MutableSpan<int> r_poly_indices,
+ const MutableSpan<float> r_distances_sq,
+ const MutableSpan<float3> r_positions)
+{
+ BLI_assert(mesh.totpoly > 0);
+
+ Array<int> looptri_indices(positions.size());
+ get_closest_mesh_looptris(mesh, positions, looptri_indices, r_distances_sq, r_positions);
+
+ Span<MLoopTri> looptris = get_mesh_looptris(mesh);
+ for (const int i : positions.index_range()) {
+ const MLoopTri &looptri = looptris[looptri_indices[i]];
+ r_poly_indices[i] = looptri.poly;
+ }
+}
+
+/* The closest corner is defined to be the closest corner on the closest face. */
+static void get_closest_mesh_corners(const Mesh &mesh,
+ const VArray<float3> &positions,
+ const MutableSpan<int> r_corner_indices,
+ const MutableSpan<float> r_distances_sq,
+ const MutableSpan<float3> r_positions)
+{
+ BLI_assert(mesh.totloop > 0);
+ Array<int> poly_indices(positions.size());
+ get_closest_mesh_polygons(mesh, positions, poly_indices, {}, {});
+
+ for (const int i : positions.index_range()) {
+ const float3 position = positions[i];
+ const int poly_index = poly_indices[i];
+ const MPoly &poly = mesh.mpoly[poly_index];
+
+ /* Find the closest vertex in the polygon. */
+ float min_distance_sq = FLT_MAX;
+ const MVert *closest_mvert;
+ int closest_loop_index = 0;
+ for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
+ const MLoop &loop = mesh.mloop[loop_index];
+ const int vertex_index = loop.v;
+ const MVert &mvert = mesh.mvert[vertex_index];
+ const float distance_sq = float3::distance_squared(position, mvert.co);
+ if (distance_sq < min_distance_sq) {
+ min_distance_sq = distance_sq;
+ closest_loop_index = loop_index;
+ closest_mvert = &mvert;
+ }
+ }
+ if (!r_corner_indices.is_empty()) {
+ r_corner_indices[i] = closest_loop_index;
+ }
+ if (!r_positions.is_empty()) {
+ r_positions[i] = closest_mvert->co;
+ }
+ if (!r_distances_sq.is_empty()) {
+ r_distances_sq[i] = min_distance_sq;
+ }
+ }
+}
+
+static void get_barycentric_coords(const Mesh &mesh,
+ const Span<int> looptri_indices,
+ const Span<float3> positions,
+ const MutableSpan<float3> r_bary_coords)
+{
+ BLI_assert(r_bary_coords.size() == positions.size());
+ BLI_assert(r_bary_coords.size() == looptri_indices.size());
+
+ Span<MLoopTri> looptris = get_mesh_looptris(mesh);
+
+ for (const int i : r_bary_coords.index_range()) {
+ const int looptri_index = looptri_indices[i];
+ const MLoopTri &looptri = looptris[looptri_index];
+
+ const int v0_index = mesh.mloop[looptri.tri[0]].v;
+ const int v1_index = mesh.mloop[looptri.tri[1]].v;
+ const int v2_index = mesh.mloop[looptri.tri[2]].v;
+
+ interp_weights_tri_v3(r_bary_coords[i],
+ mesh.mvert[v0_index].co,
+ mesh.mvert[v1_index].co,
+ mesh.mvert[v2_index].co,
+ positions[i]);
+ }
+}
+
+static void transfer_attribute_nearest_face_interpolated(const GeometrySet &src_geometry,
+ GeometryComponent &dst_component,
+ const VArray<float3> &dst_positions,
+ const AttributeDomain dst_domain,
+ const CustomDataType data_type,
+ const StringRef src_name,
+ const StringRef dst_name)
+{
+ const int tot_samples = dst_positions.size();
+ const MeshComponent *component = src_geometry.get_component_for_read<MeshComponent>();
+ if (component == nullptr) {
+ return;
+ }
+ const Mesh *mesh = component->get_for_read();
+ if (mesh == nullptr) {
+ return;
+ }
+ if (mesh->totpoly == 0) {
+ return;
+ }
+ ReadAttributeLookup src_attribute = component->attribute_try_get_for_read(src_name, data_type);
+ if (!src_attribute) {
+ return;
+ }
+
+ /* Find closest points on the mesh surface. */
+ Array<int> looptri_indices(tot_samples);
+ Array<float3> positions(tot_samples);
+ get_closest_mesh_looptris(*mesh, dst_positions, looptri_indices, {}, positions);
+
+ OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
+ dst_name, dst_domain, data_type);
+ if (!dst_attribute) {
+ return;
+ }
+ GMutableSpan dst_span = dst_attribute.as_span();
+ Array<float3> bary_coords;
+
+ /* Compute barycentric coordinates only when they are needed. */
+ if (src_attribute.domain != ATTR_DOMAIN_FACE) {
+ bary_coords.reinitialize(tot_samples);
+ get_barycentric_coords(*mesh, looptri_indices, positions, bary_coords);
+ }
+ /* Interpolate the source attribute on the surface. */
+ switch (src_attribute.domain) {
+ case ATTR_DOMAIN_POINT: {
+ bke::mesh_surface_sample::sample_point_attribute(
+ *mesh, looptri_indices, bary_coords, *src_attribute.varray, dst_span);
+ break;
+ }
+ case ATTR_DOMAIN_FACE: {
+ bke::mesh_surface_sample::sample_face_attribute(
+ *mesh, looptri_indices, *src_attribute.varray, dst_span);
+ break;
+ }
+ case ATTR_DOMAIN_CORNER: {
+ bke::mesh_surface_sample::sample_corner_attribute(
+ *mesh, looptri_indices, bary_coords, *src_attribute.varray, dst_span);
+ break;
+ }
+ case ATTR_DOMAIN_EDGE: {
+ /* Not yet supported. */
+ break;
+ }
+ default: {
+ BLI_assert_unreachable();
+ break;
+ }
+ }
+ dst_attribute.save();
+}
+
+static void transfer_attribute_nearest(const GeometrySet &src_geometry,
+ GeometryComponent &dst_component,
+ const VArray<float3> &dst_positions,
+ const AttributeDomain dst_domain,
+ const CustomDataType data_type,
+ const StringRef src_name,
+ const StringRef dst_name)
+{
+ const CPPType &type = *bke::custom_data_type_to_cpp_type(data_type);
+
+ /* Get pointcloud data from geometry. */
+ const PointCloudComponent *pointcloud_component =
+ src_geometry.get_component_for_read<PointCloudComponent>();
+ const PointCloud *pointcloud = pointcloud_component ? pointcloud_component->get_for_read() :
+ nullptr;
+
+ /* Get mesh data from geometry. */
+ const MeshComponent *mesh_component = src_geometry.get_component_for_read<MeshComponent>();
+ const Mesh *mesh = mesh_component ? mesh_component->get_for_read() : nullptr;
+
+ const int tot_samples = dst_positions.size();
+
+ Array<int> pointcloud_indices;
+ Array<float> pointcloud_distances_sq;
+ bool use_pointcloud = false;
+
+ /* Depending on where what domain the source attribute lives, these indices are either vertex,
+ * corner, edge or polygon indices. */
+ Array<int> mesh_indices;
+ Array<float> mesh_distances_sq;
+ bool use_mesh = false;
+
+ /* If there is a pointcloud, find the closest points. */
+ if (pointcloud != nullptr && pointcloud->totpoint > 0) {
+ if (pointcloud_component->attribute_exists(src_name)) {
+ use_pointcloud = true;
+ pointcloud_indices.reinitialize(tot_samples);
+ pointcloud_distances_sq.reinitialize(tot_samples);
+ get_closest_pointcloud_points(
+ *pointcloud, dst_positions, pointcloud_indices, pointcloud_distances_sq);
+ }
+ }
+
+ /* If there is a mesh, find the closest mesh elements. */
+ if (mesh != nullptr) {
+ ReadAttributeLookup src_attribute = mesh_component->attribute_try_get_for_read(src_name);
+ if (src_attribute) {
+ switch (src_attribute.domain) {
+ case ATTR_DOMAIN_POINT: {
+ if (mesh->totvert > 0) {
+ use_mesh = true;
+ mesh_indices.reinitialize(tot_samples);
+ mesh_distances_sq.reinitialize(tot_samples);
+ get_closest_mesh_points(*mesh, dst_positions, mesh_indices, mesh_distances_sq, {});
+ }
+ break;
+ }
+ case ATTR_DOMAIN_EDGE: {
+ if (mesh->totedge > 0) {
+ use_mesh = true;
+ mesh_indices.reinitialize(tot_samples);
+ mesh_distances_sq.reinitialize(tot_samples);
+ get_closest_mesh_edges(*mesh, dst_positions, mesh_indices, mesh_distances_sq, {});
+ }
+ break;
+ }
+ case ATTR_DOMAIN_FACE: {
+ if (mesh->totpoly > 0) {
+ use_mesh = true;
+ mesh_indices.reinitialize(tot_samples);
+ mesh_distances_sq.reinitialize(tot_samples);
+ get_closest_mesh_polygons(*mesh, dst_positions, mesh_indices, mesh_distances_sq, {});
+ }
+ break;
+ }
+ case ATTR_DOMAIN_CORNER: {
+ use_mesh = true;
+ mesh_indices.reinitialize(tot_samples);
+ mesh_distances_sq.reinitialize(tot_samples);
+ get_closest_mesh_corners(*mesh, dst_positions, mesh_indices, mesh_distances_sq, {});
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+ }
+ }
+
+ if (!use_pointcloud && !use_mesh) {
+ return;
+ }
+
+ OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only(
+ dst_name, dst_domain, data_type);
+ if (!dst_attribute) {
+ return;
+ }
+
+ /* Create a buffer for intermediate values. */
+ BUFFER_FOR_CPP_TYPE_VALUE(type, buffer);
+
+ if (use_mesh && use_pointcloud) {
+ /* When there is a mesh and a pointcloud, we still have to check whether a pointcloud point or
+ * a mesh element is closer to every point. */
+ ReadAttributeLookup pointcloud_src_attribute =
+ pointcloud_component->attribute_try_get_for_read(src_name, data_type);
+ ReadAttributeLookup mesh_src_attribute = mesh_component->attribute_try_get_for_read(src_name,
+ data_type);
+ for (const int i : IndexRange(tot_samples)) {
+ if (pointcloud_distances_sq[i] < mesh_distances_sq[i]) {
+ /* Pointcloud point is closer. */
+ const int index = pointcloud_indices[i];
+ pointcloud_src_attribute.varray->get(index, buffer);
+ dst_attribute->set_by_relocate(i, buffer);
+ }
+ else {
+ /* Mesh element is closer. */
+ const int index = mesh_indices[i];
+ mesh_src_attribute.varray->get(index, buffer);
+ dst_attribute->set_by_relocate(i, buffer);
+ }
+ }
+ }
+ else if (use_pointcloud) {
+ /* The source geometry only has a pointcloud. */
+ ReadAttributeLookup src_attribute = pointcloud_component->attribute_try_get_for_read(
+ src_name, data_type);
+ for (const int i : IndexRange(tot_samples)) {
+ const int index = pointcloud_indices[i];
+ src_attribute.varray->get(index, buffer);
+ dst_attribute->set_by_relocate(i, buffer);
+ }
+ }
+ else if (use_mesh) {
+ /* The source geometry only has a mesh. */
+ ReadAttributeLookup src_attribute = mesh_component->attribute_try_get_for_read(src_name,
+ data_type);
+ for (const int i : IndexRange(tot_samples)) {
+ const int index = mesh_indices[i];
+ src_attribute.varray->get(index, buffer);
+ dst_attribute->set_by_relocate(i, buffer);
+ }
+ }
+
+ dst_attribute.save();
+}
+
+static void transfer_attribute(const GeoNodeExecParams &params,
+ const GeometrySet &src_geometry,
+ GeometryComponent &dst_component,
+ const StringRef src_name,
+ const StringRef dst_name)
+{
+ const NodeGeometryAttributeTransfer &storage =
+ *(const NodeGeometryAttributeTransfer *)params.node().storage;
+ const GeometryNodeAttributeTransferMapMode mapping = (GeometryNodeAttributeTransferMapMode)
+ storage.mapping;
+ const AttributeDomain input_domain = (AttributeDomain)storage.domain;
+
+ CustomDataType data_type;
+ AttributeDomain auto_domain;
+ get_result_domain_and_data_type(src_geometry, dst_component, src_name, &data_type, &auto_domain);
+ const AttributeDomain dst_domain = (input_domain == ATTR_DOMAIN_AUTO) ? auto_domain :
+ input_domain;
+
+ GVArray_Typed<float3> dst_positions = dst_component.attribute_get_for_read<float3>(
+ "position", dst_domain, {0, 0, 0});
+
+ switch (mapping) {
+ case GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED: {
+ transfer_attribute_nearest_face_interpolated(
+ src_geometry, dst_component, dst_positions, dst_domain, data_type, src_name, dst_name);
+ break;
+ }
+ case GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST: {
+ transfer_attribute_nearest(
+ src_geometry, dst_component, dst_positions, dst_domain, data_type, src_name, dst_name);
+ break;
+ }
+ }
+}
+
+static void geo_node_attribute_transfer_exec(GeoNodeExecParams params)
+{
+ GeometrySet dst_geometry_set = params.extract_input<GeometrySet>("Geometry");
+ GeometrySet src_geometry_set = params.extract_input<GeometrySet>("Source Geometry");
+ const std::string src_attribute_name = params.extract_input<std::string>("Source");
+ const std::string dst_attribute_name = params.extract_input<std::string>("Destination");
+
+ if (src_attribute_name.empty() || dst_attribute_name.empty()) {
+ params.set_output("Geometry", dst_geometry_set);
+ return;
+ }
+
+ dst_geometry_set = bke::geometry_set_realize_instances(dst_geometry_set);
+ src_geometry_set = bke::geometry_set_realize_instances(src_geometry_set);
+
+ if (dst_geometry_set.has<MeshComponent>()) {
+ transfer_attribute(params,
+ src_geometry_set,
+ dst_geometry_set.get_component_for_write<MeshComponent>(),
+ src_attribute_name,
+ dst_attribute_name);
+ }
+ if (dst_geometry_set.has<PointCloudComponent>()) {
+ transfer_attribute(params,
+ src_geometry_set,
+ dst_geometry_set.get_component_for_write<PointCloudComponent>(),
+ src_attribute_name,
+ dst_attribute_name);
+ }
+
+ params.set_output("Geometry", dst_geometry_set);
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_attribute_transfer()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_ATTRIBUTE_TRANSFER, "Attribute Transfer", NODE_CLASS_ATTRIBUTE, 0);
+ node_type_socket_templates(
+ &ntype, geo_node_attribute_transfer_in, geo_node_attribute_transfer_out);
+ node_type_init(&ntype, blender::nodes::geo_node_attribute_transfer_init);
+ node_type_storage(&ntype,
+ "NodeGeometryAttributeTransfer",
+ node_free_standard_storage,
+ node_copy_standard_storage);
+ ntype.geometry_node_execute = blender::nodes::geo_node_attribute_transfer_exec;
+ ntype.draw_buttons = geo_node_attribute_transfer_layout;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc
index ee2fd763ead..5a5fc099779 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc
@@ -14,23 +14,16 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "node_geometry_util.hh"
-
-#include "BKE_attribute.h"
-#include "BKE_attribute_access.hh"
-
-#include "BLI_array.hh"
#include "BLI_math_base_safe.h"
-#include "BLI_rand.hh"
-
-#include "DNA_mesh_types.h"
-#include "DNA_pointcloud_types.h"
+#include "BLI_task.hh"
#include "UI_interface.h"
#include "UI_resources.h"
#include "NOD_math_functions.hh"
+#include "node_geometry_util.hh"
+
static bNodeSocketTemplate geo_node_attribute_vector_math_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("A")},
@@ -66,8 +59,11 @@ static bool operation_use_input_b(const NodeVectorMathOperation operation)
static bool operation_use_input_c(const NodeVectorMathOperation operation)
{
- return ELEM(
- operation, NODE_VECTOR_MATH_WRAP, NODE_VECTOR_MATH_REFRACT, NODE_VECTOR_MATH_FACEFORWARD);
+ return ELEM(operation,
+ NODE_VECTOR_MATH_WRAP,
+ NODE_VECTOR_MATH_REFRACT,
+ NODE_VECTOR_MATH_FACEFORWARD,
+ NODE_VECTOR_MATH_MULTIPLY_ADD);
}
static CustomDataType operation_get_read_type_b(const NodeVectorMathOperation operation)
@@ -129,6 +125,7 @@ static CustomDataType operation_get_result_type(const NodeVectorMathOperation op
case NODE_VECTOR_MATH_TANGENT:
case NODE_VECTOR_MATH_REFRACT:
case NODE_VECTOR_MATH_FACEFORWARD:
+ case NODE_VECTOR_MATH_MULTIPLY_ADD:
return CD_PROP_FLOAT3;
case NODE_VECTOR_MATH_DOT_PRODUCT:
case NODE_VECTOR_MATH_DISTANCE:
@@ -182,196 +179,210 @@ static void geo_node_attribute_vector_math_update(bNodeTree *UNUSED(ntree), bNod
operation_use_input_c(operation));
}
-static void do_math_operation_fl3_fl3_to_fl3(const Float3ReadAttribute &input_a,
- const Float3ReadAttribute &input_b,
- Float3WriteAttribute result,
+static void do_math_operation_fl3_fl3_to_fl3(const VArray<float3> &input_a,
+ const VArray<float3> &input_b,
+ VMutableArray<float3> &result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
- Span<float3> span_a = input_a.get_span();
- Span<float3> span_b = input_b.get_span();
- MutableSpan<float3> span_result = result.get_span_for_write_only();
+ VArray_Span<float3> span_a{input_a};
+ VArray_Span<float3> span_b{input_b};
+ VMutableArray_Span<float3> span_result{result, false};
bool success = try_dispatch_float_math_fl3_fl3_to_fl3(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(size)) {
- const float3 a = span_a[i];
- const float3 b = span_b[i];
- const float3 out = math_function(a, b);
- span_result[i] = out;
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const float3 a = span_a[i];
+ const float3 b = span_b[i];
+ const float3 out = math_function(a, b);
+ span_result[i] = out;
+ }
+ });
});
- result.apply_span();
+ span_result.save();
/* The operation is not supported by this node currently. */
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
}
-static void do_math_operation_fl3_fl3_fl3_to_fl3(const Float3ReadAttribute &input_a,
- const Float3ReadAttribute &input_b,
- const Float3ReadAttribute &input_c,
- Float3WriteAttribute result,
+static void do_math_operation_fl3_fl3_fl3_to_fl3(const VArray<float3> &input_a,
+ const VArray<float3> &input_b,
+ const VArray<float3> &input_c,
+ VMutableArray<float3> &result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
- Span<float3> span_a = input_a.get_span();
- Span<float3> span_b = input_b.get_span();
- Span<float3> span_c = input_c.get_span();
- MutableSpan<float3> span_result = result.get_span_for_write_only();
+ VArray_Span<float3> span_a{input_a};
+ VArray_Span<float3> span_b{input_b};
+ VArray_Span<float3> span_c{input_c};
+ VMutableArray_Span<float3> span_result{result};
bool success = try_dispatch_float_math_fl3_fl3_fl3_to_fl3(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(size)) {
- const float3 a = span_a[i];
- const float3 b = span_b[i];
- const float3 c = span_c[i];
- const float3 out = math_function(a, b, c);
- span_result[i] = out;
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const float3 a = span_a[i];
+ const float3 b = span_b[i];
+ const float3 c = span_c[i];
+ const float3 out = math_function(a, b, c);
+ span_result[i] = out;
+ }
+ });
});
- result.apply_span();
+ span_result.save();
/* The operation is not supported by this node currently. */
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
}
-static void do_math_operation_fl3_fl3_fl_to_fl3(const Float3ReadAttribute &input_a,
- const Float3ReadAttribute &input_b,
- const FloatReadAttribute &input_c,
- Float3WriteAttribute result,
+static void do_math_operation_fl3_fl3_fl_to_fl3(const VArray<float3> &input_a,
+ const VArray<float3> &input_b,
+ const VArray<float> &input_c,
+ VMutableArray<float3> &result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
- Span<float3> span_a = input_a.get_span();
- Span<float3> span_b = input_b.get_span();
- Span<float> span_c = input_c.get_span();
- MutableSpan<float3> span_result = result.get_span_for_write_only();
+ VArray_Span<float3> span_a{input_a};
+ VArray_Span<float3> span_b{input_b};
+ VArray_Span<float> span_c{input_c};
+ VMutableArray_Span<float3> span_result{result, false};
bool success = try_dispatch_float_math_fl3_fl3_fl_to_fl3(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(size)) {
- const float3 a = span_a[i];
- const float3 b = span_b[i];
- const float c = span_c[i];
- const float3 out = math_function(a, b, c);
- span_result[i] = out;
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const float3 a = span_a[i];
+ const float3 b = span_b[i];
+ const float c = span_c[i];
+ const float3 out = math_function(a, b, c);
+ span_result[i] = out;
+ }
+ });
});
- result.apply_span();
+ span_result.save();
/* The operation is not supported by this node currently. */
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
}
-static void do_math_operation_fl3_fl3_to_fl(const Float3ReadAttribute &input_a,
- const Float3ReadAttribute &input_b,
- FloatWriteAttribute result,
+static void do_math_operation_fl3_fl3_to_fl(const VArray<float3> &input_a,
+ const VArray<float3> &input_b,
+ VMutableArray<float> &result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
- Span<float3> span_a = input_a.get_span();
- Span<float3> span_b = input_b.get_span();
- MutableSpan<float> span_result = result.get_span_for_write_only();
+ VArray_Span<float3> span_a{input_a};
+ VArray_Span<float3> span_b{input_b};
+ VMutableArray_Span<float> span_result{result, false};
bool success = try_dispatch_float_math_fl3_fl3_to_fl(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(size)) {
- const float3 a = span_a[i];
- const float3 b = span_b[i];
- const float out = math_function(a, b);
- span_result[i] = out;
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const float3 a = span_a[i];
+ const float3 b = span_b[i];
+ const float out = math_function(a, b);
+ span_result[i] = out;
+ }
+ });
});
- result.apply_span();
+ span_result.save();
/* The operation is not supported by this node currently. */
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
}
-static void do_math_operation_fl3_fl_to_fl3(const Float3ReadAttribute &input_a,
- const FloatReadAttribute &input_b,
- Float3WriteAttribute result,
+static void do_math_operation_fl3_fl_to_fl3(const VArray<float3> &input_a,
+ const VArray<float> &input_b,
+ VMutableArray<float3> &result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
- Span<float3> span_a = input_a.get_span();
- Span<float> span_b = input_b.get_span();
- MutableSpan<float3> span_result = result.get_span_for_write_only();
+ VArray_Span<float3> span_a{input_a};
+ VArray_Span<float> span_b{input_b};
+ VMutableArray_Span<float3> span_result{result, false};
bool success = try_dispatch_float_math_fl3_fl_to_fl3(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(size)) {
- const float3 a = span_a[i];
- const float b = span_b[i];
- const float3 out = math_function(a, b);
- span_result[i] = out;
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const float3 a = span_a[i];
+ const float b = span_b[i];
+ const float3 out = math_function(a, b);
+ span_result[i] = out;
+ }
+ });
});
- result.apply_span();
+ span_result.save();
/* The operation is not supported by this node currently. */
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
}
-static void do_math_operation_fl3_to_fl3(const Float3ReadAttribute &input_a,
- Float3WriteAttribute result,
+static void do_math_operation_fl3_to_fl3(const VArray<float3> &input_a,
+ VMutableArray<float3> &result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
- Span<float3> span_a = input_a.get_span();
- MutableSpan<float3> span_result = result.get_span_for_write_only();
+ VArray_Span<float3> span_a{input_a};
+ VMutableArray_Span<float3> span_result{result, false};
bool success = try_dispatch_float_math_fl3_to_fl3(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(size)) {
- const float3 in = span_a[i];
- const float3 out = math_function(in);
- span_result[i] = out;
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const float3 in = span_a[i];
+ const float3 out = math_function(in);
+ span_result[i] = out;
+ }
+ });
});
- result.apply_span();
+ span_result.save();
/* The operation is not supported by this node currently. */
BLI_assert(success);
UNUSED_VARS_NDEBUG(success);
}
-static void do_math_operation_fl3_to_fl(const Float3ReadAttribute &input_a,
- FloatWriteAttribute result,
+static void do_math_operation_fl3_to_fl(const VArray<float3> &input_a,
+ VMutableArray<float> &result,
const NodeVectorMathOperation operation)
{
const int size = input_a.size();
- Span<float3> span_a = input_a.get_span();
- MutableSpan<float> span_result = result.get_span_for_write_only();
+ VArray_Span<float3> span_a{input_a};
+ VMutableArray_Span<float> span_result{result, false};
bool success = try_dispatch_float_math_fl3_to_fl(
operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
- for (const int i : IndexRange(size)) {
- const float3 in = span_a[i];
- const float out = math_function(in);
- span_result[i] = out;
- }
+ parallel_for(IndexRange(size), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const float3 in = span_a[i];
+ const float out = math_function(in);
+ span_result[i] = out;
+ }
+ });
});
- result.apply_span();
+ span_result.save();
/* The operation is not supported by this node currently. */
BLI_assert(success);
@@ -384,9 +395,9 @@ static AttributeDomain get_result_domain(const GeometryComponent &component,
StringRef result_name)
{
/* Use the domain of the result attribute if it already exists. */
- ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
+ ReadAttributeLookup result_attribute = component.attribute_try_get_for_read(result_name);
if (result_attribute) {
- return result_attribute->domain();
+ return result_attribute.domain;
}
/* Otherwise use the highest priority domain from existing input attributes, or the default. */
@@ -420,13 +431,13 @@ static void attribute_vector_math_calc(GeometryComponent &component,
const AttributeDomain result_domain = get_result_domain(
component, params, operation, result_name);
- ReadAttributePtr attribute_a = params.get_input_attribute(
+ GVArrayPtr attribute_a = params.get_input_attribute(
"A", component, result_domain, read_type_a, nullptr);
if (!attribute_a) {
return;
}
- ReadAttributePtr attribute_b;
- ReadAttributePtr attribute_c;
+ GVArrayPtr attribute_b;
+ GVArrayPtr attribute_c;
if (use_input_b) {
attribute_b = params.get_input_attribute("B", component, result_domain, read_type_b, nullptr);
if (!attribute_b) {
@@ -441,7 +452,7 @@ static void attribute_vector_math_calc(GeometryComponent &component,
}
/* Get result attribute first, in case it has to overwrite one of the existing attributes. */
- OutputAttributePtr attribute_result = component.attribute_try_get_for_output(
+ OutputAttribute attribute_result = component.attribute_try_get_for_output_only(
result_name, result_domain, result_type);
if (!attribute_result) {
return;
@@ -459,17 +470,27 @@ static void attribute_vector_math_calc(GeometryComponent &component,
case NODE_VECTOR_MATH_MODULO:
case NODE_VECTOR_MATH_MINIMUM:
case NODE_VECTOR_MATH_MAXIMUM:
- do_math_operation_fl3_fl3_to_fl3(*attribute_a, *attribute_b, *attribute_result, operation);
+ do_math_operation_fl3_fl3_to_fl3(attribute_a->typed<float3>(),
+ attribute_b->typed<float3>(),
+ attribute_result->typed<float3>(),
+ operation);
break;
case NODE_VECTOR_MATH_DOT_PRODUCT:
case NODE_VECTOR_MATH_DISTANCE:
- do_math_operation_fl3_fl3_to_fl(*attribute_a, *attribute_b, *attribute_result, operation);
+ do_math_operation_fl3_fl3_to_fl(attribute_a->typed<float3>(),
+ attribute_b->typed<float3>(),
+ attribute_result->typed<float>(),
+ operation);
break;
case NODE_VECTOR_MATH_LENGTH:
- do_math_operation_fl3_to_fl(*attribute_a, *attribute_result, operation);
+ do_math_operation_fl3_to_fl(
+ attribute_a->typed<float3>(), attribute_result->typed<float>(), operation);
break;
case NODE_VECTOR_MATH_SCALE:
- do_math_operation_fl3_fl_to_fl3(*attribute_a, *attribute_b, *attribute_result, operation);
+ do_math_operation_fl3_fl_to_fl3(attribute_a->typed<float3>(),
+ attribute_b->typed<float>(),
+ attribute_result->typed<float3>(),
+ operation);
break;
case NODE_VECTOR_MATH_NORMALIZE:
case NODE_VECTOR_MATH_FLOOR:
@@ -479,16 +500,24 @@ static void attribute_vector_math_calc(GeometryComponent &component,
case NODE_VECTOR_MATH_SINE:
case NODE_VECTOR_MATH_COSINE:
case NODE_VECTOR_MATH_TANGENT:
- do_math_operation_fl3_to_fl3(*attribute_a, *attribute_result, operation);
+ do_math_operation_fl3_to_fl3(
+ attribute_a->typed<float3>(), attribute_result->typed<float3>(), operation);
break;
case NODE_VECTOR_MATH_WRAP:
case NODE_VECTOR_MATH_FACEFORWARD:
- do_math_operation_fl3_fl3_fl3_to_fl3(
- *attribute_a, *attribute_b, *attribute_c, *attribute_result, operation);
+ case NODE_VECTOR_MATH_MULTIPLY_ADD:
+ do_math_operation_fl3_fl3_fl3_to_fl3(attribute_a->typed<float3>(),
+ attribute_b->typed<float3>(),
+ attribute_c->typed<float3>(),
+ attribute_result->typed<float3>(),
+ operation);
break;
case NODE_VECTOR_MATH_REFRACT:
- do_math_operation_fl3_fl3_fl_to_fl3(
- *attribute_a, *attribute_b, *attribute_c, *attribute_result, operation);
+ do_math_operation_fl3_fl3_fl_to_fl3(attribute_a->typed<float3>(),
+ attribute_b->typed<float3>(),
+ attribute_c->typed<float>(),
+ attribute_result->typed<float3>(),
+ operation);
break;
}
attribute_result.save();
@@ -507,6 +536,9 @@ static void geo_node_attribute_vector_math_exec(GeoNodeExecParams params)
attribute_vector_math_calc(geometry_set.get_component_for_write<PointCloudComponent>(),
params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ attribute_vector_math_calc(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc
new file mode 100644
index 00000000000..4d568ab5c3a
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc
@@ -0,0 +1,352 @@
+/*
+ * 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.
+ */
+
+#include "node_geometry_util.hh"
+
+#include "BLI_task.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+static bNodeSocketTemplate geo_node_attribute_vector_rotate_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_STRING, N_("Vector")},
+ {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE},
+ {SOCK_STRING, N_("Center")},
+ {SOCK_VECTOR, N_("Center"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_XYZ},
+ {SOCK_STRING, N_("Axis")},
+ {SOCK_VECTOR, N_("Axis"), 0.0f, 0.0f, 1.0f, 0.0f, -1.0f, 1.0f, PROP_XYZ, PROP_NONE},
+ {SOCK_STRING, N_("Angle")},
+ {SOCK_FLOAT, N_("Angle"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_ANGLE, PROP_NONE},
+ {SOCK_STRING, N_("Rotation")},
+ {SOCK_VECTOR, N_("Rotation"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_EULER},
+ {SOCK_BOOLEAN, N_("Invert")},
+ {SOCK_STRING, N_("Result")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_attribute_vector_rotate_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+static void geo_node_attribute_vector_rotate_layout(uiLayout *layout,
+ bContext *UNUSED(C),
+ PointerRNA *ptr)
+{
+ bNode *node = (bNode *)ptr->data;
+ const NodeAttributeVectorRotate &node_storage = *(NodeAttributeVectorRotate *)node->storage;
+ const GeometryNodeAttributeVectorRotateMode mode = (const GeometryNodeAttributeVectorRotateMode)
+ node_storage.mode;
+
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+ uiLayout *column = uiLayoutColumn(layout, false);
+
+ uiItemR(column, ptr, "rotation_mode", 0, "", ICON_NONE);
+
+ uiItemR(column, ptr, "input_type_vector", 0, IFACE_("Vector"), ICON_NONE);
+ uiItemR(column, ptr, "input_type_center", 0, IFACE_("Center"), ICON_NONE);
+ if (mode == GEO_NODE_VECTOR_ROTATE_TYPE_AXIS) {
+ uiItemR(column, ptr, "input_type_axis", 0, IFACE_("Axis"), ICON_NONE);
+ }
+ if (mode != GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ) {
+ uiItemR(column, ptr, "input_type_angle", 0, IFACE_("Angle"), ICON_NONE);
+ }
+ if (mode == GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ) {
+ uiItemR(column, ptr, "input_type_rotation", 0, IFACE_("Rotation"), ICON_NONE);
+ }
+}
+
+namespace blender::nodes {
+
+static void geo_node_attribute_vector_rotate_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ const NodeAttributeVectorRotate *node_storage = (NodeAttributeVectorRotate *)node->storage;
+ const GeometryNodeAttributeVectorRotateMode mode = (const GeometryNodeAttributeVectorRotateMode)
+ node_storage->mode;
+
+ update_attribute_input_socket_availabilities(
+ *node, "Vector", (GeometryNodeAttributeInputMode)node_storage->input_type_vector);
+ update_attribute_input_socket_availabilities(
+ *node, "Center", (GeometryNodeAttributeInputMode)node_storage->input_type_center);
+ update_attribute_input_socket_availabilities(
+ *node,
+ "Axis",
+ (GeometryNodeAttributeInputMode)node_storage->input_type_axis,
+ (mode == GEO_NODE_VECTOR_ROTATE_TYPE_AXIS));
+ update_attribute_input_socket_availabilities(
+ *node,
+ "Angle",
+ (GeometryNodeAttributeInputMode)node_storage->input_type_angle,
+ (mode != GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ));
+ update_attribute_input_socket_availabilities(
+ *node,
+ "Rotation",
+ (GeometryNodeAttributeInputMode)node_storage->input_type_rotation,
+ (mode == GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ));
+}
+
+static float3 vector_rotate_around_axis(const float3 vector,
+ const float3 center,
+ const float3 axis,
+ const float angle)
+{
+ float3 result = vector - center;
+ float mat[3][3];
+ axis_angle_to_mat3(mat, axis, angle);
+ mul_m3_v3(mat, result);
+ return result + center;
+}
+
+static void geo_node_attribute_vector_rotate_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeAttributeVectorRotate *node_storage = (NodeAttributeVectorRotate *)MEM_callocN(
+ sizeof(NodeAttributeVectorRotate), __func__);
+
+ node_storage->mode = GEO_NODE_VECTOR_ROTATE_TYPE_AXIS;
+ node_storage->input_type_vector = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE;
+ node_storage->input_type_center = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
+ node_storage->input_type_axis = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
+ node_storage->input_type_angle = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
+ node_storage->input_type_rotation = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
+
+ node->storage = node_storage;
+}
+
+static float3 vector_rotate_euler(const float3 vector,
+ const float3 center,
+ const float3 rotation,
+ const bool invert)
+{
+ float mat[3][3];
+ float3 result = vector - center;
+ eul_to_mat3(mat, rotation);
+ if (invert) {
+ invert_m3(mat);
+ }
+ mul_m3_v3(mat, result);
+ return result + center;
+}
+
+static void do_vector_rotate_around_axis(const VArray<float3> &vector,
+ const VArray<float3> &center,
+ const VArray<float3> &axis,
+ const VArray<float> &angle,
+ MutableSpan<float3> results,
+ const bool invert)
+{
+ VArray_Span<float3> span_vector{vector};
+ VArray_Span<float3> span_center{center};
+ VArray_Span<float3> span_axis{axis};
+ VArray_Span<float> span_angle{angle};
+
+ parallel_for(IndexRange(results.size()), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ float angle = (invert) ? -span_angle[i] : span_angle[i];
+ results[i] = vector_rotate_around_axis(span_vector[i], span_center[i], span_axis[i], angle);
+ }
+ });
+}
+
+static void do_vector_rotate_around_fixed_axis(const VArray<float3> &vector,
+ const VArray<float3> &center,
+ const float3 axis,
+ const VArray<float> &angle,
+ MutableSpan<float3> results,
+ const bool invert)
+{
+ VArray_Span<float3> span_vector{vector};
+ VArray_Span<float3> span_center{center};
+ VArray_Span<float> span_angle{angle};
+
+ parallel_for(IndexRange(results.size()), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ float angle = (invert) ? -span_angle[i] : span_angle[i];
+ results[i] = vector_rotate_around_axis(span_vector[i], span_center[i], axis, angle);
+ }
+ });
+}
+
+static void do_vector_rotate_euler(const VArray<float3> &vector,
+ const VArray<float3> &center,
+ const VArray<float3> &rotation,
+ MutableSpan<float3> results,
+ const bool invert)
+{
+ VArray_Span<float3> span_vector{vector};
+ VArray_Span<float3> span_center{center};
+ VArray_Span<float3> span_rotation{rotation};
+
+ parallel_for(IndexRange(results.size()), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ results[i] = vector_rotate_euler(span_vector[i], span_center[i], span_rotation[i], invert);
+ }
+ });
+}
+
+static AttributeDomain get_result_domain(const GeometryComponent &component,
+ const GeoNodeExecParams &params,
+ StringRef result_name)
+{
+ /* Use the domain of the result attribute if it already exists. */
+ std::optional<AttributeMetaData> meta_data = component.attribute_get_meta_data(result_name);
+ if (meta_data) {
+ return meta_data->domain;
+ }
+
+ /* Otherwise use the highest priority domain from existing input attributes, or the default. */
+ const AttributeDomain default_domain = ATTR_DOMAIN_POINT;
+ return params.get_highest_priority_input_domain({"Vector", "Center"}, component, default_domain);
+}
+
+static void execute_on_component(const GeoNodeExecParams &params, GeometryComponent &component)
+{
+ const bNode &node = params.node();
+ const NodeAttributeVectorRotate *node_storage = (const NodeAttributeVectorRotate *)node.storage;
+ const GeometryNodeAttributeVectorRotateMode mode = (GeometryNodeAttributeVectorRotateMode)
+ node_storage->mode;
+ const std::string result_name = params.get_input<std::string>("Result");
+ const AttributeDomain result_domain = get_result_domain(component, params, result_name);
+ const bool invert = params.get_input<bool>("Invert");
+
+ GVArrayPtr attribute_vector = params.get_input_attribute(
+ "Vector", component, result_domain, CD_PROP_FLOAT3, nullptr);
+ if (!attribute_vector) {
+ return;
+ }
+ GVArrayPtr attribute_center = params.get_input_attribute(
+ "Center", component, result_domain, CD_PROP_FLOAT3, nullptr);
+ if (!attribute_center) {
+ return;
+ }
+
+ OutputAttribute attribute_result = component.attribute_try_get_for_output_only(
+ result_name, result_domain, CD_PROP_FLOAT3);
+ if (!attribute_result) {
+ return;
+ }
+
+ if (mode == GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ) {
+ GVArrayPtr attribute_rotation = params.get_input_attribute(
+ "Rotation", component, result_domain, CD_PROP_FLOAT3, nullptr);
+ if (!attribute_rotation) {
+ return;
+ }
+ do_vector_rotate_euler(attribute_vector->typed<float3>(),
+ attribute_center->typed<float3>(),
+ attribute_rotation->typed<float3>(),
+ attribute_result.as_span<float3>(),
+ invert);
+ attribute_result.save();
+ return;
+ }
+
+ GVArrayPtr attribute_angle = params.get_input_attribute(
+ "Angle", component, result_domain, CD_PROP_FLOAT, nullptr);
+ if (!attribute_angle) {
+ return;
+ }
+
+ switch (mode) {
+ case GEO_NODE_VECTOR_ROTATE_TYPE_AXIS: {
+ GVArrayPtr attribute_axis = params.get_input_attribute(
+ "Axis", component, result_domain, CD_PROP_FLOAT3, nullptr);
+ if (!attribute_axis) {
+ return;
+ }
+ do_vector_rotate_around_axis(attribute_vector->typed<float3>(),
+ attribute_center->typed<float3>(),
+ attribute_axis->typed<float3>(),
+ attribute_angle->typed<float>(),
+ attribute_result.as_span<float3>(),
+ invert);
+ } break;
+ case GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_X:
+ do_vector_rotate_around_fixed_axis(attribute_vector->typed<float3>(),
+ attribute_center->typed<float3>(),
+ float3(1.0f, 0.0f, 0.0f),
+ attribute_angle->typed<float>(),
+ attribute_result.as_span<float3>(),
+ invert);
+ break;
+ case GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_Y:
+ do_vector_rotate_around_fixed_axis(attribute_vector->typed<float3>(),
+ attribute_center->typed<float3>(),
+ float3(0.0f, 1.0f, 0.0f),
+ attribute_angle->typed<float>(),
+ attribute_result.as_span<float3>(),
+ invert);
+
+ break;
+ case GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_Z:
+ do_vector_rotate_around_fixed_axis(attribute_vector->typed<float3>(),
+ attribute_center->typed<float3>(),
+ float3(0.0f, 0.0f, 1.0f),
+ attribute_angle->typed<float>(),
+ attribute_result.as_span<float3>(),
+ invert);
+
+ break;
+ case GEO_NODE_VECTOR_ROTATE_TYPE_EULER_XYZ:
+ /* Euler is handled before other modes to avoid processing the unavailable angle socket. */
+ BLI_assert_unreachable();
+ break;
+ }
+ attribute_result.save();
+}
+
+static void geo_node_attribute_vector_rotate_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ geometry_set = geometry_set_realize_instances(geometry_set);
+
+ if (geometry_set.has<MeshComponent>()) {
+ execute_on_component(params, geometry_set.get_component_for_write<MeshComponent>());
+ }
+ if (geometry_set.has<PointCloudComponent>()) {
+ execute_on_component(params, geometry_set.get_component_for_write<PointCloudComponent>());
+ }
+ if (geometry_set.has<CurveComponent>()) {
+ execute_on_component(params, geometry_set.get_component_for_write<CurveComponent>());
+ }
+
+ params.set_output("Geometry", std::move(geometry_set));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_attribute_vector_rotate()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype,
+ GEO_NODE_ATTRIBUTE_VECTOR_ROTATE,
+ "Attribute Vector Rotate",
+ NODE_CLASS_ATTRIBUTE,
+ 0);
+ node_type_socket_templates(
+ &ntype, geo_node_attribute_vector_rotate_in, geo_node_attribute_vector_rotate_out);
+ node_type_update(&ntype, blender::nodes::geo_node_attribute_vector_rotate_update);
+ node_type_init(&ntype, blender::nodes::geo_node_attribute_vector_rotate_init);
+ node_type_size(&ntype, 165, 100, 600);
+ node_type_storage(
+ &ntype, "NodeAttributeVectorRotate", node_free_standard_storage, node_copy_standard_storage);
+ ntype.geometry_node_execute = blender::nodes::geo_node_attribute_vector_rotate_exec;
+ ntype.draw_buttons = geo_node_attribute_vector_rotate_layout;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc
index 9f331190420..d8029ea1eeb 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc
@@ -14,29 +14,29 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "MEM_guardedalloc.h"
-
-#include "BLI_alloca.h"
-#include "BLI_math_matrix.h"
-
#include "DNA_mesh_types.h"
-#include "DNA_modifier_types.h"
-#include "RNA_enum_types.h"
+#include "BKE_mesh_boolean_convert.hh"
#include "UI_interface.h"
#include "UI_resources.h"
-#include "BKE_mesh.h"
-
-#include "bmesh.h"
-#include "tools/bmesh_boolean.h"
-
#include "node_geometry_util.hh"
static bNodeSocketTemplate geo_node_boolean_in[] = {
{SOCK_GEOMETRY, N_("Geometry 1")},
- {SOCK_GEOMETRY, N_("Geometry 2")},
+ {SOCK_GEOMETRY,
+ N_("Geometry 2"),
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ PROP_NONE,
+ SOCK_MULTI_INPUT},
+ {SOCK_BOOLEAN, N_("Self Intersection")},
+ {SOCK_BOOLEAN, N_("Hole Tolerant")},
{-1, ""},
};
@@ -50,109 +50,87 @@ static void geo_node_boolean_layout(uiLayout *layout, bContext *UNUSED(C), Point
uiItemR(layout, ptr, "operation", 0, "", ICON_NONE);
}
-static int bm_face_isect_pair(BMFace *f, void *UNUSED(user_data))
+static void geo_node_boolean_update(bNodeTree *UNUSED(ntree), bNode *node)
{
- return BM_elem_flag_test(f, BM_ELEM_DRAW) ? 1 : 0;
-}
-
-static Mesh *mesh_boolean_calc(const Mesh *mesh_a, const Mesh *mesh_b, int boolean_mode)
-{
- const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh_a, mesh_b);
-
- BMesh *bm;
- {
- struct BMeshCreateParams bmesh_create_params = {0};
- bmesh_create_params.use_toolflags = false;
- bm = BM_mesh_create(&allocsize, &bmesh_create_params);
- }
-
- {
- struct BMeshFromMeshParams bmesh_from_mesh_params = {0};
- bmesh_from_mesh_params.calc_face_normal = true;
- BM_mesh_bm_from_me(bm, mesh_b, &bmesh_from_mesh_params);
- BM_mesh_bm_from_me(bm, mesh_a, &bmesh_from_mesh_params);
- }
-
- const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop);
- int tottri;
- BMLoop *(*looptris)[3] = (BMLoop *
- (*)[3])(MEM_malloc_arrayN(looptris_tot, sizeof(*looptris), __func__));
- BM_mesh_calc_tessellation_beauty(bm, looptris, &tottri);
-
- const int i_faces_end = mesh_b->totpoly;
-
- /* We need face normals because of 'BM_face_split_edgenet'
- * we could calculate on the fly too (before calling split). */
-
- int i = 0;
- BMIter iter;
- BMFace *bm_face;
- BM_ITER_MESH (bm_face, &iter, bm, BM_FACES_OF_MESH) {
- normalize_v3(bm_face->no);
+ GeometryNodeBooleanOperation operation = (GeometryNodeBooleanOperation)node->custom1;
- /* Temp tag to test which side split faces are from. */
- BM_elem_flag_enable(bm_face, BM_ELEM_DRAW);
+ bNodeSocket *geometry_1_socket = (bNodeSocket *)node->inputs.first;
+ bNodeSocket *geometry_2_socket = geometry_1_socket->next;
- i++;
- if (i == i_faces_end) {
+ switch (operation) {
+ case GEO_NODE_BOOLEAN_INTERSECT:
+ case GEO_NODE_BOOLEAN_UNION:
+ nodeSetSocketAvailability(geometry_1_socket, false);
+ nodeSetSocketAvailability(geometry_2_socket, true);
+ node_sock_label(geometry_2_socket, N_("Geometry"));
+ break;
+ case GEO_NODE_BOOLEAN_DIFFERENCE:
+ nodeSetSocketAvailability(geometry_1_socket, true);
+ nodeSetSocketAvailability(geometry_2_socket, true);
+ node_sock_label(geometry_2_socket, N_("Geometry 2"));
break;
- }
}
+}
- BM_mesh_boolean(
- bm, looptris, tottri, bm_face_isect_pair, nullptr, 2, false, false, false, boolean_mode);
-
- Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(bm, nullptr, mesh_a);
- BM_mesh_free(bm);
- result->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
- MEM_freeN(looptris);
-
- return result;
+static void geo_node_boolean_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ node->custom1 = GEO_NODE_BOOLEAN_DIFFERENCE;
}
namespace blender::nodes {
+
static void geo_node_boolean_exec(GeoNodeExecParams params)
{
- GeometrySet geometry_set_in_a = params.extract_input<GeometrySet>("Geometry 1");
- GeometrySet geometry_set_in_b = params.extract_input<GeometrySet>("Geometry 2");
- GeometrySet geometry_set_out;
-
GeometryNodeBooleanOperation operation = (GeometryNodeBooleanOperation)params.node().custom1;
- if (operation < 0 || operation > 2) {
- BLI_assert(false);
- params.set_output("Geometry", std::move(geometry_set_out));
- return;
+ const bool use_self = params.get_input<bool>("Self Intersection");
+ const bool hole_tolerant = params.get_input<bool>("Hole Tolerant");
+
+#ifndef WITH_GMP
+ params.error_message_add(NodeWarningType::Error,
+ TIP_("Disabled, Blender was compiled without GMP"));
+#endif
+
+ Vector<const Mesh *> meshes;
+ Vector<const float4x4 *> transforms;
+
+ GeometrySet set_a;
+ if (operation == GEO_NODE_BOOLEAN_DIFFERENCE) {
+ set_a = params.extract_input<GeometrySet>("Geometry 1");
+ /* Note that it technically wouldn't be necessary to realize the instances for the first
+ * geometry input, but the boolean code expects the first shape for the difference operation
+ * to be a single mesh. */
+ set_a = geometry_set_realize_instances(set_a);
+ const Mesh *mesh_in_a = set_a.get_mesh_for_read();
+ if (mesh_in_a != nullptr) {
+ meshes.append(mesh_in_a);
+ transforms.append(nullptr);
+ }
}
- /* TODO: Boolean does support an input of multiple meshes. Currently they must all be
- * converted to BMesh before running the operation though. D9957 will make it possible
- * to use the mesh structure directly. */
- geometry_set_in_a = geometry_set_realize_instances(geometry_set_in_a);
- geometry_set_in_b = geometry_set_realize_instances(geometry_set_in_b);
-
- const Mesh *mesh_in_a = geometry_set_in_a.get_mesh_for_read();
- const Mesh *mesh_in_b = geometry_set_in_b.get_mesh_for_read();
+ /* The instance transform matrices are owned by the instance group, so we have to
+ * keep all of them around for use during the boolean operation. */
+ Vector<bke::GeometryInstanceGroup> set_groups;
+ Vector<GeometrySet> geometry_sets = params.extract_multi_input<GeometrySet>("Geometry 2");
+ for (const GeometrySet &geometry_set : geometry_sets) {
+ bke::geometry_set_gather_instances(geometry_set, set_groups);
+ }
- if (mesh_in_a == nullptr || mesh_in_b == nullptr) {
- if (operation == GEO_NODE_BOOLEAN_UNION) {
- if (mesh_in_a != nullptr) {
- params.set_output("Geometry", geometry_set_in_a);
+ for (const bke::GeometryInstanceGroup &set_group : set_groups) {
+ const Mesh *mesh_in = set_group.geometry_set.get_mesh_for_read();
+ if (mesh_in != nullptr) {
+ meshes.append_n_times(mesh_in, set_group.transforms.size());
+ for (const int i : set_group.transforms.index_range()) {
+ transforms.append(set_group.transforms.begin() + i);
}
- else {
- params.set_output("Geometry", geometry_set_in_b);
- }
- }
- else {
- params.set_output("Geometry", geometry_set_in_a);
}
- return;
}
- Mesh *mesh_out = mesh_boolean_calc(mesh_in_a, mesh_in_b, operation);
- geometry_set_out = GeometrySet::create_with_mesh(mesh_out);
+ Mesh *result = blender::meshintersect::direct_mesh_boolean(
+ meshes, transforms, float4x4::identity(), {}, use_self, hole_tolerant, operation);
- params.set_output("Geometry", std::move(geometry_set_out));
+ params.set_output("Geometry", GeometrySet::create_with_mesh(result));
}
+
} // namespace blender::nodes
void register_node_type_geo_boolean()
@@ -162,6 +140,8 @@ void register_node_type_geo_boolean()
geo_node_type_base(&ntype, GEO_NODE_BOOLEAN, "Boolean", NODE_CLASS_GEOMETRY, 0);
node_type_socket_templates(&ntype, geo_node_boolean_in, geo_node_boolean_out);
ntype.draw_buttons = geo_node_boolean_layout;
+ ntype.updatefunc = geo_node_boolean_update;
+ node_type_init(&ntype, geo_node_boolean_init);
ntype.geometry_node_execute = blender::nodes::geo_node_boolean_exec;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc b/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc
new file mode 100644
index 00000000000..83d3558a7cd
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc
@@ -0,0 +1,177 @@
+/*
+ * 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.
+ */
+
+#include "BKE_spline.hh"
+#include "BKE_volume.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_bounding_box_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_bounding_box_out[] = {
+ {SOCK_GEOMETRY, N_("Bounding Box")},
+ {SOCK_VECTOR, N_("Min")},
+ {SOCK_VECTOR, N_("Max")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+using bke::GeometryInstanceGroup;
+
+static void compute_min_max_from_position_and_transform(const GeometryComponent &component,
+ Span<float4x4> transforms,
+ float3 &r_min,
+ float3 &r_max)
+{
+ GVArray_Typed<float3> positions = component.attribute_get_for_read<float3>(
+ "position", ATTR_DOMAIN_POINT, {0, 0, 0});
+
+ for (const float4x4 &transform : transforms) {
+ for (const int i : positions.index_range()) {
+ const float3 position = positions[i];
+ const float3 transformed_position = transform * position;
+ minmax_v3v3_v3(r_min, r_max, transformed_position);
+ }
+ }
+}
+
+static void compute_min_max_from_volume_and_transforms(const VolumeComponent &volume_component,
+ Span<float4x4> transforms,
+ float3 &r_min,
+ float3 &r_max)
+{
+#ifdef WITH_OPENVDB
+ const Volume *volume = volume_component.get_for_read();
+ if (volume == nullptr) {
+ return;
+ }
+ for (const int i : IndexRange(BKE_volume_num_grids(volume))) {
+ const VolumeGrid *volume_grid = BKE_volume_grid_get_for_read(volume, i);
+ openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid);
+
+ for (const float4x4 &transform : transforms) {
+ openvdb::GridBase::ConstPtr instance_grid = BKE_volume_grid_shallow_transform(grid,
+ transform);
+ float3 grid_min = float3(FLT_MAX);
+ float3 grid_max = float3(-FLT_MAX);
+ if (BKE_volume_grid_bounds(instance_grid, grid_min, grid_max)) {
+ DO_MIN(grid_min, r_min);
+ DO_MAX(grid_max, r_max);
+ }
+ }
+ }
+#else
+ UNUSED_VARS(volume_component, transforms, r_min, r_max);
+#endif
+}
+
+static void compute_min_max_from_curve_and_transforms(const CurveComponent &curve_component,
+ Span<float4x4> transforms,
+ float3 &r_min,
+ float3 &r_max)
+{
+ const CurveEval *curve = curve_component.get_for_read();
+ if (curve == nullptr) {
+ return;
+ }
+ for (const SplinePtr &spline : curve->splines()) {
+ Span<float3> positions = spline->evaluated_positions();
+
+ for (const float4x4 &transform : transforms) {
+ for (const int i : positions.index_range()) {
+ const float3 position = positions[i];
+ const float3 transformed_position = transform * position;
+ minmax_v3v3_v3(r_min, r_max, transformed_position);
+ }
+ }
+ }
+}
+
+static void compute_geometry_set_instances_boundbox(const GeometrySet &geometry_set,
+ float3 &r_min,
+ float3 &r_max)
+{
+ Vector<GeometryInstanceGroup> set_groups;
+ bke::geometry_set_gather_instances(geometry_set, set_groups);
+
+ for (const GeometryInstanceGroup &set_group : set_groups) {
+ const GeometrySet &set = set_group.geometry_set;
+ Span<float4x4> transforms = set_group.transforms;
+
+ if (set.has<PointCloudComponent>()) {
+ compute_min_max_from_position_and_transform(
+ *set.get_component_for_read<PointCloudComponent>(), transforms, r_min, r_max);
+ }
+ if (set.has<MeshComponent>()) {
+ compute_min_max_from_position_and_transform(
+ *set.get_component_for_read<MeshComponent>(), transforms, r_min, r_max);
+ }
+ if (set.has<VolumeComponent>()) {
+ compute_min_max_from_volume_and_transforms(
+ *set.get_component_for_read<VolumeComponent>(), transforms, r_min, r_max);
+ }
+ if (set.has<CurveComponent>()) {
+ compute_min_max_from_curve_and_transforms(
+ *set.get_component_for_read<CurveComponent>(), transforms, r_min, r_max);
+ }
+ }
+}
+
+static void geo_node_bounding_box_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ float3 min = float3(FLT_MAX);
+ float3 max = float3(-FLT_MAX);
+
+ if (geometry_set.has_instances()) {
+ compute_geometry_set_instances_boundbox(geometry_set, min, max);
+ }
+ else {
+ geometry_set.compute_boundbox_without_instances(&min, &max);
+ }
+
+ if (min == float3(FLT_MAX)) {
+ params.set_output("Bounding Box", GeometrySet());
+ params.set_output("Min", float3(0));
+ params.set_output("Max", float3(0));
+ }
+ else {
+ const float3 scale = max - min;
+ const float3 center = min + scale / 2.0f;
+ Mesh *mesh = create_cube_mesh(1.0f);
+ transform_mesh(mesh, center, float3(0), scale);
+ params.set_output("Bounding Box", GeometrySet::create_with_mesh(mesh));
+ params.set_output("Min", min);
+ params.set_output("Max", max);
+ }
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_bounding_box()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_BOUNDING_BOX, "Bounding Box", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(&ntype, geo_node_bounding_box_in, geo_node_bounding_box_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_bounding_box_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc b/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc
index 8991a26ba4b..b2dc4661e9c 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc
@@ -14,8 +14,6 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "node_geometry_util.hh"
-
#include "BLI_math_matrix.h"
#include "DNA_collection_types.h"
@@ -23,8 +21,19 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "node_geometry_util.hh"
+
static bNodeSocketTemplate geo_node_collection_info_in[] = {
- {SOCK_COLLECTION, N_("Collection")},
+ {SOCK_COLLECTION,
+ N_("Collection"),
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ PROP_NONE,
+ SOCK_HIDE_LABEL},
{-1, ""},
};
@@ -40,11 +49,17 @@ static void geo_node_collection_info_layout(uiLayout *layout, bContext *UNUSED(C
namespace blender::nodes {
+static void geo_node_collection_info_node_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryCollectionInfo *data = (NodeGeometryCollectionInfo *)MEM_callocN(
+ sizeof(NodeGeometryCollectionInfo), __func__);
+ data->transform_space = GEO_NODE_TRANSFORM_SPACE_ORIGINAL;
+ node->storage = data;
+}
+
static void geo_node_collection_info_exec(GeoNodeExecParams params)
{
- bke::PersistentCollectionHandle collection_handle =
- params.extract_input<bke::PersistentCollectionHandle>("Collection");
- Collection *collection = params.handle_map().lookup(collection_handle);
+ Collection *collection = params.get_input<Collection *>("Collection");
GeometrySet geometry_set_out;
@@ -58,10 +73,6 @@ static void geo_node_collection_info_exec(GeoNodeExecParams params)
const bool transform_space_relative = (node_storage->transform_space ==
GEO_NODE_TRANSFORM_SPACE_RELATIVE);
- InstancedData instance;
- instance.type = INSTANCE_DATA_TYPE_COLLECTION;
- instance.data.collection = collection;
-
InstancesComponent &instances = geometry_set_out.get_component_for_write<InstancesComponent>();
float transform_mat[4][4];
@@ -73,17 +84,11 @@ static void geo_node_collection_info_exec(GeoNodeExecParams params)
mul_m4_m4_pre(transform_mat, self_object->imat);
}
- instances.add_instance(instance, transform_mat, -1);
- params.set_output("Geometry", geometry_set_out);
-}
+ const int handle = instances.add_reference(*collection);
+ instances.add_instance(handle, transform_mat, -1);
-static void geo_node_collection_info_node_init(bNodeTree *UNUSED(tree), bNode *node)
-{
- NodeGeometryCollectionInfo *data = (NodeGeometryCollectionInfo *)MEM_callocN(
- sizeof(NodeGeometryCollectionInfo), __func__);
- data->transform_space = GEO_NODE_TRANSFORM_SPACE_ORIGINAL;
- node->storage = data;
+ params.set_output("Geometry", geometry_set_out);
}
} // namespace blender::nodes
diff --git a/source/blender/nodes/geometry/nodes/node_geo_common.cc b/source/blender/nodes/geometry/nodes/node_geo_common.cc
index 441ad6bdc13..e2bb7e9f939 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_common.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_common.cc
@@ -43,3 +43,17 @@ void register_node_type_geo_group(void)
nodeRegisterType(&ntype);
}
+
+void register_node_type_geo_custom_group(bNodeType *ntype)
+{
+ /* These methods can be overridden but need a default implementation otherwise. */
+ if (ntype->poll == nullptr) {
+ ntype->poll = geo_node_poll_default;
+ }
+ if (ntype->insert_link == nullptr) {
+ ntype->insert_link = node_insert_link_default;
+ }
+ if (ntype->update_internal_links == nullptr) {
+ ntype->update_internal_links = node_update_internal_links_default;
+ }
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc
new file mode 100644
index 00000000000..b1b17a321b8
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc
@@ -0,0 +1,321 @@
+/*
+ * 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.
+ */
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_pointcloud_types.h"
+
+#include "BKE_material.h"
+#include "BKE_mesh.h"
+#include "BKE_spline.hh"
+
+#include "node_geometry_util.hh"
+
+#ifdef WITH_BULLET
+# include "RBI_hull_api.h"
+#endif
+
+static bNodeSocketTemplate geo_node_convex_hull_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_convex_hull_out[] = {
+ {SOCK_GEOMETRY, N_("Convex Hull")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+using bke::GeometryInstanceGroup;
+
+#ifdef WITH_BULLET
+
+static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords)
+{
+ plConvexHull hull = plConvexHullCompute((float(*)[3])coords.data(), coords.size());
+
+ const int num_verts = plConvexHullNumVertices(hull);
+ const int num_faces = num_verts <= 2 ? 0 : plConvexHullNumFaces(hull);
+ const int num_loops = num_verts <= 2 ? 0 : plConvexHullNumLoops(hull);
+ /* Half as many edges as loops, because the mesh is manifold. */
+ const int num_edges = num_verts == 2 ? 1 : num_verts < 2 ? 0 : num_loops / 2;
+
+ /* Create Mesh *result with proper capacity. */
+ Mesh *result;
+ if (mesh) {
+ result = BKE_mesh_new_nomain_from_template(
+ mesh, num_verts, num_edges, 0, num_loops, num_faces);
+ }
+ else {
+ result = BKE_mesh_new_nomain(num_verts, num_edges, 0, num_loops, num_faces);
+ BKE_id_material_eval_ensure_default_slot(&result->id);
+ }
+
+ /* Copy vertices. */
+ for (const int i : IndexRange(num_verts)) {
+ float co[3];
+ int original_index;
+ plConvexHullGetVertex(hull, i, co, &original_index);
+
+ if (original_index >= 0 && original_index < coords.size()) {
+# if 0 /* Disabled because it only works for meshes, not predictable enough. */
+ /* Copy custom data on vertices, like vertex groups etc. */
+ if (mesh && original_index < mesh->totvert) {
+ CustomData_copy_data(&mesh->vdata, &result->vdata, (int)original_index, (int)i, 1);
+ }
+# endif
+ /* Copy the position of the original point. */
+ copy_v3_v3(result->mvert[i].co, co);
+ }
+ else {
+ BLI_assert(!"Unexpected new vertex in hull output");
+ }
+ }
+
+ /* Copy edges and loops. */
+
+ /* NOTE: ConvexHull from Bullet uses a half-edge data structure
+ * for its mesh. To convert that, each half-edge needs to be converted
+ * to a loop and edges need to be created from that. */
+ Array<MLoop> mloop_src(num_loops);
+ uint edge_index = 0;
+ for (const int i : IndexRange(num_loops)) {
+ int v_from;
+ int v_to;
+ plConvexHullGetLoop(hull, i, &v_from, &v_to);
+
+ mloop_src[i].v = (uint)v_from;
+ /* Add edges for ascending order loops only. */
+ if (v_from < v_to) {
+ MEdge &edge = result->medge[edge_index];
+ edge.v1 = v_from;
+ edge.v2 = v_to;
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
+
+ /* Write edge index into both loops that have it. */
+ int reverse_index = plConvexHullGetReversedLoopIndex(hull, i);
+ mloop_src[i].e = edge_index;
+ mloop_src[reverse_index].e = edge_index;
+ edge_index++;
+ }
+ }
+ if (num_edges == 1) {
+ /* In this case there are no loops. */
+ MEdge &edge = result->medge[0];
+ edge.v1 = 0;
+ edge.v2 = 1;
+ edge.flag |= ME_EDGEDRAW | ME_EDGERENDER | ME_LOOSEEDGE;
+ edge_index++;
+ }
+ BLI_assert(edge_index == num_edges);
+
+ /* Copy faces. */
+ Array<int> loops;
+ int j = 0;
+ MLoop *loop = result->mloop;
+ for (const int i : IndexRange(num_faces)) {
+ const int len = plConvexHullGetFaceSize(hull, i);
+
+ BLI_assert(len > 2);
+
+ /* Get face loop indices. */
+ loops.reinitialize(len);
+ plConvexHullGetFaceLoops(hull, i, loops.data());
+
+ MPoly &face = result->mpoly[i];
+ face.loopstart = j;
+ face.totloop = len;
+ for (const int k : IndexRange(len)) {
+ MLoop &src_loop = mloop_src[loops[k]];
+ loop->v = src_loop.v;
+ loop->e = src_loop.e;
+ loop++;
+ }
+ j += len;
+ }
+
+ plConvexHullDelete(hull);
+
+ BKE_mesh_calc_normals(result);
+ return result;
+}
+
+static Mesh *compute_hull(const GeometrySet &geometry_set)
+{
+ int span_count = 0;
+ int count = 0;
+ int total_size = 0;
+
+ Span<float3> positions_span;
+
+ if (geometry_set.has_mesh()) {
+ count++;
+ const MeshComponent *component = geometry_set.get_component_for_read<MeshComponent>();
+ total_size += component->attribute_domain_size(ATTR_DOMAIN_POINT);
+ }
+
+ if (geometry_set.has_pointcloud()) {
+ count++;
+ span_count++;
+ const PointCloudComponent *component =
+ geometry_set.get_component_for_read<PointCloudComponent>();
+ GVArray_Typed<float3> varray = component->attribute_get_for_read<float3>(
+ "position", ATTR_DOMAIN_POINT, {0, 0, 0});
+ total_size += varray->size();
+ positions_span = varray->get_internal_span();
+ }
+
+ if (geometry_set.has_curve()) {
+ const CurveEval &curve = *geometry_set.get_curve_for_read();
+ for (const SplinePtr &spline : curve.splines()) {
+ positions_span = spline->evaluated_positions();
+ total_size += positions_span.size();
+ count++;
+ span_count++;
+ }
+ }
+
+ if (count == 0) {
+ return nullptr;
+ }
+
+ /* If there is only one positions virtual array and it is already contiguous, avoid copying
+ * all of the positions and instead pass the span directly to the convex hull function. */
+ if (span_count == 1 && count == 1) {
+ return hull_from_bullet(nullptr, positions_span);
+ }
+
+ Array<float3> positions(total_size);
+ int offset = 0;
+
+ if (geometry_set.has_mesh()) {
+ const MeshComponent *component = geometry_set.get_component_for_read<MeshComponent>();
+ GVArray_Typed<float3> varray = component->attribute_get_for_read<float3>(
+ "position", ATTR_DOMAIN_POINT, {0, 0, 0});
+ varray->materialize(positions.as_mutable_span().slice(offset, varray.size()));
+ offset += varray.size();
+ }
+
+ if (geometry_set.has_pointcloud()) {
+ const PointCloudComponent *component =
+ geometry_set.get_component_for_read<PointCloudComponent>();
+ GVArray_Typed<float3> varray = component->attribute_get_for_read<float3>(
+ "position", ATTR_DOMAIN_POINT, {0, 0, 0});
+ varray->materialize(positions.as_mutable_span().slice(offset, varray.size()));
+ offset += varray.size();
+ }
+
+ if (geometry_set.has_curve()) {
+ const CurveEval &curve = *geometry_set.get_curve_for_read();
+ for (const SplinePtr &spline : curve.splines()) {
+ Span<float3> array = spline->evaluated_positions();
+ positions.as_mutable_span().slice(offset, array.size()).copy_from(array);
+ offset += array.size();
+ }
+ }
+
+ return hull_from_bullet(geometry_set.get_mesh_for_read(), positions);
+}
+
+static void read_positions(const GeometryComponent &component,
+ Span<float4x4> transforms,
+ Vector<float3> *r_coords)
+{
+ GVArray_Typed<float3> positions = component.attribute_get_for_read<float3>(
+ "position", ATTR_DOMAIN_POINT, {0, 0, 0});
+
+ /* NOTE: could use convex hull operation here to
+ * cut out some vertices, before accumulating,
+ * but can also be done by the user beforehand. */
+
+ r_coords->reserve(r_coords->size() + positions.size() * transforms.size());
+ for (const float4x4 &transform : transforms) {
+ for (const int i : positions.index_range()) {
+ const float3 position = positions[i];
+ const float3 transformed_position = transform * position;
+ r_coords->append(transformed_position);
+ }
+ }
+}
+
+static void read_curve_positions(const CurveEval &curve,
+ Span<float4x4> transforms,
+ Vector<float3> *r_coords)
+{
+ const Array<int> offsets = curve.evaluated_point_offsets();
+ const int total_size = offsets.last();
+ r_coords->reserve(r_coords->size() + total_size * transforms.size());
+ for (const SplinePtr &spline : curve.splines()) {
+ Span<float3> positions = spline->evaluated_positions();
+ for (const float4x4 &transform : transforms) {
+ for (const float3 &position : positions) {
+ r_coords->append(transform * position);
+ }
+ }
+ }
+}
+
+#endif /* WITH_BULLET */
+
+static void geo_node_convex_hull_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+#ifdef WITH_BULLET
+ Mesh *mesh = nullptr;
+ if (geometry_set.has_instances()) {
+ Vector<GeometryInstanceGroup> set_groups;
+ bke::geometry_set_gather_instances(geometry_set, set_groups);
+
+ Vector<float3> coords;
+
+ for (const GeometryInstanceGroup &set_group : set_groups) {
+ const GeometrySet &set = set_group.geometry_set;
+ Span<float4x4> transforms = set_group.transforms;
+
+ if (set.has_pointcloud()) {
+ read_positions(*set.get_component_for_read<PointCloudComponent>(), transforms, &coords);
+ }
+ if (set.has_mesh()) {
+ read_positions(*set.get_component_for_read<MeshComponent>(), transforms, &coords);
+ }
+ if (set.has_curve()) {
+ read_curve_positions(*set.get_curve_for_read(), transforms, &coords);
+ }
+ }
+ mesh = hull_from_bullet(nullptr, coords);
+ }
+ else {
+ mesh = compute_hull(geometry_set);
+ }
+ params.set_output("Convex Hull", GeometrySet::create_with_mesh(mesh));
+#else
+ params.set_output("Convex Hull", geometry_set);
+#endif /* WITH_BULLET */
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_convex_hull()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_CONVEX_HULL, "Convex Hull", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(&ntype, geo_node_convex_hull_in, geo_node_convex_hull_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_convex_hull_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc
new file mode 100644
index 00000000000..306085e3b75
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+#include "BKE_spline.hh"
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_curve_length_in[] = {
+ {SOCK_GEOMETRY, N_("Curve")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_curve_length_out[] = {
+ {SOCK_FLOAT, N_("Length")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+static void geo_node_curve_length_exec(GeoNodeExecParams params)
+{
+ GeometrySet curve_set = params.extract_input<GeometrySet>("Curve");
+ curve_set = bke::geometry_set_realize_instances(curve_set);
+ if (!curve_set.has_curve()) {
+ params.set_output("Length", 0.0f);
+ return;
+ }
+ const CurveEval &curve = *curve_set.get_curve_for_read();
+ float length = 0.0f;
+ for (const SplinePtr &spline : curve.splines()) {
+ length += spline->length();
+ }
+ params.set_output("Length", length);
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_curve_length()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_CURVE_LENGTH, "Curve Length", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(&ntype, geo_node_curve_length_in, geo_node_curve_length_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_curve_length_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc
new file mode 100644
index 00000000000..e879ec624c0
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc
@@ -0,0 +1,211 @@
+/*
+ * 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.
+ */
+
+#include "BLI_array.hh"
+#include "BLI_task.hh"
+#include "BLI_timeit.hh"
+
+#include "BKE_attribute_math.hh"
+#include "BKE_spline.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+using blender::fn::GVArray_For_GSpan;
+using blender::fn::GVArray_For_Span;
+using blender::fn::GVArray_Typed;
+
+static bNodeSocketTemplate geo_node_curve_resample_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_INT, N_("Count"), 10, 0, 0, 0, 1, 100000},
+ {SOCK_FLOAT, N_("Length"), 0.1f, 0.0f, 0.0f, 0.0f, 0.001f, FLT_MAX, PROP_DISTANCE},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_curve_resample_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+static void geo_node_curve_resample_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
+}
+
+static void geo_node_curve_resample_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryCurveResample *data = (NodeGeometryCurveResample *)MEM_callocN(
+ sizeof(NodeGeometryCurveResample), __func__);
+
+ data->mode = GEO_NODE_CURVE_SAMPLE_COUNT;
+ node->storage = data;
+}
+
+static void geo_node_curve_resample_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryCurveResample &node_storage = *(NodeGeometryCurveResample *)node->storage;
+ const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode;
+
+ bNodeSocket *count_socket = ((bNodeSocket *)node->inputs.first)->next;
+ bNodeSocket *length_socket = count_socket->next;
+
+ nodeSetSocketAvailability(count_socket, mode == GEO_NODE_CURVE_SAMPLE_COUNT);
+ nodeSetSocketAvailability(length_socket, mode == GEO_NODE_CURVE_SAMPLE_LENGTH);
+}
+
+namespace blender::nodes {
+
+struct SampleModeParam {
+ GeometryNodeCurveSampleMode mode;
+ std::optional<float> length;
+ std::optional<int> count;
+};
+
+static SplinePtr resample_spline(const Spline &input_spline, const int count)
+{
+ std::unique_ptr<PolySpline> output_spline = std::make_unique<PolySpline>();
+ output_spline->set_cyclic(input_spline.is_cyclic());
+ output_spline->normal_mode = input_spline.normal_mode;
+
+ if (input_spline.evaluated_edges_size() < 1 || count == 1) {
+ output_spline->add_point(input_spline.positions().first(),
+ input_spline.tilts().first(),
+ input_spline.radii().first());
+ output_spline->attributes.reallocate(1);
+ return output_spline;
+ }
+
+ output_spline->resize(count);
+
+ Array<float> uniform_samples = input_spline.sample_uniform_index_factors(count);
+
+ input_spline.sample_based_on_index_factors<float3>(
+ input_spline.evaluated_positions(), uniform_samples, output_spline->positions());
+
+ input_spline.sample_based_on_index_factors<float>(
+ input_spline.interpolate_to_evaluated_points(input_spline.radii()),
+ uniform_samples,
+ output_spline->radii());
+
+ input_spline.sample_based_on_index_factors<float>(
+ input_spline.interpolate_to_evaluated_points(input_spline.tilts()),
+ uniform_samples,
+ output_spline->tilts());
+
+ output_spline->attributes.reallocate(count);
+ input_spline.attributes.foreach_attribute(
+ [&](StringRefNull name, const AttributeMetaData &meta_data) {
+ std::optional<GSpan> input_attribute = input_spline.attributes.get_for_read(name);
+ BLI_assert(input_attribute);
+ if (!output_spline->attributes.create(name, meta_data.data_type)) {
+ BLI_assert_unreachable();
+ return false;
+ }
+ std::optional<GMutableSpan> output_attribute = output_spline->attributes.get_for_write(
+ name);
+ if (!output_attribute) {
+ BLI_assert_unreachable();
+ return false;
+ }
+
+ input_spline.sample_based_on_index_factors(
+ *input_spline.interpolate_to_evaluated_points(*input_attribute),
+ uniform_samples,
+ *output_attribute);
+
+ return true;
+ },
+ ATTR_DOMAIN_POINT);
+
+ return output_spline;
+}
+
+static std::unique_ptr<CurveEval> resample_curve(const CurveEval &input_curve,
+ const SampleModeParam &mode_param)
+{
+ std::unique_ptr<CurveEval> output_curve = std::make_unique<CurveEval>();
+
+ for (const SplinePtr &spline : input_curve.splines()) {
+ if (mode_param.mode == GEO_NODE_CURVE_SAMPLE_COUNT) {
+ BLI_assert(mode_param.count);
+ output_curve->add_spline(resample_spline(*spline, *mode_param.count));
+ }
+ else if (mode_param.mode == GEO_NODE_CURVE_SAMPLE_LENGTH) {
+ BLI_assert(mode_param.length);
+ const float length = spline->length();
+ const int count = std::max(int(length / *mode_param.length), 1);
+ output_curve->add_spline(resample_spline(*spline, count));
+ }
+ }
+
+ output_curve->attributes = input_curve.attributes;
+
+ return output_curve;
+}
+
+static void geo_node_resample_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ geometry_set = bke::geometry_set_realize_instances(geometry_set);
+
+ if (!geometry_set.has_curve()) {
+ params.set_output("Geometry", GeometrySet());
+ return;
+ }
+
+ const CurveEval &input_curve = *geometry_set.get_curve_for_read();
+ NodeGeometryCurveResample &node_storage = *(NodeGeometryCurveResample *)params.node().storage;
+ const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode;
+ SampleModeParam mode_param;
+ mode_param.mode = mode;
+ if (mode == GEO_NODE_CURVE_SAMPLE_COUNT) {
+ const int count = params.extract_input<int>("Count");
+ if (count < 1) {
+ params.set_output("Geometry", GeometrySet());
+ return;
+ }
+ mode_param.count.emplace(count);
+ }
+ else if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) {
+ /* Don't allow asymptotic count increase for low resolution values. */
+ const float resolution = std::max(params.extract_input<float>("Length"), 0.0001f);
+ mode_param.length.emplace(resolution);
+ }
+
+ std::unique_ptr<CurveEval> output_curve = resample_curve(input_curve, mode_param);
+
+ params.set_output("Geometry", GeometrySet::create_with_curve(output_curve.release()));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_curve_resample()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_CURVE_RESAMPLE, "Resample Curve", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(&ntype, geo_node_curve_resample_in, geo_node_curve_resample_out);
+ ntype.draw_buttons = geo_node_curve_resample_layout;
+ node_type_storage(
+ &ntype, "NodeGeometryCurveResample", node_free_standard_storage, node_copy_standard_storage);
+ node_type_init(&ntype, geo_node_curve_resample_init);
+ node_type_update(&ntype, geo_node_curve_resample_update);
+ ntype.geometry_node_execute = blender::nodes::geo_node_resample_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc
new file mode 100644
index 00000000000..b6f04352929
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc
@@ -0,0 +1,313 @@
+/*
+ * 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.
+ */
+
+#include "BLI_array.hh"
+#include "BLI_float4x4.hh"
+#include "BLI_timeit.hh"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_material.h"
+#include "BKE_mesh.h"
+#include "BKE_spline.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_curve_to_mesh_in[] = {
+ {SOCK_GEOMETRY, N_("Curve")},
+ {SOCK_GEOMETRY, N_("Profile Curve")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_curve_to_mesh_out[] = {
+ {SOCK_GEOMETRY, N_("Mesh")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+static void vert_extrude_to_mesh_data(const Spline &spline,
+ const float3 profile_vert,
+ MutableSpan<MVert> r_verts,
+ MutableSpan<MEdge> r_edges,
+ int &vert_offset,
+ int &edge_offset)
+{
+ Span<float3> positions = spline.evaluated_positions();
+
+ for (const int i : IndexRange(positions.size() - 1)) {
+ MEdge &edge = r_edges[edge_offset++];
+ edge.v1 = vert_offset + i;
+ edge.v2 = vert_offset + i + 1;
+ edge.flag = ME_LOOSEEDGE;
+ }
+
+ if (spline.is_cyclic() && spline.evaluated_edges_size() > 1) {
+ MEdge &edge = r_edges[edge_offset++];
+ edge.v1 = vert_offset;
+ edge.v2 = vert_offset + positions.size() - 1;
+ edge.flag = ME_LOOSEEDGE;
+ }
+
+ for (const int i : positions.index_range()) {
+ MVert &vert = r_verts[vert_offset++];
+ copy_v3_v3(vert.co, positions[i] + profile_vert);
+ }
+}
+
+static void mark_edges_sharp(MutableSpan<MEdge> edges)
+{
+ for (MEdge &edge : edges) {
+ edge.flag |= ME_SHARP;
+ }
+}
+
+static void spline_extrude_to_mesh_data(const Spline &spline,
+ const Spline &profile_spline,
+ MutableSpan<MVert> r_verts,
+ MutableSpan<MEdge> r_edges,
+ MutableSpan<MLoop> r_loops,
+ MutableSpan<MPoly> r_polys,
+ int &vert_offset,
+ int &edge_offset,
+ int &loop_offset,
+ int &poly_offset)
+{
+ const int spline_vert_len = spline.evaluated_points_size();
+ const int spline_edge_len = spline.evaluated_edges_size();
+ const int profile_vert_len = profile_spline.evaluated_points_size();
+ const int profile_edge_len = profile_spline.evaluated_edges_size();
+ if (spline_vert_len == 0) {
+ return;
+ }
+
+ if (profile_vert_len == 1) {
+ vert_extrude_to_mesh_data(spline,
+ profile_spline.evaluated_positions()[0],
+ r_verts,
+ r_edges,
+ vert_offset,
+ edge_offset);
+ return;
+ }
+
+ /* Add the edges running along the length of the curve, starting at each profile vertex. */
+ const int spline_edges_start = edge_offset;
+ for (const int i_profile : IndexRange(profile_vert_len)) {
+ for (const int i_ring : IndexRange(spline_edge_len)) {
+ const int i_next_ring = (i_ring == spline_vert_len - 1) ? 0 : i_ring + 1;
+
+ const int ring_vert_offset = vert_offset + profile_vert_len * i_ring;
+ const int next_ring_vert_offset = vert_offset + profile_vert_len * i_next_ring;
+
+ MEdge &edge = r_edges[edge_offset++];
+ edge.v1 = ring_vert_offset + i_profile;
+ edge.v2 = next_ring_vert_offset + i_profile;
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
+ }
+ }
+
+ /* Add the edges running along each profile ring. */
+ const int profile_edges_start = edge_offset;
+ for (const int i_ring : IndexRange(spline_vert_len)) {
+ const int ring_vert_offset = vert_offset + profile_vert_len * i_ring;
+
+ for (const int i_profile : IndexRange(profile_edge_len)) {
+ const int i_next_profile = (i_profile == profile_vert_len - 1) ? 0 : i_profile + 1;
+
+ MEdge &edge = r_edges[edge_offset++];
+ edge.v1 = ring_vert_offset + i_profile;
+ edge.v2 = ring_vert_offset + i_next_profile;
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
+ }
+ }
+
+ /* Calculate poly and corner indices. */
+ for (const int i_ring : IndexRange(spline_edge_len)) {
+ const int i_next_ring = (i_ring == spline_vert_len - 1) ? 0 : i_ring + 1;
+
+ const int ring_vert_offset = vert_offset + profile_vert_len * i_ring;
+ const int next_ring_vert_offset = vert_offset + profile_vert_len * i_next_ring;
+
+ const int ring_edge_start = profile_edges_start + profile_edge_len * i_ring;
+ const int next_ring_edge_offset = profile_edges_start + profile_edge_len * i_next_ring;
+
+ for (const int i_profile : IndexRange(profile_edge_len)) {
+ const int i_next_profile = (i_profile == profile_vert_len - 1) ? 0 : i_profile + 1;
+
+ const int spline_edge_start = spline_edges_start + spline_edge_len * i_profile;
+ const int next_spline_edge_start = spline_edges_start + spline_edge_len * i_next_profile;
+
+ MPoly &poly = r_polys[poly_offset++];
+ poly.loopstart = loop_offset;
+ poly.totloop = 4;
+ poly.flag = ME_SMOOTH;
+
+ MLoop &loop_a = r_loops[loop_offset++];
+ loop_a.v = ring_vert_offset + i_profile;
+ loop_a.e = ring_edge_start + i_profile;
+ MLoop &loop_b = r_loops[loop_offset++];
+ loop_b.v = ring_vert_offset + i_next_profile;
+ loop_b.e = next_spline_edge_start + i_ring;
+ MLoop &loop_c = r_loops[loop_offset++];
+ loop_c.v = next_ring_vert_offset + i_next_profile;
+ loop_c.e = next_ring_edge_offset + i_profile;
+ MLoop &loop_d = r_loops[loop_offset++];
+ loop_d.v = next_ring_vert_offset + i_profile;
+ loop_d.e = spline_edge_start + i_ring;
+ }
+ }
+
+ /* Calculate the positions of each profile ring profile along the spline. */
+ Span<float3> positions = spline.evaluated_positions();
+ Span<float3> tangents = spline.evaluated_tangents();
+ Span<float3> normals = spline.evaluated_normals();
+ Span<float3> profile_positions = profile_spline.evaluated_positions();
+
+ GVArray_Typed<float> radii = spline.interpolate_to_evaluated_points(spline.radii());
+ for (const int i_ring : IndexRange(spline_vert_len)) {
+ float4x4 point_matrix = float4x4::from_normalized_axis_data(
+ positions[i_ring], normals[i_ring], tangents[i_ring]);
+
+ point_matrix.apply_scale(radii[i_ring]);
+
+ for (const int i_profile : IndexRange(profile_vert_len)) {
+ MVert &vert = r_verts[vert_offset++];
+ copy_v3_v3(vert.co, point_matrix * profile_positions[i_profile]);
+ }
+ }
+
+ /* Mark edge loops from sharp vector control points sharp. */
+ if (profile_spline.type() == Spline::Type::Bezier) {
+ const BezierSpline &bezier_spline = static_cast<const BezierSpline &>(profile_spline);
+ Span<int> control_point_offsets = bezier_spline.control_point_offsets();
+ for (const int i : IndexRange(bezier_spline.size())) {
+ if (bezier_spline.point_is_sharp(i)) {
+ mark_edges_sharp(r_edges.slice(
+ spline_edges_start + spline_edge_len * control_point_offsets[i], spline_edge_len));
+ }
+ }
+ }
+}
+
+static Mesh *curve_to_mesh_calculate(const CurveEval &curve, const CurveEval &profile_curve)
+{
+ int profile_vert_total = 0;
+ int profile_edge_total = 0;
+ for (const SplinePtr &profile_spline : profile_curve.splines()) {
+ profile_vert_total += profile_spline->evaluated_points_size();
+ profile_edge_total += profile_spline->evaluated_edges_size();
+ }
+
+ int vert_total = 0;
+ int edge_total = 0;
+ int poly_total = 0;
+ for (const SplinePtr &spline : curve.splines()) {
+ const int spline_vert_len = spline->evaluated_points_size();
+ const int spline_edge_len = spline->evaluated_edges_size();
+ vert_total += spline_vert_len * profile_vert_total;
+ poly_total += spline_edge_len * profile_edge_total;
+
+ /* Add the ring edges, with one ring for every curve vertex, and the edge loops
+ * that run along the length of the curve, starting on the first profile. */
+ edge_total += profile_edge_total * spline_vert_len + profile_vert_total * spline_edge_len;
+ }
+ const int corner_total = poly_total * 4;
+
+ if (vert_total == 0) {
+ return nullptr;
+ }
+
+ Mesh *mesh = BKE_mesh_new_nomain(vert_total, edge_total, 0, corner_total, poly_total);
+ BKE_id_material_eval_ensure_default_slot(&mesh->id);
+ MutableSpan<MVert> verts{mesh->mvert, mesh->totvert};
+ MutableSpan<MEdge> edges{mesh->medge, mesh->totedge};
+ MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop};
+ MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly};
+ mesh->flag |= ME_AUTOSMOOTH;
+ mesh->smoothresh = DEG2RADF(180.0f);
+
+ int vert_offset = 0;
+ int edge_offset = 0;
+ int loop_offset = 0;
+ int poly_offset = 0;
+ for (const SplinePtr &spline : curve.splines()) {
+ for (const SplinePtr &profile_spline : profile_curve.splines()) {
+ spline_extrude_to_mesh_data(*spline,
+ *profile_spline,
+ verts,
+ edges,
+ loops,
+ polys,
+ vert_offset,
+ edge_offset,
+ loop_offset,
+ poly_offset);
+ }
+ }
+
+ BKE_mesh_calc_normals(mesh);
+
+ return mesh;
+}
+
+static CurveEval get_curve_single_vert()
+{
+ CurveEval curve;
+ std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>();
+ spline->add_point(float3(0), 0, 0.0f);
+ curve.add_spline(std::move(spline));
+
+ return curve;
+}
+
+static void geo_node_curve_to_mesh_exec(GeoNodeExecParams params)
+{
+ GeometrySet curve_set = params.extract_input<GeometrySet>("Curve");
+ GeometrySet profile_set = params.extract_input<GeometrySet>("Profile Curve");
+
+ curve_set = bke::geometry_set_realize_instances(curve_set);
+ profile_set = bke::geometry_set_realize_instances(profile_set);
+
+ if (!curve_set.has_curve()) {
+ params.set_output("Mesh", GeometrySet());
+ return;
+ }
+
+ const CurveEval *profile_curve = profile_set.get_curve_for_read();
+
+ static const CurveEval vert_curve = get_curve_single_vert();
+
+ Mesh *mesh = curve_to_mesh_calculate(*curve_set.get_curve_for_read(),
+ (profile_curve == nullptr) ? vert_curve : *profile_curve);
+ params.set_output("Mesh", GeometrySet::create_with_mesh(mesh));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_curve_to_mesh()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_CURVE_TO_MESH, "Curve to Mesh", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(&ntype, geo_node_curve_to_mesh_in, geo_node_curve_to_mesh_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_curve_to_mesh_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc
new file mode 100644
index 00000000000..910adc467d6
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc
@@ -0,0 +1,678 @@
+/*
+ * 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.
+ */
+
+#include "BLI_array.hh"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_customdata.h"
+#include "BKE_mesh.h"
+#include "BKE_pointcloud.h"
+#include "BKE_spline.hh"
+
+#include "node_geometry_util.hh"
+
+using blender::bke::CustomDataAttributes;
+
+/* Code from the mask modifier in MOD_mask.cc. */
+extern void copy_masked_vertices_to_new_mesh(const Mesh &src_mesh,
+ Mesh &dst_mesh,
+ blender::Span<int> vertex_map);
+extern void copy_masked_edges_to_new_mesh(const Mesh &src_mesh,
+ Mesh &dst_mesh,
+ blender::Span<int> vertex_map,
+ blender::Span<int> edge_map);
+extern void copy_masked_polys_to_new_mesh(const Mesh &src_mesh,
+ Mesh &dst_mesh,
+ blender::Span<int> vertex_map,
+ blender::Span<int> edge_map,
+ blender::Span<int> masked_poly_indices,
+ blender::Span<int> new_loop_starts);
+
+static bNodeSocketTemplate geo_node_delete_geometry_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_STRING, N_("Selection")},
+ {SOCK_BOOLEAN, N_("Invert")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_delete_geometry_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+template<typename T> static void copy_data(Span<T> data, MutableSpan<T> r_data, IndexMask mask)
+{
+ for (const int i_out : mask.index_range()) {
+ r_data[i_out] = data[mask[i_out]];
+ }
+}
+
+static void spline_copy_builtin_attributes(const Spline &spline,
+ Spline &r_spline,
+ const IndexMask mask)
+{
+ copy_data(spline.positions(), r_spline.positions(), mask);
+ copy_data(spline.radii(), r_spline.radii(), mask);
+ copy_data(spline.tilts(), r_spline.tilts(), mask);
+ switch (spline.type()) {
+ case Spline::Type::Poly:
+ break;
+ case Spline::Type::Bezier: {
+ const BezierSpline &src = static_cast<const BezierSpline &>(spline);
+ BezierSpline &dst = static_cast<BezierSpline &>(r_spline);
+ copy_data(src.handle_positions_left(), dst.handle_positions_left(), mask);
+ copy_data(src.handle_positions_right(), dst.handle_positions_right(), mask);
+ copy_data(src.handle_types_left(), dst.handle_types_left(), mask);
+ copy_data(src.handle_types_right(), dst.handle_types_right(), mask);
+ break;
+ }
+ case Spline::Type::NURBS: {
+ const NURBSpline &src = static_cast<const NURBSpline &>(spline);
+ NURBSpline &dst = static_cast<NURBSpline &>(r_spline);
+ copy_data(src.weights(), dst.weights(), mask);
+ break;
+ }
+ }
+}
+
+static void copy_dynamic_attributes(const CustomDataAttributes &src,
+ CustomDataAttributes &dst,
+ const IndexMask mask)
+{
+ src.foreach_attribute(
+ [&](StringRefNull name, const AttributeMetaData &meta_data) {
+ std::optional<GSpan> src_attribute = src.get_for_read(name);
+ BLI_assert(src_attribute);
+
+ if (!dst.create(name, meta_data.data_type)) {
+ /* Since the source spline of the same type had the attribute, adding it should work. */
+ BLI_assert_unreachable();
+ }
+
+ std::optional<GMutableSpan> new_attribute = dst.get_for_write(name);
+ BLI_assert(new_attribute);
+
+ attribute_math::convert_to_static_type(new_attribute->type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ copy_data(src_attribute->typed<T>(), new_attribute->typed<T>(), mask);
+ });
+ return true;
+ },
+ ATTR_DOMAIN_POINT);
+}
+
+static SplinePtr spline_delete(const Spline &spline, const IndexMask mask)
+{
+ SplinePtr new_spline = spline.copy_settings();
+ new_spline->resize(mask.size());
+
+ spline_copy_builtin_attributes(spline, *new_spline, mask);
+ copy_dynamic_attributes(spline.attributes, new_spline->attributes, mask);
+
+ return new_spline;
+}
+
+static std::unique_ptr<CurveEval> curve_delete(const CurveEval &input_curve,
+ const StringRef name,
+ const bool invert)
+{
+ Span<SplinePtr> input_splines = input_curve.splines();
+ std::unique_ptr<CurveEval> output_curve = std::make_unique<CurveEval>();
+
+ /* Keep track of which splines were copied to the result to copy spline domain attributes. */
+ Vector<int64_t> copied_splines;
+
+ if (input_curve.attributes.get_for_read(name)) {
+ GVArray_Typed<bool> selection = input_curve.attributes.get_for_read<bool>(name, false);
+ for (const int i : input_splines.index_range()) {
+ if (selection[i] == invert) {
+ output_curve->add_spline(input_splines[i]->copy());
+ copied_splines.append(i);
+ }
+ }
+ }
+ else {
+ /* Reuse index vector for each spline. */
+ Vector<int64_t> indices_to_copy;
+
+ for (const int i : input_splines.index_range()) {
+ const Spline &spline = *input_splines[i];
+ GVArray_Typed<bool> selection = spline.attributes.get_for_read<bool>(name, false);
+
+ indices_to_copy.clear();
+ for (const int i_point : IndexRange(spline.size())) {
+ if (selection[i_point] == invert) {
+ indices_to_copy.append(i_point);
+ }
+ }
+
+ /* Avoid creating an empty spline. */
+ if (indices_to_copy.is_empty()) {
+ continue;
+ }
+
+ SplinePtr new_spline = spline_delete(spline, IndexMask(indices_to_copy));
+ output_curve->add_spline(std::move(new_spline));
+ copied_splines.append(i);
+ }
+ }
+
+ if (copied_splines.is_empty()) {
+ return {};
+ }
+
+ output_curve->attributes.reallocate(output_curve->splines().size());
+ copy_dynamic_attributes(
+ input_curve.attributes, output_curve->attributes, IndexMask(copied_splines));
+
+ return output_curve;
+}
+
+static void delete_curve_selection(const CurveComponent &in_component,
+ CurveComponent &r_component,
+ const StringRef selection_name,
+ const bool invert)
+{
+ std::unique_ptr<CurveEval> r_curve = curve_delete(
+ *in_component.get_for_read(), selection_name, invert);
+ if (r_curve) {
+ r_component.replace(r_curve.release());
+ }
+ else {
+ r_component.clear();
+ }
+}
+
+static void delete_point_cloud_selection(const PointCloudComponent &in_component,
+ PointCloudComponent &out_component,
+ const StringRef selection_name,
+ const bool invert)
+{
+ const GVArray_Typed<bool> selection_attribute = in_component.attribute_get_for_read<bool>(
+ selection_name, ATTR_DOMAIN_POINT, false);
+ VArray_Span<bool> selection{selection_attribute};
+
+ const int total = selection.count(invert);
+ if (total == 0) {
+ out_component.clear();
+ return;
+ }
+ out_component.replace(BKE_pointcloud_new_nomain(total));
+
+ /* Invert the inversion, because this deletes the selected points instead of keeping them. */
+ copy_point_attributes_based_on_mask(in_component, out_component, selection, !invert);
+}
+
+static void compute_selected_vertices_from_vertex_selection(const VArray<bool> &vertex_selection,
+ const bool invert,
+ MutableSpan<int> r_vertex_map,
+ uint *r_num_selected_vertices)
+{
+ BLI_assert(vertex_selection.size() == r_vertex_map.size());
+
+ uint num_selected_vertices = 0;
+ for (const int i : r_vertex_map.index_range()) {
+ if (vertex_selection[i] != invert) {
+ r_vertex_map[i] = num_selected_vertices;
+ num_selected_vertices++;
+ }
+ else {
+ r_vertex_map[i] = -1;
+ }
+ }
+
+ *r_num_selected_vertices = num_selected_vertices;
+}
+
+static void compute_selected_edges_from_vertex_selection(const Mesh &mesh,
+ const VArray<bool> &vertex_selection,
+ const bool invert,
+ MutableSpan<int> r_edge_map,
+ uint *r_num_selected_edges)
+{
+ BLI_assert(mesh.totedge == r_edge_map.size());
+
+ uint num_selected_edges = 0;
+ for (const int i : IndexRange(mesh.totedge)) {
+ const MEdge &edge = mesh.medge[i];
+
+ /* Only add the edge if both vertices will be in the new mesh. */
+ if (vertex_selection[edge.v1] != invert && vertex_selection[edge.v2] != invert) {
+ r_edge_map[i] = num_selected_edges;
+ num_selected_edges++;
+ }
+ else {
+ r_edge_map[i] = -1;
+ }
+ }
+
+ *r_num_selected_edges = num_selected_edges;
+}
+
+static void compute_selected_polygons_from_vertex_selection(const Mesh &mesh,
+ const VArray<bool> &vertex_selection,
+ const bool invert,
+ Vector<int> &r_selected_poly_indices,
+ Vector<int> &r_loop_starts,
+ uint *r_num_selected_polys,
+ uint *r_num_selected_loops)
+{
+ BLI_assert(mesh.totvert == vertex_selection.size());
+
+ r_selected_poly_indices.reserve(mesh.totpoly);
+ r_loop_starts.reserve(mesh.totloop);
+
+ uint num_selected_loops = 0;
+ for (const int i : IndexRange(mesh.totpoly)) {
+ const MPoly &poly_src = mesh.mpoly[i];
+
+ bool all_verts_in_selection = true;
+ Span<MLoop> loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop);
+ for (const MLoop &loop : loops_src) {
+ if (vertex_selection[loop.v] == invert) {
+ all_verts_in_selection = false;
+ break;
+ }
+ }
+
+ if (all_verts_in_selection) {
+ r_selected_poly_indices.append_unchecked(i);
+ r_loop_starts.append_unchecked(num_selected_loops);
+ num_selected_loops += poly_src.totloop;
+ }
+ }
+
+ *r_num_selected_polys = r_selected_poly_indices.size();
+ *r_num_selected_loops = num_selected_loops;
+}
+
+/**
+ * Checks for every edge if it is in `edge_selection`. If it is, then the two vertices of the edge
+ * are kept along with the edge.
+ */
+static void compute_selected_vertices_and_edges_from_edge_selection(
+ const Mesh &mesh,
+ const VArray<bool> &edge_selection,
+ const bool invert,
+ MutableSpan<int> r_vertex_map,
+ MutableSpan<int> r_edge_map,
+ uint *r_num_selected_vertices,
+ uint *r_num_selected_edges)
+{
+ BLI_assert(mesh.totedge == edge_selection.size());
+
+ uint num_selected_edges = 0;
+ uint num_selected_vertices = 0;
+ for (const int i : IndexRange(mesh.totedge)) {
+ const MEdge &edge = mesh.medge[i];
+ if (edge_selection[i] != invert) {
+ r_edge_map[i] = num_selected_edges;
+ num_selected_edges++;
+ if (r_vertex_map[edge.v1] == -1) {
+ r_vertex_map[edge.v1] = num_selected_vertices;
+ num_selected_vertices++;
+ }
+ if (r_vertex_map[edge.v2] == -1) {
+ r_vertex_map[edge.v2] = num_selected_vertices;
+ num_selected_vertices++;
+ }
+ }
+ else {
+ r_edge_map[i] = -1;
+ }
+ }
+
+ *r_num_selected_vertices = num_selected_vertices;
+ *r_num_selected_edges = num_selected_edges;
+}
+
+/**
+ * Checks for every polygon if all the edges are in `edge_selection`. If they are, then that
+ * polygon is kept.
+ */
+static void compute_selected_polygons_from_edge_selection(const Mesh &mesh,
+ const VArray<bool> &edge_selection,
+ const bool invert,
+ Vector<int> &r_selected_poly_indices,
+ Vector<int> &r_loop_starts,
+ uint *r_num_selected_polys,
+ uint *r_num_selected_loops)
+{
+ r_selected_poly_indices.reserve(mesh.totpoly);
+ r_loop_starts.reserve(mesh.totloop);
+
+ uint num_selected_loops = 0;
+ for (const int i : IndexRange(mesh.totpoly)) {
+ const MPoly &poly_src = mesh.mpoly[i];
+
+ bool all_edges_in_selection = true;
+ Span<MLoop> loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop);
+ for (const MLoop &loop : loops_src) {
+ if (edge_selection[loop.e] == invert) {
+ all_edges_in_selection = false;
+ break;
+ }
+ }
+
+ if (all_edges_in_selection) {
+ r_selected_poly_indices.append_unchecked(i);
+ r_loop_starts.append_unchecked(num_selected_loops);
+ num_selected_loops += poly_src.totloop;
+ }
+ }
+
+ *r_num_selected_polys = r_selected_poly_indices.size();
+ *r_num_selected_loops = num_selected_loops;
+}
+
+/**
+ * Checks for every vertex if it is in `vertex_selection`. The polygons and edges are kept if all
+ * vertices of that polygon or edge are in the selection.
+ */
+static void compute_selected_mesh_data_from_vertex_selection(const Mesh &mesh,
+ const VArray<bool> &vertex_selection,
+ const bool invert,
+ MutableSpan<int> r_vertex_map,
+ MutableSpan<int> r_edge_map,
+ Vector<int> &r_selected_poly_indices,
+ Vector<int> &r_loop_starts,
+ uint *r_num_selected_vertices,
+ uint *r_num_selected_edges,
+ uint *r_num_selected_polys,
+ uint *r_num_selected_loops)
+{
+ compute_selected_vertices_from_vertex_selection(
+ vertex_selection, invert, r_vertex_map, r_num_selected_vertices);
+
+ compute_selected_edges_from_vertex_selection(
+ mesh, vertex_selection, invert, r_edge_map, r_num_selected_edges);
+
+ compute_selected_polygons_from_vertex_selection(mesh,
+ vertex_selection,
+ invert,
+ r_selected_poly_indices,
+ r_loop_starts,
+ r_num_selected_polys,
+ r_num_selected_loops);
+}
+
+/**
+ * Checks for every edge if it is in `edge_selection`. If it is, the vertices belonging to
+ * that edge are kept as well. The polygons are kept if all edges are in the selection.
+ */
+static void compute_selected_mesh_data_from_edge_selection(const Mesh &mesh,
+ const VArray<bool> &edge_selection,
+ const bool invert,
+ MutableSpan<int> r_vertex_map,
+ MutableSpan<int> r_edge_map,
+ Vector<int> &r_selected_poly_indices,
+ Vector<int> &r_loop_starts,
+ uint *r_num_selected_vertices,
+ uint *r_num_selected_edges,
+ uint *r_num_selected_polys,
+ uint *r_num_selected_loops)
+{
+ r_vertex_map.fill(-1);
+ compute_selected_vertices_and_edges_from_edge_selection(mesh,
+ edge_selection,
+ invert,
+ r_vertex_map,
+ r_edge_map,
+ r_num_selected_vertices,
+ r_num_selected_edges);
+ compute_selected_polygons_from_edge_selection(mesh,
+ edge_selection,
+ invert,
+ r_selected_poly_indices,
+ r_loop_starts,
+ r_num_selected_polys,
+ r_num_selected_loops);
+}
+
+/**
+ * Checks for every polygon if it is in `poly_selection`. If it is, the edges and vertices
+ * belonging to that polygon are kept as well.
+ */
+static void compute_selected_mesh_data_from_poly_selection(const Mesh &mesh,
+ const VArray<bool> &poly_selection,
+ const bool invert,
+ MutableSpan<int> r_vertex_map,
+ MutableSpan<int> r_edge_map,
+ Vector<int> &r_selected_poly_indices,
+ Vector<int> &r_loop_starts,
+ uint *r_num_selected_vertices,
+ uint *r_num_selected_edges,
+ uint *r_num_selected_polys,
+ uint *r_num_selected_loops)
+{
+ BLI_assert(mesh.totpoly == poly_selection.size());
+ BLI_assert(mesh.totedge == r_edge_map.size());
+ r_vertex_map.fill(-1);
+ r_edge_map.fill(-1);
+
+ r_selected_poly_indices.reserve(mesh.totpoly);
+ r_loop_starts.reserve(mesh.totloop);
+
+ uint num_selected_loops = 0;
+ uint num_selected_vertices = 0;
+ uint num_selected_edges = 0;
+ for (const int i : IndexRange(mesh.totpoly)) {
+ const MPoly &poly_src = mesh.mpoly[i];
+ /* We keep this one. */
+ if (poly_selection[i] != invert) {
+ r_selected_poly_indices.append_unchecked(i);
+ r_loop_starts.append_unchecked(num_selected_loops);
+ num_selected_loops += poly_src.totloop;
+
+ /* Add the vertices and the edges. */
+ Span<MLoop> loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop);
+ for (const MLoop &loop : loops_src) {
+ /* Check first if it has not yet been added. */
+ if (r_vertex_map[loop.v] == -1) {
+ r_vertex_map[loop.v] = num_selected_vertices;
+ num_selected_vertices++;
+ }
+ if (r_edge_map[loop.e] == -1) {
+ r_edge_map[loop.e] = num_selected_edges;
+ num_selected_edges++;
+ }
+ }
+ }
+ }
+ *r_num_selected_vertices = num_selected_vertices;
+ *r_num_selected_edges = num_selected_edges;
+ *r_num_selected_polys = r_selected_poly_indices.size();
+ *r_num_selected_loops = num_selected_loops;
+}
+
+using FillMapsFunction = void (*)(const Mesh &mesh,
+ const VArray<bool> &selection,
+ const bool invert,
+ MutableSpan<int> r_vertex_map,
+ MutableSpan<int> r_edge_map,
+ Vector<int> &r_selected_poly_indices,
+ Vector<int> &r_loop_starts,
+ uint *r_num_selected_vertices,
+ uint *r_num_selected_edges,
+ uint *r_num_selected_polys,
+ uint *r_num_selected_loops);
+
+/**
+ * Delete the parts of the mesh that are in the selection. The `fill_maps_function`
+ * depends on the selection type: vertices, edges or faces.
+ */
+static Mesh *delete_mesh_selection(const Mesh &mesh_in,
+ const VArray<bool> &selection,
+ const bool invert,
+ FillMapsFunction fill_maps_function)
+{
+ Array<int> vertex_map(mesh_in.totvert);
+ uint num_selected_vertices;
+
+ Array<int> edge_map(mesh_in.totedge);
+ uint num_selected_edges;
+
+ Vector<int> selected_poly_indices;
+ Vector<int> new_loop_starts;
+ uint num_selected_polys;
+ uint num_selected_loops;
+
+ /* Fill all the maps based on the selection. We delete everything
+ * in the selection instead of keeping it, so we need to invert it. */
+ fill_maps_function(mesh_in,
+ selection,
+ !invert,
+ vertex_map,
+ edge_map,
+ selected_poly_indices,
+ new_loop_starts,
+ &num_selected_vertices,
+ &num_selected_edges,
+ &num_selected_polys,
+ &num_selected_loops);
+
+ Mesh *result = BKE_mesh_new_nomain_from_template(&mesh_in,
+ num_selected_vertices,
+ num_selected_edges,
+ 0,
+ num_selected_loops,
+ num_selected_polys);
+
+ /* Copy the selected parts of the mesh over to the new mesh. */
+ copy_masked_vertices_to_new_mesh(mesh_in, *result, vertex_map);
+ copy_masked_edges_to_new_mesh(mesh_in, *result, vertex_map, edge_map);
+ copy_masked_polys_to_new_mesh(
+ mesh_in, *result, vertex_map, edge_map, selected_poly_indices, new_loop_starts);
+ BKE_mesh_calc_edges_loose(result);
+ /* Tag to recalculate normals later. */
+ result->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
+
+ return result;
+}
+
+static AttributeDomain get_mesh_selection_domain(MeshComponent &component, const StringRef name)
+{
+ std::optional<AttributeMetaData> selection_attribute = component.attribute_get_meta_data(name);
+ if (!selection_attribute) {
+ /* The node will not do anything in this case, but this function must return something. */
+ return ATTR_DOMAIN_POINT;
+ }
+
+ /* Corners can't be deleted separately, so interpolate corner attributes
+ * to the face domain. Note that this choice is somewhat arbitrary. */
+ if (selection_attribute->domain == ATTR_DOMAIN_CORNER) {
+ return ATTR_DOMAIN_FACE;
+ }
+
+ return selection_attribute->domain;
+}
+
+static void delete_mesh_selection(MeshComponent &component,
+ const Mesh &mesh_in,
+ const StringRef selection_name,
+ const bool invert)
+{
+ /* Figure out the best domain to use. */
+ const AttributeDomain selection_domain = get_mesh_selection_domain(component, selection_name);
+
+ /* This already checks if the attribute exists, and displays a warning in that case. */
+ GVArray_Typed<bool> selection = component.attribute_get_for_read<bool>(
+ selection_name, selection_domain, false);
+
+ /* Check if there is anything to delete. */
+ bool delete_nothing = true;
+ for (const int i : selection.index_range()) {
+ if (selection[i] != invert) {
+ delete_nothing = false;
+ break;
+ }
+ }
+ if (delete_nothing) {
+ return;
+ }
+
+ Mesh *mesh_out;
+ switch (selection_domain) {
+ case ATTR_DOMAIN_POINT:
+ mesh_out = delete_mesh_selection(
+ mesh_in, selection, invert, compute_selected_mesh_data_from_vertex_selection);
+ break;
+ case ATTR_DOMAIN_EDGE:
+ mesh_out = delete_mesh_selection(
+ mesh_in, selection, invert, compute_selected_mesh_data_from_edge_selection);
+ break;
+ case ATTR_DOMAIN_FACE:
+ mesh_out = delete_mesh_selection(
+ mesh_in, selection, invert, compute_selected_mesh_data_from_poly_selection);
+ break;
+ default:
+ BLI_assert_unreachable();
+ mesh_out = nullptr;
+ break;
+ }
+ component.replace_mesh_but_keep_vertex_group_names(mesh_out);
+}
+
+static void geo_node_delete_geometry_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ geometry_set = bke::geometry_set_realize_instances(geometry_set);
+
+ const bool invert = params.extract_input<bool>("Invert");
+ const std::string selection_name = params.extract_input<std::string>("Selection");
+ if (selection_name.empty()) {
+ params.set_output("Geometry", std::move(geometry_set));
+ return;
+ }
+
+ GeometrySet out_set(geometry_set);
+ if (geometry_set.has<PointCloudComponent>()) {
+ delete_point_cloud_selection(*geometry_set.get_component_for_read<PointCloudComponent>(),
+ out_set.get_component_for_write<PointCloudComponent>(),
+ selection_name,
+ invert);
+ }
+ if (geometry_set.has<MeshComponent>()) {
+ delete_mesh_selection(out_set.get_component_for_write<MeshComponent>(),
+ *geometry_set.get_mesh_for_read(),
+ selection_name,
+ invert);
+ }
+ if (geometry_set.has<CurveComponent>()) {
+ delete_curve_selection(*geometry_set.get_component_for_read<CurveComponent>(),
+ out_set.get_component_for_write<CurveComponent>(),
+ selection_name,
+ invert);
+ }
+
+ params.set_output("Geometry", std::move(out_set));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_delete_geometry()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_DELETE_GEOMETRY, "Delete Geometry", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(&ntype, geo_node_delete_geometry_in, geo_node_delete_geometry_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_delete_geometry_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc
index 1c794d1f43e..740b828d503 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc
@@ -14,9 +14,6 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "BLI_math_base.h"
-#include "BLI_math_rotation.h"
-
#include "DNA_modifier_types.h"
#include "node_geometry_util.hh"
@@ -47,6 +44,7 @@ static bNodeSocketTemplate geo_node_edge_split_out[] = {
};
namespace blender::nodes {
+
static void geo_node_edge_split_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
@@ -85,6 +83,7 @@ static void geo_node_edge_split_exec(GeoNodeExecParams params)
params.set_output("Geometry", std::move(geometry_set));
}
+
} // namespace blender::nodes
void register_node_type_geo_edge_split()
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_material.cc b/source/blender/nodes/geometry/nodes/node_geo_input_material.cc
new file mode 100644
index 00000000000..6bad71a62a2
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_material.cc
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_input_material_out[] = {
+ {SOCK_MATERIAL, N_("Material")},
+ {-1, ""},
+};
+
+static void geo_node_input_material_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "material", 0, "", ICON_NONE);
+}
+
+namespace blender::nodes {
+
+static void geo_node_input_material_exec(GeoNodeExecParams params)
+{
+ Material *material = (Material *)params.node().id;
+ params.set_output("Material", material);
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_input_material()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_INPUT_MATERIAL, "Material", NODE_CLASS_INPUT, 0);
+ node_type_socket_templates(&ntype, nullptr, geo_node_input_material_out);
+ ntype.draw_buttons = geo_node_input_material_layout;
+ ntype.geometry_node_execute = blender::nodes::geo_node_input_material_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc b/source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc
index 667beaa1722..ec875b9f983 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc
@@ -14,10 +14,10 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "node_geometry_util.hh"
-
#include "DEG_depsgraph_query.h"
+#include "node_geometry_util.hh"
+
static bNodeSocketTemplate geo_node_is_viewport_out[] = {
{SOCK_BOOLEAN, N_("Is Viewport")},
{-1, ""},
diff --git a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
index 52512769a47..adfd924f185 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
@@ -14,9 +14,11 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#include "BKE_material.h"
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
#include "BKE_pointcloud.h"
+#include "BKE_spline.hh"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
@@ -56,6 +58,8 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<const MeshComponent
int64_t cd_dirty_edge = 0;
int64_t cd_dirty_loop = 0;
+ VectorSet<Material *> materials;
+
for (const MeshComponent *mesh_component : src_components) {
const Mesh *mesh = mesh_component->get_for_read();
totverts += mesh->totvert;
@@ -66,12 +70,22 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<const MeshComponent
cd_dirty_poly |= mesh->runtime.cd_dirty_poly;
cd_dirty_edge |= mesh->runtime.cd_dirty_edge;
cd_dirty_loop |= mesh->runtime.cd_dirty_loop;
+
+ for (const int slot_index : IndexRange(mesh->totcol)) {
+ Material *material = mesh->mat[slot_index];
+ materials.add(material);
+ }
}
const Mesh *first_input_mesh = src_components[0]->get_for_read();
Mesh *new_mesh = BKE_mesh_new_nomain(totverts, totedges, 0, totloops, totpolys);
BKE_mesh_copy_settings(new_mesh, first_input_mesh);
+ for (const int i : IndexRange(materials.size())) {
+ Material *material = materials[i];
+ BKE_id_material_eval_assign(&new_mesh->id, i + 1, material);
+ }
+
new_mesh->runtime.cd_dirty_vert = cd_dirty_vert;
new_mesh->runtime.cd_dirty_poly = cd_dirty_poly;
new_mesh->runtime.cd_dirty_edge = cd_dirty_edge;
@@ -87,6 +101,13 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<const MeshComponent
continue;
}
+ Array<int> material_index_map(mesh->totcol);
+ for (const int i : IndexRange(mesh->totcol)) {
+ Material *material = mesh->mat[i];
+ const int new_material_index = materials.index_of(material);
+ material_index_map[i] = new_material_index;
+ }
+
for (const int i : IndexRange(mesh->totvert)) {
const MVert &old_vert = mesh->mvert[i];
MVert &new_vert = new_mesh->mvert[vert_offset + i];
@@ -112,6 +133,13 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<const MeshComponent
MPoly &new_poly = new_mesh->mpoly[poly_offset + i];
new_poly = old_poly;
new_poly.loopstart += loop_offset;
+ if (old_poly.mat_nr >= 0 && old_poly.mat_nr < mesh->totcol) {
+ new_poly.mat_nr = material_index_map[new_poly.mat_nr];
+ }
+ else {
+ /* The material index was invalid before. */
+ new_poly.mat_nr = 0;
+ }
}
vert_offset += mesh->totvert;
@@ -149,10 +177,10 @@ static void determine_final_data_type_and_domain(Span<const GeometryComponent *>
Vector<CustomDataType> data_types;
Vector<AttributeDomain> domains;
for (const GeometryComponent *component : components) {
- ReadAttributePtr attribute = component->attribute_try_get_for_read(attribute_name);
+ ReadAttributeLookup attribute = component->attribute_try_get_for_read(attribute_name);
if (attribute) {
- data_types.append(attribute->custom_data_type());
- domains.append(attribute->domain());
+ data_types.append(bke::cpp_type_to_custom_data_type(attribute.varray->type()));
+ domains.append(attribute.domain);
}
}
@@ -164,7 +192,7 @@ static void fill_new_attribute(Span<const GeometryComponent *> src_components,
StringRef attribute_name,
const CustomDataType data_type,
const AttributeDomain domain,
- fn::GMutableSpan dst_span)
+ GMutableSpan dst_span)
{
const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(data_type);
BLI_assert(cpp_type != nullptr);
@@ -175,10 +203,10 @@ static void fill_new_attribute(Span<const GeometryComponent *> src_components,
if (domain_size == 0) {
continue;
}
- ReadAttributePtr read_attribute = component->attribute_get_for_read(
+ GVArrayPtr read_attribute = component->attribute_get_for_read(
attribute_name, domain, data_type, nullptr);
- fn::GSpan src_span = read_attribute->get_span();
+ GVArray_GSpan src_span{*read_attribute};
const void *src_buffer = src_span.data();
void *dst_buffer = dst_span[offset];
cpp_type->copy_to_initialized_n(src_buffer, dst_buffer, domain_size);
@@ -201,16 +229,14 @@ static void join_attributes(Span<const GeometryComponent *> src_components,
AttributeDomain domain;
determine_final_data_type_and_domain(src_components, attribute_name, &data_type, &domain);
- OutputAttributePtr write_attribute = result.attribute_try_get_for_output(
+ OutputAttribute write_attribute = result.attribute_try_get_for_output_only(
attribute_name, domain, data_type);
- if (!write_attribute ||
- &write_attribute->cpp_type() != bke::custom_data_type_to_cpp_type(data_type) ||
- write_attribute->domain() != domain) {
+ if (!write_attribute) {
continue;
}
- fn::GMutableSpan dst_span = write_attribute->get_span_for_write_only();
+ GMutableSpan dst_span = write_attribute.as_span();
fill_new_attribute(src_components, attribute_name, data_type, domain, dst_span);
- write_attribute.apply_span_and_save();
+ write_attribute.save();
}
}
@@ -244,12 +270,30 @@ static void join_components(Span<const PointCloudComponent *> src_components, Ge
static void join_components(Span<const InstancesComponent *> src_components, GeometrySet &result)
{
InstancesComponent &dst_component = result.get_component_for_write<InstancesComponent>();
- for (const InstancesComponent *component : src_components) {
- const int size = component->instances_amount();
- Span<InstancedData> instanced_data = component->instanced_data();
- Span<float4x4> transforms = component->transforms();
- for (const int i : IndexRange(size)) {
- dst_component.add_instance(instanced_data[i], transforms[i]);
+
+ int tot_instances = 0;
+ for (const InstancesComponent *src_component : src_components) {
+ tot_instances += src_component->instances_amount();
+ }
+ dst_component.reserve(tot_instances);
+
+ for (const InstancesComponent *src_component : src_components) {
+ Span<InstanceReference> src_references = src_component->references();
+ Array<int> handle_map(src_references.size());
+ for (const int src_handle : src_references.index_range()) {
+ handle_map[src_handle] = dst_component.add_reference(src_references[src_handle]);
+ }
+
+ Span<float4x4> src_transforms = src_component->instance_transforms();
+ Span<int> src_ids = src_component->instance_ids();
+ Span<int> src_reference_handles = src_component->instance_reference_handles();
+
+ for (const int i : src_transforms.index_range()) {
+ const int src_handle = src_reference_handles[i];
+ const int dst_handle = handle_map[src_handle];
+ const float4x4 &transform = src_transforms[i];
+ const int id = src_ids[i];
+ dst_component.add_instance(dst_handle, transform, id);
}
}
}
@@ -262,6 +306,48 @@ static void join_components(Span<const VolumeComponent *> src_components, Geomet
UNUSED_VARS(src_components, dst_component);
}
+static void join_curve_components(MutableSpan<GeometrySet> src_geometry_sets, GeometrySet &result)
+{
+ Vector<CurveComponent *> src_components;
+ for (GeometrySet &geometry_set : src_geometry_sets) {
+ if (geometry_set.has_curve()) {
+ /* Retrieving with write access seems counterintuitive, but it can allow avoiding a copy
+ * in the case where the input spline has no other users, because the splines can be
+ * moved from the source curve rather than copied from a read-only source. Retrieving
+ * the curve for write will make a copy only when it has a user elsewhere. */
+ CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>();
+ src_components.append(&component);
+ }
+ }
+
+ if (src_components.size() == 0) {
+ return;
+ }
+ if (src_components.size() == 1) {
+ result.add(*src_components[0]);
+ return;
+ }
+
+ CurveComponent &dst_component = result.get_component_for_write<CurveComponent>();
+ CurveEval *dst_curve = new CurveEval();
+ for (CurveComponent *component : src_components) {
+ CurveEval *src_curve = component->get_for_write();
+ for (SplinePtr &spline : src_curve->splines()) {
+ dst_curve->add_spline(std::move(spline));
+ }
+ }
+
+ /* For now, remove all custom attributes, since they might have different types,
+ * or an attribute might not exist on all splines. */
+ dst_curve->attributes.reallocate(dst_curve->splines().size());
+ CustomData_reset(&dst_curve->attributes.data);
+ for (SplinePtr &spline : dst_curve->splines()) {
+ CustomData_reset(&spline->attributes.data);
+ }
+
+ dst_component.replace(dst_curve);
+}
+
template<typename Component>
static void join_component_type(Span<GeometrySet> src_geometry_sets, GeometrySet &result)
{
@@ -292,6 +378,7 @@ static void geo_node_join_geometry_exec(GeoNodeExecParams params)
join_component_type<PointCloudComponent>(geometry_sets, geometry_set_result);
join_component_type<InstancesComponent>(geometry_sets, geometry_set_result);
join_component_type<VolumeComponent>(geometry_sets, geometry_set_result);
+ join_curve_components(geometry_sets, geometry_set_result);
params.set_output("Geometry", std::move(geometry_set_result));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc b/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc
new file mode 100644
index 00000000000..02b2d685bdd
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc
@@ -0,0 +1,107 @@
+/*
+ * 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.
+ */
+
+#include "node_geometry_util.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_material.h"
+
+static bNodeSocketTemplate geo_node_material_assign_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_MATERIAL,
+ N_("Material"),
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ PROP_NONE,
+ SOCK_HIDE_LABEL},
+ {SOCK_STRING, N_("Selection")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_material_assign_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+static void assign_material_to_faces(Mesh &mesh, const VArray<bool> &face_mask, Material *material)
+{
+ int new_material_index = -1;
+ for (const int i : IndexRange(mesh.totcol)) {
+ Material *other_material = mesh.mat[i];
+ if (other_material == material) {
+ new_material_index = i;
+ break;
+ }
+ }
+ if (new_material_index == -1) {
+ /* Append a new material index. */
+ new_material_index = mesh.totcol;
+ BKE_id_material_eval_assign(&mesh.id, new_material_index + 1, material);
+ }
+
+ mesh.mpoly = (MPoly *)CustomData_duplicate_referenced_layer(&mesh.pdata, CD_MPOLY, mesh.totpoly);
+ for (const int i : IndexRange(mesh.totpoly)) {
+ if (face_mask[i]) {
+ MPoly &poly = mesh.mpoly[i];
+ poly.mat_nr = new_material_index;
+ }
+ }
+}
+
+static void geo_node_material_assign_exec(GeoNodeExecParams params)
+{
+ Material *material = params.extract_input<Material *>("Material");
+ const std::string mask_name = params.extract_input<std::string>("Selection");
+
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ geometry_set = geometry_set_realize_instances(geometry_set);
+
+ if (geometry_set.has<MeshComponent>()) {
+ MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
+ Mesh *mesh = mesh_component.get_for_write();
+ if (mesh != nullptr) {
+ GVArray_Typed<bool> face_mask = mesh_component.attribute_get_for_read<bool>(
+ mask_name, ATTR_DOMAIN_FACE, true);
+ assign_material_to_faces(*mesh, face_mask, material);
+ }
+ }
+
+ params.set_output("Geometry", std::move(geometry_set));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_material_assign()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_MATERIAL_ASSIGN, "Material Assign", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(&ntype, geo_node_material_assign_in, geo_node_material_assign_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_material_assign_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc b/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc
new file mode 100644
index 00000000000..40ecab98dea
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+
+#include "node_geometry_util.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_material.h"
+
+static bNodeSocketTemplate geo_node_material_replace_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_MATERIAL, N_("Old")},
+ {SOCK_MATERIAL, N_("New")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_material_replace_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+static void geo_node_material_replace_exec(GeoNodeExecParams params)
+{
+ Material *old_material = params.extract_input<Material *>("Old");
+ Material *new_material = params.extract_input<Material *>("New");
+
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ geometry_set = geometry_set_realize_instances(geometry_set);
+
+ if (geometry_set.has<MeshComponent>()) {
+ MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
+ Mesh *mesh = mesh_component.get_for_write();
+ if (mesh != nullptr) {
+ for (const int i : IndexRange(mesh->totcol)) {
+ if (mesh->mat[i] == old_material) {
+ mesh->mat[i] = new_material;
+ }
+ }
+ }
+ }
+
+ params.set_output("Geometry", std::move(geometry_set));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_material_replace()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_MATERIAL_REPLACE, "Material Replace", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(&ntype, geo_node_material_replace_in, geo_node_material_replace_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_material_replace_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc
index 01016ec9b44..2915a17d2c8 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc
@@ -17,6 +17,7 @@
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "BKE_material.h"
#include "BKE_mesh.h"
#include "UI_interface.h"
@@ -119,6 +120,7 @@ static Mesh *create_circle_mesh(const float radius,
0,
circle_corner_total(fill_type, verts_num),
circle_face_total(fill_type, verts_num));
+ BKE_id_material_eval_ensure_default_slot(&mesh->id);
MutableSpan<MVert> verts{mesh->mvert, mesh->totvert};
MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop};
MutableSpan<MEdge> edges{mesh->medge, mesh->totedge};
@@ -161,6 +163,7 @@ static Mesh *create_circle_mesh(const float radius,
MEdge &edge = edges[verts_num + i];
edge.v1 = verts_num;
edge.v2 = i;
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
}
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc
index e9228a2942b..925ed0f8da8 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc
@@ -17,14 +17,12 @@
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
-#include "BKE_lib_id.h"
+#include "BKE_material.h"
#include "BKE_mesh.h"
#include "UI_interface.h"
#include "UI_resources.h"
-#include "bmesh.h"
-
#include "node_geometry_util.hh"
static bNodeSocketTemplate geo_node_mesh_primitive_cone_in[] = {
@@ -189,45 +187,358 @@ static int face_total(const GeometryNodeMeshCircleFillType fill_type,
return face_total;
}
+static void calculate_uvs(Mesh *mesh,
+ const bool top_is_point,
+ const bool bottom_is_point,
+ const int verts_num,
+ const GeometryNodeMeshCircleFillType fill_type)
+{
+ MeshComponent mesh_component;
+ mesh_component.replace(mesh, GeometryOwnershipType::Editable);
+ OutputAttribute_Typed<float2> uv_attribute =
+ mesh_component.attribute_try_get_for_output_only<float2>("uv_map", ATTR_DOMAIN_CORNER);
+ MutableSpan<float2> uvs = uv_attribute.as_span();
+
+ Array<float2> circle(verts_num);
+ float angle = 0.0f;
+ const float angle_delta = 2.0f * M_PI / static_cast<float>(verts_num);
+ for (const int i : IndexRange(verts_num)) {
+ circle[i].x = std::cos(angle) * 0.225f + 0.25f;
+ circle[i].y = std::sin(angle) * 0.225f + 0.25f;
+ angle += angle_delta;
+ }
+
+ int loop_index = 0;
+ if (!top_is_point) {
+ if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
+ for (const int i : IndexRange(verts_num)) {
+ uvs[loop_index++] = circle[i];
+ }
+ }
+ else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
+ for (const int i : IndexRange(verts_num)) {
+ uvs[loop_index++] = circle[i];
+ uvs[loop_index++] = circle[(i + 1) % verts_num];
+ uvs[loop_index++] = float2(0.25f, 0.25f);
+ }
+ }
+ }
+
+ /* Create side corners and faces. */
+ if (!top_is_point && !bottom_is_point) {
+ const float bottom = (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NONE) ? 0.0f : 0.5f;
+ /* Quads connect the top and bottom. */
+ for (const int i : IndexRange(verts_num)) {
+ const float vert = static_cast<float>(i);
+ uvs[loop_index++] = float2(vert / verts_num, bottom);
+ uvs[loop_index++] = float2(vert / verts_num, 1.0f);
+ uvs[loop_index++] = float2((vert + 1.0f) / verts_num, 1.0f);
+ uvs[loop_index++] = float2((vert + 1.0f) / verts_num, bottom);
+ }
+ }
+ else {
+ /* Triangles connect the top and bottom section. */
+ if (!top_is_point) {
+ for (const int i : IndexRange(verts_num)) {
+ uvs[loop_index++] = circle[i] + float2(0.5f, 0.0f);
+ uvs[loop_index++] = float2(0.75f, 0.25f);
+ uvs[loop_index++] = circle[(i + 1) % verts_num] + float2(0.5f, 0.0f);
+ }
+ }
+ else {
+ BLI_assert(!bottom_is_point);
+ for (const int i : IndexRange(verts_num)) {
+ uvs[loop_index++] = circle[i];
+ uvs[loop_index++] = circle[(i + 1) % verts_num];
+ uvs[loop_index++] = float2(0.25f, 0.25f);
+ }
+ }
+ }
+
+ /* Create bottom corners and faces. */
+ if (!bottom_is_point) {
+ if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
+ for (const int i : IndexRange(verts_num)) {
+ /* Go backwards because of reversed face normal. */
+ uvs[loop_index++] = circle[verts_num - 1 - i] + float2(0.5f, 0.0f);
+ }
+ }
+ else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
+ for (const int i : IndexRange(verts_num)) {
+ uvs[loop_index++] = circle[i] + float2(0.5f, 0.0f);
+ uvs[loop_index++] = float2(0.75f, 0.25f);
+ uvs[loop_index++] = circle[(i + 1) % verts_num] + float2(0.5f, 0.0f);
+ }
+ }
+ }
+
+ uv_attribute.save();
+}
+
Mesh *create_cylinder_or_cone_mesh(const float radius_top,
const float radius_bottom,
const float depth,
const int verts_num,
const GeometryNodeMeshCircleFillType fill_type)
{
- const float4x4 transform = float4x4::identity();
-
const bool top_is_point = radius_top == 0.0f;
const bool bottom_is_point = radius_bottom == 0.0f;
+ const float height = depth * 0.5f;
+ /* Handle the case of a line / single point before everything else to avoid
+ * the need to check for it later. */
+ if (top_is_point && bottom_is_point) {
+ const bool single_vertex = height == 0.0f;
+ Mesh *mesh = BKE_mesh_new_nomain(single_vertex ? 1 : 2, single_vertex ? 0 : 1, 0, 0, 0);
+ copy_v3_v3(mesh->mvert[0].co, float3(0.0f, 0.0f, height));
+ if (single_vertex) {
+ const short up[3] = {0, 0, SHRT_MAX};
+ copy_v3_v3_short(mesh->mvert[0].no, up);
+ return mesh;
+ }
+ copy_v3_v3(mesh->mvert[1].co, float3(0.0f, 0.0f, -height));
+ mesh->medge[0].v1 = 0;
+ mesh->medge[0].v2 = 1;
+ mesh->medge[0].flag |= ME_LOOSEEDGE;
+ BKE_mesh_calc_normals(mesh);
+ return mesh;
+ }
- const BMeshCreateParams bmcp = {true};
- const BMAllocTemplate allocsize = {
+ Mesh *mesh = BKE_mesh_new_nomain(
vert_total(fill_type, verts_num, top_is_point, bottom_is_point),
edge_total(fill_type, verts_num, top_is_point, bottom_is_point),
+ 0,
corner_total(fill_type, verts_num, top_is_point, bottom_is_point),
- face_total(fill_type, verts_num, top_is_point, bottom_is_point)};
- BMesh *bm = BM_mesh_create(&allocsize, &bmcp);
-
- const bool cap_end = (fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE);
- const bool cap_tri = (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN);
- BMO_op_callf(bm,
- BMO_FLAG_DEFAULTS,
- "create_cone segments=%i diameter1=%f diameter2=%f cap_ends=%b "
- "cap_tris=%b depth=%f matrix=%m4 calc_uvs=%b",
- verts_num,
- radius_bottom,
- radius_top,
- cap_end,
- cap_tri,
- depth,
- transform.values,
- true);
-
- BMeshToMeshParams params{};
- params.calc_object_remap = false;
- Mesh *mesh = (Mesh *)BKE_id_new_nomain(ID_ME, nullptr);
- BM_mesh_bm_to_me(nullptr, bm, mesh, &params);
- BM_mesh_free(bm);
+ face_total(fill_type, verts_num, top_is_point, bottom_is_point));
+ BKE_id_material_eval_ensure_default_slot(&mesh->id);
+ MutableSpan<MVert> verts{mesh->mvert, mesh->totvert};
+ MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop};
+ MutableSpan<MEdge> edges{mesh->medge, mesh->totedge};
+ MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly};
+
+ /* Calculate vertex positions. */
+ const int top_verts_start = 0;
+ const int bottom_verts_start = top_verts_start + (!top_is_point ? verts_num : 1);
+ float angle = 0.0f;
+ const float angle_delta = 2.0f * M_PI / static_cast<float>(verts_num);
+ for (const int i : IndexRange(verts_num)) {
+ const float x = std::cos(angle);
+ const float y = std::sin(angle);
+ if (!top_is_point) {
+ copy_v3_v3(verts[top_verts_start + i].co, float3(x * radius_top, y * radius_top, height));
+ }
+ if (!bottom_is_point) {
+ copy_v3_v3(verts[bottom_verts_start + i].co,
+ float3(x * radius_bottom, y * radius_bottom, -height));
+ }
+ angle += angle_delta;
+ }
+ if (top_is_point) {
+ copy_v3_v3(verts[top_verts_start].co, float3(0.0f, 0.0f, height));
+ }
+ if (bottom_is_point) {
+ copy_v3_v3(verts[bottom_verts_start].co, float3(0.0f, 0.0f, -height));
+ }
+
+ /* Add center vertices for the triangle fans at the end. */
+ const int top_center_vert_index = bottom_verts_start + (bottom_is_point ? 1 : verts_num);
+ const int bottom_center_vert_index = top_center_vert_index + (top_is_point ? 0 : 1);
+ if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
+ if (!top_is_point) {
+ copy_v3_v3(verts[top_center_vert_index].co, float3(0.0f, 0.0f, height));
+ }
+ if (!bottom_is_point) {
+ copy_v3_v3(verts[bottom_center_vert_index].co, float3(0.0f, 0.0f, -height));
+ }
+ }
+
+ /* Create top edges. */
+ const int top_edges_start = 0;
+ const int top_fan_edges_start = (!top_is_point &&
+ fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) ?
+ top_edges_start + verts_num :
+ top_edges_start;
+ if (!top_is_point) {
+ for (const int i : IndexRange(verts_num)) {
+ MEdge &edge = edges[top_edges_start + i];
+ edge.v1 = top_verts_start + i;
+ edge.v2 = top_verts_start + (i + 1) % verts_num;
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
+ }
+ if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
+ for (const int i : IndexRange(verts_num)) {
+ MEdge &edge = edges[top_fan_edges_start + i];
+ edge.v1 = top_center_vert_index;
+ edge.v2 = top_verts_start + i;
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
+ }
+ }
+ }
+
+ /* Create connecting edges. */
+ const int connecting_edges_start = top_fan_edges_start + (!top_is_point ? verts_num : 0);
+ for (const int i : IndexRange(verts_num)) {
+ MEdge &edge = edges[connecting_edges_start + i];
+ edge.v1 = top_verts_start + (!top_is_point ? i : 0);
+ edge.v2 = bottom_verts_start + (!bottom_is_point ? i : 0);
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
+ }
+
+ /* Create bottom edges. */
+ const int bottom_edges_start = connecting_edges_start + verts_num;
+ const int bottom_fan_edges_start = (!bottom_is_point &&
+ fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) ?
+ bottom_edges_start + verts_num :
+ bottom_edges_start;
+ if (!bottom_is_point) {
+ for (const int i : IndexRange(verts_num)) {
+ MEdge &edge = edges[bottom_edges_start + i];
+ edge.v1 = bottom_verts_start + i;
+ edge.v2 = bottom_verts_start + (i + 1) % verts_num;
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
+ }
+ if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
+ for (const int i : IndexRange(verts_num)) {
+ MEdge &edge = edges[bottom_fan_edges_start + i];
+ edge.v1 = bottom_center_vert_index;
+ edge.v2 = bottom_verts_start + i;
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
+ }
+ }
+ }
+
+ /* Create top corners and faces. */
+ int loop_index = 0;
+ int poly_index = 0;
+ if (!top_is_point) {
+ if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
+ MPoly &poly = polys[poly_index++];
+ poly.loopstart = loop_index;
+ poly.totloop = verts_num;
+
+ for (const int i : IndexRange(verts_num)) {
+ MLoop &loop = loops[loop_index++];
+ loop.v = top_verts_start + i;
+ loop.e = top_edges_start + i;
+ }
+ }
+ else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
+ for (const int i : IndexRange(verts_num)) {
+ MPoly &poly = polys[poly_index++];
+ poly.loopstart = loop_index;
+ poly.totloop = 3;
+
+ MLoop &loop_a = loops[loop_index++];
+ loop_a.v = top_verts_start + i;
+ loop_a.e = top_edges_start + i;
+ MLoop &loop_b = loops[loop_index++];
+ loop_b.v = top_verts_start + (i + 1) % verts_num;
+ loop_b.e = top_fan_edges_start + (i + 1) % verts_num;
+ MLoop &loop_c = loops[loop_index++];
+ loop_c.v = top_center_vert_index;
+ loop_c.e = top_fan_edges_start + i;
+ }
+ }
+ }
+
+ /* Create side corners and faces. */
+ if (!top_is_point && !bottom_is_point) {
+ /* Quads connect the top and bottom. */
+ for (const int i : IndexRange(verts_num)) {
+ MPoly &poly = polys[poly_index++];
+ poly.loopstart = loop_index;
+ poly.totloop = 4;
+
+ MLoop &loop_a = loops[loop_index++];
+ loop_a.v = top_verts_start + i;
+ loop_a.e = connecting_edges_start + i;
+ MLoop &loop_b = loops[loop_index++];
+ loop_b.v = bottom_verts_start + i;
+ loop_b.e = bottom_edges_start + i;
+ MLoop &loop_c = loops[loop_index++];
+ loop_c.v = bottom_verts_start + (i + 1) % verts_num;
+ loop_c.e = connecting_edges_start + (i + 1) % verts_num;
+ MLoop &loop_d = loops[loop_index++];
+ loop_d.v = top_verts_start + (i + 1) % verts_num;
+ loop_d.e = top_edges_start + i;
+ }
+ }
+ else {
+ /* Triangles connect the top and bottom section. */
+ if (!top_is_point) {
+ for (const int i : IndexRange(verts_num)) {
+ MPoly &poly = polys[poly_index++];
+ poly.loopstart = loop_index;
+ poly.totloop = 3;
+
+ MLoop &loop_a = loops[loop_index++];
+ loop_a.v = top_verts_start + i;
+ loop_a.e = connecting_edges_start + i;
+ MLoop &loop_b = loops[loop_index++];
+ loop_b.v = bottom_verts_start;
+ loop_b.e = connecting_edges_start + (i + 1) % verts_num;
+ MLoop &loop_c = loops[loop_index++];
+ loop_c.v = top_verts_start + (i + 1) % verts_num;
+ loop_c.e = top_edges_start + i;
+ }
+ }
+ else {
+ BLI_assert(!bottom_is_point);
+ for (const int i : IndexRange(verts_num)) {
+ MPoly &poly = polys[poly_index++];
+ poly.loopstart = loop_index;
+ poly.totloop = 3;
+
+ MLoop &loop_a = loops[loop_index++];
+ loop_a.v = bottom_verts_start + i;
+ loop_a.e = bottom_edges_start + i;
+ MLoop &loop_b = loops[loop_index++];
+ loop_b.v = bottom_verts_start + (i + 1) % verts_num;
+ loop_b.e = connecting_edges_start + (i + 1) % verts_num;
+ MLoop &loop_c = loops[loop_index++];
+ loop_c.v = top_verts_start;
+ loop_c.e = connecting_edges_start + i;
+ }
+ }
+ }
+
+ /* Create bottom corners and faces. */
+ if (!bottom_is_point) {
+ if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
+ MPoly &poly = polys[poly_index++];
+ poly.loopstart = loop_index;
+ poly.totloop = verts_num;
+
+ for (const int i : IndexRange(verts_num)) {
+ /* Go backwards to reverse surface normal. */
+ MLoop &loop = loops[loop_index++];
+ loop.v = bottom_verts_start + verts_num - 1 - i;
+ loop.e = bottom_edges_start + verts_num - 1 - (i + 1) % verts_num;
+ }
+ }
+ else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
+ for (const int i : IndexRange(verts_num)) {
+ MPoly &poly = polys[poly_index++];
+ poly.loopstart = loop_index;
+ poly.totloop = 3;
+
+ MLoop &loop_a = loops[loop_index++];
+ loop_a.v = bottom_verts_start + i;
+ loop_a.e = bottom_fan_edges_start + i;
+ MLoop &loop_b = loops[loop_index++];
+ loop_b.v = bottom_center_vert_index;
+ loop_b.e = bottom_fan_edges_start + (i + 1) % verts_num;
+ MLoop &loop_c = loops[loop_index++];
+ loop_c.v = bottom_verts_start + (i + 1) % verts_num;
+ loop_c.e = bottom_edges_start + i;
+ }
+ }
+ }
+
+ BKE_mesh_calc_normals(mesh);
+
+ calculate_uvs(mesh, top_is_point, bottom_is_point, verts_num, fill_type);
+
+ BLI_assert(BKE_mesh_is_valid(mesh));
return mesh;
}
@@ -253,6 +564,7 @@ static void geo_node_mesh_primitive_cone_exec(GeoNodeExecParams params)
Mesh *mesh = create_cylinder_or_cone_mesh(
radius_top, radius_bottom, depth, verts_num, fill_type);
+ /* Transform the mesh so that the base of the cone is at the origin. */
BKE_mesh_translate(mesh, float3(0.0f, 0.0f, depth * 0.5f), false);
params.set_output("Geometry", GeometrySet::create_with_mesh(mesh));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc
index f8a9bfd2ed1..9651301cb34 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc
@@ -17,6 +17,7 @@
#include "DNA_mesh_types.h"
#include "BKE_lib_id.h"
+#include "BKE_material.h"
#include "BKE_mesh.h"
#include "bmesh.h"
@@ -35,7 +36,7 @@ static bNodeSocketTemplate geo_node_mesh_primitive_cube_out[] = {
namespace blender::nodes {
-static Mesh *create_cube_mesh(const float size)
+Mesh *create_cube_mesh(const float size)
{
const float4x4 transform = float4x4::identity();
@@ -53,6 +54,7 @@ static Mesh *create_cube_mesh(const float size)
BMeshToMeshParams params{};
params.calc_object_remap = false;
Mesh *mesh = (Mesh *)BKE_id_new_nomain(ID_ME, nullptr);
+ BKE_id_material_eval_ensure_default_slot(&mesh->id);
BM_mesh_bm_to_me(nullptr, bm, mesh, &params);
BM_mesh_free(bm);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc
index f443b4387d3..1767f765da4 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc
@@ -17,6 +17,7 @@
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "BKE_material.h"
#include "BKE_mesh.h"
#include "UI_interface.h"
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_plane.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc
index eff84d7d1ad..ac2f5a23a4d 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_plane.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc
@@ -14,12 +14,10 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "BLI_map.hh"
-#include "BLI_math_matrix.h"
-
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "BKE_material.h"
#include "BKE_mesh.h"
#include "UI_interface.h"
@@ -27,39 +25,47 @@
#include "node_geometry_util.hh"
-static bNodeSocketTemplate geo_node_mesh_primitive_plane_in[] = {
- {SOCK_FLOAT, N_("Size"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE},
- {SOCK_INT, N_("Vertices X"), 10, 0.0f, 0.0f, 0.0f, 2, 1000},
- {SOCK_INT, N_("Vertices Y"), 10, 0.0f, 0.0f, 0.0f, 2, 1000},
+static bNodeSocketTemplate geo_node_mesh_primitive_grid_in[] = {
+ {SOCK_FLOAT, N_("Size X"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE},
+ {SOCK_FLOAT, N_("Size Y"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE},
+ {SOCK_INT, N_("Vertices X"), 3, 0.0f, 0.0f, 0.0f, 2, 1000},
+ {SOCK_INT, N_("Vertices Y"), 3, 0.0f, 0.0f, 0.0f, 2, 1000},
{-1, ""},
};
-static bNodeSocketTemplate geo_node_mesh_primitive_plane_out[] = {
+static bNodeSocketTemplate geo_node_mesh_primitive_grid_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{-1, ""},
};
namespace blender::nodes {
-static void calculate_uvs(Mesh *mesh, Span<MVert> verts, Span<MLoop> loops, const float size)
+static void calculate_uvs(
+ Mesh *mesh, Span<MVert> verts, Span<MLoop> loops, const float size_x, const float size_y)
{
MeshComponent mesh_component;
mesh_component.replace(mesh, GeometryOwnershipType::Editable);
- OutputAttributePtr uv_attribute = mesh_component.attribute_try_get_for_output(
- "uv", ATTR_DOMAIN_CORNER, CD_PROP_FLOAT2, nullptr);
- MutableSpan<float2> uvs = uv_attribute->get_span_for_write_only<float2>();
+ OutputAttribute_Typed<float2> uv_attribute =
+ mesh_component.attribute_try_get_for_output_only<float2>("uv_map", ATTR_DOMAIN_CORNER);
+ MutableSpan<float2> uvs = uv_attribute.as_span();
+ const float dx = (size_x == 0.0f) ? 0.0f : 1.0f / size_x;
+ const float dy = (size_y == 0.0f) ? 0.0f : 1.0f / size_y;
for (const int i : loops.index_range()) {
const float3 &co = verts[loops[i].v].co;
- uvs[i].x = (co.x + size) / (size * 2.0f);
- uvs[i].y = (co.y + size) / (size * 2.0f);
+ uvs[i].x = (co.x + size_x * 0.5f) * dx;
+ uvs[i].y = (co.y + size_y * 0.5f) * dy;
}
- uv_attribute.apply_span_and_save();
+ uv_attribute.save();
}
-static Mesh *create_plane_mesh(const int verts_x, const int verts_y, const float size)
+static Mesh *create_grid_mesh(const int verts_x,
+ const int verts_y,
+ const float size_x,
+ const float size_y)
{
+ BLI_assert(verts_x > 1 && verts_y > 1);
const int edges_x = verts_x - 1;
const int edges_y = verts_y - 1;
Mesh *mesh = BKE_mesh_new_nomain(verts_x * verts_y,
@@ -73,11 +79,11 @@ static Mesh *create_plane_mesh(const int verts_x, const int verts_y, const float
MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly};
{
- const float dx = size / edges_x;
- const float dy = size / edges_y;
- float x = -size;
+ const float dx = size_x / edges_x;
+ const float dy = size_y / edges_y;
+ float x = -size_x * 0.5;
for (const int x_index : IndexRange(verts_x)) {
- float y = -size;
+ float y = -size_y * 0.5;
for (const int y_index : IndexRange(verts_y)) {
const int vert_index = x_index * verts_y + y_index;
verts[vert_index].co[0] = x;
@@ -104,6 +110,7 @@ static Mesh *create_plane_mesh(const int verts_x, const int verts_y, const float
MEdge &edge = edges[edge_index++];
edge.v1 = vert_index;
edge.v2 = vert_index + 1;
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
}
}
@@ -115,6 +122,7 @@ static Mesh *create_plane_mesh(const int verts_x, const int verts_y, const float
MEdge &edge = edges[edge_index++];
edge.v1 = vert_index;
edge.v2 = vert_index + verts_y;
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
}
}
@@ -142,14 +150,15 @@ static Mesh *create_plane_mesh(const int verts_x, const int verts_y, const float
}
}
- calculate_uvs(mesh, verts, loops, size);
+ calculate_uvs(mesh, verts, loops, size_x, size_y);
return mesh;
}
-static void geo_node_mesh_primitive_plane_exec(GeoNodeExecParams params)
+static void geo_node_mesh_primitive_grid_exec(GeoNodeExecParams params)
{
- const float size = params.extract_input<float>("Size");
+ const float size_x = params.extract_input<float>("Size X");
+ const float size_y = params.extract_input<float>("Size Y");
const int verts_x = params.extract_input<int>("Vertices X");
const int verts_y = params.extract_input<int>("Vertices Y");
if (verts_x < 2 || verts_y < 2) {
@@ -157,21 +166,22 @@ static void geo_node_mesh_primitive_plane_exec(GeoNodeExecParams params)
return;
}
- Mesh *mesh = create_plane_mesh(verts_x, verts_y, size);
+ Mesh *mesh = create_grid_mesh(verts_x, verts_y, size_x, size_y);
BLI_assert(BKE_mesh_is_valid(mesh));
+ BKE_id_material_eval_ensure_default_slot(&mesh->id);
params.set_output("Geometry", GeometrySet::create_with_mesh(mesh));
}
} // namespace blender::nodes
-void register_node_type_geo_mesh_primitive_plane()
+void register_node_type_geo_mesh_primitive_grid()
{
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_PLANE, "Plane", NODE_CLASS_GEOMETRY, 0);
+ geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_GRID, "Grid", NODE_CLASS_GEOMETRY, 0);
node_type_socket_templates(
- &ntype, geo_node_mesh_primitive_plane_in, geo_node_mesh_primitive_plane_out);
- ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_plane_exec;
+ &ntype, geo_node_mesh_primitive_grid_in, geo_node_mesh_primitive_grid_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_grid_exec;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc
index 242cc6ed7df..a3a1b72006c 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc
@@ -17,6 +17,7 @@
#include "DNA_mesh_types.h"
#include "BKE_lib_id.h"
+#include "BKE_material.h"
#include "BKE_mesh.h"
#include "bmesh.h"
@@ -55,6 +56,7 @@ static Mesh *create_ico_sphere_mesh(const int subdivisions, const float radius)
BMeshToMeshParams params{};
params.calc_object_remap = false;
Mesh *mesh = (Mesh *)BKE_id_new_nomain(ID_ME, nullptr);
+ BKE_id_material_eval_ensure_default_slot(&mesh->id);
BM_mesh_bm_to_me(nullptr, bm, mesh, &params);
BM_mesh_free(bm);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc
index a7d40571c39..e841455e58c 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc
@@ -14,12 +14,10 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "BLI_map.hh"
-#include "BLI_math_matrix.h"
-
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "BKE_material.h"
#include "BKE_mesh.h"
#include "UI_interface.h"
@@ -113,6 +111,7 @@ static Mesh *create_line_mesh(const float3 start, const float3 delta, const int
}
Mesh *mesh = BKE_mesh_new_nomain(count, count - 1, 0, 0, 0);
+ BKE_id_material_eval_ensure_default_slot(&mesh->id);
MutableSpan<MVert> verts{mesh->mvert, mesh->totvert};
MutableSpan<MEdge> edges{mesh->medge, mesh->totedge};
@@ -154,7 +153,10 @@ static void geo_node_mesh_primitive_line_exec(GeoNodeExecParams params)
}
else if (count_mode == GEO_NODE_MESH_LINE_COUNT_TOTAL) {
const int count = params.extract_input<int>("Count");
- if (count > 1) {
+ if (count == 1) {
+ mesh = create_line_mesh(start, float3(0), count);
+ }
+ else {
const float3 delta = total_delta / (float)(count - 1);
mesh = create_line_mesh(start, delta, count);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc
index 8efba91da1a..599c59e4a2e 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc
@@ -17,19 +17,17 @@
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
-#include "BKE_lib_id.h"
+#include "BKE_material.h"
#include "BKE_mesh.h"
#include "UI_interface.h"
#include "UI_resources.h"
-#include "bmesh.h"
-
#include "node_geometry_util.hh"
static bNodeSocketTemplate geo_node_mesh_primitive_uv_sphere_in[] = {
{SOCK_INT, N_("Segments"), 32, 0.0f, 0.0f, 0.0f, 3, 1024},
- {SOCK_INT, N_("Rings"), 16, 0.0f, 0.0f, 0.0f, 3, 1024},
+ {SOCK_INT, N_("Rings"), 16, 0.0f, 0.0f, 0.0f, 2, 1024},
{SOCK_FLOAT, N_("Radius"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE},
{-1, ""},
};
@@ -65,31 +63,225 @@ static int sphere_face_total(const int segments, const int rings)
return quads + triangles;
}
-static Mesh *create_uv_sphere_mesh_bmesh(const float radius, const int segments, const int rings)
+static void calculate_sphere_vertex_data(MutableSpan<MVert> verts,
+ const float radius,
+ const int segments,
+ const int rings)
+{
+ const float delta_theta = M_PI / rings;
+ const float delta_phi = (2 * M_PI) / segments;
+
+ copy_v3_v3(verts[0].co, float3(0.0f, 0.0f, radius));
+ normal_float_to_short_v3(verts[0].no, float3(0.0f, 0.0f, 1.0f));
+
+ int vert_index = 1;
+ float theta = delta_theta;
+ for (const int UNUSED(ring) : IndexRange(rings - 1)) {
+ float phi = 0.0f;
+ const float z = cosf(theta);
+ for (const int UNUSED(segment) : IndexRange(segments)) {
+ const float sin_theta = std::sin(theta);
+ const float x = sin_theta * std::cos(phi);
+ const float y = sin_theta * std::sin(phi);
+ copy_v3_v3(verts[vert_index].co, float3(x, y, z) * radius);
+ normal_float_to_short_v3(verts[vert_index].no, float3(x, y, z));
+ phi += delta_phi;
+ vert_index++;
+ }
+ theta += delta_theta;
+ }
+
+ copy_v3_v3(verts.last().co, float3(0.0f, 0.0f, -radius));
+ normal_float_to_short_v3(verts.last().no, float3(0.0f, 0.0f, -1.0f));
+}
+
+static void calculate_sphere_edge_indices(MutableSpan<MEdge> edges,
+ const int segments,
+ const int rings)
+{
+ int edge_index = 0;
+
+ /* Add the edges connecting the top vertex to the first ring. */
+ const int first_vert_ring_index_start = 1;
+ for (const int segment : IndexRange(segments)) {
+ MEdge &edge = edges[edge_index++];
+ edge.v1 = 0;
+ edge.v2 = first_vert_ring_index_start + segment;
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
+ }
+
+ int ring_vert_index_start = 1;
+ for (const int ring : IndexRange(rings - 1)) {
+ const int next_ring_vert_index_start = ring_vert_index_start + segments;
+
+ /* Add the edges running along each ring. */
+ for (const int segment : IndexRange(segments)) {
+ MEdge &edge = edges[edge_index++];
+ edge.v1 = ring_vert_index_start + segment;
+ edge.v2 = ring_vert_index_start + ((segment + 1) % segments);
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
+ }
+
+ /* Add the edges connecting to the next ring. */
+ if (ring < rings - 2) {
+ for (const int segment : IndexRange(segments)) {
+ MEdge &edge = edges[edge_index++];
+ edge.v1 = ring_vert_index_start + segment;
+ edge.v2 = next_ring_vert_index_start + segment;
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
+ }
+ }
+ ring_vert_index_start += segments;
+ }
+
+ /* Add the edges connecting the last ring to the bottom vertex. */
+ const int last_vert_index = sphere_vert_total(segments, rings) - 1;
+ const int last_vert_ring_start = last_vert_index - segments;
+ for (const int segment : IndexRange(segments)) {
+ MEdge &edge = edges[edge_index++];
+ edge.v1 = last_vert_index;
+ edge.v2 = last_vert_ring_start + segment;
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
+ }
+}
+
+static void calculate_sphere_faces(MutableSpan<MLoop> loops,
+ MutableSpan<MPoly> polys,
+ const int segments,
+ const int rings)
{
- const float4x4 transform = float4x4::identity();
-
- const BMeshCreateParams bmcp = {true};
- const BMAllocTemplate allocsize = {sphere_vert_total(segments, rings),
- sphere_edge_total(segments, rings),
- sphere_corner_total(segments, rings),
- sphere_face_total(segments, rings)};
- BMesh *bm = BM_mesh_create(&allocsize, &bmcp);
-
- BMO_op_callf(bm,
- BMO_FLAG_DEFAULTS,
- "create_uvsphere u_segments=%i v_segments=%i diameter=%f matrix=%m4 calc_uvs=%b",
- segments,
- rings,
- radius,
- transform.values,
- true);
-
- BMeshToMeshParams params{};
- params.calc_object_remap = false;
- Mesh *mesh = (Mesh *)BKE_id_new_nomain(ID_ME, nullptr);
- BM_mesh_bm_to_me(nullptr, bm, mesh, &params);
- BM_mesh_free(bm);
+ int loop_index = 0;
+ int poly_index = 0;
+
+ /* Add the triangles connected to the top vertex. */
+ const int first_vert_ring_index_start = 1;
+ for (const int segment : IndexRange(segments)) {
+ MPoly &poly = polys[poly_index++];
+ poly.loopstart = loop_index;
+ poly.totloop = 3;
+ MLoop &loop_a = loops[loop_index++];
+ loop_a.v = 0;
+ loop_a.e = segment;
+ MLoop &loop_b = loops[loop_index++];
+ loop_b.v = first_vert_ring_index_start + segment;
+ loop_b.e = segments + segment;
+ MLoop &loop_c = loops[loop_index++];
+ loop_c.v = first_vert_ring_index_start + (segment + 1) % segments;
+ loop_c.e = (segment + 1) % segments;
+ }
+
+ int ring_vert_index_start = 1;
+ int ring_edge_index_start = segments;
+ for (const int UNUSED(ring) : IndexRange(1, rings - 2)) {
+ const int next_ring_vert_index_start = ring_vert_index_start + segments;
+ const int next_ring_edge_index_start = ring_edge_index_start + segments * 2;
+ const int ring_vertical_edge_index_start = ring_edge_index_start + segments;
+
+ for (const int segment : IndexRange(segments)) {
+ MPoly &poly = polys[poly_index++];
+ poly.loopstart = loop_index;
+ poly.totloop = 4;
+
+ MLoop &loop_a = loops[loop_index++];
+ loop_a.v = ring_vert_index_start + segment;
+ loop_a.e = ring_vertical_edge_index_start + segment;
+ MLoop &loop_b = loops[loop_index++];
+ loop_b.v = next_ring_vert_index_start + segment;
+ loop_b.e = next_ring_edge_index_start + segment;
+ MLoop &loop_c = loops[loop_index++];
+ loop_c.v = next_ring_vert_index_start + (segment + 1) % segments;
+ loop_c.e = ring_vertical_edge_index_start + (segment + 1) % segments;
+ MLoop &loop_d = loops[loop_index++];
+ loop_d.v = ring_vert_index_start + (segment + 1) % segments;
+ loop_d.e = ring_edge_index_start + segment;
+ }
+ ring_vert_index_start += segments;
+ ring_edge_index_start += segments * 2;
+ }
+
+ /* Add the triangles connected to the bottom vertex. */
+ const int last_edge_ring_start = segments * (rings - 2) * 2 + segments;
+ const int bottom_edge_fan_start = last_edge_ring_start + segments;
+ const int last_vert_index = sphere_vert_total(segments, rings) - 1;
+ const int last_vert_ring_start = last_vert_index - segments;
+ for (const int segment : IndexRange(segments)) {
+ MPoly &poly = polys[poly_index++];
+ poly.loopstart = loop_index;
+ poly.totloop = 3;
+
+ MLoop &loop_a = loops[loop_index++];
+ loop_a.v = last_vert_index;
+ loop_a.e = bottom_edge_fan_start + (segment + 1) % segments;
+ MLoop &loop_b = loops[loop_index++];
+ loop_b.v = last_vert_ring_start + (segment + 1) % segments;
+ loop_b.e = last_edge_ring_start + segment;
+ MLoop &loop_c = loops[loop_index++];
+ loop_c.v = last_vert_ring_start + segment;
+ loop_c.e = bottom_edge_fan_start + segment;
+ }
+}
+
+static void calculate_sphere_uvs(Mesh *mesh, const float segments, const float rings)
+{
+ MeshComponent mesh_component;
+ mesh_component.replace(mesh, GeometryOwnershipType::Editable);
+ OutputAttribute_Typed<float2> uv_attribute =
+ mesh_component.attribute_try_get_for_output_only<float2>("uv_map", ATTR_DOMAIN_CORNER);
+ MutableSpan<float2> uvs = uv_attribute.as_span();
+
+ int loop_index = 0;
+ const float dy = 1.0f / rings;
+
+ for (const int i_segment : IndexRange(segments)) {
+ const float segment = static_cast<float>(i_segment);
+ uvs[loop_index++] = float2((segment + 0.5f) / segments, 0.0f);
+ uvs[loop_index++] = float2(segment / segments, dy);
+ uvs[loop_index++] = float2((segment + 1.0f) / segments, dy);
+ }
+
+ for (const int i_ring : IndexRange(1, rings - 2)) {
+ const float ring = static_cast<float>(i_ring);
+ for (const int i_segment : IndexRange(segments)) {
+ const float segment = static_cast<float>(i_segment);
+ uvs[loop_index++] = float2(segment / segments, ring / rings);
+ uvs[loop_index++] = float2(segment / segments, (ring + 1.0f) / rings);
+ uvs[loop_index++] = float2((segment + 1.0f) / segments, (ring + 1.0f) / rings);
+ uvs[loop_index++] = float2((segment + 1.0f) / segments, ring / rings);
+ }
+ }
+
+ for (const int i_segment : IndexRange(segments)) {
+ const float segment = static_cast<float>(i_segment);
+ uvs[loop_index++] = float2((segment + 0.5f) / segments, 1.0f);
+ uvs[loop_index++] = float2((segment + 1.0f) / segments, 1.0f - dy);
+ uvs[loop_index++] = float2(segment / segments, 1.0f - dy);
+ }
+
+ uv_attribute.save();
+}
+
+static Mesh *create_uv_sphere_mesh(const float radius, const int segments, const int rings)
+{
+ Mesh *mesh = BKE_mesh_new_nomain(sphere_vert_total(segments, rings),
+ sphere_edge_total(segments, rings),
+ 0,
+ sphere_corner_total(segments, rings),
+ sphere_face_total(segments, rings));
+ BKE_id_material_eval_ensure_default_slot(&mesh->id);
+ MutableSpan<MVert> verts{mesh->mvert, mesh->totvert};
+ MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop};
+ MutableSpan<MEdge> edges{mesh->medge, mesh->totedge};
+ MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly};
+
+ calculate_sphere_vertex_data(verts, radius, segments, rings);
+
+ calculate_sphere_edge_indices(edges, segments, rings);
+
+ calculate_sphere_faces(loops, polys, segments, rings);
+
+ calculate_sphere_uvs(mesh, segments, rings);
+
+ BLI_assert(BKE_mesh_is_valid(mesh));
return mesh;
}
@@ -98,14 +290,14 @@ static void geo_node_mesh_primitive_uv_sphere_exec(GeoNodeExecParams params)
{
const int segments_num = params.extract_input<int>("Segments");
const int rings_num = params.extract_input<int>("Rings");
- if (segments_num < 3 || rings_num < 3) {
+ if (segments_num < 3 || rings_num < 2) {
params.set_output("Geometry", GeometrySet());
return;
}
const float radius = params.extract_input<float>("Radius");
- Mesh *mesh = create_uv_sphere_mesh_bmesh(radius, segments_num, rings_num);
+ Mesh *mesh = create_uv_sphere_mesh(radius, segments_num, rings_num);
params.set_output("Geometry", GeometrySet::create_with_mesh(mesh));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc
new file mode 100644
index 00000000000..0fb7910c904
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc
@@ -0,0 +1,316 @@
+/*
+ * 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.
+ */
+
+#include "BLI_array.hh"
+#include "BLI_task.hh"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_attribute_math.hh"
+#include "BKE_spline.hh"
+
+#include "node_geometry_util.hh"
+
+using blender::Array;
+
+static bNodeSocketTemplate geo_node_mesh_to_curve_in[] = {
+ {SOCK_GEOMETRY, N_("Mesh")},
+ {SOCK_STRING, N_("Selection")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_mesh_to_curve_out[] = {
+ {SOCK_GEOMETRY, N_("Curve")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+template<typename T>
+static void copy_attribute_to_points(const VArray<T> &source_data,
+ Span<int> map,
+ MutableSpan<T> dest_data)
+{
+ for (const int point_index : map.index_range()) {
+ const int vert_index = map[point_index];
+ dest_data[point_index] = source_data[vert_index];
+ }
+}
+
+static void copy_attributes_to_points(CurveEval &curve,
+ const MeshComponent &mesh_component,
+ Span<Vector<int>> point_to_vert_maps)
+{
+ MutableSpan<SplinePtr> splines = curve.splines();
+ Set<std::string> source_attribute_names = mesh_component.attribute_names();
+
+ /* Copy builtin control point attributes. */
+ if (source_attribute_names.contains_as("tilt")) {
+ const GVArray_Typed<float> tilt_attribute = mesh_component.attribute_get_for_read<float>(
+ "tilt", ATTR_DOMAIN_POINT, 0.0f);
+ parallel_for(splines.index_range(), 256, [&](IndexRange range) {
+ for (const int i : range) {
+ copy_attribute_to_points<float>(
+ *tilt_attribute, point_to_vert_maps[i], splines[i]->tilts());
+ }
+ });
+ source_attribute_names.remove_contained_as("tilt");
+ }
+ if (source_attribute_names.contains_as("radius")) {
+ const GVArray_Typed<float> radius_attribute = mesh_component.attribute_get_for_read<float>(
+ "radius", ATTR_DOMAIN_POINT, 1.0f);
+ parallel_for(splines.index_range(), 256, [&](IndexRange range) {
+ for (const int i : range) {
+ copy_attribute_to_points<float>(
+ *radius_attribute, point_to_vert_maps[i], splines[i]->radii());
+ }
+ });
+ source_attribute_names.remove_contained_as("radius");
+ }
+
+ /* Don't copy other builtin control point attributes. */
+ source_attribute_names.remove_as("position");
+
+ /* Copy dynamic control point attributes. */
+ for (const StringRef name : source_attribute_names) {
+ const GVArrayPtr mesh_attribute = mesh_component.attribute_try_get_for_read(name,
+ ATTR_DOMAIN_POINT);
+ /* Some attributes might not exist if they were builtin attribute on domains that don't
+ * have any elements, i.e. a face attribute on the output of the line primitive node. */
+ if (!mesh_attribute) {
+ continue;
+ }
+
+ const CustomDataType data_type = bke::cpp_type_to_custom_data_type(mesh_attribute->type());
+
+ parallel_for(splines.index_range(), 128, [&](IndexRange range) {
+ for (const int i : range) {
+ /* Create attribute on the spline points. */
+ splines[i]->attributes.create(name, data_type);
+ std::optional<GMutableSpan> spline_attribute = splines[i]->attributes.get_for_write(name);
+ BLI_assert(spline_attribute);
+
+ /* Copy attribute based on the map for this spline. */
+ attribute_math::convert_to_static_type(mesh_attribute->type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ copy_attribute_to_points<T>(
+ mesh_attribute->typed<T>(), point_to_vert_maps[i], spline_attribute->typed<T>());
+ });
+ }
+ });
+ }
+
+ curve.assert_valid_point_attributes();
+}
+
+struct CurveFromEdgesOutput {
+ std::unique_ptr<CurveEval> curve;
+ Vector<Vector<int>> point_to_vert_maps;
+};
+
+static CurveFromEdgesOutput mesh_to_curve(Span<MVert> verts, Span<std::pair<int, int>> edges)
+{
+ std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>();
+ Vector<Vector<int>> point_to_vert_maps;
+
+ /* Compute the number of edges connecting to each vertex. */
+ Array<int> neighbor_count(verts.size(), 0);
+ for (const std::pair<int, int> &edge : edges) {
+ neighbor_count[edge.first]++;
+ neighbor_count[edge.second]++;
+ }
+
+ /* Compute an offset into the array of neighbor edges based on the counts. */
+ Array<int> neighbor_offsets(verts.size());
+ int start = 0;
+ for (const int i : verts.index_range()) {
+ neighbor_offsets[i] = start;
+ start += neighbor_count[i];
+ }
+
+ /* Use as an index into the "neighbor group" for each vertex. */
+ Array<int> used_slots(verts.size(), 0);
+ /* Calculate the indices of each vertex's neighboring edges. */
+ Array<int> neighbors(edges.size() * 2);
+ for (const int i : edges.index_range()) {
+ const int v1 = edges[i].first;
+ const int v2 = edges[i].second;
+ neighbors[neighbor_offsets[v1] + used_slots[v1]] = v2;
+ neighbors[neighbor_offsets[v2] + used_slots[v2]] = v1;
+ used_slots[v1]++;
+ used_slots[v2]++;
+ }
+
+ /* Now use the neighbor group offsets calculated above as a count used edges at each vertex. */
+ Array<int> unused_edges = std::move(used_slots);
+
+ for (const int start_vert : verts.index_range()) {
+ /* The vertex will be part of a cyclic spline. */
+ if (neighbor_count[start_vert] == 2) {
+ continue;
+ }
+
+ /* The vertex has no connected edges, or they were already used. */
+ if (unused_edges[start_vert] == 0) {
+ continue;
+ }
+
+ for (const int i : IndexRange(neighbor_count[start_vert])) {
+ int current_vert = start_vert;
+ int next_vert = neighbors[neighbor_offsets[current_vert] + i];
+
+ if (unused_edges[next_vert] == 0) {
+ continue;
+ }
+
+ std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>();
+ Vector<int> point_to_vert_map;
+
+ spline->add_point(verts[current_vert].co, 1.0f, 0.0f);
+ point_to_vert_map.append(current_vert);
+
+ /* Follow connected edges until we read a vertex with more than two connected edges. */
+ while (true) {
+ int last_vert = current_vert;
+ current_vert = next_vert;
+
+ spline->add_point(verts[current_vert].co, 1.0f, 0.0f);
+ point_to_vert_map.append(current_vert);
+ unused_edges[current_vert]--;
+ unused_edges[last_vert]--;
+
+ if (neighbor_count[current_vert] != 2) {
+ break;
+ }
+
+ const int offset = neighbor_offsets[current_vert];
+ const int next_a = neighbors[offset];
+ const int next_b = neighbors[offset + 1];
+ next_vert = (last_vert == next_a) ? next_b : next_a;
+ }
+
+ spline->attributes.reallocate(spline->size());
+ curve->add_spline(std::move(spline));
+ point_to_vert_maps.append(std::move(point_to_vert_map));
+ }
+ }
+
+ /* All remaining edges are part of cyclic splines (we skipped vertices with two edges before). */
+ for (const int start_vert : verts.index_range()) {
+ if (unused_edges[start_vert] != 2) {
+ continue;
+ }
+
+ int current_vert = start_vert;
+ int next_vert = neighbors[neighbor_offsets[current_vert]];
+
+ std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>();
+ Vector<int> point_to_vert_map;
+ spline->set_cyclic(true);
+
+ spline->add_point(verts[current_vert].co, 1.0f, 0.0f);
+ point_to_vert_map.append(current_vert);
+
+ /* Follow connected edges until we loop back to the start vertex. */
+ while (next_vert != start_vert) {
+ const int last_vert = current_vert;
+ current_vert = next_vert;
+
+ spline->add_point(verts[current_vert].co, 1.0f, 0.0f);
+ point_to_vert_map.append(current_vert);
+ unused_edges[current_vert]--;
+ unused_edges[last_vert]--;
+
+ const int offset = neighbor_offsets[current_vert];
+ const int next_a = neighbors[offset];
+ const int next_b = neighbors[offset + 1];
+ next_vert = (last_vert == next_a) ? next_b : next_a;
+ }
+
+ spline->attributes.reallocate(spline->size());
+ curve->add_spline(std::move(spline));
+ point_to_vert_maps.append(std::move(point_to_vert_map));
+ }
+
+ curve->attributes.reallocate(curve->splines().size());
+ return {std::move(curve), std::move(point_to_vert_maps)};
+}
+
+/**
+ * Get a separate array of the indices for edges in a selection (a boolean attribute).
+ * This helps to make the above algorithm simpler by removing the need to check for selection
+ * in many places.
+ */
+static Vector<std::pair<int, int>> get_selected_edges(GeoNodeExecParams params,
+ const MeshComponent &component)
+{
+ const Mesh &mesh = *component.get_for_read();
+ const std::string selection_name = params.extract_input<std::string>("Selection");
+ if (!selection_name.empty() && !component.attribute_exists(selection_name)) {
+ params.error_message_add(NodeWarningType::Error,
+ TIP_("No attribute with name \"") + selection_name + "\"");
+ }
+ GVArray_Typed<bool> selection = component.attribute_get_for_read<bool>(
+ selection_name, ATTR_DOMAIN_EDGE, true);
+
+ Vector<std::pair<int, int>> selected_edges;
+ for (const int i : IndexRange(mesh.totedge)) {
+ if (selection[i]) {
+ selected_edges.append({mesh.medge[i].v1, mesh.medge[i].v2});
+ }
+ }
+
+ return selected_edges;
+}
+
+static void geo_node_mesh_to_curve_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
+
+ geometry_set = bke::geometry_set_realize_instances(geometry_set);
+
+ if (!geometry_set.has_mesh()) {
+ params.set_output("Curve", GeometrySet());
+ return;
+ }
+
+ const MeshComponent &component = *geometry_set.get_component_for_read<MeshComponent>();
+ const Mesh &mesh = *component.get_for_read();
+ Span<MVert> verts = Span{mesh.mvert, mesh.totvert};
+ Vector<std::pair<int, int>> selected_edges = get_selected_edges(params, component);
+ if (selected_edges.size() == 0) {
+ params.set_output("Curve", GeometrySet());
+ return;
+ }
+
+ CurveFromEdgesOutput output = mesh_to_curve(verts, selected_edges);
+ copy_attributes_to_points(*output.curve, component, output.point_to_vert_maps);
+
+ params.set_output("Curve", GeometrySet::create_with_curve(output.curve.release()));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_mesh_to_curve()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_MESH_TO_CURVE, "Mesh to Curve", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(&ntype, geo_node_mesh_to_curve_in, geo_node_mesh_to_curve_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_mesh_to_curve_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc
index 54ecb20dae7..167812d5656 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc
@@ -14,20 +14,15 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "node_geometry_util.hh"
-
-#include "BKE_mesh.h"
-#include "BKE_mesh_wrapper.h"
-#include "BKE_modifier.h"
-#include "BKE_volume.h"
-
#include "BLI_math_matrix.h"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "node_geometry_util.hh"
+
static bNodeSocketTemplate geo_node_object_info_in[] = {
- {SOCK_OBJECT, N_("Object")},
+ {SOCK_OBJECT, N_("Object"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, PROP_NONE, SOCK_HIDE_LABEL},
{-1, ""},
};
@@ -52,9 +47,7 @@ static void geo_node_object_info_exec(GeoNodeExecParams params)
const bool transform_space_relative = (node_storage->transform_space ==
GEO_NODE_TRANSFORM_SPACE_RELATIVE);
- bke::PersistentObjectHandle object_handle = params.extract_input<bke::PersistentObjectHandle>(
- "Object");
- Object *object = params.handle_map().lookup(object_handle);
+ Object *object = params.get_input<Object *>("Object");
float3 location = {0, 0, 0};
float3 rotation = {0, 0, 0};
@@ -78,14 +71,15 @@ static void geo_node_object_info_exec(GeoNodeExecParams params)
if (object != self_object) {
InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
+ const int handle = instances.add_reference(*object);
if (transform_space_relative) {
- instances.add_instance(object, transform);
+ instances.add_instance(handle, transform);
}
else {
float unit_transform[4][4];
unit_m4(unit_transform);
- instances.add_instance(object, unit_transform);
+ instances.add_instance(handle, unit_transform);
}
}
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc b/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc
index 2e3460ee5fe..772bd8a1080 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc
@@ -14,12 +14,9 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "BLI_float3.hh"
#include "BLI_hash.h"
#include "BLI_kdtree.h"
-#include "BLI_math_vector.h"
#include "BLI_rand.hh"
-#include "BLI_span.hh"
#include "BLI_timeit.hh"
#include "DNA_mesh_types.h"
@@ -28,10 +25,10 @@
#include "BKE_attribute_math.hh"
#include "BKE_bvhutils.h"
-#include "BKE_deform.h"
#include "BKE_geometry_set_instances.hh"
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
+#include "BKE_mesh_sample.hh"
#include "BKE_pointcloud.h"
#include "UI_interface.h"
@@ -44,7 +41,7 @@ using blender::bke::GeometryInstanceGroup;
static bNodeSocketTemplate geo_node_point_distribute_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
- {SOCK_FLOAT, N_("Distance Min"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 100000.0f, PROP_NONE},
+ {SOCK_FLOAT, N_("Distance Min"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 100000.0f, PROP_DISTANCE},
{SOCK_FLOAT, N_("Density Max"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 100000.0f, PROP_NONE},
{SOCK_STRING, N_("Density Attribute")},
{SOCK_INT, N_("Seed"), 0, 0, 0, 0, -10000, 10000},
@@ -95,7 +92,7 @@ static Span<MLoopTri> get_mesh_looptris(const Mesh &mesh)
static void sample_mesh_surface(const Mesh &mesh,
const float4x4 &transform,
const float base_density,
- const FloatReadAttribute *density_factors,
+ const VArray<float> *density_factors,
const int seed,
Vector<float3> &r_positions,
Vector<float3> &r_bary_coords,
@@ -117,9 +114,9 @@ static void sample_mesh_surface(const Mesh &mesh,
float looptri_density_factor = 1.0f;
if (density_factors != nullptr) {
- const float v0_density_factor = std::max(0.0f, (*density_factors)[v0_loop]);
- const float v1_density_factor = std::max(0.0f, (*density_factors)[v1_loop]);
- const float v2_density_factor = std::max(0.0f, (*density_factors)[v2_loop]);
+ const float v0_density_factor = std::max(0.0f, density_factors->get(v0_loop));
+ const float v1_density_factor = std::max(0.0f, density_factors->get(v1_loop));
+ const float v2_density_factor = std::max(0.0f, density_factors->get(v2_loop));
looptri_density_factor = (v0_density_factor + v1_density_factor + v2_density_factor) / 3.0f;
}
const float area = area_tri_v3(v0_pos, v1_pos, v2_pos);
@@ -207,7 +204,7 @@ BLI_NOINLINE static void update_elimination_mask_for_close_points(
BLI_NOINLINE static void update_elimination_mask_based_on_density_factors(
const Mesh &mesh,
- const FloatReadAttribute &density_factors,
+ const VArray<float> &density_factors,
Span<float3> bary_coords,
Span<int> looptri_indices,
MutableSpan<bool> elimination_mask)
@@ -253,99 +250,27 @@ BLI_NOINLINE static void eliminate_points_based_on_mask(Span<bool> elimination_m
}
}
-template<typename T>
-BLI_NOINLINE static void interpolate_attribute_point(const Mesh &mesh,
- const Span<float3> bary_coords,
- const Span<int> looptri_indices,
- const Span<T> data_in,
- MutableSpan<T> data_out)
-{
- BLI_assert(data_in.size() == mesh.totvert);
- Span<MLoopTri> looptris = get_mesh_looptris(mesh);
-
- for (const int i : bary_coords.index_range()) {
- const int looptri_index = looptri_indices[i];
- const MLoopTri &looptri = looptris[looptri_index];
- const float3 &bary_coord = bary_coords[i];
-
- const int v0_index = mesh.mloop[looptri.tri[0]].v;
- const int v1_index = mesh.mloop[looptri.tri[1]].v;
- const int v2_index = mesh.mloop[looptri.tri[2]].v;
-
- const T &v0 = data_in[v0_index];
- const T &v1 = data_in[v1_index];
- const T &v2 = data_in[v2_index];
-
- const T interpolated_value = attribute_math::mix3(bary_coord, v0, v1, v2);
- data_out[i] = interpolated_value;
- }
-}
-
-template<typename T>
-BLI_NOINLINE static void interpolate_attribute_corner(const Mesh &mesh,
- const Span<float3> bary_coords,
- const Span<int> looptri_indices,
- const Span<T> data_in,
- MutableSpan<T> data_out)
-{
- BLI_assert(data_in.size() == mesh.totloop);
- Span<MLoopTri> looptris = get_mesh_looptris(mesh);
-
- for (const int i : bary_coords.index_range()) {
- const int looptri_index = looptri_indices[i];
- const MLoopTri &looptri = looptris[looptri_index];
- const float3 &bary_coord = bary_coords[i];
-
- const int loop_index_0 = looptri.tri[0];
- const int loop_index_1 = looptri.tri[1];
- const int loop_index_2 = looptri.tri[2];
-
- const T &v0 = data_in[loop_index_0];
- const T &v1 = data_in[loop_index_1];
- const T &v2 = data_in[loop_index_2];
-
- const T interpolated_value = attribute_math::mix3(bary_coord, v0, v1, v2);
- data_out[i] = interpolated_value;
- }
-}
-
-template<typename T>
-BLI_NOINLINE static void interpolate_attribute_face(const Mesh &mesh,
- const Span<int> looptri_indices,
- const Span<T> data_in,
- MutableSpan<T> data_out)
-{
- BLI_assert(data_in.size() == mesh.totpoly);
- Span<MLoopTri> looptris = get_mesh_looptris(mesh);
-
- for (const int i : data_out.index_range()) {
- const int looptri_index = looptri_indices[i];
- const MLoopTri &looptri = looptris[looptri_index];
- const int poly_index = looptri.poly;
- data_out[i] = data_in[poly_index];
- }
-}
-
-template<typename T>
BLI_NOINLINE static void interpolate_attribute(const Mesh &mesh,
Span<float3> bary_coords,
Span<int> looptri_indices,
const AttributeDomain source_domain,
- Span<T> source_span,
- MutableSpan<T> output_span)
+ const GVArray &source_data,
+ GMutableSpan output_data)
{
switch (source_domain) {
case ATTR_DOMAIN_POINT: {
- interpolate_attribute_point<T>(mesh, bary_coords, looptri_indices, source_span, output_span);
+ bke::mesh_surface_sample::sample_point_attribute(
+ mesh, looptri_indices, bary_coords, source_data, output_data);
break;
}
case ATTR_DOMAIN_CORNER: {
- interpolate_attribute_corner<T>(
- mesh, bary_coords, looptri_indices, source_span, output_span);
+ bke::mesh_surface_sample::sample_corner_attribute(
+ mesh, looptri_indices, bary_coords, source_data, output_data);
break;
}
case ATTR_DOMAIN_FACE: {
- interpolate_attribute_face<T>(mesh, looptri_indices, source_span, output_span);
+ bke::mesh_surface_sample::sample_face_attribute(
+ mesh, looptri_indices, source_data, output_data);
break;
}
default: {
@@ -367,13 +292,13 @@ BLI_NOINLINE static void interpolate_existing_attributes(
StringRef attribute_name = entry.key;
const CustomDataType output_data_type = entry.value.data_type;
/* The output domain is always #ATTR_DOMAIN_POINT, since we are creating a point cloud. */
- OutputAttributePtr attribute_out = component.attribute_try_get_for_output(
+ OutputAttribute attribute_out = component.attribute_try_get_for_output_only(
attribute_name, ATTR_DOMAIN_POINT, output_data_type);
if (!attribute_out) {
continue;
}
- fn::GMutableSpan out_span = attribute_out->get_span_for_write_only();
+ GMutableSpan out_span = attribute_out.as_span();
int i_instance = 0;
for (const GeometryInstanceGroup &set_group : set_groups) {
@@ -381,47 +306,41 @@ BLI_NOINLINE static void interpolate_existing_attributes(
const MeshComponent &source_component = *set.get_component_for_read<MeshComponent>();
const Mesh &mesh = *source_component.get_for_read();
- /* Use a dummy read without specifying a domain or data type in order to
- * get the existing attribute's domain. Interpolation is done manually based
- * on the bary coords in #interpolate_attribute. */
- ReadAttributePtr dummy_attribute = source_component.attribute_try_get_for_read(
+ std::optional<AttributeMetaData> attribute_info = component.attribute_get_meta_data(
attribute_name);
- if (!dummy_attribute) {
+ if (!attribute_info) {
i_instance += set_group.transforms.size();
continue;
}
- const AttributeDomain source_domain = dummy_attribute->domain();
- ReadAttributePtr source_attribute = source_component.attribute_get_for_read(
+ const AttributeDomain source_domain = attribute_info->domain;
+ GVArrayPtr source_attribute = source_component.attribute_get_for_read(
attribute_name, source_domain, output_data_type, nullptr);
if (!source_attribute) {
i_instance += set_group.transforms.size();
continue;
}
- fn::GSpan source_span = source_attribute->get_span();
+
+ for (const int UNUSED(i_set_instance) : set_group.transforms.index_range()) {
+ const int offset = instance_start_offsets[i_instance];
+ Span<float3> bary_coords = bary_coords_array[i_instance];
+ Span<int> looptri_indices = looptri_indices_array[i_instance];
+
+ GMutableSpan instance_span = out_span.slice(offset, bary_coords.size());
+ interpolate_attribute(
+ mesh, bary_coords, looptri_indices, source_domain, *source_attribute, instance_span);
+
+ i_instance++;
+ }
attribute_math::convert_to_static_type(output_data_type, [&](auto dummy) {
using T = decltype(dummy);
- for (const int UNUSED(i_set_instance) : set_group.transforms.index_range()) {
- const int offset = instance_start_offsets[i_instance];
- Span<float3> bary_coords = bary_coords_array[i_instance];
- Span<int> looptri_indices = looptri_indices_array[i_instance];
-
- MutableSpan<T> instance_span = out_span.typed<T>().slice(offset, bary_coords.size());
- interpolate_attribute<T>(mesh,
- bary_coords,
- looptri_indices,
- source_domain,
- source_span.typed<T>(),
- instance_span);
-
- i_instance++;
- }
+ GVArray_Span<T> source_span{*source_attribute};
});
}
- attribute_out.apply_span_and_save();
+ attribute_out.save();
}
}
@@ -431,16 +350,16 @@ BLI_NOINLINE static void compute_special_attributes(Span<GeometryInstanceGroup>
Span<Vector<float3>> bary_coords_array,
Span<Vector<int>> looptri_indices_array)
{
- OutputAttributePtr id_attribute = component.attribute_try_get_for_output(
- "id", ATTR_DOMAIN_POINT, CD_PROP_INT32);
- OutputAttributePtr normal_attribute = component.attribute_try_get_for_output(
- "normal", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3);
- OutputAttributePtr rotation_attribute = component.attribute_try_get_for_output(
- "rotation", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3);
+ OutputAttribute_Typed<int> id_attribute = component.attribute_try_get_for_output_only<int>(
+ "id", ATTR_DOMAIN_POINT);
+ OutputAttribute_Typed<float3> normal_attribute =
+ component.attribute_try_get_for_output_only<float3>("normal", ATTR_DOMAIN_POINT);
+ OutputAttribute_Typed<float3> rotation_attribute =
+ component.attribute_try_get_for_output_only<float3>("rotation", ATTR_DOMAIN_POINT);
- MutableSpan<int> result_ids = id_attribute->get_span_for_write_only<int>();
- MutableSpan<float3> result_normals = normal_attribute->get_span_for_write_only<float3>();
- MutableSpan<float3> result_rotations = rotation_attribute->get_span_for_write_only<float3>();
+ MutableSpan<int> result_ids = id_attribute.as_span();
+ MutableSpan<float3> result_normals = normal_attribute.as_span();
+ MutableSpan<float3> result_rotations = rotation_attribute.as_span();
int i_instance = 0;
for (const GeometryInstanceGroup &set_group : sets) {
@@ -484,9 +403,9 @@ BLI_NOINLINE static void compute_special_attributes(Span<GeometryInstanceGroup>
}
}
- id_attribute.apply_span_and_save();
- normal_attribute.apply_span_and_save();
- rotation_attribute.apply_span_and_save();
+ id_attribute.save();
+ normal_attribute.save();
+ rotation_attribute.save();
}
BLI_NOINLINE static void add_remaining_point_attributes(
@@ -524,7 +443,7 @@ static void distribute_points_random(Span<GeometryInstanceGroup> set_groups,
for (const GeometryInstanceGroup &set_group : set_groups) {
const GeometrySet &set = set_group.geometry_set;
const MeshComponent &component = *set.get_component_for_read<MeshComponent>();
- const FloatReadAttribute density_factors = component.attribute_get_for_read<float>(
+ GVArray_Typed<float> density_factors = component.attribute_get_for_read<float>(
density_attribute_name, ATTR_DOMAIN_CORNER, use_one_default ? 1.0f : 0.0f);
const Mesh &mesh = *component.get_for_read();
for (const float4x4 &transform : set_group.transforms) {
@@ -534,7 +453,7 @@ static void distribute_points_random(Span<GeometryInstanceGroup> set_groups,
sample_mesh_surface(mesh,
transform,
density,
- &density_factors,
+ &*density_factors,
seed,
positions,
bary_coords,
@@ -593,7 +512,7 @@ static void distribute_points_poisson_disk(Span<GeometryInstanceGroup> set_group
const GeometrySet &set = set_group.geometry_set;
const MeshComponent &component = *set.get_component_for_read<MeshComponent>();
const Mesh &mesh = *component.get_for_read();
- const FloatReadAttribute density_factors = component.attribute_get_for_read<float>(
+ const GVArray_Typed<float> density_factors = component.attribute_get_for_read<float>(
density_attribute_name, ATTR_DOMAIN_CORNER, use_one_default ? 1.0f : 0.0f);
for (const int UNUSED(i_set_instance) : set_group.transforms.index_range()) {
@@ -626,7 +545,7 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params)
const GeometryNodePointDistributeMode distribute_method =
static_cast<GeometryNodePointDistributeMode>(params.node().custom1);
- const int seed = params.get_input<int>("Seed");
+ const int seed = params.get_input<int>("Seed") * 5383843;
const float density = params.extract_input<float>("Density Max");
const std::string density_attribute_name = params.extract_input<std::string>(
"Density Attribute");
@@ -636,7 +555,8 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params)
return;
}
- Vector<GeometryInstanceGroup> set_groups = bke::geometry_set_gather_instances(geometry_set);
+ Vector<GeometryInstanceGroup> set_groups;
+ geometry_set_gather_instances(geometry_set, set_groups);
if (set_groups.is_empty()) {
params.set_output("Geometry", GeometrySet());
return;
@@ -715,8 +635,8 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params)
geometry_set_out.get_component_for_write<PointCloudComponent>();
Map<std::string, AttributeKind> attributes;
- bke::gather_attribute_info(
- attributes, {GEO_COMPONENT_TYPE_MESH}, set_groups, {"position", "normal", "id"});
+ bke::geometry_set_gather_instances_attribute_info(
+ set_groups, {GEO_COMPONENT_TYPE_MESH}, {"position", "normal", "id"}, attributes);
add_remaining_point_attributes(set_groups,
instance_start_offsets,
attributes,
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc
index dbbb73bd36d..e52ab1b2127 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc
@@ -14,15 +14,10 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "BKE_mesh.h"
-#include "BKE_persistent_data_handle.hh"
-
#include "DNA_collection_types.h"
-#include "DNA_mesh_types.h"
-#include "DNA_meshdata_types.h"
-#include "DNA_pointcloud_types.h"
#include "BLI_hash.h"
+#include "BLI_task.hh"
#include "UI_interface.h"
#include "UI_resources.h"
@@ -31,8 +26,17 @@
static bNodeSocketTemplate geo_node_point_instance_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
- {SOCK_OBJECT, N_("Object")},
- {SOCK_COLLECTION, N_("Collection")},
+ {SOCK_OBJECT, N_("Object"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, PROP_NONE, SOCK_HIDE_LABEL},
+ {SOCK_COLLECTION,
+ N_("Collection"),
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ PROP_NONE,
+ SOCK_HIDE_LABEL},
{SOCK_INT, N_("Seed"), 0, 0, 0, 0, -10000, 10000},
{-1, ""},
};
@@ -52,6 +56,15 @@ static void geo_node_point_instance_layout(uiLayout *layout, bContext *UNUSED(C)
namespace blender::nodes {
+static void geo_node_point_instance_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryPointInstance *data = (NodeGeometryPointInstance *)MEM_callocN(
+ sizeof(NodeGeometryPointInstance), __func__);
+ data->instance_type = GEO_NODE_POINT_INSTANCE_TYPE_OBJECT;
+ data->flag |= GEO_NODE_POINT_INSTANCE_WHOLE_COLLECTION;
+ node->storage = data;
+}
+
static void geo_node_point_instance_update(bNodeTree *UNUSED(tree), bNode *node)
{
bNodeSocket *object_socket = (bNodeSocket *)BLI_findlink(&node->inputs, 1);
@@ -69,129 +82,133 @@ static void geo_node_point_instance_update(bNodeTree *UNUSED(tree), bNode *node)
seed_socket, type == GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION && !use_whole_collection);
}
-static void get_instanced_data__object(const GeoNodeExecParams &params,
- MutableSpan<std::optional<InstancedData>> r_instances_data)
+static Vector<InstanceReference> get_instance_references__object(GeoNodeExecParams &params)
{
- bke::PersistentObjectHandle object_handle = params.get_input<bke::PersistentObjectHandle>(
- "Object");
- Object *object = params.handle_map().lookup(object_handle);
+ Object *object = params.extract_input<Object *>("Object");
if (object == params.self_object()) {
- object = nullptr;
+ return {};
}
if (object != nullptr) {
- InstancedData instance;
- instance.type = INSTANCE_DATA_TYPE_OBJECT;
- instance.data.object = object;
- r_instances_data.fill(instance);
+ return {*object};
}
+ return {};
}
-static void get_instanced_data__collection(
- const GeoNodeExecParams &params,
- const GeometryComponent &component,
- MutableSpan<std::optional<InstancedData>> r_instances_data)
+static Vector<InstanceReference> get_instance_references__collection(GeoNodeExecParams &params)
{
const bNode &node = params.node();
NodeGeometryPointInstance *node_storage = (NodeGeometryPointInstance *)node.storage;
- bke::PersistentCollectionHandle collection_handle =
- params.get_input<bke::PersistentCollectionHandle>("Collection");
- Collection *collection = params.handle_map().lookup(collection_handle);
+ Collection *collection = params.get_input<Collection *>("Collection");
if (collection == nullptr) {
- return;
+ return {};
}
if (BLI_listbase_is_empty(&collection->children) &&
BLI_listbase_is_empty(&collection->gobject)) {
params.error_message_add(NodeWarningType::Info, TIP_("Collection is empty"));
- return;
+ return {};
}
- const bool use_whole_collection = (node_storage->flag &
- GEO_NODE_POINT_INSTANCE_WHOLE_COLLECTION) != 0;
- if (use_whole_collection) {
- InstancedData instance;
- instance.type = INSTANCE_DATA_TYPE_COLLECTION;
- instance.data.collection = collection;
- r_instances_data.fill(instance);
+ if (node_storage->flag & GEO_NODE_POINT_INSTANCE_WHOLE_COLLECTION) {
+ return {*collection};
}
- else {
- Vector<InstancedData> possible_instances;
- /* Direct child objects are instanced as objects. */
- LISTBASE_FOREACH (CollectionObject *, cob, &collection->gobject) {
- Object *object = cob->ob;
- InstancedData instance;
- instance.type = INSTANCE_DATA_TYPE_OBJECT;
- instance.data.object = object;
- possible_instances.append(instance);
- }
- /* Direct child collections are instanced as collections. */
- LISTBASE_FOREACH (CollectionChild *, child, &collection->children) {
- Collection *child_collection = child->collection;
- InstancedData instance;
- instance.type = INSTANCE_DATA_TYPE_COLLECTION;
- instance.data.collection = child_collection;
- possible_instances.append(instance);
- }
- if (!possible_instances.is_empty()) {
- const int seed = params.get_input<int>("Seed");
- Array<uint32_t> ids = get_geometry_element_ids_as_uints(component, ATTR_DOMAIN_POINT);
- for (const int i : r_instances_data.index_range()) {
- const int index = BLI_hash_int_2d(ids[i], seed) % possible_instances.size();
- r_instances_data[i] = possible_instances[index];
- }
- }
+ Vector<InstanceReference> references;
+ /* Direct child objects are instanced as objects. */
+ LISTBASE_FOREACH (CollectionObject *, cob, &collection->gobject) {
+ references.append(*cob->ob);
+ }
+ /* Direct child collections are instanced as collections. */
+ LISTBASE_FOREACH (CollectionChild *, child, &collection->children) {
+ references.append(*child->collection);
}
+
+ return references;
}
-static Array<std::optional<InstancedData>> get_instanced_data(const GeoNodeExecParams &params,
- const GeometryComponent &component,
- const int amount)
+static Vector<InstanceReference> get_instance_references(GeoNodeExecParams &params)
{
const bNode &node = params.node();
NodeGeometryPointInstance *node_storage = (NodeGeometryPointInstance *)node.storage;
const GeometryNodePointInstanceType type = (GeometryNodePointInstanceType)
node_storage->instance_type;
- Array<std::optional<InstancedData>> instances_data(amount);
switch (type) {
case GEO_NODE_POINT_INSTANCE_TYPE_OBJECT: {
- get_instanced_data__object(params, instances_data);
- break;
+ return get_instance_references__object(params);
}
case GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION: {
- get_instanced_data__collection(params, component, instances_data);
- break;
+ return get_instance_references__collection(params);
}
}
- return instances_data;
+ return {};
+}
+
+/**
+ * Add the instance references to the component as a separate step from actually creating the
+ * instances in order to avoid a map lookup for every transform. While this might add some
+ * unnecessary references if they are not chosen while adding transforms, in the common cases
+ * there are many more transforms than there are references, so that isn't likely.
+ */
+static Array<int> add_instance_references(InstancesComponent &instance_component,
+ Span<InstanceReference> possible_references)
+{
+ Array<int> possible_handles(possible_references.size());
+ for (const int i : possible_references.index_range()) {
+ possible_handles[i] = instance_component.add_reference(possible_references[i]);
+ }
+ return possible_handles;
}
-static void add_instances_from_geometry_component(InstancesComponent &instances,
- const GeometryComponent &src_geometry,
- const GeoNodeExecParams &params)
+static void add_instances_from_component(InstancesComponent &instances,
+ const GeometryComponent &src_geometry,
+ Span<int> possible_handles,
+ const GeoNodeExecParams &params)
{
const AttributeDomain domain = ATTR_DOMAIN_POINT;
const int domain_size = src_geometry.attribute_domain_size(domain);
- Array<std::optional<InstancedData>> instances_data = get_instanced_data(
- params, src_geometry, domain_size);
- Float3ReadAttribute positions = src_geometry.attribute_get_for_read<float3>(
+ GVArray_Typed<float3> positions = src_geometry.attribute_get_for_read<float3>(
"position", domain, {0, 0, 0});
- Float3ReadAttribute rotations = src_geometry.attribute_get_for_read<float3>(
+ GVArray_Typed<float3> rotations = src_geometry.attribute_get_for_read<float3>(
"rotation", domain, {0, 0, 0});
- Float3ReadAttribute scales = src_geometry.attribute_get_for_read<float3>(
+ GVArray_Typed<float3> scales = src_geometry.attribute_get_for_read<float3>(
"scale", domain, {1, 1, 1});
- Int32ReadAttribute ids = src_geometry.attribute_get_for_read<int>("id", domain, -1);
-
- for (const int i : IndexRange(domain_size)) {
- if (instances_data[i].has_value()) {
- float transform[4][4];
- loc_eul_size_to_mat4(transform, positions[i], rotations[i], scales[i]);
- instances.add_instance(*instances_data[i], transform, ids[i]);
- }
+ GVArray_Typed<int> id_attribute = src_geometry.attribute_get_for_read<int>("id", domain, -1);
+
+ /* The initial size of the component might be non-zero if there are two component types. */
+ const int start_len = instances.instances_amount();
+ instances.resize(start_len + domain_size);
+ MutableSpan<int> handles = instances.instance_reference_handles().slice(start_len, domain_size);
+ MutableSpan<float4x4> transforms = instances.instance_transforms().slice(start_len, domain_size);
+ MutableSpan<int> instance_ids = instances.instance_ids().slice(start_len, domain_size);
+
+ /* Skip all of the randomness handling if there is only a single possible instance
+ * (anything except for collection mode with "Whole Collection" turned off). */
+ if (possible_handles.size() == 1) {
+ const int handle = possible_handles.first();
+ parallel_for(IndexRange(domain_size), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ handles[i] = handle;
+ transforms[i] = float4x4::from_loc_eul_scale(positions[i], rotations[i], scales[i]);
+ instance_ids[i] = id_attribute[i];
+ }
+ });
+ }
+ else {
+ const int seed = params.get_input<int>("Seed");
+ Array<uint32_t> ids = get_geometry_element_ids_as_uints(src_geometry, ATTR_DOMAIN_POINT);
+ parallel_for(IndexRange(domain_size), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ const int index = BLI_hash_int_2d(ids[i], seed) % possible_handles.size();
+ const int handle = possible_handles[index];
+ handles[i] = handle;
+ transforms[i] = float4x4::from_loc_eul_scale(positions[i], rotations[i], scales[i]);
+ instance_ids[i] = id_attribute[i];
+ }
+ });
}
}
@@ -204,28 +221,37 @@ static void geo_node_point_instance_exec(GeoNodeExecParams params)
* rather than making the entire input geometry set real. */
geometry_set = geometry_set_realize_instances(geometry_set);
+ const Vector<InstanceReference> possible_references = get_instance_references(params);
+ if (possible_references.is_empty()) {
+ params.set_output("Geometry", std::move(geometry_set_out));
+ return;
+ }
+
InstancesComponent &instances = geometry_set_out.get_component_for_write<InstancesComponent>();
+ Array<int> possible_handles = add_instance_references(instances, possible_references);
+
if (geometry_set.has<MeshComponent>()) {
- add_instances_from_geometry_component(
- instances, *geometry_set.get_component_for_read<MeshComponent>(), params);
+ add_instances_from_component(instances,
+ *geometry_set.get_component_for_read<MeshComponent>(),
+ possible_handles,
+ params);
}
if (geometry_set.has<PointCloudComponent>()) {
- add_instances_from_geometry_component(
- instances, *geometry_set.get_component_for_read<PointCloudComponent>(), params);
+ add_instances_from_component(instances,
+ *geometry_set.get_component_for_read<PointCloudComponent>(),
+ possible_handles,
+ params);
+ }
+ if (geometry_set.has<CurveComponent>()) {
+ add_instances_from_component(instances,
+ *geometry_set.get_component_for_read<CurveComponent>(),
+ possible_handles,
+ params);
}
params.set_output("Geometry", std::move(geometry_set_out));
}
-static void geo_node_point_instance_init(bNodeTree *UNUSED(tree), bNode *node)
-{
- NodeGeometryPointInstance *data = (NodeGeometryPointInstance *)MEM_callocN(
- sizeof(NodeGeometryPointInstance), __func__);
- data->instance_type = GEO_NODE_POINT_INSTANCE_TYPE_OBJECT;
- data->flag |= GEO_NODE_POINT_INSTANCE_WHOLE_COLLECTION;
- node->storage = data;
-}
-
} // namespace blender::nodes
void register_node_type_geo_point_instance()
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc b/source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc
index 5eef2fabce0..828d3f50551 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc
@@ -14,13 +14,13 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "node_geometry_util.hh"
-
#include "BLI_math_rotation.h"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "node_geometry_util.hh"
+
static bNodeSocketTemplate geo_node_point_rotate_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("Axis")},
@@ -59,9 +59,43 @@ static void geo_node_point_rotate_layout(uiLayout *layout, bContext *UNUSED(C),
namespace blender::nodes {
+static void geo_node_point_rotate_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryRotatePoints *node_storage = (NodeGeometryRotatePoints *)MEM_callocN(
+ sizeof(NodeGeometryRotatePoints), __func__);
+
+ node_storage->type = GEO_NODE_POINT_ROTATE_TYPE_EULER;
+ node_storage->space = GEO_NODE_POINT_ROTATE_SPACE_OBJECT;
+ node_storage->input_type_axis = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
+ node_storage->input_type_angle = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
+ node_storage->input_type_rotation = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
+
+ node->storage = node_storage;
+}
+
+static void geo_node_point_rotate_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryRotatePoints *node_storage = (NodeGeometryRotatePoints *)node->storage;
+ update_attribute_input_socket_availabilities(
+ *node,
+ "Axis",
+ (GeometryNodeAttributeInputMode)node_storage->input_type_axis,
+ node_storage->type == GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE);
+ update_attribute_input_socket_availabilities(
+ *node,
+ "Angle",
+ (GeometryNodeAttributeInputMode)node_storage->input_type_angle,
+ node_storage->type == GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE);
+ update_attribute_input_socket_availabilities(
+ *node,
+ "Rotation",
+ (GeometryNodeAttributeInputMode)node_storage->input_type_rotation,
+ node_storage->type == GEO_NODE_POINT_ROTATE_TYPE_EULER);
+}
+
static void point_rotate__axis_angle__object_space(const int domain_size,
- const Float3ReadAttribute &axis,
- const FloatReadAttribute &angles,
+ const VArray<float3> &axis,
+ const VArray<float> &angles,
MutableSpan<float3> rotations)
{
for (const int i : IndexRange(domain_size)) {
@@ -76,8 +110,8 @@ static void point_rotate__axis_angle__object_space(const int domain_size,
}
static void point_rotate__axis_angle__point_space(const int domain_size,
- const Float3ReadAttribute &axis,
- const FloatReadAttribute &angles,
+ const VArray<float3> &axis,
+ const VArray<float> &angles,
MutableSpan<float3> rotations)
{
for (const int i : IndexRange(domain_size)) {
@@ -92,7 +126,7 @@ static void point_rotate__axis_angle__point_space(const int domain_size,
}
static void point_rotate__euler__object_space(const int domain_size,
- const Float3ReadAttribute &eulers,
+ const VArray<float3> &eulers,
MutableSpan<float3> rotations)
{
for (const int i : IndexRange(domain_size)) {
@@ -107,7 +141,7 @@ static void point_rotate__euler__object_space(const int domain_size,
}
static void point_rotate__euler__point_space(const int domain_size,
- const Float3ReadAttribute &eulers,
+ const VArray<float3> &eulers,
MutableSpan<float3> rotations)
{
for (const int i : IndexRange(domain_size)) {
@@ -127,19 +161,19 @@ static void point_rotate_on_component(GeometryComponent &component,
const bNode &node = params.node();
const NodeGeometryRotatePoints &storage = *(const NodeGeometryRotatePoints *)node.storage;
- OutputAttributePtr rotation_attribute = component.attribute_try_get_for_output(
- "rotation", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3);
+ OutputAttribute_Typed<float3> rotation_attribute =
+ component.attribute_try_get_for_output<float3>("rotation", ATTR_DOMAIN_POINT, {0, 0, 0});
if (!rotation_attribute) {
return;
}
- MutableSpan<float3> rotations = rotation_attribute->get_span<float3>();
+ MutableSpan<float3> rotations = rotation_attribute.as_span();
const int domain_size = rotations.size();
if (storage.type == GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE) {
- Float3ReadAttribute axis = params.get_input_attribute<float3>(
+ GVArray_Typed<float3> axis = params.get_input_attribute<float3>(
"Axis", component, ATTR_DOMAIN_POINT, {0, 0, 1});
- FloatReadAttribute angles = params.get_input_attribute<float>(
+ GVArray_Typed<float> angles = params.get_input_attribute<float>(
"Angle", component, ATTR_DOMAIN_POINT, 0);
if (storage.space == GEO_NODE_POINT_ROTATE_SPACE_OBJECT) {
@@ -150,7 +184,7 @@ static void point_rotate_on_component(GeometryComponent &component,
}
}
else {
- Float3ReadAttribute eulers = params.get_input_attribute<float3>(
+ GVArray_Typed<float3> eulers = params.get_input_attribute<float3>(
"Rotation", component, ATTR_DOMAIN_POINT, {0, 0, 0});
if (storage.space == GEO_NODE_POINT_ROTATE_SPACE_OBJECT) {
@@ -161,7 +195,7 @@ static void point_rotate_on_component(GeometryComponent &component,
}
}
- rotation_attribute.apply_span_and_save();
+ rotation_attribute.save();
}
static void geo_node_point_rotate_exec(GeoNodeExecParams params)
@@ -176,44 +210,13 @@ static void geo_node_point_rotate_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
point_rotate_on_component(geometry_set.get_component_for_write<PointCloudComponent>(), params);
}
+ if (geometry_set.has<CurveComponent>()) {
+ point_rotate_on_component(geometry_set.get_component_for_write<CurveComponent>(), params);
+ }
params.set_output("Geometry", geometry_set);
}
-static void geo_node_point_rotate_init(bNodeTree *UNUSED(ntree), bNode *node)
-{
- NodeGeometryRotatePoints *node_storage = (NodeGeometryRotatePoints *)MEM_callocN(
- sizeof(NodeGeometryRotatePoints), __func__);
-
- node_storage->type = GEO_NODE_POINT_ROTATE_TYPE_EULER;
- node_storage->space = GEO_NODE_POINT_ROTATE_SPACE_OBJECT;
- node_storage->input_type_axis = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
- node_storage->input_type_angle = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
- node_storage->input_type_rotation = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
-
- node->storage = node_storage;
-}
-
-static void geo_node_point_rotate_update(bNodeTree *UNUSED(ntree), bNode *node)
-{
- NodeGeometryRotatePoints *node_storage = (NodeGeometryRotatePoints *)node->storage;
- update_attribute_input_socket_availabilities(
- *node,
- "Axis",
- (GeometryNodeAttributeInputMode)node_storage->input_type_axis,
- node_storage->type == GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE);
- update_attribute_input_socket_availabilities(
- *node,
- "Angle",
- (GeometryNodeAttributeInputMode)node_storage->input_type_angle,
- node_storage->type == GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE);
- update_attribute_input_socket_availabilities(
- *node,
- "Rotation",
- (GeometryNodeAttributeInputMode)node_storage->input_type_rotation,
- node_storage->type == GEO_NODE_POINT_ROTATE_TYPE_EULER);
-}
-
} // namespace blender::nodes
void register_node_type_geo_point_rotate()
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_scale.cc b/source/blender/nodes/geometry/nodes/node_geo_point_scale.cc
index 9df103ff057..265ec9fb776 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_scale.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_point_scale.cc
@@ -14,17 +14,18 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "node_geometry_util.hh"
-
#include "BKE_colorband.h"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "node_geometry_util.hh"
+
static bNodeSocketTemplate geo_node_point_scale_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("Factor")},
{SOCK_VECTOR, N_("Factor"), 1.0f, 1.0f, 1.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_XYZ},
+ {SOCK_FLOAT, N_("Factor"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX},
{-1, ""},
};
@@ -42,27 +43,64 @@ static void geo_node_point_scale_layout(uiLayout *layout, bContext *UNUSED(C), P
namespace blender::nodes {
+static void geo_node_point_scale_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryPointScale *data = (NodeGeometryPointScale *)MEM_callocN(
+ sizeof(NodeGeometryPointScale), __func__);
+
+ data->input_type = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
+ node->storage = data;
+}
+
+static void geo_node_point_scale_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryPointScale &node_storage = *(NodeGeometryPointScale *)node->storage;
+
+ update_attribute_input_socket_availabilities(
+ *node, "Factor", (GeometryNodeAttributeInputMode)node_storage.input_type);
+}
+
static void execute_on_component(GeoNodeExecParams params, GeometryComponent &component)
{
+ /* Note that scale doesn't necessarily need to be created with a vector type-- it could also use
+ * the highest complexity of the existing attribute's type (if it exists) and the data type used
+ * for the factor. But for it's simpler to simply always use float3, since that is usually
+ * expected anyway. */
static const float3 scale_default = float3(1.0f);
- OutputAttributePtr scale_attribute = component.attribute_try_get_for_output(
+ OutputAttribute_Typed<float3> scale_attribute = component.attribute_try_get_for_output(
"scale", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, &scale_default);
if (!scale_attribute) {
return;
}
- ReadAttributePtr attribute = params.get_input_attribute(
- "Factor", component, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, nullptr);
+
+ const bNode &node = params.node();
+ const NodeGeometryPointScale &node_storage = *(const NodeGeometryPointScale *)node.storage;
+ const GeometryNodeAttributeInputMode input_type = (GeometryNodeAttributeInputMode)
+ node_storage.input_type;
+ const CustomDataType data_type = (input_type == GEO_NODE_ATTRIBUTE_INPUT_FLOAT) ? CD_PROP_FLOAT :
+ CD_PROP_FLOAT3;
+
+ GVArrayPtr attribute = params.get_input_attribute(
+ "Factor", component, ATTR_DOMAIN_POINT, data_type, nullptr);
if (!attribute) {
return;
}
- Span<float3> data = attribute->get_span<float3>();
- MutableSpan<float3> scale_span = scale_attribute->get_span<float3>();
- for (const int i : scale_span.index_range()) {
- scale_span[i] = scale_span[i] * data[i];
+ MutableSpan<float3> scale_span = scale_attribute.as_span();
+ if (data_type == CD_PROP_FLOAT) {
+ GVArray_Typed<float> factors{*attribute};
+ for (const int i : scale_span.index_range()) {
+ scale_span[i] = scale_span[i] * factors[i];
+ }
+ }
+ else if (data_type == CD_PROP_FLOAT3) {
+ GVArray_Typed<float3> factors{*attribute};
+ for (const int i : scale_span.index_range()) {
+ scale_span[i] = scale_span[i] * factors[i];
+ }
}
- scale_attribute.apply_span_and_save();
+ scale_attribute.save();
}
static void geo_node_point_scale_exec(GeoNodeExecParams params)
@@ -77,27 +115,13 @@ static void geo_node_point_scale_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
execute_on_component(params, geometry_set.get_component_for_write<PointCloudComponent>());
}
+ if (geometry_set.has<CurveComponent>()) {
+ execute_on_component(params, geometry_set.get_component_for_write<CurveComponent>());
+ }
params.set_output("Geometry", std::move(geometry_set));
}
-static void geo_node_point_scale_init(bNodeTree *UNUSED(tree), bNode *node)
-{
- NodeGeometryPointScale *data = (NodeGeometryPointScale *)MEM_callocN(
- sizeof(NodeGeometryPointScale), __func__);
-
- data->input_type = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE;
- node->storage = data;
-}
-
-static void geo_node_point_scale_update(bNodeTree *UNUSED(ntree), bNode *node)
-{
- NodeGeometryPointScale &node_storage = *(NodeGeometryPointScale *)node->storage;
-
- update_attribute_input_socket_availabilities(
- *node, "Factor", (GeometryNodeAttributeInputMode)node_storage.input_type);
-}
-
} // namespace blender::nodes
void register_node_type_geo_point_scale()
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc b/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc
index 522dea4aa0e..fc04d1e275f 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc
@@ -52,33 +52,33 @@ static void copy_data_based_on_mask(Span<T> data,
}
}
-static void copy_attributes_based_on_mask(const GeometryComponent &in_component,
- GeometryComponent &result_component,
- Span<bool> masks,
- const bool invert)
+void copy_point_attributes_based_on_mask(const GeometryComponent &in_component,
+ GeometryComponent &result_component,
+ Span<bool> masks,
+ const bool invert)
{
for (const std::string &name : in_component.attribute_names()) {
- ReadAttributePtr attribute = in_component.attribute_try_get_for_read(name);
- const CustomDataType data_type = attribute->custom_data_type();
+ ReadAttributeLookup attribute = in_component.attribute_try_get_for_read(name);
+ const CustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute.varray->type());
/* Only copy point attributes. Theoretically this could interpolate attributes on other
* domains to the point domain, but that would conflict with attributes that are built-in
* on other domains, which causes creating the attributes to fail. */
- if (attribute->domain() != ATTR_DOMAIN_POINT) {
+ if (attribute.domain != ATTR_DOMAIN_POINT) {
continue;
}
- OutputAttributePtr result_attribute = result_component.attribute_try_get_for_output(
+ OutputAttribute result_attribute = result_component.attribute_try_get_for_output_only(
name, ATTR_DOMAIN_POINT, data_type);
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);
- Span<T> span = attribute->get_span<T>();
- MutableSpan<T> out_span = result_attribute->get_span_for_write_only<T>();
+ GVArray_Span<T> span{*attribute.varray};
+ MutableSpan<T> out_span = result_attribute.as_span<T>();
copy_data_based_on_mask(span, masks, invert, out_span);
});
- result_attribute.apply_span_and_save();
+ result_attribute.save();
}
}
@@ -107,9 +107,9 @@ static void separate_points_from_component(const GeometryComponent &in_component
return;
}
- const BooleanReadAttribute mask_attribute = in_component.attribute_get_for_read<bool>(
+ const GVArray_Typed<bool> mask_attribute = in_component.attribute_get_for_read<bool>(
mask_name, ATTR_DOMAIN_POINT, false);
- Span<bool> masks = mask_attribute.get_span();
+ VArray_Span<bool> masks{mask_attribute};
const int total = masks.count(!invert);
if (total == 0) {
@@ -118,7 +118,7 @@ static void separate_points_from_component(const GeometryComponent &in_component
create_component_points(out_component, total);
- copy_attributes_based_on_mask(in_component, out_component, masks, invert);
+ copy_point_attributes_based_on_mask(in_component, out_component, masks, invert);
}
static GeometrySet separate_geometry_set(const GeometrySet &set_in,
@@ -127,6 +127,10 @@ static GeometrySet separate_geometry_set(const GeometrySet &set_in,
{
GeometrySet set_out;
for (const GeometryComponent *component : set_in.get_components_for_read()) {
+ if (component->type() == GEO_COMPONENT_TYPE_CURVE) {
+ /* Don't support the curve component for now, even though it has a point domain. */
+ continue;
+ }
GeometryComponent &out_component = set_out.get_component_for_write(component->type());
separate_points_from_component(*component, out_component, mask_name, invert);
}
@@ -135,15 +139,27 @@ static GeometrySet separate_geometry_set(const GeometrySet &set_in,
static void geo_node_point_separate_exec(GeoNodeExecParams params)
{
- const std::string mask_attribute_name = params.extract_input<std::string>("Mask");
- GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ bool wait_for_inputs = false;
+ wait_for_inputs |= params.lazy_require_input("Geometry");
+ wait_for_inputs |= params.lazy_require_input("Mask");
+ if (wait_for_inputs) {
+ return;
+ }
+ const std::string mask_attribute_name = params.get_input<std::string>("Mask");
+ GeometrySet geometry_set = params.get_input<GeometrySet>("Geometry");
/* TODO: This is not necessary-- the input geometry set can be read only,
* but it must be rewritten to handle instance groups. */
geometry_set = geometry_set_realize_instances(geometry_set);
- params.set_output("Geometry 1", separate_geometry_set(geometry_set, mask_attribute_name, true));
- params.set_output("Geometry 2", separate_geometry_set(geometry_set, mask_attribute_name, false));
+ if (params.lazy_output_is_required("Geometry 1")) {
+ params.set_output("Geometry 1",
+ separate_geometry_set(geometry_set, mask_attribute_name, true));
+ }
+ if (params.lazy_output_is_required("Geometry 2")) {
+ params.set_output("Geometry 2",
+ separate_geometry_set(geometry_set, mask_attribute_name, false));
+ }
}
} // namespace blender::nodes
@@ -155,5 +171,6 @@ void register_node_type_geo_point_separate()
geo_node_type_base(&ntype, GEO_NODE_POINT_SEPARATE, "Point Separate", NODE_CLASS_GEOMETRY, 0);
node_type_socket_templates(&ntype, geo_node_point_instance_in, geo_node_point_instance_out);
ntype.geometry_node_execute = blender::nodes::geo_node_point_separate_exec;
+ ntype.geometry_node_execute_supports_laziness = true;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc b/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc
index 015f4cd38e7..293f151fe19 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc
@@ -14,13 +14,11 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "node_geometry_util.hh"
-
-#include "BKE_colorband.h"
-
#include "UI_interface.h"
#include "UI_resources.h"
+#include "node_geometry_util.hh"
+
static bNodeSocketTemplate geo_node_point_translate_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_STRING, N_("Translation")},
@@ -44,24 +42,19 @@ namespace blender::nodes {
static void execute_on_component(GeoNodeExecParams params, GeometryComponent &component)
{
- OutputAttributePtr position_attribute = component.attribute_try_get_for_output(
- "position", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3);
+ OutputAttribute_Typed<float3> position_attribute =
+ component.attribute_try_get_for_output<float3>("position", ATTR_DOMAIN_POINT, {0, 0, 0});
if (!position_attribute) {
return;
}
- ReadAttributePtr attribute = params.get_input_attribute(
- "Translation", component, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, nullptr);
- if (!attribute) {
- return;
- }
+ GVArray_Typed<float3> attribute = params.get_input_attribute<float3>(
+ "Translation", component, ATTR_DOMAIN_POINT, {0, 0, 0});
- Span<float3> data = attribute->get_span<float3>();
- MutableSpan<float3> scale_span = position_attribute->get_span<float3>();
- for (const int i : scale_span.index_range()) {
- scale_span[i] = scale_span[i] + data[i];
+ for (const int i : IndexRange(attribute.size())) {
+ position_attribute->set(i, position_attribute->get(i) + attribute[i]);
}
- position_attribute.apply_span_and_save();
+ position_attribute.save();
}
static void geo_node_point_translate_exec(GeoNodeExecParams params)
@@ -76,6 +69,9 @@ static void geo_node_point_translate_exec(GeoNodeExecParams params)
if (geometry_set.has<PointCloudComponent>()) {
execute_on_component(params, geometry_set.get_component_for_write<PointCloudComponent>());
}
+ if (geometry_set.has<CurveComponent>()) {
+ execute_on_component(params, geometry_set.get_component_for_write<CurveComponent>());
+ }
params.set_output("Geometry", std::move(geometry_set));
}
@@ -85,7 +81,7 @@ static void geo_node_point_translate_init(bNodeTree *UNUSED(tree), bNode *node)
NodeGeometryPointTranslate *data = (NodeGeometryPointTranslate *)MEM_callocN(
sizeof(NodeGeometryPointTranslate), __func__);
- data->input_type = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE;
+ data->input_type = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
node->storage = data;
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc
index b79ca0a6197..65306b1c452 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc
@@ -31,14 +31,14 @@
static bNodeSocketTemplate geo_node_points_to_volume_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_FLOAT, N_("Density"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX},
- {SOCK_FLOAT, N_("Voxel Size"), 0.3f, 0.0f, 0.0f, 0.0f, 0.01f, FLT_MAX},
+ {SOCK_FLOAT, N_("Voxel Size"), 0.3f, 0.0f, 0.0f, 0.0f, 0.01f, FLT_MAX, PROP_DISTANCE},
{SOCK_FLOAT, N_("Voxel Amount"), 64.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX},
{SOCK_STRING, N_("Radius")},
{SOCK_FLOAT, N_("Radius"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX},
{-1, ""},
};
-static bNodeSocketTemplate geo_node_point_translate_out[] = {
+static bNodeSocketTemplate geo_node_points_to_volume_out[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{-1, ""},
};
@@ -55,6 +55,35 @@ static void geo_node_points_to_volume_layout(uiLayout *layout,
namespace blender::nodes {
+static void geo_node_points_to_volume_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryPointsToVolume *data = (NodeGeometryPointsToVolume *)MEM_callocN(
+ sizeof(NodeGeometryPointsToVolume), __func__);
+ data->resolution_mode = GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT;
+ data->input_type_radius = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
+ node->storage = data;
+
+ bNodeSocket *radius_attribute_socket = nodeFindSocket(node, SOCK_IN, "Radius");
+ bNodeSocketValueString *radius_attribute_socket_value =
+ (bNodeSocketValueString *)radius_attribute_socket->default_value;
+ STRNCPY(radius_attribute_socket_value->value, "radius");
+}
+
+static void geo_node_points_to_volume_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryPointsToVolume *data = (NodeGeometryPointsToVolume *)node->storage;
+ bNodeSocket *voxel_size_socket = nodeFindSocket(node, SOCK_IN, "Voxel Size");
+ bNodeSocket *voxel_amount_socket = nodeFindSocket(node, SOCK_IN, "Voxel Amount");
+ nodeSetSocketAvailability(voxel_amount_socket,
+ data->resolution_mode ==
+ GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT);
+ nodeSetSocketAvailability(
+ voxel_size_socket, data->resolution_mode == GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_SIZE);
+
+ update_attribute_input_socket_availabilities(
+ *node, "Radius", (GeometryNodeAttributeInputMode)data->input_type_radius);
+}
+
#ifdef WITH_OPENVDB
namespace {
/* Implements the interface required by #openvdb::tools::ParticlesToLevelSet. */
@@ -147,13 +176,15 @@ static void gather_point_data_from_component(const GeoNodeExecParams &params,
Vector<float3> &r_positions,
Vector<float> &r_radii)
{
- Float3ReadAttribute positions = component.attribute_get_for_read<float3>(
+ GVArray_Typed<float3> positions = component.attribute_get_for_read<float3>(
"position", ATTR_DOMAIN_POINT, {0, 0, 0});
- FloatReadAttribute radii = params.get_input_attribute<float>(
+ GVArray_Typed<float> radii = params.get_input_attribute<float>(
"Radius", component, ATTR_DOMAIN_POINT, 0.0f);
- r_positions.extend(positions.get_span());
- r_radii.extend(radii.get_span());
+ for (const int i : IndexRange(positions.size())) {
+ r_positions.append(positions[i]);
+ r_radii.append(radii[i]);
+ }
}
static void convert_to_grid_index_space(const float voxel_size,
@@ -184,6 +215,10 @@ static void initialize_volume_component_from_points(const GeometrySet &geometry_
gather_point_data_from_component(
params, *geometry_set_in.get_component_for_read<PointCloudComponent>(), positions, radii);
}
+ if (geometry_set_in.has<CurveComponent>()) {
+ gather_point_data_from_component(
+ params, *geometry_set_in.get_component_for_read<CurveComponent>(), positions, radii);
+ }
const float max_radius = *std::max_element(radii.begin(), radii.end());
const float voxel_size = compute_voxel_size(params, positions, max_radius);
@@ -225,35 +260,6 @@ static void geo_node_points_to_volume_exec(GeoNodeExecParams params)
params.set_output("Geometry", std::move(geometry_set_out));
}
-static void geo_node_points_to_volume_init(bNodeTree *UNUSED(ntree), bNode *node)
-{
- NodeGeometryPointsToVolume *data = (NodeGeometryPointsToVolume *)MEM_callocN(
- sizeof(NodeGeometryPointsToVolume), __func__);
- data->resolution_mode = GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT;
- data->input_type_radius = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
- node->storage = data;
-
- bNodeSocket *radius_attribute_socket = nodeFindSocket(node, SOCK_IN, "Radius");
- bNodeSocketValueString *radius_attribute_socket_value =
- (bNodeSocketValueString *)radius_attribute_socket->default_value;
- STRNCPY(radius_attribute_socket_value->value, "radius");
-}
-
-static void geo_node_points_to_volume_update(bNodeTree *UNUSED(ntree), bNode *node)
-{
- NodeGeometryPointsToVolume *data = (NodeGeometryPointsToVolume *)node->storage;
- bNodeSocket *voxel_size_socket = nodeFindSocket(node, SOCK_IN, "Voxel Size");
- bNodeSocket *voxel_amount_socket = nodeFindSocket(node, SOCK_IN, "Voxel Amount");
- nodeSetSocketAvailability(voxel_amount_socket,
- data->resolution_mode ==
- GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT);
- nodeSetSocketAvailability(
- voxel_size_socket, data->resolution_mode == GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_SIZE);
-
- update_attribute_input_socket_availabilities(
- *node, "Radius", (GeometryNodeAttributeInputMode)data->input_type_radius);
-}
-
} // namespace blender::nodes
void register_node_type_geo_points_to_volume()
@@ -262,7 +268,7 @@ void register_node_type_geo_points_to_volume()
geo_node_type_base(
&ntype, GEO_NODE_POINTS_TO_VOLUME, "Points to Volume", NODE_CLASS_GEOMETRY, 0);
- node_type_socket_templates(&ntype, geo_node_points_to_volume_in, geo_node_point_translate_out);
+ node_type_socket_templates(&ntype, geo_node_points_to_volume_in, geo_node_points_to_volume_out);
node_type_storage(&ntype,
"NodeGeometryPointsToVolume",
node_free_standard_storage,
diff --git a/source/blender/nodes/geometry/nodes/node_geo_select_by_material.cc b/source/blender/nodes/geometry/nodes/node_geo_select_by_material.cc
new file mode 100644
index 00000000000..9bc963eec43
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_select_by_material.cc
@@ -0,0 +1,106 @@
+/*
+ * 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.
+ */
+
+#include "node_geometry_util.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BLI_task.hh"
+
+#include "BKE_material.h"
+
+static bNodeSocketTemplate geo_node_select_by_material_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_MATERIAL,
+ N_("Material"),
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ PROP_NONE,
+ SOCK_HIDE_LABEL},
+ {SOCK_STRING, N_("Selection")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_select_by_material_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+namespace blender::nodes {
+
+static void select_mesh_by_material(const Mesh &mesh,
+ const Material *material,
+ const MutableSpan<bool> r_selection)
+{
+ BLI_assert(mesh.totpoly == r_selection.size());
+ Vector<int> material_indices;
+ for (const int i : IndexRange(mesh.totcol)) {
+ if (mesh.mat[i] == material) {
+ material_indices.append(i);
+ }
+ }
+ parallel_for(r_selection.index_range(), 1024, [&](IndexRange range) {
+ for (const int i : range) {
+ r_selection[i] = material_indices.contains(mesh.mpoly[i].mat_nr);
+ }
+ });
+}
+
+static void geo_node_select_by_material_exec(GeoNodeExecParams params)
+{
+ Material *material = params.extract_input<Material *>("Material");
+ const std::string selection_name = params.extract_input<std::string>("Selection");
+
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ geometry_set = geometry_set_realize_instances(geometry_set);
+
+ if (geometry_set.has<MeshComponent>()) {
+ MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
+ const Mesh *mesh = mesh_component.get_for_read();
+ if (mesh != nullptr) {
+ OutputAttribute_Typed<bool> selection =
+ mesh_component.attribute_try_get_for_output_only<bool>(selection_name, ATTR_DOMAIN_FACE);
+ if (selection) {
+ select_mesh_by_material(*mesh, material, selection.as_span());
+ selection.save();
+ }
+ }
+ }
+
+ params.set_output("Geometry", std::move(geometry_set));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_select_by_material()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_SELECT_BY_MATERIAL, "Select by Material", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(
+ &ntype, geo_node_select_by_material_in, geo_node_select_by_material_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_select_by_material_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_subdivide.cc
index 06c5586a3ff..a4c6522c57a 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_subdivide.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_subdivide.cc
@@ -14,7 +14,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "MEM_guardedalloc.h"
+// #include "MEM_guardedalloc.h"
#include "BKE_mesh.h"
#include "BKE_subdiv.h"
@@ -41,6 +41,7 @@ namespace blender::nodes {
static void geo_node_subdivide_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ geometry_set = geometry_set_realize_instances(geometry_set);
if (!geometry_set.has_mesh()) {
params.set_output("Geometry", geometry_set);
@@ -49,7 +50,7 @@ static void geo_node_subdivide_exec(GeoNodeExecParams params)
#ifndef WITH_OPENSUBDIV
params.error_message_add(NodeWarningType::Error,
- TIP_("Disabled, Blender was built without OpenSubdiv"));
+ TIP_("Disabled, Blender was compiled without OpenSubdiv"));
params.set_output("Geometry", std::move(geometry_set));
return;
#endif
@@ -98,6 +99,7 @@ static void geo_node_subdivide_exec(GeoNodeExecParams params)
params.set_output("Geometry", std::move(geometry_set));
}
+
} // namespace blender::nodes
void register_node_type_geo_subdivide()
diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc
index b14a512ce28..20ffcdb516d 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc
@@ -14,8 +14,6 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "MEM_guardedalloc.h"
-
#include "BKE_mesh.h"
#include "BKE_subdiv.h"
#include "BKE_subdiv_mesh.h"
@@ -39,17 +37,6 @@ static bNodeSocketTemplate geo_node_subdivision_surface_out[] = {
{-1, ""},
};
-static void geo_node_subdivision_surface_layout(uiLayout *layout,
- bContext *UNUSED(C),
- PointerRNA *UNUSED(ptr))
-{
-#ifndef WITH_OPENSUBDIV
- uiItemL(layout, IFACE_("Disabled, built without OpenSubdiv"), ICON_ERROR);
-#else
- UNUSED_VARS(layout);
-#endif
-}
-
namespace blender::nodes {
static void geo_node_subdivision_surface_exec(GeoNodeExecParams params)
{
@@ -63,9 +50,8 @@ static void geo_node_subdivision_surface_exec(GeoNodeExecParams params)
}
#ifndef WITH_OPENSUBDIV
- /* Return input geometry if Blender is built without OpenSubdiv. */
- params.set_output("Geometry", std::move(geometry_set));
- return;
+ params.error_message_add(NodeWarningType::Error,
+ TIP_("Disabled, Blender was compiled without OpenSubdiv"));
#else
const int subdiv_level = clamp_i(params.extract_input<int>("Level"), 0, 30);
@@ -115,9 +101,11 @@ static void geo_node_subdivision_surface_exec(GeoNodeExecParams params)
// BKE_subdiv_stats_print(&subdiv->stats);
BKE_subdiv_free(subdiv);
- params.set_output("Geometry", std::move(geometry_set));
#endif
+
+ params.set_output("Geometry", std::move(geometry_set));
}
+
} // namespace blender::nodes
void register_node_type_geo_subdivision_surface()
@@ -129,6 +117,5 @@ void register_node_type_geo_subdivision_surface()
node_type_socket_templates(
&ntype, geo_node_subdivision_surface_in, geo_node_subdivision_surface_out);
ntype.geometry_node_execute = blender::nodes::geo_node_subdivision_surface_exec;
- ntype.draw_buttons = geo_node_subdivision_surface_layout;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_switch.cc b/source/blender/nodes/geometry/nodes/node_geo_switch.cc
new file mode 100644
index 00000000000..0aa5c68aaf5
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_switch.cc
@@ -0,0 +1,191 @@
+/*
+ * 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.
+ */
+
+#include "node_geometry_util.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+static bNodeSocketTemplate geo_node_switch_in[] = {
+ {SOCK_BOOLEAN, N_("Switch")},
+
+ {SOCK_FLOAT, N_("False"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX},
+ {SOCK_FLOAT, N_("True"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX},
+ {SOCK_INT, N_("False"), 0, 0, 0, 0, -100000, 100000},
+ {SOCK_INT, N_("True"), 0, 0, 0, 0, -100000, 100000},
+ {SOCK_BOOLEAN, N_("False")},
+ {SOCK_BOOLEAN, N_("True")},
+ {SOCK_VECTOR, N_("False"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX},
+ {SOCK_VECTOR, N_("True"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX},
+ {SOCK_RGBA, N_("False"), 0.8, 0.8, 0.8, 1.0},
+ {SOCK_RGBA, N_("True"), 0.8, 0.8, 0.8, 1.0},
+ {SOCK_STRING, N_("False")},
+ {SOCK_STRING, N_("True")},
+ {SOCK_GEOMETRY, N_("False")},
+ {SOCK_GEOMETRY, N_("True")},
+ {SOCK_OBJECT, N_("False")},
+ {SOCK_OBJECT, N_("True")},
+ {SOCK_COLLECTION, N_("False")},
+ {SOCK_COLLECTION, N_("True")},
+ {SOCK_TEXTURE, N_("False")},
+ {SOCK_TEXTURE, N_("True")},
+ {SOCK_MATERIAL, N_("False")},
+ {SOCK_MATERIAL, N_("True")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_switch_out[] = {
+ {SOCK_FLOAT, N_("Output")},
+ {SOCK_INT, N_("Output")},
+ {SOCK_BOOLEAN, N_("Output")},
+ {SOCK_VECTOR, N_("Output")},
+ {SOCK_RGBA, N_("Output")},
+ {SOCK_STRING, N_("Output")},
+ {SOCK_GEOMETRY, N_("Output")},
+ {SOCK_OBJECT, N_("Output")},
+ {SOCK_COLLECTION, N_("Output")},
+ {SOCK_TEXTURE, N_("Output")},
+ {SOCK_MATERIAL, N_("Output")},
+ {-1, ""},
+};
+
+static void geo_node_switch_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "input_type", 0, "", ICON_NONE);
+}
+
+static void geo_node_switch_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeSwitch *data = (NodeSwitch *)MEM_callocN(sizeof(NodeSwitch), __func__);
+ data->input_type = SOCK_GEOMETRY;
+ node->storage = data;
+}
+
+namespace blender::nodes {
+
+static void geo_node_switch_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeSwitch *node_storage = (NodeSwitch *)node->storage;
+ int index = 0;
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
+ nodeSetSocketAvailability(
+ socket, index == 0 || socket->type == (eNodeSocketDatatype)node_storage->input_type);
+ index++;
+ }
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) {
+ nodeSetSocketAvailability(socket,
+ socket->type == (eNodeSocketDatatype)node_storage->input_type);
+ }
+}
+
+template<typename T>
+static void output_input(GeoNodeExecParams &params,
+ const bool input,
+ const StringRef input_suffix,
+ const StringRef output_identifier)
+{
+ const std::string name_a = "False" + input_suffix;
+ const std::string name_b = "True" + input_suffix;
+ if (input) {
+ params.set_input_unused(name_a);
+ if (params.lazy_require_input(name_b)) {
+ return;
+ }
+ params.set_output(output_identifier, params.extract_input<T>(name_b));
+ }
+ else {
+ params.set_input_unused(name_b);
+ if (params.lazy_require_input(name_a)) {
+ return;
+ }
+ params.set_output(output_identifier, params.extract_input<T>(name_a));
+ }
+}
+
+static void geo_node_switch_exec(GeoNodeExecParams params)
+{
+ if (params.lazy_require_input("Switch")) {
+ return;
+ }
+ const NodeSwitch &storage = *(const NodeSwitch *)params.node().storage;
+ const bool input = params.get_input<bool>("Switch");
+ switch ((eNodeSocketDatatype)storage.input_type) {
+ case SOCK_FLOAT: {
+ output_input<float>(params, input, "", "Output");
+ break;
+ }
+ case SOCK_INT: {
+ output_input<int>(params, input, "_001", "Output_001");
+ break;
+ }
+ case SOCK_BOOLEAN: {
+ output_input<bool>(params, input, "_002", "Output_002");
+ break;
+ }
+ case SOCK_VECTOR: {
+ output_input<float3>(params, input, "_003", "Output_003");
+ break;
+ }
+ case SOCK_RGBA: {
+ output_input<ColorGeometry4f>(params, input, "_004", "Output_004");
+ break;
+ }
+ case SOCK_STRING: {
+ output_input<std::string>(params, input, "_005", "Output_005");
+ break;
+ }
+ case SOCK_GEOMETRY: {
+ output_input<GeometrySet>(params, input, "_006", "Output_006");
+ break;
+ }
+ case SOCK_OBJECT: {
+ output_input<Object *>(params, input, "_007", "Output_007");
+ break;
+ }
+ case SOCK_COLLECTION: {
+ output_input<Collection *>(params, input, "_008", "Output_008");
+ break;
+ }
+ case SOCK_TEXTURE: {
+ output_input<Tex *>(params, input, "_009", "Output_009");
+ break;
+ }
+ case SOCK_MATERIAL: {
+ output_input<Material *>(params, input, "_010", "Output_010");
+ break;
+ }
+ default:
+ BLI_assert_unreachable();
+ break;
+ }
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_switch()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_SWITCH, "Switch", NODE_CLASS_CONVERTOR, 0);
+ node_type_socket_templates(&ntype, geo_node_switch_in, geo_node_switch_out);
+ node_type_init(&ntype, geo_node_switch_init);
+ node_type_update(&ntype, blender::nodes::geo_node_switch_update);
+ node_type_storage(&ntype, "NodeSwitch", node_free_standard_storage, node_copy_standard_storage);
+ ntype.geometry_node_execute = blender::nodes::geo_node_switch_exec;
+ ntype.geometry_node_execute_supports_laziness = true;
+ ntype.draw_buttons = geo_node_switch_layout;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_transform.cc b/source/blender/nodes/geometry/nodes/node_geo_transform.cc
index 842a78b839a..cdd2df56b77 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_transform.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_transform.cc
@@ -18,12 +18,14 @@
# include <openvdb/openvdb.h>
#endif
-#include "BLI_math_matrix.h"
+#include "BLI_float4x4.hh"
+#include "DNA_mesh_types.h"
#include "DNA_pointcloud_types.h"
#include "DNA_volume_types.h"
#include "BKE_mesh.h"
+#include "BKE_spline.hh"
#include "BKE_volume.h"
#include "DEG_depsgraph_query.h"
@@ -81,14 +83,14 @@ void transform_mesh(Mesh *mesh,
/* Use only translation if rotation and scale are zero. */
if (use_translate(rotation, scale)) {
if (!translation.is_zero()) {
- BKE_mesh_translate(mesh, translation, true);
+ BKE_mesh_translate(mesh, translation, false);
}
}
else {
- float mat[4][4];
- loc_eul_size_to_mat4(mat, translation, rotation, scale);
- BKE_mesh_transform(mesh, mat, true);
- BKE_mesh_calc_normals(mesh);
+ const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, scale);
+ BKE_mesh_transform(mesh, matrix.values, false);
+ mesh->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
+ mesh->runtime.cd_dirty_poly |= CD_MASK_NORMAL;
}
}
@@ -99,15 +101,15 @@ static void transform_pointcloud(PointCloud *pointcloud,
{
/* Use only translation if rotation and scale don't apply. */
if (use_translate(rotation, scale)) {
- for (int i = 0; i < pointcloud->totpoint; i++) {
+ for (const int i : IndexRange(pointcloud->totpoint)) {
add_v3_v3(pointcloud->co[i], translation);
}
}
else {
- float mat[4][4];
- loc_eul_size_to_mat4(mat, translation, rotation, scale);
- for (int i = 0; i < pointcloud->totpoint; i++) {
- mul_m4_v3(mat, pointcloud->co[i]);
+ const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, scale);
+ for (const int i : IndexRange(pointcloud->totpoint)) {
+ float3 &co = *(float3 *)pointcloud->co[i];
+ co = matrix * co;
}
}
}
@@ -117,7 +119,7 @@ static void transform_instances(InstancesComponent &instances,
const float3 rotation,
const float3 scale)
{
- MutableSpan<float4x4> transforms = instances.transforms();
+ MutableSpan<float4x4> transforms = instances.instance_transforms();
/* Use only translation if rotation and scale don't apply. */
if (use_translate(rotation, scale)) {
@@ -126,11 +128,9 @@ static void transform_instances(InstancesComponent &instances,
}
}
else {
- float mat[4][4];
-
- loc_eul_size_to_mat4(mat, translation, rotation, scale);
+ const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, scale);
for (float4x4 &transform : transforms) {
- mul_m4_m4_pre(transform.ptr(), mat);
+ transform = matrix * transform;
}
}
}
@@ -149,11 +149,10 @@ static void transform_volume(Volume *volume,
(scale.z == 0.0f) ? FLT_EPSILON : scale.z,
};
- Main *bmain = DEG_get_bmain(params.depsgraph());
+ const Main *bmain = DEG_get_bmain(params.depsgraph());
BKE_volume_load(volume, bmain);
- float matrix[4][4];
- loc_eul_size_to_mat4(matrix, translation, rotation, limited_scale);
+ const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, limited_scale);
openvdb::Mat4s vdb_matrix;
memcpy(vdb_matrix.asPointer(), matrix, sizeof(float[4][4]));
@@ -161,7 +160,7 @@ static void transform_volume(Volume *volume,
const int num_grids = BKE_volume_num_grids(volume);
for (const int i : IndexRange(num_grids)) {
- VolumeGrid *volume_grid = BKE_volume_grid_get(volume, i);
+ VolumeGrid *volume_grid = BKE_volume_grid_get_for_write(volume, i);
openvdb::GridBase::Ptr grid = BKE_volume_grid_openvdb_for_write(volume, volume_grid, false);
openvdb::math::Transform &grid_transform = grid->transform();
@@ -172,6 +171,20 @@ static void transform_volume(Volume *volume,
#endif
}
+static void transform_curve(CurveEval &curve,
+ const float3 translation,
+ const float3 rotation,
+ const float3 scale)
+{
+ if (use_translate(rotation, scale)) {
+ curve.translate(translation);
+ }
+ else {
+ const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, scale);
+ curve.transform(matrix);
+ }
+}
+
static void geo_node_transform_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
@@ -183,21 +196,22 @@ static void geo_node_transform_exec(GeoNodeExecParams params)
Mesh *mesh = geometry_set.get_mesh_for_write();
transform_mesh(mesh, translation, rotation, scale);
}
-
if (geometry_set.has_pointcloud()) {
PointCloud *pointcloud = geometry_set.get_pointcloud_for_write();
transform_pointcloud(pointcloud, translation, rotation, scale);
}
-
if (geometry_set.has_instances()) {
InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
transform_instances(instances, translation, rotation, scale);
}
-
if (geometry_set.has_volume()) {
Volume *volume = geometry_set.get_volume_for_write();
transform_volume(volume, translation, rotation, scale, params);
}
+ if (geometry_set.has_curve()) {
+ CurveEval *curve = geometry_set.get_curve_for_write();
+ transform_curve(*curve, translation, rotation, scale);
+ }
params.set_output("Geometry", std::move(geometry_set));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc
index 0c754ad3b37..03581b28f18 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc
@@ -14,10 +14,6 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "DNA_node_types.h"
-
-#include "RNA_enum_types.h"
-
#include "UI_interface.h"
#include "UI_resources.h"
diff --git a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc
index 54dccb613a1..403f4906d07 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc
@@ -23,6 +23,7 @@
#include "node_geometry_util.hh"
#include "BKE_lib_id.h"
+#include "BKE_material.h"
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
#include "BKE_volume.h"
@@ -36,8 +37,8 @@
static bNodeSocketTemplate geo_node_volume_to_mesh_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
- {SOCK_STRING, N_("Grid")},
- {SOCK_FLOAT, N_("Voxel Size"), 0.3f, 0.0f, 0.0f, 0.0f, 0.01f, FLT_MAX},
+ {SOCK_STRING, N_("Density")},
+ {SOCK_FLOAT, N_("Voxel Size"), 0.3f, 0.0f, 0.0f, 0.0f, 0.01f, FLT_MAX, PROP_DISTANCE},
{SOCK_FLOAT, N_("Voxel Amount"), 64.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX},
{SOCK_FLOAT, N_("Threshold"), 0.1f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX},
{SOCK_FLOAT, N_("Adaptivity"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR},
@@ -64,7 +65,7 @@ static void geo_node_volume_to_mesh_init(bNodeTree *UNUSED(ntree), bNode *node)
sizeof(NodeGeometryVolumeToMesh), __func__);
data->resolution_mode = VOLUME_TO_MESH_RESOLUTION_MODE_GRID;
- bNodeSocket *grid_socket = nodeFindSocket(node, SOCK_IN, "Grid");
+ bNodeSocket *grid_socket = nodeFindSocket(node, SOCK_IN, "Density");
bNodeSocketValueString *grid_socket_value = (bNodeSocketValueString *)grid_socket->default_value;
STRNCPY(grid_socket_value->value, "density");
@@ -117,11 +118,11 @@ static void create_mesh_from_volume(GeometrySet &geometry_set_in,
return;
}
- Main *bmain = DEG_get_bmain(params.depsgraph());
- BKE_volume_load(const_cast<Volume *>(volume), bmain);
+ const Main *bmain = DEG_get_bmain(params.depsgraph());
+ BKE_volume_load(volume, bmain);
- const std::string grid_name = params.get_input<std::string>("Grid");
- VolumeGrid *volume_grid = BKE_volume_grid_find(volume, grid_name.c_str());
+ const std::string grid_name = params.get_input<std::string>("Density");
+ const VolumeGrid *volume_grid = BKE_volume_grid_find_for_read(volume, grid_name.c_str());
if (volume_grid == nullptr) {
return;
}
@@ -134,6 +135,7 @@ static void create_mesh_from_volume(GeometrySet &geometry_set_in,
if (mesh == nullptr) {
return;
}
+ BKE_id_material_eval_ensure_default_slot(&mesh->id);
MeshComponent &dst_component = geometry_set_out.get_component_for_write<MeshComponent>();
dst_component.replace(mesh);
}
diff --git a/source/blender/nodes/intern/derived_node_tree.cc b/source/blender/nodes/intern/derived_node_tree.cc
index e4f8a0c5fda..9a3eb574bcd 100644
--- a/source/blender/nodes/intern/derived_node_tree.cc
+++ b/source/blender/nodes/intern/derived_node_tree.cc
@@ -40,6 +40,7 @@ DTreeContext &DerivedNodeTree::construct_context_recursively(DTreeContext *paren
DTreeContext &context = *allocator_.construct<DTreeContext>().release();
context.parent_context_ = parent_context;
context.parent_node_ = parent_node;
+ context.derived_tree_ = this;
context.tree_ = &get_tree_ref_from_map(node_tree_refs, btree);
used_node_tree_refs_.add(context.tree_);
@@ -83,6 +84,16 @@ bool DerivedNodeTree::has_link_cycles() const
return false;
}
+bool DerivedNodeTree::has_undefined_nodes_or_sockets() const
+{
+ for (const NodeTreeRef *tree_ref : used_node_tree_refs_) {
+ if (tree_ref->has_undefined_nodes_or_sockets()) {
+ return true;
+ }
+ }
+ return false;
+}
+
/* Calls the given callback on all nodes in the (possibly nested) derived node tree. */
void DerivedNodeTree::foreach_node(FunctionRef<void(DNode)> callback) const
{
@@ -167,10 +178,10 @@ DInputSocket DOutputSocket::get_active_corresponding_group_output_socket() const
return {};
}
-/* Call the given callback for every "real" origin socket. "Real" means that reroutes, muted nodes
+/* Call `origin_fn` for every "real" origin socket. "Real" means that reroutes, muted nodes
* and node groups are handled by this function. Origin sockets are ones where a node gets its
* inputs from. */
-void DInputSocket::foreach_origin_socket(FunctionRef<void(DSocket)> callback) const
+void DInputSocket::foreach_origin_socket(FunctionRef<void(DSocket)> origin_fn) const
{
BLI_assert(*this);
for (const OutputSocketRef *linked_socket : socket_ref_->as_input().logically_linked_sockets()) {
@@ -180,18 +191,18 @@ void DInputSocket::foreach_origin_socket(FunctionRef<void(DSocket)> callback) co
if (linked_node.is_group_input_node()) {
if (context_->is_root()) {
/* This is a group input in the root node group. */
- callback(linked_dsocket);
+ origin_fn(linked_dsocket);
}
else {
DInputSocket socket_in_parent_group = linked_dsocket.get_corresponding_group_node_input();
if (socket_in_parent_group->is_logically_linked()) {
/* Follow the links coming into the corresponding socket on the parent group node. */
- socket_in_parent_group.foreach_origin_socket(callback);
+ socket_in_parent_group.foreach_origin_socket(origin_fn);
}
else {
/* The corresponding input on the parent group node is not connected. Therefore, we use
* the value of that input socket directly. */
- callback(socket_in_parent_group);
+ origin_fn(socket_in_parent_group);
}
}
}
@@ -200,27 +211,32 @@ void DInputSocket::foreach_origin_socket(FunctionRef<void(DSocket)> callback) co
if (socket_in_group) {
if (socket_in_group->is_logically_linked()) {
/* Follow the links coming into the group output node of the child node group. */
- socket_in_group.foreach_origin_socket(callback);
+ socket_in_group.foreach_origin_socket(origin_fn);
}
else {
/* The output of the child node group is not connected, so we have to get the value from
* that socket. */
- callback(socket_in_group);
+ origin_fn(socket_in_group);
}
}
}
else {
/* The normal case: just use the value of a linked output socket. */
- callback(linked_dsocket);
+ origin_fn(linked_dsocket);
}
}
}
-/* Calls the given callback for every "real" target socket. "Real" means that reroutes, muted nodes
+/* Calls `target_fn` for every "real" target socket. "Real" means that reroutes, muted nodes
* and node groups are handled by this function. Target sockets are on the nodes that use the value
- * from this socket. */
-void DOutputSocket::foreach_target_socket(FunctionRef<void(DInputSocket)> callback) const
+ * from this socket. The `skipped_fn` function is called for sockets that have been skipped during
+ * the search for target sockets (e.g. reroutes). */
+void DOutputSocket::foreach_target_socket(FunctionRef<void(DInputSocket)> target_fn,
+ FunctionRef<void(DSocket)> skipped_fn) const
{
+ for (const SocketRef *skipped_socket : socket_ref_->logically_linked_skipped_sockets()) {
+ skipped_fn.call_safe({context_, skipped_socket});
+ }
for (const InputSocketRef *linked_socket : socket_ref_->as_output().logically_linked_sockets()) {
const NodeRef &linked_node = linked_socket->node();
DInputSocket linked_dsocket{context_, linked_socket};
@@ -228,26 +244,30 @@ void DOutputSocket::foreach_target_socket(FunctionRef<void(DInputSocket)> callba
if (linked_node.is_group_output_node()) {
if (context_->is_root()) {
/* This is a group output in the root node group. */
- callback(linked_dsocket);
+ target_fn(linked_dsocket);
}
else {
/* Follow the links going out of the group node in the parent node group. */
DOutputSocket socket_in_parent_group =
linked_dsocket.get_corresponding_group_node_output();
- socket_in_parent_group.foreach_target_socket(callback);
+ skipped_fn.call_safe(linked_dsocket);
+ skipped_fn.call_safe(socket_in_parent_group);
+ socket_in_parent_group.foreach_target_socket(target_fn, skipped_fn);
}
}
else if (linked_node.is_group_node()) {
/* Follow the links within the nested node group. */
Vector<DOutputSocket> sockets_in_group =
linked_dsocket.get_corresponding_group_input_sockets();
+ skipped_fn.call_safe(linked_dsocket);
for (DOutputSocket socket_in_group : sockets_in_group) {
- socket_in_group.foreach_target_socket(callback);
+ skipped_fn.call_safe(socket_in_group);
+ socket_in_group.foreach_target_socket(target_fn, skipped_fn);
}
}
else {
/* The normal case: just use the linked input socket as target. */
- callback(linked_dsocket);
+ target_fn(linked_dsocket);
}
}
}
diff --git a/source/blender/nodes/intern/math_functions.cc b/source/blender/nodes/intern/math_functions.cc
index fb34144abf6..aa23777b664 100644
--- a/source/blender/nodes/intern/math_functions.cc
+++ b/source/blender/nodes/intern/math_functions.cc
@@ -209,6 +209,8 @@ const FloatMathOperationInfo *get_float3_math_operation_info(const int operation
RETURN_OPERATION_INFO("Refract", "vector_math_refract");
case NODE_VECTOR_MATH_FACEFORWARD:
RETURN_OPERATION_INFO("Faceforward", "vector_math_faceforward");
+ case NODE_VECTOR_MATH_MULTIPLY_ADD:
+ RETURN_OPERATION_INFO("Multiply Add", "vector_math_multiply_add");
}
#undef RETURN_OPERATION_INFO
diff --git a/source/blender/nodes/intern/node_common.c b/source/blender/nodes/intern/node_common.c
index 0b1ab85c059..7fc9f664df0 100644
--- a/source/blender/nodes/intern/node_common.c
+++ b/source/blender/nodes/intern/node_common.c
@@ -79,12 +79,12 @@ void node_group_label(bNodeTree *UNUSED(ntree), bNode *node, char *label, int ma
BLI_strncpy(label, (node->id) ? node->id->name + 2 : IFACE_("Missing Data-Block"), maxlen);
}
-bool node_group_poll_instance(bNode *node, bNodeTree *nodetree)
+bool node_group_poll_instance(bNode *node, bNodeTree *nodetree, const char **disabled_hint)
{
- if (node->typeinfo->poll(node->typeinfo, nodetree)) {
+ if (node->typeinfo->poll(node->typeinfo, nodetree, disabled_hint)) {
bNodeTree *grouptree = (bNodeTree *)node->id;
if (grouptree) {
- return nodeGroupPoll(nodetree, grouptree);
+ return nodeGroupPoll(nodetree, grouptree, disabled_hint);
}
return true; /* without a linked node tree, group node is always ok */
@@ -93,25 +93,27 @@ bool node_group_poll_instance(bNode *node, bNodeTree *nodetree)
return false;
}
-int nodeGroupPoll(bNodeTree *nodetree, bNodeTree *grouptree)
+bool nodeGroupPoll(bNodeTree *nodetree, bNodeTree *grouptree, const char **r_disabled_hint)
{
bNode *node;
- int valid = 1;
+ bool valid = true;
/* unspecified node group, generally allowed
* (if anything, should be avoided on operator level)
*/
if (grouptree == NULL) {
- return 1;
+ return true;
}
if (nodetree == grouptree) {
- return 0;
+ *r_disabled_hint = "Nesting a node group inside of itself is not allowed";
+ return false;
}
for (node = grouptree->nodes.first; node; node = node->next) {
- if (node->typeinfo->poll_instance && !node->typeinfo->poll_instance(node, nodetree)) {
- valid = 0;
+ if (node->typeinfo->poll_instance &&
+ !node->typeinfo->poll_instance(node, nodetree, r_disabled_hint)) {
+ valid = false;
break;
}
}
diff --git a/source/blender/nodes/intern/node_common.h b/source/blender/nodes/intern/node_common.h
index 7aad6782640..cdb7b6897b9 100644
--- a/source/blender/nodes/intern/node_common.h
+++ b/source/blender/nodes/intern/node_common.h
@@ -32,7 +32,9 @@ extern "C" {
struct bNodeTree;
void node_group_label(struct bNodeTree *ntree, struct bNode *node, char *label, int maxlen);
-bool node_group_poll_instance(struct bNode *node, struct bNodeTree *nodetree);
+bool node_group_poll_instance(struct bNode *node,
+ struct bNodeTree *nodetree,
+ const char **r_disabled_hint);
void ntree_update_reroute_nodes(struct bNodeTree *ntree);
diff --git a/source/blender/nodes/intern/node_exec.c b/source/blender/nodes/intern/node_exec.cc
index dd9d0b6796a..18403417af3 100644
--- a/source/blender/nodes/intern/node_exec.c
+++ b/source/blender/nodes/intern/node_exec.cc
@@ -47,7 +47,7 @@ bNodeStack *node_get_socket_stack(bNodeStack *stack, bNodeSocket *sock)
if (stack && sock && sock->stack_index >= 0) {
return stack + sock->stack_index;
}
- return NULL;
+ return nullptr;
}
void node_get_stack(bNode *node, bNodeStack *stack, bNodeStack **in, bNodeStack **out)
@@ -56,13 +56,13 @@ void node_get_stack(bNode *node, bNodeStack *stack, bNodeStack **in, bNodeStack
/* build pointer stack */
if (in) {
- for (sock = node->inputs.first; sock; sock = sock->next) {
+ for (sock = (bNodeSocket *)node->inputs.first; sock; sock = sock->next) {
*(in++) = node_get_socket_stack(stack, sock);
}
}
if (out) {
- for (sock = node->outputs.first; sock; sock = sock->next) {
+ for (sock = (bNodeSocket *)node->outputs.first; sock; sock = sock->next) {
*(out++) = node_get_socket_stack(stack, sock);
}
}
@@ -90,7 +90,7 @@ static void node_init_output_index(bNodeSocket *sock, int *index, ListBase *inte
if (internal_links) {
bNodeLink *link;
/* copy the stack index from internally connected input to skip the node */
- for (link = internal_links->first; link; link = link->next) {
+ for (link = (bNodeLink *)internal_links->first; link; link = link->next) {
if (link->tosock == sock) {
sock->stack_index = link->fromsock->stack_index;
/* set the link pointer to indicate that this socket
@@ -128,7 +128,7 @@ static struct bNodeStack *setup_stack(bNodeStack *stack,
{
bNodeStack *ns = node_get_socket_stack(stack, sock);
if (!ns) {
- return NULL;
+ return nullptr;
}
/* don't mess with remote socket stacks, these are initialized by other nodes! */
@@ -166,7 +166,7 @@ bNodeTreeExec *ntree_exec_begin(bNodeExecContext *context,
int index;
bNode **nodelist;
int totnodes, n;
- /* XXX texnodes have threading issues with muting, have to disable it there ... */
+ /* XXX: texture-nodes have threading issues with muting, have to disable it there. */
/* ensure all sock->link pointers and node levels are correct */
/* Using global main here is likely totally wrong, not sure what to do about that one though...
@@ -178,7 +178,7 @@ bNodeTreeExec *ntree_exec_begin(bNodeExecContext *context,
ntreeGetDependencyList(ntree, &nodelist, &totnodes);
/* XXX could let callbacks do this for specialized data */
- exec = MEM_callocN(sizeof(bNodeTreeExec), "node tree execution data");
+ exec = (bNodeTreeExec *)MEM_callocN(sizeof(bNodeTreeExec), "node tree execution data");
/* backpointer to node tree */
exec->nodetree = ntree;
@@ -188,28 +188,29 @@ bNodeTreeExec *ntree_exec_begin(bNodeExecContext *context,
node = nodelist[n];
/* init node socket stack indexes */
- for (sock = node->inputs.first; sock; sock = sock->next) {
+ for (sock = (bNodeSocket *)node->inputs.first; sock; sock = sock->next) {
node_init_input_index(sock, &index);
}
if (node->flag & NODE_MUTED || node->type == NODE_REROUTE) {
- for (sock = node->outputs.first; sock; sock = sock->next) {
+ for (sock = (bNodeSocket *)node->outputs.first; sock; sock = sock->next) {
node_init_output_index(sock, &index, &node->internal_links);
}
}
else {
- for (sock = node->outputs.first; sock; sock = sock->next) {
- node_init_output_index(sock, &index, NULL);
+ for (sock = (bNodeSocket *)node->outputs.first; sock; sock = sock->next) {
+ node_init_output_index(sock, &index, nullptr);
}
}
}
/* allocated exec data pointers for nodes */
exec->totnodes = totnodes;
- exec->nodeexec = MEM_callocN(exec->totnodes * sizeof(bNodeExec), "node execution data");
+ exec->nodeexec = (bNodeExec *)MEM_callocN(exec->totnodes * sizeof(bNodeExec),
+ "node execution data");
/* allocate data pointer for node stack */
exec->stacksize = index;
- exec->stack = MEM_callocN(exec->stacksize * sizeof(bNodeStack), "bNodeStack");
+ exec->stack = (bNodeStack *)MEM_callocN(exec->stacksize * sizeof(bNodeStack), "bNodeStack");
/* all non-const results are considered inputs */
for (n = 0; n < exec->stacksize; n++) {
@@ -222,7 +223,7 @@ bNodeTreeExec *ntree_exec_begin(bNodeExecContext *context,
nodeexec->free_exec_fn = node->typeinfo->free_exec_fn;
/* tag inputs */
- for (sock = node->inputs.first; sock; sock = sock->next) {
+ for (sock = (bNodeSocket *)node->inputs.first; sock; sock = sock->next) {
/* disable the node if an input link is invalid */
if (sock->link && !(sock->link->flag & NODE_LINK_VALID)) {
node->need_exec = 0;
@@ -235,14 +236,14 @@ bNodeTreeExec *ntree_exec_begin(bNodeExecContext *context,
}
/* tag all outputs */
- for (sock = node->outputs.first; sock; sock = sock->next) {
+ for (sock = (bNodeSocket *)node->outputs.first; sock; sock = sock->next) {
/* ns = */ setup_stack(exec->stack, ntree, node, sock);
}
nodekey = BKE_node_instance_key(parent_key, ntree, node);
- nodeexec->data.preview = context->previews ?
- BKE_node_instance_hash_lookup(context->previews, nodekey) :
- NULL;
+ nodeexec->data.preview = context->previews ? (bNodePreview *)BKE_node_instance_hash_lookup(
+ context->previews, nodekey) :
+ nullptr;
if (node->typeinfo->init_exec_fn) {
nodeexec->data.data = node->typeinfo->init_exec_fn(context, node, nodekey);
}
@@ -284,7 +285,7 @@ bNodeThreadStack *ntreeGetThreadStack(bNodeTreeExec *exec, int thread)
ListBase *lb = &exec->threadstack[thread];
bNodeThreadStack *nts;
- for (nts = lb->first; nts; nts = nts->next) {
+ for (nts = (bNodeThreadStack *)lb->first; nts; nts = nts->next) {
if (!nts->used) {
nts->used = true;
break;
@@ -292,8 +293,8 @@ bNodeThreadStack *ntreeGetThreadStack(bNodeTreeExec *exec, int thread)
}
if (!nts) {
- nts = MEM_callocN(sizeof(bNodeThreadStack), "bNodeThreadStack");
- nts->stack = MEM_dupallocN(exec->stack);
+ nts = (bNodeThreadStack *)MEM_callocN(sizeof(bNodeThreadStack), "bNodeThreadStack");
+ nts->stack = (bNodeStack *)MEM_dupallocN(exec->stack);
nts->used = true;
BLI_addtail(lb, nts);
}
@@ -303,13 +304,13 @@ bNodeThreadStack *ntreeGetThreadStack(bNodeTreeExec *exec, int thread)
void ntreeReleaseThreadStack(bNodeThreadStack *nts)
{
- nts->used = 0;
+ nts->used = false;
}
bool ntreeExecThreadNodes(bNodeTreeExec *exec, bNodeThreadStack *nts, void *callerdata, int thread)
{
- bNodeStack *nsin[MAX_SOCKET] = {NULL}; /* arbitrary... watch this */
- bNodeStack *nsout[MAX_SOCKET] = {NULL}; /* arbitrary... watch this */
+ bNodeStack *nsin[MAX_SOCKET] = {nullptr}; /* arbitrary... watch this */
+ bNodeStack *nsout[MAX_SOCKET] = {nullptr}; /* arbitrary... watch this */
bNodeExec *nodeexec;
bNode *node;
int n;
diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc
index a4fb99a988e..188d198e159 100644
--- a/source/blender/nodes/intern/node_geometry_exec.cc
+++ b/source/blender/nodes/intern/node_geometry_exec.cc
@@ -22,6 +22,7 @@
#include "NOD_geometry_exec.hh"
#include "NOD_type_callbacks.hh"
+#include "NOD_type_conversions.hh"
#include "node_geometry_util.hh"
@@ -29,22 +30,22 @@ namespace blender::nodes {
void GeoNodeExecParams::error_message_add(const NodeWarningType type, std::string message) const
{
- bNodeTree *btree_cow = node_->btree();
+ bNodeTree *btree_cow = provider_->dnode->btree();
BLI_assert(btree_cow != nullptr);
if (btree_cow == nullptr) {
return;
}
bNodeTree *btree_original = (bNodeTree *)DEG_get_original_id((ID *)btree_cow);
- const NodeTreeEvaluationContext context(*self_object_, *modifier_);
+ const NodeTreeEvaluationContext context(*provider_->self_object, *provider_->modifier);
BKE_nodetree_error_message_add(
- *btree_original, context, *node_->bnode(), type, std::move(message));
+ *btree_original, context, *provider_->dnode->bnode(), type, std::move(message));
}
const bNodeSocket *GeoNodeExecParams::find_available_socket(const StringRef name) const
{
- for (const InputSocketRef *socket : node_->inputs()) {
+ for (const InputSocketRef *socket : provider_->dnode->inputs()) {
if (socket->is_available() && socket->name() == name) {
return socket->bsocket();
}
@@ -53,22 +54,29 @@ const bNodeSocket *GeoNodeExecParams::find_available_socket(const StringRef name
return nullptr;
}
-ReadAttributePtr GeoNodeExecParams::get_input_attribute(const StringRef name,
- const GeometryComponent &component,
- const AttributeDomain domain,
- const CustomDataType type,
- const void *default_value) const
+GVArrayPtr GeoNodeExecParams::get_input_attribute(const StringRef name,
+ const GeometryComponent &component,
+ const AttributeDomain domain,
+ const CustomDataType type,
+ const void *default_value) const
{
const bNodeSocket *found_socket = this->find_available_socket(name);
BLI_assert(found_socket != nullptr); /* There should always be available socket for the name. */
+ const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(type);
+ const int64_t domain_size = component.attribute_domain_size(domain);
+
+ if (default_value == nullptr) {
+ default_value = cpp_type->default_value();
+ }
+
if (found_socket == nullptr) {
- return component.attribute_get_constant_for_read(domain, type, default_value);
+ return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, default_value);
}
if (found_socket->type == SOCK_STRING) {
const std::string name = this->get_input<std::string>(found_socket->identifier);
/* Try getting the attribute without the default value. */
- ReadAttributePtr attribute = component.attribute_try_get_for_read(name, domain, type);
+ GVArrayPtr attribute = component.attribute_try_get_for_read(name, domain, type);
if (attribute) {
return attribute;
}
@@ -80,25 +88,30 @@ ReadAttributePtr GeoNodeExecParams::get_input_attribute(const StringRef name,
this->error_message_add(NodeWarningType::Error,
TIP_("No attribute with name \"") + name + "\"");
}
- return component.attribute_get_constant_for_read(domain, type, default_value);
+ return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, default_value);
}
+ const DataTypeConversions &conversions = get_implicit_type_conversions();
if (found_socket->type == SOCK_FLOAT) {
const float value = this->get_input<float>(found_socket->identifier);
- return component.attribute_get_constant_for_read_converted(
- domain, CD_PROP_FLOAT, type, &value);
+ BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer);
+ conversions.convert_to_uninitialized(CPPType::get<float>(), *cpp_type, &value, buffer);
+ return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, buffer);
}
if (found_socket->type == SOCK_VECTOR) {
const float3 value = this->get_input<float3>(found_socket->identifier);
- return component.attribute_get_constant_for_read_converted(
- domain, CD_PROP_FLOAT3, type, &value);
+ BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer);
+ conversions.convert_to_uninitialized(CPPType::get<float3>(), *cpp_type, &value, buffer);
+ return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, buffer);
}
if (found_socket->type == SOCK_RGBA) {
- const Color4f value = this->get_input<Color4f>(found_socket->identifier);
- return component.attribute_get_constant_for_read_converted(
- domain, CD_PROP_COLOR, type, &value);
+ const ColorGeometry4f value = this->get_input<ColorGeometry4f>(found_socket->identifier);
+ BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer);
+ conversions.convert_to_uninitialized(
+ CPPType::get<ColorGeometry4f>(), *cpp_type, &value, buffer);
+ return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, buffer);
}
BLI_assert(false);
- return component.attribute_get_constant_for_read(domain, type, default_value);
+ return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, default_value);
}
CustomDataType GeoNodeExecParams::get_input_attribute_data_type(
@@ -114,11 +127,11 @@ CustomDataType GeoNodeExecParams::get_input_attribute_data_type(
if (found_socket->type == SOCK_STRING) {
const std::string name = this->get_input<std::string>(found_socket->identifier);
- ReadAttributePtr attribute = component.attribute_try_get_for_read(name);
- if (!attribute) {
- return default_type;
+ std::optional<AttributeMetaData> info = component.attribute_get_meta_data(name);
+ if (info) {
+ return info->data_type;
}
- return attribute->custom_data_type();
+ return default_type;
}
if (found_socket->type == SOCK_FLOAT) {
return CD_PROP_FLOAT;
@@ -157,9 +170,9 @@ AttributeDomain GeoNodeExecParams::get_highest_priority_input_domain(
if (found_socket->type == SOCK_STRING) {
const std::string name = this->get_input<std::string>(found_socket->identifier);
- ReadAttributePtr attribute = component.attribute_try_get_for_read(name);
- if (attribute) {
- input_domains.append(attribute->domain());
+ std::optional<AttributeMetaData> info = component.attribute_get_meta_data(name);
+ if (info) {
+ input_domains.append(info->domain);
}
}
}
@@ -171,11 +184,11 @@ AttributeDomain GeoNodeExecParams::get_highest_priority_input_domain(
return default_domain;
}
-void GeoNodeExecParams::check_extract_input(StringRef identifier,
- const CPPType *requested_type) const
+void GeoNodeExecParams::check_input_access(StringRef identifier,
+ const CPPType *requested_type) const
{
bNodeSocket *found_socket = nullptr;
- for (const InputSocketRef *socket : node_->inputs()) {
+ for (const InputSocketRef *socket : provider_->dnode->inputs()) {
if (socket->identifier() == identifier) {
found_socket = socket->bsocket();
break;
@@ -185,39 +198,39 @@ void GeoNodeExecParams::check_extract_input(StringRef identifier,
if (found_socket == nullptr) {
std::cout << "Did not find an input socket with the identifier '" << identifier << "'.\n";
std::cout << "Possible identifiers are: ";
- for (const InputSocketRef *socket : node_->inputs()) {
+ for (const InputSocketRef *socket : provider_->dnode->inputs()) {
if (socket->is_available()) {
std::cout << "'" << socket->identifier() << "', ";
}
}
std::cout << "\n";
- BLI_assert(false);
+ BLI_assert_unreachable();
}
else if (found_socket->flag & SOCK_UNAVAIL) {
std::cout << "The socket corresponding to the identifier '" << identifier
<< "' is disabled.\n";
- BLI_assert(false);
+ BLI_assert_unreachable();
}
- else if (!input_values_.contains(identifier)) {
+ else if (!provider_->can_get_input(identifier)) {
std::cout << "The identifier '" << identifier
<< "' is valid, but there is no value for it anymore.\n";
std::cout << "Most likely it has been extracted before.\n";
- BLI_assert(false);
+ BLI_assert_unreachable();
}
else if (requested_type != nullptr) {
const CPPType &expected_type = *socket_cpp_type_get(*found_socket->typeinfo);
if (*requested_type != expected_type) {
std::cout << "The requested type '" << requested_type->name() << "' is incorrect. Expected '"
<< expected_type.name() << "'.\n";
- BLI_assert(false);
+ BLI_assert_unreachable();
}
}
}
-void GeoNodeExecParams::check_set_output(StringRef identifier, const CPPType &value_type) const
+void GeoNodeExecParams::check_output_access(StringRef identifier, const CPPType &value_type) const
{
bNodeSocket *found_socket = nullptr;
- for (const OutputSocketRef *socket : node_->outputs()) {
+ for (const OutputSocketRef *socket : provider_->dnode->outputs()) {
if (socket->identifier() == identifier) {
found_socket = socket->bsocket();
break;
@@ -227,29 +240,29 @@ void GeoNodeExecParams::check_set_output(StringRef identifier, const CPPType &va
if (found_socket == nullptr) {
std::cout << "Did not find an output socket with the identifier '" << identifier << "'.\n";
std::cout << "Possible identifiers are: ";
- for (const OutputSocketRef *socket : node_->outputs()) {
+ for (const OutputSocketRef *socket : provider_->dnode->outputs()) {
if (socket->is_available()) {
std::cout << "'" << socket->identifier() << "', ";
}
}
std::cout << "\n";
- BLI_assert(false);
+ BLI_assert_unreachable();
}
else if (found_socket->flag & SOCK_UNAVAIL) {
std::cout << "The socket corresponding to the identifier '" << identifier
<< "' is disabled.\n";
- BLI_assert(false);
+ BLI_assert_unreachable();
}
- else if (output_values_.contains(identifier)) {
+ else if (!provider_->can_set_output(identifier)) {
std::cout << "The identifier '" << identifier << "' has been set already.\n";
- BLI_assert(false);
+ BLI_assert_unreachable();
}
else {
const CPPType &expected_type = *socket_cpp_type_get(*found_socket->typeinfo);
if (value_type != expected_type) {
std::cout << "The value type '" << value_type.name() << "' is incorrect. Expected '"
<< expected_type.name() << "'.\n";
- BLI_assert(false);
+ BLI_assert_unreachable();
}
}
}
diff --git a/source/blender/nodes/intern/node_socket.cc b/source/blender/nodes/intern/node_socket.cc
index 26df3f77738..d00bf636e15 100644
--- a/source/blender/nodes/intern/node_socket.cc
+++ b/source/blender/nodes/intern/node_socket.cc
@@ -35,9 +35,9 @@
#include "BKE_geometry_set.hh"
#include "BKE_lib_id.h"
#include "BKE_node.h"
-#include "BKE_persistent_data_handle.hh"
#include "DNA_collection_types.h"
+#include "DNA_material_types.h"
#include "RNA_access.h"
#include "RNA_types.h"
@@ -52,7 +52,7 @@
struct bNodeSocket *node_add_socket_from_template(struct bNodeTree *ntree,
struct bNode *node,
struct bNodeSocketTemplate *stemp,
- int in_out)
+ eNodeSocketInOut in_out)
{
bNodeSocket *sock = nodeAddStaticSocket(
ntree, node, in_out, stemp->type, stemp->subtype, stemp->identifier, stemp->name);
@@ -102,8 +102,11 @@ struct bNodeSocket *node_add_socket_from_template(struct bNodeTree *ntree,
return sock;
}
-static bNodeSocket *verify_socket_template(
- bNodeTree *ntree, bNode *node, int in_out, ListBase *socklist, bNodeSocketTemplate *stemp)
+static bNodeSocket *verify_socket_template(bNodeTree *ntree,
+ bNode *node,
+ eNodeSocketInOut in_out,
+ ListBase *socklist,
+ bNodeSocketTemplate *stemp)
{
bNodeSocket *sock;
@@ -132,7 +135,7 @@ static bNodeSocket *verify_socket_template(
static void verify_socket_template_list(bNodeTree *ntree,
bNode *node,
- int in_out,
+ eNodeSocketInOut in_out,
ListBase *socklist,
bNodeSocketTemplate *stemp_first)
{
@@ -291,6 +294,21 @@ void node_socket_init_default_value(bNodeSocket *sock)
sock->default_value = dval;
break;
+ }
+ case SOCK_TEXTURE: {
+ bNodeSocketValueTexture *dval = (bNodeSocketValueTexture *)MEM_callocN(
+ sizeof(bNodeSocketValueTexture), "node socket value texture");
+ dval->value = nullptr;
+
+ sock->default_value = dval;
+ break;
+ }
+ case SOCK_MATERIAL: {
+ bNodeSocketValueMaterial *dval = (bNodeSocketValueMaterial *)MEM_callocN(
+ sizeof(bNodeSocketValueMaterial), "node socket value material");
+ dval->value = nullptr;
+
+ sock->default_value = dval;
break;
}
}
@@ -372,6 +390,20 @@ void node_socket_copy_default_value(bNodeSocket *to, const bNodeSocket *from)
id_us_plus(&toval->value->id);
break;
}
+ case SOCK_TEXTURE: {
+ bNodeSocketValueTexture *toval = (bNodeSocketValueTexture *)to->default_value;
+ bNodeSocketValueTexture *fromval = (bNodeSocketValueTexture *)from->default_value;
+ *toval = *fromval;
+ id_us_plus(&toval->value->id);
+ break;
+ }
+ case SOCK_MATERIAL: {
+ bNodeSocketValueMaterial *toval = (bNodeSocketValueMaterial *)to->default_value;
+ bNodeSocketValueMaterial *fromval = (bNodeSocketValueMaterial *)from->default_value;
+ *toval = *fromval;
+ id_us_plus(&toval->value->id);
+ break;
+ }
}
to->flag |= (from->flag & SOCK_HIDE_VALUE);
@@ -613,9 +645,9 @@ static bNodeSocketType *make_socket_type_vector(PropertySubType subtype)
static bNodeSocketType *make_socket_type_rgba()
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_RGBA, PROP_NONE);
- socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<blender::Color4f>(); };
+ socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<blender::ColorGeometry4f>(); };
socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) {
- *(blender::Color4f *)r_value = ((bNodeSocketValueRGBA *)socket.default_value)->value;
+ *(blender::ColorGeometry4f *)r_value = ((bNodeSocketValueRGBA *)socket.default_value)->value;
};
return socktype;
}
@@ -630,63 +662,17 @@ static bNodeSocketType *make_socket_type_string()
return socktype;
}
-class ObjectSocketMultiFunction : public blender::fn::MultiFunction {
- private:
- Object *object_;
-
- public:
- ObjectSocketMultiFunction(Object *object) : object_(object)
- {
- static blender::fn::MFSignature signature = create_signature();
- this->set_signature(&signature);
- }
-
- static blender::fn::MFSignature create_signature()
- {
- blender::fn::MFSignatureBuilder signature{"Object Socket"};
- signature.depends_on_context();
- signature.single_output<blender::bke::PersistentObjectHandle>("Object");
- return signature.build();
- }
-
- void call(blender::IndexMask mask,
- blender::fn::MFParams params,
- blender::fn::MFContext context) const override
- {
- blender::MutableSpan output =
- params.uninitialized_single_output<blender::bke::PersistentObjectHandle>(0, "Object");
-
- /* Try to get a handle map, so that the object can be converted to a handle. */
- const blender::bke::PersistentDataHandleMap *handle_map =
- context.get_global_context<blender::bke::PersistentDataHandleMap>(
- "PersistentDataHandleMap");
-
- if (handle_map == nullptr) {
- /* Return empty handles when there is no handle map. */
- output.fill_indices(mask, blender::bke::PersistentObjectHandle());
- return;
- }
-
- blender::bke::PersistentObjectHandle handle = handle_map->lookup(object_);
- for (int64_t i : mask) {
- output[i] = handle;
- }
- }
-};
-
-MAKE_CPP_TYPE(PersistentObjectHandle, blender::bke::PersistentObjectHandle);
-MAKE_CPP_TYPE(PersistentCollectionHandle, blender::bke::PersistentCollectionHandle);
+MAKE_CPP_TYPE(Object, Object *)
+MAKE_CPP_TYPE(Collection, Collection *)
+MAKE_CPP_TYPE(Texture, Tex *)
+MAKE_CPP_TYPE(Material, Material *)
static bNodeSocketType *make_socket_type_object()
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_OBJECT, PROP_NONE);
- socktype->get_cpp_type = []() {
- /* Objects are not passed along as raw pointers, but as handles. */
- return &blender::fn::CPPType::get<blender::bke::PersistentObjectHandle>();
- };
- socktype->expand_in_mf_network = [](blender::nodes::SocketMFNetworkBuilder &builder) {
- Object *object = builder.socket_default_value<bNodeSocketValueObject>()->value;
- builder.construct_generator_fn<ObjectSocketMultiFunction>(object);
+ socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<Object *>(); };
+ socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) {
+ *(Object **)r_value = ((bNodeSocketValueObject *)socket.default_value)->value;
};
return socktype;
}
@@ -704,9 +690,29 @@ static bNodeSocketType *make_socket_type_geometry()
static bNodeSocketType *make_socket_type_collection()
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_COLLECTION, PROP_NONE);
- socktype->get_cpp_type = []() {
- /* Objects are not passed along as raw pointers, but as handles. */
- return &blender::fn::CPPType::get<blender::bke::PersistentCollectionHandle>();
+ socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<Collection *>(); };
+ socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) {
+ *(Collection **)r_value = ((bNodeSocketValueCollection *)socket.default_value)->value;
+ };
+ return socktype;
+}
+
+static bNodeSocketType *make_socket_type_texture()
+{
+ bNodeSocketType *socktype = make_standard_socket_type(SOCK_TEXTURE, PROP_NONE);
+ socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<Tex *>(); };
+ socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) {
+ *(Tex **)r_value = ((bNodeSocketValueTexture *)socket.default_value)->value;
+ };
+ return socktype;
+}
+
+static bNodeSocketType *make_socket_type_material()
+{
+ bNodeSocketType *socktype = make_standard_socket_type(SOCK_MATERIAL, PROP_NONE);
+ socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<Material *>(); };
+ socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) {
+ *(Material **)r_value = ((bNodeSocketValueMaterial *)socket.default_value)->value;
};
return socktype;
}
@@ -721,6 +727,7 @@ void register_standard_node_socket_types(void)
nodeRegisterSocketType(make_socket_type_float(PROP_FACTOR));
nodeRegisterSocketType(make_socket_type_float(PROP_ANGLE));
nodeRegisterSocketType(make_socket_type_float(PROP_TIME));
+ nodeRegisterSocketType(make_socket_type_float(PROP_TIME_ABSOLUTE));
nodeRegisterSocketType(make_socket_type_float(PROP_DISTANCE));
nodeRegisterSocketType(make_socket_type_int(PROP_NONE));
@@ -752,5 +759,9 @@ void register_standard_node_socket_types(void)
nodeRegisterSocketType(make_socket_type_collection());
+ nodeRegisterSocketType(make_socket_type_texture());
+
+ nodeRegisterSocketType(make_socket_type_material());
+
nodeRegisterSocketType(make_socket_type_virtual());
}
diff --git a/source/blender/nodes/intern/node_tree_multi_function.cc b/source/blender/nodes/intern/node_tree_multi_function.cc
index b973350becd..7ab6495f733 100644
--- a/source/blender/nodes/intern/node_tree_multi_function.cc
+++ b/source/blender/nodes/intern/node_tree_multi_function.cc
@@ -15,6 +15,7 @@
*/
#include "NOD_node_tree_multi_function.hh"
+#include "NOD_type_conversions.hh"
#include "FN_multi_function_network_evaluation.hh"
@@ -142,118 +143,16 @@ static void insert_nodes(CommonMFNetworkBuilderData &common)
});
}
-template<typename From, typename To>
-static void add_implicit_conversion(DataTypeConversions &conversions)
-{
- static fn::CustomMF_Convert<From, To> function;
- conversions.add(fn::MFDataType::ForSingle<From>(), fn::MFDataType::ForSingle<To>(), function);
-}
-
-template<typename From, typename To, typename ConversionF>
-static void add_implicit_conversion(DataTypeConversions &conversions,
- StringRef name,
- ConversionF conversion)
-{
- static fn::CustomMF_SI_SO<From, To> function{name, conversion};
- conversions.add(fn::MFDataType::ForSingle<From>(), fn::MFDataType::ForSingle<To>(), function);
-}
-
-static DataTypeConversions create_implicit_conversions()
-{
- DataTypeConversions conversions;
- add_implicit_conversion<float, float2>(conversions);
- add_implicit_conversion<float, float3>(conversions);
- add_implicit_conversion<float, int32_t>(conversions);
- add_implicit_conversion<float, bool>(conversions);
- add_implicit_conversion<float, Color4f>(
- conversions, "float to Color4f", [](float a) { return Color4f(a, a, a, 1.0f); });
-
- add_implicit_conversion<float2, float3>(
- conversions, "float2 to float3", [](float2 a) { return float3(a.x, a.y, 0.0f); });
- add_implicit_conversion<float2, float>(
- conversions, "float2 to float", [](float2 a) { return (a.x + a.y) / 2.0f; });
- add_implicit_conversion<float2, int32_t>(
- conversions, "float2 to int32_t", [](float2 a) { return (int32_t)((a.x + a.y) / 2.0f); });
- add_implicit_conversion<float2, bool>(
- conversions, "float2 to bool", [](float2 a) { return !is_zero_v2(a); });
- add_implicit_conversion<float2, Color4f>(
- conversions, "float2 to Color4f", [](float2 a) { return Color4f(a.x, a.y, 0.0f, 1.0f); });
-
- add_implicit_conversion<float3, bool>(
- conversions, "float3 to boolean", [](float3 a) { return !is_zero_v3(a); });
- add_implicit_conversion<float3, float>(
- conversions, "float3 to float", [](float3 a) { return (a.x + a.y + a.z) / 3.0f; });
- add_implicit_conversion<float3, int32_t>(
- conversions, "float3 to int32_t", [](float3 a) { return (int)((a.x + a.y + a.z) / 3.0f); });
- add_implicit_conversion<float3, float2>(conversions);
- add_implicit_conversion<float3, Color4f>(
- conversions, "float3 to Color4f", [](float3 a) { return Color4f(a.x, a.y, a.z, 1.0f); });
-
- add_implicit_conversion<int32_t, bool>(conversions);
- add_implicit_conversion<int32_t, float>(conversions);
- add_implicit_conversion<int32_t, float2>(
- conversions, "int32 to float2", [](int32_t a) { return float2((float)a); });
- add_implicit_conversion<int32_t, float3>(
- conversions, "int32 to float3", [](int32_t a) { return float3((float)a); });
- add_implicit_conversion<int32_t, Color4f>(conversions, "int32 to Color4f", [](int32_t a) {
- return Color4f((float)a, (float)a, (float)a, 1.0f);
- });
-
- add_implicit_conversion<bool, float>(conversions);
- add_implicit_conversion<bool, int32_t>(conversions);
- add_implicit_conversion<bool, float2>(
- conversions, "boolean to float2", [](bool a) { return (a) ? float2(1.0f) : float2(0.0f); });
- add_implicit_conversion<bool, float3>(
- conversions, "boolean to float3", [](bool a) { return (a) ? float3(1.0f) : float3(0.0f); });
- add_implicit_conversion<bool, Color4f>(conversions, "boolean to Color4f", [](bool a) {
- return (a) ? Color4f(1.0f, 1.0f, 1.0f, 1.0f) : Color4f(0.0f, 0.0f, 0.0f, 1.0f);
- });
-
- add_implicit_conversion<Color4f, bool>(conversions, "Color4f to boolean", [](Color4f a) {
- return a.r != 0.0f && a.g != 0.0f && a.b != 0.0f;
- });
- add_implicit_conversion<Color4f, float>(
- conversions, "Color4f to float", [](Color4f a) { return rgb_to_grayscale(a); });
- add_implicit_conversion<Color4f, float2>(
- conversions, "Color4f to float2", [](Color4f a) { return float2(a.r, a.g); });
- add_implicit_conversion<Color4f, float3>(
- conversions, "Color4f to float3", [](Color4f a) { return float3(a.r, a.g, a.b); });
-
- return conversions;
-}
-
-const DataTypeConversions &get_implicit_type_conversions()
-{
- static const DataTypeConversions conversions = create_implicit_conversions();
- return conversions;
-}
-
-void DataTypeConversions::convert(const CPPType &from_type,
- const CPPType &to_type,
- const void *from_value,
- void *to_value) const
-{
- const fn::MultiFunction *fn = this->get_conversion(MFDataType::ForSingle(from_type),
- MFDataType::ForSingle(to_type));
- BLI_assert(fn != nullptr);
-
- fn::MFContextBuilder context;
- fn::MFParamsBuilder params{*fn, 1};
- params.add_readonly_single_input(fn::GSpan(from_type, from_value, 1));
- params.add_uninitialized_single_output(fn::GMutableSpan(to_type, to_value, 1));
- fn->call({0}, params, context);
-}
-
static fn::MFOutputSocket &insert_default_value_for_type(CommonMFNetworkBuilderData &common,
fn::MFDataType type)
{
const fn::MultiFunction *default_fn;
if (type.is_single()) {
- default_fn = &common.resources.construct<fn::CustomMF_GenericConstant>(
+ default_fn = &common.scope.construct<fn::CustomMF_GenericConstant>(
AT, type.single_type(), type.single_type().default_value());
}
else {
- default_fn = &common.resources.construct<fn::CustomMF_GenericConstantArray>(
+ default_fn = &common.scope.construct<fn::CustomMF_GenericConstantArray>(
AT, fn::GSpan(type.vector_base_type()));
}
@@ -320,8 +219,8 @@ static void insert_links_and_unlinked_inputs(CommonMFNetworkBuilderData &common)
const fn::MFDataType from_type = from_socket->data_type();
if (from_type != to_type) {
- const fn::MultiFunction *conversion_fn = get_implicit_type_conversions().get_conversion(
- from_type, to_type);
+ const fn::MultiFunction *conversion_fn =
+ get_implicit_type_conversions().get_conversion_multi_function(from_type, to_type);
if (conversion_fn != nullptr) {
fn::MFNode &node = common.network.add_function(*conversion_fn);
common.network.add_link(*from_socket, node.input(0));
@@ -348,11 +247,11 @@ static void insert_links_and_unlinked_inputs(CommonMFNetworkBuilderData &common)
*/
MFNetworkTreeMap insert_node_tree_into_mf_network(fn::MFNetwork &network,
const DerivedNodeTree &tree,
- ResourceCollector &resources)
+ ResourceScope &scope)
{
MFNetworkTreeMap network_map{tree, network};
- CommonMFNetworkBuilderData common{resources, network, network_map, tree};
+ CommonMFNetworkBuilderData common{scope, network, network_map, tree};
insert_nodes(common);
insert_links_and_unlinked_inputs(common);
@@ -426,7 +325,7 @@ static const fn::MultiFunction &create_function_for_node_that_expands_into_multi
const DNode &dnode,
fn::MFNetwork &network,
MFNetworkTreeMap &network_map,
- ResourceCollector &resources)
+ ResourceScope &scope)
{
Vector<const fn::MFOutputSocket *> dummy_fn_inputs;
for (const InputSocketRef *dsocket : dnode->inputs()) {
@@ -451,7 +350,7 @@ static const fn::MultiFunction &create_function_for_node_that_expands_into_multi
}
}
- fn::MFNetworkEvaluator &fn_evaluator = resources.construct<fn::MFNetworkEvaluator>(
+ fn::MFNetworkEvaluator &fn_evaluator = scope.construct<fn::MFNetworkEvaluator>(
__func__, std::move(dummy_fn_inputs), std::move(dummy_fn_outputs));
return fn_evaluator;
}
@@ -460,16 +359,15 @@ static const fn::MultiFunction &create_function_for_node_that_expands_into_multi
* Returns a single multi-function for every node that supports it. This makes it easier to reuse
* the multi-function implementation of nodes in different contexts.
*/
-MultiFunctionByNode get_multi_function_per_node(const DerivedNodeTree &tree,
- ResourceCollector &resources)
+MultiFunctionByNode get_multi_function_per_node(const DerivedNodeTree &tree, ResourceScope &scope)
{
/* Build a network that nodes can insert themselves into. However, the individual nodes are not
* connected. */
- fn::MFNetwork &network = resources.construct<fn::MFNetwork>(__func__);
+ fn::MFNetwork &network = scope.construct<fn::MFNetwork>(__func__);
MFNetworkTreeMap network_map{tree, network};
MultiFunctionByNode functions_by_node;
- CommonMFNetworkBuilderData common{resources, network, network_map, tree};
+ CommonMFNetworkBuilderData common{scope, network, network_map, tree};
tree.foreach_node([&](DNode dnode) {
const bNodeType *node_type = dnode->typeinfo();
@@ -498,7 +396,7 @@ MultiFunctionByNode get_multi_function_per_node(const DerivedNodeTree &tree,
/* If a node expanded into multiple functions, a new function has to be created that
* combines those. */
const fn::MultiFunction &fn = create_function_for_node_that_expands_into_multiple(
- dnode, network, network_map, resources);
+ dnode, network, network_map, scope);
functions_by_node.add_new(dnode, &fn);
break;
}
diff --git a/source/blender/nodes/intern/node_tree_ref.cc b/source/blender/nodes/intern/node_tree_ref.cc
index e627a7f4497..154ee716153 100644
--- a/source/blender/nodes/intern/node_tree_ref.cc
+++ b/source/blender/nodes/intern/node_tree_ref.cc
@@ -14,6 +14,8 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#include <mutex>
+
#include "NOD_node_tree_ref.hh"
#include "BLI_dot_export.hh"
@@ -105,6 +107,7 @@ NodeTreeRef::NodeTreeRef(bNodeTree *btree) : btree_(btree)
}
}
+ this->create_socket_identifier_maps();
this->create_linked_socket_caches();
for (NodeRef *node : nodes_by_id_) {
@@ -163,7 +166,7 @@ void NodeTreeRef::create_linked_socket_caches()
{
for (InputSocketRef *socket : input_sockets_) {
/* Find directly linked socket based on incident links. */
- Vector<SocketRef *> directly_linked_sockets;
+ Vector<const SocketRef *> directly_linked_sockets;
for (LinkRef *link : socket->directly_linked_links_) {
directly_linked_sockets.append(link->from_);
}
@@ -171,9 +174,14 @@ void NodeTreeRef::create_linked_socket_caches()
directly_linked_sockets.as_span());
/* Find logically linked sockets. */
- Vector<SocketRef *> logically_linked_sockets;
- this->foreach_logical_origin(
- *socket, [&](OutputSocketRef &origin) { logically_linked_sockets.append(&origin); });
+ Vector<const SocketRef *> logically_linked_sockets;
+ Vector<const SocketRef *> logically_linked_skipped_sockets;
+ Vector<const InputSocketRef *> handled_sockets;
+ socket->foreach_logical_origin(
+ [&](const OutputSocketRef &origin) { logically_linked_sockets.append(&origin); },
+ [&](const SocketRef &socket) { logically_linked_skipped_sockets.append(&socket); },
+ false,
+ handled_sockets);
if (logically_linked_sockets == directly_linked_sockets) {
socket->logically_linked_sockets_ = socket->directly_linked_sockets_;
}
@@ -181,11 +189,13 @@ void NodeTreeRef::create_linked_socket_caches()
socket->logically_linked_sockets_ = allocator_.construct_array_copy(
logically_linked_sockets.as_span());
}
+ socket->logically_linked_skipped_sockets_ = allocator_.construct_array_copy(
+ logically_linked_skipped_sockets.as_span());
}
for (OutputSocketRef *socket : output_sockets_) {
/* Find directly linked socket based on incident links. */
- Vector<SocketRef *> directly_linked_sockets;
+ Vector<const SocketRef *> directly_linked_sockets;
for (LinkRef *link : socket->directly_linked_links_) {
directly_linked_sockets.append(link->to_);
}
@@ -193,9 +203,13 @@ void NodeTreeRef::create_linked_socket_caches()
directly_linked_sockets.as_span());
/* Find logically linked sockets. */
- Vector<SocketRef *> logically_linked_sockets;
- this->foreach_logical_target(
- *socket, [&](InputSocketRef &target) { logically_linked_sockets.append(&target); });
+ Vector<const SocketRef *> logically_linked_sockets;
+ Vector<const SocketRef *> logically_linked_skipped_sockets;
+ Vector<const OutputSocketRef *> handled_sockets;
+ socket->foreach_logical_target(
+ [&](const InputSocketRef &target) { logically_linked_sockets.append(&target); },
+ [&](const SocketRef &socket) { logically_linked_skipped_sockets.append(&socket); },
+ handled_sockets);
if (logically_linked_sockets == directly_linked_sockets) {
socket->logically_linked_sockets_ = socket->directly_linked_sockets_;
}
@@ -203,61 +217,187 @@ void NodeTreeRef::create_linked_socket_caches()
socket->logically_linked_sockets_ = allocator_.construct_array_copy(
logically_linked_sockets.as_span());
}
+ socket->logically_linked_skipped_sockets_ = allocator_.construct_array_copy(
+ logically_linked_skipped_sockets.as_span());
}
}
-void NodeTreeRef::foreach_logical_origin(InputSocketRef &socket,
- FunctionRef<void(OutputSocketRef &)> callback,
- bool only_follow_first_input_link)
+void InputSocketRef::foreach_logical_origin(FunctionRef<void(const OutputSocketRef &)> origin_fn,
+ FunctionRef<void(const SocketRef &)> skipped_fn,
+ bool only_follow_first_input_link,
+ Vector<const InputSocketRef *> &handled_sockets) const
{
- Span<LinkRef *> links_to_check = socket.directly_linked_links_;
+ /* Protect against loops. */
+ if (handled_sockets.contains(this)) {
+ return;
+ }
+ handled_sockets.append(this);
+
+ Span<const LinkRef *> links_to_check = this->directly_linked_links();
if (only_follow_first_input_link) {
links_to_check = links_to_check.take_front(1);
}
- for (LinkRef *link : links_to_check) {
+ for (const LinkRef *link : links_to_check) {
if (link->is_muted()) {
continue;
}
- OutputSocketRef *origin = link->from_;
- NodeRef *origin_node = origin->node_;
- if (origin_node->is_reroute_node()) {
- this->foreach_logical_origin(*origin_node->inputs_[0], callback, false);
+ const OutputSocketRef &origin = link->from();
+ const NodeRef &origin_node = origin.node();
+ if (!origin.is_available()) {
+ /* Non available sockets are ignored. */
}
- else if (origin_node->is_muted()) {
- for (InternalLinkRef *internal_link : origin_node->internal_links_) {
- if (internal_link->to_ == origin) {
- this->foreach_logical_origin(*internal_link->from_, callback, true);
+ else if (origin_node.is_reroute_node()) {
+ const InputSocketRef &reroute_input = origin_node.input(0);
+ const OutputSocketRef &reroute_output = origin_node.output(0);
+ skipped_fn.call_safe(reroute_input);
+ skipped_fn.call_safe(reroute_output);
+ reroute_input.foreach_logical_origin(origin_fn, skipped_fn, false, handled_sockets);
+ }
+ else if (origin_node.is_muted()) {
+ for (const InternalLinkRef *internal_link : origin_node.internal_links()) {
+ if (&internal_link->to() == &origin) {
+ const InputSocketRef &mute_input = internal_link->from();
+ skipped_fn.call_safe(origin);
+ skipped_fn.call_safe(mute_input);
+ mute_input.foreach_logical_origin(origin_fn, skipped_fn, true, handled_sockets);
break;
}
}
}
else {
- callback(*origin);
+ origin_fn(origin);
}
}
}
-void NodeTreeRef::foreach_logical_target(OutputSocketRef &socket,
- FunctionRef<void(InputSocketRef &)> callback)
+void OutputSocketRef::foreach_logical_target(
+ FunctionRef<void(const InputSocketRef &)> target_fn,
+ FunctionRef<void(const SocketRef &)> skipped_fn,
+ Vector<const OutputSocketRef *> &handled_sockets) const
{
- for (LinkRef *link : socket.directly_linked_links_) {
+ /* Protect against loops. */
+ if (handled_sockets.contains(this)) {
+ return;
+ }
+ handled_sockets.append(this);
+
+ for (const LinkRef *link : this->directly_linked_links()) {
if (link->is_muted()) {
continue;
}
- InputSocketRef *target = link->to_;
- NodeRef *target_node = target->node_;
- if (target_node->is_reroute_node()) {
- this->foreach_logical_target(*target_node->outputs_[0], callback);
+ const InputSocketRef &target = link->to();
+ const NodeRef &target_node = target.node();
+ if (!target.is_available()) {
+ /* Non available sockets are ignored. */
}
- else if (target_node->is_muted()) {
- for (InternalLinkRef *internal_link : target_node->internal_links_) {
- if (internal_link->from_ == target) {
- this->foreach_logical_target(*internal_link->to_, callback);
+ else if (target_node.is_reroute_node()) {
+ const OutputSocketRef &reroute_output = target_node.output(0);
+ skipped_fn.call_safe(target);
+ skipped_fn.call_safe(reroute_output);
+ reroute_output.foreach_logical_target(target_fn, skipped_fn, handled_sockets);
+ }
+ else if (target_node.is_muted()) {
+ skipped_fn.call_safe(target);
+ for (const InternalLinkRef *internal_link : target_node.internal_links()) {
+ if (&internal_link->from() == &target) {
+ /* The internal link only forwards the first incoming link. */
+ if (target.is_multi_input_socket()) {
+ if (target.directly_linked_links()[0] != link) {
+ continue;
+ }
+ }
+ const OutputSocketRef &mute_output = internal_link->to();
+ skipped_fn.call_safe(target);
+ skipped_fn.call_safe(mute_output);
+ mute_output.foreach_logical_target(target_fn, skipped_fn, handled_sockets);
}
}
}
else {
- callback(*target);
+ target_fn(target);
+ }
+ }
+}
+
+namespace {
+struct SocketByIdentifierMap {
+ SocketIndexByIdentifierMap *map = nullptr;
+ std::unique_ptr<SocketIndexByIdentifierMap> owned_map;
+};
+} // namespace
+
+static std::unique_ptr<SocketIndexByIdentifierMap> create_identifier_map(const ListBase &sockets)
+{
+ std::unique_ptr<SocketIndexByIdentifierMap> map = std::make_unique<SocketIndexByIdentifierMap>();
+ int index;
+ LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &sockets, index) {
+ map->add_new(socket->identifier, index);
+ }
+ return map;
+}
+
+/* This function is not threadsafe. */
+static SocketByIdentifierMap get_or_create_identifier_map(
+ const bNode &node, const ListBase &sockets, const bNodeSocketTemplate *sockets_template)
+{
+ SocketByIdentifierMap map;
+ if (sockets_template == nullptr) {
+ if (BLI_listbase_is_empty(&sockets)) {
+ static SocketIndexByIdentifierMap empty_map;
+ map.map = &empty_map;
+ }
+ else if (node.type == NODE_REROUTE) {
+ if (&node.inputs == &sockets) {
+ static SocketIndexByIdentifierMap reroute_input_map = [] {
+ SocketIndexByIdentifierMap map;
+ map.add_new("Input", 0);
+ return map;
+ }();
+ map.map = &reroute_input_map;
+ }
+ else {
+ static SocketIndexByIdentifierMap reroute_output_map = [] {
+ SocketIndexByIdentifierMap map;
+ map.add_new("Output", 0);
+ return map;
+ }();
+ map.map = &reroute_output_map;
+ }
+ }
+ else {
+ /* The node has a dynamic amount of sockets. Therefore we need to create a new map. */
+ map.owned_map = create_identifier_map(sockets);
+ map.map = &*map.owned_map;
+ }
+ }
+ else {
+ /* Cache only one map for nodes that have the same sockets. */
+ static Map<const bNodeSocketTemplate *, std::unique_ptr<SocketIndexByIdentifierMap>> maps;
+ map.map = &*maps.lookup_or_add_cb(sockets_template,
+ [&]() { return create_identifier_map(sockets); });
+ }
+ return map;
+}
+
+void NodeTreeRef::create_socket_identifier_maps()
+{
+ /* `get_or_create_identifier_map` is not threadsafe, therefore we have to hold a lock here. */
+ static std::mutex mutex;
+ std::lock_guard lock{mutex};
+
+ for (NodeRef *node : nodes_by_id_) {
+ bNode &bnode = *node->bnode_;
+ SocketByIdentifierMap inputs_map = get_or_create_identifier_map(
+ bnode, bnode.inputs, bnode.typeinfo->inputs);
+ SocketByIdentifierMap outputs_map = get_or_create_identifier_map(
+ bnode, bnode.outputs, bnode.typeinfo->outputs);
+ node->input_index_by_identifier_ = inputs_map.map;
+ node->output_index_by_identifier_ = outputs_map.map;
+ if (inputs_map.owned_map) {
+ owned_identifier_maps_.append(std::move(inputs_map.owned_map));
+ }
+ if (outputs_map.owned_map) {
+ owned_identifier_maps_.append(std::move(outputs_map.owned_map));
}
}
}
@@ -304,6 +444,21 @@ bool NodeTreeRef::has_link_cycles() const
return false;
}
+bool NodeTreeRef::has_undefined_nodes_or_sockets() const
+{
+ for (const NodeRef *node : nodes_by_id_) {
+ if (node->is_undefined()) {
+ return true;
+ }
+ }
+ for (const SocketRef *socket : sockets_by_id_) {
+ if (socket->is_undefined()) {
+ return true;
+ }
+ }
+ return true;
+}
+
std::string NodeTreeRef::to_dot() const
{
dot::DirectedGraph digraph;
diff --git a/source/blender/nodes/intern/node_util.c b/source/blender/nodes/intern/node_util.c
index 00db819721c..1aec280fd2b 100644
--- a/source/blender/nodes/intern/node_util.c
+++ b/source/blender/nodes/intern/node_util.c
@@ -140,8 +140,8 @@ void node_math_update(bNodeTree *UNUSED(ntree), bNode *node)
switch (node->custom1) {
case NODE_MATH_WRAP:
- node_sock_label(sock2, "Min");
- node_sock_label(sock3, "Max");
+ node_sock_label(sock2, "Max");
+ node_sock_label(sock3, "Min");
break;
case NODE_MATH_MULTIPLY_ADD:
node_sock_label(sock2, "Multiplier");
@@ -239,24 +239,21 @@ void node_filter_label(bNodeTree *UNUSED(ntree), bNode *node, char *label, int m
/** \name Link Insertion
* \{ */
-/* test if two sockets are interchangeable */
-static bool node_link_socket_match(bNodeSocket *a, bNodeSocket *b)
+static bool node_link_socket_match(const bNodeSocket *a, const bNodeSocket *b)
{
- /* check if sockets are of the same type */
+ /* Check if sockets are of the same type. */
if (a->typeinfo != b->typeinfo) {
return false;
}
- /* tests if alphabetic prefix matches
- * this allows for imperfect matches, such as numeric suffixes,
- * like Color1/Color2
- */
+ /* Test if alphabetic prefix matches, allowing for imperfect matches, such as numeric suffixes
+ * like Color1/Color2. */
int prefix_len = 0;
- char *ca = a->name, *cb = b->name;
+ const char *ca = a->name, *cb = b->name;
for (; *ca != '\0' && *cb != '\0'; ca++, cb++) {
- /* end of common prefix? */
+ /* End of common prefix? */
if (*ca != *cb) {
- /* prefix delimited by non-alphabetic char */
+ /* Prefix delimited by non-alphabetic char. */
if (isalpha(*ca) || isalpha(*cb)) {
return false;
}
@@ -267,75 +264,73 @@ static bool node_link_socket_match(bNodeSocket *a, bNodeSocket *b)
return prefix_len > 0;
}
-static int node_count_links(bNodeTree *ntree, bNodeSocket *sock)
+static int node_count_links(const bNodeTree *ntree, const bNodeSocket *socket)
{
- bNodeLink *link;
int count = 0;
- for (link = ntree->links.first; link; link = link->next) {
- if (link->fromsock == sock) {
- count++;
- }
- if (link->tosock == sock) {
+ LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
+ if (ELEM(socket, link->fromsock, link->tosock)) {
count++;
}
}
return count;
}
-/* Find an eligible socket for linking. */
-static bNodeSocket *node_find_linkable_socket(bNodeTree *ntree, bNode *node, bNodeSocket *cur)
+static bNodeSocket *node_find_linkable_socket(bNodeTree *ntree,
+ bNode *node,
+ bNodeSocket *to_socket)
{
- bNodeSocket *first = cur->in_out == SOCK_IN ? node->inputs.first : node->outputs.first;
- bNodeSocket *sock;
-
- /* Iterate over all sockets of the target node, to find one that matches the same socket type.
- * The idea behind this is: When a user connects an input to a socket that is
- * already linked (and if its not an Multi Input Socket), we try to find a replacement socket for
- * the link that we try to overwrite and connect that previous link to the new socket. */
- sock = cur->next ? cur->next : first; /* Wrap around the list end. */
- while (sock != cur) {
- if (!nodeSocketIsHidden(sock) && node_link_socket_match(sock, cur)) {
- break;
+ bNodeSocket *first = to_socket->in_out == SOCK_IN ? node->inputs.first : node->outputs.first;
+
+ /* Wrap around the list end. */
+ bNodeSocket *socket_iter = to_socket->next ? to_socket->next : first;
+ while (socket_iter != to_socket) {
+ if (!nodeSocketIsHidden(socket_iter) && node_link_socket_match(socket_iter, to_socket)) {
+ const int link_count = node_count_links(ntree, socket_iter);
+ /* Add one to account for the new link being added. */
+ if (link_count + 1 <= nodeSocketLinkLimit(socket_iter)) {
+ return socket_iter; /* Found a valid free socket we can swap to. */
+ }
}
- sock = sock->next ? sock->next : first; /* Wrap around the list end. */
+ socket_iter = socket_iter->next ? socket_iter->next : first; /* Wrap around the list end. */
}
- if (!nodeSocketIsHidden(sock) && node_link_socket_match(sock, cur)) {
- int link_count = node_count_links(ntree, sock);
- /* Take +1 into account since we would add a new link. */
- if (link_count + 1 <= nodeSocketLinkLimit(sock)) {
- return sock; /* Found a valid free socket we can swap to. */
- }
- }
return NULL;
}
+/**
+ * The idea behind this is: When a user connects an input to a socket that is
+ * already linked (and if its not an Multi Input Socket), we try to find a replacement socket for
+ * the link that we try to overwrite and connect that previous link to the new socket.
+ */
void node_insert_link_default(bNodeTree *ntree, bNode *node, bNodeLink *link)
{
- bNodeSocket *sock = link->tosock;
- bNodeLink *tlink, *tlink_next;
+ bNodeSocket *socket = link->tosock;
if (node != link->tonode) {
return;
}
- for (tlink = ntree->links.first; tlink; tlink = tlink_next) {
- bNodeSocket *new_sock;
- tlink_next = tlink->next;
+ /* If we're not at the link limit of the target socket, we can skip
+ * trying to move existing links to another socket. */
+ const int to_link_limit = nodeSocketLinkLimit(socket);
+ if (socket->total_inputs + 1 < to_link_limit) {
+ return;
+ }
- if (sock != tlink->tosock) {
- continue;
- }
+ LISTBASE_FOREACH_MUTABLE (bNodeLink *, to_link, &ntree->links) {
+ if (socket == to_link->tosock) {
+ bNodeSocket *new_socket = node_find_linkable_socket(ntree, node, socket);
+ if (new_socket && new_socket != socket) {
+ /* Attempt to redirect the existing link to the new socket. */
+ to_link->tosock = new_socket;
+ return;
+ }
- new_sock = node_find_linkable_socket(ntree, node, sock);
- if (new_sock && new_sock != sock) {
- /* redirect existing link */
- tlink->tosock = new_sock;
- }
- else if (!new_sock) {
- /* no possible replacement, remove tlink */
- nodeRemLink(ntree, tlink);
- tlink = NULL;
+ if (new_socket == NULL) {
+ /* No possible replacement, remove the existing link. */
+ nodeRemLink(ntree, to_link);
+ return;
+ }
}
}
}
@@ -464,6 +459,22 @@ static int node_datatype_priority(eNodeSocketDatatype from, eNodeSocketDatatype
return -1;
}
}
+ case SOCK_TEXTURE: {
+ switch (from) {
+ case SOCK_TEXTURE:
+ return 1;
+ default:
+ return -1;
+ }
+ }
+ case SOCK_MATERIAL: {
+ switch (from) {
+ case SOCK_MATERIAL:
+ return 1;
+ default:
+ return -1;
+ }
+ }
default:
return -1;
}
diff --git a/source/blender/nodes/intern/type_callbacks.cc b/source/blender/nodes/intern/type_callbacks.cc
index 26eeaebc6d4..5160432aea5 100644
--- a/source/blender/nodes/intern/type_callbacks.cc
+++ b/source/blender/nodes/intern/type_callbacks.cc
@@ -64,7 +64,8 @@ void socket_expand_in_mf_network(SocketMFNetworkBuilder &builder)
}
else if (socket.typeinfo->get_cpp_value != nullptr) {
const CPPType &type = *socket_cpp_type_get(*socket.typeinfo);
- void *buffer = builder.resources().linear_allocator().allocate(type.size(), type.alignment());
+ void *buffer = builder.resource_scope().linear_allocator().allocate(type.size(),
+ type.alignment());
socket.typeinfo->get_cpp_value(socket, buffer);
builder.set_constant_value(type, buffer);
}
diff --git a/source/blender/nodes/intern/type_conversions.cc b/source/blender/nodes/intern/type_conversions.cc
new file mode 100644
index 00000000000..220e5ea9046
--- /dev/null
+++ b/source/blender/nodes/intern/type_conversions.cc
@@ -0,0 +1,349 @@
+/*
+ * 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.
+ */
+
+#include "NOD_type_conversions.hh"
+
+#include "FN_multi_function_builder.hh"
+
+#include "BLI_color.hh"
+#include "BLI_float2.hh"
+#include "BLI_float3.hh"
+
+namespace blender::nodes {
+
+using fn::GVArrayPtr;
+using fn::GVMutableArray;
+using fn::GVMutableArrayPtr;
+using fn::MFDataType;
+
+template<typename From, typename To, To (*ConversionF)(const From &)>
+static void add_implicit_conversion(DataTypeConversions &conversions)
+{
+ const CPPType &from_type = CPPType::get<From>();
+ const CPPType &to_type = CPPType::get<To>();
+ const std::string conversion_name = from_type.name() + " to " + to_type.name();
+
+ static fn::CustomMF_SI_SO<From, To> multi_function{conversion_name, ConversionF};
+ static auto convert_single_to_initialized = [](const void *src, void *dst) {
+ *(To *)dst = ConversionF(*(const From *)src);
+ };
+ static auto convert_single_to_uninitialized = [](const void *src, void *dst) {
+ new (dst) To(ConversionF(*(const From *)src));
+ };
+ conversions.add(fn::MFDataType::ForSingle<From>(),
+ fn::MFDataType::ForSingle<To>(),
+ multi_function,
+ convert_single_to_initialized,
+ convert_single_to_uninitialized);
+}
+
+static float2 float_to_float2(const float &a)
+{
+ return float2(a);
+}
+static float3 float_to_float3(const float &a)
+{
+ return float3(a);
+}
+static int32_t float_to_int(const float &a)
+{
+ return (int32_t)a;
+}
+static bool float_to_bool(const float &a)
+{
+ return a > 0.0f;
+}
+static ColorGeometry4f float_to_color(const float &a)
+{
+ return ColorGeometry4f(a, a, a, 1.0f);
+}
+
+static float3 float2_to_float3(const float2 &a)
+{
+ return float3(a.x, a.y, 0.0f);
+}
+static float float2_to_float(const float2 &a)
+{
+ return (a.x + a.y) / 2.0f;
+}
+static int float2_to_int(const float2 &a)
+{
+ return (int32_t)((a.x + a.y) / 2.0f);
+}
+static bool float2_to_bool(const float2 &a)
+{
+ return !is_zero_v2(a);
+}
+static ColorGeometry4f float2_to_color(const float2 &a)
+{
+ return ColorGeometry4f(a.x, a.y, 0.0f, 1.0f);
+}
+
+static bool float3_to_bool(const float3 &a)
+{
+ return !is_zero_v3(a);
+}
+static float float3_to_float(const float3 &a)
+{
+ return (a.x + a.y + a.z) / 3.0f;
+}
+static int float3_to_int(const float3 &a)
+{
+ return (int)((a.x + a.y + a.z) / 3.0f);
+}
+static float2 float3_to_float2(const float3 &a)
+{
+ return float2(a);
+}
+static ColorGeometry4f float3_to_color(const float3 &a)
+{
+ return ColorGeometry4f(a.x, a.y, a.z, 1.0f);
+}
+
+static bool int_to_bool(const int32_t &a)
+{
+ return a > 0;
+}
+static float int_to_float(const int32_t &a)
+{
+ return (float)a;
+}
+static float2 int_to_float2(const int32_t &a)
+{
+ return float2((float)a);
+}
+static float3 int_to_float3(const int32_t &a)
+{
+ return float3((float)a);
+}
+static ColorGeometry4f int_to_color(const int32_t &a)
+{
+ return ColorGeometry4f((float)a, (float)a, (float)a, 1.0f);
+}
+
+static float bool_to_float(const bool &a)
+{
+ return (bool)a;
+}
+static int32_t bool_to_int(const bool &a)
+{
+ return (int32_t)a;
+}
+static float2 bool_to_float2(const bool &a)
+{
+ return (a) ? float2(1.0f) : float2(0.0f);
+}
+static float3 bool_to_float3(const bool &a)
+{
+ return (a) ? float3(1.0f) : float3(0.0f);
+}
+static ColorGeometry4f bool_to_color(const bool &a)
+{
+ return (a) ? ColorGeometry4f(1.0f, 1.0f, 1.0f, 1.0f) : ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f);
+}
+
+static bool color_to_bool(const ColorGeometry4f &a)
+{
+ return rgb_to_grayscale(a) > 0.0f;
+}
+static float color_to_float(const ColorGeometry4f &a)
+{
+ return rgb_to_grayscale(a);
+}
+static int32_t color_to_int(const ColorGeometry4f &a)
+{
+ return (int)rgb_to_grayscale(a);
+}
+static float2 color_to_float2(const ColorGeometry4f &a)
+{
+ return float2(a.r, a.g);
+}
+static float3 color_to_float3(const ColorGeometry4f &a)
+{
+ return float3(a.r, a.g, a.b);
+}
+
+static DataTypeConversions create_implicit_conversions()
+{
+ DataTypeConversions conversions;
+
+ add_implicit_conversion<float, float2, float_to_float2>(conversions);
+ add_implicit_conversion<float, float3, float_to_float3>(conversions);
+ add_implicit_conversion<float, int32_t, float_to_int>(conversions);
+ add_implicit_conversion<float, bool, float_to_bool>(conversions);
+ add_implicit_conversion<float, ColorGeometry4f, float_to_color>(conversions);
+
+ add_implicit_conversion<float2, float3, float2_to_float3>(conversions);
+ add_implicit_conversion<float2, float, float2_to_float>(conversions);
+ add_implicit_conversion<float2, int32_t, float2_to_int>(conversions);
+ add_implicit_conversion<float2, bool, float2_to_bool>(conversions);
+ add_implicit_conversion<float2, ColorGeometry4f, float2_to_color>(conversions);
+
+ add_implicit_conversion<float3, bool, float3_to_bool>(conversions);
+ add_implicit_conversion<float3, float, float3_to_float>(conversions);
+ add_implicit_conversion<float3, int32_t, float3_to_int>(conversions);
+ add_implicit_conversion<float3, float2, float3_to_float2>(conversions);
+ add_implicit_conversion<float3, ColorGeometry4f, float3_to_color>(conversions);
+
+ add_implicit_conversion<int32_t, bool, int_to_bool>(conversions);
+ add_implicit_conversion<int32_t, float, int_to_float>(conversions);
+ add_implicit_conversion<int32_t, float2, int_to_float2>(conversions);
+ add_implicit_conversion<int32_t, float3, int_to_float3>(conversions);
+ add_implicit_conversion<int32_t, ColorGeometry4f, int_to_color>(conversions);
+
+ add_implicit_conversion<bool, float, bool_to_float>(conversions);
+ add_implicit_conversion<bool, int32_t, bool_to_int>(conversions);
+ add_implicit_conversion<bool, float2, bool_to_float2>(conversions);
+ add_implicit_conversion<bool, float3, bool_to_float3>(conversions);
+ add_implicit_conversion<bool, ColorGeometry4f, bool_to_color>(conversions);
+
+ add_implicit_conversion<ColorGeometry4f, bool, color_to_bool>(conversions);
+ add_implicit_conversion<ColorGeometry4f, float, color_to_float>(conversions);
+ add_implicit_conversion<ColorGeometry4f, int32_t, color_to_int>(conversions);
+ add_implicit_conversion<ColorGeometry4f, float2, color_to_float2>(conversions);
+ add_implicit_conversion<ColorGeometry4f, float3, color_to_float3>(conversions);
+
+ return conversions;
+}
+
+const DataTypeConversions &get_implicit_type_conversions()
+{
+ static const DataTypeConversions conversions = create_implicit_conversions();
+ return conversions;
+}
+
+void DataTypeConversions::convert_to_uninitialized(const CPPType &from_type,
+ const CPPType &to_type,
+ const void *from_value,
+ void *to_value) const
+{
+ if (from_type == to_type) {
+ from_type.copy_to_uninitialized(from_value, to_value);
+ return;
+ }
+
+ const ConversionFunctions *functions = this->get_conversion_functions(
+ MFDataType::ForSingle(from_type), MFDataType::ForSingle(to_type));
+ BLI_assert(functions != nullptr);
+
+ functions->convert_single_to_uninitialized(from_value, to_value);
+}
+
+class GVArray_For_ConvertedGVArray : public GVArray {
+ private:
+ GVArrayPtr varray_;
+ const CPPType &from_type_;
+ ConversionFunctions old_to_new_conversions_;
+
+ public:
+ GVArray_For_ConvertedGVArray(GVArrayPtr varray,
+ const CPPType &to_type,
+ const DataTypeConversions &conversions)
+ : GVArray(to_type, varray->size()), varray_(std::move(varray)), from_type_(varray_->type())
+ {
+ old_to_new_conversions_ = *conversions.get_conversion_functions(from_type_, to_type);
+ }
+
+ private:
+ void get_impl(const int64_t index, void *r_value) const override
+ {
+ BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer);
+ varray_->get(index, buffer);
+ old_to_new_conversions_.convert_single_to_initialized(buffer, r_value);
+ from_type_.destruct(buffer);
+ }
+
+ void get_to_uninitialized_impl(const int64_t index, void *r_value) const override
+ {
+ BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer);
+ varray_->get(index, buffer);
+ old_to_new_conversions_.convert_single_to_uninitialized(buffer, r_value);
+ from_type_.destruct(buffer);
+ }
+};
+
+class GVMutableArray_For_ConvertedGVMutableArray : public GVMutableArray {
+ private:
+ GVMutableArrayPtr varray_;
+ const CPPType &from_type_;
+ ConversionFunctions old_to_new_conversions_;
+ ConversionFunctions new_to_old_conversions_;
+
+ public:
+ GVMutableArray_For_ConvertedGVMutableArray(GVMutableArrayPtr varray,
+ const CPPType &to_type,
+ const DataTypeConversions &conversions)
+ : GVMutableArray(to_type, varray->size()),
+ varray_(std::move(varray)),
+ from_type_(varray_->type())
+ {
+ old_to_new_conversions_ = *conversions.get_conversion_functions(from_type_, to_type);
+ new_to_old_conversions_ = *conversions.get_conversion_functions(to_type, from_type_);
+ }
+
+ private:
+ void get_impl(const int64_t index, void *r_value) const override
+ {
+ BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer);
+ varray_->get(index, buffer);
+ old_to_new_conversions_.convert_single_to_initialized(buffer, r_value);
+ from_type_.destruct(buffer);
+ }
+
+ void get_to_uninitialized_impl(const int64_t index, void *r_value) const override
+ {
+ BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer);
+ varray_->get(index, buffer);
+ old_to_new_conversions_.convert_single_to_uninitialized(buffer, r_value);
+ from_type_.destruct(buffer);
+ }
+
+ void set_by_move_impl(const int64_t index, void *value) override
+ {
+ BUFFER_FOR_CPP_TYPE_VALUE(from_type_, buffer);
+ new_to_old_conversions_.convert_single_to_uninitialized(value, buffer);
+ varray_->set_by_relocate(index, buffer);
+ }
+};
+
+fn::GVArrayPtr DataTypeConversions::try_convert(fn::GVArrayPtr varray,
+ const CPPType &to_type) const
+{
+ const CPPType &from_type = varray->type();
+ if (from_type == to_type) {
+ return varray;
+ }
+ if (!this->is_convertible(from_type, to_type)) {
+ return {};
+ }
+ return std::make_unique<GVArray_For_ConvertedGVArray>(std::move(varray), to_type, *this);
+}
+
+fn::GVMutableArrayPtr DataTypeConversions::try_convert(fn::GVMutableArrayPtr varray,
+ const CPPType &to_type) const
+{
+ const CPPType &from_type = varray->type();
+ if (from_type == to_type) {
+ return varray;
+ }
+ if (!this->is_convertible(from_type, to_type)) {
+ return {};
+ }
+ return std::make_unique<GVMutableArray_For_ConvertedGVMutableArray>(
+ std::move(varray), to_type, *this);
+}
+
+} // namespace blender::nodes
diff --git a/source/blender/nodes/shader/node_shader_tree.c b/source/blender/nodes/shader/node_shader_tree.c
index 3fb4d10979d..5ec982c4e7f 100644
--- a/source/blender/nodes/shader/node_shader_tree.c
+++ b/source/blender/nodes/shader/node_shader_tree.c
@@ -184,6 +184,12 @@ static bool shader_validate_link(bNodeTree *UNUSED(ntree), bNodeLink *link)
return true;
}
+static bool shader_node_tree_socket_type_valid(eNodeSocketDatatype socket_type,
+ bNodeTreeType *UNUSED(ntreetype))
+{
+ return ELEM(socket_type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_SHADER);
+}
+
bNodeTreeType *ntreeType_Shader;
void register_node_tree_type_sh(void)
@@ -205,6 +211,7 @@ void register_node_tree_type_sh(void)
tt->poll = shader_tree_poll;
tt->get_from_context = shader_get_from_context;
tt->validate_link = shader_validate_link;
+ tt->valid_socket_type = shader_node_tree_socket_type_valid;
tt->rna_ext.srna = &RNA_ShaderNodeTree;
@@ -557,12 +564,14 @@ static bool ntree_shader_has_displacement(bNodeTree *ntree,
/* Non-cycles node is used as an output. */
return false;
}
+
if ((displacement->link != NULL) && !(displacement->link->flag & NODE_LINK_MUTED)) {
*r_node = displacement->link->fromnode;
*r_socket = displacement->link->fromsock;
*r_link = displacement->link;
+ return true;
}
- return displacement->link != NULL;
+ return false;
}
static void ntree_shader_relink_node_normal(bNodeTree *ntree,
@@ -1028,30 +1037,3 @@ void ntreeShaderEndExecTree(bNodeTreeExec *exec)
ntree->execdata = NULL;
}
}
-
-/* TODO: left over from Blender Internal, could reuse for new texture nodes. */
-bool ntreeShaderExecTree(bNodeTree *ntree, int thread)
-{
- ShaderCallData scd;
- bNodeThreadStack *nts = NULL;
- bNodeTreeExec *exec = ntree->execdata;
- int compat;
-
- /* ensure execdata is only initialized once */
- if (!exec) {
- BLI_thread_lock(LOCK_NODES);
- if (!ntree->execdata) {
- ntree->execdata = ntreeShaderBeginExecTree(ntree);
- }
- BLI_thread_unlock(LOCK_NODES);
-
- exec = ntree->execdata;
- }
-
- nts = ntreeGetThreadStack(exec, thread);
- compat = ntreeExecThreadNodes(exec, nts, &scd, thread);
- ntreeReleaseThreadStack(nts);
-
- /* if compat is zero, it has been using non-compatible nodes */
- return compat;
-}
diff --git a/source/blender/nodes/shader/node_shader_util.c b/source/blender/nodes/shader/node_shader_util.c
index 1a2405e021f..abc2c7008c7 100644
--- a/source/blender/nodes/shader/node_shader_util.c
+++ b/source/blender/nodes/shader/node_shader_util.c
@@ -27,14 +27,24 @@
#include "node_exec.h"
-bool sh_node_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree)
+bool sh_node_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree, const char **r_disabled_hint)
{
- return STREQ(ntree->idname, "ShaderNodeTree");
+ if (!STREQ(ntree->idname, "ShaderNodeTree")) {
+ *r_disabled_hint = "Not a shader node tree";
+ return false;
+ }
+ return true;
}
-static bool sh_fn_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree)
+static bool sh_fn_poll_default(bNodeType *UNUSED(ntype),
+ bNodeTree *ntree,
+ const char **r_disabled_hint)
{
- return STREQ(ntree->idname, "ShaderNodeTree") || STREQ(ntree->idname, "GeometryNodeTree");
+ if (!STREQ(ntree->idname, "ShaderNodeTree") && !STREQ(ntree->idname, "GeometryNodeTree")) {
+ *r_disabled_hint = "Not a shader or geometry node tree";
+ return false;
+ }
+ return true;
}
void sh_node_type_base(
@@ -322,3 +332,17 @@ void node_shader_gpu_tex_mapping(GPUMaterial *mat,
}
}
}
+
+void get_XYZ_to_RGB_for_gpu(XYZ_to_RGB *data)
+{
+ const float *xyz_to_rgb = IMB_colormanagement_get_xyz_to_rgb();
+ data->r[0] = xyz_to_rgb[0];
+ data->r[1] = xyz_to_rgb[3];
+ data->r[2] = xyz_to_rgb[6];
+ data->g[0] = xyz_to_rgb[1];
+ data->g[1] = xyz_to_rgb[4];
+ data->g[2] = xyz_to_rgb[7];
+ data->b[0] = xyz_to_rgb[2];
+ data->b[1] = xyz_to_rgb[5];
+ data->b[2] = xyz_to_rgb[8];
+}
diff --git a/source/blender/nodes/shader/node_shader_util.h b/source/blender/nodes/shader/node_shader_util.h
index 91454c3c982..dc44f0fa98f 100644
--- a/source/blender/nodes/shader/node_shader_util.h
+++ b/source/blender/nodes/shader/node_shader_util.h
@@ -80,7 +80,9 @@
extern "C" {
#endif
-bool sh_node_poll_default(struct bNodeType *ntype, struct bNodeTree *ntree);
+bool sh_node_poll_default(struct bNodeType *ntype,
+ struct bNodeTree *ntree,
+ const char **r_disabled_hint);
void sh_node_type_base(
struct bNodeType *ntype, int type, const char *name, short nclass, short flag);
void sh_fn_node_type_base(
@@ -93,6 +95,11 @@ typedef struct ShaderCallData {
int dummy;
} ShaderCallData;
+typedef struct XYZ_to_RGB /* Transposed #imbuf_xyz_to_rgb, passed as 3x vec3. */
+{
+ float r[3], g[3], b[3];
+} XYZ_to_RGB;
+
void nodestack_get_vec(float *in, short type_in, bNodeStack *ns);
void node_gpu_stack_from_data(struct GPUNodeStack *gs, int type, struct bNodeStack *ns);
@@ -111,6 +118,7 @@ void node_shader_gpu_tex_mapping(struct GPUMaterial *mat,
void ntreeExecGPUNodes(struct bNodeTreeExec *exec,
struct GPUMaterial *mat,
struct bNode *output_node);
+void get_XYZ_to_RGB_for_gpu(XYZ_to_RGB *data);
#ifdef __cplusplus
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_curves.c b/source/blender/nodes/shader/nodes/node_shader_curves.cc
index 42299a193e2..f1d5040a292 100644
--- a/source/blender/nodes/shader/nodes/node_shader_curves.c
+++ b/source/blender/nodes/shader/nodes/node_shader_curves.cc
@@ -47,7 +47,7 @@ static void node_shader_exec_curve_vec(void *UNUSED(data),
/* stack order input: vec */
/* stack order output: vec */
nodestack_get_vec(vec, SOCK_VECTOR, in[1]);
- BKE_curvemapping_evaluate3F(node->storage, out[0]->vec, vec);
+ BKE_curvemapping_evaluate3F((CurveMapping *)node->storage, out[0]->vec, vec);
}
static void node_shader_init_curve_vec(bNodeTree *UNUSED(ntree), bNode *node)
@@ -64,7 +64,7 @@ static int gpu_shader_curve_vec(GPUMaterial *mat,
float *array, layer;
int size;
- CurveMapping *cumap = node->storage;
+ CurveMapping *cumap = (CurveMapping *)node->storage;
BKE_curvemapping_table_RGBA(cumap, &array, &size);
GPUNodeLink *tex = GPU_color_band(mat, size, array, &layer);
@@ -104,17 +104,65 @@ static int gpu_shader_curve_vec(GPUMaterial *mat,
GPU_uniform(ext_xyz[2]));
}
+class CurveVecFunction : public blender::fn::MultiFunction {
+ private:
+ const CurveMapping &cumap_;
+
+ public:
+ CurveVecFunction(const CurveMapping &cumap) : cumap_(cumap)
+ {
+ static blender::fn::MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static blender::fn::MFSignature create_signature()
+ {
+ blender::fn::MFSignatureBuilder signature{"Curve Vec"};
+ signature.single_input<float>("Fac");
+ signature.single_input<blender::float3>("Vector");
+ signature.single_output<blender::float3>("Vector");
+ return signature.build();
+ }
+
+ void call(blender::IndexMask mask,
+ blender::fn::MFParams params,
+ blender::fn::MFContext UNUSED(context)) const override
+ {
+ const blender::VArray<float> &fac = params.readonly_single_input<float>(0, "Fac");
+ const blender::VArray<blender::float3> &vec_in = params.readonly_single_input<blender::float3>(
+ 1, "Vector");
+ blender::MutableSpan<blender::float3> vec_out =
+ params.uninitialized_single_output<blender::float3>(2, "Vector");
+
+ for (int64_t i : mask) {
+ BKE_curvemapping_evaluate3F(&cumap_, vec_out[i], vec_in[i]);
+ if (fac[i] != 1.0f) {
+ interp_v3_v3v3(vec_out[i], vec_in[i], vec_out[i], fac[i]);
+ }
+ }
+ }
+};
+
+static void sh_node_curve_vec_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
+{
+ bNode &bnode = builder.bnode();
+ CurveMapping *cumap = (CurveMapping *)bnode.storage;
+ BKE_curvemapping_init(cumap);
+ builder.construct_and_set_matching_fn<CurveVecFunction>(*cumap);
+}
+
void register_node_type_sh_curve_vec(void)
{
static bNodeType ntype;
- sh_node_type_base(&ntype, SH_NODE_CURVE_VEC, "Vector Curves", NODE_CLASS_OP_VECTOR, 0);
+ sh_fn_node_type_base(&ntype, SH_NODE_CURVE_VEC, "Vector Curves", NODE_CLASS_OP_VECTOR, 0);
node_type_socket_templates(&ntype, sh_node_curve_vec_in, sh_node_curve_vec_out);
node_type_init(&ntype, node_shader_init_curve_vec);
node_type_size_preset(&ntype, NODE_SIZE_LARGE);
node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves);
- node_type_exec(&ntype, node_initexec_curves, NULL, node_shader_exec_curve_vec);
+ node_type_exec(&ntype, node_initexec_curves, nullptr, node_shader_exec_curve_vec);
node_type_gpu(&ntype, gpu_shader_curve_vec);
+ ntype.expand_in_mf_network = sh_node_curve_vec_expand_in_mf_network;
nodeRegisterType(&ntype);
}
@@ -145,7 +193,7 @@ static void node_shader_exec_curve_rgb(void *UNUSED(data),
/* stack order output: vec */
nodestack_get_vec(&fac, SOCK_FLOAT, in[0]);
nodestack_get_vec(vec, SOCK_VECTOR, in[1]);
- BKE_curvemapping_evaluateRGBF(node->storage, out[0]->vec, vec);
+ BKE_curvemapping_evaluateRGBF((CurveMapping *)node->storage, out[0]->vec, vec);
if (fac != 1.0f) {
interp_v3_v3v3(out[0]->vec, vec, out[0]->vec, fac);
}
@@ -166,7 +214,7 @@ static int gpu_shader_curve_rgb(GPUMaterial *mat,
int size;
bool use_opti = true;
- CurveMapping *cumap = node->storage;
+ CurveMapping *cumap = (CurveMapping *)node->storage;
BKE_curvemapping_init(cumap);
BKE_curvemapping_table_RGBA(cumap, &array, &size);
@@ -230,17 +278,65 @@ static int gpu_shader_curve_rgb(GPUMaterial *mat,
GPU_uniform(ext_rgba[3]));
}
+class CurveRGBFunction : public blender::fn::MultiFunction {
+ private:
+ const CurveMapping &cumap_;
+
+ public:
+ CurveRGBFunction(const CurveMapping &cumap) : cumap_(cumap)
+ {
+ static blender::fn::MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static blender::fn::MFSignature create_signature()
+ {
+ blender::fn::MFSignatureBuilder signature{"Curve RGB"};
+ signature.single_input<float>("Fac");
+ signature.single_input<blender::ColorGeometry4f>("Color");
+ signature.single_output<blender::ColorGeometry4f>("Color");
+ return signature.build();
+ }
+
+ void call(blender::IndexMask mask,
+ blender::fn::MFParams params,
+ blender::fn::MFContext UNUSED(context)) const override
+ {
+ const blender::VArray<float> &fac = params.readonly_single_input<float>(0, "Fac");
+ const blender::VArray<blender::ColorGeometry4f> &col_in =
+ params.readonly_single_input<blender::ColorGeometry4f>(1, "Color");
+ blender::MutableSpan<blender::ColorGeometry4f> col_out =
+ params.uninitialized_single_output<blender::ColorGeometry4f>(2, "Color");
+
+ for (int64_t i : mask) {
+ BKE_curvemapping_evaluateRGBF(&cumap_, col_out[i], col_in[i]);
+ if (fac[i] != 1.0f) {
+ interp_v3_v3v3(col_out[i], col_in[i], col_out[i], fac[i]);
+ }
+ }
+ }
+};
+
+static void sh_node_curve_rgb_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
+{
+ bNode &bnode = builder.bnode();
+ CurveMapping *cumap = (CurveMapping *)bnode.storage;
+ BKE_curvemapping_init(cumap);
+ builder.construct_and_set_matching_fn<CurveRGBFunction>(*cumap);
+}
+
void register_node_type_sh_curve_rgb(void)
{
static bNodeType ntype;
- sh_node_type_base(&ntype, SH_NODE_CURVE_RGB, "RGB Curves", NODE_CLASS_OP_COLOR, 0);
+ sh_fn_node_type_base(&ntype, SH_NODE_CURVE_RGB, "RGB Curves", NODE_CLASS_OP_COLOR, 0);
node_type_socket_templates(&ntype, sh_node_curve_rgb_in, sh_node_curve_rgb_out);
node_type_init(&ntype, node_shader_init_curve_rgb);
node_type_size_preset(&ntype, NODE_SIZE_LARGE);
node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves);
- node_type_exec(&ntype, node_initexec_curves, NULL, node_shader_exec_curve_rgb);
+ node_type_exec(&ntype, node_initexec_curves, nullptr, node_shader_exec_curve_rgb);
node_type_gpu(&ntype, gpu_shader_curve_rgb);
+ ntype.expand_in_mf_network = sh_node_curve_rgb_expand_in_mf_network;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_map_range.cc b/source/blender/nodes/shader/nodes/node_shader_map_range.cc
index 3b4ea3d1bdf..ad7abd9d491 100644
--- a/source/blender/nodes/shader/nodes/node_shader_map_range.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_map_range.cc
@@ -23,6 +23,8 @@
#include "node_shader_util.h"
+#include "BLI_math_base_safe.h"
+
/* **************** Map Range ******************** */
static bNodeSocketTemplate sh_node_map_range_in[] = {
{SOCK_FLOAT, N_("Value"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_NONE},
@@ -88,6 +90,19 @@ static int gpu_shader_map_range(GPUMaterial *mat,
return ret;
}
+static void map_range_signature(blender::fn::MFSignatureBuilder *signature, bool use_steps)
+{
+ signature->single_input<float>("Value");
+ signature->single_input<float>("From Min");
+ signature->single_input<float>("From Max");
+ signature->single_input<float>("To Min");
+ signature->single_input<float>("To Max");
+ if (use_steps) {
+ signature->single_input<float>("Steps");
+ }
+ signature->single_output<float>("Result");
+}
+
class MapRangeFunction : public blender::fn::MultiFunction {
private:
bool clamp_;
@@ -102,12 +117,7 @@ class MapRangeFunction : public blender::fn::MultiFunction {
static blender::fn::MFSignature create_signature()
{
blender::fn::MFSignatureBuilder signature{"Map Range"};
- signature.single_input<float>("Value");
- signature.single_input<float>("From Min");
- signature.single_input<float>("From Max");
- signature.single_input<float>("To Min");
- signature.single_input<float>("To Max");
- signature.single_output<float>("Result");
+ map_range_signature(&signature, false);
return signature.build();
}
@@ -136,25 +146,163 @@ class MapRangeFunction : public blender::fn::MultiFunction {
}
};
+class MapRangeSteppedFunction : public blender::fn::MultiFunction {
+ private:
+ bool clamp_;
+
+ public:
+ MapRangeSteppedFunction(bool clamp) : clamp_(clamp)
+ {
+ static blender::fn::MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static blender::fn::MFSignature create_signature()
+ {
+ blender::fn::MFSignatureBuilder signature{"Map Range Stepped"};
+ map_range_signature(&signature, true);
+ return signature.build();
+ }
+
+ void call(blender::IndexMask mask,
+ blender::fn::MFParams params,
+ blender::fn::MFContext UNUSED(context)) const override
+ {
+ const blender::VArray<float> &values = params.readonly_single_input<float>(0, "Value");
+ const blender::VArray<float> &from_min = params.readonly_single_input<float>(1, "From Min");
+ const blender::VArray<float> &from_max = params.readonly_single_input<float>(2, "From Max");
+ const blender::VArray<float> &to_min = params.readonly_single_input<float>(3, "To Min");
+ const blender::VArray<float> &to_max = params.readonly_single_input<float>(4, "To Max");
+ const blender::VArray<float> &steps = params.readonly_single_input<float>(5, "Steps");
+ blender::MutableSpan<float> results = params.uninitialized_single_output<float>(6, "Result");
+
+ for (int64_t i : mask) {
+ float factor = safe_divide(values[i] - from_min[i], from_max[i] - from_min[i]);
+ factor = safe_divide(floorf(factor * (steps[i] + 1.0f)), steps[i]);
+ results[i] = to_min[i] + factor * (to_max[i] - to_min[i]);
+ }
+
+ if (clamp_) {
+ for (int64_t i : mask) {
+ results[i] = (to_min[i] > to_max[i]) ? clamp_f(results[i], to_max[i], to_min[i]) :
+ clamp_f(results[i], to_min[i], to_max[i]);
+ }
+ }
+ }
+};
+
+class MapRangeSmoothstepFunction : public blender::fn::MultiFunction {
+ public:
+ MapRangeSmoothstepFunction()
+ {
+ static blender::fn::MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static blender::fn::MFSignature create_signature()
+ {
+ blender::fn::MFSignatureBuilder signature{"Map Range Smoothstep"};
+ map_range_signature(&signature, false);
+ return signature.build();
+ }
+
+ void call(blender::IndexMask mask,
+ blender::fn::MFParams params,
+ blender::fn::MFContext UNUSED(context)) const override
+ {
+ const blender::VArray<float> &values = params.readonly_single_input<float>(0, "Value");
+ const blender::VArray<float> &from_min = params.readonly_single_input<float>(1, "From Min");
+ const blender::VArray<float> &from_max = params.readonly_single_input<float>(2, "From Max");
+ const blender::VArray<float> &to_min = params.readonly_single_input<float>(3, "To Min");
+ const blender::VArray<float> &to_max = params.readonly_single_input<float>(4, "To Max");
+ blender::MutableSpan<float> results = params.uninitialized_single_output<float>(5, "Result");
+
+ for (int64_t i : mask) {
+ float factor = safe_divide(values[i] - from_min[i], from_max[i] - from_min[i]);
+ factor = std::clamp(factor, 0.0f, 1.0f);
+ factor = (3.0f - 2.0f * factor) * (factor * factor);
+ results[i] = to_min[i] + factor * (to_max[i] - to_min[i]);
+ }
+ }
+};
+
+class MapRangeSmootherstepFunction : public blender::fn::MultiFunction {
+ public:
+ MapRangeSmootherstepFunction()
+ {
+ static blender::fn::MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static blender::fn::MFSignature create_signature()
+ {
+ blender::fn::MFSignatureBuilder signature{"Map Range Smoothstep"};
+ map_range_signature(&signature, false);
+ return signature.build();
+ }
+
+ void call(blender::IndexMask mask,
+ blender::fn::MFParams params,
+ blender::fn::MFContext UNUSED(context)) const override
+ {
+ const blender::VArray<float> &values = params.readonly_single_input<float>(0, "Value");
+ const blender::VArray<float> &from_min = params.readonly_single_input<float>(1, "From Min");
+ const blender::VArray<float> &from_max = params.readonly_single_input<float>(2, "From Max");
+ const blender::VArray<float> &to_min = params.readonly_single_input<float>(3, "To Min");
+ const blender::VArray<float> &to_max = params.readonly_single_input<float>(4, "To Max");
+ blender::MutableSpan<float> results = params.uninitialized_single_output<float>(5, "Result");
+
+ for (int64_t i : mask) {
+ float factor = safe_divide(values[i] - from_min[i], from_max[i] - from_min[i]);
+ factor = std::clamp(factor, 0.0f, 1.0f);
+ factor = factor * factor * factor * (factor * (factor * 6.0f - 15.0f) + 10.0f);
+ results[i] = to_min[i] + factor * (to_max[i] - to_min[i]);
+ }
+ }
+};
+
static void sh_node_map_range_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
{
bNode &bnode = builder.bnode();
bool clamp = bnode.custom1 != 0;
int interpolation_type = bnode.custom2;
- if (interpolation_type == NODE_MAP_RANGE_LINEAR) {
- static MapRangeFunction fn_with_clamp{true};
- static MapRangeFunction fn_without_clamp{false};
-
- if (clamp) {
- builder.set_matching_fn(fn_with_clamp);
+ switch (interpolation_type) {
+ case NODE_MAP_RANGE_LINEAR: {
+ if (clamp) {
+ static MapRangeFunction fn_with_clamp{true};
+ builder.set_matching_fn(fn_with_clamp);
+ }
+ else {
+ static MapRangeFunction fn_without_clamp{false};
+ builder.set_matching_fn(fn_without_clamp);
+ }
+ break;
}
- else {
- builder.set_matching_fn(fn_without_clamp);
+ case NODE_MAP_RANGE_STEPPED: {
+ if (clamp) {
+ static MapRangeSteppedFunction fn_stepped_with_clamp{true};
+ builder.set_matching_fn(fn_stepped_with_clamp);
+ }
+ else {
+ static MapRangeSteppedFunction fn_stepped_without_clamp{false};
+ builder.set_matching_fn(fn_stepped_without_clamp);
+ }
+ break;
}
- }
- else {
- builder.set_not_implemented();
+ case NODE_MAP_RANGE_SMOOTHSTEP: {
+ static MapRangeSmoothstepFunction smoothstep;
+ builder.set_matching_fn(smoothstep);
+ break;
+ }
+ case NODE_MAP_RANGE_SMOOTHERSTEP: {
+ static MapRangeSmootherstepFunction smootherstep;
+ builder.set_matching_fn(smootherstep);
+ break;
+ }
+ default:
+ builder.set_not_implemented();
+ break;
}
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_mixRgb.c b/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc
index 090c6216224..52f2adb10dd 100644
--- a/source/blender/nodes/shader/nodes/node_shader_mixRgb.c
+++ b/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc
@@ -61,35 +61,60 @@ static void node_shader_exec_mix_rgb(void *UNUSED(data),
copy_v3_v3(out[0]->vec, col);
}
+static const char *gpu_shader_get_name(int mode)
+{
+ switch (mode) {
+ case MA_RAMP_BLEND:
+ return "mix_blend";
+ case MA_RAMP_ADD:
+ return "mix_add";
+ case MA_RAMP_MULT:
+ return "mix_mult";
+ case MA_RAMP_SUB:
+ return "mix_sub";
+ case MA_RAMP_SCREEN:
+ return "mix_screen";
+ case MA_RAMP_DIV:
+ return "mix_div";
+ case MA_RAMP_DIFF:
+ return "mix_diff";
+ case MA_RAMP_DARK:
+ return "mix_dark";
+ case MA_RAMP_LIGHT:
+ return "mix_light";
+ case MA_RAMP_OVERLAY:
+ return "mix_overlay";
+ case MA_RAMP_DODGE:
+ return "mix_dodge";
+ case MA_RAMP_BURN:
+ return "mix_burn";
+ case MA_RAMP_HUE:
+ return "mix_hue";
+ case MA_RAMP_SAT:
+ return "mix_sat";
+ case MA_RAMP_VAL:
+ return "mix_val";
+ case MA_RAMP_COLOR:
+ return "mix_color";
+ case MA_RAMP_SOFT:
+ return "mix_soft";
+ case MA_RAMP_LINEAR:
+ return "mix_linear";
+ }
+
+ return nullptr;
+}
+
static int gpu_shader_mix_rgb(GPUMaterial *mat,
bNode *node,
bNodeExecData *UNUSED(execdata),
GPUNodeStack *in,
GPUNodeStack *out)
{
- static const char *names[] = {
- "mix_blend",
- "mix_add",
- "mix_mult",
- "mix_sub",
- "mix_screen",
- "mix_div",
- "mix_diff",
- "mix_dark",
- "mix_light",
- "mix_overlay",
- "mix_dodge",
- "mix_burn",
- "mix_hue",
- "mix_sat",
- "mix_val",
- "mix_color",
- "mix_soft",
- "mix_linear",
- };
+ const char *name = gpu_shader_get_name(node->custom1);
- if (node->custom1 < ARRAY_SIZE(names) && names[node->custom1]) {
- int ret = GPU_stack_link(mat, node, names[node->custom1], in, out);
+ if (name != nullptr) {
+ int ret = GPU_stack_link(mat, node, name, in, out);
if (ret && node->custom2 & SHD_MIXRGB_CLAMP) {
const float min[3] = {0.0f, 0.0f, 0.0f};
const float max[3] = {1.0f, 1.0f, 1.0f};
diff --git a/source/blender/nodes/shader/nodes/node_shader_output_aov.c b/source/blender/nodes/shader/nodes/node_shader_output_aov.c
index 403b3e6d9d6..7e7e1b703f1 100644
--- a/source/blender/nodes/shader/nodes/node_shader_output_aov.c
+++ b/source/blender/nodes/shader/nodes/node_shader_output_aov.c
@@ -43,8 +43,9 @@ static int node_shader_gpu_output_aov(GPUMaterial *mat,
{
GPUNodeLink *outlink;
NodeShaderOutputAOV *aov = (NodeShaderOutputAOV *)node->storage;
- /* Keep in sync with `renderpass_lib.glsl#render_pass_aov_hash`. */
- unsigned int hash = BLI_hash_string(aov->name) & ~1;
+ /* Keep in sync with `renderpass_lib.glsl#render_pass_aov_hash` and
+ * `EEVEE_renderpasses_aov_hash`. */
+ unsigned int hash = BLI_hash_string(aov->name) << 1;
GPU_stack_link(mat, node, "node_output_aov", in, out, &outlink);
GPU_material_add_output_link_aov(mat, outlink, hash);
diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc
index 8ca4a6bab5f..a7239154633 100644
--- a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc
@@ -70,7 +70,7 @@ class SeparateRGBFunction : public blender::fn::MultiFunction {
static blender::fn::MFSignature create_signature()
{
blender::fn::MFSignatureBuilder signature{"Separate RGB"};
- signature.single_input<blender::Color4f>("Color");
+ signature.single_input<blender::ColorGeometry4f>("Color");
signature.single_output<float>("R");
signature.single_output<float>("G");
signature.single_output<float>("B");
@@ -81,14 +81,14 @@ class SeparateRGBFunction : public blender::fn::MultiFunction {
blender::fn::MFParams params,
blender::fn::MFContext UNUSED(context)) const override
{
- const blender::VArray<blender::Color4f> &colors =
- params.readonly_single_input<blender::Color4f>(0, "Color");
+ const blender::VArray<blender::ColorGeometry4f> &colors =
+ params.readonly_single_input<blender::ColorGeometry4f>(0, "Color");
blender::MutableSpan<float> rs = params.uninitialized_single_output<float>(1, "R");
blender::MutableSpan<float> gs = params.uninitialized_single_output<float>(2, "G");
blender::MutableSpan<float> bs = params.uninitialized_single_output<float>(3, "B");
for (int64_t i : mask) {
- blender::Color4f color = colors[i];
+ blender::ColorGeometry4f color = colors[i];
rs[i] = color.r;
gs[i] = color.g;
bs[i] = color.b;
@@ -155,8 +155,9 @@ static int gpu_shader_combrgb(GPUMaterial *mat,
static void sh_node_combrgb_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
{
- static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, blender::Color4f> fn{
- "Combine RGB", [](float r, float g, float b) { return blender::Color4f(r, g, b, 1.0f); }};
+ static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, blender::ColorGeometry4f> fn{
+ "Combine RGB",
+ [](float r, float g, float b) { return blender::ColorGeometry4f(r, g, b, 1.0f); }};
builder.set_matching_fn(fn);
}
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 9ef05d781bd..5dc11c4df00 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_sky.c
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_sky.c
@@ -60,11 +60,6 @@ typedef struct SkyModelPreetham {
float radiance[3];
} SkyModelPreetham;
-typedef struct XYZ_to_RGB /* transposed imbuf_xyz_to_rgb, passed as 3x vec3 */
-{
- float r[3], g[3], b[3];
-} XYZ_to_RGB;
-
static float sky_perez_function(const float *lam, float theta, float gamma)
{
float ctheta = cosf(theta);
@@ -119,20 +114,6 @@ static void sky_precompute_old(SkyModelPreetham *sunsky, const float sun_angles[
sunsky->radiance[2] /= sky_perez_function(sunsky->config_y, 0, theta);
}
-static void get_XYZ_to_RGB_for_gpu(XYZ_to_RGB *data)
-{
- const float *xyz_to_rgb = IMB_colormangement_get_xyz_to_rgb();
- data->r[0] = xyz_to_rgb[0];
- data->r[1] = xyz_to_rgb[3];
- data->r[2] = xyz_to_rgb[6];
- data->g[0] = xyz_to_rgb[1];
- data->g[1] = xyz_to_rgb[4];
- data->g[2] = xyz_to_rgb[7];
- data->b[0] = xyz_to_rgb[2];
- data->b[1] = xyz_to_rgb[5];
- data->b[2] = xyz_to_rgb[8];
-}
-
static int node_shader_gpu_tex_sky(GPUMaterial *mat,
bNode *node,
bNodeExecData *UNUSED(execdata),
diff --git a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc
index 90e8161c09f..5b2eb300aac 100644
--- a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc
@@ -140,7 +140,7 @@ class ColorBandFunction : public blender::fn::MultiFunction {
{
blender::fn::MFSignatureBuilder signature{"Color Band"};
signature.single_input<float>("Value");
- signature.single_output<blender::Color4f>("Color");
+ signature.single_output<blender::ColorGeometry4f>("Color");
signature.single_output<float>("Alpha");
return signature.build();
}
@@ -150,12 +150,12 @@ class ColorBandFunction : public blender::fn::MultiFunction {
blender::fn::MFContext UNUSED(context)) const override
{
const blender::VArray<float> &values = params.readonly_single_input<float>(0, "Value");
- blender::MutableSpan<blender::Color4f> colors =
- params.uninitialized_single_output<blender::Color4f>(1, "Color");
+ blender::MutableSpan<blender::ColorGeometry4f> colors =
+ params.uninitialized_single_output<blender::ColorGeometry4f>(1, "Color");
blender::MutableSpan<float> alphas = params.uninitialized_single_output<float>(2, "Alpha");
for (int64_t i : mask) {
- blender::Color4f color;
+ blender::ColorGeometry4f color;
BKE_colorband_evaluate(&color_band_, values[i], color);
colors[i] = color;
alphas[i] = color.a;
diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc
index 3a9822fbc8e..419a11201aa 100644
--- a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc
@@ -23,6 +23,8 @@
#include "node_shader_util.h"
+#include "NOD_math_functions.hh"
+
/* **************** VECTOR MATH ******************** */
static bNodeSocketTemplate sh_node_vector_math_in[] = {
{SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 1.0f, -10000.0f, 10000.0f, PROP_NONE},
@@ -92,6 +94,8 @@ static const char *gpu_shader_get_name(int mode)
return "vector_math_refract";
case NODE_VECTOR_MATH_FACEFORWARD:
return "vector_math_faceforward";
+ case NODE_VECTOR_MATH_MULTIPLY_ADD:
+ return "vector_math_multiply_add";
}
return nullptr;
@@ -132,8 +136,11 @@ static void node_shader_update_vector_math(bNodeTree *UNUSED(ntree), bNode *node
NODE_VECTOR_MATH_ABSOLUTE,
NODE_VECTOR_MATH_FRACTION,
NODE_VECTOR_MATH_NORMALIZE));
- nodeSetSocketAvailability(
- sockC, ELEM(node->custom1, NODE_VECTOR_MATH_WRAP, NODE_VECTOR_MATH_FACEFORWARD));
+ nodeSetSocketAvailability(sockC,
+ ELEM(node->custom1,
+ NODE_VECTOR_MATH_WRAP,
+ NODE_VECTOR_MATH_FACEFORWARD,
+ NODE_VECTOR_MATH_MULTIPLY_ADD));
nodeSetSocketAvailability(sockScale,
ELEM(node->custom1, NODE_VECTOR_MATH_SCALE, NODE_VECTOR_MATH_REFRACT));
nodeSetSocketAvailability(sockVector,
@@ -152,6 +159,10 @@ static void node_shader_update_vector_math(bNodeTree *UNUSED(ntree), bNode *node
node_sock_label_clear(sockC);
node_sock_label_clear(sockScale);
switch (node->custom1) {
+ case NODE_VECTOR_MATH_MULTIPLY_ADD:
+ node_sock_label(sockB, "Multiplier");
+ node_sock_label(sockC, "Addend");
+ break;
case NODE_VECTOR_MATH_FACEFORWARD:
node_sock_label(sockB, "Incident");
node_sock_label(sockC, "Reference");
@@ -177,117 +188,79 @@ static const blender::fn::MultiFunction &get_multi_function(
{
using blender::float3;
- const int mode = builder.bnode().custom1;
- switch (mode) {
- case NODE_VECTOR_MATH_ADD: {
- static blender::fn::CustomMF_SI_SI_SO<float3, float3, float3> fn{
- "Add", [](float3 a, float3 b) { return a + b; }};
- return fn;
- }
- case NODE_VECTOR_MATH_SUBTRACT: {
- static blender::fn::CustomMF_SI_SI_SO<float3, float3, float3> fn{
- "Subtract", [](float3 a, float3 b) { return a - b; }};
- return fn;
- }
- case NODE_VECTOR_MATH_MULTIPLY: {
- static blender::fn::CustomMF_SI_SI_SO<float3, float3, float3> fn{
- "Multiply", [](float3 a, float3 b) { return a * b; }};
- return fn;
- }
- case NODE_VECTOR_MATH_DIVIDE: {
- static blender::fn::CustomMF_SI_SI_SO<float3, float3, float3> fn{
- "Divide", [](float3 a, float3 b) { return float3::safe_divide(a, b); }};
- return fn;
- }
- case NODE_VECTOR_MATH_CROSS_PRODUCT: {
- static blender::fn::CustomMF_SI_SI_SO<float3, float3, float3> fn{
- "Cross Product", float3::cross_high_precision};
- return fn;
- }
- case NODE_VECTOR_MATH_PROJECT: {
- static blender::fn::CustomMF_SI_SI_SO<float3, float3, float3> fn{"Project", float3::project};
- return fn;
- }
- case NODE_VECTOR_MATH_REFLECT: {
- static blender::fn::CustomMF_SI_SI_SO<float3, float3, float3> fn{
- "Reflect", [](float3 a, float3 b) { return a.reflected(b); }};
- return fn;
- }
- case NODE_VECTOR_MATH_DOT_PRODUCT: {
- static blender::fn::CustomMF_SI_SI_SO<float3, float3, float> fn{"Dot Product", float3::dot};
- return fn;
- }
- case NODE_VECTOR_MATH_DISTANCE: {
- static blender::fn::CustomMF_SI_SI_SO<float3, float3, float> fn{"Distance",
- float3::distance};
- return fn;
- }
- case NODE_VECTOR_MATH_LENGTH: {
- static blender::fn::CustomMF_SI_SO<float3, float> fn{"Length",
- [](float3 a) { return a.length(); }};
- return fn;
- }
- case NODE_VECTOR_MATH_SCALE: {
- static blender::fn::CustomMF_SI_SI_SO<float3, float, float3> fn{
- "Scale", [](float3 a, float factor) { return a * factor; }};
- return fn;
- }
- case NODE_VECTOR_MATH_NORMALIZE: {
- static blender::fn::CustomMF_SI_SO<float3, float3> fn{
- "Normalize", [](float3 a) { return a.normalized(); }};
- return fn;
- }
- case NODE_VECTOR_MATH_REFRACT: {
- static blender::fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{
- "Refract",
- [](float3 a, float3 b, float c) { return float3::refract(a, b.normalized(), c); }};
- return fn;
- }
- case NODE_VECTOR_MATH_FACEFORWARD: {
- static blender::fn::CustomMF_SI_SI_SI_SO<float3, float3, float3, float3> fn{
- "Faceforward", float3::faceforward};
- return fn;
- }
- case NODE_VECTOR_MATH_SNAP: {
- return builder.get_not_implemented_fn();
- }
- case NODE_VECTOR_MATH_FLOOR: {
- return builder.get_not_implemented_fn();
- }
- case NODE_VECTOR_MATH_CEIL: {
- return builder.get_not_implemented_fn();
- }
- case NODE_VECTOR_MATH_MODULO: {
- return builder.get_not_implemented_fn();
- }
- case NODE_VECTOR_MATH_FRACTION: {
- return builder.get_not_implemented_fn();
- }
- case NODE_VECTOR_MATH_ABSOLUTE: {
- return builder.get_not_implemented_fn();
- }
- case NODE_VECTOR_MATH_MINIMUM: {
- return builder.get_not_implemented_fn();
- }
- case NODE_VECTOR_MATH_MAXIMUM: {
- return builder.get_not_implemented_fn();
- }
- case NODE_VECTOR_MATH_WRAP: {
- return builder.get_not_implemented_fn();
- }
- case NODE_VECTOR_MATH_SINE: {
- return builder.get_not_implemented_fn();
- }
- case NODE_VECTOR_MATH_COSINE: {
- return builder.get_not_implemented_fn();
- }
- case NODE_VECTOR_MATH_TANGENT: {
- return builder.get_not_implemented_fn();
- }
- default:
- BLI_assert_unreachable();
- return builder.get_not_implemented_fn();
- };
+ NodeVectorMathOperation operation = NodeVectorMathOperation(builder.bnode().custom1);
+
+ const blender::fn::MultiFunction *multi_fn = nullptr;
+
+ blender::nodes::try_dispatch_float_math_fl3_fl3_to_fl3(
+ operation, [&](auto function, const blender::nodes::FloatMathOperationInfo &info) {
+ static blender::fn::CustomMF_SI_SI_SO<float3, float3, float3> fn{info.title_case_name,
+ function};
+ multi_fn = &fn;
+ });
+ if (multi_fn != nullptr) {
+ return *multi_fn;
+ }
+
+ blender::nodes::try_dispatch_float_math_fl3_fl3_fl3_to_fl3(
+ operation, [&](auto function, const blender::nodes::FloatMathOperationInfo &info) {
+ static blender::fn::CustomMF_SI_SI_SI_SO<float3, float3, float3, float3> fn{
+ info.title_case_name, function};
+ multi_fn = &fn;
+ });
+ if (multi_fn != nullptr) {
+ return *multi_fn;
+ }
+
+ blender::nodes::try_dispatch_float_math_fl3_fl3_fl_to_fl3(
+ operation, [&](auto function, const blender::nodes::FloatMathOperationInfo &info) {
+ static blender::fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{
+ info.title_case_name, function};
+ multi_fn = &fn;
+ });
+ if (multi_fn != nullptr) {
+ return *multi_fn;
+ }
+
+ blender::nodes::try_dispatch_float_math_fl3_fl3_to_fl(
+ operation, [&](auto function, const blender::nodes::FloatMathOperationInfo &info) {
+ static blender::fn::CustomMF_SI_SI_SO<float3, float3, float> fn{info.title_case_name,
+ function};
+ multi_fn = &fn;
+ });
+ if (multi_fn != nullptr) {
+ return *multi_fn;
+ }
+
+ blender::nodes::try_dispatch_float_math_fl3_fl_to_fl3(
+ operation, [&](auto function, const blender::nodes::FloatMathOperationInfo &info) {
+ static blender::fn::CustomMF_SI_SI_SO<float3, float, float3> fn{info.title_case_name,
+ function};
+ multi_fn = &fn;
+ });
+ if (multi_fn != nullptr) {
+ return *multi_fn;
+ }
+
+ blender::nodes::try_dispatch_float_math_fl3_to_fl3(
+ operation, [&](auto function, const blender::nodes::FloatMathOperationInfo &info) {
+ static blender::fn::CustomMF_SI_SO<float3, float3> fn{info.title_case_name, function};
+ multi_fn = &fn;
+ });
+ if (multi_fn != nullptr) {
+ return *multi_fn;
+ }
+
+ blender::nodes::try_dispatch_float_math_fl3_to_fl(
+ operation, [&](auto function, const blender::nodes::FloatMathOperationInfo &info) {
+ static blender::fn::CustomMF_SI_SO<float3, float> fn{info.title_case_name, function};
+ multi_fn = &fn;
+ });
+ if (multi_fn != nullptr) {
+ return *multi_fn;
+ }
+
+ return builder.get_not_implemented_fn();
}
static void sh_node_vector_math_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
diff --git a/source/blender/nodes/shader/nodes/node_shader_wavelength.c b/source/blender/nodes/shader/nodes/node_shader_wavelength.c
index 6b7e1399328..30f69557020 100644
--- a/source/blender/nodes/shader/nodes/node_shader_wavelength.c
+++ b/source/blender/nodes/shader/nodes/node_shader_wavelength.c
@@ -30,6 +30,33 @@ static bNodeSocketTemplate sh_node_wavelength_out[] = {
{-1, ""},
};
+static int node_shader_gpu_wavelength(GPUMaterial *mat,
+ bNode *node,
+ bNodeExecData *UNUSED(execdata),
+ GPUNodeStack *in,
+ GPUNodeStack *out)
+{
+ const int size = CM_TABLE + 1;
+ float *data = MEM_mallocN(sizeof(float) * size * 4, "cie_xyz texture");
+
+ wavelength_to_xyz_table(data, size);
+
+ float layer;
+ GPUNodeLink *ramp_texture = GPU_color_band(mat, size, data, &layer);
+ XYZ_to_RGB xyz_to_rgb;
+ get_XYZ_to_RGB_for_gpu(&xyz_to_rgb);
+ return GPU_stack_link(mat,
+ node,
+ "node_wavelength",
+ in,
+ out,
+ ramp_texture,
+ GPU_constant(&layer),
+ GPU_uniform(xyz_to_rgb.r),
+ GPU_uniform(xyz_to_rgb.g),
+ GPU_uniform(xyz_to_rgb.b));
+}
+
/* node type definition */
void register_node_type_sh_wavelength(void)
{
@@ -40,6 +67,7 @@ void register_node_type_sh_wavelength(void)
node_type_socket_templates(&ntype, sh_node_wavelength_in, sh_node_wavelength_out);
node_type_init(&ntype, NULL);
node_type_storage(&ntype, "", NULL, NULL);
+ node_type_gpu(&ntype, node_shader_gpu_wavelength);
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/texture/node_texture_tree.c b/source/blender/nodes/texture/node_texture_tree.c
index 48027dc847b..2ae722e3cd8 100644
--- a/source/blender/nodes/texture/node_texture_tree.c
+++ b/source/blender/nodes/texture/node_texture_tree.c
@@ -152,6 +152,12 @@ static void update(bNodeTree *ntree)
}
}
+static bool texture_node_tree_socket_type_valid(eNodeSocketDatatype socket_type,
+ bNodeTreeType *UNUSED(ntreetype))
+{
+ return ELEM(socket_type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA);
+}
+
bNodeTreeType *ntreeType_Texture;
void register_node_tree_type_tex(void)
@@ -171,6 +177,7 @@ void register_node_tree_type_tex(void)
tt->local_sync = local_sync;
tt->local_merge = local_merge;
tt->get_from_context = texture_get_from_context;
+ tt->valid_socket_type = texture_node_tree_socket_type_valid;
tt->rna_ext.srna = &RNA_TextureNodeTree;
diff --git a/source/blender/nodes/texture/node_texture_util.c b/source/blender/nodes/texture/node_texture_util.c
index 2091a8bf10e..570b10d6e89 100644
--- a/source/blender/nodes/texture/node_texture_util.c
+++ b/source/blender/nodes/texture/node_texture_util.c
@@ -39,9 +39,15 @@
#include "node_texture_util.h"
-bool tex_node_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree)
+bool tex_node_poll_default(bNodeType *UNUSED(ntype),
+ bNodeTree *ntree,
+ const char **r_disabled_hint)
{
- return STREQ(ntree->idname, "TextureNodeTree");
+ if (!STREQ(ntree->idname, "TextureNodeTree")) {
+ *r_disabled_hint = "Not a texture node tree";
+ return false;
+ }
+ return true;
}
void tex_node_type_base(
diff --git a/source/blender/nodes/texture/node_texture_util.h b/source/blender/nodes/texture/node_texture_util.h
index 74f27ef3974..8f63a1ad07d 100644
--- a/source/blender/nodes/texture/node_texture_util.h
+++ b/source/blender/nodes/texture/node_texture_util.h
@@ -106,7 +106,9 @@ typedef struct TexDelegate {
int type;
} TexDelegate;
-bool tex_node_poll_default(struct bNodeType *ntype, struct bNodeTree *ntree);
+bool tex_node_poll_default(struct bNodeType *ntype,
+ struct bNodeTree *ntree,
+ const char **r_disabled_hint);
void tex_node_type_base(
struct bNodeType *ntype, int type, const char *name, short nclass, short flag);
diff --git a/source/blender/python/BPY_extern_run.h b/source/blender/python/BPY_extern_run.h
index 5f12ada4ff2..b65b5d61b9d 100644
--- a/source/blender/python/BPY_extern_run.h
+++ b/source/blender/python/BPY_extern_run.h
@@ -42,27 +42,43 @@ bool BPY_run_text(struct bContext *C,
bool BPY_run_string_exec(struct bContext *C, const char *imports[], const char *expr);
bool BPY_run_string_eval(struct bContext *C, const char *imports[], const char *expr);
+/**
+ * \note When this struct is passed in as NULL,
+ * print errors to the `stdout` and clear.
+ */
+struct BPy_RunErrInfo {
+ /** Brief text, single line (can show this in status bar for e.g.). */
+ bool use_single_line_error;
+
+ /** Report with optional prefix (when non-NULL). */
+ struct ReportList *reports;
+ const char *report_prefix;
+
+ /** Allocated exception text (assign when non-NULL). */
+ char **r_string;
+};
+
/* Run, evaluating to fixed type result. */
bool BPY_run_string_as_number(struct bContext *C,
const char *imports[],
const char *expr,
- const char *report_prefix,
+ struct BPy_RunErrInfo *err_info,
double *r_value);
bool BPY_run_string_as_intptr(struct bContext *C,
const char *imports[],
const char *expr,
- const char *report_prefix,
+ struct BPy_RunErrInfo *err_info,
intptr_t *r_value);
bool BPY_run_string_as_string_and_size(struct bContext *C,
const char *imports[],
const char *expr,
- const char *report_prefix,
+ struct BPy_RunErrInfo *err_info,
char **r_value,
size_t *r_value_size);
bool BPY_run_string_as_string(struct bContext *C,
const char *imports[],
const char *expr,
- const char *report_prefix,
+ struct BPy_RunErrInfo *err_info,
char **r_value);
#ifdef __cplusplus
diff --git a/source/blender/python/bmesh/bmesh_py_api.c b/source/blender/python/bmesh/bmesh_py_api.c
index 6dcf8bc5f1c..906f8ab702f 100644
--- a/source/blender/python/bmesh/bmesh_py_api.c
+++ b/source/blender/python/bmesh/bmesh_py_api.c
@@ -125,7 +125,7 @@ static PyObject *bpy_bm_update_edit_mesh(PyObject *UNUSED(self), PyObject *args,
if (!PyArg_ParseTupleAndKeywords(args,
kw,
- "O|O&O&:update_edit_mesh",
+ "O|$O&O&:update_edit_mesh",
(char **)kwlist,
&py_me,
PyC_ParseBool,
diff --git a/source/blender/python/bmesh/bmesh_py_types.c b/source/blender/python/bmesh/bmesh_py_types.c
index 563a76ac824..638975f8fba 100644
--- a/source/blender/python/bmesh/bmesh_py_types.c
+++ b/source/blender/python/bmesh/bmesh_py_types.c
@@ -1065,29 +1065,21 @@ static PyObject *bpy_bmesh_to_mesh(BPy_BMesh *self, PyObject *args)
Py_RETURN_NONE;
}
-PyDoc_STRVAR(
- bpy_bmesh_from_object_doc,
- ".. method:: from_object(object, depsgraph, deform=True, cage=False, face_normals=True)\n"
- "\n"
- " Initialize this bmesh from existing object datablock (currently only meshes are "
- "supported).\n"
- "\n"
- " :arg object: The object data to load.\n"
- " :type object: :class:`Object`\n"
- " :arg deform: Apply deformation modifiers.\n"
- " :type deform: boolean\n"
- " :arg cage: Get the mesh as a deformed cage.\n"
- " :type cage: boolean\n"
- " :arg face_normals: Calculate face normals.\n"
- " :type face_normals: boolean\n"
- "\n"
- " .. deprecated:: 2.93\n"
- "\n"
- " The deform parameter is deprecated, assumed to be True, and will be removed in version "
- "3.0.\n");
+PyDoc_STRVAR(bpy_bmesh_from_object_doc,
+ ".. method:: from_object(object, depsgraph, cage=False, face_normals=True)\n"
+ "\n"
+ " Initialize this bmesh from existing object data-block (only meshes are currently "
+ "supported).\n"
+ "\n"
+ " :arg object: The object data to load.\n"
+ " :type object: :class:`Object`\n"
+ " :arg cage: Get the mesh as a deformed cage.\n"
+ " :type cage: boolean\n"
+ " :arg face_normals: Calculate face normals.\n"
+ " :type face_normals: boolean\n");
static PyObject *bpy_bmesh_from_object(BPy_BMesh *self, PyObject *args, PyObject *kw)
{
- static const char *kwlist[] = {"object", "depsgraph", "deform", "cage", "face_normals", NULL};
+ static const char *kwlist[] = {"object", "depsgraph", "cage", "face_normals", NULL};
PyObject *py_object;
PyObject *py_depsgraph;
Object *ob, *ob_eval;
@@ -1095,7 +1087,6 @@ static PyObject *bpy_bmesh_from_object(BPy_BMesh *self, PyObject *args, PyObject
struct Scene *scene_eval;
Mesh *me_eval;
BMesh *bm;
- bool use_deform = true;
bool use_cage = false;
bool use_fnorm = true;
const CustomData_MeshMasks data_masks = CD_MASK_BMESH;
@@ -1104,13 +1095,11 @@ static PyObject *bpy_bmesh_from_object(BPy_BMesh *self, PyObject *args, PyObject
if (!PyArg_ParseTupleAndKeywords(args,
kw,
- "OO|O&O&O&:from_object",
+ "OO|$O&O&:from_object",
(char **)kwlist,
&py_object,
&py_depsgraph,
PyC_ParseBool,
- &use_deform,
- PyC_ParseBool,
&use_cage,
PyC_ParseBool,
&use_fnorm) ||
@@ -1125,13 +1114,6 @@ static PyObject *bpy_bmesh_from_object(BPy_BMesh *self, PyObject *args, PyObject
return NULL;
}
- if (use_deform == false) {
- PyErr_WarnEx(PyExc_FutureWarning,
- "from_object(...): the deform parameter is deprecated, assumed to be True, and "
- "will be removed in version 3.0",
- 1);
- }
-
const bool use_render = DEG_get_mode(depsgraph) == DAG_EVAL_RENDER;
scene_eval = DEG_get_evaluated_scene(depsgraph);
ob_eval = DEG_get_evaluated_object(depsgraph, ob);
@@ -1146,7 +1128,7 @@ static PyObject *bpy_bmesh_from_object(BPy_BMesh *self, PyObject *args, PyObject
return NULL;
}
- me_eval = BKE_mesh_new_from_object(depsgraph, ob_eval, true);
+ me_eval = BKE_mesh_new_from_object(depsgraph, ob_eval, true, false);
need_free = true;
}
else {
@@ -1214,7 +1196,7 @@ static PyObject *bpy_bmesh_from_mesh(BPy_BMesh *self, PyObject *args, PyObject *
if (!PyArg_ParseTupleAndKeywords(args,
kw,
- "O|O&O&i:from_mesh",
+ "O|$O&O&i:from_mesh",
(char **)kwlist,
&py_mesh,
PyC_ParseBool,
@@ -1314,7 +1296,7 @@ static PyObject *bpy_bmesh_transform(BPy_BMElem *self, PyObject *args, PyObject
if (!PyArg_ParseTupleAndKeywords(args,
kw,
- "O!|O!:transform",
+ "O!|$O!:transform",
(char **)kwlist,
&matrix_Type,
&mat,
@@ -1376,7 +1358,7 @@ static PyObject *bpy_bmesh_calc_volume(BPy_BMElem *self, PyObject *args, PyObjec
BPY_BM_CHECK_OBJ(self);
if (!PyArg_ParseTupleAndKeywords(
- args, kw, "|O!:calc_volume", (char **)kwlist, &PyBool_Type, &is_signed)) {
+ args, kw, "|$O!:calc_volume", (char **)kwlist, &PyBool_Type, &is_signed)) {
return NULL;
}
@@ -1395,7 +1377,6 @@ static PyObject *bpy_bmesh_calc_loop_triangles(BPy_BMElem *self)
BMesh *bm;
int looptris_tot;
- int tottri;
BMLoop *(*looptris)[3];
PyObject *ret;
@@ -1408,10 +1389,10 @@ static PyObject *bpy_bmesh_calc_loop_triangles(BPy_BMElem *self)
looptris_tot = poly_to_tri_count(bm->totface, bm->totloop);
looptris = PyMem_MALLOC(sizeof(*looptris) * looptris_tot);
- BM_mesh_calc_tessellation(bm, looptris, &tottri);
+ BM_mesh_calc_tessellation(bm, looptris);
- ret = PyList_New(tottri);
- for (i = 0; i < tottri; i++) {
+ ret = PyList_New(looptris_tot);
+ for (i = 0; i < looptris_tot; i++) {
PyList_SET_ITEM(ret, i, BPy_BMLoop_Array_As_Tuple(bm, looptris[i], 3));
}
@@ -1869,7 +1850,7 @@ static PyObject *bpy_bmface_copy(BPy_BMFace *self, PyObject *args, PyObject *kw)
if (!PyArg_ParseTupleAndKeywords(args,
kw,
- "|O&O&:BMFace.copy",
+ "|$O&O&:BMFace.copy",
(char **)kwlist,
PyC_ParseBool,
&do_verts,
@@ -2665,7 +2646,7 @@ static PyObject *bpy_bmelemseq_sort(BPy_BMElemSeq *self, PyObject *args, PyObjec
if (args != NULL) {
if (!PyArg_ParseTupleAndKeywords(args,
kw,
- "|OO&:BMElemSeq.sort",
+ "|$OO&:BMElemSeq.sort",
(char **)kwlist,
&keyfunc,
PyC_ParseBool,
diff --git a/source/blender/python/bmesh/bmesh_py_types.h b/source/blender/python/bmesh/bmesh_py_types.h
index ce894760eff..ed307ce59a0 100644
--- a/source/blender/python/bmesh/bmesh_py_types.h
+++ b/source/blender/python/bmesh/bmesh_py_types.h
@@ -53,38 +53,45 @@ extern PyTypeObject BPy_BMIter_Type;
/* cast from _any_ bmesh type - they all have BMesh first */
typedef struct BPy_BMGeneric {
- PyObject_VAR_HEAD struct BMesh *bm; /* keep first */
+ PyObject_VAR_HEAD
+ struct BMesh *bm; /* keep first */
} BPy_BMGeneric;
/* BPy_BMVert/BPy_BMEdge/BPy_BMFace/BPy_BMLoop can cast to this */
typedef struct BPy_BMElem {
- PyObject_VAR_HEAD struct BMesh *bm; /* keep first */
+ PyObject_VAR_HEAD
+ struct BMesh *bm; /* keep first */
struct BMElem *ele;
} BPy_BMElem;
typedef struct BPy_BMesh {
- PyObject_VAR_HEAD struct BMesh *bm; /* keep first */
+ PyObject_VAR_HEAD
+ struct BMesh *bm; /* keep first */
int flag;
} BPy_BMesh;
/* element types */
typedef struct BPy_BMVert {
- PyObject_VAR_HEAD struct BMesh *bm; /* keep first */
+ PyObject_VAR_HEAD
+ struct BMesh *bm; /* keep first */
struct BMVert *v;
} BPy_BMVert;
typedef struct BPy_BMEdge {
- PyObject_VAR_HEAD struct BMesh *bm; /* keep first */
+ PyObject_VAR_HEAD
+ struct BMesh *bm; /* keep first */
struct BMEdge *e;
} BPy_BMEdge;
typedef struct BPy_BMFace {
- PyObject_VAR_HEAD struct BMesh *bm; /* keep first */
+ PyObject_VAR_HEAD
+ struct BMesh *bm; /* keep first */
struct BMFace *f;
} BPy_BMFace;
typedef struct BPy_BMLoop {
- PyObject_VAR_HEAD struct BMesh *bm; /* keep first */
+ PyObject_VAR_HEAD
+ struct BMesh *bm; /* keep first */
struct BMLoop *l;
} BPy_BMLoop;
@@ -98,7 +105,8 @@ typedef struct BPy_BMLoop {
* - BPy_BMLoopSeq_Type
*/
typedef struct BPy_BMElemSeq {
- PyObject_VAR_HEAD struct BMesh *bm; /* keep first */
+ PyObject_VAR_HEAD
+ struct BMesh *bm; /* keep first */
/* if this is a sequence on an existing element,
* loops of faces for eg.
@@ -114,7 +122,8 @@ typedef struct BPy_BMElemSeq {
} BPy_BMElemSeq;
typedef struct BPy_BMIter {
- PyObject_VAR_HEAD struct BMesh *bm; /* keep first */
+ PyObject_VAR_HEAD
+ struct BMesh *bm; /* keep first */
BMIter iter;
} BPy_BMIter;
diff --git a/source/blender/python/bmesh/bmesh_py_types_customdata.h b/source/blender/python/bmesh/bmesh_py_types_customdata.h
index 3173813a912..8552942f73a 100644
--- a/source/blender/python/bmesh/bmesh_py_types_customdata.h
+++ b/source/blender/python/bmesh/bmesh_py_types_customdata.h
@@ -38,20 +38,23 @@ extern PyTypeObject BPy_BMLayerItem_Type;
/* all layers for vert/edge/face/loop */
typedef struct BPy_BMLayerAccess {
- PyObject_VAR_HEAD struct BMesh *bm; /* keep first */
+ PyObject_VAR_HEAD
+ struct BMesh *bm; /* keep first */
char htype;
} BPy_BMLayerAccess;
/* access different layer types deform/uv/vertexcolor */
typedef struct BPy_BMLayerCollection {
- PyObject_VAR_HEAD struct BMesh *bm; /* keep first */
+ PyObject_VAR_HEAD
+ struct BMesh *bm; /* keep first */
char htype;
int type; /* customdata type - CD_XXX */
} BPy_BMLayerCollection;
/* access a specific layer directly */
typedef struct BPy_BMLayerItem {
- PyObject_VAR_HEAD struct BMesh *bm; /* keep first */
+ PyObject_VAR_HEAD
+ struct BMesh *bm; /* keep first */
char htype;
int type; /* customdata type - CD_XXX */
int index; /* index of this layer type */
diff --git a/source/blender/python/bmesh/bmesh_py_types_meshdata.c b/source/blender/python/bmesh/bmesh_py_types_meshdata.c
index 127ce7db503..84267a83a44 100644
--- a/source/blender/python/bmesh/bmesh_py_types_meshdata.c
+++ b/source/blender/python/bmesh/bmesh_py_types_meshdata.c
@@ -48,7 +48,8 @@
#define BPy_BMLoopUV_Check(v) (Py_TYPE(v) == &BPy_BMLoopUV_Type)
typedef struct BPy_BMLoopUV {
- PyObject_VAR_HEAD MLoopUV *data;
+ PyObject_VAR_HEAD
+ MLoopUV *data;
} BPy_BMLoopUV;
PyDoc_STRVAR(bpy_bmloopuv_uv_doc,
@@ -155,7 +156,8 @@ PyObject *BPy_BMLoopUV_CreatePyObject(struct MLoopUV *mloopuv)
#define BPy_BMVertSkin_Check(v) (Py_TYPE(v) == &BPy_BMVertSkin_Type)
typedef struct BPy_BMVertSkin {
- PyObject_VAR_HEAD MVertSkin *data;
+ PyObject_VAR_HEAD
+ MVertSkin *data;
} BPy_BMVertSkin;
PyDoc_STRVAR(bpy_bmvertskin_radius_doc,
@@ -392,7 +394,8 @@ PyObject *BPy_BMLoopColor_CreatePyObject(struct MLoopCol *mloopcol)
#define BPy_BMDeformVert_Check(v) (Py_TYPE(v) == &BPy_BMDeformVert_Type)
typedef struct BPy_BMDeformVert {
- PyObject_VAR_HEAD MDeformVert *data;
+ PyObject_VAR_HEAD
+ MDeformVert *data;
} BPy_BMDeformVert;
/* Mapping Protocols
diff --git a/source/blender/python/bmesh/bmesh_py_types_meshdata.h b/source/blender/python/bmesh/bmesh_py_types_meshdata.h
index b7699b387e5..426bfcef6a0 100644
--- a/source/blender/python/bmesh/bmesh_py_types_meshdata.h
+++ b/source/blender/python/bmesh/bmesh_py_types_meshdata.h
@@ -29,7 +29,8 @@ extern PyTypeObject BPy_BMDeformVert_Type;
#define BPy_BMLoopUV_Check(v) (Py_TYPE(v) == &BPy_BMLoopUV_Type)
typedef struct BPy_BMGenericMeshData {
- PyObject_VAR_HEAD void *data;
+ PyObject_VAR_HEAD
+ void *data;
} BPy_BMGenericMeshData;
struct MDeformVert;
diff --git a/source/blender/python/bmesh/bmesh_py_types_select.h b/source/blender/python/bmesh/bmesh_py_types_select.h
index c33aa3675c5..34ca162dd09 100644
--- a/source/blender/python/bmesh/bmesh_py_types_select.h
+++ b/source/blender/python/bmesh/bmesh_py_types_select.h
@@ -32,11 +32,13 @@ extern PyTypeObject BPy_BMEditSelIter_Type;
#define BPy_BMSelectHistoryIter_Check(v) (Py_TYPE(v) == &BPy_BMEditSelIter_Type)
typedef struct BPy_BMEditSelSeq {
- PyObject_VAR_HEAD struct BMesh *bm; /* keep first */
+ PyObject_VAR_HEAD
+ struct BMesh *bm; /* keep first */
} BPy_BMEditSelSeq;
typedef struct BPy_BMEditSelIter {
- PyObject_VAR_HEAD struct BMesh *bm; /* keep first */
+ PyObject_VAR_HEAD
+ struct BMesh *bm; /* keep first */
struct BMEditSelection *ese;
} BPy_BMEditSelIter;
diff --git a/source/blender/python/bmesh/bmesh_py_utils.c b/source/blender/python/bmesh/bmesh_py_utils.c
index 22c141e0958..c1e28182c53 100644
--- a/source/blender/python/bmesh/bmesh_py_utils.c
+++ b/source/blender/python/bmesh/bmesh_py_utils.c
@@ -86,7 +86,7 @@ static PyObject *bpy_bm_utils_vert_collapse_edge(PyObject *UNUSED(self), PyObjec
bm = py_edge->bm;
- e_new = BM_vert_collapse_edge(bm, py_edge->e, py_vert->v, true, true);
+ e_new = BM_vert_collapse_edge(bm, py_edge->e, py_vert->v, true, true, true);
if (e_new) {
return BPy_BMEdge_CreatePyObject(bm, e_new);
@@ -155,7 +155,7 @@ static PyObject *bpy_bm_utils_vert_collapse_faces(PyObject *UNUSED(self), PyObje
bm = py_edge->bm;
e_new = BM_vert_collapse_faces(
- bm, py_edge->e, py_vert->v, clamp_f(fac, 0.0f, 1.0f), true, do_join_faces, true);
+ bm, py_edge->e, py_vert->v, clamp_f(fac, 0.0f, 1.0f), true, do_join_faces, true, true);
if (e_new) {
return BPy_BMEdge_CreatePyObject(bm, e_new);
@@ -450,7 +450,7 @@ static PyObject *bpy_bm_utils_face_split(PyObject *UNUSED(self), PyObject *args,
if (!PyArg_ParseTupleAndKeywords(args,
kw,
- "O!O!O!|OO&O!:face_split",
+ "O!O!O!|$OO&O!:face_split",
(char **)kwlist,
&BPy_BMFace_Type,
&py_face,
diff --git a/source/blender/python/generic/bgl.h b/source/blender/python/generic/bgl.h
index ee8c293945a..4e59eab46ce 100644
--- a/source/blender/python/generic/bgl.h
+++ b/source/blender/python/generic/bgl.h
@@ -32,7 +32,8 @@ int BGL_typeSize(int type);
* For Python access to OpenGL functions requiring a pointer.
*/
typedef struct _Buffer {
- PyObject_VAR_HEAD PyObject *parent;
+ PyObject_VAR_HEAD
+ PyObject *parent;
int type; /* GL_BYTE, GL_SHORT, GL_INT, GL_FLOAT */
int ndimensions;
diff --git a/source/blender/python/generic/idprop_py_api.c b/source/blender/python/generic/idprop_py_api.c
index c329ea7965c..9b6ca7fcec5 100644
--- a/source/blender/python/generic/idprop_py_api.c
+++ b/source/blender/python/generic/idprop_py_api.c
@@ -42,6 +42,18 @@ extern bool pyrna_id_FromPyObject(PyObject *obj, ID **id);
extern PyObject *pyrna_id_CreatePyObject(ID *id);
extern bool pyrna_id_CheckPyObject(PyObject *obj);
+/* Currently there is no need to expose this publicly. */
+static PyObject *BPy_IDGroup_IterKeys_CreatePyObject(BPy_IDProperty *group, const bool reversed);
+static PyObject *BPy_IDGroup_IterValues_CreatePyObject(BPy_IDProperty *group, const bool reversed);
+static PyObject *BPy_IDGroup_IterItems_CreatePyObject(BPy_IDProperty *group, const bool reversed);
+
+static PyObject *BPy_IDGroup_ViewKeys_CreatePyObject(BPy_IDProperty *group);
+static PyObject *BPy_IDGroup_ViewValues_CreatePyObject(BPy_IDProperty *group);
+static PyObject *BPy_IDGroup_ViewItems_CreatePyObject(BPy_IDProperty *group);
+
+static BPy_IDGroup_View *IDGroup_View_New_WithType(BPy_IDProperty *group, PyTypeObject *type);
+static int BPy_IDGroup_Contains(BPy_IDProperty *self, PyObject *value);
+
/* -------------------------------------------------------------------- */
/** \name Python from ID-Property (Internal Conversions)
*
@@ -756,16 +768,11 @@ static int BPy_IDGroup_Map_SetItem(BPy_IDProperty *self, PyObject *key, PyObject
static PyObject *BPy_IDGroup_iter(BPy_IDProperty *self)
{
- BPy_IDGroup_Iter *iter = PyObject_New(BPy_IDGroup_Iter, &BPy_IDGroup_Iter_Type);
- iter->group = self;
- iter->mode = IDPROP_ITER_KEYS;
- iter->cur = self->prop->data.group.first;
- Py_XINCREF(iter);
- return (PyObject *)iter;
+ return BPy_IDGroup_ViewKeys_CreatePyObject(self);
}
/* for simple, non nested types this is the same as BPy_IDGroup_WrapData */
-static PyObject *BPy_IDGroup_MapDataToPy(IDProperty *prop)
+PyObject *BPy_IDGroup_MapDataToPy(IDProperty *prop)
{
switch (prop->type) {
case IDP_STRING:
@@ -874,6 +881,370 @@ static PyObject *BPy_IDGroup_MapDataToPy(IDProperty *prop)
/** \} */
/* -------------------------------------------------------------------- */
+/** \name ID-Property Group Iterator Type
+ * \{ */
+
+static PyObject *BPy_IDGroup_Iter_repr(BPy_IDGroup_Iter *self)
+{
+ if (self->group == NULL) {
+ return PyUnicode_FromFormat("<%s>", Py_TYPE(self)->tp_name);
+ }
+ return PyUnicode_FromFormat("<%s \"%s\">", Py_TYPE(self)->tp_name, self->group->prop->name);
+}
+
+static void BPy_IDGroup_Iter_dealloc(BPy_IDGroup_Iter *self)
+{
+ if (self->group != NULL) {
+ PyObject_GC_UnTrack(self);
+ }
+ Py_CLEAR(self->group);
+ PyObject_GC_Del(self);
+}
+
+static int BPy_IDGroup_Iter_traverse(BPy_IDGroup_Iter *self, visitproc visit, void *arg)
+{
+ Py_VISIT(self->group);
+ return 0;
+}
+
+static int BPy_IDGroup_Iter_clear(BPy_IDGroup_Iter *self)
+{
+ Py_CLEAR(self->group);
+ return 0;
+}
+
+static bool BPy_Group_Iter_same_size_or_raise_error(BPy_IDGroup_Iter *self)
+{
+ if (self->len_init == self->group->prop->len) {
+ return true;
+ }
+ PyErr_SetString(PyExc_RuntimeError, "IDPropertyGroup changed size during iteration");
+ return false;
+}
+
+static PyObject *BPy_Group_IterKeys_next(BPy_IDGroup_Iter *self)
+{
+ if (self->cur != NULL) {
+ /* When `cur` is set, `group` cannot be NULL. */
+ if (!BPy_Group_Iter_same_size_or_raise_error(self)) {
+ return NULL;
+ }
+ IDProperty *cur = self->cur;
+ self->cur = self->reversed ? self->cur->prev : self->cur->next;
+ return PyUnicode_FromString(cur->name);
+ }
+ PyErr_SetNone(PyExc_StopIteration);
+ return NULL;
+}
+
+static PyObject *BPy_Group_IterValues_next(BPy_IDGroup_Iter *self)
+{
+ if (self->cur != NULL) {
+ /* When `cur` is set, `group` cannot be NULL. */
+ if (!BPy_Group_Iter_same_size_or_raise_error(self)) {
+ return NULL;
+ }
+ IDProperty *cur = self->cur;
+ self->cur = self->reversed ? self->cur->prev : self->cur->next;
+ return BPy_IDGroup_WrapData(self->group->id, cur, self->group->prop);
+ }
+ PyErr_SetNone(PyExc_StopIteration);
+ return NULL;
+}
+
+static PyObject *BPy_Group_IterItems_next(BPy_IDGroup_Iter *self)
+{
+ if (self->cur != NULL) {
+ /* When `cur` is set, `group` cannot be NULL. */
+ if (!BPy_Group_Iter_same_size_or_raise_error(self)) {
+ return NULL;
+ }
+ IDProperty *cur = self->cur;
+ self->cur = self->reversed ? self->cur->prev : self->cur->next;
+ PyObject *ret = PyTuple_New(2);
+ PyTuple_SET_ITEMS(ret,
+ PyUnicode_FromString(cur->name),
+ BPy_IDGroup_WrapData(self->group->id, cur, self->group->prop));
+ return ret;
+ }
+ PyErr_SetNone(PyExc_StopIteration);
+ return NULL;
+}
+
+PyTypeObject BPy_IDGroup_IterKeys_Type = {PyVarObject_HEAD_INIT(NULL, 0)};
+PyTypeObject BPy_IDGroup_IterValues_Type = {PyVarObject_HEAD_INIT(NULL, 0)};
+PyTypeObject BPy_IDGroup_IterItems_Type = {PyVarObject_HEAD_INIT(NULL, 0)};
+
+/* ID Property Group Iterator. */
+static void IDGroup_Iter_init_type(void)
+{
+#define SHARED_MEMBER_SET(member, value) \
+ { \
+ k_ty->member = v_ty->member = i_ty->member = value; \
+ } \
+ ((void)0)
+
+ PyTypeObject *k_ty = &BPy_IDGroup_IterKeys_Type;
+ PyTypeObject *v_ty = &BPy_IDGroup_IterValues_Type;
+ PyTypeObject *i_ty = &BPy_IDGroup_IterItems_Type;
+
+ /* Unique members. */
+ k_ty->tp_name = "IDPropertyGroupIterKeys";
+ v_ty->tp_name = "IDPropertyGroupIterValues";
+ i_ty->tp_name = "IDPropertyGroupIterItems";
+
+ k_ty->tp_iternext = (iternextfunc)BPy_Group_IterKeys_next;
+ v_ty->tp_iternext = (iternextfunc)BPy_Group_IterValues_next;
+ i_ty->tp_iternext = (iternextfunc)BPy_Group_IterItems_next;
+
+ /* Shared members. */
+ SHARED_MEMBER_SET(tp_basicsize, sizeof(BPy_IDGroup_Iter));
+ SHARED_MEMBER_SET(tp_dealloc, (destructor)BPy_IDGroup_Iter_dealloc);
+ SHARED_MEMBER_SET(tp_repr, (reprfunc)BPy_IDGroup_Iter_repr);
+ SHARED_MEMBER_SET(tp_flags, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC);
+ SHARED_MEMBER_SET(tp_traverse, (traverseproc)BPy_IDGroup_Iter_traverse);
+ SHARED_MEMBER_SET(tp_clear, (inquiry)BPy_IDGroup_Iter_clear);
+ SHARED_MEMBER_SET(tp_iter, PyObject_SelfIter);
+
+#undef SHARED_MEMBER_SET
+}
+
+static PyObject *IDGroup_Iter_New_WithType(BPy_IDProperty *group,
+ const bool reversed,
+ PyTypeObject *type)
+{
+ BLI_assert(group ? group->prop->type == IDP_GROUP : true);
+ BPy_IDGroup_Iter *iter = PyObject_GC_New(BPy_IDGroup_Iter, type);
+ iter->reversed = reversed;
+ iter->group = group;
+ if (group != NULL) {
+ Py_INCREF(group);
+ PyObject_GC_Track(iter);
+ iter->cur = (reversed ? group->prop->data.group.last : group->prop->data.group.first);
+ iter->len_init = group->prop->len;
+ }
+ else {
+ iter->cur = NULL;
+ iter->len_init = 0;
+ }
+ return (PyObject *)iter;
+}
+
+static PyObject *BPy_IDGroup_IterKeys_CreatePyObject(BPy_IDProperty *group, const bool reversed)
+{
+ return IDGroup_Iter_New_WithType(group, reversed, &BPy_IDGroup_IterKeys_Type);
+}
+
+static PyObject *BPy_IDGroup_IterValues_CreatePyObject(BPy_IDProperty *group, const bool reversed)
+{
+ return IDGroup_Iter_New_WithType(group, reversed, &BPy_IDGroup_IterValues_Type);
+}
+
+static PyObject *BPy_IDGroup_IterItems_CreatePyObject(BPy_IDProperty *group, const bool reversed)
+{
+ return IDGroup_Iter_New_WithType(group, reversed, &BPy_IDGroup_IterItems_Type);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name ID-Property Group View Types (Keys/Values/Items)
+ *
+ * This view types is a thin wrapper on keys/values/items, this matches Python's `dict_view` type.
+ * The is returned by `property.keys()` and is separate from the iterator that loops over keys.
+ *
+ * There are some less common features this type could support (matching Python's `dict_view`)
+ *
+ * TODO:
+ * - Efficient contains checks for values and items which currently convert to a list first.
+ * - Missing `dict_views.isdisjoint`.
+ * - Missing `tp_as_number` (`nb_subtract`, `nb_and`, `nb_xor`, `nb_or`).
+ * \{ */
+
+static PyObject *BPy_IDGroup_View_repr(BPy_IDGroup_View *self)
+{
+ if (self->group == NULL) {
+ return PyUnicode_FromFormat("<%s>", Py_TYPE(self)->tp_name);
+ }
+ return PyUnicode_FromFormat("<%s \"%s\">", Py_TYPE(self)->tp_name, self->group->prop->name);
+}
+
+static void BPy_IDGroup_View_dealloc(BPy_IDGroup_View *self)
+{
+ if (self->group != NULL) {
+ PyObject_GC_UnTrack(self);
+ }
+ Py_CLEAR(self->group);
+ PyObject_GC_Del(self);
+}
+
+static int BPy_IDGroup_View_traverse(BPy_IDGroup_View *self, visitproc visit, void *arg)
+{
+ Py_VISIT(self->group);
+ return 0;
+}
+
+static int BPy_IDGroup_View_clear(BPy_IDGroup_View *self)
+{
+ Py_CLEAR(self->group);
+ return 0;
+}
+
+/* View Specific API's (Key/Value/Items). */
+
+static PyObject *BPy_Group_ViewKeys_iter(BPy_IDGroup_View *self)
+{
+ return BPy_IDGroup_IterKeys_CreatePyObject(self->group, self->reversed);
+}
+
+static PyObject *BPy_Group_ViewValues_iter(BPy_IDGroup_View *self)
+{
+ return BPy_IDGroup_IterValues_CreatePyObject(self->group, self->reversed);
+}
+
+static PyObject *BPy_Group_ViewItems_iter(BPy_IDGroup_View *self)
+{
+ return BPy_IDGroup_IterItems_CreatePyObject(self->group, self->reversed);
+}
+
+static Py_ssize_t BPy_Group_View_len(BPy_IDGroup_View *self)
+{
+ if (self->group == NULL) {
+ return 0;
+ }
+ return self->group->prop->len;
+}
+
+static int BPy_Group_ViewKeys_Contains(BPy_IDGroup_View *self, PyObject *value)
+{
+ if (self->group == NULL) {
+ return 0;
+ }
+ return BPy_IDGroup_Contains(self->group, value);
+}
+
+static int BPy_Group_ViewValues_Contains(BPy_IDGroup_View *self, PyObject *value)
+{
+ if (self->group == NULL) {
+ return 0;
+ }
+ /* TODO: implement this without first converting to a list. */
+ PyObject *list = PySequence_List((PyObject *)self);
+ const int result = PySequence_Contains(list, value);
+ Py_DECREF(list);
+ return result;
+}
+
+static int BPy_Group_ViewItems_Contains(BPy_IDGroup_View *self, PyObject *value)
+{
+ if (self->group == NULL) {
+ return 0;
+ }
+ /* TODO: implement this without first converting to a list. */
+ PyObject *list = PySequence_List((PyObject *)self);
+ const int result = PySequence_Contains(list, value);
+ Py_DECREF(list);
+ return result;
+}
+
+static PySequenceMethods BPy_IDGroup_ViewKeys_as_sequence = {
+ (lenfunc)BPy_Group_View_len, /* sq_length */
+ 0, /* sq_concat */
+ 0, /* sq_repeat */
+ 0, /* sq_item */
+ 0, /* sq_slice */
+ 0, /* sq_ass_item */
+ 0, /* sq_ass_slice */
+ (objobjproc)BPy_Group_ViewKeys_Contains, /* sq_contains */
+};
+
+static PySequenceMethods BPy_IDGroup_ViewValues_as_sequence = {
+ (lenfunc)BPy_Group_View_len, /* sq_length */
+ 0, /* sq_concat */
+ 0, /* sq_repeat */
+ 0, /* sq_item */
+ 0, /* sq_slice */
+ 0, /* sq_ass_item */
+ 0, /* sq_ass_slice */
+ (objobjproc)BPy_Group_ViewValues_Contains, /* sq_contains */
+};
+
+static PySequenceMethods BPy_IDGroup_ViewItems_as_sequence = {
+ (lenfunc)BPy_Group_View_len, /* sq_length */
+ 0, /* sq_concat */
+ 0, /* sq_repeat */
+ 0, /* sq_item */
+ 0, /* sq_slice */
+ 0, /* sq_ass_item */
+ 0, /* sq_ass_slice */
+ (objobjproc)BPy_Group_ViewItems_Contains, /* sq_contains */
+};
+
+/* Methods. */
+
+PyDoc_STRVAR(BPy_IDGroup_View_reversed_doc,
+ "Return a reverse iterator over the ID Property keys values or items.");
+
+static PyObject *BPy_IDGroup_View_reversed(BPy_IDGroup_View *self, PyObject *UNUSED(ignored))
+{
+ BPy_IDGroup_View *result = IDGroup_View_New_WithType(self->group, Py_TYPE(self));
+ result->reversed = !self->reversed;
+ return (PyObject *)result;
+}
+
+static PyMethodDef BPy_IDGroup_View_methods[] = {
+ {"__reversed__",
+ (PyCFunction)(void (*)(void))BPy_IDGroup_View_reversed,
+ METH_NOARGS,
+ BPy_IDGroup_View_reversed_doc},
+ {NULL, NULL},
+};
+
+PyTypeObject BPy_IDGroup_ViewKeys_Type = {PyVarObject_HEAD_INIT(NULL, 0)};
+PyTypeObject BPy_IDGroup_ViewValues_Type = {PyVarObject_HEAD_INIT(NULL, 0)};
+PyTypeObject BPy_IDGroup_ViewItems_Type = {PyVarObject_HEAD_INIT(NULL, 0)};
+
+/* ID Property Group View. */
+static void IDGroup_View_init_type(void)
+{
+ PyTypeObject *k_ty = &BPy_IDGroup_ViewKeys_Type;
+ PyTypeObject *v_ty = &BPy_IDGroup_ViewValues_Type;
+ PyTypeObject *i_ty = &BPy_IDGroup_ViewItems_Type;
+
+ /* Unique members. */
+ k_ty->tp_name = "IDPropertyGroupViewKeys";
+ v_ty->tp_name = "IDPropertyGroupViewValues";
+ i_ty->tp_name = "IDPropertyGroupViewItems";
+
+ k_ty->tp_iter = (getiterfunc)BPy_Group_ViewKeys_iter;
+ v_ty->tp_iter = (getiterfunc)BPy_Group_ViewValues_iter;
+ i_ty->tp_iter = (getiterfunc)BPy_Group_ViewItems_iter;
+
+ k_ty->tp_as_sequence = &BPy_IDGroup_ViewKeys_as_sequence;
+ v_ty->tp_as_sequence = &BPy_IDGroup_ViewValues_as_sequence;
+ i_ty->tp_as_sequence = &BPy_IDGroup_ViewItems_as_sequence;
+
+ /* Shared members. */
+#define SHARED_MEMBER_SET(member, value) \
+ { \
+ k_ty->member = v_ty->member = i_ty->member = value; \
+ } \
+ ((void)0)
+
+ SHARED_MEMBER_SET(tp_basicsize, sizeof(BPy_IDGroup_View));
+ SHARED_MEMBER_SET(tp_dealloc, (destructor)BPy_IDGroup_View_dealloc);
+ SHARED_MEMBER_SET(tp_repr, (reprfunc)BPy_IDGroup_View_repr);
+ SHARED_MEMBER_SET(tp_flags, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC);
+ SHARED_MEMBER_SET(tp_traverse, (traverseproc)BPy_IDGroup_View_traverse);
+ SHARED_MEMBER_SET(tp_clear, (inquiry)BPy_IDGroup_View_clear);
+ SHARED_MEMBER_SET(tp_methods, BPy_IDGroup_View_methods);
+
+#undef SHARED_MEMBER_SET
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name ID-Property Group Methods
* \{ */
@@ -918,25 +1289,10 @@ static PyObject *BPy_IDGroup_pop(BPy_IDProperty *self, PyObject *args)
return NULL;
}
- IDP_RemoveFromGroup(self->prop, idprop);
+ IDP_FreeFromGroup(self->prop, idprop);
return pyform;
}
-PyDoc_STRVAR(
- BPy_IDGroup_iter_items_doc,
- ".. method:: iteritems()\n"
- "\n"
- " Iterate through the items in the dict; behaves like dictionary method iteritems.\n");
-static PyObject *BPy_IDGroup_iter_items(BPy_IDProperty *self)
-{
- BPy_IDGroup_Iter *iter = PyObject_New(BPy_IDGroup_Iter, &BPy_IDGroup_Iter_Type);
- iter->group = self;
- iter->mode = IDPROP_ITER_ITEMS;
- iter->cur = self->prop->data.group.first;
- Py_XINCREF(iter);
- return (PyObject *)iter;
-}
-
/* utility function */
static void BPy_IDGroup_CorrectListLen(IDProperty *prop, PyObject *seq, int len, const char *func)
{
@@ -1021,13 +1377,37 @@ PyObject *BPy_Wrap_GetItems(ID *id, IDProperty *prop)
return seq;
}
+PyObject *BPy_Wrap_GetKeys_View_WithID(ID *id, IDProperty *prop)
+{
+ PyObject *self = prop ? idprop_py_from_idp_group(id, prop, NULL) : NULL;
+ PyObject *ret = BPy_IDGroup_ViewKeys_CreatePyObject((BPy_IDProperty *)self);
+ Py_XDECREF(self); /* Owned by `ret`. */
+ return ret;
+}
+
+PyObject *BPy_Wrap_GetValues_View_WithID(ID *id, IDProperty *prop)
+{
+ PyObject *self = prop ? idprop_py_from_idp_group(id, prop, NULL) : NULL;
+ PyObject *ret = BPy_IDGroup_ViewValues_CreatePyObject((BPy_IDProperty *)self);
+ Py_XDECREF(self); /* Owned by `ret`. */
+ return ret;
+}
+
+PyObject *BPy_Wrap_GetItems_View_WithID(ID *id, IDProperty *prop)
+{
+ PyObject *self = prop ? idprop_py_from_idp_group(id, prop, NULL) : NULL;
+ PyObject *ret = BPy_IDGroup_ViewItems_CreatePyObject((BPy_IDProperty *)self);
+ Py_XDECREF(self); /* Owned by `ret`. */
+ return ret;
+}
+
PyDoc_STRVAR(BPy_IDGroup_keys_doc,
".. method:: keys()\n"
"\n"
" Return the keys associated with this group as a list of strings.\n");
static PyObject *BPy_IDGroup_keys(BPy_IDProperty *self)
{
- return BPy_Wrap_GetKeys(self->prop);
+ return BPy_IDGroup_ViewKeys_CreatePyObject(self);
}
PyDoc_STRVAR(BPy_IDGroup_values_doc,
@@ -1036,16 +1416,16 @@ PyDoc_STRVAR(BPy_IDGroup_values_doc,
" Return the values associated with this group.\n");
static PyObject *BPy_IDGroup_values(BPy_IDProperty *self)
{
- return BPy_Wrap_GetValues(self->id, self->prop);
+ return BPy_IDGroup_ViewValues_CreatePyObject(self);
}
PyDoc_STRVAR(BPy_IDGroup_items_doc,
".. method:: items()\n"
"\n"
- " Return the items associated with this group.\n");
+ " Iterate through the items in the dict; behaves like dictionary method items.\n");
static PyObject *BPy_IDGroup_items(BPy_IDProperty *self)
{
- return BPy_Wrap_GetItems(self->id, self->prop);
+ return BPy_IDGroup_ViewItems_CreatePyObject(self);
}
static int BPy_IDGroup_Contains(BPy_IDProperty *self, PyObject *value)
@@ -1146,7 +1526,6 @@ static PyObject *BPy_IDGroup_get(BPy_IDProperty *self, PyObject *args)
static struct PyMethodDef BPy_IDGroup_methods[] = {
{"pop", (PyCFunction)BPy_IDGroup_pop, METH_VARARGS, BPy_IDGroup_pop_doc},
- {"iteritems", (PyCFunction)BPy_IDGroup_iter_items, METH_NOARGS, BPy_IDGroup_iter_items_doc},
{"keys", (PyCFunction)BPy_IDGroup_keys, METH_NOARGS, BPy_IDGroup_keys_doc},
{"values", (PyCFunction)BPy_IDGroup_values, METH_NOARGS, BPy_IDGroup_values_doc},
{"items", (PyCFunction)BPy_IDGroup_items, METH_NOARGS, BPy_IDGroup_items_doc},
@@ -1676,101 +2055,59 @@ PyTypeObject BPy_IDArray_Type = {
/** \} */
/* -------------------------------------------------------------------- */
-/** \name ID-Property Group Iterator Type
+/** \name Initialize Types
* \{ */
-static PyObject *IDGroup_Iter_repr(BPy_IDGroup_Iter *self)
-{
- return PyUnicode_FromFormat("(ID Property Group Iter \"%s\")", self->group->prop->name);
-}
-
-static PyObject *BPy_Group_Iter_Next(BPy_IDGroup_Iter *self)
+void IDProp_Init_Types(void)
{
+ IDGroup_Iter_init_type();
+ IDGroup_View_init_type();
- if (self->cur) {
- PyObject *ret;
- IDProperty *cur;
+ PyType_Ready(&BPy_IDGroup_Type);
+ PyType_Ready(&BPy_IDArray_Type);
- cur = self->cur;
- self->cur = self->cur->next;
+ PyType_Ready(&BPy_IDGroup_IterKeys_Type);
+ PyType_Ready(&BPy_IDGroup_IterValues_Type);
+ PyType_Ready(&BPy_IDGroup_IterItems_Type);
- if (self->mode == IDPROP_ITER_ITEMS) {
- ret = PyTuple_New(2);
- PyTuple_SET_ITEMS(ret,
- PyUnicode_FromString(cur->name),
- BPy_IDGroup_WrapData(self->group->id, cur, self->group->prop));
- return ret;
- }
+ PyType_Ready(&BPy_IDGroup_ViewKeys_Type);
+ PyType_Ready(&BPy_IDGroup_ViewValues_Type);
+ PyType_Ready(&BPy_IDGroup_ViewItems_Type);
+}
- return PyUnicode_FromString(cur->name);
+/**
+ * \note `group` may be NULL, unlike most other uses of this argument.
+ * This is supported so RNA keys/values/items methods returns an iterator with the expected type:
+ * - Without having ID-properties.
+ * - Without supporting #BPy_IDProperty.prop being NULL, which would incur many more checks.
+ * Python's own dictionary-views also works this way too.
+ */
+static BPy_IDGroup_View *IDGroup_View_New_WithType(BPy_IDProperty *group, PyTypeObject *type)
+{
+ BLI_assert(group ? group->prop->type == IDP_GROUP : true);
+ BPy_IDGroup_View *iter = PyObject_GC_New(BPy_IDGroup_View, type);
+ iter->reversed = false;
+ iter->group = group;
+ if (group != NULL) {
+ Py_INCREF(group);
+ PyObject_GC_Track(iter);
}
-
- PyErr_SetNone(PyExc_StopIteration);
- return NULL;
+ return iter;
}
-PyTypeObject BPy_IDGroup_Iter_Type = {
- PyVarObject_HEAD_INIT(NULL, 0)
- /* For printing, in format "<module>.<name>" */
- "IDPropertyGroupIter", /* char *tp_name; */
- sizeof(BPy_IDGroup_Iter), /* int tp_basicsize; */
- 0, /* tp_itemsize; For allocation */
-
- /* Methods to implement standard operations */
-
- NULL, /* destructor tp_dealloc; */
- 0, /* tp_vectorcall_offset */
- NULL, /* getattrfunc tp_getattr; */
- NULL, /* setattrfunc tp_setattr; */
- NULL, /* cmpfunc tp_compare; */
- (reprfunc)IDGroup_Iter_repr, /* reprfunc tp_repr; */
-
- /* Method suites for standard classes */
-
- NULL, /* PyNumberMethods *tp_as_number; */
- NULL, /* PySequenceMethods *tp_as_sequence; */
- NULL, /* PyMappingMethods *tp_as_mapping; */
-
- /* More standard operations (here for binary compatibility) */
-
- NULL, /* hashfunc tp_hash; */
- NULL, /* ternaryfunc tp_call; */
- NULL, /* reprfunc tp_str; */
- NULL, /* getattrofunc tp_getattro; */
- NULL, /* setattrofunc tp_setattro; */
-
- /* Functions to access object as input/output buffer */
- NULL, /* PyBufferProcs *tp_as_buffer; */
-
- /*** Flags to define presence of optional/expanded features ***/
- Py_TPFLAGS_DEFAULT, /* long tp_flags; */
-
- NULL, /* char *tp_doc; Documentation string */
- /*** Assigned meaning in release 2.0 ***/
- /* call function for all accessible objects */
- NULL, /* traverseproc tp_traverse; */
-
- /* delete references to contained objects */
- NULL, /* inquiry tp_clear; */
-
- /*** Assigned meaning in release 2.1 ***/
- /*** rich comparisons ***/
- NULL, /* richcmpfunc tp_richcompare; */
-
- /*** weak reference enabler ***/
- 0, /* long tp_weaklistoffset; */
+static PyObject *BPy_IDGroup_ViewKeys_CreatePyObject(BPy_IDProperty *group)
+{
+ return (PyObject *)IDGroup_View_New_WithType(group, &BPy_IDGroup_ViewKeys_Type);
+}
- /*** Added in release 2.2 ***/
- /* Iterators */
- PyObject_SelfIter, /* getiterfunc tp_iter; */
- (iternextfunc)BPy_Group_Iter_Next, /* iternextfunc tp_iternext; */
-};
+static PyObject *BPy_IDGroup_ViewValues_CreatePyObject(BPy_IDProperty *group)
+{
+ return (PyObject *)IDGroup_View_New_WithType(group, &BPy_IDGroup_ViewValues_Type);
+}
-void IDProp_Init_Types(void)
+static PyObject *BPy_IDGroup_ViewItems_CreatePyObject(BPy_IDProperty *group)
{
- PyType_Ready(&BPy_IDGroup_Type);
- PyType_Ready(&BPy_IDGroup_Iter_Type);
- PyType_Ready(&BPy_IDArray_Type);
+ return (PyObject *)IDGroup_View_New_WithType(group, &BPy_IDGroup_ViewItems_Type);
}
/** \} */
@@ -1801,7 +2138,15 @@ static PyObject *BPyInit_idprop_types(void)
/* bmesh_py_types.c */
PyModule_AddType(submodule, &BPy_IDGroup_Type);
- PyModule_AddType(submodule, &BPy_IDGroup_Iter_Type);
+
+ PyModule_AddType(submodule, &BPy_IDGroup_ViewKeys_Type);
+ PyModule_AddType(submodule, &BPy_IDGroup_ViewValues_Type);
+ PyModule_AddType(submodule, &BPy_IDGroup_ViewItems_Type);
+
+ PyModule_AddType(submodule, &BPy_IDGroup_IterKeys_Type);
+ PyModule_AddType(submodule, &BPy_IDGroup_IterValues_Type);
+ PyModule_AddType(submodule, &BPy_IDGroup_IterItems_Type);
+
PyModule_AddType(submodule, &BPy_IDArray_Type);
return submodule;
diff --git a/source/blender/python/generic/idprop_py_api.h b/source/blender/python/generic/idprop_py_api.h
index 478dc99f73d..1e8e26a3b6d 100644
--- a/source/blender/python/generic/idprop_py_api.h
+++ b/source/blender/python/generic/idprop_py_api.h
@@ -25,45 +25,80 @@ struct ID;
struct IDProperty;
extern PyTypeObject BPy_IDArray_Type;
-extern PyTypeObject BPy_IDGroup_Iter_Type;
extern PyTypeObject BPy_IDGroup_Type;
+extern PyTypeObject BPy_IDGroup_ViewKeys_Type;
+extern PyTypeObject BPy_IDGroup_ViewValues_Type;
+extern PyTypeObject BPy_IDGroup_ViewItems_Type;
+
+extern PyTypeObject BPy_IDGroup_IterKeys_Type;
+extern PyTypeObject BPy_IDGroup_IterValues_Type;
+extern PyTypeObject BPy_IDGroup_IterItems_Type;
+
#define BPy_IDArray_Check(v) (PyObject_TypeCheck(v, &BPy_IDArray_Type))
#define BPy_IDArray_CheckExact(v) (Py_TYPE(v) == &BPy_IDArray_Type)
-#define BPy_IDGroup_Iter_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_Iter_Type))
-#define BPy_IDGroup_Iter_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_Iter_Type)
#define BPy_IDGroup_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_Type))
#define BPy_IDGroup_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_Type)
+#define BPy_IDGroup_ViewKeys_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_ViewKeys_Type))
+#define BPy_IDGroup_ViewKeys_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_ViewKeys_Type)
+#define BPy_IDGroup_ViewValues_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_ViewValues_Type))
+#define BPy_IDGroup_ViewValues_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_ViewValues_Type)
+#define BPy_IDGroup_ViewItems_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_ViewItems_Type))
+#define BPy_IDGroup_ViewItems_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_ViewItems_Type)
+
+#define BPy_IDGroup_IterKeys_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_IterKeys_Type))
+#define BPy_IDGroup_IterKeys_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_IterKeys_Type)
+#define BPy_IDGroup_IterValues_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_IterValues_Type))
+#define BPy_IDGroup_IterValues_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_IterValues_Type)
+#define BPy_IDGroup_IterItems_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_IterItems_Type))
+#define BPy_IDGroup_IterItems_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_IterItems_Type)
+
typedef struct BPy_IDProperty {
- PyObject_VAR_HEAD struct ID *id; /* can be NULL */
- struct IDProperty *prop; /* must be second member */
+ PyObject_VAR_HEAD
+ struct ID *id; /* can be NULL */
+ struct IDProperty *prop; /* must be second member */
struct IDProperty *parent;
- PyObject *data_wrap;
} BPy_IDProperty;
typedef struct BPy_IDArray {
- PyObject_VAR_HEAD struct ID *id; /* can be NULL */
- struct IDProperty *prop; /* must be second member */
+ PyObject_VAR_HEAD
+ struct ID *id; /* can be NULL */
+ struct IDProperty *prop; /* must be second member */
} BPy_IDArray;
typedef struct BPy_IDGroup_Iter {
- PyObject_VAR_HEAD BPy_IDProperty *group;
+ PyObject_VAR_HEAD
+ BPy_IDProperty *group;
struct IDProperty *cur;
- int mode;
+ /** Use for detecting manipulation during iteration (which is not allowed). */
+ int len_init;
+ /** Iterate in the reverse direction. */
+ bool reversed;
} BPy_IDGroup_Iter;
+/** Use to implement `IDPropertyGroup.keys/values/items` */
+typedef struct BPy_IDGroup_View {
+ PyObject_VAR_HEAD
+ /** This will be NULL when accessing keys on data that has no ID properties. */
+ BPy_IDProperty *group;
+ bool reversed;
+} BPy_IDGroup_View;
+
PyObject *BPy_Wrap_GetKeys(struct IDProperty *prop);
PyObject *BPy_Wrap_GetValues(struct ID *id, struct IDProperty *prop);
PyObject *BPy_Wrap_GetItems(struct ID *id, struct IDProperty *prop);
+
+PyObject *BPy_Wrap_GetKeys_View_WithID(struct ID *id, struct IDProperty *prop);
+PyObject *BPy_Wrap_GetValues_View_WithID(struct ID *id, struct IDProperty *prop);
+PyObject *BPy_Wrap_GetItems_View_WithID(struct ID *id, struct IDProperty *prop);
+
int BPy_Wrap_SetMapItem(struct IDProperty *prop, PyObject *key, PyObject *val);
+PyObject *BPy_IDGroup_MapDataToPy(struct IDProperty *prop);
PyObject *BPy_IDGroup_WrapData(struct ID *id, struct IDProperty *prop, struct IDProperty *parent);
bool BPy_IDProperty_Map_ValidateAndCreate(PyObject *key, struct IDProperty *group, PyObject *ob);
void IDProp_Init_Types(void);
PyObject *BPyInit_idprop(void);
-
-#define IDPROP_ITER_KEYS 0
-#define IDPROP_ITER_ITEMS 1
diff --git a/source/blender/python/generic/imbuf_py_api.c b/source/blender/python/generic/imbuf_py_api.c
index 97a66bc23c0..53e22314ec4 100644
--- a/source/blender/python/generic/imbuf_py_api.c
+++ b/source/blender/python/generic/imbuf_py_api.c
@@ -50,8 +50,8 @@ static PyObject *Py_ImBuf_CreatePyObject(ImBuf *ibuf);
typedef struct Py_ImBuf {
PyObject_VAR_HEAD
- /* can be NULL */
- ImBuf *ibuf;
+ /* can be NULL */
+ ImBuf *ibuf;
} Py_ImBuf;
static int py_imbuf_valid_check(Py_ImBuf *self)
@@ -106,7 +106,7 @@ static PyObject *py_imbuf_resize(Py_ImBuf *self, PyObject *args, PyObject *kw)
struct PyC_StringEnum method = {method_items, FAST};
static const char *_keywords[] = {"size", "method", NULL};
- static _PyArg_Parser _parser = {"(ii)|O&:resize", _keywords, 0};
+ static _PyArg_Parser _parser = {"(ii)|$O&:resize", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(
args, kw, &_parser, &size[0], &size[1], PyC_ParseStringEnum, &method)) {
return NULL;
@@ -173,7 +173,15 @@ PyDoc_STRVAR(py_imbuf_copy_doc,
static PyObject *py_imbuf_copy(Py_ImBuf *self)
{
PY_IMBUF_CHECK_OBJ(self);
- return Py_ImBuf_CreatePyObject(self->ibuf);
+ ImBuf *ibuf_copy = IMB_dupImBuf(self->ibuf);
+
+ if (UNLIKELY(ibuf_copy == NULL)) {
+ PyErr_SetString(PyExc_MemoryError,
+ "ImBuf.copy(): "
+ "failed to allocate memory memory");
+ return NULL;
+ }
+ return Py_ImBuf_CreatePyObject(ibuf_copy);
}
static PyObject *py_imbuf_deepcopy(Py_ImBuf *self, PyObject *args)
@@ -502,7 +510,7 @@ static PyObject *M_imbuf_write(PyObject *UNUSED(self), PyObject *args, PyObject
const char *filepath = NULL;
static const char *_keywords[] = {"image", "filepath", NULL};
- static _PyArg_Parser _parser = {"O!|s:write", _keywords, 0};
+ static _PyArg_Parser _parser = {"O!|$s:write", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args, kw, &_parser, &Py_ImBuf_Type, &py_imb, &filepath)) {
return NULL;
}
diff --git a/source/blender/python/generic/py_capi_utils.c b/source/blender/python/generic/py_capi_utils.c
index 78f5b9ba2cc..9824d5f17c4 100644
--- a/source/blender/python/generic/py_capi_utils.c
+++ b/source/blender/python/generic/py_capi_utils.c
@@ -733,7 +733,6 @@ PyObject *PyC_ExceptionBuffer_Simple(void)
PyErr_Restore(error_type, error_value, error_traceback);
- PyErr_Print();
PyErr_Clear();
return string_io_buf;
}
diff --git a/source/blender/python/gpu/CMakeLists.txt b/source/blender/python/gpu/CMakeLists.txt
index fe5c559fcc0..1424b35a004 100644
--- a/source/blender/python/gpu/CMakeLists.txt
+++ b/source/blender/python/gpu/CMakeLists.txt
@@ -37,10 +37,12 @@ set(SRC
gpu_py_api.c
gpu_py_batch.c
gpu_py_buffer.c
+ gpu_py_capabilities.c
gpu_py_element.c
gpu_py_framebuffer.c
gpu_py_matrix.c
gpu_py_offscreen.c
+ gpu_py_platform.c
gpu_py_select.c
gpu_py_shader.c
gpu_py_state.c
@@ -54,10 +56,12 @@ set(SRC
gpu_py_api.h
gpu_py_batch.h
gpu_py_buffer.h
+ gpu_py_capabilities.h
gpu_py_element.h
gpu_py_framebuffer.h
gpu_py_matrix.h
gpu_py_offscreen.h
+ gpu_py_platform.h
gpu_py_select.h
gpu_py_shader.h
gpu_py_state.h
diff --git a/source/blender/python/gpu/gpu_py_api.c b/source/blender/python/gpu/gpu_py_api.c
index 0bc18e73d0c..5119b3612f8 100644
--- a/source/blender/python/gpu/gpu_py_api.c
+++ b/source/blender/python/gpu/gpu_py_api.c
@@ -30,7 +30,9 @@
#include "../generic/python_utildefines.h"
+#include "gpu_py_capabilities.h"
#include "gpu_py_matrix.h"
+#include "gpu_py_platform.h"
#include "gpu_py_select.h"
#include "gpu_py_state.h"
#include "gpu_py_types.h"
@@ -61,9 +63,15 @@ PyObject *BPyInit_gpu(void)
PyModule_AddObject(mod, "types", (submodule = bpygpu_types_init()));
PyDict_SetItem(sys_modules, PyModule_GetNameObject(submodule), submodule);
+ PyModule_AddObject(mod, "capabilities", (submodule = bpygpu_capabilities_init()));
+ PyDict_SetItem(sys_modules, PyModule_GetNameObject(submodule), submodule);
+
PyModule_AddObject(mod, "matrix", (submodule = bpygpu_matrix_init()));
PyDict_SetItem(sys_modules, PyModule_GetNameObject(submodule), submodule);
+ PyModule_AddObject(mod, "platform", (submodule = bpygpu_platform_init()));
+ PyDict_SetItem(sys_modules, PyModule_GetNameObject(submodule), submodule);
+
PyModule_AddObject(mod, "select", (submodule = bpygpu_select_init()));
PyDict_SetItem(sys_modules, PyModule_GetNameObject(submodule), submodule);
diff --git a/source/blender/python/gpu/gpu_py_batch.h b/source/blender/python/gpu/gpu_py_batch.h
index 7c882eab8fc..84332ab4ca6 100644
--- a/source/blender/python/gpu/gpu_py_batch.h
+++ b/source/blender/python/gpu/gpu_py_batch.h
@@ -30,8 +30,8 @@ extern PyTypeObject BPyGPUBatch_Type;
typedef struct BPyGPUBatch {
PyObject_VAR_HEAD
- /* The batch is owned, we may support thin wrapped batches later. */
- struct GPUBatch *batch;
+ /* The batch is owned, we may support thin wrapped batches later. */
+ struct GPUBatch *batch;
#ifdef USE_GPU_PY_REFERENCES
/* Just to keep a user to prevent freeing buf's we're using */
PyObject *references;
diff --git a/source/blender/python/gpu/gpu_py_buffer.c b/source/blender/python/gpu/gpu_py_buffer.c
index d0965e83e33..e36e3b42617 100644
--- a/source/blender/python/gpu/gpu_py_buffer.c
+++ b/source/blender/python/gpu/gpu_py_buffer.c
@@ -37,17 +37,90 @@
#include "gpu_py_buffer.h"
-// #define PYGPU_BUFFER_PROTOCOL
+//#define PYGPU_BUFFER_PROTOCOL
+#define MAX_DIMENSIONS 64
/* -------------------------------------------------------------------- */
/** \name Utility Functions
* \{ */
-static bool pygpu_buffer_dimensions_compare(int ndim,
- const Py_ssize_t *shape_a,
- const Py_ssize_t *shape_b)
+static Py_ssize_t pygpu_buffer_dimensions_tot_elem(const Py_ssize_t *shape, Py_ssize_t shape_len)
+{
+ Py_ssize_t tot = shape[0];
+ for (int i = 1; i < shape_len; i++) {
+ tot *= shape[i];
+ }
+
+ return tot;
+}
+
+static bool pygpu_buffer_dimensions_tot_len_compare(const Py_ssize_t *shape_a,
+ const Py_ssize_t shape_a_len,
+ const Py_ssize_t *shape_b,
+ const Py_ssize_t shape_b_len)
+{
+ if (pygpu_buffer_dimensions_tot_elem(shape_a, shape_a_len) !=
+ pygpu_buffer_dimensions_tot_elem(shape_b, shape_b_len)) {
+ PyErr_Format(PyExc_BufferError, "array size does not match");
+ return false;
+ }
+
+ return true;
+}
+
+static bool pygpu_buffer_pyobj_as_shape(PyObject *shape_obj,
+ Py_ssize_t r_shape[MAX_DIMENSIONS],
+ Py_ssize_t *r_shape_len)
{
- return (bool)memcmp(shape_a, shape_b, ndim * sizeof(Py_ssize_t));
+ Py_ssize_t shape_len = 0;
+ if (PyLong_Check(shape_obj)) {
+ shape_len = 1;
+ if (((r_shape[0] = PyLong_AsLong(shape_obj)) < 1)) {
+ PyErr_SetString(PyExc_AttributeError, "dimension must be greater than or equal to 1");
+ return false;
+ }
+ }
+ else if (PySequence_Check(shape_obj)) {
+ shape_len = PySequence_Size(shape_obj);
+ if (shape_len > MAX_DIMENSIONS) {
+ PyErr_SetString(PyExc_AttributeError,
+ "too many dimensions, max is " STRINGIFY(MAX_DIMENSIONS));
+ return false;
+ }
+ if (shape_len < 1) {
+ PyErr_SetString(PyExc_AttributeError, "sequence must have at least one dimension");
+ return false;
+ }
+
+ for (int i = 0; i < shape_len; i++) {
+ PyObject *ob = PySequence_GetItem(shape_obj, i);
+ if (!PyLong_Check(ob)) {
+ PyErr_Format(PyExc_TypeError,
+ "invalid dimension %i, expected an int, not a %.200s",
+ i,
+ Py_TYPE(ob)->tp_name);
+ Py_DECREF(ob);
+ return false;
+ }
+
+ r_shape[i] = PyLong_AsLong(ob);
+ Py_DECREF(ob);
+
+ if (r_shape[i] < 1) {
+ PyErr_SetString(PyExc_AttributeError, "dimension must be greater than or equal to 1");
+ return false;
+ }
+ }
+ }
+ else {
+ PyErr_Format(PyExc_TypeError,
+ "invalid second argument argument expected a sequence "
+ "or an int, not a %.200s",
+ Py_TYPE(shape_obj)->tp_name);
+ }
+
+ *r_shape_len = shape_len;
+ return true;
}
static const char *pygpu_buffer_formatstr(eGPUDataFormat data_format)
@@ -174,7 +247,7 @@ static PyObject *pygpu_buffer_to_list_recursive(BPyGPUBuffer *self)
return list;
}
-static PyObject *pygpu_buffer_dimensions(BPyGPUBuffer *self, void *UNUSED(arg))
+static PyObject *pygpu_buffer_dimensions_get(BPyGPUBuffer *self, void *UNUSED(arg))
{
PyObject *list = PyList_New(self->shape_len);
int i;
@@ -186,6 +259,30 @@ static PyObject *pygpu_buffer_dimensions(BPyGPUBuffer *self, void *UNUSED(arg))
return list;
}
+static int pygpu_buffer_dimensions_set(BPyGPUBuffer *self, PyObject *value, void *UNUSED(type))
+{
+ Py_ssize_t shape[MAX_DIMENSIONS];
+ Py_ssize_t shape_len = 0;
+
+ if (!pygpu_buffer_pyobj_as_shape(value, shape, &shape_len)) {
+ return -1;
+ }
+
+ if (!pygpu_buffer_dimensions_tot_len_compare(shape, shape_len, self->shape, self->shape_len)) {
+ return -1;
+ }
+
+ size_t size = shape_len * sizeof(*self->shape);
+ if (shape_len != self->shape_len) {
+ MEM_freeN(self->shape);
+ self->shape = MEM_mallocN(size, __func__);
+ }
+
+ self->shape_len = shape_len;
+ memcpy(self->shape, shape, size);
+ return 0;
+}
+
static int pygpu_buffer__tp_traverse(BPyGPUBuffer *self, visitproc visit, void *arg)
{
Py_VISIT(self->parent);
@@ -280,14 +377,13 @@ static int pygpu_buffer_ass_slice(BPyGPUBuffer *self,
return err;
}
-#define MAX_DIMENSIONS 64
static PyObject *pygpu_buffer__tp_new(PyTypeObject *UNUSED(type), PyObject *args, PyObject *kwds)
{
PyObject *length_ob, *init = NULL;
BPyGPUBuffer *buffer = NULL;
Py_ssize_t shape[MAX_DIMENSIONS];
- Py_ssize_t i, shape_len = 0;
+ Py_ssize_t shape_len = 0;
if (kwds && PyDict_Size(kwds)) {
PyErr_SetString(PyExc_TypeError, "Buffer(): takes no keyword args");
@@ -300,49 +396,7 @@ static PyObject *pygpu_buffer__tp_new(PyTypeObject *UNUSED(type), PyObject *args
return NULL;
}
- if (PyLong_Check(length_ob)) {
- shape_len = 1;
- if (((shape[0] = PyLong_AsLong(length_ob)) < 1)) {
- PyErr_SetString(PyExc_AttributeError, "dimension must be greater than or equal to 1");
- return NULL;
- }
- }
- else if (PySequence_Check(length_ob)) {
- shape_len = PySequence_Size(length_ob);
- if (shape_len > MAX_DIMENSIONS) {
- PyErr_SetString(PyExc_AttributeError,
- "too many dimensions, max is " STRINGIFY(MAX_DIMENSIONS));
- return NULL;
- }
- if (shape_len < 1) {
- PyErr_SetString(PyExc_AttributeError, "sequence must have at least one dimension");
- return NULL;
- }
-
- for (i = 0; i < shape_len; i++) {
- PyObject *ob = PySequence_GetItem(length_ob, i);
- if (!PyLong_Check(ob)) {
- PyErr_Format(PyExc_TypeError,
- "invalid dimension %i, expected an int, not a %.200s",
- i,
- Py_TYPE(ob)->tp_name);
- Py_DECREF(ob);
- return NULL;
- }
- shape[i] = PyLong_AsLong(ob);
- Py_DECREF(ob);
-
- if (shape[i] < 1) {
- PyErr_SetString(PyExc_AttributeError, "dimension must be greater than or equal to 1");
- return NULL;
- }
- }
- }
- else {
- PyErr_Format(PyExc_TypeError,
- "invalid second argument argument expected a sequence "
- "or an int, not a %.200s",
- Py_TYPE(length_ob)->tp_name);
+ if (!pygpu_buffer_pyobj_as_shape(length_ob, shape, &shape_len)) {
return NULL;
}
@@ -354,11 +408,7 @@ static PyObject *pygpu_buffer__tp_new(PyTypeObject *UNUSED(type), PyObject *args
return NULL;
}
- if (shape_len != pybuffer.ndim ||
- !pygpu_buffer_dimensions_compare(shape_len, shape, pybuffer.shape)) {
- PyErr_Format(PyExc_TypeError, "array size does not match");
- }
- else {
+ if (pygpu_buffer_dimensions_tot_len_compare(shape, shape_len, pybuffer.shape, pybuffer.ndim)) {
buffer = pygpu_buffer_make_from_data(
init, pygpu_dataformat.value_found, pybuffer.ndim, shape, pybuffer.buf);
}
@@ -518,7 +568,11 @@ static PyMethodDef pygpu_buffer__tp_methods[] = {
};
static PyGetSetDef pygpu_buffer_getseters[] = {
- {"dimensions", (getter)pygpu_buffer_dimensions, NULL, NULL, NULL},
+ {"dimensions",
+ (getter)pygpu_buffer_dimensions_get,
+ (setter)pygpu_buffer_dimensions_set,
+ NULL,
+ NULL},
{NULL, NULL, NULL, NULL, NULL},
};
@@ -625,13 +679,7 @@ static size_t pygpu_buffer_calc_size(const int format,
const int shape_len,
const Py_ssize_t *shape)
{
- size_t r_size = GPU_texture_dataformat_size(format);
-
- for (int i = 0; i < shape_len; i++) {
- r_size *= shape[i];
- }
-
- return r_size;
+ return pygpu_buffer_dimensions_tot_elem(shape, shape_len) * GPU_texture_dataformat_size(format);
}
size_t bpygpu_Buffer_size(BPyGPUBuffer *buffer)
diff --git a/source/blender/python/gpu/gpu_py_buffer.h b/source/blender/python/gpu/gpu_py_buffer.h
index 5eac5e3d309..9df22e9b780 100644
--- a/source/blender/python/gpu/gpu_py_buffer.h
+++ b/source/blender/python/gpu/gpu_py_buffer.h
@@ -30,7 +30,8 @@ extern PyTypeObject BPyGPU_BufferType;
* For Python access to GPU functions requiring a pointer.
*/
typedef struct BPyGPUBuffer {
- PyObject_VAR_HEAD PyObject *parent;
+ PyObject_VAR_HEAD
+ PyObject *parent;
int format;
int shape_len;
diff --git a/source/blender/python/gpu/gpu_py_capabilities.c b/source/blender/python/gpu/gpu_py_capabilities.c
new file mode 100644
index 00000000000..cedce485253
--- /dev/null
+++ b/source/blender/python/gpu/gpu_py_capabilities.c
@@ -0,0 +1,148 @@
+/*
+ * 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.
+ */
+
+/** \file
+ * \ingroup bpygpu
+ *
+ * - Use ``bpygpu_`` for local API.
+ * - Use ``BPyGPU`` for public API.
+ */
+
+#include <Python.h>
+
+#include "BLI_utildefines.h"
+
+#include "GPU_capabilities.h"
+
+#include "gpu_py_capabilities.h" /* own include */
+
+/* -------------------------------------------------------------------- */
+/** \name Functions
+ * \{ */
+
+static PyObject *pygpu_max_texture_size_get(PyObject *UNUSED(self))
+{
+ return PyLong_FromLong(GPU_max_texture_size());
+}
+
+static PyObject *pygpu_max_texture_layers_get(PyObject *UNUSED(self))
+{
+ return PyLong_FromLong(GPU_max_texture_layers());
+}
+
+static PyObject *pygpu_max_textures_get(PyObject *UNUSED(self))
+{
+ return PyLong_FromLong(GPU_max_textures());
+}
+
+static PyObject *pygpu_max_textures_vert_get(PyObject *UNUSED(self))
+{
+ return PyLong_FromLong(GPU_max_textures_vert());
+}
+
+static PyObject *pygpu_max_textures_geom_get(PyObject *UNUSED(self))
+{
+ return PyLong_FromLong(GPU_max_textures_geom());
+}
+
+static PyObject *pygpu_max_textures_frag_get(PyObject *UNUSED(self))
+{
+ return PyLong_FromLong(GPU_max_textures_frag());
+}
+
+static PyObject *pygpu_max_uniforms_vert_get(PyObject *UNUSED(self))
+{
+ return PyLong_FromLong(GPU_max_uniforms_vert());
+}
+
+static PyObject *pygpu_max_uniforms_frag_get(PyObject *UNUSED(self))
+{
+ return PyLong_FromLong(GPU_max_uniforms_frag());
+}
+
+static PyObject *pygpu_max_batch_indices_get(PyObject *UNUSED(self))
+{
+ return PyLong_FromLong(GPU_max_batch_indices());
+}
+
+static PyObject *pygpu_max_batch_vertices_get(PyObject *UNUSED(self))
+{
+ return PyLong_FromLong(GPU_max_batch_vertices());
+}
+
+static PyObject *pygpu_max_vertex_attribs_get(PyObject *UNUSED(self))
+{
+ return PyLong_FromLong(GPU_max_vertex_attribs());
+}
+
+static PyObject *pygpu_max_varying_floats_get(PyObject *UNUSED(self))
+{
+ return PyLong_FromLong(GPU_max_varying_floats());
+}
+
+static PyObject *pygpu_extensions_get(PyObject *UNUSED(self))
+{
+ int extensions_len = GPU_extensions_len();
+ PyObject *ret = PyTuple_New(extensions_len);
+ PyObject **ob_items = ((PyTupleObject *)ret)->ob_item;
+ for (int i = 0; i < extensions_len; i++) {
+ ob_items[i] = PyUnicode_FromString(GPU_extension_get(i));
+ }
+
+ return ret;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Module
+ * \{ */
+
+static struct PyMethodDef pygpu_capabilities__tp_methods[] = {
+ {"max_texture_size_get", (PyCFunction)pygpu_max_texture_size_get, METH_NOARGS, NULL},
+ {"max_texture_layers_get", (PyCFunction)pygpu_max_texture_layers_get, METH_NOARGS, NULL},
+ {"max_textures_get", (PyCFunction)pygpu_max_textures_get, METH_NOARGS, NULL},
+ {"max_textures_vert_get", (PyCFunction)pygpu_max_textures_vert_get, METH_NOARGS, NULL},
+ {"max_textures_geom_get", (PyCFunction)pygpu_max_textures_geom_get, METH_NOARGS, NULL},
+ {"max_textures_frag_get", (PyCFunction)pygpu_max_textures_frag_get, METH_NOARGS, NULL},
+ {"max_uniforms_vert_get", (PyCFunction)pygpu_max_uniforms_vert_get, METH_NOARGS, NULL},
+ {"max_uniforms_frag_get", (PyCFunction)pygpu_max_uniforms_frag_get, METH_NOARGS, NULL},
+ {"max_batch_indices_get", (PyCFunction)pygpu_max_batch_indices_get, METH_NOARGS, NULL},
+ {"max_batch_vertices_get", (PyCFunction)pygpu_max_batch_vertices_get, METH_NOARGS, NULL},
+ {"max_vertex_attribs_get", (PyCFunction)pygpu_max_vertex_attribs_get, METH_NOARGS, NULL},
+ {"max_varying_floats_get", (PyCFunction)pygpu_max_varying_floats_get, METH_NOARGS, NULL},
+ {"extensions_get", (PyCFunction)pygpu_extensions_get, METH_NOARGS, NULL},
+ {NULL, NULL, 0, NULL},
+};
+
+PyDoc_STRVAR(pygpu_capabilities__tp_doc, "This module provides access to the GPU capabilities.");
+static PyModuleDef pygpu_capabilities_module_def = {
+ PyModuleDef_HEAD_INIT,
+ .m_name = "gpu.capabilities",
+ .m_doc = pygpu_capabilities__tp_doc,
+ .m_methods = pygpu_capabilities__tp_methods,
+};
+
+PyObject *bpygpu_capabilities_init(void)
+{
+ PyObject *submodule;
+
+ submodule = PyModule_Create(&pygpu_capabilities_module_def);
+
+ return submodule;
+}
+
+/** \} */
diff --git a/source/blender/python/gpu/gpu_py_capabilities.h b/source/blender/python/gpu/gpu_py_capabilities.h
new file mode 100644
index 00000000000..ac138dda0c9
--- /dev/null
+++ b/source/blender/python/gpu/gpu_py_capabilities.h
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+/** \file
+ * \ingroup bpygpu
+ */
+
+#pragma once
+
+PyObject *bpygpu_capabilities_init(void);
diff --git a/source/blender/python/gpu/gpu_py_element.h b/source/blender/python/gpu/gpu_py_element.h
index a8e22aae15a..1e4e905dda6 100644
--- a/source/blender/python/gpu/gpu_py_element.h
+++ b/source/blender/python/gpu/gpu_py_element.h
@@ -25,7 +25,8 @@ extern PyTypeObject BPyGPUIndexBuf_Type;
#define BPyGPUIndexBuf_Check(v) (Py_TYPE(v) == &BPyGPUIndexBuf_Type)
typedef struct BPyGPUIndexBuf {
- PyObject_VAR_HEAD struct GPUIndexBuf *elem;
+ PyObject_VAR_HEAD
+ struct GPUIndexBuf *elem;
} BPyGPUIndexBuf;
PyObject *BPyGPUIndexBuf_CreatePyObject(struct GPUIndexBuf *elem);
diff --git a/source/blender/python/gpu/gpu_py_framebuffer.c b/source/blender/python/gpu/gpu_py_framebuffer.c
index 34d17bb10c3..0efc0713538 100644
--- a/source/blender/python/gpu/gpu_py_framebuffer.c
+++ b/source/blender/python/gpu/gpu_py_framebuffer.c
@@ -37,6 +37,8 @@
#include "gpu_py.h"
#include "gpu_py_texture.h"
+#include "gpu_py.h"
+#include "gpu_py_buffer.h"
#include "gpu_py_framebuffer.h" /* own include */
/* -------------------------------------------------------------------- */
@@ -46,13 +48,7 @@
static int pygpu_framebuffer_valid_check(BPyGPUFrameBuffer *bpygpu_fb)
{
if (UNLIKELY(bpygpu_fb->fb == NULL)) {
- PyErr_SetString(PyExc_ReferenceError,
-#ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD
- "GPU framebuffer was freed, no further access is valid"
-#else
- "GPU framebuffer: internal error"
-#endif
- );
+ PyErr_SetString(PyExc_ReferenceError, "GPU framebuffer was freed, no further access is valid");
return -1;
}
return 0;
@@ -68,10 +64,6 @@ static int pygpu_framebuffer_valid_check(BPyGPUFrameBuffer *bpygpu_fb)
static void pygpu_framebuffer_free_if_possible(GPUFrameBuffer *fb)
{
- if (!fb) {
- return;
- }
-
if (GPU_is_init()) {
GPU_framebuffer_free(fb);
}
@@ -80,6 +72,21 @@ static void pygpu_framebuffer_free_if_possible(GPUFrameBuffer *fb)
}
}
+static void pygpu_framebuffer_free_safe(BPyGPUFrameBuffer *self)
+{
+ if (self->fb) {
+#ifndef GPU_NO_USE_PY_REFERENCES
+ GPU_framebuffer_py_reference_set(self->fb, NULL);
+ if (!self->shared_reference)
+#endif
+ {
+ pygpu_framebuffer_free_if_possible(self->fb);
+ }
+
+ self->fb = NULL;
+ }
+}
+
/* Keep less than or equal to #FRAMEBUFFER_STACK_DEPTH */
#define GPU_PY_FRAMEBUFFER_STACK_LEN 16
@@ -316,9 +323,9 @@ static PyObject *pygpu_framebuffer__tp_new(PyTypeObject *UNUSED(self),
return NULL;
}
- for (int i = 1; i <= color_attachements_len; i++) {
+ for (int i = 0; i < color_attachements_len; i++) {
PyObject *o = PySequence_GetItem(color_attachements, i);
- bool ok = pygpu_framebuffer_new_parse_arg(o, &config[i]);
+ bool ok = pygpu_framebuffer_new_parse_arg(o, &config[i + 1]);
Py_DECREF(o);
if (!ok) {
return NULL;
@@ -336,7 +343,7 @@ static PyObject *pygpu_framebuffer__tp_new(PyTypeObject *UNUSED(self),
GPUFrameBuffer *fb_python = GPU_framebuffer_create("fb_python");
GPU_framebuffer_config_array(fb_python, config, color_attachements_len + 1);
- return BPyGPUFrameBuffer_CreatePyObject(fb_python);
+ return BPyGPUFrameBuffer_CreatePyObject(fb_python, false);
}
PyDoc_STRVAR(pygpu_framebuffer_is_bound_doc,
@@ -450,6 +457,100 @@ static PyObject *pygpu_framebuffer_viewport_get(BPyGPUFrameBuffer *self, void *U
return ret;
}
+PyDoc_STRVAR(
+ pygpu_framebuffer_read_color_doc,
+ ".. function:: read_color(x, y, xsize, ysize, channels, slot, format, data=data)\n"
+ "\n"
+ " Read a block of pixels from the frame buffer.\n"
+ "\n"
+ " :param x, y: Lower left corner of a rectangular block of pixels.\n"
+ " :param xsize, ysize: Dimensions of the pixel rectangle.\n"
+ " :type x, y, xsize, ysize: int\n"
+ " :param channels: Number of components to read.\n"
+ " :type channels: int\n"
+ " :param slot: The framebuffer slot to read data from.\n"
+ " :type slot: int\n"
+ " :param format: The format that describes the content of a single channel.\n"
+ " Possible values are `FLOAT`, `INT`, `UINT`, `UBYTE`, `UINT_24_8` and `10_11_11_REV`.\n"
+ " :type type: str\n"
+ " :arg data: Optional Buffer object to fill with the pixels values.\n"
+ " :type data: :class:`gpu.types.Buffer`\n"
+ " :return: The Buffer with the read pixels.\n"
+ " :rtype: :class:`gpu.types.Buffer`\n");
+static PyObject *pygpu_framebuffer_read_color(BPyGPUFrameBuffer *self,
+ PyObject *args,
+ PyObject *kwds)
+{
+ PYGPU_FRAMEBUFFER_CHECK_OBJ(self);
+ int x, y, w, h, channels;
+ uint slot;
+ struct PyC_StringEnum pygpu_dataformat = {bpygpu_dataformat_items, GPU_RGBA8};
+ BPyGPUBuffer *py_buffer = NULL;
+
+ static const char *_keywords[] = {
+ "x", "y", "xsize", "ysize", "channels", "slot", "format", "data", NULL};
+ static _PyArg_Parser _parser = {"iiiiiIO&|$O!:GPUTexture.__new__", _keywords, 0};
+ if (!_PyArg_ParseTupleAndKeywordsFast(args,
+ kwds,
+ &_parser,
+ &x,
+ &y,
+ &w,
+ &h,
+ &channels,
+ &slot,
+ PyC_ParseStringEnum,
+ &pygpu_dataformat,
+ &BPyGPU_BufferType,
+ &py_buffer)) {
+ return NULL;
+ }
+
+ if (!IN_RANGE_INCL(channels, 1, 4)) {
+ PyErr_SetString(PyExc_AttributeError, "Color channels must be 1, 2, 3 or 4");
+ return NULL;
+ }
+
+ if (slot >= BPYGPU_FB_MAX_COLOR_ATTACHMENT) {
+ PyErr_SetString(PyExc_ValueError, "slot overflow");
+ return NULL;
+ }
+
+ if (py_buffer) {
+ if (pygpu_dataformat.value_found != py_buffer->format) {
+ PyErr_SetString(PyExc_AttributeError,
+ "the format of the buffer is different from that specified");
+ return NULL;
+ }
+
+ size_t size_curr = bpygpu_Buffer_size(py_buffer);
+ size_t size_expected = w * h * channels *
+ GPU_texture_dataformat_size(pygpu_dataformat.value_found);
+ if (size_curr < size_expected) {
+ PyErr_SetString(PyExc_BufferError, "the buffer size is smaller than expected");
+ return NULL;
+ }
+ }
+ else {
+ py_buffer = BPyGPU_Buffer_CreatePyObject(
+ pygpu_dataformat.value_found, (Py_ssize_t[3]){h, w, channels}, 3, NULL);
+ BLI_assert(bpygpu_Buffer_size(py_buffer) ==
+ w * h * channels * GPU_texture_dataformat_size(pygpu_dataformat.value_found));
+ }
+
+ GPU_framebuffer_read_color(self->fb,
+ x,
+ y,
+ w,
+ h,
+ channels,
+ (int)slot,
+ pygpu_dataformat.value_found,
+ py_buffer->buf.as_void);
+
+ return (PyObject *)py_buffer;
+}
+
#ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD
PyDoc_STRVAR(pygpu_framebuffer_free_doc,
".. method:: free()\n"
@@ -459,15 +560,14 @@ PyDoc_STRVAR(pygpu_framebuffer_free_doc,
static PyObject *pygpu_framebuffer_free(BPyGPUFrameBuffer *self)
{
PYGPU_FRAMEBUFFER_CHECK_OBJ(self);
- pygpu_framebuffer_free_if_possible(self->fb);
- self->fb = NULL;
+ pygpu_framebuffer_free_safe(self);
Py_RETURN_NONE;
}
#endif
static void BPyGPUFrameBuffer__tp_dealloc(BPyGPUFrameBuffer *self)
{
- pygpu_framebuffer_free_if_possible(self->fb);
+ pygpu_framebuffer_free_safe(self);
Py_TYPE(self)->tp_free((PyObject *)self);
}
@@ -492,8 +592,12 @@ static struct PyMethodDef pygpu_framebuffer__tp_methods[] = {
pygpu_framebuffer_viewport_set_doc},
{"viewport_get",
(PyCFunction)pygpu_framebuffer_viewport_get,
- METH_VARARGS,
+ METH_NOARGS,
pygpu_framebuffer_viewport_get_doc},
+ {"read_color",
+ (PyCFunction)pygpu_framebuffer_read_color,
+ METH_VARARGS | METH_KEYWORDS,
+ pygpu_framebuffer_read_color_doc},
#ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD
{"free", (PyCFunction)pygpu_framebuffer_free, METH_NOARGS, pygpu_framebuffer_free_doc},
#endif
@@ -531,13 +635,35 @@ PyTypeObject BPyGPUFrameBuffer_Type = {
/** \name Public API
* \{ */
-PyObject *BPyGPUFrameBuffer_CreatePyObject(GPUFrameBuffer *fb)
+PyObject *BPyGPUFrameBuffer_CreatePyObject(GPUFrameBuffer *fb, bool shared_reference)
{
BPyGPUFrameBuffer *self;
+#ifndef GPU_NO_USE_PY_REFERENCES
+ if (shared_reference) {
+ void **ref = GPU_framebuffer_py_reference_get(fb);
+ if (ref) {
+ /* Retrieve BPyGPUFrameBuffer reference. */
+ self = (BPyGPUFrameBuffer *)POINTER_OFFSET(ref, -offsetof(BPyGPUFrameBuffer, fb));
+ BLI_assert(self->fb == fb);
+ Py_INCREF(self);
+ return (PyObject *)self;
+ }
+ }
+#else
+ UNUSED_VARS(shared_reference);
+#endif
+
self = PyObject_New(BPyGPUFrameBuffer, &BPyGPUFrameBuffer_Type);
self->fb = fb;
+#ifndef GPU_NO_USE_PY_REFERENCES
+ self->shared_reference = shared_reference;
+
+ BLI_assert(GPU_framebuffer_py_reference_get(fb) == NULL);
+ GPU_framebuffer_py_reference_set(fb, (void **)&self->fb);
+#endif
+
return (PyObject *)self;
}
diff --git a/source/blender/python/gpu/gpu_py_framebuffer.h b/source/blender/python/gpu/gpu_py_framebuffer.h
index 7113e7c35aa..6727328518c 100644
--- a/source/blender/python/gpu/gpu_py_framebuffer.h
+++ b/source/blender/python/gpu/gpu_py_framebuffer.h
@@ -28,6 +28,11 @@ extern PyTypeObject BPyGPUFrameBuffer_Type;
typedef struct BPyGPUFrameBuffer {
PyObject_HEAD struct GPUFrameBuffer *fb;
+
+#ifndef GPU_NO_USE_PY_REFERENCES
+ bool shared_reference;
+#endif
} BPyGPUFrameBuffer;
-PyObject *BPyGPUFrameBuffer_CreatePyObject(struct GPUFrameBuffer *fb) ATTR_NONNULL(1);
+PyObject *BPyGPUFrameBuffer_CreatePyObject(struct GPUFrameBuffer *fb, bool shared_reference)
+ ATTR_NONNULL(1);
diff --git a/source/blender/python/gpu/gpu_py_offscreen.c b/source/blender/python/gpu/gpu_py_offscreen.c
index 9e9a0b9066a..0d29bc98a31 100644
--- a/source/blender/python/gpu/gpu_py_offscreen.c
+++ b/source/blender/python/gpu/gpu_py_offscreen.c
@@ -53,6 +53,8 @@
#include "../generic/py_capi_utils.h"
#include "gpu_py.h"
+#include "gpu_py_texture.h"
+
#include "gpu_py_offscreen.h" /* own include */
/* Define the free method to avoid breakage. */
@@ -192,7 +194,7 @@ static PyObject *pygpu_offscreen_unbind(BPyGPUOffScreen *self, PyObject *args, P
BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
static const char *_keywords[] = {"restore", NULL};
- static _PyArg_Parser _parser = {"|O&:unbind", _keywords, 0};
+ static _PyArg_Parser _parser = {"|$O&:unbind", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args, kwds, &_parser, PyC_ParseBool, &restore)) {
return NULL;
}
@@ -264,6 +266,17 @@ static PyObject *pygpu_offscreen_color_texture_get(BPyGPUOffScreen *self, void *
return PyLong_FromLong(GPU_texture_opengl_bindcode(texture));
}
+PyDoc_STRVAR(pygpu_offscreen_texture_color_doc,
+ "The color texture attached.\n"
+ "\n"
+ ":type: :class:`gpu.types.GPUTexture`");
+static PyObject *pygpu_offscreen_texture_color_get(BPyGPUOffScreen *self, void *UNUSED(type))
+{
+ BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
+ GPUTexture *texture = GPU_offscreen_color_texture(self->ofs);
+ return BPyGPUTexture_CreatePyObject(texture, true);
+}
+
PyDoc_STRVAR(
pygpu_offscreen_draw_view3d_doc,
".. method:: draw_view3d(scene, view_layer, view3d, region, view_matrix, projection_matrix)\n"
@@ -385,6 +398,11 @@ static PyGetSetDef pygpu_offscreen__tp_getseters[] = {
(setter)NULL,
pygpu_offscreen_color_texture_doc,
NULL},
+ {"texture_color",
+ (getter)pygpu_offscreen_texture_color_get,
+ (setter)NULL,
+ pygpu_offscreen_texture_color_doc,
+ NULL},
{"width", (getter)pygpu_offscreen_width_get, (setter)NULL, pygpu_offscreen_width_doc, NULL},
{"height", (getter)pygpu_offscreen_height_get, (setter)NULL, pygpu_offscreen_height_doc, NULL},
{NULL, NULL, NULL, NULL, NULL} /* Sentinel */
diff --git a/source/blender/python/gpu/gpu_py_platform.c b/source/blender/python/gpu/gpu_py_platform.c
new file mode 100644
index 00000000000..e49ad18dfd8
--- /dev/null
+++ b/source/blender/python/gpu/gpu_py_platform.c
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ */
+
+/** \file
+ * \ingroup bpygpu
+ *
+ * - Use ``bpygpu_`` for local API.
+ * - Use ``BPyGPU`` for public API.
+ */
+
+#include <Python.h>
+
+#include "BLI_utildefines.h"
+
+#include "GPU_platform.h"
+
+#include "gpu_py_platform.h" /* own include */
+
+/* -------------------------------------------------------------------- */
+/** \name Functions
+ * \{ */
+
+static PyObject *pygpu_platform_vendor_get(PyObject *UNUSED(self))
+{
+ return PyUnicode_FromString(GPU_platform_vendor());
+}
+
+static PyObject *pygpu_platform_renderer_get(PyObject *UNUSED(self))
+{
+ return PyUnicode_FromString(GPU_platform_renderer());
+}
+
+static PyObject *pygpu_platform_version_get(PyObject *UNUSED(self))
+{
+ return PyUnicode_FromString(GPU_platform_version());
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Module
+ * \{ */
+
+static struct PyMethodDef pygpu_platform__tp_methods[] = {
+ {"vendor_get", (PyCFunction)pygpu_platform_vendor_get, METH_NOARGS, NULL},
+ {"renderer_get", (PyCFunction)pygpu_platform_renderer_get, METH_NOARGS, NULL},
+ {"version_get", (PyCFunction)pygpu_platform_version_get, METH_NOARGS, NULL},
+ {NULL, NULL, 0, NULL},
+};
+
+PyDoc_STRVAR(pygpu_platform__tp_doc, "This module provides access to GPU Platform definitions.");
+static PyModuleDef pygpu_platform_module_def = {
+ PyModuleDef_HEAD_INIT,
+ .m_name = "gpu.platform",
+ .m_doc = pygpu_platform__tp_doc,
+ .m_methods = pygpu_platform__tp_methods,
+};
+
+PyObject *bpygpu_platform_init(void)
+{
+ PyObject *submodule;
+
+ submodule = PyModule_Create(&pygpu_platform_module_def);
+
+ return submodule;
+}
+
+/** \} */
diff --git a/source/blender/compositor/intern/COM_SocketReader.cc b/source/blender/python/gpu/gpu_py_platform.h
index 93c8a143b86..19e3e41fb49 100644
--- a/source/blender/compositor/intern/COM_SocketReader.cc
+++ b/source/blender/python/gpu/gpu_py_platform.h
@@ -12,8 +12,12 @@
* 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.
- *
- * Copyright 2011, Blender Foundation.
*/
-#include "COM_SocketReader.h"
+/** \file
+ * \ingroup bpygpu
+ */
+
+#pragma once
+
+PyObject *bpygpu_platform_init(void);
diff --git a/source/blender/python/gpu/gpu_py_shader.h b/source/blender/python/gpu/gpu_py_shader.h
index 745b9351649..b15caa3e66c 100644
--- a/source/blender/python/gpu/gpu_py_shader.h
+++ b/source/blender/python/gpu/gpu_py_shader.h
@@ -25,7 +25,8 @@ extern PyTypeObject BPyGPUShader_Type;
#define BPyGPUShader_Check(v) (Py_TYPE(v) == &BPyGPUShader_Type)
typedef struct BPyGPUShader {
- PyObject_VAR_HEAD struct GPUShader *shader;
+ PyObject_VAR_HEAD
+ struct GPUShader *shader;
bool is_builtin;
} BPyGPUShader;
diff --git a/source/blender/python/gpu/gpu_py_state.c b/source/blender/python/gpu/gpu_py_state.c
index 110f5a6ff55..173c5afba56 100644
--- a/source/blender/python/gpu/gpu_py_state.c
+++ b/source/blender/python/gpu/gpu_py_state.c
@@ -25,11 +25,13 @@
#include <Python.h>
+#include "GPU_framebuffer.h"
#include "GPU_state.h"
#include "../generic/py_capi_utils.h"
#include "../generic/python_utildefines.h"
+#include "gpu_py_framebuffer.h"
#include "gpu_py_state.h" /* own include */
/* -------------------------------------------------------------------- */
@@ -85,19 +87,20 @@ PyDoc_STRVAR(
" Defines the fixed pipeline blending equation.\n"
"\n"
" :param mode: The type of blend mode.\n"
- " * ``NONE`` No blending.\n"
- " * ``ALPHA`` The original color channels are interpolated according to the alpha value.\n"
- " * ``ALPHA_PREMULT`` The original color channels are interpolated according to the alpha "
- "value with the new colors pre-multiplied by this value.\n"
- " * ``ADDITIVE`` The original color channels are added by the corresponding ones.\n"
- " * ``ADDITIVE_PREMULT`` The original color channels are added by the corresponding ones "
+ " * ``NONE`` No blending.\n"
+ " * ``ALPHA`` The original color channels are interpolated according to the alpha "
+ "value.\n"
+ " * ``ALPHA_PREMULT`` The original color channels are interpolated according to the "
+ "alpha value with the new colors pre-multiplied by this value.\n"
+ " * ``ADDITIVE`` The original color channels are added by the corresponding ones.\n"
+ " * ``ADDITIVE_PREMULT`` The original color channels are added by the corresponding ones "
"that are pre-multiplied by the alpha value.\n"
- " * ``MULTIPLY`` The original color channels are multiplied by the corresponding ones.\n"
- " * ``SUBTRACT`` The original color channels are subtracted by the corresponding ones.\n"
- " * ``INVERT`` The original color channels are replaced by its complementary color.\n"
- //" * ``OIT``.\n"
- //" * ``BACKGROUND`` .\n"
- //" * ``CUSTOM`` .\n"
+ " * ``MULTIPLY`` The original color channels are multiplied by the corresponding ones.\n"
+ " * ``SUBTRACT`` The original color channels are subtracted by the corresponding ones.\n"
+ " * ``INVERT`` The original color channels are replaced by its complementary color.\n"
+ //" * ``OIT``.\n"
+ //" * ``BACKGROUND`` .\n"
+ //" * ``CUSTOM`` .\n"
" :type mode: str\n");
static PyObject *pygpu_state_blend_set(PyObject *UNUSED(self), PyObject *value)
{
@@ -333,6 +336,16 @@ static PyObject *pygpu_state_program_point_size_set(PyObject *UNUSED(self), PyOb
Py_RETURN_NONE;
}
+PyDoc_STRVAR(pygpu_state_framebuffer_active_get_doc,
+ ".. function:: framebuffer_active_get(enable)\n"
+ "\n"
+ " Return the active framefuffer in context.\n");
+static PyObject *pygpu_state_framebuffer_active_get(PyObject *UNUSED(self))
+{
+ GPUFrameBuffer *fb = GPU_framebuffer_active_get();
+ return BPyGPUFrameBuffer_CreatePyObject(fb, true);
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -395,6 +408,10 @@ static struct PyMethodDef pygpu_state__tp_methods[] = {
(PyCFunction)pygpu_state_program_point_size_set,
METH_O,
pygpu_state_program_point_size_set_doc},
+ {"active_framebuffer_get",
+ (PyCFunction)pygpu_state_framebuffer_active_get,
+ METH_NOARGS,
+ pygpu_state_framebuffer_active_get_doc},
{NULL, NULL, 0, NULL},
};
diff --git a/source/blender/python/gpu/gpu_py_texture.c b/source/blender/python/gpu/gpu_py_texture.c
index 1ae65c1dd11..2181c09b537 100644
--- a/source/blender/python/gpu/gpu_py_texture.c
+++ b/source/blender/python/gpu/gpu_py_texture.c
@@ -247,7 +247,7 @@ static PyObject *pygpu_texture__tp_new(PyTypeObject *UNUSED(self), PyObject *arg
return NULL;
}
- return BPyGPUTexture_CreatePyObject(tex);
+ return BPyGPUTexture_CreatePyObject(tex, false);
}
PyDoc_STRVAR(pygpu_texture_width_doc, "Width of the texture.\n\n:type: `int`");
@@ -349,11 +349,12 @@ PyDoc_STRVAR(pygpu_texture_read_doc,
static PyObject *pygpu_texture_read(BPyGPUTexture *self)
{
BPYGPU_TEXTURE_CHECK_OBJ(self);
+ eGPUTextureFormat tex_format = GPU_texture_format(self->tex);
/* #GPU_texture_read is restricted in combining 'data_format' with 'tex_format'.
* So choose data_format here. */
eGPUDataFormat best_data_format;
- switch (GPU_texture_format(self->tex)) {
+ switch (tex_format) {
case GPU_DEPTH_COMPONENT24:
case GPU_DEPTH_COMPONENT16:
case GPU_DEPTH_COMPONENT32F:
@@ -389,8 +390,12 @@ static PyObject *pygpu_texture_read(BPyGPUTexture *self)
}
void *buf = GPU_texture_read(self->tex, best_data_format, 0);
- const Py_ssize_t shape[2] = {GPU_texture_height(self->tex), GPU_texture_width(self->tex)};
- return (PyObject *)BPyGPU_Buffer_CreatePyObject(best_data_format, shape, ARRAY_SIZE(shape), buf);
+ const Py_ssize_t shape[3] = {GPU_texture_height(self->tex),
+ GPU_texture_width(self->tex),
+ GPU_texture_component_len(tex_format)};
+
+ int shape_len = (shape[2] == 1) ? 2 : 3;
+ return (PyObject *)BPyGPU_Buffer_CreatePyObject(best_data_format, shape, shape_len, buf);
}
#ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD
@@ -412,6 +417,9 @@ static PyObject *pygpu_texture_free(BPyGPUTexture *self)
static void BPyGPUTexture__tp_dealloc(BPyGPUTexture *self)
{
if (self->tex) {
+#ifndef GPU_NO_USE_PY_REFERENCES
+ GPU_texture_py_reference_set(self->tex, NULL);
+#endif
GPU_texture_free(self->tex);
}
Py_TYPE(self)->tp_free((PyObject *)self);
@@ -535,10 +543,7 @@ static PyObject *pygpu_texture_from_image(PyObject *UNUSED(self), PyObject *arg)
BKE_imageuser_default(&iuser);
GPUTexture *tex = BKE_image_get_gpu_texture(ima, &iuser, NULL);
- /* Increase the texture reference count. */
- GPU_texture_ref(tex);
-
- return BPyGPUTexture_CreatePyObject(tex);
+ return BPyGPUTexture_CreatePyObject(tex, true);
}
static struct PyMethodDef pygpu_texture__m_methods[] = {
@@ -595,13 +600,33 @@ PyObject *bpygpu_texture_init(void)
/** \name Public API
* \{ */
-PyObject *BPyGPUTexture_CreatePyObject(GPUTexture *tex)
+PyObject *BPyGPUTexture_CreatePyObject(GPUTexture *tex, bool shared_reference)
{
BPyGPUTexture *self;
+ if (shared_reference) {
+#ifndef GPU_NO_USE_PY_REFERENCES
+ void **ref = GPU_texture_py_reference_get(tex);
+ if (ref) {
+ /* Retrieve BPyGPUTexture reference. */
+ self = (BPyGPUTexture *)POINTER_OFFSET(ref, -offsetof(BPyGPUTexture, tex));
+ BLI_assert(self->tex == tex);
+ Py_INCREF(self);
+ return (PyObject *)self;
+ }
+#endif
+
+ GPU_texture_ref(tex);
+ }
+
self = PyObject_New(BPyGPUTexture, &BPyGPUTexture_Type);
self->tex = tex;
+#ifndef GPU_NO_USE_PY_REFERENCES
+ BLI_assert(GPU_texture_py_reference_get(tex) == NULL);
+ GPU_texture_py_reference_set(tex, (void **)&self->tex);
+#endif
+
return (PyObject *)self;
}
diff --git a/source/blender/python/gpu/gpu_py_texture.h b/source/blender/python/gpu/gpu_py_texture.h
index 5130273f971..3eaaa3411bd 100644
--- a/source/blender/python/gpu/gpu_py_texture.h
+++ b/source/blender/python/gpu/gpu_py_texture.h
@@ -33,4 +33,5 @@ typedef struct BPyGPUTexture {
int bpygpu_ParseTexture(PyObject *o, void *p);
PyObject *bpygpu_texture_init(void);
-PyObject *BPyGPUTexture_CreatePyObject(struct GPUTexture *tex) ATTR_NONNULL(1);
+PyObject *BPyGPUTexture_CreatePyObject(struct GPUTexture *tex, bool shared_reference)
+ ATTR_NONNULL(1);
diff --git a/source/blender/python/gpu/gpu_py_vertex_buffer.h b/source/blender/python/gpu/gpu_py_vertex_buffer.h
index 8adf2aa0792..d105faf9bc1 100644
--- a/source/blender/python/gpu/gpu_py_vertex_buffer.h
+++ b/source/blender/python/gpu/gpu_py_vertex_buffer.h
@@ -28,8 +28,8 @@ extern PyTypeObject BPyGPUVertBuf_Type;
typedef struct BPyGPUVertBuf {
PyObject_VAR_HEAD
- /* The buf is owned, we may support thin wrapped batches later. */
- struct GPUVertBuf *buf;
+ /* The buf is owned, we may support thin wrapped batches later. */
+ struct GPUVertBuf *buf;
} BPyGPUVertBuf;
PyObject *BPyGPUVertBuf_CreatePyObject(struct GPUVertBuf *buf) ATTR_NONNULL(1);
diff --git a/source/blender/python/gpu/gpu_py_vertex_format.h b/source/blender/python/gpu/gpu_py_vertex_format.h
index 54d090e2923..6c99d13cf03 100644
--- a/source/blender/python/gpu/gpu_py_vertex_format.h
+++ b/source/blender/python/gpu/gpu_py_vertex_format.h
@@ -27,7 +27,8 @@ extern PyTypeObject BPyGPUVertFormat_Type;
#define BPyGPUVertFormat_Check(v) (Py_TYPE(v) == &BPyGPUVertFormat_Type)
typedef struct BPyGPUVertFormat {
- PyObject_VAR_HEAD struct GPUVertFormat fmt;
+ PyObject_VAR_HEAD
+ struct GPUVertFormat fmt;
} BPyGPUVertFormat;
PyObject *BPyGPUVertFormat_CreatePyObject(struct GPUVertFormat *fmt);
diff --git a/source/blender/python/intern/CMakeLists.txt b/source/blender/python/intern/CMakeLists.txt
index 9ac8d4d9f47..2be2105d327 100644
--- a/source/blender/python/intern/CMakeLists.txt
+++ b/source/blender/python/intern/CMakeLists.txt
@@ -79,6 +79,7 @@ set(SRC
bpy_rna_driver.c
bpy_rna_gizmo.c
bpy_rna_id_collection.c
+ bpy_rna_operator.c
bpy_rna_types_capi.c
bpy_rna_ui.c
bpy_traceback.c
@@ -118,6 +119,7 @@ set(SRC
bpy_rna_driver.h
bpy_rna_gizmo.h
bpy_rna_id_collection.h
+ bpy_rna_operator.h
bpy_rna_types_capi.h
bpy_rna_ui.h
bpy_traceback.h
diff --git a/source/blender/python/intern/bpy.c b/source/blender/python/intern/bpy.c
index 547cf2ad38f..dc9b8b52821 100644
--- a/source/blender/python/intern/bpy.c
+++ b/source/blender/python/intern/bpy.c
@@ -118,7 +118,7 @@ static PyObject *bpy_blend_paths(PyObject *UNUSED(self), PyObject *args, PyObjec
bool local = false;
static const char *_keywords[] = {"absolute", "packed", "local", NULL};
- static _PyArg_Parser _parser = {"|O&O&O&:blend_paths", _keywords, 0};
+ static _PyArg_Parser _parser = {"|$O&O&O&:blend_paths", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kw,
&_parser,
@@ -164,8 +164,8 @@ static PyObject *bpy_user_resource(PyObject *UNUSED(self), PyObject *args, PyObj
const char *path;
- static const char *_keywords[] = {"type", "subdir", NULL};
- static _PyArg_Parser _parser = {"O&|s:user_resource", _keywords, 0};
+ static const char *_keywords[] = {"type", "path", NULL};
+ static _PyArg_Parser _parser = {"O&|$s:user_resource", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args, kw, &_parser, PyC_ParseStringEnum, &type, &subdir)) {
return NULL;
}
@@ -201,7 +201,7 @@ static PyObject *bpy_system_resource(PyObject *UNUSED(self), PyObject *args, PyO
const char *path;
static const char *_keywords[] = {"type", "path", NULL};
- static _PyArg_Parser _parser = {"O&|s:system_resource", _keywords, 0};
+ static _PyArg_Parser _parser = {"O&|$s:system_resource", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args, kw, &_parser, PyC_ParseStringEnum, &type, &subdir)) {
return NULL;
}
@@ -239,7 +239,7 @@ static PyObject *bpy_resource_path(PyObject *UNUSED(self), PyObject *args, PyObj
const char *path;
static const char *_keywords[] = {"type", "major", "minor", NULL};
- static _PyArg_Parser _parser = {"O&|ii:resource_path", _keywords, 0};
+ static _PyArg_Parser _parser = {"O&|$ii:resource_path", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(
args, kw, &_parser, PyC_ParseStringEnum, &type, &major, &minor)) {
return NULL;
diff --git a/source/blender/python/intern/bpy_app.c b/source/blender/python/intern/bpy_app.c
index 927ec11c376..4de6063098b 100644
--- a/source/blender/python/intern/bpy_app.c
+++ b/source/blender/python/intern/bpy_app.c
@@ -82,7 +82,10 @@ static PyTypeObject BlenderAppType;
static PyStructSequence_Field app_info_fields[] = {
{"version", "The Blender version as a tuple of 3 numbers. eg. (2, 83, 1)"},
- {"version_file", "The blend file version, compatible with ``bpy.data.version``"},
+ {"version_file",
+ "The Blender version, as a tuple, last used to save a .blend file, compatible with "
+ "``bpy.data.version``. This value should be used for handling compatibility changes between "
+ "Blender versions"},
{"version_string", "The Blender version formatted as a string"},
{"version_cycle", "The release status of this build alpha/beta/rc/release"},
{"version_char", "Deprecated, always an empty string"},
diff --git a/source/blender/python/intern/bpy_app_handlers.c b/source/blender/python/intern/bpy_app_handlers.c
index 8ecee9b3f2e..bc05c51414f 100644
--- a/source/blender/python/intern/bpy_app_handlers.c
+++ b/source/blender/python/intern/bpy_app_handlers.c
@@ -43,6 +43,9 @@ void bpy_app_generic_callback(struct Main *main,
static PyTypeObject BlenderAppCbType;
+/**
+ * See `BKE_callbacks.h` #eCbEvent declaration for the policy on naming.
+ */
static PyStructSequence_Field app_cb_info_fields[] = {
{"frame_change_pre",
"Called after frame change for playback and rendering, before any data is evaluated for the "
@@ -74,6 +77,7 @@ static PyStructSequence_Field app_cb_info_fields[] = {
{"version_update", "on ending the versioning code"},
{"load_factory_preferences_post", "on loading factory preferences (after)"},
{"load_factory_startup_post", "on loading factory startup (after)"},
+ {"xr_session_start_pre", "on starting an xr session (before)"},
/* sets the permanent tag */
#define APP_CB_OTHER_FIELDS 1
diff --git a/source/blender/python/intern/bpy_capi_utils.h b/source/blender/python/intern/bpy_capi_utils.h
index 0854713982d..d7ee1eedabb 100644
--- a/source/blender/python/intern/bpy_capi_utils.h
+++ b/source/blender/python/intern/bpy_capi_utils.h
@@ -33,11 +33,6 @@ struct ReportList;
char *BPy_enum_as_string(const struct EnumPropertyItem *item);
-#define BLANK_PYTHON_TYPE \
- { \
- PyVarObject_HEAD_INIT(NULL, 0) NULL \
- }
-
/* error reporting */
short BPy_reports_to_error(struct ReportList *reports, PyObject *exception, const bool clear);
void BPy_reports_write_stdout(const struct ReportList *reports, const char *header);
diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c
index 5f31e0bb74d..f95e655d0c6 100644
--- a/source/blender/python/intern/bpy_interface.c
+++ b/source/blender/python/intern/bpy_interface.c
@@ -167,6 +167,14 @@ void bpy_context_clear(bContext *UNUSED(C), const PyGILState_STATE *gilstate)
}
}
+static void bpy_context_end(bContext *C)
+{
+ if (UNLIKELY(C == NULL)) {
+ return;
+ }
+ CTX_wm_operator_poll_msg_clear(C);
+}
+
/**
* Use for `CTX_*_set(..)` functions need to set values which are later read back as expected.
* In this case we don't want the Python context to override the values as it causes problems
@@ -392,7 +400,7 @@ void BPY_python_start(bContext *C, int argc, const char **argv)
/* Needed for Python's initialization for portable Python installations.
* We could use #Py_SetPath, but this overrides Python's internal logic
- * for calculating it's own module search paths.
+ * for calculating its own module search paths.
*
* `sys.executable` is overwritten after initialization to the Python binary. */
{
@@ -524,6 +532,9 @@ void BPY_python_end(void)
/* finalizing, no need to grab the state, except when we are a module */
gilstate = PyGILState_Ensure();
+ /* Clear Python values in the context so freeing the context after Python exits doesn't crash. */
+ bpy_context_end(BPY_context_get());
+
/* Decrement user counts of all callback functions. */
BPY_rna_props_clear_all();
diff --git a/source/blender/python/intern/bpy_interface_run.c b/source/blender/python/intern/bpy_interface_run.c
index a7593ae7d79..f7272a87a17 100644
--- a/source/blender/python/intern/bpy_interface_run.c
+++ b/source/blender/python/intern/bpy_interface_run.c
@@ -31,6 +31,7 @@
#include "BKE_context.h"
#include "BKE_main.h"
+#include "BKE_report.h"
#include "BKE_text.h"
#include "DNA_text_types.h"
@@ -294,13 +295,47 @@ bool BPY_run_string_exec(bContext *C, const char *imports[], const char *expr)
* in code that doesn't deal with Python data-types.
* \{ */
+static void run_string_handle_error(struct BPy_RunErrInfo *err_info)
+{
+ if (err_info == NULL) {
+ PyErr_Print();
+ PyErr_Clear();
+ return;
+ }
+
+ /* Signal to do nothing. */
+ if (!(err_info->reports || err_info->r_string)) {
+ PyErr_Clear();
+ return;
+ }
+
+ PyObject *py_err_str = err_info->use_single_line_error ? PyC_ExceptionBuffer_Simple() :
+ PyC_ExceptionBuffer();
+ const char *err_str = py_err_str ? PyUnicode_AsUTF8(py_err_str) : "Unable to extract exception";
+
+ if (err_info->reports != NULL) {
+ if (err_info->report_prefix) {
+ BKE_reportf(err_info->reports, RPT_ERROR, "%s: %s", err_info->report_prefix, err_str);
+ }
+ else {
+ BKE_report(err_info->reports, RPT_ERROR, err_str);
+ }
+ }
+
+ if (err_info->r_string != NULL) {
+ *err_info->r_string = BLI_strdup(err_str);
+ }
+
+ Py_XDECREF(py_err_str);
+}
+
/**
* \return success
*/
bool BPY_run_string_as_number(bContext *C,
const char *imports[],
const char *expr,
- const char *report_prefix,
+ struct BPy_RunErrInfo *err_info,
double *r_value)
{
PyGILState_STATE gilstate;
@@ -320,12 +355,7 @@ bool BPY_run_string_as_number(bContext *C,
ok = PyC_RunString_AsNumber(imports, expr, "<expr as number>", r_value);
if (ok == false) {
- if (report_prefix != NULL) {
- BPy_errors_to_report_ex(CTX_wm_reports(C), report_prefix, false, false);
- }
- else {
- PyErr_Clear();
- }
+ run_string_handle_error(err_info);
}
bpy_context_clear(C, &gilstate);
@@ -339,7 +369,7 @@ bool BPY_run_string_as_number(bContext *C,
bool BPY_run_string_as_string_and_size(bContext *C,
const char *imports[],
const char *expr,
- const char *report_prefix,
+ struct BPy_RunErrInfo *err_info,
char **r_value,
size_t *r_value_size)
{
@@ -357,12 +387,7 @@ bool BPY_run_string_as_string_and_size(bContext *C,
ok = PyC_RunString_AsStringAndSize(imports, expr, "<expr as str>", r_value, r_value_size);
if (ok == false) {
- if (report_prefix != NULL) {
- BPy_errors_to_report_ex(CTX_wm_reports(C), false, false, report_prefix);
- }
- else {
- PyErr_Clear();
- }
+ run_string_handle_error(err_info);
}
bpy_context_clear(C, &gilstate);
@@ -373,12 +398,11 @@ bool BPY_run_string_as_string_and_size(bContext *C,
bool BPY_run_string_as_string(bContext *C,
const char *imports[],
const char *expr,
- const char *report_prefix,
+ struct BPy_RunErrInfo *err_info,
char **r_value)
{
size_t value_dummy_size;
- return BPY_run_string_as_string_and_size(
- C, imports, expr, report_prefix, r_value, &value_dummy_size);
+ return BPY_run_string_as_string_and_size(C, imports, expr, err_info, r_value, &value_dummy_size);
}
/**
@@ -389,7 +413,7 @@ bool BPY_run_string_as_string(bContext *C,
bool BPY_run_string_as_intptr(bContext *C,
const char *imports[],
const char *expr,
- const char *report_prefix,
+ struct BPy_RunErrInfo *err_info,
intptr_t *r_value)
{
BLI_assert(r_value && expr);
@@ -406,12 +430,7 @@ bool BPY_run_string_as_intptr(bContext *C,
ok = PyC_RunString_AsIntPtr(imports, expr, "<expr as intptr>", r_value);
if (ok == false) {
- if (report_prefix != NULL) {
- BPy_errors_to_report_ex(CTX_wm_reports(C), report_prefix, false, false);
- }
- else {
- PyErr_Clear();
- }
+ run_string_handle_error(err_info);
}
bpy_context_clear(C, &gilstate);
diff --git a/source/blender/python/intern/bpy_library_load.c b/source/blender/python/intern/bpy_library_load.c
index 96ff6a111d9..7a688b8c77d 100644
--- a/source/blender/python/intern/bpy_library_load.c
+++ b/source/blender/python/intern/bpy_library_load.c
@@ -195,7 +195,7 @@ static PyObject *bpy_lib_load(BPy_PropertyRNA *self, PyObject *args, PyObject *k
bool is_rel = false, is_link = false, use_assets_only = false;
static const char *_keywords[] = {"filepath", "link", "relative", "assets_only", NULL};
- static _PyArg_Parser _parser = {"s|O&O&O&:load", _keywords, 0};
+ static _PyArg_Parser _parser = {"s|$O&O&O&:load", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kw,
&_parser,
diff --git a/source/blender/python/intern/bpy_msgbus.c b/source/blender/python/intern/bpy_msgbus.c
index 7cc7f864c9c..4e6d2aacb49 100644
--- a/source/blender/python/intern/bpy_msgbus.c
+++ b/source/blender/python/intern/bpy_msgbus.c
@@ -245,7 +245,7 @@ static PyObject *bpy_msgbus_subscribe_rna(PyObject *UNUSED(self), PyObject *args
"options",
NULL,
};
- static _PyArg_Parser _parser = {"OOO!O|O!:subscribe_rna", _keywords, 0};
+ static _PyArg_Parser _parser = {"OOO!O|$O!:subscribe_rna", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kw,
&_parser,
diff --git a/source/blender/python/intern/bpy_operator.c b/source/blender/python/intern/bpy_operator.c
index 94ad6a8ef78..4a5e2552598 100644
--- a/source/blender/python/intern/bpy_operator.c
+++ b/source/blender/python/intern/bpy_operator.c
@@ -244,12 +244,16 @@ static PyObject *pyop_call(PyObject *UNUSED(self), PyObject *args)
}
if (WM_operator_poll_context((bContext *)C, ot, context) == false) {
- const char *msg = CTX_wm_operator_poll_msg_get(C);
+ bool msg_free = false;
+ const char *msg = CTX_wm_operator_poll_msg_get(C, &msg_free);
PyErr_Format(PyExc_RuntimeError,
"Operator bpy.ops.%.200s.poll() %.200s",
opname,
msg ? msg : "failed, context is incorrect");
- CTX_wm_operator_poll_msg_set(C, NULL); /* better set to NULL else it could be used again */
+ CTX_wm_operator_poll_msg_clear(C);
+ if (msg_free) {
+ MEM_freeN((void *)msg);
+ }
error_val = -1;
}
else {
diff --git a/source/blender/python/intern/bpy_props.c b/source/blender/python/intern/bpy_props.c
index e4e6b3ea8f2..6ad80f9160b 100644
--- a/source/blender/python/intern/bpy_props.c
+++ b/source/blender/python/intern/bpy_props.c
@@ -147,7 +147,17 @@ static const EnumPropertyItem property_subtype_number_items[] = {
{PROP_PERCENTAGE, "PERCENTAGE", 0, "Percentage", ""},
{PROP_FACTOR, "FACTOR", 0, "Factor", ""},
{PROP_ANGLE, "ANGLE", 0, "Angle", ""},
- {PROP_TIME, "TIME", 0, "Time", ""},
+ {PROP_TIME,
+ "TIME",
+ 0,
+ "Time (Scene Relative)",
+ "Time specified in frames, converted to seconds based on scene frame rate"},
+ {PROP_TIME_ABSOLUTE,
+ "TIME_ABSOLUTE",
+ 0,
+ "Time (Absolute)",
+ "Time specified in seconds, independent of the scene"},
+ {PROP_TIME_ABSOLUTE, "TIME_ABSOLUTE", 0, "Time Absolute", ""},
{PROP_DISTANCE, "DISTANCE", 0, "Distance", ""},
{PROP_DISTANCE_CAMERA, "DISTANCE_CAMERA", 0, "Camera Distance", ""},
{PROP_POWER, "POWER", 0, "Power", ""},
@@ -2464,7 +2474,7 @@ static PyObject *BPy_BoolProperty(PyObject *self, PyObject *args, PyObject *kw)
"set",
NULL,
};
- static _PyArg_Parser _parser = {"s#|ssO&O!O!O!sOOO:BoolProperty", _keywords, 0};
+ static _PyArg_Parser _parser = {"s#|$ssO&O!O!O!sOOO:BoolProperty", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kw,
&_parser,
@@ -2583,7 +2593,7 @@ static PyObject *BPy_BoolVectorProperty(PyObject *self, PyObject *args, PyObject
"set",
NULL,
};
- static _PyArg_Parser _parser = {"s#|ssOO!O!O!siOOO:BoolVectorProperty", _keywords, 0};
+ static _PyArg_Parser _parser = {"s#|$ssOO!O!O!siOOO:BoolVectorProperty", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kw,
&_parser,
@@ -2728,7 +2738,7 @@ static PyObject *BPy_IntProperty(PyObject *self, PyObject *args, PyObject *kw)
"set",
NULL,
};
- static _PyArg_Parser _parser = {"s#|ssiiiiiiO!O!O!sOOO:IntProperty", _keywords, 0};
+ static _PyArg_Parser _parser = {"s#|$ssiiiiiiO!O!O!sOOO:IntProperty", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kw,
&_parser,
@@ -2865,7 +2875,7 @@ static PyObject *BPy_IntVectorProperty(PyObject *self, PyObject *args, PyObject
"set",
NULL,
};
- static _PyArg_Parser _parser = {"s#|ssOiiiiiO!O!O!siOOO:IntVectorProperty", _keywords, 0};
+ static _PyArg_Parser _parser = {"s#|$ssOiiiiiO!O!O!siOOO:IntVectorProperty", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kw,
&_parser,
@@ -3005,7 +3015,7 @@ static PyObject *BPy_FloatProperty(PyObject *self, PyObject *args, PyObject *kw)
"soft_max", "step", "precision", "options", "override", "tags", "subtype",
"unit", "update", "get", "set", NULL,
};
- static _PyArg_Parser _parser = {"s#|ssffffffiO!O!O!ssOOO:FloatProperty", _keywords, 0};
+ static _PyArg_Parser _parser = {"s#|$ssffffffiO!O!O!ssOOO:FloatProperty", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kw,
&_parser,
@@ -3138,7 +3148,7 @@ static PyObject *BPy_FloatVectorProperty(PyObject *self, PyObject *args, PyObjec
"soft_max", "step", "precision", "options", "override", "tags", "subtype",
"unit", "size", "update", "get", "set", NULL,
};
- static _PyArg_Parser _parser = {"s#|ssOfffffiO!O!O!ssiOOO:FloatVectorProperty", _keywords, 0};
+ static _PyArg_Parser _parser = {"s#|$ssOfffffiO!O!O!ssiOOO:FloatVectorProperty", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kw,
&_parser,
@@ -3287,7 +3297,7 @@ static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw
"set",
NULL,
};
- static _PyArg_Parser _parser = {"s#|sssiO!O!O!sOOO:StringProperty", _keywords, 0};
+ static _PyArg_Parser _parser = {"s#|$sssiO!O!O!sOOO:StringProperty", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kw,
&_parser,
@@ -3445,7 +3455,7 @@ static PyObject *BPy_EnumProperty(PyObject *self, PyObject *args, PyObject *kw)
"set",
NULL,
};
- static _PyArg_Parser _parser = {"s#O|ssOO!O!O!OOO:EnumProperty", _keywords, 0};
+ static _PyArg_Parser _parser = {"s#O|$ssOO!O!O!OOO:EnumProperty", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kw,
&_parser,
@@ -3628,7 +3638,7 @@ PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *kw)
"update",
NULL,
};
- static _PyArg_Parser _parser = {"s#O|ssO!O!O!OO:PointerProperty", _keywords, 0};
+ static _PyArg_Parser _parser = {"s#O|$ssO!O!O!OO:PointerProperty", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kw,
&_parser,
@@ -3731,7 +3741,7 @@ PyObject *BPy_CollectionProperty(PyObject *self, PyObject *args, PyObject *kw)
"tags",
NULL,
};
- static _PyArg_Parser _parser = {"s#O|ssO!O!O!:CollectionProperty", _keywords, 0};
+ static _PyArg_Parser _parser = {"s#O|$ssO!O!O!:CollectionProperty", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kw,
&_parser,
diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c
index 49ac2662a31..fb1cb823964 100644
--- a/source/blender/python/intern/bpy_rna.c
+++ b/source/blender/python/intern/bpy_rna.c
@@ -2546,7 +2546,7 @@ static int pyrna_prop_collection_subscript_str_lib_pair_ptr(BPy_PropertyRNA *sel
if (lib == NULL) {
if (err_not_found) {
PyErr_Format(PyExc_KeyError,
- "%s: lib name '%.240s' "
+ "%s: lib filepath '%.1024s' "
"does not reference a valid library",
err_prefix,
keylib_str);
@@ -3562,7 +3562,7 @@ PyDoc_STRVAR(pyrna_struct_keys_doc,
" dictionary function of the same name).\n"
"\n"
" :return: custom property keys.\n"
- " :rtype: list of strings\n"
+ " :rtype: :class:`idprop.type.IDPropertyGroupViewKeys`\n"
"\n" BPY_DOC_ID_PROP_TYPE_NOTE);
static PyObject *pyrna_struct_keys(BPy_PropertyRNA *self)
{
@@ -3573,13 +3573,9 @@ static PyObject *pyrna_struct_keys(BPy_PropertyRNA *self)
return NULL;
}
+ /* `group` may be NULL. */
group = RNA_struct_idprops(&self->ptr, 0);
-
- if (group == NULL) {
- return PyList_New(0);
- }
-
- return BPy_Wrap_GetKeys(group);
+ return BPy_Wrap_GetKeys_View_WithID(self->ptr.owner_id, group);
}
PyDoc_STRVAR(pyrna_struct_items_doc,
@@ -3589,7 +3585,7 @@ PyDoc_STRVAR(pyrna_struct_items_doc,
" dictionary function of the same name).\n"
"\n"
" :return: custom property key, value pairs.\n"
- " :rtype: list of key, value tuples\n"
+ " :rtype: :class:`idprop.type.IDPropertyGroupViewItems`\n"
"\n" BPY_DOC_ID_PROP_TYPE_NOTE);
static PyObject *pyrna_struct_items(BPy_PropertyRNA *self)
{
@@ -3600,13 +3596,9 @@ static PyObject *pyrna_struct_items(BPy_PropertyRNA *self)
return NULL;
}
+ /* `group` may be NULL. */
group = RNA_struct_idprops(&self->ptr, 0);
-
- if (group == NULL) {
- return PyList_New(0);
- }
-
- return BPy_Wrap_GetItems(self->ptr.owner_id, group);
+ return BPy_Wrap_GetItems_View_WithID(self->ptr.owner_id, group);
}
PyDoc_STRVAR(pyrna_struct_values_doc,
@@ -3616,7 +3608,7 @@ PyDoc_STRVAR(pyrna_struct_values_doc,
" dictionary function of the same name).\n"
"\n"
" :return: custom property values.\n"
- " :rtype: list\n"
+ " :rtype: :class:`idprop.type.IDPropertyGroupViewValues`\n"
"\n" BPY_DOC_ID_PROP_TYPE_NOTE);
static PyObject *pyrna_struct_values(BPy_PropertyRNA *self)
{
@@ -3628,13 +3620,9 @@ static PyObject *pyrna_struct_values(BPy_PropertyRNA *self)
return NULL;
}
+ /* `group` may be NULL. */
group = RNA_struct_idprops(&self->ptr, 0);
-
- if (group == NULL) {
- return PyList_New(0);
- }
-
- return BPy_Wrap_GetValues(self->ptr.owner_id, group);
+ return BPy_Wrap_GetValues_View_WithID(self->ptr.owner_id, group);
}
PyDoc_STRVAR(pyrna_struct_is_property_set_doc,
@@ -4207,6 +4195,10 @@ static void pyrna_dir_members_rna(PyObject *list, PointerRNA *ptr)
iterprop = RNA_struct_iterator_property(ptr->type);
RNA_PROP_BEGIN (ptr, itemptr, iterprop) {
+ /* Custom-properties are exposed using `__getitem__`, exclude from `__dir__`. */
+ if (RNA_property_is_idprop(itemptr.data)) {
+ continue;
+ }
nameptr = RNA_struct_name_get_alloc(&itemptr, name, sizeof(name), &namelen);
if (nameptr) {
@@ -5002,8 +4994,13 @@ static PyObject *pyrna_struct_pop(BPy_StructRNA *self, PyObject *args)
idprop = IDP_GetPropertyFromGroup(group, key);
if (idprop) {
- PyObject *ret = BPy_IDGroup_WrapData(self->ptr.owner_id, idprop, group);
- IDP_RemoveFromGroup(group, idprop);
+ /* Don't use #BPy_IDGroup_WrapData as the id-property is being removed from the ID. */
+ PyObject *ret = BPy_IDGroup_MapDataToPy(idprop);
+ /* Internal error. */
+ if (UNLIKELY(ret == NULL)) {
+ return NULL;
+ }
+ IDP_FreeFromGroup(group, idprop);
return ret;
}
}
diff --git a/source/blender/python/intern/bpy_rna_anim.c b/source/blender/python/intern/bpy_rna_anim.c
index b7648ee830f..b359e93315e 100644
--- a/source/blender/python/intern/bpy_rna_anim.c
+++ b/source/blender/python/intern/bpy_rna_anim.c
@@ -324,7 +324,7 @@ PyObject *pyrna_struct_keyframe_insert(BPy_StructRNA *self, PyObject *args, PyOb
if (pyrna_struct_keyframe_parse(&self->ptr,
args,
kw,
- "s|ifsO!:bpy_struct.keyframe_insert()",
+ "s|$ifsO!:bpy_struct.keyframe_insert()",
"bpy_struct.keyframe_insert()",
&path_full,
&index,
@@ -443,7 +443,7 @@ PyObject *pyrna_struct_keyframe_delete(BPy_StructRNA *self, PyObject *args, PyOb
if (pyrna_struct_keyframe_parse(&self->ptr,
args,
kw,
- "s|ifsO!:bpy_struct.keyframe_delete()",
+ "s|$ifsO!:bpy_struct.keyframe_delete()",
"bpy_struct.keyframe_insert()",
&path_full,
&index,
diff --git a/source/blender/python/intern/bpy_rna_id_collection.c b/source/blender/python/intern/bpy_rna_id_collection.c
index d4127b26629..ac061c3dd60 100644
--- a/source/blender/python/intern/bpy_rna_id_collection.c
+++ b/source/blender/python/intern/bpy_rna_id_collection.c
@@ -171,7 +171,7 @@ static PyObject *bpy_user_map(PyObject *UNUSED(self), PyObject *args, PyObject *
IDUserMapData data_cb = {NULL};
static const char *_keywords[] = {"subset", "key_types", "value_types", NULL};
- static _PyArg_Parser _parser = {"|O$O!O!:user_map", _keywords, 0};
+ static _PyArg_Parser _parser = {"|$OO!O!:user_map", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(
args, kwds, &_parser, &subset, &PySet_Type, &key_types, &PySet_Type, &val_types)) {
return NULL;
diff --git a/source/blender/python/intern/bpy_rna_operator.c b/source/blender/python/intern/bpy_rna_operator.c
new file mode 100644
index 00000000000..490d9aa5212
--- /dev/null
+++ b/source/blender/python/intern/bpy_rna_operator.c
@@ -0,0 +1,152 @@
+/*
+ * 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.
+ */
+
+/** \file
+ * \ingroup pythonintern
+ *
+ * This file extends `bpy.types.Operator` with C/Python API methods and attributes.
+ */
+
+#include <Python.h>
+
+#include "BLI_string.h"
+
+#include "BKE_context.h"
+
+#include "../generic/python_utildefines.h"
+
+#include "BPY_extern.h"
+#include "bpy_capi_utils.h"
+
+#include "bpy_rna_operator.h" /* Own include. */
+
+/* -------------------------------------------------------------------- */
+/** \name Operator `poll_message_set` Method
+ * \{ */
+
+static char *pyop_poll_message_get_fn(bContext *UNUSED(C), void *user_data)
+{
+ PyGILState_STATE gilstate = PyGILState_Ensure();
+
+ PyObject *py_args = user_data;
+ PyObject *py_func_or_msg = PyTuple_GET_ITEM(py_args, 0);
+
+ if (PyUnicode_Check(py_func_or_msg)) {
+ return BLI_strdup(PyUnicode_AsUTF8(py_func_or_msg));
+ }
+
+ PyObject *py_args_after_first = PyTuple_GetSlice(py_args, 1, PY_SSIZE_T_MAX);
+ PyObject *py_msg = PyObject_CallObject(py_func_or_msg, py_args_after_first);
+ Py_DECREF(py_args_after_first);
+
+ char *msg = NULL;
+ bool error = false;
+
+ /* NULL for no string. */
+ if (py_msg == NULL) {
+ error = true;
+ }
+ else {
+ if (py_msg == Py_None) {
+ /* pass */
+ }
+ else if (PyUnicode_Check(py_msg)) {
+ msg = BLI_strdup(PyUnicode_AsUTF8(py_msg));
+ }
+ else {
+ PyErr_Format(PyExc_TypeError,
+ "poll_message_set(function, ...): expected string or None, got %.200s",
+ Py_TYPE(py_msg)->tp_name);
+ error = true;
+ }
+ Py_DECREF(py_msg);
+ }
+
+ if (error) {
+ PyErr_Print();
+ PyErr_Clear();
+ }
+
+ PyGILState_Release(gilstate);
+ return msg;
+}
+
+static void pyop_poll_message_free_fn(bContext *UNUSED(C), void *user_data)
+{
+ /* Handles the GIL. */
+ BPY_DECREF(user_data);
+}
+
+PyDoc_STRVAR(BPY_rna_operator_poll_message_set_doc,
+ ".. method:: poll_message_set(message, ...)\n"
+ "\n"
+ " Set the message to show in the tool-tip when poll fails.\n"
+ "\n"
+ " When message is callable, "
+ "additional user defined positional arguments are passed to the message function.\n"
+ "\n"
+ " :param message: The message or a function that returns the message.\n"
+ " :type message: string or a callable that returns a string or None.\n");
+
+static PyObject *BPY_rna_operator_poll_message_set(PyObject *UNUSED(self), PyObject *args)
+{
+ const ssize_t args_len = PyTuple_GET_SIZE(args);
+ if (args_len == 0) {
+ PyErr_SetString(PyExc_ValueError,
+ "poll_message_set(message, ...): requires a message argument");
+ return NULL;
+ }
+
+ PyObject *py_func_or_msg = PyTuple_GET_ITEM(args, 0);
+
+ if (PyUnicode_Check(py_func_or_msg)) {
+ if (args_len > 1) {
+ PyErr_SetString(PyExc_ValueError,
+ "poll_message_set(message): does not support additional arguments");
+ return NULL;
+ }
+ }
+ else if (PyCallable_Check(py_func_or_msg)) {
+ /* pass */
+ }
+ else {
+ PyErr_Format(PyExc_TypeError,
+ "poll_message_set(message, ...): "
+ "expected at least 1 string or callable argument, got %.200s",
+ Py_TYPE(py_func_or_msg)->tp_name);
+ return NULL;
+ }
+
+ bContext *C = BPY_context_get();
+ struct bContextPollMsgDyn_Params params = {
+ .get_fn = pyop_poll_message_get_fn,
+ .free_fn = pyop_poll_message_free_fn,
+ .user_data = Py_INCREF_RET(args),
+ };
+
+ CTX_wm_operator_poll_msg_set_dynamic(C, &params);
+
+ Py_RETURN_NONE;
+}
+
+PyMethodDef BPY_rna_operator_poll_message_set_method_def = {
+ "poll_message_set",
+ (PyCFunction)BPY_rna_operator_poll_message_set,
+ METH_VARARGS | METH_STATIC,
+ BPY_rna_operator_poll_message_set_doc,
+};
+
+/** \} */
diff --git a/source/blender/python/intern/bpy_rna_operator.h b/source/blender/python/intern/bpy_rna_operator.h
new file mode 100644
index 00000000000..8040d8a492a
--- /dev/null
+++ b/source/blender/python/intern/bpy_rna_operator.h
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+/** \file
+ * \ingroup pythonintern
+ */
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern PyMethodDef BPY_rna_operator_poll_message_set_method_def;
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/python/intern/bpy_rna_types_capi.c b/source/blender/python/intern/bpy_rna_types_capi.c
index 9b15e84663d..2f6e197d1e2 100644
--- a/source/blender/python/intern/bpy_rna_types_capi.c
+++ b/source/blender/python/intern/bpy_rna_types_capi.c
@@ -41,6 +41,8 @@
#include "bpy_rna_types_capi.h"
#include "bpy_rna_ui.h"
+#include "bpy_rna_operator.h"
+
#include "../generic/py_capi_utils.h"
#include "RNA_access.h"
@@ -87,6 +89,17 @@ static struct PyMethodDef pyrna_uilayout_methods[] = {
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Operator
+ * \{ */
+
+static struct PyMethodDef pyrna_operator_methods[] = {
+ {NULL, NULL, 0, NULL}, /* #BPY_rna_operator_poll_message_set */
+ {NULL, NULL, 0, NULL},
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Window Manager Clipboard Property
*
* Avoid using the RNA API because this value may change between checking its length
@@ -228,6 +241,11 @@ void BPY_rna_types_extend_capi(void)
/* Space */
pyrna_struct_type_extend_capi(&RNA_Space, pyrna_space_methods, NULL);
+ /* wmOperator */
+ ARRAY_SET_ITEMS(pyrna_operator_methods, BPY_rna_operator_poll_message_set_method_def);
+ BLI_assert(ARRAY_SIZE(pyrna_operator_methods) == 2);
+ pyrna_struct_type_extend_capi(&RNA_Operator, pyrna_operator_methods, NULL);
+
/* WindowManager */
pyrna_struct_type_extend_capi(
&RNA_WindowManager, pyrna_windowmanager_methods, pyrna_windowmanager_getset);
diff --git a/source/blender/python/intern/bpy_utils_units.c b/source/blender/python/intern/bpy_utils_units.c
index b460ef5e0c0..e5ac1ba9a95 100644
--- a/source/blender/python/intern/bpy_utils_units.c
+++ b/source/blender/python/intern/bpy_utils_units.c
@@ -183,7 +183,7 @@ static PyObject *bpyunits_to_value(PyObject *UNUSED(self), PyObject *args, PyObj
"str_ref_unit",
NULL,
};
- static _PyArg_Parser _parser = {"sss#|z:to_value", _keywords, 0};
+ static _PyArg_Parser _parser = {"sss#|$z:to_value", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(
args, kw, &_parser, &usys_str, &ucat_str, &inpt, &str_len, &uref)) {
return NULL;
@@ -260,7 +260,7 @@ static PyObject *bpyunits_to_string(PyObject *UNUSED(self), PyObject *args, PyOb
"compatible_unit",
NULL,
};
- static _PyArg_Parser _parser = {"ssd|iO&O&:to_string", _keywords, 0};
+ static _PyArg_Parser _parser = {"ssd|$iO&O&:to_string", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kw,
&_parser,
diff --git a/source/blender/python/mathutils/mathutils.c b/source/blender/python/mathutils/mathutils.c
index 3791a6c2d29..16bf7120606 100644
--- a/source/blender/python/mathutils/mathutils.c
+++ b/source/blender/python/mathutils/mathutils.c
@@ -152,7 +152,7 @@ int mathutils_array_parse(
return -1;
}
- memcpy(array, ((BaseMathObject *)value)->data, size * sizeof(float));
+ memcpy(array, ((const BaseMathObject *)value)->data, size * sizeof(float));
}
else
#endif
@@ -235,7 +235,7 @@ int mathutils_array_parse_alloc(float **array,
}
*array = PyMem_Malloc(size * sizeof(float));
- memcpy(*array, ((BaseMathObject *)value)->data, size * sizeof(float));
+ memcpy(*array, ((const BaseMathObject *)value)->data, size * sizeof(float));
return size;
}
@@ -471,7 +471,7 @@ int mathutils_any_to_rotmat(float rmat[3][3], PyObject *value, const char *error
return -1;
}
- eulO_to_mat3(rmat, ((EulerObject *)value)->eul, ((EulerObject *)value)->order);
+ eulO_to_mat3(rmat, ((const EulerObject *)value)->eul, ((const EulerObject *)value)->order);
return 0;
}
if (QuaternionObject_Check(value)) {
@@ -480,7 +480,7 @@ int mathutils_any_to_rotmat(float rmat[3][3], PyObject *value, const char *error
}
float tquat[4];
- normalize_qt_qt(tquat, ((QuaternionObject *)value)->quat);
+ normalize_qt_qt(tquat, ((const QuaternionObject *)value)->quat);
quat_to_mat3(rmat, tquat);
return 0;
}
@@ -530,8 +530,8 @@ int EXPP_FloatsAreEqual(float af, float bf, int maxDiff)
{
/* solid, fast routine across all platforms
* with constant time behavior */
- const int ai = *(int *)(&af);
- const int bi = *(int *)(&bf);
+ const int ai = *(const int *)(&af);
+ const int bi = *(const int *)(&bf);
const int test = SIGNMASK(ai ^ bi);
int diff, v1, v2;
diff --git a/source/blender/python/mathutils/mathutils.h b/source/blender/python/mathutils/mathutils.h
index 75aa08ee1df..80be841785a 100644
--- a/source/blender/python/mathutils/mathutils.h
+++ b/source/blender/python/mathutils/mathutils.h
@@ -52,7 +52,8 @@ enum {
#define BASE_MATH_MEMBERS(_data) \
/** Array of data (alias), wrapped status depends on wrapped status. */ \
- PyObject_VAR_HEAD float *_data; \
+ PyObject_VAR_HEAD \
+ float *_data; \
/** If this vector references another object, otherwise NULL, *Note* this owns its reference */ \
PyObject *cb_user; \
/** Which user funcs do we adhere to, RNA, etc */ \
diff --git a/source/blender/python/mathutils/mathutils_Euler.c b/source/blender/python/mathutils/mathutils_Euler.c
index f2a8af18073..b6a0183d04e 100644
--- a/source/blender/python/mathutils/mathutils_Euler.c
+++ b/source/blender/python/mathutils/mathutils_Euler.c
@@ -88,7 +88,7 @@ short euler_order_from_string(const char *str, const char *error_prefix)
# define MAKE_ID3(a, b, c) (((a) << 24) | ((b) << 16) | ((c) << 8))
#endif
- switch (*((PY_INT32_T *)str)) {
+ switch (*((const PY_INT32_T *)str)) {
case MAKE_ID3('X', 'Y', 'Z'):
return EULER_ORDER_XYZ;
case MAKE_ID3('X', 'Z', 'Y'):
diff --git a/source/blender/python/mathutils/mathutils_Matrix.c b/source/blender/python/mathutils/mathutils_Matrix.c
index 161d2f41592..5d38a3692c3 100644
--- a/source/blender/python/mathutils/mathutils_Matrix.c
+++ b/source/blender/python/mathutils/mathutils_Matrix.c
@@ -187,7 +187,7 @@ static int mathutils_matrix_col_get(BaseMathObject *bmo, int col)
}
/* for 'translation' size will always be '3' even on 4x4 vec */
- num_row = min_ii(self->num_row, ((VectorObject *)bmo)->size);
+ num_row = min_ii(self->num_row, ((const VectorObject *)bmo)->size);
for (row = 0; row < num_row; row++) {
bmo->data[row] = MATRIX_ITEM(self, row, col);
@@ -210,7 +210,7 @@ static int mathutils_matrix_col_set(BaseMathObject *bmo, int col)
}
/* for 'translation' size will always be '3' even on 4x4 vec */
- num_row = min_ii(self->num_row, ((VectorObject *)bmo)->size);
+ num_row = min_ii(self->num_row, ((const VectorObject *)bmo)->size);
for (row = 0; row < num_row; row++) {
MATRIX_ITEM(self, row, col) = bmo->data[row];
@@ -969,6 +969,104 @@ static PyObject *C_Matrix_Shear(PyObject *cls, PyObject *args)
return Matrix_CreatePyObject(mat, matSize, matSize, (PyTypeObject *)cls);
}
+PyDoc_STRVAR(
+ C_Matrix_LocRotScale_doc,
+ ".. classmethod:: LocRotScale(location, rotation, scale)\n"
+ "\n"
+ " Create a matrix combining translation, rotation and scale,\n"
+ " acting as the inverse of the decompose() method.\n"
+ "\n"
+ " Any of the inputs may be replaced with None if not needed.\n"
+ "\n"
+ " :arg location: The translation component.\n"
+ " :type location: :class:`Vector` or None\n"
+ " :arg rotation: The rotation component.\n"
+ " :type rotation: 3x3 :class:`Matrix`, :class:`Quaternion`, :class:`Euler` or None\n"
+ " :arg scale: The scale component.\n"
+ " :type scale: :class:`Vector` or None\n"
+ " :return: Combined transformation matrix. \n"
+ " :rtype: 4x4 :class:`Matrix`\n");
+static PyObject *C_Matrix_LocRotScale(PyObject *cls, PyObject *args)
+{
+ PyObject *loc_obj, *rot_obj, *scale_obj;
+ float mat[4][4], loc[3];
+
+ if (!PyArg_ParseTuple(args, "OOO:Matrix.LocRotScale", &loc_obj, &rot_obj, &scale_obj)) {
+ return NULL;
+ }
+
+ /* Decode location. */
+ if (loc_obj == Py_None) {
+ zero_v3(loc);
+ }
+ else if (mathutils_array_parse(
+ loc, 3, 3, loc_obj, "Matrix.LocRotScale(), invalid location argument") == -1) {
+ return NULL;
+ }
+
+ /* Decode rotation. */
+ if (rot_obj == Py_None) {
+ unit_m4(mat);
+ }
+ else if (QuaternionObject_Check(rot_obj)) {
+ QuaternionObject *quat_obj = (QuaternionObject *)rot_obj;
+
+ if (BaseMath_ReadCallback(quat_obj) == -1) {
+ return NULL;
+ }
+
+ quat_to_mat4(mat, quat_obj->quat);
+ }
+ else if (EulerObject_Check(rot_obj)) {
+ EulerObject *eul_obj = (EulerObject *)rot_obj;
+
+ if (BaseMath_ReadCallback(eul_obj) == -1) {
+ return NULL;
+ }
+
+ eulO_to_mat4(mat, eul_obj->eul, eul_obj->order);
+ }
+ else if (MatrixObject_Check(rot_obj)) {
+ MatrixObject *mat_obj = (MatrixObject *)rot_obj;
+
+ if (BaseMath_ReadCallback(mat_obj) == -1) {
+ return NULL;
+ }
+
+ if (mat_obj->num_col == 3 && mat_obj->num_row == 3) {
+ copy_m4_m3(mat, (const float(*)[3])mat_obj->matrix);
+ }
+ else {
+ PyErr_SetString(PyExc_ValueError,
+ "Matrix.LocRotScale(): "
+ "inappropriate rotation matrix size - expects 3x3 matrix");
+ return NULL;
+ }
+ }
+ else {
+ PyErr_SetString(PyExc_ValueError,
+ "Matrix.LocRotScale(): "
+ "rotation argument must be Matrix, Quaternion, Euler or None");
+ return NULL;
+ }
+
+ /* Decode scale. */
+ if (scale_obj != Py_None) {
+ float scale[3];
+
+ if (mathutils_array_parse(
+ scale, 3, 3, scale_obj, "Matrix.LocRotScale(), invalid scale argument") == -1) {
+ return NULL;
+ }
+
+ rescale_m4(mat, scale);
+ }
+
+ copy_v3_v3(mat[3], loc);
+
+ return Matrix_CreatePyObject(&mat[0][0], 4, 4, (PyTypeObject *)cls);
+}
+
void matrix_as_3x3(float mat[3][3], MatrixObject *self)
{
copy_v3_v3(mat[0], MATRIX_COL_PTR(self, 0));
@@ -1029,7 +1127,7 @@ static float matrix_determinant_internal(const MatrixObject *self)
MATRIX_ITEM(self, 2, 2));
}
- return determinant_m4((float(*)[4])self->matrix);
+ return determinant_m4((const float(*)[4])self->matrix);
}
static void adjoint_matrix_n(float *mat_dst, const float *mat_src, const ushort dim)
@@ -1037,15 +1135,15 @@ static void adjoint_matrix_n(float *mat_dst, const float *mat_src, const ushort
/* calculate the classical adjoint */
switch (dim) {
case 2: {
- adjoint_m2_m2((float(*)[2])mat_dst, (float(*)[2])mat_src);
+ adjoint_m2_m2((float(*)[2])mat_dst, (const float(*)[2])mat_src);
break;
}
case 3: {
- adjoint_m3_m3((float(*)[3])mat_dst, (float(*)[3])mat_src);
+ adjoint_m3_m3((float(*)[3])mat_dst, (const float(*)[3])mat_src);
break;
}
case 4: {
- adjoint_m4_m4((float(*)[4])mat_dst, (float(*)[4])mat_src);
+ adjoint_m4_m4((float(*)[4])mat_dst, (const float(*)[4])mat_src);
break;
}
default:
@@ -1115,7 +1213,7 @@ static void matrix_invert_safe_internal(const MatrixObject *self, float *r_mat)
float(*mat)[2] = (float(*)[2])in_mat;
if (in_mat != self->matrix) {
- copy_m2_m2(mat, (float(*)[2])self->matrix);
+ copy_m2_m2(mat, (const float(*)[2])self->matrix);
}
mat[0][0] += eps;
mat[1][1] += eps;
@@ -1130,7 +1228,7 @@ static void matrix_invert_safe_internal(const MatrixObject *self, float *r_mat)
float(*mat)[3] = (float(*)[3])in_mat;
if (in_mat != self->matrix) {
- copy_m3_m3(mat, (float(*)[3])self->matrix);
+ copy_m3_m3(mat, (const float(*)[3])self->matrix);
}
mat[0][0] += eps;
mat[1][1] += eps;
@@ -1146,7 +1244,7 @@ static void matrix_invert_safe_internal(const MatrixObject *self, float *r_mat)
float(*mat)[4] = (float(*)[4])in_mat;
if (in_mat != self->matrix) {
- copy_m4_m4(mat, (float(*)[4])self->matrix);
+ copy_m4_m4(mat, (const float(*)[4])self->matrix);
}
mat[0][0] += eps;
mat[1][1] += eps;
@@ -1194,7 +1292,7 @@ static PyObject *Matrix_to_quaternion(MatrixObject *self)
mat3_to_quat(quat, (float(*)[3])self->matrix);
}
else {
- mat4_to_quat(quat, (float(*)[4])self->matrix);
+ mat4_to_quat(quat, (const float(*)[4])self->matrix);
}
return Quaternion_CreatePyObject(quat, NULL);
@@ -1243,10 +1341,10 @@ static PyObject *Matrix_to_euler(MatrixObject *self, PyObject *args)
/*must be 3-4 cols, 3-4 rows, square matrix */
if (self->num_row == 3 && self->num_col == 3) {
- copy_m3_m3(mat, (float(*)[3])self->matrix);
+ copy_m3_m3(mat, (const float(*)[3])self->matrix);
}
else if (self->num_row == 4 && self->num_col == 4) {
- copy_m3_m4(mat, (float(*)[4])self->matrix);
+ copy_m3_m4(mat, (const float(*)[4])self->matrix);
}
else {
PyErr_SetString(PyExc_ValueError,
@@ -1321,7 +1419,7 @@ static PyObject *Matrix_resize_4x4(MatrixObject *self)
memcpy(mat[col], MATRIX_COL_PTR(self, col), self->num_row * sizeof(float));
}
- copy_m4_m4((float(*)[4])self->matrix, (float(*)[4])mat);
+ copy_m4_m4((float(*)[4])self->matrix, (const float(*)[4])mat);
self->num_col = 4;
self->num_row = 4;
@@ -1479,7 +1577,7 @@ static bool matrix_invert_args_check(const MatrixObject *self, PyObject *args, b
return true;
case 1:
if (check_type) {
- const MatrixObject *fallback = (MatrixObject *)PyTuple_GET_ITEM(args, 0);
+ const MatrixObject *fallback = (const MatrixObject *)PyTuple_GET_ITEM(args, 0);
if (!MatrixObject_Check(fallback)) {
PyErr_SetString(PyExc_TypeError,
"Matrix.invert: "
@@ -1797,7 +1895,7 @@ static PyObject *Matrix_decompose(MatrixObject *self)
return NULL;
}
- mat4_to_loc_rot_size(loc, rot, size, (float(*)[4])self->matrix);
+ mat4_to_loc_rot_size(loc, rot, size, (const float(*)[4])self->matrix);
mat3_to_quat(quat, rot);
ret = PyTuple_New(3);
@@ -2059,7 +2157,7 @@ static PyObject *Matrix_identity(MatrixObject *self)
static PyObject *Matrix_copy_notest(MatrixObject *self, const float *matrix)
{
- return Matrix_CreatePyObject((float *)matrix, self->num_col, self->num_row, Py_TYPE(self));
+ return Matrix_CreatePyObject((const float *)matrix, self->num_col, self->num_row, Py_TYPE(self));
}
PyDoc_STRVAR(Matrix_copy_doc,
@@ -2155,7 +2253,7 @@ static PyObject *Matrix_str(MatrixObject *self)
for (col = 0; col < self->num_col; col++) {
maxsize[col] = 0;
for (row = 0; row < self->num_row; row++) {
- const int size = BLI_snprintf(
+ const int size = BLI_snprintf_rlen(
dummy_buf, sizeof(dummy_buf), "%.4f", MATRIX_ITEM(self, row, col));
maxsize[col] = max_ii(maxsize[col], size);
}
@@ -2960,10 +3058,10 @@ static PyObject *Matrix_is_negative_get(MatrixObject *self, void *UNUSED(closure
/*must be 3-4 cols, 3-4 rows, square matrix*/
if (self->num_row == 4 && self->num_col == 4) {
- return PyBool_FromLong(is_negative_m4((float(*)[4])self->matrix));
+ return PyBool_FromLong(is_negative_m4((const float(*)[4])self->matrix));
}
if (self->num_row == 3 && self->num_col == 3) {
- return PyBool_FromLong(is_negative_m3((float(*)[3])self->matrix));
+ return PyBool_FromLong(is_negative_m3((const float(*)[3])self->matrix));
}
PyErr_SetString(PyExc_AttributeError,
@@ -2982,10 +3080,10 @@ static PyObject *Matrix_is_orthogonal_get(MatrixObject *self, void *UNUSED(closu
/*must be 3-4 cols, 3-4 rows, square matrix*/
if (self->num_row == 4 && self->num_col == 4) {
- return PyBool_FromLong(is_orthonormal_m4((float(*)[4])self->matrix));
+ return PyBool_FromLong(is_orthonormal_m4((const float(*)[4])self->matrix));
}
if (self->num_row == 3 && self->num_col == 3) {
- return PyBool_FromLong(is_orthonormal_m3((float(*)[3])self->matrix));
+ return PyBool_FromLong(is_orthonormal_m3((const float(*)[3])self->matrix));
}
PyErr_SetString(PyExc_AttributeError,
@@ -3005,10 +3103,10 @@ static PyObject *Matrix_is_orthogonal_axis_vectors_get(MatrixObject *self, void
/*must be 3-4 cols, 3-4 rows, square matrix*/
if (self->num_row == 4 && self->num_col == 4) {
- return PyBool_FromLong(is_orthogonal_m4((float(*)[4])self->matrix));
+ return PyBool_FromLong(is_orthogonal_m4((const float(*)[4])self->matrix));
}
if (self->num_row == 3 && self->num_col == 3) {
- return PyBool_FromLong(is_orthogonal_m3((float(*)[3])self->matrix));
+ return PyBool_FromLong(is_orthogonal_m3((const float(*)[3])self->matrix));
}
PyErr_SetString(PyExc_AttributeError,
@@ -3111,6 +3209,10 @@ static struct PyMethodDef Matrix_methods[] = {
(PyCFunction)C_Matrix_OrthoProjection,
METH_VARARGS | METH_CLASS,
C_Matrix_OrthoProjection_doc},
+ {"LocRotScale",
+ (PyCFunction)C_Matrix_LocRotScale,
+ METH_VARARGS | METH_CLASS,
+ C_Matrix_LocRotScale_doc},
{NULL, NULL, 0, NULL},
};
diff --git a/source/blender/python/mathutils/mathutils_bvhtree.c b/source/blender/python/mathutils/mathutils_bvhtree.c
index 1acbcc006ca..b00dbacad15 100644
--- a/source/blender/python/mathutils/mathutils_bvhtree.c
+++ b/source/blender/python/mathutils/mathutils_bvhtree.c
@@ -434,7 +434,7 @@ static void py_bvhtree_nearest_point_range_cb(void *userdata,
struct PyBVH_RangeData *data = userdata;
PyBVHTree *self = data->self;
- const float(*coords)[3] = (const float(*)[3])self->coords;
+ const float(*coords)[3] = self->coords;
const uint *tri = self->tris[index];
const float *tri_co[3] = {coords[tri[0]], coords[tri[1]], coords[tri[2]]};
float nearest_tmp[3], dist_sq;
@@ -961,8 +961,6 @@ static PyObject *C_BVHTree_FromBMesh(PyObject *UNUSED(cls), PyObject *args, PyOb
/* Get data for tessellation */
{
- int tris_len_dummy;
-
coords_len = (uint)bm->totvert;
tris_len = (uint)poly_to_tri_count(bm->totface, bm->totloop);
@@ -971,8 +969,7 @@ static PyObject *C_BVHTree_FromBMesh(PyObject *UNUSED(cls), PyObject *args, PyOb
looptris = MEM_mallocN(sizeof(*looptris) * (size_t)tris_len, __func__);
- BM_mesh_calc_tessellation(bm, looptris, &tris_len_dummy);
- BLI_assert(tris_len_dummy == (int)tris_len);
+ BM_mesh_calc_tessellation(bm, looptris);
}
{
diff --git a/source/blender/python/mathutils/mathutils_geometry.c b/source/blender/python/mathutils/mathutils_geometry.c
index 4b09c08f62c..1304447be65 100644
--- a/source/blender/python/mathutils/mathutils_geometry.c
+++ b/source/blender/python/mathutils/mathutils_geometry.c
@@ -349,7 +349,7 @@ static PyObject *M_Geometry_normal(PyObject *UNUSED(self), PyObject *args)
goto finally;
}
- normal_poly_v3(n, (const float(*)[3])coords, coords_len);
+ normal_poly_v3(n, coords, coords_len);
ret = Vector_CreatePyObject(n, 3, NULL);
finally:
diff --git a/source/blender/python/mathutils/mathutils_interpolate.c b/source/blender/python/mathutils/mathutils_interpolate.c
index 6abb66899d5..bb6a7720c44 100644
--- a/source/blender/python/mathutils/mathutils_interpolate.c
+++ b/source/blender/python/mathutils/mathutils_interpolate.c
@@ -54,24 +54,15 @@ static PyObject *M_Interpolate_poly_3d_calc(PyObject *UNUSED(self), PyObject *ar
PyObject *point, *veclist, *ret;
int i;
- if (!PyArg_ParseTuple(args, "OO!:poly_3d_calc", &veclist, &vector_Type, &point)) {
+ if (!PyArg_ParseTuple(args, "OO:poly_3d_calc", &veclist, &point)) {
return NULL;
}
- if (BaseMath_ReadCallback((VectorObject *)point) == -1) {
+ if (mathutils_array_parse(
+ fp, 2, 3 | MU_ARRAY_ZERO, point, "pt must be a 2-3 dimensional vector") == -1) {
return NULL;
}
- fp[0] = ((VectorObject *)point)->vec[0];
- fp[1] = ((VectorObject *)point)->vec[1];
- if (((VectorObject *)point)->size > 2) {
- fp[2] = ((VectorObject *)point)->vec[2];
- }
- else {
- /* if its a 2d vector then set the z to be zero */
- fp[2] = 0.0f;
- }
-
len = mathutils_array_parse_alloc_v(((float **)&vecs), 3, veclist, __func__);
if (len == -1) {
return NULL;
diff --git a/source/blender/python/mathutils/mathutils_kdtree.c b/source/blender/python/mathutils/mathutils_kdtree.c
index fe8f9ec0334..1ff574fefa8 100644
--- a/source/blender/python/mathutils/mathutils_kdtree.c
+++ b/source/blender/python/mathutils/mathutils_kdtree.c
@@ -52,7 +52,7 @@ static void kdtree_nearest_to_py_tuple(const KDTreeNearest_3d *nearest, PyObject
BLI_assert(PyTuple_GET_SIZE(py_retval) == 3);
PyTuple_SET_ITEMS(py_retval,
- Vector_CreatePyObject((float *)nearest->co, 3, NULL),
+ Vector_CreatePyObject(nearest->co, 3, NULL),
PyLong_FromLong(nearest->index),
PyFloat_FromDouble(nearest->dist));
}
@@ -222,7 +222,7 @@ static PyObject *py_kdtree_find(PyKDTree *self, PyObject *args, PyObject *kwargs
const char *keywords[] = {"co", "filter", NULL};
if (!PyArg_ParseTupleAndKeywords(
- args, kwargs, "O|O:find", (char **)keywords, &py_co, &py_filter)) {
+ args, kwargs, "O|$O:find", (char **)keywords, &py_co, &py_filter)) {
return NULL;
}
diff --git a/source/blender/python/rna_dump.py b/source/blender/python/rna_dump.py
index 7d469d20110..bd45a36e8a4 100644
--- a/source/blender/python/rna_dump.py
+++ b/source/blender/python/rna_dump.py
@@ -62,7 +62,7 @@ def seek(r, txt, recurs):
# print(dir(r))
# basic types
- if type_r in (float, int, bool, type(None)):
+ if type_r in {float, int, bool, type(None)}:
if PRINT_DATA:
print(txt + ' -> ' + str(r))
return
diff --git a/source/blender/render/RE_engine.h b/source/blender/render/RE_engine.h
index c31a41f66d5..7352ac7b12e 100644
--- a/source/blender/render/RE_engine.h
+++ b/source/blender/render/RE_engine.h
@@ -195,7 +195,7 @@ float RE_engine_get_camera_shift_x(RenderEngine *engine,
void RE_engine_get_camera_model_matrix(RenderEngine *engine,
struct Object *camera,
bool use_spherical_stereo,
- float *r_modelmat);
+ float r_modelmat[16]);
bool RE_engine_get_spherical_stereo(RenderEngine *engine, struct Object *camera);
bool RE_engine_test_break(RenderEngine *engine);
@@ -224,6 +224,8 @@ void RE_engine_register_pass(struct RenderEngine *engine,
const char *chanid,
eNodeSocketDatatype type);
+bool RE_engine_use_persistent_data(struct RenderEngine *engine);
+
/* Engine Types */
void RE_engines_init(void);
diff --git a/source/blender/render/RE_pipeline.h b/source/blender/render/RE_pipeline.h
index 27dcd9e70ed..4534c86f7f7 100644
--- a/source/blender/render/RE_pipeline.h
+++ b/source/blender/render/RE_pipeline.h
@@ -184,14 +184,14 @@ void RE_InitRenderCB(struct Render *re);
void RE_FreeRender(struct Render *re);
/* only called on exit */
void RE_FreeAllRender(void);
-/* Free memory used by persistent data.
- * Invoked when loading new file.
- */
-void RE_FreeAllPersistentData(void);
-/* only call on file load */
+
+/* On file load, free render results. */
void RE_FreeAllRenderResults(void);
-/* for external render engines that can keep persistent data */
-void RE_FreePersistentData(void);
+/* On file load or changes engines, free persistent render data.
+ * Assumes no engines are currently rendering. */
+void RE_FreeAllPersistentData(void);
+/* Free persistent render data, optionally only for the given scene. */
+void RE_FreePersistentData(const Scene *scene);
/* get results and statistics */
void RE_FreeRenderResult(struct RenderResult *rr);
@@ -245,8 +245,6 @@ void RE_InitState(struct Render *re,
int winx,
int winy,
rcti *disprect);
-void RE_ChangeResolution(struct Render *re, int winx, int winy, rcti *disprect);
-void RE_ChangeModeFlag(struct Render *re, int flag, bool clear);
/* set up the viewplane/perspective matrix, three choices */
struct Object *RE_GetCamera(struct Render *re); /* return camera override if set */
@@ -297,9 +295,6 @@ void RE_RenderFreestyleStrokes(struct Render *re,
void RE_RenderFreestyleExternal(struct Render *re);
#endif
-/* Free memory and clear runtime data which is only needed during rendering. */
-void RE_CleanAfterRender(struct Render *re);
-
void RE_SetActiveRenderView(struct Render *re, const char *viewname);
const char *RE_GetActiveRenderView(struct Render *re);
diff --git a/source/blender/render/intern/engine.c b/source/blender/render/intern/engine.c
index a43a78f5d3d..306d144f79d 100644
--- a/source/blender/render/intern/engine.c
+++ b/source/blender/render/intern/engine.c
@@ -140,6 +140,24 @@ RenderEngine *RE_engine_create(RenderEngineType *type)
return engine;
}
+static void engine_depsgraph_free(RenderEngine *engine)
+{
+ if (engine->depsgraph) {
+ /* Need GPU context since this might free GPU buffers. */
+ const bool use_gpu_context = (engine->type->flag & RE_USE_GPU_CONTEXT);
+ if (use_gpu_context) {
+ DRW_render_context_enable(engine->re);
+ }
+
+ DEG_graph_free(engine->depsgraph);
+ engine->depsgraph = NULL;
+
+ if (use_gpu_context) {
+ DRW_render_context_disable(engine->re);
+ }
+ }
+}
+
void RE_engine_free(RenderEngine *engine)
{
#ifdef WITH_PYTHON
@@ -148,6 +166,8 @@ void RE_engine_free(RenderEngine *engine)
}
#endif
+ engine_depsgraph_free(engine);
+
BLI_mutex_end(&engine->update_render_passes_mutex);
MEM_freeN(engine);
@@ -526,7 +546,7 @@ float RE_engine_get_camera_shift_x(RenderEngine *engine, Object *camera, bool us
void RE_engine_get_camera_model_matrix(RenderEngine *engine,
Object *camera,
bool use_spherical_stereo,
- float *r_modelmat)
+ float r_modelmat[16])
{
/* When using spherical stereo, get model matrix without multiview,
* leaving stereo to be handled by the engine. */
@@ -598,34 +618,94 @@ RenderData *RE_engine_get_render_data(Render *re)
return &re->r;
}
+bool RE_engine_use_persistent_data(RenderEngine *engine)
+{
+ /* Re-rendering is not supported with GPU contexts, since the GPU context
+ * is destroyed when the render thread exists. */
+ return (engine->re->r.mode & R_PERSISTENT_DATA) && !(engine->type->flag & RE_USE_GPU_CONTEXT);
+}
+
+static bool engine_keep_depsgraph(RenderEngine *engine)
+{
+ /* For persistent data or GPU engines like Eevee, reuse the depsgraph between
+ * view layers and animation frames. For renderers like Cycles that create
+ * their own copy of the scene, persistent data must be explicitly enabled to
+ * keep memory usage low by default. */
+ return (engine->re->r.mode & R_PERSISTENT_DATA) || (engine->type->flag & RE_USE_GPU_CONTEXT);
+}
+
/* Depsgraph */
static void engine_depsgraph_init(RenderEngine *engine, ViewLayer *view_layer)
{
Main *bmain = engine->re->main;
Scene *scene = engine->re->scene;
+ bool reuse_depsgraph = false;
+
+ /* Reuse depsgraph from persistent data if possible. */
+ if (engine->depsgraph) {
+ if (DEG_get_bmain(engine->depsgraph) != bmain ||
+ DEG_get_input_scene(engine->depsgraph) != scene) {
+ /* If bmain or scene changes, we need a completely new graph. */
+ engine_depsgraph_free(engine);
+ }
+ else if (DEG_get_input_view_layer(engine->depsgraph) != view_layer) {
+ /* If only view layer changed, reuse depsgraph in the hope of reusing
+ * objects shared between view layers. */
+ DEG_graph_replace_owners(engine->depsgraph, bmain, scene, view_layer);
+ DEG_graph_tag_relations_update(engine->depsgraph);
+ }
+
+ reuse_depsgraph = true;
+ }
+
+ if (!engine->depsgraph) {
+ /* Ensure we only use persistent data for one scene / view layer at a time,
+ * to avoid excessive memory usage. */
+ RE_FreePersistentData(NULL);
- engine->depsgraph = DEG_graph_new(bmain, scene, view_layer, DAG_EVAL_RENDER);
- DEG_debug_name_set(engine->depsgraph, "RENDER");
+ /* Create new depsgraph if not cached with persistent data. */
+ engine->depsgraph = DEG_graph_new(bmain, scene, view_layer, DAG_EVAL_RENDER);
+ DEG_debug_name_set(engine->depsgraph, "RENDER");
+ }
if (engine->re->r.scemode & R_BUTS_PREVIEW) {
+ /* Update for preview render. */
Depsgraph *depsgraph = engine->depsgraph;
DEG_graph_relations_update(depsgraph);
+
+ /* Need GPU context since this might free GPU buffers. */
+ const bool use_gpu_context = (engine->type->flag & RE_USE_GPU_CONTEXT) && reuse_depsgraph;
+ if (use_gpu_context) {
+ DRW_render_context_enable(engine->re);
+ }
+
DEG_evaluate_on_framechange(depsgraph, CFRA);
- DEG_ids_check_recalc(bmain, depsgraph, scene, view_layer, true);
- DEG_ids_clear_recalc(bmain, depsgraph);
+
+ if (use_gpu_context) {
+ DRW_render_context_disable(engine->re);
+ }
}
else {
- BKE_scene_graph_update_for_newframe(engine->depsgraph);
+ /* Go through update with full Python callbacks for regular render. */
+ BKE_scene_graph_update_for_newframe_ex(engine->depsgraph, false);
}
engine->has_grease_pencil = DRW_render_check_grease_pencil(engine->depsgraph);
}
-static void engine_depsgraph_free(RenderEngine *engine)
+static void engine_depsgraph_exit(RenderEngine *engine)
{
- DEG_graph_free(engine->depsgraph);
-
- engine->depsgraph = NULL;
+ if (engine->depsgraph) {
+ if (engine_keep_depsgraph(engine)) {
+ /* Clear recalc flags since the engine should have handled the updates for the currently
+ * rendered framed by now. */
+ DEG_ids_clear_recalc(engine->depsgraph, false);
+ }
+ else {
+ /* Free immediately to save memory. */
+ engine_depsgraph_free(engine);
+ }
+ }
}
void RE_engine_frame_set(RenderEngine *engine, int frame, float subframe)
@@ -634,12 +714,15 @@ void RE_engine_frame_set(RenderEngine *engine, int frame, float subframe)
return;
}
+ /* Clear recalc flags before update so engine can detect what changed. */
+ DEG_ids_clear_recalc(engine->depsgraph, false);
+
Render *re = engine->re;
double cfra = (double)frame + (double)subframe;
CLAMP(cfra, MINAFRAME, MAXFRAME);
BKE_scene_frame_set(re->scene, cfra);
- BKE_scene_graph_update_for_newframe(engine->depsgraph);
+ BKE_scene_graph_update_for_newframe_ex(engine->depsgraph, false);
BKE_scene_camera_switch_update(re->scene);
}
@@ -670,7 +753,6 @@ bool RE_bake_engine(Render *re,
{
RenderEngineType *type = RE_engines_find(re->r.engine);
RenderEngine *engine;
- bool persistent_data = (re->r.mode & R_PERSISTENT_DATA) != 0;
/* set render info */
re->i.cfra = re->scene->r.cfra;
@@ -729,13 +811,13 @@ bool RE_bake_engine(Render *re,
engine->tile_y = 0;
engine->flag &= ~RE_ENGINE_RENDERING;
+ /* Free depsgraph outside of parts mutex lock, since this locks OpenGL context
+ * while the the UI drawing might also lock the OpenGL context and parts mutex. */
+ engine_depsgraph_free(engine);
BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_WRITE);
- /* re->engine becomes zero if user changed active render engine during render */
- if (!persistent_data || !re->engine) {
- RE_engine_free(engine);
- re->engine = NULL;
- }
+ RE_engine_free(engine);
+ re->engine = NULL;
RE_parts_free(re);
BLI_rw_mutex_unlock(&re->partsmutex);
@@ -778,13 +860,14 @@ static void engine_render_view_layer(Render *re,
/* Perform render with engine. */
if (use_engine) {
- if (engine->type->flag & RE_USE_GPU_CONTEXT) {
+ const bool use_gpu_context = (engine->type->flag & RE_USE_GPU_CONTEXT);
+ if (use_gpu_context) {
DRW_render_context_enable(engine->re);
}
engine->type->render(engine, engine->depsgraph);
- if (engine->type->flag & RE_USE_GPU_CONTEXT) {
+ if (use_gpu_context) {
DRW_render_context_disable(engine->re);
}
}
@@ -800,13 +883,12 @@ static void engine_render_view_layer(Render *re,
}
/* Free dependency graph, if engine has not done it already. */
- engine_depsgraph_free(engine);
+ engine_depsgraph_exit(engine);
}
bool RE_engine_render(Render *re, bool do_all)
{
RenderEngineType *type = RE_engines_find(re->r.engine);
- bool persistent_data = (re->r.mode & R_PERSISTENT_DATA) != 0;
/* verify if we can render */
if (!type->render) {
@@ -953,7 +1035,13 @@ bool RE_engine_render(Render *re, bool do_all)
}
/* re->engine becomes zero if user changed active render engine during render */
- if (!persistent_data || !re->engine) {
+ if (!engine_keep_depsgraph(engine) || !re->engine) {
+ /* Free depsgraph outside of parts mutex lock, since this locks OpenGL context
+ * while the the UI drawing might also lock the OpenGL context and parts mutex. */
+ BLI_rw_mutex_unlock(&re->partsmutex);
+ engine_depsgraph_free(engine);
+ BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_WRITE);
+
RE_engine_free(engine);
re->engine = NULL;
}
@@ -1023,9 +1111,8 @@ void RE_engine_free_blender_memory(RenderEngine *engine)
*
* TODO(sergey): Find better solution for this.
*/
- if (engine->has_grease_pencil) {
+ if (engine->has_grease_pencil || engine_keep_depsgraph(engine)) {
return;
}
- DEG_graph_free(engine->depsgraph);
- engine->depsgraph = NULL;
+ engine_depsgraph_free(engine);
}
diff --git a/source/blender/render/intern/pipeline.c b/source/blender/render/intern/pipeline.c
index 92bec9c6fd4..a39214b609d 100644
--- a/source/blender/render/intern/pipeline.c
+++ b/source/blender/render/intern/pipeline.c
@@ -662,17 +662,6 @@ void RE_FreeAllRender(void)
#endif
}
-void RE_FreeAllPersistentData(void)
-{
- Render *re;
- for (re = RenderGlobal.renderlist.first; re != NULL; re = re->next) {
- if ((re->r.mode & R_PERSISTENT_DATA) != 0 && re->engine != NULL) {
- RE_engine_free(re->engine);
- re->engine = NULL;
- }
- }
-}
-
/* on file load, free all re */
void RE_FreeAllRenderResults(void)
{
@@ -687,19 +676,39 @@ void RE_FreeAllRenderResults(void)
}
}
-void RE_FreePersistentData(void)
+void RE_FreeAllPersistentData(void)
{
Render *re;
+ for (re = RenderGlobal.renderlist.first; re != NULL; re = re->next) {
+ if (re->engine != NULL) {
+ BLI_assert(!(re->engine->flag & RE_ENGINE_RENDERING));
+ RE_engine_free(re->engine);
+ re->engine = NULL;
+ }
+ }
+}
- /* render engines can be kept around for quick re-render, this clears all */
- for (re = RenderGlobal.renderlist.first; re; re = re->next) {
- if (re->engine) {
- /* if engine is currently rendering, just tag it to be freed when render is finished */
- if (!(re->engine->flag & RE_ENGINE_RENDERING)) {
- RE_engine_free(re->engine);
- }
+static void re_free_persistent_data(Render *re)
+{
+ /* If engine is currently rendering, just wait for it to be freed when it finishes rendering. */
+ if (re->engine && !(re->engine->flag & RE_ENGINE_RENDERING)) {
+ RE_engine_free(re->engine);
+ re->engine = NULL;
+ }
+}
- re->engine = NULL;
+void RE_FreePersistentData(const Scene *scene)
+{
+ /* Render engines can be kept around for quick re-render, this clears all or one scene. */
+ if (scene) {
+ Render *re = RE_GetSceneRender(scene);
+ if (re) {
+ re_free_persistent_data(re);
+ }
+ }
+ else {
+ for (Render *re = RenderGlobal.renderlist.first; re; re = re->next) {
+ re_free_persistent_data(re);
}
}
}
@@ -732,7 +741,7 @@ static void re_init_resolution(Render *re, Render *source, int winx, int winy, r
re->winy = winy;
if (source && (source->r.mode & R_BORDER)) {
/* eeh, doesn't seem original bordered disprect is storing anywhere
- * after insertion on black happening in do_render(),
+ * after insertion on black happening in do_render_engine(),
* so for now simply re-calculate disprect using border from source
* renderer (sergey)
*/
@@ -884,82 +893,6 @@ void RE_InitState(Render *re,
RE_point_density_fix_linking();
}
-/* This function is only called by view3d rendering, which doesn't support
- * multiview at the moment. so handle only one view here */
-static void render_result_rescale(Render *re)
-{
- RenderResult *result = re->result;
- RenderView *rv;
- int x, y;
- float scale_x, scale_y;
- float *src_rectf;
-
- rv = RE_RenderViewGetById(result, 0);
- src_rectf = rv->rectf;
-
- if (src_rectf == NULL) {
- RenderLayer *rl = render_get_active_layer(re, re->result);
- if (rl != NULL) {
- src_rectf = RE_RenderLayerGetPass(rl, RE_PASSNAME_COMBINED, NULL);
- }
- }
-
- if (src_rectf != NULL) {
- float *dst_rectf = NULL;
- re->result = render_result_new(re, &re->disprect, RR_USE_MEM, RR_ALL_LAYERS, "");
-
- if (re->result != NULL) {
- dst_rectf = RE_RenderViewGetById(re->result, 0)->rectf;
- if (dst_rectf == NULL) {
- RenderLayer *rl;
- rl = render_get_active_layer(re, re->result);
- if (rl != NULL) {
- dst_rectf = RE_RenderLayerGetPass(rl, RE_PASSNAME_COMBINED, NULL);
- }
- }
-
- scale_x = (float)result->rectx / re->result->rectx;
- scale_y = (float)result->recty / re->result->recty;
- for (x = 0; x < re->result->rectx; x++) {
- for (y = 0; y < re->result->recty; y++) {
- int src_x = x * scale_x;
- int src_y = y * scale_y;
- int dst_index = y * re->result->rectx + x;
- int src_index = src_y * result->rectx + src_x;
- copy_v4_v4(dst_rectf + dst_index * 4, src_rectf + src_index * 4);
- }
- }
- }
- render_result_free(result);
- }
-}
-
-void RE_ChangeResolution(Render *re, int winx, int winy, rcti *disprect)
-{
- re_init_resolution(re, NULL, winx, winy, disprect);
- RE_parts_clamp(re);
-
- if (re->result) {
- BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE);
- render_result_rescale(re);
- BLI_rw_mutex_unlock(&re->resultmutex);
- }
-}
-
-/* TODO(sergey): This is a bit hackish, used to temporary disable freestyle when
- * doing viewport render. Needs some better integration of BI viewport rendering
- * into the pipeline.
- */
-void RE_ChangeModeFlag(Render *re, int flag, bool clear)
-{
- if (clear) {
- re->r.mode &= ~flag;
- }
- else {
- re->r.mode |= flag;
- }
-}
-
/* update some variables that can be animated, and otherwise wouldn't be due to
* RenderData getting copied once at the start of animation render */
void render_update_anim_renderdata(Render *re, RenderData *rd, ListBase *render_layers)
@@ -1068,24 +1001,8 @@ void *RE_gpu_context_get(Render *re)
return re->gpu_context;
}
-/* ********* add object data (later) ******** */
-
-/* object is considered fully prepared on correct time etc */
-/* includes lights */
-#if 0
-void RE_AddObject(Render *UNUSED(re), Object *UNUSED(ob))
-{
-}
-#endif
-
/* ************ This part uses API, for rendering Blender scenes ********** */
-static void do_render_3d(Render *re)
-{
- re->current_scene_update(re->suh, re->scene);
- RE_engine_render(re, false);
-}
-
/* make sure disprect is not affected by the render border */
static void render_result_disprect_to_full_resolution(Render *re)
{
@@ -1146,8 +1063,8 @@ static void render_result_uncrop(Render *re)
}
}
-/* main render routine, no compositing */
-static void do_render(Render *re)
+/* Render scene into render result, with a render engine. */
+static void do_render_engine(Render *re)
{
Object *camera = RE_GetCamera(re);
/* also check for camera here */
@@ -1160,16 +1077,16 @@ static void do_render(Render *re)
/* now use renderdata and camera to set viewplane */
RE_SetCamera(re, camera);
- do_render_3d(re);
+ re->current_scene_update(re->suh, re->scene);
+ RE_engine_render(re, false);
/* when border render, check if we have to insert it in black */
render_result_uncrop(re);
}
-/* within context of current Render *re, render another scene.
- * it uses current render image size and disprect, but doesn't execute composite
- */
-static void render_scene(Render *re, Scene *sce, int cfra)
+/* Render scene into render result, within a compositor node tree.
+ * Uses the same image dimensions, does not recursively perform compositing. */
+static void do_render_compositor_scene(Render *re, Scene *sce, int cfra)
{
Render *resc = RE_NewSceneRender(sce);
int winx = re->winx, winy = re->winy;
@@ -1204,12 +1121,12 @@ static void render_scene(Render *re, Scene *sce, int cfra)
resc->current_scene_update = re->current_scene_update;
resc->suh = re->suh;
- do_render(resc);
+ do_render_engine(resc);
}
/* helper call to detect if this scene needs a render,
* or if there's a any render layer to render. */
-static int composite_needs_render(Scene *sce, int this_scene)
+static int compositor_needs_render(Scene *sce, int this_scene)
{
bNodeTree *ntree = sce->nodetree;
bNode *node;
@@ -1234,19 +1151,8 @@ static int composite_needs_render(Scene *sce, int this_scene)
return 0;
}
-bool RE_allow_render_generic_object(Object *ob)
-{
- /* override not showing object when duplis are used with particles */
- if (ob->transflag & OB_DUPLIPARTS) {
- /* pass */ /* let particle system(s) handle showing vs. not showing */
- }
- else if (ob->transflag & OB_DUPLI) {
- return false;
- }
- return true;
-}
-
-static void ntree_render_scenes(Render *re)
+/* Render all scenes within a compositor node tree. */
+static void do_render_compositor_scenes(Render *re)
{
bNode *node;
int cfra = re->scene->r.cfra;
@@ -1265,7 +1171,7 @@ static void ntree_render_scenes(Render *re)
Scene *scene = (Scene *)node->id;
if (!BLI_gset_haskey(scenes_rendered, scene) &&
render_scene_has_layers_to_render(scene, false)) {
- render_scene(re, scene, cfra);
+ do_render_compositor_scene(re, scene, cfra);
BLI_gset_add(scenes_rendered, scene);
nodeUpdate(restore_scene->nodetree, node);
}
@@ -1276,7 +1182,7 @@ static void ntree_render_scenes(Render *re)
}
/* bad call... need to think over proper method still */
-static void render_composit_stats(void *arg, const char *str)
+static void render_compositor_stats(void *arg, const char *str)
{
Render *re = (Render *)arg;
@@ -1286,13 +1192,14 @@ static void render_composit_stats(void *arg, const char *str)
re->stats_draw(re->sdh, &i);
}
-/* returns fully composited render-result on given time step (in RenderData) */
-static void do_render_composite(Render *re)
+/* Render compositor nodes, along with any scenes required for them.
+ * The result will be output into a compositing render layer in the render result. */
+static void do_render_compositor(Render *re)
{
bNodeTree *ntree = re->pipeline_scene_eval->nodetree;
int update_newframe = 0;
- if (composite_needs_render(re->pipeline_scene_eval, 1)) {
+ if (compositor_needs_render(re->pipeline_scene_eval, 1)) {
/* save memory... free all cached images */
ntreeFreeCache(ntree);
@@ -1300,7 +1207,7 @@ static void do_render_composite(Render *re)
* it could be optimized to render only the needed view
* but what if a scene has a different number of views
* than the main scene? */
- do_render(re);
+ do_render_engine(re);
}
else {
re->i.cfra = re->r.cfra;
@@ -1336,11 +1243,11 @@ static void do_render_composite(Render *re)
if (ntree && re->scene->use_nodes && re->r.scemode & R_DOCOMP) {
/* checks if there are render-result nodes that need scene */
if ((re->r.scemode & R_SINGLE_LAYER) == 0) {
- ntree_render_scenes(re);
+ do_render_compositor_scenes(re);
}
if (!re->test_break(re->tbh)) {
- ntree->stats_draw = render_composit_stats;
+ ntree->stats_draw = render_compositor_stats;
ntree->test_break = re->test_break;
ntree->progress = re->progress;
ntree->sdh = re;
@@ -1423,7 +1330,8 @@ int RE_seq_render_active(Scene *scene, RenderData *rd)
return 0;
}
-static void do_render_seq(Render *re)
+/* Render sequencer strips into render result. */
+static void do_render_sequencer(Render *re)
{
static int recurs_depth = 0;
struct ImBuf *out;
@@ -1537,8 +1445,8 @@ static void do_render_seq(Render *re)
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
-/* main loop: doing sequence + 3d render + compositing */
-static void do_render_all_options(Render *re)
+/* Render full pipeline, using render engine, sequencer and compositing nodes. */
+static void do_render_full_pipeline(Render *re)
{
bool render_seq = false;
@@ -1556,9 +1464,9 @@ static void do_render_all_options(Render *re)
/* in this case external render overrides all */
}
else if (RE_seq_render_active(re->scene, &re->r)) {
- /* note: do_render_seq() frees rect32 when sequencer returns float images */
+ /* note: do_render_sequencer() frees rect32 when sequencer returns float images */
if (!re->test_break(re->tbh)) {
- do_render_seq(re);
+ do_render_sequencer(re);
render_seq = true;
}
@@ -1566,7 +1474,7 @@ static void do_render_all_options(Render *re)
re->display_update(re->duh, re->result, NULL);
}
else {
- do_render_composite(re);
+ do_render_compositor(re);
}
re->i.lastframetime = PIL_check_seconds_timer() - re->i.starttime;
@@ -1701,7 +1609,7 @@ static int check_valid_camera(Scene *scene, Object *camera_override, ReportList
return true;
}
-static bool node_tree_has_composite_output(bNodeTree *ntree)
+static bool node_tree_has_compositor_output(bNodeTree *ntree)
{
bNode *node;
@@ -1711,7 +1619,7 @@ static bool node_tree_has_composite_output(bNodeTree *ntree)
}
if (ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP)) {
if (node->id) {
- if (node_tree_has_composite_output((bNodeTree *)node->id)) {
+ if (node_tree_has_compositor_output((bNodeTree *)node->id)) {
return true;
}
}
@@ -1721,9 +1629,9 @@ static bool node_tree_has_composite_output(bNodeTree *ntree)
return false;
}
-static int check_composite_output(Scene *scene)
+static int check_compositor_output(Scene *scene)
{
- return node_tree_has_composite_output(scene->nodetree);
+ return node_tree_has_compositor_output(scene->nodetree);
}
bool RE_is_rendering_allowed(Scene *scene,
@@ -1766,13 +1674,13 @@ bool RE_is_rendering_allowed(Scene *scene,
return 0;
}
- if (!check_composite_output(scene)) {
+ if (!check_compositor_output(scene)) {
BKE_report(reports, RPT_ERROR, "No render output node in scene");
return 0;
}
if (scemode & R_FULL_SAMPLE) {
- if (composite_needs_render(scene, 0) == 0) {
+ if (compositor_needs_render(scene, 0) == 0) {
BKE_report(reports, RPT_ERROR, "Full sample AA not supported without 3D rendering");
return 0;
}
@@ -1941,6 +1849,22 @@ static void render_init_depsgraph(Render *re)
re->pipeline_scene_eval = DEG_get_evaluated_scene(re->pipeline_depsgraph);
}
+/* Free data only needed during rendering operation. */
+static void render_pipeline_free(Render *re)
+{
+ if (re->engine && !RE_engine_use_persistent_data(re->engine)) {
+ RE_engine_free(re->engine);
+ re->engine = NULL;
+ }
+ if (re->pipeline_depsgraph != NULL) {
+ DEG_graph_free(re->pipeline_depsgraph);
+ re->pipeline_depsgraph = NULL;
+ re->pipeline_scene_eval = NULL;
+ }
+ /* Destroy the opengl context in the correct thread. */
+ RE_gl_context_destroy(re);
+}
+
/* general Blender frame render call */
void RE_RenderFrame(Render *re,
Main *bmain,
@@ -1966,7 +1890,7 @@ void RE_RenderFrame(Render *re,
render_init_depsgraph(re);
- do_render_all_options(re);
+ do_render_full_pipeline(re);
if (write_still && !G.is_break) {
if (BKE_imtype_is_movie(rd.im_format.imtype)) {
@@ -2001,7 +1925,7 @@ void RE_RenderFrame(Render *re,
&scene->id,
G.is_break ? BKE_CB_EVT_RENDER_CANCEL : BKE_CB_EVT_RENDER_COMPLETE);
- RE_CleanAfterRender(re);
+ render_pipeline_free(re);
/* UGLY WARNING */
G.is_rendering = false;
@@ -2038,7 +1962,7 @@ void RE_RenderFreestyleStrokes(Render *re, Main *bmain, Scene *scene, int render
change_renderdata_engine(re, RE_engine_id_BLENDER_EEVEE);
}
- do_render_3d(re);
+ RE_engine_render(re, false);
change_renderdata_engine(re, scene_engine);
}
@@ -2444,7 +2368,7 @@ void RE_RenderAnim(Render *re,
if (is_error) {
/* report is handled above */
re_movie_free_all(re, mh, i + 1);
- RE_CleanAfterRender(re);
+ render_pipeline_free(re);
return;
}
}
@@ -2568,7 +2492,7 @@ void RE_RenderAnim(Render *re,
/* run callbacks before rendering, before the scene is updated */
render_callback_exec_id(re, re->main, &scene->id, BKE_CB_EVT_RENDER_PRE);
- do_render_all_options(re);
+ do_render_full_pipeline(re);
totrendered++;
if (re->test_break(re->tbh) == 0) {
@@ -2642,7 +2566,7 @@ void RE_RenderAnim(Render *re,
G.is_break ? BKE_CB_EVT_RENDER_CANCEL : BKE_CB_EVT_RENDER_COMPLETE);
BKE_sound_reset_scene_specs(re->pipeline_scene_eval);
- RE_CleanAfterRender(re);
+ render_pipeline_free(re);
/* UGLY WARNING */
G.is_rendering = false;
@@ -2664,18 +2588,13 @@ void RE_PreviewRender(Render *re, Main *bmain, Scene *sce)
camera = RE_GetCamera(re);
RE_SetCamera(re, camera);
- do_render_3d(re);
-}
+ RE_engine_render(re, false);
-void RE_CleanAfterRender(Render *re)
-{
- /* Destroy the opengl context in the correct thread. */
- RE_gl_context_destroy(re);
- if (re->pipeline_depsgraph != NULL) {
- DEG_graph_free(re->pipeline_depsgraph);
+ /* No persistent data for preview render. */
+ if (re->engine) {
+ RE_engine_free(re->engine);
+ re->engine = NULL;
}
- re->pipeline_depsgraph = NULL;
- re->pipeline_scene_eval = NULL;
}
/* note; repeated win/disprect calc... solve that nicer, also in compo */
@@ -2917,3 +2836,15 @@ RenderPass *RE_create_gp_pass(RenderResult *rr, const char *layername, const cha
/* create a totally new pass */
return render_layer_add_pass(rr, rl, 4, RE_PASSNAME_COMBINED, viewname, "RGBA");
}
+
+bool RE_allow_render_generic_object(Object *ob)
+{
+ /* override not showing object when duplis are used with particles */
+ if (ob->transflag & OB_DUPLIPARTS) {
+ /* pass */ /* let particle system(s) handle showing vs. not showing */
+ }
+ else if (ob->transflag & OB_DUPLI) {
+ return false;
+ }
+ return true;
+}
diff --git a/source/blender/render/intern/render_result.c b/source/blender/render/intern/render_result.c
index f73a200f3c6..74c96392d48 100644
--- a/source/blender/render/intern/render_result.c
+++ b/source/blender/render/intern/render_result.c
@@ -126,7 +126,7 @@ void render_result_free(RenderResult *rr)
MEM_freeN(rr);
}
-/* version that's compatible with fullsample buffers */
+/** Version that's compatible with full-sample buffers. */
void render_result_free_list(ListBase *lb, RenderResult *rr)
{
RenderResult *rrnext;
diff --git a/source/blender/render/intern/render_types.h b/source/blender/render/intern/render_types.h
index 0488bf6e87a..9f399971049 100644
--- a/source/blender/render/intern/render_types.h
+++ b/source/blender/render/intern/render_types.h
@@ -79,7 +79,7 @@ struct Render {
RenderResult *result;
/* if render with single-layer option, other rendered layers are stored here */
RenderResult *pushedresult;
- /* a list of RenderResults, for fullsample */
+ /** A list of #RenderResults, for full-samples. */
ListBase fullresult;
/* read/write mutex, all internal code that writes to re->result must use a
* write lock, all external code must use a read lock. internal code is assumed
diff --git a/source/blender/render/intern/zbuf.c b/source/blender/render/intern/zbuf.c
index 33af3bbaf29..242c8a199fb 100644
--- a/source/blender/render/intern/zbuf.c
+++ b/source/blender/render/intern/zbuf.c
@@ -175,7 +175,7 @@ static void zbuf_add_to_span(ZSpan *zspan, const float v1[2], const float v2[2])
/* Functions */
/*-----------------------------------------------------------*/
-/* Scanconvert for strand triangles, calls func for each x, y coordinate
+/* Scan-convert for strand triangles, calls function for each x, y coordinate
* and gives UV barycentrics and z. */
void zspan_scanconvert(ZSpan *zspan,
diff --git a/source/blender/sequencer/CMakeLists.txt b/source/blender/sequencer/CMakeLists.txt
index 9d9553ed858..2b402b4750f 100644
--- a/source/blender/sequencer/CMakeLists.txt
+++ b/source/blender/sequencer/CMakeLists.txt
@@ -72,9 +72,9 @@ set(SRC
intern/multiview.h
intern/prefetch.c
intern/prefetch.h
- intern/proxy_job.c
intern/proxy.c
intern/proxy.h
+ intern/proxy_job.c
intern/render.c
intern/render.h
intern/sequencer.c
diff --git a/source/blender/sequencer/SEQ_add.h b/source/blender/sequencer/SEQ_add.h
index 9cb52145c04..2941eb6f4c0 100644
--- a/source/blender/sequencer/SEQ_add.h
+++ b/source/blender/sequencer/SEQ_add.h
@@ -36,6 +36,7 @@ typedef enum eSeqLoadFlags {
SEQ_LOAD_SOUND_CACHE = (1 << 1),
SEQ_LOAD_SOUND_MONO = (1 << 2),
SEQ_LOAD_MOVIE_SYNC_FPS = (1 << 3),
+ SEQ_LOAD_SET_VIEW_TRANSFORM = (1 << 4),
} eSeqLoadFlags;
/* Api for adding new sequence strips. */
diff --git a/source/blender/sequencer/SEQ_iterator.h b/source/blender/sequencer/SEQ_iterator.h
index 44908dca85a..c7c2dc275ee 100644
--- a/source/blender/sequencer/SEQ_iterator.h
+++ b/source/blender/sequencer/SEQ_iterator.h
@@ -27,48 +27,70 @@
extern "C" {
#endif
+#include "BLI_ghash.h"
+
struct Editing;
struct Sequence;
+struct GSet;
+struct GSetIterator;
-typedef struct SeqIterator {
- struct Sequence **array;
- int tot, cur;
+#define SEQ_ITERATOR_FOREACH(var, collection) \
+ for (SeqIterator iter = {{{NULL}}}; \
+ SEQ_iterator_ensure(collection, &iter, &var) && var != NULL; \
+ var = SEQ_iterator_yield(&iter))
- struct Sequence *seq;
- int valid;
-} SeqIterator;
-
-#define SEQ_ALL_BEGIN(ed, _seq) \
+#define SEQ_ALL_BEGIN(ed, var) \
{ \
- SeqIterator iter_macro; \
- for (SEQ_iterator_begin(ed, &iter_macro, false); iter_macro.valid; \
- SEQ_iterator_next(&iter_macro)) { \
- _seq = iter_macro.seq;
+ if (ed != NULL) { \
+ SeqCollection *all_strips = SEQ_query_all_strips_recursive(&ed->seqbase); \
+ GSetIterator gsi; \
+ GSET_ITER (gsi, all_strips->set) { \
+ var = (Sequence *)(BLI_gsetIterator_getKey(&gsi));
#define SEQ_ALL_END \
} \
- SEQ_iterator_end(&iter_macro); \
+ SEQ_collection_free(all_strips); \
+ } \
} \
((void)0)
-#define SEQ_CURRENT_BEGIN(_ed, _seq) \
- { \
- SeqIterator iter_macro; \
- for (SEQ_iterator_begin(_ed, &iter_macro, true); iter_macro.valid; \
- SEQ_iterator_next(&iter_macro)) { \
- _seq = iter_macro.seq;
+typedef struct SeqCollection {
+ struct SeqCollection *next, *prev;
+ struct GSet *set;
+} SeqCollection;
+
+typedef struct SeqIterator {
+ GSetIterator gsi;
+ SeqCollection *collection;
+ bool iterator_initialized;
+} SeqIterator;
-#define SEQ_CURRENT_END SEQ_ALL_END
+bool SEQ_iterator_ensure(SeqCollection *collection,
+ SeqIterator *iterator,
+ struct Sequence **r_seq);
+struct Sequence *SEQ_iterator_yield(SeqIterator *iterator);
-void SEQ_iterator_begin(struct Editing *ed, SeqIterator *iter, const bool use_current_sequences);
-void SEQ_iterator_next(SeqIterator *iter);
-void SEQ_iterator_end(SeqIterator *iter);
-int SEQ_iterator_seqbase_recursive_apply(struct ListBase *seqbase,
- int (*apply_fn)(struct Sequence *seq, void *),
- void *arg);
-int SEQ_iterator_recursive_apply(struct Sequence *seq,
- int (*apply_fn)(struct Sequence *, void *),
- void *arg);
+SeqCollection *SEQ_collection_create(void);
+bool SEQ_collection_append_strip(struct Sequence *seq, SeqCollection *data);
+bool SEQ_collection_remove_strip(struct Sequence *seq, SeqCollection *data);
+void SEQ_collection_free(SeqCollection *collection);
+void SEQ_collection_merge(SeqCollection *collection_dst, SeqCollection *collection_src);
+void SEQ_collection_expand(struct ListBase *seqbase,
+ SeqCollection *collection,
+ void query_func(struct Sequence *seq_reference,
+ struct ListBase *seqbase,
+ SeqCollection *collection));
+SeqCollection *SEQ_query_by_reference(struct Sequence *seq_reference,
+ struct ListBase *seqbase,
+ void seq_query_func(struct Sequence *seq_reference,
+ struct ListBase *seqbase,
+ SeqCollection *collection));
+SeqCollection *SEQ_query_selected_strips(struct ListBase *seqbase);
+SeqCollection *SEQ_query_all_strips(ListBase *seqbase);
+SeqCollection *SEQ_query_all_strips_recursive(ListBase *seqbase);
+void SEQ_query_strip_effect_chain(struct Sequence *seq_reference,
+ struct ListBase *seqbase,
+ SeqCollection *collection);
#ifdef __cplusplus
}
diff --git a/source/blender/sequencer/SEQ_sequencer.h b/source/blender/sequencer/SEQ_sequencer.h
index 85513faf3e6..ad0815892f7 100644
--- a/source/blender/sequencer/SEQ_sequencer.h
+++ b/source/blender/sequencer/SEQ_sequencer.h
@@ -44,16 +44,13 @@ enum {
SEQ_SIDE_NO_CHANGE,
};
-#define SEQ_CACHE_COST_MAX 10.0f
-
/* seq_dupli' flags */
#define SEQ_DUPE_UNIQUE_NAME (1 << 0)
-#define SEQ_DUPE_CONTEXT (1 << 1)
-#define SEQ_DUPE_ANIM (1 << 2)
#define SEQ_DUPE_ALL (1 << 3) /* otherwise only selected are copied */
#define SEQ_DUPE_IS_RECURSIVE_CALL (1 << 4)
struct SequencerToolSettings *SEQ_tool_settings_init(void);
+struct SequencerToolSettings *SEQ_tool_settings_ensure(struct Scene *scene);
void SEQ_tool_settings_free(struct SequencerToolSettings *tool_settings);
eSeqImageFitMethod SEQ_tool_settings_fit_method_get(struct Scene *scene);
void SEQ_tool_settings_fit_method_set(struct Scene *scene, eSeqImageFitMethod fit_method);
diff --git a/source/blender/sequencer/SEQ_time.h b/source/blender/sequencer/SEQ_time.h
index 31549ff3994..67d3a2e5960 100644
--- a/source/blender/sequencer/SEQ_time.h
+++ b/source/blender/sequencer/SEQ_time.h
@@ -45,6 +45,7 @@ int SEQ_time_find_next_prev_edit(struct Scene *scene,
void SEQ_time_update_sequence(struct Scene *scene, struct Sequence *seq);
void SEQ_time_update_sequence_bounds(struct Scene *scene, struct Sequence *seq);
int SEQ_time_cmp_time_startdisp(const void *a, const void *b);
+bool SEQ_time_strip_intersects_frame(const struct Sequence *seq, const int timeline_frame);
#ifdef __cplusplus
}
diff --git a/source/blender/sequencer/SEQ_transform.h b/source/blender/sequencer/SEQ_transform.h
index d448fbdb43e..d587bd0f1a1 100644
--- a/source/blender/sequencer/SEQ_transform.h
+++ b/source/blender/sequencer/SEQ_transform.h
@@ -31,8 +31,8 @@ struct ListBase;
struct Scene;
struct Sequence;
-int SEQ_transform_get_left_handle_frame(struct Sequence *seq, bool metaclip);
-int SEQ_transform_get_right_handle_frame(struct Sequence *seq, bool metaclip);
+int SEQ_transform_get_left_handle_frame(struct Sequence *seq);
+int SEQ_transform_get_right_handle_frame(struct Sequence *seq);
void SEQ_transform_set_left_handle_frame(struct Sequence *seq, int val);
void SEQ_transform_set_right_handle_frame(struct Sequence *seq, int val);
void SEQ_transform_handle_xlimits(struct Sequence *seq, int leftflag, int rightflag);
diff --git a/source/blender/sequencer/SEQ_utils.h b/source/blender/sequencer/SEQ_utils.h
index 45f53a64688..a4dc80d75db 100644
--- a/source/blender/sequencer/SEQ_utils.h
+++ b/source/blender/sequencer/SEQ_utils.h
@@ -35,7 +35,7 @@ struct Scene;
struct Sequence;
struct StripElem;
-void SEQ_sort(struct Scene *scene);
+void SEQ_sort(struct ListBase *seqbase);
void SEQ_sequence_base_unique_name_recursive(struct ListBase *seqbasep, struct Sequence *seq);
const char *SEQ_sequence_give_name(struct Sequence *seq);
struct ListBase *SEQ_get_seqbase_from_sequence(struct Sequence *seq, int *r_offset);
@@ -54,6 +54,13 @@ void SEQ_set_scale_to_fit(const struct Sequence *seq,
const int preview_width,
const int preview_height,
const eSeqImageFitMethod fit_method);
+int SEQ_seqbase_recursive_apply(struct ListBase *seqbase,
+ int (*apply_fn)(struct Sequence *seq, void *),
+ void *arg);
+int SEQ_recursive_apply(struct Sequence *seq,
+ int (*apply_fn)(struct Sequence *, void *),
+ void *arg);
+void SEQ_ensure_unique_name(struct Sequence *seq, struct Scene *scene);
#ifdef __cplusplus
}
diff --git a/source/blender/sequencer/intern/effects.c b/source/blender/sequencer/intern/effects.c
index 278320d873e..d41a2c19d55 100644
--- a/source/blender/sequencer/intern/effects.c
+++ b/source/blender/sequencer/intern/effects.c
@@ -3033,10 +3033,9 @@ static ImBuf *do_adjustment_impl(const SeqRenderData *context, Sequence *seq, fl
i = seq_render_give_ibuf_seqbase(context, timeline_frame, seq->machine - 1, seqbasep);
}
- /* found nothing? so let's work the way up the metastrip stack, so
+ /* Found nothing? so let's work the way up the meta-strip stack, so
* that it is possible to group a bunch of adjustment strips into
- * a metastrip and have that work on everything below the metastrip
- */
+ * a meta-strip and have that work on everything below the meta-strip. */
if (!i) {
Sequence *meta;
diff --git a/source/blender/sequencer/intern/image_cache.c b/source/blender/sequencer/intern/image_cache.c
index 290ee185865..a6e4b6ea7ed 100644
--- a/source/blender/sequencer/intern/image_cache.c
+++ b/source/blender/sequencer/intern/image_cache.c
@@ -35,6 +35,7 @@
#include "IMB_imbuf_types.h"
#include "BLI_blenlib.h"
+#include "BLI_endian_defines.h"
#include "BLI_endian_switch.h"
#include "BLI_fileops.h"
#include "BLI_fileops_types.h"
@@ -44,7 +45,6 @@
#include "BLI_path_util.h"
#include "BLI_threads.h"
-#include "BKE_global.h"
#include "BKE_main.h"
#include "BKE_scene.h"
@@ -352,15 +352,18 @@ static void seq_disk_cache_update_file(SeqDiskCache *disk_cache, char *path)
}
/* Path format:
- * <cache dir>/<project name>/<scene name>-<timestamp>/<seq name>/DCACHE_FNAME_FORMAT
+ * <cache dir>/<project name>_seq_cache/<scene name>-<timestamp>/<seq name>/DCACHE_FNAME_FORMAT
*/
static void seq_disk_cache_get_project_dir(SeqDiskCache *disk_cache, char *path, size_t path_len)
{
- char main_name[FILE_MAX];
- BLI_split_file_part(BKE_main_blendfile_path(disk_cache->bmain), main_name, sizeof(main_name));
+ char cache_dir[FILE_MAX];
+ BLI_split_file_part(BKE_main_blendfile_path(disk_cache->bmain), cache_dir, sizeof(cache_dir));
+ /* Use suffix, so that the cache directory name does not conflict with the bmain's blend file. */
+ const char *suffix = "_seq_cache";
+ strncat(cache_dir, suffix, sizeof(cache_dir) - strlen(cache_dir) - 1);
BLI_strncpy(path, seq_disk_cache_base_dir(), path_len);
- BLI_path_append(path, path_len, main_name);
+ BLI_path_append(path, path_len, cache_dir);
}
static void seq_disk_cache_get_dir(
@@ -421,7 +424,7 @@ static void seq_disk_cache_handle_versioning(SeqDiskCache *disk_cache)
BLI_strncpy(path_version_file, path, sizeof(path_version_file));
BLI_path_append(path_version_file, sizeof(path_version_file), "cache_version");
- if (BLI_exists(path)) {
+ if (BLI_exists(path) && BLI_is_dir(path)) {
FILE *file = BLI_fopen(path_version_file, "r");
if (file) {
@@ -515,7 +518,7 @@ static size_t inflate_file_to_imbuf(ImBuf *ibuf, FILE *file, DiskCacheHeaderEntr
static bool seq_disk_cache_read_header(FILE *file, DiskCacheHeader *header)
{
- fseek(file, 0, 0);
+ BLI_fseek(file, 0LL, SEEK_SET);
const size_t num_items_read = fread(header, sizeof(*header), 1, file);
if (num_items_read < 1) {
BLI_assert(!"unable to read disk cache header");
@@ -537,7 +540,7 @@ static bool seq_disk_cache_read_header(FILE *file, DiskCacheHeader *header)
static size_t seq_disk_cache_write_header(FILE *file, DiskCacheHeader *header)
{
- fseek(file, 0, 0);
+ BLI_fseek(file, 0LL, SEEK_SET);
return fwrite(header, sizeof(*header), 1, file);
}
@@ -976,14 +979,34 @@ static void seq_cache_recycle_linked(Scene *scene, SeqCacheKey *base)
SeqCacheKey *next = base->link_next;
while (base) {
+ if (!BLI_ghash_haskey(cache->hash, base)) {
+ break; /* Key has already been removed from cache. */
+ }
+
SeqCacheKey *prev = base->link_prev;
+ if (prev != NULL && prev->link_next != base) {
+ /* Key has been removed and replaced and doesn't belong to this chain anymore. */
+ base->link_prev = NULL;
+ break;
+ }
+
BLI_ghash_remove(cache->hash, base, seq_cache_keyfree, seq_cache_valfree);
base = prev;
}
base = next;
while (base) {
+ if (!BLI_ghash_haskey(cache->hash, base)) {
+ break; /* Key has already been removed from cache. */
+ }
+
next = base->link_next;
+ if (next != NULL && next->link_prev != base) {
+ /* Key has been removed and replaced and doesn't belong to this chain anymore. */
+ base->link_next = NULL;
+ break;
+ }
+
BLI_ghash_remove(cache->hash, base, seq_cache_keyfree, seq_cache_valfree);
base = next;
}
diff --git a/source/blender/sequencer/intern/iterator.c b/source/blender/sequencer/intern/iterator.c
index f99667dea04..9bbc5362f18 100644
--- a/source/blender/sequencer/intern/iterator.c
+++ b/source/blender/sequencer/intern/iterator.c
@@ -31,138 +31,268 @@
#include "DNA_scene_types.h"
#include "DNA_sequence_types.h"
+#include "BLI_ghash.h"
#include "BLI_listbase.h"
#include "BKE_scene.h"
#include "SEQ_iterator.h"
-/* ************************* iterator ************************** */
-/* *************** (replaces old WHILE_SEQ) ********************* */
-/* **************** use now SEQ_ALL_BEGIN () SEQ_ALL_END ***************** */
+/* -------------------------------------------------------------------- */
+/** \Iterator API
+ * \{ */
-/* sequence strip iterator:
- * - builds a full array, recursively into meta strips
+/**
+ * Utility function for SEQ_ITERATOR_FOREACH macro.
+ * Ensure, that iterator is initialized. During initialization return pointer to collection element
+ * and step gset iterator. When this function is called after iterator has been initialized, it
+ * will do nothing and return true.
+ *
+ * \param collection: collection to iterate
+ * \param iterator: iterator to be initialized
+ * \param r_seq: pointer to Sequence pointer
+ *
+ * \return false when iterator can not be initialized, true otherwise
*/
-
-static void seq_count(ListBase *seqbase, int *tot)
+bool SEQ_iterator_ensure(SeqCollection *collection, SeqIterator *iterator, Sequence **r_seq)
{
- Sequence *seq;
-
- for (seq = seqbase->first; seq; seq = seq->next) {
- (*tot)++;
-
- if (seq->seqbase.first) {
- seq_count(&seq->seqbase, tot);
- }
+ if (iterator->iterator_initialized) {
+ return true;
}
-}
-static void seq_build_array(ListBase *seqbase, Sequence ***array, int depth)
-{
- Sequence *seq;
+ if (BLI_gset_len(collection->set) == 0) {
+ return false;
+ }
- for (seq = seqbase->first; seq; seq = seq->next) {
- seq->depth = depth;
+ iterator->collection = collection;
+ BLI_gsetIterator_init(&iterator->gsi, iterator->collection->set);
+ iterator->iterator_initialized = true;
- if (seq->seqbase.first) {
- seq_build_array(&seq->seqbase, array, depth + 1);
- }
+ *r_seq = BLI_gsetIterator_getKey(&iterator->gsi);
+ BLI_gsetIterator_step(&iterator->gsi);
- **array = seq;
- (*array)++;
- }
+ return true;
}
-static void seq_array(Editing *ed,
- const bool use_current_sequences,
- Sequence ***r_seqarray,
- int *r_seqarray_len)
+/**
+ * Utility function for SEQ_ITERATOR_FOREACH macro.
+ * Yield collection element
+ *
+ * \param iterator: iterator to be initialized
+ *
+ * \return collection element or NULL when iteration has ended
+ */
+Sequence *SEQ_iterator_yield(SeqIterator *iterator)
{
- Sequence **array;
+ Sequence *seq = BLI_gsetIterator_done(&iterator->gsi) ? NULL :
+ BLI_gsetIterator_getKey(&iterator->gsi);
+ BLI_gsetIterator_step(&iterator->gsi);
+ return seq;
+}
- *r_seqarray = NULL;
- *r_seqarray_len = 0;
+/**
+ * Free strip collection.
+ *
+ * \param collection: collection to be freed
+ */
+void SEQ_collection_free(SeqCollection *collection)
+{
+ BLI_gset_free(collection->set, NULL);
+ MEM_freeN(collection);
+}
- if (ed == NULL) {
- return;
- }
+/**
+ * Create new empty strip collection.
+ *
+ * \return empty strip collection.
+ */
+SeqCollection *SEQ_collection_create(void)
+{
+ SeqCollection *collection = MEM_callocN(sizeof(SeqCollection), "SeqCollection");
+ collection->set = BLI_gset_new(
+ BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "SeqCollection GSet");
+ return collection;
+}
- if (use_current_sequences) {
- seq_count(ed->seqbasep, r_seqarray_len);
- }
- else {
- seq_count(&ed->seqbase, r_seqarray_len);
+/**
+ * Query strips from seqbase. seq_reference is used by query function as filter condition.
+ *
+ * \param seq_reference: reference strip for query function
+ * \param seqbase: ListBase in which strips are queried
+ * \param seq_query_func: query function callback
+ * \return strip collection
+ */
+SeqCollection *SEQ_query_by_reference(Sequence *seq_reference,
+ ListBase *seqbase,
+ void seq_query_func(Sequence *seq_reference,
+ ListBase *seqbase,
+ SeqCollection *collection))
+{
+ SeqCollection *collection = SEQ_collection_create();
+ seq_query_func(seq_reference, seqbase, collection);
+ return collection;
+}
+/**
+ * Add strip to collection.
+ *
+ * \param seq: strip to be added
+ * \param collection: collection to which strip will be added
+ * \return false if strip is already in set, otherwise true
+ */
+bool SEQ_collection_append_strip(Sequence *seq, SeqCollection *collection)
+{
+ if (BLI_gset_lookup(collection->set, seq) != NULL) {
+ return false;
}
+ BLI_gset_insert(collection->set, seq);
+ return true;
+}
- if (*r_seqarray_len == 0) {
- return;
- }
+/**
+ * Remove strip from collection.
+ *
+ * \param seq: strip to be removed
+ * \param collection: collection from which strip will be removed
+ * \return true if strip exists in set and it was removed from set, otherwise false
+ */
+bool SEQ_collection_remove_strip(Sequence *seq, SeqCollection *collection)
+{
+ return BLI_gset_remove(collection->set, seq, NULL);
+}
- *r_seqarray = array = MEM_mallocN(sizeof(Sequence *) * (*r_seqarray_len), "SeqArray");
- if (use_current_sequences) {
- seq_build_array(ed->seqbasep, &array, 0);
- }
- else {
- seq_build_array(&ed->seqbase, &array, 0);
+/**
+ * Move strips from collection_src to collection_dst. Source collection will be freed.
+ *
+ * \param collection_dst: destination collection
+ * \param collection_src: source collection
+ */
+void SEQ_collection_merge(SeqCollection *collection_dst, SeqCollection *collection_src)
+{
+ Sequence *seq;
+ SEQ_ITERATOR_FOREACH (seq, collection_src) {
+ SEQ_collection_append_strip(seq, collection_dst);
}
+ SEQ_collection_free(collection_src);
}
-void SEQ_iterator_begin(Editing *ed, SeqIterator *iter, const bool use_current_sequences)
+/**
+ * Expand collection by running SEQ_query() for each strip, which will be used as reference.
+ * Results of these queries will be merged into provided collection.
+ *
+ * \param seqbase: ListBase in which strips are queried
+ * \param collection: SeqCollection to be expanded
+ * \param seq_query_func: query function callback
+ */
+void SEQ_collection_expand(ListBase *seqbase,
+ SeqCollection *collection,
+ void seq_query_func(Sequence *seq_reference,
+ ListBase *seqbase,
+ SeqCollection *collection))
{
- memset(iter, 0, sizeof(*iter));
- seq_array(ed, use_current_sequences, &iter->array, &iter->tot);
+ /* Collect expanded results for each sequence in provided SeqIteratorCollection. */
+ ListBase expand_collections = {0};
- if (iter->tot) {
- iter->cur = 0;
- iter->seq = iter->array[iter->cur];
- iter->valid = 1;
+ Sequence *seq;
+ SEQ_ITERATOR_FOREACH (seq, collection) {
+ SeqCollection *expand_collection = SEQ_query_by_reference(seq, seqbase, seq_query_func);
+ BLI_addtail(&expand_collections, expand_collection);
+ }
+
+ /* Merge all expanded results in provided SeqIteratorCollection. */
+ LISTBASE_FOREACH_MUTABLE (SeqCollection *, expand_collection, &expand_collections) {
+ BLI_remlink(&expand_collections, expand_collection);
+ SEQ_collection_merge(collection, expand_collection);
}
}
-void SEQ_iterator_next(SeqIterator *iter)
+/** \} */
+
+/**
+ * Query all strips in seqbase and nested meta strips.
+ *
+ * \param seqbase: ListBase in which strips are queried
+ * \return strip collection
+ */
+SeqCollection *SEQ_query_all_strips_recursive(ListBase *seqbase)
{
- if (++iter->cur < iter->tot) {
- iter->seq = iter->array[iter->cur];
- }
- else {
- iter->valid = 0;
+ SeqCollection *collection = SEQ_collection_create();
+ LISTBASE_FOREACH (Sequence *, seq, seqbase) {
+ if (seq->type == SEQ_TYPE_META) {
+ SEQ_collection_merge(collection, SEQ_query_all_strips_recursive(&seq->seqbase));
+ }
+ SEQ_collection_append_strip(seq, collection);
}
+ return collection;
}
-void SEQ_iterator_end(SeqIterator *iter)
+/**
+ * Query all strips in seqbase. This does not include strips nested in meta strips.
+ *
+ * \param seqbase: ListBase in which strips are queried
+ * \return strip collection
+ */
+SeqCollection *SEQ_query_all_strips(ListBase *seqbase)
{
- if (iter->array) {
- MEM_freeN(iter->array);
+ SeqCollection *collection = SEQ_collection_create();
+ LISTBASE_FOREACH (Sequence *, seq, seqbase) {
+ SEQ_collection_append_strip(seq, collection);
}
-
- iter->valid = 0;
+ return collection;
}
-int SEQ_iterator_seqbase_recursive_apply(ListBase *seqbase,
- int (*apply_fn)(Sequence *seq, void *),
- void *arg)
+/**
+ * Query all selected strips in seqbase.
+ *
+ * \param seqbase: ListBase in which strips are queried
+ * \return strip collection
+ */
+SeqCollection *SEQ_query_selected_strips(ListBase *seqbase)
{
- Sequence *iseq;
- for (iseq = seqbase->first; iseq; iseq = iseq->next) {
- if (SEQ_iterator_recursive_apply(iseq, apply_fn, arg) == -1) {
- return -1; /* bail out */
+ SeqCollection *collection = SEQ_collection_create();
+ LISTBASE_FOREACH (Sequence *, seq, seqbase) {
+ if ((seq->flag & SELECT) == 0) {
+ continue;
}
+ SEQ_collection_append_strip(seq, collection);
}
- return 1;
+ return collection;
}
-int SEQ_iterator_recursive_apply(Sequence *seq, int (*apply_fn)(Sequence *, void *), void *arg)
+/**
+ * Query all effect strips that are directly or indirectly connected to seq_reference.
+ * This includes all effects of seq_reference, strips used by another inputs and their effects, so
+ * that whole chain is fully independent of other strips.
+ *
+ * \param seq_reference: reference strip
+ * \param seqbase: ListBase in which strips are queried
+ * \param collection: collection to be filled
+ */
+void SEQ_query_strip_effect_chain(Sequence *seq_reference,
+ ListBase *seqbase,
+ SeqCollection *collection)
{
- int ret = apply_fn(seq, arg);
-
- if (ret == -1) {
- return -1; /* bail out */
+ if (!SEQ_collection_append_strip(seq_reference, collection)) {
+ return; /* Strip is already in set, so all effects connected to it are as well. */
}
- if (ret && seq->seqbase.first) {
- ret = SEQ_iterator_seqbase_recursive_apply(&seq->seqbase, apply_fn, arg);
+ /* Find all strips that seq_reference is connected to. */
+ if (seq_reference->type & SEQ_TYPE_EFFECT) {
+ if (seq_reference->seq1) {
+ SEQ_query_strip_effect_chain(seq_reference->seq1, seqbase, collection);
+ }
+ if (seq_reference->seq2) {
+ SEQ_query_strip_effect_chain(seq_reference->seq2, seqbase, collection);
+ }
+ if (seq_reference->seq3) {
+ SEQ_query_strip_effect_chain(seq_reference->seq3, seqbase, collection);
+ }
}
- return ret;
+ /* Find all strips connected to seq_reference. */
+ LISTBASE_FOREACH (Sequence *, seq_test, seqbase) {
+ if (seq_test->seq1 == seq_reference || seq_test->seq2 == seq_reference ||
+ seq_test->seq3 == seq_reference) {
+ SEQ_query_strip_effect_chain(seq_test, seqbase, collection);
+ }
+ }
}
diff --git a/source/blender/sequencer/intern/proxy.c b/source/blender/sequencer/intern/proxy.c
index a0e6bc7f4f1..bd7ea5b958c 100644
--- a/source/blender/sequencer/intern/proxy.c
+++ b/source/blender/sequencer/intern/proxy.c
@@ -466,16 +466,18 @@ bool SEQ_proxy_rebuild_context(Main *bmain,
seq_open_anim_file(scene, nseq, true);
sanim = BLI_findlink(&nseq->anims, i);
- context->index_context = IMB_anim_index_rebuild_context(sanim->anim,
- context->tc_flags,
- context->size_flags,
- context->quality,
- context->overwrite,
- file_list);
- }
- if (!context->index_context) {
- SEQ_proxy_rebuild_finish(context, false);
- return false;
+ if (sanim->anim) {
+ context->index_context = IMB_anim_index_rebuild_context(sanim->anim,
+ context->tc_flags,
+ context->size_flags,
+ context->quality,
+ context->overwrite,
+ file_list);
+ }
+ if (!context->index_context) {
+ MEM_freeN(context);
+ return false;
+ }
}
link = BLI_genericNodeN(context);
@@ -585,7 +587,7 @@ void SEQ_proxy_set(struct Sequence *seq, bool value)
seq->flag |= SEQ_USE_PROXY;
if (seq->strip->proxy == NULL) {
seq->strip->proxy = MEM_callocN(sizeof(struct StripProxy), "StripProxy");
- seq->strip->proxy->quality = 90;
+ seq->strip->proxy->quality = 50;
seq->strip->proxy->build_tc_flags = SEQ_PROXY_TC_ALL;
seq->strip->proxy->build_size_flags = SEQ_PROXY_IMAGE_SIZE_25;
}
diff --git a/source/blender/sequencer/intern/render.c b/source/blender/sequencer/intern/render.c
index 572fff0ad38..b0a8605e922 100644
--- a/source/blender/sequencer/intern/render.c
+++ b/source/blender/sequencer/intern/render.c
@@ -37,6 +37,7 @@
#include "BLI_linklist.h"
#include "BLI_listbase.h"
#include "BLI_path_util.h"
+#include "BLI_rect.h"
#include "BKE_anim_data.h"
#include "BKE_animsys.h"
@@ -65,10 +66,12 @@
#include "RE_pipeline.h"
#include "SEQ_effects.h"
+#include "SEQ_iterator.h"
#include "SEQ_modifier.h"
#include "SEQ_proxy.h"
#include "SEQ_render.h"
#include "SEQ_sequencer.h"
+#include "SEQ_time.h"
#include "SEQ_utils.h"
#include "effects.h"
@@ -259,120 +262,132 @@ StripElem *SEQ_render_give_stripelem(Sequence *seq, int timeline_frame)
return se;
}
-static int evaluate_seq_frame_gen(Sequence **seq_arr,
- ListBase *seqbase,
- int timeline_frame,
- int chanshown)
+static bool seq_is_effect_of(const Sequence *seq_effect, const Sequence *possibly_input)
{
- /* Use arbitrary sized linked list, the size could be over MAXSEQ. */
- LinkNodePair effect_inputs = {NULL, NULL};
- int totseq = 0;
+ if (seq_effect->seq1 == possibly_input || seq_effect->seq2 == possibly_input ||
+ seq_effect->seq3 == possibly_input) {
+ return true;
+ }
+ return false;
+}
- memset(seq_arr, 0, sizeof(Sequence *) * (MAXSEQ + 1));
+/* Check if seq must be rendered. This depends on whole stack in some cases, not only seq itself.
+ * Order of applying these conditions is important. */
+static bool must_render_strip(const Sequence *seq, SeqCollection *strips_under_playhead)
+{
+ bool seq_have_effect_in_stack = false;
+ Sequence *seq_iter;
+ SEQ_ITERATOR_FOREACH (seq_iter, strips_under_playhead) {
+ /* Strips is below another strip with replace blending are not rendered. */
+ if (seq_iter->blend_mode == SEQ_BLEND_REPLACE && seq->machine < seq_iter->machine) {
+ return false;
+ }
- LISTBASE_FOREACH (Sequence *, seq, seqbase) {
- if ((seq->startdisp <= timeline_frame) && (seq->enddisp > timeline_frame)) {
- if ((seq->type & SEQ_TYPE_EFFECT) && !(seq->flag & SEQ_MUTE)) {
+ if ((seq_iter->type & SEQ_TYPE_EFFECT) != 0 && seq_is_effect_of(seq_iter, seq)) {
+ /* Strips in same channel or higher than its effect are rendered. */
+ if (seq->machine >= seq_iter->machine) {
+ return true;
+ }
+ /* Mark that this strip has effect in stack, that is above the strip. */
+ seq_have_effect_in_stack = true;
+ }
+ }
- if (seq->seq1) {
- BLI_linklist_append_alloca(&effect_inputs, seq->seq1);
- }
+ /* All effects are rendered (with respect to conditions above). */
+ if ((seq->type & SEQ_TYPE_EFFECT) != 0) {
+ return true;
+ }
- if (seq->seq2) {
- BLI_linklist_append_alloca(&effect_inputs, seq->seq2);
- }
+ /* If strip has effects in stack, and all effects are above this strip, it it not rendered. */
+ if (seq_have_effect_in_stack) {
+ return false;
+ }
- if (seq->seq3) {
- BLI_linklist_append_alloca(&effect_inputs, seq->seq3);
- }
- }
+ return true;
+}
- seq_arr[seq->machine] = seq;
- totseq++;
+static SeqCollection *query_strips_at_frame(ListBase *seqbase, const int timeline_frame)
+{
+ SeqCollection *collection = SEQ_collection_create();
+
+ LISTBASE_FOREACH (Sequence *, seq, seqbase) {
+ if (SEQ_time_strip_intersects_frame(seq, timeline_frame)) {
+ SEQ_collection_append_strip(seq, collection);
}
}
+ return collection;
+}
- /* Drop strips which are used for effect inputs, we don't want
- * them to blend into render stack in any other way than effect
- * string rendering. */
- for (LinkNode *seq_item = effect_inputs.list; seq_item; seq_item = seq_item->next) {
- Sequence *seq = seq_item->link;
- /* It's possible that effect strip would be placed to the same
- * 'machine' as its inputs. We don't want to clear such strips
- * from the stack. */
- if (seq_arr[seq->machine] && seq_arr[seq->machine]->type & SEQ_TYPE_EFFECT) {
- continue;
- }
- /* If we're shown a specified channel, then we want to see the strips
- * which belongs to this machine. */
- if (chanshown != 0 && chanshown <= seq->machine) {
+static void collection_filter_channel_up_to_incl(SeqCollection *collection, const int channel)
+{
+ Sequence *seq;
+ SEQ_ITERATOR_FOREACH (seq, collection) {
+ if (seq->machine <= channel) {
continue;
}
- seq_arr[seq->machine] = NULL;
+ SEQ_collection_remove_strip(seq, collection);
}
-
- return totseq;
}
-/**
- * Count number of strips in timeline at timeline_frame
- *
- * \param seqbase: ListBase in which strips are located
- * \param timeline_frame: frame on timeline from where gaps are searched for
- * \return number of strips
- */
-int SEQ_render_evaluate_frame(ListBase *seqbase, int timeline_frame)
+/* Remove strips we don't want to render from collection. */
+static void collection_filter_rendered_strips(SeqCollection *collection)
{
- Sequence *seq_arr[MAXSEQ + 1];
- return evaluate_seq_frame_gen(seq_arr, seqbase, timeline_frame, 0);
+ Sequence *seq;
+
+ /* Remove sound strips and muted strips from collection, because these are not rendered.
+ * Function must_render_strip() don't have to check for these strips anymore. */
+ SEQ_ITERATOR_FOREACH (seq, collection) {
+ if (seq->type == SEQ_TYPE_SOUND_RAM || (seq->flag & SEQ_MUTE) != 0) {
+ SEQ_collection_remove_strip(seq, collection);
+ }
+ }
+
+ SEQ_ITERATOR_FOREACH (seq, collection) {
+ if (must_render_strip(seq, collection)) {
+ continue;
+ }
+ SEQ_collection_remove_strip(seq, collection);
+ }
}
-static bool video_seq_is_rendered(Sequence *seq)
+static int seq_channel_cmp_fn(const void *a, const void *b)
{
- return (seq && !(seq->flag & SEQ_MUTE) && seq->type != SEQ_TYPE_SOUND_RAM);
+ return (*(Sequence **)a)->machine - (*(Sequence **)b)->machine;
}
-int seq_get_shown_sequences(ListBase *seqbasep,
- int timeline_frame,
- int chanshown,
- Sequence **seq_arr_out)
+int seq_get_shown_sequences(ListBase *seqbase,
+ const int timeline_frame,
+ const int chanshown,
+ Sequence **r_seq_arr)
{
- Sequence *seq_arr[MAXSEQ + 1];
- int b = chanshown;
- int cnt = 0;
+ SeqCollection *collection = query_strips_at_frame(seqbase, timeline_frame);
- if (b > MAXSEQ) {
- return 0;
+ if (chanshown != 0) {
+ collection_filter_channel_up_to_incl(collection, chanshown);
}
+ collection_filter_rendered_strips(collection);
- if (evaluate_seq_frame_gen(seq_arr, seqbasep, timeline_frame, chanshown)) {
- if (b == 0) {
- b = MAXSEQ;
- }
- for (; b > 0; b--) {
- if (video_seq_is_rendered(seq_arr[b])) {
- break;
- }
- }
- }
-
- chanshown = b;
+ const int strip_count = BLI_gset_len(collection->set);
- for (; b > 0; b--) {
- if (video_seq_is_rendered(seq_arr[b])) {
- if (seq_arr[b]->blend_mode == SEQ_BLEND_REPLACE) {
- break;
- }
- }
+ if (strip_count > MAXSEQ) {
+ BLI_assert(!"Too many strips, this shouldn't happen");
+ return 0;
}
- for (; b <= chanshown && b >= 0; b++) {
- if (video_seq_is_rendered(seq_arr[b])) {
- seq_arr_out[cnt++] = seq_arr[b];
- }
+ /* Copy collection elements into array. */
+ memset(r_seq_arr, 0, sizeof(Sequence *) * (MAXSEQ + 1));
+ Sequence *seq;
+ int index = 0;
+ SEQ_ITERATOR_FOREACH (seq, collection) {
+ r_seq_arr[index] = seq;
+ index++;
}
+ SEQ_collection_free(collection);
- return cnt;
+ /* Sort array by channel. */
+ qsort(r_seq_arr, strip_count, sizeof(Sequence *), seq_channel_cmp_fn);
+
+ return strip_count;
}
/** \} */
@@ -456,84 +471,86 @@ static bool seq_input_have_to_preprocess(const SeqRenderData *context,
return false;
}
-typedef struct ImageTransformThreadInitData {
- ImBuf *ibuf_source;
- ImBuf *ibuf_out;
- StripTransform *transform;
- float scale_to_fit;
- float image_scale_factor;
- float preview_scale_factor;
- bool for_render;
-} ImageTransformThreadInitData;
-
-typedef struct ImageTransformThreadData {
- ImBuf *ibuf_source;
- ImBuf *ibuf_out;
- StripTransform *transform;
- float scale_to_fit;
- /* image_scale_factor is used to scale proxies to correct preview size. */
- float image_scale_factor;
- /* Preview scale factor is needed to correct translation to match preview size. */
- float preview_scale_factor;
- bool for_render;
- int start_line;
- int tot_line;
-} ImageTransformThreadData;
-
-static void sequencer_image_transform_init(void *handle_v,
- int start_line,
- int tot_line,
- void *init_data_v)
+/**
+ * Effect, mask and scene in strip input strips are rendered in preview resolution.
+ * They are already down-scaled. #input_preprocess() does not expect this to happen.
+ * Other strip types are rendered with original media resolution, unless proxies are
+ * enabled for them. With proxies `is_proxy_image` will be set correctly to true.
+ */
+static bool seq_need_scale_to_render_size(const Sequence *seq, bool is_proxy_image)
{
- ImageTransformThreadData *handle = (ImageTransformThreadData *)handle_v;
- const ImageTransformThreadInitData *init_data = (ImageTransformThreadInitData *)init_data_v;
-
- handle->ibuf_source = init_data->ibuf_source;
- handle->ibuf_out = init_data->ibuf_out;
- handle->transform = init_data->transform;
- handle->image_scale_factor = init_data->image_scale_factor;
- handle->preview_scale_factor = init_data->preview_scale_factor;
- handle->for_render = init_data->for_render;
-
- handle->start_line = start_line;
- handle->tot_line = tot_line;
+ if (is_proxy_image) {
+ return true;
+ }
+ if ((seq->type & SEQ_TYPE_EFFECT) != 0 || seq->type == SEQ_TYPE_MASK ||
+ seq->type == SEQ_TYPE_META ||
+ (seq->type == SEQ_TYPE_SCENE && ((seq->flag & SEQ_SCENE_STRIPS) != 0))) {
+ return true;
+ }
+ return false;
}
-static void *sequencer_image_transform_do_thread(void *data_v)
+static void sequencer_image_crop_transform_matrix(const Sequence *seq,
+ const ImBuf *in,
+ const ImBuf *out,
+ const float image_scale_factor,
+ const float preview_scale_factor,
+ float r_transform_matrix[3][3])
{
- const ImageTransformThreadData *data = (ImageTransformThreadData *)data_v;
- const StripTransform *transform = data->transform;
- const float scale_x = transform->scale_x * data->image_scale_factor;
- const float scale_y = transform->scale_y * data->image_scale_factor;
- const float image_center_offs_x = (data->ibuf_out->x - data->ibuf_source->x) / 2;
- const float image_center_offs_y = (data->ibuf_out->y - data->ibuf_source->y) / 2;
- const float translate_x = transform->xofs * data->preview_scale_factor + image_center_offs_x;
- const float translate_y = transform->yofs * data->preview_scale_factor + image_center_offs_y;
- const float pivot[2] = {data->ibuf_source->x / 2, data->ibuf_source->y / 2};
- float transform_matrix[3][3];
- loc_rot_size_to_mat3(transform_matrix,
+ const StripTransform *transform = seq->strip->transform;
+ const float scale_x = transform->scale_x * image_scale_factor;
+ const float scale_y = transform->scale_y * image_scale_factor;
+ const float image_center_offs_x = (out->x - in->x) / 2;
+ const float image_center_offs_y = (out->y - in->y) / 2;
+ const float translate_x = transform->xofs * preview_scale_factor + image_center_offs_x;
+ const float translate_y = transform->yofs * preview_scale_factor + image_center_offs_y;
+ const float pivot[2] = {in->x / 2, in->y / 2};
+ loc_rot_size_to_mat3(r_transform_matrix,
(const float[]){translate_x, translate_y},
transform->rotation,
(const float[]){scale_x, scale_y});
- invert_m3(transform_matrix);
- transform_pivot_set_m3(transform_matrix, pivot);
+ transform_pivot_set_m3(r_transform_matrix, pivot);
+ invert_m3(r_transform_matrix);
+}
- const int width = data->ibuf_out->x;
- for (int yi = data->start_line; yi < data->start_line + data->tot_line; yi++) {
- for (int xi = 0; xi < width; xi++) {
- float uv[2] = {xi, yi};
- mul_v2_m3v2(uv, transform_matrix, uv);
+static void sequencer_image_crop_init(const Sequence *seq,
+ const ImBuf *in,
+ float crop_scale_factor,
+ rctf *r_crop)
+{
+ const StripCrop *c = seq->strip->crop;
+ const int left = c->left * crop_scale_factor;
+ const int right = c->right * crop_scale_factor;
+ const int top = c->top * crop_scale_factor;
+ const int bottom = c->bottom * crop_scale_factor;
- if (data->for_render) {
- bilinear_interpolation(data->ibuf_source, data->ibuf_out, uv[0], uv[1], xi, yi);
- }
- else {
- nearest_interpolation(data->ibuf_source, data->ibuf_out, uv[0], uv[1], xi, yi);
- }
- }
- }
+ BLI_rctf_init(r_crop, left, in->x - right, bottom, in->y - top);
+}
- return NULL;
+static void sequencer_preprocess_transform_crop(
+ ImBuf *in, ImBuf *out, const SeqRenderData *context, Sequence *seq, const bool is_proxy_image)
+{
+ const Scene *scene = context->scene;
+ const float preview_scale_factor = context->preview_render_size == SEQ_RENDER_SIZE_SCENE ?
+ (float)scene->r.size / 100 :
+ SEQ_rendersize_to_scale_factor(
+ context->preview_render_size);
+ const bool do_scale_to_render_size = seq_need_scale_to_render_size(seq, is_proxy_image);
+ const float image_scale_factor = do_scale_to_render_size ? 1.0f : preview_scale_factor;
+
+ float transform_matrix[3][3];
+ sequencer_image_crop_transform_matrix(
+ seq, in, out, image_scale_factor, preview_scale_factor, transform_matrix);
+
+ /* Proxy image is smaller, so crop values must be corrected by proxy scale factor.
+ * Proxy scale factor always matches preview_scale_factor. */
+ rctf source_crop;
+ const float crop_scale_factor = do_scale_to_render_size ? preview_scale_factor : 1.0f;
+ sequencer_image_crop_init(seq, in, crop_scale_factor, &source_crop);
+
+ const eIMBInterpolationFilterMode filter = context->for_render ? IMB_FILTER_BILINEAR :
+ IMB_FILTER_NEAREST;
+ IMB_transform(in, out, transform_matrix, &source_crop, filter);
}
static void multibuf(ImBuf *ibuf, const float fmul)
@@ -571,25 +588,6 @@ static void multibuf(ImBuf *ibuf, const float fmul)
}
}
-/**
- * Effect, mask and scene in strip input strips are rendered in preview resolution.
- * They are already down-scaled. #input_preprocess() does not expect this to happen.
- * Other strip types are rendered with original media resolution, unless proxies are
- * enabled for them. With proxies `is_proxy_image` will be set correctly to true.
- */
-static bool seq_need_scale_to_render_size(const Sequence *seq, bool is_proxy_image)
-{
- if (is_proxy_image) {
- return true;
- }
- if ((seq->type & SEQ_TYPE_EFFECT) != 0 || seq->type == SEQ_TYPE_MASK ||
- seq->type == SEQ_TYPE_META ||
- (seq->type == SEQ_TYPE_SCENE && ((seq->flag & SEQ_SCENE_STRIPS) != 0))) {
- return true;
- }
- return false;
-}
-
static ImBuf *input_preprocess(const SeqRenderData *context,
Sequence *seq,
float timeline_frame,
@@ -608,67 +606,14 @@ static ImBuf *input_preprocess(const SeqRenderData *context,
IMB_filtery(preprocessed_ibuf);
}
- /* Get scale factor if preview resolution doesn't match project resolution. */
- float preview_scale_factor;
- if (context->preview_render_size == SEQ_RENDER_SIZE_SCENE) {
- preview_scale_factor = (float)scene->r.size / 100;
- }
- else {
- preview_scale_factor = SEQ_rendersize_to_scale_factor(context->preview_render_size);
- }
-
- if (sequencer_use_crop(seq)) {
- /* Change original image pointer to avoid another duplication in SEQ_USE_TRANSFORM. */
- preprocessed_ibuf = IMB_makeSingleUser(ibuf);
- ibuf = preprocessed_ibuf;
-
- const int width = ibuf->x;
- const int height = ibuf->y;
- const StripCrop *c = seq->strip->crop;
-
- /* Proxy image is smaller, so crop values must be corrected by proxy scale factor.
- * Proxy scale factor always matches preview_scale_factor. */
- const float crop_scale_factor = seq_need_scale_to_render_size(seq, is_proxy_image) ?
- preview_scale_factor :
- 1.0f;
- const int left = c->left * crop_scale_factor;
- const int right = c->right * crop_scale_factor;
- const int top = c->top * crop_scale_factor;
- const int bottom = c->bottom * crop_scale_factor;
- const float col[4] = {0.0f, 0.0f, 0.0f, 0.0f};
-
- /* Left. */
- IMB_rectfill_area_replace(preprocessed_ibuf, col, 0, 0, left, height);
- /* Bottom. */
- IMB_rectfill_area_replace(preprocessed_ibuf, col, left, 0, width, bottom);
- /* Right. */
- IMB_rectfill_area_replace(preprocessed_ibuf, col, width - right, bottom, width, height);
- /* Top. */
- IMB_rectfill_area_replace(preprocessed_ibuf, col, left, height - top, width - right, height);
- }
-
- if (sequencer_use_transform(seq) || context->rectx != ibuf->x || context->recty != ibuf->y) {
+ if (sequencer_use_crop(seq) || sequencer_use_transform(seq) || context->rectx != ibuf->x ||
+ context->recty != ibuf->y) {
const int x = context->rectx;
const int y = context->recty;
preprocessed_ibuf = IMB_allocImBuf(x, y, 32, ibuf->rect_float ? IB_rectfloat : IB_rect);
- ImageTransformThreadInitData init_data = {NULL};
- init_data.ibuf_source = ibuf;
- init_data.ibuf_out = preprocessed_ibuf;
- init_data.transform = seq->strip->transform;
- if (seq_need_scale_to_render_size(seq, is_proxy_image)) {
- init_data.image_scale_factor = 1.0f;
- }
- else {
- init_data.image_scale_factor = preview_scale_factor;
- }
- init_data.preview_scale_factor = preview_scale_factor;
- init_data.for_render = context->for_render;
- IMB_processor_apply_threaded(context->recty,
- sizeof(ImageTransformThreadData),
- &init_data,
- sequencer_image_transform_init,
- sequencer_image_transform_do_thread);
+ sequencer_preprocess_transform_crop(ibuf, preprocessed_ibuf, context, seq, is_proxy_image);
+
seq_imbuf_assign_spaces(scene, preprocessed_ibuf);
IMB_metadata_copy(preprocessed_ibuf, ibuf);
IMB_freeImBuf(ibuf);
@@ -1117,8 +1062,6 @@ static ImBuf *seq_render_movie_strip_view(const SeqRenderData *context,
ImBuf *ibuf = NULL;
IMB_Proxy_Size psize = SEQ_rendersize_to_proxysize(context->preview_render_size);
- IMB_anim_set_preseek(sanim->anim, seq->anim_preseek);
-
if (SEQ_can_use_proxy(context, seq, psize)) {
/* Try to get a proxy image.
* Movie proxies are handled by ImBuf module with exception of `custom file` setting. */
@@ -1230,6 +1173,12 @@ static ImBuf *seq_render_movie_strip(const SeqRenderData *context,
}
if (*r_is_proxy_image == false) {
+ if (sanim && sanim->anim) {
+ short fps_denom;
+ float fps_num;
+ IMB_anim_get_fps(sanim->anim, &fps_denom, &fps_num, true);
+ seq->strip->stripdata->orig_fps = fps_denom / fps_num;
+ }
seq->strip->stripdata->orig_width = ibuf->x;
seq->strip->stripdata->orig_height = ibuf->y;
}
@@ -1501,7 +1450,7 @@ static ImBuf *seq_render_scene_strip(const SeqRenderData *context,
goto finally;
}
- if (seq->flag & SEQ_SCENE_NO_GPENCIL) {
+ if (seq->flag & SEQ_SCENE_NO_ANNOTATION) {
use_gpencil = false;
}
@@ -1537,13 +1486,14 @@ static ImBuf *seq_render_scene_strip(const SeqRenderData *context,
/* opengl offscreen render */
depsgraph = BKE_scene_ensure_depsgraph(context->bmain, scene, view_layer);
BKE_scene_graph_update_for_newframe(depsgraph);
+ Object *camera_eval = DEG_get_evaluated_object(depsgraph, camera);
ibuf = sequencer_view3d_fn(
/* set for OpenGL render (NULL when scrubbing) */
depsgraph,
scene,
&context->scene->display.shading,
context->scene->r.seq_prev_type,
- camera,
+ camera_eval,
width,
height,
IB_rect,
@@ -1966,7 +1916,7 @@ static ImBuf *seq_render_strip_stack(const SeqRenderData *context,
/**
* \return The image buffer or NULL.
*
- * \note The returned #ImBuf is has it's reference increased, free after usage!
+ * \note The returned #ImBuf has its reference increased, free after usage!
*/
ImBuf *SEQ_render_give_ibuf(const SeqRenderData *context, float timeline_frame, int chanshown)
{
diff --git a/source/blender/sequencer/intern/render.h b/source/blender/sequencer/intern/render.h
index 1147516b8ec..a0cdf24d84b 100644
--- a/source/blender/sequencer/intern/render.h
+++ b/source/blender/sequencer/intern/render.h
@@ -60,10 +60,10 @@ struct ImBuf *seq_render_effect_execute_threaded(struct SeqEffectHandle *sh,
struct ImBuf *ibuf2,
struct ImBuf *ibuf3);
void seq_imbuf_to_sequencer_space(struct Scene *scene, struct ImBuf *ibuf, bool make_float);
-int seq_get_shown_sequences(struct ListBase *seqbasep,
+int seq_get_shown_sequences(struct ListBase *seqbase,
int timeline_frame,
int chanshown,
- struct Sequence **seq_arr_out);
+ struct Sequence **r_seq_arr);
struct ImBuf *seq_render_strip(const struct SeqRenderData *context,
struct SeqRenderState *state,
struct Sequence *seq,
diff --git a/source/blender/sequencer/intern/sequencer.c b/source/blender/sequencer/intern/sequencer.c
index cc11796496c..d0bc41062a1 100644
--- a/source/blender/sequencer/intern/sequencer.c
+++ b/source/blender/sequencer/intern/sequencer.c
@@ -313,6 +313,17 @@ SequencerToolSettings *SEQ_tool_settings_init(void)
return tool_settings;
}
+SequencerToolSettings *SEQ_tool_settings_ensure(Scene *scene)
+{
+ SequencerToolSettings *tool_settings = scene->toolsettings->sequencer_tool_settings;
+ if (tool_settings == NULL) {
+ scene->toolsettings->sequencer_tool_settings = SEQ_tool_settings_init();
+ tool_settings = scene->toolsettings->sequencer_tool_settings;
+ }
+
+ return tool_settings;
+}
+
void SEQ_tool_settings_free(SequencerToolSettings *tool_settings)
{
MEM_freeN(tool_settings);
@@ -320,13 +331,13 @@ void SEQ_tool_settings_free(SequencerToolSettings *tool_settings)
eSeqImageFitMethod SEQ_tool_settings_fit_method_get(Scene *scene)
{
- const SequencerToolSettings *tool_settings = scene->toolsettings->sequencer_tool_settings;
+ const SequencerToolSettings *tool_settings = SEQ_tool_settings_ensure(scene);
return tool_settings->fit_method;
}
void SEQ_tool_settings_fit_method_set(Scene *scene, eSeqImageFitMethod fit_method)
{
- SequencerToolSettings *tool_settings = scene->toolsettings->sequencer_tool_settings;
+ SequencerToolSettings *tool_settings = SEQ_tool_settings_ensure(scene);
tool_settings->fit_method = fit_method;
}
@@ -448,8 +459,8 @@ static Sequence *seq_dupli(const Scene *scene_src,
seqn->strip->stripdata = NULL;
BLI_listbase_clear(&seqn->seqbase);
- /* WATCH OUT!!! - This metastrip is not recursively duplicated here - do this after!!! */
- /* - seq_dupli_recursive(&seq->seqbase, &seqn->seqbase);*/
+ /* WARNING: This meta-strip is not recursively duplicated here - do this after! */
+ // seq_dupli_recursive(&seq->seqbase, &seqn->seqbase);
}
else if (seq->type == SEQ_TYPE_SCENE) {
seqn->strip->stripdata = NULL;
@@ -505,10 +516,6 @@ static Sequence *seq_dupli(const Scene *scene_src,
if (dupe_flag & SEQ_DUPE_UNIQUE_NAME) {
SEQ_sequence_base_unique_name_recursive(&scene_dst->ed->seqbase, seqn);
}
-
- if (dupe_flag & SEQ_DUPE_ANIM) {
- SEQ_dupe_animdata(scene_dst, seq->name + 2, seqn->name + 2);
- }
}
return seqn;
@@ -554,30 +561,21 @@ void SEQ_sequence_base_dupli_recursive(const Scene *scene_src,
{
Sequence *seq;
Sequence *seqn = NULL;
- Sequence *last_seq = SEQ_select_active_get((Scene *)scene_src);
- /* always include meta's strips */
- int dupe_flag_recursive = dupe_flag | SEQ_DUPE_ALL | SEQ_DUPE_IS_RECURSIVE_CALL;
for (seq = seqbase->first; seq; seq = seq->next) {
seq->tmp = NULL;
if ((seq->flag & SELECT) || (dupe_flag & SEQ_DUPE_ALL)) {
seqn = seq_dupli(scene_src, scene_dst, nseqbase, seq, dupe_flag, flag);
- if (seqn) { /*should never fail */
- if (dupe_flag & SEQ_DUPE_CONTEXT) {
- seq->flag &= ~SEQ_ALLSEL;
- seqn->flag &= ~(SEQ_LEFTSEL + SEQ_RIGHTSEL + SEQ_LOCK);
- }
- if (seq->type == SEQ_TYPE_META) {
- SEQ_sequence_base_dupli_recursive(
- scene_src, scene_dst, &seqn->seqbase, &seq->seqbase, dupe_flag_recursive, flag);
- }
+ if (seqn == NULL) {
+ continue; /* Should never fail. */
+ }
- if (dupe_flag & SEQ_DUPE_CONTEXT) {
- if (seq == last_seq) {
- SEQ_select_active_set(scene_dst, seqn);
- }
- }
+ if (seq->type == SEQ_TYPE_META) {
+ /* Always include meta all strip children. */
+ int dupe_flag_recursive = dupe_flag | SEQ_DUPE_ALL | SEQ_DUPE_IS_RECURSIVE_CALL;
+ SEQ_sequence_base_dupli_recursive(
+ scene_src, scene_dst, &seqn->seqbase, &seq->seqbase, dupe_flag_recursive, flag);
}
}
}
diff --git a/source/blender/sequencer/intern/strip_add.c b/source/blender/sequencer/intern/strip_add.c
index 33dd74cb527..888220b7111 100644
--- a/source/blender/sequencer/intern/strip_add.c
+++ b/source/blender/sequencer/intern/strip_add.c
@@ -50,6 +50,7 @@
#include "DEG_depsgraph_query.h"
+#include "IMB_colormanagement.h"
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
#include "IMB_metadata.h"
@@ -99,7 +100,7 @@ static void seq_add_generic_update(Scene *scene, ListBase *seqbase, Sequence *se
{
SEQ_sequence_base_unique_name_recursive(seqbase, seq);
SEQ_time_update_sequence_bounds(scene, seq);
- SEQ_sort(scene);
+ SEQ_sort(seqbase);
SEQ_relations_invalidate_cache_composite(scene, seq);
}
@@ -128,6 +129,24 @@ static void seq_add_set_name(Sequence *seq, SeqLoadData *load_data)
}
}
+static void seq_add_set_view_transform(Scene *scene, Sequence *seq, SeqLoadData *load_data)
+{
+ const char *strip_colorspace = seq->strip->colorspace_settings.name;
+
+ if (load_data->flags & SEQ_LOAD_SET_VIEW_TRANSFORM) {
+ const char *role_colorspace_byte;
+ role_colorspace_byte = IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_DEFAULT_BYTE);
+
+ if (STREQ(strip_colorspace, role_colorspace_byte)) {
+ struct ColorManagedDisplay *display = IMB_colormanagement_display_get_named(
+ scene->display_settings.display_device);
+ const char *default_view_transform =
+ IMB_colormanagement_display_get_default_view_transform_name(display);
+ STRNCPY(scene->view_settings.view_transform, default_view_transform);
+ }
+ }
+}
+
/**
* Add scene strip.
*
@@ -324,7 +343,7 @@ Sequence *SEQ_add_image_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqL
/* Set initial scale based on load_data->fit_method. */
char file_path[FILE_MAX];
- BLI_join_dirfile(file_path, sizeof(file_path), load_data->path, load_data->name);
+ BLI_strncpy(file_path, load_data->path, sizeof(file_path));
BLI_path_abs(file_path, BKE_main_blendfile_path(bmain));
ImBuf *ibuf = IMB_loadiffname(file_path, IB_rect, seq->strip->colorspace_settings.name);
if (ibuf != NULL) {
@@ -344,6 +363,7 @@ Sequence *SEQ_add_image_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqL
/* Set Last active directory. */
BLI_strncpy(scene->ed->act_imagedir, seq->strip->dir, sizeof(scene->ed->act_imagedir));
+ seq_add_set_view_transform(scene, seq, load_data);
seq_add_set_name(seq, load_data);
seq_add_generic_update(scene, seqbase, seq);
@@ -389,7 +409,7 @@ Sequence *SEQ_add_sound_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqL
Strip *strip = seq->strip;
/* We only need 1 element to store the filename. */
- StripElem *se = strip->stripdata = se = MEM_callocN(sizeof(StripElem), "stripelem");
+ StripElem *se = strip->stripdata = MEM_callocN(sizeof(StripElem), "stripelem");
BLI_split_dirfile(load_data->path, strip->dir, se->name, sizeof(strip->dir), sizeof(se->name));
if (seq != NULL && seq->sound != NULL) {
@@ -528,15 +548,24 @@ Sequence *SEQ_add_movie_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqL
seq->blend_mode = SEQ_TYPE_CROSS; /* so alpha adjustment fade to the strip below */
+ float video_fps = 0.0f;
+
if (anim_arr[0] != NULL) {
- seq->anim_preseek = IMB_anim_get_preseek(anim_arr[0]);
seq->len = IMB_anim_get_duration(anim_arr[0], IMB_TC_RECORD_RUN);
IMB_anim_load_metadata(anim_arr[0]);
+ short fps_denom;
+ float fps_num;
+
+ IMB_anim_get_fps(anim_arr[0], &fps_denom, &fps_num, true);
+
+ video_fps = fps_denom / fps_num;
+
/* Adjust scene's frame rate settings to match. */
if (load_data->flags & SEQ_LOAD_MOVIE_SYNC_FPS) {
- IMB_anim_get_fps(anim_arr[0], &scene->r.frs_sec, &scene->r.frs_sec_base, true);
+ scene->r.frs_sec = fps_denom;
+ scene->r.frs_sec_base = fps_num;
}
/* Set initial scale based on load_data->fit_method. */
@@ -557,8 +586,10 @@ Sequence *SEQ_add_movie_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqL
strip->stripdata = se = MEM_callocN(sizeof(StripElem), "stripelem");
strip->stripdata->orig_width = orig_width;
strip->stripdata->orig_height = orig_height;
+ strip->stripdata->orig_fps = video_fps;
BLI_split_dirfile(load_data->path, strip->dir, se->name, sizeof(strip->dir), sizeof(se->name));
+ seq_add_set_view_transform(scene, seq, load_data);
seq_add_set_name(seq, load_data);
seq_add_generic_update(scene, seqbase, seq);
@@ -670,8 +701,6 @@ void SEQ_add_reload_new_file(Main *bmain, Scene *scene, Sequence *seq, const boo
seq->len = IMB_anim_get_duration(
sanim->anim, seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN);
- seq->anim_preseek = IMB_anim_get_preseek(sanim->anim);
-
seq->len -= seq->anim_startofs;
seq->len -= seq->anim_endofs;
if (seq->len < 0) {
diff --git a/source/blender/sequencer/intern/strip_edit.c b/source/blender/sequencer/intern/strip_edit.c
index 4a27fb3a087..4de6ec3583c 100644
--- a/source/blender/sequencer/intern/strip_edit.c
+++ b/source/blender/sequencer/intern/strip_edit.c
@@ -39,10 +39,12 @@
#include "BKE_sound.h"
#include "strip_time.h"
+#include "utils.h"
#include "SEQ_add.h"
#include "SEQ_edit.h"
#include "SEQ_effects.h"
+#include "SEQ_iterator.h"
#include "SEQ_relations.h"
#include "SEQ_sequencer.h"
#include "SEQ_time.h"
@@ -149,7 +151,7 @@ void SEQ_edit_update_muting(Editing *ed)
static void sequencer_flag_users_for_removal(Scene *scene, ListBase *seqbase, Sequence *seq)
{
LISTBASE_FOREACH (Sequence *, user_seq, seqbase) {
- /* Look in metas for usage of seq. */
+ /* Look in meta-strips for usage of seq. */
if (user_seq->type == SEQ_TYPE_META) {
sequencer_flag_users_for_removal(scene, &user_seq->seqbase, seq);
}
@@ -251,22 +253,26 @@ bool SEQ_edit_move_strip_to_meta(Scene *scene,
return false;
}
- /* Remove users of src_seq. Ideally these could be moved into meta as well, but this would be
- * best to do with generalized iterator as described in D10337. */
- sequencer_flag_users_for_removal(scene, seqbase, src_seq);
- SEQ_edit_remove_flagged_sequences(scene, seqbase);
+ SeqCollection *collection = SEQ_collection_create();
+ SEQ_collection_append_strip(src_seq, collection);
+ SEQ_collection_expand(seqbase, collection, SEQ_query_strip_effect_chain);
- /* Move to meta. */
- BLI_remlink(seqbase, src_seq);
- BLI_addtail(&dst_seqm->seqbase, src_seq);
- SEQ_relations_invalidate_cache_preprocessed(scene, src_seq);
-
- /* Update meta. */
- SEQ_time_update_sequence(scene, dst_seqm);
- if (SEQ_transform_test_overlap(&dst_seqm->seqbase, src_seq)) {
- SEQ_transform_seqbase_shuffle(&dst_seqm->seqbase, src_seq, scene);
+ Sequence *seq;
+ SEQ_ITERATOR_FOREACH (seq, collection) {
+ /* Move to meta. */
+ BLI_remlink(seqbase, seq);
+ BLI_addtail(&dst_seqm->seqbase, seq);
+ SEQ_relations_invalidate_cache_preprocessed(scene, seq);
+
+ /* Update meta. */
+ SEQ_time_update_sequence(scene, dst_seqm);
+ if (SEQ_transform_test_overlap(&dst_seqm->seqbase, seq)) {
+ SEQ_transform_seqbase_shuffle(&dst_seqm->seqbase, seq, scene);
+ }
}
+ SEQ_collection_free(collection);
+
return true;
}
@@ -343,6 +349,29 @@ static void seq_split_set_left_offset(Sequence *seq, int timeline_frame)
SEQ_transform_set_left_handle_frame(seq, timeline_frame);
}
+static void seq_edit_split_handle_strip_offsets(Main *bmain,
+ Scene *scene,
+ Sequence *left_seq,
+ Sequence *right_seq,
+ const int timeline_frame,
+ const eSeqSplitMethod method)
+{
+ switch (method) {
+ case SEQ_SPLIT_SOFT:
+ seq_split_set_left_offset(right_seq, timeline_frame);
+ seq_split_set_right_offset(left_seq, timeline_frame);
+ break;
+ case SEQ_SPLIT_HARD:
+ seq_split_set_right_hold_offset(left_seq, timeline_frame);
+ seq_split_set_left_hold_offset(right_seq, timeline_frame);
+ SEQ_add_reload_new_file(bmain, scene, left_seq, false);
+ SEQ_add_reload_new_file(bmain, scene, right_seq, false);
+ break;
+ }
+ SEQ_time_update_sequence(scene, left_seq);
+ SEQ_time_update_sequence(scene, right_seq);
+}
+
/**
* Split Sequence at timeline_frame in two.
*
@@ -365,33 +394,44 @@ Sequence *SEQ_edit_strip_split(Main *bmain,
return NULL;
}
- if (method == SEQ_SPLIT_HARD) {
- /* Precaution, needed because the length saved on-disk may not match the length saved in the
- * blend file, or our code may have minor differences reading file length between versions.
- * This causes hard-split to fail, see: T47862. */
- SEQ_add_reload_new_file(bmain, scene, seq, true);
- SEQ_time_update_sequence(scene, seq);
+ SeqCollection *collection = SEQ_collection_create();
+ SEQ_collection_append_strip(seq, collection);
+ SEQ_collection_expand(seqbase, collection, SEQ_query_strip_effect_chain);
+
+ /* Move strips in collection from seqbase to new ListBase. */
+ ListBase left_strips = {NULL, NULL};
+ SEQ_ITERATOR_FOREACH (seq, collection) {
+ BLI_remlink(seqbase, seq);
+ BLI_addtail(&left_strips, seq);
}
- Sequence *left_seq = seq;
- Sequence *right_seq = SEQ_sequence_dupli_recursive(
- scene, scene, seqbase, seq, SEQ_DUPE_UNIQUE_NAME | SEQ_DUPE_ANIM);
+ /* Sort list, so that no strip can depend on next strip in list.
+ * This is important for SEQ_time_update_sequence functionality. */
+ SEQ_sort(&left_strips);
+
+ /* Duplicate ListBase. */
+ ListBase right_strips = {NULL, NULL};
+ SEQ_sequence_base_dupli_recursive(scene, scene, &right_strips, &left_strips, SEQ_DUPE_ALL, 0);
+
+ /* Split strips. */
+ Sequence *left_seq = left_strips.first;
+ Sequence *right_seq = right_strips.first;
+ Sequence *return_seq = right_strips.first;
+ while (left_seq && right_seq) {
+ seq_edit_split_handle_strip_offsets(bmain, scene, left_seq, right_seq, timeline_frame, method);
+ left_seq = left_seq->next;
+ right_seq = right_seq->next;
+ }
- switch (method) {
- case SEQ_SPLIT_SOFT:
- seq_split_set_left_offset(right_seq, timeline_frame);
- seq_split_set_right_offset(left_seq, timeline_frame);
- break;
- case SEQ_SPLIT_HARD:
- seq_split_set_right_hold_offset(left_seq, timeline_frame);
- seq_split_set_left_hold_offset(right_seq, timeline_frame);
- SEQ_add_reload_new_file(bmain, scene, left_seq, false);
- SEQ_add_reload_new_file(bmain, scene, right_seq, false);
- break;
+ seq = right_strips.first;
+ BLI_movelisttolist(seqbase, &left_strips);
+ BLI_movelisttolist(seqbase, &right_strips);
+
+ for (; seq; seq = seq->next) {
+ SEQ_ensure_unique_name(seq, scene);
}
- SEQ_time_update_sequence(scene, left_seq);
- SEQ_time_update_sequence(scene, right_seq);
- return right_seq;
+
+ return return_seq;
}
/**
diff --git a/source/blender/sequencer/intern/strip_relations.c b/source/blender/sequencer/intern/strip_relations.c
index 1a2ff08bd08..7c5a3f031db 100644
--- a/source/blender/sequencer/intern/strip_relations.c
+++ b/source/blender/sequencer/intern/strip_relations.c
@@ -114,7 +114,6 @@ static void sequence_invalidate_cache(Scene *scene,
Editing *ed = scene->ed;
if (invalidate_self) {
- SEQ_relations_sequence_free_anim(seq);
seq_cache_cleanup_sequence(scene, seq, seq, invalidate_types, false);
}
@@ -260,7 +259,7 @@ void SEQ_relations_free_imbuf(Scene *scene, ListBase *seqbase, bool for_render)
SEQ_prefetch_stop(scene);
for (seq = seqbase->first; seq; seq = seq->next) {
- if (for_render && CFRA >= seq->startdisp && CFRA <= seq->enddisp) {
+ if (for_render && SEQ_time_strip_intersects_frame(seq, CFRA)) {
continue;
}
@@ -359,7 +358,7 @@ void SEQ_relations_update_changed_seq_and_deps(Scene *scene,
static void sequencer_all_free_anim_ibufs(ListBase *seqbase, int timeline_frame)
{
for (Sequence *seq = seqbase->first; seq != NULL; seq = seq->next) {
- if (seq->enddisp < timeline_frame || seq->startdisp > timeline_frame) {
+ if (!SEQ_time_strip_intersects_frame(seq, timeline_frame)) {
SEQ_relations_sequence_free_anim(seq);
}
if (seq->type == SEQ_TYPE_META) {
diff --git a/source/blender/sequencer/intern/strip_time.c b/source/blender/sequencer/intern/strip_time.c
index 21dc9aa2cdd..b8b6f62c7aa 100644
--- a/source/blender/sequencer/intern/strip_time.c
+++ b/source/blender/sequencer/intern/strip_time.c
@@ -36,9 +36,11 @@
#include "IMB_imbuf.h"
+#include "SEQ_iterator.h"
#include "SEQ_render.h"
#include "SEQ_sequencer.h"
#include "SEQ_time.h"
+#include "SEQ_transform.h"
#include "strip_time.h"
#include "utils.h"
@@ -161,11 +163,41 @@ void SEQ_time_update_sequence_bounds(Scene *scene, Sequence *seq)
}
}
+static void seq_time_update_meta_strip(Scene *scene, Sequence *seq_meta)
+{
+ if (BLI_listbase_is_empty(&seq_meta->seqbase)) {
+ return;
+ }
+
+ int min = MAXFRAME * 2;
+ int max = -MAXFRAME * 2;
+ LISTBASE_FOREACH (Sequence *, seq, &seq_meta->seqbase) {
+ min = min_ii(seq->startdisp, min);
+ max = max_ii(seq->enddisp, max);
+ }
+
+ seq_meta->start = min + seq_meta->anim_startofs;
+ seq_meta->len = max - min;
+ seq_meta->len -= seq_meta->anim_startofs;
+ seq_meta->len -= seq_meta->anim_endofs;
+
+ seq_update_sound_bounds_recursive(scene, seq_meta);
+}
+
+static void seq_time_update_meta_strip_range(Scene *scene, Sequence *seq_meta)
+{
+ seq_time_update_meta_strip(scene, seq_meta);
+
+ /* Prevent meta-strip to move in timeline. */
+ SEQ_transform_set_left_handle_frame(seq_meta, seq_meta->startdisp);
+ SEQ_transform_set_right_handle_frame(seq_meta, seq_meta->enddisp);
+}
+
void SEQ_time_update_sequence(Scene *scene, Sequence *seq)
{
Sequence *seqm;
- /* check all metas recursively */
+ /* Check all meta-strips recursively. */
seqm = seq->seqbase.first;
while (seqm) {
if (seqm->seqbase.first) {
@@ -211,6 +243,16 @@ void SEQ_time_update_sequence(Scene *scene, Sequence *seq)
}
}
else {
+ if (seq->type == SEQ_TYPE_META) {
+ seq_time_update_meta_strip(scene, seq);
+ }
+
+ Editing *ed = SEQ_editing_get(scene, false);
+ MetaStack *ms = SEQ_meta_stack_active_get(ed);
+ if (ms != NULL) {
+ seq_time_update_meta_strip_range(scene, ms->parseq);
+ }
+
SEQ_time_update_sequence_bounds(scene, seq);
}
}
@@ -363,6 +405,17 @@ void SEQ_timeline_boundbox(const Scene *scene, const ListBase *seqbase, rctf *re
}
}
+static bool strip_exists_at_frame(SeqCollection *all_strips, const int timeline_frame)
+{
+ Sequence *seq;
+ SEQ_ITERATOR_FOREACH (seq, all_strips) {
+ if (SEQ_time_strip_intersects_frame(seq, timeline_frame)) {
+ return true;
+ }
+ }
+ return false;
+}
+
/**
* Find first gap between strips after initial_frame and describe it by filling data of r_gap_info
*
@@ -384,10 +437,12 @@ void seq_time_gap_info_get(const Scene *scene,
int timeline_frame = initial_frame;
r_gap_info->gap_exists = false;
- if (SEQ_render_evaluate_frame(seqbase, initial_frame) == 0) {
+ SeqCollection *collection = SEQ_query_all_strips(seqbase);
+
+ if (!strip_exists_at_frame(collection, initial_frame)) {
/* Search backward for gap_start_frame. */
for (; timeline_frame >= sfra; timeline_frame--) {
- if (SEQ_render_evaluate_frame(seqbase, timeline_frame) != 0) {
+ if (strip_exists_at_frame(collection, timeline_frame)) {
break;
}
}
@@ -397,7 +452,7 @@ void seq_time_gap_info_get(const Scene *scene,
else {
/* Search forward for gap_start_frame. */
for (; timeline_frame <= efra; timeline_frame++) {
- if (SEQ_render_evaluate_frame(seqbase, timeline_frame) == 0) {
+ if (!strip_exists_at_frame(collection, timeline_frame)) {
r_gap_info->gap_start_frame = timeline_frame;
break;
}
@@ -405,7 +460,7 @@ void seq_time_gap_info_get(const Scene *scene,
}
/* Search forward for gap_end_frame. */
for (; timeline_frame <= efra; timeline_frame++) {
- if (SEQ_render_evaluate_frame(seqbase, timeline_frame) != 0) {
+ if (strip_exists_at_frame(collection, timeline_frame)) {
const int gap_end_frame = timeline_frame;
r_gap_info->gap_length = gap_end_frame - r_gap_info->gap_start_frame;
r_gap_info->gap_exists = true;
@@ -413,3 +468,17 @@ void seq_time_gap_info_get(const Scene *scene,
}
}
}
+
+/**
+ * Test if strip intersects with timeline frame.
+ * Note: This checks if strip would be rendered at this frame. For rendering it is assumed, that
+ * timeline frame has width of 1 frame and therefore ends at timeline_frame + 1
+ *
+ * \param seq: Sequence to be checked
+ * \param timeline_frame: absolute frame position
+ * \return true if strip intersects with timeline frame.
+ */
+bool SEQ_time_strip_intersects_frame(const Sequence *seq, const int timeline_frame)
+{
+ return (seq->startdisp <= timeline_frame) && (seq->enddisp > timeline_frame);
+}
diff --git a/source/blender/sequencer/intern/strip_transform.c b/source/blender/sequencer/intern/strip_transform.c
index 3a0ba36b795..a02d8a1428e 100644
--- a/source/blender/sequencer/intern/strip_transform.c
+++ b/source/blender/sequencer/intern/strip_transform.c
@@ -48,24 +48,12 @@ static int seq_tx_get_end(Sequence *seq)
return seq->start + seq->len;
}
-int SEQ_transform_get_left_handle_frame(Sequence *seq, bool metaclip)
+int SEQ_transform_get_left_handle_frame(Sequence *seq)
{
- if (metaclip && seq->tmp) {
- /* return the range clipped by the parents range */
- return max_ii(SEQ_transform_get_left_handle_frame(seq, false),
- SEQ_transform_get_left_handle_frame((Sequence *)seq->tmp, true));
- }
-
return (seq->start - seq->startstill) + seq->startofs;
}
-int SEQ_transform_get_right_handle_frame(Sequence *seq, bool metaclip)
+int SEQ_transform_get_right_handle_frame(Sequence *seq)
{
- if (metaclip && seq->tmp) {
- /* return the range clipped by the parents range */
- return min_ii(SEQ_transform_get_right_handle_frame(seq, false),
- SEQ_transform_get_right_handle_frame((Sequence *)seq->tmp, true));
- }
-
return ((seq->start + seq->len) + seq->endstill) - seq->endofs;
}
@@ -151,14 +139,12 @@ bool SEQ_transform_seqbase_isolated_sel_check(ListBase *seqbase)
void SEQ_transform_handle_xlimits(Sequence *seq, int leftflag, int rightflag)
{
if (leftflag) {
- if (SEQ_transform_get_left_handle_frame(seq, false) >=
- SEQ_transform_get_right_handle_frame(seq, false)) {
- SEQ_transform_set_left_handle_frame(seq,
- SEQ_transform_get_right_handle_frame(seq, false) - 1);
+ if (SEQ_transform_get_left_handle_frame(seq) >= SEQ_transform_get_right_handle_frame(seq)) {
+ SEQ_transform_set_left_handle_frame(seq, SEQ_transform_get_right_handle_frame(seq) - 1);
}
if (SEQ_transform_single_image_check(seq) == 0) {
- if (SEQ_transform_get_left_handle_frame(seq, false) >= seq_tx_get_end(seq)) {
+ if (SEQ_transform_get_left_handle_frame(seq) >= seq_tx_get_end(seq)) {
SEQ_transform_set_left_handle_frame(seq, seq_tx_get_end(seq) - 1);
}
@@ -175,14 +161,12 @@ void SEQ_transform_handle_xlimits(Sequence *seq, int leftflag, int rightflag)
}
if (rightflag) {
- if (SEQ_transform_get_right_handle_frame(seq, false) <=
- SEQ_transform_get_left_handle_frame(seq, false)) {
- SEQ_transform_set_right_handle_frame(seq,
- SEQ_transform_get_left_handle_frame(seq, false) + 1);
+ if (SEQ_transform_get_right_handle_frame(seq) <= SEQ_transform_get_left_handle_frame(seq)) {
+ SEQ_transform_set_right_handle_frame(seq, SEQ_transform_get_left_handle_frame(seq) + 1);
}
if (SEQ_transform_single_image_check(seq) == 0) {
- if (SEQ_transform_get_right_handle_frame(seq, false) <= seq_tx_get_start(seq)) {
+ if (SEQ_transform_get_right_handle_frame(seq) <= seq_tx_get_start(seq)) {
SEQ_transform_set_right_handle_frame(seq, seq_tx_get_start(seq) + 1);
}
}
@@ -204,14 +188,12 @@ void SEQ_transform_fix_single_image_seq_offsets(Sequence *seq)
/* make sure the image is always at the start since there is only one,
* adjusting its start should be ok */
- left = SEQ_transform_get_left_handle_frame(seq, false);
+ left = SEQ_transform_get_left_handle_frame(seq);
start = seq->start;
if (start != left) {
offset = left - start;
- SEQ_transform_set_left_handle_frame(seq,
- SEQ_transform_get_left_handle_frame(seq, false) - offset);
- SEQ_transform_set_right_handle_frame(
- seq, SEQ_transform_get_right_handle_frame(seq, false) - offset);
+ SEQ_transform_set_left_handle_frame(seq, SEQ_transform_get_left_handle_frame(seq) - offset);
+ SEQ_transform_set_right_handle_frame(seq, SEQ_transform_get_right_handle_frame(seq) - offset);
seq->start += offset;
}
}
diff --git a/source/blender/sequencer/intern/utils.c b/source/blender/sequencer/intern/utils.c
index a15465eb3c0..9aeb2961751 100644
--- a/source/blender/sequencer/intern/utils.c
+++ b/source/blender/sequencer/intern/utils.c
@@ -33,10 +33,7 @@
#include "DNA_scene_types.h"
#include "DNA_sequence_types.h"
-#include "BLI_listbase.h"
-#include "BLI_path_util.h"
-#include "BLI_string.h"
-#include "BLI_utildefines.h"
+#include "BLI_blenlib.h"
#include "BKE_image.h"
#include "BKE_main.h"
@@ -46,6 +43,7 @@
#include "SEQ_relations.h"
#include "SEQ_select.h"
#include "SEQ_sequencer.h"
+#include "SEQ_time.h"
#include "SEQ_utils.h"
#include "IMB_imbuf.h"
@@ -55,21 +53,27 @@
#include "proxy.h"
#include "utils.h"
-void SEQ_sort(Scene *scene)
+/**
+ * Sort strips in provided seqbase. Effect strips are trailing the list and they are sorted by
+ * channel position as well.
+ * This is important for SEQ_time_update_sequence to work properly
+ *
+ * \param seqbase: ListBase with strips
+ */
+void SEQ_sort(ListBase *seqbase)
{
- /* all strips together per kind, and in order of y location ("machine") */
- ListBase seqbase, effbase;
- Editing *ed = SEQ_editing_get(scene, false);
- Sequence *seq, *seqt;
-
- if (ed == NULL) {
+ if (seqbase == NULL) {
return;
}
- BLI_listbase_clear(&seqbase);
+ /* all strips together per kind, and in order of y location ("machine") */
+ ListBase inputbase, effbase;
+ Sequence *seq, *seqt;
+
+ BLI_listbase_clear(&inputbase);
BLI_listbase_clear(&effbase);
- while ((seq = BLI_pophead(ed->seqbasep))) {
+ while ((seq = BLI_pophead(seqbase))) {
if (seq->type & SEQ_TYPE_EFFECT) {
seqt = effbase.first;
@@ -85,22 +89,22 @@ void SEQ_sort(Scene *scene)
}
}
else {
- seqt = seqbase.first;
+ seqt = inputbase.first;
while (seqt) {
if (seqt->machine >= seq->machine) {
- BLI_insertlinkbefore(&seqbase, seqt, seq);
+ BLI_insertlinkbefore(&inputbase, seqt, seq);
break;
}
seqt = seqt->next;
}
if (seqt == NULL) {
- BLI_addtail(&seqbase, seq);
+ BLI_addtail(&inputbase, seq);
}
}
}
- BLI_movelisttolist(&seqbase, &effbase);
- *(ed->seqbasep) = seqbase;
+ BLI_movelisttolist(seqbase, &inputbase);
+ BLI_movelisttolist(seqbase, &effbase);
}
typedef struct SeqUniqueInfo {
@@ -160,7 +164,7 @@ void SEQ_sequence_base_unique_name_recursive(ListBase *seqbasep, Sequence *seq)
while (sui.match) {
sui.match = 0;
seqbase_unique_name(seqbasep, &sui);
- SEQ_iterator_seqbase_recursive_apply(seqbasep, seqbase_unique_name_recursive_fn, &sui);
+ SEQ_seqbase_recursive_apply(seqbasep, seqbase_unique_name_recursive_fn, &sui);
}
BLI_strncpy(seq->name + 2, sui.name_dest, sizeof(seq->name) - 2);
@@ -403,7 +407,7 @@ const Sequence *SEQ_get_topmost_sequence(const Scene *scene, int frame)
}
for (seq = ed->seqbasep->first; seq; seq = seq->next) {
- if (seq->flag & SEQ_MUTE || seq->startdisp > frame || seq->enddisp <= frame) {
+ if (seq->flag & SEQ_MUTE || !SEQ_time_strip_intersects_frame(seq, frame)) {
continue;
}
/* Only use strips that generate an image, not ones that combine
@@ -584,3 +588,53 @@ void SEQ_set_scale_to_fit(const Sequence *seq,
break;
}
}
+
+int SEQ_seqbase_recursive_apply(ListBase *seqbase,
+ int (*apply_fn)(Sequence *seq, void *),
+ void *arg)
+{
+ Sequence *iseq;
+ for (iseq = seqbase->first; iseq; iseq = iseq->next) {
+ if (SEQ_recursive_apply(iseq, apply_fn, arg) == -1) {
+ return -1; /* bail out */
+ }
+ }
+ return 1;
+}
+
+int SEQ_recursive_apply(Sequence *seq, int (*apply_fn)(Sequence *, void *), void *arg)
+{
+ int ret = apply_fn(seq, arg);
+
+ if (ret == -1) {
+ return -1; /* bail out */
+ }
+
+ if (ret && seq->seqbase.first) {
+ ret = SEQ_seqbase_recursive_apply(&seq->seqbase, apply_fn, arg);
+ }
+
+ return ret;
+}
+
+/**
+ * Ensure, that provided Sequence has unique name. If animation data exists for this Sequence, it
+ * will be duplicated and mapped onto new name
+ *
+ * \param seq: Sequence which name will be ensured to be unique
+ * \param scene: Scene in which name must be unique
+ */
+void SEQ_ensure_unique_name(Sequence *seq, Scene *scene)
+{
+ char name[SEQ_NAME_MAXSTR];
+
+ BLI_strncpy_utf8(name, seq->name + 2, sizeof(name));
+ SEQ_sequence_base_unique_name_recursive(&scene->ed->seqbase, seq);
+ SEQ_dupe_animdata(scene, name, seq->name + 2);
+
+ if (seq->type == SEQ_TYPE_META) {
+ LISTBASE_FOREACH (Sequence *, seq_child, &seq->seqbase) {
+ SEQ_ensure_unique_name(seq_child, scene);
+ }
+ }
+}
diff --git a/source/blender/shader_fx/intern/FX_shader_glow.c b/source/blender/shader_fx/intern/FX_shader_glow.c
index 30eaa35a049..fc75771cd62 100644
--- a/source/blender/shader_fx/intern/FX_shader_glow.c
+++ b/source/blender/shader_fx/intern/FX_shader_glow.c
@@ -71,12 +71,11 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel)
uiItemR(layout, ptr, "mode", 0, NULL, ICON_NONE);
- if (mode == eShaderFxGlowMode_Luminance) {
- uiItemR(layout, ptr, "threshold", 0, NULL, ICON_NONE);
- }
- else {
+ uiItemR(layout, ptr, "threshold", 0, NULL, ICON_NONE);
+ if (mode == eShaderFxGlowMode_Color) {
uiItemR(layout, ptr, "select_color", 0, NULL, ICON_NONE);
}
+
uiItemR(layout, ptr, "glow_color", 0, NULL, ICON_NONE);
uiItemS(layout);
diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt
index 0f26ec50816..183b22c9791 100644
--- a/source/blender/windowmanager/CMakeLists.txt
+++ b/source/blender/windowmanager/CMakeLists.txt
@@ -203,6 +203,7 @@ if(WITH_XR_OPENXR)
list(APPEND SRC
xr/intern/wm_xr.c
+ xr/intern/wm_xr_actions.c
xr/intern/wm_xr_draw.c
xr/intern/wm_xr_session.c
diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h
index 1a505b91ac5..3525502a6dc 100644
--- a/source/blender/windowmanager/WM_api.h
+++ b/source/blender/windowmanager/WM_api.h
@@ -71,6 +71,11 @@ struct wmTabletData;
struct wmNDOFMotionData;
#endif
+#ifdef WITH_XR_OPENXR
+struct wmXrActionState;
+struct wmXrPose;
+#endif
+
typedef struct wmGizmo wmGizmo;
typedef struct wmGizmoMap wmGizmoMap;
typedef struct wmGizmoMapType wmGizmoMapType;
@@ -185,6 +190,7 @@ struct wmWindow *WM_window_open(struct bContext *C,
int sizex,
int sizey,
int space_type,
+ bool toplevel,
bool dialog,
bool temp,
WindowAlignment alignment);
@@ -200,6 +206,13 @@ void WM_autosave_init(struct wmWindowManager *wm);
bool WM_recover_last_session(struct bContext *C, struct ReportList *reports);
void WM_file_tag_modified(void);
+struct ID *WM_file_link_datablock(struct Main *bmain,
+ struct Scene *scene,
+ struct ViewLayer *view_layer,
+ struct View3D *v3d,
+ const char *filepath,
+ const short id_code,
+ const char *id_name);
struct ID *WM_file_append_datablock(struct Main *bmain,
struct Scene *scene,
struct ViewLayer *view_layer,
@@ -861,6 +874,7 @@ int WM_event_modifier_flag(const struct wmEvent *event);
bool WM_event_is_modal_tweak_exit(const struct wmEvent *event, int tweak_event);
bool WM_event_is_last_mousemove(const struct wmEvent *event);
+bool WM_event_is_mouse_drag(const struct wmEvent *event);
int WM_event_drag_threshold(const struct wmEvent *event);
bool WM_event_drag_test(const struct wmEvent *event, const int prev_xy[2]);
@@ -928,7 +942,7 @@ void WM_generic_user_data_free(struct wmGenericUserData *wm_userdata);
bool WM_region_use_viewport(struct ScrArea *area, struct ARegion *region);
#ifdef WITH_XR_OPENXR
-/* wm_xr.c */
+/* wm_xr_session.c */
bool WM_xr_session_exists(const wmXrData *xr);
bool WM_xr_session_is_ready(const wmXrData *xr);
struct wmXrSessionState *WM_xr_session_state_handle_get(const wmXrData *xr);
@@ -938,7 +952,74 @@ bool WM_xr_session_state_viewer_pose_rotation_get(const wmXrData *xr, float r_ro
bool WM_xr_session_state_viewer_pose_matrix_info_get(const wmXrData *xr,
float r_viewmat[4][4],
float *r_focal_len);
-#endif
+bool WM_xr_session_state_controller_pose_location_get(const wmXrData *xr,
+ unsigned int subaction_idx,
+ float r_location[3]);
+bool WM_xr_session_state_controller_pose_rotation_get(const wmXrData *xr,
+ unsigned int subaction_idx,
+ float r_rotation[4]);
+
+/* wm_xr_actions.c */
+/* XR action functions to be called pre-XR session start.
+ * Note: The "destroy" functions can also be called post-session start. */
+bool WM_xr_action_set_create(wmXrData *xr, const char *action_set_name);
+void WM_xr_action_set_destroy(wmXrData *xr, const char *action_set_name);
+bool WM_xr_action_create(wmXrData *xr,
+ const char *action_set_name,
+ const char *action_name,
+ eXrActionType type,
+ unsigned int count_subaction_paths,
+ const char **subaction_paths,
+ const float *float_threshold,
+ struct wmOperatorType *ot,
+ struct IDProperty *op_properties,
+ eXrOpFlag op_flag);
+void WM_xr_action_destroy(wmXrData *xr, const char *action_set_name, const char *action_name);
+bool WM_xr_action_space_create(wmXrData *xr,
+ const char *action_set_name,
+ const char *action_name,
+ unsigned int count_subaction_paths,
+ const char **subaction_paths,
+ const struct wmXrPose *poses);
+void WM_xr_action_space_destroy(wmXrData *xr,
+ const char *action_set_name,
+ const char *action_name,
+ unsigned int count_subaction_paths,
+ const char **subaction_paths);
+bool WM_xr_action_binding_create(wmXrData *xr,
+ const char *action_set_name,
+ const char *profile_path,
+ const char *action_name,
+ unsigned int count_interaction_paths,
+ const char **interaction_paths);
+void WM_xr_action_binding_destroy(wmXrData *xr,
+ const char *action_set_name,
+ const char *profile_path,
+ const char *action_name,
+ unsigned int count_interaction_paths,
+ const char **interaction_paths);
+
+bool WM_xr_active_action_set_set(
+ wmXrData *xr, const char *action_set_name); /* If action_set_name is NULL, then
+ * all action sets will be treated as active. */
+bool WM_xr_controller_pose_action_set(wmXrData *xr,
+ const char *action_set_name,
+ const char *action_name);
+
+/* XR action functions to be called post-XR session start. */
+bool WM_xr_action_state_get(const wmXrData *xr,
+ const char *action_set_name,
+ const char *action_name,
+ const char *subaction_path,
+ struct wmXrActionState *r_state);
+bool WM_xr_haptic_action_apply(wmXrData *xr,
+ const char *action_set_name,
+ const char *action_name,
+ const long long *duration,
+ const float *frequency,
+ const float *amplitude);
+void WM_xr_haptic_action_stop(wmXrData *xr, const char *action_set_name, const char *action_name);
+#endif /* WITH_XR_OPENXR */
#ifdef __cplusplus
}
diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h
index d54925272de..0b26f3c54ae 100644
--- a/source/blender/windowmanager/WM_types.h
+++ b/source/blender/windowmanager/WM_types.h
@@ -132,17 +132,21 @@ struct wmWindowManager;
extern "C" {
#endif
+typedef void (*wmGenericUserDataFreeFn)(void *data);
+
typedef struct wmGenericUserData {
void *data;
/** When NULL, use #MEM_freeN. */
- void (*free_fn)(void *data);
+ wmGenericUserDataFreeFn free_fn;
bool use_free;
} wmGenericUserData;
+typedef void (*wmGenericCallbackFn)(struct bContext *C, void *user_data);
+
typedef struct wmGenericCallback {
- void (*exec)(struct bContext *C, void *user_data);
+ wmGenericCallbackFn exec;
void *user_data;
- void (*free_user_data)(void *user_data);
+ wmGenericUserDataFreeFn free_user_data;
} wmGenericCallback;
/* ************** wmOperatorType ************************ */
@@ -680,6 +684,25 @@ typedef struct wmNDOFMotionData {
} wmNDOFMotionData;
#endif /* WITH_INPUT_NDOF */
+#ifdef WITH_XR_OPENXR
+/* Similar to GHOST_XrPose. */
+typedef struct wmXrPose {
+ float position[3];
+ /* Blender convention (w, x, y, z) */
+ float orientation_quat[4];
+} wmXrPose;
+
+typedef struct wmXrActionState {
+ union {
+ bool state_boolean;
+ float state_float;
+ float state_vector2f[2];
+ wmXrPose state_pose;
+ };
+ int type; /* eXrActionType */
+} wmXrActionState;
+#endif
+
/** Timer flags. */
typedef enum {
/** Do not attempt to free customdata pointer even if non-NULL. */
@@ -897,6 +920,7 @@ typedef struct wmDragAsset {
/* Always freed. */
const char *path;
int id_type;
+ int import_type; /* eFileAssetImportType */
} wmDragAsset;
typedef struct wmDrag {
diff --git a/source/blender/windowmanager/gizmo/WM_gizmo_api.h b/source/blender/windowmanager/gizmo/WM_gizmo_api.h
index cf1a7628267..c7a4b064d0e 100644
--- a/source/blender/windowmanager/gizmo/WM_gizmo_api.h
+++ b/source/blender/windowmanager/gizmo/WM_gizmo_api.h
@@ -146,6 +146,7 @@ void WM_gizmotype_append(void (*gtfunc)(struct wmGizmoType *));
void WM_gizmotype_append_ptr(void (*gtfunc)(struct wmGizmoType *, void *), void *userdata);
bool WM_gizmotype_remove(struct bContext *C, struct Main *bmain, const char *idname);
void WM_gizmotype_remove_ptr(struct bContext *C, struct Main *bmain, struct wmGizmoType *gzt);
+void WM_gizmotype_free_ptr(struct wmGizmoType *gzt);
void WM_gizmotype_iter(struct GHashIterator *ghi);
/* wm_gizmo_group_type.c */
@@ -154,8 +155,6 @@ struct wmGizmoGroupType *WM_gizmogrouptype_append(void (*wtfunc)(struct wmGizmoG
struct wmGizmoGroupType *WM_gizmogrouptype_append_ptr(void (*wtfunc)(struct wmGizmoGroupType *,
void *),
void *userdata);
-bool WM_gizmogrouptype_free(const char *idname);
-void WM_gizmogrouptype_free_ptr(struct wmGizmoGroupType *gzgt);
void WM_gizmogrouptype_iter(struct GHashIterator *ghi);
struct wmGizmoGroupTypeRef *WM_gizmogrouptype_append_and_link(
@@ -378,6 +377,9 @@ void WM_gizmo_group_unlink_delayed_ptr_from_space(struct wmGizmoGroupType *gzgt,
struct wmGizmoMapType *gzmap_type,
struct ScrArea *area);
+void WM_gizmo_group_type_free_ptr(wmGizmoGroupType *gzgt);
+bool WM_gizmo_group_type_free(const char *idname);
+
/* Has the result of unlinking and linking (re-initializes gizmo's). */
void WM_gizmo_group_type_reinit_ptr_ex(struct Main *bmain,
struct wmGizmoGroupType *gzgt,
diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c
index 32b6a6e6b31..062731dfb3d 100644
--- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c
+++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c
@@ -592,7 +592,7 @@ static int gizmo_tweak_invoke(bContext *C, wmOperator *op, const wmEvent *event)
const int highlight_part_init = gz->highlight_part;
if (gz->drag_part != -1) {
- if (ISTWEAK(event->type) || (event->val == KM_CLICK_DRAG)) {
+ if (WM_event_is_mouse_drag(event)) {
gz->highlight_part = gz->drag_part;
}
}
@@ -1058,12 +1058,14 @@ bool WM_gizmo_group_type_ensure(const char *idname)
return WM_gizmo_group_type_ensure_ptr(gzgt);
}
+/**
+ * Call #WM_gizmo_group_type_free_ptr after to remove & free.
+ */
void WM_gizmo_group_type_remove_ptr_ex(struct Main *bmain,
wmGizmoGroupType *gzgt,
wmGizmoMapType *gzmap_type)
{
WM_gizmomaptype_group_unlink(NULL, bmain, gzmap_type, gzgt);
- WM_gizmogrouptype_free_ptr(gzgt);
}
void WM_gizmo_group_type_remove_ptr(struct Main *bmain, wmGizmoGroupType *gzgt)
{
diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group_type.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group_type.c
index ab5a265547d..6ebeb5a76b6 100644
--- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group_type.c
+++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group_type.c
@@ -154,7 +154,7 @@ static void gizmogrouptype_free(wmGizmoGroupType *gzgt)
MEM_freeN(gzgt);
}
-void WM_gizmogrouptype_free_ptr(wmGizmoGroupType *gzgt)
+void WM_gizmo_group_type_free_ptr(wmGizmoGroupType *gzgt)
{
BLI_assert(gzgt == WM_gizmogrouptype_find(gzgt->idname, false));
@@ -165,7 +165,7 @@ void WM_gizmogrouptype_free_ptr(wmGizmoGroupType *gzgt)
/* XXX, TODO, update the world! */
}
-bool WM_gizmogrouptype_free(const char *idname)
+bool WM_gizmo_group_type_free(const char *idname)
{
wmGizmoGroupType *gzgt = BLI_ghash_lookup(global_gizmogrouptype_hash, idname);
@@ -173,7 +173,7 @@ bool WM_gizmogrouptype_free(const char *idname)
return false;
}
- WM_gizmogrouptype_free_ptr(gzgt);
+ WM_gizmo_group_type_free_ptr(gzgt);
return true;
}
diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c
index 45950a32d85..2ffa04bd8ae 100644
--- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c
+++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c
@@ -620,7 +620,7 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos,
struct GPUMatrixUnproject_Precalc unproj_precalc;
GPU_matrix_unproject_precalc(&unproj_precalc, rv3d->viewmat, rv3d->winmat, viewport);
- GPU_matrix_unproject_with_precalc(&unproj_precalc, co_screen, co_3d_origin);
+ GPU_matrix_unproject_3fv_with_precalc(&unproj_precalc, co_screen, co_3d_origin);
uint *buf_iter = buffer;
int hit_found = -1;
@@ -631,7 +631,7 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos,
wmGizmo *gz = visible_gizmos[buf_iter[3] >> 8];
float co_3d[3];
co_screen[2] = int_as_float(buf_iter[1]);
- GPU_matrix_unproject_with_precalc(&unproj_precalc, co_screen, co_3d);
+ GPU_matrix_unproject_3fv_with_precalc(&unproj_precalc, co_screen, co_3d);
float select_bias = gz->select_bias;
if ((gz->flag & WM_GIZMO_DRAW_NO_SCALE) == 0) {
select_bias *= gz->scale_final;
@@ -731,15 +731,6 @@ wmGizmo *wm_gizmomap_highlight_find(wmGizmoMap *gzmap,
BLI_buffer_declare_static(wmGizmo *, visible_3d_gizmos, BLI_BUFFER_NOP, 128);
bool do_step[WM_GIZMOMAP_DRAWSTEP_MAX];
- int mval[2] = {UNPACK2(event->mval)};
-
- /* Ensure for drag events we use the location where the user clicked.
- * Without this click-dragging on a gizmo can accidentally act on the wrong gizmo. */
- if (ISTWEAK(event->type) || (event->val == KM_CLICK_DRAG)) {
- mval[0] += event->x - event->prevclickx;
- mval[1] += event->y - event->prevclicky;
- }
-
for (int i = 0; i < ARRAY_SIZE(do_step); i++) {
do_step[i] = WM_gizmo_context_check_drawstep(C, i);
}
@@ -775,7 +766,7 @@ wmGizmo *wm_gizmomap_highlight_find(wmGizmoMap *gzmap,
}
else if (step == WM_GIZMOMAP_DRAWSTEP_2D) {
if ((gz = wm_gizmogroup_find_intersected_gizmo(
- wm, gzgroup, C, event_modifier, mval, r_part))) {
+ wm, gzgroup, C, event_modifier, event->mval, r_part))) {
break;
}
}
@@ -787,7 +778,7 @@ wmGizmo *wm_gizmomap_highlight_find(wmGizmoMap *gzmap,
/* 2D gizmos get priority. */
if (gz == NULL) {
gz = gizmo_find_intersected_3d(
- C, mval, visible_3d_gizmos.data, visible_3d_gizmos.count, r_part);
+ C, event->mval, visible_3d_gizmos.data, visible_3d_gizmos.count, r_part);
}
}
BLI_buffer_free(&visible_3d_gizmos);
diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_type.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_type.c
index 185854b1ca0..1523246d08b 100644
--- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_type.c
+++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_type.c
@@ -121,7 +121,7 @@ void WM_gizmotype_append_ptr(void (*gtfunc)(struct wmGizmoType *, void *), void
/**
* Free but don't remove from ghash.
*/
-static void gizmotype_free(wmGizmoType *gzt)
+void WM_gizmotype_free_ptr(wmGizmoType *gzt)
{
if (gzt->rna_ext.srna) { /* python gizmo, allocs own string */
MEM_freeN((void *)gzt->idname);
@@ -169,8 +169,6 @@ void WM_gizmotype_remove_ptr(bContext *C, Main *bmain, wmGizmoType *gzt)
BLI_ghash_remove(global_gizmotype_hash, gzt->idname, NULL, NULL);
gizmotype_unlink(C, bmain, gzt);
-
- gizmotype_free(gzt);
}
bool WM_gizmotype_remove(bContext *C, Main *bmain, const char *idname)
@@ -186,9 +184,9 @@ bool WM_gizmotype_remove(bContext *C, Main *bmain, const char *idname)
return true;
}
-static void wm_gizmotype_ghash_free_cb(wmGizmoType *mt)
+static void wm_gizmotype_ghash_free_cb(wmGizmoType *gzt)
{
- gizmotype_free(mt);
+ WM_gizmotype_free_ptr(gzt);
}
void wm_gizmotype_free(void)
diff --git a/source/blender/windowmanager/gizmo/wm_gizmo_fn.h b/source/blender/windowmanager/gizmo/wm_gizmo_fn.h
index 84e6308223f..ac4f6b761ac 100644
--- a/source/blender/windowmanager/gizmo/wm_gizmo_fn.h
+++ b/source/blender/windowmanager/gizmo/wm_gizmo_fn.h
@@ -62,9 +62,9 @@ typedef void (*wmGizmoFnMatrixBasisGet)(const struct wmGizmo *, float[4][4]);
typedef int (*wmGizmoFnInvoke)(struct bContext *, struct wmGizmo *, const struct wmEvent *);
typedef void (*wmGizmoFnExit)(struct bContext *, struct wmGizmo *, const bool);
typedef int (*wmGizmoFnCursorGet)(struct wmGizmo *);
-typedef void (*wmGizmoFnScreenBoundsGet)(struct bContext *,
+typedef bool (*wmGizmoFnScreenBoundsGet)(struct bContext *,
struct wmGizmo *,
- rcti *r_bounding_box);
+ rcti *r_bounding_box) ATTR_WARN_UNUSED_RESULT;
typedef void (*wmGizmoFnSelectRefresh)(struct wmGizmo *);
typedef void (*wmGizmoFnFree)(struct wmGizmo *);
diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c
index ae5b6c468f7..46e47ae95c4 100644
--- a/source/blender/windowmanager/intern/wm.c
+++ b/source/blender/windowmanager/intern/wm.c
@@ -186,6 +186,7 @@ static void window_manager_blend_read_data(BlendDataReader *reader, ID *id)
win->addmousemove = true;
win->event_queue_check_click = 0;
win->event_queue_check_drag = 0;
+ win->event_queue_check_drag_handled = 0;
BLO_read_data_address(reader, &win->stereo3d_format);
/* Multi-view always fallback to anaglyph at file opening
diff --git a/source/blender/windowmanager/intern/wm_cursors.c b/source/blender/windowmanager/intern/wm_cursors.c
index cdb7b591907..11783ae3517 100644
--- a/source/blender/windowmanager/intern/wm_cursors.c
+++ b/source/blender/windowmanager/intern/wm_cursors.c
@@ -264,7 +264,7 @@ void WM_cursor_grab_enable(wmWindow *win, int wrap, bool hide, int bounds[4])
if (wrap == WM_CURSOR_WRAP_X) {
mode_axis = GHOST_kAxisX;
}
- if (wrap == WM_CURSOR_WRAP_Y) {
+ else if (wrap == WM_CURSOR_WRAP_Y) {
mode_axis = GHOST_kGrabAxisY;
}
}
diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c
index 9684c21605a..fc62d0c6e06 100644
--- a/source/blender/windowmanager/intern/wm_dragdrop.c
+++ b/source/blender/windowmanager/intern/wm_dragdrop.c
@@ -26,6 +26,7 @@
#include <string.h>
#include "DNA_screen_types.h"
+#include "DNA_space_types.h"
#include "DNA_windowmanager_types.h"
#include "MEM_guardedalloc.h"
@@ -372,14 +373,22 @@ wmDragAsset *WM_drag_get_asset_data(const wmDrag *drag, int idcode)
}
wmDragAsset *asset_drag = drag->poin;
- return (idcode == 0 || asset_drag->id_type == idcode) ? asset_drag : NULL;
+ return (ELEM(idcode, 0, asset_drag->id_type)) ? asset_drag : NULL;
}
static ID *wm_drag_asset_id_import(wmDragAsset *asset_drag)
{
- /* Append only for now, wmDragAsset could have a `link` bool. */
- return WM_file_append_datablock(
- G_MAIN, NULL, NULL, NULL, asset_drag->path, asset_drag->id_type, asset_drag->name);
+ switch ((eFileAssetImportType)asset_drag->import_type) {
+ case FILE_ASSET_IMPORT_LINK:
+ return WM_file_link_datablock(
+ G_MAIN, NULL, NULL, NULL, asset_drag->path, asset_drag->id_type, asset_drag->name);
+ case FILE_ASSET_IMPORT_APPEND:
+ return WM_file_append_datablock(
+ G_MAIN, NULL, NULL, NULL, asset_drag->path, asset_drag->id_type, asset_drag->name);
+ }
+
+ BLI_assert_unreachable();
+ return NULL;
}
/**
diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c
index e0c4ab8eaf3..0922aaaee53 100644
--- a/source/blender/windowmanager/intern/wm_draw.c
+++ b/source/blender/windowmanager/intern/wm_draw.c
@@ -218,13 +218,6 @@ static bool wm_draw_region_stereo_set(Main *bmain,
return false;
}
-static void wm_area_mark_invalid_backbuf(ScrArea *area)
-{
- if (area->spacetype == SPACE_VIEW3D) {
- ((View3D *)area->spacedata.first)->flag |= V3D_INVALID_BACKBUF;
- }
-}
-
static void wm_region_test_gizmo_do_draw(bContext *C,
ScrArea *area,
ARegion *region,
@@ -739,7 +732,6 @@ static void wm_draw_window_offscreen(bContext *C, wmWindow *win, bool stereo)
}
}
- wm_area_mark_invalid_backbuf(area);
CTX_wm_area_set(C, NULL);
GPU_debug_group_end();
diff --git a/source/blender/windowmanager/intern/wm_event_query.c b/source/blender/windowmanager/intern/wm_event_query.c
index 9b9be6bb497..0050c834a56 100644
--- a/source/blender/windowmanager/intern/wm_event_query.c
+++ b/source/blender/windowmanager/intern/wm_event_query.c
@@ -265,6 +265,11 @@ bool WM_event_is_last_mousemove(const wmEvent *event)
return true;
}
+bool WM_event_is_mouse_drag(const wmEvent *event)
+{
+ return ISTWEAK(event->type) || (ISMOUSE_BUTTON(event->type) && (event->val == KM_CLICK_DRAG));
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -277,15 +282,17 @@ bool WM_event_is_last_mousemove(const wmEvent *event)
int WM_event_drag_threshold(const struct wmEvent *event)
{
int drag_threshold;
- if (WM_event_is_tablet(event)) {
- drag_threshold = U.drag_threshold_tablet;
- }
- else if (ISMOUSE(event->prevtype)) {
+ if (ISMOUSE(event->prevtype)) {
BLI_assert(event->prevtype != MOUSEMOVE);
/* Using the previous type is important is we want to check the last pressed/released button,
* The `event->type` would include #MOUSEMOVE which is always the case when dragging
* and does not help us know which threshold to use. */
- drag_threshold = U.drag_threshold_mouse;
+ if (WM_event_is_tablet(event)) {
+ drag_threshold = U.drag_threshold_tablet;
+ }
+ else {
+ drag_threshold = U.drag_threshold_mouse;
+ }
}
else {
/* Typically keyboard, could be NDOF button or other less common types. */
diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c
index 791aeeb39cd..a6588c40f0f 100644
--- a/source/blender/windowmanager/intern/wm_event_system.c
+++ b/source/blender/windowmanager/intern/wm_event_system.c
@@ -580,7 +580,7 @@ void wm_event_do_notifiers(bContext *C)
if ((note->category == NC_SPACE) && note->reference) {
/* Filter out notifiers sent to other spaces. RNA sets the reference to the owning ID
* though, the screen, so let notifiers through that reference the entire screen. */
- if ((note->reference != area->spacedata.first) && (note->reference != screen)) {
+ if (!ELEM(note->reference, area->spacedata.first, screen)) {
continue;
}
}
@@ -1048,7 +1048,7 @@ static int wm_operator_exec(bContext *C, wmOperator *op, const bool repeat, cons
wmWindowManager *wm = CTX_wm_manager(C);
int retval = OPERATOR_CANCELLED;
- CTX_wm_operator_poll_msg_set(C, NULL);
+ CTX_wm_operator_poll_msg_clear(C);
if (op == NULL || op->type == NULL) {
return retval;
@@ -1469,7 +1469,7 @@ static int wm_operator_call_internal(bContext *C,
{
int retval;
- CTX_wm_operator_poll_msg_set(C, NULL);
+ CTX_wm_operator_poll_msg_clear(C);
/* Dummy test. */
if (ot) {
@@ -1621,7 +1621,7 @@ int WM_operator_name_call_with_properties(struct bContext *C,
{
PointerRNA props_ptr;
wmOperatorType *ot = WM_operatortype_find(opstring, false);
- RNA_pointer_create(NULL, ot->srna, properties, &props_ptr);
+ RNA_pointer_create(G_MAIN->wm.first, ot->srna, properties, &props_ptr);
return WM_operator_name_call_ptr(C, ot, context, &props_ptr);
}
@@ -2595,6 +2595,15 @@ static int wm_handlers_do_gizmo_handler(bContext *C,
ListBase *handlers,
const bool do_debug_handler)
{
+ /* Drag events use the previous click location to highlight the gizmos,
+ * Get the highlight again in case the user dragged off the gizmo. */
+ const bool is_event_drag = ISTWEAK(event->type) || (event->val == KM_CLICK_DRAG);
+ const bool is_event_modifier = ISKEYMODIFIER(event->type);
+ /* Only keep the highlight if the gizmo becomes modal as result of event handling.
+ * Without this check, even un-handled drag events will set the highlight if the drag
+ * was initiated over a gizmo. */
+ const bool restore_highlight_unless_activated = is_event_drag;
+
int action = WM_HANDLER_CONTINUE;
ScrArea *area = CTX_wm_area(C);
ARegion *region = CTX_wm_region(C);
@@ -2608,13 +2617,25 @@ static int wm_handlers_do_gizmo_handler(bContext *C,
if (region->type->clip_gizmo_events_by_ui) {
if (UI_region_block_find_mouse_over(region, &event->x, true)) {
if (gz != NULL && event->type != EVT_GIZMO_UPDATE) {
- WM_tooltip_clear(C, CTX_wm_window(C));
- wm_gizmomap_highlight_set(gzmap, C, NULL, 0);
+ if (restore_highlight_unless_activated == false) {
+ WM_tooltip_clear(C, CTX_wm_window(C));
+ wm_gizmomap_highlight_set(gzmap, C, NULL, 0);
+ }
}
return action;
}
}
+ struct {
+ wmGizmo *gz_modal;
+ wmGizmo *gz;
+ int part;
+ } prev = {
+ .gz_modal = wm_gizmomap_modal_get(gzmap),
+ .gz = gz,
+ .part = gz ? gz->highlight_part : 0,
+ };
+
if (region->gizmo_map != handler->gizmo_map) {
WM_gizmomap_tag_refresh(handler->gizmo_map);
}
@@ -2622,16 +2643,11 @@ static int wm_handlers_do_gizmo_handler(bContext *C,
wm_gizmomap_handler_context_gizmo(C, handler);
wm_region_mouse_co(C, event);
- /* Drag events use the previous click location to highlight the gizmos,
- * Get the highlight again in case the user dragged off the gizmo. */
- const bool is_event_drag = ISTWEAK(event->type) || (event->val == KM_CLICK_DRAG);
- const bool is_event_modifier = ISKEYMODIFIER(event->type);
-
bool handle_highlight = false;
bool handle_keymap = false;
/* Handle gizmo highlighting. */
- if (!wm_gizmomap_modal_get(gzmap) &&
+ if ((prev.gz_modal == NULL) &&
((event->type == MOUSEMOVE) || is_event_modifier || is_event_drag)) {
handle_highlight = true;
if (is_event_modifier || is_event_drag) {
@@ -2642,14 +2658,15 @@ static int wm_handlers_do_gizmo_handler(bContext *C,
handle_keymap = true;
}
+ /* There is no need to handle this event when the key-map isn't being applied
+ * since any change to the highlight will be restored to the previous value. */
+ if (restore_highlight_unless_activated) {
+ if ((handle_highlight == true) && (handle_keymap == false)) {
+ return action;
+ }
+ }
+
if (handle_highlight) {
- struct {
- wmGizmo *gz;
- int part;
- } prev = {
- .gz = gz,
- .part = gz ? gz->highlight_part : 0,
- };
int part = -1;
gz = wm_gizmomap_highlight_find(gzmap, C, event, &part);
@@ -2734,6 +2751,16 @@ static int wm_handlers_do_gizmo_handler(bContext *C,
}
}
+ if (handle_highlight) {
+ if (restore_highlight_unless_activated) {
+ /* Check handling the key-map didn't activate a gizmo. */
+ wmGizmo *gz_modal = wm_gizmomap_modal_get(gzmap);
+ if (!(gz_modal && (gz_modal != prev.gz_modal))) {
+ wm_gizmomap_highlight_set(gzmap, C, prev.gz, prev.part);
+ }
+ }
+ }
+
if (is_event_handle_all) {
if (action == WM_HANDLER_CONTINUE) {
action |= WM_HANDLER_BREAK | WM_HANDLER_MODAL;
@@ -2941,6 +2968,8 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
if (wm_action_not_handled(action)) {
if (win->event_queue_check_drag) {
if (WM_event_drag_test(event, &event->prevclickx)) {
+ win->event_queue_check_drag_handled = true;
+
int x = event->x;
int y = event->y;
short val = event->val;
@@ -2984,6 +3013,7 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
if (event->is_repeat == false) {
win->event_queue_check_click = true;
win->event_queue_check_drag = true;
+ win->event_queue_check_drag_handled = false;
}
}
else if (event->val == KM_RELEASE) {
@@ -3470,6 +3500,13 @@ void wm_event_do_handlers(bContext *C)
win->event_queue_check_click = false;
}
+ /* If the drag even was handled, don't attempt to keep re-handing the same
+ * drag event on every cursor motion, see: T87511. */
+ if (win->event_queue_check_drag_handled) {
+ win->event_queue_check_drag = false;
+ win->event_queue_check_drag_handled = false;
+ }
+
/* Update previous mouse position for following events to use. */
win->eventstate->prevx = event->x;
win->eventstate->prevy = event->y;
@@ -4441,16 +4478,21 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void
event.prevtype = event.type;
event.prevval = event.val;
- /* Ensure the event state is correct, any deviation from this may cause bugs. */
+ /* Ensure the event state is correct, any deviation from this may cause bugs.
+ *
+ * NOTE: #EVENT_NONE is set when unknown keys are pressed,
+ * while not common, avoid a false alarm. */
#ifndef NDEBUG
if ((event_state->type || event_state->val) && /* Ignore cleared event state. */
- !(ISMOUSE_BUTTON(event_state->type) || ISKEYBOARD(event_state->type))) {
+ !(ISMOUSE_BUTTON(event_state->type) || ISKEYBOARD(event_state->type) ||
+ (event_state->type == EVENT_NONE))) {
CLOG_WARN(WM_LOG_HANDLERS,
"Non-keyboard/mouse button found in 'win->eventstate->type = %d'",
event_state->type);
}
if ((event_state->prevtype || event_state->prevval) && /* Ignore cleared event state. */
- !(ISMOUSE_BUTTON(event_state->prevtype) || ISKEYBOARD(event_state->prevtype))) {
+ !(ISMOUSE_BUTTON(event_state->prevtype) || ISKEYBOARD(event_state->prevtype) ||
+ (event_state->type == EVENT_NONE))) {
CLOG_WARN(WM_LOG_HANDLERS,
"Non-keyboard/mouse button found in 'win->eventstate->prevtype = %d'",
event_state->prevtype);
diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c
index bbcb0669cce..0aff2c00644 100644
--- a/source/blender/windowmanager/intern/wm_files.c
+++ b/source/blender/windowmanager/intern/wm_files.c
@@ -911,7 +911,10 @@ void wm_homefile_read(bContext *C,
const char *app_template_override,
bool *r_is_factory_startup)
{
- Main *bmain = G_MAIN; /* Context does not always have valid main pointer here... */
+#if 0 /* UNUSED, keep as this may be needed later & the comment below isn't self evident. */
+ /* Context does not always have valid main pointer here. */
+ Main *bmain = G_MAIN;
+#endif
ListBase wmbase;
bool success = false;
@@ -1170,7 +1173,7 @@ void wm_homefile_read(bContext *C,
BLI_strncpy(U.app_template, app_template_override, sizeof(U.app_template));
}
- bmain = CTX_data_main(C);
+ Main *bmain = CTX_data_main(C);
if (use_userdef) {
/* check userdef before open window, keymaps etc */
@@ -2157,21 +2160,9 @@ static void wm_homefile_read_after_dialog_callback(bContext *C, void *user_data)
C, "WM_OT_read_homefile", WM_OP_EXEC_DEFAULT, (IDProperty *)user_data);
}
-static void wm_free_operator_properties_callback(void *user_data)
-{
- IDProperty *properties = (IDProperty *)user_data;
- IDP_FreeProperty(properties);
-}
-
static int wm_homefile_read_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
- if (U.uiflag & USER_SAVE_PROMPT &&
- wm_file_or_image_is_modified(CTX_data_main(C), CTX_wm_manager(C))) {
- wmGenericCallback *callback = MEM_callocN(sizeof(*callback), __func__);
- callback->exec = wm_homefile_read_after_dialog_callback;
- callback->user_data = IDP_CopyProperty(op->properties);
- callback->free_user_data = wm_free_operator_properties_callback;
- wm_close_file_dialog(C, callback);
+ if (wm_operator_close_file_dialog_if_needed(C, op, wm_homefile_read_after_dialog_callback)) {
return OPERATOR_INTERFACE;
}
return wm_homefile_read_exec(C, op);
@@ -2331,13 +2322,7 @@ static int wm_open_mainfile__discard_changes(bContext *C, wmOperator *op)
set_next_operator_state(op, OPEN_MAINFILE_STATE_OPEN);
}
- if (U.uiflag & USER_SAVE_PROMPT &&
- wm_file_or_image_is_modified(CTX_data_main(C), CTX_wm_manager(C))) {
- wmGenericCallback *callback = MEM_callocN(sizeof(*callback), __func__);
- callback->exec = wm_open_mainfile_after_dialog_callback;
- callback->user_data = IDP_CopyProperty(op->properties);
- callback->free_user_data = wm_free_operator_properties_callback;
- wm_close_file_dialog(C, callback);
+ if (wm_operator_close_file_dialog_if_needed(C, op, wm_open_mainfile_after_dialog_callback)) {
return OPERATOR_INTERFACE;
}
return wm_open_mainfile_dispatch(C, op);
@@ -2500,12 +2485,11 @@ static void wm_open_mainfile_ui(bContext *UNUSED(C), wmOperator *op)
{
struct FileRuntime *file_info = (struct FileRuntime *)&op->customdata;
uiLayout *layout = op->layout;
- uiLayout *col = op->layout;
const char *autoexec_text;
uiItemR(layout, op->ptr, "load_ui", 0, NULL, ICON_NONE);
- col = uiLayoutColumn(layout, false);
+ uiLayout *col = uiLayoutColumn(layout, false);
if (file_info->is_untrusted) {
autoexec_text = IFACE_("Trusted Source [Untrusted Path]");
uiLayoutSetActive(col, false);
@@ -2637,12 +2621,25 @@ static int wm_recover_last_session_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
-static int wm_recover_last_session_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+static void wm_recover_last_session_after_dialog_callback(bContext *C, void *user_data)
+{
+ WM_operator_name_call_with_properties(
+ C, "WM_OT_recover_last_session", WM_OP_EXEC_DEFAULT, (IDProperty *)user_data);
+}
+
+static int wm_recover_last_session_invoke(bContext *C,
+ wmOperator *op,
+ const wmEvent *UNUSED(event))
{
/* Keep the current setting instead of using the preferences since a file selector
* doesn't give us the option to change the setting. */
wm_open_init_use_scripts(op, false);
- return WM_operator_confirm(C, op, event);
+
+ if (wm_operator_close_file_dialog_if_needed(
+ C, op, wm_recover_last_session_after_dialog_callback)) {
+ return OPERATOR_INTERFACE;
+ }
+ return wm_recover_last_session_exec(C, op);
}
void WM_OT_recover_last_session(wmOperatorType *ot)
@@ -3270,7 +3267,10 @@ static void wm_block_file_close_save(bContext *C, void *arg_block, void *arg_dat
bool file_has_been_saved_before = BKE_main_blendfile_path(bmain)[0] != '\0';
if (file_has_been_saved_before) {
- WM_operator_name_call(C, "WM_OT_save_mainfile", WM_OP_EXEC_DEFAULT, NULL);
+ if (WM_operator_name_call(C, "WM_OT_save_mainfile", WM_OP_EXEC_DEFAULT, NULL) &
+ OPERATOR_CANCELLED) {
+ execute_callback = false;
+ }
}
else {
WM_operator_name_call(C, "WM_OT_save_mainfile", WM_OP_INVOKE_DEFAULT, NULL);
@@ -3456,4 +3456,31 @@ void wm_close_file_dialog(bContext *C, wmGenericCallback *post_action)
}
}
+static void wm_free_operator_properties_callback(void *user_data)
+{
+ IDProperty *properties = (IDProperty *)user_data;
+ IDP_FreeProperty(properties);
+}
+
+/**
+ * \return True if the dialog was created, the calling operator should return #OPERATOR_INTERFACE
+ * then.
+ */
+bool wm_operator_close_file_dialog_if_needed(bContext *C,
+ wmOperator *op,
+ wmGenericCallbackFn post_action_fn)
+{
+ if (U.uiflag & USER_SAVE_PROMPT &&
+ wm_file_or_image_is_modified(CTX_data_main(C), CTX_wm_manager(C))) {
+ wmGenericCallback *callback = MEM_callocN(sizeof(*callback), __func__);
+ callback->exec = post_action_fn;
+ callback->user_data = IDP_CopyProperty(op->properties);
+ callback->free_user_data = wm_free_operator_properties_callback;
+ wm_close_file_dialog(C, callback);
+ return true;
+ }
+
+ return false;
+}
+
/** \} */
diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c
index 840debad01b..e467dcd243e 100644
--- a/source/blender/windowmanager/intern/wm_files_link.c
+++ b/source/blender/windowmanager/intern/wm_files_link.c
@@ -642,23 +642,23 @@ void WM_OT_append(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Append Single Data-Block & Return it
+/** \name Link/Append Single Data-Block & Return it
*
- * Used for appending workspace from startup files.
* \{ */
-ID *WM_file_append_datablock(Main *bmain,
- Scene *scene,
- ViewLayer *view_layer,
- View3D *v3d,
- const char *filepath,
- const short id_code,
- const char *id_name)
+static ID *wm_file_link_datablock_ex(Main *bmain,
+ Scene *scene,
+ ViewLayer *view_layer,
+ View3D *v3d,
+ const char *filepath,
+ const short id_code,
+ const char *id_name,
+ bool clear_pre_existing_flag)
{
/* Tag everything so we can make local only the new datablock. */
BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, true);
- /* Define working data, with just the one item we want to append. */
+ /* Define working data, with just the one item we want to link. */
WMLinkAppendData *lapp_data = wm_link_append_data_new(0);
wm_link_append_data_library_add(lapp_data, filepath);
@@ -672,6 +672,36 @@ ID *WM_file_append_datablock(Main *bmain,
ID *id = item->new_id;
wm_link_append_data_free(lapp_data);
+ if (clear_pre_existing_flag) {
+ BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false);
+ }
+
+ return id;
+}
+
+ID *WM_file_link_datablock(Main *bmain,
+ Scene *scene,
+ ViewLayer *view_layer,
+ View3D *v3d,
+ const char *filepath,
+ const short id_code,
+ const char *id_name)
+{
+ return wm_file_link_datablock_ex(
+ bmain, scene, view_layer, v3d, filepath, id_code, id_name, true);
+}
+
+ID *WM_file_append_datablock(Main *bmain,
+ Scene *scene,
+ ViewLayer *view_layer,
+ View3D *v3d,
+ const char *filepath,
+ const short id_code,
+ const char *id_name)
+{
+ ID *id = wm_file_link_datablock_ex(
+ bmain, scene, view_layer, v3d, filepath, id_code, id_name, false);
+
/* Make datablock local. */
BKE_library_make_local(bmain, NULL, NULL, true, false);
diff --git a/source/blender/windowmanager/intern/wm_gesture.c b/source/blender/windowmanager/intern/wm_gesture.c
index 4c1b403ac96..ae2cdb5608c 100644
--- a/source/blender/windowmanager/intern/wm_gesture.c
+++ b/source/blender/windowmanager/intern/wm_gesture.c
@@ -385,7 +385,7 @@ static void draw_filled_lasso(wmGesture *gt)
mcoords[i][1] = lasso[1];
}
- BLI_lasso_boundbox(&rect, (const int(*)[2])mcoords, mcoords_len);
+ BLI_lasso_boundbox(&rect, mcoords, mcoords_len);
BLI_rcti_translate(&rect, gt->winrct.xmin, gt->winrct.ymin);
BLI_rcti_isect(&gt->winrct, &rect, &rect);
@@ -402,7 +402,7 @@ static void draw_filled_lasso(wmGesture *gt)
rect.ymin,
rect.xmax,
rect.ymax,
- (const int(*)[2])mcoords,
+ mcoords,
mcoords_len,
draw_filled_lasso_px_cb,
&lasso_fill_data);
diff --git a/source/blender/windowmanager/intern/wm_gesture_ops.c b/source/blender/windowmanager/intern/wm_gesture_ops.c
index 07d68293714..94535427dac 100644
--- a/source/blender/windowmanager/intern/wm_gesture_ops.c
+++ b/source/blender/windowmanager/intern/wm_gesture_ops.c
@@ -316,6 +316,7 @@ int WM_gesture_circle_invoke(bContext *C, wmOperator *op, const wmEvent *event)
if (gesture->wait_for_input == false) {
gesture->is_active = true;
gesture_circle_apply(C, op);
+ gesture->is_active_prev = true;
}
/* add modal handler */
diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c
index 56fd51ac6fd..15f0978f87d 100644
--- a/source/blender/windowmanager/intern/wm_init_exit.c
+++ b/source/blender/windowmanager/intern/wm_init_exit.c
@@ -343,8 +343,13 @@ void WM_init(bContext *C, int argc, const char **argv)
(void)argv; /* unused */
#endif
- if (!G.background && !wm_start_with_console) {
- GHOST_toggleConsole(3);
+ if (!G.background) {
+ if (wm_start_with_console) {
+ GHOST_toggleConsole(1);
+ }
+ else {
+ GHOST_toggleConsole(3);
+ }
}
BKE_material_copybuf_clear();
@@ -496,7 +501,7 @@ void WM_exit_ex(bContext *C, const bool do_python)
if ((has_edited &&
BLO_write_file(
bmain, filename, fileflags, &(const struct BlendFileWriteParams){0}, NULL)) ||
- (undo_memfile && BLO_memfile_write_file(undo_memfile, filename))) {
+ (BLO_memfile_write_file(undo_memfile, filename))) {
printf("Saved session recovery to '%s'\n", filename);
}
}
diff --git a/source/blender/windowmanager/intern/wm_keymap.c b/source/blender/windowmanager/intern/wm_keymap.c
index 38d06ea83d3..0a157e63b09 100644
--- a/source/blender/windowmanager/intern/wm_keymap.c
+++ b/source/blender/windowmanager/intern/wm_keymap.c
@@ -80,6 +80,9 @@ static wmKeyMapItem *wm_keymap_item_copy(wmKeyMapItem *kmi)
kmin->ptr = MEM_callocN(sizeof(PointerRNA), "UserKeyMapItemPtr");
WM_operator_properties_create(kmin->ptr, kmin->idname);
+ /* Signal for no context, see #STRUCT_NO_CONTEXT_WITHOUT_OWNER_ID. */
+ kmin->ptr->owner_id = NULL;
+
kmin->properties = IDP_CopyProperty(kmin->properties);
kmin->ptr->data = kmin->properties;
}
@@ -106,6 +109,9 @@ static void wm_keymap_item_properties_set(wmKeyMapItem *kmi)
{
WM_operator_properties_alloc(&(kmi->ptr), &(kmi->properties), kmi->idname);
WM_operator_properties_sanitize(kmi->ptr, 1);
+
+ /* Signal for no context, see #STRUCT_NO_CONTEXT_WITHOUT_OWNER_ID. */
+ kmi->ptr->owner_id = NULL;
}
/**
@@ -136,6 +142,9 @@ static void wm_keymap_item_properties_update_ot(wmKeyMapItem *kmi)
kmi->ptr->data = kmi->properties;
}
WM_operator_properties_sanitize(kmi->ptr, 1);
+
+ /* Signal for no context, see #STRUCT_NO_CONTEXT_WITHOUT_OWNER_ID. */
+ kmi->ptr->owner_id = NULL;
}
}
else {
diff --git a/source/blender/windowmanager/intern/wm_operator_type.c b/source/blender/windowmanager/intern/wm_operator_type.c
index 0e57a92b685..f33db7d50b5 100644
--- a/source/blender/windowmanager/intern/wm_operator_type.c
+++ b/source/blender/windowmanager/intern/wm_operator_type.c
@@ -252,7 +252,7 @@ void WM_operatortype_props_advanced_end(wmOperatorType *ot)
return;
}
- RNA_pointer_create(NULL, ot->srna, NULL, &struct_ptr);
+ WM_operator_properties_create_ptr(&struct_ptr, ot);
RNA_STRUCT_BEGIN (&struct_ptr, prop) {
counter++;
diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c
index 84c16999c1b..9175eb2dbf7 100644
--- a/source/blender/windowmanager/intern/wm_operators.c
+++ b/source/blender/windowmanager/intern/wm_operators.c
@@ -583,7 +583,8 @@ char *WM_prop_pystring_assign(bContext *C, PointerRNA *ptr, PropertyRNA *prop, i
void WM_operator_properties_create_ptr(PointerRNA *ptr, wmOperatorType *ot)
{
- RNA_pointer_create(NULL, ot->srna, NULL, ptr);
+ /* Set the ID so the context can be accessed: see #STRUCT_NO_CONTEXT_WITHOUT_OWNER_ID. */
+ RNA_pointer_create(G_MAIN->wm.first, ot->srna, NULL, ptr);
}
void WM_operator_properties_create(PointerRNA *ptr, const char *opstring)
@@ -594,7 +595,8 @@ void WM_operator_properties_create(PointerRNA *ptr, const char *opstring)
WM_operator_properties_create_ptr(ptr, ot);
}
else {
- RNA_pointer_create(NULL, &RNA_OperatorProperties, NULL, ptr);
+ /* Set the ID so the context can be accessed: see #STRUCT_NO_CONTEXT_WITHOUT_OWNER_ID. */
+ RNA_pointer_create(G_MAIN->wm.first, &RNA_OperatorProperties, NULL, ptr);
}
}
@@ -1205,7 +1207,7 @@ IDProperty *WM_operator_last_properties_ensure_idprops(wmOperatorType *ot)
void WM_operator_last_properties_ensure(wmOperatorType *ot, PointerRNA *ptr)
{
IDProperty *props = WM_operator_last_properties_ensure_idprops(ot);
- RNA_pointer_create(NULL, ot->srna, props, ptr);
+ RNA_pointer_create(G_MAIN->wm.first, ot->srna, props, ptr);
}
/**
@@ -1909,6 +1911,9 @@ static bool wm_operator_winactive_normal(bContext *C)
if (!((screen = WM_window_get_active_screen(win)) && (screen->state == SCREENNORMAL))) {
return 0;
}
+ if (G.background) {
+ return 0;
+ }
return 1;
}
diff --git a/source/blender/windowmanager/intern/wm_playanim.c b/source/blender/windowmanager/intern/wm_playanim.c
index 58030920fc7..5300649a0cd 100644
--- a/source/blender/windowmanager/intern/wm_playanim.c
+++ b/source/blender/windowmanager/intern/wm_playanim.c
@@ -47,9 +47,11 @@
#include "BLI_fileops.h"
#include "BLI_listbase.h"
#include "BLI_path_util.h"
+#include "BLI_rect.h"
#include "BLI_string.h"
#include "BLI_utildefines.h"
+#include "IMB_colormanagement.h"
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
@@ -95,64 +97,85 @@ static AUD_Device *audio_device = NULL;
struct PlayState;
static void playanim_window_zoom(struct PlayState *ps, const float zoom_offset);
+/**
+ * The current state of the player.
+ *
+ * \warning Don't store results of parsing command-line arguments
+ * in this struct if they need to persist across playing back different
+ * files as these will be cleared when playing other files (drag & drop).
+ */
typedef struct PlayState {
- /* window and viewport size */
+ /** Window and viewport size. */
int win_x, win_y;
- /* current zoom level */
+ /** Current zoom level. */
float zoom;
- /* playback state */
+ /** Playback direction (-1, 1). */
short direction;
+ /** Set the next frame to implement frame stepping (using shortcuts). */
short next_frame;
+ /** Playback once then wait. */
bool once;
- bool turbo;
+ /** Play forwards/backwards. */
bool pingpong;
+ /** Disable frame skipping. */
bool noskip;
+ /** Display current frame over the window. */
bool indicator;
+ /** Single-frame stepping has been enabled (frame loading and update pending). */
bool sstep;
+ /** Playback has stopped the image has been displayed. */
bool wait2;
+ /** Playback stopped state once stop/start variables have been handled. */
bool stopped;
+ /**
+ * When disabled the current animation will exit,
+ * after this either the application exits or a new animation window is opened.
+ *
+ * This is used so drag & drop can load new files which setup a newly created animation window.
+ */
bool go;
- /* waiting for images to load */
+ /** True when waiting for images to load. */
bool loading;
- /* x/y image flip */
+ /** X/Y image flip (set via key bindings). */
bool draw_flip[2];
+ /** The number of frames to step each update (default to 1, command line argument). */
int fstep;
- /* current picture */
+ /** Current frame (picture). */
struct PlayAnimPict *picture;
- /* set once at the start */
+ /** Image size in pixels, set once at the start. */
int ibufx, ibufy;
+ /** Mono-space font ID. */
int fontid;
- /* saves passing args */
- struct ImBuf *curframe_ibuf;
-
- /* restarts player for file drop */
+ /** Restarts player for file drop (drag & drop). */
char dropped_file[FILE_MAX];
+ /** Force update when scrubbing with the cursor. */
bool need_frame_update;
+ /** The current frame calculated by scrubbing the mouse cursor. */
int frame_cursor_x;
+
+ ColorManagedViewSettings view_settings;
+ ColorManagedDisplaySettings display_settings;
} PlayState;
/* for debugging */
#if 0
-void print_ps(PlayState *ps)
+static void print_ps(PlayState *ps)
{
printf("ps:\n");
printf(" direction=%d,\n", (int)ps->direction);
- printf(" next=%d,\n", ps->next);
printf(" once=%d,\n", ps->once);
- printf(" turbo=%d,\n", ps->turbo);
printf(" pingpong=%d,\n", ps->pingpong);
printf(" noskip=%d,\n", ps->noskip);
printf(" sstep=%d,\n", ps->sstep);
- printf(" pause=%d,\n", ps->pause);
printf(" wait2=%d,\n", ps->wait2);
printf(" stopped=%d,\n", ps->stopped);
printf(" go=%d,\n\n", ps->go);
@@ -237,6 +260,12 @@ typedef struct PlayAnimPict {
struct anim *anim;
int frame;
int IB_flags;
+
+#ifdef USE_FRAME_CACHE_LIMIT
+ /** Back pointer to the #LinkData node for this struct in the #g_frame_cache.pics list. */
+ LinkData *frame_cache_node;
+ size_t size_in_memory;
+#endif
} PlayAnimPict;
static struct ListBase picsbase = {NULL, NULL};
@@ -248,9 +277,103 @@ static double fps_movie;
#endif
#ifdef USE_FRAME_CACHE_LIMIT
-static struct ListBase inmempicsbase = {NULL, NULL};
-static int added_images = 0;
-#endif
+static struct {
+ /** A list of #LinkData nodes referencing #PlayAnimPict to track cached frames. */
+ struct ListBase pics;
+ /** Number if elements in `pics`. */
+ int pics_len;
+ /** Keep track of memory used by #g_frame_cache.pics when `g_frame_cache.memory_limit != 0`. */
+ size_t pics_size_in_memory;
+ /** Optionally limit the amount of memory used for cache (in bytes), ignored when zero. */
+ size_t memory_limit;
+} g_frame_cache = {
+ .pics = {NULL, NULL},
+ .pics_len = 0,
+ .pics_size_in_memory = 0,
+ .memory_limit = 0,
+};
+
+static void frame_cache_add(PlayAnimPict *pic)
+{
+ pic->frame_cache_node = BLI_genericNodeN(pic);
+ BLI_addhead(&g_frame_cache.pics, pic->frame_cache_node);
+ g_frame_cache.pics_len++;
+
+ if (g_frame_cache.memory_limit != 0) {
+ BLI_assert(pic->size_in_memory == 0);
+ pic->size_in_memory = IMB_get_size_in_memory(pic->ibuf);
+ g_frame_cache.pics_size_in_memory += pic->size_in_memory;
+ }
+}
+
+static void frame_cache_remove(PlayAnimPict *pic)
+{
+ LinkData *node = pic->frame_cache_node;
+ IMB_freeImBuf(pic->ibuf);
+ if (g_frame_cache.memory_limit != 0) {
+ BLI_assert(pic->size_in_memory != 0);
+ g_frame_cache.pics_size_in_memory -= pic->size_in_memory;
+ pic->size_in_memory = 0;
+ }
+ pic->ibuf = NULL;
+ pic->frame_cache_node = NULL;
+ BLI_freelinkN(&g_frame_cache.pics, node);
+ g_frame_cache.pics_len--;
+}
+
+/* Don't free the current frame by moving it to the head of the list. */
+static void frame_cache_touch(PlayAnimPict *pic)
+{
+ BLI_assert(pic->frame_cache_node->data == pic);
+ BLI_remlink(&g_frame_cache.pics, pic->frame_cache_node);
+ BLI_addhead(&g_frame_cache.pics, pic->frame_cache_node);
+}
+
+static bool frame_cache_limit_exceeded(void)
+{
+ return g_frame_cache.memory_limit ?
+ (g_frame_cache.pics_size_in_memory > g_frame_cache.memory_limit) :
+ (g_frame_cache.pics_len > PLAY_FRAME_CACHE_MAX);
+}
+
+static void frame_cache_limit_apply(ImBuf *ibuf_keep)
+{
+ /* Really basic memory conservation scheme. Keep frames in a FIFO queue. */
+ LinkData *node = g_frame_cache.pics.last;
+ while (node && frame_cache_limit_exceeded()) {
+ PlayAnimPict *pic = node->data;
+ BLI_assert(pic->frame_cache_node == node);
+
+ node = node->prev;
+ if (pic->ibuf && pic->ibuf != ibuf_keep) {
+ frame_cache_remove(pic);
+ }
+ }
+}
+
+#endif /* USE_FRAME_CACHE_LIMIT */
+
+static ImBuf *ibuf_from_picture(PlayAnimPict *pic)
+{
+ ImBuf *ibuf = NULL;
+
+ if (pic->ibuf) {
+ ibuf = pic->ibuf;
+ }
+ else if (pic->anim) {
+ ibuf = IMB_anim_absolute(pic->anim, pic->frame, IMB_TC_NONE, IMB_PROXY_NONE);
+ }
+ else if (pic->mem) {
+ /* use correct colorspace here */
+ ibuf = IMB_ibImageFromMemory(pic->mem, pic->size, pic->IB_flags, NULL, pic->name);
+ }
+ else {
+ /* use correct colorspace here */
+ ibuf = IMB_loadiffname(pic->name, pic->IB_flags, NULL);
+ }
+
+ return ibuf;
+}
static PlayAnimPict *playanim_step(PlayAnimPict *playanim, int step)
{
@@ -278,6 +401,151 @@ static int pupdate_time(void)
return (ptottime < 0);
}
+static void *ocio_transform_ibuf(PlayState *ps,
+ ImBuf *ibuf,
+ bool *r_glsl_used,
+ eGPUTextureFormat *r_format,
+ eGPUDataFormat *r_data,
+ void **r_buffer_cache_handle)
+{
+ void *display_buffer;
+ bool force_fallback = false;
+ *r_glsl_used = false;
+ force_fallback |= (ED_draw_imbuf_method(ibuf) != IMAGE_DRAW_METHOD_GLSL);
+ force_fallback |= (ibuf->dither != 0.0f);
+
+ /* Default */
+ *r_format = GPU_RGBA8;
+ *r_data = GPU_DATA_UBYTE;
+
+ /* Fallback to CPU based color space conversion. */
+ if (force_fallback) {
+ *r_glsl_used = false;
+ display_buffer = NULL;
+ }
+ else if (ibuf->rect_float) {
+ display_buffer = ibuf->rect_float;
+
+ *r_data = GPU_DATA_FLOAT;
+ if (ibuf->channels == 4) {
+ *r_format = GPU_RGBA16F;
+ }
+ else if (ibuf->channels == 3) {
+ /* Alpha is implicitly 1. */
+ *r_format = GPU_RGB16F;
+ }
+
+ if (ibuf->float_colorspace) {
+ *r_glsl_used = IMB_colormanagement_setup_glsl_draw_from_space(&ps->view_settings,
+ &ps->display_settings,
+ ibuf->float_colorspace,
+ ibuf->dither,
+ false,
+ false);
+ }
+ else {
+ *r_glsl_used = IMB_colormanagement_setup_glsl_draw(
+ &ps->view_settings, &ps->display_settings, ibuf->dither, false);
+ }
+ }
+ else if (ibuf->rect) {
+ display_buffer = ibuf->rect;
+ *r_glsl_used = IMB_colormanagement_setup_glsl_draw_from_space(&ps->view_settings,
+ &ps->display_settings,
+ ibuf->rect_colorspace,
+ ibuf->dither,
+ false,
+ false);
+ }
+ else {
+ display_buffer = NULL;
+ }
+
+ /* There is data to be displayed, but GLSL is not initialized
+ * properly, in this case we fallback to CPU-based display transform. */
+ if ((ibuf->rect || ibuf->rect_float) && !*r_glsl_used) {
+ display_buffer = IMB_display_buffer_acquire(
+ ibuf, &ps->view_settings, &ps->display_settings, r_buffer_cache_handle);
+ *r_format = GPU_RGBA8;
+ *r_data = GPU_DATA_UBYTE;
+ }
+
+ return display_buffer;
+}
+
+static void draw_display_buffer(PlayState *ps, ImBuf *ibuf)
+{
+ void *display_buffer;
+
+ /* Format needs to be created prior to any #immBindShader call.
+ * Do it here because OCIO binds its own shader. */
+ eGPUTextureFormat format;
+ eGPUDataFormat data;
+ bool glsl_used = false;
+ GPUVertFormat *imm_format = immVertexFormat();
+ uint pos = GPU_vertformat_attr_add(imm_format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ uint texCoord = GPU_vertformat_attr_add(
+ imm_format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+
+ void *buffer_cache_handle = NULL;
+ display_buffer = ocio_transform_ibuf(ps, ibuf, &glsl_used, &format, &data, &buffer_cache_handle);
+
+ GPUTexture *texture = GPU_texture_create_2d("display_buf", ibuf->x, ibuf->y, 1, format, NULL);
+ GPU_texture_update(texture, data, display_buffer);
+ GPU_texture_filter_mode(texture, false);
+
+ GPU_texture_bind(texture, 0);
+
+ if (!glsl_used) {
+ immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_COLOR);
+ immUniformColor3f(1.0f, 1.0f, 1.0f);
+ immUniform1i("image", 0);
+ }
+
+ immBegin(GPU_PRIM_TRI_FAN, 4);
+
+ rctf preview;
+ rctf canvas;
+
+ BLI_rctf_init(&canvas, 0.0f, 1.0f, 0.0f, 1.0f);
+ BLI_rctf_init(&preview, 0.0f, 1.0f, 0.0f, 1.0f);
+
+ if (ps->draw_flip[0]) {
+ SWAP(float, canvas.xmin, canvas.xmax);
+ }
+ if (ps->draw_flip[1]) {
+ SWAP(float, canvas.ymin, canvas.ymax);
+ }
+
+ immAttr2f(texCoord, canvas.xmin, canvas.ymin);
+ immVertex2f(pos, preview.xmin, preview.ymin);
+
+ immAttr2f(texCoord, canvas.xmin, canvas.ymax);
+ immVertex2f(pos, preview.xmin, preview.ymax);
+
+ immAttr2f(texCoord, canvas.xmax, canvas.ymax);
+ immVertex2f(pos, preview.xmax, preview.ymax);
+
+ immAttr2f(texCoord, canvas.xmax, canvas.ymin);
+ immVertex2f(pos, preview.xmax, preview.ymin);
+
+ immEnd();
+
+ GPU_texture_unbind(texture);
+ GPU_texture_free(texture);
+
+ if (!glsl_used) {
+ immUnbindProgram();
+ }
+ else {
+ IMB_colormanagement_finish_glsl_draw();
+ }
+
+ if (buffer_cache_handle) {
+ IMB_display_buffer_release(buffer_cache_handle);
+ }
+}
+
static void playanim_toscreen(
PlayState *ps, PlayAnimPict *picture, struct ImBuf *ibuf, int fontid, int fstep)
{
@@ -285,13 +553,6 @@ static void playanim_toscreen(
printf("%s: no ibuf for picture '%s'\n", __func__, picture ? picture->name : "<NIL>");
return;
}
- if (ibuf->rect == NULL && ibuf->rect_float) {
- IMB_rect_from_float(ibuf);
- imb_freerectfloatImBuf(ibuf);
- }
- if (ibuf->rect == NULL) {
- return;
- }
GHOST_ActivateWindowDrawingContext(g_WS.ghost_window);
@@ -321,19 +582,7 @@ static void playanim_toscreen(
8);
}
- IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR);
-
- immDrawPixelsTex(&state,
- offs_x + (ps->draw_flip[0] ? span_x : 0.0f),
- offs_y + (ps->draw_flip[1] ? span_y : 0.0f),
- ibuf->x,
- ibuf->y,
- GPU_RGBA8,
- false,
- ibuf->rect,
- ((ps->draw_flip[0] ? -1.0f : 1.0f)) * (ps->zoom / (float)ps->win_x),
- ((ps->draw_flip[1] ? -1.0f : 1.0f)) * (ps->zoom / (float)ps->win_y),
- NULL);
+ draw_display_buffer(ps, ibuf);
GPU_blend(GPU_BLEND_NONE);
@@ -413,6 +662,14 @@ static void build_pict_list_ex(
}
}
else {
+ /* Load images into cache until the cache is full,
+ * this resolves choppiness for images that are slow to load, see: T81751. */
+#ifdef USE_FRAME_CACHE_LIMIT
+ bool fill_cache = true;
+#else
+ bool fill_cache = false;
+#endif
+
int count = 0;
int fp_framenr;
@@ -441,7 +698,7 @@ static void build_pict_list_ex(
*/
while (IMB_ispic(filepath) && totframes) {
- bool hasevent;
+ bool has_event;
size_t size;
int file;
@@ -499,22 +756,33 @@ static void build_pict_list_ex(
pupdate_time();
- if (ptottime > 1.0) {
+ const bool display_imbuf = ptottime > 1.0;
+
+ if (display_imbuf || fill_cache) {
/* OCIO_TODO: support different input color space */
- struct ImBuf *ibuf;
- if (picture->mem) {
- ibuf = IMB_ibImageFromMemory(
- picture->mem, picture->size, picture->IB_flags, NULL, picture->name);
- }
- else {
- ibuf = IMB_loadiffname(picture->name, picture->IB_flags, NULL);
- }
+ ImBuf *ibuf = ibuf_from_picture(picture);
+
if (ibuf) {
- playanim_toscreen(ps, picture, ibuf, fontid, fstep);
- IMB_freeImBuf(ibuf);
+ if (display_imbuf) {
+ playanim_toscreen(ps, picture, ibuf, fontid, fstep);
+ }
+#ifdef USE_FRAME_CACHE_LIMIT
+ if (fill_cache) {
+ picture->ibuf = ibuf;
+ frame_cache_add(picture);
+ fill_cache = !frame_cache_limit_exceeded();
+ }
+ else
+#endif
+ {
+ IMB_freeImBuf(ibuf);
+ }
+ }
+
+ if (display_imbuf) {
+ pupdate_time();
+ ptottime = 0.0;
}
- pupdate_time();
- ptottime = 0.0;
}
/* create a new filepath each time */
@@ -522,7 +790,7 @@ static void build_pict_list_ex(
BLI_path_sequence_encode(
filepath, fp_decoded.head, fp_decoded.tail, fp_decoded.digits, fp_framenr);
- while ((hasevent = GHOST_ProcessEvents(g_WS.ghost_system, 0))) {
+ while ((has_event = GHOST_ProcessEvents(g_WS.ghost_system, false))) {
GHOST_DispatchEvents(g_WS.ghost_system);
if (ps->loading == false) {
return;
@@ -622,16 +890,14 @@ static void change_frame(PlayState *ps)
static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr ps_void)
{
PlayState *ps = (PlayState *)ps_void;
- GHOST_TEventType type = GHOST_GetEventType(evt);
- int val;
+ const GHOST_TEventType type = GHOST_GetEventType(evt);
+ /* Convert ghost event into value keyboard or mouse. */
+ const int val = ELEM(type, GHOST_kEventKeyDown, GHOST_kEventButtonDown);
// print_ps(ps);
playanim_event_qual_update();
- /* convert ghost event into value keyboard or mouse */
- val = ELEM(type, GHOST_kEventKeyDown, GHOST_kEventButtonDown);
-
/* first check if we're busy loading files */
if (ps->loading) {
switch (type) {
@@ -655,8 +921,8 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr ps_void)
return 1;
}
- if (ps->wait2 && ps->stopped) {
- ps->stopped = false;
+ if (ps->wait2 && ps->stopped == false) {
+ ps->stopped = true;
}
if (ps->wait2) {
@@ -815,9 +1081,9 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr ps_void)
case GHOST_kKeyNumpadSlash:
if (val) {
if (g_WS.qual & WS_QUAL_SHIFT) {
- if (ps->curframe_ibuf) {
+ if (ps->picture && ps->picture->ibuf) {
printf(" Name: %s | Speed: %.2f frames/s\n",
- ps->curframe_ibuf->name,
+ ps->picture->ibuf->name,
ps->fstep / swaptime);
}
}
@@ -1054,7 +1320,8 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr ps_void)
playanim_gl_matrix();
ptottime = 0.0;
- playanim_toscreen(ps, ps->picture, ps->curframe_ibuf, ps->fontid, ps->fstep);
+ playanim_toscreen(
+ ps, ps->picture, ps->picture ? ps->picture->ibuf : NULL, ps->fontid, ps->fstep);
break;
}
@@ -1131,7 +1398,9 @@ static void playanim_window_zoom(PlayState *ps, const float zoom_offset)
GHOST_SetClientSize(g_WS.ghost_window, sizex, sizey);
}
-/* return path for restart */
+/**
+ * \return The a path used to restart the animation player or NULL to exit.
+ */
static char *wm_main_playanim_intern(int argc, const char **argv)
{
struct ImBuf *ibuf = NULL;
@@ -1150,7 +1419,6 @@ static char *wm_main_playanim_intern(int argc, const char **argv)
ps.direction = true;
ps.next_frame = 1;
ps.once = false;
- ps.turbo = false;
ps.pingpong = false;
ps.noskip = false;
ps.sstep = false;
@@ -1168,6 +1436,11 @@ static char *wm_main_playanim_intern(int argc, const char **argv)
ps.fontid = -1;
+ STRNCPY(ps.display_settings.display_device,
+ IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_DEFAULT_BYTE));
+ IMB_colormanagement_init_default_view_settings(&ps.view_settings, &ps.display_settings);
+
+ /* Skip the first argument which is assumed to be '-a' (used to launch this player). */
while (argc > 1) {
if (argv[1][0] == '-') {
switch (argv[1][1]) {
@@ -1222,6 +1495,15 @@ static char *wm_main_playanim_intern(int argc, const char **argv)
argc--;
argv++;
break;
+ case 'c': {
+#ifdef USE_FRAME_CACHE_LIMIT
+ const int memory_in_mb = max_ii(0, atoi(argv[2]));
+ g_frame_cache.memory_limit = (size_t)memory_in_mb * (1024 * 1024);
+#endif
+ argc--;
+ argv++;
+ break;
+ }
default:
printf("unknown option '%c': skipping\n", argv[1][1]);
break;
@@ -1389,60 +1671,29 @@ static char *wm_main_playanim_intern(int argc, const char **argv)
#endif
while (ps.picture) {
- int hasevent;
+ bool has_event;
#ifndef USE_IMB_CACHE
if (ibuf != NULL && ibuf->ftype == IMB_FTYPE_NONE) {
IMB_freeImBuf(ibuf);
}
#endif
- if (ps.picture->ibuf) {
- ibuf = ps.picture->ibuf;
- }
- else if (ps.picture->anim) {
- ibuf = IMB_anim_absolute(ps.picture->anim, ps.picture->frame, IMB_TC_NONE, IMB_PROXY_NONE);
- }
- else if (ps.picture->mem) {
- /* use correct colorspace here */
- ibuf = IMB_ibImageFromMemory(
- ps.picture->mem, ps.picture->size, ps.picture->IB_flags, NULL, ps.picture->name);
- }
- else {
- /* use correct colorspace here */
- ibuf = IMB_loadiffname(ps.picture->name, ps.picture->IB_flags, NULL);
- }
- if (ibuf) {
-#ifdef USE_FRAME_CACHE_LIMIT
- LinkData *node;
-#endif
+ ibuf = ibuf_from_picture(ps.picture);
+ if (ibuf) {
#ifdef USE_IMB_CACHE
ps.picture->ibuf = ibuf;
#endif
#ifdef USE_FRAME_CACHE_LIMIT
- /* Really basic memory conservation scheme. Keep frames in a FIFO queue. */
- node = inmempicsbase.last;
-
- while (node && added_images > PLAY_FRAME_CACHE_MAX) {
- PlayAnimPict *pic = node->data;
-
- if (pic->ibuf && pic->ibuf != ibuf) {
- LinkData *node_tmp;
- IMB_freeImBuf(pic->ibuf);
- pic->ibuf = NULL;
- node_tmp = node->prev;
- BLI_freelinkN(&inmempicsbase, node);
- added_images--;
- node = node_tmp;
- }
- else {
- node = node->prev;
- }
+ if (ps.picture->frame_cache_node == NULL) {
+ frame_cache_add(ps.picture);
+ }
+ else {
+ frame_cache_touch(ps.picture);
}
+ frame_cache_limit_apply(ibuf);
- BLI_addhead(&inmempicsbase, BLI_genericNodeN(ps.picture));
- added_images++;
#endif /* USE_FRAME_CACHE_LIMIT */
BLI_strncpy(ibuf->name, ps.picture->name, sizeof(ibuf->name));
@@ -1474,14 +1725,14 @@ static char *wm_main_playanim_intern(int argc, const char **argv)
ps.next_frame = ps.direction;
- while ((hasevent = GHOST_ProcessEvents(g_WS.ghost_system, 0))) {
+ while ((has_event = GHOST_ProcessEvents(g_WS.ghost_system, false))) {
GHOST_DispatchEvents(g_WS.ghost_system);
}
if (ps.go == false) {
break;
}
change_frame(&ps);
- if (!hasevent) {
+ if (!has_event) {
PIL_sleep_ms(1);
}
if (ps.wait2) {
@@ -1490,14 +1741,15 @@ static char *wm_main_playanim_intern(int argc, const char **argv)
ps.wait2 = ps.sstep;
- if (ps.wait2 == false && ps.stopped == false) {
- ps.stopped = true;
+ if (ps.wait2 == false && ps.stopped) {
+ ps.stopped = false;
}
pupdate_time();
if (ps.picture && ps.next_frame) {
- /* always at least set one step */
+ /* Advance to the next frame, always at least set one step.
+ * Implement frame-skipping when enabled and playback is not fast enough. */
while (ps.picture) {
ps.picture = playanim_step(ps.picture, ps.next_frame);
@@ -1510,7 +1762,7 @@ static char *wm_main_playanim_intern(int argc, const char **argv)
}
}
- if (ps.wait2 || ptottime < swaptime || ps.turbo || ps.noskip) {
+ if (ps.wait2 || ptottime < swaptime || ps.noskip) {
break;
}
ptottime -= swaptime;
@@ -1550,8 +1802,12 @@ static char *wm_main_playanim_intern(int argc, const char **argv)
#endif
BLI_freelistN(&picsbase);
- BLI_freelistN(&inmempicsbase);
- added_images = 0;
+
+#ifdef USE_FRAME_CACHE_LIMIT
+ BLI_freelistN(&g_frame_cache.pics);
+ g_frame_cache.pics_len = 0;
+ g_frame_cache.pics_size_in_memory = 0;
+#endif
#ifdef WITH_AUDASPACE
if (playback_handle) {
diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c
index eb1da15807c..6aedfb10dde 100644
--- a/source/blender/windowmanager/intern/wm_window.c
+++ b/source/blender/windowmanager/intern/wm_window.c
@@ -133,7 +133,7 @@ static struct WMInitStruct {
* \{ */
static void wm_window_set_drawable(wmWindowManager *wm, wmWindow *win, bool activate);
-static int wm_window_timer(const bContext *C);
+static bool wm_window_timer(const bContext *C);
/* XXX this one should correctly check for apple top header...
* done for Cocoa : returns window contents (and not frame) max size*/
@@ -550,7 +550,7 @@ static void wm_window_ghostwindow_add(wmWindowManager *wm,
}
int scr_w, scr_h;
- wm_get_screensize(&scr_w, &scr_h);
+ wm_get_desktopsize(&scr_w, &scr_h);
int posy = (scr_h - win->posy - win->sizey);
/* Clear drawable so we can set the new window. */
@@ -756,6 +756,7 @@ static bool wm_window_update_size_position(wmWindow *win)
/**
* \param space_type: SPACE_VIEW3D, SPACE_INFO, ... (eSpace_Type)
+ * \param toplevel: Not a child owned by other windows. A peer of main window.
* \param dialog: whether this should be made as a dialog-style window
* \param temp: whether this is considered a short-lived window
* \param alignment: how this window is positioned relative to its parent
@@ -768,6 +769,7 @@ wmWindow *WM_window_open(bContext *C,
int sizex,
int sizey,
int space_type,
+ bool toplevel,
bool dialog,
bool temp,
WindowAlignment alignment)
@@ -812,7 +814,7 @@ wmWindow *WM_window_open(bContext *C,
LISTBASE_FOREACH (wmWindow *, win_iter, &wm->windows) {
if (WM_window_is_temp_screen(win_iter)) {
char *wintitle = GHOST_GetTitle(win_iter->ghostwin);
- if (strcmp(title, wintitle) == 0) {
+ if (STREQ(title, wintitle)) {
win = win_iter;
}
free(wintitle);
@@ -822,7 +824,7 @@ wmWindow *WM_window_open(bContext *C,
/* add new window? */
if (win == NULL) {
- win = wm_window_new(bmain, wm, win_prev, dialog);
+ win = wm_window_new(bmain, wm, toplevel ? NULL : win_prev, dialog);
win->posx = rect.xmin;
win->posy = rect.ymin;
*win->stereo3d_format = *win_prev->stereo3d_format;
@@ -925,6 +927,7 @@ int wm_window_new_exec(bContext *C, wmOperator *UNUSED(op))
area->spacetype,
false,
false,
+ false,
WIN_ALIGN_PARENT_CENTER) != NULL);
return ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
@@ -1498,12 +1501,12 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr
* Timer handlers should check for delta to decide if they just update, or follow real time.
* Timer handlers can also set duration to match frames passed
*/
-static int wm_window_timer(const bContext *C)
+static bool wm_window_timer(const bContext *C)
{
Main *bmain = CTX_data_main(C);
wmWindowManager *wm = CTX_wm_manager(C);
double time = PIL_check_seconds_timer();
- int retval = 0;
+ bool has_event = false;
/* Mutable in case the timer gets removed. */
LISTBASE_FOREACH_MUTABLE (wmTimer *, wt, &wm->timers) {
@@ -1540,31 +1543,34 @@ static int wm_window_timer(const bContext *C)
event.customdata = wt;
wm_event_add(win, &event);
- retval = 1;
+ has_event = true;
}
}
}
- return retval;
+ return has_event;
}
void wm_window_process_events(const bContext *C)
{
BLI_assert(BLI_thread_is_main());
- int hasevent = GHOST_ProcessEvents(g_system, 0); /* 0 is no wait */
+ bool has_event = GHOST_ProcessEvents(g_system, false); /* `false` is no wait. */
- if (hasevent) {
+ if (has_event) {
GHOST_DispatchEvents(g_system);
}
- hasevent |= wm_window_timer(C);
+ has_event |= wm_window_timer(C);
#ifdef WITH_XR_OPENXR
/* XR events don't use the regular window queues. So here we don't only trigger
* processing/dispatching but also handling. */
- hasevent |= wm_xr_events_handle(CTX_wm_manager(C));
+ has_event |= wm_xr_events_handle(CTX_wm_manager(C));
#endif
- /* no event, we sleep 5 milliseconds */
- if (hasevent == 0) {
+ /* When there is no event, sleep 5 milliseconds not to use too much CPU when idle.
+ *
+ * Skip sleeping when simulating events so tests don't idle unnecessarily as simulated
+ * events are typically generated from a timer that runs in the main loop. */
+ if ((has_event == false) && !(G.f & G_FLAG_EVENT_SIMULATE)) {
PIL_sleep_ms(5);
}
}
diff --git a/source/blender/windowmanager/wm.h b/source/blender/windowmanager/wm.h
index 7fddf60eb97..af696ddd8f9 100644
--- a/source/blender/windowmanager/wm.h
+++ b/source/blender/windowmanager/wm.h
@@ -23,7 +23,6 @@
#pragma once
-struct ReportList;
struct wmWindow;
#include "gizmo/wm_gizmo_wmapi.h"
diff --git a/source/blender/windowmanager/wm_files.h b/source/blender/windowmanager/wm_files.h
index d54090a6025..c7fe07cad7f 100644
--- a/source/blender/windowmanager/wm_files.h
+++ b/source/blender/windowmanager/wm_files.h
@@ -45,6 +45,9 @@ void wm_homefile_read(struct bContext *C,
void wm_file_read_report(bContext *C, struct Main *bmain);
void wm_close_file_dialog(bContext *C, struct wmGenericCallback *post_action);
+bool wm_operator_close_file_dialog_if_needed(bContext *C,
+ wmOperator *op,
+ wmGenericCallbackFn exec_fn);
bool wm_file_or_image_is_modified(const Main *bmain, const wmWindowManager *wm);
void WM_OT_save_homefile(struct wmOperatorType *ot);
diff --git a/source/blender/windowmanager/xr/intern/wm_xr.c b/source/blender/windowmanager/xr/intern/wm_xr.c
index 439d611b085..2a67c2bee9f 100644
--- a/source/blender/windowmanager/xr/intern/wm_xr.c
+++ b/source/blender/windowmanager/xr/intern/wm_xr.c
@@ -128,6 +128,11 @@ bool wm_xr_events_handle(wmWindowManager *wm)
if (wm->xr.runtime && wm->xr.runtime->context) {
GHOST_XrEventsHandle(wm->xr.runtime->context);
+ /* Process OpenXR action events. */
+ if (WM_xr_session_is_ready(&wm->xr)) {
+ wm_xr_session_actions_update(&wm->xr);
+ }
+
/* wm_window_process_events() uses the return value to determine if it can put the main thread
* to sleep for some milliseconds. We never want that to happen while the VR session runs on
* the main thread. So always return true. */
diff --git a/source/blender/windowmanager/xr/intern/wm_xr_actions.c b/source/blender/windowmanager/xr/intern/wm_xr_actions.c
new file mode 100644
index 00000000000..51ed3dcfd3c
--- /dev/null
+++ b/source/blender/windowmanager/xr/intern/wm_xr_actions.c
@@ -0,0 +1,480 @@
+/*
+ * 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.
+ */
+
+/** \file
+ * \ingroup wm
+ *
+ * \name Window-Manager XR Actions
+ *
+ * Uses the Ghost-XR API to manage OpenXR actions.
+ * All functions are designed to be usable by RNA / the Python API.
+ */
+
+#include "BLI_math.h"
+
+#include "GHOST_C-api.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "wm_xr_intern.h"
+
+/* -------------------------------------------------------------------- */
+/** \name XR-Action API
+ *
+ * API functions for managing OpenXR actions.
+ *
+ * \{ */
+
+static wmXrActionSet *action_set_create(const char *action_set_name)
+{
+ wmXrActionSet *action_set = MEM_callocN(sizeof(*action_set), __func__);
+ action_set->name = MEM_mallocN(strlen(action_set_name) + 1, "XrActionSet_Name");
+ strcpy(action_set->name, action_set_name);
+
+ return action_set;
+}
+
+static void action_set_destroy(void *val)
+{
+ wmXrActionSet *action_set = val;
+
+ MEM_SAFE_FREE(action_set->name);
+
+ MEM_freeN(action_set);
+}
+
+static wmXrActionSet *action_set_find(wmXrData *xr, const char *action_set_name)
+{
+ return GHOST_XrGetActionSetCustomdata(xr->runtime->context, action_set_name);
+}
+
+static wmXrAction *action_create(const char *action_name,
+ eXrActionType type,
+ unsigned int count_subaction_paths,
+ const char **subaction_paths,
+ const float *float_threshold,
+ wmOperatorType *ot,
+ IDProperty *op_properties,
+ eXrOpFlag op_flag)
+{
+ wmXrAction *action = MEM_callocN(sizeof(*action), __func__);
+ action->name = MEM_mallocN(strlen(action_name) + 1, "XrAction_Name");
+ strcpy(action->name, action_name);
+ action->type = type;
+
+ const unsigned int count = count_subaction_paths;
+ action->count_subaction_paths = count;
+
+ action->subaction_paths = MEM_mallocN(sizeof(*action->subaction_paths) * count,
+ "XrAction_SubactionPaths");
+ for (unsigned int i = 0; i < count; ++i) {
+ action->subaction_paths[i] = MEM_mallocN(strlen(subaction_paths[i]) + 1,
+ "XrAction_SubactionPath");
+ strcpy(action->subaction_paths[i], subaction_paths[i]);
+ }
+
+ size_t size;
+ switch (type) {
+ case XR_BOOLEAN_INPUT:
+ size = sizeof(bool);
+ break;
+ case XR_FLOAT_INPUT:
+ size = sizeof(float);
+ break;
+ case XR_VECTOR2F_INPUT:
+ size = sizeof(float) * 2;
+ break;
+ case XR_POSE_INPUT:
+ size = sizeof(GHOST_XrPose);
+ break;
+ case XR_VIBRATION_OUTPUT:
+ return action;
+ }
+ action->states = MEM_calloc_arrayN(count, size, "XrAction_States");
+ action->states_prev = MEM_calloc_arrayN(count, size, "XrAction_StatesPrev");
+
+ if (float_threshold) {
+ BLI_assert(type == XR_FLOAT_INPUT || type == XR_VECTOR2F_INPUT);
+ action->float_threshold = *float_threshold;
+ CLAMP(action->float_threshold, 0.0f, 1.0f);
+ }
+
+ action->ot = ot;
+ action->op_properties = op_properties;
+ action->op_flag = op_flag;
+
+ return action;
+}
+
+static void action_destroy(void *val)
+{
+ wmXrAction *action = val;
+
+ MEM_SAFE_FREE(action->name);
+
+ const unsigned int count = action->count_subaction_paths;
+ char **subaction_paths = action->subaction_paths;
+ if (subaction_paths) {
+ for (unsigned int i = 0; i < count; ++i) {
+ MEM_SAFE_FREE(subaction_paths[i]);
+ }
+ MEM_freeN(subaction_paths);
+ }
+
+ MEM_SAFE_FREE(action->states);
+ MEM_SAFE_FREE(action->states_prev);
+
+ MEM_freeN(action);
+}
+
+static wmXrAction *action_find(wmXrData *xr, const char *action_set_name, const char *action_name)
+{
+ return GHOST_XrGetActionCustomdata(xr->runtime->context, action_set_name, action_name);
+}
+
+bool WM_xr_action_set_create(wmXrData *xr, const char *action_set_name)
+{
+ if (action_set_find(xr, action_set_name)) {
+ return false;
+ }
+
+ wmXrActionSet *action_set = action_set_create(action_set_name);
+
+ GHOST_XrActionSetInfo info = {
+ .name = action_set_name,
+ .customdata_free_fn = action_set_destroy,
+ .customdata = action_set,
+ };
+
+ if (!GHOST_XrCreateActionSet(xr->runtime->context, &info)) {
+ return false;
+ }
+
+ return true;
+}
+
+void WM_xr_action_set_destroy(wmXrData *xr, const char *action_set_name)
+{
+ wmXrActionSet *action_set = action_set_find(xr, action_set_name);
+ if (!action_set) {
+ return;
+ }
+
+ wmXrSessionState *session_state = &xr->runtime->session_state;
+
+ if (action_set == session_state->active_action_set) {
+ if (action_set->controller_pose_action) {
+ wm_xr_session_controller_data_clear(session_state);
+ action_set->controller_pose_action = NULL;
+ }
+ if (action_set->active_modal_action) {
+ action_set->active_modal_action = NULL;
+ }
+ session_state->active_action_set = NULL;
+ }
+
+ GHOST_XrDestroyActionSet(xr->runtime->context, action_set_name);
+}
+
+bool WM_xr_action_create(wmXrData *xr,
+ const char *action_set_name,
+ const char *action_name,
+ eXrActionType type,
+ unsigned int count_subaction_paths,
+ const char **subaction_paths,
+ const float *float_threshold,
+ wmOperatorType *ot,
+ IDProperty *op_properties,
+ eXrOpFlag op_flag)
+{
+ if (action_find(xr, action_set_name, action_name)) {
+ return false;
+ }
+
+ wmXrAction *action = action_create(action_name,
+ type,
+ count_subaction_paths,
+ subaction_paths,
+ float_threshold,
+ ot,
+ op_properties,
+ op_flag);
+
+ GHOST_XrActionInfo info = {
+ .name = action_name,
+ .count_subaction_paths = count_subaction_paths,
+ .subaction_paths = subaction_paths,
+ .states = action->states,
+ .customdata_free_fn = action_destroy,
+ .customdata = action,
+ };
+
+ switch (type) {
+ case XR_BOOLEAN_INPUT:
+ info.type = GHOST_kXrActionTypeBooleanInput;
+ break;
+ case XR_FLOAT_INPUT:
+ info.type = GHOST_kXrActionTypeFloatInput;
+ break;
+ case XR_VECTOR2F_INPUT:
+ info.type = GHOST_kXrActionTypeVector2fInput;
+ break;
+ case XR_POSE_INPUT:
+ info.type = GHOST_kXrActionTypePoseInput;
+ break;
+ case XR_VIBRATION_OUTPUT:
+ info.type = GHOST_kXrActionTypeVibrationOutput;
+ break;
+ }
+
+ if (!GHOST_XrCreateActions(xr->runtime->context, action_set_name, 1, &info)) {
+ return false;
+ }
+
+ return true;
+}
+
+void WM_xr_action_destroy(wmXrData *xr, const char *action_set_name, const char *action_name)
+{
+ wmXrActionSet *action_set = action_set_find(xr, action_set_name);
+ if (!action_set) {
+ return;
+ }
+
+ if (action_set->controller_pose_action &&
+ STREQ(action_set->controller_pose_action->name, action_name)) {
+ if (action_set == xr->runtime->session_state.active_action_set) {
+ wm_xr_session_controller_data_clear(&xr->runtime->session_state);
+ }
+ action_set->controller_pose_action = NULL;
+ }
+ if (action_set->active_modal_action &&
+ STREQ(action_set->active_modal_action->name, action_name)) {
+ action_set->active_modal_action = NULL;
+ }
+
+ wmXrAction *action = action_find(xr, action_set_name, action_name);
+ if (!action) {
+ return;
+ }
+}
+
+bool WM_xr_action_space_create(wmXrData *xr,
+ const char *action_set_name,
+ const char *action_name,
+ unsigned int count_subaction_paths,
+ const char **subaction_paths,
+ const wmXrPose *poses)
+{
+ GHOST_XrActionSpaceInfo info = {
+ .action_name = action_name,
+ .count_subaction_paths = count_subaction_paths,
+ .subaction_paths = subaction_paths,
+ };
+
+ GHOST_XrPose *ghost_poses = MEM_malloc_arrayN(
+ count_subaction_paths, sizeof(*ghost_poses), __func__);
+ for (unsigned int i = 0; i < count_subaction_paths; ++i) {
+ const wmXrPose *pose = &poses[i];
+ GHOST_XrPose *ghost_pose = &ghost_poses[i];
+ copy_v3_v3(ghost_pose->position, pose->position);
+ copy_qt_qt(ghost_pose->orientation_quat, pose->orientation_quat);
+ }
+ info.poses = ghost_poses;
+
+ bool ret = GHOST_XrCreateActionSpaces(xr->runtime->context, action_set_name, 1, &info) ? true :
+ false;
+ MEM_freeN(ghost_poses);
+ return ret;
+}
+
+void WM_xr_action_space_destroy(wmXrData *xr,
+ const char *action_set_name,
+ const char *action_name,
+ unsigned int count_subaction_paths,
+ const char **subaction_paths)
+{
+ GHOST_XrActionSpaceInfo info = {
+ .action_name = action_name,
+ .count_subaction_paths = count_subaction_paths,
+ .subaction_paths = subaction_paths,
+ };
+
+ GHOST_XrDestroyActionSpaces(xr->runtime->context, action_set_name, 1, &info);
+}
+
+bool WM_xr_action_binding_create(wmXrData *xr,
+ const char *action_set_name,
+ const char *profile_path,
+ const char *action_name,
+ unsigned int count_interaction_paths,
+ const char **interaction_paths)
+{
+ GHOST_XrActionBindingInfo binding_info = {
+ .action_name = action_name,
+ .count_interaction_paths = count_interaction_paths,
+ .interaction_paths = interaction_paths,
+ };
+
+ GHOST_XrActionProfileInfo profile_info = {
+ .profile_path = profile_path,
+ .count_bindings = 1,
+ .bindings = &binding_info,
+ };
+
+ return GHOST_XrCreateActionBindings(xr->runtime->context, action_set_name, 1, &profile_info);
+}
+
+void WM_xr_action_binding_destroy(wmXrData *xr,
+ const char *action_set_name,
+ const char *profile_path,
+ const char *action_name,
+ unsigned int count_interaction_paths,
+ const char **interaction_paths)
+{
+ GHOST_XrActionBindingInfo binding_info = {
+ .action_name = action_name,
+ .count_interaction_paths = count_interaction_paths,
+ .interaction_paths = interaction_paths,
+ };
+
+ GHOST_XrActionProfileInfo profile_info = {
+ .profile_path = profile_path,
+ .count_bindings = 1,
+ .bindings = &binding_info,
+ };
+
+ GHOST_XrDestroyActionBindings(xr->runtime->context, action_set_name, 1, &profile_info);
+}
+
+bool WM_xr_active_action_set_set(wmXrData *xr, const char *action_set_name)
+{
+ wmXrActionSet *action_set = action_set_find(xr, action_set_name);
+ if (!action_set) {
+ return false;
+ }
+
+ {
+ /* Unset active modal action (if any). */
+ wmXrActionSet *active_action_set = xr->runtime->session_state.active_action_set;
+ if (active_action_set) {
+ wmXrAction *active_modal_action = active_action_set->active_modal_action;
+ if (active_modal_action) {
+ if (active_modal_action->active_modal_path) {
+ active_modal_action->active_modal_path = NULL;
+ }
+ active_action_set->active_modal_action = NULL;
+ }
+ }
+ }
+
+ xr->runtime->session_state.active_action_set = action_set;
+
+ if (action_set->controller_pose_action) {
+ wm_xr_session_controller_data_populate(action_set->controller_pose_action, xr);
+ }
+
+ return true;
+}
+
+bool WM_xr_controller_pose_action_set(wmXrData *xr,
+ const char *action_set_name,
+ const char *action_name)
+{
+ wmXrActionSet *action_set = action_set_find(xr, action_set_name);
+ if (!action_set) {
+ return false;
+ }
+
+ wmXrAction *action = action_find(xr, action_set_name, action_name);
+ if (!action) {
+ return false;
+ }
+
+ action_set->controller_pose_action = action;
+
+ if (action_set == xr->runtime->session_state.active_action_set) {
+ wm_xr_session_controller_data_populate(action, xr);
+ }
+
+ return true;
+}
+
+bool WM_xr_action_state_get(const wmXrData *xr,
+ const char *action_set_name,
+ const char *action_name,
+ const char *subaction_path,
+ wmXrActionState *r_state)
+{
+ const wmXrAction *action = action_find((wmXrData *)xr, action_set_name, action_name);
+ if (!action) {
+ return false;
+ }
+
+ BLI_assert(action->type == (eXrActionType)r_state->type);
+
+ /* Find the action state corresponding to the subaction path. */
+ for (unsigned int i = 0; i < action->count_subaction_paths; ++i) {
+ if (STREQ(subaction_path, action->subaction_paths[i])) {
+ switch ((eXrActionType)r_state->type) {
+ case XR_BOOLEAN_INPUT:
+ r_state->state_boolean = ((bool *)action->states)[i];
+ break;
+ case XR_FLOAT_INPUT:
+ r_state->state_float = ((float *)action->states)[i];
+ break;
+ case XR_VECTOR2F_INPUT:
+ copy_v2_v2(r_state->state_vector2f, ((float(*)[2])action->states)[i]);
+ break;
+ case XR_POSE_INPUT: {
+ const GHOST_XrPose *pose = &((GHOST_XrPose *)action->states)[i];
+ copy_v3_v3(r_state->state_pose.position, pose->position);
+ copy_qt_qt(r_state->state_pose.orientation_quat, pose->orientation_quat);
+ break;
+ }
+ case XR_VIBRATION_OUTPUT:
+ BLI_assert_unreachable();
+ break;
+ }
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool WM_xr_haptic_action_apply(wmXrData *xr,
+ const char *action_set_name,
+ const char *action_name,
+ const long long *duration,
+ const float *frequency,
+ const float *amplitude)
+{
+ return GHOST_XrApplyHapticAction(
+ xr->runtime->context, action_set_name, action_name, duration, frequency, amplitude) ?
+ true :
+ false;
+}
+
+void WM_xr_haptic_action_stop(wmXrData *xr, const char *action_set_name, const char *action_name)
+{
+ GHOST_XrStopHapticAction(xr->runtime->context, action_set_name, action_name);
+}
+
+/** \} */ /* XR-Action API */
diff --git a/source/blender/windowmanager/xr/intern/wm_xr_draw.c b/source/blender/windowmanager/xr/intern/wm_xr_draw.c
index cc4a7e41e82..1f722855696 100644
--- a/source/blender/windowmanager/xr/intern/wm_xr_draw.c
+++ b/source/blender/windowmanager/xr/intern/wm_xr_draw.c
@@ -45,6 +45,12 @@ void wm_xr_pose_to_viewmat(const GHOST_XrPose *pose, float r_viewmat[4][4])
translate_m4(r_viewmat, -pose->position[0], -pose->position[1], -pose->position[2]);
}
+void wm_xr_controller_pose_to_mat(const GHOST_XrPose *pose, float r_mat[4][4])
+{
+ quat_to_mat4(r_mat, pose->orientation_quat);
+ copy_v3_v3(r_mat[3], pose->position);
+}
+
static void wm_xr_draw_matrices_create(const wmXrDrawData *draw_data,
const GHOST_XrDrawViewInfo *draw_view,
const XrSessionSettings *session_settings,
diff --git a/source/blender/windowmanager/xr/intern/wm_xr_intern.h b/source/blender/windowmanager/xr/intern/wm_xr_intern.h
index 25e3da3ffb4..9bf63be61dd 100644
--- a/source/blender/windowmanager/xr/intern/wm_xr_intern.h
+++ b/source/blender/windowmanager/xr/intern/wm_xr_intern.h
@@ -24,6 +24,21 @@
#include "wm_xr.h"
+struct wmXrActionSet;
+
+typedef struct wmXrControllerData {
+ /** OpenXR path identifier. Length is dependent on OpenXR's XR_MAX_PATH_LENGTH (256).
+ This subaction path will later be combined with a component path, and that combined path should
+ also have a max of XR_MAX_PATH_LENGTH (e.g. subaction_path = /user/hand/left, component_path =
+ /input/trigger/value, interaction_path = /user/hand/left/input/trigger/value).
+ */
+ char subaction_path[64];
+ /** Last known controller pose (in world space) stored for queries. */
+ GHOST_XrPose pose;
+ /** The last known controller matrix, calculated from above's controller pose. */
+ float mat[4][4];
+} wmXrControllerData;
+
typedef struct wmXrSessionState {
bool is_started;
@@ -39,11 +54,23 @@ typedef struct wmXrSessionState {
Object *prev_base_pose_object;
/** Copy of XrSessionSettings.flag created on the last draw call, stored to detect changes. */
int prev_settings_flag;
+ /** Copy of wmXrDrawData.base_pose. */
+ GHOST_XrPose prev_base_pose;
+ /** Copy of GHOST_XrDrawViewInfo.local_pose. */
+ GHOST_XrPose prev_local_pose;
/** Copy of wmXrDrawData.eye_position_ofs. */
float prev_eye_position_ofs[3];
bool force_reset_to_base_pose;
bool is_view_data_set;
+
+ /** Last known controller data. */
+ wmXrControllerData controllers[2];
+
+ /** The currently active action set that will be updated on calls to
+ * wm_xr_session_actions_update(). If NULL, all action sets will be treated as active and
+ * updated. */
+ struct wmXrActionSet *active_action_set;
} wmXrSessionState;
typedef struct wmXrRuntimeData {
@@ -79,6 +106,40 @@ typedef struct wmXrDrawData {
float eye_position_ofs[3]; /* Local/view space. */
} wmXrDrawData;
+typedef struct wmXrAction {
+ char *name;
+ eXrActionType type;
+ unsigned int count_subaction_paths;
+ char **subaction_paths;
+ /** States for each subaction path. */
+ void *states;
+ /** Previous states, stored to determine XR events. */
+ void *states_prev;
+
+ /** Input threshold for float/vector2f actions. */
+ float float_threshold;
+
+ /** The currently active subaction path (if any) for modal actions. */
+ char **active_modal_path;
+
+ /** Operator to be called on XR events. */
+ struct wmOperatorType *ot;
+ IDProperty *op_properties;
+ eXrOpFlag op_flag;
+} wmXrAction;
+
+typedef struct wmXrActionSet {
+ char *name;
+
+ /** The XR pose action that determines the controller
+ * transforms. This is usually identified by the OpenXR path "/grip/pose" or "/aim/pose",
+ * although it could differ depending on the specification and hardware. */
+ wmXrAction *controller_pose_action;
+
+ /** The currently active modal action (if any). */
+ wmXrAction *active_modal_action;
+} wmXrActionSet;
+
wmXrRuntimeData *wm_xr_runtime_data_create(void);
void wm_xr_runtime_data_free(wmXrRuntimeData **runtime);
@@ -95,5 +156,12 @@ bool wm_xr_session_surface_offscreen_ensure(wmXrSurfaceData *surface_data,
void *wm_xr_session_gpu_binding_context_create(void);
void wm_xr_session_gpu_binding_context_destroy(GHOST_ContextHandle context);
+void wm_xr_session_actions_init(wmXrData *xr);
+void wm_xr_session_actions_update(wmXrData *xr);
+void wm_xr_session_controller_data_populate(const wmXrAction *controller_pose_action,
+ wmXrData *xr);
+void wm_xr_session_controller_data_clear(wmXrSessionState *state);
+
void wm_xr_pose_to_viewmat(const GHOST_XrPose *pose, float r_viewmat[4][4]);
+void wm_xr_controller_pose_to_mat(const GHOST_XrPose *pose, float r_mat[4][4]);
void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata);
diff --git a/source/blender/windowmanager/xr/intern/wm_xr_session.c b/source/blender/windowmanager/xr/intern/wm_xr_session.c
index b9ef40e3398..1ddbe228e05 100644
--- a/source/blender/windowmanager/xr/intern/wm_xr_session.c
+++ b/source/blender/windowmanager/xr/intern/wm_xr_session.c
@@ -18,7 +18,9 @@
* \ingroup wm
*/
+#include "BKE_callbacks.h"
#include "BKE_context.h"
+#include "BKE_global.h"
#include "BKE_main.h"
#include "BKE_scene.h"
@@ -49,11 +51,24 @@ static CLG_LogRef LOG = {"wm.xr"};
/* -------------------------------------------------------------------- */
+static void wm_xr_session_create_cb(void)
+{
+ Main *bmain = G_MAIN;
+ wmWindowManager *wm = bmain->wm.first;
+ wmXrData *xr_data = &wm->xr;
+
+ /* Get action set data from Python. */
+ BKE_callback_exec_null(bmain, BKE_CB_EVT_XR_SESSION_START_PRE);
+
+ wm_xr_session_actions_init(xr_data);
+}
+
static void wm_xr_session_exit_cb(void *customdata)
{
wmXrData *xr_data = customdata;
xr_data->runtime->session_state.is_started = false;
+
if (xr_data->runtime->exit_fn) {
xr_data->runtime->exit_fn(xr_data);
}
@@ -65,6 +80,10 @@ static void wm_xr_session_exit_cb(void *customdata)
static void wm_xr_session_begin_info_create(wmXrData *xr_data,
GHOST_XrSessionBeginInfo *r_begin_info)
{
+ /* Callback for when the session is created. This is needed to create and bind OpenXR actions
+ * after the session is created but before it is started. */
+ r_begin_info->create_fn = wm_xr_session_create_cb;
+
/* WM-XR exit function, does some own stuff and calls callback passed to wm_xr_session_toggle(),
* to allow external code to execute its own session-exit logic. */
r_begin_info->exit_fn = wm_xr_session_exit_cb;
@@ -289,6 +308,7 @@ void wm_xr_session_draw_data_update(const wmXrSessionState *state,
/**
* Update information that is only stored for external state queries. E.g. for Python API to
* request the current (as in, last known) viewer pose.
+ * Controller data and action sets will be updated separately via wm_xr_session_actions_update().
*/
void wm_xr_session_state_update(const XrSessionSettings *settings,
const wmXrDrawData *draw_data,
@@ -322,6 +342,8 @@ void wm_xr_session_state_update(const XrSessionSettings *settings,
DEFAULT_SENSOR_WIDTH);
copy_v3_v3(state->prev_eye_position_ofs, draw_data->eye_position_ofs);
+ memcpy(&state->prev_base_pose, &draw_data->base_pose, sizeof(state->prev_base_pose));
+ memcpy(&state->prev_local_pose, &draw_view->local_pose, sizeof(state->prev_local_pose));
state->prev_settings_flag = settings->flag;
state->prev_base_pose_type = settings->base_pose_type;
state->prev_base_pose_object = settings->base_pose_object;
@@ -373,6 +395,132 @@ bool WM_xr_session_state_viewer_pose_matrix_info_get(const wmXrData *xr,
return true;
}
+bool WM_xr_session_state_controller_pose_location_get(const wmXrData *xr,
+ unsigned int subaction_idx,
+ float r_location[3])
+{
+ if (!WM_xr_session_is_ready(xr) || !xr->runtime->session_state.is_view_data_set ||
+ subaction_idx >= ARRAY_SIZE(xr->runtime->session_state.controllers)) {
+ zero_v3(r_location);
+ return false;
+ }
+
+ copy_v3_v3(r_location, xr->runtime->session_state.controllers[subaction_idx].pose.position);
+ return true;
+}
+
+bool WM_xr_session_state_controller_pose_rotation_get(const wmXrData *xr,
+ unsigned int subaction_idx,
+ float r_rotation[4])
+{
+ if (!WM_xr_session_is_ready(xr) || !xr->runtime->session_state.is_view_data_set ||
+ subaction_idx >= ARRAY_SIZE(xr->runtime->session_state.controllers)) {
+ unit_qt(r_rotation);
+ return false;
+ }
+
+ copy_v4_v4(r_rotation,
+ xr->runtime->session_state.controllers[subaction_idx].pose.orientation_quat);
+ return true;
+}
+
+/* -------------------------------------------------------------------- */
+/** \name XR-Session Actions
+ *
+ * XR action processing and event dispatching.
+ *
+ * \{ */
+
+void wm_xr_session_actions_init(wmXrData *xr)
+{
+ if (!xr->runtime) {
+ return;
+ }
+
+ GHOST_XrAttachActionSets(xr->runtime->context);
+}
+
+static void wm_xr_session_controller_mats_update(const XrSessionSettings *settings,
+ const wmXrAction *controller_pose_action,
+ wmXrSessionState *state)
+{
+ const unsigned int count = (unsigned int)min_ii(
+ (int)controller_pose_action->count_subaction_paths, (int)ARRAY_SIZE(state->controllers));
+
+ float view_ofs[3];
+ float base_inv[4][4];
+ float tmp[4][4];
+
+ zero_v3(view_ofs);
+ if ((settings->flag & XR_SESSION_USE_POSITION_TRACKING) == 0) {
+ add_v3_v3(view_ofs, state->prev_local_pose.position);
+ }
+
+ wm_xr_pose_to_viewmat(&state->prev_base_pose, base_inv);
+ invert_m4(base_inv);
+
+ for (unsigned int i = 0; i < count; ++i) {
+ wmXrControllerData *controller = &state->controllers[i];
+
+ /* Calculate controller matrix in world space. */
+ wm_xr_controller_pose_to_mat(&((GHOST_XrPose *)controller_pose_action->states)[i], tmp);
+
+ /* Apply eye position and base pose offsets. */
+ sub_v3_v3(tmp[3], view_ofs);
+ mul_m4_m4m4(controller->mat, base_inv, tmp);
+
+ /* Save final pose. */
+ mat4_to_loc_quat(
+ controller->pose.position, controller->pose.orientation_quat, controller->mat);
+ }
+}
+
+void wm_xr_session_actions_update(wmXrData *xr)
+{
+ if (!xr->runtime) {
+ return;
+ }
+
+ GHOST_XrContextHandle xr_context = xr->runtime->context;
+ wmXrSessionState *state = &xr->runtime->session_state;
+ wmXrActionSet *active_action_set = state->active_action_set;
+
+ int ret = GHOST_XrSyncActions(xr_context, active_action_set ? active_action_set->name : NULL);
+ if (!ret) {
+ return;
+ }
+
+ /* Only update controller mats for active action set. */
+ if (active_action_set) {
+ if (active_action_set->controller_pose_action) {
+ wm_xr_session_controller_mats_update(
+ &xr->session_settings, active_action_set->controller_pose_action, state);
+ }
+ }
+}
+
+void wm_xr_session_controller_data_populate(const wmXrAction *controller_pose_action, wmXrData *xr)
+{
+ wmXrSessionState *state = &xr->runtime->session_state;
+
+ const unsigned int count = (unsigned int)min_ii(
+ (int)ARRAY_SIZE(state->controllers), (int)controller_pose_action->count_subaction_paths);
+
+ for (unsigned int i = 0; i < count; ++i) {
+ wmXrControllerData *c = &state->controllers[i];
+ strcpy(c->subaction_path, controller_pose_action->subaction_paths[i]);
+ memset(&c->pose, 0, sizeof(c->pose));
+ zero_m4(c->mat);
+ }
+}
+
+void wm_xr_session_controller_data_clear(wmXrSessionState *state)
+{
+ memset(state->controllers, 0, sizeof(state->controllers));
+}
+
+/** \} */ /* XR-Session Actions */
+
/* -------------------------------------------------------------------- */
/** \name XR-Session Surface
*
diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt
index c7b940d0012..39bf2f1e32d 100644
--- a/source/creator/CMakeLists.txt
+++ b/source/creator/CMakeLists.txt
@@ -116,9 +116,14 @@ if(WITH_XR_OPENXR)
endif()
if(WITH_GMP)
+ blender_include_dirs(${GMP_INCLUDE_DIRS})
add_definitions(-DWITH_GMP)
endif()
+if(WITH_OPENCOLORIO)
+ add_definitions(-DWITH_OCIO)
+endif()
+
# Setup the exe sources and buildinfo
set(SRC
creator.c
@@ -130,14 +135,11 @@ set(SRC
# MSVC 2010 gives linking errors with the manifest
if(WIN32 AND NOT UNIX)
- string(SUBSTRING ${BLENDER_VERSION} 0 1 bver1)
- string(SUBSTRING ${BLENDER_VERSION} 2 1 bver2)
- string(SUBSTRING ${BLENDER_VERSION} 3 1 bver3)
add_definitions(
-DBLEN_VER_RC_STR="${BLENDER_VERSION}"
- -DBLEN_VER_RC_1=${bver1}
- -DBLEN_VER_RC_2=${bver2}
- -DBLEN_VER_RC_3=${bver3}
+ -DBLEN_VER_RC_1=${BLENDER_VERSION_MAJOR}
+ -DBLEN_VER_RC_2=${BLENDER_VERSION_MINOR}
+ -DBLEN_VER_RC_3=${BLENDER_VERSION_PATCH}
-DBLEN_VER_RC_4=0
)
@@ -188,7 +190,7 @@ if(WITH_BUILDINFO)
# --------------------------------------------------------------------------
# write header for values that change each build
- # note, generaed file is in build dir's source/creator
+ # note, generated file is in build dir's source/creator
# except when used as an include path.
add_definitions(-DWITH_BUILDINFO_HEADER)
@@ -288,6 +290,15 @@ if(WITH_PYTHON_MODULE)
else()
add_executable(blender ${EXETYPE} ${SRC})
+ if(WIN32)
+ add_executable(blender-launcher WIN32
+ blender_launcher_win32.c
+ ${CMAKE_SOURCE_DIR}/release/windows/icons/winblender.rc
+ ${CMAKE_BINARY_DIR}/blender.exe.manifest
+ )
+ target_compile_definitions (blender-launcher PRIVATE -D_UNICODE -DUNICODE)
+ target_link_libraries(blender-launcher Pathcch.lib)
+ endif()
endif()
if(WITH_BUILDINFO)
@@ -326,7 +337,14 @@ elseif(WIN32)
elseif(APPLE)
if(WITH_PYTHON_MODULE)
- set(TARGETDIR_VER ${BLENDER_VERSION})
+ if(WITH_INSTALL_PORTABLE)
+ set(TARGETDIR_VER $<TARGET_FILE_DIR:blender>/../Resources/${BLENDER_VERSION})
+ # Keep the `BLENDER_VERSION` folder and bpy.so in the build folder.
+ set(INSTALL_BPY_TO_SITE_PACKAGES OFF)
+ else()
+ set(TARGETDIR_VER "${PYTHON_LIBPATH}/Resources/${BLENDER_VERSION}")
+ set(INSTALL_BPY_TO_SITE_PACKAGES ON)
+ endif()
else()
set(TARGETDIR_VER Blender.app/Contents/Resources/${BLENDER_VERSION})
endif()
@@ -456,8 +474,8 @@ if(UNIX AND NOT APPLE)
add_custom_target(
blender_man_page ALL
COMMAND ${CMAKE_SOURCE_DIR}/doc/manpage/blender.1.py
- ${EXECUTABLE_OUTPUT_PATH}/blender
- ${CMAKE_CURRENT_BINARY_DIR}/blender.1)
+ --blender ${EXECUTABLE_OUTPUT_PATH}/blender
+ --output ${CMAKE_CURRENT_BINARY_DIR}/blender.1)
add_dependencies(blender_man_page blender)
endif()
endif()
@@ -686,6 +704,26 @@ elseif(WIN32)
DESTINATION "."
)
endif()
+ if(MSVC_ASAN)
+ # The asan dll's can be found in the same folder as the compiler, this is the easiest way to find these.
+ string(REPLACE "cl.exe" "clang_rt.asan_dynamic-x86_64.dll" ASAN_DLL ${CMAKE_C_COMPILER})
+ string(REPLACE "cl.exe" "clang_rt.asan_dbg_dynamic-x86_64.dll" ASAN_DEBUG_DLL ${CMAKE_C_COMPILER})
+ if(NOT EXISTS "${ASAN_DLL}")
+ message(FATAL_ERROR "Asan is enabled, but the ASAN runtime is not detected, this is an optional component during the MSVC install, please install it")
+ endif()
+ install(
+ FILES ${ASAN_DLL}
+ DESTINATION "."
+ CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel
+ )
+ install(
+ FILES ${ASAN_DEBUG_DLL}
+ DESTINATION "."
+ CONFIGURATIONS Debug
+ )
+ unset(ASAN_DLL)
+ unset(ASAN_DEBUG_DLL)
+ endif()
if(WITH_GMP)
install(
@@ -846,13 +884,13 @@ elseif(WIN32)
if(WITH_TBB)
install(
FILES
- ${LIBDIR}/tbb/lib/tbb.dll
+ ${LIBDIR}/tbb/bin/tbb.dll
DESTINATION "."
CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel
)
install(
FILES
- ${LIBDIR}/tbb/lib/debug/tbb_debug.dll
+ ${LIBDIR}/tbb/bin/tbb_debug.dll
DESTINATION "."
CONFIGURATIONS Debug
)
@@ -860,18 +898,19 @@ elseif(WIN32)
if(WITH_TBB_MALLOC_PROXY)
install(
FILES
- ${LIBDIR}/tbb/lib/tbbmalloc.dll
- ${LIBDIR}/tbb/lib/tbbmalloc_proxy.dll
+ ${LIBDIR}/tbb/bin/tbbmalloc.dll
+ ${LIBDIR}/tbb/bin/tbbmalloc_proxy.dll
DESTINATION "."
CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel
)
install(
FILES
- ${LIBDIR}/tbb/lib/debug/tbbmalloc.dll
- ${LIBDIR}/tbb/lib/debug/tbbmalloc_proxy.dll
+ ${LIBDIR}/tbb/bin/tbbmalloc_debug.dll
+ ${LIBDIR}/tbb/bin/tbbmalloc_proxy_debug.dll
DESTINATION "."
CONFIGURATIONS Debug
)
+ list(APPEND LIB ${TBB_MALLOC_LIBRARIES})
endif()
if(WITH_CODEC_SNDFILE)
@@ -1005,6 +1044,20 @@ elseif(APPLE)
FILES ${LIBDIR}/openmp/lib/libomp.dylib
DESTINATION Blender.app/Contents/Resources/lib
)
+ if(WITH_PYTHON_MODULE)
+ # Move the dylib in a Blender version folder to keep the corresponding OpenMP version.
+ install(
+ DIRECTORY ${CMAKE_BINARY_DIR}/Resources/lib
+ DESTINATION ${TARGETDIR_VER}
+ )
+ add_custom_command(TARGET blender POST_BUILD
+ # The old `LC_LOAD_DYLIB` is the `LC_ID_DYLIB` of the LIBDIR OpenMP dylib.
+ # Change it to support multiple rpaths.
+ COMMAND xcrun install_name_tool -change "@executable_path/../Resources/lib/libomp.dylib" "@rpath/libomp.dylib" "$<TARGET_FILE:blender>"
+ # For installation into site-packages.
+ COMMAND xcrun install_name_tool -add_rpath "@loader_path/../Resources/${BLENDER_VERSION}/lib" "$<TARGET_FILE:blender>"
+ )
+ endif()
endif()
if(WITH_LLVM AND NOT LLVM_STATIC)
@@ -1036,6 +1089,14 @@ elseif(APPLE)
)
unset(_py_inc_suffix)
endif()
+ if(WITH_PYTHON_MODULE)
+ if(INSTALL_BPY_TO_SITE_PACKAGES)
+ install(
+ TARGETS blender
+ LIBRARY DESTINATION ${PYTHON_LIBPATH}/site-packages
+ )
+ endif()
+ endif()
if(WITH_DRACO)
install(
@@ -1166,7 +1227,7 @@ endif()
if(WIN32 AND NOT WITH_PYTHON_MODULE)
install(
- TARGETS blender
+ TARGETS blender blender-launcher
COMPONENT Blender
DESTINATION "."
)
diff --git a/source/creator/blender_launcher_win32.c b/source/creator/blender_launcher_win32.c
new file mode 100644
index 00000000000..86b0f4f3b97
--- /dev/null
+++ b/source/creator/blender_launcher_win32.c
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+
+#include <Windows.h>
+#include <strsafe.h>
+
+#include <PathCch.h>
+
+int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
+{
+ STARTUPINFO siStartInfo = {0};
+ PROCESS_INFORMATION procInfo;
+ wchar_t path[MAX_PATH];
+
+ siStartInfo.wShowWindow = SW_HIDE;
+ siStartInfo.dwFlags = STARTF_USESHOWWINDOW;
+
+ /* Get the path to the currently running executable (blender-launcher.exe) */
+
+ DWORD nSize = GetModuleFileName(NULL, path, MAX_PATH);
+ if (!nSize) {
+ return -1;
+ }
+
+ /* GetModuleFileName returns the number of characters written, but GetLastError needs to be
+ * called to see if it ran out of space or not. However where would we be without exceptions
+ * to the rule: "If the buffer is too small to hold the module name, the function returns nSize.
+ * The last error code remains ERROR_SUCCESS." - source: MSDN. */
+
+ if (GetLastError() == ERROR_SUCCESS && nSize == MAX_PATH) {
+ return -1;
+ }
+
+ /* Remove the filename (blender-launcher.exe) from path. */
+ if (PathCchRemoveFileSpec(path, MAX_PATH) != S_OK) {
+ return -1;
+ }
+
+ /* Add blender.exe to path, resulting in the full path to the blender executable. */
+ if (PathCchCombine(path, MAX_PATH, path, L"blender.exe") != S_OK) {
+ return -1;
+ }
+
+ int required_size_chars = lstrlenW(path) + /* Module name */
+ 3 + /* 2 quotes + Space */
+ lstrlenW(pCmdLine) + /* Original command line */
+ 1; /* Zero terminator */
+ size_t required_size_bytes = required_size_chars * sizeof(wchar_t);
+ wchar_t *buffer = (wchar_t *)malloc(required_size_bytes);
+ if (!buffer) {
+ return -1;
+ }
+
+ if (StringCbPrintfEx(buffer,
+ required_size_bytes,
+ NULL,
+ NULL,
+ STRSAFE_NULL_ON_FAILURE,
+ L"\"%s\" %s",
+ path,
+ pCmdLine) != S_OK) {
+ free(buffer);
+ return -1;
+ }
+
+ BOOL success = CreateProcess(
+ path, buffer, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &siStartInfo, &procInfo);
+
+ if (success) {
+ /* Handles in PROCESS_INFORMATION must be closed with CloseHandle when they are no longer
+ * needed - MSDN. Closing the handles will NOT terminate the thread/process that we just
+ * started. */
+ CloseHandle(procInfo.hThread);
+ CloseHandle(procInfo.hProcess);
+ }
+
+ free(buffer);
+ return success ? 0 : -1;
+}
diff --git a/source/creator/creator.c b/source/creator/creator.c
index b40718d1f7c..2ec4a2aa616 100644
--- a/source/creator/creator.c
+++ b/source/creator/creator.c
@@ -132,7 +132,6 @@ struct ApplicationState app_state = {
/** \name Application Level Callbacks
*
* Initialize callbacks for the modules that need them.
- *
* \{ */
static void callback_mem_error(const char *errorStr)
@@ -211,6 +210,41 @@ char **environ = NULL;
/** \} */
/* -------------------------------------------------------------------- */
+/** \name GMP Allocator Workaround
+ * \{ */
+
+#if (defined(WITH_TBB_MALLOC) && defined(_MSC_VER) && defined(NDEBUG) && defined(WITH_GMP)) || \
+ defined(DOXYGEN)
+# include "gmp.h"
+# include "tbb/scalable_allocator.h"
+
+void *gmp_alloc(size_t size)
+{
+ return scalable_malloc(size);
+}
+void *gmp_realloc(void *ptr, size_t old_size, size_t new_size)
+{
+ return scalable_realloc(ptr, new_size);
+}
+
+void gmp_free(void *ptr, size_t size)
+{
+ scalable_free(ptr);
+}
+/**
+ * Use TBB's scalable_allocator on Windows.
+ * `TBBmalloc` correctly captures all allocations already,
+ * however, GMP is built with MINGW since it doesn't build with MSVC,
+ * which TBB has issues hooking into automatically.
+ */
+void gmp_blender_init_allocator()
+{
+ mp_set_memory_functions(gmp_alloc, gmp_realloc, gmp_free);
+}
+#endif
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Main Function
* \{ */
@@ -343,6 +377,10 @@ int main(int argc,
CCL_init_logging(argv[0]);
#endif
+#if defined(WITH_TBB_MALLOC) && defined(_MSC_VER) && defined(NDEBUG) && defined(WITH_GMP)
+ gmp_blender_init_allocator();
+#endif
+
main_callback_setup();
#if defined(__APPLE__) && !defined(WITH_PYTHON_MODULE) && !defined(WITH_HEADLESS)
diff --git a/source/creator/creator_args.c b/source/creator/creator_args.c
index d1899cc1408..43f23510927 100644
--- a/source/creator/creator_args.c
+++ b/source/creator/creator_args.c
@@ -674,6 +674,9 @@ static int arg_handle_print_help(int UNUSED(argc), const char **UNUSED(argv), vo
printf(" $BLENDER_USER_DATAFILES Directory for user data files (icons, translations, ..).\n");
printf(" $BLENDER_SYSTEM_DATAFILES Directory for system wide data files.\n");
printf(" $BLENDER_SYSTEM_PYTHON Directory for system Python libraries.\n");
+# ifdef WITH_OCIO
+ printf(" $OCIO Path to override the OpenColorIO config file.\n");
+# endif
# ifdef WIN32
printf(" $TEMP Store temporary files here.\n");
# else
@@ -1189,18 +1192,23 @@ static const char arg_handle_playback_mode_doc[] =
"\t-s <frame>\n"
"\t\tPlay from <frame>.\n"
"\t-e <frame>\n"
- "\t\tPlay until <frame>.";
+ "\t\tPlay until <frame>.\n"
+ "\t-c <cache_memory>\n"
+ "\t\tAmount of memory in megabytes to allow for caching images during playback.\n"
+ "\t\tZero disables (clamping to a fixed number of frames instead).";
static int arg_handle_playback_mode(int argc, const char **argv, void *UNUSED(data))
{
- /* not if -b was given first */
+ /* Ignore the animation player if `-b` was given first. */
if (G.background == 0) {
# ifdef WITH_FFMPEG
/* Setup FFmpeg with current debug flags. */
IMB_ffmpeg_init();
# endif
- WM_main_playanim(argc, argv); /* not the same argc and argv as before */
- exit(0); /* 2.4x didn't do this */
+ /* This function knows to skip this argument ('-a'). */
+ WM_main_playanim(argc, argv);
+
+ exit(0);
}
return -2;
@@ -1951,7 +1959,9 @@ static int arg_handle_load_file(int UNUSED(argc), const char **argv, void *data)
}
BLI_strncpy(filename, argv[0], sizeof(filename));
+ BLI_path_slash_native(filename);
BLI_path_abs_from_cwd(filename, sizeof(filename));
+ BLI_path_normalize(NULL, filename);
/* load the file */
BKE_reports_init(&reports, RPT_PRINT);
@@ -2041,6 +2051,15 @@ void main_args_setup(bContext *C, bArgs *ba)
BLI_args_add(ba, "-t", "--threads", CB(arg_handle_threads_set), NULL);
+ /* Include in the environment pass so it's possible display errors initializing subsystems,
+ * especially `bpy.appdir` since it's useful to show errors finding paths on startup. */
+ BLI_args_add(ba, NULL, "--log", CB(arg_handle_log_set), ba);
+ BLI_args_add(ba, NULL, "--log-level", CB(arg_handle_log_level_set), ba);
+ BLI_args_add(ba, NULL, "--log-show-basename", CB(arg_handle_log_show_basename_set), ba);
+ BLI_args_add(ba, NULL, "--log-show-backtrace", CB(arg_handle_log_show_backtrace_set), ba);
+ BLI_args_add(ba, NULL, "--log-show-timestamp", CB(arg_handle_log_show_timestamp_set), ba);
+ BLI_args_add(ba, NULL, "--log-file", CB(arg_handle_log_file_set), ba);
+
/* Pass: Background Mode & Settings
*
* Also and commands that exit after usage. */
@@ -2062,13 +2081,6 @@ void main_args_setup(bContext *C, bArgs *ba)
BLI_args_add(ba, "-a", NULL, CB(arg_handle_playback_mode), NULL);
- BLI_args_add(ba, NULL, "--log", CB(arg_handle_log_set), ba);
- BLI_args_add(ba, NULL, "--log-level", CB(arg_handle_log_level_set), ba);
- BLI_args_add(ba, NULL, "--log-show-basename", CB(arg_handle_log_show_basename_set), ba);
- BLI_args_add(ba, NULL, "--log-show-backtrace", CB(arg_handle_log_show_backtrace_set), ba);
- BLI_args_add(ba, NULL, "--log-show-timestamp", CB(arg_handle_log_show_timestamp_set), ba);
- BLI_args_add(ba, NULL, "--log-file", CB(arg_handle_log_file_set), ba);
-
BLI_args_add(ba, "-d", "--debug", CB(arg_handle_debug_mode_set), ba);
# ifdef WITH_FFMPEG
diff --git a/source/creator/creator_intern.h b/source/creator/creator_intern.h
index bcc8a15355a..2260da8db11 100644
--- a/source/creator/creator_intern.h
+++ b/source/creator/creator_intern.h
@@ -72,7 +72,7 @@ enum {
/* for the callbacks: */
#ifndef WITH_PYTHON_MODULE
-# define BLEND_VERSION_FMT "Blender %d.%02d.%d"
+# define BLEND_VERSION_FMT "Blender %d.%d.%d"
# define BLEND_VERSION_ARG (BLENDER_VERSION / 100), (BLENDER_VERSION % 100), BLENDER_VERSION_PATCH
#endif
diff --git a/source/tools b/source/tools
-Subproject b66c22e1fb977bf8dd3797ebedc28fbe28f0305
+Subproject 2afbb8ec472cac5102eb239f57b006f8c938768
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 8941cc671dd..3d9201bec04 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -47,9 +47,14 @@ unset(_default_test_python_exe)
set(TEST_BLENDER_EXE_PARAMS --background -noaudio --factory-startup --debug-memory --debug-exit-on-error --python-exit-code 1)
# Python CTests
-if(WITH_BLENDER AND WITH_PYTHON)
+if(WITH_BLENDER AND WITH_PYTHON AND NOT WITH_PYTHON_MODULE)
add_subdirectory(python)
endif()
+# Blender as python module tests.
+if(WITH_PYTHON_MODULE)
+ add_subdirectory(blender_as_python_module)
+endif()
+
# GTest
add_subdirectory(gtests)
diff --git a/source/blender/python/simple_enum_gen.py b/tests/blender_as_python_module/CMakeLists.txt
index 861701f4b4c..98e081672e9 100644
--- a/source/blender/python/simple_enum_gen.py
+++ b/tests/blender_as_python_module/CMakeLists.txt
@@ -14,50 +14,20 @@
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
+# The Original Code is Copyright (C) 2021, Blender Foundation
+# All rights reserved.
+#
# ***** END GPL LICENSE BLOCK *****
-# <pep8 compliant>
-
-defs = """
- SPACE_EMPTY,
- SPACE_VIEW3D,
- SPACE_IPO,
- SPACE_OUTLINER,
- SPACE_BUTS,
- SPACE_FILE,
- SPACE_IMAGE,
- SPACE_INFO,
- SPACE_SEQ,
- SPACE_TEXT,
- SPACE_IMASEL, #Deprecated
- SPACE_SOUND, #Deprecated
- SPACE_ACTION,
- SPACE_NLA,
- SPACE_SCRIPT, #Deprecated
- SPACE_TIME, #Deprecated
- SPACE_NODE,
- SPACEICONMAX
-"""
-
-print('\tmod = PyModule_New("dummy");')
-print('\tPyModule_AddObject(submodule, "key", mod);')
-
-for d in defs.split('\n'):
-
- d = d.replace(',', ' ')
- w = d.split()
-
- if not w:
- continue
-
- try:
- w.remove("#define")
- except:
- pass
-
- # print w
+function(add_blender_as_python_module_test testname testscript)
+ if(NOT TEST_PYTHON_EXE)
+ message(FATAL_ERROR "No Python configured for running tests, set TEST_PYTHON_EXE.")
+ endif()
- val = w[0]
- py_val = w[0]
+ add_test(
+ NAME ${testname}
+ COMMAND ${TEST_PYTHON_EXE} ${testscript} ${ARGN}
+ )
+endfunction()
- print('\tPyModule_AddObject(mod, "%s", PyLong_FromSize_t(%s));' % (val, py_val))
+add_blender_as_python_module_test(import_bpy ${CMAKE_CURRENT_LIST_DIR}/import_bpy.py)
diff --git a/tests/blender_as_python_module/import_bpy.py b/tests/blender_as_python_module/import_bpy.py
new file mode 100644
index 00000000000..bdc0277cfec
--- /dev/null
+++ b/tests/blender_as_python_module/import_bpy.py
@@ -0,0 +1,19 @@
+# ***** 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 *****
+
+# Just import bpy and see if there are any dynamic loader errors.
+import bpy
diff --git a/tests/python/CMakeLists.txt b/tests/python/CMakeLists.txt
index 969b748e973..92cebb7d274 100644
--- a/tests/python/CMakeLists.txt
+++ b/tests/python/CMakeLists.txt
@@ -713,6 +713,33 @@ if(WITH_CYCLES OR WITH_OPENGL_RENDER_TESTS)
endif()
endif()
+if(WITH_COMPOSITOR)
+ set(compositor_tests
+ color
+ converter
+ distort
+ filter
+ input
+ matte
+ output
+ vector
+
+ multiple_node_setups
+ )
+
+ foreach(comp_test ${compositor_tests})
+ add_python_test(
+ compositor_${comp_test}_test
+ ${CMAKE_CURRENT_LIST_DIR}/compositor_render_tests.py
+ -blender "${TEST_BLENDER_EXE}"
+ -testdir "${TEST_SRC_DIR}/compositor/${comp_test}"
+ -idiff "${OPENIMAGEIO_IDIFF}"
+ -outdir "${TEST_OUT_DIR}/compositor"
+ )
+ endforeach()
+
+endif()
+
if(WITH_OPENGL_DRAW_TESTS)
if(NOT OPENIMAGEIO_IDIFF)
MESSAGE(STATUS "Disabling OpenGL draw tests because OIIO idiff does not exist")
diff --git a/tests/python/bl_blendfile_library_overrides.py b/tests/python/bl_blendfile_library_overrides.py
index ab75a410590..48625a1ecdb 100644
--- a/tests/python/bl_blendfile_library_overrides.py
+++ b/tests/python/bl_blendfile_library_overrides.py
@@ -1,6 +1,6 @@
# Apache License, Version 2.0
-# ./blender.bin --background -noaudio --python tests/python/bl_blendfile_library_overrides.py
+# ./blender.bin --background -noaudio --python tests/python/bl_blendfile_library_overrides.py -- --output-dir=/tmp/
import pathlib
import bpy
import sys
@@ -16,6 +16,8 @@ class TestLibraryOverrides(TestHelper, unittest.TestCase):
OBJECT_LIBRARY_PARENT = "LibMeshParent"
MESH_LIBRARY_CHILD = "LibMeshChild"
OBJECT_LIBRARY_CHILD = "LibMeshChild"
+ MESH_LIBRARY_PERMISSIVE = "LibMeshPermissive"
+ OBJECT_LIBRARY_PERMISSIVE = "LibMeshPermissive"
def __init__(self, args):
self.args = args
@@ -33,6 +35,14 @@ class TestLibraryOverrides(TestHelper, unittest.TestCase):
obj_child = bpy.data.objects.new(TestLibraryOverrides.OBJECT_LIBRARY_CHILD, object_data=mesh_child)
obj_child.parent = obj
bpy.context.collection.objects.link(obj_child)
+
+ mesh = bpy.data.meshes.new(TestLibraryOverrides.MESH_LIBRARY_PERMISSIVE)
+ obj = bpy.data.objects.new(TestLibraryOverrides.OBJECT_LIBRARY_PERMISSIVE, object_data=mesh)
+ bpy.context.collection.objects.link(obj)
+ obj.override_template_create()
+ prop = obj.override_library.properties.add(rna_path='scale')
+ prop.operations.add(operation='NOOP')
+
bpy.ops.wm.save_as_mainfile(filepath=str(self.output_path), check_existing=False, compress=False)
def __ensure_override_library_updated(self):
@@ -47,29 +57,109 @@ class TestLibraryOverrides(TestHelper, unittest.TestCase):
bpy.ops.wm.link(directory=str(link_dir), filename=TestLibraryOverrides.OBJECT_LIBRARY_PARENT)
obj = bpy.data.objects[TestLibraryOverrides.OBJECT_LIBRARY_PARENT]
- assert(obj.override_library is None)
+ self.assertIsNone(obj.override_library)
local_id = obj.override_create()
- assert(local_id.override_library)
- assert(local_id.data.override_library is None)
+ self.assertIsNotNone(local_id.override_library)
+ self.assertIsNone(local_id.data.override_library)
assert(len(local_id.override_library.properties) == 0)
local_id.location.y = 1.0
self.__ensure_override_library_updated()
- assert (len(local_id.override_library.properties) == 1)
assert(len(local_id.override_library.properties) == 1)
override_prop = local_id.override_library.properties[0]
assert(override_prop.rna_path == "location");
assert(len(override_prop.operations) == 1)
override_operation = override_prop.operations[0]
- assert (override_operation.operation == 'REPLACE')
+ assert(override_operation.operation == 'REPLACE')
# Setting location.y overridded all elements in the location array. -1 is a wildcard.
assert(override_operation.subitem_local_index == -1)
-
+
+ def test_link_permissive(self):
+ """
+ Linked assets with a permissive template.
+
+ - Checks if the NOOP is properly handled.
+ - Checks if the correct properties and operations are created/updated.
+ """
+ bpy.ops.wm.read_homefile(use_empty=True, use_factory_startup=True)
+ bpy.data.orphans_purge()
+
+ link_dir = self.output_path / "Object"
+ bpy.ops.wm.link(directory=str(link_dir), filename=TestLibraryOverrides.OBJECT_LIBRARY_PERMISSIVE)
+
+ obj = bpy.data.objects[TestLibraryOverrides.OBJECT_LIBRARY_PERMISSIVE]
+ self.assertIsNotNone(obj.override_library)
+ local_id = obj.override_create()
+ self.assertIsNotNone(local_id.override_library)
+ self.assertIsNone(local_id.data.override_library)
+ assert(len(local_id.override_library.properties) == 1)
+ override_prop = local_id.override_library.properties[0]
+ assert(override_prop.rna_path == "scale");
+ assert(len(override_prop.operations) == 1)
+ override_operation = override_prop.operations[0]
+ assert(override_operation.operation == 'NOOP')
+ assert(override_operation.subitem_local_index == -1)
+
+ local_id.location.y = 1.0
+ local_id.scale.x = 0.5
+ # `scale.x` will apply, but will be reverted when the library overrides
+ # are updated. This is by design so python scripts can still alter the
+ # properties locally what is a typical usecase in productions.
+ assert(local_id.scale.x == 0.5)
+ assert(local_id.location.y == 1.0)
+
+ self.__ensure_override_library_updated()
+ assert(local_id.scale.x == 1.0)
+ assert(local_id.location.y == 1.0)
+
+ assert(len(local_id.override_library.properties) == 2)
+ override_prop = local_id.override_library.properties[0]
+ assert(override_prop.rna_path == "scale");
+ assert(len(override_prop.operations) == 1)
+ override_operation = override_prop.operations[0]
+ assert(override_operation.operation == 'NOOP')
+ assert(override_operation.subitem_local_index == -1)
+
+ override_prop = local_id.override_library.properties[1]
+ assert(override_prop.rna_path == "location");
+ assert(len(override_prop.operations) == 1)
+ override_operation = override_prop.operations[0]
+ assert(override_operation.operation == 'REPLACE')
+ assert (override_operation.subitem_local_index == -1)
+
+
+class TestLibraryTemplate(TestHelper, unittest.TestCase):
+ MESH_LIBRARY_PERMISSIVE = "LibMeshPermissive"
+ OBJECT_LIBRARY_PERMISSIVE = "LibMeshPermissive"
+
+ def __init__(self, args):
+ pass
+
+ def test_permissive_template(self):
+ """
+ Test setting up a permissive template.
+ """
+ bpy.ops.wm.read_homefile(use_empty=True, use_factory_startup=True)
+ mesh = bpy.data.meshes.new(TestLibraryTemplate.MESH_LIBRARY_PERMISSIVE)
+ obj = bpy.data.objects.new(TestLibraryTemplate.OBJECT_LIBRARY_PERMISSIVE, object_data=mesh)
+ bpy.context.collection.objects.link(obj)
+ assert(obj.override_library is None)
+ obj.override_template_create()
+ assert(obj.override_library is not None)
+ assert(len(obj.override_library.properties) == 0)
+ prop = obj.override_library.properties.add(rna_path='scale')
+ assert(len(obj.override_library.properties) == 1)
+ assert(len(prop.operations) == 0)
+ operation = prop.operations.add(operation='NOOP')
+ assert(len(prop.operations) == 1)
+ assert(operation.operation == 'NOOP')
+
TESTS = (
TestLibraryOverrides,
+ TestLibraryTemplate,
)
@@ -95,6 +185,7 @@ def main():
# Don't write thumbnails into the home directory.
bpy.context.preferences.filepaths.use_save_preview_images = False
+ bpy.context.preferences.experimental.use_override_templates = True
for Test in TESTS:
Test(args).run_all_tests()
diff --git a/tests/python/bl_load_addons.py b/tests/python/bl_load_addons.py
index 01f0b4d72d8..60f44c9452a 100644
--- a/tests/python/bl_load_addons.py
+++ b/tests/python/bl_load_addons.py
@@ -59,7 +59,8 @@ def _init_addon_blacklist():
def addon_modules_sorted():
- modules = addon_utils.modules({})
+ # Pass in an empty module cache to prevent `addon_utils` local module cache being manipulated.
+ modules = addon_utils.modules(module_cache={})
modules[:] = [
mod for mod in modules
if not (mod.__file__.startswith(BLACKLIST_DIRS))
diff --git a/tests/python/bl_pyapi_bpy_path.py b/tests/python/bl_pyapi_bpy_path.py
index 2d6019fbb07..ddb13cfe2ce 100644
--- a/tests/python/bl_pyapi_bpy_path.py
+++ b/tests/python/bl_pyapi_bpy_path.py
@@ -26,12 +26,12 @@ class TestBpyPath(unittest.TestCase):
self.assertEqual(ensure_ext('', ''), '')
self.assertEqual(ensure_ext('', '.blend'), '.blend')
- # Test case-sensitive behaviour.
- self.assertEqual(ensure_ext('demo', '.blend', True), 'demo.blend')
- self.assertEqual(ensure_ext('demo.BLEND', '.blend', True), 'demo.BLEND.blend')
- self.assertEqual(ensure_ext('demo', 'Blend', True), 'demoBlend')
- self.assertEqual(ensure_ext('demoBlend', 'blend', True), 'demoBlendblend')
- self.assertEqual(ensure_ext('demo', '', True), 'demo')
+ # Test case-sensitive behavior.
+ self.assertEqual(ensure_ext('demo', '.blend', case_sensitive=True), 'demo.blend')
+ self.assertEqual(ensure_ext('demo.BLEND', '.blend', case_sensitive=True), 'demo.BLEND.blend')
+ self.assertEqual(ensure_ext('demo', 'Blend', case_sensitive=True), 'demoBlend')
+ self.assertEqual(ensure_ext('demoBlend', 'blend', case_sensitive=True), 'demoBlendblend')
+ self.assertEqual(ensure_ext('demo', '', case_sensitive=True), 'demo')
if __name__ == '__main__':
diff --git a/tests/python/bl_pyapi_bpy_utils_units.py b/tests/python/bl_pyapi_bpy_utils_units.py
index d5d9c9c707b..bdb64fc361e 100644
--- a/tests/python/bl_pyapi_bpy_utils_units.py
+++ b/tests/python/bl_pyapi_bpy_utils_units.py
@@ -54,7 +54,7 @@ class UnitsTesting(unittest.TestCase):
return ((abs(v1 - v2) / max(abs(v1), abs(v2))) <= e)
for usys, utype, ref, inpt, val in self.INPUT_TESTS:
- opt_val = units.to_value(usys, utype, inpt, ref)
+ opt_val = units.to_value(usys, utype, inpt, str_ref_unit=ref)
# Note: almostequal is not good here, precision is fixed on decimal digits, not variable with
# magnitude of numbers (i.e. 1609.4416 ~= 1609.4456 fails even at 5 of 'places'...).
self.assertTrue(similar_values(opt_val, val, 1e-7),
@@ -63,7 +63,7 @@ class UnitsTesting(unittest.TestCase):
def test_units_outputs(self):
for usys, utype, prec, sep, compat, val, output in self.OUTPUT_TESTS:
- opt_str = units.to_string(usys, utype, val, prec, sep, compat)
+ opt_str = units.to_string(usys, utype, val, precision=prec, split_unit=sep, compatible_unit=compat)
self.assertEqual(
opt_str, output,
msg=(
diff --git a/tests/python/bl_pyapi_idprop.py b/tests/python/bl_pyapi_idprop.py
index 3d0cbd2a7bb..1e570bf9a7f 100644
--- a/tests/python/bl_pyapi_idprop.py
+++ b/tests/python/bl_pyapi_idprop.py
@@ -2,6 +2,7 @@
# ./blender.bin --background -noaudio --python tests/python/bl_pyapi_idprop.py -- --verbose
import bpy
+import idprop
import unittest
import numpy as np
from array import array
@@ -15,12 +16,12 @@ class TestHelper:
def setUp(self):
self._id = bpy.context.scene
- assert(len(self._id.keys()) == 0 or self._id.keys() == ["cycles"])
+ self._id.pop("cycles", None)
+ assert(len(self._id.keys()) == 0)
def tearDown(self):
for key in list(self._id.keys()):
- if key != "cycles":
- del self._id[key]
+ del self._id[key]
def assertAlmostEqualSeq(self, list1, list2):
self.assertEqual(len(list1), len(list2))
@@ -139,6 +140,51 @@ class TestIdPropertyCreation(TestHelper, unittest.TestCase):
with self.assertRaises(TypeError):
self.id["a"] = self
+class TestIdPropertyGroupView(TestHelper, unittest.TestCase):
+
+ def test_type(self):
+ self.assertEqual(type(self.id.keys()), idprop.types.IDPropertyGroupViewKeys)
+ self.assertEqual(type(self.id.values()), idprop.types.IDPropertyGroupViewValues)
+ self.assertEqual(type(self.id.items()), idprop.types.IDPropertyGroupViewItems)
+
+ self.assertEqual(type(iter(self.id.keys())), idprop.types.IDPropertyGroupIterKeys)
+ self.assertEqual(type(iter(self.id.values())), idprop.types.IDPropertyGroupIterValues)
+ self.assertEqual(type(iter(self.id.items())), idprop.types.IDPropertyGroupIterItems)
+
+ def test_basic(self):
+ text = ["A", "B", "C"]
+ for i, ch in enumerate(text):
+ self.id[ch] = i
+ self.assertEqual(len(self.id.keys()), len(text))
+ self.assertEqual(list(self.id.keys()), text)
+ self.assertEqual(list(reversed(self.id.keys())), list(reversed(text)))
+
+ self.assertEqual(len(self.id.values()), len(text))
+ self.assertEqual(list(self.id.values()), list(range(len(text))))
+ self.assertEqual(list(reversed(self.id.values())), list(reversed(range(len(text)))))
+
+ self.assertEqual(len(self.id.items()), len(text))
+ self.assertEqual(list(self.id.items()), [(k, v) for v, k in enumerate(text)])
+ self.assertEqual(list(reversed(self.id.items())), list(reversed([(k, v) for v, k in enumerate(text)])))
+
+ def test_contains(self):
+ # Check `idprop.types.IDPropertyGroupView{Keys/Values/Items}.__contains__`
+ text = ["A", "B", "C"]
+ for i, ch in enumerate(text):
+ self.id[ch] = i
+
+ self.assertIn("A", self.id)
+ self.assertNotIn("D", self.id)
+
+ self.assertIn("A", self.id.keys())
+ self.assertNotIn("D", self.id.keys())
+
+ self.assertIn(2, self.id.values())
+ self.assertNotIn(3, self.id.values())
+
+ self.assertIn(("A", 0), self.id.items())
+ self.assertNotIn(("D", 3), self.id.items())
+
class TestBufferProtocol(TestHelper, unittest.TestCase):
diff --git a/tests/python/bl_pyapi_mathutils.py b/tests/python/bl_pyapi_mathutils.py
index 9dfc6c159cc..c3a22be421a 100644
--- a/tests/python/bl_pyapi_mathutils.py
+++ b/tests/python/bl_pyapi_mathutils.py
@@ -2,7 +2,7 @@
# ./blender.bin --background -noaudio --python tests/python/bl_pyapi_mathutils.py -- --verbose
import unittest
-from mathutils import Matrix, Vector, Quaternion
+from mathutils import Matrix, Vector, Quaternion, Euler
from mathutils import kdtree, geometry
import math
@@ -233,6 +233,27 @@ class MatrixTesting(unittest.TestCase):
self.assertEqual(mat @ mat, prod_mat)
+ def test_loc_rot_scale(self):
+ euler = Euler((math.radians(90), 0, math.radians(90)), 'ZYX')
+ expected = Matrix(((0, -5, 0, 1),
+ (0, 0, -6, 2),
+ (4, 0, 0, 3),
+ (0, 0, 0, 1)))
+
+ result = Matrix.LocRotScale((1, 2, 3), euler, (4, 5, 6))
+ self.assertAlmostEqualMatrix(result, expected, 4)
+
+ result = Matrix.LocRotScale((1, 2, 3), euler.to_quaternion(), (4, 5, 6))
+ self.assertAlmostEqualMatrix(result, expected, 4)
+
+ result = Matrix.LocRotScale((1, 2, 3), euler.to_matrix(), (4, 5, 6))
+ self.assertAlmostEqualMatrix(result, expected, 4)
+
+ def assertAlmostEqualMatrix(self, first, second, size, *, places=6, msg=None, delta=None):
+ for i in range(size):
+ for j in range(size):
+ self.assertAlmostEqual(first[i][j], second[i][j], places=places, msg=msg, delta=delta)
+
class VectorTesting(unittest.TestCase):
@@ -442,20 +463,20 @@ class KDTreeTesting(unittest.TestCase):
ret_regular = k_odd.find(co)
self.assertEqual(ret_regular[1] % 2, 1)
- ret_filter = k_all.find(co, lambda i: (i % 2) == 1)
+ ret_filter = k_all.find(co, filter=lambda i: (i % 2) == 1)
self.assertAlmostEqualVector(ret_regular, ret_filter)
ret_regular = k_evn.find(co)
self.assertEqual(ret_regular[1] % 2, 0)
- ret_filter = k_all.find(co, lambda i: (i % 2) == 0)
+ ret_filter = k_all.find(co, filter=lambda i: (i % 2) == 0)
self.assertAlmostEqualVector(ret_regular, ret_filter)
# filter out all values (search odd tree for even values and the reverse)
co = (0,) * 3
- ret_filter = k_odd.find(co, lambda i: (i % 2) == 0)
+ ret_filter = k_odd.find(co, filter=lambda i: (i % 2) == 0)
self.assertEqual(ret_filter[1], None)
- ret_filter = k_evn.find(co, lambda i: (i % 2) == 1)
+ ret_filter = k_evn.find(co, filter=lambda i: (i % 2) == 1)
self.assertEqual(ret_filter[1], None)
def test_kdtree_invalid_size(self):
diff --git a/tests/python/bl_run_operators_event_simulate.py b/tests/python/bl_run_operators_event_simulate.py
new file mode 100644
index 00000000000..92315d3e853
--- /dev/null
+++ b/tests/python/bl_run_operators_event_simulate.py
@@ -0,0 +1,597 @@
+# ##### 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>
+
+r"""
+Overview
+========
+
+This is a utility to generate events from the command line,
+so reproducible test cases can be written without having to create a custom script each time.
+
+The key differentiating feature for this utility as is that it's able to control modal operators.
+
+Possible use cases for this script include:
+
+- Creating reproducible user interactions for the purpose of benchmarking and profiling.
+
+ Note that cursor-motion actions report the update time between events
+ which can be helpful when measuring optimizations.
+
+- As a convenient way to replay interactive actions that reproduce a bug.
+
+- For writing tests (although some extra functionality may be necessary in this case).
+
+
+Actions
+=======
+
+You will notice most of the functionality is supported using the actions command line argument,
+this is a kind of mini-language to drive Blender.
+
+While the current set of commands is fairly limited more can be added as needed.
+
+To see a list of actions as well as their arguments run:
+
+ ./blender.bin --python tests/python/bl_run_operators_event_simulate.py -- --help
+
+
+Examples
+========
+
+Rotate in edit-mode examples:
+
+ ./blender.bin \
+ --factory-startup \
+ --enable-event-simulate \
+ --python tests/python/bl_run_operators_event_simulate.py \
+ -- \
+ --actions \
+ 'area_maximize(ui_type="VIEW_3D")' \
+ 'operator("object.mode_set", mode="EDIT")' \
+ 'operator("mesh.select_all", action="SELECT")' \
+ 'operator("mesh.subdivide", number_cuts=5)' \
+ 'operator("transform.rotate")' \
+ 'cursor_motion(path="CIRCLE", radius=300, steps=100, repeat=2)'
+
+Sculpt stroke:
+
+ ./blender.bin \
+ --factory-startup \
+ --enable-event-simulate \
+ --python tests/python/bl_run_operators_event_simulate.py \
+ -- \
+ --actions \
+ 'area_maximize(ui_type="VIEW_3D")' \
+ 'event(type="FIVE", value="TAP", ctrl=True)' \
+ 'menu("Visual Geometry to Mesh")' \
+ 'menu("Frame Selected")' \
+ 'menu("Toggle Sculpt Mode")' \
+ 'event(type="WHEELDOWNMOUSE", value="TAP", repeat=2)' \
+ 'event(type="LEFTMOUSE", value="PRESS")' \
+ 'cursor_motion(path="CIRCLE", radius=300, steps=100, repeat=5)' \
+ 'event(type="LEFTMOUSE", value="RELEASE")'
+
+
+Implementation
+==============
+
+While most of the operations listed above can be executed in Python directly,
+either the event loop won't be handled between actions (the case for typical Python script),
+or the context for executing the actions is not properly set (the case for timers).
+
+This utility executes actions as if the user initiated them from a key shortcut.
+"""
+
+
+import os
+import sys
+import argparse
+from argparse import ArgumentTypeError
+
+import bpy
+
+
+# -----------------------------------------------------------------------------
+# Constants
+
+EVENT_TYPES = tuple(bpy.types.Event.bl_rna.properties["type"].enum_items.keys())
+EVENT_VALUES = tuple(bpy.types.Event.bl_rna.properties["value"].enum_items.keys())
+# `TAP` is just convenience for (`PRESS`, `RELEASE`).
+EVENT_VALUES_EXTRA = EVENT_VALUES + ('TAP',)
+
+
+# -----------------------------------------------------------------------------
+# Globals
+
+# Assign a global since this script is not going to be loading new files (which would free the window).
+win = bpy.context.window_manager.windows[0]
+
+
+# -----------------------------------------------------------------------------
+# Utilities
+
+def find_main_area(ui_type=None):
+ """
+ Find the largest area from the current screen.
+ """
+ area_best = None
+ size_best = -1
+ for area in win.screen.areas:
+ if ui_type is not None:
+ if ui_type != area.ui_type:
+ continue
+
+ size = area.width * area.height
+ if size > size_best:
+ size_best = size
+ area_best = area
+ return area_best
+
+
+def gen_events_type_text(text):
+ """
+ Generate events to type in `text`.
+ """
+ for ch in text:
+ kw_extra = {}
+ # The event type in this case is ignored as only the unicode value is used for text input.
+ type = 'SPACE'
+ if ch == '\t':
+ type = 'TAB'
+ elif ch == '\n':
+ type = 'RET'
+ else:
+ kw_extra["unicode"] = ch
+
+ yield dict(type=type, value='PRESS', **kw_extra)
+ kw_extra.pop("unicode", None)
+ yield dict(type=type, value='RELEASE', **kw_extra)
+
+
+# -----------------------------------------------------------------------------
+# Simulate Events
+
+def mouse_location_get():
+ return (
+ run_event_simulate.last_event["x"],
+ run_event_simulate.last_event["y"],
+ )
+
+
+def run_event_simulate(*, event_iter, exit_fn):
+ """
+ Pass events from event_iter into Blender.
+ """
+ last_event = run_event_simulate.last_event
+
+ def event_step():
+ win = bpy.context.window_manager.windows[0]
+
+ val = next(event_step.run_events, Ellipsis)
+ if val is Ellipsis:
+ bpy.app.use_event_simulate = False
+ print("Finished simulation")
+ exit_fn()
+ return None
+
+ # Run event simulation.
+ for attr in ("x", "y"):
+ if attr in val:
+ last_event[attr] = val[attr]
+ else:
+ val[attr] = last_event[attr]
+
+ # Fake event value, since press, release is so common.
+ if val.get("value") == 'TAP':
+ del val["value"]
+ win.event_simulate(**val, value='PRESS')
+ # Needed if new files are loaded.
+ # win = bpy.context.window_manager.windows[0]
+ win.event_simulate(**val, value='RELEASE')
+ else:
+ # print("val", val)
+ win.event_simulate(**val)
+ return 0.0
+
+ event_step.run_events = iter(event_iter)
+
+ bpy.app.timers.register(event_step, first_interval=0.0, persistent=True)
+
+
+run_event_simulate.last_event = dict(
+ x=win.width // 2,
+ y=win.height // 2,
+)
+
+
+# -----------------------------------------------------------------------------
+# Action Implementations
+
+# Static methods from this class are automatically exposed as actions and included in the help text.
+class action_handlers:
+
+ @staticmethod
+ def area_maximize(*, ui_type=None, only_validate=False):
+ """
+ ui_type:
+ Select the area type (typically 'VIEW_3D').
+ Note that this area type needs to exist in the current screen.
+ """
+ if not ((ui_type is None) or (isinstance(ui_type, str))):
+ raise ArgumentTypeError("'type' argument %r not None or a string type")
+
+ if only_validate:
+ return
+
+ area = find_main_area(ui_type=ui_type)
+ if area is None:
+ raise ArgumentTypeError("Area with ui_type=%r not found" % ui_type)
+
+ x = area.x + (area.width // 2)
+ y = area.y + (area.height // 2)
+
+ yield dict(type='MOUSEMOVE', value='NOTHING', x=x, y=y)
+ yield dict(type='SPACE', value='TAP', ctrl=True, alt=True)
+
+ x = win.width // 2
+ y = win.height // 2
+
+ yield dict(type='MOUSEMOVE', value='NOTHING', x=x, y=y)
+
+ @staticmethod
+ def menu(text, *, only_validate=False):
+ """
+ text: Menu item to search for and execute.
+ """
+ if not isinstance(text, str):
+ raise ArgumentTypeError("'text' argument not a string")
+
+ if only_validate:
+ return
+
+ yield dict(type='F3', value='TAP')
+ yield from gen_events_type_text(text)
+ yield dict(type='RET', value='TAP')
+
+ @staticmethod
+ def event(*, value, type, ctrl=False, alt=False, shift=False, repeat=1, only_validate=False):
+ """
+ value: The event, typically key, e.g. 'ESC', 'RET', 'SPACE', 'A'.
+ type: The event type, valid values include: 'PRESS', 'RELEASE', 'TAP'.
+ ctrl: Control modifier.
+ alt: Alt modifier.
+ shift: Shift modifier.
+ """
+ valid_items = EVENT_VALUES_EXTRA
+ if value not in valid_items:
+ raise ArgumentTypeError("'value' argument %r not in %r" % (value, valid_items))
+ valid_items = EVENT_TYPES
+ if type not in valid_items:
+ raise ArgumentTypeError("'type' argument %r not in %r" % (value, valid_items))
+ valid_items = range(1, sys.maxsize)
+ if repeat not in valid_items:
+ raise ArgumentTypeError("'repeat' argument %r not in %r" % (repeat, valid_items))
+ del valid_items
+
+ if only_validate:
+ return
+
+ for _ in range(repeat):
+ yield dict(type=type, ctrl=ctrl, alt=alt, shift=shift, value=value)
+
+ @staticmethod
+ def cursor_motion(*, path, steps, radius=100, repeat=1, only_validate=False):
+ """
+ path: The path type to use in ('CIRCLE').
+ steps: The number of events to generate.
+ radius: The radius in pixels.
+ repeat: Number of times to repeat the cursor rotation.
+ """
+
+ import time
+ from math import sin, cos, pi
+
+ valid_items = range(1, sys.maxsize)
+ if steps not in valid_items:
+ raise ArgumentTypeError("'steps' argument %r not in %r" % (steps, valid_items))
+
+ valid_items = range(1, sys.maxsize)
+ if radius not in valid_items:
+ raise ArgumentTypeError("'radius' argument %r not in %r" % (steps, valid_items))
+
+ valid_items = ('CIRCLE',)
+ if path not in valid_items:
+ raise ArgumentTypeError("'path' argument %r not in %r" % (path, valid_items))
+
+ valid_items = range(1, sys.maxsize)
+ if repeat not in valid_items:
+ raise ArgumentTypeError("'repeat' argument %r not in %r" % (repeat, valid_items))
+ del valid_items
+
+ if only_validate:
+ return
+
+ x_init, y_init = mouse_location_get()
+
+ y_init_ofs = y_init + radius
+
+ yield dict(type='MOUSEMOVE', value='NOTHING', x=x_init, y=y_init_ofs)
+
+ print("\n" "Times for: %s" % os.path.basename(bpy.data.filepath))
+
+ t = time.time()
+ step_total = 0
+
+ if path == 'CIRCLE':
+ for _ in range(repeat):
+ for i in range(1, steps + 1):
+ phi = (i / steps) * 2.0 * pi
+ x_ofs = -radius * sin(phi)
+ y_ofs = +radius * cos(phi)
+ step_total += 1
+ yield dict(
+ type='MOUSEMOVE',
+ value='NOTHING',
+ x=int(x_init + x_ofs),
+ y=int(y_init + y_ofs),
+ )
+
+ delta = time.time() - t
+ delta_step = delta / step_total
+ print(
+ "Average:",
+ ("%.6f FPS" % (1 / delta_step)).rjust(10),
+ )
+
+ yield dict(type='MOUSEMOVE', value='NOTHING', x=x_init, y=y_init)
+
+ @staticmethod
+ def operator(idname, *, only_validate=False, **kw):
+ """
+ idname: The operator identifier (positional argument only).
+ kw: Passed to the operator.
+ """
+
+ # Create a temporary key binding to call the operator.
+ wm = bpy.context.window_manager
+ keyconf = wm.keyconfigs.user
+
+ keymap_id = "Screen"
+ key_to_map = 'F24'
+
+ if only_validate:
+ op_mod, op_submod = idname.partition(".")[0::2]
+ op = getattr(getattr(bpy.ops, op_mod), op_submod)
+ try:
+ # The poll result doesn't matter we only want to know if the operator exists or not.
+ op.poll()
+ except AttributeError:
+ raise ArgumentTypeError("Operator %r does not exist" % (idname))
+
+ keymap = keyconf.keymaps[keymap_id]
+ kmi = keymap.keymap_items.new(idname=idname, type=key_to_map, value='PRESS')
+ kmi.idname = idname
+ props = kmi.properties
+ for key, value in kw.items():
+ if not hasattr(props, key):
+ raise ArgumentTypeError("Operator %r does not have a %r property" % (idname, key))
+
+ try:
+ setattr(props, key, value)
+ except Exception as ex:
+ raise ArgumentTypeError("Operator %r assign %r property with error %s" % (idname, key, str(ex)))
+
+ keymap.keymap_items.remove(kmi)
+ return
+
+ keymap = keyconf.keymaps[keymap_id]
+ kmi = keymap.keymap_items.new(idname=idname, type=key_to_map, value='PRESS')
+ kmi.idname = idname
+ props = kmi.properties
+ for key, value in kw.items():
+ setattr(props, key, value)
+
+ yield dict(type=key_to_map, value='TAP')
+
+ keymap = keyconf.keymaps[keymap_id]
+ kmi = keymap.keymap_items[-1]
+ keymap.keymap_items.remove(kmi)
+
+
+ACTION_DIR = tuple([
+ key for key in sorted(action_handlers.__dict__.keys())
+ if not key.startswith("_")
+])
+
+
+def handle_action(op, args, kwargs, only_validate=False):
+ fn = getattr(action_handlers, op, None)
+ if fn is None:
+ raise ArgumentTypeError("Action %r is not found in %r" % (op, ACTION_DIR))
+ yield from fn(*args, **kwargs, only_validate=only_validate)
+
+
+# -----------------------------------------------------------------------------
+# Argument Parsing
+
+
+class BlenderAction(argparse.Action):
+ """
+ This class is used to extract positional & keyword arguments from
+ a string, validate them, and return the (action, positional_args, keyword_args).
+
+ All of this happens during argument parsing so any errors in the actions
+ show useful error messages instead of failing to execute part way through.
+ """
+
+ @staticmethod
+ def _parse_value(value, index):
+ """
+ Convert:
+ "value(1, 2, a=1, b='', c=None)"
+ To:
+ ("value", (1, 2), {"a": 1, "b": "", "c": None})
+ """
+ split = value.find("(")
+ if split == -1:
+ op = value
+ args = None
+ kwargs = None
+ else:
+ op = value[:split]
+ namespace = {op: lambda *args, **kwargs: (args, kwargs)}
+ expr = value
+ try:
+ args, kwargs = eval(expr, namespace, namespace)
+ except Exception as ex:
+ raise ArgumentTypeError("Unable to parse \"%s\" at index %d, error: %s" % (expr, index, str(ex)))
+
+ # Creating a list is necessary since this is a generator.
+ try:
+ dummy_result = list(handle_action(op, args, kwargs, only_validate=True))
+ except ArgumentTypeError as ex:
+ raise ArgumentTypeError("Invalid 'action' arguments \"%s\" at index %d, %s" % (value, index, str(ex)))
+ # Validation should never yield any events.
+ assert(not dummy_result)
+
+ return (op, args, kwargs)
+
+ def __call__(self, parser, namespace, values, option_string=None):
+ setattr(
+ namespace,
+ self.dest, [
+ self._parse_value(value, index)
+ for index, value in enumerate(values)
+ ],
+ )
+
+
+def argparse_create():
+ import inspect
+ import textwrap
+
+ # When --help or no args are given, print this help
+ parser = argparse.ArgumentParser(
+ description=__doc__,
+ formatter_class=argparse.RawTextHelpFormatter,
+ )
+
+ parser.add_argument(
+ "--keep-open",
+ dest="keep_open",
+ default=False,
+ action="store_true",
+ help=(
+ "Keep the window open instead of exiting once event simulation is complete.\n"
+ "This can be useful to inspect the state of the file once the simulation is complete."
+ ),
+ required=False,
+ )
+
+ # Collect doc-strings from static methods in `actions`.
+ actions_docstring = []
+ for action_key in ACTION_DIR:
+ action = getattr(action_handlers, action_key)
+ args = str(inspect.signature(action))
+ args = "(" + args[1:].removeprefix("*, ")
+ args = args.replace(", *, ", ", ") # Needed in case the are positional arguments.
+ args = args.replace(", only_validate=False", "")
+
+ actions_docstring.append("- %s%s\n" % (action_key, args))
+ docs = textwrap.dedent((action.__doc__ or "").lstrip("\n").rstrip()) + "\n\n"
+
+ actions_docstring.append(textwrap.indent(docs, " "))
+
+ parser.add_argument(
+ "--actions",
+ dest="actions",
+ metavar='ACTIONS', type=str,
+ help=(
+ "\n" "Arguments must use one of the following prefix:\n"
+ "\n" + "".join(actions_docstring)
+ ),
+ nargs='+',
+ required=True,
+ action=BlenderAction,
+ )
+
+ return parser
+
+
+# -----------------------------------------------------------------------------
+# Default Startup
+
+
+def setup_default_preferences(prefs):
+ """
+ Set preferences useful for automation.
+ """
+ prefs.view.show_splash = False
+ prefs.view.smooth_view = 0
+ prefs.view.use_save_prompt = False
+ prefs.view.show_developer_ui = True
+ prefs.filepaths.use_auto_save_temporary_files = False
+
+
+# -----------------------------------------------------------------------------
+# Main Function
+
+
+def main_event_iter(*, action_list):
+ """
+ Yield all events from action handlers.
+ """
+ area = find_main_area()
+
+ x_init = area.x + (area.width // 2)
+ y_init = area.y + (area.height // 2)
+
+ yield dict(type='MOUSEMOVE', value='NOTHING', x=x_init, y=y_init)
+
+ for (op, args, kwargs) in action_list:
+ yield from handle_action(op, args, kwargs)
+
+
+def main():
+ from sys import argv
+ argv = argv[argv.index("--") + 1:] if "--" in argv else []
+
+ try:
+ args = argparse_create().parse_args(argv)
+ except ArgumentTypeError as ex:
+ print(ex)
+ sys.exit(1)
+
+ setup_default_preferences(bpy.context.preferences)
+
+ def exit_fn():
+ if not args.keep_open:
+ sys.exit(0)
+ else:
+ bpy.app.use_event_simulate = False
+
+ run_event_simulate(
+ event_iter=main_event_iter(action_list=args.actions),
+ exit_fn=exit_fn,
+ )
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tests/python/bl_test.py b/tests/python/bl_test.py
index 6315ffbfa9d..110b4238f6c 100644
--- a/tests/python/bl_test.py
+++ b/tests/python/bl_test.py
@@ -32,9 +32,18 @@ def replace_bpy_app_version():
app = bpy.app
app_fake = type(bpy)("bpy.app")
+ app_attr_exclude = {
+ # This causes a noisy warning every time.
+ "binary_path_python",
+ }
+
for attr in dir(app):
- if not attr.startswith("_"):
- setattr(app_fake, attr, getattr(app, attr))
+ if attr.startswith("_"):
+ continue
+ if attr in app_attr_exclude:
+ continue
+
+ setattr(app_fake, attr, getattr(app, attr))
app_fake.version = 0, 0, 0
app_fake.version_string = "0.00 (sub 0)"
diff --git a/tests/python/compositor_render_tests.py b/tests/python/compositor_render_tests.py
new file mode 100644
index 00000000000..057d4a2e6dd
--- /dev/null
+++ b/tests/python/compositor_render_tests.py
@@ -0,0 +1,68 @@
+#!/usr/bin/env python3
+# Apache License, Version 2.0
+
+import argparse
+import os
+import shlex
+import shutil
+import subprocess
+import sys
+
+
+# When run from inside Blender, render and exit.
+try:
+ import bpy
+ inside_blender = True
+except ImportError:
+ inside_blender = False
+
+def get_arguments(filepath, output_filepath):
+ return [
+ "--background",
+ "-noaudio",
+ "--factory-startup",
+ "--enable-autoexec",
+ "--debug-memory",
+ "--debug-exit-on-error",
+ filepath,
+ "-P",
+ os.path.realpath(__file__),
+ "-o", output_filepath,
+ "-F", "PNG",
+ "-f", "1"]
+
+
+def create_argparse():
+ parser = argparse.ArgumentParser()
+ parser.add_argument("-blender", nargs="+")
+ parser.add_argument("-testdir", nargs=1)
+ parser.add_argument("-outdir", nargs=1)
+ parser.add_argument("-idiff", nargs=1)
+ return parser
+
+
+def main():
+ parser = create_argparse()
+ args = parser.parse_args()
+
+ blender = args.blender[0]
+ test_dir = args.testdir[0]
+ idiff = args.idiff[0]
+ output_dir = args.outdir[0]
+
+ from modules import render_report
+ report = render_report.Report("Compositor", output_dir, idiff)
+ report.set_pixelated(True)
+ report.set_reference_dir("compositor_renders")
+
+ # Temporary change to pass OpenImageDenoise test with both 1.3 and 1.4.
+ if os.path.basename(test_dir) == 'filter':
+ report.set_fail_threshold(0.05)
+
+ ok = report.run(test_dir, blender, get_arguments, batch=True)
+
+ sys.exit(not ok)
+
+
+if not inside_blender and __name__ == "__main__":
+ main()
diff --git a/tests/python/operators.py b/tests/python/operators.py
index 461880ec214..c209b01c20c 100644
--- a/tests/python/operators.py
+++ b/tests/python/operators.py
@@ -137,7 +137,6 @@ def main():
MeshTest("CubeEdgeSplit", "testCubeEdgeSplit", "expectedCubeEdgeSplit",
[OperatorSpecEditMode("edge_split", {}, "EDGE", {2, 5, 8, 11, 14, 17, 20, 23})]),
- ### 25
# edge ring select - Cannot be tested. Need user input.
# MeshTest("CubeEdgeRingSelect", "testCubeEdgeRingSelect", "expectedCubeEdgeRingSelect",
# [OperatorSpecEditMode("edgering_select", {}, "EDGE", {5, 20, 25, 26})]),
@@ -146,6 +145,16 @@ def main():
# MeshTest("EmptyMeshEdgeRingSelect", "testEmptyMeshdgeRingSelect", "expectedEmptyMeshEdgeRingSelect",
# [OperatorSpecEditMode("edgering_select", {}, "VERT", {})]),
+ # edges select sharp
+ MeshTest("CubeEdgesSelectSharp", "testCubeEdgeSelectSharp", "expectedCubeEdgeSelectSharp",
+ [OperatorSpecEditMode("edges_select_sharp", {}, "EDGE", {20})]),
+ MeshTest("SphereEdgesSelectSharp", "testSphereEdgesSelectSharp", "expectedSphereEdgeSelectSharp",
+ [OperatorSpecEditMode("edges_select_sharp", {"sharpness": 0.25}, "EDGE", {288})]),
+ MeshTest("HoledSphereEdgesSelectSharp", "testHoledSphereEdgesSelectSharp", "expectedHoledSphereEdgeSelectSharp",
+ [OperatorSpecEditMode("edges_select_sharp", {"sharpness": 0.18}, "VERT", {})]),
+ MeshTest("EmptyMeshEdgesSelectSharp", "testEmptyMeshEdgeSelectSharp", "expectedEmptyMeshEdgeSelectSharp",
+ [OperatorSpecEditMode("edges_select_sharp", {}, "VERT", {})]),
+
# face make planar
MeshTest("MonkeyFaceMakePlanar", "testMonkeyFaceMakePlanar",
"expectedMonkeyFaceMakePlanar",
@@ -187,6 +196,14 @@ def main():
MeshTest("SphereFillHoles", "testSphereFillHoles", "expectedSphereFillHoles",
[OperatorSpecEditMode("fill_holes", {"sides": 9}, "VERT", {i for i in range(481)})]),
+ # face shade smooth (not a real test)
+ MeshTest("CubeShadeSmooth", "testCubeShadeSmooth", "expectedCubeShadeSmooth",
+ [OperatorSpecEditMode("faces_shade_smooth", {}, "VERT", {i for i in range(8)})]),
+
+ # faces shade flat (not a real test)
+ MeshTest("CubeShadeFlat", "testCubeShadeFlat", "expectedCubeShadeFlat",
+ [OperatorSpecEditMode("faces_shade_flat", {}, "FACE", {i for i in range(6)})]),
+
# inset faces
MeshTest("CubeInset",
"testCubeInset", "expectedCubeInset", [OperatorSpecEditMode("inset", {"thickness": 0.2}, "VERT",
@@ -208,6 +225,109 @@ def main():
[OperatorSpecEditMode("inset", {"thickness": 0.4,
"use_relative_offset": True}, "FACE",
{35, 36, 37, 45, 46, 47, 55, 56, 57})]),
+
+ # loop multi select
+ MeshTest("MokeyLoopMultiSelect", "testMonkeyLoopMultiSelect", "expectedMonkeyLoopMultiSelect",
+ [OperatorSpecEditMode("loop_multi_select", {}, "VERT", {355, 359, 73, 301, 302})]),
+ MeshTest("HoledGridLoopMultiSelect", "testGridLoopMultiSelect", "expectedGridLoopMultiSelect",
+ [OperatorSpecEditMode("loop_multi_select", {}, "VERT", {257, 169, 202, 207, 274, 278, 63})]),
+ MeshTest("EmptyMeshLoopMultiSelect", "testEmptyMeshLoopMultiSelect", "expectedEmptyMeshLoopMultiSelect",
+ [OperatorSpecEditMode("loop_multi_select", {}, "VERT", {})]),
+
+ # mark seam
+ MeshTest("CubeMarkSeam", "testCubeMarkSeam", "expectedCubeMarkSeam",
+ [OperatorSpecEditMode("mark_seam", {}, "EDGE", {1})]),
+
+ # select all
+ MeshTest("CircleSelectAll", "testCircleSelectAll", "expectedCircleSelectAll",
+ [OperatorSpecEditMode("select_all", {}, "VERT", {1})]),
+ MeshTest("IsolatedVertsSelectAll", "testIsolatedVertsSelectAll", "expectedIsolatedVertsSelectAll",
+ [OperatorSpecEditMode("select_all", {}, "VERT", {})]),
+ MeshTest("EmptyMeshSelectAll", "testEmptyMeshSelectAll", "expectedEmptyMeshSelectAll",
+ [OperatorSpecEditMode("select_all", {}, "VERT", {})]),
+
+ # select axis - Cannot be tested. Needs active vert selection
+ # MeshTest("MonkeySelectAxisX", "testMonkeySelectAxisX", "expectedMonkeySelectAxisX",
+ # [OperatorSpecEditMode("select_axis", {"axis": "X"}, "VERT", {13})]),
+ # MeshTest("MonkeySelectAxisY", "testMonkeySelectAxisY", "expectedMonkeySelectAxisY",
+ # [OperatorSpecEditMode("select_axis", {"axis": "Y", "sign": "NEG"}, "FACE", {317})]),
+ # MeshTest("MonkeySelectAxisXYZ", "testMonkeySelectAxisXYZ", "expectedMonkeySelectAxisXYZ",
+ # [OperatorSpecEditMode("select_axis", {"axis": "X", "sign": "NEG"}, "FACE", {317}),
+ # OperatorSpecEditMode("select_axis", {"axis": "Y", "sign": "POS"}, "FACE", {}),
+ # OperatorSpecEditMode("select_axis", {"axis": "Z", "sign": "NEG"}, "FACE", {})]),
+
+ # select faces by sides
+ MeshTest("CubeSelectFacesBySide", "testCubeSelectFacesBySide", "expectedCubeSelectFacesBySide",
+ [OperatorSpecEditMode("select_face_by_sides", {"number": 4}, "FACE", {})]),
+ MeshTest("CubeSelectFacesBySideGreater", "testCubeSelectFacesBySideGreater", "expectedCubeSelectFacesBySideGreater",
+ [OperatorSpecEditMode("select_face_by_sides", {"number": 4, "type": "GREATER", "extend": True}, "FACE", {})]),
+ MeshTest("CubeSelectFacesBySideLess", "testCubeSelectFacesBySideLess", "expectedCubeSelectFacesBySideLess",
+ [OperatorSpecEditMode("select_face_by_sides", {"number": 4, "type": "GREATER", "extend": True}, "FACE", {})]),
+
+ # select interior faces
+ MeshTest("CubeSelectInteriorFaces", "testCubeSelectInteriorFaces", "expectedCubeSelectInteriorFaces",
+ [OperatorSpecEditMode("select_face_by_sides", {"number": 4}, "FACE", {})]),
+ MeshTest("HoledCubeSelectInteriorFaces", "testHoledCubeSelectInteriorFaces", "expectedHoledCubeSelectInteriorFaces",
+ [OperatorSpecEditMode("select_face_by_sides", {"number": 4}, "FACE", {})]),
+ MeshTest("EmptyMeshSelectInteriorFaces", "testEmptyMeshSelectInteriorFaces", "expectedEmptyMeshSelectInteriorFaces",
+ [OperatorSpecEditMode("select_face_by_sides", {"number": 4}, "FACE", {})]),
+
+ # select less
+ MeshTest("MonkeySelectLess", "testMonkeySelectLess", "expectedMonkeySelectLess",
+ [OperatorSpecEditMode("select_less", {}, "VERT", {2, 8, 24, 34, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 68,
+ 69, 70, 71, 74, 75, 78, 80, 81, 82, 83, 90, 91, 93, 95, 97, 99,
+ 101, 109, 111, 115, 117, 119, 121, 123, 125, 127, 129, 130, 131,
+ 132, 133, 134, 135, 136, 138, 141, 143, 145, 147, 149, 151, 153,
+ 155, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174,
+ 175, 176, 177, 178, 181, 182, 184, 185, 186, 187, 188, 189, 190,
+ 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204,
+ 206, 207, 208, 210, 216, 217, 218, 219, 220, 221, 222, 229, 230,
+ 231, 233, 235, 237, 239, 241, 243, 245, 247, 249, 251, 253, 255,
+ 257, 259, 263, 267, 269, 271, 275, 277, 289, 291, 293, 295, 309,
+ 310, 311, 312, 316, 317, 318, 319, 320, 323, 325, 327, 329, 331,
+ 341, 347, 349, 350, 351, 354, 356, 359, 361, 363, 365, 367, 369,
+ 375, 379, 381, 382, 385, 386, 387, 388, 389, 390, 391, 392, 393,
+ 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406,
+ 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419,
+ 420, 421, 423, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434,
+ 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447,
+ 448, 449, 450, 451, 452, 454, 455, 456, 457, 458, 459, 460, 461,
+ 462, 463, 464, 471, 473, 474, 475, 476, 477, 478, 479, 480, 481,
+ 482, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 495,
+ 496, 497, 498, 499, 502, 505})]),
+ MeshTest("HoledCubeSelectLess", "testHoledCubeSelectLess", "expectedHoledCubeSelectLess",
+ [OperatorSpecEditMode("select_face_by_sides", {}, "FACE", {})]),
+ MeshTest("EmptyMeshSelectLess", "testEmptyMeshSelectLess", "expectedEmptyMeshSelectLess",
+ [OperatorSpecEditMode("select_face_by_sides", {}, "VERT", {})]),
+
+ # select linked
+ MeshTest("PlanesSelectLinked", "testPlanesSelectLinked", "expectedPlanesSelectedLinked",
+ [OperatorSpecEditMode("select_linked", {}, "VERT", {7})]),
+ MeshTest("CubesSelectLinked", "testCubesSelectLinked", "expectedCubesSelectLinked",
+ [OperatorSpecEditMode("select_linked", {}, "VERT", {11})]),
+ MeshTest("EmptyMeshSelectLinked", "testEmptyMeshSelectLinked", "expectedEmptyMeshSelectLinked",
+ [OperatorSpecEditMode("select_linked", {}, "VERT", {})]),
+
+ # select nth (checkered deselect)
+ MeshTest("CircleSelect2nd", "testCircleSelect2nd", "expectedCircleSelect2nd",
+ [OperatorSpecEditMode("select_nth", {}, "VERT", {i for i in range(32)})]),
+
+ # unsubdivide
+ # normal case
+ MeshTest("CubeFaceUnsubdivide", "testCubeUnsubdivide", "expectedCubeUnsubdivide",
+ [OperatorSpecEditMode("unsubdivide", {}, "FACE", {i for i in range(6)})]),
+
+ # T87259 - test cases
+ MeshTest("CubeEdgeUnsubdivide", "testCubeEdgeUnsubdivide", "expectedCubeEdgeUnsubdivide",
+ [OperatorSpecEditMode("unsubdivide", {}, "EDGE", {i for i in range(6)})]),
+ MeshTest("UVSphereUnsubdivide", "testUVSphereUnsubdivide", "expectedUVSphereUnsubdivide",
+ [OperatorSpecEditMode("unsubdivide", {'iterations': 9}, "FACE", {i for i in range(512)})]),
+
+ # vert connect path
+ # Tip: It works only if there is an already existing face or more than 2 vertices.
+ MeshTest("CubeVertConnectPath", "testCubeVertConnectPath", "expectedCubeVertConnectPath",
+ [OperatorSpecEditMode("vert_connect_path", {}, "VERT", {0, 5})]),
+
]
operators_test = RunTest(tests)