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:
authorJulian Eisel <julian@blender.org>2021-06-05 18:51:46 +0300
committerJulian Eisel <julian@blender.org>2021-06-05 18:51:46 +0300
commit87a821397fa4db7b72ea809ee29c319cf4788a1a (patch)
tree5b14c3657585b7e64ce1a3e14492925e0a79f473
parentd6e9b0ce5d61f64a3879459c9d39a151aabdea81 (diff)
parentedaaa2afddb2132e56f39791e559b084b6df8773 (diff)
Merge branch 'master' into asset-system-filelistasset-system-filelist
-rw-r--r--build_files/build_environment/cmake/embree.cmake6
-rw-r--r--build_files/build_environment/cmake/tbb.cmake25
-rw-r--r--build_files/build_environment/cmake/versions.cmake8
-rwxr-xr-xbuild_files/build_environment/install_deps.sh6
-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/config/blender_release.cmake8
-rw-r--r--build_files/cmake/macros.cmake8
-rw-r--r--build_files/cmake/packaging.cmake4
-rw-r--r--build_files/cmake/platform/platform_unix.cmake16
-rw-r--r--build_files/cmake/platform/platform_win32.cmake8
-rw-r--r--build_files/cmake/platform/platform_win32_bundle_crt.cmake9
-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
-rw-r--r--doc/blender_file_format/BlendFileReader.py4
-rw-r--r--doc/python_api/sphinx_doc_gen.py4
-rw-r--r--extern/audaspace/plugins/ffmpeg/FFMPEGWriter.cpp1
-rw-r--r--intern/cycles/blender/blender_camera.cpp27
-rw-r--r--intern/cycles/blender/blender_session.cpp13
-rw-r--r--intern/cycles/blender/blender_shader.cpp6
-rw-r--r--intern/cycles/blender/blender_sync.cpp10
-rw-r--r--intern/cycles/blender/blender_sync.h5
-rw-r--r--intern/cycles/blender/blender_viewport.cpp55
-rw-r--r--intern/cycles/blender/blender_viewport.h28
-rw-r--r--intern/cycles/device/device_optix.cpp6
-rw-r--r--intern/cycles/kernel/kernel_montecarlo.h117
-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.h3
-rw-r--r--intern/cycles/kernel/svm/svm_types.h1
-rw-r--r--intern/cycles/render/alembic.cpp3
-rw-r--r--intern/cycles/render/alembic_read.cpp3
-rw-r--r--intern/cycles/render/attribute.cpp61
-rw-r--r--intern/cycles/render/attribute.h33
-rw-r--r--intern/cycles/render/geometry.cpp88
-rw-r--r--intern/cycles/render/nodes.cpp4
-rw-r--r--intern/cycles/util/util_task.h4
-rw-r--r--intern/ffmpeg/ffmpeg_compat.h49
-rw-r--r--intern/ghost/CMakeLists.txt6
-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.cpp73
-rw-r--r--intern/ghost/intern/GHOST_WaylandCursorSettings.h130
-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.cpp45
-rw-r--r--intern/ghost/intern/GHOST_WindowWin32.h168
-rw-r--r--intern/mikktspace/README.md4
-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/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
m---------release/scripts/addons0
m---------release/scripts/addons_contrib0
-rw-r--r--release/scripts/modules/bl_previews_utils/bl_previews_render.py14
-rw-r--r--release/scripts/modules/bpy/path.py1
-rw-r--r--release/scripts/modules/rna_manual_reference.py74
-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/keyconfig/Blender.py6
-rw-r--r--release/scripts/presets/keyconfig/keymap_data/blender_default.py39
-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/geometry_nodes.py4
-rw-r--r--release/scripts/startup/bl_operators/userpref.py2
-rw-r--r--release/scripts/startup/bl_operators/uvcalc_lightmap.py2
-rw-r--r--release/scripts/startup/bl_ui/properties_constraint.py1
-rw-r--r--release/scripts/startup/bl_ui/properties_grease_pencil_common.py6
-rw-r--r--release/scripts/startup/bl_ui/properties_paint_common.py4
-rw-r--r--release/scripts/startup/bl_ui/space_node.py4
-rw-r--r--release/scripts/startup/bl_ui/space_sequencer.py15
-rw-r--r--release/scripts/startup/bl_ui/space_toolsystem_toolbar.py24
-rw-r--r--release/scripts/startup/bl_ui/space_userpref.py1
-rw-r--r--release/scripts/startup/bl_ui/space_view3d.py8
-rw-r--r--release/scripts/startup/bl_ui/space_view3d_toolbar.py14
-rw-r--r--release/scripts/startup/nodeitems_builtins.py13
-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/blenfont/intern/blf_font_win32_compat.c6
-rw-r--r--source/blender/blenkernel/BKE_attribute_access.hh15
-rw-r--r--source/blender/blenkernel/BKE_attribute_math.hh35
-rw-r--r--source/blender/blenkernel/BKE_blender_version.h2
-rw-r--r--source/blender/blenkernel/BKE_callbacks.h59
-rw-r--r--source/blender/blenkernel/BKE_editmesh.h3
-rw-r--r--source/blender/blenkernel/BKE_geometry_set.hh18
-rw-r--r--source/blender/blenkernel/BKE_lib_id.h8
-rw-r--r--source/blender/blenkernel/BKE_lib_override.h8
-rw-r--r--source/blender/blenkernel/BKE_lib_remap.h4
-rw-r--r--source/blender/blenkernel/BKE_material.h1
-rw-r--r--source/blender/blenkernel/BKE_node.h16
-rw-r--r--source/blender/blenkernel/BKE_node_ui_storage.hh10
-rw-r--r--source/blender/blenkernel/BKE_spline.hh28
-rw-r--r--source/blender/blenkernel/intern/attribute_access.cc37
-rw-r--r--source/blender/blenkernel/intern/attribute_math.cc15
-rw-r--r--source/blender/blenkernel/intern/collection.c27
-rw-r--r--source/blender/blenkernel/intern/constraint.c22
-rw-r--r--source/blender/blenkernel/intern/customdata_file.c8
-rw-r--r--source/blender/blenkernel/intern/editmesh.c11
-rw-r--r--source/blender/blenkernel/intern/fcurve.c3
-rw-r--r--source/blender/blenkernel/intern/fluid.c6
-rw-r--r--source/blender/blenkernel/intern/geometry_component_curve.cc224
-rw-r--r--source/blender/blenkernel/intern/geometry_component_mesh.cc56
-rw-r--r--source/blender/blenkernel/intern/geometry_set_instances.cc26
-rw-r--r--source/blender/blenkernel/intern/gpencil.c20
-rw-r--r--source/blender/blenkernel/intern/key.c9
-rw-r--r--source/blender/blenkernel/intern/lib_id.c42
-rw-r--r--source/blender/blenkernel/intern/lib_id_test.cc61
-rw-r--r--source/blender/blenkernel/intern/lib_override.c759
-rw-r--r--source/blender/blenkernel/intern/lib_query.c7
-rw-r--r--source/blender/blenkernel/intern/lib_remap.c7
-rw-r--r--source/blender/blenkernel/intern/material.c30
-rw-r--r--source/blender/blenkernel/intern/node.cc5
-rw-r--r--source/blender/blenkernel/intern/node_ui_storage.cc27
-rw-r--r--source/blender/blenkernel/intern/object.c12
-rw-r--r--source/blender/blenkernel/intern/particle_system.c5
-rw-r--r--source/blender/blenkernel/intern/scene.c6
-rw-r--r--source/blender/blenkernel/intern/spline_bezier.cc24
-rw-r--r--source/blender/blenkernel/intern/spline_nurbs.cc10
-rw-r--r--source/blender/blenkernel/intern/spline_poly.cc7
-rw-r--r--source/blender/blenkernel/intern/unit.c5
-rw-r--r--source/blender/blenkernel/intern/writeffmpeg.c196
-rw-r--r--source/blender/blenlib/BLI_color.hh302
-rw-r--r--source/blender/blenlib/BLI_index_mask.hh2
-rw-r--r--source/blender/blenlib/BLI_math_base.h3
-rw-r--r--source/blender/blenlib/BLI_mpq3.hh16
-rw-r--r--source/blender/blenlib/BLI_vector.hh2
-rw-r--r--source/blender/blenlib/CMakeLists.txt2
-rw-r--r--source/blender/blenlib/intern/BLI_color.cc55
-rw-r--r--source/blender/blenlib/intern/fileops.c4
-rw-r--r--source/blender/blenlib/intern/math_base_inline.c15
-rw-r--r--source/blender/blenlib/intern/memory_utils.c2
-rw-r--r--source/blender/blenlib/intern/mesh_boolean.cc223
-rw-r--r--source/blender/blenlib/intern/mesh_intersect.cc91
-rw-r--r--source/blender/blenlib/intern/polyfill_2d_beautify.c2
-rw-r--r--source/blender/blenlib/intern/timecode.c4
-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/blenloader/intern/readfile.c7
-rw-r--r--source/blender/blenloader/intern/versioning_250.c2
-rw-r--r--source/blender/blenloader/intern/versioning_300.c124
-rw-r--r--source/blender/bmesh/CMakeLists.txt12
-rw-r--r--source/blender/bmesh/bmesh.h2
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh.c153
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh.h3
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh_partial_update.c274
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh_partial_update.h69
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh_tessellate.c405
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh_tessellate.h30
-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/compositor/CMakeLists.txt12
-rw-r--r--source/blender/compositor/COM_defines.h10
-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.cc23
-rw-r--r--source/blender/compositor/intern/COM_CompositorContext.cc18
-rw-r--r--source/blender/compositor/intern/COM_CompositorContext.h5
-rw-r--r--source/blender/compositor/intern/COM_Debug.cc37
-rw-r--r--source/blender/compositor/intern/COM_Debug.h2
-rw-r--r--source/blender/compositor/intern/COM_Enums.h15
-rw-r--r--source/blender/compositor/intern/COM_ExecutionGroup.cc1
-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.cc143
-rw-r--r--source/blender/compositor/intern/COM_ExecutionSystem.h25
-rw-r--r--source/blender/compositor/intern/COM_FullFrameExecutionModel.cc327
-rw-r--r--source/blender/compositor/intern/COM_FullFrameExecutionModel.h89
-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_NodeOperation.cc176
-rw-r--r--source/blender/compositor/intern/COM_NodeOperation.h66
-rw-r--r--source/blender/compositor/intern/COM_NodeOperationBuilder.cc12
-rw-r--r--source/blender/compositor/intern/COM_SharedOperationBuffers.cc128
-rw-r--r--source/blender/compositor/intern/COM_SharedOperationBuffers.h70
-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.h13
-rw-r--r--source/blender/compositor/intern/COM_WorkScheduler.cc13
-rw-r--r--source/blender/compositor/intern/COM_WorkScheduler.h2
-rw-r--r--source/blender/compositor/nodes/COM_CryptomatteNode.cc14
-rw-r--r--source/blender/compositor/nodes/COM_TranslateNode.cc5
-rw-r--r--source/blender/compositor/operations/COM_ConvertOperation.cc35
-rw-r--r--source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc68
-rw-r--r--source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h58
-rw-r--r--source/blender/compositor/operations/COM_PlaneTrackOperation.cc53
-rw-r--r--source/blender/compositor/operations/COM_PlaneTrackOperation.h5
-rw-r--r--source/blender/compositor/operations/COM_TextureOperation.cc17
-rw-r--r--source/blender/compositor/operations/COM_TextureOperation.h2
-rw-r--r--source/blender/depsgraph/DEG_depsgraph_build.h2
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes.cc4
-rw-r--r--source/blender/depsgraph/intern/depsgraph_build.cc6
-rw-r--r--source/blender/depsgraph/intern/depsgraph_query_iter.cc23
-rw-r--r--source/blender/draw/CMakeLists.txt7
-rw-r--r--source/blender/draw/engines/eevee/eevee_lights.c6
-rw-r--r--source/blender/draw/engines/eevee/eevee_renderpasses.c2
-rw-r--r--source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl65
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_cache_utils.c8
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_engine.h4
-rw-r--r--source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl4
-rw-r--r--source/blender/draw/engines/overlay/overlay_edit_mesh.c4
-rw-r--r--source/blender/draw/engines/overlay/overlay_engine.c8
-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_private.h12
-rw-r--r--source/blender/draw/intern/DRW_render.h7
-rw-r--r--source/blender/draw/intern/draw_cache_extract.h43
-rw-r--r--source/blender/draw/intern/draw_cache_extract_mesh.cc1004
-rw-r--r--source/blender/draw/intern/draw_cache_extract_mesh_extractors.c (renamed from source/blender/draw/intern/draw_cache_extract_mesh.c)3296
-rw-r--r--source/blender/draw/intern/draw_cache_extract_mesh_private.h514
-rw-r--r--source/blender/draw/intern/draw_cache_extract_mesh_render_data.c371
-rw-r--r--source/blender/draw/intern/draw_cache_impl.h1
-rw-r--r--source/blender/draw/intern/draw_cache_impl_curve.cc171
-rw-r--r--source/blender/draw/intern/draw_cache_impl_displist.c4
-rw-r--r--source/blender/draw/intern/draw_cache_impl_gpencil.c2
-rw-r--r--source/blender/draw/intern/draw_cache_impl_hair.c3
-rw-r--r--source/blender/draw/intern/draw_cache_impl_mesh.c59
-rw-r--r--source/blender/draw/intern/draw_cache_inline.h4
-rw-r--r--source/blender/draw/intern/draw_hair.c200
-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_shader.c1
-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.glsl2
-rw-r--r--source/blender/editors/curve/editcurve_paint.c2
-rw-r--r--source/blender/editors/geometry/geometry_attributes.c10
-rw-r--r--source/blender/editors/gpencil/CMakeLists.txt1
-rw-r--r--source/blender/editors/gpencil/gpencil_bake_animation.c448
-rw-r--r--source/blender/editors/gpencil/gpencil_edit.c178
-rw-r--r--source/blender/editors/gpencil/gpencil_intern.h5
-rw-r--r--source/blender/editors/gpencil/gpencil_mesh.c26
-rw-r--r--source/blender/editors/gpencil/gpencil_ops.c2
-rw-r--r--source/blender/editors/include/ED_uvedit.h1
-rw-r--r--source/blender/editors/include/ED_view3d.h17
-rw-r--r--source/blender/editors/interface/interface.c10
-rw-r--r--source/blender/editors/interface/interface_eyedropper.c4
-rw-r--r--source/blender/editors/interface/interface_eyedropper_color.c100
-rw-r--r--source/blender/editors/interface/interface_handlers.c65
-rw-r--r--source/blender/editors/interface/interface_templates.c6
-rw-r--r--source/blender/editors/mesh/editmesh_knife.c6
-rw-r--r--source/blender/editors/mesh/editmesh_undo.c102
-rw-r--r--source/blender/editors/object/object_add.c23
-rw-r--r--source/blender/editors/object/object_modes.c13
-rw-r--r--source/blender/editors/object/object_relations.c6
-rw-r--r--source/blender/editors/object/object_remesh.c4
-rw-r--r--source/blender/editors/object/object_transform.c4
-rw-r--r--source/blender/editors/object/object_vgroup.c4
-rw-r--r--source/blender/editors/physics/particle_edit.c2
-rw-r--r--source/blender/editors/sculpt_paint/paint_cursor.c2
-rw-r--r--source/blender/editors/sculpt_paint/paint_mask.c5
-rw-r--r--source/blender/editors/sculpt_paint/paint_stroke.c6
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c4
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_intern.h2
-rw-r--r--source/blender/editors/space_clip/clip_buttons.c6
-rw-r--r--source/blender/editors/space_file/file_ops.c13
-rw-r--r--source/blender/editors/space_image/image_buttons.c5
-rw-r--r--source/blender/editors/space_info/info_stats.c122
-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)950
-rw-r--r--source/blender/editors/space_node/node_add.cc (renamed from source/blender/editors/space_node/node_add.c)68
-rw-r--r--source/blender/editors/space_node/node_edit.cc (renamed from source/blender/editors/space_node/node_edit.c)253
-rw-r--r--source/blender/editors/space_node/node_group.cc (renamed from source/blender/editors/space_node/node_group.c)68
-rw-r--r--source/blender/editors/space_node/node_relationships.cc (renamed from source/blender/editors/space_node/node_relationships.c)329
-rw-r--r--source/blender/editors/space_node/node_select.cc (renamed from source/blender/editors/space_node/node_select.c)151
-rw-r--r--source/blender/editors/space_node/node_templates.cc (renamed from source/blender/editors/space_node/node_templates.c)111
-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_outliner/outliner_tools.c6
-rw-r--r--source/blender/editors/space_outliner/outliner_tree.c2
-rw-r--r--source/blender/editors/space_sequencer/sequencer_add.c5
-rw-r--r--source/blender/editors/space_sequencer/sequencer_buttons.c2
-rw-r--r--source/blender/editors/space_sequencer/sequencer_edit.c22
-rw-r--r--source/blender/editors/space_sequencer/sequencer_select.c5
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh2
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc2
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_layout.cc2
-rw-r--r--source/blender/editors/space_view3d/view3d_draw.c3
-rw-r--r--source/blender/editors/space_view3d/view3d_edit.c7
-rw-r--r--source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c4
-rw-r--r--source/blender/editors/space_view3d/view3d_project.c15
-rw-r--r--source/blender/editors/space_view3d/view3d_utils.c20
-rw-r--r--source/blender/editors/transform/transform.c156
-rw-r--r--source/blender/editors/transform/transform.h5
-rw-r--r--source/blender/editors/transform/transform_constraints.c7
-rw-r--r--source/blender/editors/transform/transform_convert_mesh.c252
-rw-r--r--source/blender/editors/transform/transform_mode.c100
-rw-r--r--source/blender/editors/transform/transform_mode.h4
-rw-r--r--source/blender/editors/transform/transform_mode_edge_rotate_normal.c2
-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_resize.c4
-rw-r--r--source/blender/editors/transform/transform_mode_rotate.c2
-rw-r--r--source/blender/editors/transform/transform_mode_shrink_fatten.c6
-rw-r--r--source/blender/editors/transform/transform_mode_skin_resize.c2
-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/uvedit/uvedit_ops.c4
-rw-r--r--source/blender/functions/intern/cpp_types.cc4
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c2
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c6
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h108
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c588
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c1159
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h39
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_util.c6
-rw-r--r--source/blender/gpu/CMakeLists.txt5
-rw-r--r--source/blender/gpu/GPU_capabilities.h4
-rw-r--r--source/blender/gpu/GPU_compute.h38
-rw-r--r--source/blender/gpu/GPU_index_buffer.h12
-rw-r--r--source/blender/gpu/GPU_matrix.h36
-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_vertex_buffer.h10
-rw-r--r--source/blender/gpu/intern/gpu_backend.hh1
-rw-r--r--source/blender/gpu/intern/gpu_capabilities.cc20
-rw-r--r--source/blender/gpu/intern/gpu_capabilities_private.hh4
-rw-r--r--source/blender/gpu/intern/gpu_compute.cc41
-rw-r--r--source/blender/gpu/intern/gpu_index_buffer.cc42
-rw-r--r--source/blender/gpu/intern/gpu_index_buffer_private.hh12
-rw-r--r--source/blender/gpu/intern/gpu_matrix.cc47
-rw-r--r--source/blender/gpu/intern/gpu_shader.cc59
-rw-r--r--source/blender/gpu/intern/gpu_shader_interface.cc9
-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_vertex_buffer.cc15
-rw-r--r--source/blender/gpu/intern/gpu_vertex_buffer_private.hh3
-rw-r--r--source/blender/gpu/opengl/gl_backend.cc10
-rw-r--r--source/blender/gpu/opengl/gl_backend.hh7
-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_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.cc2
-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/material/gpu_shader_material_vector_math.glsl6
-rw-r--r--source/blender/gpu/tests/gpu_shader_test.cc301
-rw-r--r--source/blender/imbuf/IMB_imbuf.h2
-rw-r--r--source/blender/imbuf/intern/IMB_anim.h11
-rw-r--r--source/blender/imbuf/intern/anim_movie.c362
-rw-r--r--source/blender/imbuf/intern/indexer.c57
-rw-r--r--source/blender/imbuf/intern/jpeg.c3
-rw-r--r--source/blender/io/gpencil/intern/gpencil_io_base.cc10
-rw-r--r--source/blender/io/gpencil/intern/gpencil_io_base.hh2
-rw-r--r--source/blender/io/gpencil/intern/gpencil_io_capi.cc10
-rw-r--r--source/blender/makesdna/DNA_ID.h2
-rw-r--r--source/blender/makesdna/DNA_constraint_types.h2
-rw-r--r--source/blender/makesdna/DNA_curve_types.h7
-rw-r--r--source/blender/makesdna/DNA_gpencil_types.h2
-rw-r--r--source/blender/makesdna/DNA_node_types.h10
-rw-r--r--source/blender/makesdna/DNA_object_types.h3
-rw-r--r--source/blender/makesdna/DNA_sequence_types.h3
-rw-r--r--source/blender/makesdna/DNA_userdef_types.h3
-rw-r--r--source/blender/makesdna/DNA_view3d_defaults.h1
-rw-r--r--source/blender/makesdna/DNA_view3d_types.h1
-rw-r--r--source/blender/makesrna/intern/rna_ID.c65
-rw-r--r--source/blender/makesrna/intern/rna_access.c16
-rw-r--r--source/blender/makesrna/intern/rna_color.c2
-rw-r--r--source/blender/makesrna/intern/rna_constraint.c6
-rw-r--r--source/blender/makesrna/intern/rna_curve.c4
-rw-r--r--source/blender/makesrna/intern/rna_gpencil.c8
-rw-r--r--source/blender/makesrna/intern/rna_mesh.c4
-rw-r--r--source/blender/makesrna/intern/rna_nodetree.c31
-rw-r--r--source/blender/makesrna/intern/rna_sequencer.c13
-rw-r--r--source/blender/makesrna/intern/rna_space.c11
-rw-r--r--source/blender/makesrna/intern/rna_ui.c2
-rw-r--r--source/blender/makesrna/intern/rna_userdef.c8
-rw-r--r--source/blender/modifiers/CMakeLists.txt6
-rw-r--r--source/blender/modifiers/intern/MOD_boolean.cc5
-rw-r--r--source/blender/modifiers/intern/MOD_mask.cc37
-rw-r--r--source/blender/modifiers/intern/MOD_nodes.cc93
-rw-r--r--source/blender/modifiers/intern/MOD_nodes_evaluator.cc35
-rw-r--r--source/blender/modifiers/intern/MOD_skin.c63
-rw-r--r--source/blender/modifiers/intern/MOD_surface.c16
-rw-r--r--source/blender/nodes/CMakeLists.txt5
-rw-r--r--source/blender/nodes/NOD_geometry.h3
-rw-r--r--source/blender/nodes/NOD_geometry_exec.hh6
-rw-r--r--source/blender/nodes/NOD_math_functions.hh2
-rw-r--r--source/blender/nodes/NOD_static_types.h3
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc124
-rw-r--r--source/blender/nodes/geometry/node_geometry_util.hh8
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc16
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc6
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc28
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc7
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc16
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc7
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc9
-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.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc677
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc2
-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.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc316
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_separate.cc16
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_switch.cc48
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_transform.cc5
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc2
-rw-r--r--source/blender/nodes/intern/math_functions.cc2
-rw-r--r--source/blender/nodes/intern/node_geometry_exec.cc5
-rw-r--r--source/blender/nodes/intern/node_socket.cc12
-rw-r--r--source/blender/nodes/intern/node_tree_ref.cc6
-rw-r--r--source/blender/nodes/intern/type_conversions.cc50
-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_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_valToRgb.cc8
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_vector_math.cc13
-rw-r--r--source/blender/python/bmesh/bmesh_py_types.c7
-rw-r--r--source/blender/python/intern/bpy_app.c5
-rw-r--r--source/blender/python/intern/bpy_app_handlers.c3
-rw-r--r--source/blender/python/mathutils/mathutils_Matrix.c2
-rw-r--r--source/blender/python/mathutils/mathutils_bvhtree.c5
-rw-r--r--source/blender/python/rna_dump.py2
-rw-r--r--source/blender/sequencer/SEQ_sequencer.h3
-rw-r--r--source/blender/sequencer/SEQ_time.h1
-rw-r--r--source/blender/sequencer/SEQ_utils.h1
-rw-r--r--source/blender/sequencer/intern/image_cache.c4
-rw-r--r--source/blender/sequencer/intern/render.c29
-rw-r--r--source/blender/sequencer/intern/sequencer.c44
-rw-r--r--source/blender/sequencer/intern/strip_add.c16
-rw-r--r--source/blender/sequencer/intern/strip_relations.c4
-rw-r--r--source/blender/sequencer/intern/strip_time.c16
-rw-r--r--source/blender/sequencer/intern/utils.c3
-rw-r--r--source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c4
-rw-r--r--source/blender/windowmanager/intern/wm_event_query.c12
-rw-r--r--source/blender/windowmanager/intern/wm_init_exit.c9
-rw-r--r--source/creator/CMakeLists.txt27
-rw-r--r--source/creator/blender_launcher_win32.c92
-rw-r--r--source/creator/creator_args.c3
m---------source/tools0
-rw-r--r--tests/python/bl_run_operators_event_simulate.py577
608 files changed, 17367 insertions, 12310 deletions
diff --git a/build_files/build_environment/cmake/embree.cmake b/build_files/build_environment/cmake/embree.cmake
index cd693d766dc..b1d5dff91a0 100644
--- a/build_files/build_environment/cmake/embree.cmake
+++ b/build_files/build_environment/cmake/embree.cmake
@@ -43,6 +43,12 @@ 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()
diff --git a/build_files/build_environment/cmake/tbb.cmake b/build_files/build_environment/cmake/tbb.cmake
index 44307cd2afb..005616a9f8d 100644
--- a/build_files/build_environment/cmake/tbb.cmake
+++ b/build_files/build_environment/cmake/tbb.cmake
@@ -22,6 +22,7 @@ if(WIN32)
-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)
@@ -55,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
)
@@ -76,11 +77,11 @@ 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_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 83e438614b6..4500245d42a 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)
@@ -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)
diff --git a/build_files/build_environment/install_deps.sh b/build_files/build_environment/install_deps.sh
index 18922799d08..71ddeaa4576 100755
--- a/build_files/build_environment/install_deps.sh
+++ b/build_files/build_environment/install_deps.sh
@@ -563,9 +563,9 @@ OIDN_SKIP=false
ISPC_VERSION="1.14.1"
-FFMPEG_VERSION="4.2.3"
-FFMPEG_VERSION_SHORT="4.2"
-FFMPEG_VERSION_MIN="3.0"
+FFMPEG_VERSION="4.4"
+FFMPEG_VERSION_SHORT="4.4"
+FFMPEG_VERSION_MIN="4.4"
FFMPEG_VERSION_MAX="5.0"
FFMPEG_FORCE_BUILD=false
FFMPEG_FORCE_REBUILD=false
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 7e9858d9268..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.%d" % (version_numbers[0], version_numbers[1])
- self.version = "%d.%d.%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/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/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_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 b6ec016aa95..fe5b179d6d1 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,8 +144,8 @@ 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")
@@ -675,7 +675,7 @@ 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)
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/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..47318b2f7dc
--- /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/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/python_api/sphinx_doc_gen.py b/doc/python_api/sphinx_doc_gen.py
index 032f8a86bd5..4be27e0f0e8 100644
--- a/doc/python_api/sphinx_doc_gen.py
+++ b/doc/python_api/sphinx_doc_gen.py
@@ -954,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__))
@@ -1246,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
])
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/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_session.cpp b/intern/cycles/blender/blender_session.cpp
index 89854f6a0e5..29de886e4ff 100644
--- a/intern/cycles/blender/blender_session.cpp
+++ b/intern/cycles/blender/blender_session.cpp
@@ -155,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);
@@ -242,8 +242,7 @@ void BlenderSession::reset_session(BL::BlendData &b_data, BL::Depsgraph &b_depsg
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,
@@ -486,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(
@@ -810,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;
@@ -889,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) {
@@ -907,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 736c10e8881..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);
diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/blender_sync.cpp
index 9d0f9f29f94..82b3abd4432 100644
--- a/intern/cycles/blender/blender_sync.cpp
+++ b/intern/cycles/blender/blender_sync.cpp
@@ -224,9 +224,13 @@ void BlenderSync::sync_recalc(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d
if (b_v3d) {
BlenderViewportParameters new_viewport_parameters(b_v3d);
- if (viewport_parameters.modified(new_viewport_parameters)) {
+
+ if (viewport_parameters.shader_modified(new_viewport_parameters)) {
world_recalc = true;
+ has_updates_ = true;
}
+
+ has_updates_ |= viewport_parameters.modified(new_viewport_parameters);
}
}
@@ -246,7 +250,7 @@ void BlenderSync::sync_data(BL::RenderSettings &b_render,
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);
@@ -441,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();
diff --git a/intern/cycles/blender/blender_sync.h b/intern/cycles/blender/blender_sync.h
index 15a10f2b46b..1c98e529190 100644
--- a/intern/cycles/blender/blender_sync.h
+++ b/intern/cycles/blender/blender_sync.h
@@ -73,7 +73,7 @@ class BlenderSync {
int width,
int height,
void **python_thread_state);
- void sync_view_layer(BL::SpaceView3D &b_v3d, BL::ViewLayer &b_view_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,
@@ -104,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,
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/device/device_optix.cpp b/intern/cycles/device/device_optix.cpp
index 01de0724cb2..b008dfa376f 100644
--- a/intern/cycles/device/device_optix.cpp
+++ b/intern/cycles/device/device_optix.cpp
@@ -726,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
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/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 19fb1da5a1f..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;
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/alembic.cpp b/intern/cycles/render/alembic.cpp
index cf345ee075d..dcb456dc1ce 100644
--- a/intern/cycles/render/alembic.cpp
+++ b/intern/cycles/render/alembic.cpp
@@ -654,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;
}
diff --git a/intern/cycles/render/alembic_read.cpp b/intern/cycles/render/alembic_read.cpp
index 4d09c40b07b..33ce3502879 100644
--- a/intern/cycles/render/alembic_read.cpp
+++ b/intern/cycles/render/alembic_read.cpp
@@ -606,7 +606,8 @@ void read_geometry_data(AlembicProcedural *proc,
template<typename T> struct value_type_converter {
using cycles_type = float;
- static constexpr TypeDesc type_desc = TypeFloat;
+ /* 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)
diff --git a/intern/cycles/render/attribute.cpp b/intern/cycles/render/attribute.cpp
index d6a638fd4cd..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,15 +695,13 @@ 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;
}
}
@@ -699,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/geometry.cpp b/intern/cycles/render/geometry.cpp
index 16fc36231b4..ce76658acb6 100644
--- a/intern/cycles/render/geometry.cpp
+++ b/intern/cycles/render/geometry.cpp
@@ -830,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;
@@ -852,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,
@@ -875,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,
@@ -906,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,
@@ -941,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;
@@ -1431,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) {
@@ -1471,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()) {
@@ -2039,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/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/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/ffmpeg/ffmpeg_compat.h b/intern/ffmpeg/ffmpeg_compat.h
index 54a004d53e2..0c22cf82688 100644
--- a/intern/ffmpeg/ffmpeg_compat.h
+++ b/intern/ffmpeg/ffmpeg_compat.h
@@ -43,6 +43,55 @@
# define FFMPEG_INLINE static inline
#endif
+#if (LIBAVFORMAT_VERSION_MAJOR < 58) || \
+ ((LIBAVFORMAT_VERSION_MAJOR == 58) && (LIBAVFORMAT_VERSION_MINOR < 76))
+# define FFMPEG_USE_DURATION_WORKAROUND 1
+
+/* 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
+void my_guess_pkt_duration(AVFormatContext *s, AVStream *st, AVPacket *pkt)
+{
+ 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;
+ }
+
+ if (pkt->duration) {
+ return;
+ }
+
+ 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;
+ }
+}
+#endif
+
FFMPEG_INLINE
void my_update_cur_dts(AVFormatContext *s, AVStream *ref_st, int64_t timestamp)
{
diff --git a/intern/ghost/CMakeLists.txt b/intern/ghost/CMakeLists.txt
index f90e8a973bf..1b5cdb3cda0 100644
--- a/intern/ghost/CMakeLists.txt
+++ b/intern/ghost/CMakeLists.txt
@@ -282,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
@@ -321,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
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 38dea9c1142..fc3cb81e26a 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();
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_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 bf142239606..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,19 +118,21 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
monitor.dwFlags = 0;
GetMonitorInfo(MonitorFromRect(&win_rect, MONITOR_DEFAULTTONEAREST), &monitor);
- /* Constrain size to fit within this monitor. */
+ /* Constrain requested size and position to fit within this monitor. */
width = min(monitor.rcWork.right - monitor.rcWork.left, win_rect.right - win_rect.left);
height = min(monitor.rcWork.bottom - monitor.rcWork.top, win_rect.bottom - win_rect.top);
-
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 our requested values to allow for caption, borders, shadows, etc.
- Windows API Note: You cannot specify WS_OVERLAPPED when calling. */
+ /* 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
@@ -151,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.
@@ -230,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) {
@@ -324,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) {
@@ -948,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 b004f7e7b19..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;
@@ -576,9 +411,6 @@ class GHOST_WindowWin32 : public GHOST_Window {
/** `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/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/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 5ab29b1331d2103dae634b987f121c4599459d7
+Subproject 2cef4877edc40875978c4e95322bb5193f5815b
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/scripts/addons b/release/scripts/addons
-Subproject 4fcdbfe7c20edfc1204c0aa46c98ea25354abcd
+Subproject 27fe7f3a4f964b53af436c4da4ddea337eff0c7
diff --git a/release/scripts/addons_contrib b/release/scripts/addons_contrib
-Subproject 7d78c8a63f2f4b146f9327ddc0d567a5921b94e
+Subproject 5a82baad9f986722104280e8354a4427d8e9eab
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/bpy/path.py b/release/scripts/modules/bpy/path.py
index e9e9671cc35..1de4542e69e 100644
--- a/release/scripts/modules/bpy/path.py
+++ b/release/scripts/modules/bpy/path.py
@@ -198,6 +198,7 @@ def _clean_utf8(name):
_display_name_literals = {
":": "_colon_",
"+": "_plus_",
+ "/": "_slash_",
}
diff --git a/release/scripts/modules/rna_manual_reference.py b/release/scripts/modules/rna_manual_reference.py
index 7e2d09efcd4..552f7eeb4f3 100644
--- a/release/scripts/modules/rna_manual_reference.py
+++ b/release/scripts/modules/rna_manual_reference.py
@@ -58,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"),
@@ -66,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"),
@@ -117,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"),
@@ -164,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"),
@@ -192,12 +200,14 @@ 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"),
@@ -291,6 +301,7 @@ url_manual_mapping = (
("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"),
@@ -410,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"),
@@ -421,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"),
@@ -475,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"),
@@ -513,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"),
@@ -575,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"),
@@ -660,7 +676,9 @@ 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"),
@@ -718,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"),
@@ -742,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"),
@@ -752,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"),
@@ -766,6 +786,7 @@ url_manual_mapping = (
("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"),
@@ -797,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"),
@@ -868,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"),
@@ -905,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"),
@@ -979,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"),
@@ -987,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"),
@@ -997,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"),
@@ -1011,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"),
@@ -1046,6 +1075,7 @@ 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.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"),
@@ -1106,6 +1136,7 @@ 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"),
@@ -1118,6 +1149,7 @@ url_manual_mapping = (
("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"),
@@ -1126,6 +1158,7 @@ url_manual_mapping = (
("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"),
@@ -1159,6 +1192,7 @@ url_manual_mapping = (
("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"),
@@ -1166,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"),
@@ -1274,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"),
@@ -1281,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"),
@@ -1297,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"),
@@ -1356,6 +1396,7 @@ url_manual_mapping = (
("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"),
@@ -1369,6 +1410,7 @@ url_manual_mapping = (
("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"),
@@ -1383,6 +1425,10 @@ 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"),
@@ -1403,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"),
@@ -1642,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"),
@@ -1659,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"),
@@ -1715,6 +1768,7 @@ 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"),
@@ -1794,6 +1848,8 @@ url_manual_mapping = (
("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"),
@@ -1815,7 +1871,6 @@ 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"),
@@ -1855,6 +1910,8 @@ url_manual_mapping = (
("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"),
@@ -1907,6 +1964,10 @@ url_manual_mapping = (
("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"),
@@ -1921,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"),
@@ -1935,6 +1998,7 @@ 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"),
@@ -1954,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"),
@@ -1971,6 +2036,10 @@ url_manual_mapping = (
("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"),
@@ -2002,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"),
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/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 6a33bc60cba..27abaa61339 100644
--- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py
+++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
@@ -1080,12 +1080,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),
@@ -1329,6 +1324,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
@@ -2525,6 +2546,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:
@@ -2565,7 +2587,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),
@@ -2573,7 +2594,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),
@@ -4486,8 +4507,6 @@ def km_sculpt(params):
)
items.extend([
- # Transfer Sculpt Mode (release to avoid conflict with grease pencil drawing).
- ("object.transfer_mode", {"type": 'D', "value": 'RELEASE'}, None),
# Brush strokes
("sculpt.brush_stroke", {"type": 'LEFTMOUSE', "value": 'PRESS'},
{"properties": [("mode", 'NORMAL')]}),
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/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/userpref.py b/release/scripts/startup/bl_operators/userpref.py
index 3969386bad7..dd84dfa2df8 100644
--- a/release/scripts/startup/bl_operators/userpref.py
+++ b/release/scripts/startup/bl_operators/userpref.py
@@ -153,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()
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_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_grease_pencil_common.py b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py
index 55a49878b71..2e89ddcb1d4 100644
--- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py
+++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py
@@ -407,7 +407,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):
@@ -857,6 +857,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:
diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py
index f3462dfb35d..4bfd2fd32b0 100644
--- a/release/scripts/startup/bl_ui/properties_paint_common.py
+++ b/release/scripts/startup/bl_ui/properties_paint_common.py
@@ -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/space_node.py b/release/scripts/startup/bl_ui/space_node.py
index 89ce742b81e..1208ca0a64a 100644
--- a/release/scripts/startup/bl_ui/space_node.py
+++ b/release/scripts/startup/bl_ui/space_node.py
@@ -660,8 +660,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_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py
index b24b6e84939..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):
@@ -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_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
index 1e52142c85c..ce1c401b14b 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):
diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py
index de78b88c0f6..d85fe16d654 100644
--- a/release/scripts/startup/bl_ui/space_userpref.py
+++ b/release/scripts/startup/bl_ui/space_userpref.py
@@ -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 18c6564b7d4..df520b38eb0 100644
--- a/release/scripts/startup/bl_ui/space_view3d.py
+++ b/release/scripts/startup/bl_ui/space_view3d.py
@@ -2314,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):
@@ -5035,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")
@@ -6181,6 +6186,9 @@ class VIEW3D_PT_overlay_geometry(Panel):
sub.active = overlay.show_fade_inactive
sub.prop(overlay, "fade_inactive_alpha", text="Fade Inactive Geometry")
+ row = col.row(align=True)
+ row.prop(overlay, "show_mode_transfer", text="Flash on Mode Transfer")
+
col = layout.column(align=True)
col.active = display_all
diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
index 08d581bfa24..604509b99f9 100644
--- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
@@ -1456,7 +1456,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)
@@ -1465,7 +1470,12 @@ 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")
diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py
index bae2c14e3d9..5e245d81de4 100644
--- a/release/scripts/startup/nodeitems_builtins.py
+++ b/release/scripts/startup/nodeitems_builtins.py
@@ -472,14 +472,6 @@ 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=[
@@ -503,6 +495,7 @@ geometry_node_categories = [
NodeItem("GeometryNodeAttributeTransfer"),
]),
GeometryNodeCategory("GEO_COLOR", "Color", items=[
+ NodeItem("ShaderNodeRGBCurve"),
NodeItem("ShaderNodeValToRGB"),
NodeItem("ShaderNodeSeparateRGB"),
NodeItem("ShaderNodeCombineRGB"),
@@ -510,9 +503,12 @@ geometry_node_categories = [
GeometryNodeCategory("GEO_CURVE", "Curve", items=[
NodeItem("GeometryNodeCurveToMesh"),
NodeItem("GeometryNodeCurveResample"),
+ NodeItem("GeometryNodeMeshToCurve"),
+ NodeItem("GeometryNodeCurveLength"),
]),
GeometryNodeCategory("GEO_GEOMETRY", "Geometry", items=[
NodeItem("GeometryNodeBoundBox"),
+ NodeItem("GeometryNodeDeleteGeometry"),
NodeItem("GeometryNodeTransform"),
NodeItem("GeometryNodeJoinGeometry"),
]),
@@ -565,6 +561,7 @@ geometry_node_categories = [
NodeItem("GeometryNodeSwitch"),
]),
GeometryNodeCategory("GEO_VECTOR", "Vector", items=[
+ NodeItem("ShaderNodeVectorCurve"),
NodeItem("ShaderNodeSeparateXYZ"),
NodeItem("ShaderNodeCombineXYZ"),
NodeItem("ShaderNodeVectorMath"),
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/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/blenkernel/BKE_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh
index c3bc4d3ca4a..381a03d29d7 100644
--- a/source/blender/blenkernel/BKE_attribute_access.hh
+++ b/source/blender/blenkernel/BKE_attribute_access.hh
@@ -333,6 +333,21 @@ class CustomDataAttributes {
void reallocate(const int size);
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
+ {
+ 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));
+ }
+
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);
diff --git a/source/blender/blenkernel/BKE_attribute_math.hh b/source/blender/blenkernel/BKE_attribute_math.hh
index 0afdc436415..ba683362e69 100644
--- a/source/blender/blenkernel/BKE_attribute_math.hh
+++ b/source/blender/blenkernel/BKE_attribute_math.hh
@@ -52,7 +52,7 @@ inline void convert_to_static_type(const CustomDataType data_type, const Func &f
func(bool());
break;
case CD_PROP_COLOR:
- func(Color4f());
+ func(ColorGeometry4f());
break;
default:
BLI_assert_unreachable();
@@ -78,8 +78,8 @@ inline void convert_to_static_type(const fn::CPPType &cpp_type, const Func &func
else if (cpp_type.is<bool>()) {
func(bool());
}
- else if (cpp_type.is<Color4f>()) {
- func(Color4f());
+ else if (cpp_type.is<ColorGeometry4f>()) {
+ func(ColorGeometry4f());
}
else {
BLI_assert_unreachable();
@@ -123,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;
}
@@ -165,9 +168,10 @@ template<> inline float3 mix2(const float factor, const float3 &a, const float3
return float3::interpolate(a, b, factor);
}
-template<> inline Color4f mix2(const float factor, const Color4f &a, const Color4f &b)
+template<>
+inline ColorGeometry4f mix2(const float factor, const ColorGeometry4f &a, const ColorGeometry4f &b)
{
- Color4f result;
+ ColorGeometry4f result;
interp_v4_v4v4(result, a, b, factor);
return result;
}
@@ -274,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();
};
@@ -299,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 0bab980cfcd..6ad910ff8ab 100644
--- a/source/blender/blenkernel/BKE_blender_version.h
+++ b/source/blender/blenkernel/BKE_blender_version.h
@@ -39,7 +39,7 @@ extern "C" {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
-#define BLENDER_FILE_SUBVERSION 1
+#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 f04b5e45720..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,
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_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh
index 3b3856f11ab..b2342a5fd96 100644
--- a/source/blender/blenkernel/BKE_geometry_set.hh
+++ b/source/blender/blenkernel/BKE_geometry_set.hh
@@ -39,7 +39,8 @@ struct Mesh;
struct Object;
struct PointCloud;
struct Volume;
-class CurveEval;
+struct Curve;
+struct CurveEval;
enum class GeometryOwnershipType {
/* The geometry is owned. This implies that it can be changed. */
@@ -406,6 +407,15 @@ class CurveComponent : public GeometryComponent {
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();
@@ -420,12 +430,18 @@ class CurveComponent : public GeometryComponent {
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:
diff --git a/source/blender/blenkernel/BKE_lib_id.h b/source/blender/blenkernel/BKE_lib_id.h
index 6b706f3bcd0..e16507bf3cc 100644
--- a/source/blender/blenkernel/BKE_lib_id.h
+++ b/source/blender/blenkernel/BKE_lib_id.h
@@ -258,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. */
@@ -273,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 f1ed5a453ba..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,15 @@ 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,
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 dc471fcb62f..69e2d52e1dd 100644
--- a/source/blender/blenkernel/BKE_material.h
+++ b/source/blender/blenkernel/BKE_material.h
@@ -113,6 +113,7 @@ void BKE_id_material_clear(struct Main *bmain, struct ID *id);
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 */
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h
index 6bebf74824d..380e75f0924 100644
--- a/source/blender/blenkernel/BKE_node.h
+++ b/source/blender/blenkernel/BKE_node.h
@@ -325,7 +325,7 @@ typedef struct bNodeType {
/* Execute a geometry node. */
NodeGeometryExecFunction geometry_node_execute;
- bool geometry_node_execute_supports_lazyness;
+ bool geometry_node_execute_supports_laziness;
/* RNA integration */
ExtensionRNA rna_ext;
@@ -1305,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);
/** \} */
@@ -1426,6 +1429,9 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#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
/** \} */
diff --git a/source/blender/blenkernel/BKE_node_ui_storage.hh b/source/blender/blenkernel/BKE_node_ui_storage.hh
index 8bf89cd8f58..4ec165aad8c 100644
--- a/source/blender/blenkernel/BKE_node_ui_storage.hh
+++ b/source/blender/blenkernel/BKE_node_ui_storage.hh
@@ -95,21 +95,13 @@ struct AvailableAttributeInfo {
};
struct NodeUIStorage {
- std::mutex mutex;
blender::Vector<NodeWarning> warnings;
blender::Set<AvailableAttributeInfo> attribute_hints;
-
- NodeUIStorage() = default;
- /* Needed because the mutex can't be moved or copied. */
- NodeUIStorage(NodeUIStorage &&other)
- : warnings(std::move(other.warnings)), attribute_hints(std::move(other.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
diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh
index 9e5552082af..fc1292e3620 100644
--- a/source/blender/blenkernel/BKE_spline.hh
+++ b/source/blender/blenkernel/BKE_spline.hh
@@ -101,15 +101,14 @@ class Spline {
Spline(const Type type) : type_(type)
{
}
- Spline(Spline &other)
- : normal_mode(other.normal_mode),
- attributes(other.attributes),
- type_(other.type_),
- is_cyclic_(other.is_cyclic_)
+ 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;
@@ -183,6 +182,12 @@ class Spline {
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_;
+ }
};
/**
@@ -236,6 +241,7 @@ class BezierSpline final : public Spline {
public:
virtual SplinePtr copy() const final;
+ SplinePtr copy_settings() const final;
BezierSpline() : Spline(Type::Bezier)
{
}
@@ -257,10 +263,10 @@ class BezierSpline final : public Spline {
void set_resolution(const int value);
void add_point(const blender::float3 position,
- const HandleType handle_type_start,
- const blender::float3 handle_position_start,
- const HandleType handle_type_end,
- const blender::float3 handle_position_end,
+ 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);
@@ -377,6 +383,7 @@ class NURBSpline final : public Spline {
public:
SplinePtr copy() const final;
+ SplinePtr copy_settings() const final;
NURBSpline() : Spline(Type::NURBS)
{
}
@@ -444,6 +451,7 @@ class PolySpline final : public Spline {
public:
SplinePtr copy() const final;
+ SplinePtr copy_settings() const final;
PolySpline() : Spline(Type::Poly)
{
}
@@ -483,7 +491,7 @@ class PolySpline final : public Spline {
* 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.
*/
-class CurveEval {
+struct CurveEval {
private:
blender::Vector<SplinePtr> splines_;
diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc
index 62833e10438..f2ad873b10e 100644
--- a/source/blender/blenkernel/intern/attribute_access.cc
+++ b/source/blender/blenkernel/intern/attribute_access.cc
@@ -46,6 +46,8 @@ using blender::StringRef;
using blender::StringRefNull;
using blender::fn::GMutableSpan;
using blender::fn::GSpan;
+using blender::fn::GVArray_For_GSpan;
+using blender::fn::GVArray_For_SingleValue;
namespace blender::bke {
@@ -61,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:
@@ -84,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>()) {
@@ -355,7 +357,7 @@ ReadAttributeLookup 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:
@@ -389,7 +391,7 @@ WriteAttributeLookup 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:
@@ -628,8 +630,35 @@ std::optional<GSpan> CustomDataAttributes::get_for_read(const StringRef name) co
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) {
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/collection.c b/source/blender/blenkernel/intern/collection.c
index 3170c3aa65c..7b1aaf04640 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);
}
@@ -1425,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;
}
}
@@ -1644,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 liboverride 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);
@@ -1652,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);
}
}
@@ -1679,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);
}
}
diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c
index 9293a2b449a..826c79c3764 100644
--- a/source/blender/blenkernel/intern/constraint.c
+++ b/source/blender/blenkernel/intern/constraint.c
@@ -1635,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 */
@@ -1671,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 = {
diff --git a/source/blender/blenkernel/intern/customdata_file.c b/source/blender/blenkernel/intern/customdata_file.c
index 470c2f2d246..4aaecc26eff 100644
--- a/source/blender/blenkernel/intern/customdata_file.c
+++ b/source/blender/blenkernel/intern/customdata_file.c
@@ -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/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/fcurve.c b/source/blender/blenkernel/intern/fcurve.c
index 30d5a54b479..68ed3c239ef 100644
--- a/source/blender/blenkernel/intern/fcurve.c
+++ b/source/blender/blenkernel/intern/fcurve.c
@@ -1559,8 +1559,7 @@ 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`.
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/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc
index d08681da6ec..de8dc355557 100644
--- a/source/blender/blenkernel/intern/geometry_component_curve.cc
+++ b/source/blender/blenkernel/intern/geometry_component_curve.cc
@@ -14,11 +14,15 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "BKE_spline.hh"
+#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"
@@ -26,6 +30,7 @@ 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;
/* -------------------------------------------------------------------- */
@@ -58,6 +63,11 @@ void CurveComponent::clear()
if (ownership_ == GeometryOwnershipType::Owned) {
delete curve_;
}
+ if (curve_for_render_ != nullptr) {
+ BKE_id_free(nullptr, curve_for_render_);
+ curve_for_render_ = nullptr;
+ }
+
curve_ = nullptr;
}
}
@@ -118,6 +128,29 @@ void CurveComponent::ensure_owns_direct_data()
}
}
+/**
+ * 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_;
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -142,6 +175,171 @@ int CurveComponent::attribute_domain_size(const AttributeDomain domain) const
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);
@@ -300,20 +498,6 @@ static GVMutableArrayPtr make_cyclic_write_attribute(CurveEval &curve)
* array implementations try to make it workable in common situations.
* \{ */
-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};
-}
-
template<typename T>
static void point_attribute_materialize(Span<Span<T>> data,
Span<int> offsets,
@@ -325,14 +509,12 @@ static void point_attribute_materialize(Span<Span<T>> data,
for (const int spline_index : data.index_range()) {
const int offset = offsets[spline_index];
const int next_offset = offsets[spline_index + 1];
- initialized_copy_n(data[spline_index].data(), next_offset - offset, r_span.data() + offset);
+ r_span.slice(offset, next_offset - offset).copy_from(data[spline_index]);
}
}
else {
int spline_index = 0;
- for (const int i : r_span.index_range()) {
- const int dst_index = mask[i];
-
+ for (const int dst_index : mask) {
while (offsets[spline_index] < dst_index) {
spline_index++;
}
@@ -360,9 +542,7 @@ static void point_attribute_materialize_to_uninitialized(Span<Span<T>> data,
}
else {
int spline_index = 0;
- for (const int i : r_span.index_range()) {
- const int dst_index = mask[i];
-
+ for (const int dst_index : mask) {
while (offsets[spline_index] < dst_index) {
spline_index++;
}
diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc
index 2ecd0e6bd85..42f3a854aec 100644
--- a/source/blender/blenkernel/intern/geometry_component_mesh.cc
+++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc
@@ -219,8 +219,7 @@ static void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh,
static GVArrayPtr adapt_mesh_domain_corner_to_point(const Mesh &mesh, GVArrayPtr varray)
{
GVArrayPtr new_varray;
- const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
- attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ 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
@@ -249,8 +248,7 @@ static void adapt_mesh_domain_point_to_corner_impl(const Mesh &mesh,
static GVArrayPtr adapt_mesh_domain_point_to_corner(const Mesh &mesh, GVArrayPtr varray)
{
GVArrayPtr new_varray;
- const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
- attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ 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
@@ -290,8 +288,7 @@ static void adapt_mesh_domain_corner_to_face_impl(const Mesh &mesh,
static GVArrayPtr adapt_mesh_domain_corner_to_face(const Mesh &mesh, GVArrayPtr varray)
{
GVArrayPtr new_varray;
- const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
- attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ 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);
@@ -329,8 +326,7 @@ static void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh,
static GVArrayPtr adapt_mesh_domain_corner_to_edge(const Mesh &mesh, GVArrayPtr varray)
{
GVArrayPtr new_varray;
- const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
- attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ 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);
@@ -365,8 +361,7 @@ void adapt_mesh_domain_face_to_point_impl(const Mesh &mesh,
static GVArrayPtr adapt_mesh_domain_face_to_point(const Mesh &mesh, GVArrayPtr varray)
{
GVArrayPtr new_varray;
- const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
- attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ 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);
@@ -394,8 +389,7 @@ void adapt_mesh_domain_face_to_corner_impl(const Mesh &mesh,
static GVArrayPtr adapt_mesh_domain_face_to_corner(const Mesh &mesh, GVArrayPtr varray)
{
GVArrayPtr new_varray;
- const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
- attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ 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);
@@ -428,8 +422,7 @@ void adapt_mesh_domain_face_to_edge_impl(const Mesh &mesh,
static GVArrayPtr adapt_mesh_domain_face_to_edge(const Mesh &mesh, GVArrayPtr varray)
{
GVArrayPtr new_varray;
- const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
- attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ 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);
@@ -467,8 +460,7 @@ static void adapt_mesh_domain_point_to_face_impl(const Mesh &mesh,
static GVArrayPtr adapt_mesh_domain_point_to_face(const Mesh &mesh, GVArrayPtr varray)
{
GVArrayPtr new_varray;
- const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
- attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ 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);
@@ -504,8 +496,7 @@ static void adapt_mesh_domain_point_to_edge_impl(const Mesh &mesh,
static GVArrayPtr adapt_mesh_domain_point_to_edge(const Mesh &mesh, GVArrayPtr varray)
{
GVArrayPtr new_varray;
- const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
- attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ 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);
@@ -543,8 +534,7 @@ void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh,
static GVArrayPtr adapt_mesh_domain_edge_to_corner(const Mesh &mesh, GVArrayPtr varray)
{
GVArrayPtr new_varray;
- const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
- attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ 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);
@@ -576,8 +566,7 @@ static void adapt_mesh_domain_edge_to_point_impl(const Mesh &mesh,
static GVArrayPtr adapt_mesh_domain_edge_to_point(const Mesh &mesh, GVArrayPtr varray)
{
GVArrayPtr new_varray;
- const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
- attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ 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);
@@ -615,8 +604,7 @@ static void adapt_mesh_domain_edge_to_face_impl(const Mesh &mesh,
static GVArrayPtr adapt_mesh_domain_edge_to_face(const Mesh &mesh, GVArrayPtr varray)
{
GVArrayPtr new_varray;
- const CustomDataType data_type = cpp_type_to_custom_data_type(varray->type());
- attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ 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);
@@ -785,18 +773,20 @@ static void set_loop_uv(MLoopUV &uv, float2 co)
copy_v2_v2(uv.uv, co);
}
-static Color4f get_loop_color(const MLoopCol &col)
+static ColorGeometry4f get_loop_color(const MLoopCol &col)
{
- Color4f srgb_color;
- rgba_uchar_to_float(srgb_color, &col.r);
- Color4f linear_color;
- srgb_to_linearrgb_v4(linear_color, srgb_color);
+ 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, Color4f linear_color)
+static void set_loop_color(MLoopCol &col, ColorGeometry4f linear_color)
{
- linearrgb_to_srgb_uchar4(&col.r, linear_color);
+ 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)
@@ -1133,8 +1123,8 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
CD_PROP_COLOR,
CD_MLOOPCOL,
corner_access,
- make_derived_read_attribute<MLoopCol, Color4f, get_loop_color>,
- make_derived_write_attribute<MLoopCol, Color4f, get_loop_color, set_loop_color>);
+ 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_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc
index 9abd00c2b4f..69840ba1612 100644
--- a/source/blender/blenkernel/intern/geometry_set_instances.cc
+++ b/source/blender/blenkernel/intern/geometry_set_instances.cc
@@ -544,9 +544,9 @@ static void join_attributes(Span<GeometryInstanceGroup> set_groups,
}
}
-static void join_curve_splines(Span<GeometryInstanceGroup> set_groups, CurveComponent &result)
+static CurveEval *join_curve_splines(Span<GeometryInstanceGroup> set_groups)
{
- CurveEval *new_curve = new CurveEval();
+ Vector<SplinePtr> new_splines;
for (const GeometryInstanceGroup &set_group : set_groups) {
const GeometrySet &set = set_group.geometry_set;
if (!set.has_curve()) {
@@ -558,10 +558,18 @@ static void join_curve_splines(Span<GeometryInstanceGroup> set_groups, CurveComp
for (const float4x4 &transform : set_group.transforms) {
SplinePtr new_spline = source_spline->copy();
new_spline->transform(transform);
- new_curve->add_spline(std::move(new_spline));
+ 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
@@ -573,8 +581,7 @@ static void join_curve_splines(Span<GeometryInstanceGroup> set_groups, CurveComp
}
new_curve->attributes.reallocate(new_curve->splines().size());
-
- result.replace(new_curve);
+ return new_curve;
}
static void join_instance_groups_mesh(Span<GeometryInstanceGroup> set_groups,
@@ -639,14 +646,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>();
- join_curve_splines(set_groups, dst_component);
+ dst_component.replace(curve);
}
GeometrySet geometry_set_realize_mesh_for_modifier(const GeometrySet &geometry_set)
diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c
index 421cb0ac4f1..6d1476485ca 100644
--- a/source/blender/blenkernel/intern/gpencil.c
+++ b/source/blender/blenkernel/intern/gpencil.c
@@ -2624,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;
@@ -2667,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;
@@ -2682,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;
}
}
@@ -2779,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;
}
@@ -2816,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;
}
@@ -2826,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/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/lib_id.c b/source/blender/blenkernel/intern/lib_id.c
index c2e5006cbc1..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;
}
}
@@ -833,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;
@@ -989,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);
@@ -1092,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);
@@ -1221,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) {
@@ -1565,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;
@@ -1621,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, '.') ==
@@ -1710,15 +1704,18 @@ 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 = false;
char name[MAX_ID_NAME - 2];
- /* If library, don't rename, but do ensure proper sorting. */
- if (ID_IS_LINKED(id)) {
+ /* 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;
@@ -1762,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;
@@ -2176,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
@@ -2201,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;
}
}
@@ -2213,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
index 9bfd19ae4d6..8e21ae88aa6 100644
--- a/source/blender/blenkernel/intern/lib_id_test.cc
+++ b/source/blender/blenkernel/intern/lib_id_test.cc
@@ -20,6 +20,7 @@
#include "MEM_guardedalloc.h"
#include "BLI_listbase.h"
+#include "BLI_string.h"
#include "BKE_idtype.h"
#include "BKE_lib_id.h"
@@ -37,6 +38,7 @@ struct LibIDMainSortTestContext {
static void test_lib_id_main_sort_init(LibIDMainSortTestContext *ctx)
{
+ BKE_idtype_init();
ctx->bmain = BKE_main_new();
}
@@ -109,4 +111,63 @@ TEST(lib_id_main_sort, linked_ids_1)
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 8341c5b6e78..c93971e7b11 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)
{
@@ -194,12 +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)
{
/* 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);
+ 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;
@@ -233,6 +252,13 @@ bool BKE_lib_override_library_is_user_edited(struct ID *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) {
@@ -257,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;
@@ -302,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;
@@ -314,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;
@@ -326,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;
}
@@ -369,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;
}
@@ -409,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.
@@ -421,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);
@@ -442,16 +505,16 @@ static bool lib_override_hierarchy_dependencies_recursive_tag(LibOverrideGroupTa
}
/* We only consider IDs from the same library. */
ID *to_id = *to_id_entry->id_pointer.to;
- if (!ID_IS_LINKED(to_id) && !ID_IS_OVERRIDE_LIBRARY(to_id)) {
- /* Pure local data is a barrier of dependency in override cases. */
+ 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;
}
- 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;
- }
+ LibOverrideGroupTagData sub_data = *data;
+ sub_data.id_root = to_id;
+ if (lib_override_hierarchy_dependencies_recursive_tag(&sub_data)) {
+ id->tag |= data->tag;
}
}
@@ -463,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;
@@ -528,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;
@@ -554,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;
@@ -585,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;
@@ -633,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);
@@ -648,7 +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);
+ return BKE_lib_override_library_create_from_tag(bmain, id_root->lib, false);
}
static void lib_override_library_create_post_process(Main *bmain,
@@ -659,6 +720,9 @@ 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);
/* We create a set of all objects referenced into the scene by its hierarchy of collections.
@@ -668,7 +732,7 @@ static void lib_override_library_create_post_process(Main *bmain,
/* 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) {
+ 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 ?
@@ -713,56 +777,58 @@ static void lib_override_library_create_post_process(Main *bmain,
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);
-
- 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;
- }
- 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_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;
- }
+ 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);
}
- 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);
}
+ 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);
}
}
@@ -772,7 +838,7 @@ static void lib_override_library_create_post_process(Main *bmain,
/**
* 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
@@ -782,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). */
@@ -862,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);
}
/**
@@ -882,17 +961,20 @@ bool BKE_lib_override_library_resync(Main *bmain,
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);
@@ -916,12 +998,35 @@ bool BKE_lib_override_library_resync(Main *bmain,
id->tag |= LIB_TAG_MISSING;
}
- if (id->tag & LIB_TAG_DOIT && !ID_IS_LINKED(id) && ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
+ 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
@@ -960,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;
@@ -969,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. */
@@ -1051,30 +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)) {
@@ -1105,6 +1274,10 @@ bool BKE_lib_override_library_resync(Main *bmain,
}
}
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
@@ -1137,50 +1310,136 @@ bool BKE_lib_override_library_resync(Main *bmain,
}
/* 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. */
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,
- ReportList *reports)
+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);
@@ -1192,7 +1451,7 @@ void BKE_lib_override_library_main_resync(Main *bmain,
* 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)) {
@@ -1203,7 +1462,8 @@ void BKE_lib_override_library_main_resync(Main *bmain,
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);
@@ -1213,13 +1473,15 @@ void BKE_lib_override_library_main_resync(Main *bmain,
/* 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;
}
@@ -1234,14 +1496,17 @@ void BKE_lib_override_library_main_resync(Main *bmain,
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;
}
}
@@ -1256,27 +1521,29 @@ void BKE_lib_override_library_main_resync(Main *bmain,
* `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) {
- continue;
- }
- BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id));
- if (ID_IS_LINKED(id)) {
+ 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;
}
- do_continue = true;
+ 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));
+ 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, false, reports);
CLOG_INFO(&LOG, 2, "\tSuccess: %d", success);
@@ -1289,6 +1556,113 @@ void BKE_lib_override_library_main_resync(Main *bmain,
}
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(
@@ -1313,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);
@@ -1961,7 +2338,7 @@ bool BKE_lib_override_library_main_operations_create(Main *bmain, const bool for
TaskPool *task_pool = BLI_task_pool_create(&create_pool_data, TASK_PRIORITY_HIGH);
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. */
diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c
index 3281783d81a..b748061ef8a 100644
--- a/source/blender/blenkernel/intern/lib_query.c
+++ b/source/blender/blenkernel/intern/lib_query.c
@@ -244,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 b32b97dc250..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++;
}
diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c
index 73b64e6efb3..468f735257a 100644
--- a/source/blender/blenkernel/intern/material.c
+++ b/source/blender/blenkernel/intern/material.c
@@ -770,6 +770,7 @@ int BKE_object_material_count_eval(Object *ob)
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)) {
@@ -793,6 +794,21 @@ void BKE_id_material_eval_assign(ID *id, int slot, Material *material)
(*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);
@@ -1497,16 +1513,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;
}
@@ -1515,10 +1531,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/node.cc b/source/blender/blenkernel/intern/node.cc
index 643dc58af18..2a509fd7df4 100644
--- a/source/blender/blenkernel/intern/node.cc
+++ b/source/blender/blenkernel/intern/node.cc
@@ -513,7 +513,7 @@ 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);
}
@@ -5050,8 +5050,10 @@ static void registerGeometryNodes()
register_node_type_geo_boolean();
register_node_type_geo_bounding_box();
register_node_type_geo_collection_info();
+ 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();
@@ -5066,6 +5068,7 @@ static void registerGeometryNodes()
register_node_type_geo_mesh_primitive_ico_sphere();
register_node_type_geo_mesh_primitive_line();
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();
diff --git a/source/blender/blenkernel/intern/node_ui_storage.cc b/source/blender/blenkernel/intern/node_ui_storage.cc
index 7a28fd295fb..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,10 +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);
- std::lock_guard lock{node_ui_storage.mutex};
+ NodeUIStorage &node_ui_storage = node_ui_storage_ensure(ui_storage, context, node);
node_ui_storage.warnings.append({type, std::move(message)});
}
@@ -163,8 +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);
- std::lock_guard lock{node_ui_storage.mutex};
+ 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 034af924ab1..b73f6a5b78c 100644
--- a/source/blender/blenkernel/intern/object.c
+++ b/source/blender/blenkernel/intern/object.c
@@ -2369,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);
@@ -2622,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,
@@ -2633,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)) {
@@ -2773,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) {
diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c
index c727a144c87..ce4be411c9a 100644
--- a/source/blender/blenkernel/intern/particle_system.c
+++ b/source/blender/blenkernel/intern/particle_system.c
@@ -4912,9 +4912,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/scene.c b/source/blender/blenkernel/intern/scene.c
index a4ab64a8a02..a58db89ad0c 100644
--- a/source/blender/blenkernel/intern/scene.c
+++ b/source/blender/blenkernel/intern/scene.c
@@ -1985,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)) {
@@ -2027,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);
}
diff --git a/source/blender/blenkernel/intern/spline_bezier.cc b/source/blender/blenkernel/intern/spline_bezier.cc
index 4be3ba8576e..3c6cf2c78cf 100644
--- a/source/blender/blenkernel/intern/spline_bezier.cc
+++ b/source/blender/blenkernel/intern/spline_bezier.cc
@@ -31,6 +31,14 @@ 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();
@@ -59,18 +67,18 @@ void BezierSpline::set_resolution(const int value)
* \warning Call #reallocate on the spline's attributes after adding all points.
*/
void BezierSpline::add_point(const float3 position,
- const HandleType handle_type_start,
- const float3 handle_position_start,
- const HandleType handle_type_end,
- const float3 handle_position_end,
+ 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_start);
- handle_positions_left_.append(handle_position_start);
+ handle_types_left_.append(handle_type_left);
+ handle_positions_left_.append(handle_position_left);
positions_.append(position);
- handle_types_right_.append(handle_type_end);
- handle_positions_right_.append(handle_position_end);
+ handle_types_right_.append(handle_type_right);
+ handle_positions_right_.append(handle_position_right);
radii_.append(radius);
tilts_.append(tilt);
this->mark_cache_invalid();
diff --git a/source/blender/blenkernel/intern/spline_nurbs.cc b/source/blender/blenkernel/intern/spline_nurbs.cc
index ae691d26cdb..cae206341a0 100644
--- a/source/blender/blenkernel/intern/spline_nurbs.cc
+++ b/source/blender/blenkernel/intern/spline_nurbs.cc
@@ -32,6 +32,16 @@ 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();
diff --git a/source/blender/blenkernel/intern/spline_poly.cc b/source/blender/blenkernel/intern/spline_poly.cc
index 5c33b0052fc..5f8e81d5ad0 100644
--- a/source/blender/blenkernel/intern/spline_poly.cc
+++ b/source/blender/blenkernel/intern/spline_poly.cc
@@ -28,6 +28,13 @@ 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();
diff --git a/source/blender/blenkernel/intern/unit.c b/source/blender/blenkernel/intern/unit.c
index 9ae1c754846..3612a26315c 100644
--- a/source/blender/blenkernel/intern/unit.c
+++ b/source/blender/blenkernel/intern/unit.c
@@ -943,7 +943,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 +957,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/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c
index 39f65d76e3c..92d19f18a93 100644
--- a/source/blender/blenkernel/intern/writeffmpeg.c
+++ b/source/blender/blenkernel/intern/writeffmpeg.c
@@ -205,12 +205,11 @@ static int write_audio_frame(FFMpegContext *context)
success = -1;
}
- av_packet_rescale_ts(pkt, c->time_base, context->audio_stream->time_base);
- if (pkt->duration > 0) {
- pkt->duration = av_rescale_q(pkt->duration, c->time_base, context->audio_stream->time_base);
- }
-
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;
@@ -349,6 +348,10 @@ static int write_video_frame(FFMpegContext *context, int cfra, AVFrame *frame, R
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;
@@ -515,6 +518,48 @@ static void set_ffmpeg_properties(RenderData *rd,
}
}
+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,
@@ -545,13 +590,24 @@ static AVStream *alloc_video_stream(FFMpegContext *context,
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;
}
@@ -559,21 +615,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;
@@ -585,6 +643,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 {
@@ -624,12 +687,6 @@ static AVStream *alloc_video_stream(FFMpegContext *context,
}
}
- codec = avcodec_find_encoder(c->codec_id);
- if (!codec) {
- avcodec_free_context(&c);
- return NULL;
- }
-
/* Be sure to use the correct pixel format(e.g. RGB, YUV) */
if (codec->pix_fmts) {
@@ -646,12 +703,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) {
@@ -678,6 +729,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;
@@ -711,10 +767,14 @@ static AVStream *alloc_video_stream(FFMpegContext *context,
c->thread_type = FF_THREAD_SLICE;
}
- if (avcodec_open2(c, codec, &opts) < 0) {
+ 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);
@@ -774,6 +834,17 @@ static AVStream *alloc_audio_stream(FFMpegContext *context,
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;
@@ -803,13 +874,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");
- avcodec_free_context(&c);
- 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,
@@ -849,11 +913,14 @@ static AVStream *alloc_audio_stream(FFMpegContext *context,
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);
@@ -1181,6 +1248,9 @@ static void flush_ffmpeg(FFMpegContext *context)
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) {
@@ -1630,49 +1700,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");
- 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");
- }
- 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");
}
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_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_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_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_vector.hh b/source/blender/blenlib/BLI_vector.hh
index 8bea2584ca7..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)
diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt
index ce3515ac153..f3dc343ee20 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
@@ -389,6 +390,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/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/math_base_inline.c b/source/blender/blenlib/intern/math_base_inline.c
index 2a7c091d1b9..d73afff64c8 100644
--- a/source/blender/blenlib/intern/math_base_inline.c
+++ b/source/blender/blenlib/intern/math_base_inline.c
@@ -514,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);
@@ -798,9 +807,9 @@ MINLINE unsigned char unit_float_to_uchar_clamp(float val)
MINLINE unsigned short unit_float_to_ushort_clamp(float val)
{
- return (unsigned short)((val >= 1.0f - 0.5f / 65535) ?
- 65535 :
- (val <= 0.0f) ? 0 : (val * 65535.0f + 0.5f));
+ return (unsigned short)((val >= 1.0f - 0.5f / 65535) ? 65535 :
+ (val <= 0.0f) ? 0 :
+ (val * 65535.0f + 0.5f));
}
#define unit_float_to_ushort_clamp(val) \
((CHECK_TYPE_INLINE(val, float)), unit_float_to_ushort_clamp(val))
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 25291b8c3b0..00d53a010b0 100644
--- a/source/blender/blenlib/intern/mesh_boolean.cc
+++ b/source/blender/blenlib/intern/mesh_boolean.cc
@@ -41,6 +41,7 @@
# 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"
@@ -48,6 +49,10 @@
# include "BLI_mesh_boolean.hh"
+# ifdef WITH_TBB
+# include "tbb/spin_mutex.h"
+# endif
+
// # define PERFDEBUG
namespace blender::meshintersect {
@@ -144,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;
}
@@ -1690,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) {
@@ -1700,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;
@@ -1712,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;
@@ -1725,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;
@@ -1751,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 {
@@ -1832,6 +1876,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;
@@ -1843,6 +1892,7 @@ static Vector<ComponentContainer> find_component_containers(int comp,
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()) {
@@ -1852,10 +1902,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) {
@@ -1867,6 +1930,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;
}
}
}
@@ -2567,47 +2631,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;
@@ -3259,9 +3334,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 636209883c3..97f856476c5 100644
--- a/source/blender/blenlib/intern/mesh_intersect.cc
+++ b/source/blender/blenlib/intern/mesh_intersect.cc
@@ -1165,13 +1165,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;
}
@@ -1179,11 +1185,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));
}
/**
@@ -1227,20 +1250,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]. */
@@ -1248,8 +1272,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 {
@@ -1262,7 +1286,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";
@@ -1271,14 +1295,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]. */
@@ -1286,8 +1310,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]);
}
}
}
@@ -1438,6 +1462,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;
@@ -1447,13 +1472,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) {
@@ -1473,13 +1504,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) {
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/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/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/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index fe7d50bfa15..801f5864c8d 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -4452,7 +4452,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);
@@ -4512,7 +4514,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/versioning_250.c b/source/blender/blenloader/intern/versioning_250.c
index 6338dc95aba..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) {
diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c
index 8c5e86eadd3..284a30a280d 100644
--- a/source/blender/blenloader/intern/versioning_300.c
+++ b/source/blender/blenloader/intern/versioning_300.c
@@ -22,10 +22,12 @@
#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"
@@ -36,6 +38,43 @@
#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)) {
@@ -46,19 +85,8 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports))
}
}
}
- /**
- * 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. */
+ 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) {
@@ -82,9 +110,55 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports))
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;
+}
+
/* NOLINTNEXTLINE: readability-function-size */
void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
{
@@ -110,6 +184,22 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
}
+
+ 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.
*
@@ -121,15 +211,5 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
*/
{
/* Keep this block, even when empty. */
- 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);
- }
- }
- }
}
}
diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt
index c215cf69e3a..4bd88ae62d7 100644
--- a/source/blender/bmesh/CMakeLists.txt
+++ b/source/blender/bmesh/CMakeLists.txt
@@ -102,6 +102,10 @@ set(SRC
intern/bmesh_mesh_convert.h
intern/bmesh_mesh_duplicate.c
intern/bmesh_mesh_duplicate.h
+ intern/bmesh_mesh_tessellate.c
+ intern/bmesh_mesh_tessellate.h
+ intern/bmesh_mesh_partial_update.c
+ intern/bmesh_mesh_partial_update.h
intern/bmesh_mesh_validate.c
intern/bmesh_mesh_validate.h
intern/bmesh_mods.c
@@ -182,10 +186,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 +225,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..d8c3f4ce556 100644
--- a/source/blender/bmesh/bmesh.h
+++ b/source/blender/bmesh/bmesh.h
@@ -215,6 +215,8 @@ extern "C" {
#include "intern/bmesh_mesh.h"
#include "intern/bmesh_mesh_convert.h"
#include "intern/bmesh_mesh_duplicate.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_mesh.c b/source/blender/bmesh/intern/bmesh_mesh.c
index 5e879d41d43..d4357fa561b 100644
--- a/source/blender/bmesh/intern/bmesh_mesh.c
+++ b/source/blender/bmesh/intern/bmesh_mesh.c
@@ -373,15 +373,16 @@ typedef struct BMVertsCalcNormalsData {
float (*vnos)[3];
} BMVertsCalcNormalsData;
-static void mesh_verts_calc_normals_accum_cb(void *userdata, MempoolIterData *mp_f)
+static void mesh_verts_calc_normals_accum(
+ BMFace *f,
+ const float *f_no,
+ const float (*edgevec)[3],
+
+ /* Read-write data, protected by an atomic-based fake spin-lock like system. */
+ float (*vnos)[3])
{
#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 {
@@ -391,8 +392,8 @@ static void mesh_verts_calc_normals_accum_cb(void *userdata, MempoolIterData *mp
/* 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)];
+ e1diff = edgevec[BM_elem_index_get(l_iter->prev->e)];
+ e2diff = edgevec[BM_elem_index_get(l_iter->e)];
dotprod = dot_v3v3(e1diff, e2diff);
/* edge vectors are calculated from e->v1 to e->v2, so
@@ -410,7 +411,7 @@ static void mesh_verts_calc_normals_accum_cb(void *userdata, MempoolIterData *mp
}
/* 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;
+ float *v_no = vnos ? 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,
@@ -447,6 +448,14 @@ static void mesh_verts_calc_normals_accum_cb(void *userdata, MempoolIterData *mp
#undef FLT_EQ_NONAN
}
+static void mesh_verts_calc_normals_accum_cb(void *userdata, MempoolIterData *mp_f)
+{
+ BMVertsCalcNormalsData *data = userdata;
+ BMFace *f = (BMFace *)mp_f;
+ const float *f_no = data->fnos ? data->fnos[BM_elem_index_get(f)] : f->no;
+ mesh_verts_calc_normals_accum(f, f_no, data->edgevec, data->vnos);
+}
+
static void mesh_verts_calc_normals_normalize_cb(void *userdata, MempoolIterData *mp_v)
{
BMVertsCalcNormalsData *data = userdata;
@@ -529,6 +538,130 @@ void BM_mesh_normals_update(BMesh *bm)
MEM_freeN(edgevec);
}
+static void mesh_faces_parallel_range_calc_normals_cb(
+ void *userdata, const int iter, const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ BMFace *f = ((BMFace **)userdata)[iter];
+ BM_face_normal_update(f);
+}
+
+static void mesh_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 mesh_verts_parallel_range_calc_normals_accum_cb(
+ void *userdata, const int iter, const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ BMFace *f = ((BMFace **)((void **)userdata)[0])[iter];
+ const float(*edgevec)[3] = (float(*)[3])((void **)userdata)[1];
+ mesh_verts_calc_normals_accum(f, f->no, edgevec, NULL);
+}
+
+static void mesh_verts_parallel_range_calc_normals_normalize_cb(
+ void *userdata, const int iter, const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ BMVert *v = ((BMVert **)userdata)[iter];
+ if (UNLIKELY(normalize_v3(v->no) == 0.0f)) {
+ normalize_v3_v3(v->no, v->co);
+ }
+}
+
+/**
+ * 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;
+ const int faces_len_normal_calc_accumulate = bmpinfo->faces_len_normal_calc_accumulate;
+
+ float(*edgevec)[3] = MEM_mallocN(sizeof(*edgevec) * edges_len, __func__);
+
+ for (int i = 0; i < verts_len; i++) {
+ zero_v3(verts[i]->no);
+ }
+
+ TaskParallelSettings settings;
+ BLI_parallel_range_settings_defaults(&settings);
+ {
+ /* Faces. */
+ BLI_task_parallel_range(
+ 0, faces_len, faces, mesh_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, mesh_edges_parallel_range_calc_vectors_cb, &settings);
+
+ /* Add weighted face normals to vertices. */
+ data[0] = faces;
+ BLI_task_parallel_range(0,
+ faces_len_normal_calc_accumulate,
+ data,
+ mesh_verts_parallel_range_calc_normals_accum_cb,
+ &settings);
+
+ /* Normalize the accumulated vertex normals. */
+ BLI_task_parallel_range(
+ 0, verts_len, verts, mesh_verts_parallel_range_calc_normals_normalize_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);
+}
+
/**
* \brief BMesh Compute Normals from/to external data.
*
@@ -1724,7 +1857,7 @@ static int bm_loop_normal_mark_indiv(BMesh *bm, BLI_bitmap *loops, const bool do
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,
+ * 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. */
diff --git a/source/blender/bmesh/intern/bmesh_mesh.h b/source/blender/bmesh/intern/bmesh_mesh.h
index c1c2f17d7c1..708786a4c55 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);
@@ -41,6 +42,8 @@ void BM_mesh_data_free(BMesh *bm);
void BM_mesh_clear(BMesh *bm);
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],
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..2290e58fe6c
--- /dev/null
+++ b/source/blender/bmesh/intern/bmesh_mesh_partial_update.c
@@ -0,0 +1,274 @@
+/*
+ * 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);
+ }
+ }
+ }
+
+ const int faces_len_direct = bmpinfo->faces_len;
+
+ 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) {
+ if (!partial_elem_edge_ensure(bmpinfo, edges_tag, e_iter)) {
+ continue;
+ }
+
+ /* These faces need to be taken into account when weighting vertex normals
+ * but aren't needed for tessellation nor do their normals need to be recalculated.
+ * These faces end up between `faces_len` and `faces_len_normal_calc_accumulate`
+ * in the faces array. */
+ BMLoop *l_first_radial = e_iter->l;
+ BMLoop *l_iter_radial = l_first_radial;
+ /* Loop over radial loops. */
+ do {
+ if (l_iter_radial->v == v) {
+ partial_elem_face_ensure(bmpinfo, faces_tag, l_iter_radial->f);
+ }
+ } while ((l_iter_radial = l_iter_radial->radial_next) != l_first_radial);
+ }
+ } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v)) != e_first);
+ } while ((l_iter = l_iter->next) != l_first);
+ }
+ }
+
+ bmpinfo->faces_len_normal_calc_accumulate = bmpinfo->faces_len;
+ bmpinfo->faces_len = faces_len_direct;
+
+ 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..c0c9b275fa4
--- /dev/null
+++ b/source/blender/bmesh/intern/bmesh_mesh_partial_update.h
@@ -0,0 +1,69 @@
+/*
+ * 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;
+ /**
+ * Faces at the end of `faces` that don't need to have the normals recalculated
+ * but must be included when waiting the vertex normals.
+ */
+ int faces_len_normal_calc_accumulate;
+
+ /** 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..f2a5fbe3bde
--- /dev/null
+++ b/source/blender/bmesh/intern/bmesh_mesh_tessellate.c
@@ -0,0 +1,405 @@
+/*
+ * 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"
+
+/* -------------------------------------------------------------------- */
+/** \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
+ */
+void BM_mesh_calc_tessellation(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);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \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);
+
+ /* 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. */
+ if (bmpinfo->faces_len < 1024) {
+ 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_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/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt
index 65391794c12..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
@@ -66,14 +68,20 @@ set(SRC
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
@@ -86,8 +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_TiledExecutionModel.cc
+ intern/COM_TiledExecutionModel.h
intern/COM_WorkPackage.cc
intern/COM_WorkPackage.h
intern/COM_WorkScheduler.cc
diff --git a/source/blender/compositor/COM_defines.h b/source/blender/compositor/COM_defines.h
index ef889807030..5a52d216117 100644
--- a/source/blender/compositor/COM_defines.h
+++ b/source/blender/compositor/COM_defines.h
@@ -20,6 +20,16 @@
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
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 29a82bec636..2ca5557e278 100644
--- a/source/blender/compositor/intern/COM_CPUDevice.cc
+++ b/source/blender/compositor/intern/COM_CPUDevice.cc
@@ -30,11 +30,24 @@ CPUDevice::CPUDevice(int thread_id) : m_thread_id(thread_id)
void CPUDevice::execute(WorkPackage *work_package)
{
- const unsigned int chunkNumber = work_package->chunk_number;
- ExecutionGroup *executionGroup = work_package->execution_group;
-
- executionGroup->getOutputOperation()->executeRegion(&work_package->rect, chunkNumber);
- executionGroup->finalizeChunkExecution(chunkNumber, nullptr);
+ switch (work_package->type) {
+ case eWorkPackageType::Tile: {
+ const unsigned int chunkNumber = work_package->chunk_number;
+ ExecutionGroup *executionGroup = work_package->execution_group;
+
+ executionGroup->getOutputOperation()->executeRegion(&work_package->rect, chunkNumber);
+ executionGroup->finalizeChunkExecution(chunkNumber, nullptr);
+ break;
+ }
+ case eWorkPackageType::CustomFunction: {
+ work_package->execute_fn();
+ break;
+ }
+ }
+
+ if (work_package->executed_fn) {
+ work_package->executed_fn();
+ }
}
} // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_CompositorContext.cc b/source/blender/compositor/intern/COM_CompositorContext.cc
index f70f3a8ebfc..61e299c045e 100644
--- a/source/blender/compositor/intern/COM_CompositorContext.cc
+++ b/source/blender/compositor/intern/COM_CompositorContext.cc
@@ -21,6 +21,7 @@
#include <cstdio>
#include "BLI_assert.h"
+#include "DNA_userdef_types.h"
namespace blender::compositor {
@@ -33,6 +34,7 @@ CompositorContext::CompositorContext()
this->m_fastCalculation = false;
this->m_viewSettings = nullptr;
this->m_displaySettings = nullptr;
+ this->m_bnodetree = nullptr;
}
int CompositorContext::getFramenumber() const
@@ -41,4 +43,20 @@ int CompositorContext::getFramenumber() const
return m_rd->cfra;
}
+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 e6164246bdd..56251511576 100644
--- a/source/blender/compositor/intern/COM_CompositorContext.h
+++ b/source/blender/compositor/intern/COM_CompositorContext.h
@@ -281,6 +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_Debug.cc b/source/blender/compositor/intern/COM_Debug.cc
index dfb4f53fee5..4cf7e09a7d8 100644
--- a/source/blender/compositor/intern/COM_Debug.cc
+++ b/source/blender/compositor/intern/COM_Debug.cc
@@ -211,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");
@@ -236,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");
@@ -387,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");
diff --git a/source/blender/compositor/intern/COM_Debug.h b/source/blender/compositor/intern/COM_Debug.h
index e1aea69e481..0de3a5e39dc 100644
--- a/source/blender/compositor/intern/COM_Debug.h
+++ b/source/blender/compositor/intern/COM_Debug.h
@@ -129,7 +129,7 @@ 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);
};
diff --git a/source/blender/compositor/intern/COM_Enums.h b/source/blender/compositor/intern/COM_Enums.h
index f65ce3e856e..519e7df940e 100644
--- a/source/blender/compositor/intern/COM_Enums.h
+++ b/source/blender/compositor/intern/COM_Enums.h
@@ -70,6 +70,21 @@ enum class eWorkPackageState {
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);
diff --git a/source/blender/compositor/intern/COM_ExecutionGroup.cc b/source/blender/compositor/intern/COM_ExecutionGroup.cc
index 80d453bf7f9..68bda8c70d6 100644
--- a/source/blender/compositor/intern/COM_ExecutionGroup.cc
+++ b/source/blender/compositor/intern/COM_ExecutionGroup.cc
@@ -157,6 +157,7 @@ void ExecutionGroup::init_work_packages()
if (this->m_chunks_len != 0) {
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;
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 e22dc17837b..a12ec774032 100644
--- a/source/blender/compositor/intern/COM_ExecutionSystem.cc
+++ b/source/blender/compositor/intern/COM_ExecutionSystem.cc
@@ -21,16 +21,11 @@
#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
@@ -73,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;
}
@@ -126,100 +103,16 @@ void ExecutionSystem::set_operations(const Vector<NodeOperation *> &operations,
m_groups = groups;
}
-static void update_read_buffer_offset(Vector<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(Vector<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(Vector<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(Vector<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(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(eCompositorPriority::High);
- if (!this->getContext().isFastCalculation()) {
- execute_groups(eCompositorPriority::Medium);
- execute_groups(eCompositorPriority::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(eCompositorPriority 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->get_flags().is_output &&
- 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 e6170c48778..e106209651c 100644
--- a/source/blender/compositor/intern/COM_ExecutionSystem.h
+++ b/source/blender/compositor/intern/COM_ExecutionSystem.h
@@ -25,6 +25,7 @@ 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"
@@ -115,13 +116,21 @@ namespace blender::compositor {
* \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;
@@ -136,6 +145,11 @@ class ExecutionSystem {
*/
Vector<ExecutionGroup *> m_groups;
+ /**
+ * Active execution model implementation.
+ */
+ ExecutionModel *execution_model_;
+
private: // methods
public:
/**
@@ -178,9 +192,14 @@ class ExecutionSystem {
return this->m_context;
}
- private:
- void execute_groups(eCompositorPriority 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;
diff --git a/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc b/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc
new file mode 100644
index 00000000000..396aa2fcf6f
--- /dev/null
+++ b/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc
@@ -0,0 +1,327 @@
+/*
+ * 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);
+ }
+ }
+ }
+}
+
+void FullFrameExecutionModel::ensure_inputs_rendered(NodeOperation *op,
+ ExecutionSystem &exec_system)
+{
+ const int num_inputs = op->getNumberOfInputSockets();
+ for (int i = 0; i < num_inputs; i++) {
+ NodeOperation *input_op = op->get_input_operation(i);
+ if (!active_buffers_.is_operation_rendered(input_op)) {
+ render_operation(input_op, exec_system);
+ }
+ }
+}
+
+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)
+{
+ if (active_buffers_.is_operation_rendered(op)) {
+ return;
+ }
+
+ ensure_inputs_rendered(op, 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_operation(op, exec_system);
+ }
+ }
+ }
+ WorkScheduler::stop();
+}
+
+/**
+ * Determines all input operations areas needed to render given operation area.
+ * \param operation: Renderer operation.
+ * \param render_area: Area within given operation bounds to render.
+ */
+void FullFrameExecutionModel::determine_areas_to_render(NodeOperation *operation,
+ const rcti &render_area)
+{
+ if (active_buffers_.is_area_registered(operation, render_area)) {
+ return;
+ }
+
+ 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);
+
+ determine_areas_to_render(input_op, input_area);
+ }
+}
+
+/**
+ * Determines the reads given operation and its inputs will receive (i.e: Number of dependent
+ * operations each operation has).
+ */
+void FullFrameExecutionModel::determine_reads(NodeOperation *operation)
+{
+ if (active_buffers_.has_registered_reads(operation)) {
+ return;
+ }
+
+ const int num_inputs = operation->getNumberOfInputSockets();
+ for (int i = 0; i < num_inputs; i++) {
+ NodeOperation *input_op = operation->get_input_operation(i);
+ determine_reads(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..2c0d5e0460a
--- /dev/null
+++ b/source/blender/compositor/intern/COM_FullFrameExecutionModel.h
@@ -0,0 +1,89 @@
+/*
+ * 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 ensure_inputs_rendered(NodeOperation *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 *operation, const rcti &render_area);
+ void determine_reads(NodeOperation *operation);
+
+ 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_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_NodeOperation.cc b/source/blender/compositor/intern/COM_NodeOperation.cc
index be3ea59efa5..83de8a751c4 100644
--- a/source/blender/compositor/intern/COM_NodeOperation.cc
+++ b/source/blender/compositor/intern/COM_NodeOperation.cc
@@ -17,8 +17,10 @@
*/
#include <cstdio>
+#include <memory>
#include <typeinfo>
+#include "COM_BufferOperation.h"
#include "COM_ExecutionSystem.h"
#include "COM_ReadBufferOperation.h"
#include "COM_defines.h"
@@ -175,6 +177,177 @@ 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 ****
*****************/
@@ -267,6 +440,9 @@ std::ostream &operator<<(std::ostream &os, const NodeOperationFlags &node_operat
if (!node_operation_flags.use_datatype_conversion) {
os << "no_conversion,";
}
+ if (node_operation_flags.is_fullframe_operation) {
+ os << "full_frame,";
+ }
return os;
}
diff --git a/source/blender/compositor/intern/COM_NodeOperation.h b/source/blender/compositor/intern/COM_NodeOperation.h
index baf3a0878b9..11b293a400f 100644
--- a/source/blender/compositor/intern/COM_NodeOperation.h
+++ b/source/blender/compositor/intern/COM_NodeOperation.h
@@ -39,6 +39,7 @@ namespace blender::compositor {
class OpenCLDevice;
class ReadBufferOperation;
class WriteBufferOperation;
+class ExecutionSystem;
class NodeOperation;
typedef NodeOperation SocketReader;
@@ -190,6 +191,10 @@ struct NodeOperationFlags {
*/
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;
/**
@@ -232,6 +237,11 @@ struct NodeOperationFlags {
*/
bool use_datatype_conversion : 1;
+ /**
+ * Has this operation fullframe implementation.
+ */
+ bool is_fullframe_operation : 1;
+
NodeOperationFlags()
{
complex = false;
@@ -247,6 +257,7 @@ struct NodeOperationFlags {
is_viewer_operation = false;
is_preview_operation = false;
use_datatype_conversion = true;
+ is_fullframe_operation = false;
}
};
@@ -341,6 +352,13 @@ class NodeOperation {
NodeOperationOutput *getOutputSocket(unsigned int index = 0);
NodeOperationInput *getInputSocket(unsigned int index);
+ NodeOperation *get_input_operation(int index)
+ {
+ /* TODO: Rename protected getInputOperation to get_input_operation and make it public replacing
+ * this method. */
+ return getInputOperation(index);
+ }
+
/**
* \brief determine the resolution of this node
* \note this method will not set the resolution, this is the responsibility of the caller
@@ -537,6 +555,33 @@ class NodeOperation {
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();
@@ -616,6 +661,27 @@ class NodeOperation {
{
}
+ 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);
+
+ /** \} */
+
/* allow the DebugInfo class to look at internals */
friend class DebugInfo;
diff --git a/source/blender/compositor/intern/COM_NodeOperationBuilder.cc b/source/blender/compositor/intern/COM_NodeOperationBuilder.cc
index 82eb969b752..c81a5a2bd98 100644
--- a/source/blender/compositor/intern/COM_NodeOperationBuilder.cc
+++ b/source/blender/compositor/intern/COM_NodeOperationBuilder.cc
@@ -99,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! */
@@ -111,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);
diff --git a/source/blender/compositor/intern/COM_SharedOperationBuffers.cc b/source/blender/compositor/intern/COM_SharedOperationBuffers.cc
new file mode 100644
index 00000000000..4ce674a1c25
--- /dev/null
+++ b/source/blender/compositor/intern/COM_SharedOperationBuffers.cc
@@ -0,0 +1,128 @@
+/*
+ * 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)
+{
+}
+
+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).buffer != nullptr;
+}
+
+/**
+ * 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);
+}
+
+/**
+ * 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..480a799d89f
--- /dev/null
+++ b/source/blender/compositor/intern/COM_SharedOperationBuffers.h
@@ -0,0 +1,70 @@
+/*
+ * 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;
+ } 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_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.h b/source/blender/compositor/intern/COM_WorkPackage.h
index 28aa746fdc4..4d503022120 100644
--- a/source/blender/compositor/intern/COM_WorkPackage.h
+++ b/source/blender/compositor/intern/COM_WorkPackage.h
@@ -22,6 +22,7 @@
#include "BLI_rect.h"
+#include <functional>
#include <ostream>
namespace blender::compositor {
@@ -33,6 +34,8 @@ class ExecutionGroup;
* \see WorkScheduler
*/
struct WorkPackage {
+ eWorkPackageType type;
+
eWorkPackageState state = eWorkPackageState::NotScheduled;
/**
@@ -50,6 +53,16 @@ struct WorkPackage {
*/
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
diff --git a/source/blender/compositor/intern/COM_WorkScheduler.cc b/source/blender/compositor/intern/COM_WorkScheduler.cc
index d578ac24a4a..157ded943d6 100644
--- a/source/blender/compositor/intern/COM_WorkScheduler.cc
+++ b/source/blender/compositor/intern/COM_WorkScheduler.cc
@@ -98,6 +98,8 @@ static struct {
bool active = false;
bool initialized = false;
} opencl;
+
+ int num_cpu_threads;
} g_work_scheduler;
/* -------------------------------------------------------------------- */
@@ -143,7 +145,8 @@ static void opencl_start(CompositorContext &context)
static bool opencl_schedule(WorkPackage *package)
{
- if (package->execution_group->get_flags().open_cl && 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;
}
@@ -532,11 +535,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;
@@ -568,6 +572,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) {
diff --git a/source/blender/compositor/intern/COM_WorkScheduler.h b/source/blender/compositor/intern/COM_WorkScheduler.h
index 85b1d7e2ebf..be88859be7c 100644
--- a/source/blender/compositor/intern/COM_WorkScheduler.h
+++ b/source/blender/compositor/intern/COM_WorkScheduler.h
@@ -87,6 +87,8 @@ struct WorkScheduler {
*/
static bool has_gpu_devices();
+ static int get_num_cpu_threads();
+
static int current_thread_id();
#ifdef WITH_CXX_GUARDEDALLOC
diff --git a/source/blender/compositor/nodes/COM_CryptomatteNode.cc b/source/blender/compositor/nodes/COM_CryptomatteNode.cc
index d97e4371dc6..4032a655633 100644
--- a/source/blender/compositor/nodes/COM_CryptomatteNode.cc
+++ b/source/blender/compositor/nodes/COM_CryptomatteNode.cc
@@ -77,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)));
}
@@ -118,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) {
@@ -129,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);
@@ -177,7 +177,7 @@ void CryptomatteNode::input_operations_from_image_source(
}
}
- const std::string prefix = prefix_from_node(node);
+ 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(
diff --git a/source/blender/compositor/nodes/COM_TranslateNode.cc b/source/blender/compositor/nodes/COM_TranslateNode.cc
index 922393f006a..1b2ce341a66 100644
--- a/source/blender/compositor/nodes/COM_TranslateNode.cc
+++ b/source/blender/compositor/nodes/COM_TranslateNode.cc
@@ -56,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());
diff --git a/source/blender/compositor/operations/COM_ConvertOperation.cc b/source/blender/compositor/operations/COM_ConvertOperation.cc
index 2ea15185c0f..384936533c7 100644
--- a/source/blender/compositor/operations/COM_ConvertOperation.cc
+++ b/source/blender/compositor/operations/COM_ConvertOperation.cc
@@ -18,6 +18,8 @@
#include "COM_ConvertOperation.h"
+#include "BLI_color.hh"
+
#include "IMB_colormanagement.h"
namespace blender::compositor {
@@ -355,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 ******** */
@@ -385,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 ******** */
diff --git a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc
index 46ae00dee34..4edcc206f5b 100644
--- a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc
+++ b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc
@@ -31,6 +31,31 @@
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])
@@ -46,13 +71,11 @@ 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, ResizeMode::None);
this->addOutputSocket(DataType::Color);
this->m_pixelReader = nullptr;
- this->m_motion_blur_samples = 1;
- this->m_motion_blur_shutter = 0.5f;
this->flags.complex = true;
}
@@ -60,24 +83,13 @@ 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);
}
@@ -147,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()
diff --git a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h
index 95e5c86bd4d..cc6e4d00d71 100644
--- a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h
+++ b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h
@@ -32,21 +32,43 @@ 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;
@@ -56,47 +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 565bde6c945..0884f2ad979 100644
--- a/source/blender/compositor/operations/COM_PlaneTrackOperation.cc
+++ b/source/blender/compositor/operations/COM_PlaneTrackOperation.cc
@@ -41,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;
@@ -84,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 ******** */
@@ -106,22 +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 95a7d536742..d240c8b06e9 100644
--- a/source/blender/compositor/operations/COM_PlaneTrackOperation.h
+++ b/source/blender/compositor/operations/COM_PlaneTrackOperation.h
@@ -40,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:
@@ -62,6 +62,9 @@ class PlaneTrackCommon {
{
this->m_framenumber = framenumber;
}
+
+ private:
+ void readCornersFromTrack(float corners[4][2], float frame);
};
class PlaneTrackMaskOperation : public PlaneDistortMaskOperation, public PlaneTrackCommon {
diff --git a/source/blender/compositor/operations/COM_TextureOperation.cc b/source/blender/compositor/operations/COM_TextureOperation.cc
index e94c457f981..7517ff8a137 100644
--- a/source/blender/compositor/operations/COM_TextureOperation.cc
+++ b/source/blender/compositor/operations/COM_TextureOperation.cc
@@ -75,16 +75,13 @@ 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];
- }
+ /* 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];
}
void TextureAlphaOperation::executePixelSampled(float output[4],
diff --git a/source/blender/compositor/operations/COM_TextureOperation.h b/source/blender/compositor/operations/COM_TextureOperation.h
index e1e04611c6c..e5f56673694 100644
--- a/source/blender/compositor/operations/COM_TextureOperation.h
+++ b/source/blender/compositor/operations/COM_TextureOperation.h
@@ -44,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;
diff --git a/source/blender/depsgraph/DEG_depsgraph_build.h b/source/blender/depsgraph/DEG_depsgraph_build.h
index 5f9e78837a7..b4acf9b010c 100644
--- a/source/blender/depsgraph/DEG_depsgraph_build.h
+++ b/source/blender/depsgraph/DEG_depsgraph_build.h
@@ -189,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_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
index 77661b4870a..ae530cc010e 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
@@ -427,9 +427,9 @@ static int foreach_id_cow_detect_need_for_update_callback(LibraryIDLinkCallbackD
*
* 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 visbility flags, and the ones from LayerCollections too). However, this
+ * (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
- * neglectable performance improvements in typical cases.
+ * 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
diff --git a/source/blender/depsgraph/intern/depsgraph_build.cc b/source/blender/depsgraph/intern/depsgraph_build.cc
index 6c1e91d068b..9e9191c5ab9 100644
--- a/source/blender/depsgraph/intern/depsgraph_build.cc
+++ b/source/blender/depsgraph/intern/depsgraph_build.cc
@@ -62,6 +62,7 @@
#include "intern/depsgraph_registry.h"
#include "intern/depsgraph_relation.h"
+#include "intern/depsgraph_tag.h"
#include "intern/depsgraph_type.h"
/* ****************** */
@@ -109,6 +110,11 @@ 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)
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/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt
index 045adf4b380..ee5b2c549a5 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,7 +51,9 @@ set(INC
set(SRC
intern/draw_cache.c
- intern/draw_cache_extract_mesh.c
+ intern/draw_cache_extract_mesh_extractors.c
+ intern/draw_cache_extract_mesh_render_data.c
+ intern/draw_cache_extract_mesh.cc
intern/draw_cache_impl_curve.cc
intern/draw_cache_impl_displist.c
intern/draw_cache_impl_gpencil.c
@@ -146,6 +149,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
@@ -321,6 +325,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)
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_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/shaders/bsdf_common_lib.glsl b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl
index fdbb70e917d..05496ad4ab0 100644
--- a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl
@@ -138,30 +138,55 @@ void accumulate_light(vec3 light, float fac, inout vec4 accum)
/* Same thing as Cycles without the comments to make it shorter. */
vec3 ensure_valid_reflection(vec3 Ng, vec3 I, vec3 N)
{
- vec3 R;
- float NI = dot(N, I);
- float NgR, threshold;
- /* Check if the incident ray is coming from behind normal N. */
- if (NI > 0.0) {
- /* Normal reflection. */
- R = (2.0 * NI) * N - I;
- NgR = dot(Ng, R);
- /* Reflection rays may always be at least as shallow as the incoming ray. */
- threshold = min(0.9 * dot(Ng, I), 0.01);
- if (NgR >= threshold) {
- return N;
+ vec3 R = -reflect(I, N);
+
+ /* Reflection rays may always be at least as shallow as the incoming ray. */
+ float threshold = min(0.9 * dot(Ng, I), 0.025);
+ if (dot(Ng, R) >= threshold) {
+ return N;
+ }
+
+ float NdotNg = dot(N, Ng);
+ vec3 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);
+ bool valid1 = (N1_z2 > 1e-5) && (N1_z2 <= (1.0 + 1e-5));
+ bool valid2 = (N2_z2 > 1e-5) && (N2_z2 <= (1.0 + 1e-5));
+
+ vec2 N_new;
+ if (valid1 && valid2) {
+ /* If both are possible, do the expensive reflection-based check. */
+ 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;
+
+ valid1 = (R1 >= 1e-5);
+ valid2 = (R2 >= 1e-5);
+ if (valid1 && valid2) {
+ N_new = (R1 < R2) ? N1 : N2;
}
+ else {
+ N_new = (R1 > R2) ? N1 : N2;
+ }
+ }
+ else if (valid1 || valid2) {
+ float Nz2 = valid1 ? N1_z2 : N2_z2;
+ N_new = vec2(safe_sqrt(1.0 - Nz2), safe_sqrt(Nz2));
}
else {
- /* Bad incident. */
- R = -I;
- NgR = dot(Ng, R);
- threshold = 0.01;
+ return Ng;
}
- /* Lift the reflection above the threshold. */
- R = R + Ng * (threshold - NgR);
- /* Find a bisector. */
- return safe_normalize(I * length(R) + R * length(I));
+ return N_new.x * X + N_new.y * Ng;
}
/* ----------- Cone angle Approximation --------- */
diff --git a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c
index adb70f97585..af8b029a08e 100644
--- a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c
+++ b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c
@@ -274,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_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/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/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 b8f721946f2..19f822e3f68 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);
@@ -323,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 && (pd->overlay.flag & V3D_OVERLAY_MODE_TRANSFER);
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);
@@ -349,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);
}
@@ -504,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);
@@ -566,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);
@@ -605,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_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_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/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_extract.h b/source/blender/draw/intern/draw_cache_extract.h
index bbb97fc09a3..810deaec349 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,13 +68,13 @@ 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_POLY_NOR = 1 << 1,
@@ -79,12 +83,11 @@ typedef enum eMRDataType {
/** 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)
{
@@ -150,6 +153,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),
@@ -195,6 +210,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;
@@ -264,9 +283,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,
@@ -275,7 +299,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.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc
new file mode 100644
index 00000000000..fca40206659
--- /dev/null
+++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc
@@ -0,0 +1,1004 @@
+/*
+ * 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 "atomic_ops.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_scene_types.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 CHUNK_SIZE 8192
+
+namespace blender::draw {
+
+/* ---------------------------------------------------------------------- */
+/** \name Mesh Elements Extract Struct
+ * \{ */
+
+struct ExtractorRunData {
+ /* Extractor where this run data belongs to. */
+ const MeshExtract *extractor;
+ /* During iteration the VBO/IBO that is being build. */
+ void *buffer = nullptr;
+ /* User data during iteration. Created in MeshExtract.init and passed along to other MeshExtract
+ * functions. */
+ void *user_data = nullptr;
+
+ ExtractorRunData(const MeshExtract *extractor) : extractor(extractor)
+ {
+ }
+};
+
+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()
+ {
+ 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;
+ }
+
+ 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;
+ }
+};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract
+ * \{ */
+
+BLI_INLINE void extract_init(const MeshRenderData *mr,
+ struct MeshBatchCache *cache,
+ ExtractorRunDatas &extractors,
+ MeshBufferCache *mbc)
+{
+ /* Multi thread. */
+ for (ExtractorRunData &run_data : extractors) {
+ const MeshExtract *extractor = run_data.extractor;
+ run_data.buffer = mesh_extract_buffer_get(extractor, mbc);
+ run_data.user_data = extractor->init(mr, cache, run_data.buffer);
+ }
+}
+
+BLI_INLINE void extract_iter_looptri_bm(const MeshRenderData *mr,
+ const ExtractTriBMesh_Params *params,
+ const ExtractorRunDatas &all_extractors)
+{
+ ExtractorRunDatas extractors;
+ all_extractors.filter_into(extractors, MR_ITER_LOOPTRI);
+
+ EXTRACT_TRIS_LOOPTRI_FOREACH_BM_BEGIN(elt, elt_index, params)
+ {
+ for (ExtractorRunData &run_data : extractors) {
+ run_data.extractor->iter_looptri_bm(mr, elt, elt_index, run_data.user_data);
+ }
+ }
+ EXTRACT_TRIS_LOOPTRI_FOREACH_BM_END;
+}
+
+BLI_INLINE void extract_iter_looptri_mesh(const MeshRenderData *mr,
+ const ExtractTriMesh_Params *params,
+ const ExtractorRunDatas &all_extractors)
+{
+ ExtractorRunDatas extractors;
+ all_extractors.filter_into(extractors, MR_ITER_LOOPTRI);
+
+ EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_BEGIN(mlt, mlt_index, params)
+ {
+ for (ExtractorRunData &run_data : extractors) {
+ run_data.extractor->iter_looptri_mesh(mr, mlt, mlt_index, run_data.user_data);
+ }
+ }
+ EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END;
+}
+
+BLI_INLINE void extract_iter_poly_bm(const MeshRenderData *mr,
+ const ExtractPolyBMesh_Params *params,
+ const ExtractorRunDatas &all_extractors)
+{
+ ExtractorRunDatas extractors;
+ all_extractors.filter_into(extractors, MR_ITER_POLY);
+
+ EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr)
+ {
+ for (ExtractorRunData &run_data : extractors) {
+ run_data.extractor->iter_poly_bm(mr, f, f_index, run_data.user_data);
+ }
+ }
+ EXTRACT_POLY_FOREACH_BM_END;
+}
+
+BLI_INLINE void extract_iter_poly_mesh(const MeshRenderData *mr,
+ const ExtractPolyMesh_Params *params,
+ const ExtractorRunDatas &all_extractors)
+{
+ ExtractorRunDatas extractors;
+ all_extractors.filter_into(extractors, MR_ITER_POLY);
+
+ EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr)
+ {
+ for (ExtractorRunData &run_data : extractors) {
+ run_data.extractor->iter_poly_mesh(mr, mp, mp_index, run_data.user_data);
+ }
+ }
+ EXTRACT_POLY_FOREACH_MESH_END;
+}
+
+BLI_INLINE void extract_iter_ledge_bm(const MeshRenderData *mr,
+ const ExtractLEdgeBMesh_Params *params,
+ const ExtractorRunDatas &all_extractors)
+{
+ ExtractorRunDatas extractors;
+ all_extractors.filter_into(extractors, MR_ITER_LEDGE);
+
+ EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params)
+ {
+ for (ExtractorRunData &run_data : extractors) {
+ run_data.extractor->iter_ledge_bm(mr, eed, ledge_index, run_data.user_data);
+ }
+ }
+ EXTRACT_LEDGE_FOREACH_BM_END;
+}
+
+BLI_INLINE void extract_iter_ledge_mesh(const MeshRenderData *mr,
+ const ExtractLEdgeMesh_Params *params,
+ const ExtractorRunDatas &all_extractors)
+{
+ ExtractorRunDatas extractors;
+ all_extractors.filter_into(extractors, MR_ITER_LEDGE);
+
+ EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr)
+ {
+ for (ExtractorRunData &run_data : extractors) {
+ run_data.extractor->iter_ledge_mesh(mr, med, ledge_index, run_data.user_data);
+ }
+ }
+ EXTRACT_LEDGE_FOREACH_MESH_END;
+}
+
+BLI_INLINE void extract_iter_lvert_bm(const MeshRenderData *mr,
+ const ExtractLVertBMesh_Params *params,
+ const ExtractorRunDatas &all_extractors)
+{
+ ExtractorRunDatas extractors;
+ all_extractors.filter_into(extractors, MR_ITER_LVERT);
+
+ EXTRACT_LVERT_FOREACH_BM_BEGIN(eve, lvert_index, params)
+ {
+ for (ExtractorRunData &run_data : extractors) {
+ run_data.extractor->iter_lvert_bm(mr, eve, lvert_index, run_data.user_data);
+ }
+ }
+ EXTRACT_LVERT_FOREACH_BM_END;
+}
+
+BLI_INLINE void extract_iter_lvert_mesh(const MeshRenderData *mr,
+ const ExtractLVertMesh_Params *params,
+ const ExtractorRunDatas &all_extractors)
+{
+ ExtractorRunDatas extractors;
+ all_extractors.filter_into(extractors, MR_ITER_LVERT);
+
+ EXTRACT_LVERT_FOREACH_MESH_BEGIN(mv, lvert_index, params, mr)
+ {
+ for (ExtractorRunData &run_data : extractors) {
+ run_data.extractor->iter_lvert_mesh(mr, mv, lvert_index, run_data.user_data);
+ }
+ }
+ EXTRACT_LVERT_FOREACH_MESH_END;
+}
+
+BLI_INLINE void extract_finish(const MeshRenderData *mr,
+ struct MeshBatchCache *cache,
+ const ExtractorRunDatas &extractors)
+{
+ for (const ExtractorRunData &run_data : extractors) {
+ const MeshExtract *extractor = run_data.extractor;
+ if (extractor->finish) {
+ extractor->finish(mr, cache, run_data.buffer, run_data.user_data);
+ }
+ }
+}
+
+/* Single Thread. */
+BLI_INLINE void extract_run_and_finish_init(const MeshRenderData *mr,
+ struct MeshBatchCache *cache,
+ ExtractorRunDatas &extractors,
+ eMRIterType iter_type,
+ MeshBufferCache *mbc)
+{
+ extract_init(mr, cache, extractors, mbc);
+
+ bool is_mesh = mr->extract_type != MR_EXTRACT_BMESH;
+ if (iter_type & MR_ITER_LOOPTRI) {
+ if (is_mesh) {
+ ExtractTriMesh_Params params;
+ params.mlooptri = mr->mlooptri;
+ params.tri_range[0] = 0;
+ params.tri_range[1] = mr->tri_len;
+ extract_iter_looptri_mesh(mr, &params, extractors);
+ }
+ else {
+ ExtractTriBMesh_Params params;
+ params.looptris = mr->edit_bmesh->looptris;
+ params.tri_range[0] = 0;
+ params.tri_range[1] = mr->tri_len;
+ extract_iter_looptri_bm(mr, &params, extractors);
+ }
+ }
+ if (iter_type & MR_ITER_POLY) {
+ if (is_mesh) {
+ ExtractPolyMesh_Params params;
+ params.poly_range[0] = 0;
+ params.poly_range[1] = mr->poly_len;
+ extract_iter_poly_mesh(mr, &params, extractors);
+ }
+ else {
+ ExtractPolyBMesh_Params params;
+ params.poly_range[0] = 0;
+ params.poly_range[1] = mr->poly_len;
+ extract_iter_poly_bm(mr, &params, extractors);
+ }
+ }
+ if (iter_type & MR_ITER_LEDGE) {
+ if (is_mesh) {
+ ExtractLEdgeMesh_Params params;
+ params.ledge = mr->ledges;
+ params.ledge_range[0] = 0;
+ params.ledge_range[1] = mr->edge_loose_len;
+ extract_iter_ledge_mesh(mr, &params, extractors);
+ }
+ else {
+ ExtractLEdgeBMesh_Params params;
+ params.ledge = mr->ledges;
+ params.ledge_range[0] = 0;
+ params.ledge_range[1] = mr->edge_loose_len;
+ extract_iter_ledge_bm(mr, &params, extractors);
+ }
+ }
+ if (iter_type & MR_ITER_LVERT) {
+ if (is_mesh) {
+ ExtractLVertMesh_Params params;
+ params.lvert = mr->lverts;
+ params.lvert_range[0] = 0;
+ params.lvert_range[1] = mr->vert_loose_len;
+ extract_iter_lvert_mesh(mr, &params, extractors);
+ }
+ else {
+ ExtractLVertBMesh_Params params;
+ params.lvert = mr->lverts;
+ params.lvert_range[0] = 0;
+ params.lvert_range[1] = mr->vert_loose_len;
+ extract_iter_lvert_bm(mr, &params, extractors);
+ }
+ }
+ extract_finish(mr, cache, extractors);
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name ExtractTaskData
+ * \{ */
+struct ExtractTaskData {
+ const MeshRenderData *mr = nullptr;
+ MeshBatchCache *cache = nullptr;
+ /* #UserData 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. */
+
+ ExtractorRunDatas *extractors = nullptr;
+ MeshBufferCache *mbc = nullptr;
+ int32_t *task_counter = nullptr;
+
+ eMRIterType iter_type;
+ int start = 0;
+ int end = INT_MAX;
+ /** Decremented each time a task is finished. */
+
+ ExtractTaskData(const MeshRenderData *mr,
+ struct MeshBatchCache *cache,
+ ExtractorRunDatas *extractors,
+ MeshBufferCache *mbc,
+ int32_t *task_counter)
+ : mr(mr), cache(cache), extractors(extractors), mbc(mbc), task_counter(task_counter)
+ {
+ 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 ExtractTaskData *extract_extract_iter_task_data_create_mesh(const MeshRenderData *mr,
+ MeshBatchCache *cache,
+ ExtractorRunDatas *extractors,
+ MeshBufferCache *mbc,
+ int32_t *task_counter)
+
+{
+ ExtractTaskData *taskdata = new ExtractTaskData(mr, cache, extractors, mbc, task_counter);
+ return taskdata;
+}
+
+static void extract_task_data_free(void *data)
+{
+ ExtractTaskData *task_data = static_cast<ExtractTaskData *>(data);
+ delete task_data;
+}
+
+static void extract_task_data_free_ex(void *data)
+{
+ ExtractTaskData *task_data = static_cast<ExtractTaskData *>(data);
+ task_data->extractors = nullptr;
+ delete task_data;
+}
+
+BLI_INLINE void mesh_extract_iter(const MeshRenderData *mr,
+ const eMRIterType iter_type,
+ int start,
+ int end,
+ ExtractorRunDatas &extractors)
+{
+ switch (mr->extract_type) {
+ case MR_EXTRACT_BMESH:
+ if (iter_type & MR_ITER_LOOPTRI) {
+ ExtractTriBMesh_Params params;
+ params.looptris = mr->edit_bmesh->looptris;
+ params.tri_range[0] = start;
+ params.tri_range[1] = min_ii(mr->tri_len, end);
+ extract_iter_looptri_bm(mr, &params, extractors);
+ }
+ if (iter_type & MR_ITER_POLY) {
+ ExtractPolyBMesh_Params params;
+ params.poly_range[0] = start;
+ params.poly_range[1] = min_ii(mr->poly_len, end);
+ extract_iter_poly_bm(mr, &params, extractors);
+ }
+ if (iter_type & MR_ITER_LEDGE) {
+ ExtractLEdgeBMesh_Params params;
+ params.ledge = mr->ledges;
+ params.ledge_range[0] = start;
+ params.ledge_range[1] = min_ii(mr->edge_loose_len, end);
+ extract_iter_ledge_bm(mr, &params, extractors);
+ }
+ if (iter_type & MR_ITER_LVERT) {
+ ExtractLVertBMesh_Params params;
+ params.lvert = mr->lverts;
+ params.lvert_range[0] = start;
+ params.lvert_range[1] = min_ii(mr->vert_loose_len, end);
+ extract_iter_lvert_bm(mr, &params, extractors);
+ }
+ break;
+ case MR_EXTRACT_MAPPED:
+ case MR_EXTRACT_MESH:
+ if (iter_type & MR_ITER_LOOPTRI) {
+ ExtractTriMesh_Params params;
+ params.mlooptri = mr->mlooptri;
+ params.tri_range[0] = start;
+ params.tri_range[1] = min_ii(mr->tri_len, end);
+ extract_iter_looptri_mesh(mr, &params, extractors);
+ }
+ if (iter_type & MR_ITER_POLY) {
+ ExtractPolyMesh_Params params;
+ params.poly_range[0] = start;
+ params.poly_range[1] = min_ii(mr->poly_len, end);
+ extract_iter_poly_mesh(mr, &params, extractors);
+ }
+ if (iter_type & MR_ITER_LEDGE) {
+ ExtractLEdgeMesh_Params params;
+ params.ledge = mr->ledges;
+ params.ledge_range[0] = start;
+ params.ledge_range[1] = min_ii(mr->edge_loose_len, end);
+ extract_iter_ledge_mesh(mr, &params, extractors);
+ }
+ if (iter_type & MR_ITER_LVERT) {
+ ExtractLVertMesh_Params params;
+ params.lvert = mr->lverts;
+ params.lvert_range[0] = start;
+ params.lvert_range[1] = min_ii(mr->vert_loose_len, end);
+ extract_iter_lvert_mesh(mr, &params, extractors);
+ }
+ break;
+ }
+}
+
+static void extract_task_init(ExtractTaskData *data)
+{
+ extract_init(data->mr, data->cache, *data->extractors, data->mbc);
+}
+
+static void extract_task_run(void *__restrict taskdata)
+{
+ ExtractTaskData *data = (ExtractTaskData *)taskdata;
+ mesh_extract_iter(data->mr, data->iter_type, data->start, data->end, *data->extractors);
+
+ /* 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) {
+ extract_finish(data->mr, data->cache, *data->extractors);
+ }
+}
+
+static void extract_task_init_and_run(void *__restrict taskdata)
+{
+ ExtractTaskData *data = (ExtractTaskData *)taskdata;
+ extract_run_and_finish_init(
+ data->mr, data->cache, *data->extractors, data->iter_type, data->mbc);
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Task Node - Update Mesh Render Data
+ * \{ */
+struct MeshRenderDataUpdateTaskData {
+ MeshRenderData *mr = nullptr;
+ eMRIterType iter_type;
+ eMRDataType 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(MeshRenderDataUpdateTaskData *taskdata)
+{
+ 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);
+ 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
+ * \{ */
+
+static struct TaskNode *extract_single_threaded_task_node_create(struct TaskGraph *task_graph,
+ ExtractTaskData *task_data)
+{
+ struct TaskNode *task_node = BLI_task_graph_node_create(
+ task_graph,
+ extract_task_init_and_run,
+ task_data,
+ (TaskGraphNodeFreeFunction)extract_task_data_free);
+ return task_node;
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Task Node - UserData Initializer
+ * \{ */
+struct UserDataInitTaskData {
+ ExtractTaskData *td;
+ int32_t task_counter = 0;
+
+ ~UserDataInitTaskData()
+ {
+ extract_task_data_free(td);
+ }
+
+#ifdef WITH_CXX_GUARDEDALLOC
+ MEM_CXX_CLASS_ALLOC_FUNCS("DRW:UserDataInitTaskData")
+#endif
+};
+
+static void user_data_init_task_data_free(UserDataInitTaskData *taskdata)
+{
+ delete taskdata;
+}
+
+static void user_data_init_task_data_exec(void *__restrict task_data)
+{
+ UserDataInitTaskData *extract_task_data = static_cast<UserDataInitTaskData *>(task_data);
+ ExtractTaskData *taskdata_base = extract_task_data->td;
+ extract_task_init(taskdata_base);
+}
+
+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 = new ExtractTaskData(*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_task_run, taskdata, extract_task_data_free_ex);
+ BLI_task_graph_edge_create(task_node_user_data_init, task_node);
+}
+
+static int extract_range_task_num_elements_get(const MeshRenderData *mr,
+ const eMRIterType iter_type)
+{
+ /* Divide task into sensible chunks. */
+ int iter_len = 0;
+ if (iter_type & MR_ITER_LOOPTRI) {
+ iter_len += mr->tri_len;
+ }
+ if (iter_type & MR_ITER_POLY) {
+ iter_len += mr->poly_len;
+ }
+ if (iter_type & MR_ITER_LEDGE) {
+ iter_len += mr->edge_loose_len;
+ }
+ if (iter_type & MR_ITER_LVERT) {
+ iter_len += mr->vert_loose_len;
+ }
+ return iter_len;
+}
+
+static int extract_range_task_chunk_size_get(const MeshRenderData *mr,
+ const eMRIterType iter_type,
+ const int num_threads)
+{
+ /* Divide task into sensible chunks. */
+ const int num_elements = extract_range_task_num_elements_get(mr, iter_type);
+ int range_len = (num_elements + num_threads) / num_threads;
+ CLAMP_MIN(range_len, CHUNK_SIZE);
+ return range_len;
+}
+
+static void extract_task_in_ranges_create(struct TaskGraph *task_graph,
+ struct TaskNode *task_node_user_data_init,
+ ExtractTaskData *taskdata_base,
+ const int num_threads)
+{
+ const MeshRenderData *mr = taskdata_base->mr;
+ const int range_len = extract_range_task_chunk_size_get(
+ mr, taskdata_base->iter_type, num_threads);
+
+ if (taskdata_base->iter_type & MR_ITER_LOOPTRI) {
+ for (int i = 0; i < mr->tri_len; i += range_len) {
+ extract_range_task_create(
+ task_graph, task_node_user_data_init, taskdata_base, MR_ITER_LOOPTRI, i, range_len);
+ }
+ }
+ if (taskdata_base->iter_type & MR_ITER_POLY) {
+ for (int i = 0; i < mr->poly_len; i += range_len) {
+ extract_range_task_create(
+ task_graph, task_node_user_data_init, taskdata_base, MR_ITER_POLY, i, range_len);
+ }
+ }
+ if (taskdata_base->iter_type & MR_ITER_LEDGE) {
+ for (int i = 0; i < mr->edge_loose_len; i += range_len) {
+ extract_range_task_create(
+ task_graph, task_node_user_data_init, taskdata_base, MR_ITER_LEDGE, i, range_len);
+ }
+ }
+ if (taskdata_base->iter_type & MR_ITER_LVERT) {
+ for (int i = 0; i < mr->vert_loose_len; i += range_len) {
+ extract_range_task_create(
+ task_graph, task_node_user_data_init, taskdata_base, MR_ITER_LVERT, i, range_len);
+ }
+ }
+}
+
+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();
+
+ /* 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); \
+ 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) > CHUNK_SIZE;
+
+ if (use_thread) {
+ uint single_threaded_extractors_len = 0;
+
+ /* 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);
+ ExtractTaskData *taskdata = extract_extract_iter_task_data_create_mesh(
+ mr, cache, single_threaded_extractors, mbc, nullptr);
+ struct TaskNode *task_node = extract_single_threaded_task_node_create(task_graph,
+ taskdata);
+ BLI_task_graph_edge_create(task_node_mesh_render_data, task_node);
+ single_threaded_extractors_len++;
+ }
+ }
+
+ /* 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()) {
+ /*
+ * Determine the number of thread to use for multithreading.
+ * Thread can be used for single threaded tasks. These typically take longer to execute so
+ * fill the rest of the threads for range operations.
+ */
+ int num_threads = BLI_task_scheduler_num_threads();
+ num_threads -= single_threaded_extractors_len % num_threads;
+
+ UserDataInitTaskData *user_data_init_task_data = new UserDataInitTaskData();
+ struct TaskNode *task_node_user_data_init = user_data_init_task_node_create(
+ task_graph, user_data_init_task_data);
+
+ user_data_init_task_data->td = extract_extract_iter_task_data_create_mesh(
+ mr, cache, multi_threaded_extractors, mbc, &user_data_init_task_data->task_counter);
+
+ extract_task_in_ranges_create(
+ task_graph, task_node_user_data_init, user_data_init_task_data->td, num_threads);
+
+ BLI_task_graph_edge_create(task_node_mesh_render_data, task_node_user_data_init);
+ }
+ 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);
+ ExtractTaskData *taskdata = extract_extract_iter_task_data_create_mesh(
+ mr, cache, extractors_copy, mbc, nullptr);
+
+ struct TaskNode *task_node = extract_single_threaded_task_node_create(task_graph, taskdata);
+ 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.c b/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c
index f167ea3d540..0a3945291bb 100644
--- a/source/blender/draw/intern/draw_cache_extract_mesh.c
+++ b/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c
@@ -13,7 +13,7 @@
* 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.
+ * The Original Code is Copyright (C) 2021 by Blender Foundation.
* All rights reserved.
*/
@@ -25,773 +25,82 @@
#include "MEM_guardedalloc.h"
-#include "BLI_alloca.h"
-#include "BLI_bitmap.h"
-#include "BLI_buffer.h"
+#include "atomic_ops.h"
+
+#include "DNA_object_types.h"
+
#include "BLI_edgehash.h"
#include "BLI_jitter_2d.h"
-#include "BLI_math_bits.h"
-#include "BLI_math_vector.h"
+#include "BLI_kdopbvh.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 "ED_uvedit.h"
-#include "GPU_batch.h"
#include "GPU_capabilities.h"
-#include "DRW_render.h"
-
-#include "ED_mesh.h"
-#include "ED_uvedit.h"
-
+#include "draw_cache_extract_mesh_private.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)
+void *mesh_extract_buffer_get(const MeshExtract *extractor, MeshBufferCache *mbc)
{
- 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);
+ /* 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;
}
-BLI_INLINE BMFace *bm_original_face_get(const MeshRenderData *mr, int idx)
+eMRIterType mesh_extract_iter_type(const MeshExtract *ext)
{
- 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;
+ 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 Mesh Elements Extract: Loop Triangles
+/** \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.
* \{ */
-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 \
- } \
- } \
+static const MeshExtract *mesh_extract_override_hq_normals(const MeshExtract *extractor)
+{
+ if (extractor == &extract_pos_nor) {
+ return &extract_pos_nor_hq;
}
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \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 \
- } \
- } \
+ if (extractor == &extract_lnor) {
+ return &extract_lnor_hq;
}
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \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 \
- } \
- } \
+ if (extractor == &extract_tan) {
+ return &extract_tan_hq;
}
+ if (extractor == &extract_fdots_nor) {
+ return &extract_fdots_nor_hq;
+ }
+ return extractor;
+}
-/** \} */
-
-/* ---------------------------------------------------------------------- */
-/** \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)
+const MeshExtract *mesh_extract_override_get(const MeshExtract *extractor,
+ const bool do_hq_normals)
{
- 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;
+ if (do_hq_normals) {
+ extractor = mesh_extract_override_hq_normals(extractor);
+ }
+ return extractor;
}
/** \} */
@@ -855,69 +164,65 @@ static void *extract_tris_init(const MeshRenderData *mr,
}
static void extract_tris_iter_looptri_bm(const MeshRenderData *mr,
- const struct ExtractTriBMesh_Params *params,
+ BMLoop **elt,
+ const int UNUSED(elt_index),
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]));
- }
+
+ 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,
+ const MLoopTri *mlt,
+ const int UNUSED(elt_index),
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]);
- }
+ 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 *buf,
void *_data)
{
+ GPUIndexBuf *ibo = buf;
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;
+ 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->tris_per_mat[i] == NULL) {
- mbc->tris_per_mat[i] = GPU_indexbuf_calloc();
+ if (mbc_final->tris_per_mat[i] == NULL) {
+ 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->tris_per_mat[i], ibo, start, len);
+ GPU_indexbuf_create_subrange_in_place(mbc_final->tris_per_mat[i], ibo, start, len);
}
}
MEM_freeN(data->tri_mat_start);
@@ -925,13 +230,14 @@ static void extract_tris_finish(const MeshRenderData *mr,
MEM_freeN(data);
}
-static const MeshExtract extract_tris = {
+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,
+ .data_type = 0,
.use_threading = false,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.tris),
};
/** \} */
@@ -951,138 +257,124 @@ static void *extract_lines_init(const MeshRenderData *mr,
return elb;
}
-static void extract_lines_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
+static void extract_lines_iter_poly_bm(const MeshRenderData *UNUSED(mr),
+ BMFace *f,
+ const int UNUSED(f_index),
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;
+ 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 ExtractPolyMesh_Params *params,
+ const MPoly *mp,
+ const int UNUSED(mp_index),
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;
+ 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 {
- 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;
+ 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 ExtractLEdgeBMesh_Params *params,
+ BMEdge *eed,
+ const int ledge_index,
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));
+ 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);
}
- EXTRACT_LEDGE_FOREACH_BM_END;
+ /* 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 ExtractLEdgeMesh_Params *params,
+ const MEdge *med,
+ const uint ledge_index,
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);
+ 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);
}
- EXTRACT_LEDGE_FOREACH_MESH_END;
+ /* Don't render the edge twice. */
+ GPU_indexbuf_set_line_restart(elb, e_index);
}
static void extract_lines_finish(const MeshRenderData *UNUSED(mr),
struct MeshBatchCache *UNUSED(cache),
- void *ibo,
+ void *buf,
void *elb)
{
+ GPUIndexBuf *ibo = buf;
GPU_indexbuf_build_in_place(elb, ibo);
MEM_freeN(elb);
}
-static const MeshExtract extract_lines = {
+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,
+ .data_type = 0,
.use_threading = false,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines),
};
+
/** \} */
/* ---------------------------------------------------------------------- */
-/** \name Extract Loose Edges Sub Buffer
+/** \name Extract Lines and Loose Edges Sub Buffer
* \{ */
static void extract_lines_loose_subbuffer(const MeshRenderData *mr, struct MeshBatchCache *cache)
@@ -1098,24 +390,47 @@ static void extract_lines_loose_subbuffer(const MeshRenderData *mr, struct MeshB
static void extract_lines_with_lines_loose_finish(const MeshRenderData *mr,
struct MeshBatchCache *cache,
- void *ibo,
+ void *buf,
void *elb)
{
+ GPUIndexBuf *ibo = buf;
GPU_indexbuf_build_in_place(elb, ibo);
extract_lines_loose_subbuffer(mr, cache);
MEM_freeN(elb);
}
-static const MeshExtract extract_lines_with_lines_loose = {
+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,
+ .data_type = 0,
.use_threading = false,
-};
+ .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines)};
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Extract Loose Edges Sub Buffer
+ * \{ */
+
+static void *extract_lines_loose_only_init(const MeshRenderData *mr,
+ struct MeshBatchCache *cache,
+ void *buf)
+{
+ BLI_assert(buf == cache->final.ibo.lines_loose);
+ UNUSED_VARS_NDEBUG(buf);
+ extract_lines_loose_subbuffer(mr, cache);
+ return NULL;
+}
+
+const MeshExtract extract_lines_loose_only = {
+ .init = extract_lines_loose_only_init,
+ .data_type = 0,
+ .use_threading = false,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines_loose)};
/** \} */
@@ -1159,86 +474,81 @@ BLI_INLINE void vert_set_mesh(GPUIndexBufBuilder *elb,
}
}
-static void extract_points_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
+static void extract_points_iter_poly_bm(const MeshRenderData *UNUSED(mr),
+ BMFace *f,
+ const int UNUSED(f_index),
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);
+ 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 ExtractPolyMesh_Params *params,
+ const MPoly *mp,
+ const int UNUSED(mp_index),
void *elb)
{
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
+ 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);
}
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
}
static void extract_points_iter_ledge_bm(const MeshRenderData *mr,
- const ExtractLEdgeBMesh_Params *params,
+ BMEdge *eed,
+ const int ledge_index,
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;
+ 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 ExtractLEdgeMesh_Params *params,
+ const MEdge *med,
+ const uint ledge_index,
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;
+ 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 ExtractLVertBMesh_Params *params,
+ BMVert *eve,
+ const int lvert_index,
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;
+ vert_set_bm(elb, eve, offset + lvert_index);
}
static void extract_points_iter_lvert_mesh(const MeshRenderData *mr,
- const ExtractLVertMesh_Params *params,
+ const MVert *UNUSED(mv),
+ const int lvert_index,
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;
+
+ vert_set_mesh(elb, mr, mr->lverts[lvert_index], offset + lvert_index);
}
static void extract_points_finish(const MeshRenderData *UNUSED(mr),
struct MeshBatchCache *UNUSED(cache),
- void *ibo,
+ void *buf,
void *elb)
{
+ GPUIndexBuf *ibo = buf;
GPU_indexbuf_build_in_place(elb, ibo);
MEM_freeN(elb);
}
-static const MeshExtract extract_points = {
+const MeshExtract extract_points = {
.init = extract_points_init,
.iter_poly_bm = extract_points_iter_poly_bm,
.iter_poly_mesh = extract_points_iter_poly_mesh,
@@ -1247,14 +557,15 @@ static const MeshExtract extract_points = {
.iter_lvert_bm = extract_points_iter_lvert_bm,
.iter_lvert_mesh = extract_points_iter_lvert_mesh,
.finish = extract_points_finish,
- .data_flag = 0,
+ .data_type = 0,
.use_threading = false,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.points),
};
/** \} */
/* ---------------------------------------------------------------------- */
-/** \name Extract Facedots Indices
+/** \name Extract Face-dots Indices
* \{ */
static void *extract_fdots_init(const MeshRenderData *mr,
@@ -1266,70 +577,66 @@ static void *extract_fdots_init(const MeshRenderData *mr,
return elb;
}
-static void extract_fdots_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
+static void extract_fdots_iter_poly_bm(const MeshRenderData *UNUSED(mr),
+ BMFace *f,
+ const int f_index,
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);
- }
+ 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,
+ const MPoly *mp,
+ const int mp_index,
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 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);
- }
- else {
- GPU_indexbuf_set_point_restart(elb, mp_index);
+ return;
}
}
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
+ GPU_indexbuf_set_point_restart(elb, mp_index);
}
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);
- }
+ 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 *buf,
void *elb)
{
+ GPUIndexBuf *ibo = buf;
GPU_indexbuf_build_in_place(elb, ibo);
MEM_freeN(elb);
}
-static const MeshExtract extract_fdots = {
+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,
+ .data_type = 0,
.use_threading = false,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.fdots),
};
/** \} */
@@ -1346,7 +653,7 @@ typedef struct MeshExtract_LinePaintMask_Data {
static void *extract_lines_paint_mask_init(const MeshRenderData *mr,
struct MeshBatchCache *UNUSED(cache),
- void *UNUSED(buf))
+ void *UNUSED(ibo))
{
size_t bitmap_size = BLI_BITMAP_SIZE(mr->edge_len);
MeshExtract_LinePaintMask_Data *data = MEM_callocN(sizeof(*data) + bitmap_size, __func__);
@@ -1355,12 +662,16 @@ static void *extract_lines_paint_mask_init(const MeshRenderData *mr,
}
static void extract_lines_paint_mask_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
+ const MPoly *mp,
+ const int UNUSED(mp_index),
void *_data)
{
MeshExtract_LinePaintMask_Data *data = _data;
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
+ 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)) ||
@@ -1390,26 +701,26 @@ static void extract_lines_paint_mask_iter_poly_mesh(const MeshRenderData *mr,
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 *buf,
void *_data)
{
+ GPUIndexBuf *ibo = buf;
MeshExtract_LinePaintMask_Data *data = _data;
-
GPU_indexbuf_build_in_place(&data->elb, ibo);
MEM_freeN(data);
}
-static const MeshExtract extract_lines_paint_mask = {
+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,
+ .data_type = 0,
.use_threading = false,
-};
+ .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines_paint_mask)};
/** \} */
@@ -1487,49 +798,44 @@ BLI_INLINE void lines_adjacency_triangle(
}
static void extract_lines_adjacency_iter_looptri_bm(const MeshRenderData *UNUSED(mr),
- const struct ExtractTriBMesh_Params *params,
+ BMLoop **elt,
+ const int UNUSED(elt_index),
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);
- }
+ 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,
+ const MLoopTri *mlt,
+ const int UNUSED(elt_index),
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);
- }
+ 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 *buf,
void *_data)
{
+ GPUIndexBuf *ibo = buf;
MeshExtract_LineAdjacency_Data *data = _data;
/* Create edges for remaining non manifold edges. */
EdgeHashIterator *ehi = BLI_edgehashIterator_new(data->eh);
@@ -1559,14 +865,14 @@ static void extract_lines_adjacency_finish(const MeshRenderData *UNUSED(mr),
#undef NO_EDGE
-static const MeshExtract extract_lines_adjacency = {
+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,
+ .data_type = 0,
.use_threading = false,
-};
+ .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines_adjacency)};
/** \} */
@@ -1598,56 +904,51 @@ BLI_INLINE void edituv_tri_add(
}
static void extract_edituv_tris_iter_looptri_bm(const MeshRenderData *UNUSED(mr),
- const struct ExtractTriBMesh_Params *params,
+ BMLoop **elt,
+ const int UNUSED(elt_index),
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;
+ 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 struct ExtractTriMesh_Params *params,
+ const MLoopTri *mlt,
+ const int UNUSED(elt_index),
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;
+ 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 *ibo,
+ void *buf,
void *data)
{
+ GPUIndexBuf *ibo = buf;
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 = {
+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,
+ .data_type = 0,
.use_threading = false,
-};
+ .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_tris)};
/** \} */
@@ -1674,27 +975,34 @@ BLI_INLINE void edituv_edge_add(
}
}
-static void extract_edituv_lines_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
+static void extract_edituv_lines_iter_poly_bm(const MeshRenderData *UNUSED(mr),
+ BMFace *f,
+ const int UNUSED(f_index),
void *data)
{
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(loop, l_index, params, mr)
- {
+ 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(loop->f, BM_ELEM_HIDDEN),
- BM_elem_flag_test(loop->f, BM_ELEM_SELECT),
+ BM_elem_flag_test_bool(f, BM_ELEM_HIDDEN),
+ BM_elem_flag_test_bool(f, BM_ELEM_SELECT),
l_index,
- BM_elem_index_get(loop->next));
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(loop);
+ 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 ExtractPolyMesh_Params *params,
+ const MPoly *mp,
+ const int UNUSED(mp_index),
void *data)
{
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
+ 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 == NULL || mr->e_origindex[ml->e] != ORIGINDEX_NONE);
@@ -1704,27 +1012,27 @@ static void extract_edituv_lines_iter_poly_mesh(const MeshRenderData *mr,
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 *buf,
void *data)
{
+ GPUIndexBuf *ibo = buf;
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 = {
+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,
+ .data_type = 0,
.use_threading = false,
-};
+ .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_lines)};
/** \} */
@@ -1753,57 +1061,62 @@ BLI_INLINE void edituv_point_add(MeshExtract_EditUvElem_Data *data,
}
}
-static void extract_edituv_points_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
+static void extract_edituv_points_iter_poly_bm(const MeshRenderData *UNUSED(mr),
+ BMFace *f,
+ const int UNUSED(f_index),
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);
+ 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 ExtractPolyMesh_Params *params,
+ const MPoly *mp,
+ const int UNUSED(mp_index),
void *data)
{
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
+ 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);
}
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
}
static void extract_edituv_points_finish(const MeshRenderData *UNUSED(mr),
struct MeshBatchCache *UNUSED(cache),
- void *ibo,
+ void *buf,
void *data)
{
+ GPUIndexBuf *ibo = buf;
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 = {
+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,
+ .data_type = 0,
.use_threading = false,
-};
+ .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_points)};
/** \} */
/* ---------------------------------------------------------------------- */
-/** \name Extract Edit UV Facedots Indices
+/** \name Extract Edit UV Face-dots Indices
* \{ */
static void *extract_edituv_fdots_init(const MeshRenderData *mr,
@@ -1830,26 +1143,29 @@ BLI_INLINE void edituv_facedot_add(MeshExtract_EditUvElem_Data *data,
}
}
-static void extract_edituv_fdots_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
+static void extract_edituv_fdots_iter_poly_bm(const MeshRenderData *UNUSED(mr),
+ BMFace *f,
+ const int f_index,
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;
+ 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 ExtractPolyMesh_Params *params,
+ const MPoly *mp,
+ const int mp_index,
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 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 ||
@@ -1859,40 +1175,34 @@ static void extract_edituv_fdots_iter_poly_mesh(const MeshRenderData *mr,
(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;
+ 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 *ibo,
+ void *buf,
void *_data)
{
+ GPUIndexBuf *ibo = buf;
MeshExtract_EditUvElem_Data *data = _data;
GPU_indexbuf_build_in_place(&data->elb, ibo);
MEM_freeN(data);
}
-static const MeshExtract extract_edituv_fdots = {
+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,
+ .data_type = 0,
.use_threading = false,
-};
+ .mesh_buffer_offset = offsetof(MeshBufferCache, ibo.edituv_fdots)};
/** \} */
@@ -1914,6 +1224,7 @@ static void *extract_pos_nor_init(const MeshRenderData *mr,
struct MeshBatchCache *UNUSED(cache),
void *buf)
{
+ GPUVertBuf *vbo = buf;
static GPUVertFormat format = {0};
if (format.attr_len == 0) {
/* WARNING Adjust #PosNorLoop struct accordingly. */
@@ -1921,7 +1232,6 @@ static void *extract_pos_nor_init(const MeshRenderData *mr,
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);
@@ -1949,28 +1259,34 @@ static void *extract_pos_nor_init(const MeshRenderData *mr,
}
static void extract_pos_nor_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
+ BMFace *f,
+ const int UNUSED(f_index),
void *_data)
{
MeshExtract_PosNor_Data *data = _data;
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
- {
+ 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->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);
+ 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 ExtractPolyMesh_Params *params,
+ const MPoly *mp,
+ const int UNUSED(mp_index),
void *_data)
{
MeshExtract_PosNor_Data *data = _data;
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
+
+ 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);
@@ -1988,85 +1304,75 @@ static void extract_pos_nor_iter_poly_mesh(const MeshRenderData *mr,
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,
+ BMEdge *eed,
+ const int ledge_index,
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;
+
+ 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 ExtractLEdgeMesh_Params *params,
+ const MEdge *med,
+ const uint ledge_index,
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;
+ 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 ExtractLVertBMesh_Params *params,
+ BMVert *eve,
+ const int lvert_index,
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;
+
+ 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 ExtractLVertMesh_Params *params,
+ 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);
- 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;
+
+ 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(vbo),
+ void *UNUSED(buf),
void *data)
{
MEM_freeN(data);
}
-static const MeshExtract extract_pos_nor = {
+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,
@@ -2075,9 +1381,11 @@ static const MeshExtract extract_pos_nor = {
.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,
+ .data_type = 0,
.use_threading = true,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.pos_nor),
};
+
/** \} */
/* ---------------------------------------------------------------------- */
@@ -2098,6 +1406,7 @@ static void *extract_pos_nor_hq_init(const MeshRenderData *mr,
struct MeshBatchCache *UNUSED(cache),
void *buf)
{
+ GPUVertBuf *vbo = buf;
static GPUVertFormat format = {0};
if (format.attr_len == 0) {
/* WARNING Adjust #PosNorHQLoop struct accordingly. */
@@ -2105,7 +1414,6 @@ static void *extract_pos_nor_hq_init(const MeshRenderData *mr,
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);
@@ -2133,29 +1441,35 @@ static void *extract_pos_nor_hq_init(const MeshRenderData *mr,
}
static void extract_pos_nor_hq_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
+ BMFace *f,
+ const int UNUSED(f_index),
void *_data)
{
MeshExtract_PosNorHQ_Data *data = _data;
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
- {
+ 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->v));
- copy_v3_v3_short(vert->nor, data->normals[BM_elem_index_get(l->v)].high);
+ 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->f;
+ BMFace *efa = l_iter->f;
vert->nor[3] = BM_elem_flag_test(efa, BM_ELEM_HIDDEN) ? -1 : 0;
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
+ } while ((l_iter = l_iter->next) != l_first);
}
static void extract_pos_nor_hq_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
+ const MPoly *mp,
+ const int UNUSED(mp_index),
void *_data)
{
MeshExtract_PosNorHQ_Data *data = _data;
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
+ 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);
@@ -2174,91 +1488,80 @@ static void extract_pos_nor_hq_iter_poly_mesh(const MeshRenderData *mr,
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,
+ BMEdge *eed,
+ const int ledge_index,
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;
+ 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 ExtractLEdgeMesh_Params *params,
+ const MEdge *med,
+ const uint ledge_index,
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;
+ 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 ExtractLVertBMesh_Params *params,
+ BMVert *eve,
+ const int lvert_index,
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;
+
+ 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 ExtractLVertMesh_Params *params,
+ 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);
- 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;
+
+ 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(vbo),
+ void *UNUSED(buf),
void *data)
{
MEM_freeN(data);
}
-static const MeshExtract extract_pos_nor_hq = {
+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,
@@ -2267,9 +1570,9 @@ static const MeshExtract extract_pos_nor_hq = {
.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,
+ .data_type = 0,
.use_threading = true,
-};
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.pos_nor)};
/** \} */
/* ---------------------------------------------------------------------- */
@@ -2284,12 +1587,12 @@ static void *extract_lnor_hq_init(const MeshRenderData *mr,
struct MeshBatchCache *UNUSED(cache),
void *buf)
{
+ 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");
}
- GPUVertBuf *vbo = buf;
GPU_vertbuf_init_with_format(vbo, &format);
GPU_vertbuf_data_alloc(vbo, mr->loop_len);
@@ -2297,36 +1600,37 @@ static void *extract_lnor_hq_init(const MeshRenderData *mr,
}
static void extract_lnor_hq_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
+ BMFace *f,
+ const int UNUSED(f_index),
void *data)
{
- if (mr->loop_normals) {
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(_l, l_index, params, mr)
- {
+ 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]);
}
- 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 {
+ 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, l->f));
+ normal_float_to_short_v3(&((gpuHQNor *)data)[l_index].x, bm_face_no_get(mr, f));
}
}
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
- }
+ } while ((l_iter = l_iter->next) != l_first);
}
static void extract_lnor_hq_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
+ const MPoly *mp,
+ const int mp_index,
void *data)
{
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
+ 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]);
@@ -2352,15 +1656,15 @@ static void extract_lnor_hq_iter_poly_mesh(const MeshRenderData *mr,
lnor_data->w = 0;
}
}
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
}
-static const MeshExtract extract_lnor_hq = {
+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,
+ .data_type = MR_DATA_LOOP_NOR,
.use_threading = true,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.lnor),
};
/** \} */
@@ -2372,12 +1676,12 @@ static void *extract_lnor_init(const MeshRenderData *mr,
struct MeshBatchCache *UNUSED(cache),
void *buf)
{
+ 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");
}
- GPUVertBuf *vbo = buf;
GPU_vertbuf_init_with_format(vbo, &format);
GPU_vertbuf_data_alloc(vbo, mr->loop_len);
@@ -2385,40 +1689,39 @@ static void *extract_lnor_init(const MeshRenderData *mr,
}
static void extract_lnor_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
+ BMFace *f,
+ const int UNUSED(f_index),
void *data)
{
- if (mr->loop_normals) {
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
- {
+ 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]);
- 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 {
+ 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, l->f));
+ ((GPUPackedNormal *)data)[l_index] = GPU_normal_convert_i10_v3(bm_face_no_get(mr, 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);
- }
+ ((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 ExtractPolyMesh_Params *params,
+ const MPoly *mp,
+ const int mp_index,
void *data)
{
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
+ 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]);
@@ -2444,15 +1747,15 @@ static void extract_lnor_iter_poly_mesh(const MeshRenderData *mr,
lnor_data->w = 0;
}
}
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
}
-static const MeshExtract extract_lnor = {
+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,
+ .data_type = MR_DATA_LOOP_NOR,
.use_threading = true,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.lnor),
};
/** \} */
@@ -2463,6 +1766,7 @@ static const MeshExtract extract_lnor = {
static void *extract_uv_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf)
{
+ GPUVertBuf *vbo = buf;
GPUVertFormat format = {0};
GPU_vertformat_deinterleave(&format);
@@ -2512,7 +1816,6 @@ static void *extract_uv_init(const MeshRenderData *mr, struct MeshBatchCache *ca
v_len = 1;
}
- GPUVertBuf *vbo = buf;
GPU_vertbuf_init_with_format(vbo, &format);
GPU_vertbuf_data_alloc(vbo, v_len);
@@ -2545,10 +1848,11 @@ static void *extract_uv_init(const MeshRenderData *mr, struct MeshBatchCache *ca
return NULL;
}
-static const MeshExtract extract_uv = {
+const MeshExtract extract_uv = {
.init = extract_uv_init,
- .data_flag = 0,
+ .data_type = 0,
.use_threading = false,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.uv),
};
/** \} */
@@ -2557,10 +1861,10 @@ static const MeshExtract extract_uv = {
/** \name Extract Tangent layers
* \{ */
-static void extract_tan_ex(const MeshRenderData *mr,
- struct MeshBatchCache *cache,
- GPUVertBuf *vbo,
- const bool do_hq)
+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;
@@ -2731,14 +2035,15 @@ static void extract_tan_ex(const MeshRenderData *mr,
static void *extract_tan_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf)
{
- extract_tan_ex(mr, cache, buf, false);
+ extract_tan_ex_init(mr, cache, buf, false);
return NULL;
}
-static const MeshExtract extract_tan = {
+const MeshExtract extract_tan = {
.init = extract_tan_init,
- .data_flag = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | MR_DATA_LOOPTRI,
+ .data_type = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | MR_DATA_LOOPTRI,
.use_threading = false,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.tan),
};
/** \} */
@@ -2749,13 +2054,13 @@ static const MeshExtract extract_tan = {
static void *extract_tan_hq_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf)
{
- extract_tan_ex(mr, cache, buf, true);
+ extract_tan_ex_init(mr, cache, buf, true);
return NULL;
}
-static const MeshExtract extract_tan_hq = {
+const MeshExtract extract_tan_hq = {
.init = extract_tan_hq_init,
- .data_flag = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | MR_DATA_LOOPTRI,
+ .data_type = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | MR_DATA_LOOPTRI,
.use_threading = false,
};
@@ -2769,6 +2074,7 @@ static void *extract_sculpt_data_init(const MeshRenderData *mr,
struct MeshBatchCache *UNUSED(cache),
void *buf)
{
+ GPUVertBuf *vbo = buf;
GPUVertFormat format = {0};
CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata;
@@ -2783,7 +2089,6 @@ static void *extract_sculpt_data_init(const MeshRenderData *mr,
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);
@@ -2852,12 +2157,12 @@ static void *extract_sculpt_data_init(const MeshRenderData *mr,
return NULL;
}
-static const MeshExtract extract_sculpt_data = {
+const MeshExtract extract_sculpt_data = {
.init = extract_sculpt_data_init,
- .data_flag = 0,
+ .data_type = 0,
/* TODO: enable threading. */
.use_threading = false,
-};
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.sculpt_data)};
/** \} */
@@ -2867,6 +2172,7 @@ static const MeshExtract extract_sculpt_data = {
static void *extract_vcol_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf)
{
+ GPUVertBuf *vbo = buf;
GPUVertFormat format = {0};
GPU_vertformat_deinterleave(&format);
@@ -2928,7 +2234,6 @@ static void *extract_vcol_init(const MeshRenderData *mr, struct MeshBatchCache *
}
}
- GPUVertBuf *vbo = buf;
GPU_vertbuf_init_with_format(vbo, &format);
GPU_vertbuf_data_alloc(vbo, mr->loop_len);
@@ -3001,10 +2306,11 @@ static void *extract_vcol_init(const MeshRenderData *mr, struct MeshBatchCache *
return NULL;
}
-static const MeshExtract extract_vcol = {
+const MeshExtract extract_vcol = {
.init = extract_vcol_init,
- .data_flag = 0,
+ .data_type = 0,
.use_threading = false,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.vcol),
};
/** \} */
@@ -3022,6 +2328,7 @@ static void *extract_orco_init(const MeshRenderData *mr,
struct MeshBatchCache *UNUSED(cache),
void *buf)
{
+ 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
@@ -3031,7 +2338,6 @@ static void *extract_orco_init(const MeshRenderData *mr,
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);
@@ -3045,32 +2351,36 @@ static void *extract_orco_init(const MeshRenderData *mr,
return data;
}
-static void extract_orco_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
+static void extract_orco_iter_poly_bm(const MeshRenderData *UNUSED(mr),
+ BMFace *f,
+ const int UNUSED(f_index),
void *data)
{
MeshExtract_Orco_Data *orco_data = (MeshExtract_Orco_Data *)data;
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(loop, l_index, params, mr)
- {
+ 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(loop->v)]);
+ 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. */
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(loop);
+ } while ((l_iter = l_iter->next) != l_first);
}
static void extract_orco_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
+ const MPoly *mp,
+ const int UNUSED(mp_index),
void *data)
{
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
+ 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. */
}
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
}
static void extract_orco_finish(const MeshRenderData *UNUSED(mr),
@@ -3081,13 +2391,14 @@ static void extract_orco_finish(const MeshRenderData *UNUSED(mr),
MEM_freeN(data);
}
-static const MeshExtract extract_orco = {
+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,
+ .data_type = 0,
.use_threading = true,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.orco),
};
/** \} */
@@ -3124,11 +2435,12 @@ static void *extract_edge_fac_init(const MeshRenderData *mr,
struct MeshBatchCache *UNUSED(cache),
void *buf)
{
+ 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);
}
- GPUVertBuf *vbo = buf;
+
GPU_vertbuf_init_with_format(vbo, &format);
GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len);
@@ -3159,43 +2471,47 @@ static void *extract_edge_fac_init(const MeshRenderData *mr,
}
static void extract_edge_fac_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
+ BMFace *f,
+ const int UNUSED(f_index),
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));
+ 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;
}
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
+ } while ((l_iter = l_iter->next) != l_first);
}
static void extract_edge_fac_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
+ const MPoly *mp,
+ const int mp_index,
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 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;
}
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
- }
- else {
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
+ else {
+
/* Count loop per edge to detect non-manifold. */
if (data->edge_loop_count[ml->e] < 3) {
data->edge_loop_count[ml->e]++;
@@ -3217,34 +2533,28 @@ static void extract_edge_fac_iter_poly_mesh(const MeshRenderData *mr,
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,
+ BMEdge *UNUSED(eed),
+ const int ledge_index,
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;
+ 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 ExtractLEdgeMesh_Params *params,
+ const MEdge *UNUSED(med),
+ const uint ledge_index,
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;
+
+ 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,
@@ -3252,10 +2562,10 @@ static void extract_edge_fac_finish(const MeshRenderData *mr,
void *buf,
void *_data)
{
+ GPUVertBuf *vbo = buf;
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. */
@@ -3281,16 +2591,16 @@ static void extract_edge_fac_finish(const MeshRenderData *mr,
MEM_freeN(data);
}
-static const MeshExtract extract_edge_fac = {
+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,
+ .data_type = MR_DATA_POLY_NOR,
.use_threading = false,
-};
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edge_fac)};
/** \} */
/* ---------------------------------------------------------------------- */
@@ -3361,11 +2671,11 @@ static void *extract_weights_init(const MeshRenderData *mr,
struct MeshBatchCache *cache,
void *buf)
{
+ 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);
}
- GPUVertBuf *vbo = buf;
GPU_vertbuf_init_with_format(vbo, &format);
GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len);
@@ -3389,48 +2699,44 @@ static void *extract_weights_init(const MeshRenderData *mr,
return data;
}
-static void extract_weights_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
+static void extract_weights_iter_poly_bm(const MeshRenderData *UNUSED(mr),
+ BMFace *f,
+ const int UNUSED(f_index),
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);
+ 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);
}
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
- }
- else {
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
- {
+ else {
data->vbo_data[l_index] = evaluate_vertex_weight(NULL, data->wstate);
}
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
- }
+ } while ((l_iter = l_iter->next) != l_first);
}
static void extract_weights_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
+ const MPoly *mp,
+ const int UNUSED(mp_index),
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 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);
}
- 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)
- {
+ else {
+ const MDeformVert *dvert = NULL;
data->vbo_data[ml_index] = evaluate_vertex_weight(dvert, data->wstate);
}
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
}
}
@@ -3442,13 +2748,14 @@ static void extract_weights_finish(const MeshRenderData *UNUSED(mr),
MEM_freeN(data);
}
-static const MeshExtract extract_weights = {
+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,
+ .data_type = 0,
.use_threading = true,
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.weights),
};
/** \} */
@@ -3599,40 +2906,45 @@ static void *extract_edit_data_init(const MeshRenderData *mr,
struct MeshBatchCache *UNUSED(cache),
void *buf)
{
+ 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");
}
- 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,
+ 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);
- 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);
+ 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 ExtractPolyMesh_Params *params,
+ const MPoly *mp,
+ const int mp_index,
void *_data)
{
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
+ 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 = (EditLoopData *)_data + ml_index;
memset(data, 0x0, sizeof(*data));
BMFace *efa = bm_original_face_get(mr, mp_index);
@@ -3648,84 +2960,72 @@ static void extract_edit_data_iter_poly_mesh(const MeshRenderData *mr,
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,
+ BMEdge *eed,
+ const int ledge_index,
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;
+ 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]);
}
static void extract_edit_data_iter_ledge_mesh(const MeshRenderData *mr,
- const ExtractLEdgeMesh_Params *params,
+ const MEdge *med,
+ const uint ledge_index,
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]);
- }
+ 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,
+ BMVert *eve,
+ const int lvert_index,
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;
+ EditLoopData *data = (EditLoopData *)_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 ExtractLVertMesh_Params *params,
+ const MVert *UNUSED(mv),
+ const int lvert_index,
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);
- }
+
+ 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 = {
+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,
@@ -3733,9 +3033,9 @@ static const MeshExtract extract_edit_data = {
.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,
+ .data_type = 0,
.use_threading = true,
-};
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edit_data)};
/** \} */
@@ -3752,6 +3052,7 @@ static void *extract_edituv_data_init(const MeshRenderData *mr,
struct MeshBatchCache *UNUSED(cache),
void *buf)
{
+ GPUVertBuf *vbo = buf;
static GPUVertFormat format = {0};
if (format.attr_len == 0) {
/* WARNING: Adjust #EditLoopData struct accordingly. */
@@ -3759,7 +3060,6 @@ static void *extract_edituv_data_init(const MeshRenderData *mr,
GPU_vertformat_alias_add(&format, "flag");
}
- GPUVertBuf *vbo = buf;
GPU_vertbuf_init_with_format(vbo, &format);
GPU_vertbuf_data_alloc(vbo, mr->loop_len);
@@ -3772,28 +3072,34 @@ static void *extract_edituv_data_init(const MeshRenderData *mr,
}
static void extract_edituv_data_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
+ BMFace *f,
+ const int UNUSED(f_index),
void *_data)
{
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
- {
+ 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, 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);
+ 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 ExtractPolyMesh_Params *params,
+ const MPoly *mp,
+ const int mp_index,
void *_data)
{
MeshExtract_EditUVData_Data *data = _data;
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
+ 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);
@@ -3823,7 +3129,6 @@ static void extract_edituv_data_iter_poly_mesh(const MeshRenderData *mr,
}
}
}
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
}
static void extract_edituv_data_finish(const MeshRenderData *UNUSED(mr),
@@ -3834,14 +3139,14 @@ static void extract_edituv_data_finish(const MeshRenderData *UNUSED(mr),
MEM_freeN(data);
}
-static const MeshExtract extract_edituv_data = {
+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,
+ .data_type = 0,
.use_threading = true,
-};
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edituv_data)};
/** \} */
@@ -3853,12 +3158,12 @@ static void *extract_edituv_stretch_area_init(const MeshRenderData *mr,
struct MeshBatchCache *UNUSED(cache),
void *buf)
{
+ 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);
}
- GPUVertBuf *vbo = buf;
GPU_vertbuf_init_with_format(vbo, &format);
GPU_vertbuf_data_alloc(vbo, mr->loop_len);
@@ -3880,11 +3185,12 @@ BLI_INLINE float area_ratio_to_stretch(float ratio, float tot_ratio, float inv_t
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))
+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__);
@@ -3926,7 +3232,6 @@ static void mesh_edituv_stretch_area_finish(const MeshRenderData *mr,
}
/* 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) {
@@ -3952,12 +3257,12 @@ static void mesh_edituv_stretch_area_finish(const MeshRenderData *mr,
MEM_freeN(area_ratio);
}
-static const MeshExtract extract_edituv_stretch_area = {
+const MeshExtract extract_edituv_stretch_area = {
.init = extract_edituv_stretch_area_init,
- .finish = mesh_edituv_stretch_area_finish,
- .data_flag = 0,
+ .finish = extract_edituv_stretch_area_finish,
+ .data_type = 0,
.use_threading = false,
-};
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edituv_stretch_area)};
/** \} */
@@ -4023,6 +3328,7 @@ static void *extract_edituv_stretch_angle_init(const MeshRenderData *mr,
struct MeshBatchCache *UNUSED(cache),
void *buf)
{
+ GPUVertBuf *vbo = buf;
static GPUVertFormat format = {0};
if (format.attr_len == 0) {
/* Waning: adjust #UVStretchAngle struct accordingly. */
@@ -4030,7 +3336,6 @@ static void *extract_edituv_stretch_angle_init(const MeshRenderData *mr,
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);
@@ -4049,21 +3354,24 @@ static void *extract_edituv_stretch_angle_init(const MeshRenderData *mr,
}
static void extract_edituv_stretch_angle_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
+ 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;
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
- {
+ 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->next;
- BMFace *efa = l->f;
- if (l == BM_FACE_FIRST_LOOP(efa)) {
+ BMLoop *l_next = l_iter->next;
+ if (l_iter == BM_FACE_FIRST_LOOP(f)) {
/* First loop in face. */
- BMLoop *l_tmp = l->prev;
- BMLoop *l_next_tmp = l;
+ 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,
@@ -4076,7 +3384,7 @@ static void extract_edituv_stretch_angle_iter_poly_bm(const MeshRenderData *mr,
copy_v2_v2(last_auv, auv[1]);
copy_v3_v3(last_av, av[1]);
}
- if (l_next == BM_FACE_FIRST_LOOP(efa)) {
+ 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]);
@@ -4085,27 +3393,31 @@ static void extract_edituv_stretch_angle_iter_poly_bm(const MeshRenderData *mr,
copy_v3_v3(av[1], last_av);
}
else {
- luv = BM_ELEM_CD_GET_VOID_P(l, data->cd_ofs);
+ 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->v), bm_vert_co_get(mr, l_next->v));
+ 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]);
- }
- EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
+ } while ((l_iter = l_iter->next) != l_first);
}
static void extract_edituv_stretch_angle_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
+ const MPoly *mp,
+ const int UNUSED(mp_index),
void *_data)
{
MeshExtract_StretchAngle_Data *data = _data;
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
+ 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, ml_index_end = mp->loopstart + mp->totloop;
+ int l_next = ml_index + 1;
const MVert *v, *v_next;
if (ml_index == mp->loopstart) {
/* First loop in face. */
@@ -4136,7 +3448,6 @@ static void extract_edituv_stretch_angle_iter_poly_mesh(const MeshRenderData *mr
}
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),
@@ -4147,14 +3458,14 @@ static void extract_edituv_stretch_angle_finish(const MeshRenderData *UNUSED(mr)
MEM_freeN(data);
}
-static const MeshExtract extract_edituv_stretch_angle = {
+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,
+ .data_type = 0,
.use_threading = false,
-};
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edituv_stretch_angle)};
/** \} */
@@ -4166,12 +3477,12 @@ static void *extract_mesh_analysis_init(const MeshRenderData *mr,
struct MeshBatchCache *UNUSED(cache),
void *buf)
{
+ 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);
}
- GPUVertBuf *vbo = buf;
GPU_vertbuf_init_with_format(vbo, &format);
GPU_vertbuf_data_alloc(vbo, mr->loop_len);
@@ -4725,14 +4036,14 @@ static void statvis_calc_sharp(const MeshRenderData *mr, float *r_sharp)
MEM_freeN(vert_angles);
}
-static void extract_mesh_analysis_finish(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf,
- void *UNUSED(data))
+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);
- GPUVertBuf *vbo = buf;
float *l_weight = (float *)GPU_vertbuf_get_data(vbo);
switch (mr->toolsettings->statvis.type) {
@@ -4754,103 +4065,99 @@ static void extract_mesh_analysis_finish(const MeshRenderData *mr,
}
}
-static const MeshExtract extract_mesh_analysis = {
+const MeshExtract extract_mesh_analysis = {
.init = extract_mesh_analysis_init,
- .finish = extract_mesh_analysis_finish,
+ .finish = extract_analysis_iter_finish_mesh,
/* This is not needed for all visualization types.
* * Maybe split into different extract. */
- .data_flag = MR_DATA_POLY_NOR | MR_DATA_LOOPTRI,
+ .data_type = MR_DATA_POLY_NOR | MR_DATA_LOOPTRI,
.use_threading = false,
-};
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.mesh_analysis)};
/** \} */
/* ---------------------------------------------------------------------- */
-/** \name Extract Facedots positions
+/** \name Extract Face-dots positions
* \{ */
static void *extract_fdots_pos_init(const MeshRenderData *mr,
struct MeshBatchCache *UNUSED(cache),
void *buf)
{
+ 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);
}
- 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,
+ BMFace *f,
+ const int f_index,
void *data)
{
float(*center)[3] = data;
- EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr)
- {
- float *co = center[f_index];
- zero_v3(co);
+ 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;
+ 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 ExtractPolyMesh_Params *params,
+ 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;
- if (mr->use_subsurf_fdots) {
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
+ 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;
}
}
- 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);
+ else {
+ const MVert *mv = &mvert[ml->v];
+ add_v3_v3(center[mp_index], mv->co);
}
- EXTRACT_POLY_FOREACH_MESH_END;
+ }
+
+ if (!mr->use_subsurf_fdots) {
+ mul_v3_fl(co, 1.0f / (float)mp->totloop);
}
}
-static const MeshExtract extract_fdots_pos = {
+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,
+ .data_type = 0,
.use_threading = true,
-};
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_pos)};
/** \} */
/* ---------------------------------------------------------------------- */
-/** \name Extract Facedots Normal and edit flag
+/** \name Extract Face-dots Normal and edit flag
* \{ */
#define NOR_AND_FLAG_DEFAULT 0
#define NOR_AND_FLAG_SELECT 1
@@ -4861,11 +4168,12 @@ static void *extract_fdots_nor_init(const MeshRenderData *mr,
struct MeshBatchCache *UNUSED(cache),
void *buf)
{
+ 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);
}
- GPUVertBuf *vbo = buf;
+
GPU_vertbuf_init_with_format(vbo, &format);
GPU_vertbuf_data_alloc(vbo, mr->poly_len);
@@ -4877,8 +4185,8 @@ static void extract_fdots_nor_finish(const MeshRenderData *mr,
void *buf,
void *UNUSED(data))
{
- static float invalid_normal[3] = {0.0f, 0.0f, 0.0f};
GPUVertBuf *vbo = buf;
+ static float invalid_normal[3] = {0.0f, 0.0f, 0.0f};
GPUPackedNormal *nor = (GPUPackedNormal *)GPU_vertbuf_get_data(vbo);
BMFace *efa;
@@ -4921,27 +4229,28 @@ static void extract_fdots_nor_finish(const MeshRenderData *mr,
}
}
-static const MeshExtract extract_fdots_nor = {
+const MeshExtract extract_fdots_nor = {
.init = extract_fdots_nor_init,
.finish = extract_fdots_nor_finish,
- .data_flag = MR_DATA_POLY_NOR,
+ .data_type = MR_DATA_POLY_NOR,
.use_threading = false,
-};
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_nor)};
/** \} */
/* ---------------------------------------------------------------------- */
-/** \name Extract Facedots High Quality Normal and edit flag
+/** \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)
{
+ 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);
}
- GPUVertBuf *vbo = buf;
+
GPU_vertbuf_init_with_format(vbo, &format);
GPU_vertbuf_data_alloc(vbo, mr->poly_len);
@@ -4953,8 +4262,8 @@ static void extract_fdots_nor_hq_finish(const MeshRenderData *mr,
void *buf,
void *UNUSED(data))
{
- static float invalid_normal[3] = {0.0f, 0.0f, 0.0f};
GPUVertBuf *vbo = buf;
+ static float invalid_normal[3] = {0.0f, 0.0f, 0.0f};
short *nor = (short *)GPU_vertbuf_get_data(vbo);
BMFace *efa;
@@ -4997,17 +4306,17 @@ static void extract_fdots_nor_hq_finish(const MeshRenderData *mr,
}
}
-static const MeshExtract extract_fdots_nor_hq = {
+const MeshExtract extract_fdots_nor_hq = {
.init = extract_fdots_nor_hq_init,
.finish = extract_fdots_nor_hq_finish,
- .data_flag = MR_DATA_POLY_NOR,
+ .data_type = MR_DATA_POLY_NOR,
.use_threading = false,
-};
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_nor)};
/** \} */
/* ---------------------------------------------------------------------- */
-/** \name Extract Facedots UV
+/** \name Extract Face-dots UV
* \{ */
typedef struct MeshExtract_FdotUV_Data {
@@ -5020,13 +4329,14 @@ static void *extract_fdots_uv_init(const MeshRenderData *mr,
struct MeshBatchCache *UNUSED(cache),
void *buf)
{
+ 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");
}
- GPUVertBuf *vbo = buf;
+
GPU_vertbuf_init_with_format(vbo, &format);
GPU_vertbuf_data_alloc(vbo, mr->poly_len);
@@ -5047,42 +4357,41 @@ static void *extract_fdots_uv_init(const MeshRenderData *mr,
return data;
}
-static void extract_fdots_uv_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
+static void extract_fdots_uv_iter_poly_bm(const MeshRenderData *UNUSED(mr),
+ BMFace *f,
+ const int UNUSED(f_index),
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);
+ 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 ExtractPolyMesh_Params *params,
+ const MPoly *mp,
+ const int mp_index,
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 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);
}
}
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
- }
- else {
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
+ else {
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;
}
}
@@ -5094,18 +4403,19 @@ static void extract_fdots_uv_finish(const MeshRenderData *UNUSED(mr),
MEM_freeN(data);
}
-static const MeshExtract extract_fdots_uv = {
+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,
+ .data_type = 0,
.use_threading = true,
-};
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_uv)};
+
/** \} */
/* ---------------------------------------------------------------------- */
-/** \name Extract Facedots Edit UV flag
+/** \name Extract Face-dots Edit UV flag
* \{ */
typedef struct MeshExtract_EditUVFdotData_Data {
@@ -5117,11 +4427,12 @@ static void *extract_fdots_edituv_data_init(const MeshRenderData *mr,
struct MeshBatchCache *UNUSED(cache),
void *buf)
{
+ 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);
}
- GPUVertBuf *vbo = buf;
+
GPU_vertbuf_init_with_format(vbo, &format);
GPU_vertbuf_data_alloc(vbo, mr->poly_len);
@@ -5132,34 +4443,28 @@ static void *extract_fdots_edituv_data_init(const MeshRenderData *mr,
}
static void extract_fdots_edituv_data_iter_poly_bm(const MeshRenderData *mr,
- const ExtractPolyBMesh_Params *params,
+ BMFace *f,
+ const int UNUSED(f_index),
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;
+ 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 ExtractPolyMesh_Params *params,
+ const MPoly *UNUSED(mp),
+ const int mp_index,
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);
- }
+ 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),
@@ -5170,14 +4475,15 @@ static void extract_fdots_edituv_data_finish(const MeshRenderData *UNUSED(mr),
MEM_freeN(data);
}
-static const MeshExtract extract_fdots_edituv_data = {
+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,
+ .data_type = 0,
.use_threading = true,
-};
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.fdots_edituv_data)};
+
/** \} */
/* ---------------------------------------------------------------------- */
@@ -5193,6 +4499,7 @@ static void *extract_skin_roots_init(const MeshRenderData *mr,
struct MeshBatchCache *UNUSED(cache),
void *buf)
{
+ GPUVertBuf *vbo = buf;
/* Exclusively for edit mode. */
BLI_assert(mr->bm);
@@ -5201,7 +4508,7 @@ static void *extract_skin_roots_init(const MeshRenderData *mr,
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);
@@ -5228,11 +4535,11 @@ static void *extract_skin_roots_init(const MeshRenderData *mr,
return NULL;
}
-static const MeshExtract extract_skin_roots = {
+const MeshExtract extract_skin_roots = {
.init = extract_skin_roots_init,
- .data_flag = 0,
+ .data_type = 0,
.use_threading = false,
-};
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.skin_roots)};
/** \} */
@@ -5244,12 +4551,12 @@ static void *extract_select_idx_init(const MeshRenderData *mr,
struct MeshBatchCache *UNUSED(cache),
void *buf)
{
+ 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);
}
- 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);
@@ -5260,169 +4567,163 @@ static void *extract_select_idx_init(const MeshRenderData *mr,
* 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,
+static void extract_poly_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr),
+ BMFace *f,
+ const int f_index,
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);
+ 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 *mr,
- const ExtractPolyBMesh_Params *params,
+static void extract_edge_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr),
+ BMFace *f,
+ const int UNUSED(f_index),
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);
+ 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 *mr,
- const ExtractPolyBMesh_Params *params,
+static void extract_vert_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr),
+ BMFace *f,
+ const int UNUSED(f_index),
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);
+ 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 ExtractLEdgeBMesh_Params *params,
+ BMEdge *eed,
+ const int ledge_index,
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;
+ ((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 ExtractLEdgeBMesh_Params *params,
+ BMEdge *eed,
+ const int ledge_index,
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;
+ ((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 ExtractLVertBMesh_Params *params,
+ BMVert *eve,
+ const int lvert_index,
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;
+
+ ((uint32_t *)data)[offset + lvert_index] = BM_elem_index_get(eve);
}
static void extract_poly_idx_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
+ const MPoly *mp,
+ const int mp_index,
void *data)
{
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
+ 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;
}
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
}
static void extract_edge_idx_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
+ const MPoly *mp,
+ const int UNUSED(mp_index),
void *data)
{
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
+ 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;
}
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
}
static void extract_vert_idx_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
+ const MPoly *mp,
+ const int UNUSED(mp_index),
void *data)
{
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
- {
+ 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;
}
- EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
}
static void extract_edge_idx_iter_ledge_mesh(const MeshRenderData *mr,
- const ExtractLEdgeMesh_Params *params,
+ const MEdge *UNUSED(med),
+ const uint ledge_index,
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;
+ 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 ExtractLEdgeMesh_Params *params,
+ const MEdge *med,
+ const uint ledge_index,
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;
+ 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 ExtractLVertMesh_Params *params,
+ const MVert *UNUSED(mv),
+ const int lvert_index,
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;
+
+ 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;
}
-static const MeshExtract extract_poly_idx = {
+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,
+ .data_type = 0,
.use_threading = true,
-};
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.poly_idx)};
-static const MeshExtract extract_edge_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_flag = 0,
+ .data_type = 0,
.use_threading = true,
-};
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.edge_idx)};
-static const MeshExtract extract_vert_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,
@@ -5430,736 +4731,51 @@ static const MeshExtract extract_vert_idx = {
.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,
+ .data_type = 0,
.use_threading = true,
-};
+ .mesh_buffer_offset = offsetof(MeshBufferCache, vbo.vert_idx)};
-static void *extract_select_fdot_idx_init(const MeshRenderData *mr,
- struct MeshBatchCache *UNUSED(cache),
- void *buf)
+static void *extract_fdot_idx_init(const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buf)
{
+ 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);
}
- 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,
+static void extract_fdot_idx_iter_poly_bm(const MeshRenderData *UNUSED(mr),
+ BMFace *UNUSED(f),
+ const int f_index,
void *data)
{
- EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr)
- {
- ((uint32_t *)data)[f_index] = f_index;
- }
- EXTRACT_POLY_FOREACH_BM_END;
+ ((uint32_t *)data)[f_index] = f_index;
}
static void extract_fdot_idx_iter_poly_mesh(const MeshRenderData *mr,
- const ExtractPolyMesh_Params *params,
+ const MPoly *UNUSED(mp),
+ const int mp_index,
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;
+ ((uint32_t *)data)[mp_index] = mr->p_origindex[mp_index];
}
else {
- EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr)
- {
- ((uint32_t *)data)[mp_index] = mp_index;
- }
- EXTRACT_POLY_FOREACH_MESH_END;
+ ((uint32_t *)data)[mp_index] = mp_index;
}
}
-static const MeshExtract extract_fdot_idx = {
- .init = extract_select_fdot_idx_init,
+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_flag = 0,
+ .data_type = 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
-}
-
-/** \} */
+ .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..6761282fe79
--- /dev/null
+++ b/source/blender/draw/intern/draw_cache_extract_mesh_private.h
@@ -0,0 +1,514 @@
+/*
+ * 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;
+}
+
+/* TODO(jbakker): phase out batch iteration macros as they are only used once. */
+/* ---------------------------------------------------------------------- */
+/** \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,
+ BMLoop **elt,
+ const int elt_index,
+ 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 MLoopTri *mlt,
+ const int elt_index,
+ 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,
+ BMFace *f,
+ const int f_index,
+ 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 MPoly *mp,
+ const int mp_index,
+ 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,
+ BMEdge *eed,
+ const int ledge_index,
+ 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 MEdge *med,
+ const uint ledge_index,
+ 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,
+ BMVert *eve,
+ const int lvert_index,
+ 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 MVert *mv,
+ const int lvert_index,
+ 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_type;
+ /** Used to know if the element callbacks are thread-safe and can be parallelized. */
+ const bool use_threading;
+ /**
+ * Offset in bytes of the buffer inside a MeshBufferCache instance. Points to a vertex or index
+ * buffer.
+ */
+ const 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);
+/*
+ * Total number of extractions types.
+ */
+#define M_EXTRACT_LEN 38
+
+extern const MeshExtract extract_tris;
+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 2c2ab9eaadd..5743f39f7da 100644
--- a/source/blender/draw/intern/draw_cache_impl.h
+++ b/source/blender/draw/intern/draw_cache_impl.h
@@ -266,7 +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.cc b/source/blender/draw/intern/draw_cache_impl_curve.cc
index 223b44724b6..ee6a47e3dc6 100644
--- a/source/blender/draw/intern/draw_cache_impl_curve.cc
+++ 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,6 +149,21 @@ 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;
@@ -192,6 +217,9 @@ 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;
@@ -230,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) {
@@ -556,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 != nullptr);
-
static GPUVertFormat format = {0};
static struct {
uint pos;
@@ -570,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 != nullptr);
-
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);
@@ -603,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);
}
@@ -769,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) : NULL))
+#define DRW_TEST_ASSIGN_IBO(v) (v = (DRW_ibo_requested(v) ? (v) : NULL))
+
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);
@@ -789,6 +866,9 @@ 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 = (Nurb *)rdata->nurbs->first; nu; nu = nu->next, nu_id++) {
const BezTriple *bezt = nu->bezt;
@@ -796,7 +876,7 @@ static void curve_create_edit_data_and_handles(CurveRenderData *rdata,
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);
@@ -831,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);
@@ -1070,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)) {
diff --git a/source/blender/draw/intern/draw_cache_impl_displist.c b/source/blender/draw/intern/draw_cache_impl_displist.c
index d606f70db9e..ee16cb1a022 100644
--- a/source/blender/draw/intern/draw_cache_impl_displist.c
+++ b/source/blender/draw/intern/draw_cache_impl_displist.c
@@ -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,6 +552,8 @@ 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) {
diff --git a/source/blender/draw/intern/draw_cache_impl_gpencil.c b/source/blender/draw/intern/draw_cache_impl_gpencil.c
index 49b5e0fecd3..bea9ba1122b 100644
--- a/source/blender/draw/intern/draw_cache_impl_gpencil.c
+++ b/source/blender/draw/intern/draw_cache_impl_gpencil.c
@@ -405,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 af54b57b162..3cc71e47f28 100644
--- a/source/blender/draw/intern/draw_cache_impl_mesh.c
+++ b/source/blender/draw/intern/draw_cache_impl_mesh.c
@@ -707,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;
@@ -714,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]);
}
@@ -1160,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]));
}
}
@@ -1542,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,
@@ -1551,7 +1569,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
false,
true,
false,
- &cache->cd_used,
scene,
ts,
true);
@@ -1560,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,
@@ -1569,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);
@@ -1577,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,
@@ -1586,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_inline.h b/source/blender/draw/intern/draw_cache_inline.h
index bfc714e5d6a..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. */
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.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_shader.c b/source/blender/draw/intern/draw_manager_shader.c
index 2aad1f10154..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,
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 33deae0b0a1..479f9cd1827 100644
--- a/source/blender/draw/intern/shaders/common_math_lib.glsl
+++ b/source/blender/draw/intern/shaders/common_math_lib.glsl
@@ -86,6 +86,8 @@ 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; }
diff --git a/source/blender/editors/curve/editcurve_paint.c b/source/blender/editors/curve/editcurve_paint.c
index 8842274e017..e4f2de1f741 100644
--- a/source/blender/editors/curve/editcurve_paint.c
+++ b/source/blender/editors/curve/editcurve_paint.c
@@ -210,7 +210,7 @@ static bool stroke_elem_project(const struct CurveDrawData *cdd,
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);
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/gpencil/CMakeLists.txt b/source/blender/editors/gpencil/CMakeLists.txt
index f7ab72a8a43..bff7310e9f7 100644
--- a/source/blender/editors/gpencil/CMakeLists.txt
+++ b/source/blender/editors/gpencil/CMakeLists.txt
@@ -41,6 +41,7 @@ set(SRC
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/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_edit.c b/source/blender/editors/gpencil/gpencil_edit.c
index e65afa1abff..e01221db277 100644
--- a/source/blender/editors/gpencil/gpencil_edit.c
+++ b/source/blender/editors/gpencil/gpencil_edit.c
@@ -5347,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_intern.h b/source/blender/editors/gpencil/gpencil_intern.h
index 9f48c81c8f1..276e8c81e0f 100644
--- a/source/blender/editors/gpencil/gpencil_intern.h
+++ b/source/blender/editors/gpencil/gpencil_intern.h
@@ -444,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);
@@ -487,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);
@@ -750,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_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 698d784c3b0..35640cf3b66 100644
--- a/source/blender/editors/gpencil/gpencil_ops.c
+++ b/source/blender/editors/gpencil/gpencil_ops.c
@@ -622,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
@@ -648,6 +649,7 @@ 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);
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 66ec57c8a31..52d69d12253 100644
--- a/source/blender/editors/include/ED_view3d.h
+++ b/source/blender/editors/include/ED_view3d.h
@@ -162,10 +162,10 @@ bool ED_view3d_depth_read_cached(const ViewDepths *vd,
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 */
@@ -410,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 */
diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c
index f26a85ac93f..61511a78925 100644
--- a/source/blender/editors/interface/interface.c
+++ b/source/blender/editors/interface/interface.c
@@ -3228,19 +3228,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;
}
}
diff --git a/source/blender/editors/interface/interface_eyedropper.c b/source/blender/editors/interface/interface_eyedropper.c
index e4f502950ab..b52bfc81b7a 100644
--- a/source/blender/editors/interface/interface_eyedropper.c
+++ b/source/blender/editors/interface/interface_eyedropper.c
@@ -138,8 +138,8 @@ void eyedropper_draw_cursor_text_region(const struct bContext *C,
}
const int mval[2] = {
- x - region->winrct.xmin,
- y - region->winrct.ymin,
+ 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 d5fb0e4e744..6583fa31dbf 100644
--- a/source/blender/editors/interface/interface_eyedropper_color.c
+++ b/source/blender/editors/interface/interface_eyedropper_color.c
@@ -118,7 +118,8 @@ static bool eyedropper_init(bContext *C, wmOperator *op)
RNA_property_float_get_array(&eye->ptr, eye->prop, col);
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);
}
@@ -199,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(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])
@@ -255,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 633d2234449..e4e641b474c 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -2395,16 +2395,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 {
@@ -2415,56 +2415,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;
@@ -2475,16 +2458,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, ...]");
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index f19666e56b0..26ac27ca8bb 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -653,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);
@@ -672,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);
@@ -1078,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,
diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c
index b5cd9c7f60d..825b7d11aef 100644
--- a/source/blender/editors/mesh/editmesh_knife.c
+++ b/source/blender/editors/mesh/editmesh_knife.c
@@ -578,8 +578,8 @@ static void knife_input_ray_segment(KnifeTool_OpData *kcd,
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);
+ 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);
@@ -1745,7 +1745,7 @@ 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);
diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c
index 79385e28aa9..112de68b52c 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,14 +621,8 @@ 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) {
@@ -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/object/object_add.c b/source/blender/editors/object/object_add.c
index 8cf7d60e6c4..ef500be0133 100644
--- a/source/blender/editors/object/object_add.c
+++ b/source/blender/editors/object/object_add.c
@@ -1312,6 +1312,7 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op)
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;
@@ -1432,6 +1433,13 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op)
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. */
if (use_in_front) {
ob->dtx |= OB_DRAW_IN_FRONT;
@@ -1474,6 +1482,7 @@ static void object_add_ui(bContext *UNUSED(C), wmOperator *op)
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);
@@ -1520,6 +1529,8 @@ void OBJECT_OT_gpencil_add(wmOperatorType *ot)
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",
@@ -2137,8 +2148,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);
}
/** \} */
@@ -2459,7 +2469,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);
@@ -2474,7 +2484,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);
@@ -3359,7 +3369,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;
}
@@ -3375,8 +3385,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(
diff --git a/source/blender/editors/object/object_modes.c b/source/blender/editors/object/object_modes.c
index dcb294bcb12..3d1a5ac2d62 100644
--- a/source/blender/editors/object/object_modes.c
+++ b/source/blender/editors/object/object_modes.c
@@ -30,6 +30,8 @@
#include "BLI_math.h"
#include "BLI_utildefines.h"
+#include "PIL_time.h"
+
#include "BLT_translation.h"
#include "BKE_context.h"
@@ -419,7 +421,7 @@ static bool object_transfer_mode_poll(bContext *C)
return false;
}
const Object *ob = CTX_data_active_object(C);
- return ob && (ob->mode & (OB_MODE_SCULPT));
+ return ob && (ob->mode != OB_MODE_OBJECT);
}
/* Update the viewport rotation origin to the mouse cursor. */
@@ -438,6 +440,13 @@ static void object_transfer_mode_reposition_view_pivot(bContext *C, const int mv
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);
@@ -475,6 +484,8 @@ static bool object_transfer_mode_to_base(bContext *C, wmOperator *op, Base *base
ob_dst_orig = DEG_get_original_object(ob_dst);
ED_object_mode_set_ex(C, last_mode, true, op->reports);
+ 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);
mode_transfered = true;
diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c
index b685a93f27b..2c188e310db 100644
--- a/source/blender/editors/object/object_relations.c
+++ b/source/blender/editors/object/object_relations.c
@@ -1944,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(
@@ -2454,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. */
@@ -2644,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 a87b5054efa..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;
}
@@ -1890,7 +1890,7 @@ static int object_transform_axis_target_modal(bContext *C, wmOperator *op, const
if ((depth > depths->depth_range[0]) && (depth < depths->depth_range[1])) {
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 5f81f9afe4f..3f40d637188 100644
--- a/source/blender/editors/object/object_vgroup.c
+++ b/source/blender/editors/object/object_vgroup.c
@@ -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 5b545784e5b..97994b65f40 100644
--- a/source/blender/editors/physics/particle_edit.c
+++ b/source/blender/editors/physics/particle_edit.c
@@ -609,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;
diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c
index 3829aeebbeb..7e111905883 100644
--- a/source/blender/editors/sculpt_paint/paint_cursor.c
+++ b/source/blender/editors/sculpt_paint/paint_cursor.c
@@ -1039,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(
diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c
index b6ae6f8bee7..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);
diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c
index b093f07226e..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;
@@ -1240,7 +1240,7 @@ static void paint_line_strokes_spacing(bContext *C,
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(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_final;
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index 2e1dd928f96..d6d54a1985d 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -1854,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);
}
@@ -4196,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])
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/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_file/file_ops.c b/source/blender/editors/space_file/file_ops.c
index 3868ad5eb35..81dce30c695 100644
--- a/source/blender/editors/space_file/file_ops.c
+++ b/source/blender/editors/space_file/file_ops.c
@@ -1744,7 +1744,7 @@ static bool file_execute(bContext *C, SpaceFile *sfile)
/* directory change */
if (file && (file->typeflag & FILE_TYPE_DIR)) {
if (!file->relpath) {
- return OPERATOR_CANCELLED;
+ return false;
}
if (FILENAME_IS_PARENT(file->relpath)) {
@@ -1783,7 +1783,7 @@ static bool file_execute(bContext *C, SpaceFile *sfile)
WM_event_fileselect_event(CTX_wm_manager(C), op, EVT_FILESELECT_EXEC);
}
- return OPERATOR_FINISHED;
+ return true;
}
static int file_exec(bContext *C, wmOperator *UNUSED(op))
@@ -2260,23 +2260,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_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_info/info_stats.c b/source/blender/editors/space_info/info_stats.c
index 0bdfceb36b6..cf847fa18a8 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;
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 fb02086e26c..6137d34fd0c 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;
}
@@ -462,7 +462,7 @@ static void node_draw_frame(const bContext *C,
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)
@@ -497,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
@@ -511,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;
@@ -541,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;
}
@@ -586,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.
@@ -601,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.
@@ -612,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);
@@ -662,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) {
@@ -693,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);
}
}
@@ -701,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)
@@ -717,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);
}
@@ -730,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)
@@ -745,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);
@@ -767,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)
@@ -782,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);
@@ -796,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);
@@ -808,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);
}
}
@@ -845,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)
@@ -901,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(
@@ -944,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");
@@ -989,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)
@@ -1036,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);
}
}
@@ -1083,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);
@@ -1100,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);
@@ -1122,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
}
@@ -1139,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)
@@ -1163,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 */
@@ -1348,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);
@@ -1369,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;
}
@@ -1386,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;
@@ -1423,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);
}
@@ -1438,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);
@@ -1461,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);
@@ -1479,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,
@@ -1495,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)
@@ -1507,27 +1517,36 @@ 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)
@@ -1536,9 +1555,9 @@ static void node_composit_buts_antialiasing(uiLayout *layout, bContext *UNUSED(C
col = uiLayoutColumn(layout, false);
- uiItemR(col, ptr, "threshold", 0, NULL, ICON_NONE);
- uiItemR(col, ptr, "contrast_limit", 0, NULL, ICON_NONE);
- uiItemR(col, ptr, "corner_rounding", 0, NULL, ICON_NONE);
+ 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 */
@@ -1548,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);
}
}
@@ -1581,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);
}
}
@@ -1598,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)
@@ -1611,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);
@@ -1619,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)
@@ -1636,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")) {
@@ -1660,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,
@@ -1683,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)
@@ -1691,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);
@@ -1712,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)
@@ -1721,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)
@@ -1749,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)
@@ -1758,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,
@@ -1772,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)
@@ -1784,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);
}
}
@@ -1809,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)
@@ -1823,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,
@@ -1836,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)
@@ -1861,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)
@@ -1905,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);
@@ -1926,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,
@@ -1944,7 +1964,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,
@@ -1959,9 +1979,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) {
@@ -1975,10 +1995,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);
@@ -1990,23 +2010,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);
@@ -2014,7 +2034,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);
}
}
}
@@ -2026,7 +2046,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);
@@ -2043,8 +2063,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)
@@ -2054,86 +2074,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;
@@ -2153,17 +2173,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;
@@ -2176,23 +2212,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)
@@ -2202,10 +2246,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;
@@ -2221,9 +2273,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);
@@ -2266,8 +2318,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,
@@ -2277,53 +2329,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,
@@ -2334,10 +2386,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)
@@ -2345,32 +2397,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(
@@ -2405,7 +2457,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;
@@ -2450,7 +2502,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;
@@ -2496,65 +2548,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;
@@ -2570,28 +2640,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;
@@ -2599,7 +2677,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);
@@ -2618,21 +2696,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;
@@ -2660,10 +2746,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);
}
}
@@ -2676,7 +2762,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,
@@ -2704,18 +2790,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");
@@ -2741,7 +2844,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)
@@ -2751,14 +2854,13 @@ static void node_composit_buts_denoise(uiLayout *layout, bContext *UNUSED(C), Po
#else
/* Always supported through Accelerate framework BNNS on macOS. */
# ifndef __APPLE__
- if (!BLI_cpu_support_sse41())
-# endif
- {
+ 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 */
@@ -3028,7 +3130,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;
@@ -3041,29 +3143,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;
@@ -3071,18 +3175,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",
@@ -3103,7 +3208,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;
@@ -3118,19 +3223,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)
@@ -3187,12 +3292,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);
}
@@ -3202,7 +3311,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);
}
}
@@ -3259,6 +3368,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
@@ -3270,8 +3381,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;
@@ -3348,7 +3459,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]);
}
@@ -3356,7 +3467,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]);
}
@@ -3369,7 +3480,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;
@@ -3379,13 +3490,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);
@@ -3412,8 +3523,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;*/
@@ -3489,7 +3600,8 @@ static void std_node_socket_draw(
break;
}
case SOCK_TEXTURE: {
- uiTemplateID(layout, C, ptr, "default_value", "texture.new", NULL, NULL, 0, ICON_NONE, NULL);
+ uiTemplateID(
+ layout, C, ptr, "default_value", "texture.new", nullptr, nullptr, 0, ICON_NONE, nullptr);
break;
}
case SOCK_MATERIAL: {
@@ -3504,7 +3616,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);
@@ -3539,7 +3651,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)
@@ -3606,7 +3718,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);
@@ -3615,7 +3727,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) {
@@ -3652,7 +3764,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,
@@ -3682,7 +3794,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);
@@ -3701,7 +3813,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);
@@ -3761,7 +3873,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,
@@ -3794,6 +3906,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 */
@@ -3804,9 +3917,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);
@@ -3833,7 +3946,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);
@@ -3912,10 +4025,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 */
@@ -4015,16 +4129,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) {
@@ -4046,7 +4160,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();
}
@@ -4088,7 +4202,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 a28d467df2e..7276688e986 100644
--- a/source/blender/editors/space_node/node_add.c
+++ b/source/blender/editors/space_node/node_add.cc
@@ -65,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);
@@ -90,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);
@@ -114,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(
@@ -127,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,
@@ -142,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;
}
@@ -162,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;
@@ -184,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);
}
@@ -198,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);
@@ -259,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;
}
@@ -275,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);
}
@@ -317,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);
}
@@ -337,10 +337,10 @@ 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;
+ return nullptr;
}
- const char *disabled_hint = NULL;
+ const char *disabled_hint = nullptr;
if ((node_group->type != ntree->type) || !nodeGroupPoll(ntree, node_group, &disabled_hint)) {
if (disabled_hint) {
BKE_reportf(op->reports,
@@ -358,7 +358,7 @@ static bNodeTree *node_add_group_get_and_poll_group_node_tree(Main *bmain,
ntree->id.name + 2);
}
- return NULL;
+ return nullptr;
}
return node_group;
@@ -450,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;
@@ -470,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;
@@ -482,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);
@@ -554,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;
@@ -574,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]);
@@ -655,7 +655,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;
@@ -675,7 +675,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;
@@ -687,7 +687,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);
@@ -788,7 +788,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");
@@ -801,7 +801,7 @@ 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);
}
@@ -876,7 +876,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];
@@ -890,7 +890,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");
@@ -976,7 +976,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) {
@@ -993,7 +993,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_edit.c b/source/blender/editors/space_node/node_edit.cc
index 50fa8b28468..d86b069aac3 100644
--- a/source/blender/editors/space_node/node_edit.c
+++ b/source/blender/editors/space_node/node_edit.cc
@@ -83,7 +83,7 @@ enum {
COM_RECALC_VIEWER = 2,
};
-typedef struct CompoJob {
+struct CompoJob {
/* Input parameters. */
Main *bmain;
Scene *scene;
@@ -97,7 +97,7 @@ typedef struct CompoJob {
const short *stop;
short *do_update;
float *progress;
-} CompoJob;
+};
float node_socket_calculate_height(const bNodeSocket *socket)
{
@@ -150,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;
@@ -161,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;
}
@@ -175,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)
@@ -188,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;
}
@@ -196,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);
@@ -218,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;
@@ -243,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;
}
@@ -261,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;
@@ -311,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;
}
/**
@@ -347,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;
@@ -359,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);
}
@@ -412,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) {
@@ -490,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"),
@@ -512,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"),
@@ -546,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;
@@ -562,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);
@@ -581,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;
@@ -592,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);
@@ -618,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);
}
}
@@ -648,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) {
@@ -685,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;
}
}
@@ -734,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);
@@ -753,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) {
@@ -806,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", "");
}
@@ -838,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;
@@ -876,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;
@@ -926,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;
@@ -937,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)
@@ -945,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: {
@@ -1112,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;
}
}
@@ -1128,17 +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.0f,
- .xmax = socket->locx + NODE_SOCKSIZE * 2.0f,
- /*.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. */
- .ymin = socket->locy - node_socket_height,
- .ymax = socket->locy + node_socket_height,
- };
+ 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;
}
@@ -1151,8 +1152,8 @@ int node_find_indicated_socket(
{
rctf rect;
- *nodep = NULL;
- *sockp = NULL;
+ *nodep = nullptr;
+ *sockp = nullptr;
/* check if we click in a socket */
LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) {
@@ -1244,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);
@@ -1262,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;
@@ -1353,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)
@@ -1414,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) {
@@ -1453,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;
@@ -1462,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;
@@ -1471,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);
@@ -1554,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;
}
@@ -1585,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;
}
@@ -1618,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;
}
@@ -1649,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;
}
@@ -1674,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;
}
@@ -1870,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) {
@@ -1919,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) {
@@ -1965,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);
@@ -1978,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;
}
@@ -2014,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";
@@ -2059,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;
}
@@ -2097,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);
}
}
@@ -2127,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;
@@ -2188,7 +2189,7 @@ 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) {
- const char *disabled_hint = NULL;
+ const char *disabled_hint = nullptr;
if (!node->typeinfo->poll_instance ||
!node->typeinfo->poll_instance(node, ntree, &disabled_hint)) {
all_nodes_valid = false;
@@ -2286,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)
@@ -2297,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";
@@ -2328,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;
}
@@ -2356,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;
}
@@ -2378,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;
}
@@ -2404,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)
@@ -2413,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;
}
@@ -2453,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;
}
@@ -2486,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);
@@ -2505,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 */
@@ -2556,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;
@@ -2575,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 */
@@ -2647,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);
@@ -2683,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;
@@ -2723,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;
}
@@ -2749,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) {
@@ -2793,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_group.c b/source/blender/editors/space_node/node_group.cc
index 335e2f93ff3..34450016698 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"
@@ -135,7 +135,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 +165,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 +200,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 +219,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 +248,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 +278,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 +286,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 +308,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 +318,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 +349,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 +386,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 +457,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 +545,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 +564,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 +630,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,12 +676,12 @@ 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)) {
- const char *disabled_hint = NULL;
+ const char *disabled_hint = nullptr;
if (node->typeinfo->poll_instance &&
!node->typeinfo->poll_instance(node, ngroup, &disabled_hint)) {
if (disabled_hint) {
@@ -781,7 +783,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) {
@@ -995,10 +997,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 */
@@ -1029,7 +1031,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;
}
@@ -1042,7 +1044,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_relationships.c b/source/blender/editors/space_node/node_relationships.cc
index 28c660b0632..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;
@@ -225,10 +231,10 @@ 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 != NULL);
+ 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, NULL, NULL);
+ snode, nldrag->last_node_hovered_while_dragging_a_link, nullptr, nullptr);
/* Send changed event to original link->tonode. */
if (node) {
@@ -256,7 +262,7 @@ static void pick_input_link_by_link_intersect(const bContext *C,
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) {
@@ -305,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;
@@ -319,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,
@@ -374,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
@@ -409,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,
@@ -430,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)
{
@@ -457,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,
@@ -477,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;
@@ -505,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);
}
@@ -522,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;
}
@@ -536,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;
@@ -588,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)) {
@@ -603,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) {
@@ -613,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;
@@ -623,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;
@@ -663,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;
}
@@ -674,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;
@@ -741,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))
{
@@ -779,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--;
}
}
@@ -787,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--;
}
@@ -806,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
@@ -867,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) {
@@ -882,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;
@@ -906,22 +923,22 @@ 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;
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) {
@@ -939,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;
}
}
}
@@ -952,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];
@@ -977,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);
@@ -993,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);
@@ -1009,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
@@ -1043,7 +1060,7 @@ 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);
@@ -1061,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)) {
@@ -1127,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);
@@ -1167,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",
@@ -1178,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)
@@ -1189,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);
@@ -1218,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);
@@ -1285,7 +1316,7 @@ 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);
}
}
@@ -1324,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)
{
@@ -1426,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))
{
@@ -1469,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))
{
@@ -1491,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;
}
@@ -1511,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
@@ -1563,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) {
@@ -1584,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;
}
@@ -1604,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,
@@ -1624,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)
@@ -1637,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 */
@@ -1666,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;
}
@@ -1687,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
@@ -1738,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;
}
@@ -1758,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,
@@ -1766,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;
}
@@ -1782,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;
@@ -1792,7 +1851,7 @@ static bool ed_node_link_conditions(ScrArea *area,
}
}
/* only one selected */
- if (node || select == NULL) {
+ if (node || select == nullptr) {
return false;
}
@@ -1835,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];
@@ -1844,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
@@ -1876,6 +1935,12 @@ void ED_node_link_intersect_test(ScrArea *area, int test)
}
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Node Insert Offset Operator
+ * \{ */
+
static int get_main_socket_priority(const bNodeSocket *socket)
{
switch ((eNodeSocketDatatype)socket->type) {
@@ -1939,7 +2004,7 @@ static bNodeSocket *get_main_socket(ListBase *sockets)
}
}
- return NULL;
+ return nullptr;
}
static bool node_parents_offset_flag_enable_cb(bNode *parent, void *UNUSED(userdata))
@@ -1977,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,
@@ -1985,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) {
@@ -2022,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) {
@@ -2035,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) {
@@ -2086,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;
@@ -2152,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,
@@ -2173,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;
}
@@ -2203,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);
@@ -2259,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)
{
@@ -2270,7 +2342,7 @@ 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;
}
@@ -2298,7 +2370,8 @@ void ED_node_link_insert(Main *bmain, ScrArea *area)
/* 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;
@@ -2314,3 +2387,5 @@ void ED_node_link_insert(Main *bmain, ScrArea *area)
}
}
}
+
+/** \} */
diff --git a/source/blender/editors/space_node/node_select.c b/source/blender/editors/space_node/node_select.cc
index de63aa07acb..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,7 +568,7 @@ static int node_mouse_select(bContext *C,
ret_value = OPERATOR_FINISHED;
}
}
- else if (deselect_all && node == NULL) {
+ 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. */
@@ -576,13 +577,13 @@ static int node_mouse_select(bContext *C,
}
else {
/* Deselect in empty space. */
- for (tnode = snode->edittree->nodes.first; tnode; tnode = tnode->next) {
+ for (tnode = (bNode *)snode->edittree->nodes.first; tnode; tnode = tnode->next) {
nodeSetSelected(tnode, false);
}
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)) {
@@ -591,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);
}
@@ -605,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);
@@ -614,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;
@@ -681,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);
@@ -693,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) {
@@ -703,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;
}
@@ -740,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)");
@@ -766,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);
@@ -780,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;
}
@@ -853,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;
@@ -873,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;
@@ -885,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);
@@ -916,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)");
@@ -940,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;
}
@@ -973,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;
}
@@ -986,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);
}
@@ -994,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;
}
@@ -1025,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;
}
@@ -1038,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);
}
@@ -1046,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;
}
@@ -1079,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);
@@ -1093,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--;
}
@@ -1112,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;
@@ -1168,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", "");
}
/** \} */
@@ -1223,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);
@@ -1261,7 +1263,7 @@ static uiBlock *node_find_menu(bContext *C, ARegion *region, void *arg_op)
0,
"");
UI_but_func_search_set(
- but, NULL, node_find_update_fn, op->type, false, NULL, node_find_exec_fn, NULL);
+ 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 */
@@ -1273,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;
}
@@ -1306,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 c880f3e99d8..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,14 +325,15 @@ 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) {
+ 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)) {
@@ -341,10 +345,11 @@ 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) {
+ 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)) {
@@ -354,7 +359,8 @@ static void ui_node_link_items(NodeLinkArg *arg,
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;
@@ -380,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++) {
@@ -474,15 +480,14 @@ 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) {
const char *disabled_hint;
@@ -498,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;
@@ -531,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;
}
@@ -553,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,
@@ -578,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)
@@ -635,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,
@@ -646,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,
@@ -662,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,
@@ -683,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;
@@ -698,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);
@@ -739,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);
}
}
@@ -749,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;
@@ -759,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 */
@@ -862,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 */
@@ -879,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_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c
index c532dd8accd..f809bb13b42 100644
--- a/source/blender/editors/space_outliner/outliner_tools.c
+++ b/source/blender/editors/space_outliner/outliner_tools.c
@@ -740,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)) {
@@ -846,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);
}
diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c
index bed7683703f..90389fc1be2 100644
--- a/source/blender/editors/space_outliner/outliner_tree.c
+++ b/source/blender/editors/space_outliner/outliner_tree.c
@@ -1925,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_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c
index cd75bc98b15..ac31e0e7c37 100644
--- a/source/blender/editors/space_sequencer/sequencer_add.c
+++ b/source/blender/editors/space_sequencer/sequencer_add.c
@@ -158,7 +158,7 @@ static void sequencer_generic_props__internal(wmOperatorType *ot, int flag)
ot->prop = RNA_def_boolean(ot->srna,
"set_view_transform",
true,
- "Set view transform",
+ "Set View Transform",
"Set appropriate view transform based on media colorspace");
}
}
@@ -1081,8 +1081,7 @@ static int sequencer_add_image_strip_invoke(bContext *C,
sequencer_disable_one_time_properties(C, op);
- const SequencerToolSettings *tool_settings = scene->toolsettings->sequencer_tool_settings;
- RNA_enum_set(op->ptr, "fit_method", tool_settings->fit_method);
+ 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")) {
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_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c
index ebd4c0090b4..f22a515a8f2 100644
--- a/source/blender/editors/space_sequencer/sequencer_edit.c
+++ b/source/blender/editors/space_sequencer/sequencer_edit.c
@@ -1582,21 +1582,29 @@ 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) {
+ 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);
}
@@ -3053,7 +3061,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_select.c b/source/blender/editors/space_sequencer/sequencer_select.c
index cfc11afce13..7e515271b13 100644
--- a/source/blender/editors/space_sequencer/sequencer_select.c
+++ b/source/blender/editors/space_sequencer/sequencer_select.c
@@ -42,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. */
@@ -807,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. */
@@ -1187,7 +1188,7 @@ static int sequencer_select_side_of_frame_exec(bContext *C, wmOperator *op)
test = (timeline_frame <= seq->startdisp);
break;
case 2:
- test = (timeline_frame <= seq->enddisp) && (timeline_frame >= seq->startdisp);
+ test = SEQ_time_strip_intersects_frame(seq, timeline_frame);
break;
}
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh b/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh
index d1e80f1d87e..c9b73aabf96 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh
@@ -50,7 +50,7 @@ class CellValue {
std::optional<bool> value_bool;
std::optional<float2> value_float2;
std::optional<float3> value_float3;
- std::optional<Color4f> value_color;
+ std::optional<ColorGeometry4f> value_color;
std::optional<ObjectCellValue> value_object;
std::optional<CollectionCellValue> value_collection;
};
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
index 02ffa1259fc..452885959f6 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
@@ -116,7 +116,7 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
column_id.name,
domain_size,
[varray](int index, CellValue &r_cell_value) {
- Color4f value;
+ ColorGeometry4f value;
varray->get(index, &value);
r_cell_value.value_color = value;
},
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc
index f1ca65817f6..8079763a339 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc
@@ -170,7 +170,7 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer {
this->draw_float_vector(params, Span(&value.x, 3));
}
else if (cell_value.value_color.has_value()) {
- const Color4f value = *cell_value.value_color;
+ const ColorGeometry4f value = *cell_value.value_color;
this->draw_float_vector(params, Span(&value.r, 4));
}
else if (cell_value.value_object.has_value()) {
diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c
index 0a89b7d0292..4a595c716b6 100644
--- a/source/blender/editors/space_view3d/view3d_draw.c
+++ b/source/blender/editors/space_view3d/view3d_draw.c
@@ -2039,6 +2039,9 @@ ImBuf *ED_view3d_draw_offscreen_imbuf_simple(Depsgraph *depsgraph,
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;
diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c
index 967ad966320..8b6d0e9ee04 100644
--- a/source/blender/editors/space_view3d/view3d_edit.c
+++ b/source/blender/editors/space_view3d/view3d_edit.c
@@ -3666,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;
}
@@ -3690,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 {
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_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_utils.c b/source/blender/editors/space_view3d/view3d_utils.c
index f96c17d7cff..8ae5d4a29e9 100644
--- a/source/blender/editors/space_view3d/view3d_utils.c
+++ b/source/blender/editors/space_view3d/view3d_utils.c
@@ -299,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 */
@@ -1057,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;
}
}
@@ -1091,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)
@@ -1716,7 +1716,7 @@ bool ED_view3d_depth_read_cached_normal(const ViewContext *vc,
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;
}
}
@@ -1751,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/transform/transform.c b/source/blender/editors/transform/transform.c
index ba8e5c1e2c6..17bfc23aea7 100644
--- a/source/blender/editors/transform/transform.c
+++ b/source/blender/editors/transform/transform.c
@@ -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 == 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));
- }
+ 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);
}
diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h
index 0533c08bdc2..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;
@@ -152,11 +153,11 @@ typedef enum {
/** #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 */
diff --git a/source/blender/editors/transform/transform_constraints.c b/source/blender/editors/transform/transform_constraints.c
index 615467932a7..8c74d5349ba 100644
--- a/source/blender/editors/transform/transform_constraints.c
+++ b/source/blender/editors/transform/transform_constraints.c
@@ -975,7 +975,6 @@ void initSelectConstraint(TransInfo *t)
}
setUserConstraint(t, CON_APPLY | CON_SELECT, "%s");
- setNearestAxis(t);
}
void selectConstraint(TransInfo *t)
@@ -1062,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);
}
@@ -1072,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);
}
@@ -1082,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_mesh.c b/source/blender/editors/transform/transform_convert_mesh.c
index f715228e25e..422370cb13b 100644
--- a/source/blender/editors/transform/transform_convert_mesh.c
+++ b/source/blender/editors/transform/transform_convert_mesh.c
@@ -52,6 +52,66 @@
/** \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;
@@ -83,33 +143,6 @@ struct TransCustomDataLayer {
bool use_merge_group;
};
-static void tc_mesh_customdatacorrect_free_fn(struct TransInfo *UNUSED(t),
- struct TransDataContainer *UNUSED(tc),
- struct TransCustomData *custom_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;
-}
-
#define USE_FACE_SUBSTITUTE
#ifdef USE_FACE_SUBSTITUTE
# define FACE_SUBSTITUTE_INDEX INT_MIN
@@ -292,8 +325,8 @@ static void tc_mesh_customdatacorrect_init_container_merge_group(TransDataContai
tcld->arena, tcld->merge_group.data_len * sizeof(*tcld->merge_group.data));
}
-static struct TransCustomDataLayer *tc_mesh_customdatacorrect_create(TransDataContainer *tc,
- const bool use_merge_group)
+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;
@@ -341,18 +374,41 @@ static struct TransCustomDataLayer *tc_mesh_customdatacorrect_create(TransDataCo
return tcld;
}
-static void tc_mesh_customdata_create(TransDataContainer *tc, const bool use_merge_group)
+static void tc_mesh_customdatacorrect_create(TransDataContainer *tc, const bool use_merge_group)
{
struct TransCustomDataLayer *customdatacorrect;
- customdatacorrect = tc_mesh_customdatacorrect_create(tc, use_merge_group);
+ customdatacorrect = tc_mesh_customdatacorrect_create_impl(tc, use_merge_group);
if (!customdatacorrect) {
return;
}
- BLI_assert(tc->custom.type.data == NULL);
- tc->custom.type.data = customdatacorrect;
- tc->custom.type.free_cb = tc_mesh_customdatacorrect_free_fn;
+ 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)
@@ -390,10 +446,14 @@ void transform_convert_mesh_customdatacorrect_init(TransInfo *t)
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
if (tc->custom.type.data != NULL) {
- tc_mesh_customdatacorrect_free_fn(t, tc, &tc->custom.type);
+ 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_customdata_create(tc, use_merge_group);
+ tc_mesh_customdatacorrect_create(tc, use_merge_group);
}
}
@@ -555,10 +615,11 @@ static void tc_mesh_customdatacorrect_apply_vert(struct TransCustomDataLayer *tc
static void tc_mesh_customdatacorrect_apply(TransDataContainer *tc, bool is_final)
{
- if (!tc->custom.type.data) {
+ struct TransCustomDataMesh *tcmd = tc->custom.type.data;
+ struct TransCustomDataLayer *tcld = tcmd ? tcmd->cd_layer_correct : NULL;
+ if (tcld == NULL) {
return;
}
- struct TransCustomDataLayer *tcld = tc->custom.type.data;
const bool use_merge_group = tcld->use_merge_group;
struct TransCustomDataMergeGroup *merge_data = tcld->merge_group.data;
@@ -590,7 +651,8 @@ static void tc_mesh_customdatacorrect_apply(TransDataContainer *tc, bool is_fina
static void tc_mesh_customdatacorrect_restore(struct TransInfo *t)
{
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- struct TransCustomDataLayer *tcld = tc->custom.type.data;
+ struct TransCustomDataMesh *tcmd = tc->custom.type.data;
+ struct TransCustomDataLayer *tcld = tcmd ? tcmd->cd_layer_correct : NULL;
if (!tcld) {
continue;
}
@@ -1617,6 +1679,100 @@ void createTransEditVerts(TransInfo *t)
/** \name Recalc Mesh Data
* \{ */
+static bool bm_vert_tag_filter_fn(BMVert *v, void *UNUSED(user_data))
+{
+ return BM_elem_flag_test(v, BM_ELEM_TAG);
+}
+
+static BMPartialUpdate *tc_mesh_ensure_partial_update(TransInfo *t, TransDataContainer *tc)
+{
+ struct TransCustomDataMesh *tcmd = tc_mesh_customdata_ensure(tc);
+
+ if (tcmd->partial_update.cache) {
+
+ /* 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. */
+
+ bool recalc;
+ if (tcmd->partial_update.prop_size_prev < t->prop_size) {
+ /* Size increase, simply recalculate. */
+ recalc = true;
+ }
+ 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;
+ }
+ 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 {
+ BLI_assert(t->prop_size == tcmd->partial_update.prop_size_prev);
+ recalc = false;
+ }
+
+ if (!recalc) {
+ return tcmd->partial_update.cache;
+ }
+
+ BM_mesh_partial_destroy(tcmd->partial_update.cache);
+ tcmd->partial_update.cache = NULL;
+ }
+
+ BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
+
+ BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false);
+
+ 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;
+ }
+ }
+
+ 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));
+
+ /* 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;
+ }
+ }
+
+ 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);
+
+ tcmd->partial_update.prop_size_prev = t->prop_size;
+ tcmd->partial_update.prop_size = t->prop_size;
+
+ return tcmd->partial_update.cache;
+}
+
static void tc_mesh_transdata_mirror_apply(TransDataContainer *tc)
{
if (tc->use_mirror_axis_any) {
@@ -1678,8 +1834,22 @@ void recalcData_mesh(TransInfo *t)
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
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);
+ }
}
}
/** \} */
diff --git a/source/blender/editors/transform/transform_mode.c b/source/blender/editors/transform/transform_mode.c
index d61e7bb867c..350be247014 100644
--- a/source/blender/editors/transform/transform_mode.c
+++ b/source/blender/editors/transform/transform_mode.c
@@ -524,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;
@@ -533,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);
}
}
@@ -811,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;
@@ -827,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);
}
}
diff --git a/source/blender/editors/transform/transform_mode.h b/source/blender/editors/transform/transform_mode.h
index 106dc68c9ee..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,7 +55,7 @@ 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(
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 b7b3de69731..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);
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_resize.c b/source/blender/editors/transform/transform_mode_resize.c
index 0d7d0be3c0e..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 */
diff --git a/source/blender/editors/transform/transform_mode_rotate.c b/source/blender/editors/transform/transform_mode_rotate.c
index 0fdbfb25989..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);
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 77e57484bef..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;
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 0bef6364214..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);
}
@@ -471,6 +471,7 @@ void initTranslation(TransInfo *t)
t->num.unit_type[2] = B_UNIT_NONE;
}
- transform_mode_default_modal_orientation_set(t, V3D_ORIENT_GLOBAL);
+ 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/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/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/gpencil_modifiers/intern/MOD_gpencilopacity.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c
index ea37558fa7f..9f03e493ea8 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c
@@ -204,8 +204,6 @@ 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);
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c
index 512e3af063a..126949cd659 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c
@@ -170,8 +170,10 @@ static void deformStroke(GpencilModifierData *md,
weight *= curvef;
}
- float fac_begin = mmd->flag & GP_THICK_NORMALIZE ? 1 : mmd->thickness_fac;
- target *= interpf(fac_begin, mmd->fading_end_factor, factor_depth);
+ /* 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);
diff --git a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h
index 44ff0616fe9..712e92d017d 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h
+++ b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h
@@ -93,8 +93,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 +107,7 @@ typedef struct LineartLineSegment {
* enough for most cases.
*/
unsigned char transparency_mask;
-} LineartLineSegment;
+} LineartEdgeSegment;
typedef struct LineartVert {
double gloc[3];
@@ -155,7 +155,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 +163,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 +179,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 +192,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 +205,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;
@@ -219,6 +230,11 @@ typedef struct LineartRenderBuffer {
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 +253,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;
@@ -334,20 +333,13 @@ 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;
@@ -387,10 +379,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;
@@ -529,7 +525,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,
@@ -565,6 +561,6 @@ void MOD_lineart_gpencil_generate(LineartRenderBuffer *rb,
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 4ad8eed6ddd..23928b4ccda 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_acquire(&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_acquire(&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_acquire(&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;
+ 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 52f7d3652a7..a9d17f19206 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
@@ -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_acquire_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,11 +474,13 @@ 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);
@@ -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_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_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_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,26 +760,26 @@ 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 \
+#define INCREASE_EDGE \
e_count++; \
v1_obi = e->v1_obindex; \
v2_obi = e->v2_obindex; \
@@ -755,40 +787,40 @@ static void lineart_triangle_cull_single(LineartRenderBuffer *rb,
e = new_e; \
e->v1_obindex = v1_obi; \
e->v2_obindex = v2_obi; \
- rls = lineart_mem_acquire(&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,35 +1463,35 @@ 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_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;
}
}
@@ -1477,11 +1509,11 @@ static void lineart_geometry_object_load(Depsgraph *dg,
BMEdge *e;
BMLoop *loop;
LineartEdge *la_e;
- LineartTriangle *rt;
+ LineartTriangle *tri;
LineartTriangleAdjacent *orta;
double new_mvp[4][4], new_mv[4][4], normal[4][4];
float imat[4][4];
- LineartElementLinkNode *reln;
+ LineartElementLinkNode *eln;
LineartVert *orv;
LineartEdge *o_la_e;
LineartTriangle *ort;
@@ -1581,10 +1613,10 @@ static void lineart_geometry_object_load(Depsgraph *dg,
orig_ob = ob->id.orig_id ? (Object *)ob->id.orig_id : ob;
- reln = lineart_list_append_pointer_pool_sized(
+ eln = 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;
+ eln->element_count = bm->totvert;
+ eln->object_ref = orig_ob;
if (ob->lineart.flags & OBJECT_LRT_OWN_CREASE) {
use_crease = cosf(M_PI - ob->lineart.crease_threshold);
@@ -1596,14 +1628,14 @@ static void lineart_geometry_object_load(Depsgraph *dg,
/* 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;
+ eln->flags |= LRT_ELEMENT_BORDER_ONLY;
}
- reln = lineart_list_append_pointer_pool_sized(
+ eln = 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);
+ eln->element_count = bm->totface;
+ eln->object_ref = orig_ob;
+ eln->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");
@@ -1621,39 +1653,39 @@ static void lineart_geometry_object_load(Depsgraph *dg,
* index to come close together. */
(*global_vindex) += bm->totvert;
- rt = ort;
+ tri = 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)];
+ tri->v[0] = &orv[BM_elem_index_get(loop->v)];
loop = loop->next;
- rt->v[1] = &orv[BM_elem_index_get(loop->v)];
+ tri->v[1] = &orv[BM_elem_index_get(loop->v)];
loop = loop->next;
- rt->v[2] = &orv[BM_elem_index_get(loop->v)];
+ tri->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);
+ tri->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);
+ mul_v3_mat3_m4v3_db(tri->gn, normal, gn);
+ normalize_v3_db(tri->gn);
if (usage == OBJECT_LRT_INTERSECTION_ONLY) {
- rt->flags |= LRT_TRIANGLE_INTERSECTION_ONLY;
+ tri->flags |= LRT_TRIANGLE_INTERSECTION_ONLY;
}
else if (ELEM(usage, OBJECT_LRT_NO_INTERSECTION, OBJECT_LRT_OCCLUSION_ONLY)) {
- rt->flags |= LRT_TRIANGLE_NO_INTERSECTION;
+ tri->flags |= LRT_TRIANGLE_NO_INTERSECTION;
}
/* Re-use this field to refer to adjacent info, will be cleared after culling stage. */
- rt->intersecting_verts = (void *)&orta[i];
+ tri->intersecting_verts = (void *)&orta[i];
- rt = (LineartTriangle *)(((uchar *)rt) + rb->triangle_size);
+ tri = (LineartTriangle *)(((uchar *)tri) + rb->triangle_size);
}
/* Use BM_ELEM_TAG in f->head.hflag to store needed faces in the first iteration. */
@@ -1676,10 +1708,10 @@ static void lineart_geometry_object_load(Depsgraph *dg,
}
o_la_e = lineart_mem_acquire(&rb->render_data_pool, sizeof(LineartEdge) * allocate_la_e);
- reln = lineart_list_append_pointer_pool_sized(
+ eln = 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;
+ eln->element_count = allocate_la_e;
+ eln->object_ref = orig_ob;
la_e = o_la_e;
for (i = 0; i < bm->totedge; i++) {
@@ -1707,9 +1739,9 @@ static void lineart_geometry_object_load(Depsgraph *dg,
la_e->flags = e->head.hflag;
la_e->object_ref = orig_ob;
- LineartLineSegment *rls = lineart_mem_acquire(&rb->render_data_pool,
- sizeof(LineartLineSegment));
- BLI_addtail(&la_e->segments, rls);
+ LineartEdgeSegment *es = lineart_mem_acquire(&rb->render_data_pool,
+ sizeof(LineartEdgeSegment));
+ BLI_addtail(&la_e->segments, es);
if (ELEM(usage, OBJECT_LRT_INHERIT, OBJECT_LRT_INCLUDE, OBJECT_LRT_NO_INTERSECTION)) {
lineart_add_edge_to_list(rb, la_e);
}
@@ -1875,51 +1907,51 @@ static void lineart_main_load_geometries(
* 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
@@ -1961,7 +1993,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,
@@ -1988,8 +2020,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])) ||
@@ -2001,7 +2033,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;
}
@@ -2013,8 +2045,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);
@@ -2025,14 +2057,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;
}
@@ -2271,17 +2307,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;
}
@@ -2294,7 +2330,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)
{
@@ -2306,11 +2342,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;
}
}
@@ -2360,7 +2396,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;
@@ -2379,14 +2415,14 @@ 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_acquire(&rb->render_data_pool, (sizeof(LineartVertIntersection)));
@@ -2394,47 +2430,47 @@ static LineartEdge *lineart_triangle_intersect(LineartRenderBuffer *rb,
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;
@@ -2443,7 +2479,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;
@@ -2452,7 +2488,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;
@@ -2484,70 +2520,67 @@ 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_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_acquire(&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;
}
@@ -2561,12 +2594,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);
}
}
@@ -2598,22 +2631,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);
@@ -2671,6 +2693,13 @@ 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;
@@ -2746,6 +2775,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(
@@ -2925,7 +2962,7 @@ static void lineart_bounding_area_split(LineartRenderBuffer *rb,
{
LineartBoundingArea *ba = lineart_mem_acquire(&rb->render_data_pool,
sizeof(LineartBoundingArea) * 4);
- LineartTriangle *rt;
+ LineartTriangle *tri;
LineartEdge *e;
ba[0].l = root->cx;
@@ -2960,35 +2997,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)
@@ -3032,11 +3081,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;
@@ -3056,9 +3105,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;
}
@@ -3071,27 +3120,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 {
@@ -3099,54 +3148,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);
}
}
}
@@ -3162,7 +3211,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);
}
}
@@ -3172,7 +3221,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,
@@ -3181,14 +3230,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;
@@ -3355,33 +3404,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);
}
}
}
@@ -3656,6 +3705,8 @@ 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. */
+ BKE_scene_camera_switch_update(scene);
+
if (!scene->camera) {
return false;
}
@@ -3815,52 +3866,52 @@ static void lineart_gpencil_generate(LineartRenderBuffer *rb,
bool invert_input = modifier_flags & LRT_GPENCIL_INVERT_SOURCE_VGROUP;
bool match_output = modifier_flags & LRT_GPENCIL_MATCH_OUTPUT_VGROUP;
- 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?. */
@@ -3874,7 +3925,7 @@ static void lineart_gpencil_generate(LineartRenderBuffer *rb,
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) {
@@ -3890,8 +3941,8 @@ 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;
}
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h b/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h
index 47ca6e45bd5..9ed98b38f07 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h
@@ -58,7 +58,7 @@ 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 +76,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_util.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_util.c
index dcb1f9cde5d..d05f931f75d 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/lineart_util.c
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_util.c
@@ -135,11 +135,11 @@ 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)
diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt
index f1ffd7827b8..cf6009c2881 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
@@ -390,6 +394,7 @@ if(WITH_GTESTS)
if(WITH_OPENGL_DRAW_TESTS)
set(TEST_SRC
tests/gpu_testing.cc
+ tests/gpu_shader_test.cc
tests/gpu_testing.hh
)
diff --git a/source/blender/gpu/GPU_capabilities.h b/source/blender/gpu/GPU_capabilities.h
index f54ecece659..0c054d4f264 100644
--- a/source/blender/gpu/GPU_capabilities.h
+++ b/source/blender/gpu/GPU_capabilities.h
@@ -37,6 +37,8 @@ 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);
@@ -55,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_compute.h b/source/blender/gpu/GPU_compute.h
new file mode 100644
index 00000000000..a048f72c0a0
--- /dev/null
+++ b/source/blender/gpu/GPU_compute.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.
+ */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#pragma once
+
+#include "BLI_sys_types.h"
+
+#include "GPU_shader.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void GPU_compute_dispatch(GPUShader *shader,
+ uint groups_x_len,
+ uint groups_y_len,
+ uint groups_z_len);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/gpu/GPU_index_buffer.h b/source/blender/gpu/GPU_index_buffer.h
index 76aab3c196b..8362dbcaccc 100644
--- a/source/blender/gpu/GPU_index_buffer.h
+++ b/source/blender/gpu/GPU_index_buffer.h
@@ -49,6 +49,7 @@ 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);
void GPU_indexbuf_add_generic_vert(GPUIndexBufBuilder *, uint v);
void GPU_indexbuf_add_primitive_restart(GPUIndexBufBuilder *);
@@ -70,6 +71,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 +80,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_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_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_capabilities.cc b/source/blender/gpu/intern/gpu_capabilities.cc
index d8764502800..c6e9dc210cb 100644
--- a/source/blender/gpu/intern/gpu_capabilities.cc
+++ b/source/blender/gpu/intern/gpu_capabilities.cc
@@ -82,6 +82,16 @@ 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;
@@ -148,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 7c1d4590ce8..95cf7fd335d 100644
--- a/source/blender/gpu/intern/gpu_capabilities_private.hh
+++ b/source/blender/gpu/intern/gpu_capabilities_private.hh
@@ -41,6 +41,8 @@ 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;
@@ -51,6 +53,8 @@ struct GPUCapabilities {
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_index_buffer.cc b/source/blender/gpu/intern/gpu_index_buffer.cc
index 65932d2dbf4..20a26c0fe9d 100644
--- a/source/blender/gpu/intern/gpu_index_buffer.cc
+++ b/source/blender/gpu/intern/gpu_index_buffer.cc
@@ -31,6 +31,8 @@
#include "gpu_index_buffer_private.hh"
+#include <cstring>
+
#define KEEP_SINGLE_COPY 1
#define RESTART_INDEX 0xFFFFFFFF
@@ -66,6 +68,14 @@ 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_add_generic_vert(GPUIndexBufBuilder *builder, uint v)
{
#if TRUST_NO_ONE
@@ -241,6 +251,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 +326,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
/** \} */
@@ -351,6 +378,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 +403,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..358258604bf 100644
--- a/source/blender/gpu/intern/gpu_index_buffer_private.hh
+++ b/source/blender/gpu/intern/gpu_index_buffer_private.hh
@@ -75,13 +75,14 @@ class IndexBuf {
void init(uint indices_len, uint32_t *indices);
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_matrix.cc b/source/blender/gpu/intern/gpu_matrix.cc
index 569b51a407a..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:
*
@@ -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_shader.cc b/source/blender/gpu/intern/gpu_shader.cc
index aea27756708..265dec7c56a 100644
--- a/source/blender/gpu/intern/gpu_shader.cc
+++ b/source/blender/gpu/intern/gpu_shader.cc
@@ -290,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,
@@ -297,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);
@@ -349,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);
@@ -380,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,
@@ -402,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,
@@ -567,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)
{
diff --git a/source/blender/gpu/intern/gpu_shader_interface.cc b/source/blender/gpu/intern/gpu_shader_interface.cc
index c584c40eca8..ae94112b17b 100644
--- a/source/blender/gpu/intern/gpu_shader_interface.cc
+++ b/source/blender/gpu/intern/gpu_shader_interface.cc
@@ -80,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";
@@ -117,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_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 31b6549fc3b..d85f9f7684d 100644
--- a/source/blender/gpu/opengl/gl_backend.cc
+++ b/source/blender/gpu/opengl/gl_backend.cc
@@ -437,6 +437,16 @@ void GLBackend::capabilities_init()
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_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_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 b65686165d9..e2478a9976c 100644
--- a/source/blender/gpu/opengl/gl_texture.cc
+++ b/source/blender/gpu/opengl/gl_texture.cc
@@ -368,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/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/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/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h
index d131e4dacdc..9c84127105a 100644
--- a/source/blender/imbuf/IMB_imbuf.h
+++ b/source/blender/imbuf/IMB_imbuf.h
@@ -380,8 +380,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);
diff --git a/source/blender/imbuf/intern/IMB_anim.h b/source/blender/imbuf/intern/IMB_anim.h
index 7d7864306a1..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/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c
index 5918a4bf16e..622b6cbfc16 100644
--- a/source/blender/imbuf/intern/anim_movie.c
+++ b/source/blender/imbuf/intern/anim_movie.c
@@ -433,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",
@@ -650,12 +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 = av_packet_alloc();
- 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;
@@ -671,7 +670,7 @@ static int startffmpeg(struct anim *anim)
fprintf(stderr, "Could not allocate frame data.\n");
avcodec_free_context(&anim->pCodecCtx);
avformat_close_input(&anim->pFormatCtx);
- av_packet_free(&anim->next_packet);
+ av_packet_free(&anim->cur_packet);
av_frame_free(&anim->pFrameRGB);
av_frame_free(&anim->pFrameDeinterlaced);
av_frame_free(&anim->pFrame);
@@ -684,7 +683,7 @@ static int startffmpeg(struct anim *anim)
fprintf(stderr, "ffmpeg has changed alloc scheme ... ARGHHH!\n");
avcodec_free_context(&anim->pCodecCtx);
avformat_close_input(&anim->pFormatCtx);
- av_packet_free(&anim->next_packet);
+ av_packet_free(&anim->cur_packet);
av_frame_free(&anim->pFrameRGB);
av_frame_free(&anim->pFrameDeinterlaced);
av_frame_free(&anim->pFrame);
@@ -706,20 +705,13 @@ static int startffmpeg(struct anim *anim)
1);
}
- if (pCodecCtx->has_b_frames) {
- anim->preseek = 25; /* FIXME: detect gopsize ... */
- }
- else {
- anim->preseek = 0;
- }
-
anim->img_convert_ctx = sws_getContext(anim->x,
anim->y,
anim->pCodecCtx->pix_fmt,
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);
@@ -728,7 +720,7 @@ static int startffmpeg(struct anim *anim)
fprintf(stderr, "Can't transform color space??? Bailing out...\n");
avcodec_free_context(&anim->pCodecCtx);
avformat_close_input(&anim->pFormatCtx);
- av_packet_free(&anim->next_packet);
+ av_packet_free(&anim->cur_packet);
av_frame_free(&anim->pFrameRGB);
av_frame_free(&anim->pFrameDeinterlaced);
av_frame_free(&anim->pFrame);
@@ -769,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) {
@@ -902,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)
{
@@ -910,40 +902,43 @@ 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_packet_unref(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_send_packet(anim->pCodecCtx, 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->pFormatCtx, 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 ", 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,
- (int64_t)anim->next_pts);
+ (int64_t)anim->cur_pts);
break;
}
}
- av_packet_unref(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) {
@@ -954,20 +949,23 @@ static int ffmpeg_decode_video_frame(struct anim *anim)
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->pFormatCtx, 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 ", 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,
- (int64_t)anim->next_pts);
+ (int64_t)anim->cur_pts);
rval = 0;
}
}
if (rval < 0) {
- av_packet_unref(anim->next_packet);
- 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,
@@ -1021,6 +1019,35 @@ static int ffmpeg_seek_by_byte(AVFormatContext *pFormatCtx)
return false;
}
+static int64_t ffmpeg_get_seek_pos(struct anim *anim, int position)
+{
+ 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;
+
+ 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 (pos < 0) {
+ pos = 0;
+ }
+
+ if (st_time != AV_NOPTS_VALUE) {
+ pos += st_time;
+ }
+
+ return pos;
+}
+
static int64_t ffmpeg_get_pts_to_search(struct anim *anim,
struct anim_index *tc_index,
int position)
@@ -1045,77 +1072,60 @@ static int64_t ffmpeg_get_pts_to_search(struct anim *anim,
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)
{
- return anim->last_frame && anim->last_pts <= pts_to_search && anim->next_pts > pts_to_search;
-}
-
-/* Requested video frame is expected to be found within different GOP as last decoded frame.
- * Seeking to new position and scanning is fastest way to get requested frame.
- * Check whether ffmpeg_can_scan() and ffmpeg_pts_matches_last_frame() is false before using this
- * function. */
-static bool ffmpeg_can_seek(struct anim *anim, int position)
-{
- return position != anim->curposition + 1;
-}
-
-/* Requested video frame is expected to be found within same GOP as last decoded frame.
- * Decoding frames in sequence until frame matches requested one is fastest way to get it. */
-static bool ffmpeg_can_scan(struct anim *anim, int position, struct anim_index *tc_index)
-{
- if (position > anim->curposition + 1 && anim->preseek && !tc_index &&
- position - (anim->curposition + 1) < anim->preseek) {
- return true;
- }
-
- if (tc_index == NULL) {
- return false;
+ if (anim->pFrame && anim->cur_frame_final) {
+ return labs(anim->cur_pts - pts_to_search) < anim->pFrame->pkt_duration;
}
- int new_frame_index = IMB_indexer_get_frame_index(tc_index, position);
- int old_frame_index = IMB_indexer_get_frame_index(tc_index, anim->curposition);
- return IMB_indexer_can_scan(tc_index, old_frame_index, new_frame_index);
+ return false;
}
static bool ffmpeg_is_first_frame_decode(struct anim *anim, int position)
{
- return position == 0 && anim->curposition == -1;
+ 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 preseek interval\n");
-
- /* there seem to exist *very* silly GOP lengths out in the wild... */
- int count = 1000;
+ av_log(anim->pFormatCtx, AV_LOG_DEBUG, "FETCH: within current GOP\n");
av_log(anim->pFormatCtx,
AV_LOG_DEBUG,
"SCAN start: considering pts=%" PRId64 " in search of %" PRId64 "\n",
- (int64_t)anim->next_pts,
+ (int64_t)anim->cur_pts,
(int64_t)pts_to_search);
- while (count > 0 && anim->next_pts < pts_to_search) {
+ int64_t start_gop_frame = anim->cur_key_frame_pts;
+
+ while (anim->cur_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)anim->cur_pts,
(int64_t)pts_to_search);
if (!ffmpeg_decode_video_frame(anim)) {
break;
}
- count--;
+
+ if (start_gop_frame != anim->cur_key_frame_pts) {
+ break;
+ }
}
- if (count == 0) {
+
+ 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_ERROR,
"SCAN failed: completely lost in stream, "
"bailing out at PTS=%" PRId64 ", searching for PTS=%" PRId64 "\n",
- (int64_t)anim->next_pts,
+ (int64_t)anim->cur_pts,
(int64_t)pts_to_search);
}
- if (anim->next_pts == pts_to_search) {
+ if (anim->cur_pts == pts_to_search) {
av_log(anim->pFormatCtx, AV_LOG_DEBUG, "SCAN HAPPY: we found our PTS!\n");
}
else {
@@ -1123,22 +1133,23 @@ static void ffmpeg_decode_video_frame_scan(struct anim *anim, int64_t pts_to_sea
}
}
-/* 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)
+/* 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)
{
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 current_pos = *requested_pos;
/* This time offset maximum limit is arbitrary. If some files fails to decode it may be
- * increased. Seek performance will be negatively affected. Small initial offset is necessary
- * because encoder can re-arrange frames as it needs but within it's delay, which is usually
- * small. */
+ * increased. Seek performance will be negatively affected. Small initial offset is
+ * necessary because encoder can re-arrange frames as it needs but within it's delay, which
+ * is usually small. */
for (int offset = 5; offset < 25; offset++) {
- current_pos = requested_pos - ((int64_t)(offset)*AV_TIME_BASE / frame_rate);
+ current_pos = *requested_pos - ((int64_t)(offset)*AV_TIME_BASE / frame_rate);
current_pos = max_ii(current_pos, 0);
/* Seek to timestamp. */
@@ -1147,42 +1158,51 @@ static int ffmpeg_generic_seek_workaround(struct anim *anim, int64_t requested_p
}
/* Read first video stream packet. */
- AVPacket read_packet = {0};
- while (av_read_frame(anim->pFormatCtx, &read_packet) >= 0) {
- if (anim->next_packet->stream_index == anim->videoStream) {
+ AVPacket *read_packet = av_packet_alloc();
+ while (av_read_frame(anim->pFormatCtx, read_packet) >= 0) {
+ if (anim->cur_packet->stream_index == anim->videoStream) {
break;
}
}
/* If this packet contains I-frame, exit loop. This should be the frame that we need. */
- if (read_packet.flags & AV_PKT_FLAG_KEY) {
+ bool is_key_frame = read_packet->flags & AV_PKT_FLAG_KEY;
+ av_packet_free(&read_packet);
+ if (is_key_frame) {
break;
}
}
+ *requested_pos = current_pos;
+
/* 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 I-frame and scan-decode until requested frame is found. */
-static void ffmpeg_seek_and_decode(struct anim *anim, int position, struct anim_index *tc_index)
+/* Seek to last necessary key frame. */
+static int ffmpeg_seek_to_key_frame(struct anim *anim, int position, struct anim_index *tc_index)
{
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 pts_to_search = ffmpeg_get_pts_to_search(anim, tc_index, position);
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 dts;
pos = IMB_indexer_get_seek_pos(tc_index, new_frame_index);
dts = IMB_indexer_get_seek_pos_dts(tc_index, new_frame_index);
+ anim->cur_key_frame_pts = pos;
+
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);
@@ -1198,22 +1218,9 @@ static void ffmpeg_seek_and_decode(struct anim *anim, int position, struct anim_
}
}
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 (pos < 0) {
- pos = 0;
- }
-
- if (st_time != AV_NOPTS_VALUE) {
- pos += st_time;
- }
-
+ /* 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;
@@ -1222,11 +1229,44 @@ static void ffmpeg_seek_and_decode(struct anim *anim, int position, struct anim_
ret = av_seek_frame(anim->pFormatCtx, -1, pos, AVSEEK_FLAG_BACKWARD);
}
else {
- ret = ffmpeg_generic_seek_workaround(anim, pos);
+ ret = ffmpeg_generic_seek_workaround(anim, &pos);
+ av_log(anim->pFormatCtx, AV_LOG_DEBUG, "Adjusted final seek pos = %" PRId64 "\n", pos);
+ }
+
+ if (ret >= 0) {
+ /* 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);
+ }
+ bool same_gop = current_gop_start_packet->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) {
+ if (temp->stream_index == anim->videoStream && temp->pts == anim->cur_packet->pts) {
+ break;
+ }
+ av_packet_unref(temp);
+ }
+ av_packet_free(&current_gop_start_packet);
+ av_packet_free(&temp);
+ return 0;
+ }
+
+ anim->cur_key_frame_pts = current_gop_start_packet->pts;
+ av_packet_free(&current_gop_start_packet);
+ /* Seek back so we are at the correct position after we decoded a frame. */
+ av_seek_frame(anim->pFormatCtx, -1, pos, AVSEEK_FLAG_BACKWARD);
}
}
if (ret < 0) {
+ int64_t pts_to_search = ffmpeg_get_pts_to_search(anim, tc_index, position);
av_log(anim->pFormatCtx,
AV_LOG_ERROR,
"FETCH: "
@@ -1234,25 +1274,19 @@ static void ffmpeg_seek_and_decode(struct anim *anim, int position, struct anim_
"): errcode = %d\n",
pos,
position,
- (int64_t)pts_to_search,
+ pts_to_search,
ret);
}
avcodec_flush_buffers(anim->pCodecCtx);
- anim->next_pts = -1;
+ anim->cur_pts = -1;
- if (anim->next_packet->stream_index == anim->videoStream) {
- av_packet_unref(anim->next_packet);
- anim->next_packet->stream_index = -1;
- }
-
- /* memset(anim->pFrame, ...) ?? */
- if (ret < 0) {
- /* Seek failed. */
- return;
+ if (anim->cur_packet->stream_index == anim->videoStream) {
+ av_packet_unref(anim->cur_packet);
+ anim->cur_packet->stream_index = -1;
}
- ffmpeg_decode_video_frame_scan(anim, pts_to_search);
+ return ret;
}
static ImBuf *ffmpeg_fetchibuf(struct anim *anim, int position, IMB_Timecode_Type tc)
@@ -1282,25 +1316,22 @@ static ImBuf *ffmpeg_fetchibuf(struct anim *anim, int position, IMB_Timecode_Typ
if (ffmpeg_pts_matches_last_frame(anim, 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;
+ "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 (ffmpeg_can_scan(anim, position, tc_index) || ffmpeg_is_first_frame_decode(anim, position)) {
- ffmpeg_decode_video_frame_scan(anim, pts_to_search);
- }
- else if (ffmpeg_can_seek(anim, position)) {
- ffmpeg_seek_and_decode(anim, position, tc_index);
- }
- else {
+ 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) >= 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
@@ -1320,23 +1351,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->cur_position = position;
- anim->curposition = position;
+ IMB_refImBuf(anim->cur_frame_final);
- IMB_refImBuf(anim->last_frame);
-
- return anim->last_frame;
+ return anim->cur_frame_final;
}
static void free_anim_ffmpeg(struct anim *anim)
@@ -1348,7 +1376,7 @@ static void free_anim_ffmpeg(struct anim *anim)
if (anim->pCodecCtx) {
avcodec_free_context(&anim->pCodecCtx);
avformat_close_input(&anim->pFormatCtx);
- av_packet_free(&anim->next_packet);
+ av_packet_free(&anim->cur_packet);
av_frame_free(&anim->pFrame);
@@ -1369,7 +1397,7 @@ static void free_anim_ffmpeg(struct anim *anim)
av_frame_free(&anim->pFrameDeinterlaced);
sws_freeContext(anim->img_convert_ctx);
- IMB_freeImBuf(anim->last_frame);
+ IMB_freeImBuf(anim->cur_frame_final);
}
anim->duration_in_frames = 0;
}
@@ -1503,13 +1531,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;
@@ -1517,7 +1545,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
@@ -1525,7 +1553,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;
@@ -1536,7 +1564,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;
}
@@ -1591,16 +1619,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/indexer.c b/source/blender/imbuf/intern/indexer.c
index 11ce77e3091..46b28086129 100644
--- a/source/blender/imbuf/intern/indexer.c
+++ b/source/blender/imbuf/intern/indexer.c
@@ -466,13 +466,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)
{
@@ -499,13 +492,6 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg(
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 = 10;
- 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);
@@ -520,6 +506,13 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg(
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];
}
@@ -533,8 +526,8 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg(
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);
@@ -542,8 +535,11 @@ 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);
+ /* 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;
@@ -595,15 +591,19 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg(
if (st->codecpar->width != width || st->codecpar->height != height ||
st->codecpar->format != rv->c->pix_fmt) {
rv->frame = av_frame_alloc();
- av_image_fill_arrays(
- rv->frame->data,
- rv->frame->linesize,
- MEM_mallocN(av_image_get_buffer_size(rv->c->pix_fmt, round_up(width, 16), height, 1),
- "alloc proxy output frame"),
- rv->c->pix_fmt,
- round_up(width, 16),
- height,
- 1);
+
+ 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,
@@ -686,6 +686,9 @@ static void add_to_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, AVFrame *fr
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
int write_ret = av_interleaved_write_frame(ctx->of, packet);
if (write_ret != 0) {
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/io/gpencil/intern/gpencil_io_base.cc b/source/blender/io/gpencil/intern/gpencil_io_base.cc
index e79a2bc98ff..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"
@@ -69,18 +70,21 @@ GpencilIO::GpencilIO(const GpencilIOParams *iparams)
cfra_ = iparams->frame_cur;
/* Calculate camera matrix. */
- prepare_camera_params(iparams);
+ prepare_camera_params(scene_, iparams);
}
-void GpencilIO::prepare_camera_params(const GpencilIOParams *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 = params_.v3d->camera;
+ Object *cam_ob = scene->camera;
if (cam_ob != nullptr) {
/* Set up parameters. */
CameraParams params;
diff --git a/source/blender/io/gpencil/intern/gpencil_io_base.hh b/source/blender/io/gpencil/intern/gpencil_io_base.hh
index 2e1e1707c78..c3c6f1156bb 100644
--- a/source/blender/io/gpencil/intern/gpencil_io_base.hh
+++ b/source/blender/io/gpencil/intern/gpencil_io_base.hh
@@ -50,7 +50,7 @@ class GpencilIO {
GpencilIO(const GpencilIOParams *iparams);
void frame_number_set(const int value);
- void prepare_camera_params(const GpencilIOParams *iparams);
+ void prepare_camera_params(Scene *scene, const GpencilIOParams *iparams);
protected:
GpencilIOParams params_;
diff --git a/source/blender/io/gpencil/intern/gpencil_io_capi.cc b/source/blender/io/gpencil/intern/gpencil_io_capi.cc
index 8093ec3c52d..544c51e0b4f 100644
--- a/source/blender/io/gpencil/intern/gpencil_io_capi.cc
+++ b/source/blender/io/gpencil/intern/gpencil_io_capi.cc
@@ -121,7 +121,7 @@ static bool gpencil_io_export_pdf(Depsgraph *depsgraph,
CFRA = i;
BKE_scene_graph_update_for_newframe(depsgraph);
- exporter->prepare_camera_params(iparams);
+ exporter->prepare_camera_params(scene, iparams);
exporter->frame_number_set(i);
exporter->add_newpage();
exporter->add_body();
@@ -130,10 +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(iparams);
+ exporter->prepare_camera_params(scene, iparams);
exporter->add_newpage();
exporter->add_body();
result = exporter->write();
@@ -146,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,
@@ -153,7 +155,7 @@ static bool gpencil_io_export_frame_svg(GpencilExporterSVG *exporter,
{
bool result = false;
exporter->frame_number_set(iparams->frame_cur);
- exporter->prepare_camera_params(iparams);
+ exporter->prepare_camera_params(scene, iparams);
if (newpage) {
result |= exporter->add_newpage();
@@ -189,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/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h
index 8bf9afafa1b..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;
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 716c480bab8..f6242679808 100644
--- a/source/blender/makesdna/DNA_curve_types.h
+++ b/source/blender/makesdna/DNA_curve_types.h
@@ -43,6 +43,7 @@ struct Key;
struct Material;
struct Object;
struct VFont;
+struct CurveEval;
/* These two Lines with # tell makesdna this struct can be excluded. */
#
@@ -300,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;
diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h
index 0acf979516e..ea3c1ff7275 100644
--- a/source/blender/makesdna/DNA_gpencil_types.h
+++ b/source/blender/makesdna/DNA_gpencil_types.h
@@ -560,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_node_types.h b/source/blender/makesdna/DNA_node_types.h
index 58c94b6f369..1ab6c5e52a6 100644
--- a/source/blender/makesdna/DNA_node_types.h
+++ b/source/blender/makesdna/DNA_node_types.h
@@ -461,7 +461,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;
@@ -472,6 +471,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;
@@ -545,6 +546,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
*/
@@ -1626,6 +1633,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. */
diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h
index aa1178fb139..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;
diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h
index 4b95dd41b30..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. */
diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h
index 1fed8e14bd6..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_sculpt_tools_tilt;
char use_asset_browser;
char use_override_templates;
- char _pad[6];
+ 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 2f4e4e57b9f..9e7e30d913e 100644
--- a/source/blender/makesdna/DNA_view3d_types.h
+++ b/source/blender/makesdna/DNA_view3d_types.h
@@ -515,6 +515,7 @@ enum {
V3D_OVERLAY_HIDE_OBJECT_ORIGINS = (1 << 10),
V3D_OVERLAY_STATS = (1 << 11),
V3D_OVERLAY_FADE_INACTIVE = (1 << 12),
+ V3D_OVERLAY_MODE_TRANSFER = (1 << 13),
};
/** #View3DOverlay.edit_flag */
diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c
index 73b6f92184c..2d6b689b88d 100644
--- a/source/blender/makesrna/intern/rna_ID.c
+++ b/source/blender/makesrna/intern/rna_ID.c
@@ -699,9 +699,29 @@ 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) {
@@ -1228,7 +1248,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);
}
@@ -1798,12 +1818,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);
@@ -1851,6 +1871,30 @@ 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);
@@ -1917,6 +1961,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 150a455f1c7..948fef1b51e 100644
--- a/source/blender/makesrna/intern/rna_access.c
+++ b/source/blender/makesrna/intern/rna_access.c
@@ -6115,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_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 a3934b12a44..b363dcd4ba9 100644
--- a/source/blender/makesrna/intern/rna_constraint.c
+++ b/source/blender/makesrna/intern/rna_constraint.c
@@ -2598,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 abc96ddc820..e5e7564f578 100644
--- a/source/blender/makesrna/intern/rna_curve.c
+++ b/source/blender/makesrna/intern/rna_curve.c
@@ -1376,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",
@@ -2068,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_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c
index 91e13a4bee3..19ed5f960cf 100644
--- a/source/blender/makesrna/intern/rna_gpencil.c
+++ b/source/blender/makesrna/intern/rna_gpencil.c
@@ -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");
diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c
index f772f9b5573..d5a1047d287 100644
--- a/source/blender/makesrna/intern/rna_mesh.c
+++ b/source/blender/makesrna/intern/rna_mesh.c
@@ -1823,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);
@@ -1933,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
diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c
index 11f5ff0441a..2bafbd57e11 100644
--- a/source/blender/makesrna/intern/rna_nodetree.c
+++ b/source/blender/makesrna/intern/rna_nodetree.c
@@ -127,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,
@@ -234,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"},
@@ -3973,7 +3988,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)
@@ -3984,7 +3999,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;
@@ -4076,7 +4091,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);
}
@@ -9236,12 +9251,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");
}
@@ -11671,6 +11686,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);
diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c
index 9ba92431723..41b60bed5bb 100644
--- a/source/blender/makesrna/intern/rna_sequencer.c
+++ b/source/blender/makesrna/intern/rna_sequencer.c
@@ -1346,6 +1346,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)
@@ -1513,7 +1518,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);
@@ -2412,12 +2417,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);
diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c
index 161db81332d..1209f22dfd3 100644
--- a/source/blender/makesrna/intern/rna_space.c
+++ b/source/blender/makesrna/intern/rna_space.c
@@ -4199,6 +4199,14 @@ static void rna_def_space_view3d_overlay(BlenderRNA *brna)
prop, "Fade Inactive Objects", "Fade inactive geometry using the viewport background color");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL);
+ prop = RNA_def_property(srna, "show_mode_transfer", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "overlay.flag", V3D_OVERLAY_MODE_TRANSFER);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_ui_text(prop,
+ "Flash on Mode Transfer",
+ "Flash the target object when tranfering the active mode to it");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL);
+
prop = RNA_def_property(srna, "fade_inactive_alpha", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "overlay.fade_alpha");
RNA_def_property_ui_text(prop, "Opacity", "Strength of the fade effect");
@@ -4562,7 +4570,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");
}
@@ -5538,7 +5545,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);
diff --git a/source/blender/makesrna/intern/rna_ui.c b/source/blender/makesrna/intern/rna_ui.c
index 97852123954..4ec6bc68f72 100644
--- a/source/blender/makesrna/intern/rna_ui.c
+++ b/source/blender/makesrna/intern/rna_ui.c
@@ -620,7 +620,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 bacd3943141..6005ec96255 100644
--- a/source/blender/makesrna/intern/rna_userdef.c
+++ b/source/blender/makesrna/intern/rna_userdef.c
@@ -6276,6 +6276,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");
diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt
index 91327b97fe4..0138dd0c3ad 100644
--- a/source/blender/modifiers/CMakeLists.txt
+++ b/source/blender/modifiers/CMakeLists.txt
@@ -195,7 +195,11 @@ 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}
)
diff --git a/source/blender/modifiers/intern/MOD_boolean.cc b/source/blender/modifiers/intern/MOD_boolean.cc
index 9b8782737c3..4b9b24e4e47 100644
--- a/source/blender/modifiers/intern/MOD_boolean.cc
+++ b/source/blender/modifiers/intern/MOD_boolean.cc
@@ -283,11 +283,10 @@ static void BMD_mesh_intersection(BMesh *bm,
/* main bmesh intersection setup */
/* create tessface & intersect */
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);
+ BM_mesh_calc_tessellation_beauty(bm, looptris);
/* postpone this until after tessellating
* so we can use the original normals before the vertex are moved */
@@ -364,7 +363,7 @@ static void BMD_mesh_intersection(BMesh *bm,
BM_mesh_intersect(bm,
looptris,
- tottri,
+ looptris_tot,
bm_face_isect_pair,
nullptr,
false,
diff --git a/source/blender/modifiers/intern/MOD_mask.cc b/source/blender/modifiers/intern/MOD_mask.cc
index 812bfe3b375..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];
diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc
index 37a21a3c14b..8fa80025790 100644
--- a/source/blender/modifiers/intern/MOD_nodes.cc
+++ b/source/blender/modifiers/intern/MOD_nodes.cc
@@ -36,6 +36,7 @@
#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"
@@ -123,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);
+ }
+ }
}
}
@@ -179,7 +192,7 @@ static void add_object_relation(const ModifierUpdateDepsgraphContext *ctx, Objec
if (object.type == OB_EMPTY && object.instance_collection != nullptr) {
add_collection_relation(ctx, *object.instance_collection);
}
- else if (ELEM(object.type, OB_MESH, OB_POINTCLOUD, OB_VOLUME, OB_CURVE)) {
+ 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);
}
@@ -197,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_relation(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)
@@ -560,6 +583,48 @@ static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bso
};
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;
+ }
default: {
return nullptr;
}
@@ -1087,6 +1152,14 @@ static void draw_property_for_socket(uiLayout *layout,
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);
}
diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc
index ac5249b40a2..10ef2f4d8eb 100644
--- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc
+++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc
@@ -175,7 +175,7 @@ enum class NodeScheduleState {
struct NodeState {
/**
- * Needs to be locked when any data in this state is accessed that is not explicitely marked as
+ * Needs to be locked when any data in this state is accessed that is not explicitly marked as
* otherwise.
*/
std::mutex mutex;
@@ -192,12 +192,12 @@ struct NodeState {
MutableSpan<OutputState> outputs;
/**
- * Nodes that don't support lazyness have some special handling the first time they are executed.
+ * 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 lazyness do not run more than once.
+ * Used to check that nodes that don't support laziness do not run more than once.
*/
bool has_been_executed = false;
@@ -309,9 +309,9 @@ static const CPPType *get_socket_cpp_type(const SocketRef &socket)
return nodes::socket_cpp_type_get(*socket.typeinfo());
}
-static bool node_supports_lazyness(const DNode node)
+static bool node_supports_laziness(const DNode node)
{
- return node->typeinfo()->geometry_node_execute_supports_lazyness;
+ return node->typeinfo()->geometry_node_execute_supports_laziness;
}
/** Implements the callbacks that might be called when a node is executed. */
@@ -380,7 +380,8 @@ class GeometryNodesEvaluator {
void execute()
{
- task_pool_ = BLI_task_pool_create(this, TASK_PRIORITY_HIGH);
+ /* Disable threading until T88598 is resolved. */
+ task_pool_ = BLI_task_pool_create_no_threads(this);
this->create_states_for_reachable_nodes();
this->forward_group_inputs();
@@ -644,10 +645,10 @@ class GeometryNodesEvaluator {
if (!this->prepare_node_outputs_for_execution(locked_node)) {
return false;
}
- /* Initialize nodes that don't support lazyness. This is done after at least one output is
+ /* 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_lazyness(node)) {
+ 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;
}
@@ -722,7 +723,7 @@ class GeometryNodesEvaluator {
/* Ignore unavailable/non-data sockets. */
continue;
}
- /* Nodes that don't support lazyness require all inputs. */
+ /* 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);
}
@@ -789,8 +790,8 @@ class GeometryNodesEvaluator {
const bNode &bnode = *node->bnode();
if (node_state.has_been_executed) {
- if (!node_supports_lazyness(node)) {
- /* Nodes that don't support lazyness must not be executed more than once. */
+ if (!node_supports_laziness(node)) {
+ /* Nodes that don't support laziness must not be executed more than once. */
BLI_assert_unreachable();
}
}
@@ -923,12 +924,12 @@ class GeometryNodesEvaluator {
return;
}
- const bool supports_lazyness = node_supports_lazyness(locked_node.node);
+ 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_lazyness) {
+ 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) {
@@ -1025,7 +1026,7 @@ class GeometryNodesEvaluator {
/* Get all origin sockets, because we have to tag those as required as well. */
Vector<DSocket> origin_sockets;
input_socket.foreach_origin_socket(
- [&, this](const DSocket origin_socket) { origin_sockets.append(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. */
@@ -1078,7 +1079,7 @@ class GeometryNodesEvaluator {
}
/* Notify origin nodes that might want to set its inputs as unused as well. */
- socket.foreach_origin_socket([&, this](const DSocket origin_socket) {
+ 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. */
@@ -1511,7 +1512,7 @@ void NodeParamsProvider::set_output(StringRef identifier, GMutablePointer value)
bool NodeParamsProvider::lazy_require_input(StringRef identifier)
{
- BLI_assert(node_supports_lazyness(this->dnode));
+ BLI_assert(node_supports_laziness(this->dnode));
const DInputSocket socket = get_input_by_identifier(this->dnode, identifier);
BLI_assert(socket);
@@ -1547,7 +1548,7 @@ bool NodeParamsProvider::output_is_required(StringRef identifier) const
bool NodeParamsProvider::lazy_output_is_required(StringRef identifier) const
{
- BLI_assert(node_supports_lazyness(this->dnode));
+ BLI_assert(node_supports_laziness(this->dnode));
const DOutputSocket socket = get_output_by_identifier(this->dnode, identifier);
BLI_assert(socket);
diff --git a/source/blender/modifiers/intern/MOD_skin.c b/source/blender/modifiers/intern/MOD_skin.c
index e2d18cf1790..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 */
diff --git a/source/blender/modifiers/intern/MOD_surface.c b/source/blender/modifiers/intern/MOD_surface.c
index d2c011a21d3..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);
+ }
}
}
}
diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt
index 33b56fd0de0..8baa891df5c 100644
--- a/source/blender/nodes/CMakeLists.txt
+++ b/source/blender/nodes/CMakeLists.txt
@@ -162,8 +162,10 @@ set(SRC
geometry/nodes/node_geo_bounding_box.cc
geometry/nodes/node_geo_collection_info.cc
geometry/nodes/node_geo_common.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
@@ -178,6 +180,7 @@ set(SRC
geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc
geometry/nodes/node_geo_mesh_primitive_line.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
@@ -219,7 +222,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
diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h
index d2a702c30a6..3c43b53da5a 100644
--- a/source/blender/nodes/NOD_geometry.h
+++ b/source/blender/nodes/NOD_geometry.h
@@ -50,8 +50,10 @@ 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_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);
@@ -66,6 +68,7 @@ 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);
diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh
index 52d7e097f0d..7b176b2f395 100644
--- a/source/blender/nodes/NOD_geometry_exec.hh
+++ b/source/blender/nodes/NOD_geometry_exec.hh
@@ -200,7 +200,7 @@ class GeoNodeExecParams {
/**
* Returns true when the output has to be computed.
- * Nodes that support lazyness could use the #lazy_output_is_required variant to possibly avoid
+ * Nodes that support laziness could use the #lazy_output_is_required variant to possibly avoid
* some computations.
*/
bool output_is_required(StringRef identifier) const
@@ -212,7 +212,7 @@ class GeoNodeExecParams {
* 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 lazyness.
+ * This can only be used when the node supports laziness.
*/
bool lazy_require_input(StringRef identifier)
{
@@ -222,7 +222,7 @@ class GeoNodeExecParams {
/**
* 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 lazyness.
+ * This can only be used when the node supports laziness.
*/
bool lazy_output_is_required(StringRef identifier)
{
diff --git a/source/blender/nodes/NOD_math_functions.hh b/source/blender/nodes/NOD_math_functions.hh
index 45de1816549..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));
diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h
index ce1813fdac3..19b671de49d 100644
--- a/source/blender/nodes/NOD_static_types.h
+++ b/source/blender/nodes/NOD_static_types.h
@@ -289,8 +289,10 @@ DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_VECTOR_ROTATE, def_geo_attribute_vector
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_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", "")
@@ -305,6 +307,7 @@ DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_GRID, 0, "MESH_PRIMITIVE_GRID", Me
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE, 0, "MESH_PRIMITIVE_ICO_SPHERE", MeshIcoSphere, "Ico Sphere", "")
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_LINE, def_geo_mesh_line, "MESH_PRIMITIVE_LINE", MeshLine, "Line", "")
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", "")
diff --git a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc
index d9b36924516..dca6dc59ca2 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 = (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));
+ }
+ 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();
}
diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh
index 81092798ca1..79a98c5ebf0 100644
--- a/source/blender/nodes/geometry/node_geometry_util.hh
+++ b/source/blender/nodes/geometry/node_geometry_util.hh
@@ -62,4 +62,12 @@ Mesh *create_cylinder_or_cone_mesh(const float radius_top,
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_attribute_clamp.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc
index 21538db5455..71643df1cb6 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc
@@ -101,9 +101,12 @@ template<> inline float3 clamp_value(const float3 val, const float3 min, const f
return tmp;
}
-template<> inline Color4f clamp_value(const Color4f val, const Color4f min, const Color4f max)
+template<>
+inline ColorGeometry4f clamp_value(const ColorGeometry4f val,
+ const ColorGeometry4f min,
+ const ColorGeometry4f max)
{
- Color4f tmp;
+ 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);
@@ -214,8 +217,8 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam
break;
}
case CD_PROP_COLOR: {
- Color4f min = params.get_input<Color4f>("Min_003");
- Color4f max = params.get_input<Color4f>("Max_003");
+ 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);
@@ -230,8 +233,9 @@ static void clamp_attribute(GeometryComponent &component, const GeoNodeExecParam
std::swap(min.a, max.a);
}
}
- MutableSpan<Color4f> results = attribute_result.as_span<Color4f>();
- clamp_attribute<Color4f>(attribute_input->typed<Color4f>(), results, min, max);
+ MutableSpan<ColorGeometry4f> results = attribute_result.as_span<ColorGeometry4f>();
+ clamp_attribute<ColorGeometry4f>(
+ attribute_input->typed<ColorGeometry4f>(), results, min, max);
break;
}
default: {
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 b13e82e676d..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
@@ -83,8 +83,8 @@ static void execute_on_component(const GeoNodeExecParams &params, GeometryCompon
* currently. */
const AttributeDomain result_domain = get_result_domain(component, input_name, result_name);
- OutputAttribute_Typed<Color4f> attribute_result =
- component.attribute_try_get_for_output_only<Color4f>(result_name, result_domain);
+ OutputAttribute_Typed<ColorGeometry4f> attribute_result =
+ component.attribute_try_get_for_output_only<ColorGeometry4f>(result_name, result_domain);
if (!attribute_result) {
return;
}
@@ -92,7 +92,7 @@ static void execute_on_component(const GeoNodeExecParams &params, GeometryCompon
GVArray_Typed<float> attribute_in = component.attribute_get_for_read<float>(
input_name, result_domain, 0.0f);
- MutableSpan<Color4f> results = attribute_result.as_span();
+ MutableSpan<ColorGeometry4f> results = attribute_result.as_span();
ColorBand *color_ramp = &node_storage->color_ramp;
parallel_for(IndexRange(attribute_in.size()), 512, [&](IndexRange range) {
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 a2ff1668a06..57ac68b4cd4 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc
@@ -131,16 +131,16 @@ static void do_equal_operation_float3(const VArray<float3> &input_a,
}
}
-static void do_equal_operation_color4f(const VArray<Color4f> &input_a,
- const VArray<Color4f> &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;
}
}
@@ -185,16 +185,16 @@ static void do_not_equal_operation_float3(const VArray<float3> &input_a,
}
}
-static void do_not_equal_operation_color4f(const VArray<Color4f> &input_a,
- const VArray<Color4f> &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;
}
}
@@ -287,8 +287,10 @@ static void attribute_compare_calc(GeometryComponent &component, const GeoNodeEx
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->typed<Color4f>(), attribute_b->typed<Color4f>(), 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(
@@ -305,8 +307,10 @@ static void attribute_compare_calc(GeometryComponent &component, const GeoNodeEx
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->typed<Color4f>(), attribute_b->typed<Color4f>(), 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(
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
index 2fc86269797..599c9e58e52 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc
@@ -165,9 +165,10 @@ static void execute_on_component(const GeoNodeExecParams &params, GeometryCompon
}
case CD_PROP_COLOR: {
const CurveMapping *cumap = (CurveMapping *)node_storage.curve_rgb;
- GVArray_Typed<Color4f> attribute_in = component.attribute_get_for_read<Color4f>(
- input_name, result_domain, Color4f(0.0f, 0.0f, 0.0f, 1.0f));
- MutableSpan<Color4f> results = attribute_result.as_span<Color4f>();
+ 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]);
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 60522fd0f72..389abe3b2aa 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc
@@ -110,7 +110,7 @@ static void fill_attribute(GeometryComponent &component, const GeoNodeExecParams
break;
}
case CD_PROP_COLOR: {
- const Color4f value = params.get_input<Color4f>("Value_002");
+ const ColorGeometry4f value = params.get_input<ColorGeometry4f>("Value_002");
attribute->fill(&value);
break;
}
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 e502a183ef5..a6bd6c0ee32 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc
@@ -120,16 +120,16 @@ static void do_mix_operation_float3(const int blend_mode,
static void do_mix_operation_color4f(const int blend_mode,
const VArray<float> &factors,
- const VArray<Color4f> &inputs_a,
- const VArray<Color4f> &inputs_b,
- VMutableArray<Color4f> &results)
+ const VArray<ColorGeometry4f> &inputs_a,
+ const VArray<ColorGeometry4f> &inputs_b,
+ VMutableArray<ColorGeometry4f> &results)
{
const int size = results.size();
parallel_for(IndexRange(size), 512, [&](IndexRange range) {
for (const int i : range) {
const float factor = factors[i];
- Color4f a = inputs_a[i];
- const Color4f b = inputs_b[i];
+ ColorGeometry4f a = inputs_a[i];
+ const ColorGeometry4f b = inputs_b[i];
ramp_blend(blend_mode, a, factor, b);
results.set(i, a);
}
@@ -160,9 +160,9 @@ static void do_mix_operation(const CustomDataType result_type,
else if (result_type == CD_PROP_COLOR) {
do_mix_operation_color4f(blend_mode,
attribute_factor,
- attribute_a.typed<Color4f>(),
- attribute_b.typed<Color4f>(),
- attribute_result.typed<Color4f>());
+ attribute_a.typed<ColorGeometry4f>(),
+ attribute_b.typed<ColorGeometry4f>(),
+ attribute_result.typed<ColorGeometry4f>());
}
}
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 aa558314b9e..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
@@ -79,8 +79,9 @@ static void execute_on_component(GeometryComponent &component, const GeoNodeExec
const AttributeDomain result_domain = get_result_domain(
component, result_attribute_name, mapping_name);
- OutputAttribute_Typed<Color4f> attribute_out =
- component.attribute_try_get_for_output_only<Color4f>(result_attribute_name, result_domain);
+ OutputAttribute_Typed<ColorGeometry4f> attribute_out =
+ component.attribute_try_get_for_output_only<ColorGeometry4f>(result_attribute_name,
+ result_domain);
if (!attribute_out) {
return;
}
@@ -88,7 +89,7 @@ static void execute_on_component(GeometryComponent &component, const GeoNodeExec
GVArray_Typed<float3> mapping_attribute = component.attribute_get_for_read<float3>(
mapping_name, result_domain, {0, 0, 0});
- MutableSpan<Color4f> colors = attribute_out.as_span();
+ 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};
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 8877af445f9..b04e04d1cb7 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
@@ -59,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 void geo_node_attribute_vector_math_layout(uiLayout *layout,
@@ -137,6 +140,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:
@@ -495,6 +499,7 @@ static void attribute_vector_math_calc(GeometryComponent &component,
break;
case NODE_VECTOR_MATH_WRAP:
case NODE_VECTOR_MATH_FACEFORWARD:
+ 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>(),
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
index 1c42b9341a0..684f7d6c702 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc
@@ -182,6 +182,8 @@ static std::unique_ptr<CurveEval> resample_curve(const CurveEval &input_curve,
}
}
+ output_curve->attributes.reallocate(output_curve->splines().size());
+
return output_curve;
}
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
index f0effdc71f6..fe1b23bf6d7 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc
@@ -21,6 +21,7 @@
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "BKE_material.h"
#include "BKE_mesh.h"
#include "BKE_spline.hh"
@@ -236,6 +237,7 @@ static Mesh *curve_to_mesh_calculate(const CurveEval &curve, const CurveEval &pr
}
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};
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..0e053a6d0e9
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc
@@ -0,0 +1,677 @@
+/*
+ * 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();
+ 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_mesh_primitive_circle.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc
index aace71d1d40..f19d533d0b0 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"
@@ -214,6 +215,7 @@ static void geo_node_mesh_primitive_circle_exec(GeoNodeExecParams params)
}
Mesh *mesh = create_circle_mesh(radius, verts_num, fill_type);
+ BKE_id_material_eval_ensure_default_slot(&mesh->id);
BLI_assert(BKE_mesh_is_valid(mesh));
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 2806472286e..4c1521aa6f1 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,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"
@@ -561,6 +562,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);
+ BKE_id_material_eval_ensure_default_slot(&mesh->id);
/* 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);
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 1d31068653e..b34913df843 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"
@@ -64,6 +65,7 @@ static void geo_node_mesh_primitive_cube_exec(GeoNodeExecParams params)
const float size = params.extract_input<float>("Size");
Mesh *mesh = create_cube_mesh(size);
+ BKE_id_material_eval_ensure_default_slot(&mesh->id);
params.set_output("Geometry", GeometrySet::create_with_mesh(mesh));
}
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..c7b9fb920f8 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"
@@ -75,6 +76,7 @@ static void geo_node_mesh_primitive_cylinder_exec(GeoNodeExecParams params)
/* The cylinder is a special case of the cone mesh where the top and bottom radius are equal. */
Mesh *mesh = create_cylinder_or_cone_mesh(radius, radius, depth, verts_num, fill_type);
+ BKE_id_material_eval_ensure_default_slot(&mesh->id);
params.set_output("Geometry", GeometrySet::create_with_mesh(mesh));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc
index 5a4bab86421..ac2f5a23a4d 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.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"
@@ -167,6 +168,7 @@ static void geo_node_mesh_primitive_grid_exec(GeoNodeExecParams params)
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));
}
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..5f7d8150022 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"
@@ -67,6 +68,7 @@ static void geo_node_mesh_primitive_ico_sphere_exec(GeoNodeExecParams params)
const float radius = params.extract_input<float>("Radius");
Mesh *mesh = create_ico_sphere_mesh(subdivisions, radius);
+ BKE_id_material_eval_ensure_default_slot(&mesh->id);
params.set_output("Geometry", GeometrySet::create_with_mesh(mesh));
}
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 2eeb87695a8..d93c4e39fda 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
@@ -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"
@@ -165,6 +166,7 @@ static void geo_node_mesh_primitive_line_exec(GeoNodeExecParams params)
const int count = params.extract_input<int>("Count");
mesh = create_line_mesh(start, delta, count);
}
+ BKE_id_material_eval_ensure_default_slot(&mesh->id);
params.set_output("Geometry", GeometrySet::create_with_mesh(mesh));
}
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 cc93e71a5dd..7d340679269 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,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"
@@ -296,6 +297,7 @@ static void geo_node_mesh_primitive_uv_sphere_exec(GeoNodeExecParams params)
const float radius = params.extract_input<float>("Radius");
Mesh *mesh = create_uv_sphere_mesh(radius, segments_num, rings_num);
+ BKE_id_material_eval_ensure_default_slot(&mesh->id);
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_point_separate.cc b/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc
index d2b024b208c..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,10 +52,10 @@ 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()) {
ReadAttributeLookup attribute = in_component.attribute_try_get_for_read(name);
@@ -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);
}
@@ -167,6 +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_lazyness = true;
+ ntype.geometry_node_execute_supports_laziness = true;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_switch.cc b/source/blender/nodes/geometry/nodes/node_geo_switch.cc
index bb0a20f4561..742fafba9e6 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_switch.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_switch.cc
@@ -22,24 +22,24 @@
static bNodeSocketTemplate geo_node_switch_in[] = {
{SOCK_BOOLEAN, N_("Switch")},
- {SOCK_FLOAT, N_("A"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX},
- {SOCK_FLOAT, N_("B"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX},
- {SOCK_INT, N_("A"), 0, 0, 0, 0, -100000, 100000},
- {SOCK_INT, N_("B"), 0, 0, 0, 0, -100000, 100000},
- {SOCK_BOOLEAN, N_("A")},
- {SOCK_BOOLEAN, N_("B")},
- {SOCK_VECTOR, N_("A"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX},
- {SOCK_VECTOR, N_("B"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX},
- {SOCK_RGBA, N_("A"), 0.8, 0.8, 0.8, 1.0},
- {SOCK_RGBA, N_("B"), 0.8, 0.8, 0.8, 1.0},
- {SOCK_STRING, N_("A")},
- {SOCK_STRING, N_("B")},
- {SOCK_GEOMETRY, N_("A")},
- {SOCK_GEOMETRY, N_("B")},
- {SOCK_OBJECT, N_("A")},
- {SOCK_OBJECT, N_("B")},
- {SOCK_COLLECTION, N_("A")},
- {SOCK_COLLECTION, N_("B")},
+ {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")},
{-1, ""},
};
@@ -64,7 +64,7 @@ static void geo_node_switch_layout(uiLayout *layout, bContext *UNUSED(C), Pointe
static void geo_node_switch_init(bNodeTree *UNUSED(tree), bNode *node)
{
NodeSwitch *data = (NodeSwitch *)MEM_callocN(sizeof(NodeSwitch), __func__);
- data->input_type = SOCK_FLOAT;
+ data->input_type = SOCK_GEOMETRY;
node->storage = data;
}
@@ -91,8 +91,8 @@ static void output_input(GeoNodeExecParams &params,
const StringRef input_suffix,
const StringRef output_identifier)
{
- const std::string name_a = "A" + input_suffix;
- const std::string name_b = "B" + input_suffix;
+ 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)) {
@@ -134,7 +134,7 @@ static void geo_node_switch_exec(GeoNodeExecParams params)
break;
}
case SOCK_RGBA: {
- output_input<Color4f>(params, input, "_004", "Output_004");
+ output_input<ColorGeometry4f>(params, input, "_004", "Output_004");
break;
}
case SOCK_STRING: {
@@ -165,13 +165,13 @@ void register_node_type_geo_switch()
{
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_SWITCH, "Switch", NODE_CLASS_GEOMETRY, 0);
+ 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_lazyness = true;
+ 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 9714a4f8a80..1ad5dbea492 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_transform.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_transform.cc
@@ -20,6 +20,7 @@
#include "BLI_float4x4.hh"
+#include "DNA_mesh_types.h"
#include "DNA_pointcloud_types.h"
#include "DNA_volume_types.h"
@@ -72,7 +73,8 @@ void transform_mesh(Mesh *mesh,
else {
const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, scale);
BKE_mesh_transform(mesh, matrix.values, false);
- BKE_mesh_calc_normals(mesh);
+ mesh->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
+ mesh->runtime.cd_dirty_poly |= CD_MASK_NORMAL;
}
}
@@ -158,7 +160,6 @@ static void transform_curve(CurveEval &curve,
const float3 rotation,
const float3 scale)
{
-
if (use_translate(rotation, scale)) {
curve.translate(translation);
}
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 c6049787970..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"
@@ -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/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_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc
index 73a702c753a..188d198e159 100644
--- a/source/blender/nodes/intern/node_geometry_exec.cc
+++ b/source/blender/nodes/intern/node_geometry_exec.cc
@@ -104,9 +104,10 @@ GVArrayPtr GeoNodeExecParams::get_input_attribute(const StringRef name,
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);
+ const ColorGeometry4f value = this->get_input<ColorGeometry4f>(found_socket->identifier);
BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer);
- conversions.convert_to_uninitialized(CPPType::get<Color4f>(), *cpp_type, &value, 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);
diff --git a/source/blender/nodes/intern/node_socket.cc b/source/blender/nodes/intern/node_socket.cc
index ce2848b52a0..052896d2f48 100644
--- a/source/blender/nodes/intern/node_socket.cc
+++ b/source/blender/nodes/intern/node_socket.cc
@@ -37,6 +37,7 @@
#include "BKE_node.h"
#include "DNA_collection_types.h"
+#include "DNA_material_types.h"
#include "RNA_access.h"
#include "RNA_types.h"
@@ -396,6 +397,13 @@ void node_socket_copy_default_value(bNodeSocket *to, const bNodeSocket *from)
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);
@@ -637,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;
}
diff --git a/source/blender/nodes/intern/node_tree_ref.cc b/source/blender/nodes/intern/node_tree_ref.cc
index 5c0bc0b5ebc..8699736e543 100644
--- a/source/blender/nodes/intern/node_tree_ref.cc
+++ b/source/blender/nodes/intern/node_tree_ref.cc
@@ -297,6 +297,12 @@ void OutputSocketRef::foreach_logical_target(
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);
diff --git a/source/blender/nodes/intern/type_conversions.cc b/source/blender/nodes/intern/type_conversions.cc
index 63f7b8a9ee8..220e5ea9046 100644
--- a/source/blender/nodes/intern/type_conversions.cc
+++ b/source/blender/nodes/intern/type_conversions.cc
@@ -66,9 +66,9 @@ static bool float_to_bool(const float &a)
{
return a > 0.0f;
}
-static Color4f float_to_color(const float &a)
+static ColorGeometry4f float_to_color(const float &a)
{
- return Color4f(a, a, a, 1.0f);
+ return ColorGeometry4f(a, a, a, 1.0f);
}
static float3 float2_to_float3(const float2 &a)
@@ -87,9 +87,9 @@ static bool float2_to_bool(const float2 &a)
{
return !is_zero_v2(a);
}
-static Color4f float2_to_color(const float2 &a)
+static ColorGeometry4f float2_to_color(const float2 &a)
{
- return Color4f(a.x, a.y, 0.0f, 1.0f);
+ return ColorGeometry4f(a.x, a.y, 0.0f, 1.0f);
}
static bool float3_to_bool(const float3 &a)
@@ -108,9 +108,9 @@ static float2 float3_to_float2(const float3 &a)
{
return float2(a);
}
-static Color4f float3_to_color(const float3 &a)
+static ColorGeometry4f float3_to_color(const float3 &a)
{
- return Color4f(a.x, a.y, a.z, 1.0f);
+ return ColorGeometry4f(a.x, a.y, a.z, 1.0f);
}
static bool int_to_bool(const int32_t &a)
@@ -129,9 +129,9 @@ static float3 int_to_float3(const int32_t &a)
{
return float3((float)a);
}
-static Color4f int_to_color(const int32_t &a)
+static ColorGeometry4f int_to_color(const int32_t &a)
{
- return Color4f((float)a, (float)a, (float)a, 1.0f);
+ return ColorGeometry4f((float)a, (float)a, (float)a, 1.0f);
}
static float bool_to_float(const bool &a)
@@ -150,28 +150,28 @@ static float3 bool_to_float3(const bool &a)
{
return (a) ? float3(1.0f) : float3(0.0f);
}
-static Color4f bool_to_color(const bool &a)
+static ColorGeometry4f bool_to_color(const bool &a)
{
- return (a) ? Color4f(1.0f, 1.0f, 1.0f, 1.0f) : Color4f(0.0f, 0.0f, 0.0f, 1.0f);
+ 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 Color4f &a)
+static bool color_to_bool(const ColorGeometry4f &a)
{
return rgb_to_grayscale(a) > 0.0f;
}
-static float color_to_float(const Color4f &a)
+static float color_to_float(const ColorGeometry4f &a)
{
return rgb_to_grayscale(a);
}
-static int32_t color_to_int(const Color4f &a)
+static int32_t color_to_int(const ColorGeometry4f &a)
{
return (int)rgb_to_grayscale(a);
}
-static float2 color_to_float2(const Color4f &a)
+static float2 color_to_float2(const ColorGeometry4f &a)
{
return float2(a.r, a.g);
}
-static float3 color_to_float3(const Color4f &a)
+static float3 color_to_float3(const ColorGeometry4f &a)
{
return float3(a.r, a.g, a.b);
}
@@ -184,37 +184,37 @@ static DataTypeConversions create_implicit_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, Color4f, float_to_color>(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, Color4f, float2_to_color>(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, Color4f, float3_to_color>(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, Color4f, int_to_color>(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, Color4f, bool_to_color>(conversions);
+ add_implicit_conversion<bool, ColorGeometry4f, bool_to_color>(conversions);
- add_implicit_conversion<Color4f, bool, color_to_bool>(conversions);
- add_implicit_conversion<Color4f, float, color_to_float>(conversions);
- add_implicit_conversion<Color4f, int32_t, color_to_int>(conversions);
- add_implicit_conversion<Color4f, float2, color_to_float2>(conversions);
- add_implicit_conversion<Color4f, float3, color_to_float3>(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;
}
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_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_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 472f903e1a5..419a11201aa 100644
--- a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc
@@ -94,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;
@@ -134,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,
@@ -154,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");
diff --git a/source/blender/python/bmesh/bmesh_py_types.c b/source/blender/python/bmesh/bmesh_py_types.c
index f1a8d450ea5..598640f8f68 100644
--- a/source/blender/python/bmesh/bmesh_py_types.c
+++ b/source/blender/python/bmesh/bmesh_py_types.c
@@ -1385,7 +1385,6 @@ static PyObject *bpy_bmesh_calc_loop_triangles(BPy_BMElem *self)
BMesh *bm;
int looptris_tot;
- int tottri;
BMLoop *(*looptris)[3];
PyObject *ret;
@@ -1398,10 +1397,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));
}
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 a0b543097e6..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 "
diff --git a/source/blender/python/mathutils/mathutils_Matrix.c b/source/blender/python/mathutils/mathutils_Matrix.c
index c312012b24c..5d38a3692c3 100644
--- a/source/blender/python/mathutils/mathutils_Matrix.c
+++ b/source/blender/python/mathutils/mathutils_Matrix.c
@@ -2253,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);
}
diff --git a/source/blender/python/mathutils/mathutils_bvhtree.c b/source/blender/python/mathutils/mathutils_bvhtree.c
index 1acbcc006ca..79ed9e68420 100644
--- a/source/blender/python/mathutils/mathutils_bvhtree.c
+++ b/source/blender/python/mathutils/mathutils_bvhtree.c
@@ -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/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/sequencer/SEQ_sequencer.h b/source/blender/sequencer/SEQ_sequencer.h
index 4f49b8cb58f..ad0815892f7 100644
--- a/source/blender/sequencer/SEQ_sequencer.h
+++ b/source/blender/sequencer/SEQ_sequencer.h
@@ -46,12 +46,11 @@ enum {
/* 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_utils.h b/source/blender/sequencer/SEQ_utils.h
index 9d529089ffc..a4dc80d75db 100644
--- a/source/blender/sequencer/SEQ_utils.h
+++ b/source/blender/sequencer/SEQ_utils.h
@@ -61,6 +61,7 @@ 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
}
#endif
diff --git a/source/blender/sequencer/intern/image_cache.c b/source/blender/sequencer/intern/image_cache.c
index a0c95c1c197..5ccf2a027b0 100644
--- a/source/blender/sequencer/intern/image_cache.c
+++ b/source/blender/sequencer/intern/image_cache.c
@@ -518,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");
@@ -540,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);
}
diff --git a/source/blender/sequencer/intern/render.c b/source/blender/sequencer/intern/render.c
index f3cea273fdf..0bad66c3d7a 100644
--- a/source/blender/sequencer/intern/render.c
+++ b/source/blender/sequencer/intern/render.c
@@ -70,6 +70,7 @@
#include "SEQ_proxy.h"
#include "SEQ_render.h"
#include "SEQ_sequencer.h"
+#include "SEQ_time.h"
#include "SEQ_utils.h"
#include "effects.h"
@@ -273,15 +274,6 @@ static bool seq_is_effect_of(const Sequence *seq_effect, const Sequence *possibl
* Order of applying these conditions is important. */
static bool must_render_strip(const Sequence *seq, SeqCollection *strips_under_playhead)
{
- /* Sound strips are not rendered. */
- if (seq->type == SEQ_TYPE_SOUND_RAM) {
- return false;
- }
- /* Muted strips are not rendered. */
- if ((seq->flag & SEQ_MUTE) != 0) {
- return false;
- }
-
bool seq_have_effect_in_stack = false;
Sequence *seq_iter;
SEQ_ITERATOR_FOREACH (seq_iter, strips_under_playhead) {
@@ -318,7 +310,7 @@ static SeqCollection *query_strips_at_frame(ListBase *seqbase, const int timelin
SeqCollection *collection = SEQ_collection_create();
LISTBASE_FOREACH (Sequence *, seq, seqbase) {
- if ((seq->startdisp <= timeline_frame) && (seq->enddisp > timeline_frame)) {
+ if (SEQ_time_strip_intersects_frame(seq, timeline_frame)) {
SEQ_collection_append_strip(seq, collection);
}
}
@@ -340,6 +332,15 @@ static void collection_filter_channel_up_to_incl(SeqCollection *collection, cons
static void collection_filter_rendered_strips(SeqCollection *collection)
{
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;
@@ -1124,8 +1125,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. */
@@ -1237,6 +1236,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;
}
diff --git a/source/blender/sequencer/intern/sequencer.c b/source/blender/sequencer/intern/sequencer.c
index 55c14944a23..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;
}
@@ -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 5ec2269b993..9e5afb45f2a 100644
--- a/source/blender/sequencer/intern/strip_add.c
+++ b/source/blender/sequencer/intern/strip_add.c
@@ -548,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. */
@@ -577,6 +586,7 @@ 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);
@@ -691,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_relations.c b/source/blender/sequencer/intern/strip_relations.c
index 1215cb78b56..7c5a3f031db 100644
--- a/source/blender/sequencer/intern/strip_relations.c
+++ b/source/blender/sequencer/intern/strip_relations.c
@@ -259,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;
}
@@ -358,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 40d7fade308..b8b6f62c7aa 100644
--- a/source/blender/sequencer/intern/strip_time.c
+++ b/source/blender/sequencer/intern/strip_time.c
@@ -409,7 +409,7 @@ static bool strip_exists_at_frame(SeqCollection *all_strips, const int timeline_
{
Sequence *seq;
SEQ_ITERATOR_FOREACH (seq, all_strips) {
- if ((seq->startdisp <= timeline_frame) && (seq->enddisp > timeline_frame)) {
+ if (SEQ_time_strip_intersects_frame(seq, timeline_frame)) {
return true;
}
}
@@ -468,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/utils.c b/source/blender/sequencer/intern/utils.c
index cf1d7d66476..9aeb2961751 100644
--- a/source/blender/sequencer/intern/utils.c
+++ b/source/blender/sequencer/intern/utils.c
@@ -43,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"
@@ -406,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
diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c
index 611a9cba000..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;
diff --git a/source/blender/windowmanager/intern/wm_event_query.c b/source/blender/windowmanager/intern/wm_event_query.c
index 3efff20f107..0050c834a56 100644
--- a/source/blender/windowmanager/intern/wm_event_query.c
+++ b/source/blender/windowmanager/intern/wm_event_query.c
@@ -282,15 +282,17 @@ bool WM_event_is_mouse_drag(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_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c
index f0735c562d1..0bdb2953014 100644
--- a/source/blender/windowmanager/intern/wm_init_exit.c
+++ b/source/blender/windowmanager/intern/wm_init_exit.c
@@ -344,8 +344,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();
diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt
index c2893317924..519b3781103 100644
--- a/source/creator/CMakeLists.txt
+++ b/source/creator/CMakeLists.txt
@@ -119,6 +119,10 @@ if(WITH_GMP)
add_definitions(-DWITH_GMP)
endif()
+if(WITH_OPENCOLORIO)
+ add_definitions(-DWITH_OCIO)
+endif()
+
# Setup the exe sources and buildinfo
set(SRC
creator.c
@@ -285,6 +289,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)
@@ -870,13 +883,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
)
@@ -884,15 +897,15 @@ 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
)
@@ -1212,7 +1225,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_args.c b/source/creator/creator_args.c
index 36fdaef507b..8b1ac05f086 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
diff --git a/source/tools b/source/tools
-Subproject f99d29ae3e6ad44d45d79309454c45f8088781a
+Subproject 01f51a0e551ab730f0934dc6488613690ac4bf8
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..251d27fce90
--- /dev/null
+++ b/tests/python/bl_run_operators_event_simulate.py
@@ -0,0 +1,577 @@
+# ##### 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):
+ """
+ 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")
+
+ sys.exit(0)
+ 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,
+ )
+
+ # 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)
+
+ run_event_simulate(event_iter=main_event_iter(action_list=args.actions))
+
+
+if __name__ == "__main__":
+ main()